From ee4b6dd66643f620888d0b8a90068744d2bb881f Mon Sep 17 00:00:00 2001 From: Julian Descottes Date: Fri, 30 Jul 2021 08:17:49 +0000 Subject: [PATCH] Bug 1713439 - [remote] Add mochitests to exercize message-handler API r=webdriver-reviewers,whimboo Depends on 120083 Differential Revision: https://phabricator.services.mozilla.com/D120084 --- remote/shared/messagehandler/ModuleCache.jsm | 20 +- .../messagehandler/test/browser/browser.ini | 11 + .../browser/browser_handle_simple_command.js | 205 ++++++++++++++++++ .../test/browser/browser_registry.js | 45 ++++ .../messagehandler/test/browser/head.js | 18 ++ .../resources/modules/root/TestModule.jsm | 23 ++ .../windowglobal-in-root/TestModule.jsm | 32 +++ .../modules/windowglobal/TestModule.jsm | 35 +++ .../TestOnlyInWindowGlobalModule.jsm | 23 ++ remote/shared/moz.build | 4 + 10 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 remote/shared/messagehandler/test/browser/browser.ini create mode 100644 remote/shared/messagehandler/test/browser/browser_handle_simple_command.js create mode 100644 remote/shared/messagehandler/test/browser/browser_registry.js create mode 100644 remote/shared/messagehandler/test/browser/head.js create mode 100644 remote/shared/messagehandler/test/browser/resources/modules/root/TestModule.jsm create mode 100644 remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/TestModule.jsm create mode 100644 remote/shared/messagehandler/test/browser/resources/modules/windowglobal/TestModule.jsm create mode 100644 remote/shared/messagehandler/test/browser/resources/modules/windowglobal/TestOnlyInWindowGlobalModule.jsm diff --git a/remote/shared/messagehandler/ModuleCache.jsm b/remote/shared/messagehandler/ModuleCache.jsm index 0cd1fd72427a..2a43c18201e5 100644 --- a/remote/shared/messagehandler/ModuleCache.jsm +++ b/remote/shared/messagehandler/ModuleCache.jsm @@ -11,6 +11,8 @@ const { XPCOMUtils } = ChromeUtils.import( ); XPCOMUtils.defineLazyModuleGetters(this, { + Services: "resource://gre/modules/Services.jsm", + Log: "chrome://remote/content/shared/Log.jsm", getMessageHandlerClass: "chrome://remote/content/shared/messagehandler/MessageHandlerRegistry.jsm", @@ -23,6 +25,9 @@ XPCOMUtils.defineLazyGetter(this, "logger", () => Log.get()); // the protocol owning the MessageHandler. See Bug 1722464. const MODULES_FOLDER = "chrome://remote/content/webdriver-bidi/modules"; +const TEST_MODULES_FOLDER = + "chrome://mochitests/content/browser/remote/shared/messagehandler/test/browser/resources/modules"; + /** * ModuleCache instances are dedicated to lazily create and cache the instances * of all the modules related to a specific MessageHandler instance. @@ -63,6 +68,19 @@ class ModuleCache { this.messageHandler = messageHandler; this._messageHandlerPath = messageHandler.constructor.modulePath; + this._modulesRootFolder = MODULES_FOLDER; + // TODO: Temporary workaround to use a different folder for tests. + // After Bug 1722464 lands, we should be able to set custom root folders + // per session and we can remove this workaround. + if ( + Services.prefs.getBoolPref( + "remote.messagehandler.modulecache.useBrowserTestRoot", + false + ) + ) { + this._modulesRootFolder = TEST_MODULES_FOLDER; + } + // Map of absolute module paths to module instances. this._modules = new Map(); } @@ -127,6 +145,6 @@ class ModuleCache { moduleFolder = `${destinationPath}-in-${this._messageHandlerPath}`; } - return `${MODULES_FOLDER}/${moduleFolder}/${moduleName}.jsm`; + return `${this._modulesRootFolder}/${moduleFolder}/${moduleName}.jsm`; } } diff --git a/remote/shared/messagehandler/test/browser/browser.ini b/remote/shared/messagehandler/test/browser/browser.ini new file mode 100644 index 000000000000..23711a0f1826 --- /dev/null +++ b/remote/shared/messagehandler/test/browser/browser.ini @@ -0,0 +1,11 @@ +[DEFAULT] +tags = remote +subsuite = remote +support-files = + head.js + resources/* +prefs = + remote.messagehandler.modulecache.useBrowserTestRoot=true + +[browser_handle_simple_command.js] +[browser_registry.js] diff --git a/remote/shared/messagehandler/test/browser/browser_handle_simple_command.js b/remote/shared/messagehandler/test/browser/browser_handle_simple_command.js new file mode 100644 index 000000000000..1dad08031ba1 --- /dev/null +++ b/remote/shared/messagehandler/test/browser/browser_handle_simple_command.js @@ -0,0 +1,205 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { RootMessageHandler } = ChromeUtils.import( + "chrome://remote/content/shared/messagehandler/RootMessageHandler.jsm" +); +const { WindowGlobalMessageHandler } = ChromeUtils.import( + "chrome://remote/content/shared/messagehandler/WindowGlobalMessageHandler.jsm" +); + +// Test calling methods only implemented in the root version of a module. +add_task(async function test_rootModule_command() { + const rootMessageHandler = createRootMessageHandler("session-id-rootModule"); + const rootValue = await rootMessageHandler.handleCommand({ + moduleName: "TestModule", + commandName: "testRootModule", + destination: { + type: RootMessageHandler.type, + }, + }); + + is( + rootValue, + "root-value", + "Retrieved the expected value from testRootModule" + ); + + rootMessageHandler.destroy(); +}); + +// Test calling methods only implemented in the windowglobal-in-root version of +// a module. +add_task(async function test_windowglobalInRootModule_command() { + const browsingContextId = gBrowser.selectedBrowser.browsingContext.id; + + const rootMessageHandler = createRootMessageHandler( + "session-id-windowglobalInRootModule" + ); + const interceptedValue = await rootMessageHandler.handleCommand({ + moduleName: "TestModule", + commandName: "testInterceptModule", + destination: { + type: WindowGlobalMessageHandler.type, + id: browsingContextId, + }, + }); + + is( + interceptedValue, + "intercepted-value", + "Retrieved the expected value from testInterceptModule" + ); + + rootMessageHandler.destroy(); +}); + +// Test calling methods only implemented in the windowglobal version of a +// module. +add_task(async function test_windowglobalModule_command() { + const browsingContextId = gBrowser.selectedBrowser.browsingContext.id; + + const rootMessageHandler = createRootMessageHandler( + "session-id-windowglobalModule" + ); + const windowGlobalValue = await rootMessageHandler.handleCommand({ + moduleName: "TestModule", + commandName: "testWindowGlobalModule", + destination: { + type: WindowGlobalMessageHandler.type, + id: browsingContextId, + }, + }); + + is( + windowGlobalValue, + "windowglobal-value", + "Retrieved the expected value from testWindowGlobalModule" + ); + + rootMessageHandler.destroy(); +}); + +// Test calling a method on a module which is only available in the "windowglobal" +// folder. This will check that the MessageHandler/ModuleCache correctly moves +// on to the next layer when no implementation can be found in the root layer. +add_task(async function test_windowglobalOnlyModule_command() { + const browsingContextId = gBrowser.selectedBrowser.browsingContext.id; + + const rootMessageHandler = createRootMessageHandler( + "session-id-windowglobalOnlyModule" + ); + const windowGlobalOnlyValue = await rootMessageHandler.handleCommand({ + moduleName: "TestOnlyInWindowGlobalModule", + commandName: "testOnlyInWindowGlobal", + destination: { + type: WindowGlobalMessageHandler.type, + id: browsingContextId, + }, + }); + + is( + windowGlobalOnlyValue, + "only-in-windowglobal", + "Retrieved the expected value from testOnlyInWindowGlobal" + ); + + rootMessageHandler.destroy(); +}); + +// Try to create 2 sessions which will both set values in individual modules +// via a command `testSetValue`, and then retrieve the values via another +// command `testGetValue`. +// This will ensure that different sessions use different module instances. +add_task(async function test_multisession() { + const browsingContextId = gBrowser.selectedBrowser.browsingContext.id; + + const rootMessageHandler1 = createRootMessageHandler( + "session-id-multisession-1" + ); + const rootMessageHandler2 = createRootMessageHandler( + "session-id-multisession-2" + ); + + info("Set value for session 1"); + await rootMessageHandler1.handleCommand({ + moduleName: "TestModule", + commandName: "testSetValue", + destination: { + type: WindowGlobalMessageHandler.type, + id: browsingContextId, + }, + params: "session1-value", + }); + + info("Set value for session 2"); + await rootMessageHandler2.handleCommand({ + moduleName: "TestModule", + commandName: "testSetValue", + destination: { + type: WindowGlobalMessageHandler.type, + id: browsingContextId, + }, + params: "session2-value", + }); + + const session1Value = await rootMessageHandler1.handleCommand({ + moduleName: "TestModule", + commandName: "testGetValue", + destination: { + type: WindowGlobalMessageHandler.type, + id: browsingContextId, + }, + }); + + is( + session1Value, + "session1-value", + "Retrieved the expected value for session 1" + ); + + const session2Value = await rootMessageHandler2.handleCommand({ + moduleName: "TestModule", + commandName: "testGetValue", + destination: { + type: WindowGlobalMessageHandler.type, + id: browsingContextId, + }, + }); + + is( + session2Value, + "session2-value", + "Retrieved the expected value for session 2" + ); + + rootMessageHandler1.destroy(); + rootMessageHandler2.destroy(); +}); + +// Test calling a method from the windowglobal-in-root module which will +// internally forward to the windowglobal module and will return a composite +// result built both in parent and content process. +add_task(async function test_forwarding_command() { + const browsingContextId = gBrowser.selectedBrowser.browsingContext.id; + + const rootMessageHandler = createRootMessageHandler("session-id-forwarding"); + const interceptAndForwardValue = await rootMessageHandler.handleCommand({ + moduleName: "TestModule", + commandName: "testInterceptAndForwardModule", + destination: { + type: WindowGlobalMessageHandler.type, + id: browsingContextId, + }, + }); + + is( + interceptAndForwardValue, + "intercepted-and-forward+forward-to-windowglobal-value", + "Retrieved the expected value from testInterceptAndForwardModule" + ); + + rootMessageHandler.destroy(); +}); diff --git a/remote/shared/messagehandler/test/browser/browser_registry.js b/remote/shared/messagehandler/test/browser/browser_registry.js new file mode 100644 index 000000000000..6d0d2841c908 --- /dev/null +++ b/remote/shared/messagehandler/test/browser/browser_registry.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { MessageHandlerRegistry } = ChromeUtils.import( + "chrome://remote/content/shared/messagehandler/MessageHandlerRegistry.jsm" +); +const { RootMessageHandler } = ChromeUtils.import( + "chrome://remote/content/shared/messagehandler/RootMessageHandler.jsm" +); + +add_task(async function test_messageHandlerRegistry_API() { + const sessionId = 1; + const type = RootMessageHandler.type; + + const rootMessageHandler = MessageHandlerRegistry.getOrCreateMessageHandler( + sessionId, + type + ); + ok(rootMessageHandler, "Valid ROOT MessageHandler created"); + + const contextId = rootMessageHandler.contextId; + ok(contextId, "ROOT MessageHandler has a valid contextId"); + + is( + rootMessageHandler, + MessageHandlerRegistry.getExistingMessageHandler( + sessionId, + type, + contextId + ), + "ROOT MessageHandler can be retrieved from the registry" + ); + + rootMessageHandler.destroy(); + ok( + !MessageHandlerRegistry.getExistingMessageHandler( + sessionId, + type, + contextId + ), + "Destroyed ROOT MessageHandler is no longer returned by the Registry" + ); +}); diff --git a/remote/shared/messagehandler/test/browser/head.js b/remote/shared/messagehandler/test/browser/head.js new file mode 100644 index 000000000000..d08fdee0bbe1 --- /dev/null +++ b/remote/shared/messagehandler/test/browser/head.js @@ -0,0 +1,18 @@ +/* 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/. */ + +"use strict"; + +function createRootMessageHandler(sessionId) { + const { MessageHandlerRegistry } = ChromeUtils.import( + "chrome://remote/content/shared/messagehandler/MessageHandlerRegistry.jsm" + ); + const { RootMessageHandler } = ChromeUtils.import( + "chrome://remote/content/shared/messagehandler/RootMessageHandler.jsm" + ); + return MessageHandlerRegistry.getOrCreateMessageHandler( + sessionId, + RootMessageHandler.type + ); +} diff --git a/remote/shared/messagehandler/test/browser/resources/modules/root/TestModule.jsm b/remote/shared/messagehandler/test/browser/resources/modules/root/TestModule.jsm new file mode 100644 index 000000000000..8fff143d72de --- /dev/null +++ b/remote/shared/messagehandler/test/browser/resources/modules/root/TestModule.jsm @@ -0,0 +1,23 @@ +/* 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/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["TestModule"]; + +class TestModule { + constructor(messageHandler) { + this.messageHandler = messageHandler; + } + + destroy() {} + + /** + * Commands + */ + + testRootModule() { + return "root-value"; + } +} diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/TestModule.jsm b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/TestModule.jsm new file mode 100644 index 000000000000..3e8f899a67d7 --- /dev/null +++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal-in-root/TestModule.jsm @@ -0,0 +1,32 @@ +/* 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/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["TestModule"]; + +class TestModule { + constructor(messageHandler) { + this.messageHandler = messageHandler; + } + + destroy() {} + + /** + * Commands + */ + + testInterceptModule() { + return "intercepted-value"; + } + + async testInterceptAndForwardModule(destination) { + const windowGlobalValue = await this.messageHandler.handleCommand({ + moduleName: "TestModule", + commandName: "testForwardToWindowGlobal", + destination, + }); + return "intercepted-and-forward+" + windowGlobalValue; + } +} diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/TestModule.jsm b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/TestModule.jsm new file mode 100644 index 000000000000..0977c1df0f0d --- /dev/null +++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/TestModule.jsm @@ -0,0 +1,35 @@ +/* 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/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["TestModule"]; + +class TestModule { + constructor(messageHandler) { + this.messageHandler = messageHandler; + } + + destroy() {} + + /** + * Commands + */ + + testWindowGlobalModule() { + return "windowglobal-value"; + } + + testSetValue(destination, value) { + this._testValue = value; + } + + testGetValue() { + return this._testValue; + } + + testForwardToWindowGlobal() { + return "forward-to-windowglobal-value"; + } +} diff --git a/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/TestOnlyInWindowGlobalModule.jsm b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/TestOnlyInWindowGlobalModule.jsm new file mode 100644 index 000000000000..4e462e3f5fc6 --- /dev/null +++ b/remote/shared/messagehandler/test/browser/resources/modules/windowglobal/TestOnlyInWindowGlobalModule.jsm @@ -0,0 +1,23 @@ +/* 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/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["TestOnlyInWindowGlobalModule"]; + +class TestOnlyInWindowGlobalModule { + constructor(messageHandler) { + this.messageHandler = messageHandler; + } + + destroy() {} + + /** + * Commands + */ + + testOnlyInWindowGlobal() { + return "only-in-windowglobal"; + } +} diff --git a/remote/shared/moz.build b/remote/shared/moz.build index fd0415b71d91..917cef40ace6 100644 --- a/remote/shared/moz.build +++ b/remote/shared/moz.build @@ -2,6 +2,10 @@ # 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/. +BROWSER_CHROME_MANIFESTS += [ + "messagehandler/test/browser/browser.ini", +] + XPCSHELL_TESTS_MANIFESTS += [ "test/xpcshell/xpcshell.ini", "webdriver/test/xpcshell/xpcshell.ini",