summaryrefslogtreecommitdiffstats
path: root/remote/shared/messagehandler/RootMessageHandler.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'remote/shared/messagehandler/RootMessageHandler.sys.mjs')
-rw-r--r--remote/shared/messagehandler/RootMessageHandler.sys.mjs237
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);
+ };
+}