From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../xpcshell/test_ext_background_service_worker.js | 321 +++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 toolkit/components/extensions/test/xpcshell/test_ext_background_service_worker.js (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_background_service_worker.js') diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_background_service_worker.js b/toolkit/components/extensions/test/xpcshell/test_ext_background_service_worker.js new file mode 100644 index 0000000000..2efbc52739 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_background_service_worker.js @@ -0,0 +1,321 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + AddonManager: "resource://gre/modules/AddonManager.sys.mjs", +}); + +AddonTestUtils.init(this); +AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "1", + "42" +); +AddonTestUtils.overrideCertDB(); + +add_task(async function setup() { + ok( + WebExtensionPolicy.useRemoteWebExtensions, + "Expect remote-webextensions mode enabled" + ); + ok( + WebExtensionPolicy.backgroundServiceWorkerEnabled, + "Expect remote-webextensions mode enabled" + ); + + await AddonTestUtils.promiseStartupManager(); + + Services.prefs.setBoolPref("dom.serviceWorkers.testing.enabled", true); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref("dom.serviceWorkers.testing.enabled"); + Services.prefs.clearUserPref("dom.serviceWorkers.idle_timeout"); + }); +}); + +add_task( + async function test_fail_spawn_extension_worker_for_disabled_extension() { + const extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", + manifest: { + version: "1.0", + background: { + service_worker: "sw.js", + }, + browser_specific_settings: { gecko: { id: "test-bg-sw@mochi.test" } }, + }, + files: { + "page.html": "", + "sw.js": "dump('Background ServiceWorker - executed\\n');", + }, + }); + + const testWorkerWatcher = new TestWorkerWatcher(); + let watcher = await testWorkerWatcher.watchExtensionServiceWorker( + extension + ); + + await extension.startup(); + + info("Wait for the background service worker to be spawned"); + + ok( + await watcher.promiseWorkerSpawned, + "The extension service worker has been spawned as expected" + ); + + info("Wait for the background service worker to be terminated"); + ok( + await watcher.terminate(), + "The extension service worker has been terminated as expected" + ); + + const swReg = testWorkerWatcher.getRegistration(extension); + ok(swReg, "Got a service worker registration"); + ok(swReg?.activeWorker, "Got an active worker"); + + info("Spawn the active worker by attaching the debugger"); + + watcher = await testWorkerWatcher.watchExtensionServiceWorker(extension); + + swReg.activeWorker.attachDebugger(); + info( + "Wait for the background service worker to be spawned after attaching the debugger" + ); + ok( + await watcher.promiseWorkerSpawned, + "The extension service worker has been spawned as expected" + ); + + swReg.activeWorker.detachDebugger(); + info( + "Wait for the background service worker to be terminated after detaching the debugger" + ); + ok( + await watcher.terminate(), + "The extension service worker has been terminated as expected" + ); + + info( + "Disabling the addon policy, and then double-check that the worker can't be spawned" + ); + const policy = WebExtensionPolicy.getByID(extension.id); + policy.active = false; + + await Assert.throws( + () => swReg.activeWorker.attachDebugger(), + /InvalidStateError/, + "Got the expected extension when trying to spawn a worker for a disabled addon" + ); + + info( + "Enabling the addon policy and double-check the worker is spawned successfully" + ); + policy.active = true; + + watcher = await testWorkerWatcher.watchExtensionServiceWorker(extension); + + swReg.activeWorker.attachDebugger(); + info( + "Wait for the background service worker to be spawned after attaching the debugger" + ); + ok( + await watcher.promiseWorkerSpawned, + "The extension service worker has been spawned as expected" + ); + + swReg.activeWorker.detachDebugger(); + info( + "Wait for the background service worker to be terminated after detaching the debugger" + ); + ok( + await watcher.terminate(), + "The extension service worker has been terminated as expected" + ); + + await testWorkerWatcher.destroy(); + await extension.unload(); + } +); + +add_task(async function test_serviceworker_lifecycle_events() { + async function assertLifecycleEvents({ extension, expected, message }) { + const getLifecycleEvents = async () => { + const { active } = await this.content.navigator.serviceWorker.ready; + const { port1, port2 } = new content.MessageChannel(); + + return new Promise(resolve => { + port1.onmessage = msg => resolve(msg.data.lifecycleEvents); + active.postMessage("test", [port2]); + }); + }; + const page = await ExtensionTestUtils.loadContentPage( + extension.extension.baseURI.resolve("page.html"), + { extension } + ); + Assert.deepEqual( + await page.spawn([], getLifecycleEvents), + expected, + `Got the expected lifecycle events on ${message}` + ); + await page.close(); + } + + const swm = Cc["@mozilla.org/serviceworkers/manager;1"].getService( + Ci.nsIServiceWorkerManager + ); + + const extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + version: "1.0", + background: { + service_worker: "sw.js", + }, + browser_specific_settings: { gecko: { id: "test-bg-sw@mochi.test" } }, + }, + files: { + "page.html": "", + "sw.js": ` + dump('Background ServiceWorker - executed\\n'); + + const lifecycleEvents = []; + self.oninstall = () => { + dump('Background ServiceWorker - oninstall\\n'); + lifecycleEvents.push("install"); + }; + self.onactivate = () => { + dump('Background ServiceWorker - onactivate\\n'); + lifecycleEvents.push("activate"); + }; + self.onmessage = (evt) => { + dump('Background ServiceWorker - onmessage\\n'); + evt.ports[0].postMessage({ lifecycleEvents }); + dump('Background ServiceWorker - postMessage\\n'); + }; + `, + }, + }); + + const testWorkerWatcher = new TestWorkerWatcher(); + let watcher = await testWorkerWatcher.watchExtensionServiceWorker(extension); + + await extension.startup(); + + await assertLifecycleEvents({ + extension, + expected: ["install", "activate"], + message: "initial worker registration", + }); + + const file = Services.dirsvc.get("ProfD", Ci.nsIFile); + file.append("serviceworker.txt"); + await TestUtils.waitForCondition( + () => file.exists(), + "Wait for service worker registrations to have been dumped on disk" + ); + + const managerShutdownCompleted = AddonTestUtils.promiseShutdownManager(); + + const firstSwReg = swm.getRegistrationByPrincipal( + extension.extension.principal, + extension.extension.principal.spec + ); + // Force the worker shutdown (in normal condition the worker would have been + // terminated as part of the entire application shutting down). + firstSwReg.forceShutdown(); + + info( + "Wait for the background service worker to be terminated while the app is shutting down" + ); + ok( + await watcher.promiseWorkerTerminated, + "The extension service worker has been terminated as expected" + ); + await managerShutdownCompleted; + + Assert.equal( + firstSwReg, + swm.getRegistrationByPrincipal( + extension.extension.principal, + extension.extension.principal.spec + ), + "Expect the service worker to not be unregistered on application shutdown" + ); + + info("Restart AddonManager (mocking Browser instance restart)"); + // Start the addon manager with `earlyStartup: false` to keep the background service worker + // from being started right away: + // + // - the call to `swm.reloadRegistrationForTest()` that follows is making sure that + // the previously registered service worker is in the same state it would be when + // the entire browser is restarted. + // + // - if the background service worker is being spawned again by the time we call + // `swm.reloadRegistrationForTest()`, ServiceWorkerUpdateJob would fail and trigger + // an `mState == State::Started` diagnostic assertion from ServiceWorkerJob::Finish + // and the xpcshell test will fail for the crash triggered by the assertion. + await AddonTestUtils.promiseStartupManager({ lateStartup: false }); + await extension.awaitStartup(); + + info( + "Force reload ServiceWorkerManager registrations (mocking a Browser instance restart)" + ); + swm.reloadRegistrationsForTest(); + + info( + "trigger delayed call to nsIServiceWorkerManager.registerForAddonPrincipal" + ); + // complete the startup notifications, then start the background + AddonTestUtils.notifyLateStartup(); + extension.extension.emit("start-background-script"); + + info("Force activate the extension worker"); + const newSwReg = swm.getRegistrationByPrincipal( + extension.extension.principal, + extension.extension.principal.spec + ); + + Assert.notEqual( + newSwReg, + firstSwReg, + "Expect the service worker registration to have been recreated" + ); + + await assertLifecycleEvents({ + extension, + expected: [], + message: "on previous registration loaded", + }); + + const { principal } = extension.extension; + const addon = await AddonManager.getAddonByID(extension.id); + await addon.disable(); + + ok( + await watcher.promiseWorkerTerminated, + "The extension service worker has been terminated as expected" + ); + + Assert.throws( + () => swm.getRegistrationByPrincipal(principal, principal.spec), + /NS_ERROR_FAILURE/, + "Expect the service worker to have been unregistered on addon disabled" + ); + + await addon.enable(); + await assertLifecycleEvents({ + extension, + expected: ["install", "activate"], + message: "on disabled addon re-enabled", + }); + + await testWorkerWatcher.destroy(); + await extension.unload(); +}); -- cgit v1.2.3