Bug 1330349 - Part 2 - install and present theme type WebExtensions as themes in the Addon Manager. r?Mossop draft
authorMike de Boer <mdeboer@mozilla.com>
Thu, 02 Mar 2017 14:22:17 +0100
changeset 491924 81d730d4ede3c934b256ab3c8d1f353c6001d3d6
parent 491923 a52ca6aba2df5c2e6d6433a1007ee669297c0447
child 491925 9d7554d6ea4faadfb42ae2dc140613d253a9bbc6
push id47458
push usermdeboer@mozilla.com
push dateThu, 02 Mar 2017 13:47:01 +0000
reviewersMossop
bugs1330349
milestone54.0a1
Bug 1330349 - Part 2 - install and present theme type WebExtensions as themes in the Addon Manager. r?Mossop MozReview-Commit-ID: 5J9BDekC7dx
toolkit/mozapps/extensions/internal/E10SAddonsRollout.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
--- a/toolkit/mozapps/extensions/internal/E10SAddonsRollout.jsm
+++ b/toolkit/mozapps/extensions/internal/E10SAddonsRollout.jsm
@@ -946,17 +946,17 @@ Object.defineProperty(this, "isAddonPart
     if (aAddon.mpcOptedOut == true) {
       return false;
     }
 
     if (policy.alladdons) {
       return true;
     }
 
-    if (policy.webextensions && aAddon.type == "webextension") {
+    if (policy.webextensions && (aAddon.type == "webextension" || aAddon.type == "webextension-theme")) {
       return true;
     }
 
     if (policy.mpc && aAddon.multiprocessCompatible) {
       return true;
     }
 
     if (policy.addonsv2) {
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -237,39 +237,42 @@ const TYPES = {
 };
 
 if (!AppConstants.RELEASE_OR_BETA)
   TYPES.apiextension = 256;
 
 // Some add-on types that we track internally are presented as other types
 // externally
 const TYPE_ALIASES = {
+  "apiextension": "extension",
   "webextension": "extension",
-  "apiextension": "extension",
+  "webextension-theme": "theme",
 };
 
 const CHROME_TYPES = new Set([
   "extension",
   "locale",
   "experiment",
 ]);
 
 const RESTARTLESS_TYPES = new Set([
-  "webextension",
+  "apiextension",
   "dictionary",
   "experiment",
   "locale",
-  "apiextension",
+  "webextension",
+  "webextension-theme",
 ]);
 
 const SIGNED_TYPES = new Set([
-  "webextension",
+  "apiextension",
   "extension",
   "experiment",
-  "apiextension",
+  "webextension",
+  "webextension-theme",
 ]);
 
 // This is a random number array that can be used as "salt" when generating
 // an automatic ID based on the directory path of an add-on. It will prevent
 // someone from creating an ID for a permanent add-on that could be replaced
 // by a temporary add-on (because that would be confusing, I guess).
 const TEMP_INSTALL_ID_GEN_SESSION =
   new Uint8Array(Float64Array.of(Math.random()).buffer);
@@ -396,16 +399,27 @@ function findMatchingStaticBlocklistItem
 /**
  * Converts an iterable of addon objects into a map with the add-on's ID as key.
  */
 function addonMap(addons) {
   return new Map(addons.map(a => [a.id, a]));
 }
 
 /**
+ * Helper function that determines whether an addon of a certain type is a
+ * WebExtension.
+ *
+ * @param  {String} type
+ * @return {Boolean}
+ */
+function isWebExtension(type) {
+  return type == "webextension" || type == "webextension-theme";
+}
+
+/**
  * Sets permissions on a file
  *
  * @param  aFile
  *         The file or directory to operate on.
  * @param  aPermissions
  *         The permisions to set
  */
 function setFilePermissions(aFile, aPermissions) {
@@ -928,16 +942,17 @@ function getRDFProperty(aDs, aResource, 
 var loadManifestFromWebManifest = Task.async(function*(aUri) {
   // We're passed the URI for the manifest file. Get the URI for its
   // parent directory.
   let uri = NetUtil.newURI("./", null, aUri);
 
   let extension = new ExtensionData(uri);
 
   let manifest = yield extension.readManifest();
+  let theme = !!manifest.theme;
 
   // Read the list of available locales, and pre-load messages for
   // all locales.
   let locales = yield extension.initAllLocales();
 
   // If there were any errors loading the extension, bail out now.
   if (extension.errors.length)
     throw new Error("Extension is invalid");
@@ -951,17 +966,17 @@ var loadManifestFromWebManifest = Task.a
   // A * is illegal in strict_min_version
   if (bss.strict_min_version && bss.strict_min_version.split(".").some(part => part == "*")) {
     throw new Error("The use of '*' in strict_min_version is invalid");
   }
 
   let addon = new AddonInternal();
   addon.id = bss.id;
   addon.version = manifest.version;
-  addon.type = "webextension";
+  addon.type = "webextension" + (theme ? "-theme" : "");
   addon.unpack = false;
   addon.strictCompatibility = true;
   addon.bootstrap = true;
   addon.hasBinaryComponents = false;
   addon.multiprocessCompatible = true;
   addon.internalName = null;
   addon.updateURL = bss.update_url;
   addon.updateKey = null;
@@ -4435,18 +4450,19 @@ this.XPIProvider = {
    * Determine if an add-on should be blocking e10s if enabled.
    *
    * @param  aAddon
    *         The add-on to test
    * @return true if enabling the add-on should block e10s
    */
   isBlockingE10s(aAddon) {
     if (aAddon.type != "extension" &&
+        aAddon.type != "theme" &&
         aAddon.type != "webextension" &&
-        aAddon.type != "theme")
+        aAddon.type != "webextension-theme")
       return false;
 
     // The hotfix is exempt
     let hotfixID = Preferences.get(PREF_EM_HOTFIX_ID, undefined);
     if (hotfixID && hotfixID == aAddon.id)
       return false;
 
     // The default theme is exempt
@@ -4723,17 +4739,17 @@ this.XPIProvider = {
                                     metadata: { addonID: aId } });
       logger.error("Attempted to load bootstrap scope from missing directory " + aFile.path);
       return;
     }
 
     let uri = getURIForResourceInFile(aFile, "bootstrap.js").spec;
     if (aType == "dictionary")
       uri = "resource://gre/modules/addons/SpellCheckDictionaryBootstrap.js"
-    else if (aType == "webextension")
+    else if (isWebExtension(aType))
       uri = "resource://gre/modules/addons/WebExtensionBootstrap.js"
     else if (aType == "apiextension")
       uri = "resource://gre/modules/addons/APIExtensionBootstrap.js"
 
     activeAddon.bootstrapScope =
       new Cu.Sandbox(principal, { sandboxName: uri,
                                   wantGlobalProperties: ["indexedDB"],
                                   addonId: aId,
@@ -5523,17 +5539,17 @@ class AddonInstall {
       if (this.existingAddon) {
         // Check various conditions related to upgrades
         if (this.addon.id != this.existingAddon.id) {
           zipreader.close();
           return Promise.reject([AddonManager.ERROR_INCORRECT_ID,
                                  `Refusing to upgrade addon ${this.existingAddon.id} to different ID ${this.addon.id}`]);
         }
 
-        if (this.existingAddon.type == "webextension" && this.addon.type != "webextension") {
+        if (isWebExtension(this.existingAddon.type) && !isWebExtension(this.addon.type)) {
           zipreader.close();
           return Promise.reject([AddonManager.ERROR_UNEXPECTED_ADDON_TYPE,
                                  "WebExtensions may not be upated to other extension types"]);
         }
       }
 
       if (mustSign(this.addon.type)) {
         if (this.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
@@ -7206,17 +7222,17 @@ AddonWrapper.prototype = {
     XPIDatabase.saveChanges();
   },
 
   get type() {
     return getExternalType(addonFor(this).type);
   },
 
   get isWebExtension() {
-    return addonFor(this).type == "webextension";
+    return isWebExtension(addonFor(this).type);
   },
 
   get temporarilyInstalled() {
     return addonFor(this)._installLocation == TemporaryInstallLocation;
   },
 
   get aboutURL() {
     return this.isActive ? addonFor(this)["aboutURL"] : null;