diff options
Diffstat (limited to 'toolkit/components/extensions/test/mochitest/test_ext_contentscript_about_blank.html')
-rw-r--r-- | toolkit/components/extensions/test/mochitest/test_ext_contentscript_about_blank.html | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_about_blank.html b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_about_blank.html new file mode 100644 index 0000000000..1ca3cd619a --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_about_blank.html @@ -0,0 +1,335 @@ +<!doctype html> +<html> +<head> + <title>Test content script match_about_blank option</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/ExtensionTestUtils.js"></script> + <script src="head.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<script type="text/javascript"> +"use strict"; + +add_task(async function test_contentscript_about_blank() { + const manifest = { + content_scripts: [ + { + match_about_blank: true, + matches: [ + "*://mochi.test/*/file_with_about_blank.html", + "https://example.com/*/file_with_about_blank.html", + ], + all_frames: true, + css: ["all.css"], + js: ["all.js"], + }, { + matches: ["*://mochi.test/*/file_with_about_blank.html"], + css: ["mochi_without.css"], + js: ["mochi_without.js"], + all_frames: true, + }, { + match_about_blank: true, + matches: ["*://mochi.test/*/file_with_about_blank.html"], + css: ["mochi_with.css"], + js: ["mochi_with.js"], + all_frames: true, + }, + ], + }; + + const files = { + "all.js": function() { + browser.runtime.sendMessage("all"); + }, + "all.css": ` + body { color: red; } + `, + "mochi_without.js": function() { + browser.runtime.sendMessage("mochi_without"); + }, + "mochi_without.css": ` + body { background: yellow; } + `, + "mochi_with.js": function() { + browser.runtime.sendMessage("mochi_with"); + }, + "mochi_with.css": ` + body { text-align: right; } + `, + }; + + function background() { + browser.runtime.onMessage.addListener((script, {url}) => { + const kind = url.startsWith("about:") ? url : "top"; + browser.test.sendMessage("script", [script, kind, url]); + browser.test.sendMessage(`${script}:${kind}`); + }); + } + + const PATH = "tests/toolkit/components/extensions/test/mochitest/file_with_about_blank.html"; + const extension = ExtensionTestUtils.loadExtension({manifest, files, background}); + await extension.startup(); + + let count = 0; + extension.onMessage("script", script => { + info(`script ran: ${script}`); + count++; + }); + + let win = window.open("https://example.com/" + PATH); + await Promise.all([ + extension.awaitMessage("all:top"), + extension.awaitMessage("all:about:blank"), + extension.awaitMessage("all:about:srcdoc"), + ]); + is(count, 3, "exactly 3 scripts ran"); + win.close(); + + win = window.open("http://mochi.test:8888/" + PATH); + await Promise.all([ + extension.awaitMessage("all:top"), + extension.awaitMessage("all:about:blank"), + extension.awaitMessage("all:about:srcdoc"), + extension.awaitMessage("mochi_without:top"), + extension.awaitMessage("mochi_with:top"), + extension.awaitMessage("mochi_with:about:blank"), + extension.awaitMessage("mochi_with:about:srcdoc"), + ]); + + let style = win.getComputedStyle(win.document.body); + is(style.color, "rgb(255, 0, 0)", "top window text color is red"); + is(style.backgroundColor, "rgb(255, 255, 0)", "top window background is yellow"); + is(style.textAlign, "right", "top window text is right-aligned"); + + let a_b = win.document.getElementById("a_b"); + style = a_b.contentWindow.getComputedStyle(a_b.contentDocument.body); + is(style.color, "rgb(255, 0, 0)", "about:blank iframe text color is red"); + is(style.backgroundColor, "rgba(0, 0, 0, 0)", "about:blank iframe background is transparent"); + is(style.textAlign, "right", "about:blank text is right-aligned"); + + is(count, 10, "exactly 7 more scripts ran"); + win.close(); + + await extension.unload(); +}); + +async function top_level_about_blank({ legacyBehavior = false } = {}) { + const content_scripts = [ + { + match_about_blank: true, + matches: ["*://*/*"], + js: ["1.matches_any_url_and_blank.js"], + run_at: "document_end", + }, + { + // Note: interestingly, if one only wants to match top-level about:blank + // in Firefox, this would be a way to do so: + match_about_blank: true, + matches: ["*://*/*"], + exclude_matches: ["<all_urls>"], + exclude_globs: ["*"], + js: ["2.does_not_care_about_exclude_matches_globs.js"], + run_at: "document_end", + }, + { + match_about_blank: true, + matches: ["*://*/*"], + include_globs: ["*"], + js: ["3.should_not_run_because_include_globs_is_set.js"], + run_at: "document_end", + }, + { + matches: ["*://*/*"], + js: ["4.should_not_run_because_no_matchAboutBlank.js"], + run_at: "document_end", + }, + { + match_about_blank: true, + matches: ["*://non.matching.example/*"], + js: ["5.should_not_run_because_matches_does_not_match_all_urls.js"], + run_at: "document_end", + }, + ]; + const files = { + "get_seenScripts.js": () => { + globalThis.seenScripts ??= []; + globalThis.seenScripts.push("get_seenScripts.js"); + return globalThis.seenScripts; + }, + }; + function makeJsFile(filename) { + files[filename] = ` + dump("Running ${filename} at " + location + ", origin " + origin + "\\n"); + globalThis.seenScripts ??= []; + globalThis.seenScripts.push("${filename}"); + `; + } + for (let { js } of content_scripts) { + for (let filename of js) { + makeJsFile(filename); + } + } + + // Send an explicit test message in the last script which is expected to only + // be injected if the legacy behavior pref is set to true, if this is sent + // when running the test for the new expected behavior then hitting this message + // will trigger an additional explicit test failure due to the non handled + // "legacy-matching-script:executed" test message, on the contrary when + // the test is executed for the legacy matchAboutBlank behavior the test + // will await for that message explicitly to avoid intermittent failures. + files["5.should_not_run_because_matches_does_not_match_all_urls.js"] += ` + browser.test.sendMessage("legacy-matching-script:executed"); + `; + + function background() { + let tabId; + browser.test.onMessage.addListener(async (msg, expected, description) => { + if (msg === "openAboutBlankTab") { + const tab = await browser.tabs.create({ url: "about:blank" }); + tabId = tab.id; + browser.test.sendMessage("openAboutBlankTab_done"); + return; + } + if (msg === "closeAboutBlankTab") { + await browser.tabs.remove(tabId); + browser.test.sendMessage("closeAboutBlankTab_done"); + return; + } + browser.test.assertEq("seenScripts_check", msg, "Checking seenScripts"); + try { + browser.test.log("Checking seen content scripts"); + let [ seenScripts ] = await browser.tabs.executeScript( + tabId, + { + matchAboutBlank: true, + file: "get_seenScripts.js", + } + ); + browser.test.assertDeepEq(expected, seenScripts.sort(), description); + } catch (e) { + browser.test.assertDeepEq(expected, { error: e.message }, description); + } + browser.test.sendMessage("seenScripts_check_done"); + }); + browser.browserAction.onClicked.addListener(tab => { + browser.test.assertTrue(tab.active, "Active tab should be clicked"); + browser.test.assertEq(tabId, tab.id, "tabId should match"); + // Upon click, activeTab should be granted, so just return control. + browser.test.sendMessage("got_activeTab"); + }); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + content_scripts, + browser_action: {}, + permissions: ["activeTab"], + }, + files, + background, + }); + + async function openAboutBlankTab() { + extension.sendMessage("openAboutBlankTab"); + await extension.awaitMessage("openAboutBlankTab_done"); + } + async function closeAboutBlankTab() { + extension.sendMessage("closeAboutBlankTab"); + await extension.awaitMessage("closeAboutBlankTab_done"); + } + async function checkSeenScripts(expected, description) { + extension.sendMessage("seenScripts_check", expected, description); + await extension.awaitMessage("seenScripts_check_done"); + } + + // TODO bug 1856071: Remove this pref setter when the pref is removed. + // This pref should be set to false by default, but we still explicitly + // set pref value to: + // (1) cover the legacy behavior + // (2) in case that is not the default anymore (e.g. if we had to uplift a + // patch to temporarily bring back the legacy behavior before have remove pref + // and support for the legacy behavior). + await SpecialPowers.pushPrefEnv({ + set: [["extensions.script_about_blank_without_permission", legacyBehavior]], + }); + + // TODO bug 1856071: Remove this entire block when the pref is removed. + if (legacyBehavior) { + // Start of "Testing legacy behavior". + await extension.startup(); + await openAboutBlankTab(); + + // Await explicitly for the last content script that we expect to be + // matching the top level about:blank page while running on legacy + // behavior, otherwise we hit an intermittent failure due to the + // get_seenScript.js being executed before any of the other + // content scripts. + await extension.awaitMessage("legacy-matching-script:executed"); + + // Legacy behavior: tabs.executeScript works in top-level about:blank + // without granted activeTab. + await checkSeenScripts( + [ + "1.matches_any_url_and_blank.js", + "2.does_not_care_about_exclude_matches_globs.js", + // While the standard behavior is to only run scripts on top-level + // null-principal about:blank when matches[] matches "all urls", the + // legacy behavior was to do so for any script that declared + // match_about_blank:true, independent of matches[]. + "3.should_not_run_because_include_globs_is_set.js", + "5.should_not_run_because_matches_does_not_match_all_urls.js", + "get_seenScripts.js", + ], + "Any content script with match_about_blank:true should (legacy behavior)" + ); + + await closeAboutBlankTab(); + await extension.unload(); + await SpecialPowers.popPrefEnv(); + return; + // End of "Testing legacy behavior". + } + + await extension.startup(); + await openAboutBlankTab(); + + info("Testing tabs.executeScript without activeTab/host permissions"); + await checkSeenScripts( + { error: "Missing host permission for the tab" }, + "tabs.executeScript without activeTab shouldn't match top-level about:blank" + ); + + info("Unlocking activeTab permission"); + await AppTestDelegate.clickBrowserAction(window, extension); + await extension.awaitMessage("got_activeTab"); + + info("Retrieving result with tabs.executeScript with activeTab"); + await checkSeenScripts( + [ + "1.matches_any_url_and_blank.js", + "2.does_not_care_about_exclude_matches_globs.js", + "get_seenScripts.js", + ], + "Only content content scripts that match all URLs and matchAboutBlank should run" + ); + + await closeAboutBlankTab(); + await extension.unload(); + // TODO bug 1856071: Remove this popPrefEnv call when the pref is removed. + await SpecialPowers.popPrefEnv(); +} + +add_task(async function test_toplevel_aboutblank_match_with_permissions() { + await top_level_about_blank({ legacyBehavior: false }); +}); + +add_task(async function test_toplevel_aboutblank_match_without_permissions() { + await top_level_about_blank({ legacyBehavior: true }); +}); + +</script> + +</body> +</html> |