mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 1911835 - Accept USER_SCRIPT as world value in internals r=zombie
This patch adds the USER_SCRIPT value as a supported value in the internal WebExtensionContentScript constructor. This patch does not introduce a distinct USER_SCRIPT sandbox yet; that will be done in the next patch. Differential Revision: https://phabricator.services.mozilla.com/D228973
This commit is contained in:
parent
2ce8ff0bb2
commit
c6451a49d3
@ -165,12 +165,21 @@ enum ContentScriptExecutionWorld {
|
||||
* The name refers to "isolated world", which is a concept from Chromium and
|
||||
* WebKit, used to enforce isolation of the JavaScript execution environments
|
||||
* of content scripts and web pages.
|
||||
*
|
||||
* Not supported when isUserScript=true.
|
||||
*/
|
||||
"ISOLATED",
|
||||
/**
|
||||
* The execution environment of the web page.
|
||||
*/
|
||||
"MAIN",
|
||||
/**
|
||||
* The execution environment of a sandbox running scripts registered through
|
||||
* the MV3 userScripts API.
|
||||
*
|
||||
* Only supported when isUserScript=true.
|
||||
*/
|
||||
"USER_SCRIPT",
|
||||
};
|
||||
|
||||
[ChromeOnly, Exposed=Window]
|
||||
|
@ -771,6 +771,12 @@ WebExtensionContentScript::WebExtensionContentScript(
|
||||
if (mExtension->ManifestVersion() >= 3) {
|
||||
mCheckPermissions = true;
|
||||
}
|
||||
|
||||
// The USER_SCRIPT world is not supported for regular content scripts.
|
||||
MOZ_ASSERT_IF(!mIsUserScript,
|
||||
mWorld != ContentScriptExecutionWorld::USER_SCRIPT);
|
||||
// User scripts should never run in privileged content script worlds.
|
||||
MOZ_ASSERT_IF(mIsUserScript, mWorld != ContentScriptExecutionWorld::ISOLATED);
|
||||
}
|
||||
|
||||
bool MozDocumentMatcher::Matches(const DocInfo& aDoc,
|
||||
|
@ -129,6 +129,11 @@ add_task(async function test_WebExtensionContentScript_isUserScript() {
|
||||
localizeCallback() {},
|
||||
});
|
||||
|
||||
// WebExtensionContentScript defaults to world "ISOLATED", but for user
|
||||
// scripts only "MAIN" and "USER_SCRIPT" worlds are permitted. "MAIN" is
|
||||
// supported by user scripts and non-userScripts, so use that here.
|
||||
const world = "MAIN";
|
||||
|
||||
const matches = new MatchPatternSet(["https://example.com/match/*"]);
|
||||
const includeGlobs = [new MatchGlob("*/glob/*")];
|
||||
const exampleMatchesURI = newURI("https://example.com/match/");
|
||||
@ -137,16 +142,19 @@ add_task(async function test_WebExtensionContentScript_isUserScript() {
|
||||
const exampleNoPermissionURI = newURI("https://example.net/glob/");
|
||||
|
||||
let defaultScript = new WebExtensionContentScript(policy, {
|
||||
world,
|
||||
matches,
|
||||
includeGlobs,
|
||||
});
|
||||
let nonUserScript = new WebExtensionContentScript(policy, {
|
||||
isUserScript: false,
|
||||
world,
|
||||
matches,
|
||||
includeGlobs,
|
||||
});
|
||||
let userScript = new WebExtensionContentScript(policy, {
|
||||
isUserScript: true,
|
||||
world,
|
||||
matches,
|
||||
includeGlobs,
|
||||
});
|
||||
@ -193,11 +201,13 @@ add_task(async function test_WebExtensionContentScript_isUserScript() {
|
||||
// Now verify that empty matches is permitted.
|
||||
let nonUserScriptEmptyMatches = new WebExtensionContentScript(policy, {
|
||||
isUserScript: false,
|
||||
world,
|
||||
matches: [],
|
||||
includeGlobs,
|
||||
});
|
||||
let userScriptEmptyMatches = new WebExtensionContentScript(policy, {
|
||||
isUserScript: true,
|
||||
world,
|
||||
matches: [],
|
||||
includeGlobs,
|
||||
});
|
||||
@ -223,6 +233,7 @@ add_task(async function test_WebExtensionContentScript_isUserScript() {
|
||||
// we just do a sanity check for isUserScript=true.
|
||||
let userScriptNoGlobs = new WebExtensionContentScript(policy, {
|
||||
isUserScript: true,
|
||||
world,
|
||||
matches,
|
||||
});
|
||||
ok(
|
||||
|
@ -147,3 +147,111 @@ add_task(async function userScript_require_host_permissions() {
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
// Tests that user scripts can run in the USER_SCRIPT world, and only for new
|
||||
// document loads (not existing ones).
|
||||
add_task(async function userScript_runs_in_USER_SCRIPT_world() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
useAddonManager: "permanent",
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
permissions: ["userScripts"],
|
||||
host_permissions: ["*://example.com/*"],
|
||||
},
|
||||
files: {
|
||||
"1.file.js": `
|
||||
var resultCollector_push = msg => {
|
||||
// window.wrappedJSObject will be undefined if we unexpectedly run
|
||||
// in the MAIN world instead of the "USER_SCRIPT" world.
|
||||
window.wrappedJSObject.resultCollector.push(msg);
|
||||
};
|
||||
resultCollector_push("1.file");dump("1.file.js ran\\n");
|
||||
`,
|
||||
"3.file.js": "resultCollector_push('3.file');dump('3.file.js ran\\n');",
|
||||
},
|
||||
async background() {
|
||||
async function register() {
|
||||
await browser.userScripts.register([
|
||||
{
|
||||
id: "userScripts ID (for register and update)",
|
||||
matches: ["*://example.com/resultCollector"],
|
||||
js: [
|
||||
{ file: "1.file.js" },
|
||||
// 1.file.js defines the "resultCollector_push" function, and that
|
||||
// function should be available to the other scripts since they all
|
||||
// run in the same USER_SCRIPT world.
|
||||
{ code: "resultCollector_push('2.code');dump('1.code ran\\n');" },
|
||||
{ file: "3.file.js" },
|
||||
{ code: "resultCollector_push('4.code');dump('4.code ran\\n');" },
|
||||
],
|
||||
runAt: "document_end",
|
||||
world: "USER_SCRIPT",
|
||||
},
|
||||
]);
|
||||
}
|
||||
async function update() {
|
||||
await browser.userScripts.update([
|
||||
{
|
||||
id: "userScripts ID (for register and update)",
|
||||
js: [
|
||||
{ file: "1.file.js" },
|
||||
{ code: "resultCollector_push('2.updated');" },
|
||||
],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
browser.test.onMessage.addListener(async msg => {
|
||||
browser.test.assertEq("update_userScripts", msg, "Expected msg");
|
||||
await update();
|
||||
browser.test.sendMessage("update_userScripts:done");
|
||||
});
|
||||
await register();
|
||||
browser.test.sendMessage("registered");
|
||||
},
|
||||
});
|
||||
|
||||
let contentPageBeforeExtStarted = await ExtensionTestUtils.loadContentPage(
|
||||
"http://example.com/resultCollector"
|
||||
);
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitMessage("registered");
|
||||
|
||||
let contentPageAfterRegister = await ExtensionTestUtils.loadContentPage(
|
||||
"http://example.com/resultCollector"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
await collectResults(contentPageAfterRegister),
|
||||
["1.file", "2.code", "3.file", "4.code"],
|
||||
"All USER_SCRIPT world scripts executed in a new page after registration"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
await collectResults(contentPageBeforeExtStarted),
|
||||
[],
|
||||
"User scripts did not execute in content that existed before registration"
|
||||
);
|
||||
|
||||
// Now call userScripts.update() and check whether it injects.
|
||||
extension.sendMessage("update_userScripts");
|
||||
await extension.awaitMessage("update_userScripts:done");
|
||||
|
||||
// Reload - this is a new load after the userScripts.update() call.
|
||||
await contentPageAfterRegister.loadURL("http://example.com/resultCollector");
|
||||
Assert.deepEqual(
|
||||
await collectResults(contentPageAfterRegister),
|
||||
["1.file", "2.updated"],
|
||||
"userScripts.update() applies new scripts to new documents"
|
||||
);
|
||||
|
||||
Assert.deepEqual(
|
||||
await collectResults(contentPageBeforeExtStarted),
|
||||
[],
|
||||
"userScripts.update() does not run code in existing documents"
|
||||
);
|
||||
|
||||
await contentPageAfterRegister.close();
|
||||
await contentPageBeforeExtStarted.close();
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
@ -70,12 +70,6 @@ add_task(async function register_and_restart() {
|
||||
id: "test1",
|
||||
matches: ["https://example.com/*"],
|
||||
js: [{ file: "file1.js" }, { file: "file2.js" }],
|
||||
// We must set this to MAIN because the USER_SCRIPT world is not yet
|
||||
// supported (bug 1911835), and using USER_SCRIPT would prevent the
|
||||
// extension from initializing in the extension process after a restart
|
||||
// due to the WebExtensionContentScript constructor not recognizing the
|
||||
// USER_SCRIPT world value. TODO: delete this after fixing bug 1911835.
|
||||
world: "MAIN",
|
||||
};
|
||||
const expectedScriptOut = {
|
||||
...scriptIn,
|
||||
@ -84,7 +78,10 @@ add_task(async function register_and_restart() {
|
||||
includeGlobs: null,
|
||||
excludeGlobs: null,
|
||||
runAt: "document_idle",
|
||||
// world: "USER_SCRIPT", // TODO 1911835: uncomment when supported.
|
||||
// Notable call-out: All other content script APIs default to "ISOLATED"
|
||||
// as the default world. In the userScripts API, we want to default to
|
||||
// "USER_SCRIPT" when the world is not specified.
|
||||
world: "USER_SCRIPT",
|
||||
};
|
||||
browser.runtime.onInstalled.addListener(async ({ reason }) => {
|
||||
browser.test.assertEq("install", reason, "onInstalled reason");
|
||||
|
Loading…
Reference in New Issue
Block a user