diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
commit | 59203c63bb777a3bacec32fb8830fba33540e809 (patch) | |
tree | 58298e711c0ff0575818c30485b44a2f21bf28a0 /toolkit/components/extensions/test | |
parent | Adding upstream version 126.0.1. (diff) | |
download | firefox-59203c63bb777a3bacec32fb8830fba33540e809.tar.xz firefox-59203c63bb777a3bacec32fb8830fba33540e809.zip |
Adding upstream version 127.0.upstream/127.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/extensions/test')
27 files changed, 1003 insertions, 166 deletions
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_getCurrent_differentExt.js b/toolkit/components/extensions/test/browser/browser_ext_themes_getCurrent_differentExt.js index 587c5d4efe..86d794669d 100644 --- a/toolkit/components/extensions/test/browser/browser_ext_themes_getCurrent_differentExt.js +++ b/toolkit/components/extensions/test/browser/browser_ext_themes_getCurrent_differentExt.js @@ -139,7 +139,9 @@ add_task(async function test_getcurrent_privateBrowsing() { "resource://gre/modules/ExtensionCommon.sys.mjs" ); const { makeWidgetId } = ExtensionCommon; - privateWin.SidebarUI.show(`${makeWidgetId(extension.id)}-sidebar-action`); + privateWin.SidebarController.show( + `${makeWidgetId(extension.id)}-sidebar-action` + ); let imageLoaded = extension.awaitMessage("theme-image"); Assert.deepEqual(await imageLoaded, { success: true }, "theme image loaded"); diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_sidebars.js b/toolkit/components/extensions/test/browser/browser_ext_themes_sidebars.js index 0d2e69716d..492fe3a263 100644 --- a/toolkit/components/extensions/test/browser/browser_ext_themes_sidebars.js +++ b/toolkit/components/extensions/test/browser/browser_ext_themes_sidebars.js @@ -17,7 +17,7 @@ async function test_sidebar_theme(theme, isBrightText) { const sidebarBox = document.getElementById("sidebar-box"); const browserRoot = document.documentElement; - const content = SidebarUI.browser.contentWindow; + const content = SidebarController.browser.contentWindow; const root = content.document.documentElement; ok( @@ -184,7 +184,7 @@ add_task(async function test_support_sidebar_colors() { for (let command of ["viewBookmarksSidebar", "viewHistorySidebar"]) { info("Executing command: " + command); - await SidebarUI.show(command); + await SidebarController.show(command); await test_sidebar_theme( { @@ -263,7 +263,7 @@ add_task(async function test_support_sidebar_border_color() { "Sidebar splitter should be colored properly" ); - SidebarUI.reversePosition(); + SidebarController.reversePosition(); is( sidebarSplitterCS.borderInlineStartColor, @@ -271,7 +271,7 @@ add_task(async function test_support_sidebar_border_color() { "Sidebar splitter should be colored properly after switching sides" ); - SidebarUI.reversePosition(); + SidebarController.reversePosition(); } await extension.unload(); diff --git a/toolkit/components/extensions/test/marionette/manifest-serviceworker.toml b/toolkit/components/extensions/test/marionette/manifest-serviceworker.toml index c8035f80c2..04bdc4e17e 100644 --- a/toolkit/components/extensions/test/marionette/manifest-serviceworker.toml +++ b/toolkit/components/extensions/test/marionette/manifest-serviceworker.toml @@ -1,4 +1,5 @@ [DEFAULT] +skip-if = ["!nightly_build"] # MOZ_WEBEXT_WEBIDL_ENABLED is only defined on nightly ["test_extension_serviceworkers_purged_on_pref_disabled.py"] ["test_temporary_extension_serviceworkers_not_persisted.py"] diff --git a/toolkit/components/extensions/test/mochitest/mochitest-common.toml b/toolkit/components/extensions/test/mochitest/mochitest-common.toml index 782069a79c..f49fb131c5 100644 --- a/toolkit/components/extensions/test/mochitest/mochitest-common.toml +++ b/toolkit/components/extensions/test/mochitest/mochitest-common.toml @@ -268,6 +268,8 @@ skip-if = [ "http2", ] +["test_ext_extension_getViews.html"] + ["test_ext_extension_iframe_messaging.html"] skip-if = [ "http3", @@ -350,6 +352,8 @@ skip-if = [ "http2", ] +["test_ext_runtime_getContexts.html"] + ["test_ext_script_filenames.html"] ["test_ext_scripting_contentScripts.html"] @@ -443,12 +447,6 @@ skip-if = [ ] ["test_ext_subframes_privileges.html"] -skip-if = [ - "os == 'android'", # Bug 1845918 - "verify", # Bug 1489771 - "http3", - "http2", -] ["test_ext_tabs_captureTab.html"] skip-if = [ diff --git a/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js b/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js index 95ac9af50d..8dec0c6ae5 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js +++ b/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js @@ -89,8 +89,10 @@ let expectedBackgroundApis = [ "permissions.remove", "permissions.onAdded", "permissions.onRemoved", + "runtime.ContextType", "runtime.getBackgroundPage", "runtime.getBrowserInfo", + "runtime.getContexts", "runtime.getPlatformInfo", "runtime.onConnectExternal", "runtime.onInstalled", diff --git a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html index 076c177dfa..28bbe3b253 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html @@ -14,7 +14,10 @@ add_task(async function setup() { await SpecialPowers.pushPrefEnv({ - set: [["extensions.manifestV3.enabled", true]], + set: [ + ["extensions.manifestV3.enabled", true], + ["extensions.originControls.grantByDefault", false], + ], }); }); diff --git a/toolkit/components/extensions/test/mochitest/test_ext_extension_getViews.html b/toolkit/components/extensions/test/mochitest/test_ext_extension_getViews.html new file mode 100644 index 0000000000..9309d45cdf --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/test_ext_extension_getViews.html @@ -0,0 +1,108 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>extension.getViews Test</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script> + <script type="text/javascript" src="head.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<script type="text/javascript"> +"use strict"; + +function genericChecker() { + const params = new URLSearchParams(window.location.search); + const kind = params.get("kind"); + const createdTabIds = []; + + browser.test.onMessage.addListener(async msg => { + if (msg == `${kind}-test-get-views`) { + try { + let result = browser.extension.getViews({}); + browser.test.assertEq(3, result.length, "Expect 3 extension views to be found"); + + result = browser.extension.getViews({ type: "tab" }); + browser.test.assertEq(1, result.length, "Expect 1 tab extension view to be found"); + + result = browser.extension.getViews({ type: "popup" }); + browser.test.assertEq(1, result.length, "Expect 1 popup extension view to be found"); + + browser.test.sendMessage(`${msg}:done`); + } catch (err) { + browser.test.fail(`browser.extension.getViews exception: ${err}`); + browser.test.sendMessage(`${msg}:done`); + } + } else if (msg == `${kind}-open-tab`) { + const tab = await browser.tabs.create({ url: "/page.html?kind=tab" }); + createdTabIds.push(tab.id); + } else if (msg == `${kind}-close-tabs`) { + await browser.tabs.remove(createdTabIds); + browser.test.sendMessage(`${msg}:done`); + } + }); + + browser.test.log(`${kind} extension page loaded`); + browser.test.sendMessage(`${kind}-loaded`); +} + +add_task(async function test_runtime_getContexts() { + const EXT_ID = "runtime-getContexts@mochitest"; + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", // To automatically show sidebar on load. + incognitoOverride: "spanning", + manifest: { + manifest_version: 3, + browser_specific_settings: { gecko: { id: EXT_ID } }, + + action: { + default_popup: "page.html?kind=action", + default_area: "navbar", + }, + + background: { + page: "page.html?kind=background", + }, + }, + + files: { + "page.html": ` + <!DOCTYPE html> + <html> + <head><meta charset="utf-8"><\/head> + <body> + <script src="page.js"><\/script> + <\/body> + <\/html> + `, + + "page.js": genericChecker, + }, + }); + + await extension.startup(); + await extension.awaitMessage("background-loaded"); + + extension.sendMessage("background-open-tab"); + await extension.awaitMessage("tab-loaded"); + + await AppTestDelegate.clickBrowserAction(window, extension); + await extension.awaitMessage("action-loaded"); + + extension.sendMessage("background-test-get-views"); + await extension.awaitMessage("background-test-get-views:done"); + + // Close popup window. + await AppTestDelegate.closeBrowserAction(window, extension); + + // Close extension page tabs. + extension.sendMessage("background-close-tabs"); + await extension.awaitMessage("background-close-tabs:done"); + + await extension.unload(); +}); + +</script> +</body> +</html> diff --git a/toolkit/components/extensions/test/mochitest/test_ext_runtime_getContexts.html b/toolkit/components/extensions/test/mochitest/test_ext_runtime_getContexts.html new file mode 100644 index 0000000000..fa3b7385da --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_getContexts.html @@ -0,0 +1,126 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>runtime.getContexts Test</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script> + <script type="text/javascript" src="head.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<script type="text/javascript"> +"use strict"; + +function genericChecker() { + const params = new URLSearchParams(window.location.search); + const kind = params.get("kind"); + const createdTabIds = []; + + browser.test.onMessage.addListener(async msg => { + if (msg == `${kind}-test-get-contexts`) { + try { + const result = await browser.runtime.getContexts({}); + browser.test.assertEq(3, result.length, "Expect 3 extension contexts to be found"); + + const bgContext = result.find(it => it.contextType === browser.runtime.ContextType.BACKGROUND); + const tabContext = result.find(it => it.documentUrl.endsWith("kind=tab")); + const popupContext = result.find(it => it.documentUrl.endsWith("kind=action")); + + browser.test.assertEq(window.location.href, bgContext.documentUrl, "background context has the expected documentUrl"); + browser.test.assertEq(-1, bgContext.windowId, "background context has the expected windowId"); + browser.test.assertEq(-1, bgContext.tabId, "background context has the expected tabId"); + browser.test.assertEq("TAB", tabContext.contextType, "Got expected tab context type"); + browser.test.assertTrue( + typeof tabContext.windowId === "number" && tabContext.windowId > 0, + "Got expected windowId on tab context" + ); + browser.test.assertTrue( + typeof tabContext.windowId === "number" && tabContext.tabId > 0, + "Got expected tabId on tab context" + ); + browser.test.assertEq( + tabContext.windowId, + popupContext.windowId, + "Poup and tab expected to have the same windowId" + ); + browser.test.assertEq(-1, popupContext.tabId, "popup context has the expected tabId"); + browser.test.assertEq("POPUP", popupContext.contextType, "Got expected popup context type"); + + browser.test.sendMessage(`${msg}:done`); + } catch (err) { + browser.test.fail(`broser.runtime.getContexts call rejected: ${err}`); + browser.test.sendMessage(`${msg}:done`); + } + } else if (msg == `${kind}-open-tab`) { + const tab = await browser.tabs.create({ url: "/page.html?kind=tab" }); + createdTabIds.push(tab.id); + } else if (msg == `${kind}-close-tabs`) { + await browser.tabs.remove(createdTabIds); + browser.test.sendMessage(`${msg}:done`); + } + }); + + browser.test.log(`${kind} extension page loaded`); + browser.test.sendMessage(`${kind}-loaded`); +} + +add_task(async function test_runtime_getContexts() { + const EXT_ID = "runtime-getContexts@mochitest"; + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "temporary", // To automatically show sidebar on load. + incognitoOverride: "spanning", + manifest: { + manifest_version: 3, + browser_specific_settings: { gecko: { id: EXT_ID } }, + + action: { + default_popup: "page.html?kind=action", + default_area: "navbar", + }, + + background: { + page: "page.html?kind=background", + }, + }, + + files: { + "page.html": ` + <!DOCTYPE html> + <html> + <head><meta charset="utf-8"><\/head> + <body> + <script src="page.js"><\/script> + <\/body> + <\/html> + `, + + "page.js": genericChecker, + }, + }); + + await extension.startup(); + await extension.awaitMessage("background-loaded"); + + extension.sendMessage("background-open-tab"); + await extension.awaitMessage("tab-loaded"); + + await AppTestDelegate.clickBrowserAction(window, extension); + await extension.awaitMessage("action-loaded"); + + extension.sendMessage("background-test-get-contexts"); + await extension.awaitMessage("background-test-get-contexts:done"); + + // Close popup window. + await AppTestDelegate.closeBrowserAction(window, extension); + + // Close extension page tabs. + extension.sendMessage("background-close-tabs"); + await extension.awaitMessage("background-close-tabs:done"); + + await extension.unload(); +}); + +</script> +</body> +</html> diff --git a/toolkit/components/extensions/test/mochitest/test_ext_scripting_executeScript_activeTab.html b/toolkit/components/extensions/test/mochitest/test_ext_scripting_executeScript_activeTab.html index 5eb2193409..a0ceed72d5 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_scripting_executeScript_activeTab.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_scripting_executeScript_activeTab.html @@ -27,7 +27,10 @@ const makeExtension = ({ manifest: manifestProps, ...otherProps }) => { add_task(async function setup() { await SpecialPowers.pushPrefEnv({ - set: [["extensions.manifestV3.enabled", true]], + set: [ + ["extensions.manifestV3.enabled", true], + ["extensions.originControls.grantByDefault", false], + ], }); }); diff --git a/toolkit/components/extensions/test/mochitest/test_ext_subframes_privileges.html b/toolkit/components/extensions/test/mochitest/test_ext_subframes_privileges.html index f791d08602..0586275808 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_subframes_privileges.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_subframes_privileges.html @@ -14,6 +14,32 @@ "use strict"; /* eslint-disable mozilla/balanced-listeners */ +const { + WebExtensionPolicy, +} = SpecialPowers.Cu.getGlobalForObject(SpecialPowers.Services); + + +// Some tests load non-moz-extension:-URLs in their extension document. When +// extensions run in-process (extensions.webextensions.remote set to false), +// that fails. +// For details, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1724099 and +// the same function in toolkit/components/extensions/test/xpcshell/head.js +async function allow_unsafe_parent_loads_when_extensions_not_remote() { + if (!WebExtensionPolicy.useRemoteWebExtensions) { + await SpecialPowers.pushPrefEnv({ + set: [["security.allow_unsafe_parent_loads", true]], + }); + } +} + +async function revert_allow_unsafe_parent_loads_when_extensions_not_remote() { + if (!WebExtensionPolicy.useRemoteWebExtensions) { + // Assume that the previous call to pushPrefEnv was from + // allow_unsafe_parent_loads_when_extensions_not_remote. + await SpecialPowers.popPrefEnv(); + } +} + add_task(async function test_webext_tab_subframe_privileges() { function background() { browser.runtime.onMessage.addListener(async ({msg, success, tabId, error}) => { @@ -198,19 +224,25 @@ add_task(async function test_webext_contentscript_iframe_subframe_privileges() { }); add_task(async function test_webext_background_remote_subframe_privileges() { - function backgroundSubframeScript() { + // file_remote_frame.html is opened at the same origin as this test page. + document.cookie = "cookie=monster"; + + function backgroundScript() { window.addEventListener("message", evt => { - browser.test.assertEq("http://mochi.test:8888", evt.origin, "postmessage origin ok"); + browser.test.assertTrue( + evt.origin === "http://mochi.test:8888" || + evt.origin === "https://mochi.test:8888", // using https-first, http2/http3 server. + `postmessage origin ok: ${evt.origin}` + ); browser.test.assertFalse(evt.data.tabs, "remote frame cannot access webextension APIs"); browser.test.assertEq("cookie=monster", evt.data.cookie, "Expected cookie value"); browser.test.notifyPass("webext-background-subframe-privileges"); }, {once: true}); - browser.cookies.set({url: "http://mochi.test:8888", name: "cookie", "value": "monster"}); } let extensionData = { manifest: { - permissions: ["cookies", "*://mochi.test/*", "tabs"], + permissions: ["*://mochi.test/*", "tabs"], background: { page: "background.html", }, @@ -219,32 +251,32 @@ add_task(async function test_webext_background_remote_subframe_privileges() { "background.html": `<!DOCTYPE> <head> <meta charset="utf-8"> - <script src="background-subframe.js"><\/script> + <script src="background.js"><\/script> </head> <body> <iframe src='${SimpleTest.getTestFileURL("file_remote_frame.html")}'></iframe> </body> </html>`, - "background-subframe.js": backgroundSubframeScript, + "background.js": backgroundScript, }, }; // Need remote webextensions to be able to load remote content from a background page. - if (!SpecialPowers.getBoolPref("extensions.webextensions.remote", true)) { - return; - } + await allow_unsafe_parent_loads_when_extensions_not_remote(); let extension = ExtensionTestUtils.loadExtension(extensionData); await extension.startup(); await extension.awaitFinish("webext-background-subframe-privileges"); await extension.unload(); + await revert_allow_unsafe_parent_loads_when_extensions_not_remote(); }); // Test a moz-extension:// iframe inside a content iframe in an extension page. add_task(async function test_sub_subframe_conduit_verified_env() { let manifest = { content_scripts: [{ - matches: ["http://mochi.test/*/file_sample.html"], + // Note: no :8888 because of bug 1468162. + matches: ["*://mochi.test/*/file_sample.html"], all_frames: true, js: ["cs.js"], }], @@ -311,19 +343,20 @@ add_task(async function test_sub_subframe_conduit_verified_env() { is(err, "Unknown sender or wrong actor for recvCreateProxyContext"); } - let remote = SpecialPowers.getBoolPref("extensions.webextensions.remote"); - let badProcess = { message: /Bad {[\w-]+} process: web/ }; let badPrincipal = { message: /Bad {[\w-]+} principal: http/ }; - consoleMonitor.start(remote ? [badPrincipal, badProcess] : [badProcess]); + consoleMonitor.start([badPrincipal, badProcess]); let extension = ExtensionTestUtils.loadExtension({ manifest, files }); + + + // Need OOP to spoof from a web iframe inside background page. + await allow_unsafe_parent_loads_when_extensions_not_remote(); await extension.startup(); - if (remote) { - info("Need OOP to spoof from a web iframe inside background page."); - await expectErrors(extension); - } + info("Spoof from a web iframe inside background page."); + await expectErrors(extension); + await revert_allow_unsafe_parent_loads_when_extensions_not_remote(); info("Try spoofing from the web process."); let win = window.open("./file_sample.html"); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_errors.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_errors.js index 6fae3b838a..c4f504c219 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_errors.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_errors.js @@ -18,6 +18,11 @@ add_task(async function test_cached_contentscript_on_document_start() { // Use distinct content scripts as some will throw and would prevent executing the next script { matches: ["http://*/*/file_content_script_errors.html"], + js: ["script_does_not_exist.js"], + run_at: "document_start", + }, + { + matches: ["http://*/*/file_content_script_errors.html"], js: ["script1.js"], run_at: "document_start", }, @@ -41,6 +46,16 @@ add_task(async function test_cached_contentscript_on_document_start() { js: ["script5.js"], run_at: "document_start", }, + { + matches: ["http://*/*/file_content_script_errors.html"], + js: ["script6.js"], + run_at: "document_start", + }, + { + matches: ["http://*/*/file_content_script_errors.html"], + js: ["script7.js"], + run_at: "document_start", + }, ], }, @@ -58,6 +73,12 @@ add_task(async function test_cached_contentscript_on_document_start() { ) `, "script5.js": ` + throw null; + `, + "script6.js": ` + throw Symbol("MySymbol"); + `, + "script7.js": ` Promise.reject("rejected promise"); (async () => { @@ -78,19 +99,22 @@ add_task(async function test_cached_contentscript_on_document_start() { }, }); + await extension.startup(); + // Error messages, in roughly the order they appear above. let expectedMessages = [ + `Unable to load script: moz-extension://${extension.uuid}/script_does_not_exist.js`, "Error: Object exception", "uncaught exception: String exception", "ReferenceError: undefinedSymbol is not defined", "SyntaxError: expected expression, got ')'", + "uncaught exception: null", + "uncaught exception: Symbol(MySymbol)", "uncaught exception: rejected promise", "Error: async function exception", "ReferenceError: asyncUndefinedSymbol is not defined", ]; - await extension.startup(); - // Load a first page in order to be able to register a console listener in the content process. // This has to be done in the same domain of the second page to stay in the same process. let contentPage = await ExtensionTestUtils.loadContentPage(TEST_URL_1); @@ -110,7 +134,7 @@ add_task(async function test_cached_contentscript_on_document_start() { innerWindowID: error.innerWindowID, message: error.errorMessage, }); - if (this.collectedErrors.length == 7) { + if (this.collectedErrors.length == 10) { Services.console.unregisterListener(this); resolve(this.collectedErrors); } @@ -128,7 +152,7 @@ add_task(async function test_cached_contentscript_on_document_start() { await extension.awaitMessage("content-script-loaded"); - equal(errors.length, 7); + equal(errors.length, 10); let messages = []; for (const { innerWindowID, message } of errors) { equal( diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_permissions_change.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_permissions_change.js index 842994858e..dbaa01fa86 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_permissions_change.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_permissions_change.js @@ -1,6 +1,7 @@ "use strict"; Services.prefs.setBoolPref("extensions.manifestV3.enabled", true); +Services.prefs.setBoolPref("extensions.originControls.grantByDefault", false); const server = createHttpServer({ hosts: ["example.com", "example.net"] }); server.registerDirectory("/data/", do_get_file("data")); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_cookieBehaviors.js b/toolkit/components/extensions/test/xpcshell/test_ext_cookieBehaviors.js index e7dd1e99c6..b8fa021a1c 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_cookieBehaviors.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_cookieBehaviors.js @@ -61,17 +61,34 @@ function assertCookiesForHost(url, cookiesCount, message) { // Test that the indexedDB and localStorage are allowed in an extension page // and that the indexedDB is allowed in a extension worker. add_task(async function test_ext_page_allowed_storage() { - function testWebStorages() { + async function testWebStorages() { const url = window.location.href; try { // In a webpage accessing indexedDB throws on cookiesBehavior reject, // here we verify that doesn't happen for an extension page. + + await new Promise((resolve, reject) => { + const begin = indexedDB.open("door"); + begin.onsuccess = resolve; + begin.onerror = err => reject(err.target); + }); + + const dbs = await indexedDB.databases(); browser.test.assertTrue( - indexedDB, - "IndexedDB global should be accessible" + dbs.some(elem => elem.name === "door"), + "Just created database should be found" ); + await new Promise((resolve, reject) => { + const end = indexedDB.deleteDatabase("door"); + end.onsuccess = () => { + browser.test.log(`IndexedDB is accessible`); + resolve(); + }; + end.onerror = reject; + }); + // In a webpage localStorage is undefined on cookiesBehavior reject, // here we verify that doesn't happen for an extension page. browser.test.assertTrue( @@ -97,9 +114,23 @@ add_task(async function test_ext_page_allowed_storage() { } function testWorker() { - this.onmessage = () => { + this.onmessage = async () => { try { void indexedDB; + await new Promise((resolve, reject) => { + const begin = indexedDB.open("door"); + begin.onerror = err => reject(err.target); + begin.onsuccess = () => { + indexedDB + .databases() + .then(() => { + const end = indexedDB.deleteDatabase("door"); + end.onerror = err => reject(err.target); + end.onsuccess = resolve; + }) + .catch(reject); + }; + }); postMessage({ pass: true }); } catch (err) { postMessage({ pass: false }); @@ -448,9 +479,43 @@ add_task( let extFrame = this.content.document.querySelector("iframe#ext"); let webFrame = this.content.document.querySelector("iframe#web"); - function testIDB(win) { + async function testIDB(win) { try { - void win.indexedDB; + if (!win.indexedDB) { + Assert.ok(false, "IndexedDB global should be accessible"); + return; + } + + await new Promise((resolve, reject) => { + const req = win.indexedDB.open("door"); + req.onerror = err => { + reject(err.target); + Assert.ok( + false, + "IDB open should be accessible: " + err.target.message + ); + }; + req.onsuccess = resolve; + }); + + const dbs = await win.indexedDB.databases(); + Assert.ok( + dbs.some(elem => elem.name === "door"), + "Just created database should be found" + ); + + await new Promise((resolve, reject) => { + const req = win.indexedDB.deleteDatabase("door"); + req.onerror = err => { + reject(err.target); + Assert.ok( + false, + "IDB deleteDatabase should be accessible: " + err.target.message + ); + }; + req.onsuccess = resolve; + }); + return { success: true }; } catch (err) { return { error: `${err}` }; @@ -467,10 +532,10 @@ add_task( } return { - extTopLevel: testIDB(this.content), + extTopLevel: await testIDB(this.content), // TODO bug 1762638: Execute the following in their own tasks. - extSubFrame: testIDB(extFrame.contentWindow), - webSubFrame: testIDB(webFrame.contentWindow), + extSubFrame: await testIDB(extFrame.contentWindow), + webSubFrame: await testIDB(webFrame.contentWindow), webServiceWorker: await testServiceWorker(webFrame.contentWindow), }; }); @@ -485,9 +550,30 @@ add_task( return new Promise(resolve => { let frame = this.content.document.createElement("iframe"); frame.setAttribute("src", `moz-extension://${uuid}/subframe.html`); - frame.onload = () => { + frame.onload = async () => { try { - void frame.contentWindow.indexedDB; + if (!frame.contentWindow.indexedDB) { + throw Error("IndexedDB global should be accessible"); + } + const indexedDB = frame.contentWindow.indexedDB; + + await new Promise((success, failure) => { + const begin = indexedDB.open("door"); + begin.onsuccess = success; + begin.onerror = err => failure(err.target); + }); + + const dbs = await indexedDB.databases(); + if (!dbs.some(elem => elem.name === "door")) { + throw Error("Just created database should be found"); + } + + await new Promise((success, failure) => { + const end = indexedDB.deleteDatabase("door"); + end.onsuccess = success; + end.onerror = err => failure(err.target); + }); + resolve({ success: true }); } catch (err) { resolve({ error: `${err}` }); @@ -512,7 +598,7 @@ add_task( Assert.deepEqual( results.webSubFrame, - { error: "SecurityError: The operation is insecure." }, + { error: "SecurityError: IDBFactory.open: The operation is insecure" }, "IndexedDB not allowed in a subframe webpage with a top level extension page" ); Assert.deepEqual( @@ -544,8 +630,14 @@ add_task(async function test_content_script_on_cookieBehaviorReject() { function contentScript() { // Ensure that when the current cookieBehavior doesn't allow a webpage to use indexedDB // or localStorage, then a WebExtension content script is not allowed to use it as well. + browser.test.assertTrue(indexedDB, "IndexedDB handle should be accessible"); + browser.test.assertThrows( - () => indexedDB, + () => { + indexedDB.open("door").onsuccess = () => { + browser.test.fail(`Unreached function`); + }; + }, /The operation is insecure/, "a content script can't use indexedDB from a page where it is disallowed" ); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_dynamic_rules.js b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_dynamic_rules.js index 4ba120852f..ff87da1dde 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_dynamic_rules.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_dynamic_rules.js @@ -414,10 +414,10 @@ add_task(async function test_save_and_load_dynamic_rules() { browser.test.onMessage.addListener(async (msg, ...args) => { switch (msg) { case "assertGetDynamicRules": { - const [{ expectedRules }] = args; + const [{ expectedRules, filter }] = args; browser.test.assertDeepEq( expectedRules, - await dnr.getDynamicRules(), + await dnr.getDynamicRules(filter), "getDynamicRules() resolves to the expected dynamic rules" ); break; @@ -503,6 +503,35 @@ add_task(async function test_save_and_load_dynamic_rules() { expectedRules: getSchemaNormalizedRules(extension, rules), }); + info("Verify getDynamicRules with some filters"); + // An empty list of rule IDs should return no rule. + await callTestMessageHandler(extension, "assertGetDynamicRules", { + expectedRules: [], + filter: { ruleIds: [] }, + }); + // Non-existent rule ID. + await callTestMessageHandler(extension, "assertGetDynamicRules", { + expectedRules: [], + filter: { ruleIds: [456] }, + }); + await callTestMessageHandler(extension, "assertGetDynamicRules", { + expectedRules: getSchemaNormalizedRules(extension, [rules[0]]), + filter: { ruleIds: [rules[0].id] }, + }); + await callTestMessageHandler(extension, "assertGetDynamicRules", { + expectedRules: getSchemaNormalizedRules(extension, [rules[1]]), + filter: { ruleIds: [rules[1].id] }, + }); + await callTestMessageHandler(extension, "assertGetDynamicRules", { + expectedRules: getSchemaNormalizedRules(extension, rules), + filter: { ruleIds: rules.map(rule => rule.id) }, + }); + // When `ruleIds` isn't defined, we return all the rules. + await callTestMessageHandler(extension, "assertGetDynamicRules", { + expectedRules: getSchemaNormalizedRules(extension, rules), + filter: {}, + }); + const extUUID = extension.uuid; const dnrStore = ExtensionDNRStore._getStoreForTesting(); await dnrStore._savePromises.get(extUUID); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_regexFilter_limits.js b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_regexFilter_limits.js index 443f69c2d1..69635e4c80 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_regexFilter_limits.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_regexFilter_limits.js @@ -26,33 +26,6 @@ add_setup(async () => { await ExtensionTestUtils.startAddonManager(); }); -const _origDescs = {}; -function restoreDefaultDnrLimit(key) { - info(`Restoring original value of ExtensionDNRLimits.${key}`); - Object.defineProperty(ExtensionDNRLimits, key, _origDescs[key]); -} -function overrideDefaultDnrLimit(key, value) { - // Until DNR limits can be customized through prefs (bug 1803370), we need to - // overwrite the internals here in the parent process. That is sufficient to - // control the limits. Notably, this does NOT affect the values of the - // constants exposed through the declarativeNetRequest keyspace, because - // their values are directly read from the extension (child) process. - if (!_origDescs[key]) { - _origDescs[key] = Object.getOwnPropertyDescriptor(ExtensionDNRLimits, key); - registerCleanupFunction(() => restoreDefaultDnrLimit(key)); - } - Assert.ok( - typeof value === "number" && Number.isInteger(value), - `Setting ExtensionDNRLimits.${key} = ${value} (was: ${ExtensionDNRLimits[key]})` - ); - Object.defineProperty(ExtensionDNRLimits, key, { - configurable: true, - writable: true, - enumerable: true, - value, - }); -} - // Create an extension composed of the given test cases, and start or reload // the extension before each test case. // @@ -302,7 +275,10 @@ add_task(async function session_and_dynamic_regexFilter_limit() { setup() { // Artificially decrease the max number of allowed regexFilter rules, // so that whatever that was stored on disk is no longer within quota. - overrideDefaultDnrLimit("MAX_NUMBER_OF_REGEX_RULES", 1); + Services.prefs.setIntPref( + "extensions.dnr.max_number_of_regex_rules", + 1 + ); }, backgroundFn: testPart3_too_many_regexFilters_stored_after_lowering_quota, checkConsoleMessages: expectError, @@ -311,7 +287,9 @@ add_task(async function session_and_dynamic_regexFilter_limit() { name: "testPart4_reload_after_quota_back", setup() { // Restore the original quota after it was lowered in testPart3. - restoreDefaultDnrLimit("MAX_NUMBER_OF_REGEX_RULES"); + Services.prefs.clearUserPref( + "extensions.dnr.max_number_of_regex_rules" + ); }, backgroundFn: testPart4_reload_after_quota_back, checkConsoleMessages: noErrors, @@ -525,8 +503,8 @@ add_task(async function static_regexFilter_limit() { { name: "testPart5_after_doubling_quota", setup() { - overrideDefaultDnrLimit( - "MAX_NUMBER_OF_REGEX_RULES", + Services.prefs.setIntPref( + "extensions.dnr.max_number_of_regex_rules", 2 * MAX_NUMBER_OF_REGEX_RULES ); }, @@ -537,7 +515,9 @@ add_task(async function static_regexFilter_limit() { name: "testPart6_after_restoring_original_quota_half", setup() { // Restore the original quota after it was raised in testPart5. - restoreDefaultDnrLimit("MAX_NUMBER_OF_REGEX_RULES"); + Services.prefs.clearUserPref( + "extensions.dnr.max_number_of_regex_rules" + ); }, backgroundFn: testPart6_after_restoring_original_quota_half, checkConsoleMessages: (n, m) => diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_session_rules.js b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_session_rules.js index 5f0b0d72a2..6ac80a4ee7 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_session_rules.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_session_rules.js @@ -218,6 +218,39 @@ add_task(async function register_and_retrieve_session_rules() { "getSessionRules() returns all registered session rules" ); + browser.test.assertDeepEq( + [], + await dnr.getSessionRules({ ruleIds: [] }), + "getSessionRules() returns no rule because ruleIds is an empty array" + ); + browser.test.assertDeepEq( + [], + await dnr.getSessionRules({ ruleIds: [1234567890] }), + "getSessionRules() returns no rule because rule ID is non-existent" + ); + browser.test.assertDeepEq( + [RULE_4321_OUT], + await dnr.getSessionRules({ ruleIds: [4321] }), + "getSessionRules() returns a rule" + ); + browser.test.assertDeepEq( + [RULE_1234_OUT], + await dnr.getSessionRules({ ruleIds: [1234] }), + "getSessionRules() returns a rule" + ); + // The order is the same as the original input above, not the order of + // the IDs in `ruleIds`. + browser.test.assertDeepEq( + [RULE_4321_OUT, RULE_1234_OUT], + await dnr.getSessionRules({ ruleIds: [1234, 4321] }), + "getSessionRules() returns two rules" + ); + browser.test.assertDeepEq( + [RULE_4321_OUT, RULE_1234_OUT], + await dnr.getSessionRules({}), + "getSessionRules() returns all the rules because there is no ruleIds" + ); + await browser.test.assertRejects( dnr.updateSessionRules({ addRules: [RULE_9001_IN, RULE_1234_IN], diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_static_rules.js b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_static_rules.js index 4d20bd330e..7d5befef2e 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_static_rules.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_static_rules.js @@ -1269,13 +1269,13 @@ add_task(async function test_static_rulesets_limits() { }, { message: - /declarative_net_request: Enabled static rulesets are exceeding the MAX_NUMBER_OF_ENABLED_STATIC_RULESETS limit .* "ruleset_10"/, + /declarative_net_request: Enabled static rulesets are exceeding the MAX_NUMBER_OF_ENABLED_STATIC_RULESETS limit .* "ruleset_20"/, }, // Error reported on the browser console as part of loading enabled rulesets) // on enabled rulesets being ignored because exceeding the limit. { message: - /Ignoring enabled static ruleset exceeding the MAX_NUMBER_OF_ENABLED_STATIC_RULESETS .* "ruleset_10"/, + /Ignoring enabled static ruleset exceeding the MAX_NUMBER_OF_ENABLED_STATIC_RULESETS .* "ruleset_20"/, }, ], }); @@ -1285,7 +1285,7 @@ add_task(async function test_static_rulesets_limits() { ); extension.sendMessage("updateEnabledRulesets", { disableRulesetIds: ["ruleset_0"], - enableRulesetIds: ["ruleset_10", "ruleset_11"], + enableRulesetIds: ["ruleset_20", "ruleset_21"], }); await Assert.rejects( @@ -1313,19 +1313,19 @@ add_task(async function test_static_rulesets_limits() { "updateEnabledRulesets", { disableRulesetIds: ["ruleset_0"], - enableRulesetIds: ["ruleset_10"], + enableRulesetIds: ["ruleset_20"], }, { - disableRulesetIds: ["ruleset_10"], - enableRulesetIds: ["ruleset_11"], + disableRulesetIds: ["ruleset_20"], + enableRulesetIds: ["ruleset_21"], } ); await extension.awaitMessage("updateEnabledRulesets:done"); - // Expect ruleset_0 disabled, ruleset_10 to be enabled but then disabled by the - // second update queued after the first one, and ruleset_11 to be enabled. + // Expect ruleset_0 disabled, ruleset_20 to be enabled but then disabled by the + // second update queued after the first one, and ruleset_21 to be enabled. delete expectedEnabledRulesets.ruleset_0; - expectedEnabledRulesets.ruleset_11 = getSchemaNormalizedRules( + expectedEnabledRulesets.ruleset_21 = getSchemaNormalizedRules( extension, rules ); @@ -1356,10 +1356,10 @@ add_task(async function test_static_rulesets_limits() { await assertDNRStoreData(dnrStore, extension, expectedEnabledRulesets); extension.sendMessage("updateEnabledRulesets", { - disableRulesetIds: ["ruleset_11"], + disableRulesetIds: ["ruleset_21"], }); await extension.awaitMessage("updateEnabledRulesets:done"); - delete expectedEnabledRulesets.ruleset_11; + delete expectedEnabledRulesets.ruleset_21; await assertDNRGetEnabledRulesets( extension, Array.from(Object.keys(expectedEnabledRulesets)) diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_downloads_urlencoded.js b/toolkit/components/extensions/test/xpcshell/test_ext_downloads_urlencoded.js index ae40faf909..8532ce4a99 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_downloads_urlencoded.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_downloads_urlencoded.js @@ -83,7 +83,7 @@ add_task(async function test_decoded_filename_download() { const FILE_NAME_DECODED_2 = "file\u{0001F6B2}encoded.txt"; const FILE_NAME_ENCODED_URL_2 = BASE + "/" + FILE_NAME_ENCODED_2; const FILE_NAME_ENCODED_3 = "file%X%20encode.txt"; - const FILE_NAME_DECODED_3 = "file%X encode.txt"; + const FILE_NAME_DECODED_3 = "file_X encode.txt"; const FILE_NAME_ENCODED_URL_3 = BASE + "/" + FILE_NAME_ENCODED_3; const FILE_NAME_ENCODED_4 = "file%E3%80%82encode.txt"; const FILE_NAME_DECODED_4 = "file\u3002encode.txt"; diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_manifest_incognito.js b/toolkit/components/extensions/test/xpcshell/test_ext_manifest_incognito.js index 4330e1b681..6c2c0f65f9 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_manifest_incognito.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_manifest_incognito.js @@ -27,19 +27,48 @@ add_task(async function test_manifest_incognito() { "Should have the expected incognito string" ); + ExtensionTestUtils.failOnSchemaWarnings(false); normalized = await ExtensionTestUtils.normalizeManifest({ - incognito: "split", + incognito: "whatisthis", }); + ExtensionTestUtils.failOnSchemaWarnings(true); equal( normalized.error, - 'Error processing incognito: Invalid enumeration value "split"', + 'Error processing incognito: Invalid enumeration value "whatisthis"', "Should have an error" ); Assert.deepEqual(normalized.errors, [], "Should not have a warning"); + + // Sanity check: Default value of "incognito" is "spanning". + normalized = await ExtensionTestUtils.normalizeManifest({}); + equal(normalized.error, undefined, "Should not have an error"); + equal(normalized.errors.length, 0, "Should not have warnings"); + equal( + normalized.value.incognito, + "spanning", + "Should have the expected default value for incognito when unspecified" + ); +}); + +add_task(async function test_manifest_incognito_split_fallback_not_allowed() { + ExtensionTestUtils.failOnSchemaWarnings(false); + let normalized = await ExtensionTestUtils.normalizeManifest({ + incognito: "split", + }); + ExtensionTestUtils.failOnSchemaWarnings(true); + + equal(normalized.error, undefined, "Should not have an error"); + Assert.deepEqual( + normalized.errors, + [ + `Warning processing incognito: incognito "split" is unsupported. Falling back to incognito "not_allowed".`, + ], + "Should have a warning" + ); equal( - normalized.value, - undefined, - "Invalid incognito string should be undefined" + normalized.value.incognito, + "not_allowed", + "incognito:split should fall back to not_allowed by default" ); }); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js b/toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js index ab3f20f12e..925f8f9dfe 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js @@ -41,7 +41,7 @@ async function getManifestPermissions(extensionData) { ExtensionTestUtils.failOnSchemaWarnings(false); await extension.loadManifest(); ExtensionTestUtils.failOnSchemaWarnings(true); - let result = extension.manifestPermissions; + let result = extension.getRequiredPermissions(); if (extension.manifest.manifest_version >= 3) { // In MV3, host permissions are optional by default. diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js b/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js index ae6ce3d27e..a0bebc3f77 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js @@ -10,6 +10,11 @@ const { ExtensionPermissions } = ChromeUtils.importESModule( "resource://gre/modules/ExtensionPermissions.sys.mjs" ); +const WITH_INSTALL_PROMPT = [ + ["extensions.originControls.grantByDefault", true], +]; +const NO_INSTALL_PROMPT = [["extensions.originControls.grantByDefault", false]]; + Services.prefs.setBoolPref("extensions.manifestV3.enabled", true); // ExtensionParent.sys.mjs is being imported lazily because when it is imported Services.appinfo will be @@ -427,12 +432,24 @@ add_task(function test_normal_mv2() { }); }); +add_task(function test_normal_mv3_noInstallPrompt() { + return runWithPrefs(NO_INSTALL_PROMPT, () => + test_permissions({ + manifest_version: 3, + useAddonManager: "permanent", + expectAllGranted: false, + }) + ); +}); + add_task(function test_normal_mv3() { - return test_permissions({ - manifest_version: 3, - useAddonManager: "permanent", - expectAllGranted: false, - }); + return runWithPrefs(WITH_INSTALL_PROMPT, () => + test_permissions({ + manifest_version: 3, + useAddonManager: "permanent", + expectAllGranted: true, + }) + ); }); add_task(function test_granted_for_temporary_mv3() { @@ -444,28 +461,30 @@ add_task(function test_granted_for_temporary_mv3() { }); }); -add_task(async function test_granted_only_for_privileged_mv3() { - try { - // For permanent non-privileged, granted_host_permissions does nothing. - await test_permissions({ - manifest_version: 3, - granted_host_permissions: true, - useAddonManager: "permanent", - expectAllGranted: false, - }); +add_task(function test_granted_only_for_privileged_mv3() { + return runWithPrefs(NO_INSTALL_PROMPT, async () => { + try { + // For permanent non-privileged, granted_host_permissions does nothing. + await test_permissions({ + manifest_version: 3, + granted_host_permissions: true, + useAddonManager: "permanent", + expectAllGranted: false, + }); - // Make extensions loaded with addon manager privileged. - AddonTestUtils.usePrivilegedSignatures = true; + // Make extensions loaded with addon manager privileged. + AddonTestUtils.usePrivilegedSignatures = true; - await test_permissions({ - manifest_version: 3, - granted_host_permissions: true, - useAddonManager: "permanent", - expectAllGranted: true, - }); - } finally { - AddonTestUtils.usePrivilegedSignatures = false; - } + await test_permissions({ + manifest_version: 3, + granted_host_permissions: true, + useAddonManager: "permanent", + expectAllGranted: true, + }); + } finally { + AddonTestUtils.usePrivilegedSignatures = false; + } + }); }); add_task(async function test_startup() { @@ -540,7 +559,7 @@ add_task(async function test_startup() { }); // Test that we don't prompt for permissions an extension already has. -async function test_alreadyGranted(manifest_version) { +async function test_alreadyGranted({ manifest_version }) { const REQUIRED_PERMISSIONS = ["geolocation"]; const REQUIRED_ORIGINS = [ "*://required-host.com/", @@ -671,10 +690,17 @@ async function test_alreadyGranted(manifest_version) { await extension.unload(); } add_task(async function test_alreadyGranted_mv2() { - return test_alreadyGranted(2); + return test_alreadyGranted({ manifest_version: 2 }); }); -add_task(async function test_alreadyGranted_mv3() { - return test_alreadyGranted(3); +add_task(function test_alreadyGranted_mv3_noInstallPrompt() { + return runWithPrefs(NO_INSTALL_PROMPT, () => + test_alreadyGranted({ manifest_version: 3 }) + ); +}); +add_task(function test_alreadyGranted_mv3() { + return runWithPrefs(WITH_INSTALL_PROMPT, () => + test_alreadyGranted({ manifest_version: 3 }) + ); }); // IMPORTANT: Do not change this list without review from a Web Extensions peer! @@ -779,7 +805,10 @@ add_task(async function test_optional_all_urls() { }); // Check when content_script match patterns are treated as optional origins. -async function test_content_script_is_optional(manifest_version) { +async function test_content_script_is_optional({ + manifest_version, + expectGranted, +}) { function background() { browser.test.onMessage.addListener(async (msg, arg) => { if (msg == "request") { @@ -816,7 +845,11 @@ async function test_content_script_is_optional(manifest_version) { extension.sendMessage("getAll"); let initial = await extension.awaitMessage("granted"); - deepEqual(initial.origins, [], "Nothing granted on install."); + if (manifest_version < 3 || !expectGranted) { + deepEqual(initial.origins, [], "Nothing granted on install."); + } else { + deepEqual(initial.origins, [CS_ORIGIN], "CS origin granted on install."); + } await withHandlingUserInput(extension, async () => { extension.sendMessage("request", { @@ -845,11 +878,32 @@ async function test_content_script_is_optional(manifest_version) { await extension.unload(); } -add_task(() => test_content_script_is_optional(2)); -add_task(() => test_content_script_is_optional(3)); + +add_task(async function test_content_script_is_optional_mv2() { + await test_content_script_is_optional({ manifest_version: 2 }); +}); +add_task(function test_content_script_is_optional_mv3_noInstallPrompt() { + return runWithPrefs(NO_INSTALL_PROMPT, () => + test_content_script_is_optional({ + manifest_version: 3, + expectGranted: false, + }) + ); +}); +add_task(function test_content_script_is_optional_mv3() { + return runWithPrefs(WITH_INSTALL_PROMPT, () => + test_content_script_is_optional({ + manifest_version: 3, + expectGranted: true, + }) + ); +}); // Check that optional permissions are not included in update prompts -async function test_permissions_prompt(manifest_version) { +async function test_permissions_prompt({ + manifest_version, + expectInitialGranted, +}) { function background() { browser.test.onMessage.addListener(async (msg, arg) => { if (msg == "request") { @@ -896,7 +950,7 @@ async function test_permissions_prompt(manifest_version) { equal(result, true, "request() for optional permissions succeeded"); }); - if (manifest_version >= 3) { + if (!expectInitialGranted) { await withHandlingUserInput(extension, async () => { extension.sendMessage("request", { origins: ["https://test1.example.com/*"], @@ -964,10 +1018,26 @@ async function test_permissions_prompt(manifest_version) { await extension.unload(); } add_task(async function test_permissions_prompt_mv2() { - return test_permissions_prompt(2); + return test_permissions_prompt({ + manifest_version: 2, + expectInitialGranted: true, + }); +}); +add_task(function test_permissions_prompt_mv3_noInstallPrompt() { + return runWithPrefs(NO_INSTALL_PROMPT, () => + test_permissions_prompt({ + manifest_version: 3, + expectInitialGranted: false, + }) + ); }); add_task(async function test_permissions_prompt_mv3() { - return test_permissions_prompt(3); + return runWithPrefs(WITH_INSTALL_PROMPT, () => + test_permissions_prompt({ + manifest_version: 3, + expectInitialGranted: true, + }) + ); }); // Check that internal permissions can not be set and are not returned by the API. diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_runtime_getContexts.js b/toolkit/components/extensions/test/xpcshell/test_ext_runtime_getContexts.js new file mode 100644 index 0000000000..efef355f74 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_runtime_getContexts.js @@ -0,0 +1,180 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_runtime_getContexts() { + function background() { + browser.test.onMessage.addListener(async (msg, ...args) => { + if (msg === "runtime.getContexts") { + try { + const filter = args[0]; + if (!filter) { + // Expected to be rejected. + await browser.runtime.getContexts(); + } else { + // Expected to be resolved. + const result = await browser.runtime.getContexts(filter); + browser.test.sendMessage(`${msg}:result`, result); + } + } catch (err) { + browser.test.log(`runtime.getContexts error: ${err}\n`); + browser.test.sendMessage(`${msg}:error`, String(err)); + } + } + }); + browser.test.sendMessage("bgpage:loaded"); + } + + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + manifest_version: 3, + }, + background, + files: { + "tab.html": `<!DOCTYPE html><html></html>`, + }, + }); + + await extension.startup(); + await extension.awaitMessage("bgpage:loaded"); + + const documentOrigin = extension.extension.baseURI.spec.slice(0, -1); + const tabDocumentUrl = extension.extension.baseURI.resolve("tab.html"); + const bgDocumentUrl = extension.extension.baseURI.resolve( + "_generated_background_page.html" + ); + + let expectedBackground = { + contextType: "BACKGROUND", + documentOrigin, + documentUrl: bgDocumentUrl, + incognito: false, + frameId: 0, + tabId: -1, + windowId: -1, + }; + + let expectedTab = { + contextType: "TAB", + documentOrigin, + documentUrl: `${tabDocumentUrl}?fistOpenedTab=true`, + incognito: false, + frameId: 0, + // tabId and windowId are expected to be -1 in xpcshell test + // (these are also covered by browser_ext_getContexts.js). + tabId: -1, + windowId: -1, + }; + + const assertValidContextId = contextId => { + Assert.equal( + typeof contextId, + "string", + "contextId should be set to a string" + ); + Assert.notEqual( + contextId.length, + 0, + "contextId should be set to a non-zero length string" + ); + }; + const assertGetContextsResult = ( + actual, + expected, + msg, + { assertContextId = false } = {} + ) => { + const actualCopy = assertContextId ? actual : actual.map(it => ({ ...it })); + if (!assertContextId) { + actualCopy.forEach(it => delete it.contextId); + } + Assert.deepEqual(actualCopy, expected, msg); + }; + + info( + "Test runtime.getContexts rejects when called without any filter parameter" + ); + extension.sendMessage("runtime.getContexts", undefined); + let resError = await extension.awaitMessage("runtime.getContexts:error"); + Assert.equal( + resError, + "Error: Incorrect argument types for runtime.getContexts.", + "Got the expected error message" + ); + + info( + "Test runtime.getContext resolved when called with an empty filter parameter" + ); + + extension.sendMessage("runtime.getContexts", {}); + let res = await extension.awaitMessage("runtime.getContexts:result"); + + assertGetContextsResult( + res, + [expectedBackground], + "Got the expected properties for the background context" + ); + + let actualBackground = res[0]; + assertValidContextId(actualBackground.contextId); + + const page = await ExtensionTestUtils.loadContentPage( + `${tabDocumentUrl}?fistOpenedTab=true` + ); + + res = await page.spawn([], () => + this.content.wrappedJSObject.browser.runtime.getContexts({}) + ); + + const bgItem = res.find(it => it.contextType === "BACKGROUND"); + const tabItem = res.find(it => it.contextType === "TAB"); + + assertValidContextId(tabItem.contextId); + + assertGetContextsResult( + res, + [expectedBackground, expectedTab], + "Got expected properties for backgrond and tab contexts" + ); + assertGetContextsResult( + [bgItem], + [actualBackground], + "Expect the expected properties for the background context (included same contextId)", + { assertContextId: true } + ); + + info("Test runtime.getContexts with a contextType filter"); + res = await page.spawn([], () => + this.content.wrappedJSObject.browser.runtime.getContexts({ + contextTypes: ["BACKGROUND"], + }) + ); + assertGetContextsResult( + res, + [actualBackground], + "Expect only the backgrund context to be included in the results", + { assertContextId: true } + ); + + info("Test runtime.ContextType enum"); + const contextTypeEnum = await page.spawn([], () => { + return this.content.wrappedJSObject.browser.runtime.ContextType; + }); + + const expectedTypesMap = ["BACKGROUND", "POPUP", "SIDE_PANEL", "TAB"].reduce( + (acc, item) => { + acc[item] = item; + return acc; + }, + {} + ); + + Assert.deepEqual( + contextTypeEnum, + expectedTypesMap, + "Got the expected values in the ContextType enum" + ); + + await extension.unload(); +}); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_service_worker_messaging.js b/toolkit/components/extensions/test/xpcshell/test_ext_service_worker_messaging.js new file mode 100644 index 0000000000..b064da259b --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_service_worker_messaging.js @@ -0,0 +1,128 @@ +/* Any copyright is dedicated to the Public Domain. +https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_runtime_sendMessage() { + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + background: { + service_worker: "sw.js", + }, + browser_specific_settings: { + gecko: { id: "@test-messaging" }, + }, + }, + files: { + "sw.js": async function () { + browser.test.onMessage.addListener(msg => { + browser.test.assertEq("send", msg, "expected correct message"); + browser.runtime.sendMessage("hello-from-sw"); + }); + + browser.test.sendMessage("background-ready"); + }, + "page.html": '<!DOCTYPE html><script src="page.js"></script>', + "page.js"() { + browser.runtime.onMessage.addListener((msg, sender) => { + browser.test.assertEq( + "hello-from-sw", + msg, + "expected message from service worker" + ); + + const { contextId, ...otherProps } = sender; + browser.test.assertTrue(!!contextId, "expected a truthy contextId"); + browser.test.assertDeepEq( + { + envType: "addon_child", + id: "@test-messaging", + origin: self.origin, + url: browser.runtime.getURL("sw.js"), + }, + otherProps, + "expected correct sender props" + ); + + browser.test.sendMessage("page-done"); + }); + + browser.test.sendMessage("page-ready"); + }, + }, + }); + + await extension.startup(); + await extension.awaitMessage("background-ready"); + + const page = await ExtensionTestUtils.loadContentPage( + `moz-extension://${extension.uuid}/page.html`, + { extension } + ); + await extension.awaitMessage("page-ready"); + + extension.sendMessage("send"); + await extension.awaitMessage("page-done"); + + await page.close(); + await extension.unload(); +}); + +add_task(async function test_runtime_connect() { + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + background: { + service_worker: "sw.js", + }, + browser_specific_settings: { + gecko: { id: "@test-messaging" }, + }, + }, + files: { + "sw.js": async function () { + browser.test.onMessage.addListener(msg => { + browser.test.assertEq("connect", msg, "expected correct message"); + browser.runtime.connect(); + }); + + browser.test.sendMessage("background-ready"); + }, + "page.html": '<!DOCTYPE html><script src="page.js"></script>', + "page.js"() { + browser.runtime.onConnect.addListener(port => { + const { contextId, ...otherProps } = port.sender; + browser.test.assertTrue(!!contextId, "expected a truthy contextId"); + browser.test.assertDeepEq( + { + envType: "addon_child", + id: "@test-messaging", + origin: self.origin, + url: browser.runtime.getURL("sw.js"), + }, + otherProps, + "expected correct sender props" + ); + + browser.test.sendMessage("page-done"); + }); + + browser.test.sendMessage("page-ready"); + }, + }, + }); + + await extension.startup(); + await extension.awaitMessage("background-ready"); + + const page = await ExtensionTestUtils.loadContentPage( + `moz-extension://${extension.uuid}/page.html`, + { extension } + ); + await extension.awaitMessage("page-ready"); + + extension.sendMessage("connect"); + await extension.awaitMessage("page-done"); + + await page.close(); + await extension.unload(); +}); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_storage_telemetry.js b/toolkit/components/extensions/test/xpcshell/test_ext_storage_telemetry.js index d0448b7b2e..4b98597b11 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_storage_telemetry.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_storage_telemetry.js @@ -15,55 +15,42 @@ const { TelemetryTestUtils } = ChromeUtils.importESModule( "resource://testing-common/TelemetryTestUtils.sys.mjs" ); -const HISTOGRAM_JSON_IDS = [ - "WEBEXT_STORAGE_LOCAL_SET_MS", - "WEBEXT_STORAGE_LOCAL_GET_MS", -]; -const KEYED_HISTOGRAM_JSON_IDS = [ - "WEBEXT_STORAGE_LOCAL_SET_MS_BY_ADDONID", - "WEBEXT_STORAGE_LOCAL_GET_MS_BY_ADDONID", -]; - -const HISTOGRAM_IDB_IDS = [ +const HISTOGRAM_IDS = [ "WEBEXT_STORAGE_LOCAL_IDB_SET_MS", "WEBEXT_STORAGE_LOCAL_IDB_GET_MS", ]; -const KEYED_HISTOGRAM_IDB_IDS = [ +const KEYED_HISTOGRAM_IDS = [ "WEBEXT_STORAGE_LOCAL_IDB_SET_MS_BY_ADDONID", "WEBEXT_STORAGE_LOCAL_IDB_GET_MS_BY_ADDONID", ]; -const HISTOGRAM_IDS = [].concat(HISTOGRAM_JSON_IDS, HISTOGRAM_IDB_IDS); -const KEYED_HISTOGRAM_IDS = [].concat( - KEYED_HISTOGRAM_JSON_IDS, - KEYED_HISTOGRAM_IDB_IDS -); - const EXTENSION_ID1 = "@test-extension1"; const EXTENSION_ID2 = "@test-extension2"; async function test_telemetry_background() { const { GleanTimingDistribution } = globalThis; + + // NOTE: we do not collect telemetry for the legacy JSON backend anymore + // and so if the IDB backend is not enabled we expect the related telemetry + // histograms and timing distributions to be empty. const expectedEmptyGleanMetrics = ExtensionStorageIDB.isBackendEnabled - ? ["storageLocalGetJson", "storageLocalSetJson"] + ? [] : ["storageLocalGetIdb", "storageLocalSetIdb"]; const expectedNonEmptyGleanMetrics = ExtensionStorageIDB.isBackendEnabled ? ["storageLocalGetIdb", "storageLocalSetIdb"] - : ["storageLocalGetJson", "storageLocalSetJson"]; - + : []; const expectedEmptyHistograms = ExtensionStorageIDB.isBackendEnabled - ? HISTOGRAM_JSON_IDS - : HISTOGRAM_IDB_IDS; + ? [] + : HISTOGRAM_IDS; const expectedEmptyKeyedHistograms = ExtensionStorageIDB.isBackendEnabled - ? KEYED_HISTOGRAM_JSON_IDS - : KEYED_HISTOGRAM_IDB_IDS; - + ? [] + : KEYED_HISTOGRAM_IDS; const expectedNonEmptyHistograms = ExtensionStorageIDB.isBackendEnabled - ? HISTOGRAM_IDB_IDS - : HISTOGRAM_JSON_IDS; + ? HISTOGRAM_IDS + : []; const expectedNonEmptyKeyedHistograms = ExtensionStorageIDB.isBackendEnabled - ? KEYED_HISTOGRAM_IDB_IDS - : KEYED_HISTOGRAM_JSON_IDS; + ? KEYED_HISTOGRAM_IDS + : []; const server = createHttpServer(); server.registerDirectory("/data/", do_get_file("data")); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js index 23c29aa155..820e04956e 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_eventPage_StreamFilter.js @@ -105,7 +105,11 @@ async function test_idletimeout_on_streamfilter({ ).catch(err => { // This request is expected to be aborted when cleared after the test is exiting, // otherwise rethrow the error to trigger an explicit failure. - if (/The operation was aborted/.test(err.message)) { + if ( + /Content-Length header of network response exceeds response Body/.test( + err.message + ) + ) { info(`Test webRequest fetching "${testURL}" aborted`); } else { ok( diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml b/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml index 6d47012eca..fd0b2d50d2 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml @@ -432,6 +432,8 @@ skip-if = ["os == 'android' && debug"] ["test_ext_runtime_getBrowserInfo.js"] +["test_ext_runtime_getContexts.js"] + ["test_ext_runtime_getPlatformInfo.js"] ["test_ext_runtime_id.js"] diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-serviceworker.toml b/toolkit/components/extensions/test/xpcshell/xpcshell-serviceworker.toml index b300959970..83ca99bc2f 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-serviceworker.toml +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-serviceworker.toml @@ -49,3 +49,5 @@ run-sequentially = "very high failure rate in parallel" ["test_ext_scripting_contentScripts_file.js"] ["test_ext_scripting_updateContentScripts.js"] + +["test_ext_service_worker_messaging.js"] |