Bug 1363925: Part 8e - Convert AddonInternal classes to ES6 classes. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 22 Apr 2018 15:21:30 -0700
changeset 786324 340fb74ac0c9cf18da58abc1ab77962f1117d783
parent 786323 7a5b72ef201f356ed1b9e5a3bada52fc3056f708
push id107433
push usermaglione.k@gmail.com
push dateSun, 22 Apr 2018 22:24:27 +0000
reviewersaswan
bugs1363925
milestone61.0a1
Bug 1363925: Part 8e - Convert AddonInternal classes to ES6 classes. r?aswan MozReview-Commit-ID: LUwU0JbRn2H
toolkit/mozapps/extensions/internal/XPIDatabase.jsm
--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
@@ -185,54 +185,61 @@ function copyProperties(aObject, aProper
   });
   return aTarget;
 }
 
 // Maps instances of AddonInternal to AddonWrapper
 const wrapperMap = new WeakMap();
 let addonFor = wrapper => wrapperMap.get(wrapper);
 
+const EMPTY_ARRAY = Object.freeze([]);
+
+let AddonWrapper;
+
 /**
  * The AddonInternal is an internal only representation of add-ons. It may
  * have come from the database (see DBAddonInternal in XPIDatabase.jsm)
  * or an install manifest.
  */
-function AddonInternal() {
-  this._hasResourceCache = new Map();
-
-  XPCOMUtils.defineLazyGetter(this, "wrapper", () => {
-    return new AddonWrapper(this);
-  });
-}
-
-AddonInternal.prototype = {
-  _selectedLocale: null,
-  _hasResourceCache: null,
-  active: false,
-  visible: false,
-  userDisabled: false,
-  appDisabled: false,
-  softDisabled: false,
-  blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
-  blocklistURL: null,
-  sourceURI: null,
-  releaseNotesURI: null,
-  foreignInstall: false,
-  seen: true,
-  skinnable: false,
-  startupData: null,
-
-  /**
-   * @property {Array<string>} dependencies
-   *   An array of bootstrapped add-on IDs on which this add-on depends.
-   *   The add-on will remain appDisabled if any of the dependent
-   *   add-ons is not installed and enabled.
-   */
-  dependencies: Object.freeze([]),
-  hasEmbeddedWebExtension: false,
+class AddonInternal {
+  constructor() {
+    this._hasResourceCache = new Map();
+
+    this._wrapper = null;
+    this._selectedLocale = null;
+    this.active = false;
+    this.visible = false;
+    this.userDisabled = false;
+    this.appDisabled = false;
+    this.softDisabled = false;
+    this.blocklistState = Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+    this.blocklistURL = null;
+    this.sourceURI = null;
+    this.releaseNotesURI = null;
+    this.foreignInstall = false;
+    this.seen = true;
+    this.skinnable = false;
+    this.startupData = null;
+
+    /**
+     * @property {Array<string>} dependencies
+     *   An array of bootstrapped add-on IDs on which this add-on depends.
+     *   The add-on will remain appDisabled if any of the dependent
+     *   add-ons is not installed and enabled.
+     */
+    this.dependencies = EMPTY_ARRAY;
+    this.hasEmbeddedWebExtension = false;
+  }
+
+  get wrapper() {
+    if (!this._wrapper) {
+      this._wrapper = new AddonWrapper(this);
+    }
+    return this._wrapper;
+  }
 
   get selectedLocale() {
     if (this._selectedLocale)
       return this._selectedLocale;
 
     /**
      * this.locales is a list of objects that have property `locales`.
      * It's value is an array of locale codes.
@@ -271,21 +278,21 @@ AddonInternal.prototype = {
        * Otherwise, we'll go through all locale entries looking for the one
        * that has the best match in it's locales list.
        */
       this._selectedLocale = this.locales.find(loc =>
         loc.locales.includes(bestLocale));
     }
 
     return this._selectedLocale;
-  },
+  }
 
   get providesUpdatesSecurely() {
     return !this.updateURL || this.updateURL.startsWith("https:");
-  },
+  }
 
   get isCorrectlySigned() {
     switch (this._installLocation.name) {
       case KEY_APP_SYSTEM_ADDONS:
         // System add-ons must be signed by the system key.
         return this.signedState == AddonManager.SIGNEDSTATE_SYSTEM;
 
       case KEY_APP_SYSTEM_DEFAULTS:
@@ -301,29 +308,29 @@ AddonInternal.prototype = {
         if (Services.appinfo.OS != "Darwin")
           return true;
         break;
     }
 
     if (this.signedState === AddonManager.SIGNEDSTATE_NOT_REQUIRED)
       return true;
     return this.signedState > AddonManager.SIGNEDSTATE_MISSING;
-  },
+  }
 
   get unpack() {
     return this.type === "dictionary";
-  },
+  }
 
   get isCompatible() {
     return this.isCompatibleWith();
-  },
+  }
 
   get disabled() {
     return (this.userDisabled || this.appDisabled || this.softDisabled);
-  },
+  }
 
   get isPlatformCompatible() {
     if (this.targetPlatforms.length == 0)
       return true;
 
     let matchedOS = false;
 
     // If any targetPlatform matches the OS and contains an ABI then we will
@@ -354,17 +361,17 @@ AddonInternal.prototype = {
                     + JSON.stringify(this.targetPlatforms);
       logger.error(message, e);
       AddonManagerPrivate.recordException("XPI", message, e);
       // don't trust this add-on
       return false;
     }
 
     return matchedOS && !needsABI;
-  },
+  }
 
   isCompatibleWith(aAppVersion, aPlatformVersion) {
     let app = this.matchingTargetApplication;
     if (!app)
       return false;
 
     // set reasonable defaults for minVersion and maxVersion
     let minVersion = app.minVersion || "0";
@@ -408,41 +415,41 @@ AddonInternal.prototype = {
           Services.vc.compare(minCompatVersion, maxVersion) > 0)
         return false;
 
       return Services.vc.compare(version, minVersion) >= 0;
     }
 
     return (Services.vc.compare(version, minVersion) >= 0) &&
            (Services.vc.compare(version, maxVersion) <= 0);
-  },
+  }
 
   get matchingTargetApplication() {
     let app = null;
     for (let targetApp of this.targetApplications) {
       if (targetApp.id == Services.appinfo.ID)
         return targetApp;
       if (targetApp.id == TOOLKIT_ID)
         app = targetApp;
     }
     return app;
-  },
+  }
 
   async findBlocklistEntry() {
     let staticItem = findMatchingStaticBlocklistItem(this);
     if (staticItem) {
       let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL);
       return {
         state: staticItem.level,
         url: url.replace(/%blockID%/g, staticItem.blockID)
       };
     }
 
     return Services.blocklist.getAddonBlocklistEntry(this.wrapper);
-  },
+  }
 
   async updateBlocklistState(options = {}) {
     let {applySoftBlock = true, oldAddon = null, updateDatabase = true} = options;
 
     if (oldAddon) {
       this.userDisabled = oldAddon.userDisabled;
       this.softDisabled = oldAddon.softDisabled;
       this.blocklistState = oldAddon.blocklistState;
@@ -478,30 +485,30 @@ AddonInternal.prototype = {
       this.appDisabled = !XPIDatabase.isUsableAddon(this);
       if (userDisabled !== undefined) {
         this.userDisabled = userDisabled;
       }
       if (softDisabled !== undefined) {
         this.softDisabled = softDisabled;
       }
     }
-  },
+  }
 
   applyCompatibilityUpdate(aUpdate, aSyncCompatibility) {
     for (let targetApp of this.targetApplications) {
       for (let updateTarget of aUpdate.targetApplications) {
         if (targetApp.id == updateTarget.id && (aSyncCompatibility ||
             Services.vc.compare(targetApp.maxVersion, updateTarget.maxVersion) < 0)) {
           targetApp.minVersion = updateTarget.minVersion;
           targetApp.maxVersion = updateTarget.maxVersion;
         }
       }
     }
     this.appDisabled = !XPIDatabase.isUsableAddon(this);
-  },
+  }
 
   /**
    * toJSON is called by JSON.stringify in order to create a filtered version
    * of this object to be serialized to a JSON file. A new object is returned
    * with copies of all non-private properties. Functions, getters and setters
    * are not copied.
    *
    * @returns {Object}
@@ -530,17 +537,17 @@ AddonInternal.prototype = {
       // Ignore functions
       if (typeof this[prop] == "function")
         continue;
 
       obj[prop] = this[prop];
     }
 
     return obj;
-  },
+  }
 
   /**
    * When an add-on install is pending its metadata will be cached in a file.
    * This method reads particular properties of that metadata that may be newer
    * than that in the install manifest, like compatibility information.
    *
    * @param {Object} aObj
    *        A JS object containing the cached metadata
@@ -550,17 +557,17 @@ AddonInternal.prototype = {
       if (!(prop in aObj))
         continue;
 
       this[prop] = aObj[prop];
     }
 
     // Compatibility info may have changed so update appDisabled
     this.appDisabled = !XPIDatabase.isUsableAddon(this);
-  },
+  }
 
   permissions() {
     let permissions = 0;
 
     // Add-ons that aren't installed cannot be modified in any way
     if (!(this.inDatabase))
       return permissions;
 
@@ -587,63 +594,63 @@ AddonInternal.prototype = {
 
     if (Services.policies &&
         !Services.policies.isAllowed(`modify-extension:${this.id}`)) {
       permissions &= ~AddonManager.PERM_CAN_UNINSTALL;
       permissions &= ~AddonManager.PERM_CAN_DISABLE;
     }
 
     return permissions;
-  },
-};
+  }
+}
 
 /**
  * The AddonWrapper wraps an Addon to provide the data visible to consumers of
  * the public API.
  *
  * @param {AddonInternal} aAddon
  *        The add-on object to wrap.
  */
-function AddonWrapper(aAddon) {
-  wrapperMap.set(this, aAddon);
-}
-
-AddonWrapper.prototype = {
+AddonWrapper = class {
+  constructor(aAddon) {
+    wrapperMap.set(this, aAddon);
+  }
+
   get __AddonInternal__() {
     return AppConstants.DEBUG ? addonFor(this) : undefined;
-  },
+  }
 
   get seen() {
     return addonFor(this).seen;
-  },
+  }
 
   get hasEmbeddedWebExtension() {
     return addonFor(this).hasEmbeddedWebExtension;
-  },
+  }
 
   markAsSeen() {
     addonFor(this).seen = true;
     XPIDatabase.saveChanges();
-  },
+  }
 
   get type() {
     return XPIInternal.getExternalType(addonFor(this).type);
-  },
+  }
 
   get isWebExtension() {
     return isWebExtension(addonFor(this).type);
-  },
+  }
 
   get temporarilyInstalled() {
     return addonFor(this)._installLocation == XPIInternal.TemporaryInstallLocation;
-  },
+  }
 
   get aboutURL() {
     return this.isActive ? addonFor(this).aboutURL : null;
-  },
+  }
 
   get optionsURL() {
     if (!this.isActive) {
       return null;
     }
 
     let addon = addonFor(this);
     if (addon.optionsURL) {
@@ -658,17 +665,17 @@ AddonWrapper.prototype = {
         }
         let base = policy.getURL();
         return new URL(addon.optionsURL, base).href;
       }
       return addon.optionsURL;
     }
 
     return null;
-  },
+  }
 
   get optionsType() {
     if (!this.isActive)
       return null;
 
     let addon = addonFor(this);
     let hasOptionsURL = !!this.optionsURL;
 
@@ -677,30 +684,30 @@ AddonWrapper.prototype = {
       case AddonManager.OPTIONS_TYPE_TAB:
       case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:
         return hasOptionsURL ? addon.optionsType : null;
       }
       return null;
     }
 
     return null;
-  },
+  }
 
   get optionsBrowserStyle() {
     let addon = addonFor(this);
     return addon.optionsBrowserStyle;
-  },
+  }
 
   get iconURL() {
     return AddonManager.getPreferredIconURL(this, 48);
-  },
+  }
 
   get icon64URL() {
     return AddonManager.getPreferredIconURL(this, 64);
-  },
+  }
 
   get icons() {
     let addon = addonFor(this);
     let icons = {};
 
     if (addon._repositoryAddon) {
       for (let size in addon._repositoryAddon.icons) {
         icons[size] = addon._repositoryAddon.icons[size];
@@ -728,38 +735,38 @@ AddonWrapper.prototype = {
     }
 
     if (canUseIconURLs && addon.icon64URL) {
       icons[64] = addon.icon64URL;
     }
 
     Object.freeze(icons);
     return icons;
-  },
+  }
 
   get screenshots() {
     let addon = addonFor(this);
     let repositoryAddon = addon._repositoryAddon;
     if (repositoryAddon && ("screenshots" in repositoryAddon)) {
       let repositoryScreenshots = repositoryAddon.screenshots;
       if (repositoryScreenshots && repositoryScreenshots.length > 0)
         return repositoryScreenshots;
     }
 
     if (isTheme(addon.type) && this.hasResource("preview.png")) {
       let url = this.getResourceURI("preview.png").spec;
       return [new AddonManagerPrivate.AddonScreenshot(url)];
     }
 
     return null;
-  },
+  }
 
   get applyBackgroundUpdates() {
     return addonFor(this).applyBackgroundUpdates;
-  },
+  }
   set applyBackgroundUpdates(val) {
     let addon = addonFor(this);
     if (val != AddonManager.AUTOUPDATE_DEFAULT &&
         val != AddonManager.AUTOUPDATE_DISABLE &&
         val != AddonManager.AUTOUPDATE_ENABLE) {
       val = val ? AddonManager.AUTOUPDATE_DEFAULT :
                   AddonManager.AUTOUPDATE_DISABLE;
     }
@@ -768,50 +775,50 @@ AddonWrapper.prototype = {
       return val;
 
     XPIDatabase.setAddonProperties(addon, {
       applyBackgroundUpdates: val
     });
     AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["applyBackgroundUpdates"]);
 
     return val;
-  },
+  }
 
   set syncGUID(val) {
     let addon = addonFor(this);
     if (addon.syncGUID == val)
       return val;
 
     if (addon.inDatabase)
       XPIDatabase.setAddonSyncGUID(addon, val);
 
     addon.syncGUID = val;
 
     return val;
-  },
+  }
 
   get install() {
     let addon = addonFor(this);
     if (!("_install" in addon) || !addon._install)
       return null;
     return addon._install.wrapper;
-  },
+  }
 
   get pendingUpgrade() {
     let addon = addonFor(this);
     return addon.pendingUpgrade ? addon.pendingUpgrade.wrapper : null;
-  },
+  }
 
   get scope() {
     let addon = addonFor(this);
     if (addon._installLocation)
       return addon._installLocation.scope;
 
     return AddonManager.SCOPE_PROFILE;
-  },
+  }
 
   get pendingOperations() {
     let addon = addonFor(this);
     let pending = 0;
     if (!(addon.inDatabase)) {
       // Add-on is pending install if there is no associated install (shouldn't
       // happen here) or if the install is in the process of or has successfully
       // completed the install. If an add-on is pending install then we ignore
@@ -829,58 +836,58 @@ AddonWrapper.prototype = {
       pending |= AddonManager.PENDING_DISABLE;
     else if (!addon.active && !addon.disabled)
       pending |= AddonManager.PENDING_ENABLE;
 
     if (addon.pendingUpgrade)
       pending |= AddonManager.PENDING_UPGRADE;
 
     return pending;
-  },
+  }
 
   get operationsRequiringRestart() {
     return 0;
-  },
+  }
 
   get isDebuggable() {
     return this.isActive && addonFor(this).bootstrap;
-  },
+  }
 
   get permissions() {
     return addonFor(this).permissions();
-  },
+  }
 
   get isActive() {
     let addon = addonFor(this);
     if (!addon.active)
       return false;
     if (!Services.appinfo.inSafeMode)
       return true;
     return addon.bootstrap && XPIInternal.canRunInSafeMode(addon);
-  },
+  }
 
   get startupPromise() {
     let addon = addonFor(this);
     if (!addon.bootstrap || !this.isActive)
       return null;
 
     let activeAddon = XPIProvider.activeAddons.get(addon.id);
     if (activeAddon)
       return activeAddon.startupPromise || null;
     return null;
-  },
+  }
 
   updateBlocklistState(applySoftBlock = true) {
     return addonFor(this).updateBlocklistState({applySoftBlock});
-  },
+  }
 
   get userDisabled() {
     let addon = addonFor(this);
     return addon.softDisabled || addon.userDisabled;
-  },
+  }
   set userDisabled(val) {
     let addon = addonFor(this);
     if (val == this.userDisabled) {
       return val;
     }
 
     if (addon.inDatabase) {
       // hidden and system add-ons should not be user disabled,
@@ -892,17 +899,17 @@ AddonWrapper.prototype = {
     } else {
       addon.userDisabled = val;
       // When enabling remove the softDisabled flag
       if (!val)
         addon.softDisabled = false;
     }
 
     return val;
-  },
+  }
 
   set softDisabled(val) {
     let addon = addonFor(this);
     if (val == addon.softDisabled)
       return val;
 
     if (addon.inDatabase) {
       // When softDisabling a theme just enable the active theme
@@ -913,69 +920,69 @@ AddonWrapper.prototype = {
         XPIDatabase.updateAddonDisabledState(addon, undefined, val);
       }
     } else if (!addon.userDisabled) {
       // Only set softDisabled if not already disabled
       addon.softDisabled = val;
     }
 
     return val;
-  },
+  }
 
   get hidden() {
     let addon = addonFor(this);
     if (addon._installLocation.name == KEY_APP_TEMPORARY)
       return false;
 
     return addon._installLocation.isSystem;
-  },
+  }
 
   get isSystem() {
     let addon = addonFor(this);
     return addon._installLocation.isSystem;
-  },
+  }
 
   // Returns true if Firefox Sync should sync this addon. Only addons
   // in the profile install location are considered syncable.
   get isSyncable() {
     let addon = addonFor(this);
     return (addon._installLocation.name == KEY_APP_PROFILE);
-  },
+  }
 
   get userPermissions() {
     return addonFor(this).userPermissions;
-  },
+  }
 
   isCompatibleWith(aAppVersion, aPlatformVersion) {
     return addonFor(this).isCompatibleWith(aAppVersion, aPlatformVersion);
-  },
+  }
 
   uninstall(alwaysAllowUndo) {
     let addon = addonFor(this);
     XPIProvider.uninstallAddon(addon, alwaysAllowUndo);
-  },
+  }
 
   cancelUninstall() {
     let addon = addonFor(this);
     XPIProvider.cancelUninstallAddon(addon);
-  },
+  }
 
   findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
     new UpdateChecker(addonFor(this), aListener, aReason, aAppVersion, aPlatformVersion);
-  },
+  }
 
   // Returns true if there was an update in progress, false if there was no update to cancel
   cancelUpdate() {
     let addon = addonFor(this);
     if (addon._updateCheck) {
       addon._updateCheck.cancel();
       return true;
     }
     return false;
-  },
+  }
 
   hasResource(aPath) {
     let addon = addonFor(this);
     if (addon._hasResourceCache.has(aPath))
       return addon._hasResourceCache.get(aPath);
 
     let bundle = addon._sourceBundle.clone();
 
@@ -1004,17 +1011,17 @@ AddonWrapper.prototype = {
       addon._hasResourceCache.set(aPath, result);
       return result;
     } catch (e) {
       addon._hasResourceCache.set(aPath, false);
       return false;
     } finally {
       zipReader.close();
     }
-  },
+  }
 
   /**
    * Reloads the add-on.
    *
    * For temporarily installed add-ons, this uninstalls and re-installs the
    * add-on. Otherwise, the addon is disabled and then re-enabled, and the cache
    * is flushed.
    *
@@ -1032,17 +1039,17 @@ AddonWrapper.prototype = {
         Services.obs.notifyObservers(addonFile, "flush-cache-entry");
         XPIDatabase.updateAddonDisabledState(addon, false);
         resolve();
       } else {
         // This function supports re-installing an existing add-on.
         resolve(AddonManager.installTemporaryAddon(addon._sourceBundle));
       }
     });
-  },
+  }
 
   /**
    * Returns a URI to the selected resource or to the add-on bundle if aPath
    * is null. URIs to the bundle will always be file: URIs. URIs to resources
    * will be file: URIs if the add-on is unpacked or jar: URIs if the add-on is
    * still an XPI file.
    *
    * @param {string?} aPath
@@ -1199,76 +1206,75 @@ PROP_LOCALE_MULTI.forEach(function(aProp
  * The DBAddonInternal is a special AddonInternal that has been retrieved from
  * the database. The constructor will initialize the DBAddonInternal with a set
  * of fields, which could come from either the JSON store or as an
  * XPIProvider.AddonInternal created from an addon's manifest
  * @constructor
  * @param {Object} aLoaded
  *        Addon data fields loaded from JSON or the addon manifest.
  */
-function DBAddonInternal(aLoaded) {
-  AddonInternal.call(this);
-
-  if (aLoaded.descriptor) {
-    if (!aLoaded.path) {
-      aLoaded.path = descriptorToPath(aLoaded.descriptor);
+class DBAddonInternal extends AddonInternal {
+  constructor(aLoaded) {
+    super();
+
+    if (aLoaded.descriptor) {
+      if (!aLoaded.path) {
+        aLoaded.path = descriptorToPath(aLoaded.descriptor);
+      }
+      delete aLoaded.descriptor;
     }
-    delete aLoaded.descriptor;
+
+    copyProperties(aLoaded, PROP_JSON_FIELDS, this);
+
+    if (!this.dependencies)
+      this.dependencies = [];
+    Object.freeze(this.dependencies);
+
+    if (aLoaded._installLocation) {
+      this._installLocation = aLoaded._installLocation;
+      this.location = aLoaded._installLocation.name;
+    } else if (aLoaded.location) {
+      this._installLocation = XPIProvider.installLocationsByName[this.location];
+    }
+
+    this._key = this.location + ":" + this.id;
+
+    if (!aLoaded._sourceBundle) {
+      throw new Error("Expected passed argument to contain a path");
+    }
+
+    this._sourceBundle = aLoaded._sourceBundle;
   }
 
-  copyProperties(aLoaded, PROP_JSON_FIELDS, this);
-
-  if (!this.dependencies)
-    this.dependencies = [];
-  Object.freeze(this.dependencies);
-
-  if (aLoaded._installLocation) {
-    this._installLocation = aLoaded._installLocation;
-    this.location = aLoaded._installLocation.name;
-  } else if (aLoaded.location) {
-    this._installLocation = XPIProvider.installLocationsByName[this.location];
-  }
-
-  this._key = this.location + ":" + this.id;
-
-  if (!aLoaded._sourceBundle) {
-    throw new Error("Expected passed argument to contain a path");
-  }
-
-  this._sourceBundle = aLoaded._sourceBundle;
-}
-
-DBAddonInternal.prototype = Object.create(AddonInternal.prototype);
-Object.assign(DBAddonInternal.prototype, {
   applyCompatibilityUpdate(aUpdate, aSyncCompatibility) {
     let wasCompatible = this.isCompatible;
 
     this.targetApplications.forEach(function(aTargetApp) {
       aUpdate.targetApplications.forEach(function(aUpdateTarget) {
         if (aTargetApp.id == aUpdateTarget.id && (aSyncCompatibility ||
             Services.vc.compare(aTargetApp.maxVersion, aUpdateTarget.maxVersion) < 0)) {
           aTargetApp.minVersion = aUpdateTarget.minVersion;
           aTargetApp.maxVersion = aUpdateTarget.maxVersion;
           XPIDatabase.saveChanges();
         }
       });
     });
 
     if (wasCompatible != this.isCompatible)
       XPIDatabase.updateAddonDisabledState(this);
-  },
+  }
 
   toJSON() {
     return copyProperties(this, PROP_JSON_FIELDS);
-  },
+  }
 
   get inDatabase() {
     return true;
   }
-});
+}
 
 /**
  * @typedef {Map<string, DBAddonInternal>} AddonDB
  */
 
 /**
  * Internal interface: find an addon from an already loaded addonDB.
  *