Bug 1363925: Part 7c - Turn on valid-jsdoc rule for XPIProviderUtils.js. r?zombie draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 22 Apr 2018 14:05:17 -0700
changeset 786319 0838d09243ba2362be2902c81f5c45419261af36
parent 786318 664bc2adc4354b9da57cbbde8c99ee48c5a6c500
child 786320 6c8467fbf0928b7d7f7b01c609c0edc52aedf3ad
push id107433
push usermaglione.k@gmail.com
push dateSun, 22 Apr 2018 22:24:27 +0000
reviewerszombie
bugs1363925
milestone61.0a1
Bug 1363925: Part 7c - Turn on valid-jsdoc rule for XPIProviderUtils.js. r?zombie MozReview-Commit-ID: ENdbZ8dicVX
toolkit/mozapps/extensions/internal/XPIProviderUtils.js
--- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -1,14 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+/* eslint "valid-jsdoc": [2, {requireReturn: false, requireReturnDescription: false, prefer: {return: "returns"}}] */
+
 // These are injected from XPIProvider.jsm
 /* globals ADDON_SIGNING, SIGNED_TYPES, BOOTSTRAP_REASONS, DB_SCHEMA,
           AddonInternal, XPIProvider, XPIStates,
           isUsableAddon, recordAddonTelemetry,
           descriptorToPath */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
@@ -62,35 +64,41 @@ const PROP_JSON_FIELDS = ["id", "syncGUI
                           "userPermissions", "icons", "iconURL", "icon64URL",
                           "blocklistState", "blocklistURL", "startupData"];
 
 // Time to wait before async save of XPI JSON database, in milliseconds
 const ASYNC_SAVE_DELAY_MS = 20;
 
 /**
  * Asynchronously fill in the _repositoryAddon field for one addon
+ *
+ * @param {AddonInternal} aAddon
+ *        The add-on to annotate.
+ * @returns {AddonInternal}
+ *        The annotated add-on.
  */
 async function getRepositoryAddon(aAddon) {
   if (aAddon) {
     aAddon._repositoryAddon = await AddonRepository.getCachedAddonByID(aAddon.id);
   }
   return aAddon;
 }
 
 /**
  * Copies properties from one object to another. If no target object is passed
  * a new object will be created and returned.
  *
- * @param  aObject
- *         An object to copy from
- * @param  aProperties
- *         An array of properties to be copied
- * @param  aTarget
- *         An optional target object to copy the properties to
- * @return the object that the properties were copied onto
+ * @param {object} aObject
+ *        An object to copy from
+ * @param {string[]} aProperties
+ *        An array of properties to be copied
+ * @param {object?} [aTarget]
+ *        An optional target object to copy the properties to
+ * @returns {Object}
+ *        The object that the properties were copied onto
  */
 function copyProperties(aObject, aProperties, aTarget) {
   if (!aTarget)
     aTarget = {};
   aProperties.forEach(function(aProp) {
     if (aProp in aObject)
       aTarget[aProp] = aObject[aProp];
   });
@@ -98,17 +106,17 @@ function copyProperties(aObject, aProper
 }
 
 /**
  * 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 aLoaded
+ * @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);
@@ -163,29 +171,49 @@ Object.assign(DBAddonInternal.prototype,
   },
 
   get inDatabase() {
     return true;
   }
 });
 
 /**
- * Internal interface: find an addon from an already loaded addonDB
+ * @typedef {Map<string, DBAddonInternal>} AddonDB
+ */
+
+/**
+ * Internal interface: find an addon from an already loaded addonDB.
+ *
+ * @param {AddonDB} addonDB
+ *        The add-on database.
+ * @param {function(DBAddonInternal) : boolean} aFilter
+ *        The filter predecate. The first add-on for which it returns
+ *        true will be returned.
+ * @returns {DBAddonInternal?}
+ *        The first matching add-on, if one is found.
  */
 function _findAddon(addonDB, aFilter) {
   for (let addon of addonDB.values()) {
     if (aFilter(addon)) {
       return addon;
     }
   }
   return null;
 }
 
 /**
  * Internal interface to get a filtered list of addons from a loaded addonDB
+ *
+ * @param {AddonDB} addonDB
+ *        The add-on database.
+ * @param {function(DBAddonInternal) : boolean} aFilter
+ *        The filter predecate. Add-ons which match this predicate will
+ *        be returned.
+ * @returns {Array<DBAddonInternal>}
+ *        The list of matching add-ons.
  */
 function _filterDB(addonDB, aFilter) {
   return Array.from(addonDB.values()).filter(aFilter);
 }
 
 this.XPIDatabase = {
   // true if the database connection has been opened
   initialized: false,
@@ -266,16 +294,18 @@ this.XPIDatabase = {
     }
 
     await this._saveTask.finalize();
   },
 
   /**
    * Converts the current internal state of the XPI addon database to
    * a JSON.stringify()-ready structure
+   *
+   * @returns {Object}
    */
   toJSON() {
     if (!this.addonDB) {
       // We never loaded the database?
       throw new Error("Attempt to save database without loading it first");
     }
 
     let toSave = {
@@ -294,20 +324,21 @@ this.XPIDatabase = {
    * 1) Perfectly good, up to date database
    * 2) Out of date JSON database needs to be upgraded => upgrade
    * 3) JSON database exists but is mangled somehow => build new JSON
    * 4) no JSON DB, but a usable SQLITE db we can upgrade from => upgrade
    * 5) useless SQLITE DB => build new JSON
    * 6) usable RDF DB => upgrade
    * 7) useless RDF DB => build new JSON
    * 8) Nothing at all => build new JSON
-   * @param  aRebuildOnError
-   *         A boolean indicating whether add-on information should be loaded
-   *         from the install locations if the database needs to be rebuilt.
-   *         (if false, caller is XPIProvider.checkForChanges() which will rebuild)
+   *
+   * @param {boolean} aRebuildOnError
+   *        A boolean indicating whether add-on information should be loaded
+   *        from the install locations if the database needs to be rebuilt.
+   *        (if false, caller is XPIProvider.checkForChanges() which will rebuild)
    */
   syncLoadDB(aRebuildOnError) {
     this.migrateData = null;
     let fstream = null;
     let data = "";
     try {
       let readTimer = AddonManagerPrivate.simpleTimer("XPIDB_syncRead_MS");
       logger.debug("Opening XPI database " + this.jsonFile.path);
@@ -353,23 +384,25 @@ this.XPIDatabase = {
       AddonManagerPrivate.recordSimpleMeasure("XPIDB_overlapped_load", 1);
     }
     this._dbPromise = Promise.resolve(this.addonDB);
     Services.obs.notifyObservers(this.addonDB, "xpi-database-loaded");
   },
 
   /**
    * Parse loaded data, reconstructing the database if the loaded data is not valid
-   * @param aRebuildOnError
+   *
+   * @param {string} aData
+   *        The stringified add-on JSON to parse.
+   * @param {boolean} aRebuildOnError
    *        If true, synchronously reconstruct the database from installed add-ons
    */
   parseDB(aData, aRebuildOnError) {
     let parseTimer = AddonManagerPrivate.simpleTimer("XPIDB_parseDB_MS");
     try {
-      // dump("Loaded JSON:\n" + aData + "\n");
       let inputAddons = JSON.parse(aData);
       // Now do some sanity checks on our JSON db
       if (!("schemaVersion" in inputAddons) || !("addons" in inputAddons)) {
         parseTimer.done();
         // Content of JSON file is bad, need to rebuild from scratch
         logger.error("bad JSON file contents");
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "badJSON");
         let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildBadJSON_MS");
@@ -425,16 +458,19 @@ this.XPIDatabase = {
       let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildReadFailed_MS");
       this.rebuildDatabase(aRebuildOnError);
       rebuildTimer.done();
     }
   },
 
   /**
    * Upgrade database from earlier (sqlite or RDF) version if available
+   *
+   * @param {boolean} aRebuildOnError
+   *        If true, synchronously reconstruct the database from installed add-ons
    */
   upgradeDB(aRebuildOnError) {
     let upgradeTimer = AddonManagerPrivate.simpleTimer("XPIDB_upgradeDB_MS");
 
     let schemaVersion = Services.prefs.getIntPref(PREF_DB_SCHEMA, 0);
     if (schemaVersion > LAST_SQLITE_DB_SCHEMA) {
       // we've upgraded before but the JSON file is gone, fall through
       // and rebuild from scratch
@@ -443,16 +479,21 @@ this.XPIDatabase = {
 
     this.rebuildDatabase(aRebuildOnError);
     upgradeTimer.done();
   },
 
   /**
    * Reconstruct when the DB file exists but is unreadable
    * (for example because read permission is denied)
+   *
+   * @param {Error} aError
+   *        The error that triggered the rebuild.
+   * @param {boolean} aRebuildOnError
+   *        If true, synchronously reconstruct the database from installed add-ons
    */
   rebuildUnreadableDB(aError, aRebuildOnError) {
     let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildUnreadableDB_MS");
     logger.warn("Extensions database " + this.jsonFile.path +
         " exists but is not readable; rebuilding", aError);
     // Remember the error message until we try and write at least once, so
     // we know at shutdown time that there was a problem
     this._loadError = aError;
@@ -461,18 +502,19 @@ this.XPIDatabase = {
     rebuildTimer.done();
   },
 
   /**
    * Open and read the XPI database asynchronously, upgrading if
    * necessary. If any DB load operation fails, we need to
    * synchronously rebuild the DB from the installed extensions.
    *
-   * @return Promise<Map> resolves to the Map of loaded JSON data stored
-   *         in this.addonDB; never rejects.
+   * @returns {Promise<AddonDB>}
+   *        Resolves to the Map of loaded JSON data stored in
+   *        this.addonDB; never rejects.
    */
   asyncLoadDB() {
     // Already started (and possibly finished) loading
     if (this._dbPromise) {
       return this._dbPromise;
     }
 
     logger.debug("Starting async load of XPI database " + this.jsonFile.path);
@@ -519,20 +561,21 @@ this.XPIDatabase = {
 
     return this._dbPromise;
   },
 
   /**
    * Rebuild the database from addon install directories. If this.migrateData
    * is available, uses migrated information for settings on the addons found
    * during rebuild
-   * @param aRebuildOnError
-   *         A boolean indicating whether add-on information should be loaded
-   *         from the install locations if the database needs to be rebuilt.
-   *         (if false, caller is XPIProvider.checkForChanges() which will rebuild)
+   *
+   * @param {boolean} aRebuildOnError
+   *        A boolean indicating whether add-on information should be loaded
+   *        from the install locations if the database needs to be rebuilt.
+   *        (if false, caller is XPIProvider.checkForChanges() which will rebuild)
    */
   rebuildDatabase(aRebuildOnError) {
     this.addonDB = new Map();
     this.initialized = true;
 
     if (XPIStates.size == 0) {
       // No extensions installed, so we're done
       logger.debug("Rebuilding XPI database with no extensions");
@@ -598,39 +641,44 @@ this.XPIDatabase = {
       delete this._saveTask;
       // re-enable the schema version setter
       delete this._schemaVersionSet;
     }
   },
 
   /**
    * Asynchronously list all addons that match the filter function
-   * @param  aFilter
-   *         Function that takes an addon instance and returns
-   *         true if that addon should be included in the selected array
-   * @return a Promise that resolves to the list of add-ons matching aFilter or
-   *         an empty array if none match
+   *
+   * @param {function(DBAddonInternal) : boolean} aFilter
+   *        Function that takes an addon instance and returns
+   *        true if that addon should be included in the selected array
+   *
+   * @returns {Array<DBAddonInternal>}
+   *        A Promise that resolves to the list of add-ons matching
+   *        aFilter or an empty array if none match
    */
   async getAddonList(aFilter) {
     try {
       let addonDB = await this.asyncLoadDB();
       let addonList = _filterDB(addonDB, aFilter);
       let addons = await Promise.all(addonList.map(addon => getRepositoryAddon(addon)));
       return addons;
     } catch (error) {
       logger.error("getAddonList failed", error);
       return [];
     }
   },
 
   /**
-   * (Possibly asynchronously) get the first addon that matches the filter function
-   * @param  aFilter
-   *         Function that takes an addon instance and returns
-   *         true if that addon should be selected
+   * Get the first addon that matches the filter function
+   *
+   * @param {function(DBAddonInternal) : boolean} aFilter
+   *        Function that takes an addon instance and returns
+   *        true if that addon should be selected
+   * @returns {Promise<DBAddonInternal?>}
    */
   getAddon(aFilter) {
     return this.asyncLoadDB()
       .then(addonDB => getRepositoryAddon(_findAddon(addonDB, aFilter)))
       .catch(
         error => {
           logger.error("getAddon failed", error);
         });
@@ -639,68 +687,72 @@ this.XPIDatabase = {
   syncGetAddon(aFilter) {
     return _findAddon(this.addonDB, aFilter);
   },
 
   /**
    * Asynchronously gets an add-on with a particular ID in a particular
    * install location.
    *
-   * @param  aId
-   *         The ID of the add-on to retrieve
-   * @param  aLocation
-   *         The name of the install location
+   * @param {string} aId
+   *        The ID of the add-on to retrieve
+   * @param {string} aLocation
+   *        The name of the install location
+   * @returns {Promise<DBAddonInternal?>}
    */
   getAddonInLocation(aId, aLocation) {
     return this.asyncLoadDB().then(
         addonDB => getRepositoryAddon(addonDB.get(aLocation + ":" + aId)));
   },
 
   /**
    * Asynchronously get all the add-ons in a particular install location.
    *
-   * @param  aLocation
-   *         The name of the install location
+   * @param {string} aLocation
+   *        The name of the install location
+   * @returns {Promise<Array<DBAddonInternal>>}
    */
   getAddonsInLocation(aLocation) {
     return this.getAddonList(aAddon => aAddon._installLocation.name == aLocation);
   },
 
   /**
    * Asynchronously gets the add-on with the specified ID that is visible.
    *
-   * @param  aId
-   *         The ID of the add-on to retrieve
+   * @param {string} aId
+   *        The ID of the add-on to retrieve
+   * @returns {Promise<DBAddonInternal?>}
    */
   getVisibleAddonForID(aId) {
     return this.getAddon(aAddon => ((aAddon.id == aId) && aAddon.visible));
   },
 
   syncGetVisibleAddonForID(aId) {
     return this.syncGetAddon(aAddon => ((aAddon.id == aId) && aAddon.visible));
   },
 
   /**
    * Asynchronously gets the visible add-ons, optionally restricting by type.
    *
-   * @param  aTypes
-   *         An array of types to include or null to include all types
+   * @param {Array<string>?} aTypes
+   *        An array of types to include or null to include all types
+   * @returns {Promise<Array<DBAddonInternal>>}
    */
   getVisibleAddons(aTypes) {
     return this.getAddonList(aAddon => (aAddon.visible &&
                                         (!aTypes || (aTypes.length == 0) ||
                                          (aTypes.indexOf(aAddon.type) > -1))));
   },
 
   /**
    * Synchronously gets all add-ons of a particular type(s).
    *
-   * @param  aType, aType2, ...
-   *         The type(s) of add-on to retrieve
-   * @return an array of DBAddonInternals
+   * @param {Array<string>} aTypes
+   *        The type(s) of add-on to retrieve
+   * @returns {Array<DBAddonInternal>}
    */
   getAddonsByType(...aTypes) {
     if (!this.addonDB) {
       // jank-tastic! Must synchronously load DB if the theme switches from
       // an XPI theme to a lightweight theme before the DB has loaded,
       // because we're called from sync XPIProvider.addonChanged
       logger.warn(`Synchronous load of XPI database due to ` +
                   `getAddonsByType([${aTypes.join(", ")}]) ` +
@@ -710,59 +762,62 @@ this.XPIDatabase = {
     }
 
     return _filterDB(this.addonDB, aAddon => aTypes.includes(aAddon.type));
   },
 
   /**
    * Asynchronously gets all add-ons with pending operations.
    *
-   * @param  aTypes
-   *         The types of add-ons to retrieve or null to get all types
+   * @param {Array<string>?} aTypes
+   *        The types of add-ons to retrieve or null to get all types
+   * @returns {Promise<Array<DBAddonInternal>>}
    */
   getVisibleAddonsWithPendingOperations(aTypes) {
     return this.getAddonList(
         aAddon => (aAddon.visible &&
                    aAddon.pendingUninstall &&
                    (!aTypes || (aTypes.length == 0) || (aTypes.indexOf(aAddon.type) > -1))));
   },
 
   /**
    * Asynchronously get an add-on by its Sync GUID.
    *
-   * @param  aGUID
-   *         Sync GUID of add-on to fetch
+   * @param {string} aGUID
+   *        Sync GUID of add-on to fetch
+   * @returns {Promise<DBAddonInternal?>}
    */
   getAddonBySyncGUID(aGUID) {
     return this.getAddon(aAddon => aAddon.syncGUID == aGUID);
   },
 
   /**
    * Synchronously gets all add-ons in the database.
    * This is only called from the preference observer for the default
    * compatibility version preference, so we can return an empty list if
    * we haven't loaded the database yet.
    *
-   * @return  an array of DBAddonInternals
+   * @returns {Array<DBAddonInternal>}
    */
   getAddons() {
     if (!this.addonDB) {
       return [];
     }
     return _filterDB(this.addonDB, aAddon => true);
   },
 
   /**
    * Synchronously adds an AddonInternal's metadata to the database.
    *
-   * @param  aAddon
-   *         AddonInternal to add
-   * @param  aPath
-   *         The file path of the add-on
-   * @return The DBAddonInternal that was added to the database
+   * @param {AddonInternal} aAddon
+   *        AddonInternal to add
+   * @param {string} aPath
+   *        The file path of the add-on
+   * @returns {DBAddonInternal}
+   *        the DBAddonInternal that was added to the database
    */
   addAddonMetadata(aAddon, aPath) {
     if (!this.addonDB) {
       AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_addMetadata",
           XPIProvider.runPhase);
       this.syncLoadDB(false);
     }
 
@@ -776,23 +831,24 @@ this.XPIDatabase = {
     this.saveChanges();
     return newAddon;
   },
 
   /**
    * Synchronously updates an add-on's metadata in the database. Currently just
    * removes and recreates.
    *
-   * @param  aOldAddon
-   *         The DBAddonInternal to be replaced
-   * @param  aNewAddon
-   *         The new AddonInternal to add
-   * @param  aPath
-   *         The file path of the add-on
-   * @return The DBAddonInternal that was added to the database
+   * @param {DBAddonInternal} aOldAddon
+   *        The DBAddonInternal to be replaced
+   * @param {AddonInternal} aNewAddon
+   *        The new AddonInternal to add
+   * @param {string} aPath
+   *        The file path of the add-on
+   * @returns {DBAddonInternal}
+   *        The DBAddonInternal that was added to the database
    */
   updateAddonMetadata(aOldAddon, aNewAddon, aPath) {
     this.removeAddonMetadata(aOldAddon);
     aNewAddon.syncGUID = aOldAddon.syncGUID;
     aNewAddon.installDate = aOldAddon.installDate;
     aNewAddon.applyBackgroundUpdates = aOldAddon.applyBackgroundUpdates;
     aNewAddon.foreignInstall = aOldAddon.foreignInstall;
     aNewAddon.seen = aOldAddon.seen;
@@ -800,18 +856,18 @@ this.XPIDatabase = {
 
     // addAddonMetadata does a saveChanges()
     return this.addAddonMetadata(aNewAddon, aPath);
   },
 
   /**
    * Synchronously removes an add-on from the database.
    *
-   * @param  aAddon
-   *         The DBAddonInternal being removed
+   * @param {DBAddonInternal} aAddon
+   *        The DBAddonInternal being removed
    */
   removeAddonMetadata(aAddon) {
     this.addonDB.delete(aAddon._key);
     this.saveChanges();
   },
 
   updateXPIStates(addon) {
     let xpiState = XPIStates.getAddon(addon.location, addon.id);
@@ -820,18 +876,18 @@ this.XPIDatabase = {
       XPIStates.save();
     }
   },
 
   /**
    * Synchronously marks a DBAddonInternal as visible marking all other
    * instances with the same ID as not visible.
    *
-   * @param  aAddon
-   *         The DBAddonInternal to make visible
+   * @param {DBAddonInternal} aAddon
+   *        The DBAddonInternal to make visible
    */
   makeAddonVisible(aAddon) {
     logger.debug("Make addon " + aAddon._key + " visible");
     for (let [, otherAddon] of this.addonDB) {
       if ((otherAddon.id == aAddon.id) && (otherAddon._key != aAddon._key)) {
         logger.debug("Hide addon " + otherAddon._key);
         otherAddon.visible = false;
         otherAddon.active = false;
@@ -843,18 +899,22 @@ this.XPIDatabase = {
     this.updateXPIStates(aAddon);
     this.saveChanges();
   },
 
   /**
    * Synchronously marks a given add-on ID visible in a given location,
    * instances with the same ID as not visible.
    *
-   * @param  aAddon
-   *         The DBAddonInternal to make visible
+   * @param {string} aId
+   *        The ID of the add-on to make visible
+   * @param {InstallLocation} aLocation
+   *        The location in which to make the add-on visible.
+   * @returns {DBAddonInternal?}
+   *        The add-on instance which was marked visible, if any.
    */
   makeAddonLocationVisible(aId, aLocation) {
     logger.debug(`Make addon ${aId} visible in location ${aLocation}`);
     let result;
     for (let [, addon] of this.addonDB) {
       if (addon.id != aId) {
         continue;
       }
@@ -873,36 +933,36 @@ this.XPIDatabase = {
     }
     this.saveChanges();
     return result;
   },
 
   /**
    * Synchronously sets properties for an add-on.
    *
-   * @param  aAddon
-   *         The DBAddonInternal being updated
-   * @param  aProperties
-   *         A dictionary of properties to set
+   * @param {DBAddonInternal} aAddon
+   *        The DBAddonInternal being updated
+   * @param {Object} aProperties
+   *        A dictionary of properties to set
    */
   setAddonProperties(aAddon, aProperties) {
     for (let key in aProperties) {
       aAddon[key] = aProperties[key];
     }
     this.saveChanges();
   },
 
   /**
    * Synchronously sets the Sync GUID for an add-on.
    * Only called when the database is already loaded.
    *
-   * @param  aAddon
-   *         The DBAddonInternal being updated
-   * @param  aGUID
-   *         GUID string to set the value to
+   * @param {DBAddonInternal} aAddon
+   *        The DBAddonInternal being updated
+   * @param {string} aGUID
+   *        GUID string to set the value to
    * @throws if another addon already has the specified GUID
    */
   setAddonSyncGUID(aAddon, aGUID) {
     // Need to make sure no other addon has this GUID
     function excludeSyncGUID(otherAddon) {
       return (otherAddon._key != aAddon._key) && (otherAddon.syncGUID == aGUID);
     }
     let otherAddon = _findAddon(this.addonDB, excludeSyncGUID);
@@ -912,18 +972,20 @@ this.XPIDatabase = {
     }
     aAddon.syncGUID = aGUID;
     this.saveChanges();
   },
 
   /**
    * Synchronously updates an add-on's active flag in the database.
    *
-   * @param  aAddon
-   *         The DBAddonInternal to update
+   * @param {DBAddonInternal} aAddon
+   *        The DBAddonInternal to update
+   * @param {boolean} aActive
+   *        The new active state for the add-on.
    */
   updateAddonActive(aAddon, aActive) {
     logger.debug("Updating active state for add-on " + aAddon.id + " to " + aActive);
 
     aAddon.active = aActive;
     this.saveChanges();
   },
 
@@ -948,16 +1010,22 @@ this.XPIDatabase = {
     }
   },
 };
 
 this.XPIDatabaseReconcile = {
   /**
    * Returns a map of ID -> add-on. When the same add-on ID exists in multiple
    * install locations the highest priority location is chosen.
+   *
+   * @param {Map<String, AddonInternal>} addonMap
+   *        The add-on map to flatten.
+   * @param {string?} [hideLocation]
+   *        An optional location from which to hide any add-ons.
+   * @returns {Map<string, AddonInternal>}
    */
   flattenByID(addonMap, hideLocation) {
     let map = new Map();
 
     for (let installLocation of XPIProvider.installLocations) {
       if (installLocation.name == hideLocation)
         continue;
 
@@ -971,16 +1039,20 @@ this.XPIDatabaseReconcile = {
       }
     }
 
     return map;
   },
 
   /**
    * Finds the visible add-ons from the map.
+   *
+   * @param {Map<String, AddonInternal>} addonMap
+   *        The add-on map to filter.
+   * @returns {Map<string, AddonInternal>}
    */
   getVisibleAddons(addonMap) {
     let map = new Map();
 
     for (let addons of addonMap.values()) {
       for (let [id, addon] of addons) {
         if (!addon.visible)
           continue;
@@ -1000,32 +1072,33 @@ this.XPIDatabaseReconcile = {
   /**
    * Called to add the metadata for an add-on in one of the install locations
    * to the database. This can be called in three different cases. Either an
    * add-on has been dropped into the location from outside of Firefox, or
    * an add-on has been installed through the application, or the database
    * has been upgraded or become corrupt and add-on data has to be reloaded
    * into it.
    *
-   * @param  aInstallLocation
-   *         The install location containing the add-on
-   * @param  aId
-   *         The ID of the add-on
-   * @param  aAddonState
-   *         The new state of the add-on
-   * @param  aNewAddon
-   *         The manifest for the new add-on if it has already been loaded
-   * @param  aOldAppVersion
-   *         The version of the application last run with this profile or null
-   *         if it is a new profile or the version is unknown
-   * @param  aOldPlatformVersion
-   *         The version of the platform last run with this profile or null
-   *         if it is a new profile or the version is unknown
-   * @return a boolean indicating if flushing caches is required to complete
-   *         changing this add-on
+   * @param {InstallLocation} aInstallLocation
+   *        The install location containing the add-on
+   * @param {string} aId
+   *        The ID of the add-on
+   * @param {XPIState} aAddonState
+   *        The new state of the add-on
+   * @param {AddonInternal?} [aNewAddon]
+   *        The manifest for the new add-on if it has already been loaded
+   * @param {string?} [aOldAppVersion]
+   *        The version of the application last run with this profile or null
+   *        if it is a new profile or the version is unknown
+   * @param {string?} [aOldPlatformVersion]
+   *        The version of the platform last run with this profile or null
+   *        if it is a new profile or the version is unknown
+   * @returns {boolean}
+   *        A boolean indicating if flushing caches is required to complete
+   *        changing this add-on
    */
   addMetadata(aInstallLocation, aId, aAddonState, aNewAddon, aOldAppVersion,
               aOldPlatformVersion) {
     logger.debug("New add-on " + aId + " installed in " + aInstallLocation.name);
 
     // If we had staged data for this add-on or we aren't recovering from a
     // corrupt database and we don't have migration data for this add-on then
     // this must be a new install.
@@ -1087,43 +1160,42 @@ this.XPIDatabaseReconcile = {
     }
 
     return XPIDatabase.addAddonMetadata(aNewAddon, aAddonState.path);
   },
 
   /**
    * Called when an add-on has been removed.
    *
-   * @param  aOldAddon
-   *         The AddonInternal as it appeared the last time the application
-   *         ran
-   * @return a boolean indicating if flushing caches is required to complete
-   *         changing this add-on
+   * @param {AddonInternal} aOldAddon
+   *        The AddonInternal as it appeared the last time the application
+   *        ran
    */
   removeMetadata(aOldAddon) {
     // This add-on has disappeared
     logger.debug("Add-on " + aOldAddon.id + " removed from " + aOldAddon.location);
     XPIDatabase.removeAddonMetadata(aOldAddon);
   },
 
   /**
    * Updates an add-on's metadata and determines. This is called when either the
    * add-on's install directory path or last modified time has changed.
    *
-   * @param  aInstallLocation
-   *         The install location containing the add-on
-   * @param  aOldAddon
-   *         The AddonInternal as it appeared the last time the application
-   *         ran
-   * @param  aAddonState
-   *         The new state of the add-on
-   * @param  aNewAddon
-   *         The manifest for the new add-on if it has already been loaded
-   * @return a boolean indicating if flushing caches is required to complete
-   *         changing this add-on
+   * @param {InstallLocation} aInstallLocation
+   *        The install location containing the add-on
+   * @param {AddonInternal} aOldAddon
+   *        The AddonInternal as it appeared the last time the application
+   *        ran
+   * @param {XPIState} aAddonState
+   *        The new state of the add-on
+   * @param {AddonInternal?} [aNewAddon]
+   *        The manifest for the new add-on if it has already been loaded
+   * @returns {boolean?}
+   *        A boolean indicating if flushing caches is required to complete
+   *        changing this add-on
    */
   updateMetadata(aInstallLocation, aOldAddon, aAddonState, aNewAddon) {
     logger.debug("Add-on " + aOldAddon.id + " modified in " + aInstallLocation.name);
 
     try {
       // If there isn't an updated install manifest for this add-on then load it.
       if (!aNewAddon) {
         let file = new nsIFile(aAddonState.path);
@@ -1152,49 +1224,49 @@ this.XPIDatabaseReconcile = {
     // Update the database
     return XPIDatabase.updateAddonMetadata(aOldAddon, aNewAddon, aAddonState.path);
   },
 
   /**
    * Updates an add-on's path for when the add-on has moved in the
    * filesystem but hasn't changed in any other way.
    *
-   * @param  aInstallLocation
-   *         The install location containing the add-on
-   * @param  aOldAddon
-   *         The AddonInternal as it appeared the last time the application
-   *         ran
-   * @param  aAddonState
-   *         The new state of the add-on
-   * @return a boolean indicating if flushing caches is required to complete
-   *         changing this add-on
+   * @param {InstallLocation} aInstallLocation
+   *        The install location containing the add-on
+   * @param {AddonInternal} aOldAddon
+   *        The AddonInternal as it appeared the last time the application
+   *        ran
+   * @param {XPIState} aAddonState
+   *        The new state of the add-on
+   * @returns {AddonInternal}
    */
   updatePath(aInstallLocation, aOldAddon, aAddonState) {
     logger.debug("Add-on " + aOldAddon.id + " moved to " + aAddonState.path);
     aOldAddon.path = aAddonState.path;
     aOldAddon._sourceBundle = new nsIFile(aAddonState.path);
 
     return aOldAddon;
   },
 
   /**
    * Called when no change has been detected for an add-on's metadata but the
    * application has changed so compatibility may have changed.
    *
-   * @param  aInstallLocation
-   *         The install location containing the add-on
-   * @param  aOldAddon
-   *         The AddonInternal as it appeared the last time the application
-   *         ran
-   * @param  aAddonState
-   *         The new state of the add-on
-   * @param  aReloadMetadata
-   *         A boolean which indicates whether metadata should be reloaded from
-   *         the addon manifests. Default to false.
-   * @return the new addon.
+   * @param {InstallLocation} aInstallLocation
+   *        The install location containing the add-on
+   * @param {AddonInternal} aOldAddon
+   *        The AddonInternal as it appeared the last time the application
+   *        ran
+   * @param {XPIState} aAddonState
+   *        The new state of the add-on
+   * @param {boolean} [aReloadMetadata = false]
+   *        A boolean which indicates whether metadata should be reloaded from
+   *        the addon manifests. Default to false.
+   * @returns {DBAddonInternal}
+   *        The new addon.
    */
   updateCompatibility(aInstallLocation, aOldAddon, aAddonState, aReloadMetadata) {
     logger.debug("Updating compatibility for add-on " + aOldAddon.id + " in " + aInstallLocation.name);
 
     let checkSigning = aOldAddon.signedState === undefined && ADDON_SIGNING &&
                        SIGNED_TYPES.has(aOldAddon.type);
 
     let manifest = null;
@@ -1237,32 +1309,33 @@ this.XPIDatabaseReconcile = {
 
   /**
    * Compares the add-ons that are currently installed to those that were
    * known to be installed when the application last ran and applies any
    * changes found to the database. Also sends "startupcache-invalidate" signal to
    * observerservice if it detects that data may have changed.
    * Always called after XPIProviderUtils.js and extensions.json have been loaded.
    *
-   * @param  aManifests
-   *         A dictionary of cached AddonInstalls for add-ons that have been
-   *         installed
-   * @param  aUpdateCompatibility
-   *         true to update add-ons appDisabled property when the application
-   *         version has changed
-   * @param  aOldAppVersion
-   *         The version of the application last run with this profile or null
-   *         if it is a new profile or the version is unknown
-   * @param  aOldPlatformVersion
-   *         The version of the platform last run with this profile or null
-   *         if it is a new profile or the version is unknown
-   * @param  aSchemaChange
-   *         The schema has changed and all add-on manifests should be re-read.
-   * @return a boolean indicating if a change requiring flushing the caches was
-   *         detected
+   * @param {Object} aManifests
+   *        A dictionary of cached AddonInstalls for add-ons that have been
+   *        installed
+   * @param {boolean} aUpdateCompatibility
+   *        true to update add-ons appDisabled property when the application
+   *        version has changed
+   * @param {string?} [aOldAppVersion]
+   *        The version of the application last run with this profile or null
+   *        if it is a new profile or the version is unknown
+   * @param {string?} [aOldPlatformVersion]
+   *        The version of the platform last run with this profile or null
+   *        if it is a new profile or the version is unknown
+   * @param {boolean} aSchemaChange
+   *        The schema has changed and all add-on manifests should be re-read.
+   * @returns {boolean}
+   *        A boolean indicating if a change requiring flushing the caches was
+   *        detected
    */
   processFileChanges(aManifests, aUpdateCompatibility, aOldAppVersion, aOldPlatformVersion,
                      aSchemaChange) {
     let loadedManifest = (aInstallLocation, aId) => {
       if (!(aInstallLocation.name in aManifests))
         return null;
       if (!(aId in aManifests[aInstallLocation.name]))
         return null;