summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html')
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html703
1 files changed, 703 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html
new file mode 100644
index 0000000000..076c177dfa
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_activeTab.html
@@ -0,0 +1,703 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for content script</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" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="text/javascript">
+"use strict";
+
+add_task(async function setup() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.manifestV3.enabled", true]],
+ });
+});
+
+// Create a test extension with the provided function as the background
+// script. The background script will have a few helpful functions
+// available.
+/* global awaitLoad, gatherFrameSources */
+function makeExtension({
+ background,
+ useScriptingAPI = false,
+ manifest_version = 2,
+ host_permissions,
+}) {
+ // Wait for a webNavigation.onCompleted event where the details for the
+ // loaded page match the attributes of `filter`.
+ function awaitLoad(filter) {
+ return new Promise(resolve => {
+ const listener = details => {
+ if (Object.keys(filter).every(key => details[key] === filter[key])) {
+ browser.webNavigation.onCompleted.removeListener(listener);
+ resolve();
+ }
+ };
+ browser.webNavigation.onCompleted.addListener(listener);
+ });
+ }
+
+ // Return a string with a (sorted) list of the source of all frames
+ // in the given tab into which this extension can inject scripts
+ // (ie all frames for which it has the activeTab permission).
+ // Source is the hostname for frames in http sources, or the full
+ // location href in other documents (eg about: pages)
+ const gatherFrameSources = useScriptingAPI ?
+ async function gatherFrameSources(tabid) {
+ let results = await browser.scripting.executeScript({
+ target: { tabId: tabid, allFrames: true },
+ func: () => window.location.hostname || window.location.href,
+ });
+ // Adjust `result` so that it looks like the one returned by
+ // `tabs.executeScript()`.
+ let result = results.map(res => res.result);
+
+ return String(result.sort());
+ } : async function gatherFrameSources(tabid) {
+ let result = await browser.tabs.executeScript(tabid, {
+ allFrames: true,
+ matchAboutBlank: true,
+ code: "window.location.hostname || window.location.href;",
+ });
+
+ return String(result.sort());
+ };
+
+ const permissions = ["webNavigation"];
+ if (useScriptingAPI) {
+ permissions.push("scripting");
+ }
+
+ // When host_permissions is passed, test "automatic activeTab" for ungranted
+ // host_permissions in mv3, else test with the normal activeTab permission.
+ if (!host_permissions) {
+ permissions.push("activeTab");
+ }
+
+ return ExtensionTestUtils.loadExtension({
+ manifest: {
+ manifest_version,
+ permissions,
+ host_permissions,
+ },
+ background: [
+ `const useScriptingAPI = ${useScriptingAPI};`,
+ `const manifest_version = ${manifest_version};`,
+ `${awaitLoad}`,
+ `${gatherFrameSources}`,
+ `${ExtensionTestCommon.serializeScript(background)}`,
+ ].join("\n")
+ });
+}
+
+// Helper function to verify that executeScript() fails without the activeTab
+// permission (or any specific origin permissions).
+const verifyNoActiveTab = async ({ useScriptingAPI, manifest_version, host_permissions }) => {
+ let extension = makeExtension({
+ async background() {
+ const URL = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/file_contentscript_activeTab.html";
+
+ let [tab] = await Promise.all([
+ browser.tabs.create({url: URL}),
+ awaitLoad({frameId: 0}),
+ ]);
+
+ await browser.test.assertRejects(
+ gatherFrameSources(tab.id),
+ /^Missing host permission/,
+ "executeScript should fail without activeTab permission"
+ );
+
+ await browser.tabs.remove(tab.id);
+
+ browser.test.notifyPass("no-active-tab");
+ },
+ useScriptingAPI,
+ manifest_version,
+ host_permissions,
+ });
+
+ await extension.startup();
+ await extension.awaitFinish("no-active-tab");
+ await extension.unload();
+};
+
+add_task(async function test_no_activeTab_tabs() {
+ await verifyNoActiveTab({ useScriptingAPI: false });
+});
+
+add_task(async function test_no_activeTab_scripting() {
+ await verifyNoActiveTab({ useScriptingAPI: true });
+});
+
+add_task(async function test_no_activeTab_scripting_mv3() {
+ await verifyNoActiveTab({
+ useScriptingAPI: true,
+ manifest_version: 3,
+ host_permissions: null,
+ });
+});
+
+add_task(async function test_no_activeTab_scripting_mv3_autoActiveTab() {
+ await verifyNoActiveTab({
+ useScriptingAPI: true,
+ manifest_version: 3,
+ host_permissions: ["http://mochi.test/"],
+ });
+});
+
+// Test helper to verify that dynamically created iframes do not get the
+// activeTab permission, unless same-origin with the top page.
+const verifyDynamicFrames = async ({ useScriptingAPI, manifest_version, host_permissions }) => {
+ let extension = makeExtension({
+ async background() {
+ const BASE_HOST = "www.example.com";
+
+ let [tab] = await Promise.all([
+ browser.tabs.create({url: `https://${BASE_HOST}/`}),
+ awaitLoad({frameId: 0}),
+ ]);
+
+ function inject() {
+ let nframes = 4;
+ function frameLoaded() {
+ nframes--;
+ if (nframes == 0) {
+ browser.runtime.sendMessage("frames-loaded");
+ }
+ }
+
+ // about:blank
+ let frame = document.createElement("iframe");
+ frame.addEventListener("load", frameLoaded, {once: true});
+ document.body.appendChild(frame);
+
+ let div = document.createElement("div");
+ div.innerHTML = "<iframe src='https://test1.example.com/'></iframe>";
+ let framelist = div.getElementsByTagName("iframe");
+ browser.test.assertEq(1, framelist.length, "Found 1 frame inside div");
+ framelist[0].addEventListener("load", frameLoaded, {once: true});
+ document.body.appendChild(div);
+
+ // about:srcdoc containing cross-origin frame.
+ let div2 = document.createElement("div");
+ div2.innerHTML = "<iframe srcdoc=\"<iframe src='https://test2.example.com/'&gt;</iframe&gt;\"></iframe>";
+ framelist = div2.getElementsByTagName("iframe");
+ browser.test.assertEq(1, framelist.length, "Found 1 frame inside div");
+ framelist[0].addEventListener("load", frameLoaded, {once: true});
+ document.body.appendChild(div2);
+
+ // Note: URL's host is BASE_HOST (same as top).
+ const URL = "https://www.example.com/tests/toolkit/components/extensions/test/mochitest/file_contentscript_iframe.html";
+
+ let xhr = new XMLHttpRequest();
+ xhr.open("GET", URL);
+ xhr.responseType = "document";
+ xhr.overrideMimeType("text/html");
+
+ xhr.addEventListener("load", () => {
+ if (xhr.readyState != 4) {
+ return;
+ }
+ if (xhr.status != 200) {
+ browser.runtime.sendMessage("error");
+ }
+
+ let frame = xhr.response.getElementById("frame");
+ browser.test.assertEq(
+ "https://test2.example.com/",
+ frame?.src,
+ "Found frame in response document with cross-origin URL"
+ );
+ frame.addEventListener("load", frameLoaded, {once: true});
+ document.body.appendChild(frame);
+ }, {once: true});
+ xhr.addEventListener("error", () => {
+ browser.runtime.sendMessage("error");
+ }, {once: true});
+ xhr.send();
+ }
+
+ browser.test.onMessage.addListener(async msg => {
+ if (msg !== "go") {
+ browser.test.fail(`unexpected message received: ${msg}`);
+ return;
+ }
+
+ let loadedPromise = new Promise((resolve, reject) => {
+ let listener = msg => {
+ let unlisten = () => browser.runtime.onMessage.removeListener(listener);
+ if (msg == "frames-loaded") {
+ unlisten();
+ resolve();
+ } else if (msg == "error") {
+ unlisten();
+ reject();
+ }
+ };
+ browser.runtime.onMessage.addListener(listener);
+ });
+
+ if (useScriptingAPI) {
+ await browser.scripting.executeScript({
+ target: { tabId: tab.id },
+ func: inject,
+ });
+ } else {
+ await browser.tabs.executeScript(tab.id, {
+ code: `(${inject})();`,
+ });
+ }
+
+ await loadedPromise;
+
+ let result = await gatherFrameSources(tab.id);
+
+ browser.test.assertEq(
+ String(["about:blank", "about:srcdoc", BASE_HOST]),
+ result,
+ `Script injected only into (same origin) about:blank-ish dynamically created frames`
+ );
+
+ await browser.tabs.remove(tab.id);
+
+ browser.test.notifyPass("dynamic-frames");
+ });
+
+ browser.test.sendMessage("ready", tab.id);
+ },
+ useScriptingAPI,
+ manifest_version,
+ host_permissions,
+ });
+
+ await extension.startup();
+
+ let tabId = await extension.awaitMessage("ready");
+ extension.grantActiveTab(tabId);
+
+ extension.sendMessage("go");
+ await extension.awaitFinish("dynamic-frames");
+
+ await extension.unload();
+};
+
+add_task(async function test_dynamic_frames_tabs() {
+ await verifyDynamicFrames({ useScriptingAPI: false });
+});
+
+add_task(async function test_dynamic_frames_scripting() {
+ await verifyDynamicFrames({ useScriptingAPI: true });
+});
+
+add_task(async function test_dynamic_frames_scripting_mv3() {
+ await verifyDynamicFrames({
+ useScriptingAPI: true,
+ manifest_version: 3,
+ host_permissions: null,
+ });
+});
+
+add_task(async function test_dynamic_frames_scripting_mv3_autoActiveTab() {
+ await verifyDynamicFrames({
+ useScriptingAPI: true,
+ manifest_version: 3,
+ host_permissions: ["https://www.example.com/"],
+ });
+});
+
+// Test helper to verify that an iframe created from an <iframe srcdoc> gets
+// the activeTab permission.
+const verifySrcdoc = async ({ useScriptingAPI, manifest_version, host_permissions }) => {
+ let extension = makeExtension({
+ async background() {
+ const URL = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/file_contentscript_activeTab2.html";
+ const OUTER_SOURCE = "about:srcdoc";
+ const PAGE_SOURCE = "mochi.test";
+ const FRAME_SOURCE = "test1.example.com";
+
+ let [tab] = await Promise.all([
+ browser.tabs.create({url: URL}),
+ awaitLoad({frameId: 0}),
+ ]);
+
+ browser.test.onMessage.addListener(async msg => {
+ if (msg !== "go") {
+ browser.test.fail(`unexpected message received: ${msg}`);
+ return;
+ }
+
+ let result = await gatherFrameSources(tab.id);
+
+ if (manifest_version < 3) {
+ browser.test.assertEq(
+ String([OUTER_SOURCE, PAGE_SOURCE, FRAME_SOURCE]),
+ result,
+ "Script is injected into frame created from <iframe srcdoc>"
+ );
+ } else {
+ browser.test.assertEq(
+ String([OUTER_SOURCE, PAGE_SOURCE]),
+ result,
+ "Script is not injected into cross-origin frame created from <iframe srcdoc>"
+ );
+ }
+
+ await browser.tabs.remove(tab.id);
+
+ browser.test.notifyPass("srcdoc");
+ });
+
+ browser.test.sendMessage("ready", tab.id);
+ },
+ useScriptingAPI,
+ manifest_version,
+ host_permissions,
+ });
+
+ await extension.startup();
+
+ let tabId = await extension.awaitMessage("ready");
+ extension.grantActiveTab(tabId);
+
+ extension.sendMessage("go");
+ await extension.awaitFinish("srcdoc");
+
+ await extension.unload();
+};
+
+add_task(async function test_srcdoc_tabs() {
+ await verifySrcdoc({ useScriptingAPI: false });
+});
+
+add_task(async function test_srcdoc_scripting() {
+ await verifySrcdoc({ useScriptingAPI: true });
+});
+
+add_task(async function test_srcdoc_scripting_mv3() {
+ await verifySrcdoc({
+ useScriptingAPI: true,
+ manifest_version: 3,
+ host_permissions: null,
+ });
+});
+
+add_task(async function test_srcdoc_scripting_mv3_autoActiveTab() {
+ await verifySrcdoc({
+ useScriptingAPI: true,
+ manifest_version: 3,
+ host_permissions: ["http://mochi.test/"],
+ });
+});
+
+// Test helper to verify that navigating frames by setting the src attribute
+// from the parent page does not grant the activeTab permission to the frame,
+// unless the frame is same-origin to the top page.
+const verifyNavigateBySrc = async ({ useScriptingAPI, manifest_version, host_permissions }) => {
+ let extension = makeExtension({
+ async background() {
+ const URL = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/file_contentscript_activeTab.html";
+ const PAGE_SOURCE = "mochi.test";
+ const EMPTY_SOURCE = "about:blank";
+ const FRAME_SOURCE = "test1.example.com";
+
+ let [tab] = await Promise.all([
+ browser.tabs.create({url: URL}),
+ awaitLoad({frameId: 0}),
+ ]);
+
+ browser.test.onMessage.addListener(async msg => {
+ if (msg !== "go") {
+ browser.test.fail(`unexpected message received: ${msg}`);
+ return;
+ }
+
+ let result = await gatherFrameSources(tab.id);
+ if (manifest_version < 3) {
+ browser.test.assertEq(
+ String([EMPTY_SOURCE, PAGE_SOURCE, FRAME_SOURCE]),
+ result,
+ "In original page, script is injected into base page and original frames"
+ );
+ } else {
+ browser.test.assertEq(
+ String([EMPTY_SOURCE, PAGE_SOURCE]),
+ result,
+ "In original page, script is injected into same-origin frames"
+ );
+ }
+
+ let loadedPromise = awaitLoad({tabId: tab.id});
+
+ let func = () => {
+ document.getElementById('emptyframe').src = 'http://test2.example.com/';
+ };
+
+ if (useScriptingAPI) {
+ await browser.scripting.executeScript({
+ target: { tabId: tab.id },
+ func,
+ });
+ } else {
+ await browser.tabs.executeScript(tab.id, { code: `(${func})();` });
+ }
+
+ await loadedPromise;
+
+
+ result = await gatherFrameSources(tab.id);
+ if (manifest_version < 3) {
+ browser.test.assertEq(
+ String([PAGE_SOURCE, FRAME_SOURCE]),
+ result,
+ "Script is not injected into initially empty frame after cross-origin navigation"
+ );
+ } else {
+ browser.test.assertEq(
+ String([PAGE_SOURCE]),
+ result,
+ "Script is not injected into initially empty frame after cross-origin navigation"
+ );
+ }
+
+ loadedPromise = awaitLoad({tabId: tab.id});
+
+ func = () => {
+ document.getElementById('regularframe').src = 'http://mochi.test:8888/';
+ };
+
+ if (useScriptingAPI) {
+ await browser.scripting.executeScript({
+ target: { tabId: tab.id },
+ func,
+ });
+ } else {
+ await browser.tabs.executeScript(tab.id, { code: `(${func})();` });
+ }
+
+ await loadedPromise;
+
+ result = await gatherFrameSources(tab.id);
+
+ browser.test.assertEq(
+ String([PAGE_SOURCE, PAGE_SOURCE]),
+ result,
+ "Script injected into frame after navigating to same-origin"
+ );
+
+ await browser.tabs.remove(tab.id);
+ browser.test.notifyPass("test-scripts");
+ });
+
+ browser.test.sendMessage("ready", tab.id);
+ },
+ useScriptingAPI,
+ manifest_version,
+ host_permissions,
+ });
+
+ await extension.startup();
+
+ let tabId = await extension.awaitMessage("ready");
+ extension.grantActiveTab(tabId);
+
+ extension.sendMessage("go");
+ await extension.awaitFinish("test-scripts");
+
+ await extension.unload();
+};
+
+add_task(async function test_navigate_by_src_tabs() {
+ await verifyNavigateBySrc({ useScriptingAPI: false });
+});
+
+add_task(async function test_navigate_by_src_scripting() {
+ await verifyNavigateBySrc({ useScriptingAPI: true });
+});
+
+add_task(async function test_navigate_by_src_scripting_mv3() {
+ await verifyNavigateBySrc({
+ useScriptingAPI: true,
+ manifest_version: 3,
+ host_permissions: null,
+ });
+});
+
+add_task(async function test_navigate_by_src_scripting_mv3_autoActiveTab() {
+ await verifyNavigateBySrc({
+ useScriptingAPI: true,
+ manifest_version: 3,
+ host_permissions: ["http://mochi.test/"],
+ });
+});
+
+// Test helper to verify that navigating frames by setting window.location from
+// inside the frame revokes the activeTab permission.
+const verifyNavigateByWindowLocation = async ({ useScriptingAPI, manifest_version, host_permissions }) => {
+ let extension = makeExtension({
+ async background() {
+ const URL = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/file_contentscript_activeTab.html";
+ const PAGE_SOURCE = "mochi.test";
+ const EMPTY_SOURCE = "about:blank";
+ const FRAME_SOURCE = "test1.example.com";
+
+ let [tab] = await Promise.all([
+ browser.tabs.create({url: URL}),
+ awaitLoad({frameId: 0}),
+ ]);
+
+ browser.test.onMessage.addListener(async msg => {
+ if (msg !== "go") {
+ browser.test.fail(`unexpected message received: ${msg}`);
+ return;
+ }
+
+ let result = await gatherFrameSources(tab.id);
+
+ if (manifest_version < 3) {
+ browser.test.assertEq(
+ String([EMPTY_SOURCE, PAGE_SOURCE, FRAME_SOURCE]),
+ result,
+ "Script initially injected into all frames"
+ );
+ } else {
+ browser.test.assertEq(
+ String([EMPTY_SOURCE, PAGE_SOURCE]),
+ result,
+ "Script initially injected into all same-origin frames"
+ );
+ }
+
+ let nframes = 0;
+ let frames = await browser.webNavigation.getAllFrames({tabId: tab.id});
+ for (let frame of frames) {
+ if (frame.parentFrameId == -1) {
+ continue;
+ }
+
+ if (manifest_version >= 3 && frame.url.includes(FRAME_SOURCE)) {
+ // In MV3, can't access cross-origin iframes from the start.
+
+ let invalidPromise = browser.scripting.executeScript({
+ target: { tabId: tab.id, frameIds: [frame.frameId] },
+ func: () => window.location.hostname,
+ });
+ await browser.test.assertRejects(
+ invalidPromise,
+ /^Missing host permission for the tab or frames/,
+ "executeScript should fail on cross-origin frame"
+ );
+
+ continue;
+ }
+
+ let loadPromise = awaitLoad({
+ tabId: tab.id,
+ frameId: frame.frameId,
+ });
+
+ let func = () => {
+ window.location.href = 'https://test2.example.com/';
+ };
+
+ if (useScriptingAPI) {
+ await browser.scripting.executeScript({
+ target: { tabId: tab.id, frameIds: [frame.frameId] },
+ func,
+ });
+ } else {
+ await browser.tabs.executeScript(tab.id, {
+ frameId: frame.frameId,
+ matchAboutBlank: true,
+ code: `(${func})();`,
+ });
+ }
+
+ await loadPromise;
+
+ let executePromise;
+ func = () => window.location.hostname;
+
+ if (useScriptingAPI) {
+ executePromise = browser.scripting.executeScript({
+ target: { tabId: tab.id, frameIds: [frame.frameId] },
+ func,
+ });
+ } else {
+ executePromise = browser.tabs.executeScript(tab.id, {
+ frameId: frame.frameId,
+ matchAboutBlank: true,
+ code: `(${func})();`,
+ });
+ }
+
+ await browser.test.assertRejects(
+ executePromise,
+ /^Missing host permission for the tab or frames/,
+ "executeScript should have failed on navigated frame"
+ );
+
+ nframes++;
+ }
+
+ if (manifest_version < 3) {
+ browser.test.assertEq(2, nframes, "Found 2 frames");
+ } else {
+ browser.test.assertEq(1, nframes, "Found 1 frame");
+ }
+
+ await browser.tabs.remove(tab.id);
+ browser.test.notifyPass("scripted-navigation");
+ });
+
+ browser.test.sendMessage("ready", tab.id);
+ },
+ useScriptingAPI,
+ manifest_version,
+ host_permissions,
+ });
+
+ await extension.startup();
+
+ let tabId = await extension.awaitMessage("ready");
+ extension.grantActiveTab(tabId);
+
+ extension.sendMessage("go");
+ await extension.awaitFinish("scripted-navigation");
+
+ await extension.unload();
+};
+
+add_task(async function test_navigate_by_window_location_tabs() {
+ await verifyNavigateByWindowLocation({ useScriptingAPI: false });
+});
+
+add_task(async function test_navigate_by_window_location_scripting() {
+ await verifyNavigateByWindowLocation({ useScriptingAPI: true });
+});
+
+add_task(async function test_navigate_by_window_location_scripting_mv3() {
+ await verifyNavigateByWindowLocation({
+ useScriptingAPI: true,
+ manifest_version: 3,
+ host_permissions: null,
+ });
+});
+
+add_task(async function test_navigate_by_window_location_scripting_mv3_autoActiveTab() {
+ await verifyNavigateByWindowLocation({
+ useScriptingAPI: true,
+ manifest_version: 3,
+ host_permissions: ["http://mochi.test/"],
+ });
+});
+
+</script>
+
+</body>
+</html>