Bug 1454820 - Add support for system addon signing for web extension bundling. r?kmag r?johannh
MozReview-Commit-ID: 3dpQKGHOgLa
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -501,16 +501,20 @@ class ExtensionData {
static comparePermissions(oldPermissions, newPermissions) {
let oldMatcher = new MatchPatternSet(oldPermissions.origins);
return {
origins: newPermissions.origins.filter(perm => !oldMatcher.subsumes(new MatchPattern(perm))),
permissions: newPermissions.permissions.filter(perm => !oldPermissions.permissions.includes(perm)),
};
}
+ canUseExperiment(manifest) {
+ return this.experimentsAllowed && manifest.experiment_apis;
+ }
+
async parseManifest() {
let [manifest] = await Promise.all([
this.readJSON("manifest.json"),
Management.lazyInit(),
]);
this.manifest = manifest;
this.rawManifest = manifest;
@@ -640,17 +644,17 @@ class ExtensionData {
scopes: data.scopes,
});
let computeModuleInit = (scope, modules) => {
let manager = new ExtensionCommon.SchemaAPIManager(scope);
return manager.initModuleJSON([modules]);
};
- if (manifest.experiment_apis) {
+ if (this.canUseExperiment(manifest)) {
let parentModules = {};
let childModules = {};
for (let [name, data] of Object.entries(manifest.experiment_apis)) {
let schema = this.getURL(data.schema);
if (!schemaPromises.has(schema)) {
schemaPromises.set(schema, this.readJSON(data.schema).then(json => Schemas.processSchema(json)));
@@ -1399,16 +1403,18 @@ class Extension extends ExtensionData {
}
get manifestCacheKey() {
return [this.id, this.version, Services.locale.getAppLocaleAsLangTag()];
}
get isPrivileged() {
return (this.addonData.signedState === AddonManager.SIGNEDSTATE_PRIVILEGED ||
+ this.addonData.signedState === AddonManager.SIGNEDSTATE_SYSTEM ||
+ this.addonData.builtIn ||
(AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS &&
this.addonData.temporarilyInstalled));
}
get experimentsAllowed() {
return (AddonSettings.ALLOW_LEGACY_EXTENSIONS ||
this.isPrivileged);
}
--- a/toolkit/components/extensions/ExtensionTestCommon.jsm
+++ b/toolkit/components/extensions/ExtensionTestCommon.jsm
@@ -368,18 +368,25 @@ var ExtensionTestCommon = class Extensio
} else if (data.manifest.browser_specific_settings && data.manifest.browser_specific_settings.gecko) {
id = data.manifest.browser_specific_settings.gecko.id;
}
}
if (!id) {
id = uuidGen.generateUUID().number;
}
+ let signedState = AddonManager.SIGNEDSTATE_SIGNED;
+ if (data.isPrivileged) {
+ signedState = AddonManager.SIGNEDSTATE_PRIVILEGED;
+ }
+ if (data.isSystem) {
+ signedState = AddonManager.SIGNEDSTATE_SYSTEM;
+ }
+
return new Extension({
id,
resourceURI: jarURI,
cleanupFile: file,
- signedState: data.isPrivileged ? AddonManager.SIGNEDSTATE_PRIVILEGED
- : AddonManager.SIGNEDSTATE_SIGNED,
+ signedState,
temporarilyInstalled: !!data.temporarilyInstalled,
});
}
};
--- a/toolkit/components/extensions/test/xpcshell/test_ext_experiments.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_experiments.js
@@ -102,46 +102,71 @@ async function testFooExperiment() {
browser.test.assertEq("child", browser.experiments.foo.child(),
"foo.child()");
browser.test.assertEq("parent", await browser.experiments.foo.parent(),
"await foo.parent()");
}
+async function testFooFailExperiment() {
+ browser.test.assertEq("object", typeof browser.experiments,
+ "typeof browser.experiments");
+
+ browser.test.assertEq("undefined", typeof browser.experiments.foo,
+ "typeof browser.experiments.foo");
+}
+
add_task(async function test_bundled_experiments() {
- async function background() {
- await testFooExperiment();
+ let testCases = [
+ {isSystem: true, temporarilyInstalled: true, shouldHaveExperiments: true},
+ {isSystem: true, temporarilyInstalled: false, shouldHaveExperiments: true},
+ {isPrivileged: true, temporarilyInstalled: true, shouldHaveExperiments: true},
+ {isPrivileged: true, temporarilyInstalled: false, shouldHaveExperiments: true},
+ {isPrivileged: false, temporarilyInstalled: true, shouldHaveExperiments: true},
+ {isPrivileged: false, temporarilyInstalled: false, shouldHaveExperiments: false},
+ ];
+
+ async function background(shouldHaveExperiments) {
+ if (shouldHaveExperiments) {
+ await testFooExperiment();
+ } else {
+ await testFooFailExperiment();
+ }
browser.test.notifyPass("background.experiments.foo");
}
- let extension = ExtensionTestUtils.loadExtension({
- isPrivileged: true,
+ for (let testCase of testCases) {
+ let extension = ExtensionTestUtils.loadExtension({
+ isPrivileged: testCase.isPrivileged,
+ isSystem: testCase.isSystem,
+ temporarilyInstalled: testCase.temporarilyInstalled,
- manifest: {
- experiment_apis: fooExperimentAPIs,
- },
+ manifest: {
+ experiment_apis: fooExperimentAPIs,
+ },
- background: `
- ${testFooExperiment}
- (${background})();
- `,
-
- files: fooExperimentFiles,
- });
+ background: `
+ ${testFooExperiment}
+ ${testFooFailExperiment}
+ (${background})(${testCase.shouldHaveExperiments});
+ `,
- await extension.startup();
+ files: fooExperimentFiles,
+ });
- await extension.awaitFinish("background.experiments.foo");
+ await extension.startup();
- await extension.unload();
+ await extension.awaitFinish("background.experiments.foo");
+
+ await extension.unload();
+ }
});
-
add_task(async function test_unbundled_experiments() {
async function background() {
await testFooExperiment();
browser.test.assertEq("object", typeof browser.experiments.crunk,
"typeof browser.experiments.crunk");
browser.test.assertEq("function", typeof browser.experiments.crunk.child,
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_interactive.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_interactive.js
@@ -86,16 +86,17 @@ function setHandlingUserInput(extension)
.getInterface(Ci.nsIDOMWindowUtils);
return winutils.setHandlingUserInput(true);
}
// Test that the schema requireUserInput flag works correctly for
// proxied api implementations.
add_task(async function test_proxy() {
let extension = ExtensionTestUtils.loadExtension({
+ isPrivileged: true,
background() {
browser.test.onMessage.addListener(async () => {
try {
await browser.userinputtest.test();
browser.test.sendMessage("result", null);
} catch (err) {
browser.test.sendMessage("result", err.message);
}
@@ -123,16 +124,17 @@ add_task(async function test_proxy() {
await extension.unload();
});
// Test that the schema requireUserInput flag works correctly for
// non-proxied api implementations.
add_task(async function test_local() {
let extension = ExtensionTestUtils.loadExtension({
+ isPrivileged: true,
background() {
browser.test.onMessage.addListener(async () => {
try {
await browser.userinputtest.child();
browser.test.sendMessage("result", null);
} catch (err) {
browser.test.sendMessage("result", err.message);
}
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -2709,16 +2709,17 @@ var XPIProvider = {
let installLocation = aAddon._installLocation || null;
let params = {
id: aAddon.id,
version: aAddon.version,
installPath: aFile.clone(),
resourceURI: getURIForResourceInFile(aFile, ""),
signedState: aAddon.signedState,
temporarilyInstalled: installLocation == TemporaryInstallLocation,
+ builtIn: installLocation instanceof BuiltInInstallLocation,
};
if (aMethod == "startup" && aAddon.startupData) {
params.startupData = aAddon.startupData;
}
if (aExtraParams) {
for (let key in aExtraParams) {