diff options
Diffstat (limited to 'remote/shared/messagehandler/RootMessageHandler.sys.mjs')
-rw-r--r-- | remote/shared/messagehandler/RootMessageHandler.sys.mjs | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/remote/shared/messagehandler/RootMessageHandler.sys.mjs b/remote/shared/messagehandler/RootMessageHandler.sys.mjs new file mode 100644 index 0000000000..06a8cd6f18 --- /dev/null +++ b/remote/shared/messagehandler/RootMessageHandler.sys.mjs @@ -0,0 +1,237 @@ +/* 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/. */ + +import { MessageHandler } from "chrome://remote/content/shared/messagehandler/MessageHandler.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + NavigationManager: "chrome://remote/content/shared/NavigationManager.sys.mjs", + RootTransport: + "chrome://remote/content/shared/messagehandler/transports/RootTransport.sys.mjs", + SessionData: + "chrome://remote/content/shared/messagehandler/sessiondata/SessionData.sys.mjs", + SessionDataMethod: + "chrome://remote/content/shared/messagehandler/sessiondata/SessionData.sys.mjs", + WindowGlobalMessageHandler: + "chrome://remote/content/shared/messagehandler/WindowGlobalMessageHandler.sys.mjs", +}); + +/** + * A RootMessageHandler is the root node of a MessageHandler network. It lives + * in the parent process. It can forward commands to MessageHandlers in other + * layers (at the moment WindowGlobalMessageHandlers in content processes). + */ +export class RootMessageHandler extends MessageHandler { + #navigationManager; + #realms; + #rootTransport; + #sessionData; + + /** + * Returns the RootMessageHandler module path. + * + * @returns {string} + */ + static get modulePath() { + return "root"; + } + + /** + * Returns the RootMessageHandler type. + * + * @returns {string} + */ + static get type() { + return "ROOT"; + } + + /** + * The ROOT MessageHandler is unique for a given MessageHandler network + * (ie for a given sessionId). Reuse the type as context id here. + */ + static getIdFromContext(context) { + return RootMessageHandler.type; + } + + /** + * Create a new RootMessageHandler instance. + * + * @param {string} sessionId + * ID of the session the handler is used for. + */ + constructor(sessionId) { + super(sessionId, null); + + this.#rootTransport = new lazy.RootTransport(this); + this.#sessionData = new lazy.SessionData(this); + this.#navigationManager = new lazy.NavigationManager(); + this.#navigationManager.startMonitoring(); + + // Map with inner window ids as keys, and sets of realm ids, assosiated with + // this window as values. + this.#realms = new Map(); + // In the general case, we don't get notified that realms got destroyed, + // because there is no communication between content and parent process at this moment, + // so we have to listen to the this notification to clean up the internal + // map and trigger the events. + Services.obs.addObserver(this, "window-global-destroyed"); + } + + get navigationManager() { + return this.#navigationManager; + } + + get realms() { + return this.#realms; + } + + get sessionData() { + return this.#sessionData; + } + + destroy() { + this.#sessionData.destroy(); + this.#navigationManager.destroy(); + + Services.obs.removeObserver(this, "window-global-destroyed"); + this.#realms = null; + + super.destroy(); + } + + /** + * Add new session data items of a given module, category and + * contextDescriptor. + * + * Forwards the call to the SessionData instance owned by this + * RootMessageHandler and propagates the information via a command to existing + * MessageHandlers. + */ + addSessionDataItem(sessionData = {}) { + sessionData.method = lazy.SessionDataMethod.Add; + return this.updateSessionData([sessionData]); + } + + emitEvent(name, eventPayload, contextInfo) { + // Intercept realm created and destroyed events to update internal map. + if (name === "realm-created") { + this.#onRealmCreated(eventPayload); + } + // We receive this events in the case of moving the page to BFCache. + if (name === "windowglobal-pagehide") { + this.#cleanUpRealmsForWindow( + eventPayload.innerWindowId, + eventPayload.context + ); + } + + super.emitEvent(name, eventPayload, contextInfo); + } + + /** + * Emit a public protocol event. This event will be sent over to the client. + * + * @param {string} name + * Name of the event. Protocol level events should be of the + * form [module name].[event name]. + * @param {object} data + * The event's data. + */ + emitProtocolEvent(name, data) { + this.emit("message-handler-protocol-event", { + name, + data, + sessionId: this.sessionId, + }); + } + + /** + * Forward the provided command to WINDOW_GLOBAL MessageHandlers via the + * RootTransport. + * + * @param {Command} command + * The command to forward. See type definition in MessageHandler.js + * @returns {Promise} + * Returns a promise that resolves with the result of the command. + */ + forwardCommand(command) { + switch (command.destination.type) { + case lazy.WindowGlobalMessageHandler.type: + return this.#rootTransport.forwardCommand(command); + default: + throw new Error( + `Cannot forward command to "${command.destination.type}" from "${this.constructor.type}".` + ); + } + } + + matchesContext() { + return true; + } + + observe(subject, topic) { + if (topic !== "window-global-destroyed") { + return; + } + + this.#cleanUpRealmsForWindow( + subject.innerWindowId, + subject.browsingContext + ); + } + + /** + * Remove session data items of a given module, category and + * contextDescriptor. + * + * Forwards the call to the SessionData instance owned by this + * RootMessageHandler and propagates the information via a command to existing + * MessageHandlers. + */ + removeSessionDataItem(sessionData = {}) { + sessionData.method = lazy.SessionDataMethod.Remove; + return this.updateSessionData([sessionData]); + } + + /** + * Update session data items of a given module, category and + * contextDescriptor. + * + * Forwards the call to the SessionData instance owned by this + * RootMessageHandler. + */ + async updateSessionData(sessionData = []) { + await this.#sessionData.updateSessionData(sessionData); + } + + #cleanUpRealmsForWindow(innerWindowId, context) { + const realms = this.#realms.get(innerWindowId); + + if (!realms) { + return; + } + + realms.forEach(realm => { + this.#realms.get(innerWindowId).delete(realm); + + this.emitEvent("realm-destroyed", { + context, + realm, + }); + }); + + this.#realms.delete(innerWindowId); + } + + #onRealmCreated = data => { + const { innerWindowId, realmInfo } = data; + + if (!this.#realms.has(innerWindowId)) { + this.#realms.set(innerWindowId, new Set()); + } + + this.#realms.get(innerWindowId).add(realmInfo.realm); + }; +} |