diff options
| author | Akshay Nair <phenax5@gmail.com> | 2026-06-06 18:50:40 +0530 |
|---|---|---|
| committer | Akshay Nair <phenax5@gmail.com> | 2026-06-06 18:50:40 +0530 |
| commit | a7214b76e3f3a01f8ec6a6f8a9fe49e67765a3c5 (patch) | |
| tree | f6f26a429d8a27b5623024963fbebdcc8d0881f6 /modules/firefox.home/chrome | |
| parent | 210d4bf063d10a723f66029c2a12610e479d172c (diff) | |
| download | nixos-config-main.tar.gz nixos-config-main.zip | |
Diffstat (limited to 'modules/firefox.home/chrome')
| -rw-r--r-- | modules/firefox.home/chrome/customNewTab.uc.js | 41 | ||||
| -rw-r--r-- | modules/firefox.home/chrome/globalKeybindings.uc.js | 171 | ||||
| -rw-r--r-- | modules/firefox.home/chrome/utils/BootstrapLoader.js | 520 | ||||
| -rw-r--r-- | modules/firefox.home/chrome/utils/ChromeManifest.sys.mjs | 415 | ||||
| -rw-r--r-- | modules/firefox.home/chrome/utils/RDFDataSource.sys.mjs | 434 | ||||
| -rw-r--r-- | modules/firefox.home/chrome/utils/RDFManifestConverter.sys.mjs | 99 | ||||
| -rw-r--r-- | modules/firefox.home/chrome/utils/chrome.manifest | 2 | ||||
| -rw-r--r-- | modules/firefox.home/chrome/utils/userChrome.js | 272 | ||||
| -rw-r--r-- | modules/firefox.home/chrome/utils/xPref.sys.mjs | 92 |
9 files changed, 0 insertions, 2046 deletions
diff --git a/modules/firefox.home/chrome/customNewTab.uc.js b/modules/firefox.home/chrome/customNewTab.uc.js deleted file mode 100644 index 7ea8ee2..0000000 --- a/modules/firefox.home/chrome/customNewTab.uc.js +++ /dev/null @@ -1,41 +0,0 @@ -// ==UserScript== -// @name Custom New Tab -// @version 1.0 -// @description Load a custom link or local file, instead of the default new tab page -// @include main -// @startup UC.customNewTab.updateNewTabURL() -// @shutdown UC.customNewTab.destroy(); -// @onlyonce -// ==/UserScript== - -console.log('------------------------- newtab') -(() => { - // Managed in about:config - const NEWTAB_URL_PREF = 'browser.newtab.url' - - if (!AboutNewTab) { - globalThis.AboutNewTab = ChromeUtils.importESModule('resource:///modules/AboutNewTab.sys.mjs').AboutNewTab; - } - - const module = { - updateNewTabURL: () => { - const newTabUrl = xPref.get(NEWTAB_URL_PREF) || 'about:blank'; - if (AboutNewTab.newTabURL === newTabUrl) return; - - AboutNewTab.newTabURL = newTabUrl; - console.log('Updated new tab url to', newTabUrl); - }, - - init() { - module.updateNewTabURL(); - Services.obs.addObserver(module.updateNewTabURL, 'newtab-url-changed'); - }, - destroy() { - Services.obs.removeObserver(module.updateNewTabURL, 'newtab-url-changed'); - }, - }; - - module.init(); - - UC.customNewTab = module; -})(); diff --git a/modules/firefox.home/chrome/globalKeybindings.uc.js b/modules/firefox.home/chrome/globalKeybindings.uc.js deleted file mode 100644 index fe1d1f7..0000000 --- a/modules/firefox.home/chrome/globalKeybindings.uc.js +++ /dev/null @@ -1,171 +0,0 @@ -// ==UserScript== -// @name Global Keybindings -// @version 1.0 -// @description Setup global keybindings on all windows -// @include main -// @startup UC.globalKeybindings.init(win); -// @shutdown UC.globalKeybindings.destroy(); -// @onlyonce -// ==/UserScript== - -console.log('------------------------- keybinds') - -(() => { - // Configure global keybindings - const KEYBINDINGS = () => ({ - RELEASE: [ - [ctrl(shift(key('J'))), moveSelectedTabBy(+1)], - [ctrl(shift(key('K'))), moveSelectedTabBy(-1)], - [ctrl(shift(key('B'))), sidetabs.toggle], - [ctrl(alt(key('p'))), togglePassthrough, { modes: ALL_MODES }], - [alt(key('h')), history.back], - [alt(key('l')), history.forward], - ], - PRESS: [ - // Block default key bindings - [ctrl(shift(key('J'))), preventDefault()], - [ctrl(shift(key('K'))), preventDefault()], - [ctrl(shift(key('B'))), preventDefault()], - [ctrl(key('h')), preventDefault()], - [ctrl(key('b')), preventDefault()], - [alt(key('h')), preventDefault()], - [alt(key('l')), preventDefault()], - - [ctrl(key('j')), preventDefault(nextTab())], - [ctrl(key('k')), preventDefault(prevTab())], - - // Ctrl + number takes to the tab at position - ...(Array.from({ length: 10 }, (_, idx) => - [ctrl(key(idx === 9 ? 0 : idx + 1)), tabIndex(idx)], - )), - ], - }); - - // Restart firefox: Services.startup.quit(Services.startup.eForceQuit | Services.startup.eRestart) - - const nextTab = () => () => updateTabIndex((n, len) => (n + 1) % len); - const prevTab = () => () => updateTabIndex((n, len) => n === 0 ? len - 1 : n - 1); - const tabIndex = idx => () => updateTabIndex((_n, _len) => idx) - const preventDefault = f => e => { e.preventDefault(); f?.(e); }; - const moveSelectedTabBy = incr => () => updateSelectedTabIndex(incr); - const togglePassthrough = () => UC.globalKeybindings.updateMode(m => m !== MODE_PASSTHRU ? MODE_PASSTHRU : MODE_NORMAL) - - const history = { - back: () => gBrowser.goBack(), - forward: () => gBrowser.goForward(), - } - - const sidetabs = { - // getExtensionId: () => { - // return [...SidebarController.sidebars].find(([_, p]) => p.label?.match(/sidetabs/i))?.[0]; - // }, - ensureReady: async () => { - // Hide sidebar - SidebarController.hide(); - // Close by default - SidebarController.toggleExpanded(false); - // for (let i = 0; i < 3; i++) { - // if (sidetabs.getExtensionId()) { - // return !SidebarController.isOpen && SidebarController.show(sidetabs.getExtensionId() ?? undefined) - // } else { - // console.log('sidebar retry...'); - // await new Promise(res => setTimeout(res), 100); - // } - // } - }, - toggle: async () => { - SidebarController.toggleExpanded(); - // await sidetabs.ensureReady(); - // const sidebar = document.getElementById('sidebar-box'); - // sidebar?.classList.toggle('open'); - }, - } - - const MODE_PASSTHRU = 'passthru'; - const MODE_NORMAL = ''; - const ALL_MODES = [MODE_NORMAL, MODE_PASSTHRU]; - - const ctrl = b => e => b(e) && e.ctrlKey; - const alt = b => e => b(e) && e.altKey; - const shift = b => e => b(e) && e.shiftKey; - const key = key => e => `${e.key}` === `${key}`; - - const updateTabIndex = (f) => { - const newIndex = f(gBrowser.tabContainer.selectedIndex, gBrowser.tabs.length) - if (typeof newIndex !== 'number') return; - if (newIndex >= gBrowser.tabContainer.allTabs.length) return; - if (newIndex < 0) return; - gBrowser.selectTabAtIndex(newIndex); - }; - - const updateSelectedTabIndex = (incr) => { - if (typeof incr !== 'number') return; - if (incr === +1) return gBrowser.moveTabForward(); - if (incr === -1) return gBrowser.moveTabBackward(); - const newIndex = gBrowser.tabContainer.selectedIndex + incr; - if (newIndex >= gBrowser.tabContainer.allTabs.length) return; - if (newIndex < 0) return; - gBrowser.moveTabTo(gBrowser.selectedTab, newIndex) - }; - - const module = { - mode: MODE_NORMAL, - allModes: ALL_MODES, - - keybindings: KEYBINDINGS(), - - evaluateKeybindings: (bindings, e) => { - for (const [isKey, action, options] of bindings) { - // Only allow keys from the given mode - if ((options?.modes ?? [MODE_NORMAL]).includes(module.mode)) { - if (isKey(e)) { - const shouldContinue = action(e); - if (!shouldContinue) break; - } - } - } - }, - - updateMode: f => { - module.mode = f(module.mode); - if (gNavToolbox) gNavToolbox.dataset.keyMode = module.mode; - Services.obs.notifyObservers(null, 'uc:globalKeybindings:modeChanged', module.mode); - }, - - handleKeyUpEvent: e => module.evaluateKeybindings(module.keybindings.RELEASE, e), - handleKeyDownEvent: e => module.evaluateKeybindings(module.keybindings.PRESS, e), - - onWindowReady: async (win) => { - win.addEventListener('keyup', module.handleKeyUpEvent, true); - win.addEventListener('keydown', module.handleKeyDownEvent, true); - - await sidetabs.ensureReady(); - }, - - init(win) { - console.log('-------------------------------------------'); - console.log('init keys'); - console.log('-------------------------------------------'); - - const observe = () => { - Services.obs.removeObserver(observe, 'browser-window-before-show'); - module.onWindowReady(win) - } - - if (win.__SSi) { - module.onWindowReady(win) - } else { - Services.obs.addObserver(observe, 'browser-window-before-show'); - } - }, - destroy() { - _uc.windows((_doc, win) => { - win.removeEventListener('keyup', module.handleKeyUpEvent, true); - win.removeEventListener('keydown', module.handleKeyDownEvent, true); - }); - delete UC.globalKeybindings; - }, - }; - - UC.globalKeybindings = module; -})(); diff --git a/modules/firefox.home/chrome/utils/BootstrapLoader.js b/modules/firefox.home/chrome/utils/BootstrapLoader.js deleted file mode 100644 index 69631ab..0000000 --- a/modules/firefox.home/chrome/utils/BootstrapLoader.js +++ /dev/null @@ -1,520 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict'; - -const Services = globalThis.Services; - -ChromeUtils.defineESModuleGetters(globalThis, { - Blocklist: 'resource://gre/modules/Blocklist.sys.mjs', - ConsoleAPI: 'resource://gre/modules/Console.sys.mjs', - InstallRDF: 'chrome://userchromejs/content/RDFManifestConverter.sys.mjs', - ChromeManifest: 'chrome://userchromejs/content/ChromeManifest.sys.mjs', -}); - -Services.obs.addObserver(doc => { - if (doc.location.protocol + doc.location.pathname === 'about:addons' || - doc.location.protocol + doc.location.pathname === 'chrome:/content/extensions/aboutaddons.html') { - const win = doc.defaultView; - let handleEvent_orig = win.customElements.get('addon-card').prototype.handleEvent; - win.customElements.get('addon-card').prototype.handleEvent = function (e) { - if (e.type === 'click' && - e.target.getAttribute('action') === 'preferences' && - this.addon.__AddonInternal__.optionsType == 1/*AddonManager.OPTIONS_TYPE_DIALOG*/ && !!this.addon.optionsURL) { - var windows = Services.wm.getEnumerator(null); - while (windows.hasMoreElements()) { - var win2 = windows.getNext(); - if (win2.closed) { - continue; - } - if (win2.document.documentURI == this.addon.optionsURL) { - win2.focus(); - return; - } - } - var features = 'chrome,titlebar,toolbar,centerscreen'; - win.docShell.rootTreeItem.domWindow.openDialog(this.addon.optionsURL, this.addon.id, features); - } else { - handleEvent_orig.apply(this, arguments); - } - } - let update_orig = win.customElements.get('addon-options').prototype.update; - win.customElements.get('addon-options').prototype.update = function (card, addon) { - update_orig.apply(this, arguments); - if (addon.__AddonInternal__?.optionsType == 1/*AddonManager.OPTIONS_TYPE_DIALOG*/ && !!addon.optionsURL) - this.querySelector('panel-item[data-l10n-id="preferences-addon-button"]').hidden = false; - } - } -}, 'chrome-document-loaded'); - -const {AddonManager} = ChromeUtils.importESModule('resource://gre/modules/AddonManager.sys.mjs'); -const {XPIDatabase, AddonInternal} = ChromeUtils.importESModule('resource://gre/modules/addons/XPIDatabase.sys.mjs'); -const {XPIExports} = ChromeUtils.importESModule('resource://gre/modules/addons/XPIExports.sys.mjs') - -XPIDatabase.isDisabledLegacy = () => false; - -var orig_verifyBundleSignedState = XPIExports.verifyBundleSignedState; -XPIExports.verifyBundleSignedState = async (aBundle, aAddon) => { - if(!aAddon.isWebExtension && aAddon.type === 'extension' || aAddon.id.includes('_N_SIGN_')) - return { signedState: undefined, signedTypes: [] }; - return orig_verifyBundleSignedState(aBundle, aAddon); -} - -ChromeUtils.defineLazyGetter(this, 'BOOTSTRAP_REASONS', () => { - const {XPIProvider} = ChromeUtils.importESModule('resource://gre/modules/addons/XPIProvider.sys.mjs'); - return XPIProvider.BOOTSTRAP_REASONS; -}); - -ChromeUtils.defineLazyGetter(this, "logger", () => { - let { ConsoleAPI } = ChromeUtils.importESModule( - "resource://gre/modules/Console.sys.mjs" - ); - let consoleOptions = { - maxLogLevel: "all", - prefix: "BootstrapLoader", - }; - return new ConsoleAPI(consoleOptions); -}); - -/** - * Valid IDs fit this pattern. - */ -var gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]*\@[a-z0-9-\._]+)$/i; - -// Properties that exist in the install manifest -const PROP_METADATA = ['id', 'version', 'type', 'internalName', 'updateURL', - 'optionsURL', 'optionsType', 'aboutURL', 'iconURL']; -const PROP_LOCALE_SINGLE = ['name', 'description', 'creator', 'homepageURL']; -const PROP_LOCALE_MULTI = ['developers', 'translators', 'contributors']; - -// Map new string type identifiers to old style nsIUpdateItem types. -// Retired values: -// 32 = multipackage xpi file -// 8 = locale -// 256 = apiextension -// 128 = experiment -// theme = 4 -const TYPES = { - extension: 2, - dictionary: 64, -}; - -const COMPATIBLE_BY_DEFAULT_TYPES = { - extension: true, - dictionary: true, -}; - -const hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); - -function isXPI(filename) { - let ext = filename.slice(-4).toLowerCase(); - return ext === '.xpi' || ext === '.zip'; -} - -/** - * Gets an nsIURI for a file within another file, either a directory or an XPI - * file. If aFile is a directory then this will return a file: URI, if it is an - * XPI file then it will return a jar: URI. - * - * @param {nsIFile} aFile - * The file containing the resources, must be either a directory or an - * XPI file - * @param {string} aPath - * The path to find the resource at, '/' separated. If aPath is empty - * then the uri to the root of the contained files will be returned - * @returns {nsIURI} - * An nsIURI pointing at the resource - */ -function getURIForResourceInFile(aFile, aPath) { - if (!isXPI(aFile.leafName)) { - let resource = aFile.clone(); - if (aPath) - aPath.split('/').forEach(part => resource.append(part)); - - return Services.io.newFileURI(resource); - } - - return buildJarURI(aFile, aPath); -} - -/** - * Creates a jar: URI for a file inside a ZIP file. - * - * @param {nsIFile} aJarfile - * The ZIP file as an nsIFile - * @param {string} aPath - * The path inside the ZIP file - * @returns {nsIURI} - * An nsIURI for the file - */ -function buildJarURI(aJarfile, aPath) { - let uri = Services.io.newFileURI(aJarfile); - uri = 'jar:' + uri.spec + '!/' + aPath; - return Services.io.newURI(uri); -} - -var BootstrapLoader = { - name: 'bootstrap', - manifestFile: 'install.rdf', - async loadManifest(pkg) { - /** - * Reads locale properties from either the main install manifest root or - * an em:localized section in the install manifest. - * - * @param {Object} aSource - * The resource to read the properties from. - * @param {boolean} isDefault - * True if the locale is to be read from the main install manifest - * root - * @param {string[]} aSeenLocales - * An array of locale names already seen for this install manifest. - * Any locale names seen as a part of this function will be added to - * this array - * @returns {Object} - * an object containing the locale properties - */ - function readLocale(aSource, isDefault, aSeenLocales) { - let locale = {}; - if (!isDefault) { - locale.locales = []; - for (let localeName of aSource.locales || []) { - if (!localeName) { - logger.warn('Ignoring empty locale in localized properties'); - continue; - } - if (aSeenLocales.includes(localeName)) { - logger.warn('Ignoring duplicate locale in localized properties'); - continue; - } - aSeenLocales.push(localeName); - locale.locales.push(localeName); - } - - if (locale.locales.length == 0) { - logger.warn('Ignoring localized properties with no listed locales'); - return null; - } - } - - for (let prop of [...PROP_LOCALE_SINGLE, ...PROP_LOCALE_MULTI]) { - if (hasOwnProperty(aSource, prop)) { - locale[prop] = aSource[prop]; - } - } - - return locale; - } - - let manifestData = await pkg.readString('install.rdf'); - let manifest = InstallRDF.loadFromString(manifestData).decode(); - - let addon = new AddonInternal(); - for (let prop of PROP_METADATA) { - if (hasOwnProperty(manifest, prop)) { - addon[prop] = manifest[prop]; - } - } - - if (!addon.type) { - addon.type = 'extension'; - } else { - let type = addon.type; - addon.type = null; - for (let name in TYPES) { - if (TYPES[name] == type) { - addon.type = name; - break; - } - } - } - - if (!(addon.type in TYPES)) - throw new Error('Install manifest specifies unknown type: ' + addon.type); - - if (!addon.id) - throw new Error('No ID in install manifest'); - if (!gIDTest.test(addon.id)) - throw new Error('Illegal add-on ID ' + addon.id); - if (!addon.version) - throw new Error('No version in install manifest'); - - addon.strictCompatibility = (!(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) || - manifest.strictCompatibility == 'true'); - - // Only read these properties for extensions. - if (addon.type == 'extension') { - if (manifest.bootstrap != 'true') { - throw new Error('Non-restartless extensions no longer supported'); - } - - if (addon.optionsType && - addon.optionsType != 1/*AddonManager.OPTIONS_TYPE_DIALOG*/ && - addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_BROWSER && - addon.optionsType != AddonManager.OPTIONS_TYPE_TAB) { - throw new Error('Install manifest specifies unknown optionsType: ' + addon.optionsType); - } - - if (addon.optionsType) - addon.optionsType = parseInt(addon.optionsType); - } - - addon.defaultLocale = readLocale(manifest, true); - - let seenLocales = []; - addon.locales = []; - for (let localeData of manifest.localized || []) { - let locale = readLocale(localeData, false, seenLocales); - if (locale) - addon.locales.push(locale); - } - - let dependencies = new Set(manifest.dependencies); - addon.dependencies = Object.freeze(Array.from(dependencies)); - - let seenApplications = []; - addon.targetApplications = []; - for (let targetApp of manifest.targetApplications || []) { - if (!targetApp.id || !targetApp.minVersion || - !targetApp.maxVersion) { - logger.warn('Ignoring invalid targetApplication entry in install manifest'); - continue; - } - if (seenApplications.includes(targetApp.id)) { - logger.warn('Ignoring duplicate targetApplication entry for ' + targetApp.id + - ' in install manifest'); - continue; - } - seenApplications.push(targetApp.id); - addon.targetApplications.push(targetApp); - } - - // Note that we don't need to check for duplicate targetPlatform entries since - // the RDF service coalesces them for us. - addon.targetPlatforms = []; - for (let targetPlatform of manifest.targetPlatforms || []) { - let platform = { - os: null, - abi: null, - }; - - let pos = targetPlatform.indexOf('_'); - if (pos != -1) { - platform.os = targetPlatform.substring(0, pos); - platform.abi = targetPlatform.substring(pos + 1); - } else { - platform.os = targetPlatform; - } - - addon.targetPlatforms.push(platform); - } - - addon.userDisabled = false; - addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED; - addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT; - - addon.userPermissions = null; - - addon.icons = {}; - if (await pkg.hasResource('icon.png')) { - addon.icons[32] = 'icon.png'; - addon.icons[48] = 'icon.png'; - } - - if (await pkg.hasResource('icon64.png')) { - addon.icons[64] = 'icon64.png'; - } - - Object.defineProperty(addon, 'appDisabled', { - set: _ => {}, - get: _ => false - }); - - Object.defineProperty(addon, 'signedState', { - set: _ => {}, - get: _ => AddonManager.SIGNEDSTATE_NOT_REQUIRED - }); - - return addon; - }, - - loadScope(addon) { - let file = addon.file || addon._sourceBundle; - let uri = getURIForResourceInFile(file, 'bootstrap.js').spec; - let principal = Services.scriptSecurityManager.getSystemPrincipal(); - - let sandbox = new Cu.Sandbox(principal, { - sandboxName: uri, - addonId: addon.id, - wantGlobalProperties: ['ChromeUtils'], - metadata: { addonID: addon.id, URI: uri }, - }); - - try { - Object.assign(sandbox, BOOTSTRAP_REASONS); - - ChromeUtils.defineLazyGetter(sandbox, 'console', () => - new ConsoleAPI({ consoleID: `addon/${addon.id}` })); - - Services.scriptloader.loadSubScript(uri, sandbox); - } catch (e) { - logger.warn(`Error loading bootstrap.js for ${addon.id}`, e); - } - - function findMethod(name) { - if (sandbox[name]) { - return sandbox[name]; - } - - try { - let method = Cu.evalInSandbox(name, sandbox); - return method; - } catch (err) { } - - return () => { - logger.warn(`Add-on ${addon.id} is missing bootstrap method ${name}`); - }; - } - - let install = findMethod('install'); - let uninstall = findMethod('uninstall'); - let startup = findMethod('startup'); - let shutdown = findMethod('shutdown'); - - /** - * Reads content from a jar: URI - * - * @param {nsIURI} jarURI - The jar: URI to read from - * @returns {Promise<string>} The content of the file inside the JAR - */ - async function readFromJarURI(jarURI) { - return new Promise((resolve, reject) => { - try { - const channel = Services.io.newChannelFromURI( - jarURI, - null, - Services.scriptSecurityManager.getSystemPrincipal(), - null, - Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, - Ci.nsIContentPolicy.TYPE_OTHER - ); - - const inputStream = channel.open(); - const scriptableStream = Cc[ - '@mozilla.org/scriptableinputstream;1' - ].createInstance(Ci.nsIScriptableInputStream); - scriptableStream.init(inputStream); - - let data = ''; - let available = 0; - while ((available = scriptableStream.available()) > 0) { - data += scriptableStream.read(available); - } - - scriptableStream.close(); - inputStream.close(); - resolve(data); - } catch (e) { - reject(e); - } - }); - } - - // Register a chrome manifest temporarily and return a function which un-does - // the registrarion when no longer needed. - function createManifestTemporarily(manifestText) { - let tempDir = Services.dirsvc.get('ProfD', Ci.nsIFile) - tempDir.append('browser-extension-data'); - tempDir.append(addon.id); - tempDir.append('manifests'); - if (tempDir.exists()) { - // Clean any leftover temp.manifest - tempDir.remove(true); - } - tempDir.append('temp.manifest.' + Date.now()); - - let foStream = Cc[ - '@mozilla.org/network/file-output-stream;1' - ].createInstance(Ci.nsIFileOutputStream); - foStream.init(tempDir, 0x02 | 0x08 | 0x20, 0o664, 0); // write, create, truncate - foStream.write(manifestText, manifestText.length); - foStream.close(); - - Components.manager - .QueryInterface(Ci.nsIComponentRegistrar) - .autoRegister(tempDir); - - Cc['@mozilla.org/uriloader/external-helper-app-service;1'] - .getService(Ci.nsPIExternalAppLauncher) - .deleteTemporaryFileOnExit(tempDir); - - return function () { - tempDir.fileSize = 0; // truncate the manifest - Cc['@mozilla.org/chrome/chrome-registry;1'] - .getService(Ci.nsIXULChromeRegistry) - .checkForNewChrome(); - }; - } - - return { - install(...args) { - install(...args); - // Forget any cached files we might've had from this extension. - Services.obs.notifyObservers(null, 'startupcache-invalidate'); - }, - - uninstall(...args) { - uninstall(...args); - // Forget any cached files we might've had from this extension. - Services.obs.notifyObservers(null, 'startupcache-invalidate'); - }, - - async startup(...args) { - if (addon.type == 'extension') { - logger.debug(`Registering manifest for ${file.path}\n`); - const manifestURI = getURIForResourceInFile(file, 'chrome.manifest'); - let manifestData = await readFromJarURI(manifestURI); - let chromeManifest = new ChromeManifest(() => { - return manifestData; - }, { - application: Services.appinfo.ID, - appversion: Services.appinfo.version, - platformversion: Services.appinfo.platformVersion, - os: Services.appinfo.OS, - osversion: Services.sysinfo.getProperty('version'), - abi: Services.appinfo.XPCOMABI - }); - await chromeManifest.parse() - this._clearManifest = createManifestTemporarily(chromeManifest.toString(getURIForResourceInFile(file, '').spec)); - } - return startup(...args); - }, - - shutdown(data, reason) { - try { - return shutdown(data, reason); - } catch (err) { - throw err; - } finally { - if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) { - logger.debug(`Removing manifest for ${file.path}\n`); - this._clearManifest(); - this._clearManifest = null; - } - } - }, - }; - }, -}; - -AddonManager.addExternalExtensionLoader(BootstrapLoader); - -if (AddonManager.isReady) { - AddonManager.getAllAddons().then(addons => { - addons.forEach(addon => { - if (addon.type == 'extension' && !addon.isWebExtension && !addon.userDisabled) { - addon.reload(); - }; - }); - }); -} diff --git a/modules/firefox.home/chrome/utils/ChromeManifest.sys.mjs b/modules/firefox.home/chrome/utils/ChromeManifest.sys.mjs deleted file mode 100644 index f2ebc18..0000000 --- a/modules/firefox.home/chrome/utils/ChromeManifest.sys.mjs +++ /dev/null @@ -1,415 +0,0 @@ -/* exported ChromeManifest */ -"use strict"; - -/** - * A default map, which assumes a default value on get() if the key doesn't exist - */ -class DefaultMap extends Map { - /** - * Constructs the default map - * - * @param {Function} _default A function that returns the default value for this map - * @param {*} iterable An iterable to initialize the map with - */ - constructor(_default, iterable) { - super(iterable); - this._default = _default; - } - - /** - * Get the given key, creating if necessary - * - * @param {String} key The key of the map to get - * @param {Boolean} create True, if the key should be created in case it doesn't exist. - */ - get(key, create = true) { - if (this.has(key)) { - return super.get(key); - } else if (create) { - this.set(key, this._default()); - return super.get(key); - } - - return this._default(); - } -} - -/** - * A parser for chrome.manifest files. Implements a subset of - * https://developer.mozilla.org/en-US/docs/Mozilla/Chrome_Registration - */ -export class ChromeManifest { - /** - * Constucts the chrome.manifest parser - * - * @param {Function} loader An asynchronous function that will load further files, e.g. - * those included via the |manifest| instruction. The - * function will take the file as an argument and should - * resolve with the string contents of that file - * @param {Object} options Object describing the current system. The keys are manifest - * instructions - */ - constructor(loader, options) { - this.loader = loader; - this.options = options; - - this.overlay = new DefaultMap(() => []); - this.locales = new DefaultMap(() => new Map()); - this.style = new DefaultMap(() => new Set()); - this.category = new DefaultMap(() => new Map()); - - this.component = new Map(); - this.contract = new Map(); - - this.content = new Map(); - this.skin = new DefaultMap(() => new Map()); - this.resource = new Map(); - this.override = new Map(); - } - - /** - * Parse the given file. - * - * @param {String} filename The filename to load - * @param {String} base The relative directory this file is expected to be in. - * @return {Promise} Resolved when loading completes - */ - async parse(filename = "chrome.manifest", base = "") { - await this.parseString(await this.loader(filename), base); - } - - /** - * Parse the given string. - * - * @param {String} data The file data to load - * @param {String} base The relative directory this file is expected to be in. - * @return {Promise} Resolved when loading completes - */ - async parseString(data, base = "") { - const lines = data.split("\n"); - const extraManifests = []; - for (const line of lines) { - const parts = line.split(/\s+/); - const directive = parts.shift(); - switch (directive) { - case "manifest": - extraManifests.push(this._parseManifest(base, ...parts)); - break; - case "component": this._parseComponent(...parts); - break; - case "contract": this._parseContract(...parts); - break; - case "category": this._parseCategory(...parts); - break; - case "content": this._parseContent(...parts); - break; - case "locale": this._parseLocale(...parts); - break; - case "skin": this._parseSkin(...parts); - break; - case "resource": this._parseResource(...parts); - break; - case "overlay": this._parseOverlay(...parts); - break; - case "style": this._parseStyle(...parts); - break; - case "override": this._parseOverride(...parts); - break; - } - } - - await Promise.all(extraManifests); - } - - /** - * Ensure the flags provided for the instruction match our options - * - * @param {String[]} flags An array of raw flag values in the form key=value. - * @return {Boolean} True, if the flags match the options provided in the constructor - */ - _parseFlags(flags) { - const matchString = (a, sign, b) => { - if (sign != "=") { - console.warn(`Invalid sign ${sign} in ${a}${sign}${b}, dropping manifest instruction`); - return false; - } - return a == b; - }; - - const matchVersion = (a, sign, b) => { - switch (sign) { - case "=": return Services.vc.compare(a, b) == 0; - case ">": return Services.vc.compare(a, b) > 0; - case "<": return Services.vc.compare(a, b) < 0; - case ">=": return Services.vc.compare(a, b) >= 0; - case "<=": return Services.vc.compare(a, b) <= 0; - default: - console.warn(`Invalid sign ${sign} in ${a}${sign}${b}, dropping manifest instruction`); - return false; - } - }; - - const flagdata = new DefaultMap(() => []); - - const flagMatches = (key, typeMatch) => { - return !flagdata.has(key) || flagdata.get(key).some(val => typeMatch(this.options[key], ...val)); - }; - - for (const flag of flags) { - const match = flag.match(/(\w+)(>=|<=|<|>|=)(.*)/); - if (match) { - flagdata.get(match[1]).push([match[2], match[3]]); - } else { - console.warn(`Invalid flag ${flag}, dropping manifest instruction`); - } - } - - return flagMatches("application", matchString) && - flagMatches("appversion", matchVersion) && - flagMatches("platformversion", matchVersion) && - flagMatches("os", matchString) && - flagMatches("osversion", matchVersion) && - flagMatches("abi", matchString); - } - - /** - * Parse the manifest instruction, to load other files - * - * @param {String} base The base directory the manifest file is in - * @param {String} filename The file and path to load - * @param {...String} flags The flags for this instruction - * @return {Promise} Promise resolved when the manifest is loaded - */ - async _parseManifest(base, filename, ...flags) { - if (this._parseFlags(flags)) { - const dirparts = filename.split("/"); - dirparts.pop(); - - try { - await this.parse(filename, base + "/" + dirparts.join("/")); - } catch (e) { - console.log(`Could not read manifest '${base}/${filename}'.`); - } - } - return null; - } - - /** - * Parse the component instruction, to load xpcom components - * - * @param {String} classid The xpcom class id to load - * @param {String} loction The file location of this component - * @param {...String} flags The flags for this instruction - */ - _parseComponent(classid, location, ...flags) { - if (this._parseFlags(flags)) { - this.component.set(classid, location); - } - } - - /** - * Parse the contract instruction, to load xpcom contract ids - * - * @param {String} contractid The xpcom contract id to load - * @param {String} location The file location of this component - * @param {...String} flags The flags for this instruction - */ - _parseContract(contractid, location, ...flags) { - if (this._parseFlags(flags)) { - this.contract.set(contractid, location); - } - } - - /** - * Parse the category instruction, to set up xpcom categories - * - * @param {String} category The name of the category - * @param {String} entryName The category entry name - * @param {String} value The category entry value - * @param {...String} flags The flags for this instruction - */ - _parseCategory(category, entryName, value, ...flags) { - if (this._parseFlags(flags)) { - this.category.get(category).set(entryName, value); - } - } - - /** - * Parse the content instruction, to set chrome content locations - * - * @param {String} shortname The content short name, e.g. chrome://shortname/content/ - * @param {String} location The location for this content registration - * @param {...String} flags The flags for this instruction - */ - _parseContent(shortname, location, ...flags) { - if (this._parseFlags(flags)) { - this.content.set(shortname, location); - } - } - - /** - * Parse the locale instruction, to set chrome locale locations - * - * @param {String} shortname The locale short name, e.g. chrome://shortname/locale/ - * @param {String} location The location for this locale registration - * @param {...String} flags The flags for this instruction - */ - _parseLocale(shortname, locale, location, ...flags) { - if (this._parseFlags(flags)) { - this.locales.get(shortname).set(locale, location); - } - } - - /** - * Parse the skin instruction, to set chrome skin locations - * - * @param {String} shortname The skin short name, e.g. chrome://shortname/skin/ - * @param {String} location The location for this skin registration - * @param {...String} flags The flags for this instruction - */ - _parseSkin(packagename, skinname, location, ...flags) { - if (this._parseFlags(flags)) { - this.skin.get(packagename).set(skinname, location); - } - } - - /** - * Parse the resource instruction, to set up resource uri subtitutions - * - * @param {String} packagename The resource package name, e.g. resource://packagename/ - * @param {String} url The location for this content registration - * @param {...String} flags The flags for this instruction - */ - _parseResource(packagename, location, ...flags) { - if (this._parseFlags(flags)) { - this.resource.set(packagename, location); - } - } - - /** - * Parse the overlay instruction, to set up xul overlays - * - * @param {String} targetUrl The chrome target url - * @param {String} overlayUrl The url of the xul overlay - * @param {...String} flags The flags for this instruction - */ - _parseOverlay(targetUrl, overlayUrl, ...flags) { - if (this._parseFlags(flags)) { - this.overlay.get(targetUrl).push(overlayUrl); - } - } - - /** - * Parse the style instruction, to add stylesheets into chrome windows - * - * @param {String} uri The uri of the chrome window - * @param {String} sheet The uri of the css sheet - * @param {...String} flags The flags for this instruction - */ - _parseStyle(uri, sheet, ...flags) { - if (this._parseFlags(flags)) { - this.style.get(uri).add(sheet); - } - } - - /** - * Parse the override instruction, to set chrome uri overrides - * - * @param {String} uri The uri being overridden - * @param {String} newuri The replacement uri for the original location - * @param {...String} flags The flags for this instruction - */ - _parseOverride(uri, newuri, ...flags) { - if (this._parseFlags(flags)) { - this.override.set(uri, newuri); - } - } - - /** - * Output the manifest data as a string in chrome.manifest format - * If root is provided, convert all relative locations to absolute paths. - * - * @param {String} [root] The absolute path root to resolve relative locations - * @return {String} The manifest contents as a string - */ - toString(root = "") { - const lines = []; - const isRelative = loc => { - // Not absolute if doesn't start with /, \, or protocol (chrome://, resource://, etc) - return ( - typeof loc === "string" && - !loc.match(/^(?:[a-zA-Z]+:|\\|\/)/) - ); - }; - const absolutize = loc => { - if (!root) return loc; - if (loc === "/") return root; - if (isRelative(loc)) { - return root.replace(/[\\/]$/, "") + "/" + loc.replace(/^([\\\/])/, ""); - } - return loc; - }; - - // Output components - for (const [classid, location] of this.component) { - lines.push(`component ${classid} ${absolutize(location)}`); - } - - // Output contracts - for (const [contractid, location] of this.contract) { - lines.push(`contract ${contractid} ${absolutize(location)}`); - } - - // Output categories - for (const [category, entries] of this.category) { - for (const [entryName, value] of entries) { - lines.push(`category ${category} ${entryName} ${value}`); - } - } - - // Output content - for (const [shortname, location] of this.content) { - lines.push(`content ${shortname} ${absolutize(location)}`); - } - - // Output locales - for (const [shortname, locales] of this.locales) { - for (const [locale, location] of locales) { - lines.push(`locale ${shortname} ${locale} ${absolutize(location)}`); - } - } - - // Output skins - for (const [packagename, skins] of this.skin) { - for (const [skinname, location] of skins) { - lines.push(`skin ${packagename} ${skinname} ${absolutize(location)}`); - } - } - - // Output resources - for (const [packagename, location] of this.resource) { - lines.push(`resource ${packagename} ${absolutize(location)}`); - } - - // Output overlays - for (const [targetUrl, overlays] of this.overlay) { - for (const overlayUrl of overlays) { - lines.push(`overlay ${targetUrl} ${absolutize(overlayUrl)}`); - } - } - - // Output styles - for (const [uri, sheets] of this.style) { - for (const sheet of sheets) { - lines.push(`style ${uri} ${absolutize(sheet)}`); - } - } - - // Output overrides - for (const [uri, newuri] of this.override) { - lines.push(`override ${uri} ${newuri}`); - } - - return lines.join("\n"); - } -} diff --git a/modules/firefox.home/chrome/utils/RDFDataSource.sys.mjs b/modules/firefox.home/chrome/utils/RDFDataSource.sys.mjs deleted file mode 100644 index c6380c1..0000000 --- a/modules/firefox.home/chrome/utils/RDFDataSource.sys.mjs +++ /dev/null @@ -1,434 +0,0 @@ - /* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This module creates a new API for accessing and modifying RDF graphs. The - * goal is to be able to serialise the graph in a human readable form. Also - * if the graph was originally loaded from an RDF/XML the serialisation should - * closely match the original with any new data closely following the existing - * layout. The output should always be compatible with Mozilla's RDF parser. - * - * This is all achieved by using a DOM Document to hold the current state of the - * graph in XML form. This can be initially loaded and parsed from disk or - * a blank document used for an empty graph. As assertions are added to the - * graph, appropriate DOM nodes are added to the document to represent them - * along with any necessary whitespace to properly layout the XML. - * - * In general the order of adding assertions to the graph will impact the form - * the serialisation takes. If a resource is first added as the object of an - * assertion then it will eventually be serialised inside the assertion's - * property element. If a resource is first added as the subject of an assertion - * then it will be serialised at the top level of the XML. - */ - -const NS_XML = "http://www.w3.org/XML/1998/namespace"; -const NS_XMLNS = "http://www.w3.org/2000/xmlns/"; -const NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; -const NS_NC = "http://home.netscape.com/NC-rdf#"; - -/* eslint prefer-template: 1 */ - -function isElement(obj) { - return Element.isInstance(obj); -} -function isText(obj) { - return obj && typeof obj == "object" && ChromeUtils.getClassName(obj) == "Text"; -} - -/** - * Returns either an rdf namespaced attribute or an un-namespaced attribute - * value. Returns null if neither exists, - */ -function getRDFAttribute(element, name) { - if (element.hasAttributeNS(NS_RDF, name)) - return element.getAttributeNS(NS_RDF, name); - if (element.hasAttribute(name)) - return element.getAttribute(name); - return undefined; -} - -/** - * Represents an assertion in the datasource - */ -class RDFAssertion { - constructor(subject, predicate, object) { - // The subject on this assertion, an RDFSubject - this._subject = subject; - // The predicate, a string - this._predicate = predicate; - // The object, an RDFNode - this._object = object; - // The datasource this assertion exists in - this._ds = this._subject._ds; - // Marks that _DOMnode is the subject's element - this._isSubjectElement = false; - // The DOM node that represents this assertion. Could be a property element, - // a property attribute or the subject's element for rdf:type - this._DOMNode = null; - } - - getPredicate() { - return this._predicate; - } - - getObject() { - return this._object; - } -} - -class RDFNode { - equals(rdfnode) { - return (rdfnode.constructor === this.constructor && - rdfnode._value == this._value); - } -} - -/** - * A simple literal value - */ -export class RDFLiteral extends RDFNode { - constructor(value) { - super(); - this._value = value; - } - - getValue() { - return this._value; - } -} - -/** - * This is an RDF node that can be a subject so a resource or a blank node - */ -class RDFSubject extends RDFNode { - constructor(ds) { - super(); - // A lookup of the assertions with this as the subject. Keyed on predicate - this._assertions = {}; - // A lookup of the assertions with this as the object. Keyed on predicate - this._backwards = {}; - // The datasource this subject belongs to - this._ds = ds; - // The DOM elements in the document that represent this subject. Array of Element - this._elements = []; - } - - /** - * Parses the given Element from the DOM document - */ - /* eslint-disable complexity */ - _parseElement(element) { - this._elements.push(element); - - // There might be an inferred rdf:type assertion in the element name - if (element.namespaceURI != NS_RDF || - element.localName != "Description") { - var assertion = new RDFAssertion(this, RDF_R("type"), - this._ds.getResource(element.namespaceURI + element.localName)); - assertion._DOMnode = element; - assertion._isSubjectElement = true; - this._addAssertion(assertion); - } - - // Certain attributes can be literal properties - for (let attr of element.attributes) { - if (attr.namespaceURI == NS_XML || attr.namespaceURI == NS_XMLNS || - attr.nodeName == "xmlns") - continue; - if ((attr.namespaceURI == NS_RDF || !attr.namespaceURI) && - (["nodeID", "about", "resource", "ID", "parseType"].includes(attr.localName))) - continue; - var object = null; - if (attr.namespaceURI == NS_RDF) { - if (attr.localName == "type") - object = this._ds.getResource(attr.nodeValue); - } - if (!object) - object = new RDFLiteral(attr.nodeValue); - assertion = new RDFAssertion(this, attr.namespaceURI + attr.localName, object); - assertion._DOMnode = attr; - this._addAssertion(assertion); - } - - var child = element.firstChild; - element.listCounter = 1; - while (child) { - if (isElement(child)) { - object = null; - var predicate = child.namespaceURI + child.localName; - if (child.namespaceURI == NS_RDF) { - if (child.localName == "li") { - predicate = RDF_R(`_${element.listCounter}`); - element.listCounter++; - } - } - - // Check for and bail out on unknown attributes on the property element - for (let attr of child.attributes) { - // Ignore XML namespaced attributes - if (attr.namespaceURI == NS_XML) - continue; - // These are reserved by XML for future use - if (attr.localName.substring(0, 3).toLowerCase() == "xml") - continue; - // We can handle these RDF attributes - if ((!attr.namespaceURI || attr.namespaceURI == NS_RDF) && - ["resource", "nodeID"].includes(attr.localName)) - continue; - // This is a special attribute we handle for compatibility with Mozilla RDF - if (attr.namespaceURI == NS_NC && - attr.localName == "parseType") - continue; - } - - var parseType = child.getAttributeNS(NS_NC, "parseType"); - - var resource = getRDFAttribute(child, "resource"); - var nodeID = getRDFAttribute(child, "nodeID"); - - if (resource !== undefined) { - var base = Services.io.newURI(element.baseURI); - object = this._ds.getResource(base.resolve(resource)); - } else if (nodeID !== undefined) { - object = this._ds.getBlankNode(nodeID); - } else { - var hasText = false; - var childElement = null; - var subchild = child.firstChild; - while (subchild) { - if (isText(subchild) && /\S/.test(subchild.nodeValue)) { - hasText = true; - } else if (isElement(subchild)) { - childElement = subchild; - } - subchild = subchild.nextSibling; - } - - if (childElement) { - object = this._ds._getSubjectForElement(childElement); - object._parseElement(childElement); - } else - object = new RDFLiteral(child.textContent); - } - - assertion = new RDFAssertion(this, predicate, object); - this._addAssertion(assertion); - assertion._DOMnode = child; - } - child = child.nextSibling; - } - } - /* eslint-enable complexity */ - - /** - * Adds a new assertion to the internal hashes. Should be called for every - * new assertion parsed or created programmatically. - */ - _addAssertion(assertion) { - var predicate = assertion.getPredicate(); - if (predicate in this._assertions) - this._assertions[predicate].push(assertion); - else - this._assertions[predicate] = [ assertion ]; - - var object = assertion.getObject(); - if (object instanceof RDFSubject) { - // Create reverse assertion - if (predicate in object._backwards) - object._backwards[predicate].push(assertion); - else - object._backwards[predicate] = [ assertion ]; - } - } - - /** - * Returns all objects in assertions with this subject and the given predicate. - */ - getObjects(predicate) { - if (predicate in this._assertions) - return Array.from(this._assertions[predicate], - i => i.getObject()); - - return []; - } - - /** - * Retrieves the first property value for the given predicate. - */ - getProperty(predicate) { - if (predicate in this._assertions) - return this._assertions[predicate][0].getObject(); - return null; - } -} - -/** - * Creates a new RDFResource for the datasource. Private. - */ -export class RDFResource extends RDFSubject { - constructor(ds, uri) { - super(ds); - // This is the uri that the resource represents. - this._uri = uri; - } -} - -/** - * Creates a new blank node. Private. - */ -export class RDFBlankNode extends RDFSubject { - constructor(ds, nodeID) { - super(ds); - // The nodeID of this node. May be null if there is no ID. - this._nodeID = nodeID; - } - - /** - * Sets attributes on the DOM element to mark it as representing this node - */ - _applyToElement(element) { - if (!this._nodeID) - return; - if (USE_RDFNS_ATTR) { - var prefix = this._ds._resolvePrefix(element, RDF_R("nodeID")); - element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID); - } else { - element.setAttribute("nodeID", this._nodeID); - } - } - - /** - * Creates a new Element in the document for holding assertions about this - * subject. The URI controls what tagname to use. - */ - _createNewElement(uri) { - // If there are already nodes representing this in the document then we need - // a nodeID to match them - if (!this._nodeID && this._elements.length > 0) { - this._ds._createNodeID(this); - for (let element of this._elements) - this._applyToElement(element); - } - - return super._createNewElement.call(uri); - } - - /** - * Adds a reference to this node to the given property Element. - */ - _addReferenceToElement(element) { - if (this._elements.length > 0 && !this._nodeID) { - // In document elsewhere already - // Create a node ID and update the other nodes referencing - this._ds._createNodeID(this); - for (let element of this._elements) - this._applyToElement(element); - } - - if (this._nodeID) { - if (USE_RDFNS_ATTR) { - let prefix = this._ds._resolvePrefix(element, RDF_R("nodeID")); - element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID); - } else { - element.setAttribute("nodeID", this._nodeID); - } - } else { - // Add the empty blank node, this is generally right since further - // assertions will be added to fill this out - var newelement = this._ds._addElement(element, RDF_R("Description")); - newelement.listCounter = 1; - this._elements.push(newelement); - } - } - - /** - * Removes any reference to this node from the given property Element. - */ - _removeReferenceFromElement(element) { - if (element.hasAttributeNS(NS_RDF, "nodeID")) - element.removeAttributeNS(NS_RDF, "nodeID"); - if (element.hasAttribute("nodeID")) - element.removeAttribute("nodeID"); - } - - getNodeID() { - return this._nodeID; - } -} - -/** - * Creates a new RDFDataSource from the given document. The document will be - * changed as assertions are added and removed to the RDF. Pass a null document - * to start with an empty graph. - */ -export class RDFDataSource { - constructor(document) { - // All known resources, indexed on URI - this._resources = {}; - // All blank nodes - this._allBlankNodes = []; - - // The underlying DOM document for this datasource - this._document = document; - this._parseDocument(); - } - - static loadFromString(text) { - let parser = new DOMParser(); - let document = parser.parseFromString(text, "application/xml"); - - return new this(document); - } - - /** - * Returns an rdf subject for the given DOM Element. If the subject has not - * been seen before a new one is created. - */ - _getSubjectForElement(element) { - var about = getRDFAttribute(element, "about"); - - if (about !== undefined) { - let base = Services.io.newURI(element.baseURI); - return this.getResource(base.resolve(about)); - } - return this.getBlankNode(null); - } - - /** - * Parses the document for subjects at the top level. - */ - _parseDocument() { - var domnode = this._document.documentElement.firstChild; - while (domnode) { - if (isElement(domnode)) { - var subject = this._getSubjectForElement(domnode); - subject._parseElement(domnode); - } - domnode = domnode.nextSibling; - } - } - - /** - * Gets a blank node. nodeID may be null and if so a new blank node is created. - * If a nodeID is given then the blank node with that ID is returned or created. - */ - getBlankNode(nodeID) { - var rdfnode = new RDFBlankNode(this, nodeID); - this._allBlankNodes.push(rdfnode); - return rdfnode; - } - - /** - * Gets the resource for the URI. The resource is created if it has not been - * used already. - */ - getResource(uri) { - if (uri in this._resources) - return this._resources[uri]; - - var resource = new RDFResource(this, uri); - this._resources[uri] = resource; - return resource; - } -}
\ No newline at end of file diff --git a/modules/firefox.home/chrome/utils/RDFManifestConverter.sys.mjs b/modules/firefox.home/chrome/utils/RDFManifestConverter.sys.mjs deleted file mode 100644 index 25cae7e..0000000 --- a/modules/firefox.home/chrome/utils/RDFManifestConverter.sys.mjs +++ /dev/null @@ -1,99 +0,0 @@ - /* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -import { RDFDataSource } from "chrome://userchromejs/content/RDFDataSource.sys.mjs"; - -const RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest"; - -function EM_R(aProperty) { - return `http://www.mozilla.org/2004/em-rdf#${aProperty}`; -} - -function getValue(literal) { - return literal && literal.getValue(); -} - -function getProperty(resource, property) { - return getValue(resource.getProperty(EM_R(property))); -} - -class Manifest { - constructor(ds) { - this.ds = ds; - } - - static loadFromString(text) { - return new this(RDFDataSource.loadFromString(text)); - } -} - -export class InstallRDF extends Manifest { - _readProps(source, obj, props) { - for (let prop of props) { - let val = getProperty(source, prop); - if (val != null) { - obj[prop] = val; - } - } - } - - _readArrayProp(source, obj, prop, target, decode = getValue) { - let result = Array.from(source.getObjects(EM_R(prop)), - target => decode(target)); - if (result.length) { - obj[target] = result; - } - } - - _readArrayProps(source, obj, props, decode = getValue) { - for (let [prop, target] of Object.entries(props)) { - this._readArrayProp(source, obj, prop, target, decode); - } - } - - _readLocaleStrings(source, obj) { - this._readProps(source, obj, ["name", "description", "creator", "homepageURL"]); - this._readArrayProps(source, obj, { - locale: "locales", - developer: "developers", - translator: "translators", - contributor: "contributors", - }); - } - - decode() { - let root = this.ds.getResource(RDFURI_INSTALL_MANIFEST_ROOT); - let result = {}; - - let props = ["id", "version", "type", "updateURL", "optionsURL", - "optionsType", "aboutURL", "iconURL", - "bootstrap", "unpack", "strictCompatibility"]; - this._readProps(root, result, props); - - let decodeTargetApplication = source => { - let app = {}; - this._readProps(source, app, ["id", "minVersion", "maxVersion"]); - return app; - }; - - let decodeLocale = source => { - let localized = {}; - this._readLocaleStrings(source, localized); - return localized; - }; - - this._readLocaleStrings(root, result); - - this._readArrayProps(root, result, {"targetPlatform": "targetPlatforms"}); - this._readArrayProps(root, result, {"targetApplication": "targetApplications"}, - decodeTargetApplication); - this._readArrayProps(root, result, {"localized": "localized"}, - decodeLocale); - this._readArrayProps(root, result, {"dependency": "dependencies"}, - source => getProperty(source, "id")); - - return result; - } -}
\ No newline at end of file diff --git a/modules/firefox.home/chrome/utils/chrome.manifest b/modules/firefox.home/chrome/utils/chrome.manifest deleted file mode 100644 index d9076b2..0000000 --- a/modules/firefox.home/chrome/utils/chrome.manifest +++ /dev/null @@ -1,2 +0,0 @@ -content userchromejs ./ -resource userchromejs ../ diff --git a/modules/firefox.home/chrome/utils/userChrome.js b/modules/firefox.home/chrome/utils/userChrome.js deleted file mode 100644 index 1870bee..0000000 --- a/modules/firefox.home/chrome/utils/userChrome.js +++ /dev/null @@ -1,272 +0,0 @@ -'use strict';
-
-(() => {
- ChromeUtils.defineESModuleGetters(globalThis, {
- xPref: 'chrome://userchromejs/content/xPref.sys.mjs',
- Management: 'resource://gre/modules/Extension.sys.mjs',
- AppConstants: 'resource://gre/modules/AppConstants.sys.mjs',
- });
-
- let UC = {
- webExts: new Map(),
- sidebar: new Map(),
- sandboxes: new WeakMap()
- };
-
- let _uc = {
- ALWAYSEXECUTE: 'rebuild_userChrome.uc.js',
- BROWSERCHROME: AppConstants.MOZ_APP_NAME == 'thunderbird' ? 'chrome://messenger/content/messenger.xhtml' : 'chrome://browser/content/browser.xhtml',
- BROWSERTYPE: AppConstants.MOZ_APP_NAME == 'thunderbird' ? 'mail:3pane' : 'navigator:browser',
- BROWSERNAME: AppConstants.MOZ_APP_NAME.charAt(0).toUpperCase() + AppConstants.MOZ_APP_NAME.slice(1),
- PREF_ENABLED: 'userChromeJS.enabled',
- PREF_SCRIPTSDISABLED: 'userChromeJS.scriptsDisabled',
-
- chromedir: Services.dirsvc.get('UChrm', Ci.nsIFile),
- scriptsDir: '',
-
- sss: Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService),
-
- getScripts: function () {
- this.scripts = {};
- let files = this.chromedir.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator);
- while (files.hasMoreElements()) {
- let file = files.getNext().QueryInterface(Ci.nsIFile);
- if (/\.uc\.js$/i.test(file.leafName)) {
- _uc.getScriptData(file);
- }
- }
- },
-
- getScriptData: function (aFile) {
- let aContent = this.readFile(aFile);
- let header = (aContent.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,
- file: aFile,
- url: Services.io.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromDir(this.chromedir) + filename,
- name: (header.match(/\/\/ @name\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 (xPref.get(_uc.PREF_SCRIPTSDISABLED) || '').split(',').indexOf(this.filename) == -1;
- }
- }
- },
-
- readFile: function (aFile, metaOnly = false) {
- let stream = Cc['@mozilla.org/network/file-input-stream;1'].createInstance(Ci.nsIFileInputStream);
- stream.init(aFile, 0x01, 0, 0);
- let cvstream = Cc['@mozilla.org/intl/converter-input-stream;1'].createInstance(Ci.nsIConverterInputStream);
- cvstream.init(stream, 'UTF-8', 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
- let content = '',
- data = {};
- while (cvstream.readString(4096, data)) {
- content += data.value;
- if (metaOnly && content.indexOf('// ==/UserScript==') > 0) {
- break;
- }
- }
- cvstream.close();
- return content.replace(/\r\n?/g, '\n');
- },
-
- everLoaded: [],
-
- loadScript: function (script, win) {
- if (!script.regex.test(win.location.href) || (script.filename != this.ALWAYSEXECUTE && !script.isEnabled)) {
- return;
- }
-
- if (script.onlyonce && script.isRunning) {
- if (script.startup) {
- Cu.evalInSandbox(`(function(script, win){${script.startup}})`, this.getSandbox(win))(script, win);
- }
- return;
- }
-
- try {
- Services.scriptloader.loadSubScript(script.url + '?' + script.file.lastModifiedTime,
- script.onlyonce ? { window: win } : win);
- script.isRunning = true;
- if (script.startup) {
- Cu.evalInSandbox(`(function(script, win){${script.startup}})`, this.getSandbox(win))(script, win);
- }
- if (!script.shutdown) {
- this.everLoaded.push(script.id);
- }
- } catch (ex) {
- Cu.reportError(ex);
- }
- },
-
- getSandbox: function (doc) {
- if (!UC.sandboxes) UC.sandboxes = new WeakMap();
- let global = Cu.getGlobalForObject(doc);
- if (UC.sandboxes.has(global))
- return UC.sandboxes.get(global);
- let sb = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), {
- sandboxPrototype: global,
- sameZoneAs: global,
- wantXrays: false,
- sandboxName: 'UCJS:Sandbox'
- });
- UC.sandboxes.set(global, sb);
- global.addEventListener('unload', () => {
- UC.sandboxes.delete(global);
- Cu.nukeSandbox(sb);
- });
- return sb;
- },
-
- windows: function (fun, onlyBrowsers = true) {
- let windows = Services.wm.getEnumerator(onlyBrowsers ? this.BROWSERTYPE : null);
- while (windows.hasMoreElements()) {
- let win = windows.getNext();
- if (!win._uc)
- continue;
- if (!onlyBrowsers) {
- let frames = win.docShell.getAllDocShellsInSubtree(Ci.nsIDocShellTreeItem.typeAll, Ci.nsIDocShell.ENUMERATE_FORWARDS);
- let res = frames.some(frame => {
- let fWin = frame.domWindow;
- let {document, location} = fWin;
- if (fun(document, fWin, location))
- return true;
- });
- if (res)
- break;
- } else {
- let {document, location} = win;
- if (fun(document, win, location))
- break;
- }
- }
- },
-
- createElement: function (doc, tag, atts, XUL = true) {
- let el = XUL ? doc.createXULElement(tag) : doc.createElement(tag);
- for (let att in atts) {
- if (att.startsWith('on'))
- el.addEventListener(att.slice(2), typeof atts[att] == "string" ?
- Cu.evalInSandbox(`(function(event){${atts[att]}})`, this.getSandbox(doc)) : atts[att]);
- else
- el.setAttribute(att, atts[att]);
- }
- return el
- }
- };
-
- if (xPref.get(_uc.PREF_ENABLED) === undefined) {
- xPref.set(_uc.PREF_ENABLED, true, true);
- }
-
- if (xPref.get(_uc.PREF_SCRIPTSDISABLED) === undefined) {
- xPref.set(_uc.PREF_SCRIPTSDISABLED, '', true);
- }
-
- let UserChrome_js = {
- observe: function (aSubject) {
- if (
- AppConstants.MOZ_APP_NAME == "thunderbird" &&
- aSubject?.location?.href.startsWith("chrome://messenger/content")
- ) {
- aSubject.addEventListener("DOMContentLoaded", () => {
- this.load(aSubject);
- }, {once: true})
- } else {
- aSubject.addEventListener('DOMContentLoaded', this, {once: true});
- }
- },
-
- handleEvent: function (aEvent) {
- let document = aEvent.originalTarget;
- let window = document.defaultView;
- if (window.document.isInitialDocument) {
- this.load(window.parent);
- } else {
- this.load(window);
- }
- },
-
- load: function (window) {
- let location = window.location;
-
- if (!this.sharedWindowOpened && location.href == 'chrome://extensions/content/dummy.xhtml') {
- this.sharedWindowOpened = true;
-
- Management.on('extension-browser-inserted', function (topic, browser) {
- browser.messageManager.addMessageListener('Extension:BackgroundViewLoaded', this.messageListener.bind(this));
- }.bind(this));
- } else if (/^(chrome:(?!\/\/global\/content\/commonDialog\.x?html)|about:(?!blank))/i.test(location.href)) {
- window.UC = UC;
- window._uc = _uc;
- window.xPref = xPref;
- if (window._gBrowser) // bug 1443849
- window.gBrowser = window._gBrowser;
-
- if (xPref.get(_uc.PREF_ENABLED)) {
- Object.values(_uc.scripts).forEach(script => {
- _uc.loadScript(script, window);
- });
- } else if (!UC.rebuild) {
- _uc.loadScript(_uc.scripts[_uc.ALWAYSEXECUTE], window);
- }
- }
- },
-
- messageListener: function (msg) {
- const browser = msg.target;
- const { addonId } = browser._contentPrincipal;
-
- browser.messageManager.removeMessageListener('Extension:BackgroundViewLoaded', this.messageListener);
-
- if (browser.ownerGlobal.location.href == 'chrome://extensions/content/dummy.xhtml') {
- UC.webExts.set(addonId, browser);
- Services.obs.notifyObservers(null, 'UCJS:WebExtLoaded', addonId);
- } else {
- let win = browser.ownerGlobal.windowRoot.ownerGlobal;
- UC.sidebar.get(addonId)?.set(win, browser) || UC.sidebar.set(addonId, new Map([[win, browser]]));
- Services.obs.notifyObservers(win, 'UCJS:SidebarLoaded', addonId);
- }
- }
- };
-
- if (!Services.appinfo.inSafeMode) {
- _uc.chromedir.append(_uc.scriptsDir);
- _uc.getScripts();
- let windows = Services.wm.getEnumerator(null);
- while (windows.hasMoreElements()) {
- let win = windows.getNext();
- if (!('UC' in win))
- UserChrome_js.load(win)
- }
- Services.obs.addObserver(UserChrome_js, 'chrome-document-global-created', false);
- }
-})();
diff --git a/modules/firefox.home/chrome/utils/xPref.sys.mjs b/modules/firefox.home/chrome/utils/xPref.sys.mjs deleted file mode 100644 index aba56db..0000000 --- a/modules/firefox.home/chrome/utils/xPref.sys.mjs +++ /dev/null @@ -1,92 +0,0 @@ -'use strict';
-
-export const xPref = {
- // Retorna o valor da preferência, seja qual for o tipo, mas não
- // testei com tipos complexos como nsIFile, não sei como detectar
- // uma preferência assim, na verdade nunca vi uma
- get: function (prefPath, def = false, valueIfUndefined, setDefault = true) {
- let sPrefs = def ?
- Services.prefs.getDefaultBranch(null) :
- Services.prefs;
-
- try {
- switch (sPrefs.getPrefType(prefPath)) {
- case 0:
- if (valueIfUndefined != undefined)
- return this.set(prefPath, valueIfUndefined, setDefault);
- else
- 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, def = false) {
- let sPrefs = def ?
- Services.prefs.getDefaultBranch(null) :
- Services.prefs;
-
- switch (typeof value) {
- case 'string':
- return sPrefs.setStringPref(prefPath, value) || value;
- case 'number':
- return sPrefs.setIntPref(prefPath, value) || value;
- case 'boolean':
- return sPrefs.setBoolPref(prefPath, value) || value;
- }
- return;
- },
-
- lock: function (prefPath, value) {
- let sPrefs = Services.prefs;
- this.lockedBackupDef[prefPath] = this.get(prefPath, true);
- if (sPrefs.prefIsLocked(prefPath))
- sPrefs.unlockPref(prefPath);
-
- this.set(prefPath, value, true);
- sPrefs.lockPref(prefPath);
- },
-
- lockedBackupDef: {},
-
- unlock: function (prefPath) {
- Services.prefs.unlockPref(prefPath);
- let bkp = this.lockedBackupDef[prefPath];
- if (bkp == undefined)
- Services.prefs.deleteBranch(prefPath);
- else
- this.set(prefPath, bkp, true);
- },
-
- clear: Services.prefs.clearUserPref,
-
- // Detecta mudanças na preferência e retorna:
- // return[0]: valor da preferência alterada
- // return[1]: nome da preferência alterada
- // Guardar chamada numa var se quiser interrompê-la depois
- addListener: function (prefPath, trat) {
- this.observer = function (aSubject, aTopic, prefPath) {
- return trat(xPref.get(prefPath), prefPath);
- }
-
- Services.prefs.addObserver(prefPath, this.observer);
- return {
- prefPath,
- observer: this.observer
- };
- },
-
- // Encerra pref observer
- // Só precisa passar a var definida quando adicionou
- removeListener: function (obs) {
- Services.prefs.removeObserver(obs.prefPath, obs.observer);
- }
-}
|
