summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/extensions')
-rw-r--r--toolkit/components/extensions/.eslintrc.js4
-rw-r--r--toolkit/components/extensions/ConduitsChild.sys.mjs27
-rw-r--r--toolkit/components/extensions/ConduitsParent.sys.mjs4
-rw-r--r--toolkit/components/extensions/Extension.sys.mjs63
-rw-r--r--toolkit/components/extensions/ExtensionActions.sys.mjs46
-rw-r--r--toolkit/components/extensions/ExtensionChild.sys.mjs33
-rw-r--r--toolkit/components/extensions/ExtensionCommon.sys.mjs38
-rw-r--r--toolkit/components/extensions/ExtensionContent.sys.mjs52
-rw-r--r--toolkit/components/extensions/ExtensionDNR.sys.mjs14
-rw-r--r--toolkit/components/extensions/ExtensionPageChild.sys.mjs13
-rw-r--r--toolkit/components/extensions/ExtensionParent.sys.mjs125
-rw-r--r--toolkit/components/extensions/ExtensionPermissions.sys.mjs52
-rw-r--r--toolkit/components/extensions/ExtensionProcessScript.sys.mjs5
-rw-r--r--toolkit/components/extensions/ExtensionShortcuts.sys.mjs2
-rw-r--r--toolkit/components/extensions/ExtensionStorage.sys.mjs5
-rw-r--r--toolkit/components/extensions/ExtensionStorageIDB.sys.mjs3
-rw-r--r--toolkit/components/extensions/ExtensionStorageSync.sys.mjs2
-rw-r--r--toolkit/components/extensions/ExtensionStorageSyncKinto.sys.mjs21
-rw-r--r--toolkit/components/extensions/ExtensionTelemetry.sys.mjs12
-rw-r--r--toolkit/components/extensions/ExtensionUtils.sys.mjs9
-rw-r--r--toolkit/components/extensions/ExtensionWorkerChild.sys.mjs42
-rw-r--r--toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs5
-rw-r--r--toolkit/components/extensions/Schemas.sys.mjs93
-rw-r--r--toolkit/components/extensions/WebNavigation.sys.mjs10
-rw-r--r--toolkit/components/extensions/WebNavigationFrames.sys.mjs2
-rw-r--r--toolkit/components/extensions/extIWebNavigation.idl4
-rw-r--r--toolkit/components/extensions/mozIExtensionAPIRequestHandling.idl2
-rw-r--r--toolkit/components/extensions/mozIExtensionProcessScript.idl2
-rw-r--r--toolkit/components/extensions/parent/ext-identity.js2
-rw-r--r--toolkit/components/extensions/parent/ext-runtime.js2
-rw-r--r--toolkit/components/extensions/parent/ext-webRequest.js6
-rw-r--r--toolkit/components/extensions/schemas/manifest.json8
-rw-r--r--toolkit/components/extensions/schemas/web_request.json3
-rw-r--r--toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs2
-rw-r--r--toolkit/components/extensions/test/browser/browser.toml1
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_extension_page_tab_navigated.js37
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js95
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js8
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_themes_pbm.js2
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js5
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_themes_separators.js7
-rw-r--r--toolkit/components/extensions/test/mochitest/test_check_startupcache.html2
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html4
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_contentscript_securecontext.html2
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html1
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_runtime_connect2.html1
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_iframe.html6
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_tabs_captureTab.html135
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_tabs_sendMessage.html180
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_test.html2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_csp_validator.js4
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_xrays.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_clear_cached_resources.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_contentscript_dynamic_registration.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xorigin_frame.js6
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xrays.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_contexts_gc.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_dnr_modifyHeaders.js1
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_file_access.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_i18n.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_ipcBlob.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_permissions.js49
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_permissions_api.js1
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_proxy_onauthrequired.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_schemas_manifest_permissions.js8
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_schemas_privileged.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_css.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_file.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_scripting_updateContentScripts.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_webRequest_auth.js18
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_webRequest_permission.js21
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js5
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_native_manifests.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_webRequest_ancestors.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_webRequest_cookies.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_webRequest_filtering.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/xpcshell-common.toml10
-rw-r--r--toolkit/components/extensions/tsconfig.json13
-rw-r--r--toolkit/components/extensions/types/README.md11
-rw-r--r--toolkit/components/extensions/types/ext-tabs-base.d.ts1447
-rw-r--r--toolkit/components/extensions/types/extensions.ts52
-rw-r--r--toolkit/components/extensions/types/gecko.ts231
-rw-r--r--toolkit/components/extensions/types/glean.d.ts79
-rw-r--r--toolkit/components/extensions/types/globals.ts37
-rw-r--r--toolkit/components/extensions/webrequest/StreamFilterParent.cpp2
90 files changed, 2652 insertions, 588 deletions
diff --git a/toolkit/components/extensions/.eslintrc.js b/toolkit/components/extensions/.eslintrc.js
index 8cc2d2a16f..2009e44ec9 100644
--- a/toolkit/components/extensions/.eslintrc.js
+++ b/toolkit/components/extensions/.eslintrc.js
@@ -50,7 +50,7 @@ module.exports = {
"no-unused-vars": [
"error",
{
- args: "none",
+ argsIgnorePattern: "^_",
vars: "all",
varsIgnorePattern: "^console$",
},
@@ -223,7 +223,7 @@ module.exports = {
"no-unused-vars": [
"error",
{
- args: "none",
+ argsIgnorePattern: "^_",
vars: "local",
},
],
diff --git a/toolkit/components/extensions/ConduitsChild.sys.mjs b/toolkit/components/extensions/ConduitsChild.sys.mjs
index 598804f74a..d69a57cc19 100644
--- a/toolkit/components/extensions/ConduitsChild.sys.mjs
+++ b/toolkit/components/extensions/ConduitsChild.sys.mjs
@@ -12,6 +12,9 @@
* @property {ConduitID} [sender]
* @property {boolean} query
* @property {object} arg
+ *
+ * @typedef {import("ConduitsParent.sys.mjs").ConduitAddress} ConduitAddress
+ * @typedef {import("ConduitsParent.sys.mjs").ConduitID} ConduitID
*/
/**
@@ -46,15 +49,15 @@ export class BaseConduit {
}
/**
- * Internal, partially @abstract, uses the actor to send the message/query.
+ * Internal, uses the actor to send the message/query.
*
* @param {string} method
* @param {boolean} query Flag indicating a response is expected.
- * @param {JSWindowActor} actor
+ * @param {JSWindowActorChild|JSWindowActorParent} actor
* @param {MessageData} data
* @returns {Promise?}
*/
- _send(method, query, actor, data) {
+ _doSend(method, query, actor, data) {
if (query) {
return actor.sendQuery(method, data);
}
@@ -62,6 +65,16 @@ export class BaseConduit {
}
/**
+ * Internal @abstract, used by sendX stubs.
+ *
+ * @param {string} _name
+ * @param {boolean} _query
+ */
+ _send(_name, _query, ..._args) {
+ throw new Error(`_send not implemented for ${this.constructor.name}`);
+ }
+
+ /**
* Internal, calls the specific recvX method based on the message.
*
* @param {string} name Message/method name.
@@ -89,6 +102,7 @@ export class BaseConduit {
* one specific ConduitsChild actor.
*/
export class PointConduit extends BaseConduit {
+ /** @type {ConduitGen} */
constructor(subject, address, actor) {
super(subject, address);
this.actor = actor;
@@ -108,7 +122,7 @@ export class PointConduit extends BaseConduit {
throw new Error(`send${method} on closed conduit ${this.id}`);
}
let sender = this.id;
- return super._send(method, query, this.actor, { arg, query, sender });
+ return super._doSend(method, query, this.actor, { arg, query, sender });
}
/**
@@ -156,9 +170,7 @@ export class ConduitsChild extends JSWindowActorChild {
/**
* Public entry point a child-side subject uses to open a conduit.
*
- * @param {object} subject
- * @param {ConduitAddress} address
- * @returns {PointConduit}
+ * @type {ConduitGen}
*/
openConduit(subject, address) {
let conduit = new PointConduit(subject, address, this);
@@ -211,6 +223,5 @@ export class ProcessConduitsChild extends JSProcessActorChild {
openConduit = ConduitsChild.prototype.openConduit;
receiveMessage = ConduitsChild.prototype.receiveMessage;
- willDestroy = ConduitsChild.prototype.willDestroy;
didDestroy = ConduitsChild.prototype.didDestroy;
}
diff --git a/toolkit/components/extensions/ConduitsParent.sys.mjs b/toolkit/components/extensions/ConduitsParent.sys.mjs
index d90bc4afd7..afd81cafc2 100644
--- a/toolkit/components/extensions/ConduitsParent.sys.mjs
+++ b/toolkit/components/extensions/ConduitsParent.sys.mjs
@@ -29,6 +29,7 @@
* @property {string} [url]
* @property {number} [frameId]
* @property {string} [workerScriptURL]
+ * @property {number} [workerDescriptorId]
* @property {string} [extensionId]
* @property {string} [envType]
* @property {string} [instanceId]
@@ -299,7 +300,7 @@ export class BroadcastConduit extends BaseConduit {
if (method === "RunListener" && arg.path.startsWith("webRequest.")) {
return actor.batch(method, { target, arg, query, sender });
}
- return super._send(method, query, actor, { target, arg, query, sender });
+ return super._doSend(method, query, actor, { target, arg, query, sender });
}
/**
@@ -482,6 +483,5 @@ export class ConduitsParent extends JSWindowActorParent {
*/
export class ProcessConduitsParent extends JSProcessActorParent {
receiveMessage = ConduitsParent.prototype.receiveMessage;
- willDestroy = ConduitsParent.prototype.willDestroy;
didDestroy = ConduitsParent.prototype.didDestroy;
}
diff --git a/toolkit/components/extensions/Extension.sys.mjs b/toolkit/components/extensions/Extension.sys.mjs
index de6d4c8bfd..8ab3c30234 100644
--- a/toolkit/components/extensions/Extension.sys.mjs
+++ b/toolkit/components/extensions/Extension.sys.mjs
@@ -31,7 +31,9 @@ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import { ExtensionCommon } from "resource://gre/modules/ExtensionCommon.sys.mjs";
import { ExtensionParent } from "resource://gre/modules/ExtensionParent.sys.mjs";
import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
+import { Log } from "resource://gre/modules/Log.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -54,7 +56,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
ExtensionTelemetry: "resource://gre/modules/ExtensionTelemetry.sys.mjs",
LightweightThemeManager:
"resource://gre/modules/LightweightThemeManager.sys.mjs",
- Log: "resource://gre/modules/Log.sys.mjs",
NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
SITEPERMS_ADDON_TYPE:
"resource://gre/modules/addons/siteperms-addon-utils.sys.mjs",
@@ -684,14 +685,13 @@ export var ExtensionProcessCrashObserver = {
// `processCrashTimeframe` milliseconds.
lastCrashTimestamps: [],
+ logger: Log.repository.getLogger("addons.process-crash-observer"),
+
init() {
if (!this.initialized) {
Services.obs.addObserver(this, "ipc:content-created");
Services.obs.addObserver(this, "process-type-set");
Services.obs.addObserver(this, "ipc:content-shutdown");
- this.logger = lazy.Log.repository.getLogger(
- "addons.process-crash-observer"
- );
if (this._isAndroid) {
Services.obs.addObserver(this, "geckoview-initial-foreground");
Services.obs.addObserver(this, "application-foreground");
@@ -948,7 +948,7 @@ export class ExtensionData {
get logger() {
let id = this.id || "<unknown>";
- return lazy.Log.repository.getLogger(LOGGER_ID_BASE + id);
+ return Log.repository.getLogger(LOGGER_ID_BASE + id);
}
/**
@@ -1120,6 +1120,24 @@ export class ExtensionData {
return !(this.isPrivileged && this.hasPermission("mozillaAddons"));
}
+ get optionsPageProperties() {
+ let page = this.manifest.options_ui?.page ?? this.manifest.options_page;
+ if (!page) {
+ return null;
+ }
+ return {
+ page,
+ open_in_tab: this.manifest.options_ui
+ ? this.manifest.options_ui.open_in_tab ?? false
+ : true,
+ // `options_ui.browser_style` is assigned the proper default value
+ // (true for MV2 and false for MV3 when not explicitly set),
+ // in `#parseBrowserStyleInManifest` (called when we are loading
+ // and parse manifest data from the `parseManifest` method).
+ browser_style: this.manifest.options_ui?.browser_style ?? false,
+ };
+ }
+
/**
* Given an array of host and permissions, generate a structured permissions object
* that contains seperate host origins and permissions arrays.
@@ -1658,6 +1676,8 @@ export class ExtensionData {
);
}
+ // manifest.options_page opens the extension page in a new tab
+ // and so we will not need to special handling browser_style.
if (manifest.options_ui) {
if (manifest.options_ui.open_in_tab) {
// browser_style:true has no effect when open_in_tab is true.
@@ -2748,10 +2768,10 @@ class DictionaryBootstrapScope extends BootstrapScope {
install() {}
uninstall() {}
- startup(data, reason) {
+ startup(data) {
// eslint-disable-next-line no-use-before-define
this.dictionary = new Dictionary(data);
- return this.dictionary.startup(BootstrapScope.BOOTSTRAP_REASON_MAP[reason]);
+ return this.dictionary.startup();
}
async shutdown(data, reason) {
@@ -2765,10 +2785,10 @@ class LangpackBootstrapScope extends BootstrapScope {
uninstall() {}
async update() {}
- startup(data, reason) {
+ startup(data) {
// eslint-disable-next-line no-use-before-define
this.langpack = new Langpack(data);
- return this.langpack.startup(BootstrapScope.BOOTSTRAP_REASON_MAP[reason]);
+ return this.langpack.startup();
}
async shutdown(data, reason) {
@@ -2782,12 +2802,10 @@ class SitePermissionBootstrapScope extends BootstrapScope {
install() {}
uninstall() {}
- startup(data, reason) {
+ startup(data) {
// eslint-disable-next-line no-use-before-define
this.sitepermission = new SitePermission(data);
- return this.sitepermission.startup(
- BootstrapScope.BOOTSTRAP_REASON_MAP[reason]
- );
+ return this.sitepermission.startup();
}
async shutdown(data, reason) {
@@ -2810,6 +2828,15 @@ export class Extension extends ExtensionData {
/** @type {Map<string, Map<string, any>>} */
persistentListeners;
+ /** @type {import("ExtensionShortcuts.sys.mjs").ExtensionShortcuts} */
+ shortcuts;
+
+ /** @type {TabManagerBase} */
+ tabManager;
+
+ /** @type {(options?: { ignoreDevToolsAttached?: boolean, disableResetIdleForTest?: boolean }) => Promise} */
+ terminateBackground;
+
constructor(addonData, startupReason, updateReason) {
super(addonData.resourceURI, addonData.isPrivileged);
@@ -3203,9 +3230,11 @@ export class Extension extends ExtensionData {
};
}
- // Extended serialized data which is only needed in the extensions process,
- // and is never deserialized in web content processes.
- // Keep in sync with BrowserExtensionContent in ExtensionChild.sys.mjs
+ /**
+ * Extended serialized data which is only needed in the extensions process,
+ * and is never deserialized in web content processes.
+ * Keep in sync with @see {ExtensionChild}.
+ */
serializeExtended() {
return {
backgroundScripts: this.backgroundScripts,
@@ -3461,7 +3490,7 @@ export class Extension extends ExtensionData {
ignoreQuarantine: this.ignoreQuarantine,
temporarilyInstalled: this.temporarilyInstalled,
allowedOrigins: new MatchPatternSet([]),
- localizeCallback() {},
+ localizeCallback: () => "",
readyPromise,
});
diff --git a/toolkit/components/extensions/ExtensionActions.sys.mjs b/toolkit/components/extensions/ExtensionActions.sys.mjs
index 29a286442e..92ea9865e3 100644
--- a/toolkit/components/extensions/ExtensionActions.sys.mjs
+++ b/toolkit/components/extensions/ExtensionActions.sys.mjs
@@ -79,8 +79,8 @@ class PanelActionBase {
/**
* Set a global, window specific or tab specific property.
*
- * @param {XULElement|ChromeWindow|null} target
- * A XULElement tab, a ChromeWindow, or null for the global data.
+ * @param {NativeTab|ChromeWindow|null} target
+ * A NativeTab tab, a ChromeWindow, or null for the global data.
* @param {string} prop
* String property to set. Should should be one of "icon", "title", "badgeText",
* "popup", "badgeBackgroundColor", "badgeTextColor" or "enabled".
@@ -104,8 +104,8 @@ class PanelActionBase {
/**
* Gets the data associated with a tab, window, or the global one.
*
- * @param {XULElement|ChromeWindow|null} target
- * A XULElement tab, a ChromeWindow, or null for the global data.
+ * @param {NativeTab|ChromeWindow|null} target
+ * A NativeTab tab, a ChromeWindow, or null for the global data.
* @returns {object}
* The icon, title, badge, etc. associated with the target.
*/
@@ -119,8 +119,8 @@ class PanelActionBase {
/**
* Retrieve the value of a global, window specific or tab specific property.
*
- * @param {XULElement|ChromeWindow|null} target
- * A XULElement tab, a ChromeWindow, or null for the global data.
+ * @param {NativeTab|ChromeWindow|null} target
+ * A NativeTab tab, a ChromeWindow, or null for the global data.
* @param {string} prop
* Name of property to retrieve. Should should be one of "icon",
* "title", "badgeText", "popup", "badgeBackgroundColor" or "enabled".
@@ -161,7 +161,7 @@ class PanelActionBase {
*
* @param {string} eventType
* The type of the event, should be "location-change".
- * @param {XULElement} tab
+ * @param {NativeTab} tab
* The tab whose location changed, or which has become selected.
* @param {boolean} [fromBrowse]
* - `true` if navigation occurred in `tab`.
@@ -178,7 +178,7 @@ class PanelActionBase {
/**
* Gets the popup url for a given tab.
*
- * @param {XULElement} tab
+ * @param {NativeTab} tab
* The tab the popup refers to.
* @param {boolean} strict
* If errors should be thrown if a URL is not available.
@@ -208,7 +208,7 @@ class PanelActionBase {
* Will clear any existing activeTab permissions previously granted for any
* other tab.
*
- * @param {XULElement} tab
+ * @param {NativeTab} tab
* The tab that should be granted activeTab permission for. Set to
* null to clear previously granted activeTab permission.
*/
@@ -229,7 +229,7 @@ class PanelActionBase {
/**
* Triggers this action and sends the appropriate event if needed.
*
- * @param {XULElement} tab
+ * @param {NativeTab} tab
* The tab on which the action was fired.
* @param {object} clickInfo
* Extra data passed to the second parameter to the action API's
@@ -322,7 +322,7 @@ class PanelActionBase {
* If it only changes a parameter for a single window, `target` will be that window.
* Otherwise `target` will be null.
*
- * @param {XULElement|ChromeWindow|null} _target
+ * @param {NativeTab|ChromeWindow} [_target]
* Browser tab or browser chrome window, may be null.
*/
updateOnChange(_target) {}
@@ -332,16 +332,22 @@ class PanelActionBase {
*
* @param {string} _tabId
* Internal id of the tab to get.
+ * @returns {NativeTab}
*/
- getTab(_tabId) {}
+ getTab(_tabId) {
+ throw new Error("Not implemented.");
+ }
/**
* Get window object from windowId
*
* @param {string} _windowId
* Internal id of the window to get.
+ * @returns {ChromeWindow}
*/
- getWindow(_windowId) {}
+ getWindow(_windowId) {
+ throw new Error("Not implemented.");
+ }
/**
* Gets the target object corresponding to the `details` parameter of the various
@@ -352,19 +358,19 @@ class PanelActionBase {
* @param {number} [_details.tabId]
* @param {number} [_details.windowId]
* @throws if both `tabId` and `windowId` are specified, or if they are invalid.
- * @returns {XULElement|ChromeWindow|null}
- * If a `tabId` was specified, the corresponding XULElement tab.
+ * @returns {NativeTab|ChromeWindow|null}
+ * If a `tabId` was specified, the corresponding NativeTab tab.
* If a `windowId` was specified, the corresponding ChromeWindow.
* Otherwise, `null`.
*/
getTargetFromDetails(_details) {
- return null;
+ throw new Error("Not Implemented");
}
/**
* Triggers a click event.
*
- * @param {XULElement} _tab
+ * @param {NativeTab} _tab
* The tab where this event should be fired.
* @param {object} _clickInfo
* Extra data passed to the second parameter to the action API's
@@ -375,7 +381,7 @@ class PanelActionBase {
/**
* Checks whether this action is shown.
*
- * @param {XULElement} _tab
+ * @param {NativeTab} _tab
* The tab to be checked
* @returns {boolean}
*/
@@ -442,7 +448,7 @@ export class PageActionBase extends PanelActionBase {
// Checks whether the tab action is shown when the specified tab becomes active.
// Does pattern matching if necessary, and caches the result as a tab-specific value.
- // @param {XULElement} tab
+ // @param {NativeTab} tab
// The tab to be checked
// @return boolean
isShownForTab(tab) {
@@ -571,6 +577,8 @@ export class BrowserActionBase extends PanelActionBase {
/**
* Determines the text badge color to be used in a tab, window, or globally.
*
+ * @typedef {number[]} ColorArray from schemas/browser_action.json.
+ *
* @param {object} values
* The values associated with the tab or window, or global values.
* @returns {ColorArray}
diff --git a/toolkit/components/extensions/ExtensionChild.sys.mjs b/toolkit/components/extensions/ExtensionChild.sys.mjs
index 70774db395..232d5cc659 100644
--- a/toolkit/components/extensions/ExtensionChild.sys.mjs
+++ b/toolkit/components/extensions/ExtensionChild.sys.mjs
@@ -15,6 +15,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
XPCOMUtils.defineLazyServiceGetter(
@@ -144,7 +145,7 @@ const StrongPromise = {
Services.obs.addObserver(StrongPromise, "extensions-onMessage-witness");
// Simple single-event emitter-like helper, exposes the EventManager api.
-class SimpleEventAPI extends EventManager {
+export class SimpleEventAPI extends EventManager {
constructor(context, name) {
let fires = new Set();
let register = fire => {
@@ -162,7 +163,7 @@ class SimpleEventAPI extends EventManager {
}
// runtime.OnMessage event helper, handles custom async/sendResponse logic.
-class MessageEvent extends SimpleEventAPI {
+export class MessageEvent extends SimpleEventAPI {
emit(holder, sender) {
if (!this.fires.size || !this.context.active) {
return { received: false };
@@ -229,7 +230,7 @@ function holdMessage(name, anonymizedName, data, native = null) {
}
// Implements the runtime.Port extension API object.
-class Port {
+export class Port {
/**
* @param {BaseContext} context The context that owns this port.
* @param {number} portId Uniquely identifies this port's channel.
@@ -310,7 +311,7 @@ class Port {
* Each extension context gets its own Messenger object. It handles the
* basics of sendMessage, onMessage, connect and onConnect.
*/
-class Messenger {
+export class Messenger {
constructor(context) {
this.context = context;
this.conduit = context.openConduit(this, {
@@ -382,8 +383,11 @@ var ExtensionManager = {
extensions: new Map(),
};
-// Represents a browser extension in the content process.
-class BrowserExtensionContent extends EventEmitter {
+/**
+ * Represents an extension instance in the child process.
+ * Corresponds to the @see {Extension} instance in the parent.
+ */
+export class ExtensionChild extends EventEmitter {
constructor(policy) {
super();
@@ -580,7 +584,7 @@ class BrowserExtensionContent extends EventEmitter {
/**
* An object that runs an remote implementation of an API.
*/
-class ProxyAPIImplementation extends SchemaAPIInterface {
+export class ProxyAPIImplementation extends SchemaAPIInterface {
/**
* @param {string} namespace The full path to the namespace that contains the
* `name` member. This may contain dots, e.g. "storage.local".
@@ -680,7 +684,7 @@ class ProxyAPIImplementation extends SchemaAPIInterface {
}
}
-class ChildLocalAPIImplementation extends LocalAPIImplementation {
+export class ChildLocalAPIImplementation extends LocalAPIImplementation {
constructor(pathObj, namespace, name, childApiManager) {
super(pathObj, name, childApiManager.context);
this.childApiManagerId = childApiManager.id;
@@ -730,7 +734,7 @@ class ChildLocalAPIImplementation extends LocalAPIImplementation {
// JSProcessActor Conduits actors (see ConduitsChild.sys.mjs) to communicate
// with the ParentAPIManager singleton in ExtensionParent.sys.mjs.
// It handles asynchronous function calls as well as event listeners.
-class ChildAPIManager {
+export class ChildAPIManager {
constructor(context, messageManager, localAPICan, contextData) {
this.context = context;
this.messageManager = messageManager;
@@ -1012,14 +1016,3 @@ class ChildAPIManager {
this.permissionsChangedCallbacks.add(callback);
}
}
-
-export var ExtensionChild = {
- BrowserExtensionContent,
- ChildAPIManager,
- ChildLocalAPIImplementation,
- MessageEvent,
- Messenger,
- Port,
- ProxyAPIImplementation,
- SimpleEventAPI,
-};
diff --git a/toolkit/components/extensions/ExtensionCommon.sys.mjs b/toolkit/components/extensions/ExtensionCommon.sys.mjs
index 512d1444a5..c06cf37a6a 100644
--- a/toolkit/components/extensions/ExtensionCommon.sys.mjs
+++ b/toolkit/components/extensions/ExtensionCommon.sys.mjs
@@ -14,6 +14,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -234,7 +235,7 @@ class NoCloneSpreadArgs {
const LISTENERS = Symbol("listeners");
const ONCE_MAP = Symbol("onceMap");
-class EventEmitter {
+export class EventEmitter {
constructor() {
this[LISTENERS] = new Map();
this[ONCE_MAP] = new WeakMap();
@@ -353,7 +354,7 @@ class EventEmitter {
* that inherits from this class, the derived class is instantiated
* once for each extension that uses the API.
*/
-class ExtensionAPI extends EventEmitter {
+export class ExtensionAPI extends EventEmitter {
constructor(extension) {
super();
@@ -388,7 +389,7 @@ class ExtensionAPI extends EventEmitter {
/**
* @param {string} _id
- * @param {Record<string, JSONValue>} _manifest
+ * @param {object} _manifest
*/
static onUpdate(_id, _manifest) {}
}
@@ -468,7 +469,7 @@ class ExtensionAPIPersistent extends ExtensionAPI {
*
* @abstract
*/
-class BaseContext {
+export class BaseContext {
/** @type {boolean} */
isTopContext;
/** @type {string} */
@@ -553,10 +554,7 @@ class BaseContext {
* Opens a conduit linked to this context, populating related address fields.
* Only available in child contexts with an associated contentWindow.
*
- * @param {object} subject
- * @param {ConduitAddress} address
- * @returns {import("ConduitsChild.sys.mjs").PointConduit}
- * @type {ConduitOpen}
+ * @type {ConduitGen}
*/
openConduit(subject, address) {
let wgc = this.contentWindow.windowGlobalChild;
@@ -614,7 +612,7 @@ class BaseContext {
// All child contexts must implement logActivity. This is handled if the child
// context subclasses ExtensionBaseContextChild. ProxyContextParent overrides
// this with a noop for parent contexts.
- logActivity() {
+ logActivity(_type, _name, _data) {
throw new Error(`Not implemented for ${this.envType}`);
}
@@ -822,7 +820,7 @@ class BaseContext {
* exception error.
*
* @param {Error|object} error
- * @param {SavedFrame?} [caller]
+ * @param {nsIStackFrame?} [caller]
* @returns {Error}
*/
normalizeError(error, caller) {
@@ -864,7 +862,7 @@ class BaseContext {
*
* @param {object} error An object with a `message` property. May
* optionally be an `Error` object belonging to the target scope.
- * @param {SavedFrame?} caller
+ * @param {nsIStackFrame?} caller
* The optional caller frame which triggered this callback, to be used
* in error reporting.
* @param {Function} callback The callback to call.
@@ -885,7 +883,7 @@ class BaseContext {
/**
* Captures the most recent stack frame which belongs to the extension.
*
- * @returns {SavedFrame?}
+ * @returns {nsIStackFrame?}
*/
getCaller() {
return ChromeUtils.getCallerLocation(this.principal);
@@ -1037,7 +1035,7 @@ class BaseContext {
*
* @interface
*/
-class SchemaAPIInterface {
+export class SchemaAPIInterface {
/**
* Calls this as a function that returns its return value.
*
@@ -1483,7 +1481,7 @@ class SchemaAPIManager extends EventEmitter {
* "addon" - An addon process.
* "content" - A content process.
* "devtools" - A devtools process.
- * @param {import("Schemas.sys.mjs").SchemaRoot} [schema]
+ * @param {import("Schemas.sys.mjs").SchemaInject} [schema]
*/
constructor(processType, schema) {
super();
@@ -2023,10 +2021,14 @@ export function LocaleData(data) {
this.locales = data.locales || new Map();
this.warnedMissingKeys = new Set();
- // Map(locale-name -> Map(message-key -> localized-string))
- //
- // Contains a key for each loaded locale, each of which is a
- // Map of message keys to their localized strings.
+ /**
+ * Map(locale-name -> Map(message-key -> localized-string))
+ *
+ * Contains a key for each loaded locale, each of which is a
+ * Map of message keys to their localized strings.
+ *
+ * @type {Map<string, Map<string, string>>}
+ */
this.messages = data.messages || new Map();
if (data.builtinMessages) {
diff --git a/toolkit/components/extensions/ExtensionContent.sys.mjs b/toolkit/components/extensions/ExtensionContent.sys.mjs
index 015d1bc7c6..a2fce282ee 100644
--- a/toolkit/components/extensions/ExtensionContent.sys.mjs
+++ b/toolkit/components/extensions/ExtensionContent.sys.mjs
@@ -7,6 +7,7 @@
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -39,8 +40,10 @@ const ScriptError = Components.Constructor(
);
import {
+ ChildAPIManager,
ExtensionChild,
ExtensionActivityLogChild,
+ Messenger,
} from "resource://gre/modules/ExtensionChild.sys.mjs";
import { ExtensionCommon } from "resource://gre/modules/ExtensionCommon.sys.mjs";
import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
@@ -63,8 +66,6 @@ const {
runSafeSyncWithoutClone,
} = ExtensionCommon;
-const { BrowserExtensionContent, ChildAPIManager, Messenger } = ExtensionChild;
-
ChromeUtils.defineLazyGetter(lazy, "isContentScriptProcess", () => {
return (
Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT ||
@@ -164,6 +165,7 @@ class ScriptCache extends CacheMap {
super(
SCRIPT_EXPIRY_TIMEOUT_MS,
url => {
+ /** @type {Promise<PrecompiledScript> & { script?: PrecompiledScript }} */
let promise = ChromeUtils.compileScript(url, options);
promise.then(script => {
promise.script = script;
@@ -282,49 +284,37 @@ class CSSCodeCache extends BaseCSSCache {
}
}
-defineLazyGetter(
- BrowserExtensionContent.prototype,
- "staticScripts",
- function () {
- return new ScriptCache({ hasReturnValue: false }, this);
- }
-);
+defineLazyGetter(ExtensionChild.prototype, "staticScripts", function () {
+ return new ScriptCache({ hasReturnValue: false }, this);
+});
-defineLazyGetter(
- BrowserExtensionContent.prototype,
- "dynamicScripts",
- function () {
- return new ScriptCache({ hasReturnValue: true }, this);
- }
-);
+defineLazyGetter(ExtensionChild.prototype, "dynamicScripts", function () {
+ return new ScriptCache({ hasReturnValue: true }, this);
+});
-defineLazyGetter(BrowserExtensionContent.prototype, "userCSS", function () {
+defineLazyGetter(ExtensionChild.prototype, "userCSS", function () {
return new CSSCache(Ci.nsIStyleSheetService.USER_SHEET, this);
});
-defineLazyGetter(BrowserExtensionContent.prototype, "authorCSS", function () {
+defineLazyGetter(ExtensionChild.prototype, "authorCSS", function () {
return new CSSCache(Ci.nsIStyleSheetService.AUTHOR_SHEET, this);
});
// These two caches are similar to the above but specialized to cache the cssCode
// using an hash computed from the cssCode string as the key (instead of the generated data
// URI which can be pretty long for bigger injected cssCode).
-defineLazyGetter(BrowserExtensionContent.prototype, "userCSSCode", function () {
+defineLazyGetter(ExtensionChild.prototype, "userCSSCode", function () {
return new CSSCodeCache(Ci.nsIStyleSheetService.USER_SHEET, this);
});
-defineLazyGetter(
- BrowserExtensionContent.prototype,
- "authorCSSCode",
- function () {
- return new CSSCodeCache(Ci.nsIStyleSheetService.AUTHOR_SHEET, this);
- }
-);
+defineLazyGetter(ExtensionChild.prototype, "authorCSSCode", function () {
+ return new CSSCodeCache(Ci.nsIStyleSheetService.AUTHOR_SHEET, this);
+});
// Represents a content script.
class Script {
/**
- * @param {BrowserExtensionContent} extension
+ * @param {ExtensionChild} extension
* @param {WebExtensionContentScript|object} matcher
* An object with a "matchesWindowGlobal" method and content script
* execution details. This is usually a plain WebExtensionContentScript
@@ -611,7 +601,7 @@ class Script {
* @param {ContentScriptContextChild} context
* The document to block the parsing on, if the scripts are not yet precompiled and cached.
*
- * @returns {Array<PreloadedScript> | Promise<Array<PreloadedScript>>}
+ * @returns {PrecompiledScript[] | Promise<PrecompiledScript[]>}
* Returns an array of preloaded scripts if they are already available, or a promise which
* resolves to the array of the preloaded scripts once they are precompiled and cached.
*/
@@ -667,7 +657,7 @@ class Script {
// Represents a user script.
class UserScript extends Script {
/**
- * @param {BrowserExtensionContent} extension
+ * @param {ExtensionChild} extension
* @param {WebExtensionContentScript|object} matcher
* An object with a "matchesWindowGlobal" method and content script
* execution details.
@@ -1014,7 +1004,7 @@ class ContentScriptContextChild extends BaseContext {
// Responsible for creating ExtensionContexts and injecting content
// scripts into them when new documents are created.
DocumentManager = {
- // Map[windowId -> Map[ExtensionChild -> ContentScriptContextChild]]
+ /** @type {Map<number, Map<ExtensionChild, ContentScriptContextChild>>} */
contexts: new Map(),
initialized: false,
@@ -1115,8 +1105,6 @@ DocumentManager = {
};
export var ExtensionContent = {
- BrowserExtensionContent,
-
contentScripts,
shutdownExtension(extension) {
diff --git a/toolkit/components/extensions/ExtensionDNR.sys.mjs b/toolkit/components/extensions/ExtensionDNR.sys.mjs
index d18856a2b8..afc7d30751 100644
--- a/toolkit/components/extensions/ExtensionDNR.sys.mjs
+++ b/toolkit/components/extensions/ExtensionDNR.sys.mjs
@@ -71,6 +71,7 @@ const gRuleManagers = [];
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -182,6 +183,8 @@ export class Rule {
class Ruleset {
/**
+ * @typedef {number} integer
+ *
* @param {string} rulesetId - extension-defined ruleset ID.
* @param {integer} rulesetPrecedence
* @param {Rule[]} rules - extension-defined rules
@@ -1316,7 +1319,7 @@ class RequestDetails {
* @param {string} options.type - ResourceType (MozContentPolicyType).
* @param {string} [options.method] - HTTP method
* @param {integer} [options.tabId]
- * @param {BrowsingContext} [options.browsingContext] - The BrowsingContext
+ * @param {CanonicalBrowsingContext} [options.browsingContext] - The CBC
* associated with the request. Typically the bc for which the subresource
* request is initiated, if any. For document requests, this is the parent
* (i.e. the parent frame for sub_frame, null for main_frame).
@@ -1975,7 +1978,10 @@ const NetworkIntegration = {
/**
* Applies the actions of the DNR rules.
*
- * @param {ChannelWrapper} channel
+ * @typedef {ChannelWrapper & { _dnrMatchedRules?: MatchedRule[] }}
+ * ChannelWrapperViaDNR
+ *
+ * @param {ChannelWrapperViaDNR} channel
* @returns {boolean} Whether to ignore any responses from the webRequest API.
*/
onBeforeRequest(channel) {
@@ -1993,7 +1999,7 @@ const NetworkIntegration = {
this.applyRedirect(channel, finalMatch);
return true;
case "upgradeScheme":
- this.applyUpgradeScheme(channel, finalMatch);
+ this.applyUpgradeScheme(channel);
return true;
}
// If there are multiple rules, then it may be a combination of allow,
@@ -2294,7 +2300,7 @@ function beforeWebRequestEvent(channel, kind) {
/**
* Applies matching DNR rules, some of which may potentially cancel the request.
*
- * @param {ChannelWrapper} channel
+ * @param {ChannelWrapperViaDNR} channel
* @param {string} kind - The name of the webRequest event.
* @returns {boolean} Whether to ignore any responses from the webRequest API.
*/
diff --git a/toolkit/components/extensions/ExtensionPageChild.sys.mjs b/toolkit/components/extensions/ExtensionPageChild.sys.mjs
index 17c208572b..f0ac5ed229 100644
--- a/toolkit/components/extensions/ExtensionPageChild.sys.mjs
+++ b/toolkit/components/extensions/ExtensionPageChild.sys.mjs
@@ -22,8 +22,9 @@ const CATEGORY_EXTENSION_SCRIPTS_DEVTOOLS = "webextension-scripts-devtools";
import { ExtensionCommon } from "resource://gre/modules/ExtensionCommon.sys.mjs";
import {
- ExtensionChild,
+ ChildAPIManager,
ExtensionActivityLogChild,
+ Messenger,
} from "resource://gre/modules/ExtensionChild.sys.mjs";
import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
@@ -32,8 +33,6 @@ const { getInnerWindowID, promiseEvent } = ExtensionUtils;
const { BaseContext, CanOfAPIs, SchemaAPIManager, redefineGetter } =
ExtensionCommon;
-const { ChildAPIManager, Messenger } = ExtensionChild;
-
const initializeBackgroundPage = context => {
// Override the `alert()` method inside background windows;
// we alias it to console.log().
@@ -188,7 +187,7 @@ export class ExtensionBaseContextChild extends BaseContext {
* This ExtensionBaseContextChild represents an addon execution environment
* that is running in an addon or devtools child process.
*
- * @param {BrowserExtensionContent} extension This context's owner.
+ * @param {ExtensionChild} extension This context's owner.
* @param {object} params
* @param {string} params.envType One of "addon_child" or "devtools_child".
* @param {nsIDOMWindow} params.contentWindow The window where the addon runs.
@@ -301,7 +300,7 @@ class ExtensionPageContextChild extends ExtensionBaseContextChild {
* This is the child side of the ExtensionPageContextParent class
* defined in ExtensionParent.sys.mjs.
*
- * @param {BrowserExtensionContent} extension This context's owner.
+ * @param {ExtensionChild} extension This context's owner.
* @param {object} params
* @param {nsIDOMWindow} params.contentWindow The window where the addon runs.
* @param {string} params.viewType One of "background", "popup", "sidebar" or "tab".
@@ -340,7 +339,7 @@ export class DevToolsContextChild extends ExtensionBaseContextChild {
* environment that has access to the devtools API namespace and to the same subset
* of APIs available in a content script execution environment.
*
- * @param {BrowserExtensionContent} extension This context's owner.
+ * @param {ExtensionChild} extension This context's owner.
* @param {object} params
* @param {nsIDOMWindow} params.contentWindow The window where the addon runs.
* @param {string} params.viewType One of "devtools_page" or "devtools_panel".
@@ -424,7 +423,7 @@ export var ExtensionPageChild = {
/**
* Create a privileged context at initial-document-element-inserted.
*
- * @param {BrowserExtensionContent} extension
+ * @param {ExtensionChild} extension
* The extension for which the context should be created.
* @param {nsIDOMWindow} contentWindow The global of the page.
*/
diff --git a/toolkit/components/extensions/ExtensionParent.sys.mjs b/toolkit/components/extensions/ExtensionParent.sys.mjs
index b4812a702a..f951433713 100644
--- a/toolkit/components/extensions/ExtensionParent.sys.mjs
+++ b/toolkit/components/extensions/ExtensionParent.sys.mjs
@@ -14,6 +14,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -245,8 +246,10 @@ let apiManager = new (class extends SchemaAPIManager {
// to relevant child messengers. Also handles Native messaging and GeckoView.
/** @typedef {typeof ProxyMessenger} NativeMessenger */
const ProxyMessenger = {
- /** @type {Map<number, Partial<ParentPort>&Promise<ParentPort>>} */
+ /** @type {Map<number, ParentPort>} */
ports: new Map(),
+ /** @type {Map<number, Promise>} */
+ portPromises: new Map(),
init() {
this.conduit = new lazy.BroadcastConduit(ProxyMessenger, {
@@ -300,7 +303,8 @@ const ProxyMessenger = {
};
if (JSWindowActorParent.isInstance(source.actor)) {
- let browser = source.actor.browsingContext.top.embedderElement;
+ let { currentWindowContext, top } = source.actor.browsingContext;
+ let browser = top.embedderElement;
let data =
browser && apiManager.global.tabTracker.getBrowserData(browser);
if (data?.tabId > 0) {
@@ -308,6 +312,13 @@ const ProxyMessenger = {
// frameId is documented to only be set if sender.tab is set.
sender.frameId = source.frameId;
}
+
+ let principal = currentWindowContext.documentPrincipal;
+ // We intend the serialization of null principals *and* file scheme to be
+ // "null".
+ sender.origin = new URL(principal.originNoSuffix).origin;
+ } else if (source.verified) {
+ sender.origin = `moz-extension://${extension.uuid}`;
}
return sender;
@@ -363,17 +374,24 @@ const ProxyMessenger = {
}
// PortMessages that follow will need to wait for the port to be opened.
- /** @type {callback} */
- let resolvePort;
- this.ports.set(arg.portId, new Promise(res => (resolvePort = res)));
+ let { promise, resolve, reject } = Promise.withResolvers();
+ this.portPromises.set(arg.portId, promise);
- let kind = await this.normalizeArgs(arg, sender);
- let all = await this.conduit.castPortConnect(kind, arg);
- resolvePort();
+ try {
+ let kind = await this.normalizeArgs(arg, sender);
+ let all = await this.conduit.castPortConnect(kind, arg);
+ resolve();
- // If there are no active onConnect listeners.
- if (!all.some(x => x.value)) {
- throw new ExtensionError(ERROR_NO_RECEIVERS);
+ // If there are no active onConnect listeners.
+ if (!all.some(x => x.value)) {
+ throw new ExtensionError(ERROR_NO_RECEIVERS);
+ }
+ } catch (err) {
+ // Throw _and_ reject with error, so everything awaiting this port fails.
+ reject(err);
+ throw err;
+ } finally {
+ this.portPromises.delete(arg.portId);
}
},
@@ -387,7 +405,7 @@ const ProxyMessenger = {
// NOTE: the following await make sure we await for promised ports
// (ports that were not yet open when added to the Map,
// see recvPortConnect).
- await this.ports.get(sender.portId);
+ await this.portPromises.get(sender.portId);
this.sendPortMessage(sender.portId, holder, !sender.source);
},
@@ -448,7 +466,7 @@ GlobalManager = {
extensionMap: new Map(),
initialized: false,
- /** @type {WeakMap<Browser, object>} Extension Context init data. */
+ /** @type {WeakMap<XULBrowserElement, object>} Extension Context init data. */
frameData: new WeakMap(),
init(extension) {
@@ -961,7 +979,6 @@ ParentAPIManager = {
throw new Error(`Bad sender context envType: ${sender.envType}`);
}
- let isBackgroundWorker = false;
if (JSWindowActorParent.isInstance(actor)) {
const target = actor.browsingContext.top.embedderElement;
let processMessageManager =
@@ -979,6 +996,22 @@ ParentAPIManager = {
"Attempt to create privileged extension parent from incorrect child process"
);
}
+
+ if (envType == "addon_parent") {
+ context = new ExtensionPageContextParent(
+ envType,
+ extension,
+ data,
+ actor.browsingContext
+ );
+ } else if (envType == "devtools_parent") {
+ context = new DevToolsExtensionPageContextParent(
+ envType,
+ extension,
+ data,
+ actor.browsingContext
+ );
+ }
} else if (JSProcessActorParent.isInstance(actor)) {
if (actor.manager.remoteType !== extension.remoteType) {
throw new Error(
@@ -996,7 +1029,7 @@ ParentAPIManager = {
`Unexpected viewType ${data.viewType} on an extension process actor`
);
}
- isBackgroundWorker = true;
+ context = new BackgroundWorkerContextParent(envType, extension, data);
} else {
// Unreacheable: JSWindowActorParent and JSProcessActorParent are the
// only actors.
@@ -1004,24 +1037,6 @@ ParentAPIManager = {
"Attempt to create privileged extension parent via incorrect actor"
);
}
-
- if (isBackgroundWorker) {
- context = new BackgroundWorkerContextParent(envType, extension, data);
- } else if (envType == "addon_parent") {
- context = new ExtensionPageContextParent(
- envType,
- extension,
- data,
- actor.browsingContext
- );
- } else if (envType == "devtools_parent") {
- context = new DevToolsExtensionPageContextParent(
- envType,
- extension,
- data,
- actor.browsingContext
- );
- }
} else if (envType == "content_parent") {
// Note: actor is always a JSWindowActorParent, with a browsingContext.
context = new ContentScriptContextParent(
@@ -1340,11 +1355,9 @@ class HiddenXULWindow {
// The windowless browser is a thin wrapper around a docShell that keeps
// its related resources alive. It implements nsIWebNavigation and
- // forwards its methods to the underlying docShell. That .docShell
- // needs `QueryInterface(nsIWebNavigation)` to give us access to the
- // webNav methods that are already available on the windowless browser.
+ // forwards its methods to the underlying docShell.
let chromeShell = windowlessBrowser.docShell;
- chromeShell.QueryInterface(Ci.nsIWebNavigation);
+ let webNav = chromeShell.QueryInterface(Ci.nsIWebNavigation);
if (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing) {
let attrs = chromeShell.getOriginAttributes();
@@ -1353,13 +1366,13 @@ class HiddenXULWindow {
}
windowlessBrowser.browsingContext.useGlobalHistory = false;
- chromeShell.loadURI(DUMMY_PAGE_URI, {
+ webNav.loadURI(DUMMY_PAGE_URI, {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
await promiseObserved(
"chrome-document-global-created",
- win => win.document == chromeShell.document
+ win => win.document == webNav.document
);
await promiseDocumentLoaded(windowlessBrowser.document);
if (this.unloaded) {
@@ -1376,7 +1389,7 @@ class HiddenXULWindow {
* An object that contains the xul attributes to set of the newly
* created browser XUL element.
*
- * @returns {Promise<XULElement>}
+ * @returns {Promise<XULBrowserElement>}
* A Promise which resolves to the newly created browser XUL element.
*/
async createBrowserElement(xulAttributes) {
@@ -1458,16 +1471,17 @@ const SharedWindow = {
* to inherits the shared boilerplate code needed to create a parent document for the hidden
* extension pages (e.g. the background page, the devtools page) in the BackgroundPage and
* DevToolsPage classes.
- *
- * @param {Extension} extension
- * The Extension which owns the hidden extension page created (used to decide
- * if the hidden extension page parent doc is going to be a windowlessBrowser or
- * a visible XUL window).
- * @param {string} viewType
- * The viewType of the WebExtension page that is going to be loaded
- * in the created browser element (e.g. "background" or "devtools_page").
*/
class HiddenExtensionPage {
+ /**
+ * @param {Extension} extension
+ * The Extension which owns the hidden extension page created (used to decide
+ * if the hidden extension page parent doc is going to be a windowlessBrowser or
+ * a visible XUL window).
+ * @param {string} viewType
+ * The viewType of the WebExtension page that is going to be loaded
+ * in the created browser element (e.g. "background" or "devtools_page").
+ */
constructor(extension, viewType) {
if (!extension || !viewType) {
throw new Error("extension and viewType parameters are mandatory");
@@ -1535,6 +1549,9 @@ class HiddenExtensionPage {
}
}
+/** @typedef {import("resource://devtools/server/actors/descriptors/webextension.js")
+ .WebExtensionDescriptorActor} WebExtensionDescriptorActor */
+
/**
* This object provides utility functions needed by the devtools actors to
* be able to connect and debug an extension (which can run in the main or in
@@ -1545,9 +1562,9 @@ const DebugUtils = {
// which are used to connect the webextension patent actor to the extension process.
hiddenXULWindow: null,
- // Map<extensionId, Promise<XULElement>>
+ /** @type {Map<string, Promise<XULBrowserElement> & { browser: XULBrowserElement }>} */
debugBrowserPromises: new Map(),
- // DefaultWeakMap<Promise<browser XULElement>, Set<WebExtensionParentActor>>
+ /** @type {WeakMap<Promise<XULBrowserElement>, Set<WebExtensionDescriptorActor>>} */
debugActors: new DefaultWeakMap(() => new Set()),
_extensionUpdatedWatcher: null,
@@ -1696,10 +1713,10 @@ const DebugUtils = {
* Retrieve a XUL browser element which has been configured to be able to connect
* the devtools actor with the process where the extension is running.
*
- * @param {WebExtensionParentActor} webExtensionParentActor
+ * @param {WebExtensionDescriptorActor} webExtensionParentActor
* The devtools actor that is retrieving the browser element.
*
- * @returns {Promise<XULElement>}
+ * @returns {Promise<XULBrowserElement>}
* A promise which resolves to the configured browser XUL element.
*/
async getExtensionProcessBrowser(webExtensionParentActor) {
@@ -1753,7 +1770,7 @@ const DebugUtils = {
* it destroys the XUL browser element, and it also destroy the hidden XUL window
* if it is not currently needed.
*
- * @param {WebExtensionParentActor} webExtensionParentActor
+ * @param {WebExtensionDescriptorActor} webExtensionParentActor
* The devtools actor that has retrieved an addon debug browser element.
*/
async releaseExtensionProcessBrowser(webExtensionParentActor) {
@@ -1783,7 +1800,7 @@ const DebugUtils = {
* was received by the message manager. The promise is rejected if the message
* manager was closed before a message was received.
*
- * @param {nsIMessageListenerManager} messageManager
+ * @param {MessageListenerManager} messageManager
* The message manager on which to listen for messages.
* @param {string} messageName
* The message to listen for.
diff --git a/toolkit/components/extensions/ExtensionPermissions.sys.mjs b/toolkit/components/extensions/ExtensionPermissions.sys.mjs
index 1ee9afdc32..964503d8f6 100644
--- a/toolkit/components/extensions/ExtensionPermissions.sys.mjs
+++ b/toolkit/components/extensions/ExtensionPermissions.sys.mjs
@@ -8,11 +8,13 @@ import { computeSha1HashAsString } from "resource://gre/modules/addons/crypto-ut
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
AddonManagerPrivate: "resource://gre/modules/AddonManager.sys.mjs",
+ Extension: "resource://gre/modules/Extension.sys.mjs",
ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
JSONFile: "resource://gre/modules/JSONFile.sys.mjs",
@@ -347,6 +349,50 @@ export var ExtensionPermissions = {
return this._getCached(extensionId);
},
+ /**
+ * Validate and normalize passed in `perms`, including a fixup to
+ * include all possible "all sites" permissions when appropriate.
+ *
+ * @throws if an origin or permission is not part of optional permissions.
+ *
+ * @typedef {object} Perms
+ * @property {string[]} origins
+ * @property {string[]} permissions
+ *
+ * @param {Perms} perms api permissions and origins to be added/removed.
+ * @param {Perms} optional permissions and origins from the manifest.
+ * @returns {Perms} normalized
+ */
+ normalizeOptional(perms, optional) {
+ let allSites = false;
+ let patterns = new MatchPatternSet(optional.origins, { ignorePath: true });
+ let normalized = Object.assign({}, perms);
+
+ for (let o of perms.origins) {
+ if (!patterns.subsumes(new MatchPattern(o))) {
+ throw new Error(`${o} was not declared in the manifest`);
+ }
+ // If this is one of the "all sites" permissions
+ allSites ||= lazy.Extension.isAllSitesPermission(o);
+ }
+
+ if (allSites) {
+ // Grant/revoke ALL "all sites" optional permissions from the manifest.
+ let origins = perms.origins.concat(
+ optional.origins.filter(o => lazy.Extension.isAllSitesPermission(o))
+ );
+ normalized.origins = Array.from(new Set(origins));
+ }
+
+ for (let p of perms.permissions) {
+ if (!optional.permissions.includes(p)) {
+ throw new Error(`${p} was not declared in optional_permissions`);
+ }
+ }
+
+ return normalized;
+ },
+
_fixupAllUrlsPerms(perms) {
// Unfortunately, we treat <all_urls> as an API permission as well.
// If it is added to either, ensure it is added to both.
@@ -361,8 +407,10 @@ export var ExtensionPermissions = {
* Add new permissions for the given extension. `permissions` is
* in the format that is passed to browser.permissions.request().
*
+ * @typedef {import("ExtensionCommon.sys.mjs").EventEmitter} EventEmitter
+ *
* @param {string} extensionId The extension id
- * @param {object} perms Object with permissions and origins array.
+ * @param {Perms} perms Object with permissions and origins array.
* @param {EventEmitter} [emitter] optional object implementing emitter interfaces
*/
async add(extensionId, perms, emitter) {
@@ -401,7 +449,7 @@ export var ExtensionPermissions = {
* in the format that is passed to browser.permissions.request().
*
* @param {string} extensionId The extension id
- * @param {object} perms Object with permissions and origins array.
+ * @param {Perms} perms Object with permissions and origins array.
* @param {EventEmitter} [emitter] optional object implementing emitter interfaces
*/
async remove(extensionId, perms, emitter) {
diff --git a/toolkit/components/extensions/ExtensionProcessScript.sys.mjs b/toolkit/components/extensions/ExtensionProcessScript.sys.mjs
index 93746cb0ca..ef5694be9f 100644
--- a/toolkit/components/extensions/ExtensionProcessScript.sys.mjs
+++ b/toolkit/components/extensions/ExtensionProcessScript.sys.mjs
@@ -11,6 +11,7 @@
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -49,7 +50,7 @@ ChromeUtils.defineLazyGetter(lazy, "isContentScriptProcess", () => {
});
var extensions = new DefaultWeakMap(policy => {
- return new lazy.ExtensionChild.BrowserExtensionContent(policy);
+ return new lazy.ExtensionChild(policy);
});
var pendingExtensions = new Map();
@@ -342,7 +343,7 @@ ExtensionManager = {
perms.delete(perm);
}
}
- policy.permissions = perms;
+ policy.permissions = Array.from(perms);
}
}
diff --git a/toolkit/components/extensions/ExtensionShortcuts.sys.mjs b/toolkit/components/extensions/ExtensionShortcuts.sys.mjs
index 7b30fa2cdf..17cff67eb9 100644
--- a/toolkit/components/extensions/ExtensionShortcuts.sys.mjs
+++ b/toolkit/components/extensions/ExtensionShortcuts.sys.mjs
@@ -6,6 +6,7 @@ import { ExtensionCommon } from "resource://gre/modules/ExtensionCommon.sys.mjs"
import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -462,7 +463,6 @@ export class ExtensionShortcuts {
let win = event.target.ownerGlobal;
action.triggerAction(win);
} else {
- this.extension.tabManager.addActiveTabPermission();
this.onCommand(name);
}
});
diff --git a/toolkit/components/extensions/ExtensionStorage.sys.mjs b/toolkit/components/extensions/ExtensionStorage.sys.mjs
index b1b09d137f..5317fb2a91 100644
--- a/toolkit/components/extensions/ExtensionStorage.sys.mjs
+++ b/toolkit/components/extensions/ExtensionStorage.sys.mjs
@@ -8,6 +8,7 @@ import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
const { DefaultWeakMap, ExtensionError } = ExtensionUtils;
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -89,7 +90,7 @@ function serialize(name, anonymizedName, value) {
}
export var ExtensionStorage = {
- // Map<extension-id, Promise<JSONFile>>
+ /** @type {Map<string, Promise<typeof lazy.JSONFile>>} */
jsonFilePromises: new Map(),
listeners: new Map(),
@@ -157,7 +158,7 @@ export var ExtensionStorage = {
*
* @param {any} value
* The value to sanitize.
- * @param {Context} context
+ * @param {BaseContext} context
* The extension context in which to sanitize the value
* @returns {value}
* The sanitized value.
diff --git a/toolkit/components/extensions/ExtensionStorageIDB.sys.mjs b/toolkit/components/extensions/ExtensionStorageIDB.sys.mjs
index 604d29b4cf..5223d57466 100644
--- a/toolkit/components/extensions/ExtensionStorageIDB.sys.mjs
+++ b/toolkit/components/extensions/ExtensionStorageIDB.sys.mjs
@@ -5,6 +5,7 @@
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { IndexedDB } from "resource://gre/modules/IndexedDB.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -801,6 +802,8 @@ export var ExtensionStorageIDB = {
* from the internal IndexedDB operations have to be converted into an ExtensionError
* to be accessible to the extension code).
*
+ * @typedef {import("ExtensionUtils.sys.mjs").ExtensionError} ExtensionError
+ *
* @param {object} params
* @param {Error|ExtensionError|DOMException} params.error
* The error object to normalize.
diff --git a/toolkit/components/extensions/ExtensionStorageSync.sys.mjs b/toolkit/components/extensions/ExtensionStorageSync.sys.mjs
index 3f82d91fac..06f0cc4310 100644
--- a/toolkit/components/extensions/ExtensionStorageSync.sys.mjs
+++ b/toolkit/components/extensions/ExtensionStorageSync.sys.mjs
@@ -10,6 +10,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const NS_ERROR_DOM_QUOTA_EXCEEDED_ERR = 0x80530016;
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -55,6 +56,7 @@ ExtensionStorageApiCallback.prototype = {
},
handleError(code, message) {
+ /** @type {Error & { code?: number }} */
let e = new Error(message);
e.code = code;
Cu.reportError(e);
diff --git a/toolkit/components/extensions/ExtensionStorageSyncKinto.sys.mjs b/toolkit/components/extensions/ExtensionStorageSyncKinto.sys.mjs
index ace6e16c2c..62493e3b07 100644
--- a/toolkit/components/extensions/ExtensionStorageSyncKinto.sys.mjs
+++ b/toolkit/components/extensions/ExtensionStorageSyncKinto.sys.mjs
@@ -32,6 +32,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -120,11 +121,6 @@ function throwIfNoFxA(fxAccounts, action) {
}
}
-// Global ExtensionStorageSyncKinto instance that extensions and Fx Sync use.
-// On Android, because there's no FXAccounts instance, any syncing
-// operations will fail.
-export var extensionStorageSyncKinto = null;
-
/**
* Utility function to enforce an order of fields when computing an HMAC.
*
@@ -558,6 +554,8 @@ class CryptoCollection {
* "characters" are values, each within [0, 255]. You can produce
* such a bytestring using e.g. CommonUtils.encodeUTF8.
*
+ * @typedef {string} bytestring
+ *
* The returned value is a base64url-encoded string of the hash.
*
* @param {bytestring} value The value to be hashed.
@@ -696,7 +694,7 @@ let CollectionKeyEncryptionRemoteTransformer = class extends EncryptionRemoteTra
*
* @param {Extension} extension
* The extension whose context just ended.
- * @param {Context} context
+ * @param {BaseContext} context
* The context that just ended.
*/
function cleanUpForContext(extension, context) {
@@ -1199,7 +1197,7 @@ export class ExtensionStorageSyncKinto {
* @param {Extension} extension
* The extension for which we are seeking
* a collection.
- * @param {Context} context
+ * @param {BaseContext} context
* The context of the extension, so that we can
* stop syncing the collection when the extension ends.
* @returns {Promise<Collection>}
@@ -1370,7 +1368,14 @@ export class ExtensionStorageSyncKinto {
}
}
-extensionStorageSyncKinto = new ExtensionStorageSyncKinto(_fxaService);
+/**
+ * Global ExtensionStorageSyncKinto instance that extensions and Fx Sync use.
+ * On Android, because there's no FXAccounts instance, any syncing
+ * operations will fail.
+ */
+export const extensionStorageSyncKinto = new ExtensionStorageSyncKinto(
+ _fxaService
+);
// For test use only.
export const KintoStorageTestUtils = {
diff --git a/toolkit/components/extensions/ExtensionTelemetry.sys.mjs b/toolkit/components/extensions/ExtensionTelemetry.sys.mjs
index 57f052372c..95b71ce007 100644
--- a/toolkit/components/extensions/ExtensionTelemetry.sys.mjs
+++ b/toolkit/components/extensions/ExtensionTelemetry.sys.mjs
@@ -68,7 +68,7 @@ export function getTrimmedString(str) {
* If the resulting string is longer than 80 characters it is going to be
* trimmed using the `getTrimmedString` helper function.
*
- * @param {Error | DOMException | Components.Exception} error
+ * @param {Error | DOMException | ReturnType<typeof Components.Exception>} error
* The error object to convert into a string representation.
*
* @returns {string}
@@ -146,7 +146,7 @@ class ExtensionTelemetryMetric {
* @param {string} metric
* The Glean timing_distribution metric to record (used to retrieve the Glean metric type from the
* GLEAN_METRICS_TYPES map).
- * @param {Extension | BrowserExtensionContent} extension
+ * @param {Extension | ExtensionChild} extension
* The extension to record the telemetry for.
* @param {any | undefined} [obj = extension]
* An optional object the timing_distribution method call should be related to
@@ -207,7 +207,7 @@ class ExtensionTelemetryMetric {
* The stopwatch method to call ("start", "finish" or "cancel").
* @param {string} metric
* The stopwatch metric to record (used to retrieve the base histogram id from the HISTOGRAMS_IDS object).
- * @param {Extension | BrowserExtensionContent} extension
+ * @param {Extension | ExtensionChild} extension
* The extension to record the telemetry for.
* @param {any | undefined} [obj = extension]
* An optional telemetry stopwatch object (which defaults to the extension parameter when missing).
@@ -242,7 +242,7 @@ class ExtensionTelemetryMetric {
* @param {string} metric
* The metric to record (used to retrieve the base histogram id from the _histogram object).
* @param {object} options
- * @param {Extension | BrowserExtensionContent} options.extension
+ * @param {Extension | ExtensionChild} options.extension
* The extension to record the telemetry for.
* @param {string | undefined} [options.category]
* An optional histogram category.
@@ -290,7 +290,7 @@ class ExtensionTelemetryMetric {
// NOTE: extensionsTiming may become a property of the GLEAN_METRICS_TYPES
// map once we may introduce new histograms that are not part of the
// extensionsTiming Glean metrics category.
- Glean.extensionsTiming[metric].accumulateSamples([value]);
+ Glean.extensionsTiming[metric].accumulateSingleSample(value);
break;
}
case "labeled_counter": {
@@ -324,6 +324,8 @@ const metricsCache = new Map();
* ExtensionTelemetry.extensionStartup.stopwatchStart(extension);
* ExtensionTelemetry.browserActionPreloadResult.histogramAdd({category: "Shown", extension});
*/
+/** @type {Record<string, ExtensionTelemetryMetric>} */
+// @ts-ignore no easy way in TS to say Proxy is a different type from target.
export var ExtensionTelemetry = new Proxy(metricsCache, {
get(target, prop) {
// NOTE: if we would be start adding glean probes that do not have a unified
diff --git a/toolkit/components/extensions/ExtensionUtils.sys.mjs b/toolkit/components/extensions/ExtensionUtils.sys.mjs
index 45f22aa530..84f7b25c01 100644
--- a/toolkit/components/extensions/ExtensionUtils.sys.mjs
+++ b/toolkit/components/extensions/ExtensionUtils.sys.mjs
@@ -43,7 +43,7 @@ function promiseTimeout(delay) {
* An Error subclass for which complete error messages are always passed
* to extensions, rather than being interpreted as an unknown error.
*/
-class ExtensionError extends DOMException {
+export class ExtensionError extends DOMException {
constructor(message) {
super(message, "ExtensionError");
}
@@ -67,7 +67,7 @@ function filterStack(error) {
* only logged internally and raised to the worker script as
* the generic unexpected error).
*/
-class WorkerExtensionError extends DOMException {
+export class WorkerExtensionError extends DOMException {
constructor(message) {
super(message, "Error");
}
@@ -122,9 +122,9 @@ function getInnerWindowID(window) {
* A set with a limited number of slots, which flushes older entries as
* newer ones are added.
*
- * @param {integer} limit
+ * @param {number} limit
* The maximum size to trim the set to after it grows too large.
- * @param {integer} [slop = limit * .25]
+ * @param {number} [slop = limit * .25]
* The number of extra entries to allow in the set after it
* reaches the size limit, before it is truncated to the limit.
* @param {Iterable} [iterable]
@@ -345,5 +345,4 @@ export var ExtensionUtils = {
DefaultWeakMap,
ExtensionError,
LimitedSet,
- WorkerExtensionError,
};
diff --git a/toolkit/components/extensions/ExtensionWorkerChild.sys.mjs b/toolkit/components/extensions/ExtensionWorkerChild.sys.mjs
index 44188d7ddb..d3fd477b10 100644
--- a/toolkit/components/extensions/ExtensionWorkerChild.sys.mjs
+++ b/toolkit/components/extensions/ExtensionWorkerChild.sys.mjs
@@ -10,8 +10,14 @@
*/
import {
- ExtensionChild,
+ ChildAPIManager,
+ ChildLocalAPIImplementation,
ExtensionActivityLogChild,
+ MessageEvent,
+ Messenger,
+ Port,
+ ProxyAPIImplementation,
+ SimpleEventAPI,
} from "resource://gre/modules/ExtensionChild.sys.mjs";
import { ExtensionCommon } from "resource://gre/modules/ExtensionCommon.sys.mjs";
@@ -19,20 +25,13 @@ import {
ExtensionPageChild,
getContextChildManagerGetter,
} from "resource://gre/modules/ExtensionPageChild.sys.mjs";
-import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
+import {
+ ExtensionUtils,
+ WorkerExtensionError,
+} from "resource://gre/modules/ExtensionUtils.sys.mjs";
const { BaseContext, redefineGetter } = ExtensionCommon;
-const {
- ChildAPIManager,
- ChildLocalAPIImplementation,
- MessageEvent,
- Messenger,
- Port,
- ProxyAPIImplementation,
- SimpleEventAPI,
-} = ExtensionChild;
-
const { DefaultMap, getUniqueId } = ExtensionUtils;
/**
@@ -341,7 +340,7 @@ class WebIDLChildAPIManager extends ChildAPIManager {
* The object that represents the API request received
* (including arguments, an event listener wrapper etc)
*
- * @returns {mozIExtensionAPIRequestResult}
+ * @returns {Partial<mozIExtensionAPIRequestResult>}
* Result for the API request, either a value to be returned
* (which has to be a value that can be structure cloned
* if the request was originated from the worker thread) or
@@ -373,9 +372,8 @@ class WebIDLChildAPIManager extends ChildAPIManager {
* into the expected mozIExtensionAPIRequestResult.
*
* @param {Error | WorkerExtensionError} error
- * @returns {mozIExtensionAPIRequestResult}
+ * @returns {Partial<mozIExtensionAPIRequestResult>}
*/
-
handleExtensionError(error) {
// Propagate an extension error to the caller on the worker thread.
if (error instanceof this.context.Error) {
@@ -402,7 +400,6 @@ class WebIDLChildAPIManager extends ChildAPIManager {
* @returns {any}
* @throws {Error | WorkerExtensionError}
*/
-
callAPIImplementation(request, impl) {
const { requestType, normalizedArgs } = request;
@@ -480,7 +477,7 @@ class WebIDLChildAPIManager extends ChildAPIManager {
* Return an ExtensionAPI class instance given its namespace.
*
* @param {string} namespace
- * @returns {ExtensionAPI}
+ * @returns {import("ExtensionCommon.sys.mjs").ExtensionAPI}
*/
getExtensionAPIInstance(namespace) {
return this.apiCan.apis.get(namespace);
@@ -569,7 +566,7 @@ class WorkerContextChild extends BaseContext {
* This WorkerContextChild represents an addon execution environment
* that is running on the worker thread in an extension child process.
*
- * @param {BrowserExtensionContent} extension This context's owner.
+ * @param {ExtensionChild} extension This context's owner.
* @param {object} params
* @param {mozIExtensionServiceWorkerInfo} params.serviceWorkerInfo
*/
@@ -607,7 +604,7 @@ class WorkerContextChild extends BaseContext {
// ExtensionAPIRequestHandler as errors that should be propagated to
// the worker thread and received by extension code that originated
// the API request.
- Error: ExtensionUtils.WorkerExtensionError,
+ Error: WorkerExtensionError,
};
}
@@ -616,6 +613,7 @@ class WorkerContextChild extends BaseContext {
return { workerDescriptorId };
}
+ /** @type {ConduitGen} */
openConduit(subject, address) {
let proc = ChromeUtils.domProcessChild;
let conduit = proc.getActor("ProcessConduits").openConduit(subject, {
@@ -658,7 +656,7 @@ class WorkerContextChild extends BaseContext {
* Captures the most recent stack frame from the WebIDL API request being
* processed.
*
- * @returns {SavedFrame?}
+ * @returns {nsIStackFrame}
*/
getCaller() {
return this.webidlAPIRequest?.callerSavedFrame;
@@ -719,7 +717,7 @@ export var ExtensionWorkerChild = {
* Create an extension worker context (on a mozExtensionAPIRequest with
* requestType "initWorkerContext").
*
- * @param {BrowserExtensionContent} extension
+ * @param {ExtensionChild} extension
* The extension for which the context should be created.
* @param {mozIExtensionServiceWorkerInfo} serviceWorkerInfo
*/
@@ -751,7 +749,7 @@ export var ExtensionWorkerChild = {
* Get an existing extension worker context for the given extension and
* service worker.
*
- * @param {BrowserExtensionContent} extension
+ * @param {ExtensionChild} extension
* The extension for which the context should be created.
* @param {mozIExtensionServiceWorkerInfo} serviceWorkerInfo
*
diff --git a/toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs b/toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs
index 27323dc8b3..3489a2caba 100644
--- a/toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs
+++ b/toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs
@@ -7,6 +7,7 @@
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import { XPCShellContentUtils } from "resource://testing-common/XPCShellContentUtils.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -32,9 +33,9 @@ let BASE_MANIFEST = Object.freeze({
});
class ExtensionWrapper {
- /** @type {AddonWrapper} */
+ /** @type {import("resource://gre/modules/addons/XPIDatabase.sys.mjs").AddonWrapper} */
addon;
- /** @type {Promise<AddonWrapper>} */
+ /** @type {Promise} */
addonPromise;
/** @type {nsIFile[]} */
cleanupFiles;
diff --git a/toolkit/components/extensions/Schemas.sys.mjs b/toolkit/components/extensions/Schemas.sys.mjs
index e98dfb36f0..b107036355 100644
--- a/toolkit/components/extensions/Schemas.sys.mjs
+++ b/toolkit/components/extensions/Schemas.sys.mjs
@@ -11,6 +11,7 @@ import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
var { DefaultMap, DefaultWeakMap } = ExtensionUtils;
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -262,6 +263,23 @@ const POSTPROCESSORS = {
return string;
},
+ webRequestBlockingOrAuthProviderPermissionRequired(string, context) {
+ if (
+ string === "blocking" &&
+ !(
+ context.hasPermission("webRequestBlocking") ||
+ context.hasPermission("webRequestAuthProvider")
+ )
+ ) {
+ throw new context.cloneScope.Error(
+ "Using webRequest.onAuthRequired.addListener with the " +
+ "blocking option requires either the 'webRequestBlocking' " +
+ "or 'webRequestAuthProvider' permission."
+ );
+ }
+
+ return string;
+ },
requireBackgroundServiceWorkerEnabled(value, context) {
if (WebExtensionPolicy.backgroundServiceWorkerEnabled) {
return value;
@@ -904,7 +922,8 @@ class InjectionContext extends Context {
* @param {string} _namespace The full path to the namespace of the API, minus
* the name of the method or property. E.g. "storage.local".
* @param {string} _name The name of the method, property or event.
- * @returns {SchemaAPIInterface} The implementation of the API.
+ * @returns {import("ExtensionCommon.sys.mjs").SchemaAPIInterface}
+ * The implementation of the API.
*/
getImplementation(_namespace, _name) {
throw new Error("Not implemented");
@@ -1125,7 +1144,7 @@ const FORMATS = {
},
strictRelativeUrl(string, context) {
- void FORMATS.unresolvedRelativeUrl(string, context);
+ void FORMATS.unresolvedRelativeUrl(string);
return FORMATS.relativeUrl(string, context);
},
@@ -1220,8 +1239,8 @@ const FORMATS = {
throw new Error(errorMessage);
},
- manifestShortcutKeyOrEmpty(string, context) {
- return string === "" ? "" : FORMATS.manifestShortcutKey(string, context);
+ manifestShortcutKeyOrEmpty(string) {
+ return string === "" ? "" : FORMATS.manifestShortcutKey(string);
},
versionString(string, context) {
@@ -1470,26 +1489,33 @@ class Type extends Entry {
}
}
- // Takes a value, checks that it has the correct type, and returns a
- // "normalized" version of the value. The normalized version will
- // include "nulls" in place of omitted optional properties. The
- // result of this function is either {error: "Some type error"} or
- // {value: <normalized-value>}.
+ /**
+ * Takes a value, checks that it has the correct type, and returns a
+ * "normalized" version of the value. The normalized version will
+ * include "nulls" in place of omitted optional properties. The
+ * result of this function is either {error: "Some type error"} or
+ * {value: <normalized-value>}.
+ */
normalize(value, context) {
return context.error("invalid type");
}
- // Unlike normalize, this function does a shallow check to see if
- // |baseType| (one of the possible getValueBaseType results) is
- // valid for this type. It returns true or false. It's used to fill
- // in optional arguments to functions before actually type checking
-
- checkBaseType() {
+ /**
+ * Unlike normalize, this function does a shallow check to see if
+ * |baseType| (one of the possible getValueBaseType results) is
+ * valid for this type. It returns true or false. It's used to fill
+ * in optional arguments to functions before actually type checking
+ *
+ * @param {string} _baseType
+ */
+ checkBaseType(_baseType) {
return false;
}
- // Helper method that simply relies on checkBaseType to implement
- // normalize. Subclasses can choose to use it or not.
+ /**
+ * Helper method that simply relies on checkBaseType to implement
+ * normalize. Subclasses can choose to use it or not.
+ */
normalizeBase(type, value, context) {
if (this.checkBaseType(getValueBaseType(value))) {
this.checkDeprecated(context, value);
@@ -3458,22 +3484,26 @@ class SchemaRoots extends Namespaces {
* other schema roots. May extend a base namespace, in which case schemas in
* this root may refer to types in a base, but not vice versa.
*
- * @param {SchemaRoot|Array<SchemaRoot>|null} base
- * A base schema root (or roots) from which to derive, or null.
- * @param {Map<string, Array|StructuredCloneHolder>} schemaJSON
- * A map of schema URLs and corresponding JSON blobs from which to
- * populate this root namespace.
+ * @implements {SchemaInject}
*/
export class SchemaRoot extends Namespace {
+ /**
+ * @param {SchemaRoot|SchemaRoot[]} base
+ * A base schema root (or roots) from which to derive, or null.
+ * @param {Map<string, Array|StructuredCloneHolder>} schemaJSON
+ * A map of schema URLs and corresponding JSON blobs from which to
+ * populate this root namespace.
+ */
constructor(base, schemaJSON) {
super(null, "", []);
if (Array.isArray(base)) {
- base = new SchemaRoots(this, base);
+ this.base = new SchemaRoots(this, base);
+ } else {
+ this.base = base;
}
this.root = this;
- this.base = base;
this.schemaJSON = schemaJSON;
}
@@ -3555,7 +3585,7 @@ export class SchemaRoot extends Namespace {
parseSchemas() {
for (let [key, schema] of this.schemaJSON.entries()) {
try {
- if (typeof schema.deserialize === "function") {
+ if (StructuredCloneHolder.isInstance(schema)) {
schema = schema.deserialize(globalThis, isParentProcess);
// If we're in the parent process, we need to keep the
@@ -3607,7 +3637,7 @@ export class SchemaRoot extends Namespace {
*
* @param {object} dest The root namespace for the APIs.
* This object is usually exposed to extensions as "chrome" or "browser".
- * @param {object} wrapperFuncs An implementation of the InjectionContext
+ * @param {InjectionContext} wrapperFuncs An implementation of the InjectionContext
* interface, which runs the actual functionality of the generated API.
*/
inject(dest, wrapperFuncs) {
@@ -3651,6 +3681,11 @@ export class SchemaRoot extends Namespace {
}
}
+/**
+ * @typedef {{ inject: typeof Schemas.inject }} SchemaInject
+ * Interface SchemaInject as used by SchemaApiManager,
+ * with the one method shared across Schemas and SchemaRoot.
+ */
export var Schemas = {
initialized: false,
@@ -3676,6 +3711,7 @@ export var Schemas = {
extContext => new Context(extContext)
),
+ /** @returns {SchemaRoot} */
get rootSchema() {
if (!this.initialized) {
this.init();
@@ -3833,7 +3869,7 @@ export var Schemas = {
*
* @param {object} dest The root namespace for the APIs.
* This object is usually exposed to extensions as "chrome" or "browser".
- * @param {object} wrapperFuncs An implementation of the InjectionContext
+ * @param {InjectionContext} wrapperFuncs An implementation of the InjectionContext
* interface, which runs the actual functionality of the generated API.
*/
inject(dest, wrapperFuncs) {
@@ -3898,6 +3934,7 @@ export var Schemas = {
? `${apiNamespace}.${apiName}.${requestType}`
: `${apiNamespace}.${apiName}`
).split(".");
+ /** @type {Namespace|CallEntry} */
let apiSchema = this.getNamespace(ns);
// Keep track of the current schema path, populated while navigating the nested API schema
@@ -3926,7 +3963,7 @@ export var Schemas = {
throw new Error(`API Schema not found for ${schemaPath.join(".")}`);
}
- if (!apiSchema.checkParameters) {
+ if (!(apiSchema instanceof CallEntry)) {
throw new Error(
`Unexpected API Schema type for ${schemaPath.join(
"."
diff --git a/toolkit/components/extensions/WebNavigation.sys.mjs b/toolkit/components/extensions/WebNavigation.sys.mjs
index 7235aaeb4e..c33a45db81 100644
--- a/toolkit/components/extensions/WebNavigation.sys.mjs
+++ b/toolkit/components/extensions/WebNavigation.sys.mjs
@@ -4,6 +4,7 @@
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -23,9 +24,12 @@ function getBrowser(bc) {
}
export var WebNavigationManager = {
- // Map[string -> Map[listener -> URLFilter]]
+ /** @type {Map<string, Set<callback>>} */
listeners: new Map(),
+ /** @type {WeakMap<XULBrowserElement, object>} */
+ recentTabTransitionData: new WeakMap(),
+
init() {
// Collect recent tab transition data in a WeakMap:
// browser -> tabTransitionData
@@ -123,9 +127,9 @@ export var WebNavigationManager = {
* The data for the autocompleted item.
* @param {object} [acData.result]
* The result information associated with the navigation action.
- * @param {UrlbarUtils.RESULT_TYPE} [acData.result.type]
+ * @param {typeof lazy.UrlbarUtils.RESULT_TYPE} [acData.result.type]
* The result type associated with the navigation action.
- * @param {UrlbarUtils.RESULT_SOURCE} [acData.result.source]
+ * @param {typeof lazy.UrlbarUtils.RESULT_SOURCE} [acData.result.source]
* The result source associated with the navigation action.
*/
onURLBarUserStartNavigation(acData) {
diff --git a/toolkit/components/extensions/WebNavigationFrames.sys.mjs b/toolkit/components/extensions/WebNavigationFrames.sys.mjs
index 211698a88e..20db413bc0 100644
--- a/toolkit/components/extensions/WebNavigationFrames.sys.mjs
+++ b/toolkit/components/extensions/WebNavigationFrames.sys.mjs
@@ -45,7 +45,7 @@ function getParentFrameId(bc) {
/**
* Convert a BrowsingContext into internal FrameDetail json.
*
- * @param {BrowsingContext} bc
+ * @param {CanonicalBrowsingContext} bc
* @returns {FrameDetail}
*/
function getFrameDetail(bc) {
diff --git a/toolkit/components/extensions/extIWebNavigation.idl b/toolkit/components/extensions/extIWebNavigation.idl
index 3095d93d9f..1db9e73808 100644
--- a/toolkit/components/extensions/extIWebNavigation.idl
+++ b/toolkit/components/extensions/extIWebNavigation.idl
@@ -17,8 +17,8 @@ interface extIWebNavigation : nsISupports
void onHistoryChange(in BrowsingContext bc,
in jsval transitionData,
in nsIURI location,
- in bool isHistoryStateUpdated,
- in bool isReferenceFragmentUpdated);
+ in boolean isHistoryStateUpdated,
+ in boolean isReferenceFragmentUpdated);
void onStateChange(in BrowsingContext bc,
in nsIURI requestURI,
diff --git a/toolkit/components/extensions/mozIExtensionAPIRequestHandling.idl b/toolkit/components/extensions/mozIExtensionAPIRequestHandling.idl
index 0a2e3c7a5d..d7eb40345b 100644
--- a/toolkit/components/extensions/mozIExtensionAPIRequestHandling.idl
+++ b/toolkit/components/extensions/mozIExtensionAPIRequestHandling.idl
@@ -35,7 +35,7 @@ interface mozIExtensionListenerCallOptions : nsISupports
// An optional boolean to be set to true if the api object should be
// prepended to the rest of the call arguments (by default it is appended).
- readonly attribute bool apiObjectPrepended;
+ readonly attribute boolean apiObjectPrepended;
cenum CallbackType: 8 {
// Default: no callback argument is passed to the call to the event listener.
diff --git a/toolkit/components/extensions/mozIExtensionProcessScript.idl b/toolkit/components/extensions/mozIExtensionProcessScript.idl
index 84b33a9d02..47e442149c 100644
--- a/toolkit/components/extensions/mozIExtensionProcessScript.idl
+++ b/toolkit/components/extensions/mozIExtensionProcessScript.idl
@@ -17,5 +17,5 @@ interface mozIExtensionProcessScript : nsISupports
in mozIDOMWindow window);
void initExtensionDocument(in nsISupports extension, in Document doc,
- in bool privileged);
+ in boolean privileged);
};
diff --git a/toolkit/components/extensions/parent/ext-identity.js b/toolkit/components/extensions/parent/ext-identity.js
index bd53163305..f0f63dbf34 100644
--- a/toolkit/components/extensions/parent/ext-identity.js
+++ b/toolkit/components/extensions/parent/ext-identity.js
@@ -12,7 +12,7 @@ var { promiseDocumentLoaded } = ExtensionUtils;
const checkRedirected = (url, redirectURI) => {
return new Promise((resolve, reject) => {
- let xhr = new XMLHttpRequest();
+ let xhr = new XMLHttpRequest({ mozAnon: false });
xhr.open("GET", url);
// We expect this if the user has not authenticated.
xhr.onload = () => {
diff --git a/toolkit/components/extensions/parent/ext-runtime.js b/toolkit/components/extensions/parent/ext-runtime.js
index d1c03d9e0d..3f9c0f8857 100644
--- a/toolkit/components/extensions/parent/ext-runtime.js
+++ b/toolkit/components/extensions/parent/ext-runtime.js
@@ -248,7 +248,7 @@ this.runtime = class extends ExtensionAPIPersistent {
},
openOptionsPage: function () {
- if (!extension.manifest.options_ui) {
+ if (!extension.optionsPageProperties) {
return Promise.reject({ message: "No `options_ui` declared" });
}
diff --git a/toolkit/components/extensions/parent/ext-webRequest.js b/toolkit/components/extensions/parent/ext-webRequest.js
index 4f0ea90abd..f94c773e2e 100644
--- a/toolkit/components/extensions/parent/ext-webRequest.js
+++ b/toolkit/components/extensions/parent/ext-webRequest.js
@@ -64,7 +64,11 @@ function registerEvent(
filter2.incognito = filter.incognito;
}
- let blockingAllowed = extension.hasPermission("webRequestBlocking");
+ let blockingAllowed =
+ eventName == "onAuthRequired"
+ ? extension.hasPermission("webRequestBlocking") ||
+ extension.hasPermission("webRequestAuthProvider")
+ : extension.hasPermission("webRequestBlocking");
let info2 = [];
if (info) {
diff --git a/toolkit/components/extensions/schemas/manifest.json b/toolkit/components/extensions/schemas/manifest.json
index 14f78ba564..384b168e39 100644
--- a/toolkit/components/extensions/schemas/manifest.json
+++ b/toolkit/components/extensions/schemas/manifest.json
@@ -173,11 +173,15 @@
"optional": true
},
+ "options_page": {
+ "$ref": "ExtensionURL",
+ "optional": true,
+ "description": "Alias property for options_ui.page, ignored when options_ui.page is set. When using this property the options page is always opened in a new tab."
+ },
+
"options_ui": {
"type": "object",
-
"optional": true,
-
"properties": {
"page": { "$ref": "ExtensionURL" },
"browser_style": {
diff --git a/toolkit/components/extensions/schemas/web_request.json b/toolkit/components/extensions/schemas/web_request.json
index e4405f24c3..a1528d87f6 100644
--- a/toolkit/components/extensions/schemas/web_request.json
+++ b/toolkit/components/extensions/schemas/web_request.json
@@ -9,6 +9,7 @@
"type": "string",
"enum": [
"webRequest",
+ "webRequestAuthProvider",
"webRequestBlocking",
"webRequestFilterResponse",
"webRequestFilterResponse.serviceWorkerScript"
@@ -82,7 +83,7 @@
"id": "OnAuthRequiredOptions",
"type": "string",
"enum": ["responseHeaders", "blocking", "asyncBlocking"],
- "postprocess": "webRequestBlockingPermissionRequired"
+ "postprocess": "webRequestBlockingOrAuthProviderPermissionRequired"
},
{
"id": "OnResponseStartedOptions",
diff --git a/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs b/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs
index 1418ccca29..ec4f2057b9 100644
--- a/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs
+++ b/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs
@@ -13,7 +13,7 @@ use std::{
};
use golden_gate::{ApplyTask, BridgedEngine, FerryTask};
-use moz_task::{self, DispatchOptions, TaskRunnable};
+use moz_task::{DispatchOptions, TaskRunnable};
use nserror::{nsresult, NS_OK};
use nsstring::{nsACString, nsCString, nsString};
use thin_vec::ThinVec;
diff --git a/toolkit/components/extensions/test/browser/browser.toml b/toolkit/components/extensions/test/browser/browser.toml
index 33d54bddc2..33a3a6dc64 100644
--- a/toolkit/components/extensions/test/browser/browser.toml
+++ b/toolkit/components/extensions/test/browser/browser.toml
@@ -9,7 +9,6 @@ support-files = [
["browser_ext_downloads_filters.js"]
["browser_ext_downloads_referrer.js"]
-https_first_disabled = true
["browser_ext_eventpage_disableResetIdleForTest.js"]
diff --git a/toolkit/components/extensions/test/browser/browser_ext_extension_page_tab_navigated.js b/toolkit/components/extensions/test/browser/browser_ext_extension_page_tab_navigated.js
index 429d584a17..f85bbfe595 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_extension_page_tab_navigated.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_extension_page_tab_navigated.js
@@ -3,10 +3,6 @@
"use strict";
-const { AddonTestUtils } = ChromeUtils.importESModule(
- "resource://testing-common/AddonTestUtils.sys.mjs"
-);
-
// The test tasks in this test file tends to trigger an intermittent
// exception raised from JSActor::AfterDestroy, because of a race between
// when the WebExtensions API event is being emitted from the parent process
@@ -18,23 +14,6 @@ PromiseTestUtils.allowMatchingRejectionsGlobally(
/Actor 'Conduits' destroyed before query 'RunListener' was resolved/
);
-AddonTestUtils.initMochitest(this);
-
-const server = AddonTestUtils.createHttpServer({
- hosts: ["example.com", "anotherwebpage.org"],
-});
-
-server.registerPathHandler("/", (request, response) => {
- response.write(`<!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>test webpage</title>
- </head>
- </html>
- `);
-});
-
function createTestExtPage({ script }) {
return `<!DOCTYPE html>
<html>
@@ -55,7 +34,7 @@ function createTestExtPageScript(name) {
);
browser.test.sendMessage(`event-received:${pageName}`);
},
- { types: ["main_frame"], urls: ["http://example.com/*"] }
+ { types: ["main_frame"], urls: ["https://example.com/*"] }
);
/* eslint-disable mozilla/balanced-listeners */
window.addEventListener("pageshow", () => {
@@ -93,7 +72,7 @@ async function triggerWebRequestListener(webPageURL) {
add_task(async function test_extension_page_sameprocess_navigation() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
- permissions: ["webRequest", "http://example.com/*"],
+ permissions: ["webRequest", "https://example.com/*"],
},
files: {
"extpage1.html": createTestExtPage({ script: "extpage1.js" }),
@@ -116,7 +95,7 @@ add_task(async function test_extension_page_sameprocess_navigation() {
info("Wait for the extension page to be loaded");
await extension.awaitMessage("pageshow:extpage1");
- await triggerWebRequestListener("http://example.com");
+ await triggerWebRequestListener("https://example.com");
await extension.awaitMessage("event-received:extpage1");
ok(true, "extpage1 got a webRequest event as expected");
@@ -131,7 +110,7 @@ add_task(async function test_extension_page_sameprocess_navigation() {
info(
"Trigger a web request event and expect extpage2 to be the only one receiving it"
);
- await triggerWebRequestListener("http://example.com");
+ await triggerWebRequestListener("https://example.com");
await extension.awaitMessage("event-received:extpage2");
ok(true, "extpage2 got a webRequest event as expected");
@@ -146,7 +125,7 @@ add_task(async function test_extension_page_sameprocess_navigation() {
await extension.awaitMessage("pagehide:extpage2");
// We only expect extpage1 to be able to receive API events.
- await triggerWebRequestListener("http://example.com");
+ await triggerWebRequestListener("https://example.com");
await extension.awaitMessage("event-received:extpage1");
ok(true, "extpage1 got a webRequest event as expected");
@@ -159,7 +138,7 @@ add_task(async function test_extension_page_sameprocess_navigation() {
add_task(async function test_extension_page_context_navigated_to_web_page() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
- permissions: ["webRequest", "http://example.com/*"],
+ permissions: ["webRequest", "https://example.com/*"],
},
files: {
"extpage.html": createTestExtPage({ script: "extpage.js" }),
@@ -178,8 +157,8 @@ add_task(async function test_extension_page_context_navigated_to_web_page() {
// navigated will be intermittently able to receive an event before it
// is navigated to the webpage url (and moved into the BFCache or destroyed)
// and trigger an intermittent failure of this test.
- const webPageURL = "http://anotherwebpage.org/";
- const triggerWebRequestURL = "http://example.com/";
+ const webPageURL = "https://example.net/";
+ const triggerWebRequestURL = "https://example.com/";
info("Opening extension page in a new tab");
const extPageTab1 = await BrowserTestUtils.addTab(gBrowser, extPageURL);
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js b/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js
index 8e2f5446c9..4ee92bf798 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js
@@ -26,17 +26,20 @@ async function test_ntp_theme(theme, isBrightText) {
});
let browser = gBrowser.selectedBrowser;
-
let { originalBackground, originalCardBackground, originalColor } =
await SpecialPowers.spawn(browser, [], function () {
let doc = content.document;
ok(
!doc.documentElement.hasAttribute("lwt-newtab"),
- "New tab page should not have lwt-newtab attribute"
+ `New tab page should not have lwt-newtab attribute`
+ );
+ ok(
+ !doc.documentElement.hasAttribute("lwtheme"),
+ `New tab page should not have lwtheme attribute`
);
ok(
!doc.documentElement.hasAttribute("lwt-newtab-brighttext"),
- `New tab page should not have lwt-newtab-brighttext attribute`
+ `New tab page not should have lwt-newtab-brighttext attribute`
);
return {
@@ -64,22 +67,39 @@ async function test_ntp_theme(theme, isBrightText) {
Services.ppmm.sharedData.flush();
+ let hasNtpColors = !!(
+ theme.colors.ntp_background ||
+ theme.colors.ntp_text ||
+ theme.colors.ntp_card_background
+ );
await SpecialPowers.spawn(
browser,
[
{
isBrightText,
+ hasNtpColors,
background: hexToCSS(theme.colors.ntp_background),
card_background: hexToCSS(theme.colors.ntp_card_background),
color: hexToCSS(theme.colors.ntp_text),
},
],
- async function ({ isBrightText, background, card_background, color }) {
+ async function ({
+ isBrightText,
+ hasNtpColors,
+ background,
+ card_background,
+ color,
+ }) {
let doc = content.document;
- ok(
+ is(
doc.documentElement.hasAttribute("lwt-newtab"),
+ hasNtpColors,
"New tab page should have lwt-newtab attribute"
);
+ ok(
+ doc.documentElement.hasAttribute("lwtheme"),
+ "New tab page should have lwtheme attribute"
+ );
is(
doc.documentElement.hasAttribute("lwt-newtab-brighttext"),
isBrightText,
@@ -88,22 +108,24 @@ async function test_ntp_theme(theme, isBrightText) {
} have lwt-newtab-brighttext attribute`
);
- is(
- content.getComputedStyle(doc.body).backgroundColor,
- background,
- "New tab page background should be set."
- );
- is(
- content.getComputedStyle(doc.querySelector(".top-site-outer .tile"))
- .backgroundColor,
- card_background,
- "New tab page card background should be set."
- );
- is(
- content.getComputedStyle(doc.querySelector(".outer-wrapper")).color,
- color,
- "New tab page text color should be set."
- );
+ if (hasNtpColors) {
+ is(
+ content.getComputedStyle(doc.body).backgroundColor,
+ background,
+ "New tab page background should be set."
+ );
+ is(
+ content.getComputedStyle(doc.querySelector(".top-site-outer .tile"))
+ .backgroundColor,
+ card_background,
+ "New tab page card background should be set."
+ );
+ is(
+ content.getComputedStyle(doc.querySelector(".outer-wrapper")).color,
+ color,
+ "New tab page text color should be set."
+ );
+ }
}
);
@@ -127,6 +149,10 @@ async function test_ntp_theme(theme, isBrightText) {
"New tab page should not have lwt-newtab attribute"
);
ok(
+ !doc.documentElement.hasAttribute("lwtheme"),
+ "New tab page should not have lwtheme attribute"
+ );
+ ok(
!doc.documentElement.hasAttribute("lwt-newtab-brighttext"),
`New tab page should not have lwt-newtab-brighttext attribute`
);
@@ -151,6 +177,17 @@ async function test_ntp_theme(theme, isBrightText) {
);
}
+async function waitForDarkMode(value) {
+ info(`waiting for dark mode: ${value}`);
+ const mq = matchMedia("(prefers-color-scheme: dark)");
+ if (mq.matches == value) {
+ return;
+ }
+ await new Promise(r => {
+ mq.addEventListener("change", r, { once: true });
+ });
+}
+
add_task(async function test_support_ntp_colors() {
await SpecialPowers.pushPrefEnv({
set: [
@@ -163,11 +200,13 @@ add_task(async function test_support_ntp_colors() {
["layout.css.prefers-color-scheme.content-override", 1],
// Override the system color scheme to light so this test passes on
// machines with dark system color scheme.
+ // FIXME(emilio): This doesn't seem working reliably, at least on macOS.
["ui.systemUsesDarkTheme", 0],
],
});
NewTabPagePreloading.removePreloadedBrowser(window);
for (let url of ["about:newtab", "about:home"]) {
+ await waitForDarkMode(false);
info("Opening url: " + url);
await BrowserTestUtils.withNewTab({ gBrowser, url }, async browser => {
await waitForAboutNewTabReady(browser, url);
@@ -185,6 +224,7 @@ add_task(async function test_support_ntp_colors() {
url
);
+ await waitForDarkMode(false);
await test_ntp_theme(
{
colors: {
@@ -198,6 +238,19 @@ add_task(async function test_support_ntp_colors() {
true,
url
);
+
+ // Test a theme without any new tab page colors
+ await waitForDarkMode(false);
+ await test_ntp_theme(
+ {
+ colors: {
+ frame: ACCENT_COLOR,
+ tab_background_text: TEXT_COLOR,
+ },
+ },
+ false,
+ url
+ );
});
}
});
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js b/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js
index 3b739322d6..54152d005c 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js
@@ -39,6 +39,10 @@ function test_ntp_theme(browser, theme, isBrightText) {
doc.documentElement.hasAttribute("lwt-newtab"),
"New tab page should have lwt-newtab attribute"
);
+ ok(
+ doc.documentElement.hasAttribute("lwtheme"),
+ "New tab page should have lwtheme attribute"
+ );
is(
doc.documentElement.hasAttribute("lwt-newtab-brighttext"),
isBrightText,
@@ -90,6 +94,10 @@ function test_ntp_default_theme(browser) {
"New tab page should not have lwt-newtab attribute"
);
ok(
+ !doc.documentElement.hasAttribute("lwtheme"),
+ "New tab page should not have lwtheme attribute"
+ );
+ ok(
!doc.documentElement.hasAttribute("lwt-newtab-brighttext"),
`New tab page should not have lwt-newtab-brighttext attribute`
);
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_pbm.js b/toolkit/components/extensions/test/browser/browser_ext_themes_pbm.js
index 3b36a256d0..d2dfb16e72 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_pbm.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_pbm.js
@@ -336,7 +336,7 @@ add_task(async function test_pbm_dark_page_info() {
await BrowserTestUtils.withNewTab(
{ gBrowser: win.gBrowser, url: "https://example.com" },
async () => {
- let pageInfo = win.BrowserPageInfo(null, "securityTab");
+ let pageInfo = win.BrowserCommands.pageInfo(null, "securityTab");
await BrowserTestUtils.waitForEvent(pageInfo, "page-info-init");
let prefersColorScheme = await getPrefersColorSchemeInfo({
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js b/toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js
index 89ebd3ae68..fdee1eb72d 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js
@@ -114,8 +114,9 @@ add_task(async function test_sanitization_transparent() {
await extension.startup();
let navbar = document.querySelector("#nav-bar");
- Assert.ok(
- window.getComputedStyle(navbar).boxShadow.includes("rgba(0, 0, 0, 0)"),
+ Assert.equal(
+ window.getComputedStyle(navbar).borderTopColor,
+ "rgba(0, 0, 0, 0)",
"Top separator should be transparent"
);
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js b/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js
index 4da4927ccf..1b953269b6 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js
@@ -48,10 +48,9 @@ add_task(async function test_support_separator_properties() {
await deprecatedMessagePromise;
let navbar = document.querySelector("#nav-bar");
- Assert.ok(
- window
- .getComputedStyle(navbar)
- .boxShadow.includes(`rgb(${hexToRGB(SEPARATOR_TOP_COLOR).join(", ")})`),
+ Assert.equal(
+ window.getComputedStyle(navbar).borderTopColor,
+ `rgb(${hexToRGB(SEPARATOR_TOP_COLOR).join(", ")})`,
"Top separator color properly set"
);
diff --git a/toolkit/components/extensions/test/mochitest/test_check_startupcache.html b/toolkit/components/extensions/test/mochitest/test_check_startupcache.html
index 8cb529d18d..d1157472ec 100644
--- a/toolkit/components/extensions/test/mochitest/test_check_startupcache.html
+++ b/toolkit/components/extensions/test/mochitest/test_check_startupcache.html
@@ -41,7 +41,7 @@ add_task(async function check_ExtensionParent_StartupCache_is_non_empty() {
let map = await chromeScript.promiseOneMessage("StartupCache_data");
chromeScript.destroy();
- // "manifests" is populated by Extension's parseManifest in Extension.jsm.
+ // "manifests" is populated by Extension's parseManifest in Extension.sys.mjs.
const keys = ["manifests", "mochikit@mozilla.org", "2.0", "en-US"];
for (let key of keys) {
map = map.get(key);
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html b/toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html
index 708b5522c3..e7fdd6aad0 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html
@@ -354,11 +354,11 @@ add_task(async function test_contentscript_clipboard_nocontents_readtext() {
await extension.unload();
});
-// Test that performing read(...) when the clipboard is empty returns an empty ClipboardItem
+// Test that performing read(...) when the clipboard is empty returns no ClipboardItem
add_task(async function test_contentscript_clipboard_nocontents_read() {
function contentScript() {
clipboardRead().then(function(items) {
- if (items[0].types.length) {
+ if (items.length) {
browser.test.fail("Read read the wrong thing from clipboard, " +
"ClipboardItem has this many entries: " + items[0].types.length);
} else {
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_securecontext.html b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_securecontext.html
index 093c26898f..f9230c9a5f 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_securecontext.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_securecontext.html
@@ -14,6 +14,8 @@
await SpecialPowers.pushPrefEnv({
"set": [
["dom.w3c_pointer_events.getcoalescedevents_only_in_securecontext", true],
+ // Test is intentionally testing in non-secure contexts.
+ ["dom.security.https_first", false]
]
});
});
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html
index 85f98d5034..d474a7caee 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html
@@ -18,6 +18,7 @@ function background() {
browser.test.assertTrue(port.sender.url.endsWith("file_sample.html"), "URL correct");
browser.test.assertTrue(port.sender.tab.url.endsWith("file_sample.html"), "tab URL correct");
browser.test.assertEq(port.sender.frameId, 0, "frameId of top frame");
+ browser.test.assertEq(new URL(port.sender.url).origin, port.sender.origin, "sender origin correct");
let expected = "message 1";
port.onMessage.addListener(msg => {
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect2.html b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect2.html
index 13b9029c48..32620b5b3b 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect2.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect2.html
@@ -21,6 +21,7 @@ function backgroundScript(token) {
browser.runtime.onConnect.addListener(port => {
browser.test.assertTrue(port.sender.url.endsWith("file_sample.html"), "sender url correct");
browser.test.assertTrue(port.sender.tab.url.endsWith("file_sample.html"), "sender url correct");
+ browser.test.assertEq(new URL(port.sender.url).origin, port.sender.origin, "sender origin correct");
let tabId = port.sender.tab.id;
browser.tabs.connect(tabId, {name: token});
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_iframe.html b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_iframe.html
index 9c64635063..e3e0e80f2a 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_iframe.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_iframe.html
@@ -30,12 +30,15 @@ add_task(async function connect_from_background_frame() {
}
async function background() {
const FRAME_URL = "https://example.com/tests/toolkit/components/extensions/test/mochitest/file_sample.html";
+ const FRAME_ORIGIN = new URL(FRAME_URL).origin;
+
browser.runtime.onConnect.addListener(port => {
// The next two assertions are the reason for this being a mochitest
// instead of a xpcshell test.
browser.test.assertEq(port.sender.tab, undefined, "Sender is not a tab");
browser.test.assertEq(port.sender.frameId, undefined, "frameId unset");
browser.test.assertEq(port.sender.url, FRAME_URL, "Expected sender URL");
+ browser.test.assertEq(port.sender.origin, FRAME_ORIGIN, "Expected sender origin");
port.onMessage.addListener(msg => {
browser.test.assertEq("pong", msg, "Reply from content script");
port.disconnect();
@@ -88,6 +91,8 @@ add_task(async function connect_from_content_script_in_frame() {
async function background() {
const TAB_URL = "https://example.org/tests/toolkit/components/extensions/test/mochitest/file_contains_iframe.html";
const FRAME_URL = "https://example.org/tests/toolkit/components/extensions/test/mochitest/file_contains_img.html";
+ const FRAME_ORIGIN = new URL(FRAME_URL).origin;
+
let createdTab;
browser.runtime.onConnect.addListener(port => {
// The next two assertions are the reason for this being a mochitest
@@ -95,6 +100,7 @@ add_task(async function connect_from_content_script_in_frame() {
browser.test.assertEq(port.sender.tab.url, TAB_URL, "Sender is the tab");
browser.test.assertTrue(port.sender.frameId > 0, "frameId is set");
browser.test.assertEq(port.sender.url, FRAME_URL, "Expected sender URL");
+ browser.test.assertEq(port.sender.origin, FRAME_ORIGIN, "Expected sender origin");
browser.test.assertEq(createdTab.id, port.sender.tab.id, "Tab to close");
browser.tabs.remove(port.sender.tab.id).then(() => {
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_tabs_captureTab.html b/toolkit/components/extensions/test/mochitest/test_ext_tabs_captureTab.html
index ab06a965ed..20f368a0ae 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_tabs_captureTab.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_tabs_captureTab.html
@@ -319,6 +319,141 @@ add_task(async function testCaptureVisibleTabPermissions() {
await extension.awaitFinish("captureVisibleTabPermissions");
await extension.unload();
});
+
+add_task(async function testCaptureVisibleTabWithActiveTab() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ browser_action: {
+ default_area: "navbar",
+ },
+ permissions: ["webNavigation", "tabs", "activeTab"],
+ },
+
+ async background() {
+ // Wait for the page (in the test window) to load.
+ await new Promise(resolve => {
+ browser.webNavigation.onCompleted.addListener(
+ () => resolve(),
+ {url: [{schemes: ["data"]}]});
+ });
+
+ browser.browserAction.onClicked.addListener(async tab => {
+ await browser.tabs.captureVisibleTab(tab.windowId);
+ browser.test.notifyPass("captureVisibleTabPermissions");
+ });
+
+ browser.test.sendMessage("ready");
+ },
+ });
+
+ let html = `
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ </head>
+ <body><h1>hello</h1></body>
+ </html>
+ `;
+
+ await extension.startup();
+
+ let testWindow = window.open(`data:text/html,${encodeURIComponent(html)}#scroll`);
+ await extension.awaitMessage("ready");
+ await AppTestDelegate.clickBrowserAction(testWindow, extension);
+ await extension.awaitFinish("captureVisibleTabPermissions");
+ await AppTestDelegate.closeBrowserAction(testWindow, extension);
+ testWindow.close();
+
+ await extension.unload();
+});
+
+add_task(async function testCaptureVisibleTabWithActiveTabAndNotUserInteraction() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["webNavigation", "tabs", "activeTab"],
+ },
+
+ async background() {
+ // Wait for the page (in the test window) to load.
+ await new Promise(resolve => {
+ browser.webNavigation.onCompleted.addListener(
+ () => resolve(),
+ {url: [{schemes: ["data"]}]});
+ });
+
+ let [tab] = await browser.tabs.query({ currentWindow: true, active: true });
+ await browser.test.assertRejects(
+ browser.tabs.captureVisibleTab(tab.windowId),
+ /Missing activeTab permission/,
+ "Expected rejection because activeTab permission isn't set"
+ );
+
+ browser.test.notifyPass("captureVisibleTabPermissions");
+ },
+ });
+
+ let html = `
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ </head>
+ <body><h1>hello</h1></body>
+ </html>
+ `;
+
+ await extension.startup();
+
+ let testWindow = window.open(`data:text/html,${encodeURIComponent(html)}#scroll`);
+ await extension.awaitFinish("captureVisibleTabPermissions");
+ testWindow.close();
+
+ await extension.unload();
+});
+
+add_task(async function testCaptureVisibleTabWithActiveTabAndAllURLs() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["webNavigation", "tabs", "activeTab", "<all_urls>"],
+ },
+
+ async background() {
+ // Wait for the page (in the test window) to load.
+ await new Promise(resolve => {
+ browser.webNavigation.onCompleted.addListener(
+ () => resolve(),
+ {url: [{schemes: ["data"]}]});
+ });
+
+ let [tab] = await browser.tabs.query({ currentWindow: true, active: true });
+ await browser.tabs.captureVisibleTab(tab.windowId);
+
+ browser.test.notifyPass("captureVisibleTabPermissions");
+ },
+ });
+
+ let html = `
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ </head>
+ <body><h1>hello</h1></body>
+ </html>
+ `;
+
+ await extension.startup();
+
+ let testWindow = window.open(`data:text/html,${encodeURIComponent(html)}#scroll`);
+ await extension.awaitFinish("captureVisibleTabPermissions");
+ testWindow.close();
+
+ await extension.unload();
+});
</script>
</body>
</html>
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_tabs_sendMessage.html b/toolkit/components/extensions/test/mochitest/test_ext_tabs_sendMessage.html
index 4b230c258c..8c6dfeee7c 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_tabs_sendMessage.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_tabs_sendMessage.html
@@ -11,6 +11,10 @@
<script>
"use strict";
+const {
+ WebExtensionPolicy
+} = SpecialPowers.Cu.getGlobalForObject(SpecialPowers.Services);
+
add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
@@ -27,6 +31,10 @@ add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
browser.runtime.onMessage.addListener(async (msg, sender) => {
browser.test.assertEq(msg, "page-script-ready");
browser.test.assertEq(sender.url, browser.runtime.getURL("page.html"));
+ browser.test.assertEq(
+ sender.origin,
+ new URL(browser.runtime.getURL("/")).origin
+ );
let tabId = sender.tab.id;
let response = await browser.tabs.sendMessage(tabId, "tab-sendMessage");
@@ -147,6 +155,178 @@ add_task(async function test_tabs_sendMessage_using_frameId() {
await extension.unload();
});
+add_task(async function test_tabs_sendMessage_aboutBlank() {
+ const PATH = "tests/toolkit/components/extensions/test/mochitest/file_with_about_blank.html";
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ match_about_blank: true,
+ matches: ["*://mochi.test/*/file_with_about_blank.html"],
+ all_frames: true,
+ js: ["cs.js"],
+ },
+ ],
+ },
+
+ background() {
+ browser.runtime.onMessage.addListener((msg, { url, origin }) => {
+ browser.test.assertEq("cs", msg, "expected message from cs.js");
+
+ const kind = url.startsWith("about:") ? url : "top";
+ switch (kind) {
+ case "top":
+ browser.test.assertTrue(
+ url.endsWith("/file_with_about_blank.html"),
+ "expected correct url"
+ );
+ browser.test.assertEq(
+ "http://mochi.test:8888",
+ origin,
+ "expected correct origin"
+ );
+ break;
+
+ case "about:blank":
+ browser.test.assertEq("about:blank", url, "expected correct url");
+ browser.test.assertEq(
+ "http://mochi.test:8888",
+ origin,
+ "expected correct origin"
+ );
+ break;
+
+ case "about:srcdoc":
+ browser.test.assertEq("about:srcdoc", url, "expected correct url");
+ browser.test.assertEq(
+ "http://mochi.test:8888",
+ origin,
+ "expected correct origin"
+ );
+ break;
+
+ default:
+ browser.test.fail(`Unexpected kind: ${kind}`);
+ }
+
+ browser.test.sendMessage(`done:${kind}`);
+ });
+
+ browser.test.sendMessage("ready");
+ },
+
+ files: {
+ "cs.js"() {
+ browser.runtime.sendMessage("cs");
+ },
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("ready");
+
+ let win = window.open("http://mochi.test:8888/" + PATH);
+ await extension.awaitMessage("done:top");
+ await extension.awaitMessage("done:about:blank");
+ await extension.awaitMessage("done:about:srcdoc");
+ win.close();
+
+ await extension.unload();
+});
+
+add_task(async function test_tabs_sendMessage_opaqueOrigin() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ match_about_blank: true,
+ // The combination of `matches` and `exclude_matches` below allows us
+ // to only inject in the top-level about:blank.
+ matches: ["*://*/*"],
+ exclude_matches: ["<all_urls>"],
+ js: ["cs.js"],
+ },
+ ],
+ },
+
+ async background() {
+ let tab;
+
+ browser.runtime.onMessage.addListener(async (msg, { url, origin }) => {
+ browser.test.assertEq("cs", msg, "expected message from cs.js");
+ browser.test.assertEq("about:blank", url, "expected correct url");
+ browser.test.assertEq("null", origin, "expected correct origin");
+
+ await browser.tabs.remove(tab.id);
+ browser.test.sendMessage("done");
+ });
+
+ tab = await browser.tabs.create({ url: "about:blank" });
+ },
+
+ files: {
+ "cs.js"() {
+ browser.runtime.sendMessage("cs");
+ },
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("done");
+ await extension.unload();
+});
+
+add_task(async function test_tabs_sendMessage_blob() {
+ // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1724099
+ if (!WebExtensionPolicy.useRemoteWebExtensions) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.allow_unsafe_parent_loads", true]],
+ });
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ async background() {
+ browser.runtime.onMessage.addListener(async (msg, { url, origin }) => {
+ browser.test.assertEq("script", msg, "expected message from script.js");
+ browser.test.assertTrue(
+ url.startsWith("blob:moz-extension://"),
+ "expected correct url"
+ );
+ browser.test.assertEq(
+ new URL(browser.runtime.getURL("/")).origin,
+ origin,
+ "expected correct origin"
+ );
+
+ browser.test.sendMessage("done");
+ });
+
+ const blob = new Blob(
+ [`<script src="${browser.runtime.getURL("script.js")}"><\/script>`],
+ { type: "text/html" }
+ );
+ const iframe = document.createElement("iframe");
+ iframe.src = URL.createObjectURL(blob);
+ document.body.appendChild(iframe);
+ },
+
+ files: {
+ "script.js"() {
+ browser.runtime.sendMessage("script");
+ },
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("done");
+ await extension.unload();
+
+ if (!WebExtensionPolicy.useRemoteWebExtensions) {
+ await SpecialPowers.popPrefEnv();
+ }
+});
+
</script>
</body>
</html>
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_test.html b/toolkit/components/extensions/test/mochitest/test_ext_test.html
index bf68786465..21093c9abf 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_test.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_test.html
@@ -126,7 +126,7 @@ function testScript() {
// The WebIDL version of assertDeepEq structurally clones before sending the
// params to the main thread. This check verifies that the behavior is
- // consistent between the WebIDL and Schemas.jsm-generated API bindings.
+ // consistent between the WebIDL and Schemas.sys.mjs-generated API bindings.
browser.test.assertThrows(
() => browser.test.assertDeepEq(obj, obj, "obj with func"),
/An unexpected error occurred/,
diff --git a/toolkit/components/extensions/test/xpcshell/test_csp_validator.js b/toolkit/components/extensions/test/xpcshell/test_csp_validator.js
index 011628f027..0e665b9730 100644
--- a/toolkit/components/extensions/test/xpcshell/test_csp_validator.js
+++ b/toolkit/components/extensions/test/xpcshell/test_csp_validator.js
@@ -202,8 +202,8 @@ add_task(async function test_csp_validator_extension_pages() {
let checkPolicy = (policy, expectedResult) => {
info(`Checking policy: ${policy}`);
- // While Schemas.jsm uses Ci.nsIAddonContentPolicy.CSP_ALLOW_WASM, we don't
- // pass that here because we are only verifying that remote scripts are
+ // While Schemas.sys.mjs uses Ci.nsIAddonContentPolicy.CSP_ALLOW_WASM, we
+ // don't pass that here because we are only verifying that remote scripts are
// blocked here.
let result = cps.validateAddonCSP(policy, 0);
equal(result, expectedResult);
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js b/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js
index 2d8b02bcd9..76b6644d44 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js
@@ -1,6 +1,6 @@
"use strict";
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
const server = createHttpServer();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_xrays.js b/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_xrays.js
index 9655c157d1..b909223302 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_xrays.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_xrays.js
@@ -1,6 +1,6 @@
"use strict";
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
const server = createHttpServer();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_clear_cached_resources.js b/toolkit/components/extensions/test/xpcshell/test_ext_clear_cached_resources.js
index 95bef23383..bd05398736 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_clear_cached_resources.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_clear_cached_resources.js
@@ -297,7 +297,7 @@ add_task(
// This temporary directory is going to be removed from the
// cleanup function, but also make it unique as we do for the
// other temporary files (e.g. like getTemporaryFile as defined
- // in XPInstall.jsm).
+ // in XPIInstall.sys.mjs).
const random = Math.round(Math.random() * 36 ** 3).toString(36);
const tmpDirName = `xpcshelltest_unpacked_addons_${random}`;
let tmpExtPath = FileUtils.getDir("TmpD", [tmpDirName]);
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_dynamic_registration.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_dynamic_registration.js
index 0133b5d86c..20f6ece95a 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_dynamic_registration.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_dynamic_registration.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell, to use
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell, to use
// the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
index 4ebe6df636..ce7f293142 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
@@ -30,7 +30,7 @@ Services.prefs.setBoolPref(
false
);
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xorigin_frame.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xorigin_frame.js
index 8a58b2475c..420fa7689c 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xorigin_frame.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xorigin_frame.js
@@ -5,6 +5,12 @@ const server = createHttpServer({
});
server.registerDirectory("/data/", do_get_file("data"));
+// By default, fission.webContentIsolationStrategy=1 (IsolateHighValue). When
+// Fission is enabled on Android, the pref value 2 (IsolateHighValue) will be
+// used instead. Set to 1 (IsolateEverything) to make sure the subframe in this
+// test gets its own process, independently of the default prefs on Android.
+Services.prefs.setIntPref("fission.webContentIsolationStrategy", 1);
+
add_task(async function test_process_switch_cross_origin_frame() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xrays.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xrays.js
index 7b92d5c4b7..30ec8ceced 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xrays.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xrays.js
@@ -1,6 +1,6 @@
"use strict";
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contexts_gc.js b/toolkit/components/extensions/test/xpcshell/test_ext_contexts_gc.js
index 2a36f51637..b6f955f7ba 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contexts_gc.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contexts_gc.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_modifyHeaders.js b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_modifyHeaders.js
index 4b7bebe188..df9e9d77ef 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_modifyHeaders.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_modifyHeaders.js
@@ -29,6 +29,7 @@ server.registerPathHandler("/echoheaders", (req, res) => {
dropDefaultHeader("accept-language");
dropDefaultHeader("accept-encoding");
dropDefaultHeader("connection");
+ dropDefaultHeader("priority");
res.write(JSON.stringify(headers));
});
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_file_access.js b/toolkit/components/extensions/test/xpcshell/test_ext_file_access.js
index c05188cd38..bea1e76c0f 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_file_access.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_file_access.js
@@ -4,7 +4,7 @@ const FILE_DUMMY_URL = Services.io.newFileURI(
do_get_file("data/dummy_page.html")
).spec;
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js b/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js
index 1e46e19527..69ba326f75 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js
@@ -4,7 +4,7 @@ const { Preferences } = ChromeUtils.importESModule(
"resource://gre/modules/Preferences.sys.mjs"
);
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_ipcBlob.js b/toolkit/components/extensions/test/xpcshell/test_ext_ipcBlob.js
index dd90d9bbc8..2c5b3378fd 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_ipcBlob.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_ipcBlob.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js b/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
index 948b75978a..ae6ce3d27e 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
@@ -12,8 +12,8 @@ const { ExtensionPermissions } = ChromeUtils.importESModule(
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
-// ExtensionParent.jsm is being imported lazily because when it is imported Services.appinfo will be
-// retrieved and cached (as a side-effect of Schemas.jsm being imported), and so Services.appinfo
+// ExtensionParent.sys.mjs is being imported lazily because when it is imported Services.appinfo will be
+// retrieved and cached (as a side-effect of Schemas.sys.mjs being imported), and so Services.appinfo
// will not be returning the version set by AddonTestUtils.createAppInfo and this test will
// fail on non-nightly builds (because the cached appinfo.version will be undefined and
// AddonManager startup will fail).
@@ -704,6 +704,7 @@ const GRANTED_WITHOUT_USER_PROMPT = [
"theme",
"unlimitedStorage",
"webRequest",
+ "webRequestAuthProvider",
"webRequestBlocking",
"webRequestFilterResponse",
"webRequestFilterResponse.serviceWorkerScript",
@@ -1058,3 +1059,47 @@ add_task(async function test_internal_permissions() {
await extension.unload();
});
+
+add_task(function test_normalizeOptional() {
+ const optional1 = {
+ origins: ["*://site.com/", "*://*.domain.com/"],
+ permissions: ["downloads", "tabs"],
+ };
+
+ function normalize(perms, optional) {
+ perms = { origins: [], permissions: [], ...perms };
+ optional = { origins: [], permissions: [], ...optional };
+ return ExtensionPermissions.normalizeOptional(perms, optional);
+ }
+
+ normalize({ origins: ["http://site.com/"] }, optional1);
+ normalize({ origins: ["https://site.com/"] }, optional1);
+ normalize({ origins: ["*://blah.domain.com/"] }, optional1);
+ normalize({ permissions: ["downloads", "tabs"] }, optional1);
+
+ Assert.throws(
+ () => normalize({ origins: ["http://www.example.com/"] }, optional1),
+ /was not declared in the manifest/
+ );
+ Assert.throws(
+ () => normalize({ permissions: ["proxy"] }, optional1),
+ /was not declared in optional_permissions/
+ );
+
+ const optional2 = {
+ origins: ["<all_urls>", "*://*/*"],
+ permissions: ["idle", "clipboardWrite"],
+ };
+
+ normalize({ origins: ["http://site.com/"] }, optional2);
+ normalize({ origins: ["https://site.com/"] }, optional2);
+ normalize({ origins: ["*://blah.domain.com/"] }, optional2);
+ normalize({ permissions: ["idle", "clipboardWrite"] }, optional2);
+
+ let perms = normalize({ origins: ["<all_urls>"] }, optional2);
+ equal(
+ perms.origins.sort().join(),
+ optional2.origins.sort().join(),
+ `Expect both "all sites" permissions`
+ );
+});
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_permissions_api.js b/toolkit/components/extensions/test/xpcshell/test_ext_permissions_api.js
index 0211787fee..97db78cf81 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permissions_api.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permissions_api.js
@@ -58,6 +58,7 @@ add_task(async function setup() {
"search",
"tabHide",
"tabs",
+ "webRequestAuthProvider",
"webRequestBlocking",
"webRequestFilterResponse",
"webRequestFilterResponse.serviceWorkerScript",
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_proxy_onauthrequired.js b/toolkit/components/extensions/test/xpcshell/test_ext_proxy_onauthrequired.js
index 4b1128a349..719fd219fe 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_proxy_onauthrequired.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_proxy_onauthrequired.js
@@ -251,7 +251,7 @@ add_task(async function test_webRequest_auth_proxy_system() {
() => {
browser.test.sendMessage("onAuthRequired");
// cancel is silently ignored, if it were not (e.g someone messes up in
- // WebRequest.jsm and allows cancel) this test would fail.
+ // WebRequest.sys.mjs and allows cancel) this test would fail.
return {
cancel: true,
authCredentials: { username: "puser", password: "ppass" },
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_manifest_permissions.js b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_manifest_permissions.js
index 85e645e67b..e5fc746e60 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_manifest_permissions.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_manifest_permissions.js
@@ -86,7 +86,7 @@ add_task(async function () {
a_manifest_property: {},
},
background() {
- // Test hasPermission method implemented in ExtensionChild.jsm.
+ // Test hasPermission method implemented in ExtensionChild.sys.mjs.
browser.test.assertTrue(
"testManifestPermission" in browser,
"The API namespace is defined as expected"
@@ -105,7 +105,7 @@ add_task(async function () {
"test-extension-manifest-without-nested-prop"
);
- // Test hasPermission method implemented in Extension.jsm.
+ // Test hasPermission method implemented in Extension.sys.mjs.
equal(
extension.extension.hasPermission("manifest:a_manifest_property"),
true,
@@ -129,7 +129,7 @@ add_task(async function () {
},
},
background() {
- // Test hasPermission method implemented in ExtensionChild.jsm.
+ // Test hasPermission method implemented in ExtensionChild.sys.mjs.
browser.test.assertTrue(
"testManifestPermission" in browser,
"The API namespace is defined as expected"
@@ -146,7 +146,7 @@ add_task(async function () {
async extension => {
await extension.awaitFinish("test-extension-manifest-with-nested-prop");
- // Test hasPermission method implemented in Extension.jsm.
+ // Test hasPermission method implemented in Extension.sys.mjs.
equal(
extension.extension.hasPermission("manifest:a_manifest_property"),
true,
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_privileged.js b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_privileged.js
index 14cbca7443..496607968e 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_privileged.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_privileged.js
@@ -144,7 +144,7 @@ add_task(
}
);
-// Test that Extension.jsm and schema correctly match.
+// Test that Extension.sys.mjs and schema correctly match.
add_task(function test_privileged_permissions_match() {
const { PRIVILEGED_PERMS } = ChromeUtils.importESModule(
"resource://gre/modules/Extension.sys.mjs"
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts.js b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts.js
index a06f34a1b4..c47cdcad4d 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell, to use
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell, to use
// the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_css.js b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_css.js
index 21190d2d59..a3c79b1cc0 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_css.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_css.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell, to use
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell, to use
// the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_file.js b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_file.js
index 3c806439ce..02ef9b0fa5 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_file.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_file.js
@@ -4,7 +4,7 @@ const FILE_DUMMY_URL = Services.io.newFileURI(
do_get_file("data/dummy_page.html")
).spec;
-// ExtensionContent.jsm needs to know when it's running from xpcshell, to use
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell, to use
// the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_updateContentScripts.js b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_updateContentScripts.js
index 9d3bf1576c..85c2376715 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_updateContentScripts.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_updateContentScripts.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell, to use
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell, to use
// the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js b/toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js
index 626d8de22d..e8316aa652 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js
@@ -1,6 +1,6 @@
"use strict";
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js
index 3108c7b9b4..57ca08aca3 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js
@@ -200,7 +200,7 @@ add_task(async function test_userScripts_no_webext_apis() {
let script = await browser.userScripts.register(userScriptOptions);
// Unregister and then register the same js code again, to verify that the last registered
- // userScript doesn't get assigned a revoked blob url (otherwise Extensioncontent.jsm
+ // userScript doesn't get assigned a revoked blob url (otherwise Extensioncontent.sys.mjs
// ScriptCache raises an error because it fails to compile the revoked blob url and the user
// script will never be loaded).
script.unregister();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_auth.js b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_auth.js
index 578e69ebdf..ef07817d00 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_auth.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_auth.js
@@ -63,7 +63,7 @@ server.registerPathHandler("/authenticate.sjs", (request, response) => {
}
});
-function getExtension(bgConfig) {
+function getExtension(bgConfig, permissions = ["webRequestBlocking"]) {
function background(config) {
let path = config.path;
browser.webRequest.onBeforeRequest.addListener(
@@ -125,18 +125,18 @@ function getExtension(bgConfig) {
return ExtensionTestUtils.loadExtension({
manifest: {
- permissions: ["webRequest", "webRequestBlocking", bgConfig.path],
+ permissions: [bgConfig.path, "webRequest", ...permissions],
},
background: `(${background})(${JSON.stringify(bgConfig)})`,
});
}
-add_task(async function test_webRequest_auth() {
+async function test_webRequest_auth(permissions) {
let config = {
path: `${BASE_URL}/*`,
realm: `webRequest_auth${Math.random()}`,
onBeforeRequest: {
- extra: ["blocking"],
+ extra: permissions.includes("webRequestBlocking") ? ["blocking"] : [],
},
onAuthRequired: {
extra: ["blocking"],
@@ -149,7 +149,7 @@ add_task(async function test_webRequest_auth() {
},
};
- let extension = getExtension(config);
+ let extension = getExtension(config, permissions);
await extension.startup();
let requestUrl = `${BASE_URL}/authenticate.sjs?realm=${config.realm}`;
@@ -174,6 +174,14 @@ add_task(async function test_webRequest_auth() {
await contentPage.close();
await extension.unload();
+}
+
+add_task(async function test_webRequest_auth_with_webRequestBlocking() {
+ await test_webRequest_auth(["webRequestBlocking"]);
+});
+
+add_task(async function test_webRequest_auth_with_webRequestAuthProvider() {
+ await test_webRequest_auth(["webRequestAuthProvider"]);
});
add_task(async function test_webRequest_auth_cancelled() {
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_permission.js b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_permission.js
index 17c22e156d..97e44498c1 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_permission.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_permission.js
@@ -18,6 +18,18 @@ function sendMessage(page, msg, data) {
return MessageChannel.sendMessage(page.browser.messageManager, msg, data);
}
+add_setup(() => {
+ // Make sure to invalidate WebExtensions API schemas that may be cached
+ // in the StartupCache when this test runs with conditioned-profiles.
+ //
+ // These tests are subject to be hitting failures consistently on
+ // landing API schema changes to the WebExtensions API permissions.
+ // or other API schema properties that are explicitly covered by
+ // this tests (e.g. errors expected to be emitted by postprocess
+ // helper functions).
+ Services.obs.notifyObservers(null, "startupcache-invalidate");
+});
+
add_task(async function test_permissions() {
function background() {
browser.webRequest.onBeforeRequest.addListener(
@@ -113,11 +125,14 @@ add_task(async function test_permissions() {
await contentPage.close();
});
-add_task(async function test_no_webRequestBlocking_error() {
+add_task(async function test_missing_required_perm_for_blocking_error() {
function background() {
const expectedError =
"Using webRequest.addListener with the blocking option " +
"requires the 'webRequestBlocking' permission.";
+ const expectedErrorOnAuthRequired =
+ "Using webRequest.onAuthRequired.addListener with the blocking option " +
+ "requires either the 'webRequestBlocking' or 'webRequestAuthProvider' permission.";
const blockingEvents = [
"onBeforeRequest",
@@ -135,7 +150,9 @@ add_task(async function test_no_webRequestBlocking_error() {
["blocking"]
);
},
- expectedError,
+ eventName === "onAuthRequired"
+ ? expectedErrorOnAuthRequired
+ : expectedError,
`Got the expected exception for a blocking webRequest.${eventName} listener`
);
}
diff --git a/toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js b/toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js
index 32299fb04e..3d28971352 100644
--- a/toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js
+++ b/toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js
@@ -18,7 +18,7 @@ let schemaURLs = new Set();
schemaURLs.add("chrome://extensions/content/schemas/experiments.json");
// Helper class used to load the API modules similarly to the apiManager
-// defined in ExtensionParent.jsm.
+// defined in ExtensionParent.sys.mjs.
class FakeAPIManager extends ExtensionCommon.SchemaAPIManager {
constructor(processType = "main") {
super(processType, Schemas);
@@ -102,7 +102,8 @@ class FakeAPIManager extends ExtensionCommon.SchemaAPIManager {
}
// Specialized helper class used to test loading "child process" modules (similarly to the
-// SchemaAPIManagers sub-classes defined in ExtensionPageChild.jsm and ExtensionContent.jsm).
+// SchemaAPIManagers sub-classes defined in ExtensionPageChild.sys.mjs and
+// ExtensionContent.sys.mjs).
class FakeChildProcessAPIManager extends FakeAPIManager {
constructor({ processType, categoryScripts }) {
super(processType, Schemas);
diff --git a/toolkit/components/extensions/test/xpcshell/test_native_manifests.js b/toolkit/components/extensions/test/xpcshell/test_native_manifests.js
index 1f5bc88740..d4f3ae7243 100644
--- a/toolkit/components/extensions/test/xpcshell/test_native_manifests.js
+++ b/toolkit/components/extensions/test/xpcshell/test_native_manifests.js
@@ -248,7 +248,7 @@ add_task(
"lookupApplication returns the correct path with platform-native slash"
);
// Side note: manifest.path does not contain a platform-native path,
- // but it is normalized when used in NativeMessaging.jsm.
+ // but it is normalized when used in NativeMessaging.sys.mjs.
deepEqual(
result.manifest,
manifest,
diff --git a/toolkit/components/extensions/test/xpcshell/test_webRequest_ancestors.js b/toolkit/components/extensions/test/xpcshell/test_webRequest_ancestors.js
index 509f821828..abbb814ac7 100644
--- a/toolkit/components/extensions/test/xpcshell/test_webRequest_ancestors.js
+++ b/toolkit/components/extensions/test/xpcshell/test_webRequest_ancestors.js
@@ -11,7 +11,7 @@ const server = createHttpServer({ hosts: ["example.com"] });
server.registerDirectory("/data/", do_get_file("data"));
add_task(async function setup() {
- // When WebRequest.jsm is used directly instead of through ext-webRequest.js,
+ // When WebRequest.sys.mjs is used directly instead of through ext-webRequest.js,
// ExtensionParent.apiManager is not automatically initialized. Do it here.
await ExtensionParent.apiManager.lazyInit();
});
diff --git a/toolkit/components/extensions/test/xpcshell/test_webRequest_cookies.js b/toolkit/components/extensions/test/xpcshell/test_webRequest_cookies.js
index 53ed465786..f4c8d75690 100644
--- a/toolkit/components/extensions/test/xpcshell/test_webRequest_cookies.js
+++ b/toolkit/components/extensions/test/xpcshell/test_webRequest_cookies.js
@@ -75,7 +75,7 @@ function onResponseStarted(details) {
}
add_task(async function setup() {
- // When WebRequest.jsm is used directly instead of through ext-webRequest.js,
+ // When WebRequest.sys.mjs is used directly instead of through ext-webRequest.js,
// ExtensionParent.apiManager is not automatically initialized. Do it here.
await ExtensionParent.apiManager.lazyInit();
});
diff --git a/toolkit/components/extensions/test/xpcshell/test_webRequest_filtering.js b/toolkit/components/extensions/test/xpcshell/test_webRequest_filtering.js
index 46a72a5926..86b2410e33 100644
--- a/toolkit/components/extensions/test/xpcshell/test_webRequest_filtering.js
+++ b/toolkit/components/extensions/test/xpcshell/test_webRequest_filtering.js
@@ -89,7 +89,7 @@ add_task(async function setup() {
// Disable rcwn to make cache behavior deterministic.
Services.prefs.setBoolPref("network.http.rcwn.enabled", false);
- // When WebRequest.jsm is used directly instead of through ext-webRequest.js,
+ // When WebRequest.sys.mjs is used directly instead of through ext-webRequest.js,
// ExtensionParent.apiManager is not automatically initialized. Do it here.
await ExtensionParent.apiManager.lazyInit();
});
diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml b/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
index 7cf8d79409..6d47012eca 100644
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
@@ -533,7 +533,7 @@ skip-if = ["os == 'android'"] # Bug 1564871
["test_ext_storage_sanitizer.js"]
skip-if = [
"appname == 'thunderbird'",
- "os == 'android'", # Sanitizer.jsm is not in toolkit.
+ "os == 'android'", # Sanitizer.sys.mjs is not in toolkit.
]
["test_ext_storage_session.js"]
@@ -587,7 +587,9 @@ skip-if = [
["test_ext_wasm.js"]
["test_ext_webRequest_auth.js"]
-skip-if = ["os == 'android' && debug"]
+skip-if = [
+ "os == 'android' && debug",
+]
["test_ext_webRequest_cached.js"]
skip-if = ["os == 'android'"] # Bug 1573511
@@ -616,7 +618,9 @@ skip-if = ["os == 'android' && debug"]
skip-if = ["tsan"] # Bug 1683730
["test_ext_webRequest_permission.js"]
-skip-if = ["os == 'android' && debug"]
+skip-if = [
+ "os == 'android' && debug",
+]
["test_ext_webRequest_redirectProperty.js"]
skip-if = ["os == 'android' && processor == 'x86_64'"] # Bug 1683253
diff --git a/toolkit/components/extensions/tsconfig.json b/toolkit/components/extensions/tsconfig.json
index d569ba9eca..992018f4a8 100644
--- a/toolkit/components/extensions/tsconfig.json
+++ b/toolkit/components/extensions/tsconfig.json
@@ -1,15 +1,11 @@
{
- "include": ["*.mjs", "types/globals.ts"],
- "exclude": [],
+ "include": ["*.mjs", "types/*.ts"],
"compilerOptions": {
"checkJs": true,
- "target": "ESNEXT",
-
- "declaration": true,
- "outDir": "./types",
- "typeRoots": [],
"noEmit": true,
+ "target": "es2022",
+ "types": ["gecko"],
// prettier-ignore
"paths": {
@@ -39,11 +35,12 @@
"resource://gre/modules/Schemas.sys.mjs": ["./Schemas.sys.mjs"],
"resource://gre/modules/WebNavigationFrames.sys.mjs": ["./WebNavigationFrames.sys.mjs"],
"resource://gre/modules/WebRequest.sys.mjs": ["./webrequest/WebRequest.sys.mjs"],
+ "resource://testing-common/ExtensionTestCommon.sys.mjs": ["./ExtensionTestCommon.sys.mjs"],
// External.
"resource://gre/modules/addons/crypto-utils.sys.mjs": ["../../mozapps/extensions/internal/crypto-utils.sys.mjs"],
"resource://gre/modules/XPCOMUtils.sys.mjs": ["../../../js/xpconnect/loader/XPCOMUtils.sys.mjs"],
- "resource://testing-common/ExtensionTestCommon.sys.mjs": ["./ExtensionTestCommon.sys.mjs"],
+ "resource://devtools/server/actors/descriptors/webextension.js": ["./types/globals.ts"],
// Types for external modules which need fixing, but we don't wanna touch.
"resource://testing-common/XPCShellContentUtils.sys.mjs": ["./types/XPCShellContentUtils.sys.d.mts"],
diff --git a/toolkit/components/extensions/types/README.md b/toolkit/components/extensions/types/README.md
index ebd01dec60..1d4588389c 100644
--- a/toolkit/components/extensions/types/README.md
+++ b/toolkit/components/extensions/types/README.md
@@ -21,11 +21,11 @@ viability and benefits of doing this for a "typical" component.
## How to use and expectations
-Use [npm or yarn to install][download] TypeScript.
-Then run `tsc` in the extensions directory to check types:
+Mach now comes with a `ts` command for type checking code using the built-in
+gecko typelibs:
```
-mozilla-central/toolkit/components/extensions $ tsc
+mozilla-central $ ./mach ts check toolkit/components/extensions/
```
You can also use an editor which supports the [language server][langserv].
@@ -64,6 +64,9 @@ These fall under 5 main categories:
5) Don't re-use local variables unnecessarily with different types.
* (general good practice, local variables are "free")
+ 6) Use `export` on individual classes instead of grouping into "namespaces".
+ * (idiomatic/ergonomic/modern JS, grouping was a leftover from JSMs)
+
### @ts-ignore recommendations
*Don't* use `@ts-ignore` for class fields and function or method signatures.
@@ -75,7 +78,7 @@ These fall under 5 main categories:
parts of the codebase.
-[handbook]: https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html
+[handbook]: https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html
[jsdoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
diff --git a/toolkit/components/extensions/types/ext-tabs-base.d.ts b/toolkit/components/extensions/types/ext-tabs-base.d.ts
new file mode 100644
index 0000000000..c889fd6620
--- /dev/null
+++ b/toolkit/components/extensions/types/ext-tabs-base.d.ts
@@ -0,0 +1,1447 @@
+// @ts-nocheck
+
+declare namespace tabs_base {
+
+declare function getUserContextIdForCookieStoreId(extension: any, cookieStoreId: any, isPrivateBrowsing: any): any;
+declare var DefaultMap: any;
+declare var DefaultWeakMap: any;
+declare var ExtensionError: any;
+declare var parseMatchPatterns: any;
+declare var defineLazyGetter: any;
+/**
+ * The platform-specific type of native tab objects, which are wrapped by
+ * TabBase instances.
+ *
+ * @typedef {object | XULElement} NativeTab
+ */
+/**
+ * @typedef {object} MutedInfo
+ * @property {boolean} muted
+ * True if the tab is currently muted, false otherwise.
+ * @property {string} [reason]
+ * The reason the tab is muted. Either "user", if the tab was muted by a
+ * user, or "extension", if it was muted by an extension.
+ * @property {string} [extensionId]
+ * If the tab was muted by an extension, contains the internal ID of that
+ * extension.
+ */
+/**
+ * A platform-independent base class for extension-specific wrappers around
+ * native tab objects.
+ *
+ * @param {Extension} extension
+ * The extension object for which this wrapper is being created. Used to
+ * determine permissions for access to certain properties and
+ * functionality.
+ * @param {NativeTab} nativeTab
+ * The native tab object which is being wrapped. The type of this object
+ * varies by platform.
+ * @param {integer} id
+ * The numeric ID of this tab object. This ID should be the same for
+ * every extension, and for the lifetime of the tab.
+ */
+declare class TabBase {
+ constructor(extension: any, nativeTab: any, id: any);
+ extension: any;
+ tabManager: any;
+ id: any;
+ nativeTab: any;
+ activeTabWindowID: any;
+ /**
+ * Capture the visible area of this tab, and return the result as a data: URI.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to perform the capture.
+ * @param {number} zoom
+ * The current zoom for the page.
+ * @param {object} [options]
+ * The options with which to perform the capture.
+ * @param {string} [options.format = "png"]
+ * The image format in which to encode the captured data. May be one of
+ * "png" or "jpeg".
+ * @param {integer} [options.quality = 92]
+ * The quality at which to encode the captured image data, ranging from
+ * 0 to 100. Has no effect for the "png" format.
+ * @param {DOMRectInit} [options.rect]
+ * Area of the document to render, in CSS pixels, relative to the page.
+ * If null, the currently visible viewport is rendered.
+ * @param {number} [options.scale]
+ * The scale to render at, defaults to devicePixelRatio.
+ * @returns {Promise<string>}
+ */
+ capture(context: BaseContext, zoom: number, options?: {
+ format?: string;
+ quality?: integer;
+ rect?: DOMRectInit;
+ scale?: number;
+ }): Promise<string>;
+ /**
+ * @property {integer | null} innerWindowID
+ * The last known innerWindowID loaded into this tab's docShell. This
+ * property must remain in sync with the last known values of
+ * properties such as `url` and `title`. Any operations on the content
+ * of an out-of-process tab will automatically fail if the
+ * innerWindowID of the tab when the message is received does not match
+ * the value of this property when the message was sent.
+ * @readonly
+ */
+ readonly get innerWindowID(): any;
+ /**
+ * @property {boolean} hasTabPermission
+ * Returns true if the extension has permission to access restricted
+ * properties of this tab, such as `url`, `title`, and `favIconUrl`.
+ * @readonly
+ */
+ readonly get hasTabPermission(): any;
+ /**
+ * @property {boolean} hasActiveTabPermission
+ * Returns true if the extension has the "activeTab" permission, and
+ * has been granted access to this tab due to a user executing an
+ * extension action.
+ *
+ * If true, the extension may load scripts and CSS into this tab, and
+ * access restricted properties, such as its `url`.
+ * @readonly
+ */
+ readonly get hasActiveTabPermission(): boolean;
+ /**
+ * @property {boolean} matchesHostPermission
+ * Returns true if the extensions host permissions match the current tab url.
+ * @readonly
+ */
+ readonly get matchesHostPermission(): any;
+ /**
+ * @property {boolean} incognito
+ * Returns true if this is a private browsing tab, false otherwise.
+ * @readonly
+ */
+ readonly get _incognito(): any;
+ /**
+ * @property {string} _url
+ * Returns the current URL of this tab. Does not do any permission
+ * checks.
+ * @readonly
+ */
+ readonly get _url(): any;
+ /**
+ * @property {string | null} url
+ * Returns the current URL of this tab if the extension has permission
+ * to read it, or null otherwise.
+ * @readonly
+ */
+ readonly get url(): any;
+ /**
+ * @property {nsIURI} _uri
+ * Returns the current URI of this tab.
+ * @readonly
+ */
+ readonly get _uri(): any;
+ /**
+ * @property {string} _title
+ * Returns the current title of this tab. Does not do any permission
+ * checks.
+ * @readonly
+ */
+ readonly get _title(): any;
+ /**
+ * @property {nsIURI | null} title
+ * Returns the current title of this tab if the extension has permission
+ * to read it, or null otherwise.
+ * @readonly
+ */
+ readonly get title(): any;
+ /**
+ * @property {string} _favIconUrl
+ * Returns the current favicon URL of this tab. Does not do any permission
+ * checks.
+ * @readonly
+ * @abstract
+ */
+ readonly get _favIconUrl(): void;
+ /**
+ * @property {nsIURI | null} faviconUrl
+ * Returns the current faviron URL of this tab if the extension has permission
+ * to read it, or null otherwise.
+ * @readonly
+ */
+ readonly get favIconUrl(): void;
+ /**
+ * @property {integer} lastAccessed
+ * Returns the last time the tab was accessed as the number of
+ * milliseconds since epoch.
+ * @readonly
+ * @abstract
+ */
+ readonly get lastAccessed(): void;
+ /**
+ * @property {boolean} audible
+ * Returns true if the tab is currently playing audio, false otherwise.
+ * @readonly
+ * @abstract
+ */
+ readonly get audible(): void;
+ /**
+ * @property {boolean} autoDiscardable
+ * Returns true if the tab can be discarded on memory pressure, false otherwise.
+ * @readonly
+ * @abstract
+ */
+ readonly get autoDiscardable(): void;
+ /**
+ * @property {XULElement} browser
+ * Returns the XUL browser for the given tab.
+ * @readonly
+ * @abstract
+ */
+ readonly get browser(): XULBrowserElement;
+ /**
+ * @property {BrowsingContext} browsingContext
+ * Returns the BrowsingContext for the given tab.
+ * @readonly
+ */
+ readonly get browsingContext(): any;
+ /**
+ * @property {FrameLoader} frameLoader
+ * Returns the frameloader for the given tab.
+ * @readonly
+ */
+ readonly get frameLoader(): void;
+ /**
+ * @property {string} cookieStoreId
+ * Returns the cookie store identifier for the given tab.
+ * @readonly
+ * @abstract
+ */
+ readonly get cookieStoreId(): void;
+ /**
+ * @property {integer} openerTabId
+ * Returns the ID of the tab which opened this one.
+ * @readonly
+ */
+ readonly get openerTabId(): any;
+ /**
+ * @property {integer} discarded
+ * Returns true if the tab is discarded.
+ * @readonly
+ * @abstract
+ */
+ readonly get discarded(): void;
+ /**
+ * @property {integer} height
+ * Returns the pixel height of the visible area of the tab.
+ * @readonly
+ * @abstract
+ */
+ readonly get height(): void;
+ /**
+ * @property {integer} hidden
+ * Returns true if the tab is hidden.
+ * @readonly
+ * @abstract
+ */
+ readonly get hidden(): void;
+ /**
+ * @property {integer} index
+ * Returns the index of the tab in its window's tab list.
+ * @readonly
+ * @abstract
+ */
+ readonly get index(): void;
+ /**
+ * @property {MutedInfo} mutedInfo
+ * Returns information about the tab's current audio muting status.
+ * @readonly
+ * @abstract
+ */
+ readonly get mutedInfo(): void;
+ /**
+ * @property {SharingState} sharingState
+ * Returns object with tab sharingState.
+ * @readonly
+ * @abstract
+ */
+ readonly get sharingState(): void;
+ /**
+ * @property {boolean} pinned
+ * Returns true if the tab is pinned, false otherwise.
+ * @readonly
+ * @abstract
+ */
+ readonly get pinned(): void;
+ /**
+ * @property {boolean} active
+ * Returns true if the tab is the currently-selected tab, false
+ * otherwise.
+ * @readonly
+ * @abstract
+ */
+ readonly get active(): void;
+ /**
+ * @property {boolean} highlighted
+ * Returns true if the tab is highlighted.
+ * @readonly
+ * @abstract
+ */
+ readonly get highlighted(): void;
+ /**
+ * @property {string} status
+ * Returns the current loading status of the tab. May be either
+ * "loading" or "complete".
+ * @readonly
+ * @abstract
+ */
+ readonly get status(): void;
+ /**
+ * @property {integer} height
+ * Returns the pixel height of the visible area of the tab.
+ * @readonly
+ * @abstract
+ */
+ readonly get width(): void;
+ /**
+ * @property {DOMWindow} window
+ * Returns the browser window to which the tab belongs.
+ * @readonly
+ * @abstract
+ */
+ readonly get window(): void;
+ /**
+ * @property {integer} window
+ * Returns the numeric ID of the browser window to which the tab belongs.
+ * @readonly
+ * @abstract
+ */
+ readonly get windowId(): void;
+ /**
+ * @property {boolean} attention
+ * Returns true if the tab is drawing attention.
+ * @readonly
+ * @abstract
+ */
+ readonly get attention(): void;
+ /**
+ * @property {boolean} isArticle
+ * Returns true if the document in the tab can be rendered in reader
+ * mode.
+ * @readonly
+ * @abstract
+ */
+ readonly get isArticle(): void;
+ /**
+ * @property {boolean} isInReaderMode
+ * Returns true if the document in the tab is being rendered in reader
+ * mode.
+ * @readonly
+ * @abstract
+ */
+ readonly get isInReaderMode(): void;
+ /**
+ * @property {integer} successorTabId
+ * @readonly
+ * @abstract
+ */
+ readonly get successorTabId(): void;
+ /**
+ * Returns true if this tab matches the the given query info object. Omitted
+ * or null have no effect on the match.
+ *
+ * @param {object} queryInfo
+ * The query info against which to match.
+ * @param {boolean} [queryInfo.active]
+ * Matches against the exact value of the tab's `active` attribute.
+ * @param {boolean} [queryInfo.audible]
+ * Matches against the exact value of the tab's `audible` attribute.
+ * @param {boolean} [queryInfo.autoDiscardable]
+ * Matches against the exact value of the tab's `autoDiscardable` attribute.
+ * @param {string} [queryInfo.cookieStoreId]
+ * Matches against the exact value of the tab's `cookieStoreId` attribute.
+ * @param {boolean} [queryInfo.discarded]
+ * Matches against the exact value of the tab's `discarded` attribute.
+ * @param {boolean} [queryInfo.hidden]
+ * Matches against the exact value of the tab's `hidden` attribute.
+ * @param {boolean} [queryInfo.highlighted]
+ * Matches against the exact value of the tab's `highlighted` attribute.
+ * @param {integer} [queryInfo.index]
+ * Matches against the exact value of the tab's `index` attribute.
+ * @param {boolean} [queryInfo.muted]
+ * Matches against the exact value of the tab's `mutedInfo.muted` attribute.
+ * @param {boolean} [queryInfo.pinned]
+ * Matches against the exact value of the tab's `pinned` attribute.
+ * @param {string} [queryInfo.status]
+ * Matches against the exact value of the tab's `status` attribute.
+ * @param {string} [queryInfo.title]
+ * Matches against the exact value of the tab's `title` attribute.
+ * @param {string|boolean } [queryInfo.screen]
+ * Matches against the exact value of the tab's `sharingState.screen` attribute, or use true to match any screen sharing tab.
+ * @param {boolean} [queryInfo.camera]
+ * Matches against the exact value of the tab's `sharingState.camera` attribute.
+ * @param {boolean} [queryInfo.microphone]
+ * Matches against the exact value of the tab's `sharingState.microphone` attribute.
+ *
+ * Note: Per specification, this should perform a pattern match, rather
+ * than an exact value match, and will do so in the future.
+ * @param {MatchPattern} [queryInfo.url]
+ * Requires the tab's URL to match the given MatchPattern object.
+ *
+ * @returns {boolean}
+ * True if the tab matches the query.
+ */
+ matches(queryInfo: {
+ active?: boolean;
+ audible?: boolean;
+ autoDiscardable?: boolean;
+ cookieStoreId?: string;
+ discarded?: boolean;
+ hidden?: boolean;
+ highlighted?: boolean;
+ index?: integer;
+ muted?: boolean;
+ pinned?: boolean;
+ status?: string;
+ title?: string;
+ screen?: string | boolean;
+ camera?: boolean;
+ microphone?: boolean;
+ url?: MatchPattern;
+ }): boolean;
+ /**
+ * Converts this tab object to a JSON-compatible object containing the values
+ * of its properties which the extension is permitted to access, in the format
+ * required to be returned by WebExtension APIs.
+ *
+ * @param {object} [fallbackTabSize]
+ * A geometry data if the lazy geometry data for this tab hasn't been
+ * initialized yet.
+ * @returns {object}
+ */
+ convert(fallbackTabSize?: object): object;
+ /**
+ * Query each content process hosting subframes of the tab, return results.
+ *
+ * @param {string} message
+ * @param {object} options
+ * These options are also sent to the message handler in the
+ * `ExtensionContentChild`.
+ * @param {number[]} options.frameIds
+ * When omitted, all frames will be queried.
+ * @param {boolean} options.returnResultsWithFrameIds
+ * @returns {Promise[]}
+ */
+ queryContent(message: string, options: {
+ frameIds: number[];
+ returnResultsWithFrameIds: boolean;
+ }): Promise<any>[];
+ /**
+ * Inserts a script or stylesheet in the given tab, and returns a promise
+ * which resolves when the operation has completed.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to perform the injection.
+ * @param {InjectDetails} details
+ * The InjectDetails object, specifying what to inject, where, and
+ * when.
+ * @param {string} kind
+ * The kind of data being injected. Either "script" or "css".
+ * @param {string} method
+ * The name of the method which was called to trigger the injection.
+ * Used to generate appropriate error messages on failure.
+ *
+ * @returns {Promise}
+ * Resolves to the result of the execution, once it has completed.
+ * @private
+ */
+ private _execute;
+ /**
+ * Executes a script in the tab's content window, and returns a Promise which
+ * resolves to the result of the evaluation, or rejects to the value of any
+ * error the injection generates.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to inject the script.
+ * @param {InjectDetails} details
+ * The InjectDetails object, specifying what to inject, where, and
+ * when.
+ *
+ * @returns {Promise}
+ * Resolves to the result of the evaluation of the given script, once
+ * it has completed, or rejects with any error the evaluation
+ * generates.
+ */
+ executeScript(context: BaseContext, details: InjectDetails): Promise<any>;
+ /**
+ * Injects CSS into the tab's content window, and returns a Promise which
+ * resolves when the injection is complete.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to inject the script.
+ * @param {InjectDetails} details
+ * The InjectDetails object, specifying what to inject, and where.
+ *
+ * @returns {Promise}
+ * Resolves when the injection has completed.
+ */
+ insertCSS(context: BaseContext, details: InjectDetails): Promise<any>;
+ /**
+ * Removes CSS which was previously into the tab's content window via
+ * `insertCSS`, and returns a Promise which resolves when the operation is
+ * complete.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to remove the CSS.
+ * @param {InjectDetails} details
+ * The InjectDetails object, specifying what to remove, and from where.
+ *
+ * @returns {Promise}
+ * Resolves when the operation has completed.
+ */
+ removeCSS(context: BaseContext, details: InjectDetails): Promise<any>;
+}
+declare const WINDOW_ID_NONE: -1;
+declare const WINDOW_ID_CURRENT: -2;
+/**
+ * A platform-independent base class for extension-specific wrappers around
+ * native browser windows
+ *
+ * @param {Extension} extension
+ * The extension object for which this wrapper is being created.
+ * @param {DOMWindow} window
+ * The browser DOM window which is being wrapped.
+ * @param {integer} id
+ * The numeric ID of this DOM window object. This ID should be the same for
+ * every extension, and for the lifetime of the window.
+ */
+declare class WindowBase {
+ /**
+ * Returns the window state of the given window.
+ *
+ * @param {DOMWindow} window
+ * The window for which to return a state.
+ *
+ * @returns {string}
+ * The window's state. One of "normal", "minimized", "maximized",
+ * "fullscreen", or "docked".
+ * @static
+ * @abstract
+ */
+ static getState(window: DOMWindow): string;
+ constructor(extension: any, window: any, id: any);
+ extension: any;
+ window: any;
+ id: any;
+ /**
+ * @property {nsIAppWindow} appWindow
+ * The nsIAppWindow object for this browser window.
+ * @readonly
+ */
+ readonly get appWindow(): any;
+ /**
+ * Returns true if this window is the current window for the given extension
+ * context, false otherwise.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to perform the check.
+ *
+ * @returns {boolean}
+ */
+ isCurrentFor(context: BaseContext): boolean;
+ /**
+ * @property {string} type
+ * The type of the window, as defined by the WebExtension API. May be
+ * either "normal" or "popup".
+ * @readonly
+ */
+ readonly get type(): "popup" | "normal";
+ /**
+ * Converts this window object to a JSON-compatible object which may be
+ * returned to an extension, in the format required to be returned by
+ * WebExtension APIs.
+ *
+ * @param {object} [getInfo]
+ * An optional object, the properties of which determine what data is
+ * available on the result object.
+ * @param {boolean} [getInfo.populate]
+ * Of true, the result object will contain a `tabs` property,
+ * containing an array of converted Tab objects, one for each tab in
+ * the window.
+ *
+ * @returns {object}
+ */
+ convert(getInfo?: {
+ populate?: boolean;
+ }): object;
+ /**
+ * Returns true if this window matches the the given query info object. Omitted
+ * or null have no effect on the match.
+ *
+ * @param {object} queryInfo
+ * The query info against which to match.
+ * @param {boolean} [queryInfo.currentWindow]
+ * Matches against against the return value of `isCurrentFor()` for the
+ * given context.
+ * @param {boolean} [queryInfo.lastFocusedWindow]
+ * Matches against the exact value of the window's `isLastFocused` attribute.
+ * @param {boolean} [queryInfo.windowId]
+ * Matches against the exact value of the window's ID, taking into
+ * account the special WINDOW_ID_CURRENT value.
+ * @param {string} [queryInfo.windowType]
+ * Matches against the exact value of the window's `type` attribute.
+ * @param {BaseContext} context
+ * The extension context for which the matching is being performed.
+ * Used to determine the current window for relevant properties.
+ *
+ * @returns {boolean}
+ * True if the window matches the query.
+ */
+ matches(queryInfo: {
+ currentWindow?: boolean;
+ lastFocusedWindow?: boolean;
+ windowId?: boolean;
+ windowType?: string;
+ }, context: BaseContext): boolean;
+ /**
+ * @property {boolean} focused
+ * Returns true if the browser window is currently focused.
+ * @readonly
+ * @abstract
+ */
+ readonly get focused(): void;
+ /**
+ * @property {integer} top
+ * Returns the pixel offset of the top of the window from the top of
+ * the screen.
+ * @readonly
+ * @abstract
+ */
+ readonly get top(): void;
+ /**
+ * @property {integer} left
+ * Returns the pixel offset of the left of the window from the left of
+ * the screen.
+ * @readonly
+ * @abstract
+ */
+ readonly get left(): void;
+ /**
+ * @property {integer} width
+ * Returns the pixel width of the window.
+ * @readonly
+ * @abstract
+ */
+ readonly get width(): void;
+ /**
+ * @property {integer} height
+ * Returns the pixel height of the window.
+ * @readonly
+ * @abstract
+ */
+ readonly get height(): void;
+ /**
+ * @property {boolean} incognito
+ * Returns true if this is a private browsing window, false otherwise.
+ * @readonly
+ * @abstract
+ */
+ readonly get incognito(): void;
+ /**
+ * @property {boolean} alwaysOnTop
+ * Returns true if this window is constrained to always remain above
+ * other windows.
+ * @readonly
+ * @abstract
+ */
+ readonly get alwaysOnTop(): void;
+ /**
+ * @property {boolean} isLastFocused
+ * Returns true if this is the browser window which most recently had
+ * focus.
+ * @readonly
+ * @abstract
+ */
+ readonly get isLastFocused(): void;
+ set state(state: void);
+ /**
+ * @property {string} state
+ * Returns or sets the current state of this window, as determined by
+ * `getState()`.
+ * @abstract
+ */
+ get state(): void;
+ /**
+ * @property {nsIURI | null} title
+ * Returns the current title of this window if the extension has permission
+ * to read it, or null otherwise.
+ * @readonly
+ */
+ readonly get title(): any;
+ /**
+ * Returns an iterator of TabBase objects for each tab in this window.
+ *
+ * @returns {Iterator<TabBase>}
+ */
+ getTabs(): Iterator<TabBase>;
+ /**
+ * Returns an iterator of TabBase objects for each highlighted tab in this window.
+ *
+ * @returns {Iterator<TabBase>}
+ */
+ getHighlightedTabs(): Iterator<TabBase>;
+ /**
+ * @property {TabBase} The window's currently active tab.
+ */
+ get activeTab(): void;
+ /**
+ * Returns the window's tab at the specified index.
+ *
+ * @param {integer} index
+ * The index of the desired tab.
+ *
+ * @returns {TabBase|undefined}
+ */
+ getTabAtIndex(index: integer): TabBase | undefined;
+}
+/**
+ * The parameter type of "tab-attached" events, which are emitted when a
+ * pre-existing tab is attached to a new window.
+ *
+ * @typedef {object} TabAttachedEvent
+ * @property {NativeTab} tab
+ * The native tab object in the window to which the tab is being
+ * attached. This may be a different object than was used to represent
+ * the tab in the old window.
+ * @property {integer} tabId
+ * The ID of the tab being attached.
+ * @property {integer} newWindowId
+ * The ID of the window to which the tab is being attached.
+ * @property {integer} newPosition
+ * The position of the tab in the tab list of the new window.
+ */
+/**
+ * The parameter type of "tab-detached" events, which are emitted when a
+ * pre-existing tab is detached from a window, in order to be attached to a new
+ * window.
+ *
+ * @typedef {object} TabDetachedEvent
+ * @property {NativeTab} tab
+ * The native tab object in the window from which the tab is being
+ * detached. This may be a different object than will be used to
+ * represent the tab in the new window.
+ * @property {NativeTab} adoptedBy
+ * The native tab object in the window to which the tab will be attached,
+ * and is adopting the contents of this tab. This may be a different
+ * object than the tab in the previous window.
+ * @property {integer} tabId
+ * The ID of the tab being detached.
+ * @property {integer} oldWindowId
+ * The ID of the window from which the tab is being detached.
+ * @property {integer} oldPosition
+ * The position of the tab in the tab list of the window from which it is
+ * being detached.
+ */
+/**
+ * The parameter type of "tab-created" events, which are emitted when a
+ * new tab is created.
+ *
+ * @typedef {object} TabCreatedEvent
+ * @property {NativeTab} tab
+ * The native tab object for the tab which is being created.
+ */
+/**
+ * The parameter type of "tab-removed" events, which are emitted when a
+ * tab is removed and destroyed.
+ *
+ * @typedef {object} TabRemovedEvent
+ * @property {NativeTab} tab
+ * The native tab object for the tab which is being removed.
+ * @property {integer} tabId
+ * The ID of the tab being removed.
+ * @property {integer} windowId
+ * The ID of the window from which the tab is being removed.
+ * @property {boolean} isWindowClosing
+ * True if the tab is being removed because the window is closing.
+ */
+/**
+ * An object containing basic, extension-independent information about the window
+ * and tab that a XUL <browser> belongs to.
+ *
+ * @typedef {object} BrowserData
+ * @property {integer} tabId
+ * The numeric ID of the tab that a <browser> belongs to, or -1 if it
+ * does not belong to a tab.
+ * @property {integer} windowId
+ * The numeric ID of the browser window that a <browser> belongs to, or -1
+ * if it does not belong to a browser window.
+ */
+/**
+ * A platform-independent base class for the platform-specific TabTracker
+ * classes, which track the opening and closing of tabs, and manage the mapping
+ * of them between numeric IDs and native tab objects.
+ *
+ * Instances of this class are EventEmitters which emit the following events,
+ * each with an argument of the given type:
+ *
+ * - "tab-attached" {@link TabAttacheEvent}
+ * - "tab-detached" {@link TabDetachedEvent}
+ * - "tab-created" {@link TabCreatedEvent}
+ * - "tab-removed" {@link TabRemovedEvent}
+ */
+declare class TabTrackerBase {
+ on(...args: any[]): any;
+ /**
+ * Called to initialize the tab tracking listeners the first time that an
+ * event listener is added.
+ *
+ * @protected
+ * @abstract
+ */
+ protected init(): void;
+ /**
+ * Returns the numeric ID for the given native tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to return an ID.
+ *
+ * @returns {integer}
+ * The tab's numeric ID.
+ * @abstract
+ */
+ getId(nativeTab: NativeTab): integer;
+ /**
+ * Returns the native tab with the given numeric ID.
+ *
+ * @param {integer} tabId
+ * The numeric ID of the tab to return.
+ * @param {*} default_
+ * The value to return if no tab exists with the given ID.
+ *
+ * @returns {NativeTab}
+ * @throws {ExtensionError}
+ * If no tab exists with the given ID and a default return value is not
+ * provided.
+ * @abstract
+ */
+ getTab(tabId: integer, default_?: any): NativeTab;
+ /**
+ * Returns basic information about the tab and window that the given browser
+ * belongs to.
+ *
+ * @param {XULElement} browser
+ * The XUL browser element for which to return data.
+ *
+ * @returns {BrowserData}
+ * @abstract
+ */
+ getBrowserData(browser: XULElement): BrowserData;
+ /**
+ * @property {NativeTab} activeTab
+ * Returns the native tab object for the active tab in the
+ * most-recently focused window, or null if no live tabs currently
+ * exist.
+ * @abstract
+ */
+ get activeTab(): void;
+}
+/**
+ * A browser progress listener instance which calls a given listener function
+ * whenever the status of the given browser changes.
+ *
+ * @param {function(object): void} listener
+ * A function to be called whenever the status of a tab's top-level
+ * browser. It is passed an object with a `browser` property pointing to
+ * the XUL browser, and a `status` property with a string description of
+ * the browser's status.
+ * @private
+ */
+declare class StatusListener {
+ constructor(listener: any);
+ listener: any;
+ onStateChange(browser: any, webProgress: any, request: any, stateFlags: any, statusCode: any): void;
+ onLocationChange(browser: any, webProgress: any, request: any, locationURI: any, flags: any): void;
+}
+/**
+ * A platform-independent base class for the platform-specific WindowTracker
+ * classes, which track the opening and closing of windows, and manage the
+ * mapping of them between numeric IDs and native tab objects.
+ */
+declare class WindowTrackerBase {
+ /**
+ * A private method which is called whenever a new browser window is opened,
+ * and adds the necessary listeners to it.
+ *
+ * @param {DOMWindow} window
+ * The window being opened.
+ * @private
+ */
+ private _handleWindowOpened;
+ _openListeners: Set<any>;
+ _closeListeners: Set<any>;
+ _listeners: any;
+ _statusListeners: any;
+ _windowIds: any;
+ isBrowserWindow(window: any): boolean;
+ /**
+ * Returns an iterator for all currently active browser windows.
+ *
+ * @param {boolean} [includeInomplete = false]
+ * If true, include browser windows which are not yet fully loaded.
+ * Otherwise, only include windows which are.
+ *
+ * @returns {Iterator<DOMWindow>}
+ */
+ browserWindows(includeIncomplete?: boolean): Iterator<DOMWindow>;
+ /**
+ * @property {DOMWindow|null} topWindow
+ * The currently active, or topmost, browser window, or null if no
+ * browser window is currently open.
+ * @readonly
+ */
+ readonly get topWindow(): any;
+ /**
+ * @property {DOMWindow|null} topWindow
+ * The currently active, or topmost, browser window that is not
+ * private browsing, or null if no browser window is currently open.
+ * @readonly
+ */
+ readonly get topNonPBWindow(): any;
+ /**
+ * Returns the top window accessible by the extension.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to return the current window.
+ *
+ * @returns {DOMWindow|null}
+ */
+ getTopWindow(context: BaseContext): DOMWindow | null;
+ /**
+ * Returns the numeric ID for the given browser window.
+ *
+ * @param {DOMWindow} window
+ * The DOM window for which to return an ID.
+ *
+ * @returns {integer}
+ * The window's numeric ID.
+ */
+ getId(window: DOMWindow): integer;
+ /**
+ * Returns the browser window to which the given context belongs, or the top
+ * browser window if the context does not belong to a browser window.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to return the current window.
+ *
+ * @returns {DOMWindow|null}
+ */
+ getCurrentWindow(context: BaseContext): DOMWindow | null;
+ /**
+ * Returns the browser window with the given ID.
+ *
+ * @param {integer} id
+ * The ID of the window to return.
+ * @param {BaseContext} context
+ * The extension context for which the matching is being performed.
+ * Used to determine the current window for relevant properties.
+ * @param {boolean} [strict = true]
+ * If false, undefined will be returned instead of throwing an error
+ * in case no window exists with the given ID.
+ *
+ * @returns {DOMWindow|undefined}
+ * @throws {ExtensionError}
+ * If no window exists with the given ID and `strict` is true.
+ */
+ getWindow(id: integer, context: BaseContext, strict?: boolean): DOMWindow | undefined;
+ /**
+ * @property {boolean} _haveListeners
+ * Returns true if any window open or close listeners are currently
+ * registered.
+ * @private
+ */
+ private get _haveListeners();
+ /**
+ * Register the given listener function to be called whenever a new browser
+ * window is opened.
+ *
+ * @param {function(DOMWindow): void} listener
+ * The listener function to register.
+ */
+ addOpenListener(listener: (arg0: DOMWindow) => void): void;
+ /**
+ * Unregister a listener function registered in a previous addOpenListener
+ * call.
+ *
+ * @param {function(DOMWindow): void} listener
+ * The listener function to unregister.
+ */
+ removeOpenListener(listener: (arg0: DOMWindow) => void): void;
+ /**
+ * Register the given listener function to be called whenever a browser
+ * window is closed.
+ *
+ * @param {function(DOMWindow): void} listener
+ * The listener function to register.
+ */
+ addCloseListener(listener: (arg0: DOMWindow) => void): void;
+ /**
+ * Unregister a listener function registered in a previous addCloseListener
+ * call.
+ *
+ * @param {function(DOMWindow): void} listener
+ * The listener function to unregister.
+ */
+ removeCloseListener(listener: (arg0: DOMWindow) => void): void;
+ /**
+ * Handles load events for recently-opened windows, and adds additional
+ * listeners which may only be safely added when the window is fully loaded.
+ *
+ * @param {Event} event
+ * A DOM event to handle.
+ * @private
+ */
+ private handleEvent;
+ /**
+ * Observes "domwindowopened" and "domwindowclosed" events, notifies the
+ * appropriate listeners, and adds necessary additional listeners to the new
+ * windows.
+ *
+ * @param {DOMWindow} window
+ * A DOM window.
+ * @param {string} topic
+ * The topic being observed.
+ * @private
+ */
+ private observe;
+ /**
+ * Add an event listener to be called whenever the given DOM event is received
+ * at the top level of any browser window.
+ *
+ * @param {string} type
+ * The type of event to listen for. May be any valid DOM event name, or
+ * one of the following special cases:
+ *
+ * - "progress": Adds a tab progress listener to every browser window.
+ * - "status": Adds a StatusListener to every tab of every browser
+ * window.
+ * - "domwindowopened": Acts as an alias for addOpenListener.
+ * - "domwindowclosed": Acts as an alias for addCloseListener.
+ * @param {Function | object} listener
+ * The listener to invoke in response to the given events.
+ *
+ * @returns {undefined}
+ */
+ addListener(type: string, listener: Function | object): undefined;
+ /**
+ * Removes an event listener previously registered via an addListener call.
+ *
+ * @param {string} type
+ * The type of event to stop listening for.
+ * @param {Function | object} listener
+ * The listener to remove.
+ *
+ * @returns {undefined}
+ */
+ removeListener(type: string, listener: Function | object): undefined;
+ /**
+ * Adds a listener for the given event to the given window.
+ *
+ * @param {DOMWindow} window
+ * The browser window to which to add the listener.
+ * @param {string} eventType
+ * The type of DOM event to listen for, or "progress" to add a tab
+ * progress listener.
+ * @param {Function | object} listener
+ * The listener to add.
+ * @private
+ */
+ private _addWindowListener;
+ /**
+ * Adds a tab progress listener to the given browser window.
+ *
+ * @param {DOMWindow} window
+ * The browser window to which to add the listener.
+ * @param {object} listener
+ * The tab progress listener to add.
+ * @abstract
+ */
+ addProgressListener(window: DOMWindow, listener: object): void;
+ /**
+ * Removes a tab progress listener from the given browser window.
+ *
+ * @param {DOMWindow} window
+ * The browser window from which to remove the listener.
+ * @param {object} listener
+ * The tab progress listener to remove.
+ * @abstract
+ */
+ removeProgressListener(window: DOMWindow, listener: object): void;
+}
+/**
+ * Manages native tabs, their wrappers, and their dynamic permissions for a
+ * particular extension.
+ *
+ * @param {Extension} extension
+ * The extension for which to manage tabs.
+ */
+declare class TabManagerBase {
+ constructor(extension: any);
+ extension: any;
+ _tabs: any;
+ /**
+ * If the extension has requested activeTab permission, grant it those
+ * permissions for the current inner window in the given native tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to grant permissions.
+ */
+ addActiveTabPermission(nativeTab: NativeTab): void;
+ /**
+ * Revoke the extension's activeTab permissions for the current inner window
+ * of the given native tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to revoke permissions.
+ */
+ revokeActiveTabPermission(nativeTab: NativeTab): void;
+ /**
+ * Returns true if the extension has requested activeTab permission, and has
+ * been granted permissions for the current inner window if this tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to check permissions.
+ * @returns {boolean}
+ * True if the extension has activeTab permissions for this tab.
+ */
+ hasActiveTabPermission(nativeTab: NativeTab): boolean;
+ /**
+ * Activate MV3 content scripts if the extension has activeTab or an
+ * (ungranted) host permission.
+ *
+ * @param {NativeTab} nativeTab
+ */
+ activateScripts(nativeTab: NativeTab): void;
+ /**
+ * Returns true if the extension has permissions to access restricted
+ * properties of the given native tab. In practice, this means that it has
+ * either requested the "tabs" permission or has activeTab permissions for the
+ * given tab.
+ *
+ * NOTE: Never use this method on an object that is not a native tab
+ * for the current platform: this method implicitly generates a wrapper
+ * for the passed nativeTab parameter and the platform-specific tabTracker
+ * instance is likely to store it in a map which is cleared only when the
+ * tab is closed (and so, if nativeTab is not a real native tab, it will
+ * never be cleared from the platform-specific tabTracker instance),
+ * See Bug 1458918 for a rationale.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to check permissions.
+ * @returns {boolean}
+ * True if the extension has permissions for this tab.
+ */
+ hasTabPermission(nativeTab: NativeTab): boolean;
+ /**
+ * Returns this extension's TabBase wrapper for the given native tab. This
+ * method will always return the same wrapper object for any given native tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The tab for which to return a wrapper.
+ *
+ * @returns {TabBase|undefined}
+ * The wrapper for this tab.
+ */
+ getWrapper(nativeTab: NativeTab): TabBase | undefined;
+ /**
+ * Determines access using extension context.
+ *
+ * @param {NativeTab} nativeTab
+ * The tab to check access on.
+ * @returns {boolean}
+ * True if the extension has permissions for this tab.
+ * @protected
+ * @abstract
+ */
+ protected canAccessTab(nativeTab: NativeTab): boolean;
+ /**
+ * Converts the given native tab to a JSON-compatible object, in the format
+ * required to be returned by WebExtension APIs, which may be safely passed to
+ * extension code.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab to convert.
+ * @param {object} [fallbackTabSize]
+ * A geometry data if the lazy geometry data for this tab hasn't been
+ * initialized yet.
+ *
+ * @returns {object}
+ */
+ convert(nativeTab: NativeTab, fallbackTabSize?: object): object;
+ /**
+ * Returns an iterator of TabBase objects which match the given query info.
+ *
+ * @param {object | null} [queryInfo = null]
+ * An object containing properties on which to filter. May contain any
+ * properties which are recognized by {@link TabBase#matches} or
+ * {@link WindowBase#matches}. Unknown properties will be ignored.
+ * @param {BaseContext|null} [context = null]
+ * The extension context for which the matching is being performed.
+ * Used to determine the current window for relevant properties.
+ *
+ * @returns {Iterator<TabBase>}
+ */
+ query(queryInfo?: object | null, context?: BaseContext | null): Iterator<TabBase>;
+ /**
+ * Returns a TabBase wrapper for the tab with the given ID.
+ *
+ * @param {integer} tabId
+ * The ID of the tab for which to return a wrapper.
+ *
+ * @returns {TabBase}
+ * @throws {ExtensionError}
+ * If no tab exists with the given ID.
+ * @abstract
+ */
+ get(tabId: integer): TabBase;
+ /**
+ * Returns a new TabBase instance wrapping the given native tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to return a wrapper.
+ *
+ * @returns {TabBase}
+ * @protected
+ * @abstract
+ */
+ protected wrapTab(nativeTab: NativeTab): TabBase;
+}
+/**
+ * Manages native browser windows and their wrappers for a particular extension.
+ *
+ * @param {Extension} extension
+ * The extension for which to manage windows.
+ */
+declare class WindowManagerBase {
+ constructor(extension: any);
+ extension: any;
+ _windows: any;
+ /**
+ * Converts the given browser window to a JSON-compatible object, in the
+ * format required to be returned by WebExtension APIs, which may be safely
+ * passed to extension code.
+ *
+ * @param {DOMWindow} window
+ * The browser window to convert.
+ * @param {*} args
+ * Additional arguments to be passed to {@link WindowBase#convert}.
+ *
+ * @returns {object}
+ */
+ convert(window: DOMWindow, ...args: any): object;
+ /**
+ * Returns this extension's WindowBase wrapper for the given browser window.
+ * This method will always return the same wrapper object for any given
+ * browser window.
+ *
+ * @param {DOMWindow} window
+ * The browser window for which to return a wrapper.
+ *
+ * @returns {WindowBase|undefined}
+ * The wrapper for this tab.
+ */
+ getWrapper(window: DOMWindow): WindowBase | undefined;
+ /**
+ * Returns whether this window can be accessed by the extension in the given
+ * context.
+ *
+ * @param {DOMWindow} window
+ * The browser window that is being tested
+ * @param {BaseContext|null} context
+ * The extension context for which this test is being performed.
+ * @returns {boolean}
+ */
+ canAccessWindow(window: DOMWindow, context: BaseContext | null): boolean;
+ /**
+ * Returns an iterator of WindowBase objects which match the given query info.
+ *
+ * @param {object | null} [queryInfo = null]
+ * An object containing properties on which to filter. May contain any
+ * properties which are recognized by {@link WindowBase#matches}.
+ * Unknown properties will be ignored.
+ * @param {BaseContext|null} [context = null]
+ * The extension context for which the matching is being performed.
+ * Used to determine the current window for relevant properties.
+ *
+ * @returns {Iterator<WindowBase>}
+ */
+ query(queryInfo?: object | null, context?: BaseContext | null): Iterator<WindowBase>;
+ /**
+ * Returns a WindowBase wrapper for the browser window with the given ID.
+ *
+ * @param {integer} windowId
+ * The ID of the browser window for which to return a wrapper.
+ * @param {BaseContext} context
+ * The extension context for which the matching is being performed.
+ * Used to determine the current window for relevant properties.
+ *
+ * @returns {WindowBase}
+ * @throws {ExtensionError}
+ * If no window exists with the given ID.
+ * @abstract
+ */
+ get(windowId: integer, context: BaseContext): WindowBase;
+ /**
+ * Returns an iterator of WindowBase wrappers for each currently existing
+ * browser window.
+ *
+ * @returns {Iterator<WindowBase>}
+ * @abstract
+ */
+ getAll(): Iterator<WindowBase>;
+ /**
+ * Returns a new WindowBase instance wrapping the given browser window.
+ *
+ * @param {DOMWindow} window
+ * The browser window for which to return a wrapper.
+ *
+ * @returns {WindowBase}
+ * @protected
+ * @abstract
+ */
+ protected wrapWindow(window: DOMWindow): WindowBase;
+}
+/**
+ * The platform-specific type of native tab objects, which are wrapped by
+ * TabBase instances.
+ */
+type NativeTab = object | XULElement;
+type MutedInfo = {
+ /**
+ * True if the tab is currently muted, false otherwise.
+ */
+ muted: boolean;
+ /**
+ * The reason the tab is muted. Either "user", if the tab was muted by a
+ * user, or "extension", if it was muted by an extension.
+ */
+ reason?: string;
+ /**
+ * If the tab was muted by an extension, contains the internal ID of that
+ * extension.
+ */
+ extensionId?: string;
+};
+/**
+ * The parameter type of "tab-attached" events, which are emitted when a
+ * pre-existing tab is attached to a new window.
+ */
+type TabAttachedEvent = {
+ /**
+ * The native tab object in the window to which the tab is being
+ * attached. This may be a different object than was used to represent
+ * the tab in the old window.
+ */
+ tab: NativeTab;
+ /**
+ * The ID of the tab being attached.
+ */
+ tabId: integer;
+ /**
+ * The ID of the window to which the tab is being attached.
+ */
+ newWindowId: integer;
+ /**
+ * The position of the tab in the tab list of the new window.
+ */
+ newPosition: integer;
+};
+/**
+ * The parameter type of "tab-detached" events, which are emitted when a
+ * pre-existing tab is detached from a window, in order to be attached to a new
+ * window.
+ */
+type TabDetachedEvent = {
+ /**
+ * The native tab object in the window from which the tab is being
+ * detached. This may be a different object than will be used to
+ * represent the tab in the new window.
+ */
+ tab: NativeTab;
+ /**
+ * The native tab object in the window to which the tab will be attached,
+ * and is adopting the contents of this tab. This may be a different
+ * object than the tab in the previous window.
+ */
+ adoptedBy: NativeTab;
+ /**
+ * The ID of the tab being detached.
+ */
+ tabId: integer;
+ /**
+ * The ID of the window from which the tab is being detached.
+ */
+ oldWindowId: integer;
+ /**
+ * The position of the tab in the tab list of the window from which it is
+ * being detached.
+ */
+ oldPosition: integer;
+};
+/**
+ * The parameter type of "tab-created" events, which are emitted when a
+ * new tab is created.
+ */
+type TabCreatedEvent = {
+ /**
+ * The native tab object for the tab which is being created.
+ */
+ tab: NativeTab;
+};
+/**
+ * The parameter type of "tab-removed" events, which are emitted when a
+ * tab is removed and destroyed.
+ */
+type TabRemovedEvent = {
+ /**
+ * The native tab object for the tab which is being removed.
+ */
+ tab: NativeTab;
+ /**
+ * The ID of the tab being removed.
+ */
+ tabId: integer;
+ /**
+ * The ID of the window from which the tab is being removed.
+ */
+ windowId: integer;
+ /**
+ * True if the tab is being removed because the window is closing.
+ */
+ isWindowClosing: boolean;
+};
+/**
+ * An object containing basic, extension-independent information about the window
+ * and tab that a XUL <browser> belongs to.
+ */
+type BrowserData = {
+ /**
+ * The numeric ID of the tab that a <browser> belongs to, or -1 if it
+ * does not belong to a tab.
+ */
+ tabId: integer;
+ /**
+ * The numeric ID of the browser window that a <browser> belongs to, or -1
+ * if it does not belong to a browser window.
+ */
+ windowId: integer;
+};
+
+} // namespace tabs_base
+
+declare global {
+ type TabTrackerBase = tabs_base.TabTrackerBase;
+ type TabManagerBase = tabs_base.TabManagerBase;
+ type TabBase = tabs_base.TabBase;
+ type WindowTrackerBase = tabs_base.WindowTrackerBase;
+ type WindowManagerBase = tabs_base.WindowManagerBase;
+ type WindowBase = tabs_base.WindowBase;
+ type getUserContextIdForCookieStoreId = tabs_base.getUserContextIdForCookieStoreId;
+ type NativeTab = tabs_base.NativeTab;
+}
+
+export {};
diff --git a/toolkit/components/extensions/types/extensions.ts b/toolkit/components/extensions/types/extensions.ts
index 8f9555421b..e232ab4a3f 100644
--- a/toolkit/components/extensions/types/extensions.ts
+++ b/toolkit/components/extensions/types/extensions.ts
@@ -1,5 +1,5 @@
/**
- * Type declarations for WebExtensions framework code.
+ * Types specific to toolkit/extensions code.
*/
// This has every possible property we import from all modules, which is not
@@ -42,39 +42,37 @@ type LazyAll = {
getTrimmedString: typeof import("ExtensionTelemetry.sys.mjs").getTrimmedString,
};
-// Utility type to extract all strings from a const array, to use as keys.
-type Items<A> = A extends ReadonlyArray<infer U extends string> ? U : never;
-
declare global {
- type Lazy<Keys extends keyof LazyAll = keyof LazyAll> = Pick<LazyAll, Keys> & { [k: string]: any };
+ type Lazy = Partial<LazyAll> & { [k: string]: any };
- // Export JSDoc types, and make other classes available globally.
- type ConduitAddress = import("ConduitsParent.sys.mjs").ConduitAddress;
- type ConduitID = import("ConduitsParent.sys.mjs").ConduitID;
+ type BaseContext = import("ExtensionCommon.sys.mjs").BaseContext;
+ type ExtensionChild = import("ExtensionChild.sys.mjs").ExtensionChild;
type Extension = import("Extension.sys.mjs").Extension;
+ type callback = (...any) => any;
+
+ interface nsIDOMProcessChild {
+ getActor(name: "ProcessConduits"): ProcessConduitsChild;
+ }
- // Something about Class type not being exported when nested in a namespace?
- type BaseContext = InstanceType<typeof import("ExtensionCommon.sys.mjs").ExtensionCommon.BaseContext>;
- type BrowserExtensionContent = InstanceType<typeof import("ExtensionContent.sys.mjs").ExtensionContent.BrowserExtensionContent>;
- type EventEmitter = InstanceType<typeof import("ExtensionCommon.sys.mjs").ExtensionCommon.EventEmitter>;
- type ExtensionAPI = InstanceType<typeof import("ExtensionCommon.sys.mjs").ExtensionCommon.ExtensionAPI>;
- type ExtensionError = InstanceType<typeof import("ExtensionUtils.sys.mjs").ExtensionUtils.ExtensionError>;
- type LocaleData = InstanceType<typeof import("ExtensionCommon.sys.mjs").ExtensionCommon.LocaleData>;
- type ProxyAPIImplementation = InstanceType<typeof import("ExtensionChild.sys.mjs").ExtensionChild.ProxyAPIImplementation>;
- type SchemaAPIInterface = InstanceType<typeof import("ExtensionCommon.sys.mjs").ExtensionCommon.SchemaAPIInterface>;
- type WorkerExtensionError = InstanceType<typeof import("ExtensionUtils.sys.mjs").ExtensionUtils.WorkerExtensionError>;
+ interface WebExtensionContentScript {
+ userScriptOptions: { scriptMetadata: object };
+ }
- // Other misc types.
- type AddonWrapper = any;
- type Context = BaseContext;
- type NativeTab = Element;
- type SavedFrame = object;
+ interface WebExtensionPolicy {
+ extension: Extension;
+ debugName: string;
+ instanceId: string;
+ optionalPermissions: string[];
+ }
// Can't define a const generic parameter in jsdocs yet.
// https://github.com/microsoft/TypeScript/issues/56634
- type ConduitInit<Send> = ConduitAddress & { send: Send; };
- type Conduit<Send> = import("../ConduitsChild.sys.mjs").PointConduit & { [s in `send${Items<Send>}`]: callback };
- type ConduitOpen = <const Send>(subject: object, address: ConduitInit<Send>) => Conduit<Send>;
+ function ConduitGen<const Send>(_, init: Init<Send>, _actor?): Conduit<Send>;
+ type Items<A> = A extends ReadonlyArray<infer U extends string> ? U : never;
}
-export {}
+import { PointConduit, ProcessConduitsChild } from "ConduitsChild.sys.mjs";
+import { ConduitAddress } from "ConduitsParent.sys.mjs";
+
+type Conduit<Send> = PointConduit & { [s in `send${Items<Send>}`]: callback };
+type Init<Send> = ConduitAddress & { send: Send; };
diff --git a/toolkit/components/extensions/types/gecko.ts b/toolkit/components/extensions/types/gecko.ts
index f6b5190f8d..720919d794 100644
--- a/toolkit/components/extensions/types/gecko.ts
+++ b/toolkit/components/extensions/types/gecko.ts
@@ -1,163 +1,94 @@
/**
- * Global Gecko type declarations.
+ * Gecko generic/specialized adjustments for xpcom and webidl types.
*/
-// @ts-ignore
-import type { CiClass } from "lib.gecko.xpidl"
-
-declare global {
- // Other misc types.
- type Browser = InstanceType<typeof XULBrowserElement>;
- type bytestring = string;
- type callback = (...args: any[]) => any;
- type ColorArray = number[];
- type integer = number;
- type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
-
- interface Document {
- createXULElement(name: string): Element;
- documentReadyForIdle: Promise<void>;
- }
- interface EventTarget {
- ownerGlobal: Window;
- }
- interface Error {
- code;
- }
- interface ErrorConstructor {
- new (message?: string, options?: ErrorOptions, lineNo?: number): Error;
- }
- interface Window {
- gBrowser;
- }
- // HACK to get the static isInstance for DOMException and Window?
- interface Object {
- isInstance(object: any): boolean;
- }
+// More specific types for parent process browsing contexts.
+interface CanonicalBrowsingContext extends LoadContextMixin {
+ embedderElement: XULBrowserElement;
+ currentWindowContext: WindowGlobalParent;
+ parent: CanonicalBrowsingContext;
+ parentWindowContext: WindowGlobalParent;
+ top: CanonicalBrowsingContext;
+ topWindowContext: WindowGlobalParent;
+}
- // XPIDL additions/overrides.
+interface ChromeWindow extends Window {
+ isChromeWindow: true;
+}
- interface nsISupports {
- // OMG it works!
- QueryInterface?<T extends CiClass<nsISupports>>(aCiClass: T): T['prototype'];
- wrappedJSObject?: object;
- }
- interface nsIProperties {
- get<T extends CiClass<nsISupports>>(prop: string, aCiClass: T): T['prototype'];
- }
- interface nsIPrefBranch {
- getComplexValue<T extends CiClass<nsISupports>>(aPrefName: string, aCiClass: T): T['prototype'];
- }
- // TODO: incorporate above into lib.xpidl.d.ts generation, somehow?
+interface Document {
+ createXULElement(name: "browser"): XULBrowserElement;
+}
- type Sandbox = typeof globalThis;
- interface nsIXPCComponents_utils_Sandbox {
- (principal: nsIPrincipal | nsIPrincipal[], options: object): Sandbox;
- }
- interface nsIXPCComponents_Utils {
- cloneInto<T>(obj: T, ...args: any[]): T;
- createObjectIn<T>(Sandbox, options?: T): T;
- exportFunction<T extends callback>(f: T, ...args: any[]): T;
- getWeakReference<T extends object>(T): { get(): T };
- readonly Sandbox: nsIXPCComponents_utils_Sandbox;
- waiveXrays<T>(obj: T): T;
- }
- interface nsIDOMWindow extends Window {
- docShell: nsIDocShell;
- }
- interface Document {
- documentURIObject: nsIURI;
- createXULElement(name: string): Element;
- }
+interface MessageListenerManagerMixin {
+ // Overloads that define `data` arg as required, since it's ~always expected.
+ addMessageListener(msg: string, listener: { receiveMessage(_: ReceiveMessageArgument & { data })});
+ removeMessageListener(msg: string, listener: { receiveMessage(_: ReceiveMessageArgument & { data })});
+}
+
+interface MozQueryInterface {
+ <T>(iid: T): nsQIResult<T>;
+}
- // nsDocShell is the only thing implementing nsIDocShell, but it also
- // implements nsIWebNavigation, and a few others, so this is "ok".
- interface nsIDocShell extends nsIWebNavigation {}
- interface nsISimpleEnumerator extends Iterable<any> {}
+interface nsICryptoHash extends nsISupports {
+ // Accepts a TypedArray.
+ update(aData: ArrayLike<number>, aLen: number): void;
+}
- namespace Components {
- type Exception = Error;
- }
- namespace UrlbarUtils {
- type RESULT_TYPE = any;
- type RESULT_SOURCE = any;
+interface nsIDOMWindow extends Window {}
+
+interface nsISimpleEnumerator extends Iterable<any> {}
+
+interface nsISupports {
+ wrappedJSObject?: object;
+}
+
+interface nsIXPCComponents_Constructor {
+ <const T, IIDs = nsIXPCComponents_Interfaces>(cid, id: T, init?): {
+ new (...any): nsQIResult<T extends keyof IIDs ? IIDs[T] : T>;
+ (...any): nsQIResult<T extends keyof IIDs ? IIDs[T] : T>;
}
+}
+
+interface nsIXPCComponents_Exception {
+ (...args: ConstructorParameters<typeof Error>): Error;
+}
+
+interface nsIXPCComponents_utils_Sandbox {
+ (principal: nsIPrincipal | nsIPrincipal[], options: object): typeof globalThis;
+}
- // Various mozilla globals.
- var Cc, Cr, ChromeUtils, Components, dump, uneval;
-
- // [ChromeOnly] WebIDL, to be generated.
- var BrowsingContext, ChannelWrapper, ChromeWindow, ChromeWorker,
- ClonedErrorHolder, Glean, InspectorUtils, IOUtils, JSProcessActorChild,
- JSProcessActorParent, JSWindowActor, JSWindowActorChild,
- JSWindowActorParent, L10nRegistry, L10nFileSource, Localization,
- MatchGlob, MatchPattern, MatchPatternSet, PathUtils, PreloadedScript,
- StructuredCloneHolder, TelemetryStopwatch, WindowGlobalChild,
- WebExtensionContentScript, WebExtensionParentActor, WebExtensionPolicy,
- XULBrowserElement, nsIMessageListenerManager;
-
- interface XULElement extends Element {}
-
- // nsIServices is not a thing.
- interface nsIServices {
- scriptloader: mozIJSSubScriptLoader;
- locale: mozILocaleService;
- intl: mozIMozIntl;
- storage: mozIStorageService;
- appShell: nsIAppShellService;
- startup: nsIAppStartup;
- blocklist: nsIBlocklistService;
- cache2: nsICacheStorageService;
- catMan: nsICategoryManager;
- clearData: nsIClearDataService;
- clipboard: nsIClipboard;
- console: nsIConsoleService;
- cookieBanners: nsICookieBannerService;
- cookies: nsICookieManager & nsICookieService;
- appinfo: nsICrashReporter & nsIXULAppInfo & nsIXULRuntime;
- DAPTelemetry: nsIDAPTelemetry;
- DOMRequest: nsIDOMRequestService;
- dns: nsIDNSService;
- dirsvc: nsIDirectoryService & nsIProperties;
- droppedLinkHandler: nsIDroppedLinkHandler;
- eTLD: nsIEffectiveTLDService;
- policies: nsIEnterprisePolicies;
- env: nsIEnvironment;
- els: nsIEventListenerService;
- fog: nsIFOG;
- focus: nsIFocusManager;
- io: nsIIOService & nsINetUtil & nsISpeculativeConnect;
- loadContextInfo: nsILoadContextInfoFactory;
- domStorageManager: nsIDOMStorageManager & nsILocalStorageManager;
- logins: nsILoginManager;
- obs: nsIObserverService;
- perms: nsIPermissionManager;
- prefs: nsIPrefBranch & nsIPrefService;
- profiler: nsIProfiler;
- prompt: nsIPromptService;
- sysinfo: nsISystemInfo & nsIPropertyBag2;
- qms: nsIQuotaManagerService;
- rfp: nsIRFPService;
- scriptSecurityManager: nsIScriptSecurityManager;
- search: nsISearchService;
- sessionStorage: nsISessionStorageService;
- strings: nsIStringBundleService;
- telemetry: nsITelemetry;
- textToSubURI: nsITextToSubURI;
- tm: nsIThreadManager;
- uriFixup: nsIURIFixup;
- urlFormatter: nsIURLFormatter;
- uuid: nsIUUIDGenerator;
- vc: nsIVersionComparator;
- wm: nsIWindowMediator;
- ww: nsIWindowWatcher;
- xulStore: nsIXULStore;
- ppmm: any;
- cpmm: any;
- mm: any;
+interface nsXPCComponents_Classes {
+ [cid: string]: {
+ createInstance<T>(aID: T): nsQIResult<T>;
+ getService<T>(aID?: T): unknown extends T ? nsISupports : nsQIResult<T>;
}
+}
- var Ci: nsIXPCComponents_Interfaces;
- var Cu: nsIXPCComponents_Utils;
- var Services: nsIServices;
+// Generic overloads.
+interface nsXPCComponents_Utils {
+ cloneInto<T>(value: T, ...any): T;
+ createObjectIn<T = object>(_, object?: T): T;
+ exportFunction<T>(func: T, ...any): T;
+ getWeakReference<T>(value: T): { get(): T };
+ waiveXrays<T>(object: T): T;
}
+
+// TODO: remove after next TS update.
+interface PromiseConstructor {
+ withResolvers<T>(): {
+ promise: Promise<T>;
+ resolve: (value: T | PromiseLike<T>) => void;
+ reject: (reason?: any) => void;
+ };
+}
+
+// Hand-crafted artisanal types.
+interface XULBrowserElement extends XULFrameElement, FrameLoader {
+ currentURI: nsIURI;
+ docShellIsActive: boolean;
+ isRemoteBrowser: boolean;
+ remoteType: string;
+}
+
+type nsQIResult<iid> = import("gecko/lib.gecko.xpcom").nsQIResult<iid>;
diff --git a/toolkit/components/extensions/types/glean.d.ts b/toolkit/components/extensions/types/glean.d.ts
new file mode 100644
index 0000000000..842a5a56de
--- /dev/null
+++ b/toolkit/components/extensions/types/glean.d.ts
@@ -0,0 +1,79 @@
+/**
+ * NOTE: Do not modify this file by hand.
+ * Content was generated from source metrics.yaml files.
+ */
+
+interface GleanImpl {
+
+ // toolkit/mozapps/extensions/metrics.yaml
+
+ addonsManager: {
+ install: GleanEvent;
+ update: GleanEvent;
+ installStats: GleanEvent;
+ manage: GleanEvent;
+ report: GleanEvent;
+ reportSuspiciousSite: GleanEvent;
+ }
+
+ blocklist: {
+ lastModifiedRsAddonsMblf: GleanDatetime;
+ mlbfSource: GleanString;
+ mlbfGenerationTime: GleanDatetime;
+ mlbfStashTimeOldest: GleanDatetime;
+ mlbfStashTimeNewest: GleanDatetime;
+ addonBlockChange: GleanEvent;
+ }
+
+ // toolkit/components/extensions/metrics.yaml
+
+ extensions: {
+ useRemotePref: GleanBoolean;
+ useRemotePolicy: GleanBoolean;
+ startupCacheLoadTime: GleanTimespan;
+ startupCacheReadErrors: Record<string, GleanCounter>;
+ startupCacheWriteBytelength: GleanQuantity;
+ processEvent: Record<string, GleanCounter>;
+ }
+
+ extensionsApisDnr: {
+ startupCacheReadSize: GleanMemoryDistribution;
+ startupCacheReadTime: GleanTimingDistribution;
+ startupCacheWriteSize: GleanMemoryDistribution;
+ startupCacheWriteTime: GleanTimingDistribution;
+ startupCacheEntries: Record<string, GleanCounter>;
+ validateRulesTime: GleanTimingDistribution;
+ evaluateRulesTime: GleanTimingDistribution;
+ evaluateRulesCountMax: GleanQuantity;
+ }
+
+ extensionsData: {
+ migrateResult: GleanEvent;
+ storageLocalError: GleanEvent;
+ }
+
+ extensionsQuarantinedDomains: {
+ listsize: GleanQuantity;
+ listhash: GleanString;
+ remotehash: GleanString;
+ }
+
+ extensionsCounters: {
+ browserActionPreloadResult: Record<string, GleanCounter>;
+ eventPageIdleResult: Record<string, GleanCounter>;
+ }
+
+ extensionsTiming: {
+ backgroundPageLoad: GleanTimingDistribution;
+ browserActionPopupOpen: GleanTimingDistribution;
+ contentScriptInjection: GleanTimingDistribution;
+ eventPageRunningTime: GleanCustomDistribution;
+ extensionStartup: GleanTimingDistribution;
+ pageActionPopupOpen: GleanTimingDistribution;
+ storageLocalGetJson: GleanTimingDistribution;
+ storageLocalSetJson: GleanTimingDistribution;
+ storageLocalGetIdb: GleanTimingDistribution;
+ storageLocalSetIdb: GleanTimingDistribution;
+ }
+
+}
diff --git a/toolkit/components/extensions/types/globals.ts b/toolkit/components/extensions/types/globals.ts
index 45722828e2..dee9de0cf4 100644
--- a/toolkit/components/extensions/types/globals.ts
+++ b/toolkit/components/extensions/types/globals.ts
@@ -1,33 +1,32 @@
/**
- * Support types for toolkit/components/extensions code.
+ * Gecko globals.
*/
+declare global {
+ const Cc: nsXPCComponents_Classes;
+ const Ci: nsIXPCComponents_Interfaces;
+ const Cr: nsIXPCComponents_Results;
+ const Components: nsIXPCComponents;
-/// <reference lib="dom" />
-/// <reference path="./gecko.ts" />
-/// <reference path="./extensions.ts" />
+ // Resolve typed generic overloads before the generated ones.
+ const Cu: nsXPCComponents_Utils & nsIXPCComponents_Utils;
-// This now relies on types generated in bug 1872918, or get the built
-// artifact tslib directly and put it in your src/node_modules/@types:
-// https://phabricator.services.mozilla.com/D197620
-/// <reference types="lib.gecko.xpidl" />
+ const Glean: GleanImpl;
+ const Services: JSServices;
+ const uneval: (any) => string;
+}
-// Exports for all other external modules redirected to globals.ts.
-export var AppConstants,
- GeckoViewConnection, GeckoViewWebExtension, IndexedDB, JSONFile, Log;
+// Exports for all modules redirected here by a catch-all rule in tsconfig.json.
+export var
+ AddonWrapper, AppConstants, GeckoViewConnection, GeckoViewWebExtension,
+ IndexedDB, JSONFile, Log, UrlbarUtils, WebExtensionDescriptorActor;
/**
- * This is a mock for the "class" from EventEmitter.sys.mjs. When we import
- * it in extensions code using resource://gre/modules/EventEmitter.sys.mjs,
- * the catch-all rule from tsconfig.json redirects it to this file. The export
- * of the class below fulfills the import. The mock is needed when we subclass
- * that EventEmitter, typescript gets confused because it's an old style
- * function-and-prototype-based "class", and some types don't match up.
- *
+ * A stub type for the "class" from EventEmitter.sys.mjs.
* TODO: Convert EventEmitter.sys.mjs into a proper class.
*/
export declare class EventEmitter {
+ emit(event: string, ...args: any[]): void;
on(event: string, listener: callback): void;
once(event: string, listener: callback): Promise<any>;
off(event: string, listener: callback): void;
- emit(event: string, ...args: any[]): void;
}
diff --git a/toolkit/components/extensions/webrequest/StreamFilterParent.cpp b/toolkit/components/extensions/webrequest/StreamFilterParent.cpp
index 467616ed0f..ba8700e7f1 100644
--- a/toolkit/components/extensions/webrequest/StreamFilterParent.cpp
+++ b/toolkit/components/extensions/webrequest/StreamFilterParent.cpp
@@ -230,7 +230,7 @@ StreamFilterParent::CheckListenerChain() {
if (trsl) {
return trsl->CheckListenerChain();
}
- return NS_ERROR_FAILURE;
+ return NS_ERROR_NO_INTERFACE;
}
NS_IMETHODIMP