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 --- .../test_ext_scripting_persistAcrossSessions.js | 760 +++++++++++++++++++++ 1 file changed, 760 insertions(+) create mode 100644 toolkit/components/extensions/test/xpcshell/test_ext_scripting_persistAcrossSessions.js (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_scripting_persistAcrossSessions.js') diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_persistAcrossSessions.js b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_persistAcrossSessions.js new file mode 100644 index 0000000000..cae09b5d2e --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_persistAcrossSessions.js @@ -0,0 +1,760 @@ +"use strict"; + +Services.prefs.setBoolPref("extensions.manifestV3.enabled", true); + +AddonTestUtils.init(this); +AddonTestUtils.overrideCertDB(); +AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "1", + "42" +); + +const { ExtensionScriptingStore } = ChromeUtils.importESModule( + "resource://gre/modules/ExtensionScriptingStore.sys.mjs" +); +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +const makeExtension = ({ manifest: manifestProps, ...otherProps }) => { + return ExtensionTestUtils.loadExtension({ + manifest: { + manifest_version: 2, + permissions: ["scripting"], + ...manifestProps, + }, + useAddonManager: "permanent", + ...otherProps, + }); +}; + +const assertNumScriptsInStore = async (extension, expectedNum) => { + // `registerContentScripts`/`updateContentScripts()`/`unregisterContentScripts` + // call `ExtensionScriptingStore.persistAll()` without awaiting it, which + // isn't a problem in practice but this becomes a problem in this test given + // that we should make sure the startup cache is updated before checking it. + await TestUtils.waitForCondition(async () => { + let scripts = + await ExtensionScriptingStore._getStoreForTesting().getByExtensionId( + extension.id + ); + return scripts.length === expectedNum; + }, "wait until the store is updated with the expected number of scripts"); + + let scripts = + await ExtensionScriptingStore._getStoreForTesting().getByExtensionId( + extension.id + ); + Assert.equal( + scripts.length, + expectedNum, + `expected ${expectedNum} script in store` + ); +}; + +const verifyRegisterContentScripts = async manifestVersion => { + await AddonTestUtils.promiseStartupManager(); + + let extension = makeExtension({ + manifest: { + manifest_version: manifestVersion, + }, + async background() { + let scripts = await browser.scripting.getRegisteredContentScripts(); + + // Only register the content script if it wasn't registered before. Since + // there is only one script, we don't check its ID. + if (!scripts.length) { + const script = { + id: "script", + js: ["script.js"], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: true, + }; + + await browser.scripting.registerContentScripts([script]); + browser.test.sendMessage("script-registered"); + return; + } + + browser.test.assertEq(1, scripts.length, "expected 1 registered script"); + browser.test.sendMessage("script-already-registered"); + }, + files: { + "script.js": "", + }, + }); + + await extension.startup(); + await extension.awaitMessage("script-registered"); + await assertNumScriptsInStore(extension, 1); + + await AddonTestUtils.promiseRestartManager(); + await assertNumScriptsInStore(extension, 1); + + await extension.awaitStartup(); + await extension.awaitMessage("script-already-registered"); + + await extension.unload(); + await AddonTestUtils.promiseShutdownManager(); + await assertNumScriptsInStore(extension, 0); +}; + +add_task(async function test_registerContentScripts_mv2() { + await verifyRegisterContentScripts(2); +}); + +add_task(async function test_registerContentScripts_mv3() { + await verifyRegisterContentScripts(3); +}); + +const verifyUpdateContentScripts = async manifestVersion => { + await AddonTestUtils.promiseStartupManager(); + + let extension = makeExtension({ + manifest: { + manifest_version: manifestVersion, + }, + async background() { + let scripts = await browser.scripting.getRegisteredContentScripts(); + + // Only register the content script if it wasn't registered before. Since + // there is only one script, we don't check its ID. + if (!scripts.length) { + const script = { + id: "script", + js: ["script.js"], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: true, + }; + + await browser.scripting.registerContentScripts([script]); + browser.test.sendMessage("script-registered"); + return; + } + + browser.test.assertEq(1, scripts.length, "expected 1 registered script"); + await browser.scripting.updateContentScripts([ + { id: scripts[0].id, persistAcrossSessions: false }, + ]); + browser.test.sendMessage("script-updated"); + }, + files: { + "script.js": "", + }, + }); + + await extension.startup(); + await extension.awaitMessage("script-registered"); + await assertNumScriptsInStore(extension, 1); + + // Simulate a new session. + await AddonTestUtils.promiseRestartManager(); + await assertNumScriptsInStore(extension, 1); + + await extension.awaitStartup(); + await extension.awaitMessage("script-updated"); + await assertNumScriptsInStore(extension, 0); + + // Simulate another new session. + await AddonTestUtils.promiseRestartManager(); + + await extension.awaitStartup(); + await extension.awaitMessage("script-registered"); + await assertNumScriptsInStore(extension, 1); + + await extension.unload(); + await AddonTestUtils.promiseShutdownManager(); + await assertNumScriptsInStore(extension, 0); +}; + +add_task(async function test_updateContentScripts() { + await verifyUpdateContentScripts(2); +}); + +add_task(async function test_updateContentScripts_mv3() { + await verifyUpdateContentScripts(3); +}); + +const verifyUnregisterContentScripts = async manifestVersion => { + await AddonTestUtils.promiseStartupManager(); + + let extension = makeExtension({ + manifest: { + manifest_version: manifestVersion, + }, + async background() { + let scripts = await browser.scripting.getRegisteredContentScripts(); + + // Only register the content script if it wasn't registered before. Since + // there is only one script, we don't check its ID. + if (!scripts.length) { + const script = { + id: "script", + js: ["script.js"], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: true, + }; + + await browser.scripting.registerContentScripts([script]); + browser.test.sendMessage("script-registered"); + return; + } + + browser.test.assertEq(1, scripts.length, "expected 1 registered script"); + await browser.scripting.unregisterContentScripts(); + browser.test.sendMessage("script-unregistered"); + }, + files: { + "script.js": "", + }, + }); + + await extension.startup(); + await extension.awaitMessage("script-registered"); + await assertNumScriptsInStore(extension, 1); + + // Simulate a new session. + await AddonTestUtils.promiseRestartManager(); + + // Script should be still persisted... + await assertNumScriptsInStore(extension, 1); + await extension.awaitStartup(); + // ...and we should now enter the second branch of the background script. + await extension.awaitMessage("script-unregistered"); + await assertNumScriptsInStore(extension, 0); + + // Simulate another new session. + await AddonTestUtils.promiseRestartManager(); + + await extension.awaitStartup(); + await extension.awaitMessage("script-registered"); + await assertNumScriptsInStore(extension, 1); + + await extension.unload(); + await AddonTestUtils.promiseShutdownManager(); + await assertNumScriptsInStore(extension, 0); +}; + +add_task(async function test_unregisterContentScripts() { + await verifyUnregisterContentScripts(2); +}); + +add_task(async function test_unregisterContentScripts_mv3() { + await verifyUnregisterContentScripts(3); +}); + +add_task(async function test_reload_extension() { + await AddonTestUtils.promiseStartupManager(); + + let extension = makeExtension({ + async background() { + browser.test.onMessage.addListener(msg => { + browser.test.assertEq("reload-extension", msg, `expected msg: ${msg}`); + browser.runtime.reload(); + }); + + let scripts = await browser.scripting.getRegisteredContentScripts(); + + // Only register the content script if it wasn't registered before. Since + // there is only one script, we don't check its ID. + if (!scripts.length) { + const script = { + id: "script", + js: ["script.js"], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: true, + }; + + await browser.scripting.registerContentScripts([script]); + browser.test.sendMessage("script-registered"); + return; + } + + browser.test.assertEq(1, scripts.length, "expected 1 registered script"); + browser.test.sendMessage("script-already-registered"); + }, + files: { + "script.js": "", + }, + }); + + await extension.startup(); + await extension.awaitMessage("script-registered"); + await assertNumScriptsInStore(extension, 1); + + extension.sendMessage("reload-extension"); + // Wait for extension to restart, to make sure reloads works. + await AddonTestUtils.promiseWebExtensionStartup(extension.id); + await extension.awaitMessage("script-already-registered"); + await assertNumScriptsInStore(extension, 1); + + await extension.unload(); + await AddonTestUtils.promiseShutdownManager(); + await assertNumScriptsInStore(extension, 0); +}); + +add_task(async function test_disable_and_reenable_extension() { + await AddonTestUtils.promiseStartupManager(); + + let extension = makeExtension({ + async background() { + let scripts = await browser.scripting.getRegisteredContentScripts(); + + // Only register the content script if it wasn't registered before. Since + // there is only one script, we don't check its ID. + if (!scripts.length) { + const script = { + id: "script", + js: ["script.js"], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: true, + }; + + await browser.scripting.registerContentScripts([script]); + browser.test.sendMessage("script-registered"); + return; + } + + browser.test.assertEq(1, scripts.length, "expected 1 registered script"); + browser.test.sendMessage("script-already-registered"); + }, + files: { + "script.js": "", + }, + }); + + await extension.startup(); + await extension.awaitMessage("script-registered"); + await assertNumScriptsInStore(extension, 1); + + // Disable... + await extension.addon.disable(); + // then re-enable the extension. + await extension.addon.enable(); + + await extension.awaitMessage("script-already-registered"); + await assertNumScriptsInStore(extension, 1); + + await extension.unload(); + await AddonTestUtils.promiseShutdownManager(); + await assertNumScriptsInStore(extension, 0); +}); + +add_task(async function test_updateContentScripts_persistAcrossSessions_true() { + await AddonTestUtils.promiseStartupManager(); + + let extension = makeExtension({ + async background() { + const script = { + id: "script", + js: ["script-1.js"], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: false, + }; + + const scripts = await browser.scripting.getRegisteredContentScripts(); + + browser.test.onMessage.addListener(async msg => { + switch (msg) { + case "persist-script": + await browser.scripting.updateContentScripts([ + { id: script.id, persistAcrossSessions: true }, + ]); + browser.test.sendMessage(`${msg}-done`); + break; + + case "add-new-js": + await browser.scripting.updateContentScripts([ + { id: script.id, js: ["script-1.js", "script-2.js"] }, + ]); + browser.test.sendMessage(`${msg}-done`); + break; + + case "verify-script": + // We expect a single registered script, which is the one declared + // above but at this point we should have 2 JS files and the + // `persistAcrossSessions` option set to `true`. + browser.test.assertEq( + JSON.stringify([ + { + id: script.id, + allFrames: false, + matches: script.matches, + runAt: "document_idle", + persistAcrossSessions: true, + js: ["script-1.js", "script-2.js"], + }, + ]), + JSON.stringify(scripts), + "expected scripts" + ); + browser.test.sendMessage(`${msg}-done`); + break; + + default: + browser.test.fail(`unexpected message: ${msg}`); + } + }); + + // Only register the content script if it wasn't registered before. Since + // there is only one script, we don't check its ID. + if (!scripts.length) { + await browser.scripting.registerContentScripts([script]); + browser.test.sendMessage("script-registered"); + } else { + browser.test.sendMessage("script-already-registered"); + } + }, + files: { + "script-1.js": "", + "script-2.js": "", + }, + }); + + await extension.startup(); + await extension.awaitMessage("script-registered"); + await assertNumScriptsInStore(extension, 0); + + // Simulate a new session. + await AddonTestUtils.promiseRestartManager(); + await assertNumScriptsInStore(extension, 0); + + // We expect the script to be registered again because it isn't persisted. + await extension.awaitStartup(); + await extension.awaitMessage("script-registered"); + await assertNumScriptsInStore(extension, 0); + + // We now tell the background script to update the script to persist it + // across sessions. + extension.sendMessage("persist-script"); + await extension.awaitMessage("persist-script-done"); + + // Simulate another new session. We expect the content script to be already + // registered since it was persisted in the previous (simulated) session. + await AddonTestUtils.promiseRestartManager(); + await assertNumScriptsInStore(extension, 1); + + await extension.awaitStartup(); + await extension.awaitMessage("script-already-registered"); + await assertNumScriptsInStore(extension, 1); + + // We tell the background script to update the content script with a new JS + // file and we don't change the `persistAcrossSessions` option. + extension.sendMessage("add-new-js"); + await extension.awaitMessage("add-new-js-done"); + + // Simulate another new session. We expect the content script to have 2 JS + // files and to be registered since it was persisted in the previous + // (simulated) session and we didn't update the option. + await AddonTestUtils.promiseRestartManager(); + await assertNumScriptsInStore(extension, 1); + + await extension.awaitStartup(); + await extension.awaitMessage("script-already-registered"); + await assertNumScriptsInStore(extension, 1); + + // Let's verify that the script fetched by the background script is the one + // we expect at this point: it should have two JS files. + extension.sendMessage("verify-script"); + await extension.awaitMessage("verify-script-done"); + + await extension.unload(); + await AddonTestUtils.promiseShutdownManager(); + await assertNumScriptsInStore(extension, 0); +}); + +add_task(async function test_multiple_extensions_and_scripts() { + await AddonTestUtils.promiseStartupManager(); + + let extension1 = makeExtension({ + async background() { + let scripts = await browser.scripting.getRegisteredContentScripts(); + + if (!scripts.length) { + await browser.scripting.registerContentScripts([ + { + id: "0", + js: ["script-1.js"], + matches: ["http://*/*/file_sample.html"], + // We should persist this script by default. + }, + { + id: "/", + js: ["script-2.js"], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: true, + }, + { + id: "3", + js: ["script-3.js"], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: false, + }, + ]); + browser.test.sendMessage("scripts-registered"); + return; + } + + browser.test.assertEq(2, scripts.length, "expected 2 registered scripts"); + browser.test.sendMessage("scripts-already-registered"); + }, + files: { + "script-1.js": "", + "script-2.js": "", + "script-3.js": "", + }, + }); + + let extension2 = makeExtension({ + async background() { + let scripts = await browser.scripting.getRegisteredContentScripts(); + + if (!scripts.length) { + await browser.scripting.registerContentScripts([ + { + id: "1", + js: ["script-1.js"], + matches: ["http://*/*/file_sample.html"], + // We should persist this script by default. + }, + { + id: "2", + js: ["script-2.js"], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: false, + }, + { + id: "\uFFFD 🍕 Boö", + js: ["script-3.js"], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: true, + }, + ]); + browser.test.sendMessage("scripts-registered"); + return; + } + + browser.test.assertEq(2, scripts.length, "expected 2 registered scripts"); + browser.test.assertEq( + JSON.stringify(["script-1.js"]), + JSON.stringify(scripts[0].js), + "expected a single 'script-1.js' js file" + ); + browser.test.assertEq( + "\uFFFD 🍕 Boö", + scripts[1].id, + "expected correct ID" + ); + browser.test.sendMessage("scripts-already-registered"); + }, + files: { + "script-1.js": "", + "script-2.js": "", + "script-3.js": "", + }, + }); + + await Promise.all([extension1.startup(), extension2.startup()]); + + await Promise.all([ + extension1.awaitMessage("scripts-registered"), + extension2.awaitMessage("scripts-registered"), + ]); + await assertNumScriptsInStore(extension1, 2); + await assertNumScriptsInStore(extension2, 2); + + await AddonTestUtils.promiseRestartManager(); + await assertNumScriptsInStore(extension1, 2); + await assertNumScriptsInStore(extension2, 2); + + await Promise.all([extension1.awaitStartup(), extension2.awaitStartup()]); + await Promise.all([ + extension1.awaitMessage("scripts-already-registered"), + extension2.awaitMessage("scripts-already-registered"), + ]); + + await Promise.all([extension1.unload(), extension2.unload()]); + await AddonTestUtils.promiseShutdownManager(); + await assertNumScriptsInStore(extension1, 0); + await assertNumScriptsInStore(extension2, 0); +}); + +add_task(async function test_persisted_scripts_cleared_on_addon_updates() { + await AddonTestUtils.promiseStartupManager(); + + function background() { + browser.test.onMessage.addListener(async (msg, ...args) => { + switch (msg) { + case "registerContentScripts": + await browser.scripting.registerContentScripts(...args); + break; + case "unregisterContentScripts": + await browser.scripting.unregisterContentScripts(...args); + break; + default: + browser.test.fail(`Unexpected test message: ${msg}`); + } + browser.test.sendMessage(`${msg}:done`); + }); + } + + async function registerContentScript(ext, scriptFileName) { + ext.sendMessage("registerContentScripts", [ + { + id: scriptFileName, + js: [scriptFileName], + matches: ["http://*/*/file_sample.html"], + persistAcrossSessions: true, + }, + ]); + await ext.awaitMessage("registerContentScripts:done"); + } + + let extension1Data = { + manifest: { + manifest_version: 2, + permissions: ["scripting"], + version: "1.0", + browser_specific_settings: { + // Set an explicit extension id so that extension.upgrade + // will trigger the extension to be started with the expected + // "ADDON_UPGRADE" / "ADDON_DOWNGRADE" extension.startupReason. + gecko: { id: "extension1@mochi.test" }, + }, + }, + useAddonManager: "permanent", + background, + files: { + "script-1.js": "", + }, + }; + + let extension1 = ExtensionTestUtils.loadExtension(extension1Data); + + let extension2 = ExtensionTestUtils.loadExtension({ + manifest: { + manifest_version: 2, + permissions: ["scripting"], + browser_specific_settings: { + gecko: { id: "extension2@mochi.test" }, + }, + }, + useAddonManager: "permanent", + background, + files: { + "script-2.js": "", + }, + }); + + await extension1.startup(); + await assertNumScriptsInStore(extension1, 0); + await assertIsPersistentScriptsCachedFlag(extension1, false); + + await extension2.startup(); + await assertNumScriptsInStore(extension2, 0); + await assertIsPersistentScriptsCachedFlag(extension2, false); + + await registerContentScript(extension1, "script-1.js"); + await assertNumScriptsInStore(extension1, 1); + await assertIsPersistentScriptsCachedFlag(extension1, true); + + await registerContentScript(extension2, "script-2.js"); + await assertNumScriptsInStore(extension2, 1); + await assertIsPersistentScriptsCachedFlag(extension2, true); + + info("Verify that scripts are still registered on a browser startup"); + await AddonTestUtils.promiseRestartManager(); + await extension1.awaitStartup(); + await extension2.awaitStartup(); + equal( + extension1.extension.startupReason, + "APP_STARTUP", + "Got the expected startupReason on AOM restart" + ); + + await assertNumScriptsInStore(extension1, 1); + await assertIsPersistentScriptsCachedFlag(extension1, true); + await assertNumScriptsInStore(extension2, 1); + await assertIsPersistentScriptsCachedFlag(extension2, true); + + async function testOnAddonUpdates( + extensionUpdateData, + expectedStartupReason + ) { + await extension1.upgrade(extensionUpdateData); + equal( + extension1.extension.startupReason, + expectedStartupReason, + "Got the expected startupReason on upgrade" + ); + + await assertNumScriptsInStore(extension1, 0); + await assertIsPersistentScriptsCachedFlag(extension1, false); + await assertNumScriptsInStore(extension2, 1); + await assertIsPersistentScriptsCachedFlag(extension2, true); + } + + info("Verify that scripts are cleared on upgrade"); + await testOnAddonUpdates( + { + ...extension1Data, + manifest: { + ...extension1Data.manifest, + version: "2.0", + }, + }, + "ADDON_UPGRADE" + ); + + await registerContentScript(extension1, "script-1.js"); + await assertNumScriptsInStore(extension1, 1); + + info("Verify that scripts are cleared on downgrade"); + await testOnAddonUpdates(extension1Data, "ADDON_DOWNGRADE"); + + await registerContentScript(extension1, "script-1.js"); + await assertNumScriptsInStore(extension1, 1); + + info("Verify that scripts are cleared on upgrade to same version"); + await testOnAddonUpdates(extension1Data, "ADDON_UPGRADE"); + + await extension1.unload(); + await extension2.unload(); + + await assertNumScriptsInStore(extension1, 0); + await assertIsPersistentScriptsCachedFlag(extension1, undefined); + await assertNumScriptsInStore(extension2, 0); + await assertIsPersistentScriptsCachedFlag(extension2, undefined); + + info("Verify stale persisted scripts cleared on re-install"); + // Inject a stale persisted script into the store. + await ExtensionScriptingStore._getStoreForTesting().writeMany(extension1.id, [ + { + id: "script-1.js", + allFrames: false, + matches: ["http://*/*/file_sample.html"], + runAt: "document_idle", + persistAcrossSessions: true, + js: ["script-1.js"], + }, + ]); + await assertNumScriptsInStore(extension1, 1); + const extension1Reinstalled = + ExtensionTestUtils.loadExtension(extension1Data); + await extension1Reinstalled.startup(); + equal( + extension1Reinstalled.extension.startupReason, + "ADDON_INSTALL", + "Got the expected startupReason on re-install" + ); + await assertNumScriptsInStore(extension1Reinstalled, 0); + await assertIsPersistentScriptsCachedFlag(extension1Reinstalled, false); + await extension1Reinstalled.unload(); + await assertNumScriptsInStore(extension1Reinstalled, 0); + await assertIsPersistentScriptsCachedFlag(extension1Reinstalled, undefined); + + await AddonTestUtils.promiseShutdownManager(); +}); -- cgit v1.2.3