From a5a5672aa14aedeb4c935ded6dc30f43f0aab921 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 16 Nov 2019 22:01:37 +0200 Subject: [PATCH 01/24] initial commit --- profile/chrome/utils/boot.jsm | 351 ++++++++++++++++++++++++++ program/config.js | 6 + program/defaults/pref/config-prefs.js | 2 + 3 files changed, 359 insertions(+) create mode 100644 profile/chrome/utils/boot.jsm create mode 100644 program/config.js create mode 100644 program/defaults/pref/config-prefs.js diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm new file mode 100644 index 0000000..446b912 --- /dev/null +++ b/profile/chrome/utils/boot.jsm @@ -0,0 +1,351 @@ +let EXPORTED_SYMBOLS = []; + +console.warn( "Browser is executing custom scripts via autoconfig" ); +const {Services} = ChromeUtils.import('resource://gre/modules/Services.jsm'); + +const yPref = { + get: function (prefPath) { + const sPrefs = Services.prefs; + try { + switch (sPrefs.getPrefType(prefPath)) { + case 0: + return undefined; + case 32: + return sPrefs.getStringPref(prefPath); + case 64: + return sPrefs.getIntPref(prefPath); + case 128: + return sPrefs.getBoolPref(prefPath); + } + } catch (ex) { + return undefined; + } + return; + }, + set: function (prefPath, value) { + const sPrefs = Services.prefs; + switch (typeof value) { + case 'string': + return sPrefs.setCharPref(prefPath, value) || value; + case 'number': + return sPrefs.setIntPref(prefPath, value) || value; + case 'boolean': + return sPrefs.setBoolPref(prefPath, value) || value; + } + return; + }, + addListener:(a,b)=>{ let o = (q,w,e)=>(b(yPref.get(e),e)); Services.prefs.addObserver(a,o);return{pref:a,observer:o}}, + removeListener:(a)=>( Services.prefs.removeObserver(a.pref,a.observer) ) +} + +let _uc = { + BROWSERCHROME: 'chrome://browser/content/browser.xhtml', + PREF_ENABLED: 'userChromeJS.enabled', + PREF_SCRIPTSDISABLED: 'userChromeJS.scriptsDisabled', + SCRIPT_DIR: 'JS', + RESOURCE_DIR: 'resources', + BASE_FILEURI: null, // set later + + get chromeDir() {return Services.dirsvc.get('UChrm',Ci.nsIFile)}, + + get chromeDirEntries() {return _uc.chromeDir.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator)}, + + getDirectoryEntries: function(nsDirEnum,name,matchDirectory = false){ + while(nsDirEnum.hasMoreElements()){ + let entry = nsDirEnum.getNext().QueryInterface(Ci.nsIFile); + if(name === entry.leafName){ + if(matchDirectory){ + if(entry.isDirectory()){ + return entry.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator); + } + }else{ + if(entry.isFile()){ + return entry + } + } + } + } + return null + }, + + getDirEntry: function(filename,isLoader = false){ + filename = filename.replace("\\","/"); + let pathParts = ((isLoader ? _uc.SCRIPT_DIR : _uc.RESOURCE_DIR) + "/" + filename).split("/").filter((a)=>(!!a)); + let leafName = pathParts.pop(); + let isFile = leafName.indexOf(".") != -1; + let currentDir = _uc.chromeDirEntries; + for(let part of pathParts){ + currentDir = _uc.getDirectoryEntries(currentDir,part,true) + } + return currentDir ? _uc.getDirectoryEntries(currentDir,leafName,!isFile) : null + }, + + createFileURI: (fileName,isLoader = false) => (`${_uc.BASE_FILEURI}${isLoader?_uc.SCRIPT_DIR:_uc.RESOURCE_DIR}/${fileName.replace("\\","/")}`), + + getScripts: function () { + this.scripts = {}; + if(!yPref.get(_uc.PREF_ENABLED) || !(/^[\w_]*$/.test(_uc.SCRIPT_DIR))){ + console.log("Scripts are disabled or the given script directory name is invalid"); + return + } + + let files = _uc.getDirEntry('',true); + while(files.hasMoreElements()){ + let file = files.getNext().QueryInterface(Ci.nsIFile); + if (/\.uc\.js$/i.test(file.leafName)) { + _uc.getScriptData(file); + } + } + }, + + getScriptData: function (aFile) { + let header = (_uc.utils.readFile(aFile,true).match(/^\/\/ ==UserScript==\s*\n(?:.*\n)*?\/\/ ==\/UserScript==\s*\n/m) || [''])[0]; + let match, rex = { + include: [], + exclude: [] + }; + let findNextRe = /^\/\/ @(include|exclude)\s+(.+)\s*$/gm; + while ((match = findNextRe.exec(header))) { + rex[match[1]].push(match[2].replace(/^main$/i, _uc.BROWSERCHROME).replace(/\*/g, '.*?')); + } + if (!rex.include.length) { + rex.include.push(_uc.BROWSERCHROME); + } + let exclude = rex.exclude.length ? `(?!${rex.exclude.join('$|')}$)` : ''; + let def = ['', '']; + let author = (header.match(/\/\/ @author\s+(.+)\s*$/im) || def)[1]; + let filename = aFile.leafName || ''; + + return this.scripts[filename] = { + filename: filename, + name: (header.match(/\/\/ @name\s+(.+)\s*$/im) || def)[1], + charset: (header.match(/\/\/ @charset\s+(.+)\s*$/im) || def)[1], + description: (header.match(/\/\/ @description\s+(.+)\s*$/im) || def)[1], + version: (header.match(/\/\/ @version\s+(.+)\s*$/im) || def)[1], + author: (header.match(/\/\/ @author\s+(.+)\s*$/im) || def)[1], + regex: new RegExp(`^${exclude}(${rex.include.join('|') || '.*'})$`,'i'), + id: (header.match(/\/\/ @id\s+(.+)\s*$/im) || ['', filename.split('.uc.js')[0] + '@' + (author || 'userChromeJS')])[1], + homepageURL: (header.match(/\/\/ @homepageURL\s+(.+)\s*$/im) || def)[1], + downloadURL: (header.match(/\/\/ @downloadURL\s+(.+)\s*$/im) || def)[1], + updateURL: (header.match(/\/\/ @updateURL\s+(.+)\s*$/im) || def)[1], + optionsURL: (header.match(/\/\/ @optionsURL\s+(.+)\s*$/im) || def)[1], + //startup: (header.match(/\/\/ @startup\s+(.+)\s*$/im) || def)[1], + //shutdown: (header.match(/\/\/ @shutdown\s+(.+)\s*$/im) || def)[1], + onlyonce: /\/\/ @onlyonce\b/.test(header), + isRunning: false, + get isEnabled() { + return (yPref.get(_uc.PREF_SCRIPTSDISABLED) || '').split(',').indexOf(this.filename) === -1; + } + } + }, + + everLoaded: [], + + loadScript: function (script, win) { + if (!script.regex.test(win.location.href) || !script.isEnabled) { + return + } + if (script.onlyonce && script.isRunning) { + if (script.startup) { + // eval(script.startup); + } + return + } + + try { + Services.scriptloader.loadSubScript(_uc.createFileURI(script.filename,true), win); + + script.isRunning = true; + if (script.startup) { + // eval(script.startup); + } + if (!script.shutdown) { + this.everLoaded.push(script.id); + } + } catch (ex) { + this.error(script.filename, ex); + } + return + }, + + error: function (aMsg, err) { + let error = Cc['@mozilla.org/scripterror;1'].createInstance(Ci.nsIScriptError); + if (typeof err == 'object') { + error.init(aMsg + '\n' + err.name + ' : ' + err.message, err.fileName || null, null, err.lineNumber, null, 2, err.name); + } else { + error.init(aMsg + '\n' + err + '\n', null, null, null, null, 2, null); + } + Services.console.logMessage(error); + }, + // things to be exported for use by userscripts + utils:{ + + createElement: function(doc,tag,props){ + let el = doc.createXULElement(tag); + for(let prop in props){ + el.setAttribute(prop,props[prop]) + } + return el + }, + + readFile: function (aFile, metaOnly = false) { + let stream = Cc['@mozilla.org/network/file-input-stream;1'].createInstance(Ci.nsIFileInputStream); + let cvstream = Cc['@mozilla.org/intl/converter-input-stream;1'].createInstance(Ci.nsIConverterInputStream); + try{ + stream.init(aFile, 0x01, 0, 0); + cvstream.init(stream, 'UTF-8', 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + }catch(e){ + console.error(e); + return null + } + let content = '', + data = {}; + while (cvstream.readString(4096, data)) { + content += data.value; + if (metaOnly && content.indexOf('// ==/UserScript==') > 0) { + break; + } + } + cvstream.close(); + stream.close(); + return content.replace(/\r\n?/g, '\n'); + }, + + createFileURI: (fileName) => ( _uc.createFileURI(fileName) ), + + getFSEntry: (fileName) => ( _uc.getDirEntry(fileName) ), + + getScriptData: () => { + let scripts = []; + for(let script in _uc.scripts){ + let data = {}; + let o = _uc.scripts[script]; + for(let p in o){ + if(p != "isEnabled"){ + data[p] = o[p]; + } + } + scripts.push(data) + } + return scripts + }, + + getWindows: function (onlyBrowsers = true) { + let windows = Services.wm.getEnumerator(onlyBrowsers ? 'navigator:browser' : null); + let wins = []; + while (windows.hasMoreElements()) { + let win = windows.getNext(); + win._ucUtils && wins.push(win); + } + return wins + }, + toggleScript: function(el){ + let isElement = !!el.tagName; + if(!isElement && typeof el != "string"){ + return + } + let script = _uc.scripts[isElement ? el.getAttribute("filename") : el]; + if(!script){ + console.log("no script to toggle"); + return + } + if (script.isEnabled) { + yPref.set(_uc.PREF_SCRIPTSDISABLED, `${script.filename},${yPref.get(_uc.PREF_SCRIPTSDISABLED)}`); + } else { + yPref.set(_uc.PREF_SCRIPTSDISABLED, yPref.get(_uc.PREF_SCRIPTSDISABLED).replace(new RegExp(`^${script.filename},?|,${script.filename}`), '')); + } + Services.appinfo.invalidateCachesOnRestart(); + }, + updateMenuStatus: function(menu){ + if(!menu){ + return + } + let disabledScripts = yPref.get(_uc.PREF_SCRIPTSDISABLED).split(","); + for(let item of menu.children){ + if (disabledScripts.includes(item.getAttribute("filename"))){ + item.removeAttribute("checked"); + }else{ + item.setAttribute("checked","true"); + } + } + }, + get prefs(){ return yPref }, + + restart: function (clearCache){ + clearCache && Services.appinfo.invalidateCachesOnRestart(); + _uc.utils.getWindows()[0].BrowserUtils.restartApplication(); + } + } +}; + +_uc.BASE_FILEURI = Services.io.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromDir(_uc.chromeDir); +Object.freeze(_uc.utils); + +if (yPref.get(_uc.PREF_ENABLED) === undefined) { + yPref.set(_uc.PREF_ENABLED, true); +} + +if (yPref.get(_uc.PREF_SCRIPTSDISABLED) === undefined) { + yPref.set(_uc.PREF_SCRIPTSDISABLED, ''); +} + +function UserChrome_js() { + _uc.getScripts(); + Services.obs.addObserver(this, 'domwindowopened', false); +} + +UserChrome_js.prototype = { + observe: function (aSubject, aTopic, aData) { + aSubject.addEventListener('DOMContentLoaded', this, true); + }, + + handleEvent: function (aEvent) { + let document = aEvent.originalTarget; + let window = document.defaultView; + if (/^chrome:(?!\/\/global\/content\/(commonDialog|alerts\/alert)\.xul)|about:(?!blank)/i.test(window.location.href)) { + window._ucUtils = _uc.utils; + document.allowUnsafeHTML = false; // https://bugzilla.mozilla.org/show_bug.cgi?id=1432966 + if (window._gBrowser){ // bug 1443849 + window.gBrowser = window._gBrowser; + } + let isWindow = window.isChromeWindow; + const ENABLED = yPref.get(_uc.PREF_ENABLED); + /* Add a way to toggle scripts in tools menu */ + let menu, popup, item; + let ce = _uc.utils.createElement; + if(isWindow){ + menu = document.querySelector("#menu_openDownloads"); + if(menu){ + try{ + popup = ce(document,"menupopup",{id:"menuUserScriptsPopup",onpopupshown:`_ucUtils.updateMenuStatus(this)`}); + item = ce(document,"menu",{id:"userScriptsMenu",label:"userScripts"}); + }catch(e){ + isWindow = false; + } + }else{ + isWindow = false; + } + } + if(ENABLED){ + Object.values(_uc.scripts).forEach(script => { + _uc.loadScript(script, window); + if(isWindow){ + popup.appendChild(ce(document,"menuitem",{type:"checkbox",label:script.name||script.filename,filename:script.filename,checked:"true",oncommand:`_ucUtils.toggleScript(this)`})) + } + }); + } + if(isWindow){ + popup.appendChild(ce(document,"menuseparator",{})); + popup.appendChild(ce(document,"menuitem",{label:"Restart now!",oncommand:"_ucUtils.restart(true)",tooltiptext:"Toggling scripts requires a restart"})); + item.appendChild(popup); + menu.parentNode.insertBefore(item,menu); + } + } + } +}; + +!Services.appinfo.inSafeMode && new UserChrome_js(); + +try{ + yPref.set("toolkit.legacyUserProfileCustomizations.stylesheets",true); +}catch(e){} \ No newline at end of file diff --git a/program/config.js b/program/config.js new file mode 100644 index 0000000..afb2c79 --- /dev/null +++ b/program/config.js @@ -0,0 +1,6 @@ +// skip 1st line +try { + let Cu = Components.utils; + Cu.import('resource://gre/modules/osfile.jsm'); + Cu.import(OS.Path.toFileURI(OS.Constants.Path.profileDir)+'/chrome/utils/boot.jsm'); +} catch(ex) {}; diff --git a/program/defaults/pref/config-prefs.js b/program/defaults/pref/config-prefs.js new file mode 100644 index 0000000..39ee55c --- /dev/null +++ b/program/defaults/pref/config-prefs.js @@ -0,0 +1,2 @@ +pref("general.config.obscure_value", 0); +pref("general.config.filename", "config.js"); \ No newline at end of file From 461d1237d6f1d84d73e3e02ae833237ccb91f888 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 16 Nov 2019 22:56:32 +0200 Subject: [PATCH 02/24] Add chromeDir to _ucUtils --- profile/chrome/utils/boot.jsm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 446b912..bd1edff 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -213,6 +213,8 @@ let _uc = { createFileURI: (fileName) => ( _uc.createFileURI(fileName) ), + get chromeDir(){ return {get files(){return _uc.chromeDirEntries},uri:_uc.BASE_FILEURI} }, + getFSEntry: (fileName) => ( _uc.getDirEntry(fileName) ), getScriptData: () => { From 2d91485b4be52768e724541a19a66c37a16b2c9c Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Tue, 10 Dec 2019 19:24:45 +0200 Subject: [PATCH 03/24] Register script folder to chrome:// and load scripts through it --- profile/chrome/utils/chrome.manifest | 3 +++ program/config.js | 20 +++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 profile/chrome/utils/chrome.manifest diff --git a/profile/chrome/utils/chrome.manifest b/profile/chrome/utils/chrome.manifest new file mode 100644 index 0000000..f6711d6 --- /dev/null +++ b/profile/chrome/utils/chrome.manifest @@ -0,0 +1,3 @@ +content userchromejs ./ +content userScripts ../JS/ +content userChrome ../resources/ diff --git a/program/config.js b/program/config.js index afb2c79..d920b0b 100644 --- a/program/config.js +++ b/program/config.js @@ -1,6 +1,20 @@ // skip 1st line try { - let Cu = Components.utils; - Cu.import('resource://gre/modules/osfile.jsm'); - Cu.import(OS.Path.toFileURI(OS.Constants.Path.profileDir)+'/chrome/utils/boot.jsm'); + + let { + classes: Cc, + interfaces: Ci, + manager: Cm, + utils: Cu + } = Components; + + let cmanifest = Cc['@mozilla.org/file/directory_service;1'].getService(Ci.nsIProperties).get('UChrm', Ci.nsIFile); + cmanifest.append('utils'); + cmanifest.append('chrome.manifest'); + + if(cmanifest.exists()){ + Cm.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(cmanifest); + Cu.import('chrome://userchromejs/content/boot.jsm'); + } + } catch(ex) {}; From 1ef19fe8df7ff0d3412b62f73b91d4fd0c138c91 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Tue, 10 Dec 2019 19:26:22 +0200 Subject: [PATCH 04/24] Load scripts through chrome:// and add some new apis --- profile/chrome/utils/boot.jsm | 152 ++++++++++++++++++++-------------- 1 file changed, 90 insertions(+), 62 deletions(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index bd1edff..afa2638 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -36,51 +36,62 @@ const yPref = { }, addListener:(a,b)=>{ let o = (q,w,e)=>(b(yPref.get(e),e)); Services.prefs.addObserver(a,o);return{pref:a,observer:o}}, removeListener:(a)=>( Services.prefs.removeObserver(a.pref,a.observer) ) +}; + +const CUSTOM_EXT = {}; +const SHARED_GLOBAL = {}; +const RUNTIME = { + startup:[] +}; + +function resolveChromeURL(str){ + const registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry); + try{ + return registry.convertChromeURL(Services.io.newURI(str.replace(/\\/g,"/"))).spec + }catch(e){ + console.error(e); + return "" + } +} +// relative to "chrome" folder +function resolveChromePath(str){ + let parts = resolveChromeURL(str).split("/"); + return parts.slice(parts.indexOf("chrome") + 1,parts.length - 1).join("/"); } let _uc = { BROWSERCHROME: 'chrome://browser/content/browser.xhtml', PREF_ENABLED: 'userChromeJS.enabled', PREF_SCRIPTSDISABLED: 'userChromeJS.scriptsDisabled', - SCRIPT_DIR: 'JS', - RESOURCE_DIR: 'resources', - BASE_FILEURI: null, // set later + SCRIPT_DIR: resolveChromePath('chrome://userScripts/content/'), + RESOURCE_DIR: resolveChromePath('chrome://userChrome/content/'), + BASE_FILEURI: Services.io.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromDir(Services.dirsvc.get('UChrm',Ci.nsIFile)), + get chromeDir() {return Services.dirsvc.get('UChrm',Ci.nsIFile)}, - - get chromeDirEntries() {return _uc.chromeDir.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator)}, - - getDirectoryEntries: function(nsDirEnum,name,matchDirectory = false){ - while(nsDirEnum.hasMoreElements()){ - let entry = nsDirEnum.getNext().QueryInterface(Ci.nsIFile); - if(name === entry.leafName){ - if(matchDirectory){ - if(entry.isDirectory()){ - return entry.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator); - } - }else{ - if(entry.isFile()){ - return entry - } - } - } - } - return null - }, getDirEntry: function(filename,isLoader = false){ filename = filename.replace("\\","/"); let pathParts = ((isLoader ? _uc.SCRIPT_DIR : _uc.RESOURCE_DIR) + "/" + filename).split("/").filter((a)=>(!!a)); - let leafName = pathParts.pop(); - let isFile = leafName.indexOf(".") != -1; - let currentDir = _uc.chromeDirEntries; + let entry = _uc.chromeDir; + for(let part of pathParts){ - currentDir = _uc.getDirectoryEntries(currentDir,part,true) + entry.append(part) + } + if(!entry.exists()){ + return null + } + if(entry.isDirectory()){ + return entry.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator); + }else if(entry.isFile()){ + return entry + }else{ + return null } - return currentDir ? _uc.getDirectoryEntries(currentDir,leafName,!isFile) : null }, - - createFileURI: (fileName,isLoader = false) => (`${_uc.BASE_FILEURI}${isLoader?_uc.SCRIPT_DIR:_uc.RESOURCE_DIR}/${fileName.replace("\\","/")}`), + + createChromeURI: (fileName) => ( + `chrome://userScripts/content/${fileName}`), getScripts: function () { this.scripts = {}; @@ -88,7 +99,6 @@ let _uc = { console.log("Scripts are disabled or the given script directory name is invalid"); return } - let files = _uc.getDirEntry('',true); while(files.hasMoreElements()){ let file = files.getNext().QueryInterface(Ci.nsIFile); @@ -129,7 +139,7 @@ let _uc = { downloadURL: (header.match(/\/\/ @downloadURL\s+(.+)\s*$/im) || def)[1], updateURL: (header.match(/\/\/ @updateURL\s+(.+)\s*$/im) || def)[1], optionsURL: (header.match(/\/\/ @optionsURL\s+(.+)\s*$/im) || def)[1], - //startup: (header.match(/\/\/ @startup\s+(.+)\s*$/im) || def)[1], + startup: (header.match(/\/\/ @startup\s+(.+)\s*$/im) || def)[1], //shutdown: (header.match(/\/\/ @shutdown\s+(.+)\s*$/im) || def)[1], onlyonce: /\/\/ @onlyonce\b/.test(header), isRunning: false, @@ -139,29 +149,36 @@ let _uc = { } }, - everLoaded: [], + //everLoaded: [], + + maybeRunStartUp: (script,win) => { + if( script.startup + && (/^\w*$/).test(script.startup) + && SHARED_GLOBAL[script.startup] + && typeof SHARED_GLOBAL[script.startup]._startup === "function") + { + SHARED_GLOBAL[script.startup]._startup(win) + } + }, loadScript: function (script, win) { if (!script.regex.test(win.location.href) || !script.isEnabled) { return } if (script.onlyonce && script.isRunning) { - if (script.startup) { - // eval(script.startup); - } + _uc.maybeRunStartUp(script,win) return } try { - Services.scriptloader.loadSubScript(_uc.createFileURI(script.filename,true), win); + Services.scriptloader.loadSubScript(_uc.createChromeURI(script.filename), win); script.isRunning = true; - if (script.startup) { - // eval(script.startup); - } - if (!script.shutdown) { + _uc.maybeRunStartUp(script,win); + + /*if (!script.shutdown) { this.everLoaded.push(script.id); - } + }*/ } catch (ex) { this.error(script.filename, ex); } @@ -180,8 +197,10 @@ let _uc = { // things to be exported for use by userscripts utils:{ - createElement: function(doc,tag,props){ - let el = doc.createXULElement(tag); + get sharedGlobal(){ return SHARED_GLOBAL }, + + createElement: function(doc,tag,props,isHTML = false){ + let el = isHTML ? doc.createElement(tag) : doc.createXULElement(tag); for(let prop in props){ el.setAttribute(prop,props[prop]) } @@ -211,9 +230,13 @@ let _uc = { return content.replace(/\r\n?/g, '\n'); }, - createFileURI: (fileName) => ( _uc.createFileURI(fileName) ), + createFileURI: (fileName = "") => { + fileName = String(fileName); + let u = resolveChromeURL(`chrome://userChrome/content/${fileName}`); + return fileName ? u : u.substr(0,u.lastIndexOf("/") + 1); + }, - get chromeDir(){ return {get files(){return _uc.chromeDirEntries},uri:_uc.BASE_FILEURI} }, + get chromeDir(){ return {get files(){return _uc.chromeDir.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator)},uri:_uc.BASE_FILEURI} }, getFSEntry: (fileName) => ( _uc.getDirEntry(fileName) ), @@ -232,15 +255,24 @@ let _uc = { return scripts }, - getWindows: function (onlyBrowsers = true) { - let windows = Services.wm.getEnumerator(onlyBrowsers ? 'navigator:browser' : null); - let wins = []; - while (windows.hasMoreElements()) { - let win = windows.getNext(); - win._ucUtils && wins.push(win); + get windows(){ + return { + get: function (onlyBrowsers = true) { + let windows = Services.wm.getEnumerator(onlyBrowsers ? 'navigator:browser' : null); + let wins = []; + while (windows.hasMoreElements()) { + let win = windows.getNext(); + win._ucUtils && wins.push(win); + } + return wins + }, + forEach: function(fun,onlyBrowsers = true){ + let wins = this.get(onlyBrowsers); + wins.every((w)=>(fun(w.document,w))) + } } - return wins }, + toggleScript: function(el){ let isElement = !!el.tagName; if(!isElement && typeof el != "string"){ @@ -258,6 +290,7 @@ let _uc = { } Services.appinfo.invalidateCachesOnRestart(); }, + updateMenuStatus: function(menu){ if(!menu){ return @@ -275,12 +308,11 @@ let _uc = { restart: function (clearCache){ clearCache && Services.appinfo.invalidateCachesOnRestart(); - _uc.utils.getWindows()[0].BrowserUtils.restartApplication(); + _uc.utils.windows.get()[0].BrowserUtils.restartApplication(); } } }; -_uc.BASE_FILEURI = Services.io.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromDir(_uc.chromeDir); Object.freeze(_uc.utils); if (yPref.get(_uc.PREF_ENABLED) === undefined) { @@ -311,7 +343,7 @@ UserChrome_js.prototype = { window.gBrowser = window._gBrowser; } let isWindow = window.isChromeWindow; - const ENABLED = yPref.get(_uc.PREF_ENABLED); + /* Add a way to toggle scripts in tools menu */ let menu, popup, item; let ce = _uc.utils.createElement; @@ -320,7 +352,7 @@ UserChrome_js.prototype = { if(menu){ try{ popup = ce(document,"menupopup",{id:"menuUserScriptsPopup",onpopupshown:`_ucUtils.updateMenuStatus(this)`}); - item = ce(document,"menu",{id:"userScriptsMenu",label:"userScripts"}); + item = ce(document,"menu",{id:"userScriptsMenu",label:"userScripts"}); }catch(e){ isWindow = false; } @@ -328,7 +360,7 @@ UserChrome_js.prototype = { isWindow = false; } } - if(ENABLED){ + if(yPref.get(_uc.PREF_ENABLED)){ Object.values(_uc.scripts).forEach(script => { _uc.loadScript(script, window); if(isWindow){ @@ -347,7 +379,3 @@ UserChrome_js.prototype = { }; !Services.appinfo.inSafeMode && new UserChrome_js(); - -try{ - yPref.set("toolkit.legacyUserProfileCustomizations.stylesheets",true); -}catch(e){} \ No newline at end of file From 7b6fb7f4ccb27d913d43b5d51df1f29020f2ba30 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Thu, 12 Dec 2019 18:53:36 +0200 Subject: [PATCH 05/24] Clean up some stuff --- profile/chrome/utils/boot.jsm | 15 ++++----------- profile/chrome/utils/chrome.manifest | 4 ++-- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index afa2638..85506a1 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -38,11 +38,7 @@ const yPref = { removeListener:(a)=>( Services.prefs.removeObserver(a.pref,a.observer) ) }; -const CUSTOM_EXT = {}; const SHARED_GLOBAL = {}; -const RUNTIME = { - startup:[] -}; function resolveChromeURL(str){ const registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry); @@ -64,8 +60,8 @@ let _uc = { PREF_ENABLED: 'userChromeJS.enabled', PREF_SCRIPTSDISABLED: 'userChromeJS.scriptsDisabled', - SCRIPT_DIR: resolveChromePath('chrome://userScripts/content/'), - RESOURCE_DIR: resolveChromePath('chrome://userChrome/content/'), + SCRIPT_DIR: resolveChromePath('chrome://userscripts/content/'), + RESOURCE_DIR: resolveChromePath('chrome://userchrome/content/'), BASE_FILEURI: Services.io.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromDir(Services.dirsvc.get('UChrm',Ci.nsIFile)), get chromeDir() {return Services.dirsvc.get('UChrm',Ci.nsIFile)}, @@ -89,9 +85,6 @@ let _uc = { return null } }, - - createChromeURI: (fileName) => ( - `chrome://userScripts/content/${fileName}`), getScripts: function () { this.scripts = {}; @@ -171,7 +164,7 @@ let _uc = { } try { - Services.scriptloader.loadSubScript(_uc.createChromeURI(script.filename), win); + Services.scriptloader.loadSubScript(`chrome://userscripts/content/${fileName}`, win); script.isRunning = true; _uc.maybeRunStartUp(script,win); @@ -232,7 +225,7 @@ let _uc = { createFileURI: (fileName = "") => { fileName = String(fileName); - let u = resolveChromeURL(`chrome://userChrome/content/${fileName}`); + let u = resolveChromeURL(`chrome://userchrome/content/${fileName}`); return fileName ? u : u.substr(0,u.lastIndexOf("/") + 1); }, diff --git a/profile/chrome/utils/chrome.manifest b/profile/chrome/utils/chrome.manifest index f6711d6..8af58b0 100644 --- a/profile/chrome/utils/chrome.manifest +++ b/profile/chrome/utils/chrome.manifest @@ -1,3 +1,3 @@ content userchromejs ./ -content userScripts ../JS/ -content userChrome ../resources/ +content userscripts ../JS/ +content userchrome ../resources/ From 853b27d39c46eca7151a3bdd472321593a80a7ef Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Thu, 12 Dec 2019 19:42:35 +0200 Subject: [PATCH 06/24] Fix mistake in _ucUtils.windows.forEach --- profile/chrome/utils/boot.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 85506a1..29925f7 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -261,7 +261,7 @@ let _uc = { }, forEach: function(fun,onlyBrowsers = true){ let wins = this.get(onlyBrowsers); - wins.every((w)=>(fun(w.document,w))) + wins.forEach((w)=>(fun(w.document,w))) } } }, From 76f9ae683656bc93a566d690dc473821f8cec315 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Fri, 13 Dec 2019 07:44:14 +0200 Subject: [PATCH 07/24] Fix a mistake in script-to-be-loaded filename --- profile/chrome/utils/boot.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 29925f7..5fb4691 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -164,7 +164,7 @@ let _uc = { } try { - Services.scriptloader.loadSubScript(`chrome://userscripts/content/${fileName}`, win); + Services.scriptloader.loadSubScript(`chrome://userscripts/content/${script.filename}`, win); script.isRunning = true; _uc.maybeRunStartUp(script,win); From 9bd411aa33e77e386af6f3660823ab028ed35ed6 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 25 Jan 2020 18:27:52 +0200 Subject: [PATCH 08/24] Don't check the existence of _ucUtils when returning windows --- profile/chrome/utils/boot.jsm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 5fb4691..b6e42d7 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -254,8 +254,7 @@ let _uc = { let windows = Services.wm.getEnumerator(onlyBrowsers ? 'navigator:browser' : null); let wins = []; while (windows.hasMoreElements()) { - let win = windows.getNext(); - win._ucUtils && wins.push(win); + wins.push(windows.getNext()); } return wins }, From eb318ef7a4de10cf8e2701038580393cdf25453b Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 1 Feb 2020 15:05:59 +0200 Subject: [PATCH 09/24] Move calling of script _startup inside try-catch block --- profile/chrome/utils/boot.jsm | 10 +++++----- program/config.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index b6e42d7..9227a72 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -158,12 +158,12 @@ let _uc = { if (!script.regex.test(win.location.href) || !script.isEnabled) { return } - if (script.onlyonce && script.isRunning) { - _uc.maybeRunStartUp(script,win) - return - } - try { + if (script.onlyonce && script.isRunning) { + _uc.maybeRunStartUp(script,win) + return + } + Services.scriptloader.loadSubScript(`chrome://userscripts/content/${script.filename}`, win); script.isRunning = true; diff --git a/program/config.js b/program/config.js index d920b0b..1e9a54a 100644 --- a/program/config.js +++ b/program/config.js @@ -17,4 +17,4 @@ try { Cu.import('chrome://userchromejs/content/boot.jsm'); } -} catch(ex) {}; +} catch(ex) {}; \ No newline at end of file From 81c3ebc7b00b7a932b5e4ab0bf46ad1c51004938 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 1 Feb 2020 15:09:27 +0200 Subject: [PATCH 10/24] use console.error() instead of custom error logger --- profile/chrome/utils/boot.jsm | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 9227a72..3ea1ee7 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -173,20 +173,11 @@ let _uc = { this.everLoaded.push(script.id); }*/ } catch (ex) { - this.error(script.filename, ex); + console.error(ex); } return }, - error: function (aMsg, err) { - let error = Cc['@mozilla.org/scripterror;1'].createInstance(Ci.nsIScriptError); - if (typeof err == 'object') { - error.init(aMsg + '\n' + err.name + ' : ' + err.message, err.fileName || null, null, err.lineNumber, null, 2, err.name); - } else { - error.init(aMsg + '\n' + err + '\n', null, null, null, null, 2, null); - } - Services.console.logMessage(error); - }, // things to be exported for use by userscripts utils:{ From 3746f3eb6d1662386321be37358745c514934d72 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Thu, 16 Apr 2020 20:23:49 +0300 Subject: [PATCH 11/24] Add startupFinished api --- profile/chrome/utils/boot.jsm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 3ea1ee7..ade09d8 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -287,6 +287,17 @@ let _uc = { } } }, + + startupFinished: function(){ + return new Promise(resolve => { + let observer = (subject, topic, data) => { + Services.obs.removeObserver(observer, "browser-delayed-startup-finished"); + resolve({ subject, data }); + }; + Services.obs.addObserver(observer, "browser-delayed-startup-finished"); + }); + }, + get prefs(){ return yPref }, restart: function (clearCache){ From 78be1f00bd9aa9a9a037a60bbd1f32f3bc6a9711 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Thu, 16 Apr 2020 22:57:54 +0300 Subject: [PATCH 12/24] Add registerHotkey api --- profile/chrome/utils/boot.jsm | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index ade09d8..4732aec 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -298,6 +298,40 @@ let _uc = { }); }, + registerHotkey: function(desc,func){ + const validMods = ["accel","alt","ctrl","meta","shift"]; + const NOK = (a) => (typeof a != "string"); + const eToO = (e) => ({"metaKey":e.metaKey,"ctrlKey":e.ctrlKey,"altKey":e.altKey,"shiftKey":e.shiftKey,"key":e.srcElement.getAttribute("key"),"id":e.srcElement.getAttribute("id")}); + + if(NOK(desc.id) || NOK(desc.key) || NOK(desc.modifiers)){ + return false + } + + try{ + let mods = desc.modifiers.toLowerCase().split(" ").filter((a)=>(validMods.includes(a))); + if(mods.length === 0){ + return false + } + _uc.utils.windows.forEach((doc,win) => { + let e = _uc.utils.createElement(doc,"key", + { + "id":desc.id, + "modifiers":mods.join(" ").replace("ctrl","accel"), + "key":desc.key[0].toUpperCase(), + "oncommand":"//" + } + ); + e.addEventListener("command",(e) => {func(e.target.ownerGlobal,eToO(e))}); + let keyset = doc.getElementById("ucKeys") || doc.body.appendChild(_uc.utils.createElement(doc,"keyset",{id:"ucKeys"})); + keyset.appendChild(e); + }); + }catch(e){ + console.error(e); + return false + } + return true + }, + get prefs(){ return yPref }, restart: function (clearCache){ From 554d5a1ab1f1c6b4865ac294fd70c0de7f65835a Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 18 Apr 2020 12:26:51 +0300 Subject: [PATCH 13/24] Support F1-F12 as hotkeys --- profile/chrome/utils/boot.jsm | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 4732aec..b5719c2 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -299,7 +299,8 @@ let _uc = { }, registerHotkey: function(desc,func){ - const validMods = ["accel","alt","ctrl","meta","shift"]; + const validMods = ["accel","alt","ctrl","meta","shift"]; + const validKey = (k)=>((/^[\w-]$/).test(k) ? 1 : (/^F(?:1[0,2]|[1-9])$/).test(k) ? 2 : 0); const NOK = (a) => (typeof a != "string"); const eToO = (e) => ({"metaKey":e.metaKey,"ctrlKey":e.ctrlKey,"altKey":e.altKey,"shiftKey":e.shiftKey,"key":e.srcElement.getAttribute("key"),"id":e.srcElement.getAttribute("id")}); @@ -309,21 +310,25 @@ let _uc = { try{ let mods = desc.modifiers.toLowerCase().split(" ").filter((a)=>(validMods.includes(a))); - if(mods.length === 0){ + let key = validKey(desc.key); + if(!key || (mods.length === 0 && key === 1)){ return false } + _uc.utils.windows.forEach((doc,win) => { - let e = _uc.utils.createElement(doc,"key", - { - "id":desc.id, - "modifiers":mods.join(" ").replace("ctrl","accel"), - "key":desc.key[0].toUpperCase(), - "oncommand":"//" - } - ); - e.addEventListener("command",(e) => {func(e.target.ownerGlobal,eToO(e))}); + + let details = { "id": desc.id, "modifiers": mods.join(",").replace("ctrl","accel"), "oncommand": "//" }; + if(key === 1){ + details.key = desc.key.toUpperCase(); + }else{ + details.keycode = `VK_${desc.key}`; + } + + let el = _uc.utils.createElement(doc,"key",details); + + el.addEventListener("command",(ev) => {func(ev.target.ownerGlobal,eToO(ev))}); let keyset = doc.getElementById("ucKeys") || doc.body.appendChild(_uc.utils.createElement(doc,"keyset",{id:"ucKeys"})); - keyset.appendChild(e); + keyset.appendChild(el); }); }catch(e){ console.error(e); From 2e247768f6e716ee52252b500fa044a79d5bc275 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 25 Apr 2020 08:38:09 +0300 Subject: [PATCH 14/24] Disable general.config.sandbox_enabled --- program/defaults/pref/config-prefs.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/program/defaults/pref/config-prefs.js b/program/defaults/pref/config-prefs.js index 39ee55c..5e34cba 100644 --- a/program/defaults/pref/config-prefs.js +++ b/program/defaults/pref/config-prefs.js @@ -1,2 +1,4 @@ pref("general.config.obscure_value", 0); -pref("general.config.filename", "config.js"); \ No newline at end of file +pref("general.config.filename", "config.js"); +// Sandbox needs to be disabled in release and Beta versions +pref("general.config.sandbox_enabled", false); \ No newline at end of file From 877f1ee56b22fda4a63abc55169c8e0c8e3fb653 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Tue, 26 May 2020 16:17:24 +0300 Subject: [PATCH 15/24] Let registerHotkey() override built-in hotkeys --- profile/chrome/utils/boot.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index b5719c2..5572d24 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -327,7 +327,7 @@ let _uc = { let el = _uc.utils.createElement(doc,"key",details); el.addEventListener("command",(ev) => {func(ev.target.ownerGlobal,eToO(ev))}); - let keyset = doc.getElementById("ucKeys") || doc.body.appendChild(_uc.utils.createElement(doc,"keyset",{id:"ucKeys"})); + let keyset = doc.getElementById("ucKeys") || doc.body.insertBefore(_uc.utils.createElement(doc,"keyset",{id:"ucKeys"}),doc.body.querySelector("keyset")); keyset.appendChild(el); }); }catch(e){ From b9e858ee558a7662e0b6e449a613ffa0b58454cc Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Tue, 26 May 2020 16:33:14 +0300 Subject: [PATCH 16/24] move hotkeys inside mainKeySet and don't create duplicate hotkeys --- profile/chrome/utils/boot.jsm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 5572d24..42d89c0 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -316,7 +316,9 @@ let _uc = { } _uc.utils.windows.forEach((doc,win) => { - + if(doc.getElementById(desc.id)){ + return + } let details = { "id": desc.id, "modifiers": mods.join(",").replace("ctrl","accel"), "oncommand": "//" }; if(key === 1){ details.key = desc.key.toUpperCase(); @@ -327,8 +329,8 @@ let _uc = { let el = _uc.utils.createElement(doc,"key",details); el.addEventListener("command",(ev) => {func(ev.target.ownerGlobal,eToO(ev))}); - let keyset = doc.getElementById("ucKeys") || doc.body.insertBefore(_uc.utils.createElement(doc,"keyset",{id:"ucKeys"}),doc.body.querySelector("keyset")); - keyset.appendChild(el); + let keyset = doc.getElementById("mainKeyset") || doc.body.appendChild(_uc.utils.createElement(doc,"keyset",{id:"ucKeys"})); + keyset.insertBefore(el,keyset.firstChild); }); }catch(e){ console.error(e); From 2a34305a287d3a29b1dab630e68c02fd3d34dd41 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Fri, 29 May 2020 08:49:26 +0300 Subject: [PATCH 17/24] Split windowIsReady from startUpFinished --- profile/chrome/utils/boot.jsm | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 42d89c0..9792fa9 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -64,6 +64,8 @@ let _uc = { RESOURCE_DIR: resolveChromePath('chrome://userchrome/content/'), BASE_FILEURI: Services.io.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromDir(Services.dirsvc.get('UChrm',Ci.nsIFile)), + SESSION_RESTORED: false, + get chromeDir() {return Services.dirsvc.get('UChrm',Ci.nsIFile)}, getDirEntry: function(filename,isLoader = false){ @@ -290,14 +292,36 @@ let _uc = { startupFinished: function(){ return new Promise(resolve => { + if(_uc.SESSION_RESTORED){ + resolve(); + } let observer = (subject, topic, data) => { - Services.obs.removeObserver(observer, "browser-delayed-startup-finished"); - resolve({ subject, data }); + Services.obs.removeObserver(observer, "sessionstore-windows-restored"); + resolve(); }; - Services.obs.addObserver(observer, "browser-delayed-startup-finished"); + Services.obs.addObserver(observer, "sessionstore-windows-restored"); }); }, + windowIsReady: function(win){ + if(win && win.isChromeWindow){ + return new Promise(resolve => { + if(win.gBrowserInit.delayedStartupFinished){ + resolve() + } + let observer = (subject, topic, data) => { + if(subject === win){ + Services.obs.removeObserver(observer, "browser-delayed-startup-finished"); + resolve(); + } + }; + Services.obs.addObserver(observer, "browser-delayed-startup-finished"); + }); + }else{ + return Promise.reject(new Error("reference is not a window")) + } + }, + registerHotkey: function(desc,func){ const validMods = ["accel","alt","ctrl","meta","shift"]; const validKey = (k)=>((/^[\w-]$/).test(k) ? 1 : (/^F(?:1[0,2]|[1-9])$/).test(k) ? 2 : 0); @@ -349,6 +373,8 @@ let _uc = { }; Object.freeze(_uc.utils); +_uc.utils.startupFinished() +.then(()=>{_uc.SESSION_RESTORED = true}); if (yPref.get(_uc.PREF_ENABLED) === undefined) { yPref.set(_uc.PREF_ENABLED, true); From a59e262a2362d8f6c829d2711854a850354eab30 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Tue, 16 Jun 2020 18:35:16 +0300 Subject: [PATCH 18/24] Exclude modal prompts and notifications from scripts --- profile/chrome/utils/boot.jsm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 9792fa9..7e33d1e 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -397,10 +397,12 @@ UserChrome_js.prototype = { handleEvent: function (aEvent) { let document = aEvent.originalTarget; let window = document.defaultView; - if (/^chrome:(?!\/\/global\/content\/(commonDialog|alerts\/alert)\.xul)|about:(?!blank)/i.test(window.location.href)) { + let regex = /^chrome:(?!\/\/global\/content\/(commonDialog|alerts\/alert)\.xhtml)|about:(?!blank)/i; + // Don't inject scripts to modal prompt windows or notifications + if(regex.test(window.location.href)) { window._ucUtils = _uc.utils; document.allowUnsafeHTML = false; // https://bugzilla.mozilla.org/show_bug.cgi?id=1432966 - if (window._gBrowser){ // bug 1443849 + if(window._gBrowser){ // bug 1443849 window.gBrowser = window._gBrowser; } let isWindow = window.isChromeWindow; From 71c41a63959bd353a74d4cd7b9a1ae1f050aef79 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Mon, 21 Sep 2020 15:28:35 +0300 Subject: [PATCH 19/24] Add new loadURI() method to ucUtils --- profile/chrome/utils/boot.jsm | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 7e33d1e..9b10fe3 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -362,7 +362,31 @@ let _uc = { } return true }, - + loadURI: function(win,desc){ + if( !win + || !desc + || !desc.url + || typeof desc.url !== "string" + || !(["tab","tabshifted","window","current"]).includes(desc.where) + ){ + return false + } + const isJsURI = desc.url.slice(0,11) === "javascript:"; + try{ + win.openTrustedLinkIn( + desc.url, + desc.where, + { "allowPopups":isJsURI, + "inBackground":desc.where==="tabshifted", // This doesn't work for some reason + "allowInheritPrincipal":false, + "private":!!desc.private, + "userContextId":desc.url.startsWith("http")?desc.userContextId:null}); + }catch(e){ + console.error(e); + return false + } + return true + }, get prefs(){ return yPref }, restart: function (clearCache){ From 6fe6d7029dfca09aef42174da08720c3eb5c0b24 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Wed, 23 Sep 2020 17:32:18 +0300 Subject: [PATCH 20/24] Add createWidget() helper to utils --- profile/chrome/utils/boot.jsm | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 9b10fe3..ef64558 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -193,6 +193,57 @@ let _uc = { return el }, + createWidget(desc){ + if(!desc || !desc.id ){ + console.error("custom widget description is missing 'id' property"); + return null + } + if(!(['toolbaritem','toolbarbutton']).includes(desc.type)){ + console.error("custom widget has unsupported type: "+desc.type); + return null + } + const CUI = Services.wm.getMostRecentBrowserWindow().CustomizableUI; + let newWidget = CUI.getWidget(desc.id); + + if(newWidget && newWidget.hasOwnProperty("source")){ + // very likely means that the widget with this id already exists + // There isn't a very reliable way to 'really' check if it exists or not + return newWidget + } + // This is pretty ugly but makes onBuild much cleaner. + let itemStyle = ""; + if(desc.image){ + if(desc.type==="toolbarbutton"){ + itemStyle += "list-style-image:"; + }else{ + itemStyle += "background: transparent center no-repeat "; + } + itemStyle += `url(chrome://userChrome/content/${desc.image});`; + itemStyle += desc.style || ""; + } + SHARED_GLOBAL.widgetCallbacks.set(desc.id,desc.callback); + + return CUI.createWidget({ + id: desc.id, + type: 'custom', + onBuild: function(aDocument) { + let toolbaritem = aDocument.createXULElement(desc.type); + let props = { + id: desc.id, + class: `toolbarbutton-1 chromeclass-toolbar-additional ${desc.class?desc.class:""}`, + label: desc.label || desc.id, + tooltiptext: desc.tooltip || desc.id, + style: itemStyle, + onclick: `${desc.allEvents?"":"event.button===0 && "}_ucUtils.sharedGlobal.widgetCallbacks.get(this.id)(event,window)` + }; + for (let p in props){ + toolbaritem.setAttribute(p, props[p]); + } + return toolbaritem; + } + }); + }, + readFile: function (aFile, metaOnly = false) { let stream = Cc['@mozilla.org/network/file-input-stream;1'].createInstance(Ci.nsIFileInputStream); let cvstream = Cc['@mozilla.org/intl/converter-input-stream;1'].createInstance(Ci.nsIConverterInputStream); From 28c285d982fc2ea58851ce2bf735431a812cbaba Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 10 Oct 2020 08:34:24 +0300 Subject: [PATCH 21/24] support loading scripts as background module --- profile/chrome/utils/boot.jsm | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index ef64558..9eaa27e 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -98,7 +98,14 @@ let _uc = { while(files.hasMoreElements()){ let file = files.getNext().QueryInterface(Ci.nsIFile); if (/\.uc\.js$/i.test(file.leafName)) { - _uc.getScriptData(file); + let script = _uc.getScriptData(file); + if(script.inbackground){ + try{ + Cu.import(`chrome://userscripts/content/${script.filename}`) + }catch(e){ + console.error(e); + } + } } } }, @@ -137,6 +144,7 @@ let _uc = { startup: (header.match(/\/\/ @startup\s+(.+)\s*$/im) || def)[1], //shutdown: (header.match(/\/\/ @shutdown\s+(.+)\s*$/im) || def)[1], onlyonce: /\/\/ @onlyonce\b/.test(header), + inbackground: /\/\/ @backgroundmodule\b/.test(header), isRunning: false, get isEnabled() { return (yPref.get(_uc.PREF_SCRIPTSDISABLED) || '').split(',').indexOf(this.filename) === -1; @@ -157,7 +165,7 @@ let _uc = { }, loadScript: function (script, win) { - if (!script.regex.test(win.location.href) || !script.isEnabled) { + if (script.inbackground || !script.regex.test(win.location.href) || !script.isEnabled) { return } try { From 6a71a1906435ebd47d7c3fcd643ad7b1aca2cbed Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 10 Oct 2020 08:56:36 +0300 Subject: [PATCH 22/24] check if a script is enabled before executing it as module --- profile/chrome/utils/boot.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index 9eaa27e..fe528a3 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -99,7 +99,7 @@ let _uc = { let file = files.getNext().QueryInterface(Ci.nsIFile); if (/\.uc\.js$/i.test(file.leafName)) { let script = _uc.getScriptData(file); - if(script.inbackground){ + if(script.inbackground && script.isEnabled){ try{ Cu.import(`chrome://userscripts/content/${script.filename}`) }catch(e){ From ea1b66eb0982bd47adb54786aee95768b90318b8 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 30 Jan 2021 23:17:03 +0200 Subject: [PATCH 23/24] Add widgetCallbacks map to shared global object --- profile/chrome/utils/boot.jsm | 1 + 1 file changed, 1 insertion(+) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index fe528a3..d34e515 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -39,6 +39,7 @@ const yPref = { }; const SHARED_GLOBAL = {}; +Object.defineProperty(SHARED_GLOBAL,"widgetCallbacks",{value:new Map()}); function resolveChromeURL(str){ const registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry); From 0178abfc53814c7f9d0c0b488efe1e2b10164a97 Mon Sep 17 00:00:00 2001 From: MrOtherGuy Date: Sat, 30 Jan 2021 23:19:59 +0200 Subject: [PATCH 24/24] Make ucUtils.restart() use Services.startup.quit --- profile/chrome/utils/boot.jsm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/profile/chrome/utils/boot.jsm b/profile/chrome/utils/boot.jsm index d34e515..75ba411 100644 --- a/profile/chrome/utils/boot.jsm +++ b/profile/chrome/utils/boot.jsm @@ -451,7 +451,13 @@ let _uc = { restart: function (clearCache){ clearCache && Services.appinfo.invalidateCachesOnRestart(); - _uc.utils.windows.get()[0].BrowserUtils.restartApplication(); + let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool); + Services.obs.notifyObservers( + cancelQuit, + "quit-application-requested", + "restart" + ); + Services.startup.quit( Services.startup.eAttemptQuit | Services.startup.eRestart ) } } };