Bug 1284407 Generate addon id from the path for temporarily loaded xpis r=rhelmer
authorAndrew Swan <aswan@mozilla.com>
Mon, 12 Sep 2016 16:13:42 -0700
changeset 357338 58846175232c221d1d6eccfdd9e26c1d41044f98
parent 357337 0123be9d2c87873e5d32bfd4ba517efdb4f38e6f
child 357339 1599a90dcf2a052cd6f5493548b6ba0b7848c8f5
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrhelmer
bugs1284407
milestone51.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 1284407 Generate addon id from the path for temporarily loaded xpis r=rhelmer MozReview-Commit-ID: 4XChacM8xE0
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -1295,16 +1295,31 @@ function defineSyncGUID(aAddon) {
       delete aAddon.syncGUID;
       aAddon.syncGUID = val;
     },
     configurable: true,
     enumerable: true,
   });
 }
 
+// Generate a unique ID based on the path to this temporary add-on location.
+function generateTemporaryInstallID(aFile) {
+  const hasher = Cc["@mozilla.org/security/hash;1"]
+        .createInstance(Ci.nsICryptoHash);
+  hasher.init(hasher.SHA1);
+  const data = new TextEncoder().encode(aFile.path);
+  // Make it so this ID cannot be guessed.
+  const sess = TEMP_INSTALL_ID_GEN_SESSION;
+  hasher.update(sess, sess.length);
+  hasher.update(data, data.length);
+  let id = `${getHashStringForCrypto(hasher)}@temporary-addon`;
+  logger.info(`Generated temp id ${id} (${sess.join("")}) for ${aFile.path}`);
+  return id;
+}
+
 /**
  * Loads an AddonInternal object from an add-on extracted in a directory.
  *
  * @param  aDir
  *         The nsIFile directory holding the add-on
  * @return an AddonInternal object
  * @throws if the directory does not contain a valid install manifest
  */
@@ -1370,29 +1385,17 @@ var loadManifestFromDir = Task.async(fun
 
   let uri = Services.io.newFileURI(file).QueryInterface(Ci.nsIFileURL);
 
   let addon;
   if (file.leafName == FILE_WEB_MANIFEST) {
     addon = yield loadManifestFromWebManifest(uri);
     if (!addon.id) {
       if (aInstallLocation == TemporaryInstallLocation) {
-        // Generate a unique ID based on the directory path of
-        // this temporary add-on location.
-        const hasher = Cc["@mozilla.org/security/hash;1"]
-          .createInstance(Ci.nsICryptoHash);
-        hasher.init(hasher.SHA1);
-        const data = new TextEncoder().encode(aDir.path);
-        // Make it so this ID cannot be guessed.
-        const sess = TEMP_INSTALL_ID_GEN_SESSION;
-        hasher.update(sess, sess.length);
-        hasher.update(data, data.length);
-        addon.id = `${getHashStringForCrypto(hasher)}@temporary-addon`;
-        logger.info(
-          `Generated temp id ${addon.id} (${sess.join("")}) for ${aDir.path}`);
+        addon.id = generateTemporaryInstallID(aDir);
       } else {
         addon.id = aDir.leafName;
       }
     }
   } else {
     addon = loadFromRDF(uri);
   }
 
@@ -1470,20 +1473,25 @@ var loadManifestFromZipReader = Task.asy
 
   addon.size = 0;
   let entries = aZipReader.findEntries(null);
   while (entries.hasMore())
     addon.size += aZipReader.getEntry(entries.getNext()).realSize;
 
   let {signedState, cert} = yield verifyZipSignedState(aZipReader.file, addon);
   addon.signedState = signedState;
-  if (isWebExtension && !addon.id && cert) {
-    addon.id = cert.commonName;
-    if (!gIDTest.test(addon.id)) {
-      throw new Error(`Webextension is signed with an invalid id (${addon.id})`);
+  if (isWebExtension && !addon.id) {
+    if (cert) {
+      addon.id = cert.commonName;
+      if (!gIDTest.test(addon.id)) {
+        throw new Error(`Webextension is signed with an invalid id (${addon.id})`);
+      }
+    }
+    if (!addon.id && aInstallLocation == TemporaryInstallLocation) {
+      addon.id = generateTemporaryInstallID(aZipReader.file);
     }
   }
   addon.appDisabled = !isUsableAddon(addon);
 
   defineSyncGUID(addon);
 
   return addon;
 });
@@ -4814,18 +4822,17 @@ this.XPIProvider = {
    * @param  aReason
    *         The reason flag to pass to the bootstrap's startup method
    * @param  aExtraParams
    *         An object of additional key/value pairs to pass to the method in
    *         the params argument
    */
   callBootstrapMethod: function(aAddon, aFile, aMethod, aReason, aExtraParams) {
     if (!aAddon.id || !aAddon.version || !aAddon.type) {
-      logger.error(new Error("aAddon must include an id, version, and type"));
-      return;
+      throw new Error("aAddon must include an id, version, and type");
     }
 
     // Only run in safe mode if allowed to
     let runInSafeMode = "runInSafeMode" in aAddon ? aAddon.runInSafeMode : canRunInSafeMode(aAddon);
     if (Services.appinfo.inSafeMode && !runInSafeMode)
       return;
 
     let timeStart = new Date();
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
@@ -64,20 +64,17 @@ add_task(function* test_implicit_id_temp
         "SourceURI of the add-on has the expected value");
 
   addon.uninstall();
 });
 
 // We should be able to temporarily install an unsigned web extension
 // that does not have an ID in its manifest.
 add_task(function* test_unsigned_no_id_temp_install() {
-  if (!TEST_UNPACKED) {
-    do_print("This test does not apply when using packed extensions");
-    return;
-  }
+  AddonTestUtils.useRealCertChecks = true;
   const manifest = {
     name: "no ID",
     description: "extension without an ID",
     manifest_version: 2,
     version: "1.0"
   };
 
   const addonDir = yield promiseWriteWebManifestForExtension(manifest, gTmpD,
@@ -92,26 +89,25 @@ add_task(function* test_unsigned_no_id_t
         "SourceURI of the add-on has the expected value");
 
   // Install the same directory again, as if re-installing or reloading.
   const secondAddon = yield AddonManager.installTemporaryAddon(addonDir);
   // The IDs should be the same.
   equal(secondAddon.id, addon.id, "Reinstalled add-on has the expected ID");
 
   secondAddon.uninstall();
+  Services.obs.notifyObservers(addonDir, "flush-cache-entry", null);
   addonDir.remove(true);
+  AddonTestUtils.useRealCertChecks = false;
 });
 
 // We should be able to install two extensions from manifests without IDs
 // at different locations and get two unique extensions.
 add_task(function* test_multiple_no_id_extensions() {
-  if (!TEST_UNPACKED) {
-    do_print("This test does not apply when using packed extensions");
-    return;
-  }
+  AddonTestUtils.useRealCertChecks = true;
   const manifest = {
     name: "no ID",
     description: "extension without an ID",
     manifest_version: 2,
     version: "1.0"
   };
 
   const firstAddonDir = yield promiseWriteWebManifestForExtension(manifest, gTmpD,
@@ -127,19 +123,22 @@ add_task(function* test_multiple_no_id_e
     AddonManager.getAllAddons(addons => resolve(addons));
   });
   do_print(`Found these add-ons: ${allAddons.map(a => a.name).join(", ")}`);
   const filtered = allAddons.filter(addon => addon.name === manifest.name);
   // Make sure we have two add-ons by the same name.
   equal(filtered.length, 2, "Two add-ons are installed with the same name");
 
   firstAddon.uninstall();
+  Services.obs.notifyObservers(firstAddonDir, "flush-cache-entry", null);
   firstAddonDir.remove(true);
   secondAddon.uninstall();
+  Services.obs.notifyObservers(secondAddonDir, "flush-cache-entry", null);
   secondAddonDir.remove(true);
+  AddonTestUtils.useRealCertChecks = false;
 });
 
 // Test that we can get the ID from browser_specific_settings
 add_task(function* test_bss_id() {
   const ID = "webext_bss_id@tests.mozilla.org";
 
   let manifest = {
     name: "bss test",