/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { ContentProcessDomains: "chrome://remote/content/cdp/domains/ContentProcessDomains.sys.mjs", ContextObserver: "chrome://remote/content/cdp/observers/ContextObserver.sys.mjs", DomainCache: "chrome://remote/content/cdp/domains/DomainCache.sys.mjs", }); export class ContentProcessSession { constructor(messageManager, browsingContext, content, docShell) { this.messageManager = messageManager; this.browsingContext = browsingContext; this.content = content; this.docShell = docShell; // Most children or sibling classes are going to assume that docShell // implements the following interface. So do the QI only once from here. this.docShell.QueryInterface(Ci.nsIWebNavigation); this.domains = new lazy.DomainCache(this, lazy.ContentProcessDomains); this.messageManager.addMessageListener("remote:request", this); this.messageManager.addMessageListener("remote:destroy", this); } destructor() { this._contextObserver?.destructor(); this.messageManager.removeMessageListener("remote:request", this); this.messageManager.removeMessageListener("remote:destroy", this); this.domains.clear(); } get contextObserver() { if (!this._contextObserver) { this._contextObserver = new lazy.ContextObserver( this.docShell.chromeEventHandler ); } return this._contextObserver; } // Domain event listener onEvent(eventName, params) { this.messageManager.sendAsyncMessage("remote:event", { browsingContextId: this.browsingContext.id, event: { eventName, params, }, }); } // nsIMessageListener async receiveMessage({ name, data }) { const { browsingContextId } = data; // We may have more than one tab loaded in the same process, // and debug the two at the same time. We want to ensure not // mixing up the requests made against two such tabs. // Each tab is going to have its own frame script instance // and two communication channels are going to be set up via // the two message managers. if (browsingContextId != this.browsingContext.id) { return; } switch (name) { case "remote:request": try { const { id, domain, command, params } = data.request; const result = await this.domains.execute(domain, command, params); this.messageManager.sendAsyncMessage("remote:result", { browsingContextId, id, result, }); } catch (e) { this.messageManager.sendAsyncMessage("remote:error", { browsingContextId, id: data.request.id, error: { name: e.name || "exception", message: e.message || String(e), stack: e.stack, }, }); } break; case "remote:destroy": this.destructor(); break; } } }