From 8dd16259287f58f9273002717ec4d27e97127719 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:43:14 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- toolkit/components/extensions/Extension.sys.mjs | 93 ++++++++--- .../components/extensions/ExtensionContent.sys.mjs | 18 ++- toolkit/components/extensions/ExtensionDNR.sys.mjs | 30 +++- .../extensions/ExtensionDNRLimits.sys.mjs | 129 ++++++++++----- .../components/extensions/ExtensionParent.sys.mjs | 69 ++++++++ .../extensions/ExtensionShortcuts.sys.mjs | 32 +++- .../extensions/ExtensionTelemetry.sys.mjs | 4 - .../extensions/ExtensionTestCommon.sys.mjs | 128 ++++++++++++++- toolkit/components/extensions/Schemas.sys.mjs | 21 +++ .../components/extensions/WebNavigation.sys.mjs | 4 +- toolkit/components/extensions/child/ext-storage.js | 28 +--- toolkit/components/extensions/metrics.yaml | 80 ++------- .../extensions/parent/ext-declarativeNetRequest.js | 12 +- .../components/extensions/parent/ext-management.js | 2 + .../components/extensions/parent/ext-runtime.js | 92 +++++++++++ .../schemas/declarative_net_request.json | 24 +++ .../extensions/schemas/geckoProfiler.json | 3 +- .../components/extensions/schemas/management.json | 4 +- .../components/extensions/schemas/manifest.json | 4 +- toolkit/components/extensions/schemas/runtime.json | 127 +++++++++++++++ .../browser_ext_themes_getCurrent_differentExt.js | 4 +- .../test/browser/browser_ext_themes_sidebars.js | 8 +- .../test/marionette/manifest-serviceworker.toml | 1 + .../test/mochitest/mochitest-common.toml | 10 +- .../extensions/test/mochitest/test_ext_all_apis.js | 2 + .../test_ext_contentscript_activeTab.html | 5 +- .../mochitest/test_ext_extension_getViews.html | 108 +++++++++++++ .../mochitest/test_ext_runtime_getContexts.html | 126 +++++++++++++++ ...test_ext_scripting_executeScript_activeTab.html | 5 +- .../mochitest/test_ext_subframes_privileges.html | 67 ++++++-- .../test/xpcshell/test_ext_contentscript_errors.js | 32 +++- .../test_ext_contentscript_permissions_change.js | 1 + .../test/xpcshell/test_ext_cookieBehaviors.js | 118 ++++++++++++-- .../test/xpcshell/test_ext_dnr_dynamic_rules.js | 33 +++- .../xpcshell/test_ext_dnr_regexFilter_limits.js | 44 ++--- .../test/xpcshell/test_ext_dnr_session_rules.js | 33 ++++ .../test/xpcshell/test_ext_dnr_static_rules.js | 22 +-- .../test/xpcshell/test_ext_downloads_urlencoded.js | 2 +- .../test/xpcshell/test_ext_manifest_incognito.js | 39 ++++- .../test/xpcshell/test_ext_permission_warnings.js | 2 +- .../test/xpcshell/test_ext_permissions.js | 144 ++++++++++++----- .../test/xpcshell/test_ext_runtime_getContexts.js | 180 +++++++++++++++++++++ .../xpcshell/test_ext_service_worker_messaging.js | 128 +++++++++++++++ .../test/xpcshell/test_ext_storage_telemetry.js | 45 ++---- .../test_ext_webRequest_eventPage_StreamFilter.js | 6 +- .../extensions/test/xpcshell/xpcshell-common.toml | 2 + .../test/xpcshell/xpcshell-serviceworker.toml | 2 + .../webidl-api/ExtensionEventListener.cpp | 3 +- .../extensions/webidl-api/ExtensionEventListener.h | 6 +- 49 files changed, 1735 insertions(+), 347 deletions(-) create mode 100644 toolkit/components/extensions/test/mochitest/test_ext_extension_getViews.html create mode 100644 toolkit/components/extensions/test/mochitest/test_ext_runtime_getContexts.html create mode 100644 toolkit/components/extensions/test/xpcshell/test_ext_runtime_getContexts.js create mode 100644 toolkit/components/extensions/test/xpcshell/test_ext_service_worker_messaging.js (limited to 'toolkit/components/extensions') diff --git a/toolkit/components/extensions/Extension.sys.mjs b/toolkit/components/extensions/Extension.sys.mjs index 8ab3c30234..56f1320d6c 100644 --- a/toolkit/components/extensions/Extension.sys.mjs +++ b/toolkit/components/extensions/Extension.sys.mjs @@ -161,6 +161,13 @@ XPCOMUtils.defineLazyPreferenceGetter( 30 * 1000 ); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "installIncludesOrigins", + "extensions.originControls.grantByDefault", + false +); + var { GlobalManager, IconDetails, @@ -509,14 +516,36 @@ var ExtensionAddonObserver = { }, onUninstalled(addon) { + this.clearOnUninstall(addon.id); + }, + + /** + * Clears persistent state from the add-on post install. + * + * @param {string} addonId The ID of the addon that has been uninstalled. + */ + clearOnUninstall(addonId) { + const tasks = []; + function addShutdownBlocker(name, promise) { + lazy.AsyncShutdown.profileChangeTeardown.addBlocker(name, promise); + tasks.push({ name, promise }); + } + function notifyUninstallTaskObservers() { + Management.emit("cleanupAfterUninstall", addonId, tasks); + } + // Cleanup anything that is used by non-extension addon types // since only extensions have uuid's. - lazy.ExtensionPermissions.removeAll(addon.id); + addShutdownBlocker( + `Clear ExtensionPermissions for ${addonId}`, + lazy.ExtensionPermissions.removeAll(addonId) + ); - lazy.QuarantinedDomains.clearUserPref(addon.id); + lazy.QuarantinedDomains.clearUserPref(addonId); - let uuid = UUIDMap.get(addon.id, false); + let uuid = UUIDMap.get(addonId, false); if (!uuid) { + notifyUninstallTaskObservers(); return; } @@ -527,8 +556,8 @@ var ExtensionAddonObserver = { ); // Clear all cached resources (e.g. CSS and images); - lazy.AsyncShutdown.profileChangeTeardown.addBlocker( - `Clear cache for ${addon.id}`, + addShutdownBlocker( + `Clear cache for ${addonId}`, clearCacheForExtensionPrincipal(principal, /* clearAll */ true) ); @@ -549,38 +578,38 @@ var ExtensionAddonObserver = { // down because is being uninstalled) and then cleared from // the persisted serviceworker registration on the next // startup. - lazy.AsyncShutdown.profileChangeTeardown.addBlocker( - `Clear ServiceWorkers for ${addon.id}`, + addShutdownBlocker( + `Clear ServiceWorkers for ${addonId}`, lazy.ServiceWorkerCleanUp.removeFromPrincipal(principal) ); // Clear the persisted dynamic content scripts created with the scripting // API (if any). - lazy.AsyncShutdown.profileChangeTeardown.addBlocker( - `Clear scripting store for ${addon.id}`, - lazy.ExtensionScriptingStore.clearOnUninstall(addon.id) + addShutdownBlocker( + `Clear scripting store for ${addonId}`, + lazy.ExtensionScriptingStore.clearOnUninstall(addonId) ); // Clear the DNR API's rules data persisted on disk (if any). - lazy.AsyncShutdown.profileChangeTeardown.addBlocker( - `Clear declarativeNetRequest store for ${addon.id}`, + addShutdownBlocker( + `Clear declarativeNetRequest store for ${addonId}`, lazy.ExtensionDNRStore.clearOnUninstall(uuid) ); if (!Services.prefs.getBoolPref(LEAVE_STORAGE_PREF, false)) { // Clear browser.storage.local backends. - lazy.AsyncShutdown.profileChangeTeardown.addBlocker( - `Clear Extension Storage ${addon.id} (File Backend)`, - lazy.ExtensionStorage.clear(addon.id, { shouldNotifyListeners: false }) + addShutdownBlocker( + `Clear Extension Storage ${addonId} (File Backend)`, + lazy.ExtensionStorage.clear(addonId, { shouldNotifyListeners: false }) ); // Clear browser.storage.sync rust-based backend. // (storage.sync clearOnUninstall will resolve and log an error on the // browser console in case of unexpected failures). if (!lazy.storageSyncOldKintoBackend) { - lazy.AsyncShutdown.profileChangeTeardown.addBlocker( - `Clear Extension StorageSync ${addon.id}`, - lazy.extensionStorageSync.clearOnUninstall(addon.id) + addShutdownBlocker( + `Clear Extension StorageSync ${addonId}`, + lazy.extensionStorageSync.clearOnUninstall(addonId) ); } @@ -595,7 +624,7 @@ var ExtensionAddonObserver = { }); Services.qms.clearStoragesForPrincipal(storagePrincipal); - lazy.ExtensionStorageIDB.clearMigratedExtensionPref(addon.id); + lazy.ExtensionStorageIDB.clearMigratedExtensionPref(addonId); // If LSNG is not enabled, we need to clear localStorage explicitly using // the old API. @@ -632,8 +661,10 @@ var ExtensionAddonObserver = { if (!Services.prefs.getBoolPref(LEAVE_UUID_PREF, false)) { // Clear the entry in the UUID map - UUIDMap.remove(addon.id); + UUIDMap.remove(addonId); } + + notifyUninstallTaskObservers(); }, onPropertyChanged(addon, properties) { @@ -1172,8 +1203,10 @@ export class ExtensionData { * includes the contents of the "permissions" property as well as other * capabilities that are derived from manifest fields that users should * be informed of (e.g., origins where content scripts are injected). + * + * For MV3 extensions with origin controls, this does not include origins. */ - get manifestPermissions() { + getRequiredPermissions() { if (this.type !== "extension") { return null; } @@ -1216,6 +1249,20 @@ export class ExtensionData { return Array.from(origins); } + /** + * Returns additional permissions that extensions is requesting based on its + * manifest. For now, this is host_permissions (and content scripts) in mv3. + */ + getRequestedPermissions() { + if (this.type !== "extension") { + return null; + } + if (this.originControls && lazy.installIncludesOrigins) { + return { permissions: [], origins: this.getManifestOrigins() }; + } + return { permissions: [], origins: [] }; + } + /** * Returns optional permissions from the manifest, including host permissions * if originControls is true. @@ -3705,8 +3752,8 @@ export class Extension extends ExtensionData { if ( this.originControls && - this.manifest.granted_host_permissions && - this.startupReason === "ADDON_INSTALL" + this.startupReason === "ADDON_INSTALL" && + (this.manifest.granted_host_permissions || lazy.installIncludesOrigins) ) { let origins = this.getManifestOrigins(); lazy.ExtensionPermissions.add(this.id, { permissions: [], origins }); diff --git a/toolkit/components/extensions/ExtensionContent.sys.mjs b/toolkit/components/extensions/ExtensionContent.sys.mjs index a2fce282ee..83d17ca84e 100644 --- a/toolkit/components/extensions/ExtensionContent.sys.mjs +++ b/toolkit/components/extensions/ExtensionContent.sys.mjs @@ -475,6 +475,10 @@ class Script { * execution is complete. */ async inject(context, reportExceptions = true) { + // NOTE: Avoid unnecessary use of "await" in this function, because doing + // so can delay script execution beyond the scheduled point. In particular, + // document_start scripts should run "immediately" in most cases. + DocumentManager.lazyInit(); if (this.requiresCleanup) { context.addScript(this); @@ -552,11 +556,19 @@ class Script { let scripts = this.getCompiledScripts(context); if (scripts instanceof Promise) { + // Note: in theory, the following async await could result in script + // execution being scheduled too late. That would be an issue for + // document_start scripts. In practice, this is not a problem because the + // compiled script is cached in the process, and preloading to compile + // starts as soon as the network request for the document has been + // received (see ExtensionPolicyService::CheckRequest). scripts = await scripts; } - // Make sure we've injected any related CSS before we run content scripts. - await cssPromise; + if (cssPromise) { + // Make sure we've injected any related CSS before we run content scripts. + await cssPromise; + } let result; @@ -623,7 +635,7 @@ class Script { p.catch(error => { Services.console.logMessage( new ScriptError( - `${error.name}: ${error.message}`, + error.toString(), error.fileName, null, error.lineNumber, diff --git a/toolkit/components/extensions/ExtensionDNR.sys.mjs b/toolkit/components/extensions/ExtensionDNR.sys.mjs index afc7d30751..6fec9f7639 100644 --- a/toolkit/components/extensions/ExtensionDNR.sys.mjs +++ b/toolkit/components/extensions/ExtensionDNR.sys.mjs @@ -2175,12 +2175,34 @@ class RuleManager { this.#updateAllowAllRequestRules(); } - getSessionRules() { - return this.sessionRules.rules; + /** + * Get the session scoped rules. + * + * @param {Array|null} ruleIds + Optional array of rule IDs to return. By default, all the session + scoped rules are returned. + */ + getSessionRules(ruleIds = null) { + if (!ruleIds) { + return this.sessionRules.rules; + } + + return this.sessionRules.rules.filter(rule => ruleIds.includes(rule.id)); } - getDynamicRules() { - return this.dynamicRules.rules; + /** + * Get the dynamic rules. + * + * @param {Array|null} ruleIds + Optional array of rule IDs to return. By default, all the dynamic + rules are returned. + */ + getDynamicRules(ruleIds = null) { + if (!ruleIds) { + return this.dynamicRules.rules; + } + + return this.dynamicRules.rules.filter(rule => ruleIds.includes(rule.id)); } getRulesCount() { diff --git a/toolkit/components/extensions/ExtensionDNRLimits.sys.mjs b/toolkit/components/extensions/ExtensionDNRLimits.sys.mjs index ac4cd79c44..12919e4ee1 100644 --- a/toolkit/components/extensions/ExtensionDNRLimits.sys.mjs +++ b/toolkit/components/extensions/ExtensionDNRLimits.sys.mjs @@ -2,58 +2,99 @@ * 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/. */ -// TODO(Bug 1803370): consider allowing changing DNR limits through about:config prefs). +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; -/** - * The minimum number of static rules guaranteed to an extension across its - * enabled static rulesets. Any rules above this limit will count towards the - * global static rule limit. - */ -const GUARANTEED_MINIMUM_STATIC_RULES = 30000; +// TODO(Bug 1803370): allow extension to exceed the GUARANTEED_MINIMUM_STATIC_RULES limit. +// +// The maximum number of static rules exceeding the per-extension +// GUARANTEED_MINIMUM_STATIC_RULES across every extensions. +// +// const MAX_GLOBAL_NUMBER_OF_STATIC_RULES = 300000; -/** - * The maximum number of static Rulesets an extension can specify as part of - * the "rule_resources" manifest key. - * - * NOTE: this limit may be increased in the future, see https://github.com/w3c/webextensions/issues/318 - */ -const MAX_NUMBER_OF_STATIC_RULESETS = 50; +const lazy = {}; -/** - * The maximum number of static Rulesets an extension can enable at any one time. - * - * NOTE: this limit may be increased in the future, see https://github.com/w3c/webextensions/issues/318 - */ -const MAX_NUMBER_OF_ENABLED_STATIC_RULESETS = 10; +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "GUARANTEED_MINIMUM_STATIC_RULES", + "extensions.dnr.guaranteed_minimum_static_rules", + 30000 +); -/** - * The maximum number of dynamic and session rules an extension can add. - * NOTE: in the Firefox we are enforcing this limit to the session and dynamic rules count separately, - * instead of enforcing it to the rules count for both combined as the Chrome implementation does. - * - * NOTE: this limit may be increased in the future, see https://github.com/w3c/webextensions/issues/319 - */ -const MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES = 5000; +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "MAX_NUMBER_OF_STATIC_RULESETS", + "extensions.dnr.max_number_of_static_rulesets", + 100 +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "MAX_NUMBER_OF_ENABLED_STATIC_RULESETS", + "extensions.dnr.max_number_of_enabled_static_rulesets", + 20 +); /** - * The maximum number of regular expression rules that an extension can add. - * Session, dynamic and static rules have their own quota. - * - * TODO bug 1821033: Bump limit after optimizing regexFilter. + * NOTE: this limit may be increased in the future, see + * https://github.com/w3c/webextensions/issues/319 */ -const MAX_NUMBER_OF_REGEX_RULES = 1000; +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES", + "extensions.dnr.max_number_of_dynamic_and_session_rules", + 5000 +); -// TODO(Bug 1803370): allow extension to exceed the GUARANTEED_MINIMUM_STATIC_RULES limit. -// -// The maximum number of static rules exceeding the per-extension -// GUARANTEED_MINIMUM_STATIC_RULES across every extensions. -// -// const MAX_GLOBAL_NUMBER_OF_STATIC_RULES = 300000; +// TODO bug 1821033: Bump limit after optimizing regexFilter. +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "MAX_NUMBER_OF_REGEX_RULES", + "extensions.dnr.max_number_of_regex_rules", + 1000 +); export const ExtensionDNRLimits = { - GUARANTEED_MINIMUM_STATIC_RULES, - MAX_NUMBER_OF_STATIC_RULESETS, - MAX_NUMBER_OF_ENABLED_STATIC_RULESETS, - MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES, - MAX_NUMBER_OF_REGEX_RULES, + /** + * The minimum number of static rules guaranteed to an extension across its + * enabled static rulesets. Any rules above this limit will count towards the + * global static rule limit. + */ + get GUARANTEED_MINIMUM_STATIC_RULES() { + return lazy.GUARANTEED_MINIMUM_STATIC_RULES; + }, + + /** + * The maximum number of static Rulesets an extension can specify as part of + * the "rule_resources" manifest key. + */ + get MAX_NUMBER_OF_STATIC_RULESETS() { + return lazy.MAX_NUMBER_OF_STATIC_RULESETS; + }, + + /** + * The maximum number of static Rulesets an extension can enable at any one + * time. + */ + get MAX_NUMBER_OF_ENABLED_STATIC_RULESETS() { + return lazy.MAX_NUMBER_OF_ENABLED_STATIC_RULESETS; + }, + + /** + * The maximum number of dynamic and session rules an extension can add. + * + * NOTE: in the Firefox we are enforcing this limit to the session and + * dynamic rules count separately, instead of enforcing it to the rules count + * for both combined as the Chrome implementation does. + */ + get MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES() { + return lazy.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES; + }, + + /** + * The maximum number of regular expression rules that an extension can add. + * Session, dynamic and static rules have their own quota. + */ + get MAX_NUMBER_OF_REGEX_RULES() { + return lazy.MAX_NUMBER_OF_REGEX_RULES; + }, }; diff --git a/toolkit/components/extensions/ExtensionParent.sys.mjs b/toolkit/components/extensions/ExtensionParent.sys.mjs index f951433713..bbc82092b0 100644 --- a/toolkit/components/extensions/ExtensionParent.sys.mjs +++ b/toolkit/components/extensions/ExtensionParent.sys.mjs @@ -31,6 +31,7 @@ ChromeUtils.defineESModuleGetters(lazy, { PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", Schemas: "resource://gre/modules/Schemas.sys.mjs", getErrorNameForTelemetry: "resource://gre/modules/ExtensionTelemetry.sys.mjs", + WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.sys.mjs", }); XPCOMUtils.defineLazyServiceGetters(lazy, { @@ -508,6 +509,7 @@ class ProxyContextParent extends BaseContext { this.childId = params.childId; this.uri = Services.io.newURI(params.url); + this.browsingContext = browsingContext; this.incognito = params.incognito; @@ -548,6 +550,39 @@ class ProxyContextParent extends BaseContext { return true; } + get frameId() { + if (!this.browsingContext) { + return -1; + } + + return lazy.WebNavigationFrames.getFrameId(this.browsingContext); + } + + get contextType() { + switch (this.viewType) { + case "background_worker": // intentionally fall-through + case "background": + return "BACKGROUND"; + case "popup": + return "POPUP"; + case "sidebar": + return "SIDE_PANEL"; + case "tab": + return "TAB"; + default: + throw new Error( + `Unexpected missing contextType mapping for viewType "${this.viewType}"` + ); + } + } + + toExtensionContext() { + // NOTE: implemented in subclasses that should be listed in runtime.getContexts results + // when they match the ContextFilter, whereas instances from subclasses that don't + // implement it will always be filtered out. + return undefined; + } + trackRunListenerPromise(runListenerPromise) { if ( // The extension was already shutdown. @@ -746,6 +781,34 @@ class ExtensionPageContextParent extends ProxyContextParent { return undefined; } + toExtensionContext() { + const { tabTracker } = apiManager.global; + const { tabId, windowId } = tabTracker.getBrowserDataForContext(this); + const windowContext = this.browsingContext?.currentWindowContext; + return { + // NOTE: the contextId property in the final set of properties returned to + // extensions code is filled in on the ext-runtime.js and it is not to be + // confused with the internal property called contextId. + contextId: undefined, + // NOTE: contextType is a getter that maps the viewType property used + // internally with the value expected for the runtime.ExtensionContext + // contextType property (which should be one of the values part of the + // runtime.ContextType enum). + contextType: this.contextType, + // TODO(Bug 1891478): add documentId. + // TODO(Bug 1890739): consider switching this to use webExposedOriginSerialization when available + // Using nsIPrincipal.originNoSuffix to avoid including the + // private browsing (or contextual identity ones) + documentOrigin: windowContext?.documentPrincipal.originNoSuffix, + documentUrl: windowContext?.documentURI.spec, + incognito: this.incognito, + frameId: this.frameId, + tabId, + windowId, + // TODO: File followup to also add a Firefox-only userContextId? + }; + } + unload() { super.unload(); this.extension.views.delete(this); @@ -775,6 +838,12 @@ class DevToolsExtensionPageContextParent extends ExtensionPageContextParent { this._onResourceAvailable = this._onResourceAvailable.bind(this); } + toExtensionContext() { + // NOTE: devtools extension contexts are currently omitted in getContexts + // results. + return undefined; + } + set devToolsToolbox(toolbox) { if (this._devToolsToolbox) { throw new Error("Cannot set the context DevTools toolbox twice"); diff --git a/toolkit/components/extensions/ExtensionShortcuts.sys.mjs b/toolkit/components/extensions/ExtensionShortcuts.sys.mjs index 17cff67eb9..e800fcc22d 100644 --- a/toolkit/components/extensions/ExtensionShortcuts.sys.mjs +++ b/toolkit/components/extensions/ExtensionShortcuts.sys.mjs @@ -261,7 +261,24 @@ export class ExtensionShortcuts { if (storedCommand && storedCommand.value) { commands.set(name, { ...manifestCommands.get(name) }); + lazy.ExtensionSettingsStore.removeSetting(extension.id, "commands", name); + if ( + name === "_execute_action" && + extension.manifestVersion > 2 && + lazy.ExtensionSettingsStore.hasSetting( + extension.id, + "commands", + "_execute_browser_action" + ) + ) { + lazy.ExtensionSettingsStore.removeSetting( + extension.id, + "commands", + "_execute_browser_action" + ); + } + this.registerKeys(commands); } } @@ -285,6 +302,19 @@ export class ExtensionShortcuts { let savedCommands = await this.loadCommandsFromStorage(extension.id); savedCommands.forEach((update, name) => { let command = commands.get(name); + if ( + name === "_execute_browser_action" && + extension.manifestVersion > 2 + ) { + // Ignore the old _execute_browser_action if there is data stored for + // the new _execute_action command. Otherwise use the stored data for + // `_execute_action` (since we renamed `_execute_browser_action` to + // `_execute_action` in MV3). + command = savedCommands.has("_execute_action") + ? null + : commands.get("_execute_action"); + } + if (command) { // We will only update commands, not add them. Object.assign(command, update); @@ -419,7 +449,7 @@ export class ExtensionShortcuts { } doc.documentElement.appendChild(keyset); if (sidebarKey) { - window.SidebarUI.updateShortcut({ keyId: sidebarKey.id }); + window.SidebarController.updateShortcut({ keyId: sidebarKey.id }); } this.keysetsMap.set(window, keyset); } diff --git a/toolkit/components/extensions/ExtensionTelemetry.sys.mjs b/toolkit/components/extensions/ExtensionTelemetry.sys.mjs index 95b71ce007..58368c01f2 100644 --- a/toolkit/components/extensions/ExtensionTelemetry.sys.mjs +++ b/toolkit/components/extensions/ExtensionTelemetry.sys.mjs @@ -18,8 +18,6 @@ const HISTOGRAMS_IDS = { eventPageIdleResult: "WEBEXT_EVENTPAGE_IDLE_RESULT_COUNT", extensionStartup: "WEBEXT_EXTENSION_STARTUP_MS", pageActionPopupOpen: "WEBEXT_PAGEACTION_POPUP_OPEN_MS", - storageLocalGetJson: "WEBEXT_STORAGE_LOCAL_GET_MS", - storageLocalSetJson: "WEBEXT_STORAGE_LOCAL_SET_MS", storageLocalGetIdb: "WEBEXT_STORAGE_LOCAL_IDB_GET_MS", storageLocalSetIdb: "WEBEXT_STORAGE_LOCAL_IDB_SET_MS", }; @@ -33,8 +31,6 @@ const GLEAN_METRICS_TYPES = { eventPageIdleResult: "labeled_counter", extensionStartup: "timing_distribution", pageActionPopupOpen: "timing_distribution", - storageLocalGetJson: "timing_distribution", - storageLocalSetJson: "timing_distribution", storageLocalGetIdb: "timing_distribution", storageLocalSetIdb: "timing_distribution", }; diff --git a/toolkit/components/extensions/ExtensionTestCommon.sys.mjs b/toolkit/components/extensions/ExtensionTestCommon.sys.mjs index 701a85d97b..6a3b068dd2 100644 --- a/toolkit/components/extensions/ExtensionTestCommon.sys.mjs +++ b/toolkit/components/extensions/ExtensionTestCommon.sys.mjs @@ -17,10 +17,13 @@ ChromeUtils.defineESModuleGetters(lazy, { AddonManager: "resource://gre/modules/AddonManager.sys.mjs", Assert: "resource://testing-common/Assert.sys.mjs", Extension: "resource://gre/modules/Extension.sys.mjs", + ExtensionAddonObserver: "resource://gre/modules/Extension.sys.mjs", ExtensionData: "resource://gre/modules/Extension.sys.mjs", ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs", ExtensionPermissions: "resource://gre/modules/ExtensionPermissions.sys.mjs", FileUtils: "resource://gre/modules/FileUtils.sys.mjs", + clearInterval: "resource://gre/modules/Timer.sys.mjs", + setInterval: "resource://gre/modules/Timer.sys.mjs", }); ChromeUtils.defineLazyGetter( @@ -36,6 +39,110 @@ const { flushJarCache } = ExtensionUtils; const { instanceOf } = ExtensionCommon; +// The tasks received here have already been registered as shutdown blockers +// (with AsyncShutdown). That means that in reality, if these tasks take +// long, that they affect Firefox's ability to quit, with a warning after 10 +// seconds and a forced quit without waiting for shutdown after 60 seconds +// (or whatever is in the toolkit.asyncshutdown.crash_timeout pref). +// +// To help with detecting unreasonable slowness in tests, we log when these +// tasks are taking too long at shutdown. +const MS_SLOW_TASK_DURATION = 2000; + +/** + * ExtensionUninstallTracker should be instantiated before extension shutdown, + * and can be used to await the completion of the uninstall and post-uninstall + * cleanup logic. Log messages are printed to aid debugging if the cleanup is + * observed to be slow (i.e. taking longer than MS_SLOW_TASK_DURATION). + * + * // Usage: + * let uninstallTracker = new ExtensionUninstallTracker(extension.id); + * await extension.shutdown(); + * await uninstallTracker.waitForUninstallCleanupDone(); + */ +class ExtensionUninstallTracker { + #resolveOnCleanupDone; + constructor(addonId) { + this.id = addonId; + + this.remainingTasks = new Set(); + // The uninstall/cleanup observer needs to be registered early, to not miss + // the notifications. + this._uninstallPromise = this.#promiseUninstallComplete(); + this._cleanupPromise = this.#promiseCleanupAfterUninstall(); + } + + async waitForUninstallCleanupDone() { + // Call #addTask() now instead of in the constructor, so that we only track + // the time after extension.shutdown() has completed. + this.#addTask("Awaiting uninstall-complete", this._uninstallPromise); + this.#addTask("Awaiting cleanupAfterUninstall", this._cleanupPromise); + // For debugging purposes, if shutdown is slow, regularly print a message + // with the remaining tasks. + let timer = lazy.setInterval( + () => this.#checkRemainingTasks(), + 2 * MS_SLOW_TASK_DURATION + ); + await new Promise(resolve => { + this.#resolveOnCleanupDone = resolve; + this.#checkRemainingTasks(); + }); + lazy.clearInterval(timer); + } + + #addTask(name, promise) { + const task = { name, promise, timeStart: Date.now() }; + this.remainingTasks.add(task); + promise.finally(() => { + this.remainingTasks.delete(task); + this.#checkRemainingTasks(); + }); + } + + #checkRemainingTasks() { + for (let task of this.remainingTasks) { + const timeSinceStart = Date.now() - task.timeStart; + if (timeSinceStart > MS_SLOW_TASK_DURATION) { + dump( + `WARNING: Detected slow post-uninstall task: ${timeSinceStart}ms for extension ${this.id}: ${task.name}\n` + ); + } + } + if (this.remainingTasks.size === 0) { + this.#resolveOnCleanupDone?.(); + } + } + + #promiseUninstallComplete() { + return new Promise(resolve => { + const onUninstallComplete = (eventName, { id }) => { + if (id === this.id) { + lazy.apiManager.off("uninstall-complete", onUninstallComplete); + resolve(); + } + }; + lazy.apiManager.on("uninstall-complete", onUninstallComplete); + }); + } + + #promiseCleanupAfterUninstall() { + return new Promise(resolve => { + const onCleanupAfterUninstall = (eventName, id, tasks) => { + if (id === this.id) { + lazy.apiManager.off("cleanupAfterUninstall", onCleanupAfterUninstall); + for (const task of tasks) { + if (task.promise) { + this.#addTask(task.name, task.promise); + } + } + resolve(); + } + }; + lazy.apiManager.on("cleanupAfterUninstall", onCleanupAfterUninstall); + }); + } +} + /** * A skeleton Extension-like object, used for testing, which installs an * add-on via the add-on manager when startup() is called, and @@ -75,7 +182,6 @@ export class MockExtension { this._extension = null; this._extensionPromise = promiseEvent("startup"); this._readyPromise = promiseEvent("ready"); - this._uninstallPromise = promiseEvent("uninstall-complete"); } maybeSetID(uri, id) { @@ -674,4 +780,24 @@ export var ExtensionTestCommon = class ExtensionTestCommon { data.startupReason ?? "ADDON_INSTALL" ); } + + /** + * Unload an extension and await completion of post-uninstall cleanup tasks. + * + * @param {Extension|MockExtension} extension + */ + static async unloadTestExtension(extension) { + const { id } = extension; + const uninstallTracker = new ExtensionUninstallTracker(id); + await extension.shutdown(); + if (extension instanceof lazy.Extension) { + // AddonManager-managed add-ons run additional (cleanup) tasks after + // shutting down an extension. Do the same even without useAddonManager. + lazy.Extension.getBootstrapScope().uninstall({ id }); + + // Data removal by ExtensionAddonObserver.onUninstalled: + lazy.ExtensionAddonObserver.clearOnUninstall(id); + } + await uninstallTracker.waitForUninstallCleanupDone(); + } }; diff --git a/toolkit/components/extensions/Schemas.sys.mjs b/toolkit/components/extensions/Schemas.sys.mjs index b107036355..e95cbd6bb5 100644 --- a/toolkit/components/extensions/Schemas.sys.mjs +++ b/toolkit/components/extensions/Schemas.sys.mjs @@ -316,6 +316,27 @@ const POSTPROCESSORS = { } return value; }, + + incognitoSplitUnsupportedAndFallback(value, context) { + if (value === "split") { + // incognito:split has not been implemented (bug 1380812). There are two + // alternatives: "spanning" and "not_allowed". + // + // "incognito":"split" is required by Chrome when extensions want to load + // any extension page in a tab in Chrome. In Firefox that is not required, + // so extensions could replace "split" with "spanning". + // Another (poorly documented) effect of "incognito":"split" is separation + // of some state between some extension APIs. Because this can in theory + // result in unwanted mixing of state between private and non-private + // browsing, we fall back to "not_allowed", which prevents the user from + // enabling the extension in private browsing windows. + value = "not_allowed"; + context.logWarning( + `incognito "split" is unsupported. Falling back to incognito "${value}".` + ); + } + return value; + }, }; // Parses a regular expression, with support for the Python extended diff --git a/toolkit/components/extensions/WebNavigation.sys.mjs b/toolkit/components/extensions/WebNavigation.sys.mjs index c33a45db81..98d537d85c 100644 --- a/toolkit/components/extensions/WebNavigation.sys.mjs +++ b/toolkit/components/extensions/WebNavigation.sys.mjs @@ -8,6 +8,7 @@ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { + BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs", ClickHandlerParent: "resource:///actors/ClickHandlerParent.sys.mjs", UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs", @@ -249,8 +250,7 @@ export var WebNavigationManager = { onContentClick(target, data) { // We are interested only on clicks to links which are not "add to bookmark" commands if (data.href && !data.bookmark) { - let ownerWin = target.ownerGlobal; - let where = ownerWin.whereToOpenLink(data); + let where = lazy.BrowserUtils.whereToOpenLink(data); if (where == "current") { this.setRecentTabTransitionData({ link: true }); } diff --git a/toolkit/components/extensions/child/ext-storage.js b/toolkit/components/extensions/child/ext-storage.js index 3d71a1cd60..c6c67a8736 100644 --- a/toolkit/components/extensions/child/ext-storage.js +++ b/toolkit/components/extensions/child/ext-storage.js @@ -28,28 +28,16 @@ this.storage = class extends ExtensionAPI { getLocalFileBackend(context, { deserialize, serialize }) { return { get(keys) { - return measureOp( - ExtensionTelemetry.storageLocalGetJson, - context.extension, - () => { - return context.childManager - .callParentAsyncFunction("storage.local.JSONFileBackend.get", [ - serialize(keys), - ]) - .then(deserialize); - } - ); + return context.childManager + .callParentAsyncFunction("storage.local.JSONFileBackend.get", [ + serialize(keys), + ]) + .then(deserialize); }, set(items) { - return measureOp( - ExtensionTelemetry.storageLocalSetJson, - context.extension, - () => { - return context.childManager.callParentAsyncFunction( - "storage.local.JSONFileBackend.set", - [serialize(items)] - ); - } + return context.childManager.callParentAsyncFunction( + "storage.local.JSONFileBackend.set", + [serialize(items)] ); }, remove(keys) { diff --git a/toolkit/components/extensions/metrics.yaml b/toolkit/components/extensions/metrics.yaml index 6d36dae614..7993832a64 100644 --- a/toolkit/components/extensions/metrics.yaml +++ b/toolkit/components/extensions/metrics.yaml @@ -58,6 +58,8 @@ extensions: data_sensitivity: - technical telemetry_mirror: EXTENSIONS_STARTUPCACHE_LOAD_TIME + no_lint: + - GIFFT_NON_PING_LIFETIME startup_cache_read_errors: type: labeled_counter @@ -75,6 +77,8 @@ extensions: data_sensitivity: - technical telemetry_mirror: EXTENSIONS_STARTUPCACHE_READ_ERRORS + no_lint: + - GIFFT_NON_PING_LIFETIME startup_cache_write_bytelength: type: quantity @@ -92,6 +96,8 @@ extensions: data_sensitivity: - technical telemetry_mirror: EXTENSIONS_STARTUPCACHE_WRITE_BYTELENGTH + no_lint: + - GIFFT_NON_PING_LIFETIME process_event: type: labeled_counter @@ -130,7 +136,6 @@ extensions.apis.dnr: expires: 138 description: | Amount of data read from the DNR startup cache file. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -149,7 +154,6 @@ extensions.apis.dnr: expires: 138 description: | Amount of time it takes to read data into the DNR startup cache file. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -168,7 +172,6 @@ extensions.apis.dnr: expires: 138 description: | Amount of data written to the DNR startup cache file. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -187,7 +190,6 @@ extensions.apis.dnr: expires: 138 description: | Amount of time it takes to write data into the DNR startup cache file. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -221,6 +223,8 @@ extensions.apis.dnr: - hit - miss telemetry_mirror: EXTENSIONS_APIS_DNR_STARTUP_CACHE_ENTRIES + no_lint: + - GIFFT_NON_PING_LIFETIME validate_rules_time: type: timing_distribution @@ -229,7 +233,6 @@ extensions.apis.dnr: description: | Amount of time it takes to validate DNR rules of individual ruleset when dynamic or static rulesets have been loaded from disk. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -248,7 +251,6 @@ extensions.apis.dnr: expires: 138 description: | Amount of time it takes to evaluate DNR rules for one network request. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -378,6 +380,8 @@ extensions.quarantined_domains: data_sensitivity: - technical telemetry_mirror: EXTENSIONS_QUARANTINEDDOMAINS_LISTSIZE + no_lint: + - GIFFT_NON_PING_LIFETIME listhash: type: string @@ -396,6 +400,8 @@ extensions.quarantined_domains: data_sensitivity: - technical telemetry_mirror: EXTENSIONS_QUARANTINEDDOMAINS_LISTHASH + no_lint: + - GIFFT_NON_PING_LIFETIME remotehash: type: string @@ -417,6 +423,8 @@ extensions.quarantined_domains: data_sensitivity: - technical telemetry_mirror: EXTENSIONS_QUARANTINEDDOMAINS_REMOTEHASH + no_lint: + - GIFFT_NON_PING_LIFETIME extensions.counters: @@ -488,7 +496,6 @@ extensions.timing: description: | Amount of time it takes to load a WebExtensions background page, from when the build function is called to when the page has finished processing the onload event. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -506,7 +513,6 @@ extensions.timing: expires: never description: | Amount of time it takes for a BrowserAction popup to open. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -532,7 +538,6 @@ extensions.timing: expires: never description: | Amount of time it takes for content scripts from a WebExtension to be injected into a window. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -555,7 +560,6 @@ extensions.timing: description: | Amount of time (keyed by addon id) that an event page has been running before being suspended, or the entire addon shutdown. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -573,7 +577,6 @@ extensions.timing: description: | Amount of time it takes for a WebExtension to start up, from when the startup function is called to when the startup promise resolves. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -591,7 +594,6 @@ extensions.timing: expires: never description: | Amount of time it takes for a PageAction popup to open. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -611,65 +613,12 @@ extensions.timing: data_sensitivity: - technical - storage_local_get_json: - type: timing_distribution - time_unit: millisecond - expires: 128 - description: | - Amount of time it takes to perform a get via storage.local using the JSONFile backend. - lifetime: application - notification_emails: - - addons-dev-internal@mozilla.com - bugs: - - https://bugzilla.mozilla.org/1371398 - - https://bugzilla.mozilla.org/1513556 - - https://bugzilla.mozilla.org/1578225 - - https://bugzilla.mozilla.org/1623315 - - https://bugzilla.mozilla.org/1666980 - - https://bugzilla.mozilla.org/1706839 - - https://bugzilla.mozilla.org/1745271 - - https://bugzilla.mozilla.org/1777402 - - https://bugzilla.mozilla.org/1811155 - - https://bugzilla.mozilla.org/1861303 - - https://bugzilla.mozilla.org/1820158 - data_reviews: - - https://bugzilla.mozilla.org/show_bug.cgi?id=1820158#c8 - data_sensitivity: - - technical - - storage_local_set_json: - type: timing_distribution - time_unit: millisecond - expires: 128 - description: | - Amount of time it takes to perform a set via storage.local using the JSONFile backend. - lifetime: application - notification_emails: - - addons-dev-internal@mozilla.com - bugs: - - https://bugzilla.mozilla.org/1371398 - - https://bugzilla.mozilla.org/1513556 - - https://bugzilla.mozilla.org/1578225 - - https://bugzilla.mozilla.org/1623315 - - https://bugzilla.mozilla.org/1666980 - - https://bugzilla.mozilla.org/1706839 - - https://bugzilla.mozilla.org/1745271 - - https://bugzilla.mozilla.org/1777402 - - https://bugzilla.mozilla.org/1811155 - - https://bugzilla.mozilla.org/1861303 - - https://bugzilla.mozilla.org/1820158 - data_reviews: - - https://bugzilla.mozilla.org/show_bug.cgi?id=1820158#c8 - data_sensitivity: - - technical - storage_local_get_idb: type: timing_distribution time_unit: millisecond expires: never description: | Amount of time it takes to perform a get via storage.local using the IndexedDB backend. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: @@ -695,7 +644,6 @@ extensions.timing: expires: never description: | Amount of time it takes to perform a set via storage.local using the Indexed backend. - lifetime: application notification_emails: - addons-dev-internal@mozilla.com bugs: diff --git a/toolkit/components/extensions/parent/ext-declarativeNetRequest.js b/toolkit/components/extensions/parent/ext-declarativeNetRequest.js index fab1d941a4..2ca66cd1d4 100644 --- a/toolkit/components/extensions/parent/ext-declarativeNetRequest.js +++ b/toolkit/components/extensions/parent/ext-declarativeNetRequest.js @@ -92,17 +92,21 @@ this.declarativeNetRequest = class extends ExtensionAPI { }); }, - async getDynamicRules() { + async getDynamicRules(details) { await ExtensionDNR.ensureInitialized(extension); - return ExtensionDNR.getRuleManager(extension).getDynamicRules(); + return ExtensionDNR.getRuleManager(extension).getDynamicRules( + details?.ruleIds + ); }, - getSessionRules() { + getSessionRules(details) { // ruleManager.getSessionRules() returns an array of Rule instances. // When these are structurally cloned (to send them to the child), // the enumerable public fields of the class instances are copied to // plain objects, as desired. - return ExtensionDNR.getRuleManager(extension).getSessionRules(); + return ExtensionDNR.getRuleManager(extension).getSessionRules( + details?.ruleIds + ); }, isRegexSupported(regexOptions) { diff --git a/toolkit/components/extensions/parent/ext-management.js b/toolkit/components/extensions/parent/ext-management.js index fb28eca92c..67c254de99 100644 --- a/toolkit/components/extensions/parent/ext-management.js +++ b/toolkit/components/extensions/parent/ext-management.js @@ -42,6 +42,8 @@ const installType = addon => { return "sideload"; } else if (addon.isSystem) { return "other"; + } else if (addon.isInstalledByEnterprisePolicy) { + return "admin"; } return "normal"; }; diff --git a/toolkit/components/extensions/parent/ext-runtime.js b/toolkit/components/extensions/parent/ext-runtime.js index 3f9c0f8857..ff7aae6e5f 100644 --- a/toolkit/components/extensions/parent/ext-runtime.js +++ b/toolkit/components/extensions/parent/ext-runtime.js @@ -12,6 +12,8 @@ var { ExtensionParent } = ChromeUtils.importESModule( "resource://gre/modules/ExtensionParent.sys.mjs" ); +var { DefaultWeakMap } = ExtensionUtils; + ChromeUtils.defineESModuleGetters(this, { AddonManager: "resource://gre/modules/AddonManager.sys.mjs", AddonManagerPrivate: "resource://gre/modules/AddonManager.sys.mjs", @@ -126,10 +128,100 @@ this.runtime = class extends ExtensionAPIPersistent { }, }; + // Although we have an internal context.contextId field, we generate a new one here because + // the internal type is an integer and the public field a (UUID) string. + // TODO: Move the implementation elsewhere when contextId is used anywhere other than + // runtime.getContexts. See https://bugzilla.mozilla.org/show_bug.cgi?id=1628178#c5 + // + // Map + #contextUUIDMap = new DefaultWeakMap(_context => + String(Services.uuid.generateUUID()).slice(1, -1) + ); + + getContexts(filter) { + const { extension } = this; + const { proxyContexts } = ExtensionParent.ParentAPIManager; + const results = []; + for (const proxyContext of proxyContexts.values()) { + if (proxyContext.extension !== extension) { + continue; + } + let ctx; + try { + ctx = proxyContext.toExtensionContext(); + } catch (err) { + // toExtensionContext may throw if the contextType getter + // raised an exception due to an internal viewType has + // not be mapped with a contextType value. + // + // When running in DEBUG builds we reject the getContexts + // call, while in non DEBUG build we just omit the result + // and log a warning in the Browser Console. + if (AppConstants.DEBUG) { + throw err; + } else { + Cu.reportError(err); + } + } + + if (this.matchContextFilter(filter, ctx)) { + results.push({ + ...ctx, + contextId: this.#contextUUIDMap.get(proxyContext), + }); + } + } + return results; + } + + matchContextFilter(filter, ctx) { + if (!ctx) { + // Filter out subclasses that do not return any ExtensionContext details + // from their toExtensionContext method. + return false; + } + if (filter.contextIds && !filter.contextIds.includes(ctx.contextId)) { + return false; + } + if (filter.contextTypes && !filter.contextTypes.includes(ctx.contextType)) { + return false; + } + if (filter.documentIds && !filter.documentIds.includes(ctx.documentId)) { + return false; + } + if ( + filter.documentOrigins && + !filter.documentOrigins.includes(ctx.documentOrigin) + ) { + return false; + } + if (filter.documentUrls && !filter.documentUrls.includes(ctx.documentUrl)) { + return false; + } + if (filter.frameIds && !filter.frameIds.includes(ctx.frameId)) { + return false; + } + if (filter.tabIds && !filter.tabIds.includes(ctx.tabId)) { + return false; + } + if (filter.windowIds && !filter.windowIds.includes(ctx.windowId)) { + return false; + } + if ( + typeof filter.incognito === "boolean" && + ctx.incognito !== filter.incognito + ) { + return false; + } + return true; + } + getAPI(context) { let { extension } = context; return { runtime: { + getContexts: filter => this.getContexts(filter), + // onStartup is special-cased in ext-backgroundPages to cause // an immediate startup. We do not prime onStartup. onStartup: new EventManager({ diff --git a/toolkit/components/extensions/schemas/declarative_net_request.json b/toolkit/components/extensions/schemas/declarative_net_request.json index e7bdc02041..5c91fc1e35 100644 --- a/toolkit/components/extensions/schemas/declarative_net_request.json +++ b/toolkit/components/extensions/schemas/declarative_net_request.json @@ -439,6 +439,18 @@ } } } + }, + { + "id": "GetRulesFilter", + "type": "object", + "properties": { + "ruleIds": { + "type": "array", + "optional": true, + "description": "If specified, only rules with matching IDs are included.", + "items": { "type": "integer" } + } + } } ], "functions": [ @@ -588,6 +600,12 @@ "description": "Returns the current set of dynamic rules for the extension.", "async": "callback", "parameters": [ + { + "name": "filter", + "$ref": "GetRulesFilter", + "optional": true, + "description": "An object to filter the set of dynamic rules for the extension." + }, { "name": "callback", "type": "function", @@ -609,6 +627,12 @@ "description": "Returns the current set of session scoped rules for the extension.", "async": "callback", "parameters": [ + { + "name": "filter", + "$ref": "GetRulesFilter", + "optional": true, + "description": "An object to filter the set of session scoped rules for the extension." + }, { "name": "callback", "type": "function", diff --git a/toolkit/components/extensions/schemas/geckoProfiler.json b/toolkit/components/extensions/schemas/geckoProfiler.json index f2c6bec4cd..6da180fd49 100644 --- a/toolkit/components/extensions/schemas/geckoProfiler.json +++ b/toolkit/components/extensions/schemas/geckoProfiler.json @@ -47,7 +47,8 @@ "power", "responsiveness", "cpufreq", - "bandwidth" + "bandwidth", + "memory" ] }, { diff --git a/toolkit/components/extensions/schemas/management.json b/toolkit/components/extensions/schemas/management.json index b75c0e2b4c..069f3b030a 100644 --- a/toolkit/components/extensions/schemas/management.json +++ b/toolkit/components/extensions/schemas/management.json @@ -46,9 +46,9 @@ }, { "id": "ExtensionInstallType", - "description": "How the extension was installed. One of
development: The extension was loaded unpacked in developer mode,
normal: The extension was installed normally via an .xpi file,
sideload: The extension was installed by other software on the machine,
other: The extension was installed by other means.", + "description": "How the extension was installed. One of
development: The extension was loaded unpacked in developer mode,
normal: The extension was installed normally via an .xpi file,
sideload: The extension was installed by other software on the machine,
admin: The extension was installed by policy,
other: The extension was installed by other means.", "type": "string", - "enum": ["development", "normal", "sideload", "other"] + "enum": ["development", "normal", "sideload", "admin", "other"] }, { "id": "ExtensionInfo", diff --git a/toolkit/components/extensions/schemas/manifest.json b/toolkit/components/extensions/schemas/manifest.json index 384b168e39..9701eda25a 100644 --- a/toolkit/components/extensions/schemas/manifest.json +++ b/toolkit/components/extensions/schemas/manifest.json @@ -121,7 +121,9 @@ "incognito": { "type": "string", - "enum": ["not_allowed", "spanning"], + "description": "The 'split' value is not supported.", + "enum": ["not_allowed", "spanning", "split"], + "postprocess": "incognitoSplitUnsupportedAndFallback", "default": "spanning", "optional": true }, diff --git a/toolkit/components/extensions/schemas/runtime.json b/toolkit/components/extensions/schemas/runtime.json index 75ff341393..d2e21706e4 100644 --- a/toolkit/components/extensions/schemas/runtime.json +++ b/toolkit/components/extensions/schemas/runtime.json @@ -18,6 +18,108 @@ "allowedContexts": ["content", "devtools"], "description": "Use the browser.runtime API to retrieve the background page, return details about the manifest, and listen for and respond to events in the app or extension lifecycle. You can also use this API to convert the relative path of URLs to fully-qualified URLs.", "types": [ + { + "id": "ContextFilter", + "min_manifest_version": 3, + "type": "object", + "description": "A filter to match against existing extension context. Matching contexts must match all specified filters.", + "properties": { + "contextIds": { + "optional": true, + "type": "array", + "items": { "type": "string" } + }, + "contextTypes": { + "optional": true, + "type": "array", + "items": { "$ref": "ContextType" } + }, + "documentIds": { + "optional": true, + "type": "array", + "items": { "type": "string" } + }, + "documentOrigins": { + "optional": true, + "type": "array", + "items": { "type": "string" } + }, + "documentUrls": { + "optional": true, + "type": "array", + "items": { "type": "string" } + }, + "frameIds": { + "optional": true, + "type": "array", + "items": { "type": "integer" } + }, + "tabIds": { + "optional": true, + "type": "array", + "items": { "type": "integer" } + }, + "windowIds": { + "optional": true, + "type": "array", + "items": { "type": "integer" } + }, + "incognito": { "optional": true, "type": "boolean" } + } + }, + { + "id": "ContextType", + "type": "string", + "enum": ["BACKGROUND", "POPUP", "SIDE_PANEL", "TAB"], + "description": "The type of extension view." + }, + { + "id": "ExtensionContext", + "type": "object", + "description": "A context hosting extension content", + "properties": { + "contextId": { + "type": "string", + "description": "An unique identifier associated to this context" + }, + "contextType": { + "$ref": "ContextType", + "description": "The type of the context" + }, + "documentId": { + "type": "string", + "unsupported": true, + "optional": true, + "description": "An UUID for the document associated with this context, or undefined if it is not hosted in a document" + }, + "documentOrigin": { + "type": "string", + "optional": true, + "description": "The origin of the document associated with this context, or undefined if it is not hosted in a document" + }, + "documentUrl": { + "type": "string", + "optional": true, + "description": "The URL of the document associated with this context, or undefined if it is not hosted in a document" + }, + "incognito": { + "type": "boolean", + "description": "Whether the context is associated with an private browsing context." + }, + "frameId": { + "type": "integer", + "description": "The frame ID for this context, or -1 if it is not hosted in a frame." + }, + "tabId": { + "type": "integer", + "description": "The tab ID for this context, or -1 if it is not hosted in a tab." + }, + "windowId": { + "type": "integer", + "description": "The window ID for this context, or -1 if it is not hosted in a window." + } + } + }, { "id": "Port", "type": "object", @@ -218,6 +320,31 @@ } ] }, + { + "name": "getContexts", + "type": "function", + "description": "Fetches information about active contexts associated with this extension", + "async": "callback", + "parameters": [ + { + "name": "filter", + "$ref": "ContextFilter", + "description": "A filter to find matching context." + }, + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "contexts", + "type": "array", + "items": { "$ref": "ExtensionContext" }, + "description": "The matching contexts, if any." + } + ] + } + ] + }, { "name": "openOptionsPage", "type": "function", diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_getCurrent_differentExt.js b/toolkit/components/extensions/test/browser/browser_ext_themes_getCurrent_differentExt.js index 587c5d4efe..86d794669d 100644 --- a/toolkit/components/extensions/test/browser/browser_ext_themes_getCurrent_differentExt.js +++ b/toolkit/components/extensions/test/browser/browser_ext_themes_getCurrent_differentExt.js @@ -139,7 +139,9 @@ add_task(async function test_getcurrent_privateBrowsing() { "resource://gre/modules/ExtensionCommon.sys.mjs" ); const { makeWidgetId } = ExtensionCommon; - privateWin.SidebarUI.show(`${makeWidgetId(extension.id)}-sidebar-action`); + privateWin.SidebarController.show( + `${makeWidgetId(extension.id)}-sidebar-action` + ); let imageLoaded = extension.awaitMessage("theme-image"); Assert.deepEqual(await imageLoaded, { success: true }, "theme image loaded"); diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_sidebars.js b/toolkit/components/extensions/test/browser/browser_ext_themes_sidebars.js index 0d2e69716d..492fe3a263 100644 --- a/toolkit/components/extensions/test/browser/browser_ext_themes_sidebars.js +++ b/toolkit/components/extensions/test/browser/browser_ext_themes_sidebars.js @@ -17,7 +17,7 @@ async function test_sidebar_theme(theme, isBrightText) { const sidebarBox = document.getElementById("sidebar-box"); const browserRoot = document.documentElement; - const content = SidebarUI.browser.contentWindow; + const content = SidebarController.browser.contentWindow; const root = content.document.documentElement; ok( @@ -184,7 +184,7 @@ add_task(async function test_support_sidebar_colors() { for (let command of ["viewBookmarksSidebar", "viewHistorySidebar"]) { info("Executing command: " + command); - await SidebarUI.show(command); + await SidebarController.show(command); await test_sidebar_theme( { @@ -263,7 +263,7 @@ add_task(async function test_support_sidebar_border_color() { "Sidebar splitter should be colored properly" ); - SidebarUI.reversePosition(); + SidebarController.reversePosition(); is( sidebarSplitterCS.borderInlineStartColor, @@ -271,7 +271,7 @@ add_task(async function test_support_sidebar_border_color() { "Sidebar splitter should be colored properly after switching sides" ); - SidebarUI.reversePosition(); + SidebarController.reversePosition(); } await extension.unload(); diff --git a/toolkit/components/extensions/test/marionette/manifest-serviceworker.toml b/toolkit/components/extensions/test/marionette/manifest-serviceworker.toml index c8035f80c2..04bdc4e17e 100644 --- a/toolkit/components/extensions/test/marionette/manifest-serviceworker.toml +++ b/toolkit/components/extensions/test/marionette/manifest-serviceworker.toml @@ -1,4 +1,5 @@ [DEFAULT] +skip-if = ["!nightly_build"] # MOZ_WEBEXT_WEBIDL_ENABLED is only defined on nightly ["test_extension_serviceworkers_purged_on_pref_disabled.py"] ["test_temporary_extension_serviceworkers_not_persisted.py"] diff --git a/toolkit/components/extensions/test/mochitest/mochitest-common.toml b/toolkit/components/extensions/test/mochitest/mochitest-common.toml index 782069a79c..f49fb131c5 100644 --- a/toolkit/components/extensions/test/mochitest/mochitest-common.toml +++ b/toolkit/components/extensions/test/mochitest/mochitest-common.toml @@ -268,6 +268,8 @@ skip-if = [ "http2", ] +["test_ext_extension_getViews.html"] + ["test_ext_extension_iframe_messaging.html"] skip-if = [ "http3", @@ -350,6 +352,8 @@ skip-if = [ "http2", ] +["test_ext_runtime_getContexts.html"] + ["test_ext_script_filenames.html"] ["test_ext_scripting_contentScripts.html"] @@ -443,12 +447,6 @@ skip-if = [ ] ["test_ext_subframes_privileges.html"] -skip-if = [ - "os == 'android'", # Bug 1845918 - "verify", # Bug 1489771 - "http3", - "http2", -] ["test_ext_tabs_captureTab.html"] skip-if = [ diff --git a/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js b/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js index 95ac9af50d..8dec0c6ae5 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js +++ b/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js @@ -89,8 +89,10 @@ let expectedBackgroundApis = [ "permissions.remove", "permissions.onAdded", "permissions.onRemoved", + "runtime.ContextType", "runtime.getBackgroundPage", "runtime.getBrowserInfo", + "runtime.getContexts", "runtime.getPlatformInfo", "runtime.onConnectExternal", "runtime.onInstalled", diff --git a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html index 076c177dfa..28bbe3b253 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html @@ -14,7 +14,10 @@ add_task(async function setup() { await SpecialPowers.pushPrefEnv({ - set: [["extensions.manifestV3.enabled", true]], + set: [ + ["extensions.manifestV3.enabled", true], + ["extensions.originControls.grantByDefault", false], + ], }); }); diff --git a/toolkit/components/extensions/test/mochitest/test_ext_extension_getViews.html b/toolkit/components/extensions/test/mochitest/test_ext_extension_getViews.html new file mode 100644 index 0000000000..9309d45cdf --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/test_ext_extension_getViews.html @@ -0,0 +1,108 @@ + + + + extension.getViews Test + + + + + + + + + + diff --git a/toolkit/components/extensions/test/mochitest/test_ext_runtime_getContexts.html b/toolkit/components/extensions/test/mochitest/test_ext_runtime_getContexts.html new file mode 100644 index 0000000000..fa3b7385da --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_getContexts.html @@ -0,0 +1,126 @@ + + + + runtime.getContexts Test + + + + + + + + + + diff --git a/toolkit/components/extensions/test/mochitest/test_ext_scripting_executeScript_activeTab.html b/toolkit/components/extensions/test/mochitest/test_ext_scripting_executeScript_activeTab.html index 5eb2193409..a0ceed72d5 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_scripting_executeScript_activeTab.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_scripting_executeScript_activeTab.html @@ -27,7 +27,10 @@ const makeExtension = ({ manifest: manifestProps, ...otherProps }) => { add_task(async function setup() { await SpecialPowers.pushPrefEnv({ - set: [["extensions.manifestV3.enabled", true]], + set: [ + ["extensions.manifestV3.enabled", true], + ["extensions.originControls.grantByDefault", false], + ], }); }); diff --git a/toolkit/components/extensions/test/mochitest/test_ext_subframes_privileges.html b/toolkit/components/extensions/test/mochitest/test_ext_subframes_privileges.html index f791d08602..0586275808 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_subframes_privileges.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_subframes_privileges.html @@ -14,6 +14,32 @@ "use strict"; /* eslint-disable mozilla/balanced-listeners */ +const { + WebExtensionPolicy, +} = SpecialPowers.Cu.getGlobalForObject(SpecialPowers.Services); + + +// Some tests load non-moz-extension:-URLs in their extension document. When +// extensions run in-process (extensions.webextensions.remote set to false), +// that fails. +// For details, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1724099 and +// the same function in toolkit/components/extensions/test/xpcshell/head.js +async function allow_unsafe_parent_loads_when_extensions_not_remote() { + if (!WebExtensionPolicy.useRemoteWebExtensions) { + await SpecialPowers.pushPrefEnv({ + set: [["security.allow_unsafe_parent_loads", true]], + }); + } +} + +async function revert_allow_unsafe_parent_loads_when_extensions_not_remote() { + if (!WebExtensionPolicy.useRemoteWebExtensions) { + // Assume that the previous call to pushPrefEnv was from + // allow_unsafe_parent_loads_when_extensions_not_remote. + await SpecialPowers.popPrefEnv(); + } +} + add_task(async function test_webext_tab_subframe_privileges() { function background() { browser.runtime.onMessage.addListener(async ({msg, success, tabId, error}) => { @@ -198,19 +224,25 @@ add_task(async function test_webext_contentscript_iframe_subframe_privileges() { }); add_task(async function test_webext_background_remote_subframe_privileges() { - function backgroundSubframeScript() { + // file_remote_frame.html is opened at the same origin as this test page. + document.cookie = "cookie=monster"; + + function backgroundScript() { window.addEventListener("message", evt => { - browser.test.assertEq("http://mochi.test:8888", evt.origin, "postmessage origin ok"); + browser.test.assertTrue( + evt.origin === "http://mochi.test:8888" || + evt.origin === "https://mochi.test:8888", // using https-first, http2/http3 server. + `postmessage origin ok: ${evt.origin}` + ); browser.test.assertFalse(evt.data.tabs, "remote frame cannot access webextension APIs"); browser.test.assertEq("cookie=monster", evt.data.cookie, "Expected cookie value"); browser.test.notifyPass("webext-background-subframe-privileges"); }, {once: true}); - browser.cookies.set({url: "http://mochi.test:8888", name: "cookie", "value": "monster"}); } let extensionData = { manifest: { - permissions: ["cookies", "*://mochi.test/*", "tabs"], + permissions: ["*://mochi.test/*", "tabs"], background: { page: "background.html", }, @@ -219,32 +251,32 @@ add_task(async function test_webext_background_remote_subframe_privileges() { "background.html": ` - ', + "page.js"() { + browser.runtime.onMessage.addListener((msg, sender) => { + browser.test.assertEq( + "hello-from-sw", + msg, + "expected message from service worker" + ); + + const { contextId, ...otherProps } = sender; + browser.test.assertTrue(!!contextId, "expected a truthy contextId"); + browser.test.assertDeepEq( + { + envType: "addon_child", + id: "@test-messaging", + origin: self.origin, + url: browser.runtime.getURL("sw.js"), + }, + otherProps, + "expected correct sender props" + ); + + browser.test.sendMessage("page-done"); + }); + + browser.test.sendMessage("page-ready"); + }, + }, + }); + + await extension.startup(); + await extension.awaitMessage("background-ready"); + + const page = await ExtensionTestUtils.loadContentPage( + `moz-extension://${extension.uuid}/page.html`, + { extension } + ); + await extension.awaitMessage("page-ready"); + + extension.sendMessage("send"); + await extension.awaitMessage("page-done"); + + await page.close(); + await extension.unload(); +}); + +add_task(async function test_runtime_connect() { + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + background: { + service_worker: "sw.js", + }, + browser_specific_settings: { + gecko: { id: "@test-messaging" }, + }, + }, + files: { + "sw.js": async function () { + browser.test.onMessage.addListener(msg => { + browser.test.assertEq("connect", msg, "expected correct message"); + browser.runtime.connect(); + }); + + browser.test.sendMessage("background-ready"); + }, + "page.html": '', + "page.js"() { + browser.runtime.onConnect.addListener(port => { + const { contextId, ...otherProps } = port.sender; + browser.test.assertTrue(!!contextId, "expected a truthy contextId"); + browser.test.assertDeepEq( + { + envType: "addon_child", + id: "@test-messaging", + origin: self.origin, + url: browser.runtime.getURL("sw.js"), + }, + otherProps, + "expected correct sender props" + ); + + browser.test.sendMessage("page-done"); + }); + + browser.test.sendMessage("page-ready"); + }, + }, + }); + + await extension.startup(); + await extension.awaitMessage("background-ready"); + + const page = await ExtensionTestUtils.loadContentPage( + `moz-extension://${extension.uuid}/page.html`, + { extension } + ); + await extension.awaitMessage("page-ready"); + + extension.sendMessage("connect"); + await extension.awaitMessage("page-done"); + + await page.close(); + await extension.unload(); +}); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_storage_telemetry.js b/toolkit/components/extensions/test/xpcshell/test_ext_storage_telemetry.js index d0448b7b2e..4b98597b11 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_storage_telemetry.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_storage_telemetry.js @@ -15,55 +15,42 @@ const { TelemetryTestUtils } = ChromeUtils.importESModule( "resource://testing-common/TelemetryTestUtils.sys.mjs" ); -const HISTOGRAM_JSON_IDS = [ - "WEBEXT_STORAGE_LOCAL_SET_MS", - "WEBEXT_STORAGE_LOCAL_GET_MS", -]; -const KEYED_HISTOGRAM_JSON_IDS = [ - "WEBEXT_STORAGE_LOCAL_SET_MS_BY_ADDONID", - "WEBEXT_STORAGE_LOCAL_GET_MS_BY_ADDONID", -]; - -const HISTOGRAM_IDB_IDS = [ +const HISTOGRAM_IDS = [ "WEBEXT_STORAGE_LOCAL_IDB_SET_MS", "WEBEXT_STORAGE_LOCAL_IDB_GET_MS", ]; -const KEYED_HISTOGRAM_IDB_IDS = [ +const KEYED_HISTOGRAM_IDS = [ "WEBEXT_STORAGE_LOCAL_IDB_SET_MS_BY_ADDONID", "WEBEXT_STORAGE_LOCAL_IDB_GET_MS_BY_ADDONID", ]; -const HISTOGRAM_IDS = [].concat(HISTOGRAM_JSON_IDS, HISTOGRAM_IDB_IDS); -const KEYED_HISTOGRAM_IDS = [].concat( - KEYED_HISTOGRAM_JSON_IDS, - KEYED_HISTOGRAM_IDB_IDS -); - const EXTENSION_ID1 = "@test-extension1"; const EXTENSION_ID2 = "@test-extension2"; async function test_telemetry_background() { const { GleanTimingDistribution } = globalThis; + + // NOTE: we do not collect telemetry for the legacy JSON backend anymore + // and so if the IDB backend is not enabled we expect the related telemetry + // histograms and timing distributions to be empty. const expectedEmptyGleanMetrics = ExtensionStorageIDB.isBackendEnabled - ? ["storageLocalGetJson", "storageLocalSetJson"] + ? [] : ["storageLocalGetIdb", "storageLocalSetIdb"]; const expectedNonEmptyGleanMetrics = ExtensionStorageIDB.isBackendEnabled ? ["storageLocalGetIdb", "storageLocalSetIdb"] - : ["storageLocalGetJson", "storageLocalSetJson"]; - + : []; const expectedEmptyHistograms = ExtensionStorageIDB.isBackendEnabled - ? HISTOGRAM_JSON_IDS - : HISTOGRAM_IDB_IDS; + ? [] + : HISTOGRAM_IDS; const expectedEmptyKeyedHistograms = ExtensionStorageIDB.isBackendEnabled - ? KEYED_HISTOGRAM_JSON_IDS - : KEYED_HISTOGRAM_IDB_IDS; - + ? [] + : KEYED_HISTOGRAM_IDS; const expectedNonEmptyHistograms = ExtensionStorageIDB.isBackendEnabled - ? HISTOGRAM_IDB_IDS - : HISTOGRAM_JSON_IDS; + ? HISTOGRAM_IDS + : []; const expectedNonEmptyKeyedHistograms = ExtensionStorageIDB.isBackendEnabled - ? KEYED_HISTOGRAM_IDB_IDS - : KEYED_HISTOGRAM_JSON_IDS; + ? KEYED_HISTOGRAM_IDS + : []; const server = createHttpServer(); server.registerDirectory("/data/", do_get_file("data")); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js index 23c29aa155..820e04956e 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js @@ -105,7 +105,11 @@ async function test_idletimeout_on_streamfilter({ ).catch(err => { // This request is expected to be aborted when cleared after the test is exiting, // otherwise rethrow the error to trigger an explicit failure. - if (/The operation was aborted/.test(err.message)) { + if ( + /Content-Length header of network response exceeds response Body/.test( + err.message + ) + ) { info(`Test webRequest fetching "${testURL}" aborted`); } else { ok( diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml b/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml index 6d47012eca..fd0b2d50d2 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml @@ -432,6 +432,8 @@ skip-if = ["os == 'android' && debug"] ["test_ext_runtime_getBrowserInfo.js"] +["test_ext_runtime_getContexts.js"] + ["test_ext_runtime_getPlatformInfo.js"] ["test_ext_runtime_id.js"] diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-serviceworker.toml b/toolkit/components/extensions/test/xpcshell/xpcshell-serviceworker.toml index b300959970..83ca99bc2f 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-serviceworker.toml +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-serviceworker.toml @@ -49,3 +49,5 @@ run-sequentially = "very high failure rate in parallel" ["test_ext_scripting_contentScripts_file.js"] ["test_ext_scripting_updateContentScripts.js"] + +["test_ext_service_worker_messaging.js"] diff --git a/toolkit/components/extensions/webidl-api/ExtensionEventListener.cpp b/toolkit/components/extensions/webidl-api/ExtensionEventListener.cpp index 5ad9f2dfd8..3dd4303729 100644 --- a/toolkit/components/extensions/webidl-api/ExtensionEventListener.cpp +++ b/toolkit/components/extensions/webidl-api/ExtensionEventListener.cpp @@ -299,7 +299,7 @@ NS_IMETHODIMP ExtensionEventListener::CallListener( RefPtr runnable = new ExtensionListenerCallWorkerRunnable(this, std::move(argsHolder), aCallOptions, retPromise); - runnable->Dispatch(); + runnable->Dispatch(GetWorkerPrivate()); retPromise.forget(aPromiseResult); return NS_OK; @@ -332,7 +332,6 @@ bool ExtensionListenerCallWorkerRunnable::WorkerRun( JSContext* aCx, dom::WorkerPrivate* aWorkerPrivate) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate); auto global = mListener->GetGlobalObject(); if (NS_WARN_IF(!global)) { return true; diff --git a/toolkit/components/extensions/webidl-api/ExtensionEventListener.h b/toolkit/components/extensions/webidl-api/ExtensionEventListener.h index e986e4f58b..f11d3e7f6b 100644 --- a/toolkit/components/extensions/webidl-api/ExtensionEventListener.h +++ b/toolkit/components/extensions/webidl-api/ExtensionEventListener.h @@ -120,7 +120,8 @@ class ExtensionEventListener final : public mozIExtensionEventListener { // A WorkerRunnable subclass used to call an ExtensionEventListener // in the thread that owns the dom::Function wrapped by the // ExtensionEventListener class. -class ExtensionListenerCallWorkerRunnable final : public dom::WorkerRunnable { +class ExtensionListenerCallWorkerRunnable final + : public dom::WorkerThreadRunnable { friend class ExtensionListenerCallPromiseResultHandler; public: @@ -133,8 +134,7 @@ class ExtensionListenerCallWorkerRunnable final : public dom::WorkerRunnable { UniquePtr aArgsHolder, ListenerCallOptions* aCallOptions, RefPtr aPromiseRetval = nullptr) - : WorkerRunnable(aExtensionEventListener->GetWorkerPrivate(), - "ExtensionListenerCallWorkerRunnable", WorkerThread), + : WorkerThreadRunnable("ExtensionListenerCallWorkerRunnable"), mListener(aExtensionEventListener), mArgsHolder(std::move(aArgsHolder)), mPromiseResult(std::move(aPromiseRetval)), -- cgit v1.2.3