From 8c5c323307938dd1927574c1396dca462cb2bd5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=A0tamcar?= Date: Fri, 31 Jan 2025 23:16:35 +0100 Subject: [PATCH] Migrate the UserChrome modifications to ESM This is necessary as support for JSM has been removed from Firefox 136. It also introduces a slight change for packaging. Instead of changing the version in `native/userchrome/profile/chrome/pwa/chrome.jsm`, the version now needs to be set in `native/userchrome/profile/chrome/pwa/chrome.sys.mjs`. --- .github/workflows/native.yaml | 2 +- docs/docs/help/faq.md | 2 +- native/packages/brew/configure.sh | 2 +- native/packages/deb/copyright | 4 +- native/packages/gentoo/firefoxpwa.ebuild | 2 +- native/scripts/set-version.ds | 16 +-- native/src/console/site.rs | 6 +- .../chrome/pwa/{boot.jsm => boot.sys.mjs} | 22 ++-- .../chrome/pwa/{chrome.jsm => chrome.sys.mjs} | 20 ++-- .../content/{browser.jsm => browser.sys.mjs} | 113 ++++++++++++------ ...enWindow.jsm => macosHiddenWindow.sys.mjs} | 2 +- .../{preferences.jsm => preferences.sys.mjs} | 15 ++- .../pwa/utils/{common.jsm => common.sys.mjs} | 4 +- ...{hookFunction.jsm => hookFunction.sys.mjs} | 4 +- ...eMessaging.jsm => nativeMessaging.sys.mjs} | 11 +- ...egration.jsm => systemIntegration.sys.mjs} | 60 +++++----- .../pwa/utils/{xPref.jsm => xPref.sys.mjs} | 6 +- native/userchrome/runtime/_autoconfig.cfg | 3 +- 18 files changed, 153 insertions(+), 141 deletions(-) rename native/userchrome/profile/chrome/pwa/{boot.jsm => boot.sys.mjs} (90%) rename native/userchrome/profile/chrome/pwa/{chrome.jsm => chrome.sys.mjs} (88%) rename native/userchrome/profile/chrome/pwa/content/{browser.jsm => browser.sys.mjs} (95%) rename native/userchrome/profile/chrome/pwa/content/{macosHiddenWindow.jsm => macosHiddenWindow.sys.mjs} (90%) rename native/userchrome/profile/chrome/pwa/content/{preferences.jsm => preferences.sys.mjs} (95%) rename native/userchrome/profile/chrome/pwa/utils/{common.jsm => common.sys.mjs} (69%) rename native/userchrome/profile/chrome/pwa/utils/{hookFunction.jsm => hookFunction.sys.mjs} (95%) rename native/userchrome/profile/chrome/pwa/utils/{nativeMessaging.jsm => nativeMessaging.sys.mjs} (77%) rename native/userchrome/profile/chrome/pwa/utils/{systemIntegration.jsm => systemIntegration.sys.mjs} (79%) rename native/userchrome/profile/chrome/pwa/utils/{xPref.jsm => xPref.sys.mjs} (93%) diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index 16978990..3e6bb5cd 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -245,7 +245,7 @@ jobs: VERSION=${GITHUB_REF/refs\/tags\/v} echo "VERSION=$VERSION" >> $GITHUB_ENV sed -i "s/version = \"0.0.0\"/version = \"$VERSION\"/g" Cargo.toml - sed -i "s/DISTRIBUTION_VERSION = '0.0.0'/DISTRIBUTION_VERSION = '$VERSION'/g" userchrome/profile/chrome/pwa/chrome.jsm + sed -i "s/DISTRIBUTION_VERSION = '0.0.0'/DISTRIBUTION_VERSION = '$VERSION'/g" userchrome/profile/chrome/pwa/chrome.sys.mjs else echo "VERSION=0.0.0" >> $GITHUB_ENV fi diff --git a/docs/docs/help/faq.md b/docs/docs/help/faq.md index 470b84f8..28ada1f3 100644 --- a/docs/docs/help/faq.md +++ b/docs/docs/help/faq.md @@ -380,7 +380,7 @@ To load custom JS into the web app profile: 1. Locate your web app profile inside [the profiles directory](../resources/installation-directories.md#profiles). 2. Inside the profile directory, create a `chrome` directory (if it does not exist yet). 3. Inside the `chrome` directory, create a `user` directory (if it does not exist yet). -4. Inside the `user` directory, create a `boot.jsm` or `boot.sys.mjs` file (if it does not exist yet). +4. Inside the `user` directory, create a `boot.sys.mjs` file (if it does not exist yet). 5. Copy your JS into the correct file (depending on the module format). 6. Relaunch the web app. diff --git a/native/packages/brew/configure.sh b/native/packages/brew/configure.sh index cb367fcf..5aa14764 100644 --- a/native/packages/brew/configure.sh +++ b/native/packages/brew/configure.sh @@ -16,7 +16,7 @@ LIBEXEC=$3 # Set the correct version in the source files sed -i"" -e "s/version = \"0.0.0\"/version = \"$VERSION\"/g" Cargo.toml -sed -i"" -e "s/DISTRIBUTION_VERSION = '0.0.0'/DISTRIBUTION_VERSION = '$VERSION'/g" userchrome/profile/chrome/pwa/chrome.jsm +sed -i"" -e "s/DISTRIBUTION_VERSION = '0.0.0'/DISTRIBUTION_VERSION = '$VERSION'/g" userchrome/profile/chrome/pwa/chrome.sys.mjs # Set the path in the manifest to the Homebrew libexec directory cp manifests/macos.json manifests/brew.json diff --git a/native/packages/deb/copyright b/native/packages/deb/copyright index 65ecbb33..3c6ef9ef 100644 --- a/native/packages/deb/copyright +++ b/native/packages/deb/copyright @@ -25,13 +25,13 @@ Copyright: Chris Simpson (@chrismsimpson) License: Unlicense Comment: Download: https://fontsarena.com/metropolis-by-chris-simpson/ -Files: userchrome/profile/utils/hookFunction.jsm +Files: userchrome/profile/utils/hookFunction.sys.mjs Copyright: xiaoxiaoflood Copyright: filips License: MPL-2.0 Comment: Original source: https://github.com/xiaoxiaoflood/firefox-scripts/blob/69675c7f09e9009b63b1cc239b94c03c5962a9d7/chrome/utils/hookFunction.jsm -Files: userchrome/profile/utils/xPref.jsm +Files: userchrome/profile/utils/xPref.sys.mjs Copyright: xiaoxiaoflood Copyright: filips License: MPL-2.0 diff --git a/native/packages/gentoo/firefoxpwa.ebuild b/native/packages/gentoo/firefoxpwa.ebuild index 64fc9735..631e6874 100644 --- a/native/packages/gentoo/firefoxpwa.ebuild +++ b/native/packages/gentoo/firefoxpwa.ebuild @@ -62,7 +62,7 @@ src_prepare() { # Set version in source files as per build instructions sed -i "s/version = \"0.0.0\"/version = \"${PV}\"/g" Cargo.toml || die sed -i "s/DISTRIBUTION_VERSION = '0.0.0'/DISTRIBUTION_VERSION = '${PV}'/g" \ - userchrome/profile/chrome/pwa/chrome.jsm || die + userchrome/profile/chrome/pwa/chrome.sys.mjs || die } src_configure() { diff --git a/native/scripts/set-version.ds b/native/scripts/set-version.ds index dd9868e8..87fc7297 100644 --- a/native/scripts/set-version.ds +++ b/native/scripts/set-version.ds @@ -38,11 +38,11 @@ end # Replace versions in files cargo_toml_path = set "./Cargo.toml" cargo_lock_path = set "./Cargo.lock" -chrome_jsm_path = set "./userchrome/profile/chrome/pwa/chrome.jsm" +chrome_mjs_path = set "./userchrome/profile/chrome/pwa/chrome.sys.mjs" cargo_toml_exists = is_file ${cargo_toml_path} cargo_lock_exists = is_file ${cargo_lock_path} -chrome_jsm_exists = is_file ${chrome_jsm_path} -if ${cargo_toml_exists} and ${cargo_lock_exists} and ${chrome_jsm_exists} +chrome_mjs_exists = is_file ${chrome_mjs_path} +if ${cargo_toml_exists} and ${cargo_lock_exists} and ${chrome_mjs_exists} echo "Setting version in Cargo.toml to \"${pwa_ver}\"" cargo_toml = readfile ${cargo_toml_path} cargo_toml = replace_version ${cargo_toml} "# Version will be set by CI from the Git tag when building and releasing\nversion = \"" ${pwa_ver} "\"" @@ -51,10 +51,10 @@ if ${cargo_toml_exists} and ${cargo_lock_exists} and ${chrome_jsm_exists} cargo_lock = readfile ${cargo_lock_path} cargo_lock = replace_version ${cargo_lock} "name = \"firefoxpwa\"\nversion = \"" ${pwa_ver} "\"" writefile ${cargo_lock_path} ${cargo_lock} - echo "Setting version in chrome.jsm to \"${pwa_ver}\"" - chrome_jsm = readfile ${chrome_jsm_path} - chrome_jsm = replace_version ${chrome_jsm} "DISTRIBUTION_VERSION = '" ${pwa_ver} "'" - writefile ${chrome_jsm_path} ${chrome_jsm} + echo "Setting version in chrome.sys.mjs to \"${pwa_ver}\"" + chrome_mjs = readfile ${chrome_mjs_path} + chrome_mjs = replace_version ${chrome_mjs} "DISTRIBUTION_VERSION = '" ${pwa_ver} "'" + writefile ${chrome_mjs_path} ${chrome_mjs} else - assert_fail "Unable to locate ./Cargo.toml, ./Cargo.lock and ./userchrome/profile/chrome/pwa/chrome.jsm" + assert_fail "Unable to locate ./Cargo.toml, ./Cargo.lock and ./userchrome/profile/chrome/pwa/chrome.sys.mjs" end diff --git a/native/src/console/site.rs b/native/src/console/site.rs index 5c44072d..bed90496 100644 --- a/native/src/console/site.rs +++ b/native/src/console/site.rs @@ -75,13 +75,13 @@ impl Run for SiteLaunchCommand { // Force patching if this is enabled true } else { - // Uses "chrome.jsm" file because it contains version info - let source = dirs.sysdata.join("userchrome/profile/chrome/pwa/chrome.jsm"); + // Uses "chrome.sys.mjs" file because it contains version info + let source = dirs.sysdata.join("../../userchrome/profile/chrome/pwa/chrome.sys.mjs"); let target = dirs .userdata .join("profiles") .join(profile.ulid.to_string()) - .join("chrome/pwa/chrome.jsm"); + .join("chrome/pwa/chrome.sys.mjs"); // Only patch if modification dates of source and target are different // In case any error happens, just force patching diff --git a/native/userchrome/profile/chrome/pwa/boot.jsm b/native/userchrome/profile/chrome/pwa/boot.sys.mjs similarity index 90% rename from native/userchrome/profile/chrome/pwa/boot.jsm rename to native/userchrome/profile/chrome/pwa/boot.sys.mjs index 034f45a9..a029453f 100644 --- a/native/userchrome/profile/chrome/pwa/boot.jsm +++ b/native/userchrome/profile/chrome/pwa/boot.sys.mjs @@ -1,15 +1,9 @@ -const EXPORTED_SYMBOLS = []; - -const { XPCOMUtils } = ChromeUtils.import('resource://gre/modules/XPCOMUtils.jsm'); -const Services = globalThis.Services || ChromeUtils.import('resource://gre/modules/Services.jsm').Services; -ChromeUtils.defineLazyGetter(this, 'gSystemPrincipal', () => Services.scriptSecurityManager.getSystemPrincipal()); -XPCOMUtils.defineLazyModuleGetters(this, { - AppConstants: 'resource://gre/modules/AppConstants.jsm', - BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.jsm', - NetUtil: 'resource://gre/modules/NetUtil.jsm', - LangPackMatcher: 'resource://gre/modules/LangPackMatcher.jsm', - applySystemIntegration: 'resource://pwa/utils/systemIntegration.jsm', -}); +import { AppConstants } from 'resource://gre/modules/AppConstants.sys.mjs'; +import { NetUtil } from 'resource://gre/modules/NetUtil.sys.mjs'; +import { nsDefaultCommandLineHandler, nsBrowserContentHandler } from 'resource:///modules/BrowserContentHandler.sys.mjs'; +import { BrowserWindowTracker } from 'resource:///modules/BrowserWindowTracker.sys.mjs'; + +import { applySystemIntegration } from 'resource://pwa/utils/systemIntegration.sys.mjs'; /** * Reads the PWAsForFirefox config file and parses it as JSON. @@ -126,7 +120,6 @@ Services.prefs.getDefaultBranch(null).setBoolPref('browser.privateWindowSeparati Services.prefs.getDefaultBranch(null).setBoolPref('browser.privacySegmentation.createdShortcut', true); // Override command line helper to intercept PWAsForFirefox arguments and start loading the site -const { nsDefaultCommandLineHandler } = Cu.import('resource:///modules/BrowserContentHandler.jsm'); nsDefaultCommandLineHandler.prototype._handle = nsDefaultCommandLineHandler.prototype.handle; nsDefaultCommandLineHandler.prototype.handle = function (cmdLine) { const isStartup = cmdLine.state === Ci.nsICommandLine.STATE_INITIAL_LAUNCH; @@ -183,7 +176,6 @@ nsDefaultCommandLineHandler.prototype.handle = function (cmdLine) { // Still does not work when multiple web apps are used in the same profile // This does not matter currently because of #81, but once it is fixed, this also needs to be reworked if (AppConstants.platform === 'macosx') { - const { nsBrowserContentHandler } = Cu.import('resource:///modules/BrowserContentHandler.jsm'); nsBrowserContentHandler.prototype._getNewWindowArgs = nsBrowserContentHandler.prototype.getNewWindowArgs; nsBrowserContentHandler.prototype.getNewWindowArgs = function () { if (globalThis.gFFPWASiteConfig) { @@ -220,4 +212,4 @@ Services.obs.addObserver(async subject => { }, 'webextension-langpack-startup'); // Import browser chrome modifications -ChromeUtils.import('resource://pwa/chrome.jsm'); +ChromeUtils.importESModule('resource://pwa/chrome.sys.mjs'); diff --git a/native/userchrome/profile/chrome/pwa/chrome.jsm b/native/userchrome/profile/chrome/pwa/chrome.sys.mjs similarity index 88% rename from native/userchrome/profile/chrome/pwa/chrome.jsm rename to native/userchrome/profile/chrome/pwa/chrome.sys.mjs index 0d167976..4f3209d6 100644 --- a/native/userchrome/profile/chrome/pwa/chrome.jsm +++ b/native/userchrome/profile/chrome/pwa/chrome.sys.mjs @@ -1,11 +1,6 @@ -const EXPORTED_SYMBOLS = []; +import { AppConstants } from 'resource://gre/modules/AppConstants.sys.mjs'; -const { XPCOMUtils } = ChromeUtils.import('resource://gre/modules/XPCOMUtils.jsm'); -const Services = globalThis.Services || ChromeUtils.import('resource://gre/modules/Services.jsm').Services; -XPCOMUtils.defineLazyModuleGetters(this, { - AppConstants: 'resource://gre/modules/AppConstants.jsm', - applySystemIntegration: 'resource://pwa/utils/systemIntegration.jsm', -}); +import { applySystemIntegration } from 'resource://pwa/utils/systemIntegration.sys.mjs'; const SSS = Cc['@mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService); @@ -16,11 +11,11 @@ class ChromeLoader { static FILES_BASE = Services.io.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromDir(Services.dirsvc.get('UChrm', Ci.nsIFile)); - static BROWSER_SCRIPT = 'pwa/content/browser.jsm'; + static BROWSER_SCRIPT = 'pwa/content/browser.sys.mjs'; static BROWSER_STYLES = 'pwa/content/browser.css'; - static PREFERENCES_SCRIPT = 'pwa/content/preferences.jsm'; + static PREFERENCES_SCRIPT = 'pwa/content/preferences.sys.mjs'; static PREFERENCES_STYLES = 'pwa/content/preferences.css'; - static MACOS_HIDDEN_WINDOW_SCRIPT = 'pwa/content/macosHiddenWindow.jsm'; + static MACOS_HIDDEN_WINDOW_SCRIPT = 'pwa/content/macosHiddenWindow.sys.mjs'; static DISTRIBUTION_ID = 'firefoxpwa'; static DISTRIBUTION_VERSION = '0.0.0'; @@ -79,7 +74,7 @@ class ChromeLoader { // Load a site config from a global object - Fix for reopening web app after closing all windows on macOS // Cannot be applied to other OSes - Does not work with multiple web apps in the same profile - // Also has some other problems - See `nsBrowserContentHandler` in `boot.jsm` for more details + // Also has some other problems - See `nsBrowserContentHandler` in `boot.sys.mjs` for more details if (AppConstants.platform === 'macosx' || AppConstants.platform === 'linux') { if (!window.gFFPWASiteConfig && globalThis.gFFPWASiteConfig) window.gFFPWASiteConfig = globalThis.gFFPWASiteConfig; globalThis.gFFPWASiteConfig = window.gFFPWASiteConfig; @@ -115,7 +110,8 @@ class ChromeLoader { } loadUserScript (scriptFilename, window) { - return Services.scriptloader.loadSubScript(ChromeLoader.FILES_BASE + scriptFilename, window, 'UTF-8'); + const script = `data:application/javascript,ChromeUtils.importESModule('resource://${scriptFilename}', { global: 'current' });`; + return Services.scriptloader.loadSubScript(script, window, 'UTF-8'); } } diff --git a/native/userchrome/profile/chrome/pwa/content/browser.jsm b/native/userchrome/profile/chrome/pwa/content/browser.sys.mjs similarity index 95% rename from native/userchrome/profile/chrome/pwa/content/browser.jsm rename to native/userchrome/profile/chrome/pwa/content/browser.sys.mjs index 320ca6f8..ee75fea8 100644 --- a/native/userchrome/profile/chrome/pwa/content/browser.jsm +++ b/native/userchrome/profile/chrome/pwa/content/browser.sys.mjs @@ -1,15 +1,26 @@ -XPCOMUtils.defineLazyModuleGetters(this, { - BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.jsm', - applyDynamicThemeColor: 'resource://pwa/utils/systemIntegration.jsm', - applySystemIntegration: 'resource://pwa/utils/systemIntegration.jsm', - buildIconList: 'resource://pwa/utils/systemIntegration.jsm', - sendNativeMessage: 'resource://pwa/utils/nativeMessaging.jsm', - hookFunction: 'resource://pwa/utils/hookFunction.jsm', - xPref: 'resource://pwa/utils/xPref.jsm', - sanitizeString: 'resource://pwa/utils/common.jsm', +import { OnboardingMessageProvider } from 'resource:///modules/asrouter/OnboardingMessageProvider.sys.mjs'; +import { nsContentDispatchChooser } from 'resource://gre/modules/ContentDispatchChooser.sys.mjs'; +import { WebNavigationManager } from 'resource://gre/modules/WebNavigation.sys.mjs'; +import { XPCOMUtils } from 'resource://gre/modules/XPCOMUtils.sys.mjs'; +import { BrowserGlue } from 'resource:///modules/BrowserGlue.sys.mjs'; +import { BrowserWindowTracker } from 'resource:///modules/BrowserWindowTracker.sys.mjs'; +import { WebProtocolHandlerRegistrar } from 'resource:///modules/WebProtocolHandlerRegistrar.sys.mjs'; + +import { sanitizeString } from 'resource://pwa/utils/common.sys.mjs'; +import { hookFunction } from 'resource://pwa/utils/hookFunction.sys.mjs'; +import { xPref } from 'resource://pwa/utils/xPref.sys.mjs'; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + sendNativeMessage: 'resource://pwa/utils/nativeMessaging.sys.mjs', + applyDynamicThemeColor: 'resource://pwa/utils/systemIntegration.sys.mjs', + applySystemIntegration: 'resource://pwa/utils/systemIntegration.sys.mjs', + buildIconList: 'resource://pwa/utils/systemIntegration.sys.mjs', }); -XPCOMUtils.defineLazyServiceGetter(this, 'ioService', '@mozilla.org/network/io-service;1', Ci.nsIIOService); -XPCOMUtils.defineLazyServiceGetter(this, 'WindowsUIUtils', '@mozilla.org/windows-ui-utils;1', Ci.nsIWindowsUIUtils); + +XPCOMUtils.defineLazyServiceGetter(lazy, 'ioService', '@mozilla.org/network/io-service;1', Ci.nsIIOService); +XPCOMUtils.defineLazyServiceGetter(lazy, 'WindowsUIUtils', '@mozilla.org/windows-ui-utils;1', Ci.nsIWindowsUIUtils); class PwaBrowser { constructor () { @@ -83,7 +94,7 @@ class PwaBrowser { document.getElementById('TabsToolbar-customization-target').append(siteInfo); // Set initial favicon and title to the site's static info - const siteIcons = buildIconList(window.gFFPWASiteConfig?.manifest.icons || []); + const siteIcons = lazy.buildIconList(window.gFFPWASiteConfig?.manifest.icons || []); const siteIcon = siteIcons.find(icon => icon.size >= 32) || siteIcons[siteIcons.length - 1]; if (siteIcon) tabIconImage.setAttribute('src', siteIcon.icon.src); @@ -357,8 +368,6 @@ class PwaBrowser { // Some checks here are directly based on the Firefox code, licensed under MPL 2.0 // Original source: https://github.com/mozilla/gecko-dev/blob/a62618baa72cd0ba6c0a5f5fc0b1d63f2866b7c6/browser/components/protocolhandler/WebProtocolHandlerRegistrar.jsm - const { WebProtocolHandlerRegistrar } = Cu.import('resource:///modules/WebProtocolHandlerRegistrar.jsm'); - WebProtocolHandlerRegistrar.prototype.registerProtocolHandler = function (protocol, url, title, documentURI, browserOrWindow) { protocol = (protocol || '').toLowerCase(); if (!url || !documentURI) return; @@ -396,7 +405,7 @@ class PwaBrowser { async callback (notification, buttonInfo) { // Send a request to the native program to register the handler - const response = await sendNativeMessage({ + const response = await lazy.sendNativeMessage({ cmd: 'RegisterProtocolHandler', params: { site: buttonInfo.protocolInfo.site, @@ -434,7 +443,7 @@ class PwaBrowser { WebProtocolHandlerRegistrar.prototype.removeProtocolHandler = function (protocol, url) { (async () => { // Send a request to the native program to unregister the handler - const response = await sendNativeMessage({ + const response = await lazy.sendNativeMessage({ cmd: 'UnregisterProtocolHandler', params: { site: window.gFFPWASiteConfig.ulid, @@ -498,7 +507,6 @@ class PwaBrowser { // Allow opening HTTP links without confirmation popup // This applies to all ways of opening HTTP links in default browser - const { nsContentDispatchChooser } = ChromeUtils.import('resource://gre/modules/ContentDispatchChooser.jsm'); nsContentDispatchChooser.prototype._hasProtocolHandlerPermissionOriginal = nsContentDispatchChooser.prototype._hasProtocolHandlerPermission; nsContentDispatchChooser.prototype._hasProtocolHandlerPermission = function(scheme, principal, triggeredExternally) { if (scheme === 'http' || scheme === 'https') return true; @@ -533,7 +541,6 @@ class PwaBrowser { // Handle passing config from old to new window and closing out-of-scope windows // Also handles applying the system integration for "disconnected" windows - const { WebNavigationManager } = Cu.import('resource://gre/modules/WebNavigation.jsm'); WebNavigationManager.addListener('onCreatedNavigationTarget', details => { const newWindow = details.browser.ownerGlobal; const sourceWindow = details.sourceTabBrowser.ownerGlobal; @@ -541,7 +548,7 @@ class PwaBrowser { // Pass config from the old window to a new one if (!newWindow.gFFPWASiteConfig) { newWindow.gFFPWASiteConfig = sourceWindow.gFFPWASiteConfig; - applySystemIntegration(newWindow, newWindow.gFFPWASiteConfig); + lazy.applySystemIntegration(newWindow, newWindow.gFFPWASiteConfig); } // Open out-of-scope links in default browser and close the newly-opened tab @@ -791,7 +798,7 @@ class PwaBrowser { // Handle switching browser tabs in tabs mode window.gBrowser.tabbox.addEventListener('select', () => { if (!window.gBrowser.selectedBrowser.gFFPWACurrentColor || !this.canLoad(window.gBrowser.currentURI)) return; - applyDynamicThemeColor(window, window.gBrowser.selectedBrowser.gFFPWACurrentColor); + lazy.applyDynamicThemeColor(window, window.gBrowser.selectedBrowser.gFFPWACurrentColor); }) // Handle theme color messages from the frame script @@ -799,7 +806,7 @@ class PwaBrowser { Services.mm.addMessageListener('firefoxpwa:theme-color', (message) => { if (window.gBrowser.selectedBrowser !== message.target || !this.canLoad(window.gBrowser.currentURI)) return; window.gBrowser.selectedBrowser.gFFPWACurrentColor = message.data.color; - applyDynamicThemeColor(window, message.data.color); + lazy.applyDynamicThemeColor(window, message.data.color); }); // Inject frame script into website content @@ -1108,7 +1115,7 @@ class PwaBrowser { const currentUrl = gURLBar.makeURIReadable(browser.currentURI).displaySpec; const currentTitle = browser.contentTitle; - WindowsUIUtils.shareUrl(currentUrl, currentTitle); + lazy.WindowsUIUtils.shareUrl(currentUrl, currentTitle); } }); } @@ -1540,7 +1547,7 @@ class PwaBrowser { }); }, onCommand (event) { - event.target.ownerGlobal.gIdentityHandler.handleIdentityButtonEvent(event); + event.target.ownerGlobal.gIdentityHandler.handleIdentityButtonEvent(event); } }); } @@ -1821,18 +1828,54 @@ class PwaBrowser { } configureLayout () { - // Configure default layout - let { gAreas } = Cu.import('resource:///modules/CustomizableUI.jsm'); - gAreas.get(CustomizableUI.AREA_NAVBAR).set('defaultPlacements', ['close-page-button', 'back-button', 'forward-button', 'urlbar-container']); - gAreas.get(CustomizableUI.AREA_TABSTRIP).set('defaultPlacements', ['site-info', 'tabbrowser-tabs', 'new-tab-button', 'alltabs-button', 'mute-button', 'notifications-button', 'permissions-button', 'downloads-button', 'tracking-protection-button', 'identity-button', 'unified-extensions-button']); - gAreas.get(CustomizableUI.AREA_BOOKMARKS).set('defaultCollapsed', 'never'); + // Configure the default placements of widgets + const defaultPlacements = { + [CustomizableUI.AREA_NAVBAR]: ['close-page-button', 'back-button', 'forward-button', 'urlbar-container'], + [CustomizableUI.AREA_TABSTRIP]: ['site-info', 'tabbrowser-tabs', 'new-tab-button', 'alltabs-button', 'mute-button', 'notifications-button', 'permissions-button', 'downloads-button', 'tracking-protection-button', 'identity-button', 'unified-extensions-button'] + }; + + // We cannot directly set the default placements because Firefox does not expose such a function + // Instead, we listen for area reset events and set the default placements when the event is fired + // In the future, we can try to implement such a function in Firefox and submit a patch + + const listener = { + onAreaReset: (area) => { + try { + // Start a batch update of items + CustomizableUI.beginBatchUpdate(); + + if (defaultPlacements.hasOwnProperty(area)) { + // Remove all existing widgets from the area + for (const widget of CustomizableUI.getWidgetIdsInArea(area)) { + CustomizableUI.removeWidgetFromArea(widget); + } + + // Add default widgets to the area + for (const [ix, widget] of defaultPlacements[area].entries()) { + CustomizableUI.addWidgetToArea(widget, area, ix); + } + } + } finally { + // End the batch update of items + CustomizableUI.endBatchUpdate(); + } + + // Hide bookmarks toolbar by default + xPref.clear('browser.toolbars.bookmarks.visibility'); + }, + }; + CustomizableUI.addListener(listener); // Reset layout to default on the first run, otherwise widgets are misplaced // We can check for the first run using telemetry reporting policy preference // Although this relies on the telemetry module, it still works on LibreWolf where telemetry is disabled setTimeout(() => { if (xPref.get('toolkit.telemetry.reportingpolicy.firstRun', false, true)) { + // Reset the layout to default CustomizableUI.reset(); + + // Hide bookmarks toolbar by default + xPref.clear('browser.toolbars.bookmarks.visibility'); } }); } @@ -1843,14 +1886,16 @@ class PwaBrowser { // this.modifyWidget('back-button', { removable: true }); // this.modifyWidget('forward-button', { removable: true }); + // We need to import these modules here as they are not available initially + // Make extensions widgets go to the tab strip area by default - const { BrowserActionBase } = ChromeUtils.import('resource://gre/modules/ExtensionActions.jsm'); + const { BrowserActionBase } = ChromeUtils.importESModule('resource://gre/modules/ExtensionActions.sys.mjs'); hookFunction(BrowserActionBase.prototype, 'getDefaultArea', null, function () { return this.globals.default_area === 'navbar' ? 'tabstrip' : this.globals.default_area; }) // Make Firefox Profiler button go to the tab strip area by default - const { ProfilerMenuButton } = ChromeUtils.import('resource://devtools/client/performance-new/popup/menu-button.jsm.js'); + const { ProfilerMenuButton } = ChromeUtils.importESModule('resource://devtools/client/performance-new/popup/menu-button.sys.mjs'); ProfilerMenuButton.addToNavbar = function (document) { CustomizableUI.addWidgetToArea('profiler-button', CustomizableUI.AREA_TABSTRIP); } @@ -2033,14 +2078,8 @@ class PwaBrowser { disableOnboarding () { // Disable default browser prompt - const { BrowserGlue } = ChromeUtils.import('resource:///modules/BrowserGlue.jsm'); BrowserGlue.prototype._maybeShowDefaultBrowserPrompt = async () => null; - // Handle both post-124 and pre-124 paths - let OnboardingMessageProvider = undefined; - try { OnboardingMessageProvider = ChromeUtils.import('resource:///modules/asrouter/OnboardingMessageProvider.jsm').OnboardingMessageProvider } - catch { OnboardingMessageProvider = ChromeUtils.import('resource://activity-stream/lib/OnboardingMessageProvider.jsm').OnboardingMessageProvider } - // Disable onboarding messages OnboardingMessageProvider.getMessages = async () => []; OnboardingMessageProvider.getUntranslatedMessages = async () => []; @@ -2132,7 +2171,7 @@ class PwaBrowser { if (!uri || uri.spec === 'about:blank') return true; if (!target.gFFPWASiteConfig) return false; - const scope = ioService.newURI(target.gFFPWASiteConfig.manifest.scope); + const scope = lazy.ioService.newURI(target.gFFPWASiteConfig.manifest.scope); if (scope.prePath !== uri.prePath) return false; return uri.filePath.startsWith(scope.filePath); diff --git a/native/userchrome/profile/chrome/pwa/content/macosHiddenWindow.jsm b/native/userchrome/profile/chrome/pwa/content/macosHiddenWindow.sys.mjs similarity index 90% rename from native/userchrome/profile/chrome/pwa/content/macosHiddenWindow.jsm rename to native/userchrome/profile/chrome/pwa/content/macosHiddenWindow.sys.mjs index d534a020..c53412f0 100644 --- a/native/userchrome/profile/chrome/pwa/content/macosHiddenWindow.jsm +++ b/native/userchrome/profile/chrome/pwa/content/macosHiddenWindow.sys.mjs @@ -1,4 +1,4 @@ -const { sanitizeString } = ChromeUtils.import('resource://pwa/utils/common.jsm'); +import { sanitizeString } from 'resource://pwa/utils/common.sys.mjs'; function OpenPwaShortcut(url) { switchToTabHavingURI(url, true); diff --git a/native/userchrome/profile/chrome/pwa/content/preferences.jsm b/native/userchrome/profile/chrome/pwa/content/preferences.sys.mjs similarity index 95% rename from native/userchrome/profile/chrome/pwa/content/preferences.jsm rename to native/userchrome/profile/chrome/pwa/content/preferences.sys.mjs index 5871df0d..e7e2356a 100644 --- a/native/userchrome/profile/chrome/pwa/content/preferences.jsm +++ b/native/userchrome/profile/chrome/pwa/content/preferences.sys.mjs @@ -1,8 +1,7 @@ -XPCOMUtils.defineLazyModuleGetters(this, { - ShortcutUtils: 'resource://gre/modules/ShortcutUtils.jsm', - hookFunction: 'resource://pwa/utils/hookFunction.jsm', - xPref: 'resource://pwa/utils/xPref.jsm', -}); +import { ShortcutUtils } from 'resource://gre/modules/ShortcutUtils.sys.mjs'; + +import { hookFunction } from 'resource://pwa/utils/hookFunction.sys.mjs'; +import { xPref } from 'resource://pwa/utils/xPref.sys.mjs'; class PwaPreferences { preferenceElementsAdded = false @@ -19,8 +18,8 @@ class PwaPreferences { hookFunction(gMainPane, 'init', null, () => { this.addPreferenceElements(); }); // Handle switch of preferences on load and when they changes - setTimeout(() => { this.handleTabsModePreferenceSwitch(true); } ); - xPref.addListener(ChromeLoader.PREF_ENABLE_TABS_MODE, () => { this.handleTabsModePreferenceSwitch() } ); + setTimeout(() => { this.handleTabsModePreferenceSwitch(true); }); + xPref.addListener(ChromeLoader.PREF_ENABLE_TABS_MODE, () => { this.handleTabsModePreferenceSwitch() }); } addPreferenceData () { @@ -165,7 +164,7 @@ class PwaPreferences { handleTabsModePreferenceSwitch (onLoad = false) { function setTabsSectionDisabled (disabled) { document.querySelectorAll('#mainPrefPane > groupbox:nth-child(11) > *').forEach(elem => elem.disabled = disabled) - document.querySelector('#launchTypeNewTab').disabled = disabled + document.querySelectorAll('#launchTypeNewTab').forEach(elem => elem.disabled = disabled) } if (xPref.get(ChromeLoader.PREF_ENABLE_TABS_MODE)) { diff --git a/native/userchrome/profile/chrome/pwa/utils/common.jsm b/native/userchrome/profile/chrome/pwa/utils/common.sys.mjs similarity index 69% rename from native/userchrome/profile/chrome/pwa/utils/common.jsm rename to native/userchrome/profile/chrome/pwa/utils/common.sys.mjs index 9d6d0c49..42349eaa 100644 --- a/native/userchrome/profile/chrome/pwa/utils/common.jsm +++ b/native/userchrome/profile/chrome/pwa/utils/common.sys.mjs @@ -1,5 +1,3 @@ -const EXPORTED_SYMBOLS = ['sanitizeString']; - /** * Removes all control characters from the string. * @@ -7,6 +5,6 @@ const EXPORTED_SYMBOLS = ['sanitizeString']; * * @returns {string|undefined} */ -function sanitizeString (string) { +export function sanitizeString (string) { return string?.replace(/[\u0000-\u001F\u007F-\u009F]/g, ''); } diff --git a/native/userchrome/profile/chrome/pwa/utils/hookFunction.jsm b/native/userchrome/profile/chrome/pwa/utils/hookFunction.sys.mjs similarity index 95% rename from native/userchrome/profile/chrome/pwa/utils/hookFunction.jsm rename to native/userchrome/profile/chrome/pwa/utils/hookFunction.sys.mjs index bfd53f70..7901caef 100644 --- a/native/userchrome/profile/chrome/pwa/utils/hookFunction.jsm +++ b/native/userchrome/profile/chrome/pwa/utils/hookFunction.sys.mjs @@ -1,5 +1,3 @@ -const EXPORTED_SYMBOLS = ['hookFunction']; - // File is mostly copied from xiaoxiaoflood/firefox-scripts repository on GitHub, licensed under MPL 2.0 // Original source: https://github.com/xiaoxiaoflood/firefox-scripts/blob/69675c7f09e9009b63b1cc239b94c03c5962a9d7/chrome/utils/hookFunction.jsm @@ -19,7 +17,7 @@ const EXPORTED_SYMBOLS = ['hookFunction']; * @returns {function} A function which can be called to safely un-hook the hook * @throws {Error} If the function is not found in context */ -function hookFunction(functionContext, functionName, onBeforeFunction, onAfterFunction) { +export function hookFunction(functionContext, functionName, onBeforeFunction, onAfterFunction) { let originalFunction = functionContext[functionName]; if (!originalFunction) { diff --git a/native/userchrome/profile/chrome/pwa/utils/nativeMessaging.jsm b/native/userchrome/profile/chrome/pwa/utils/nativeMessaging.sys.mjs similarity index 77% rename from native/userchrome/profile/chrome/pwa/utils/nativeMessaging.jsm rename to native/userchrome/profile/chrome/pwa/utils/nativeMessaging.sys.mjs index 1a069f9d..6fbe0329 100644 --- a/native/userchrome/profile/chrome/pwa/utils/nativeMessaging.jsm +++ b/native/userchrome/profile/chrome/pwa/utils/nativeMessaging.sys.mjs @@ -1,10 +1,5 @@ -const EXPORTED_SYMBOLS = ['sendNativeMessage']; - -const { XPCOMUtils } = ChromeUtils.import('resource://gre/modules/XPCOMUtils.jsm'); -XPCOMUtils.defineLazyModuleGetters(this, { - ExtensionCommon: 'resource://gre/modules/ExtensionCommon.jsm', - NativeApp: 'resource://gre/modules/NativeMessaging.jsm', -}); +import { ExtensionCommon } from 'resource://gre/modules/ExtensionCommon.sys.mjs'; +import { NativeApp } from 'resource://gre/modules/NativeMessaging.sys.mjs'; const extensionId = 'firefoxpwa@filips.si'; const nativeAppId = 'firefoxpwa'; @@ -39,7 +34,7 @@ class UserChromeContext extends ExtensionCommon.BaseContext { * @returns {Promise} The response from the native program * @throws {Error} If sending the message failed */ -function sendNativeMessage(message) { +export function sendNativeMessage(message) { const userChromeContext = new UserChromeContext(); const nativeMessage = NativeApp.encodeMessage(userChromeContext, message); const nativeApp = new NativeApp(userChromeContext, nativeAppId); diff --git a/native/userchrome/profile/chrome/pwa/utils/systemIntegration.jsm b/native/userchrome/profile/chrome/pwa/utils/systemIntegration.sys.mjs similarity index 79% rename from native/userchrome/profile/chrome/pwa/utils/systemIntegration.jsm rename to native/userchrome/profile/chrome/pwa/utils/systemIntegration.sys.mjs index 735ecfca..4b14a54e 100644 --- a/native/userchrome/profile/chrome/pwa/utils/systemIntegration.jsm +++ b/native/userchrome/profile/chrome/pwa/utils/systemIntegration.sys.mjs @@ -1,17 +1,17 @@ -const EXPORTED_SYMBOLS = ['buildIconList', 'applySystemIntegration', 'applyDynamicThemeColor']; - -const { XPCOMUtils } = ChromeUtils.import('resource://gre/modules/XPCOMUtils.jsm'); -const Services = globalThis.Services || ChromeUtils.import('resource://gre/modules/Services.jsm').Services; -XPCOMUtils.defineLazyModuleGetters(this, { - AppConstants: 'resource://gre/modules/AppConstants.jsm', - ImageTools: 'resource:///modules/ssb/ImageTools.jsm', - NetUtil: 'resource://gre/modules/NetUtil.jsm', - xPref: 'resource://pwa/utils/xPref.jsm', - sanitizeString: 'resource://pwa/utils/common.jsm', +import { XPCOMUtils } from 'resource://gre/modules/XPCOMUtils.sys.mjs'; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + AppConstants: 'resource://gre/modules/AppConstants.sys.mjs', + NetUtil: 'resource://gre/modules/NetUtil.sys.mjs', + sanitizeString: 'resource://pwa/utils/common.sys.mjs', + xPref: 'resource://pwa/utils/xPref.sys.mjs', }); -XPCOMUtils.defineLazyServiceGetter(this, 'ImgTools', '@mozilla.org/image/tools;1', Ci.imgITools); -XPCOMUtils.defineLazyServiceGetter(this, 'WinUIUtils', '@mozilla.org/windows-ui-utils;1', Ci.nsIWindowsUIUtils); -XPCOMUtils.defineLazyServiceGetter(this, 'WinTaskbar', '@mozilla.org/windows-taskbar;1', Ci.nsIWinTaskbar); + +XPCOMUtils.defineLazyServiceGetter(lazy, 'ImgTools', '@mozilla.org/image/tools;1', Ci.imgITools); +XPCOMUtils.defineLazyServiceGetter(lazy, 'WinTaskbar', '@mozilla.org/windows-taskbar;1', Ci.nsIWinTaskbar); +XPCOMUtils.defineLazyServiceGetter(lazy, 'WinUIUtils', '@mozilla.org/windows-ui-utils;1', Ci.nsIWindowsUIUtils); const INTEGRATION_STATIC_STYLES = 'firefoxpwa-system-integration-styles' const INTEGRATION_DYNAMIC_STYLES = 'firefoxpwa-system-integration-styles-dynamic' @@ -43,12 +43,12 @@ function configureThemeColor (window, styles, colorR, colorG, colorB) { // Set toolbar color to fix wrong window controls on Linux if ( - AppConstants.platform === 'linux' && + lazy.AppConstants.platform === 'linux' && window.document.documentElement.getAttribute('lwtheme') !== 'true' && window.document.location.href.startsWith('chrome://browser/') ) { - if (brightness > 125) xPref.set('browser.theme.toolbar-theme', 1); // Light theme - else xPref.set('browser.theme.toolbar-theme', 0); // Dark theme + if (brightness > 125) lazy.xPref.set('browser.theme.toolbar-theme', 1); // Light theme + else lazy.xPref.set('browser.theme.toolbar-theme', 0); // Dark theme } // Set background and text colors to the titlebar and tabs @@ -58,12 +58,12 @@ function configureThemeColor (window, styles, colorR, colorG, colorB) { function loadImage (uri) { return new Promise((resolve, reject) => { - let channel = NetUtil.newChannel({ + let channel = lazy.NetUtil.newChannel({ uri: uri, loadUsingSystemPrincipal: true, }); - ImgTools.decodeImageFromChannelAsync( + lazy.ImgTools.decodeImageFromChannelAsync( uri, channel, (container, status) => { @@ -81,7 +81,7 @@ function loadImage (uri) { }); } -function buildIconList (icons, purpose = 'any') { +export function buildIconList (icons, purpose = 'any') { let iconList = []; for (let icon of icons) { @@ -124,15 +124,15 @@ async function setWindowIcons (window, site) { }] : site.manifest.icons); let windowIcons = await Promise.all([ - getIcon(iconList, WinUIUtils.systemSmallIconSize), - getIcon(iconList, WinUIUtils.systemLargeIconSize), + getIcon(iconList, lazy.WinUIUtils.systemSmallIconSize), + getIcon(iconList, lazy.WinUIUtils.systemLargeIconSize), ]); if (windowIcons[0] || windowIcons[1]) { // There is a small delay here because otherwise `setWindowIcon` may fail // It shouldn't visually matter because the icon will be set by a shortcut anyway window.setTimeout(() => { - WinUIUtils.setWindowIcon(window, windowIcons[0], windowIcons[1]); + lazy.WinUIUtils.setWindowIcon(window, windowIcons[0], windowIcons[1]); }, 100); } } @@ -144,7 +144,7 @@ function setWindowColors (window, site) { const styles = createOrGetStyles(window, INTEGRATION_STATIC_STYLES); // Set the window background color - if (xPref.get(window.ChromeLoader.PREF_SITES_SET_BACKGROUND_COLOR) && site.manifest.background_color) { + if (lazy.xPref.get(window.ChromeLoader.PREF_SITES_SET_BACKGROUND_COLOR) && site.manifest.background_color) { const backgroundColor = site.manifest.background_color.substring(0, 7); // Set background color to the browser window @@ -163,7 +163,7 @@ function setWindowColors (window, site) { } // Set the theme (titlebar) background and text colors - if (xPref.get(window.ChromeLoader.PREF_SITES_SET_THEME_COLOR) && site.manifest.theme_color) { + if (lazy.xPref.get(window.ChromeLoader.PREF_SITES_SET_THEME_COLOR) && site.manifest.theme_color) { // Set the static theme color from the manifest const colorHex = site.manifest.theme_color.substring(0, 7); const colorRGB = colorHex.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i).slice(1).map(c => parseInt(c, 16)); @@ -190,10 +190,10 @@ function setWindowColors (window, site) { * @param {ChromeWindow&Window} window - Window where integration should be applied * @param {object} site - Site config for which integration should be used */ -function applySystemIntegration (window, site) { +export function applySystemIntegration (window, site) { // Set title only on the main browser chrome window - if (window.location.href === AppConstants.BROWSER_CHROME_URL) { - const name = sanitizeString(site.config.name || site.manifest.name || site.manifest.short_name); + if (window.location.href === lazy.AppConstants.BROWSER_CHROME_URL) { + const name = lazy.sanitizeString(site.config.name || site.manifest.name || site.manifest.short_name); window.document.title = name || new URL(site.manifest.scope).host; } @@ -201,8 +201,8 @@ function applySystemIntegration (window, site) { window.document.documentElement.setAttribute('windowclass', `FFPWA-${site.ulid}`); window.document.documentElement.setAttribute('windowname', `FFPWA-${site.ulid}`); - if (AppConstants.platform === 'win') { - WinTaskbar.setGroupIdForWindow(window, `filips.firefoxpwa.${site.ulid}`); + if (lazy.AppConstants.platform === 'win') { + lazy.WinTaskbar.setGroupIdForWindow(window, `filips.firefoxpwa.${site.ulid}`); setWindowIcons(window, site); } @@ -221,7 +221,7 @@ function applySystemIntegration (window, site) { * @param {ChromeWindow&Window} window - Window where integration should be applied * @param {{r: Number, g: Number, b: Number, a: Number} | null} color - Color that should be applied */ -function applyDynamicThemeColor (window, color) { +export function applyDynamicThemeColor (window, color) { // This will always reset the dynamic styles element const styles = createOrGetStyles(window, INTEGRATION_DYNAMIC_STYLES); diff --git a/native/userchrome/profile/chrome/pwa/utils/xPref.jsm b/native/userchrome/profile/chrome/pwa/utils/xPref.sys.mjs similarity index 93% rename from native/userchrome/profile/chrome/pwa/utils/xPref.jsm rename to native/userchrome/profile/chrome/pwa/utils/xPref.sys.mjs index 0f071088..7d3ff28e 100644 --- a/native/userchrome/profile/chrome/pwa/utils/xPref.jsm +++ b/native/userchrome/profile/chrome/pwa/utils/xPref.sys.mjs @@ -1,11 +1,7 @@ -const EXPORTED_SYMBOLS = ['xPref']; - // File is mostly copied from xiaoxiaoflood/firefox-scripts repository on GitHub, licensed under MPL 2.0 // Original source: https://github.com/xiaoxiaoflood/firefox-scripts/blob/69675c7f09e9009b63b1cc239b94c03c5962a9d7/chrome/utils/xPref.jsm -const Services = globalThis.Services || ChromeUtils.import('resource://gre/modules/Services.jsm').Services; - -const xPref = { +export const xPref = { get: function (prefPath, def = false, valueIfUndefined, setDefault = true) { let sPrefs = def ? Services.prefs.getDefaultBranch(null) : Services.prefs; diff --git a/native/userchrome/runtime/_autoconfig.cfg b/native/userchrome/runtime/_autoconfig.cfg index a1021229..c401c2ea 100644 --- a/native/userchrome/runtime/_autoconfig.cfg +++ b/native/userchrome/runtime/_autoconfig.cfg @@ -7,8 +7,7 @@ cmanifest.append('chrome.manifest'); Components.manager.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(cmanifest); // Import the main boot file -try { ChromeUtils.import('resource://pwa/boot.jsm'); } catch (error) { Cu.reportError(error); } +try { ChromeUtils.importESModule('resource://pwa/boot.sys.mjs'); } catch (error) { Cu.reportError(error); } // Import the optional user boot files try { ChromeUtils.importESModule('resource://user/boot.sys.mjs'); } catch (_) {} -try { ChromeUtils.import('resource://user/boot.jsm'); } catch (_) {}