Bug 1454820 - Add support for system addon signing for web extension bundling. r=johannh,kmag
☠☠ backed out by 04e9bfc67922 ☠ ☠
authorJonathan Kingston <jkt@mozilla.com>
Wed, 18 Apr 2018 00:43:39 +0100
changeset 469626 5e8342edb62d87e2d8a3f2f20a13edc11526581d
parent 469625 c6a702c01ca6f56f21129b622f9dbbbbf6adf1e6
child 469627 31104079416b1f4a0db7cb08505000314fa8062f
push id9174
push userarchaeopteryx@coole-files.de
push dateMon, 30 Apr 2018 15:33:30 +0000
treeherdermozilla-beta@1b1a8ab75f1f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh, kmag
bugs1454820
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1454820 - Add support for system addon signing for web extension bundling. r=johannh,kmag MozReview-Commit-ID: 3dpQKGHOgLa
accessible/tests/mochitest/elm/test_HTMLSpec.html
editor/libeditor/HTMLEditUtils.cpp
testing/web-platform/meta/html/syntax/parsing/template/creating-an-element-for-the-token/template-owner-document.html.ini
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ExtensionTestCommon.jsm
toolkit/components/extensions/test/xpcshell/test_ext_experiments.js
toolkit/components/extensions/test/xpcshell/test_ext_schemas_interactive.js
toolkit/mozapps/extensions/internal/XPIProvider.jsm
--- a/accessible/tests/mochitest/elm/test_HTMLSpec.html
+++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html
@@ -903,33 +903,16 @@
         children: [
           { role: ROLE_TEXT_LEAF }, // plain text
           { role: ROLE_TEXT_LEAF } // HTML:kbd text
         ]
       };
       testElm("kbd_container", obj);
 
       // ////////////////////////////////////////////////////////////////////////
-      // HTML:keygen
-
-      obj = {
-        role: ROLE_COMBOBOX,
-        states: STATE_COLLAPSED | STATE_HASPOPUP,
-        extraStates: EXT_STATE_EXPANDABLE,
-        actions: "open",
-        children: [
-          { COMBOBOX_LIST: [
-            { role: ROLE_COMBOBOX_OPTION }, // high grade
-            { role: ROLE_COMBOBOX_OPTION } // medium grade
-          ] }
-        ]
-      };
-      testElm("keygen", obj);
-
-      // ////////////////////////////////////////////////////////////////////////
       // HTML:label
 
       obj = {
         role: ROLE_LABEL,
         todo_relations: {
           RELATION_LABEL_FOR: "label_input"
         },
         children: [
--- a/editor/libeditor/HTMLEditUtils.cpp
+++ b/editor/libeditor/HTMLEditUtils.cpp
@@ -430,17 +430,16 @@ HTMLEditUtils::IsFormWidget(nsIDOMNode* 
 bool
 HTMLEditUtils::IsFormWidget(nsINode* aNode)
 {
   MOZ_ASSERT(aNode);
   return aNode->IsAnyOfHTMLElements(nsGkAtoms::textarea,
                                     nsGkAtoms::select,
                                     nsGkAtoms::button,
                                     nsGkAtoms::output,
-                                    nsGkAtoms::keygen,
                                     nsGkAtoms::progress,
                                     nsGkAtoms::meter,
                                     nsGkAtoms::input);
 }
 
 bool
 HTMLEditUtils::SupportsAlignAttr(nsINode& aNode)
 {
@@ -655,17 +654,16 @@ static const ElementInfo kElements[eHTML
   ELEM(html, true, false, GROUP_TOPLEVEL, GROUP_TOPLEVEL),
   ELEM(i, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   ELEM(iframe, true, true, GROUP_SPECIAL | GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(image, false, false, GROUP_NONE, GROUP_NONE),
   ELEM(img, false, false, GROUP_SPECIAL | GROUP_PICTURE_CONTENT, GROUP_NONE),
   ELEM(input, false, false, GROUP_FORMCONTROL, GROUP_NONE),
   ELEM(ins, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(kbd, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
-  ELEM(keygen, false, false, GROUP_FORMCONTROL, GROUP_NONE),
   ELEM(label, true, false, GROUP_FORMCONTROL, GROUP_INLINE_ELEMENT),
   ELEM(legend, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT),
   ELEM(li, true, false, GROUP_LI, GROUP_FLOW_ELEMENT),
   ELEM(link, false, false, GROUP_HEAD_CONTENT, GROUP_NONE),
   ELEM(listing, false, false, GROUP_NONE, GROUP_NONE),
   ELEM(main, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(map, true, true, GROUP_SPECIAL, GROUP_BLOCK | GROUP_MAP_CONTENT),
   ELEM(mark, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
--- a/testing/web-platform/meta/html/syntax/parsing/template/creating-an-element-for-the-token/template-owner-document.html.ini
+++ b/testing/web-platform/meta/html/syntax/parsing/template/creating-an-element-for-the-token/template-owner-document.html.ini
@@ -1,10 +1,4 @@
 [template-owner-document.html]
   [Test ownerDocument property of the element in a template. Current DOCUMENT has no browsing context. Test template element in the root of the frameset]
     expected: FAIL
 
-  [Test ownerDocument for the element keygen in the template]
-    expected: FAIL
-
-  [Test ownerDocument for the element keygen in the template. Document has browsing context]
-    expected: FAIL
-
--- 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) {