fune/toolkit/components/extensions/test/mochitest/test_ext_scripting_executeScript.html
William Durand a718b52a24 Bug 1736576 - Add support for multiple files in scripting.executeScript(). r=robwu
Instead of trying to make `_execute()` in `ext-tabs-base.js` accept
options for both the Scripting and Tabs APIs, we introduced an
`execute()` function in `parent/ext-scripting.js` that is similar to
`_execute()` but specific to the Scripting API options.

Differential Revision: https://phabricator.services.mozilla.com/D133872
2022-01-18 16:33:57 +00:00

816 lines
22 KiB
HTML

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Tests scripting.executeScript()</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>
<iframe src="https://example.com/tests/toolkit/components/extensions/test/mochitest/file_sample.html"></iframe>
<script type="text/javascript">
"use strict";
const MOCHITEST_HOST_PERMISSIONS = [
"*://mochi.test/",
"*://mochi.xorigin-test/",
"*://test1.example.com/",
];
const makeExtension = ({ manifest: manifestProps, ...otherProps }) => {
return ExtensionTestUtils.loadExtension({
manifest: {
manifest_version: 3,
permissions: ["scripting"],
host_permissions: [
...MOCHITEST_HOST_PERMISSIONS,
"https://example.com/",
// Used in `file_contains_iframe.html`
"https://example.org/",
],
...manifestProps,
},
...otherProps,
});
};
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [["extensions.manifestV3.enabled", true]],
});
});
add_task(async function test_executeScript_params_validation() {
let extension = makeExtension({
async background() {
const TEST_CASES = [
{
title: "no files and no func",
executeScriptParams: {},
expectedError: /Exactly one of files and func must be specified/,
},
{
title: "both files and func are passed",
executeScriptParams: { files: ["script.js"], func() {} },
expectedError: /Exactly one of files and func must be specified/,
},
{
title: "non-empty args is passed with files",
executeScriptParams: { files: ["script.js"], args: [123] },
expectedError: /'args' may not be used with file injections/,
},
{
title: "empty args is passed with files",
executeScriptParams: { files: ["script.js"], args: [] },
expectedError: /'args' may not be used with file injections/,
},
{
title: "unserializable argument",
executeScriptParams: { func() {}, args: [window] },
expectedError: /Unserializable arguments/,
},
];
const tabs = await browser.tabs.query({ active: true });
for (const { title, executeScriptParams, expectedError } of TEST_CASES) {
await browser.test.assertRejects(
browser.scripting.executeScript({
target: { tabId: tabs[0].id },
...executeScriptParams,
}),
expectedError,
`expected error when: ${title}`
);
}
browser.test.notifyPass("execute-script");
},
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
add_task(async function test_executeScript_with_wrong_host_permissions() {
let extension = makeExtension({
manifest: {
host_permissions: [],
},
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
await browser.test.assertRejects(
browser.scripting.executeScript({
target: { tabId: tabs[0].id },
func: () => {},
}),
/Missing host permission for the tab/,
"expected error"
);
browser.test.notifyPass("execute-script");
},
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
add_task(async function test_executeScript_with_invalid_tabId() {
let extension = makeExtension({
async background() {
// This tab ID should not exist.
const tabId = 123456789;
await browser.test.assertRejects(
browser.scripting.executeScript({
target: { tabId },
func: () => {},
}),
`Invalid tab ID: ${tabId}`
);
browser.test.notifyPass("execute-script");
},
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
add_task(async function test_executeScript_with_func() {
let extension = makeExtension({
async background() {
const getTitle = () => {
return document.title;
};
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const results = await browser.scripting.executeScript({
target: { tabId: tabs[0].id },
func: getTitle,
});
browser.test.assertEq(
1,
results.length,
"got expected number of results"
);
browser.test.assertEq(
"file sample",
results[0].result,
"got the expected title"
);
browser.test.assertEq(
0,
results[0].frameId,
"got the expected frameId"
);
browser.test.notifyPass("execute-script");
}
});
let tab = await AppTestDelegate.openNewForegroundTab(
window,
"https://test1.example.com/tests/toolkit/components/extensions/test/mochitest/file_sample.html",
true
);
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
await AppTestDelegate.removeTab(window, tab);
});
add_task(async function test_executeScript_with_func_and_args() {
let extension = makeExtension({
async background() {
const formatArgs = (a, b, c) => {
return `received ${a}, ${b} and ${c}`;
};
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const results = await browser.scripting.executeScript({
target: { tabId: tabs[0].id },
func: formatArgs,
args: [true, undefined, "str"],
});
browser.test.assertEq(
1,
results.length,
"got expected number of results"
);
browser.test.assertEq(
// undefined is converted to null when json-stringified in an array.
"received true, null and str",
results[0].result,
"got the expected return value"
);
browser.test.assertEq(
0,
results[0].frameId,
"got the expected frameId"
);
browser.test.notifyPass("execute-script");
}
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
add_task(async function test_executeScript_returns_nothing() {
let extension = makeExtension({
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const results = await browser.scripting.executeScript({
target: { tabId: tabs[0].id },
func: () => {},
});
browser.test.assertEq(
1,
results.length,
"got expected number of results"
);
browser.test.assertEq(
null,
results[0].result,
"got expected null result"
);
browser.test.assertEq(
0,
results[0].frameId,
"got the expected frameId"
);
browser.test.notifyPass("execute-script");
}
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
add_task(async function test_executeScript_returns_null() {
let extension = makeExtension({
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const results = await browser.scripting.executeScript({
target: { tabId: tabs[0].id },
func: () => { return null; },
});
browser.test.assertEq(
1,
results.length,
"got expected number of results"
);
browser.test.assertEq(
null,
results[0].result,
"got expected null result"
);
browser.test.assertEq(
0,
results[0].frameId,
"got the expected frameId"
);
browser.test.notifyPass("execute-script");
}
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
add_task(async function test_executeScript_with_error_in_func() {
let extension = makeExtension({
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const results = await browser.scripting.executeScript({
target: { tabId: tabs[0].id },
func: () => {
throw new Error(`Thrown at ${location.pathname.split("/").pop()}`);
},
});
browser.test.assertEq(
1,
results.length,
"got expected number of results"
);
browser.test.assertEq(
null,
results[0].result,
"got the expected result"
);
browser.test.assertEq(
0,
results[0].frameId,
"got the expected frameId"
);
browser.test.notifyPass("execute-script");
}
});
let tab = await AppTestDelegate.openNewForegroundTab(
window,
"https://test1.example.com/tests/toolkit/components/extensions/test/mochitest/file_sample.html",
true
);
consoleMonitor.start([
{ message: /Thrown at file_sample/ },
]);
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
await consoleMonitor.finished();
await AppTestDelegate.removeTab(window, tab);
});
add_task(async function test_executeScript_with_a_file() {
let extension = makeExtension({
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const results = await browser.scripting.executeScript({
target: { tabId: tabs[0].id },
files: ["script.js"],
});
browser.test.assertEq(
1,
results.length,
"got expected number of results"
);
browser.test.assertEq(
"value from script.js",
results[0].result,
"got the expected result"
);
browser.test.assertEq(
0,
results[0].frameId,
"got the expected frameId"
);
browser.test.notifyPass("execute-script");
},
files: {
"script.js": function () {
return 'value from script.js';
},
},
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
add_task(async function test_executeScript_in_one_frameId() {
let extension = makeExtension({
manifest: {
permissions: ["scripting", "webNavigation"],
},
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const tabId = tabs[0].id;
const frames = await browser.webNavigation.getAllFrames({ tabId });
// 1. Top-level frame with the MochiTest runner
// 2. Frame for this file
// 3. Frame that loads `file_sample.html` at the top of this file
browser.test.assertEq(3, frames.length, "expected 3 frames");
const fileSampleFrameId = frames[2].frameId;
browser.test.assertTrue(
frames[2].url.includes("file_sample.html"),
"expected frame URL"
);
const results = await browser.scripting.executeScript({
target: { tabId, frameIds: [fileSampleFrameId] },
files: ["script.js"],
});
browser.test.assertEq(
1,
results.length,
"got expected number of results"
);
browser.test.assertEq(
"Sample text",
results[0].result,
"got the expected result"
);
browser.test.assertEq(
fileSampleFrameId,
results[0].frameId,
"got the expected frameId"
);
browser.test.notifyPass("execute-script");
},
files: {
"script.js": function () {
return document.getElementById("test").textContent;
},
},
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
add_task(async function test_executeScript_in_multiple_frameIds() {
let extension = makeExtension({
manifest: {
permissions: ["scripting", "webNavigation"],
},
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const tabId = tabs[0].id;
const frames = await browser.webNavigation.getAllFrames({ tabId });
// 1. Top-level frame that loads `file_contains_iframe.html`
// 2. Frame that loads `file_contains_img.html`
browser.test.assertEq(2, frames.length, "expected 2 frames");
const frameIds = frames.map(frame => frame.frameId);
const getTitle = () => {
return document.title;
};
const results = await browser.scripting.executeScript({
target: { tabId, frameIds },
func: getTitle,
});
browser.test.assertEq(
2,
results.length,
"got expected number of results"
);
// Sort injection results by frameId to always assert the results in the
// same order.
results.sort((a, b) => a.frameId - b.frameId);
browser.test.assertEq(
"file contains iframe",
results[0].result,
"got the expected title in result 0"
);
browser.test.assertEq(
frameIds[0],
results[0].frameId,
"got the expected frameId in result 0"
);
browser.test.assertEq(
"file contains img",
results[1].result,
"got the expected title in result 1"
);
browser.test.assertEq(
frameIds[1],
results[1].frameId,
"got the expected frameId in result 1"
);
browser.test.notifyPass("execute-script");
},
});
let tab = await AppTestDelegate.openNewForegroundTab(
window,
"https://test1.example.com/tests/toolkit/components/extensions/test/mochitest/file_contains_iframe.html",
true
);
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
await AppTestDelegate.removeTab(window, tab);
});
add_task(async function test_executeScript_with_errors_in_multiple_frameIds() {
let extension = makeExtension({
manifest: {
permissions: ["scripting", "webNavigation"],
},
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const tabId = tabs[0].id;
const frames = await browser.webNavigation.getAllFrames({ tabId });
// 1. Top-level frame that loads `file_contains_iframe.html`
// 2. Frame that loads `file_contains_img.html`
browser.test.assertEq(2, frames.length, "expected 2 frames");
const frameIds = frames.map(frame => frame.frameId);
const results = await browser.scripting.executeScript({
target: { tabId, frameIds },
func: () => {
throw new Error(`Thrown at ${location.pathname.split("/").pop()}`);
},
});
browser.test.assertEq(
2,
results.length,
"got expected number of results"
);
browser.test.assertEq(
2,
results.filter(result => result.result === null).length,
"got null results"
);
browser.test.notifyPass("execute-script");
},
});
let tab = await AppTestDelegate.openNewForegroundTab(
window,
"https://test1.example.com/tests/toolkit/components/extensions/test/mochitest/file_contains_iframe.html",
true
);
consoleMonitor.start([
{ message: /Thrown at file_contains_iframe/ },
{ message: /Thrown at file_contains_img/ },
]);
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
await consoleMonitor.finished();
await AppTestDelegate.removeTab(window, tab);
});
add_task(async function test_executeScript_with_multiple_frameIds_and_wrong_host_permissions() {
let extension = makeExtension({
manifest: {
host_permissions: MOCHITEST_HOST_PERMISSIONS,
permissions: ["scripting", "webNavigation"],
},
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const tabId = tabs[0].id;
const frames = await browser.webNavigation.getAllFrames({ tabId });
// 1. Top-level frame with the MochiTest runner
// 2. Frame for this file
// 3. Frame that loads `file_sample.html` at the top of this file
browser.test.assertEq(3, frames.length, "expected 3 frames");
const frameIds = frames.map(frame => frame.frameId);
browser.test.assertRejects(
browser.scripting.executeScript({
target: { tabId, frameIds },
func: () => {},
}),
/Frame not found, or missing host permission/,
"expected error"
);
browser.test.notifyPass("execute-script");
},
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1739643
add_task(async function test_executeScript_with_iframe_srcdoc() {
let iframe = document.createElement("iframe");
iframe.srcdoc = `<!DOCTYPE html>
<html>
<head><title>iframe with srcdoc</title></head>
</html>`;
document.body.appendChild(iframe);
let extension = makeExtension({
manifest: {
permissions: ["scripting", "webNavigation"],
},
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const tabId = tabs[0].id;
const frames = await browser.webNavigation.getAllFrames({ tabId });
// 1. Top-level frame with the MochiTest runner
// 2. Frame for this file
// 3. Frame that loads `file_sample.html` at the top of this file
// 4. Frame that loads the `srcdoc`
browser.test.assertEq(4, frames.length, "expected 4 frames");
const frameIds = frames.map(frame => frame.frameId);
// TODO Bug 1739643: when we support `srcdoc`, `executeScript()` should
// return 4 results and should not reject. It currently rejects because
// it cannot inject into the `srcdoc` frame.
browser.test.assertRejects(
browser.scripting.executeScript({
target: { tabId, frameIds },
func: () => {},
}),
/Frame not found, or missing host permission/,
"expected error"
);
browser.test.notifyPass("execute-script");
},
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
add_task(async function test_executeScript_with_multiple_files() {
let extension = makeExtension({
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const results = await browser.scripting.executeScript({
target: { tabId: tabs[0].id },
files: ["1.js", "2.js"],
});
browser.test.assertEq(
1,
results.length,
"got expected number of results"
);
browser.test.assertEq(
"value from 2.js",
results[0].result,
"got the expected result"
);
browser.test.assertEq(0, results[0].frameId, "got the expected frameId");
browser.test.notifyPass("execute-script");
},
files: {
"1.js": function() {
return "value from 1.js";
},
"2.js": function() {
return "value from 2.js";
},
},
});
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
});
add_task(async function test_executeScript_with_multiple_files_and_an_error() {
let tab = await AppTestDelegate.openNewForegroundTab(
window,
"https://test1.example.com/tests/toolkit/components/extensions/test/mochitest/file_contains_iframe.html",
true
);
let extension = makeExtension({
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
const results = await browser.scripting.executeScript({
target: { tabId: tabs[0].id },
files: ["1.js", "2.js"],
});
browser.test.assertEq(
1,
results.length,
"got expected number of results"
);
browser.test.assertEq(null, results[0].result, "got the expected result");
browser.test.assertEq(0, results[0].frameId, "got the expected frameId");
browser.test.notifyPass("execute-script");
},
files: {
"1.js": function() {
throw new Error(`Thrown at ${location.pathname.split("/").pop()}`);
},
"2.js": function() {
return "value from 2.js";
},
},
});
consoleMonitor.start([{ message: /Thrown at file_contains_iframe/ }]);
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
await consoleMonitor.finished();
await AppTestDelegate.removeTab(window, tab);
});
add_task(async function test_executeScript_with_file_not_in_extension() {
let tab = await AppTestDelegate.openNewForegroundTab(
window,
"https://test1.example.com/tests/toolkit/components/extensions/test/mochitest/file_contains_iframe.html",
true
);
let extension = makeExtension({
async background() {
const tabs = await browser.tabs.query({ active: true });
browser.test.assertEq(1, tabs.length, "expected 1 tab");
await browser.scripting.executeScript({
target: { tabId: tabs[0].id },
files: ["https://example.com/script.js"],
});
browser.test.notifyPass("execute-script");
},
});
consoleMonitor.start([{
message: /Files to be injected must be within the extension/,
}]);
await extension.startup();
await extension.awaitFinish("execute-script");
await extension.unload();
await consoleMonitor.finished();
await AppTestDelegate.removeTab(window, tab);
});
</script>
</body>
</html>