Bug 1363925: Part 7b - Turn on valid-jsdoc rule for XPIProvider.jsm. r?zombie draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 21 Apr 2018 20:51:52 -0700
changeset 786318 664bc2adc4354b9da57cbbde8c99ee48c5a6c500
parent 786317 e637881fe80460f95c6c72f26213c68350f323f0
child 786319 0838d09243ba2362be2902c81f5c45419261af36
push id107433
push usermaglione.k@gmail.com
push dateSun, 22 Apr 2018 22:24:27 +0000
reviewerszombie
bugs1363925
milestone61.0a1
Bug 1363925: Part 7b - Turn on valid-jsdoc rule for XPIProvider.jsm. r?zombie MozReview-Commit-ID: BQ6N84B2pC3
toolkit/mozapps/extensions/internal/XPIProvider.jsm
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -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"}}] */
+
 var EXPORTED_SYMBOLS = ["XPIProvider", "XPIInternal"];
 
 /* globals WebExtensionPolicy */
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
 
@@ -423,43 +425,46 @@ function findMatchingStaticBlocklistItem
   }
   return null;
 }
 
 /**
  * Helper function that determines whether an addon of a certain type is a
  * WebExtension.
  *
- * @param  {String} type
- * @return {Boolean}
+ * @param {string} type
+ *        The add-on type to check.
+ * @returns {boolean}
  */
 function isWebExtension(type) {
   return type == "webextension" || type == "webextension-theme";
 }
 
 var gThemeAliases = null;
 /**
  * Helper function that determines whether an addon of a certain type is a
  * theme.
  *
- * @param  {String} type
- * @return {Boolean}
+ * @param {string} type
+ *        The add-on type to check.
+ * @returns {boolean}
  */
 function isTheme(type) {
   if (!gThemeAliases)
     gThemeAliases = getAllAliasesForTypes(["theme"]);
   return gThemeAliases.includes(type);
 }
 
 /**
  * Evaluates whether an add-on is allowed to run in safe mode.
  *
- * @param  aAddon
- *         The add-on to check
- * @return true if the add-on should run in safe mode
+ * @param {AddonInternal} aAddon
+ *        The add-on to check
+ * @returns {boolean}
+ *        True if the add-on should run in safe mode
  */
 function canRunInSafeMode(aAddon) {
   // Even though the updated system add-ons aren't generally run in safe mode we
   // include them here so their uninstall functions get called when switching
   // back to the default set.
 
   // TODO product should make the call about temporary add-ons running
   // in safe mode. assuming for now that they are.
@@ -495,19 +500,20 @@ function isDisabledLegacy(addon) {
 
           // Properly signed legacy extensions are allowed.
           addon.signedState !== AddonManager.SIGNEDSTATE_PRIVILEGED);
 }
 
 /**
  * Calculates whether an add-on should be appDisabled or not.
  *
- * @param  aAddon
- *         The add-on to check
- * @return true if the add-on should not be appDisabled
+ * @param {AddonInternal} aAddon
+ *        The add-on to check
+ * @returns {boolean}
+ *        True if the add-on should not be appDisabled
  */
 function isUsableAddon(aAddon) {
   if (mustSign(aAddon.type) && !aAddon.isCorrectlySigned) {
     logger.warn(`Add-on ${aAddon.id} is not correctly signed.`);
     if (Services.prefs.getBoolPref(PREF_XPI_SIGNATURES_DEV_ROOT, false)) {
       logger.warn(`Preference ${PREF_XPI_SIGNATURES_DEV_ROOT} is set.`);
     }
     return false;
@@ -563,19 +569,20 @@ function isUsableAddon(aAddon) {
   }
 
   return true;
 }
 
 /**
  * Converts an internal add-on type to the type presented through the API.
  *
- * @param  aType
- *         The internal add-on type
- * @return an external add-on type
+ * @param {string} aType
+ *        The internal add-on type
+ * @returns {string}
+ *        An external add-on type
  */
 function getExternalType(aType) {
   if (aType in TYPE_ALIASES)
     return TYPE_ALIASES[aType];
   return aType;
 }
 
 function getManifestFileForDir(aDir) {
@@ -587,19 +594,20 @@ function getManifestFileForDir(aDir) {
     return file;
   return null;
 }
 
 /**
  * Converts a list of API types to a list of API types and any aliases for those
  * types.
  *
- * @param  aTypes
- *         An array of types or null for all types
- * @return an array of types or null for all types
+ * @param {Array<string>?} aTypes
+ *        An array of types or null for all types
+ * @returns {Array<string>?}
+ *        An array of types or null for all types
  */
 function getAllAliasesForTypes(aTypes) {
   if (!aTypes)
     return null;
 
   // Build a set of all requested types and their aliases
   let typeset = new Set(aTypes);
 
@@ -615,59 +623,62 @@ function getAllAliasesForTypes(aTypes) {
   return [...typeset];
 }
 
 /**
  * Gets an nsIURI for a file within another file, either a directory or an XPI
  * file. If aFile is a directory then this will return a file: URI, if it is an
  * XPI file then it will return a jar: URI.
  *
- * @param  aFile
- *         The file containing the resources, must be either a directory or an
- *         XPI file
- * @param  aPath
- *         The path to find the resource at, "/" separated. If aPath is empty
- *         then the uri to the root of the contained files will be returned
- * @return an nsIURI pointing at the resource
+ * @param {nsIFile} aFile
+ *        The file containing the resources, must be either a directory or an
+ *        XPI file
+ * @param {string} aPath
+ *        The path to find the resource at, "/" separated. If aPath is empty
+ *        then the uri to the root of the contained files will be returned
+ * @returns {nsIURI}
+ *        An nsIURI pointing at the resource
  */
 function getURIForResourceInFile(aFile, aPath) {
   if (aFile.exists() && aFile.isDirectory()) {
     let resource = aFile.clone();
     if (aPath)
       aPath.split("/").forEach(part => resource.append(part));
 
     return Services.io.newFileURI(resource);
   }
 
   return buildJarURI(aFile, aPath);
 }
 
 /**
  * Creates a jar: URI for a file inside a ZIP file.
  *
- * @param  aJarfile
- *         The ZIP file as an nsIFile
- * @param  aPath
- *         The path inside the ZIP file
- * @return an nsIURI for the file
+ * @param {nsIFile} aJarfile
+ *        The ZIP file as an nsIFile
+ * @param {string} aPath
+ *        The path inside the ZIP file
+ * @returns {nsIURI}
+ *        An nsIURI for the file
  */
 function buildJarURI(aJarfile, aPath) {
   let uri = Services.io.newFileURI(aJarfile);
   uri = "jar:" + uri.spec + "!/" + aPath;
   return Services.io.newURI(uri);
 }
 
 /**
  * Gets a snapshot of directory entries.
  *
- * @param  aDir
- *         Directory to look at
- * @param  aSortEntries
- *         True to sort entries by filename
- * @return An array of nsIFile, or an empty array if aDir is not a readable directory
+ * @param {nsIURI} aDir
+ *        Directory to look at
+ * @param {boolean} aSortEntries
+ *        True to sort entries by filename
+ * @returns {Array<nsIFile>}
+ *        An array of nsIFile, or an empty array if aDir is not a readable directory
  */
 function getDirectoryEntries(aDir, aSortEntries) {
   let dirEnum;
   try {
     dirEnum = aDir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
     let entries = [];
     while (dirEnum.hasMoreElements())
       entries.push(dirEnum.nextFile);
@@ -687,18 +698,23 @@ function getDirectoryEntries(aDir, aSort
   } finally {
     if (dirEnum) {
       dirEnum.close();
     }
   }
 }
 
 /**
- * Record a bit of per-addon telemetry
- * @param aAddon the addon to record
+ * Record a bit of per-addon telemetry.
+ *
+ * Yes, this description is extremely helpful. How dare you question its
+ * utility?
+ *
+ * @param {AddonInternal} aAddon
+ *        The addon to record
  */
 function recordAddonTelemetry(aAddon) {
   let locale = aAddon.defaultLocale;
   if (locale) {
     if (locale.name)
       XPIProvider.setTelemetry(aAddon.id, "name", locale.name);
     if (locale.creator)
       XPIProvider.setTelemetry(aAddon.id, "creator", locale.creator);
@@ -756,21 +772,22 @@ class XPIState {
   /**
    * Migrates an add-on's data from xpiState and bootstrappedAddons
    * preferences, and returns an XPIState object for it.
    *
    * @param {XPIStateLocation} location
    *        The location of the add-on.
    * @param {string} id
    *        The ID of the add-on to migrate.
-   * @param {object} state
+   * @param {object} saved
    *        The add-on's data from the xpiState preference.
    * @param {object} [bootstrapped]
    *        The add-on's data from the bootstrappedAddons preference, if
    *        applicable.
+   * @returns {XPIState}
    */
   static migrate(location, id, saved, bootstrapped) {
     let data = {
       enabled: saved.e,
       path: descriptorToPath(saved.d, location.dir),
       lastModifiedTime: saved.mt || saved.st,
       version: saved.v,
     };
@@ -824,16 +841,18 @@ class XPIState {
       return path;
     }
     return this.path;
   }
 
   /**
    * Returns a JSON-compatible representation of this add-on's state
    * data, to be saved to addonStartup.json.
+   *
+   * @returns {Object}
    */
   toJSON() {
     let json = {
       enabled: this.enabled,
       lastModifiedTime: this.lastModifiedTime,
       path: this.relativePath,
       version: this.version,
     };
@@ -849,19 +868,23 @@ class XPIState {
     if (this.startupData) {
       json.startupData = this.startupData;
     }
     return json;
   }
 
   /**
    * Update the last modified time for an add-on on disk.
-   * @param aFile: nsIFile path of the add-on.
-   * @param aId: The add-on ID.
-   * @return True if the time stamp has changed.
+   *
+   * @param {nsIFile} aFile
+   *        The location of the add-on.
+   * @param {string} aId
+   *        The add-on ID.
+   * @returns {boolean}
+   *       True if the time stamp has changed.
    */
   getModTime(aFile, aId) {
     // Modified time is the install manifest time, if any. If no manifest
     // exists, we assume this is a packed .xpi and use the time stamp of
     // {path}
     let mtime = (tryGetMtime(getManifestFileForDir(aFile)) ||
                  tryGetMtime(aFile));
     if (!mtime) {
@@ -872,19 +895,23 @@ class XPIState {
     this.lastModifiedTime = mtime;
     return this.changed;
   }
 
   /**
    * Update the XPIState to match an XPIDatabase entry; if 'enabled' is changed to true,
    * update the last-modified time. This should probably be made async, but for now we
    * don't want to maintain parallel sync and async versions of the scan.
+   *
    * Caller is responsible for doing XPIStates.save() if necessary.
-   * @param aDBAddon The DBAddonInternal for this add-on.
-   * @param aUpdated The add-on was updated, so we must record new modified time.
+   *
+   * @param {DBAddonInternal} aDBAddon
+   *        The DBAddonInternal for this add-on.
+   * @param {boolean} [aUpdated = false]
+   *        The add-on was updated, so we must record new modified time.
    */
   syncWithDB(aDBAddon, aUpdated = false) {
     logger.debug("Updating XPIState for " + JSON.stringify(aDBAddon));
     // If the add-on changes from disabled to enabled, we should re-check the modified time.
     // If this is a newly found add-on, it won't have an 'enabled' field but we
     // did a full recursive scan in that case, so we don't need to do it again.
     // We don't use aDBAddon.active here because it's not updated until after restart.
     let mustGetMod = (aDBAddon.visible && !aDBAddon.disabled && !this.enabled);
@@ -942,16 +969,18 @@ class XPIStateLocation extends Map {
       // Make a note that this state was restored from saved data.
       xpiState.wasRestored = true;
     }
   }
 
   /**
    * Returns a JSON-compatible representation of this location's state
    * data, to be saved to addonStartup.json.
+   *
+   * @returns {Object}
    */
   toJSON() {
     let json = {
       addons: {},
       staged: this.staged,
     };
 
     if (this.path) {
@@ -1137,16 +1166,18 @@ var XPIStates = {
     logger.debug("Migrated data: ${}", data);
 
     return data;
   },
 
   /**
    * Load extension state data from addonStartup.json, or migrates it
    * from legacy state preferences, if they exist.
+   *
+   * @returns {Object}
    */
   loadExtensionState() {
     let state;
     try {
       state = aomStartup.readStartupData();
     } catch (e) {
       logger.warn("Error parsing extensions state: ${error}",
                   {error: e});
@@ -1165,21 +1196,22 @@ var XPIStates = {
     logger.debug("Loaded add-on state: ${}", state);
     return state || {};
   },
 
   /**
    * Walk through all install locations, highest priority first,
    * comparing the on-disk state of extensions to what is stored in prefs.
    *
-   * @param {bool} [ignoreSideloads = true]
+   * @param {boolean} [ignoreSideloads = true]
    *        If true, ignore changes in scopes where we don't accept
    *        side-loads.
    *
-   * @return true if anything has changed.
+   * @returns {boolean}
+   *        True if anything has changed.
    */
   getInstallState(ignoreSideloads = true) {
     if (!this.db) {
       this.db = new Map();
     }
 
     let oldState = this.initialStateData || this.loadExtensionState();
     this.initialStateData = oldState;
@@ -1245,18 +1277,27 @@ var XPIStates = {
 
     logger.debug("getInstallState changed: ${rv}, state: ${state}",
         {rv: changed, state: this.db});
     return changed;
   },
 
   /**
    * Get the Map of XPI states for a particular location.
-   * @param name The name of the install location.
-   * @return XPIStateLocation (id -> XPIState) or null if there are no add-ons in the location.
+   *
+   * @param {string} name
+   *        The name of the install location.
+   * @param {string?} [path]
+   *        The expected path of the location, if known.
+   * @param {Object?} [saved]
+   *        The saved data for the location, as read from the
+   *        addonStartup.json file.
+   *
+   * @returns {XPIStateLocation?}
+   *        (id -> XPIState) or null if there are no add-ons in the location.
    */
   getLocation(name, path, saved) {
     let location = this.db.get(name);
 
     if (path && location && location.path != path) {
       location = null;
       saved = null;
     }
@@ -1269,35 +1310,40 @@ var XPIStates = {
       }
     }
     return location;
   },
 
   /**
    * Get the XPI state for a specific add-on in a location.
    * If the state is not in our cache, return null.
-   * @param aLocation The name of the location where the add-on is installed.
-   * @param aId       The add-on ID
-   * @return The XPIState entry for the add-on, or null.
+   *
+   * @param {string} aLocation
+   *        The name of the location where the add-on is installed.
+   * @param {string} aId
+   *        The add-on ID
+   *
+   * @returns {XPIState?}
+   *        The XPIState entry for the add-on, or null.
    */
   getAddon(aLocation, aId) {
     let location = this.db.get(aLocation);
     return location && location.get(aId);
   },
 
   /**
    * Find the highest priority location of an add-on by ID and return the
    * XPIState.
    * @param {string} aId
    *        The add-on IDa
    * @param {function} aFilter
    *        An optional filter to apply to install locations.  If provided,
    *        addons in locations that do not match the filter are not considered.
    *
-   * @return {XPIState?}
+   * @returns {XPIState?}
    */
   findAddon(aId, aFilter = location => true) {
     // Fortunately the Map iterator returns in order of insertion, which is
     // also our highest -> lowest priority order.
     for (let location of this.db.values()) {
       if (!aFilter(location)) {
         continue;
       }
@@ -1341,17 +1387,19 @@ var XPIStates = {
       if (addon.bootstrapped) {
         yield addon;
       }
     }
   },
 
   /**
    * Add a new XPIState for an add-on and synchronize it with the DBAddonInternal.
-   * @param aAddon DBAddonInternal for the new add-on.
+   *
+   * @param {DBAddonInternal} aAddon
+   *        The add-on to add.
    */
   addAddon(aAddon) {
     let location = this.getLocation(aAddon._installLocation.name);
     location.addAddon(aAddon);
   },
 
   /**
    * Save the current state of installed add-ons.
@@ -1376,33 +1424,40 @@ var XPIStates = {
         data[key] = loc;
       }
     }
     return data;
   },
 
   /**
    * Remove the XPIState for an add-on and save the new state.
-   * @param aLocation  The name of the add-on location.
-   * @param aId        The ID of the add-on.
+   *
+   * @param {string} aLocation
+   *        The name of the add-on location.
+   * @param {string} aId
+   *        The ID of the add-on.
+   *
    */
   removeAddon(aLocation, aId) {
     logger.debug("Removing XPIState for " + aLocation + ":" + aId);
     let location = this.db.get(aLocation);
     if (location) {
       location.delete(aId);
       if (location.size == 0) {
         this.db.delete(aLocation);
       }
       this.save();
     }
   },
 
   /**
    * Disable the XPIState for an add-on.
+   *
+   * @param {string} aId
+   *        The ID of the add-on.
    */
   disableAddon(aId) {
     logger.debug(`Disabling XPIState for ${aId}`);
     let state = this.findAddon(aId);
     if (state) {
       state.enabled = false;
     }
   },
@@ -1543,28 +1598,28 @@ var XPIProvider = {
         logger.warn("Cancel failed", e);
       }
     }
   },
 
   /**
    * Starts the XPI provider initializes the install locations and prefs.
    *
-   * @param  aAppChanged
-   *         A tri-state value. Undefined means the current profile was created
-   *         for this session, true means the profile already existed but was
-   *         last used with an application with a different version number,
-   *         false means that the profile was last used by this version of the
-   *         application.
-   * @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 {boolean?} aAppChanged
+   *        A tri-state value. Undefined means the current profile was created
+   *        for this session, true means the profile already existed but was
+   *        last used with an application with a different version number,
+   *        false means that the profile was last used by this version of the
+   *        application.
+   * @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
    */
   startup(aAppChanged, aOldAppVersion, aOldPlatformVersion) {
     function addDirectoryInstallLocation(aName, aKey, aPaths, aScope, aLocked) {
       try {
         var dir = FileUtils.getDir(aKey, aPaths);
       } catch (e) {
         // Some directories aren't defined on some platforms, ignore them
         logger.debug("Skipping unavailable install location " + aName);
@@ -2021,20 +2076,21 @@ var XPIProvider = {
       ChromeUtils.import("resource://gre/modules/TelemetrySession.jsm", {}).TelemetrySession;
     TelemetrySession.setAddOns(data);
   },
 
   /**
    * Check the staging directories of install locations for any add-ons to be
    * installed or add-ons to be uninstalled.
    *
-   * @param  aManifests
+   * @param {Object} aManifests
    *         A dictionary to add detected install manifests to for the purpose
    *         of passing through updated compatibility information
-   * @return true if an add-on was installed or uninstalled
+   * @returns {boolean}
+   *        True if an add-on was installed or uninstalled
    */
   processPendingFileChanges(aManifests) {
     let changed = false;
     for (let location of this.installLocations) {
       aManifests[location.name] = {};
       // We can't install or uninstall anything in locked locations
       if (location.locked) {
         continue;
@@ -2080,22 +2136,23 @@ var XPIProvider = {
   },
 
   /**
    * Installs any add-ons located in the extensions directory of the
    * application's distribution specific directory into the profile unless a
    * newer version already exists or the user has previously uninstalled the
    * distributed add-on.
    *
-   * @param  aManifests
-   *         A dictionary to add new install manifests to to save having to
-   *         reload them later
-   * @param  aAppChanged
-   *         See checkForChanges
-   * @return true if any new add-ons were installed
+   * @param {Object} aManifests
+   *        A dictionary to add new install manifests to to save having to
+   *        reload them later
+   * @param {string} [aAppChanged]
+   *        See checkForChanges
+   * @returns {boolean}
+   *        True if any new add-ons were installed
    */
   installDistributionAddons(aManifests, aAppChanged) {
     let distroDir;
     try {
       distroDir = FileUtils.getDir(KEY_APP_DISTRIBUTION, [DIR_EXTENSIONS]);
     } catch (e) {
       return false;
     }
@@ -2182,29 +2239,30 @@ var XPIProvider = {
     return Array.from(XPIDatabase.getAddons())
                 .filter(addon => addon.dependencies.includes(aAddon.id));
   },
 
   /**
    * Checks for any changes that have occurred since the last time the
    * application was launched.
    *
-   * @param  aAppChanged
-   *         A tri-state value. Undefined means the current profile was created
-   *         for this session, true means the profile already existed but was
-   *         last used with an application with a different version number,
-   *         false means that the profile was last used by this version of the
-   *         application.
-   * @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 true if a change requiring a restart was detected
+   * @param {boolean?} [aAppChanged]
+   *        A tri-state value. Undefined means the current profile was created
+   *        for this session, true means the profile already existed but was
+   *        last used with an application with a different version number,
+   *        false means that the profile was last used by this version of the
+   *        application.
+   * @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}
+   *        True if a change requiring a restart was detected
    */
   checkForChanges(aAppChanged, aOldAppVersion, aOldPlatformVersion) {
     logger.debug("checkForChanges");
 
     // Keep track of whether and why we need to open and update the database at
     // startup time.
     let updateReasons = [];
     if (aAppChanged) {
@@ -2325,19 +2383,20 @@ var XPIProvider = {
     return addons.filter(addon => (addon.seen === false &&
                                    addon.permissions & AddonManager.PERM_CAN_ENABLE));
   },
 
   /**
    * Called to test whether this provider supports installing a particular
    * mimetype.
    *
-   * @param  aMimetype
-   *         The mimetype to check for
-   * @return true if the mimetype is application/x-xpinstall
+   * @param {string} aMimetype
+   *        The mimetype to check for
+   * @returns {boolean}
+   *        True if the mimetype is application/x-xpinstall
    */
   supportsMimetype(aMimetype) {
     return aMimetype == "application/x-xpinstall";
   },
 
   // Identify temporary install IDs.
   isTemporaryInstallID(id) {
     return id.endsWith(TEMPORARY_ADDON_SUFFIX);
@@ -2357,22 +2416,23 @@ var XPIProvider = {
   setStartupData(aID, aData) {
     let state = XPIStates.findAddon(aID);
     state.startupData = aData;
     XPIStates.save();
   },
 
   /**
    * Returns an Addon corresponding to an instance ID.
-   * @param aInstanceID
+   *
+   * @param {Symbol} aInstanceID
    *        An Addon Instance ID
-   * @return {Promise}
-   * @resolves The found Addon or null if no such add-on exists.
-   * @rejects  Never
-   * @throws if the aInstanceID argument is not specified
+   *
+   * @returns {AddonInternal?}
+   *
+   * @throws if the aInstanceID argument is not valid.
    */
    getAddonByInstanceID(aInstanceID) {
      let id = this.getAddonIDByInstanceID(aInstanceID);
      if (id) {
        return this.syncGetAddonByID(id);
      }
 
      return null;
@@ -2390,28 +2450,29 @@ var XPIProvider = {
      }
 
      return null;
    },
 
   /**
    * Removes an AddonInstall from the list of active installs.
    *
-   * @param  install
-   *         The AddonInstall to remove
+   * @param {AddonInstall} aInstall
+   *        The AddonInstall to remove
    */
   removeActiveInstall(aInstall) {
     this.installs.delete(aInstall);
   },
 
   /**
    * Called to get an Addon with a particular ID.
    *
-   * @param  aId
-   *         The ID of the add-on to retrieve
+   * @param {string} aId
+   *        The ID of the add-on to retrieve
+   * @returns {Addon?}
    */
   async getAddonByID(aId) {
     let aAddon = await XPIDatabase.getVisibleAddonForID(aId);
     return aAddon ? aAddon.wrapper : null;
   },
 
   /**
    * Synchronously returns the Addon object for the add-on with the
@@ -2431,34 +2492,35 @@ var XPIProvider = {
   syncGetAddonByID(aId) {
     let aAddon = XPIDatabase.syncGetVisibleAddonForID(aId);
     return aAddon ? aAddon.wrapper : null;
   },
 
   /**
    * Called to get Addons of a particular type.
    *
-   * @param  aTypes
-   *         An array of types to fetch. Can be null to get all types.
+   * @param {Array<string>?} aTypes
+   *        An array of types to fetch. Can be null to get all types.
+   * @returns {Addon[]}
    */
   async getAddonsByTypes(aTypes) {
     let typesToGet = getAllAliasesForTypes(aTypes);
     if (typesToGet && !typesToGet.some(type => ALL_EXTERNAL_TYPES.has(type))) {
       return [];
     }
 
     let addons = await XPIDatabase.getVisibleAddons(typesToGet);
     return addons.map(a => a.wrapper);
   },
 
   /**
    * Called to get active Addons of a particular type
    *
-   * @param  aTypes
-   *         An array of types to fetch. Can be null to get all types.
+   * @param {Array<string>?} aTypes
+   *        An array of types to fetch. Can be null to get all types.
    * @returns {Promise<Array<Addon>>}
    */
   async getActiveAddons(aTypes) {
     // If we already have the database loaded, returning full info is fast.
     if (this.isDBLoaded) {
       let addons = await this.getAddonsByTypes(aTypes);
       return {
         addons: addons.filter(addon => addon.isActive),
@@ -2495,29 +2557,31 @@ var XPIProvider = {
 
     return {addons: result, fullData: false};
   },
 
 
   /**
    * Obtain an Addon having the specified Sync GUID.
    *
-   * @param  aGUID
-   *         String GUID of add-on to retrieve
+   * @param {string} aGUID
+   *        String GUID of add-on to retrieve
+   * @returns {Addon?}
    */
   async getAddonBySyncGUID(aGUID) {
     let addon = await XPIDatabase.getAddonBySyncGUID(aGUID);
     return addon ? addon.wrapper : null;
   },
 
   /**
    * Called to get Addons that have pending operations.
    *
-   * @param  aTypes
-   *         An array of types to fetch. Can be null to get all types
+   * @param {Array<string>?} aTypes
+   *        An array of types to fetch. Can be null to get all types
+   * @returns {Addon[]}
    */
   async getAddonsWithOperationsByTypes(aTypes) {
     let typesToGet = getAllAliasesForTypes(aTypes);
 
     let aAddons = await XPIDatabase.getVisibleAddonsWithPendingOperations(typesToGet);
     let results = aAddons.map(a => a.wrapper);
     for (let install of XPIProvider.installs) {
       if (install.state == AddonManager.STATE_INSTALLED &&
@@ -2526,38 +2590,39 @@ var XPIProvider = {
     }
     return results;
   },
 
   /**
    * Called to get the current AddonInstalls, optionally limiting to a list of
    * types.
    *
-   * @param  aTypes
-   *         An array of types or null to get all types
+   * @param {Array<string>?} aTypes
+   *        An array of types or null to get all types
+   * @returns {AddonInstall[]}
    */
   getInstallsByTypes(aTypes) {
     let results = [...this.installs];
     if (aTypes) {
       results = results.filter(install => {
         return aTypes.includes(getExternalType(install.type));
       });
     }
 
     return results.map(install => install.wrapper);
   },
 
   /**
    * Called when a new add-on has been enabled when only one add-on of that type
    * can be enabled.
    *
-   * @param  aId
-   *         The ID of the newly enabled add-on
-   * @param  aType
-   *         The type of the newly enabled add-on
+   * @param {string} aId
+   *        The ID of the newly enabled add-on
+   * @param {string} aType
+   *        The type of the newly enabled add-on
    */
   addonChanged(aId, aType) {
     // We only care about themes in this provider
     if (!isTheme(aType))
       return;
 
     let addons = XPIDatabase.getAddonsByType("webextension-theme");
     for (let theme of addons) {
@@ -2608,17 +2673,17 @@ var XPIProvider = {
       return;
 
     for (let [id, val] of this.activeAddons) {
       connection.setAddonOptions(
         id, { global: val.bootstrapScope });
     }
   },
 
-  /**
+  /*
    * Notified when a preference we're interested in has changed.
    *
    * @see nsIObserver
    */
   observe(aSubject, aTopic, aData) {
     if (aTopic == NOTIFICATION_FLUSH_PERMISSIONS) {
       if (!aData || aData == XPI_PERMISSION) {
         this.importPermissions();
@@ -2651,31 +2716,30 @@ var XPIProvider = {
   },
 
   /**
    * Loads a bootstrapped add-on's bootstrap.js into a sandbox and the reason
    * values as constants in the scope. This will also add information about the
    * add-on to the bootstrappedAddons dictionary and notify the crash reporter
    * that new add-ons have been loaded.
    *
-   * @param  aId
-   *         The add-on's ID
-   * @param  aFile
-   *         The nsIFile for the add-on
-   * @param  aVersion
-   *         The add-on's version
-   * @param  aType
-   *         The type for the add-on
-   * @param  aRunInSafeMode
-   *         Boolean indicating whether the add-on can run in safe mode.
-   * @param  aDependencies
-   *         An array of add-on IDs on which this add-on depends.
-   * @param  hasEmbeddedWebExtension
-   *         Boolean indicating whether the add-on has an embedded webextension.
-   * @return a JavaScript scope
+   * @param {string} aId
+   *        The add-on's ID
+   * @param {nsIFile} aFile
+   *        The nsIFile for the add-on
+   * @param {string} aVersion
+   *        The add-on's version
+   * @param {string} aType
+   *        The type for the add-on
+   * @param {boolean} aRunInSafeMode
+   *        Boolean indicating whether the add-on can run in safe mode.
+   * @param {string[]} aDependencies
+   *        An array of add-on IDs on which this add-on depends.
+   * @param {boolean} hasEmbeddedWebExtension
+   *        Boolean indicating whether the add-on has an embedded webextension.
    */
   loadBootstrapScope(aId, aFile, aVersion, aType, aRunInSafeMode, aDependencies,
                      hasEmbeddedWebExtension) {
     this.activeAddons.set(aId, {
       bootstrapScope: null,
       // a Symbol passed to this add-on, which it can use to identify itself
       instanceID: Symbol(aId),
       started: false,
@@ -2740,42 +2804,42 @@ var XPIProvider = {
     let wrappedJSObject = { id: aId, options: { global: activeAddon.bootstrapScope }};
     Services.obs.notifyObservers({ wrappedJSObject }, "toolbox-update-addon-options");
   },
 
   /**
    * Unloads a bootstrap scope by dropping all references to it and then
    * updating the list of active add-ons with the crash reporter.
    *
-   * @param  aId
-   *         The add-on's ID
+   * @param {string} aId
+   *        The add-on's ID
    */
   unloadBootstrapScope(aId) {
     this.activeAddons.delete(aId);
     this.addAddonsToCrashReporter();
 
     // Notify the BrowserToolboxProcess that an addon has been unloaded.
     let wrappedJSObject = { id: aId, options: { global: null }};
     Services.obs.notifyObservers({ wrappedJSObject }, "toolbox-update-addon-options");
   },
 
   /**
    * Calls a bootstrap method for an add-on.
    *
-   * @param  aAddon
-   *         An object representing the add-on, with `id`, `type` and `version`
-   * @param  aFile
-   *         The nsIFile for the add-on
-   * @param  aMethod
-   *         The name of the bootstrap method to call
-   * @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
+   * @param {Object} aAddon
+   *        An object representing the add-on, with `id`, `type` and `version`
+   * @param {nsIFile} aFile
+   *        The nsIFile for the add-on
+   * @param {string} aMethod
+   *        The name of the bootstrap method to call
+   * @param {integer} aReason
+   *        The reason flag to pass to the bootstrap's startup method
+   * @param {Object?} [aExtraParams]
+   *        An object of additional key/value pairs to pass to the method in
+   *        the params argument
    */
   callBootstrapMethod(aAddon, aFile, aMethod, aReason, aExtraParams) {
     if (!aAddon.id || !aAddon.version || !aAddon.type) {
       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);
@@ -2897,28 +2961,29 @@ var XPIProvider = {
     }
   },
 
   /**
    * Updates the disabled state for an add-on. Its appDisabled property will be
    * calculated and if the add-on is changed the database will be saved and
    * appropriate notifications will be sent out to the registered AddonListeners.
    *
-   * @param  aAddon
-   *         The DBAddonInternal to update
-   * @param  aUserDisabled
-   *         Value for the userDisabled property. If undefined the value will
-   *         not change
-   * @param  aSoftDisabled
-   *         Value for the softDisabled property. If undefined the value will
-   *         not change. If true this will force userDisabled to be true
-   * @param {boolean} aBecauseSelecting
+   * @param {DBAddonInternal} aAddon
+   *        The DBAddonInternal to update
+   * @param {boolean?} [aUserDisabled]
+   *        Value for the userDisabled property. If undefined the value will
+   *        not change
+   * @param {boolean?} [aSoftDisabled]
+   *        Value for the softDisabled property. If undefined the value will
+   *        not change. If true this will force userDisabled to be true
+   * @param {boolean?} [aBecauseSelecting]
    *        True if we're disabling this add-on because we're selecting
    *        another.
-   * @return a tri-state indicating the action taken for the add-on:
+   * @returns {boolean?}
+   *       A tri-state indicating the action taken for the add-on:
    *           - undefined: The add-on did not change state
    *           - true: The add-on because disabled
    *           - false: The add-on became enabled
    * @throws if addon is not a DBAddonInternal
    */
   updateAddonDisabledState(aAddon, aUserDisabled, aSoftDisabled, aBecauseSelecting) {
     if (!(aAddon.inDatabase))
       throw new Error("Can only update addon states for installed addons.");
@@ -3354,24 +3419,21 @@ AddonInternal.prototype = {
   },
 
   /**
    * 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.
    *
-   * @param  aKey
-   *         The key that this object is being serialized as in the JSON.
-   *         Unused here since this is always the main object serialized
-   *
-   * @return an object containing copies of the properties of this object
-   *         ignoring private properties, functions, getters and setters
+   * @returns {Object}
+   *       An object containing copies of the properties of this object
+   *       ignoring private properties, functions, getters and setters.
    */
-  toJSON(aKey) {
+  toJSON() {
     let obj = {};
     for (let prop in this) {
       // Ignore the wrapper property
       if (prop == "wrapper")
         continue;
 
       // Ignore private properties
       if (prop.substring(0, 1) == "_")
@@ -3395,18 +3457,18 @@ AddonInternal.prototype = {
     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  aObj
-   *         A JS object containing the cached metadata
+   * @param {Object} aObj
+   *        A JS object containing the cached metadata
    */
   importMetadata(aObj) {
     for (let prop of PENDING_INSTALL_METADATA) {
       if (!(prop in aObj))
         continue;
 
       this[prop] = aObj[prop];
     }
@@ -3451,16 +3513,19 @@ AddonInternal.prototype = {
 
     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 = {
   get __AddonInternal__() {
     return AppConstants.DEBUG ? addonFor(this) : undefined;
@@ -3868,17 +3933,17 @@ AddonWrapper.prototype = {
 
   /**
    * 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.
    *
-   * @return Promise
+   * @returns {Promise}
    */
   reload() {
     return new Promise((resolve) => {
       const addon = addonFor(this);
 
       logger.debug(`reloading add-on ${addon.id}`);
 
       if (!this.temporarilyInstalled) {
@@ -3895,20 +3960,20 @@ AddonWrapper.prototype = {
   },
 
   /**
    * 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  aPath
-   *         The path in the add-on to get the URI for or null to get a URI to
-   *         the file or directory the add-on is installed as.
-   * @return an nsIURI
+   * @param {string?} aPath
+   *        The path in the add-on to get the URI for or null to get a URI to
+   *        the file or directory the add-on is installed as.
+   * @returns {nsIURI}
    */
   getResourceURI(aPath) {
     let addon = addonFor(this);
     if (!aPath)
       return Services.io.newFileURI(addon._sourceBundle);
 
     return getURIForResourceInFile(addon._sourceBundle, aPath);
   }
@@ -4065,22 +4130,22 @@ function forwardInstallMethods(cls, meth
  */
 class DirectoryInstallLocation {
   /**
    * Each add-on installed in the location is either a directory containing the
    * add-on's files or a text file containing an absolute path to the directory
    * containing the add-ons files. The directory or text file must have the same
    * name as the add-on's ID.
    *
-   * @param  aName
-   *         The string identifier for the install location
-   * @param  aDirectory
-   *         The nsIFile directory for the install location
-   * @param  aScope
-   *         The scope of add-ons installed in this location
+   * @param {string} aName
+   *        The string identifier for the install location
+   * @param {nsIFile} aDirectory
+   *        The nsIFile directory for the install location
+   * @param {integer} aScope
+   *        The scope of add-ons installed in this location
   */
   constructor(aName, aDirectory, aScope) {
     this._name = aName;
     this.locked = true;
     this._directory = aDirectory;
     this._scope = aScope;
     this._IDToFileMap = {};
     this._linkedAddons = [];
@@ -4098,19 +4163,20 @@ class DirectoryInstallLocation {
 
   get path() {
     return this._directory && this._directory.path;
   }
 
   /**
    * Reads a directory linked to in a file.
    *
-   * @param   file
-   *          The file containing the directory path
-   * @return  An nsIFile object representing the linked directory.
+   * @param {nsIFile} aFile
+   *        The file containing the directory path
+   * @returns {nsIFile}
+   *        An nsIFile object representing the linked directory.
    */
   _readDirectoryFromFile(aFile) {
     let linkedDirectory;
     if (aFile.isSymlink()) {
       linkedDirectory = aFile.clone();
       try {
         linkedDirectory.normalize();
       } catch (e) {
@@ -4155,16 +4221,20 @@ class DirectoryInstallLocation {
     }
 
     logger.warn("File pointer " + aFile.path + " does not contain a path");
     return null;
   }
 
   /**
    * Finds all the add-ons installed in this location.
+   *
+   * @param {boolean} [rescan = false]
+   *        True if the directory should be re-scanned, even if it has
+   *        already been initialized.
    */
   _readAddons(rescan = false) {
     if ((this.initialized && !rescan) || !this._directory) {
       return;
     }
     this.initialized = true;
 
     // Use a snapshot of the directory contents to avoid possible issues with
@@ -4222,68 +4292,75 @@ class DirectoryInstallLocation {
    * Gets the scope of this install location.
    */
   get scope() {
     return this._scope;
   }
 
   /**
    * Gets an array of nsIFiles for add-ons installed in this location.
+   *
+   * @param {boolean} [rescan = false]
+   *        True if the directory should be re-scanned, even if it has
+   *        already been initialized.
+   *
+   * @returns {nsIFile[]}
    */
   getAddonLocations(rescan = false) {
     this._readAddons(rescan);
 
     let locations = new Map();
     for (let id in this._IDToFileMap) {
       locations.set(id, this._IDToFileMap[id].clone());
     }
     return locations;
   }
 
   /**
    * Gets the directory that the add-on with the given ID is installed in.
    *
-   * @param  aId
-   *         The ID of the add-on
-   * @return The nsIFile
+   * @param {string} aId
+   *        The ID of the add-on
+   * @returns {nsIFile}
    * @throws if the ID does not match any of the add-ons installed
    */
   getLocationForID(aId) {
     if (!(aId in this._IDToFileMap))
       this._readAddons();
 
     if (aId in this._IDToFileMap)
       return this._IDToFileMap[aId].clone();
     throw new Error("Unknown add-on ID " + aId);
   }
 
   /**
    * Returns true if the given addon was installed in this location by a text
    * file pointing to its real path.
    *
-   * @param aId
+   * @param {string} aId
    *        The ID of the addon
+   * @returns {boolean}
    */
   isLinkedAddon(aId) {
     return this._linkedAddons.includes(aId);
   }
 }
 
 /**
  * An extension of DirectoryInstallLocation which adds methods to installing
  * and removing add-ons from the directory at runtime.
  */
 class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
   /**
-   * @param  aName
-   *         The string identifier for the install location
-   * @param  aDirectory
-   *         The nsIFile directory for the install location
-   * @param  aScope
-   *         The scope of add-ons installed in this location
+   * @param {string} aName
+   *        The string identifier for the install location
+   * @param {nsIFile} aDirectory
+   *        The nsIFile directory for the install location
+   * @param {integer} aScope
+   *        The scope of add-ons installed in this location
    */
   constructor(aName, aDirectory, aScope) {
     super(aName, aDirectory, aScope);
 
     this.locked = false;
     this._stagingDirLock = 0;
   }
 }
@@ -4337,24 +4414,24 @@ class BuiltInInstallLocation extends Dir
 /**
  * An object which identifies a directory install location for system add-ons
  * updates.
  */
 class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
   /**
     * The location consists of a directory which contains the add-ons installed.
     *
-    * @param  aName
-    *         The string identifier for the install location
-    * @param  aDirectory
-    *         The nsIFile directory for the install location
-    * @param  aScope
-    *         The scope of add-ons installed in this location
-    * @param  aResetSet
-    *         True to throw away the current add-on set
+    * @param {string} aName
+    *        The string identifier for the install location
+    * @param {nsIFile} aDirectory
+    *        The nsIFile directory for the install location
+    * @param {integer} aScope
+    *        The scope of add-ons installed in this location
+    * @param {boolean} aResetSet
+    *        True to throw away the current add-on set
     */
   constructor(aName, aDirectory, aScope, aResetSet) {
     let addonSet = SystemAddonInstallLocation._loadAddonSet();
     let directory = null;
 
     // The system add-on update directory is stored in a pref.
     // Therefore, this is looked up before calling the
     // constructor on the superclass.
@@ -4378,16 +4455,18 @@ class SystemAddonInstallLocation extends
       this.resetAddonSet();
     }
 
     this.locked = false;
   }
 
   /**
    * Reads the current set of system add-ons
+   *
+   * @returns {Object}
    */
   static _loadAddonSet() {
     try {
       let setStr = Services.prefs.getStringPref(PREF_SYSTEM_ADDON_SET, null);
       if (setStr) {
         let addonSet = JSON.parse(setStr);
         if ((typeof addonSet == "object") && addonSet.schema == 1) {
           return addonSet;
@@ -4415,16 +4494,18 @@ class SystemAddonInstallLocation extends
       }
     }
 
     return addons;
   }
 
   /**
    * Tests whether updated system add-ons are expected.
+   *
+   * @returns {boolean}
    */
   isActive() {
     return this._directory != null;
   }
 }
 
 forwardInstallMethods(SystemAddonInstallLocation,
                       ["cleanDirectories", "cleanStagingDir", "getStagingDir",
@@ -4445,22 +4526,22 @@ const TemporaryInstallLocation = { locke
 /**
  * An object that identifies a registry install location for add-ons. The location
  * consists of a registry key which contains string values mapping ID to the
  * path where an add-on is installed
  *
  */
 class WinRegInstallLocation extends DirectoryInstallLocation {
   /**
-    * @param  aName
-    *         The string identifier of this Install Location.
-    * @param  aRootKey
-    *         The root key (one of the ROOT_KEY_ values from nsIWindowsRegKey).
-    * @param  scope
-    *         The scope of add-ons installed in this location
+    * @param {string} aName
+    *        The string identifier of this Install Location.
+    * @param {integer} aRootKey
+    *        The root key (one of the ROOT_KEY_ values from nsIWindowsRegKey).
+    * @param {integer} aScope
+    *        The scope of add-ons installed in this location
     */
   constructor(aName, aRootKey, aScope) {
     super(aName, undefined, aScope);
 
     this.locked = true;
     this._name = aName;
     this._rootKey = aRootKey;
     this._scope = aScope;
@@ -4499,18 +4580,18 @@ class WinRegInstallLocation extends Dire
 
     return "SOFTWARE\\" + appVendor + appName;
   }
 
   /**
    * Read the registry and build a mapping between ID and path for each
    * installed add-on.
    *
-   * @param  key
-   *         The key that contains the ID to path mapping
+   * @param {nsIWindowsRegKey} aKey
+   *        The key that contains the ID to path mapping
    */
   _readAddons(aKey) {
     let count = aKey.valueCount;
     for (let i = 0; i < count; ++i) {
       let id = aKey.getValueName(i);
 
       let file = new nsIFile(aKey.readStringValue(id));
 
@@ -4525,17 +4606,17 @@ class WinRegInstallLocation extends Dire
 
   /**
    * Gets the name of this install location.
    */
   get name() {
     return this._name;
   }
 
-  /**
+  /*
    * @see DirectoryInstallLocation
    */
   isLinkedAddon(aId) {
     return true;
   }
 }
 
 var XPIInternal = {