Bug 1363925: Part 7a - Turn on valid-jsdoc rule for XPIInstall.jsm. r?zombie draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 21 Apr 2018 20:28:19 -0700
changeset 786317 e637881fe80460f95c6c72f26213c68350f323f0
parent 786316 d0bde577daca1cc4091f02e6c678ef72a42bfb68
child 786318 664bc2adc4354b9da57cbbde8c99ee48c5a6c500
push id107433
push usermaglione.k@gmail.com
push dateSun, 22 Apr 2018 22:24:27 +0000
reviewerszombie
bugs1363925
milestone61.0a1
Bug 1363925: Part 7a - Turn on valid-jsdoc rule for XPIInstall.jsm. r?zombie MozReview-Commit-ID: Ch1NaeLAxtJ
toolkit/mozapps/extensions/internal/XPIInstall.jsm
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.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 = [
   "UpdateChecker",
   "XPIInstall",
   "verifyBundleSignedState",
 ];
 
 /* globals DownloadAddonInstall, LocalAddonInstall */
 
@@ -140,17 +142,17 @@ function getFile(path, base = null) {
   let file = base.clone();
   file.appendRelativePath(path);
   return file;
 }
 
 /**
  * Sends local and remote notifications to flush a JAR file cache entry
  *
- * @param aJarFile
+ * @param {nsIFile} aJarFile
  *        The ZIP/XPI/JAR file as a nsIFile
  */
 function flushJarCache(aJarFile) {
   Services.obs.notifyObservers(aJarFile, "flush-cache-entry");
   Services.mm.broadcastAsyncMessage(MSG_JAR_FLUSH, aJarFile.path);
 }
 
 const PREF_EM_UPDATE_BACKGROUND_URL   = "extensions.update.background.url";
@@ -417,17 +419,18 @@ XPIPackage = class XPIPackage extends Pa
 
 /**
  * Determine the reason to pass to an extension's bootstrap methods when
  * switch between versions.
  *
  * @param {string} oldVersion The version of the existing extension instance.
  * @param {string} newVersion The version of the extension being installed.
  *
- * @return {BOOSTRAP_REASONS.ADDON_UPGRADE|BOOSTRAP_REASONS.ADDON_DOWNGRADE}
+ * @returns {integer}
+ *        BOOSTRAP_REASONS.ADDON_UPGRADE or BOOSTRAP_REASONS.ADDON_DOWNGRADE
  */
 function newVersionReason(oldVersion, newVersion) {
   return Services.vc.compare(oldVersion, newVersion) <= 0 ?
          BOOTSTRAP_REASONS.ADDON_UPGRADE :
          BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
 }
 
 // Behaves like Promise.all except waits for all promises to resolve/reject
@@ -450,51 +453,53 @@ function waitForAllPromises(promises) {
 
 function EM_R(aProperty) {
   return gRDF.GetResource(PREFIX_NS_EM + aProperty);
 }
 
 /**
  * Converts an RDF literal, resource or integer into a string.
  *
- * @param  aLiteral
- *         The RDF object to convert
- * @return a string if the object could be converted or null
+ * @param {nsISupports} aLiteral
+ *        The RDF object to convert
+ * @returns {string?}
+ *        A string if the object could be converted or null
  */
 function getRDFValue(aLiteral) {
   if (aLiteral instanceof Ci.nsIRDFLiteral)
     return aLiteral.Value;
   if (aLiteral instanceof Ci.nsIRDFResource)
     return aLiteral.Value;
   if (aLiteral instanceof Ci.nsIRDFInt)
     return aLiteral.Value;
   return null;
 }
 
 /**
  * Gets an RDF property as a string
  *
- * @param  aDs
- *         The RDF datasource to read the property from
- * @param  aResource
- *         The RDF resource to read the property from
- * @param  aProperty
- *         The property to read
- * @return a string if the property existed or null
+ * @param {nsIRDFDataSource} aDs
+ *        The RDF datasource to read the property from
+ * @param {nsIRDFResource} aResource
+ *        The RDF resource to read the property from
+ * @param {string} aProperty
+ *        The property to read
+ * @returns {string?}
+ *        A string if the property existed or null
  */
 function getRDFProperty(aDs, aResource, aProperty) {
   return getRDFValue(aDs.GetTarget(aResource, EM_R(aProperty), true));
 }
 
 /**
  * Reads an AddonInternal object from a manifest stream.
  *
- * @param  aUri
- *         A |file:| or |jar:| URL for the manifest
- * @return an AddonInternal object
+ * @param {nsIURI} aUri
+ *        A |file:| or |jar:| URL for the manifest
+ * @returns {AddonInternal}
  * @throws if the install manifest in the stream is corrupt or could not
  *         be read
  */
 async function loadManifestFromWebManifest(aUri) {
   // We're passed the URI for the manifest file. Get the URI for its
   // parent directory.
   let uri = Services.io.newURI("./", null, aUri);
 
@@ -612,21 +617,21 @@ async function loadManifestFromWebManife
   addon.softDisabled = addon.blocklistState == nsIBlocklistService.STATE_SOFTBLOCKED;
 
   return addon;
 }
 
 /**
  * Reads an AddonInternal object from an RDF stream.
  *
- * @param  aUri
- *         The URI that the manifest is being read from
- * @param  aData
- *         The manifest text
- * @return an AddonInternal object
+ * @param {nsIURI} aUri
+ *        The URI that the manifest is being read from
+ * @param {string} aData
+ *        The manifest text
+ * @returns {AddonInternal}
  * @throws if the install manifest in the RDF stream is corrupt or could not
  *         be read
  */
 async function loadManifestFromRDF(aUri, aData) {
   function getPropertyArray(aDs, aSource, aProperty) {
     let values = [];
     let targets = aDs.GetTargets(aSource, EM_R(aProperty), true);
     while (targets.hasMoreElements())
@@ -634,28 +639,29 @@ async function loadManifestFromRDF(aUri,
 
     return values;
   }
 
   /**
    * Reads locale properties from either the main install manifest root or
    * an em:localized section in the install manifest.
    *
-   * @param  aDs
-   *         The nsIRDFDatasource to read from
-   * @param  aSource
-   *         The nsIRDFResource to read the properties from
-   * @param  isDefault
-   *         True if the locale is to be read from the main install manifest
-   *         root
-   * @param  aSeenLocales
-   *         An array of locale names already seen for this install manifest.
-   *         Any locale names seen as a part of this function will be added to
-   *         this array
-   * @return an object containing the locale properties
+   * @param {nsIRDFDataSource} aDs
+   *         The datasource to read from.
+   * @param {nsIRDFResource} aSource
+   *         The resource to read the properties from.
+   * @param {boolean} isDefault
+   *        True if the locale is to be read from the main install manifest
+   *        root
+   * @param {string[]} aSeenLocales
+   *        An array of locale names already seen for this install manifest.
+   *        Any locale names seen as a part of this function will be added to
+   *        this array
+   * @returns {Object}
+   *        an object containing the locale properties
    */
   function readLocale(aDs, aSource, isDefault, aSeenLocales) {
     let locale = { };
     if (!isDefault) {
       locale.locales = [];
       let targets = ds.GetTargets(aSource, EM_R("locale"), true);
       while (targets.hasMoreElements()) {
         let localeName = getRDFValue(targets.getNext());
@@ -940,29 +946,44 @@ var loadManifest = async function(aPacka
   await addon.updateBlocklistState({oldAddon: aOldAddon});
   addon.appDisabled = !isUsableAddon(addon);
 
   defineSyncGUID(addon);
 
   return addon;
 };
 
+/**
+ * Loads an add-on's manifest from the given file or directory.
+ *
+ * @param {nsIFile} aFile
+ *        The file to load the manifest from.
+ * @param {InstallLocation} aInstallLocation
+ *        The install location the add-on is installed in, or will be
+ *        installed to.
+ * @param {AddonInternal?} aOldAddon
+ *        The currently-installed add-on with the same ID, if one exist.
+ *        This is used to migrate user settings like the add-on's
+ *        disabled state.
+ * @returns {AddonInternal}
+ *        The parsed Addon object for the file's manifest.
+ */
 var loadManifestFromFile = async function(aFile, aInstallLocation, aOldAddon) {
   let pkg = Package.get(aFile);
   try {
     let addon = await loadManifest(pkg, aInstallLocation, aOldAddon);
     return addon;
   } finally {
     pkg.close();
   }
 };
 
-/**
- * A synchronous method for loading an add-on's manifest. This should only ever
- * be used during startup or a sync load of the add-ons DB
+/*
+ * A synchronous method for loading an add-on's manifest. Do not use
+ * this.
  */
 function syncLoadManifestFromFile(aFile, aInstallLocation, aOldAddon) {
   return XPIInternal.awaitPromise(loadManifestFromFile(aFile, aInstallLocation, aOldAddon));
 }
 
 function flushChromeCaches() {
   // Init this, so it will get the notification.
   Services.obs.notifyObservers(null, "startupcache-invalidate");
@@ -971,30 +992,44 @@ function flushChromeCaches() {
   // Also dispatch this event to child processes
   Services.mm.broadcastAsyncMessage(MSG_MESSAGE_MANAGER_CACHES_FLUSH, null);
 }
 
 /**
  * Creates and returns a new unique temporary file. The caller should delete
  * the file when it is no longer needed.
  *
- * @return an nsIFile that points to a randomly named, initially empty file in
- *         the OS temporary files directory
+ * @returns {nsIFile}
+ *       An nsIFile that points to a randomly named, initially empty file in
+ *       the OS temporary files directory
  */
 function getTemporaryFile() {
   let file = FileUtils.getDir(KEY_TEMPDIR, []);
   let random = Math.round(Math.random() * 36 ** 3).toString(36);
   file.append("tmp-" + random + ".xpi");
   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
   return file;
 }
 
 /**
  * Returns the signedState for a given return code and certificate by verifying
  * it against the expected ID.
+ *
+ * @param {nsresult} aRv
+ *        The result code returned by the signature checker for the
+ *        signature check operation.
+ * @param {nsIX509Cert?} aCert
+ *        The certificate the add-on was signed with, if a valid
+ *        certificate exists.
+ * @param {string?} aAddonID
+ *        The expected ID of the add-on. If passed, this must match the
+ *        ID in the certificate's CN field.
+ * @returns {number}
+ *        A SIGNEDSTATE result code constant, as defined on the
+ *        AddonManager class.
  */
 function getSignedStatus(aRv, aCert, aAddonID) {
   let expectedCommonName = aAddonID;
   if (aAddonID && aAddonID.length > 64) {
     let data = new Uint8Array(new TextEncoder().encode(aAddonID));
 
     let crypto = CryptoHash("sha256");
     crypto.update(data, data.length);
@@ -1046,46 +1081,48 @@ function shouldVerifySignedState(aAddon)
   // of the signed types.
   return AddonSettings.ADDON_SIGNING && SIGNED_TYPES.has(aAddon.type);
 }
 
 /**
  * Verifies that a bundle's contents are all correctly signed by an
  * AMO-issued certificate
  *
- * @param  aBundle
- *         the nsIFile for the bundle to check, either a directory or zip file
- * @param  aAddon
- *         the add-on object to verify
- * @return a Promise that resolves to an AddonManager.SIGNEDSTATE_* constant.
+ * @param {nsIFile}aBundle
+ *        The nsIFile for the bundle to check, either a directory or zip file.
+ * @param {AddonInternal} aAddon
+ *        The add-on object to verify.
+ * @returns {Prommise<number>}
+ *        A Promise that resolves to an AddonManager.SIGNEDSTATE_* constant.
  */
 var verifyBundleSignedState = async function(aBundle, aAddon) {
   let pkg = Package.get(aBundle);
   try {
     let {signedState} = await pkg.verifySignedState(aAddon);
     return signedState;
   } finally {
     pkg.close();
   }
 };
 
 /**
  * Replaces %...% strings in an addon url (update and updateInfo) with
  * appropriate values.
  *
- * @param  aAddon
- *         The AddonInternal representing the add-on
- * @param  aUri
- *         The uri to escape
- * @param  aUpdateType
- *         An optional number representing the type of update, only applicable
- *         when creating a url for retrieving an update manifest
- * @param  aAppVersion
- *         The optional application version to use for %APP_VERSION%
- * @return the appropriately escaped uri.
+ * @param {AddonInternal} aAddon
+ *        The AddonInternal representing the add-on
+ * @param {string} aUri
+ *        The URI to escape
+ * @param {integer?} aUpdateType
+ *        An optional number representing the type of update, only applicable
+ *        when creating a url for retrieving an update manifest
+ * @param {string?} aAppVersion
+ *        The optional application version to use for %APP_VERSION%
+ * @returns {string}
+ *       The appropriately escaped URI.
  */
 function escapeAddonURI(aAddon, aUri, aUpdateType, aAppVersion) {
   let uri = AddonManager.escapeAddonURI(aAddon, aUri, aAppVersion);
 
   // If there is an updateType then replace the UPDATE_TYPE string
   if (aUpdateType)
     uri = uri.replace(/%UPDATE_TYPE%/g, aUpdateType);
 
@@ -1106,16 +1143,21 @@ function escapeAddonURI(aAddon, aUri, aU
     compatMode = "strict";
   uri = uri.replace(/%COMPATIBILITY_MODE%/g, compatMode);
 
   return uri;
 }
 
 /**
  * Converts an iterable of addon objects into a map with the add-on's ID as key.
+ *
+ * @param {sequence<AddonInternal>} addons
+ *        A sequence of AddonInternal objects.
+ *
+ * @returns {Map<string, AddonInternal>}
  */
 function addonMap(addons) {
   return new Map(addons.map(a => [a.id, a]));
 }
 
 async function removeAsync(aFile) {
   let info = null;
   try {
@@ -1129,18 +1171,18 @@ async function removeAsync(aFile) {
       throw e;
     // The file has already gone away
   }
 }
 
 /**
  * Recursively removes a directory or file fixing permissions when necessary.
  *
- * @param  aFile
- *         The nsIFile to remove
+ * @param {nsIFile} aFile
+ *        The nsIFile to remove
  */
 function recursiveRemove(aFile) {
   let isDir = null;
 
   try {
     isDir = aFile.isDirectory();
   } catch (e) {
     // If the file has already gone away then don't worry about it, this can
@@ -1180,37 +1222,37 @@ function recursiveRemove(aFile) {
     logger.error("Failed to remove empty directory " + aFile.path, e);
     throw e;
   }
 }
 
 /**
  * Sets permissions on a file
  *
- * @param  aFile
- *         The file or directory to operate on.
- * @param  aPermissions
- *         The permissions to set
+ * @param {nsIFile} aFile
+ *        The file or directory to operate on.
+ * @param {integer} aPermissions
+ *        The permissions to set
  */
 function setFilePermissions(aFile, aPermissions) {
   try {
     aFile.permissions = aPermissions;
   } catch (e) {
     logger.warn("Failed to set permissions " + aPermissions.toString(8) + " on " +
          aFile.path, e);
   }
 }
 
 /**
  * Write a given string to a file
  *
- * @param  file
- *         The nsIFile instance to write into
- * @param  string
- *         The string to write
+ * @param {nsIFile} file
+ *        The nsIFile instance to write into
+ * @param {string} string
+ *        The string to write
  */
 function writeStringToFile(file, string) {
   let fileStream = new FileOutputStream(
     file, (FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
            FileUtils.MODE_TRUNCATE),
     FileUtils.PERMS_FILE, 0);
 
   try {
@@ -1341,60 +1383,60 @@ SafeInstallOperation.prototype = {
       throw e;
     }
   },
 
   /**
    * Moves a file or directory into a new directory. If an error occurs then all
    * files that have been moved will be moved back to their original location.
    *
-   * @param  aFile
-   *         The file or directory to be moved.
-   * @param  aTargetDirectory
-   *         The directory to move into, this is expected to be an empty
-   *         directory.
+   * @param {nsIFile} aFile
+   *        The file or directory to be moved.
+   * @param {nsIFile} aTargetDirectory
+   *        The directory to move into, this is expected to be an empty
+   *        directory.
    */
   moveUnder(aFile, aTargetDirectory) {
     try {
       this._installDirEntry(aFile, aTargetDirectory, false);
     } catch (e) {
       this.rollback();
       throw e;
     }
   },
 
   /**
    * Renames a file to a new location.  If an error occurs then all
    * files that have been moved will be moved back to their original location.
    *
-   * @param  aOldLocation
-   *         The old location of the file.
-   * @param  aNewLocation
-   *         The new location of the file.
+   * @param {nsIFile} aOldLocation
+   *        The old location of the file.
+   * @param {nsIFile} aNewLocation
+   *        The new location of the file.
    */
   moveTo(aOldLocation, aNewLocation) {
     try {
       let oldFile = aOldLocation.clone(), newFile = aNewLocation.clone();
       oldFile.moveTo(newFile.parent, newFile.leafName);
       this._installedFiles.push({ oldFile, newFile, isMoveTo: true});
     } catch (e) {
       this.rollback();
       throw e;
     }
   },
 
   /**
    * Copies a file or directory into a new directory. If an error occurs then
    * all new files that have been created will be removed.
    *
-   * @param  aFile
-   *         The file or directory to be copied.
-   * @param  aTargetDirectory
-   *         The directory to copy into, this is expected to be an empty
-   *         directory.
+   * @param {nsIFile} aFile
+   *        The file or directory to be copied.
+   * @param {nsIFile} aTargetDirectory
+   *        The directory to copy into, this is expected to be an empty
+   *        directory.
    */
   copy(aFile, aTargetDirectory) {
     try {
       this._installDirEntry(aFile, aTargetDirectory, true);
     } catch (e) {
       this.rollback();
       throw e;
     }
@@ -1424,21 +1466,22 @@ SafeInstallOperation.prototype = {
     while (this._createdDirs.length > 0)
       recursiveRemove(this._createdDirs.pop());
   }
 };
 
 /**
  * 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 {nsIFile} aDir
+ *        Directory to look at
+ * @param {boolean} aSortEntries
+ *        True to sort entries by filename
+ * @returns {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);
@@ -1475,37 +1518,37 @@ function getHashStringForCrypto(aCrypto)
 /**
  * Base class for objects that manage the installation of an addon.
  * This class isn't instantiated directly, see the derived classes below.
  */
 class AddonInstall {
   /**
    * Instantiates an AddonInstall.
    *
-   * @param  installLocation
-   *         The install location the add-on will be installed into
-   * @param  url
-   *         The nsIURL to get the add-on from. If this is an nsIFileURL then
-   *         the add-on will not need to be downloaded
-   * @param  options
-   *         Additional options for the install
-   * @param  options.hash
-   *         An optional hash for the add-on
-   * @param  options.existingAddon
-   *         The add-on this install will update if known
-   * @param  options.name
-   *         An optional name for the add-on
-   * @param  options.type
-   *         An optional type for the add-on
-   * @param  options.icons
-   *         Optional icons for the add-on
-   * @param  options.version
-   *         An optional version for the add-on
-   * @param  options.promptHandler
-   *         A callback to prompt the user before installing.
+   * @param {InstallLocation} installLocation
+   *        The install location the add-on will be installed into
+   * @param {nsIURL} url
+   *        The nsIURL to get the add-on from. If this is an nsIFileURL then
+   *        the add-on will not need to be downloaded
+   * @param {Object} [options = {}]
+   *        Additional options for the install
+   * @param {string} [options.hash]
+   *        An optional hash for the add-on
+   * @param {AddonInternal} [options.existingAddon]
+   *        The add-on this install will update if known
+   * @param {string} [options.name]
+   *        An optional name for the add-on
+   * @param {string} [options.type]
+   *        An optional type for the add-on
+   * @param {object} [options.icons]
+   *        Optional icons for the add-on
+   * @param {string} [options.version]
+   *        An optional version for the add-on
+   * @param {function(string) : Promise<void>} [options.promptHandler]
+   *        A callback to prompt the user before installing.
    */
   constructor(installLocation, url, options = {}) {
     this.wrapper = new AddonInstallWrapper(this);
     this.installLocation = installLocation;
     this.sourceURI = url;
 
     if (options.hash) {
       let hashSplit = options.hash.toLowerCase().split(":");
@@ -1551,16 +1594,17 @@ class AddonInstall {
 
   /**
    * Starts installation of this add-on from whatever state it is currently at
    * if possible.
    *
    * Note this method is overridden to handle additional state in
    * the subclassses below.
    *
+   * @returns {Promise<Addon>}
    * @throws if installation cannot proceed from the current state
    */
   install() {
     switch (this.state) {
     case AddonManager.STATE_DOWNLOADED:
       this.checkPrompt();
       break;
     case AddonManager.STATE_PROMPTS_DONE:
@@ -1646,29 +1690,29 @@ class AddonInstall {
                       " from this state (" + this.state + ")");
     }
   }
 
   /**
    * Adds an InstallListener for this instance if the listener is not already
    * registered.
    *
-   * @param  aListener
-   *         The InstallListener to add
+   * @param {InstallListener} aListener
+   *        The InstallListener to add
    */
   addListener(aListener) {
     if (!this.listeners.some(function(i) { return i == aListener; }))
       this.listeners.push(aListener);
   }
 
   /**
    * Removes an InstallListener for this instance if it is registered.
    *
-   * @param  aListener
-   *         The InstallListener to remove
+   * @param {InstallListener} aListener
+   *        The InstallListener to remove
    */
   removeListener(aListener) {
     this.listeners = this.listeners.filter(function(i) {
       return i != aListener;
     });
   }
 
   /**
@@ -1702,20 +1746,19 @@ class AddonInstall {
     if (this.releaseNotesURI)
       this.addon.releaseNotesURI = this.releaseNotesURI.spec;
   }
 
   /**
    * Called after the add-on is a local file and the signature and install
    * manifest can be read.
    *
-   * @param  aCallback
-   *         A function to call when the manifest has been loaded
-   * @throws if the add-on does not contain a valid install manifest or the
-   *         XPI is incorrectly signed
+   * @param {nsIFile} file
+   *        The file from which to load the manifest.
+   * @returns {Promise<void>}
    */
   async loadManifest(file) {
     let pkg;
     try {
       pkg = Package.get(file);
     } catch (e) {
       return Promise.reject([AddonManager.ERROR_CORRUPT_FILE, e]);
     }
@@ -2018,17 +2061,26 @@ class AddonInstall {
       this._callInstallListeners("onInstallFailed");
     }).then(() => {
       this.removeTemporaryFile();
       return this.installLocation.releaseStagingDir();
     });
   }
 
   /**
-   * Stages an upgrade for next application restart.
+   * Stages an add-on for install.
+   *
+   * @param {boolean} restartRequired
+   *        If true, the final installation will be deferred until the
+   *        next app startup.
+   * @param {AddonInternal} stagedAddon
+   *        The AddonInternal object for the staged install.
+   * @param {boolean} isUpgrade
+   *        True if this installation is an upgrade for an existing
+   *        add-on.
    */
   async stageInstall(restartRequired, stagedAddon, isUpgrade) {
     // First stage the file regardless of whether restarting is necessary
     if (this.addon.unpack) {
       logger.debug("Addon " + this.addon.id + " will be installed as " +
                    "an unpacked directory");
       stagedAddon.leafName = this.addon.id;
       await OS.File.makeDir(stagedAddon.path);
@@ -2053,30 +2105,33 @@ class AddonInstall {
         delete this.existingAddon.pendingUpgrade;
         this.existingAddon.pendingUpgrade = this.addon;
       }
     }
   }
 
   /**
    * Removes any previously staged upgrade.
+   *
+   * @param {nsIFile} stagingDir
+   *        The staging directory from which to unstage the install.
    */
-  async unstageInstall(stagedAddon) {
+  async unstageInstall(stagingDir) {
     XPIStates.getLocation(this.installLocation.name).unstageAddon(this.addon.id);
 
-    await removeAsync(getFile(this.addon.id, stagedAddon));
-
-    await removeAsync(getFile(`${this.addon.id}.xpi`, stagedAddon));
+    await removeAsync(getFile(this.addon.id, stagingDir));
+
+    await removeAsync(getFile(`${this.addon.id}.xpi`, stagingDir));
   }
 
   /**
     * Postone a pending update, until restart or until the add-on resumes.
     *
-    * @param {Function} resumeFn - a function for the add-on to run
-    *                                    when resuming.
+    * @param {function} resumeFn
+    *        A function for the add-on to run when resuming.
     */
   async postpone(resumeFn) {
     this.state = AddonManager.STATE_POSTPONED;
 
     let stagingDir = this.installLocation.getStagingDir();
 
     await this.installLocation.requestStagingDir();
     await this.unstageInstall(stagingDir);
@@ -2130,19 +2185,16 @@ class AddonInstall {
     return AddonManagerPrivate.callInstallListeners(event, this.listeners, this.wrapper,
                                                     ...args);
   }
 }
 
 var LocalAddonInstall = class extends AddonInstall {
   /**
    * Initialises this install to be an install from a local file.
-   *
-   * @returns Promise
-   *          A Promise that resolves when the object is ready to use.
    */
   async init() {
     this.file = this.sourceURI.QueryInterface(Ci.nsIFileURL).file;
 
     if (!this.file.exists()) {
       logger.warn("XPI file " + this.file.path + " does not exist");
       this.state = AddonManager.STATE_DOWNLOAD_FAILED;
       this.error = AddonManager.ERROR_NETWORK_FAILURE;
@@ -2231,39 +2283,39 @@ var LocalAddonInstall = class extends Ad
     return super.install();
   }
 };
 
 var DownloadAddonInstall = class extends AddonInstall {
   /**
    * Instantiates a DownloadAddonInstall
    *
-   * @param  installLocation
-   *         The InstallLocation the add-on will be installed into
-   * @param  url
-   *         The nsIURL to get the add-on from
-   * @param  options
-   *         Additional options for the install
-   * @param  options.hash
-   *         An optional hash for the add-on
-   * @param  options.existingAddon
-   *         The add-on this install will update if known
-   * @param  options.browser
-   *         The browser performing the install, used to display
-   *         authentication prompts.
-   * @param  options.name
-   *         An optional name for the add-on
-   * @param  options.type
-   *         An optional type for the add-on
-   * @param  options.icons
-   *         Optional icons for the add-on
-   * @param  options.version
-   *         An optional version for the add-on
-   * @param  options.promptHandler
-   *         A callback to prompt the user before installing.
+   * @param {InstallLocation} installLocation
+   *        The InstallLocation the add-on will be installed into
+   * @param {nsIURL} url
+   *        The nsIURL to get the add-on from
+   * @param {Object} [options = {}]
+   *        Additional options for the install
+   * @param {string} [options.hash]
+   *        An optional hash for the add-on
+   * @param {AddonInternal} [options.existingAddon]
+   *        The add-on this install will update if known
+   * @param {XULElement} [options.browser]
+   *        The browser performing the install, used to display
+   *        authentication prompts.
+   * @param {string} [options.name]
+   *        An optional name for the add-on
+   * @param {string} [options.type]
+   *        An optional type for the add-on
+   * @param {Object} [options.icons]
+   *        Optional icons for the add-on
+   * @param {string} [options.version]
+   *        An optional version for the add-on
+   * @param {function(string) : Promise<void>} [options.promptHandler]
+   *        A callback to prompt the user before installing.
    */
   constructor(installLocation, url, options = {}) {
     super(installLocation, url, options);
 
     this.browser = options.browser;
 
     this.state = AddonManager.STATE_AVAILABLE;
 
@@ -2384,30 +2436,30 @@ var DownloadAddonInstall = class extends
       logger.warn("Failed to start download for addon " + this.sourceURI.spec, e);
       this.state = AddonManager.STATE_DOWNLOAD_FAILED;
       this.error = AddonManager.ERROR_NETWORK_FAILURE;
       XPIProvider.removeActiveInstall(this);
       this._callInstallListeners("onDownloadFailed");
     }
   }
 
-  /**
+  /*
    * Update the crypto hasher with the new data and call the progress listeners.
    *
    * @see nsIStreamListener
    */
   onDataAvailable(aRequest, aContext, aInputstream, aOffset, aCount) {
     this.crypto.updateFromStream(aInputstream, aCount);
     this.progress += aCount;
     if (!this._callInstallListeners("onDownloadProgress")) {
       // TODO cancel the download and make it available again (bug 553024)
     }
   }
 
-  /**
+  /*
    * Check the redirect response for a hash of the target XPI and verify that
    * we don't end up on an insecure channel.
    *
    * @see nsIChannelEventSink
    */
   asyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, aCallback) {
     if (!this.hash && aOldChannel.originalURI.schemeIs("https") &&
         aOldChannel instanceof Ci.nsIHttpChannel) {
@@ -2427,17 +2479,17 @@ var DownloadAddonInstall = class extends
     if (!this.hash)
       this.badCertHandler.asyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, aCallback);
     else
       aCallback.onRedirectVerifyCallback(Cr.NS_OK);
 
     this.channel = aNewChannel;
   }
 
-  /**
+  /*
    * This is the first chance to get at real headers on the channel.
    *
    * @see nsIStreamListener
    */
   onStartRequest(aRequest, aContext) {
     if (this.hash) {
       try {
         this.crypto = CryptoHash(this.hash.algorithm);
@@ -2462,17 +2514,17 @@ var DownloadAddonInstall = class extends
         this.maxProgress = aRequest.contentLength;
       } catch (e) {
       }
       logger.debug("Download started for " + this.sourceURI.spec + " to file " +
           this.file.path);
     }
   }
 
-  /**
+  /*
    * The download is complete.
    *
    * @see nsIStreamListener
    */
   onStopRequest(aRequest, aContext, aStatus) {
     this.stream.close();
     this.channel = null;
     this.badCerthandler = null;
@@ -2545,20 +2597,20 @@ var DownloadAddonInstall = class extends
     } else {
       this.downloadFailed(AddonManager.ERROR_NETWORK_FAILURE, aStatus);
     }
   }
 
   /**
    * Notify listeners that the download failed.
    *
-   * @param  aReason
-   *         Something to log about the failure
-   * @param  error
-   *         The error code to pass to the listeners
+   * @param {string} aReason
+   *        Something to log about the failure
+   * @param {integer} aError
+   *        The error code to pass to the listeners
    */
   downloadFailed(aReason, aError) {
     logger.warn("Download of " + this.sourceURI.spec + " failed", aError);
     this.state = AddonManager.STATE_DOWNLOAD_FAILED;
     this.error = aReason;
     XPIProvider.removeActiveInstall(this);
     this._callInstallListeners("onDownloadFailed");
 
@@ -2622,22 +2674,22 @@ var DownloadAddonInstall = class extends
 
     return this.badCertHandler.getInterface(iid);
   }
 };
 
 /**
  * Creates a new AddonInstall for an update.
  *
- * @param  aCallback
- *         The callback to pass the new AddonInstall to
- * @param  aAddon
- *         The add-on being updated
- * @param  aUpdate
- *         The metadata about the new version from the update manifest
+ * @param {function} aCallback
+ *        The callback to pass the new AddonInstall to
+ * @param {AddonInternal} aAddon
+ *        The add-on being updated
+ * @param {Object} aUpdate
+ *        The metadata about the new version from the update manifest
  */
 function createUpdate(aCallback, aAddon, aUpdate) {
   let url = Services.io.newURI(aUpdate.updateURL);
 
   (async function() {
     let opts = {
       hash: aUpdate.updateHash,
       existingAddon: aAddon,
@@ -2666,18 +2718,18 @@ function createUpdate(aCallback, aAddon,
 
 // Maps instances of AddonInstall to AddonInstallWrapper
 const wrapperMap = new WeakMap();
 let installFor = wrapper => wrapperMap.get(wrapper);
 
 /**
  * Creates a wrapper for an AddonInstall that only exposes the public API
  *
- * @param  install
- *         The AddonInstall to create a wrapper for
+ * @param {AddonInstall} aInstall
+ *        The AddonInstall to create a wrapper for
  */
 function AddonInstallWrapper(aInstall) {
   wrapperMap.set(this, aInstall);
 }
 
 AddonInstallWrapper.prototype = {
   get __AddonInstallInternal__() {
     return AppConstants.DEBUG ? installFor(this) : undefined;
@@ -2734,26 +2786,26 @@ AddonInstallWrapper.prototype = {
     },
     enumerable: true,
   });
 });
 
 /**
  * Creates a new update checker.
  *
- * @param  aAddon
- *         The add-on to check for updates
- * @param  aListener
- *         An UpdateListener to notify of updates
- * @param  aReason
- *         The reason for the update check
- * @param  aAppVersion
- *         An optional application version to check for updates for
- * @param  aPlatformVersion
- *         An optional platform version to check for updates for
+ * @param {AddonInternal} aAddon
+ *        The add-on to check for updates
+ * @param {UpdateListener} aListener
+ *        An UpdateListener to notify of updates
+ * @param {integer} aReason
+ *        The reason for the update check
+ * @param {string} [aAppVersion]
+ *        An optional application version to check for updates for
+ * @param {string} [aPlatformVersion]
+ *        An optional platform version to check for updates for
  * @throws if the aListener or aReason arguments are not valid
  */
 var UpdateChecker = function(aAddon, aListener, aReason, aAppVersion, aPlatformVersion) {
   if (!aListener || !aReason)
     throw Cr.NS_ERROR_INVALID_ARG;
 
   ChromeUtils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
 
@@ -2792,35 +2844,37 @@ UpdateChecker.prototype = {
   appVersion: null,
   platformVersion: null,
   syncCompatibility: null,
 
   /**
    * Calls a method on the listener passing any number of arguments and
    * consuming any exceptions.
    *
-   * @param  aMethod
-   *         The method to call on the listener
+   * @param {string} aMethod
+   *        The method to call on the listener
+   * @param {any[]} aArgs
+   *        Additional arguments to pass to the listener.
    */
   callListener(aMethod, ...aArgs) {
     if (!(aMethod in this.listener))
       return;
 
     try {
       this.listener[aMethod].apply(this.listener, aArgs);
     } catch (e) {
       logger.warn("Exception calling UpdateListener method " + aMethod, e);
     }
   },
 
   /**
    * Called when AddonUpdateChecker completes the update check
    *
-   * @param  updates
-   *         The list of update details for the add-on
+   * @param {object[]} aUpdates
+   *        The list of update details for the add-on
    */
   async onUpdateCheckComplete(aUpdates) {
     XPIProvider.done(this.addon._updateCheck);
     this.addon._updateCheck = null;
     let AUC = AddonUpdateChecker;
 
     let ignoreMaxVersion = false;
     let ignoreStrictCompat = false;
@@ -2906,18 +2960,18 @@ UpdateChecker.prototype = {
     } else {
       sendUpdateAvailableMessages(this, null);
     }
   },
 
   /**
    * Called when AddonUpdateChecker fails the update check
    *
-   * @param  aError
-   *         An error status
+   * @param {any} aError
+   *        An error status
    */
   onUpdateCheckError(aError) {
     XPIProvider.done(this.addon._updateCheck);
     this.addon._updateCheck = null;
     this.callListener("onNoCompatibilityUpdateAvailable", this.addon.wrapper);
     this.callListener("onNoUpdateAvailable", this.addon.wrapper);
     this.callListener("onUpdateFinished", this.addon.wrapper, aError);
   },
@@ -2933,22 +2987,22 @@ UpdateChecker.prototype = {
       parser.cancel();
     }
   }
 };
 
 /**
  * Creates a new AddonInstall to install an add-on from a local file.
  *
- * @param  file
- *         The file to install
- * @param  location
- *         The location to install to
- * @returns Promise
- *          A Promise that resolves with the new install object.
+ * @param {nsIFile} file
+ *        The file to install
+ * @param {InstallLocation} location
+ *        The location to install to
+ * @returns {Promise<AddonInstall>}
+ *        A Promise that resolves with the new install object.
  */
 function createLocalInstall(file, location) {
   if (!location) {
     location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
   }
   let url = Services.io.newFileURI(file);
 
   try {
@@ -2967,17 +3021,17 @@ function createLocalInstall(file, locati
 // an instance the class as defined in XPIProvider.
 class DirectoryInstallLocation {}
 
 class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
   /**
    * Gets the staging directory to put add-ons that are pending install and
    * uninstall into.
    *
-   * @return an nsIFile
+   * @returns {nsIFile}
    */
   getStagingDir() {
     return getFile(DIR_STAGE, this._directory);
   }
 
   requestStagingDir() {
     this._stagingDirLock++;
 
@@ -3004,19 +3058,19 @@ class MutableDirectoryInstallLocation ex
 
     return Promise.resolve();
   }
 
   /**
    * Removes the specified files or directories in the staging directory and
    * then if the staging directory is empty attempts to remove it.
    *
-   * @param  aLeafNames
-   *         An array of file or directory to remove from the directory, the
-   *         array may be empty
+   * @param {string[]} [aLeafNames = []]
+   *        An array of file or directory to remove from the directory, the
+   *        array may be empty
    */
   cleanStagingDir(aLeafNames = []) {
     let dir = this.getStagingDir();
 
     for (let name of aLeafNames) {
       let file = getFile(name, dir);
       recursiveRemove(file);
     }
@@ -3042,17 +3096,17 @@ class MutableDirectoryInstallLocation ex
   }
 
   /**
    * Returns a directory that is normally on the same filesystem as the rest of
    * the install location and can be used for temporarily storing files during
    * safe move operations. Calling this method will delete the existing trash
    * directory and its contents.
    *
-   * @return an nsIFile
+   * @returns {nsIFile}
    */
   getTrashDir() {
     let trashDir = getFile(DIR_TRASH, this._directory);
     let trashDirExists = trashDir.exists();
     try {
       if (trashDirExists)
         recursiveRemove(trashDir);
       trashDirExists = false;
@@ -3063,32 +3117,35 @@ class MutableDirectoryInstallLocation ex
       trashDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 
     return trashDir;
   }
 
   /**
    * Installs an add-on into the install location.
    *
-   * @param  id
-   *         The ID of the add-on to install
-   * @param  source
-   *         The source nsIFile to install from
-   * @param  existingAddonID
-   *         The ID of an existing add-on to uninstall at the same time
-   * @param  action
-   *         What to we do with the given source file:
-   *           "move"
-   *           Default action, the source files will be moved to the new
-   *           location,
-   *           "copy"
-   *           The source files will be copied,
-   *           "proxy"
-   *           A "proxy file" is going to refer to the source file path
-   * @return an nsIFile indicating where the add-on was installed to
+   * @param {Object} options
+   *        Installation options.
+   * @param {string} options.id
+   *        The ID of the add-on to install
+   * @param {nsIFile} options.source
+   *        The source nsIFile to install from
+   * @param {string?} [options.existingAddonID]
+   *        The ID of an existing add-on to uninstall at the same time
+   * @param {string} options.action
+   *        What to we do with the given source file:
+   *          "move"
+   *          Default action, the source files will be moved to the new
+   *          location,
+   *          "copy"
+   *          The source files will be copied,
+   *          "proxy"
+   *          A "proxy file" is going to refer to the source file path
+   * @returns {nsIFile}
+   *        An nsIFile indicating where the add-on was installed to
    */
   installAddon({ id, source, existingAddonID, action = "move" }) {
     let trashDir = this.getTrashDir();
 
     let transaction = new SafeInstallOperation();
 
     let moveOldAddon = aId => {
       let file = getFile(aId, this._directory);
@@ -3177,18 +3234,18 @@ class MutableDirectoryInstallLocation ex
     }
 
     return newFile;
   }
 
   /**
    * Uninstalls an add-on from this location.
    *
-   * @param  aId
-   *         The ID of the add-on to uninstall
+   * @param {string} aId
+   *        The ID of the add-on to uninstall
    * @throws if the ID does not match any of the add-ons installed
    */
   uninstallAddon(aId) {
     let file = this._IDToFileMap[aId];
     if (!file) {
       logger.warn("Attempted to remove " + aId + " from " +
            this._name + " but it was already gone");
       return;
@@ -3247,17 +3304,18 @@ class SystemAddonInstallLocation extends
   static _loadAddonSet() {
     return XPIInternal.SystemAddonInstallLocation._loadAddonSet();
   }
 
   /**
    * Gets the staging directory to put add-ons that are pending install and
    * uninstall into.
    *
-   * @return {nsIFile} - staging directory for system add-on upgrades.
+   * @returns {nsIFile}
+   *        Staging directory for system add-on upgrades.
    */
   getStagingDir() {
     this._addonSet = SystemAddonInstallLocation._loadAddonSet();
     let dir = null;
     if (this._addonSet.directory) {
       this._directory = getFile(this._addonSet.directory, this._baseDir);
       dir = getFile(DIR_STAGE, this._directory);
     } else {
@@ -3281,16 +3339,21 @@ class SystemAddonInstallLocation extends
       return false;
     }
 
     return true;
   }
 
   /**
    * Tests whether the loaded add-on information matches what is expected.
+   *
+   * @param {Map<string, AddonInternal>} aAddons
+   *        The set of add-ons to check.
+   * @returns {boolean}
+   *        True if all of the given add-ons are valid.
    */
   isValid(aAddons) {
     for (let id of Object.keys(this._addonSet.addons)) {
       if (!aAddons.has(id)) {
         logger.warn(`Expected add-on ${id} is missing from the system add-on location.`);
         return false;
       }
 
@@ -3496,16 +3559,19 @@ class SystemAddonInstallLocation extends
         logger.warn(`Failed to remove failed system add-on directory ${newDir.path}.`, e);
       }
       throw e;
     }
   }
 
  /**
   * Resumes upgrade of a previously-delayed add-on set.
+  *
+  * @param {AddonInstall[]} installs
+  *        The set of installs to resume.
   */
   async resumeAddonSet(installs) {
     async function resumeAddon(install) {
       install.state = AddonManager.STATE_DOWNLOADED;
       install.installLocation.releaseStagingDir();
       install.install();
     }
 
@@ -3521,17 +3587,17 @@ class SystemAddonInstallLocation extends
   }
 
   /**
    * Returns a directory that is normally on the same filesystem as the rest of
    * the install location and can be used for temporarily storing files during
    * safe move operations. Calling this method will delete the existing trash
    * directory and its contents.
    *
-   * @return an nsIFile
+   * @returns {nsIFile}
    */
   getTrashDir() {
     let trashDir = getFile(DIR_TRASH, this._directory);
     let trashDirExists = trashDir.exists();
     try {
       if (trashDirExists)
         recursiveRemove(trashDir);
       trashDirExists = false;
@@ -3542,21 +3608,22 @@ class SystemAddonInstallLocation extends
       trashDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 
     return trashDir;
   }
 
   /**
    * Installs an add-on into the install location.
    *
-   * @param  id
-   *         The ID of the add-on to install
-   * @param  source
-   *         The source nsIFile to install from
-   * @return an nsIFile indicating where the add-on was installed to
+   * @param {string} id
+   *        The ID of the add-on to install
+   * @param {nsIFile} source
+   *        The source nsIFile to install from
+   * @returns {nsIFile}
+   *        An nsIFile indicating where the add-on was installed to
    */
   installAddon({id, source}) {
     let trashDir = this.getTrashDir();
     let transaction = new SafeInstallOperation();
 
     // If any of these operations fails the finally block will clean up the
     // temporary directory
     try {
@@ -3853,51 +3920,55 @@ var XPIInstall = {
     logger.info("Installing new system add-on set");
     await systemAddonLocation.installAddonSet(Array.from(addonList.values())
       .map(a => a.addon));
   },
 
   /**
    * Called to test whether installing XPI add-ons is enabled.
    *
-   * @return true if installing is enabled
+   * @returns {boolean}
+   *        True if installing is enabled.
    */
   isInstallEnabled() {
     // Default to enabled if the preference does not exist
     return Services.prefs.getBoolPref(PREF_XPI_ENABLED, true);
   },
 
   /**
    * Called to test whether installing XPI add-ons by direct URL requests is
    * whitelisted.
    *
-   * @return true if installing by direct requests is whitelisted
+   * @returns {boolean}
+   *        True if installing by direct requests is whitelisted
    */
   isDirectRequestWhitelisted() {
     // Default to whitelisted if the preference does not exist.
     return Services.prefs.getBoolPref(PREF_XPI_DIRECT_WHITELISTED, true);
   },
 
   /**
    * Called to test whether installing XPI add-ons from file referrers is
    * whitelisted.
    *
-   * @return true if installing from file referrers is whitelisted
+   * @returns {boolean}
+   *       True if installing from file referrers is whitelisted
    */
   isFileRequestWhitelisted() {
     // Default to whitelisted if the preference does not exist.
     return Services.prefs.getBoolPref(PREF_XPI_FILE_WHITELISTED, true);
   },
 
   /**
    * Called to test whether installing XPI add-ons from a URI is allowed.
    *
-   * @param  aInstallingPrincipal
-   *         The nsIPrincipal that initiated the install
-   * @return true if installing is allowed
+   * @param {nsIPrincipal}  aInstallingPrincipal
+   *        The nsIPrincipal that initiated the install
+   * @returns {boolean}
+   *        True if installing is allowed
    */
   isInstallAllowed(aInstallingPrincipal) {
     if (!this.isInstallEnabled())
       return false;
 
     let uri = aInstallingPrincipal.URI;
 
     // Direct requests without a referrer are either whitelisted or blocked.
@@ -3925,28 +3996,29 @@ var XPIInstall = {
       return false;
 
     return true;
   },
 
   /**
    * Called to get an AddonInstall to download and install an add-on from a URL.
    *
-   * @param  aUrl
+   * @param {nsIURI} aUrl
    *         The URL to be installed
-   * @param  aHash
-   *         A hash for the install
-   * @param  aName
-   *         A name for the install
-   * @param  aIcons
-   *         Icon URLs for the install
-   * @param  aVersion
-   *         A version for the install
-   * @param  aBrowser
-   *         The browser performing the install
+   * @param {string?} [aHash]
+   *        A hash for the install
+   * @param {string} [aName]
+   *        A name for the install
+   * @param {Object} [aIcons]
+   *        Icon URLs for the install
+   * @param {string} [aVersion]
+   *        A version for the install
+   * @param {XULElement?} [aBrowser]
+   *        The browser performing the install
+   * @returns {AddonInstall}
    */
   async getInstallForURL(aUrl, aHash, aName, aIcons, aVersion, aBrowser) {
     let location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
     let url = Services.io.newURI(aUrl);
 
     let options = {
       hash: aHash,
       browser: aBrowser,
@@ -3963,66 +4035,70 @@ var XPIInstall = {
 
     let install = new DownloadAddonInstall(location, url, options);
     return install.wrapper;
   },
 
   /**
    * Called to get an AddonInstall to install an add-on from a local file.
    *
-   * @param  aFile
-   *         The file to be installed
+   * @param {nsIFile} aFile
+   *        The file to be installed
+   * @returns {AddonInstall?}
    */
   async getInstallForFile(aFile) {
     let install = await createLocalInstall(aFile);
     return install ? install.wrapper : null;
   },
 
   /**
    * Temporarily installs add-on from a local XPI file or directory.
    * As this is intended for development, the signature is not checked and
    * the add-on does not persist on application restart.
    *
-   * @param aFile
+   * @param {nsIFile} aFile
    *        An nsIFile for the unpacked add-on directory or XPI file.
    *
-   * @return See installAddonFromLocation return value.
+   * @returns {Addon}
+   *        See installAddonFromLocation return value.
    */
   installTemporaryAddon(aFile) {
     return this.installAddonFromLocation(aFile, XPIInternal.TemporaryInstallLocation);
   },
 
   /**
    * Permanently installs add-on from a local XPI file or directory.
    * The signature is checked but the add-on persist on application restart.
    *
-   * @param aFile
+   * @param {nsIFile} aFile
    *        An nsIFile for the unpacked add-on directory or XPI file.
    *
-   * @return See installAddonFromLocation return value.
+   * @returns {Addon}
+   *        See installAddonFromLocation return value.
    */
   async installAddonFromSources(aFile) {
     let location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
     return this.installAddonFromLocation(aFile, location, "proxy");
   },
 
   /**
    * Installs add-on from a local XPI file or directory.
    *
-   * @param aFile
+   * @param {nsIFile} aFile
    *        An nsIFile for the unpacked add-on directory or XPI file.
-   * @param aInstallLocation
+   * @param {InstallLocation} aInstallLocation
    *        Define a custom install location object to use for the install.
-   * @param aInstallAction
+   * @param {string?} [aInstallAction]
    *        Optional action mode to use when installing the addon
    *        (see MutableDirectoryInstallLocation.installAddon)
    *
-   * @return a Promise that resolves to an Addon object on success, or rejects
-   *         if the add-on is not a valid restartless add-on or if the
-   *         same ID is already installed.
+   * @returns {Promise<Addon>}
+   *        A Promise that resolves to an Addon object on success, or rejects
+   *        if the add-on is not a valid restartless add-on or if the
+   *        same ID is already installed.
    */
   async installAddonFromLocation(aFile, aInstallLocation, aInstallAction) {
     if (aFile.exists() && aFile.isFile()) {
       flushJarCache(aFile);
     }
     let addon = await loadManifestFromFile(aFile, aInstallLocation);
 
     aInstallLocation.installAddon({ id: addon.id, source: aFile, action: aInstallAction });
@@ -4128,22 +4204,22 @@ var XPIInstall = {
 
     return addon.wrapper;
   },
 
   /**
    * Uninstalls an add-on, immediately if possible or marks it as pending
    * uninstall if not.
    *
-   * @param  aAddon
-   *         The DBAddonInternal to uninstall
-   * @param  aForcePending
-   *         Force this addon into the pending uninstall state (used
-   *         e.g. while the add-on manager is open and offering an
-   *         "undo" button)
+   * @param {DBAddonInternal} aAddon
+   *        The DBAddonInternal to uninstall
+   * @param {boolean} aForcePending
+   *        Force this addon into the pending uninstall state (used
+   *        e.g. while the add-on manager is open and offering an
+   *        "undo" button)
    * @throws if the addon cannot be uninstalled because it is in an install
    *         location that does not allow it
    */
   async uninstallAddon(aAddon, aForcePending) {
     if (!(aAddon.inDatabase))
       throw new Error("Cannot uninstall addon " + aAddon.id + " because it is not installed");
 
     if (aAddon._installLocation.locked)
@@ -4263,18 +4339,18 @@ var XPIInstall = {
     // Notify any other providers that a new theme has been enabled
     if (isTheme(aAddon.type) && aAddon.active)
       AddonManagerPrivate.notifyAddonChanged(null, aAddon.type);
   },
 
   /**
    * Cancels the pending uninstall of an add-on.
    *
-   * @param  aAddon
-   *         The DBAddonInternal to cancel uninstall for
+   * @param {DBAddonInternal} aAddon
+   *        The DBAddonInternal to cancel uninstall for
    */
   cancelUninstallAddon(aAddon) {
     if (!(aAddon.inDatabase))
       throw new Error("Can only cancel uninstall for installed addons.");
     if (!aAddon.pendingUninstall)
       throw new Error("Add-on is not marked to be uninstalled");
 
     if (aAddon._installLocation.name != KEY_APP_TEMPORARY)
@@ -4307,18 +4383,22 @@ var XPIInstall = {
       AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type, false);
   },
 
   /**
    * If the application has been upgraded and there are add-ons outside the
    * application directory then we may need to synchronize compatibility
    * information but only if the mismatch UI isn't disabled.
    *
-   * @returns null if no update check is needed, otherwise an array of add-on
-   *          IDs to check for updates.
+   * @param {boolean} aAppChanged
+   *        True id the app version has changed since the last startup.
+   *
+   * @returns {Array<string>?}
+   *        null if no update check is needed, otherwise an array of add-on
+   *        IDs to check for updates.
    */
   shouldForceUpdateCheck(aAppChanged) {
     AddonManagerPrivate.recordSimpleMeasure("XPIDB_metadata_age", AddonRepository.metadataAge());
 
     let startupChanges = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
     logger.debug("shouldForceUpdateCheck startupChanges: " + startupChanges.toSource());
     AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_disabled", startupChanges.length);