forked from mirrors/gecko-dev
Bug 1811443 - Support optional background.type "module" for WebExtensions Event Page scripts. r=willdurand,robwu
Differential Revision: https://phabricator.services.mozilla.com/D169922
This commit is contained in:
parent
58c4c9c0ff
commit
94cb12435e
8 changed files with 169 additions and 3 deletions
|
|
@ -316,5 +316,8 @@ dictionary WebExtensionInit {
|
||||||
sequence<DOMString>? backgroundScripts = null;
|
sequence<DOMString>? backgroundScripts = null;
|
||||||
DOMString? backgroundWorkerScript = null;
|
DOMString? backgroundWorkerScript = null;
|
||||||
|
|
||||||
|
// Whether the background scripts should be loaded as ES modules.
|
||||||
|
boolean backgroundTypeModule = false;
|
||||||
|
|
||||||
Promise<WebExtensionPolicy?> readyPromise;
|
Promise<WebExtensionPolicy?> readyPromise;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2760,6 +2760,10 @@ class Extension extends ExtensionData {
|
||||||
return this.manifest.background?.scripts;
|
return this.manifest.background?.scripts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get backgroundTypeModule() {
|
||||||
|
return this.manifest.background?.type === "module";
|
||||||
|
}
|
||||||
|
|
||||||
get backgroundWorkerScript() {
|
get backgroundWorkerScript() {
|
||||||
return this.manifest.background?.service_worker;
|
return this.manifest.background?.service_worker;
|
||||||
}
|
}
|
||||||
|
|
@ -2828,6 +2832,7 @@ class Extension extends ExtensionData {
|
||||||
return {
|
return {
|
||||||
backgroundScripts: this.backgroundScripts,
|
backgroundScripts: this.backgroundScripts,
|
||||||
backgroundWorkerScript: this.backgroundWorkerScript,
|
backgroundWorkerScript: this.backgroundWorkerScript,
|
||||||
|
backgroundTypeModule: this.backgroundTypeModule,
|
||||||
childModules: this.modules && this.modules.child,
|
childModules: this.modules && this.modules.child,
|
||||||
dependencies: this.dependencies,
|
dependencies: this.dependencies,
|
||||||
persistentBackground: this.persistentBackground,
|
persistentBackground: this.persistentBackground,
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,14 @@ ExtensionManager = {
|
||||||
({ backgroundWorkerScript } = getData(extension, "extendedData") || {});
|
({ backgroundWorkerScript } = getData(extension, "extendedData") || {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let { backgroundTypeModule } = extension;
|
||||||
|
if (
|
||||||
|
backgroundTypeModule == null &&
|
||||||
|
WebExtensionPolicy.isExtensionProcess
|
||||||
|
) {
|
||||||
|
({ backgroundTypeModule } = getData(extension, "extendedData") || {});
|
||||||
|
}
|
||||||
|
|
||||||
policy = new WebExtensionPolicy({
|
policy = new WebExtensionPolicy({
|
||||||
id: extension.id,
|
id: extension.id,
|
||||||
mozExtensionHostname: extension.uuid,
|
mozExtensionHostname: extension.uuid,
|
||||||
|
|
@ -162,6 +170,7 @@ ExtensionManager = {
|
||||||
|
|
||||||
backgroundScripts,
|
backgroundScripts,
|
||||||
backgroundWorkerScript,
|
backgroundWorkerScript,
|
||||||
|
backgroundTypeModule,
|
||||||
|
|
||||||
contentScripts: extension.contentScripts,
|
contentScripts: extension.contentScripts,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@ using namespace dom;
|
||||||
|
|
||||||
static const char kProto[] = "moz-extension";
|
static const char kProto[] = "moz-extension";
|
||||||
|
|
||||||
|
static const char kBackgroundScriptTypeDefault[] = "text/javascript";
|
||||||
|
|
||||||
|
static const char kBackgroundScriptTypeModule[] = "module";
|
||||||
|
|
||||||
static const char kBackgroundPageHTMLStart[] =
|
static const char kBackgroundPageHTMLStart[] =
|
||||||
"<!DOCTYPE html>\n\
|
"<!DOCTYPE html>\n\
|
||||||
<html>\n\
|
<html>\n\
|
||||||
|
|
@ -38,7 +42,7 @@ static const char kBackgroundPageHTMLStart[] =
|
||||||
|
|
||||||
static const char kBackgroundPageHTMLScript[] =
|
static const char kBackgroundPageHTMLScript[] =
|
||||||
"\n\
|
"\n\
|
||||||
<script type=\"text/javascript\" src=\"%s\"></script>";
|
<script type=\"%s\" src=\"%s\"></script>";
|
||||||
|
|
||||||
static const char kBackgroundPageHTMLEnd[] =
|
static const char kBackgroundPageHTMLEnd[] =
|
||||||
"\n\
|
"\n\
|
||||||
|
|
@ -297,6 +301,8 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
|
||||||
aInit.mBackgroundScripts.Value());
|
aInit.mBackgroundScripts.Value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mBackgroundTypeModule = aInit.mBackgroundTypeModule;
|
||||||
|
|
||||||
mContentScripts.SetCapacity(aInit.mContentScripts.Length());
|
mContentScripts.SetCapacity(aInit.mContentScripts.Length());
|
||||||
for (const auto& scriptInit : aInit.mContentScripts) {
|
for (const auto& scriptInit : aInit.mContentScripts) {
|
||||||
// The activeTab permission is only for dynamically injected scripts,
|
// The activeTab permission is only for dynamically injected scripts,
|
||||||
|
|
@ -525,11 +531,13 @@ nsCString WebExtensionPolicy::BackgroundPageHTML() const {
|
||||||
|
|
||||||
result.AppendLiteral(kBackgroundPageHTMLStart);
|
result.AppendLiteral(kBackgroundPageHTMLStart);
|
||||||
|
|
||||||
|
const char* scriptType = mBackgroundTypeModule ? kBackgroundScriptTypeModule
|
||||||
|
: kBackgroundScriptTypeDefault;
|
||||||
|
|
||||||
for (auto& script : mBackgroundScripts.Value()) {
|
for (auto& script : mBackgroundScripts.Value()) {
|
||||||
nsCString escaped;
|
nsCString escaped;
|
||||||
nsAppendEscapedHTML(NS_ConvertUTF16toUTF8(script), escaped);
|
nsAppendEscapedHTML(NS_ConvertUTF16toUTF8(script), escaped);
|
||||||
|
result.AppendPrintf(kBackgroundPageHTMLScript, scriptType, escaped.get());
|
||||||
result.AppendPrintf(kBackgroundPageHTMLScript, escaped.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.AppendLiteral(kBackgroundPageHTMLEnd);
|
result.AppendLiteral(kBackgroundPageHTMLEnd);
|
||||||
|
|
|
||||||
|
|
@ -368,6 +368,8 @@ class WebExtensionPolicy final : public nsISupports, public nsWrapperCache {
|
||||||
|
|
||||||
dom::Nullable<nsTArray<nsString>> mBackgroundScripts;
|
dom::Nullable<nsTArray<nsString>> mBackgroundScripts;
|
||||||
|
|
||||||
|
bool mBackgroundTypeModule = false;
|
||||||
|
|
||||||
nsTArray<RefPtr<WebExtensionContentScript>> mContentScripts;
|
nsTArray<RefPtr<WebExtensionContentScript>> mContentScripts;
|
||||||
|
|
||||||
RefPtr<dom::Promise> mReadyPromise;
|
RefPtr<dom::Promise> mReadyPromise;
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,11 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "$ref": "ExtensionURL" }
|
"items": { "$ref": "ExtensionURL" }
|
||||||
},
|
},
|
||||||
|
"type": {
|
||||||
|
"optional": true,
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["module", "classic"]
|
||||||
|
},
|
||||||
"persistent": {
|
"persistent": {
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
async function assertBackgroundScriptTypes(
|
||||||
|
extensionTestWrapper,
|
||||||
|
expectedScriptTypesMap
|
||||||
|
) {
|
||||||
|
const { baseURI } = extensionTestWrapper.extension;
|
||||||
|
let expectedMapWithResolvedURLs = Object.keys(expectedScriptTypesMap).reduce(
|
||||||
|
(result, scriptPath) => {
|
||||||
|
result[baseURI.resolve(scriptPath)] = expectedScriptTypesMap[scriptPath];
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const page = await ExtensionTestUtils.loadContentPage(
|
||||||
|
baseURI.resolve("_generated_background_page.html")
|
||||||
|
);
|
||||||
|
const scriptTypesMap = await page.spawn([], () => {
|
||||||
|
const scripts = Array.from(
|
||||||
|
this.content.document.querySelectorAll("script")
|
||||||
|
);
|
||||||
|
return scripts.reduce((result, script) => {
|
||||||
|
result[script.getAttribute("src")] = script.getAttribute("type");
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
});
|
||||||
|
await page.close();
|
||||||
|
Assert.deepEqual(
|
||||||
|
scriptTypesMap,
|
||||||
|
expectedMapWithResolvedURLs,
|
||||||
|
"Got the expected script type from the generated background page"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testBackgroundScriptClassic({ manifestTypeClassicSet }) {
|
||||||
|
const extension = ExtensionTestUtils.loadExtension({
|
||||||
|
manifest: {
|
||||||
|
background: {
|
||||||
|
scripts: ["anotherScript.js", "main.js"],
|
||||||
|
type: manifestTypeClassicSet ? "classic" : undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
"main.js": ``,
|
||||||
|
"anotherScript.js": ``,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await extension.startup();
|
||||||
|
await assertBackgroundScriptTypes(extension, {
|
||||||
|
"main.js": "text/javascript",
|
||||||
|
"anotherScript.js": "text/javascript",
|
||||||
|
});
|
||||||
|
await extension.unload();
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function test_background_scripts_type_default() {
|
||||||
|
await testBackgroundScriptClassic({ manifestTypeClassicSet: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_background_scripts_type_classic() {
|
||||||
|
await testBackgroundScriptClassic({ manifestTypeClassicSet: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_background_scripts_type_module() {
|
||||||
|
const extension = ExtensionTestUtils.loadExtension({
|
||||||
|
manifest: {
|
||||||
|
background: {
|
||||||
|
scripts: ["anotherModule.js", "mainModule.js"],
|
||||||
|
type: "module",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
"mainModule.js": `
|
||||||
|
import { initBackground } from "/importedModule.js";
|
||||||
|
browser.test.log("mainModule.js - ESM module executing");
|
||||||
|
initBackground();
|
||||||
|
`,
|
||||||
|
"importedModule.js": `
|
||||||
|
export function initBackground() {
|
||||||
|
browser.test.onMessage.addListener((msg) => {
|
||||||
|
browser.test.log("importedModule.js - test message received");
|
||||||
|
browser.test.sendMessage("esm-module-reply", msg);
|
||||||
|
});
|
||||||
|
browser.test.log("importedModule.js - initBackground executed");
|
||||||
|
}
|
||||||
|
browser.test.log("importedModule.js - ESM module loaded");
|
||||||
|
`,
|
||||||
|
"anotherModule.js": `
|
||||||
|
browser.test.log("anotherModule.js - ESM module loaded");
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await extension.startup();
|
||||||
|
await extension.sendMessage("test-event-value");
|
||||||
|
equal(
|
||||||
|
await extension.awaitMessage("esm-module-reply"),
|
||||||
|
"test-event-value",
|
||||||
|
"Got the expected event from the ESM module loaded from the background script"
|
||||||
|
);
|
||||||
|
await assertBackgroundScriptTypes(extension, {
|
||||||
|
"mainModule.js": "module",
|
||||||
|
"anotherModule.js": "module",
|
||||||
|
});
|
||||||
|
await extension.unload();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_background_scripts_type_invalid() {
|
||||||
|
const extension = ExtensionTestUtils.loadExtension({
|
||||||
|
manifest: {
|
||||||
|
background: {
|
||||||
|
scripts: ["anotherScript.js", "main.js"],
|
||||||
|
type: "invalid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
"main.js": ``,
|
||||||
|
"anotherScript.js": ``,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
ExtensionTestUtils.failOnSchemaWarnings(false);
|
||||||
|
await Assert.rejects(
|
||||||
|
extension.startup(),
|
||||||
|
/Error processing background: .* \.type must be one of/,
|
||||||
|
"Expected install to fail"
|
||||||
|
);
|
||||||
|
ExtensionTestUtils.failOnSchemaWarnings(true);
|
||||||
|
});
|
||||||
|
|
@ -28,6 +28,7 @@ skip-if = os == "android" # Android does not use Places for history.
|
||||||
[test_ext_background_sub_windows.js]
|
[test_ext_background_sub_windows.js]
|
||||||
[test_ext_background_teardown.js]
|
[test_ext_background_teardown.js]
|
||||||
[test_ext_background_telemetry.js]
|
[test_ext_background_telemetry.js]
|
||||||
|
[test_ext_background_type_module.js]
|
||||||
[test_ext_background_window_properties.js]
|
[test_ext_background_window_properties.js]
|
||||||
skip-if = os == "android"
|
skip-if = os == "android"
|
||||||
[test_ext_browserSettings.js]
|
[test_ext_browserSettings.js]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue