Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Tests tabs/scripting.executeScript() + userScripts.execute() on moz-extension pages</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";
add_setup(async function () {
// Make sure the restriction is enabled while running this test file,
//
// to be riding the release train.
await SpecialPowers.pushPrefEnv({
set: [
["extensions.webextensions.allow_executeScript_in_moz_extension", false],
// required to request optional userScripts permission
["extensions.webextOptionalPermissionPrompts", false],
],
});
});
// Note: this tests the scripting API for both manifest versions 2 and 3.
async function testExecuteScript({
manifest_version,
userScript = false,
activeTabPermission = false,
}) {
const EXPECTED_ERROR_MESSAGE = "Missing host permission for the tab";
const EXPECTED_SUCCESS_RESULT = `Executed successfully on ${TEST_SUBFRAME_URL}`;
const manifestPart = {
permissions: [],
};
if (userScript) {
manifestPart.optional_permissions = ["userScripts"];
} else {
manifestPart.permissions.push("scripting");
}
if (activeTabPermission) {
manifestPart.permissions.push("activeTab");
if (manifest_version === 3) {
manifestPart.action = {};
} else {
manifestPart.browser_action = {};
}
} else {
// host_permissions is merged into permissions as part of
// ExtensionTestCommon's internal normalization if the
// test extension is an manifest_version 2 extension.
}
const web_accessible_resources =
manifest_version === 3
? [
{
resources: ["extpage.html", "extpage-sandboxed.html"],
matches: ["*://*/*"],
},
]
: ["extpage.html", "extpage-sandboxed.html"];
let extension = ExtensionTestUtils.loadExtension({
manifest: {
manifest_version,
content_scripts: [
{
js: ["createSubFrame.js"],
matches: [TEST_SUBFRAME_URL],
},
],
web_accessible_resources,
...manifestPart,
},
background() {
function contentScript() {
return `Executed successfully on ${window.location}`;
}
function tabsExecuteScript({ allFrames = false }) {
return browser.tabs.executeScript({
code: `(${contentScript})()`,
allFrames,
});
}
async function scriptingExecuteScript({ allFrames = false }) {
const [tab] = await browser.tabs.query({ active: true });
return browser.scripting.executeScript({
target: { tabId: tab.id, allFrames },
func: contentScript,
});
}
async function userScriptsExecute({ allFrames = false }) {
await new Promise(resolve => {
browser.test.withHandlingUserInput(() => {
resolve(
browser.permissions.request({ permissions: ["userScripts"] })
);
});
});
const [tab] = await browser.tabs.query({ active: true });
return browser.userScripts.execute({
target: { tabId: tab.id, allFrames },
js: [{ code: `(${contentScript})()` }],
});
}
browser.test.onMessage.addListener(async (msg, opts = {}) => {
const { assertion, activeTabPermission } = opts;
const runTestCase = async () => {
try {
let res;
switch (msg) {
case "tabs.executeScript":
res = await tabsExecuteScript(opts);
break;
case "scripting.executeScript":
res = await scriptingExecuteScript(opts);
break;
case "userScripts.execute":
res = await userScriptsExecute(opts);
break;
default:
browser.test.fail(`Got unexpected test message: ${msg}`);
browser.test.sendMessage(`${msg}:done`);
return;
}
browser.test.assertDeepEq(
assertion.expected,
{ success: res },
assertion.message
);
browser.test.sendMessage(`${msg}:done`);
} catch (err) {
browser.test.assertDeepEq(
assertion.expected,
{ error: `${err.message}` },
assertion.message
);
browser.test.sendMessage(`${msg}:done`);
}
};
if (activeTabPermission) {
const actionAPI = browser.action ?? browser.browserAction;
actionAPI.onClicked.addListener(async function runOnActionClicked() {
actionAPI.onClicked.removeListener(runOnActionClicked);
await runTestCase();
});
browser.test.sendMessage("bgpage:action-listener-registered");
} else {
await runTestCase();
}
});
browser.test.sendMessage("bgpage:ready");
},
files: {
"extpage.html": `<script src='extpage.js'><\/script>`,
"extpage.js": function () {
browser.test.sendMessage("extpage:ready");
},
"extpage-sandboxed.html": `<h1>sandboxed iframe</h1>`,
"createSubFrame.js": function () {
const iframeExt = document.createElement("iframe");
iframeExt.src = browser.runtime.getURL("extpage.html");
const iframeExtSandboxed = document.createElement("iframe");
iframeExtSandboxed.src = browser.runtime.getURL(
"extpage-sandboxed.html"
);
iframeExtSandboxed.setAttribute("sandbox", "allow-scripts");
const promiseSandboxFrameLoaded = new Promise(resolve =>
iframeExtSandboxed.addEventListener("load", resolve, { once: true })
);
document.body.append(iframeExtSandboxed);
document.body.append(iframeExt);
promiseSandboxFrameLoaded.then(() =>
browser.test.sendMessage("sandboxed-subframe:ready")
);
},
},
});
const activateActiveTabPermission = async () => {
info("Clicking on the browser action to activate the activeTab permission");
await extension.awaitMessage("bgpage:action-listener-registered");
await AppTestDelegate.clickBrowserAction(window, extension);
};
await extension.startup();
await extension.awaitMessage("bgpage:ready");
info("Test on top-level moz-extension page");
const testTopLevelMozExtTab = await AppTestDelegate.openNewForegroundTab(
window,
);
const scriptingAPI = userScript
? "userScripts.execute"
: "scripting.executeScript";
await extension.awaitMessage("extpage:ready");
if (manifest_version < 3) {
extension.sendMessage("tabs.executeScript", {
activeTabPermission,
assertion: {
expected: { error: EXPECTED_ERROR_MESSAGE },
message:
"tabs.executeScript should reject on top-level moz-extension page",
},
});
if (activeTabPermission) {
await activateActiveTabPermission();
}
await extension.awaitMessage("tabs.executeScript:done");
}
extension.sendMessage(scriptingAPI, {
activeTabPermission,
assertion: {
expected: { error: EXPECTED_ERROR_MESSAGE },
message: `${scriptingAPI} should reject on top-level moz-extension page`,
},
});
if (activeTabPermission) {
// NOTE: the activeTab permission may have been granted at this point
// if it was already granted right above for tabs.executeScript and
// the tab has not been navigated.
await activateActiveTabPermission();
}
await extension.awaitMessage(`${scriptingAPI}:done`);
await AppTestDelegate.removeTab(window, testTopLevelMozExtTab);
info("Test on sub-frame moz-extension page");
const testSubFrameMozExtTab = await AppTestDelegate.openNewForegroundTab(
window,
TEST_SUBFRAME_URL,
true
);
info("Wait for the extension subframe to be loaded");
await extension.awaitMessage("extpage:ready");
info("Wait for the sandboxed subframe to be loaded");
await extension.awaitMessage("sandboxed-subframe:ready");
if (manifest_version < 3) {
extension.sendMessage("tabs.executeScript", {
activeTabPermission,
allFrames: true,
assertion: {
expected: { success: [EXPECTED_SUCCESS_RESULT] },
message:
"tabs.executeScript should ignore and omit moz-extension sub-frames",
},
});
if (activeTabPermission) {
await activateActiveTabPermission();
}
await extension.awaitMessage("tabs.executeScript:done");
}
extension.sendMessage(scriptingAPI, {
activeTabPermission,
allFrames: true,
assertion: {
expected: {
success: [
{
frameId: 0,
result: EXPECTED_SUCCESS_RESULT,
},
],
},
message: `${scriptingAPI} should ignore and omit moz-extension sub-frames`,
},
});
if (activeTabPermission) {
// NOTE: the activeTab permission may have been granted at this point
// if it was already granted right above for tabs.executeScript and
// the tab has not been navigated.
await activateActiveTabPermission();
}
await extension.awaitMessage(`${scriptingAPI}:done`);
await AppTestDelegate.removeTab(window, testSubFrameMozExtTab);
await extension.unload();
}
add_task(function testExecuteScript_manifestV2() {
return testExecuteScript({ manifest_version: 2 });
});
add_task(function testExecuteScript_manifestV2_activeTabPermission() {
return testExecuteScript({ manifest_version: 2, activeTabPermission: true });
});
add_task(function testExecuteScript_manifestV3() {
return testExecuteScript({ manifest_version: 3 });
});
add_task(function testExecuteScript_manifestV3_activeTabPermission() {
return testExecuteScript({ manifest_version: 3, activeTabPermission: true });
});
add_task(function testExecuteScript_manifestV3_userScripts() {
return testExecuteScript({ manifest_version: 3, userScript: true });
});
add_task(
function testExecuteScript_manifestV3_userScripts_activeTabPermission() {
return testExecuteScript({
manifest_version: 3,
userScript: true,
activeTabPermission: true,
});
}
);
add_task(async function testContentScriptsAndUserScriptsRegister() {
const extensionMV2 = ExtensionTestUtils.loadExtension({
manifest: {
manifest_version: 2,
user_scripts: {},
},
files: {
"content-script.js": function () {},
},
background() {
browser.test.assertThrows(
() =>
browser.contentScripts.register({
js: [{ file: "content-script.js" }],
}),
/Error processing matches\.0: Value "moz-extension:\/\/\*\/\*" must either/,
"MV2 contentScripts.register should throw on invalid moz-extension:// matches"
);
browser.test.assertThrows(
() =>
browser.userScripts.register({
js: [{ file: "content-script.js" }],
}),
/Error processing matches\.0: Value "moz-extension:\/\/\*\/\*" must either/,
"MV2 userScripts.register should throw on invalid moz-extension:// matches"
);
browser.test.sendMessage("bgpage:test-done");
},
});
await extensionMV2.startup();
await extensionMV2.awaitMessage("bgpage:test-done");
await extensionMV2.unload();
const extensionMV3 = ExtensionTestUtils.loadExtension({
manifest: {
manifest_version: 3,
optional_permissions: ["userScripts"],
},
files: {
"content-script.js": function () {},
},
async background() {
await new Promise(resolve => {
browser.test.withHandlingUserInput(() => {
resolve(
browser.permissions.request({ permissions: ["userScripts"] })
);
});
});
await browser.test.assertThrows(
() =>
browser.userScripts.register([
{
id: "",
js: [{ file: "content-script.js" }],
},
]),
/Error processing 0\.matches\.0: Value "moz-extension:\/\/\*\/\*" must either/,
"MV3 userScripts.register should throw on invalid moz-extension:// matches"
);
await browser.permissions.remove({ permissions: ["userScripts"] });
browser.test.sendMessage("bgpage:test-done");
},
});
await extensionMV3.startup();
await extensionMV3.awaitMessage("bgpage:test-done");
await extensionMV3.unload();
});
add_task(async function testScriptingRegister() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["scripting"],
},
files: {
"content-script.js": function contentScript() {
browser.test.fail(
`Script executed on ${window.location} (${document.readyState})`
);
},
"extpage.html": `<h1>TestExtPage</h1>`,
},
async background() {
await browser.scripting.registerContentScripts([
{
id: "test-mozextension-cs-matching",
js: ["content-script.js"],
// Registering the script to be executed on document start
// so that by the time the extension page had been fully
// loaded we expect the content script to have been already
// detected and blocked.
runAt: "document_start",
},
]);
browser.test.sendMessage(
"bgpage:script-registered",
browser.runtime.getURL("extpage.html")
);
},
});
await extension.startup();
const extPageURL = await extension.awaitMessage("bgpage:script-registered");
info("Open an extension page in a new tab");
let tab = await AppTestDelegate.openNewForegroundTab(
window,
extPageURL,
true
);
// NOTE: using an assertion here to make sure the test
// doesn't hit failue due to no checks being detected
// by the test harness when executed on its own.
ok(true, "Extension page load completed");
await AppTestDelegate.removeTab(window, tab);
await extension.unload();
});
</script>
</body>
</html>