summaryrefslogtreecommitdiffstats
path: root/remote/shared/messagehandler/test/browser/resources
diff options
context:
space:
mode:
Diffstat (limited to 'remote/shared/messagehandler/test/browser/resources')
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/ModuleRegistry.sys.mjs40
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/root/command.sys.mjs29
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/root/event.sys.mjs21
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/root/invalid.sys.mjs4
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/root/rootOnly.sys.mjs70
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/root/windowglobaltoroot.sys.mjs29
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/command.sys.mjs28
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/event.sys.mjs31
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal/command.sys.mjs85
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal/commandwindowglobalonly.sys.mjs41
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal/event.sys.mjs26
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventemitter.sys.mjs81
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventnointercept.sys.mjs16
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventonprefchange.sys.mjs33
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal/retry.sys.mjs84
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal/sessiondataupdate.sys.mjs33
-rw-r--r--remote/shared/messagehandler/test/browser/resources/modules/windowglobal/windowglobaltoroot.sys.mjs47
17 files changed, 698 insertions, 0 deletions
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/ModuleRegistry.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/ModuleRegistry.sys.mjs
new file mode 100644
index 0000000000..7d93f45b33
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/ModuleRegistry.sys.mjs
@@ -0,0 +1,40 @@
+/* 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/. */
+
+export const modules = {
+ root: {},
+ "windowglobal-in-root": {},
+ windowglobal: {},
+};
+
+const BASE_FOLDER =
+ "chrome://mochitests/content/browser/remote/shared/messagehandler/test/browser/resources/modules";
+
+// eslint-disable-next-line mozilla/lazy-getter-object-name
+ChromeUtils.defineESModuleGetters(modules.root, {
+ command: `${BASE_FOLDER}/root/command.sys.mjs`,
+ event: `${BASE_FOLDER}/root/event.sys.mjs`,
+ invalid: `${BASE_FOLDER}/root/invalid.sys.mjs`,
+ rootOnly: `${BASE_FOLDER}/root/rootOnly.sys.mjs`,
+ windowglobaltoroot: `${BASE_FOLDER}/root/windowglobaltoroot.sys.mjs`,
+});
+
+// eslint-disable-next-line mozilla/lazy-getter-object-name
+ChromeUtils.defineESModuleGetters(modules["windowglobal-in-root"], {
+ command: `${BASE_FOLDER}/windowglobal-in-root/command.sys.mjs`,
+ event: `${BASE_FOLDER}/windowglobal-in-root/event.sys.mjs`,
+});
+
+// eslint-disable-next-line mozilla/lazy-getter-object-name
+ChromeUtils.defineESModuleGetters(modules.windowglobal, {
+ command: `${BASE_FOLDER}/windowglobal/command.sys.mjs`,
+ commandwindowglobalonly: `${BASE_FOLDER}/windowglobal/commandwindowglobalonly.sys.mjs`,
+ event: `${BASE_FOLDER}/windowglobal/event.sys.mjs`,
+ eventemitter: `${BASE_FOLDER}/windowglobal/eventemitter.sys.mjs`,
+ eventnointercept: `${BASE_FOLDER}/windowglobal/eventnointercept.sys.mjs`,
+ eventonprefchange: `${BASE_FOLDER}/windowglobal/eventonprefchange.sys.mjs`,
+ retry: `${BASE_FOLDER}/windowglobal/retry.sys.mjs`,
+ sessiondataupdate: `${BASE_FOLDER}/windowglobal/sessiondataupdate.sys.mjs`,
+ windowglobaltoroot: `${BASE_FOLDER}/windowglobal/windowglobaltoroot.sys.mjs`,
+});
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/root/command.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/root/command.sys.mjs
new file mode 100644
index 0000000000..29e4a75828
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/root/command.sys.mjs
@@ -0,0 +1,29 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class CommandModule extends Module {
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ testRootModule() {
+ return "root-value";
+ }
+
+ testMissingIntermediaryMethod(params, destination) {
+ // Spawn a new internal command, but with a commandName which doesn't match
+ // any method.
+ return this.messageHandler.handleCommand({
+ moduleName: "command",
+ commandName: "missingMethod",
+ destination,
+ });
+ }
+}
+
+export const command = CommandModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/root/event.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/root/event.sys.mjs
new file mode 100644
index 0000000000..e49437e80d
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/root/event.sys.mjs
@@ -0,0 +1,21 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class EventModule extends Module {
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ testEmitRootEvent() {
+ this.emitEvent("event-from-root", {
+ text: "event from root",
+ });
+ }
+}
+
+export const event = EventModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/root/invalid.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/root/invalid.sys.mjs
new file mode 100644
index 0000000000..3b74769d06
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/root/invalid.sys.mjs
@@ -0,0 +1,4 @@
+// This module is meant to check error reporting when importing a module fails
+// due to an actual issue (syntax error etc...).
+
+SyntaxError(;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/root/rootOnly.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/root/rootOnly.sys.mjs
new file mode 100644
index 0000000000..0931a7ee8e
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/root/rootOnly.sys.mjs
@@ -0,0 +1,70 @@
+/* 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 { ContextDescriptorType } from "chrome://remote/content/shared/messagehandler/MessageHandler.sys.mjs";
+import { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class RootOnlyModule extends Module {
+ #sessionDataReceived;
+ #subscribedEvents;
+
+ constructor(messageHandler) {
+ super(messageHandler);
+
+ this.#sessionDataReceived = [];
+ this.#subscribedEvents = new Set();
+ }
+
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ getSessionDataReceived() {
+ return this.#sessionDataReceived;
+ }
+
+ testCommand(params = {}) {
+ return params;
+ }
+
+ _applySessionData(params) {
+ const added = [];
+ const removed = [];
+
+ const filteredSessionData = params.sessionData.filter(item =>
+ this.messageHandler.matchesContext(item.contextDescriptor)
+ );
+ for (const event of this.#subscribedEvents.values()) {
+ const hasSessionItem = filteredSessionData.some(
+ item => item.value === event
+ );
+ // If there are no session items for this context, we should unsubscribe from the event.
+ if (!hasSessionItem) {
+ this.#subscribedEvents.delete(event);
+ removed.push(event);
+ }
+ }
+
+ // Subscribe to all events, which have an item in SessionData
+ for (const { value } of filteredSessionData) {
+ if (!this.#subscribedEvents.has(value)) {
+ this.#subscribedEvents.add(value);
+ added.push(value);
+ }
+ }
+
+ this.#sessionDataReceived.push({
+ category: params.category,
+ added,
+ removed,
+ contextDescriptor: {
+ type: ContextDescriptorType.All,
+ },
+ });
+ }
+}
+
+export const rootOnly = RootOnlyModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/root/windowglobaltoroot.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/root/windowglobaltoroot.sys.mjs
new file mode 100644
index 0000000000..0975c4abd5
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/root/windowglobaltoroot.sys.mjs
@@ -0,0 +1,29 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class WindowGlobalToRootModule extends Module {
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ getValueFromRoot() {
+ this.#assertParentProcess();
+ return "root-value-called-from-windowglobal";
+ }
+
+ #assertParentProcess() {
+ const isParent =
+ Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT;
+
+ if (!isParent) {
+ throw new Error("Can only run in the parent process");
+ }
+ }
+}
+
+export const windowglobaltoroot = WindowGlobalToRootModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/command.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/command.sys.mjs
new file mode 100644
index 0000000000..f9a2e5d4eb
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/command.sys.mjs
@@ -0,0 +1,28 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class CommandModule extends Module {
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ testInterceptModule() {
+ return "intercepted-value";
+ }
+
+ async testInterceptAndForwardModule(params, destination) {
+ const windowGlobalValue = await this.messageHandler.handleCommand({
+ moduleName: "command",
+ commandName: "testForwardToWindowGlobal",
+ destination,
+ });
+ return "intercepted-and-forward+" + windowGlobalValue;
+ }
+}
+
+export const command = CommandModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/event.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/event.sys.mjs
new file mode 100644
index 0000000000..2969e0a3e8
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/event.sys.mjs
@@ -0,0 +1,31 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class EventModule extends Module {
+ destroy() {}
+
+ interceptEvent(name, payload) {
+ if (name === "event.testEventWithInterception") {
+ return {
+ ...payload,
+ additionalInformation: "information added through interception",
+ };
+ }
+ return payload;
+ }
+
+ /**
+ * Commands
+ */
+
+ testEmitWindowGlobalInRootEvent(params, destination) {
+ this.emitEvent("event-from-window-global-in-root", {
+ text: `windowglobal-in-root event for ${destination.id}`,
+ });
+ }
+}
+
+export const event = EventModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/command.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/command.sys.mjs
new file mode 100644
index 0000000000..99ee76a4b8
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/command.sys.mjs
@@ -0,0 +1,85 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class CommandModule extends Module {
+ constructor(messageHandler) {
+ super(messageHandler);
+ this._subscribedEvents = new Set();
+
+ this._createdByMessageHandlerConstructor =
+ this._isCreatedByMessageHandlerConstructor();
+ }
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ _applySessionData(params) {
+ if (params.category === "testCategory") {
+ const filteredSessionData = params.sessionData.filter(item =>
+ this.messageHandler.matchesContext(item.contextDescriptor)
+ );
+ for (const event of this._subscribedEvents.values()) {
+ const hasSessionItem = filteredSessionData.some(
+ item => item.value === event
+ );
+ // If there are no session items for this context, we should unsubscribe from the event.
+ if (!hasSessionItem) {
+ this._subscribedEvents.delete(event);
+ }
+ }
+
+ // Subscribe to all events, which have an item in SessionData
+ for (const { value } of filteredSessionData) {
+ if (!this._subscribedEvents.has(value)) {
+ this._subscribedEvents.add(value);
+ }
+ }
+ }
+
+ if (params.category === "browser_session_data_browser_element") {
+ this.emitEvent("received-session-data", {
+ contextId: this.messageHandler.contextId,
+ });
+ }
+ }
+
+ testWindowGlobalModule() {
+ return "windowglobal-value";
+ }
+
+ testSetValue(params) {
+ const { value } = params;
+
+ this._testValue = value;
+ }
+
+ testGetValue() {
+ return this._testValue;
+ }
+
+ testForwardToWindowGlobal() {
+ return "forward-to-windowglobal-value";
+ }
+
+ testIsCreatedByMessageHandlerConstructor() {
+ return this._createdByMessageHandlerConstructor;
+ }
+
+ _isCreatedByMessageHandlerConstructor() {
+ let caller = Components.stack.caller;
+ while (caller) {
+ if (caller.name === this.messageHandler.constructor.name) {
+ return true;
+ }
+ caller = caller.caller;
+ }
+ return false;
+ }
+}
+
+export const command = CommandModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/commandwindowglobalonly.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/commandwindowglobalonly.sys.mjs
new file mode 100644
index 0000000000..1e4e6c1574
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/commandwindowglobalonly.sys.mjs
@@ -0,0 +1,41 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class CommandWindowGlobalOnlyModule extends Module {
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ testOnlyInWindowGlobal() {
+ return "only-in-windowglobal";
+ }
+
+ testBroadcast() {
+ return `broadcast-${this.messageHandler.contextId}`;
+ }
+
+ testBroadcastWithParameter(params) {
+ return `broadcast-${this.messageHandler.contextId}-${params.value}`;
+ }
+
+ testError() {
+ throw new Error("error-from-module");
+ }
+
+ testMissingIntermediaryMethod(params, destination) {
+ // Spawn a new internal command, but with a commandName which doesn't match
+ // any method.
+ return this.messageHandler.handleCommand({
+ moduleName: "commandwindowglobalonly",
+ commandName: "missingMethod",
+ destination,
+ });
+ }
+}
+
+export const commandwindowglobalonly = CommandWindowGlobalOnlyModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/event.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/event.sys.mjs
new file mode 100644
index 0000000000..5bb50cb83d
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/event.sys.mjs
@@ -0,0 +1,26 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class EventModule extends Module {
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ testEmitEvent() {
+ // Emit a payload including the contextId to check which context emitted
+ // a specific event.
+ const text = `event from ${this.messageHandler.contextId}`;
+ this.emitEvent("event-from-window-global", { text });
+ }
+
+ testEmitEventWithInterception() {
+ this.emitEvent("event.testEventWithInterception", {});
+ }
+}
+
+export const event = EventModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventemitter.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventemitter.sys.mjs
new file mode 100644
index 0000000000..c86954c5e0
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventemitter.sys.mjs
@@ -0,0 +1,81 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class EventEmitterModule extends Module {
+ #isSubscribed;
+ #subscribedEvents;
+
+ constructor(messageHandler) {
+ super(messageHandler);
+ this.#isSubscribed = false;
+ this.#subscribedEvents = new Set();
+ }
+
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ emitTestEvent() {
+ if (this.#isSubscribed) {
+ const text = `event from ${this.messageHandler.contextId}`;
+ this.emitEvent("eventemitter.testEvent", { text });
+ }
+
+ // Emit another event consistently for monitoring during the test.
+ this.emitEvent("eventemitter.monitoringEvent", {});
+ }
+
+ isSubscribed() {
+ return this.#isSubscribed;
+ }
+
+ _applySessionData(params) {
+ const { category } = params;
+ if (category === "event") {
+ const filteredSessionData = params.sessionData.filter(item =>
+ this.messageHandler.matchesContext(item.contextDescriptor)
+ );
+ for (const event of this.#subscribedEvents.values()) {
+ const hasSessionItem = filteredSessionData.some(
+ item => item.value === event
+ );
+ // If there are no session items for this context, we should unsubscribe from the event.
+ if (!hasSessionItem) {
+ this.#unsubscribeEvent(event);
+ }
+ }
+
+ // Subscribe to all events, which have an item in SessionData
+ for (const { value } of filteredSessionData) {
+ this.#subscribeEvent(value);
+ }
+ }
+ }
+
+ #subscribeEvent(event) {
+ if (event === "eventemitter.testEvent") {
+ if (this.#isSubscribed) {
+ throw new Error("Already subscribed to eventemitter.testEvent");
+ }
+ this.#isSubscribed = true;
+ this.#subscribedEvents.add(event);
+ }
+ }
+
+ #unsubscribeEvent(event) {
+ if (event === "eventemitter.testEvent") {
+ if (!this.#isSubscribed) {
+ throw new Error("Not subscribed to eventemitter.testEvent");
+ }
+ this.#isSubscribed = false;
+ this.#subscribedEvents.delete(event);
+ }
+ }
+}
+
+export const eventemitter = EventEmitterModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventnointercept.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventnointercept.sys.mjs
new file mode 100644
index 0000000000..48bbfbf951
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventnointercept.sys.mjs
@@ -0,0 +1,16 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class EventNoInterceptModule extends Module {
+ destroy() {}
+
+ testEvent() {
+ const text = `event no interception`;
+ this.emitEvent("eventnointercept.testEvent", { text });
+ }
+}
+
+export const eventnointercept = EventNoInterceptModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventonprefchange.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventonprefchange.sys.mjs
new file mode 100644
index 0000000000..33cb25d10b
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/eventonprefchange.sys.mjs
@@ -0,0 +1,33 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+const TEST_PREF = "remote.messagehandler.test.pref";
+
+class EventOnPrefChangeModule extends Module {
+ constructor(messageHandler) {
+ super(messageHandler);
+ Services.prefs.addObserver(TEST_PREF, this.#onPreferenceUpdated);
+ }
+
+ destroy() {
+ Services.prefs.removeObserver(TEST_PREF, this.#onPreferenceUpdated);
+ }
+
+ #onPreferenceUpdated = () => {
+ this.emitEvent("preference-changed");
+ };
+
+ /**
+ * Commands
+ */
+
+ ping() {
+ // We only use this command to force creating the module.
+ return 1;
+ }
+}
+
+export const eventonprefchange = EventOnPrefChangeModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/retry.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/retry.sys.mjs
new file mode 100644
index 0000000000..f7b2279018
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/retry.sys.mjs
@@ -0,0 +1,84 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+// Store counters in the JSM scope to persist them across reloads.
+let callsToBlockedOneTime = 0;
+let callsToBlockedTenTimes = 0;
+let callsToBlockedElevenTimes = 0;
+
+// This module provides various commands which all hang for various reasons.
+// The test is supposed to trigger the command and then destroy the
+// JSWindowActor pair by any mean (eg a navigation) in order to trigger an
+// AbortError and a retry.
+class RetryModule extends Module {
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ // Resolves only if called while on the example.net domain.
+ async blockedOnNetDomain(params) {
+ // Note: we do not store a call counter here, because this is used for a
+ // cross-group navigation test, and the JSM will be loaded in different
+ // processes.
+ const uri = this.messageHandler.window.document.baseURI;
+ if (!uri.includes("example.net")) {
+ await new Promise(r => {});
+ }
+
+ return { ...params };
+ }
+
+ // Resolves only if called more than once.
+ async blockedOneTime(params) {
+ callsToBlockedOneTime++;
+ if (callsToBlockedOneTime < 2) {
+ await new Promise(r => {});
+ }
+
+ // Return:
+ // - params sent to the command to check that retries have correct params
+ // - the call counter
+ return { ...params, callsToCommand: callsToBlockedOneTime };
+ }
+
+ // Resolves only if called more than ten times (which is exactly the maximum
+ // of retry attempts).
+ async blockedTenTimes(params) {
+ callsToBlockedTenTimes++;
+ if (callsToBlockedTenTimes < 11) {
+ await new Promise(r => {});
+ }
+
+ // Return:
+ // - params sent to the command to check that retries have correct params
+ // - the call counter
+ return { ...params, callsToCommand: callsToBlockedTenTimes };
+ }
+
+ // Resolves only if called more than eleven times (which is greater than the
+ // maximum of retry attempts).
+ async blockedElevenTimes(params) {
+ callsToBlockedElevenTimes++;
+ if (callsToBlockedElevenTimes < 12) {
+ await new Promise(r => {});
+ }
+
+ // Return:
+ // - params sent to the command to check that retries have correct params
+ // - the call counter
+ return { ...params, callsToCommand: callsToBlockedElevenTimes };
+ }
+
+ cleanup() {
+ callsToBlockedOneTime = 0;
+ callsToBlockedTenTimes = 0;
+ callsToBlockedElevenTimes = 0;
+ }
+}
+
+export const retry = RetryModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/sessiondataupdate.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/sessiondataupdate.sys.mjs
new file mode 100644
index 0000000000..5e9ce00b46
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/sessiondataupdate.sys.mjs
@@ -0,0 +1,33 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+
+class SessionDataUpdateModule extends Module {
+ #sessionDataUpdates;
+
+ constructor(messageHandler) {
+ super(messageHandler);
+ this.#sessionDataUpdates = [];
+ }
+
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ _applySessionData(params) {
+ const filteredSessionData = params.sessionData.filter(item =>
+ this.messageHandler.matchesContext(item.contextDescriptor)
+ );
+ this.#sessionDataUpdates.push(filteredSessionData);
+ }
+
+ getSessionDataUpdates() {
+ return this.#sessionDataUpdates;
+ }
+}
+
+export const sessiondataupdate = SessionDataUpdateModule;
diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/windowglobaltoroot.sys.mjs b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/windowglobaltoroot.sys.mjs
new file mode 100644
index 0000000000..815a836d9c
--- /dev/null
+++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/windowglobaltoroot.sys.mjs
@@ -0,0 +1,47 @@
+/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
+import { RootMessageHandler } from "chrome://remote/content/shared/messagehandler/RootMessageHandler.sys.mjs";
+
+class WindowGlobalToRootModule extends Module {
+ constructor(messageHandler) {
+ super(messageHandler);
+ this.#assertContentProcess();
+ }
+
+ destroy() {}
+
+ /**
+ * Commands
+ */
+
+ testHandleCommandToRoot(params, destination) {
+ return this.messageHandler.handleCommand({
+ moduleName: "windowglobaltoroot",
+ commandName: "getValueFromRoot",
+ destination: {
+ type: RootMessageHandler.type,
+ },
+ });
+ }
+
+ testSendRootCommand(params, destination) {
+ return this.messageHandler.sendRootCommand({
+ moduleName: "windowglobaltoroot",
+ commandName: "getValueFromRoot",
+ });
+ }
+
+ #assertContentProcess() {
+ const isContent =
+ Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
+
+ if (!isContent) {
+ throw new Error("Can only run in a content process");
+ }
+ }
+}
+
+export const windowglobaltoroot = WindowGlobalToRootModule;