diff options
Diffstat (limited to 'toolkit/components/extensions/ExtensionParent.sys.mjs')
-rw-r--r-- | toolkit/components/extensions/ExtensionParent.sys.mjs | 125 |
1 files changed, 71 insertions, 54 deletions
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. |