Bug 1363925: Part 7a - Turn on valid-jsdoc rule for XPIInstall.jsm. r=me,npotb
authorKris Maglione <maglione.k@gmail.com>
Sat, 21 Apr 2018 20:28:19 -0700
changeset 468745 ee1bdb001079770b20899df675e6cd0640a2d5fe
parent 468744 afd6f3e6e6af154925a2c13bd363d38187cecf2a
child 468746 2132b4cf18db3a8c0ab9ffc4977be05ec58748b1
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme, npotb
bugs1363925
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1363925: Part 7a - Turn on valid-jsdoc rule for XPIInstall.jsm. r=me,npotb 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 */
 
@@ -137,17 +139,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";
@@ -414,17 +416,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
@@ -447,51 +450,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);
 
@@ -609,21 +614,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())
@@ -631,28 +636,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());
@@ -937,29 +943,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");
@@ -968,30 +989,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);
@@ -1043,46 +1078,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);
 
@@ -1103,16 +1140,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 {
@@ -1126,18 +1168,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
@@ -1177,37 +1219,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 {
@@ -1338,60 +1380,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;
     }
@@ -1421,21 +1463,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);
@@ -1472,37 +1515,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(":");
@@ -1548,16 +1591,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:
@@ -1643,29 +1687,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;
     });
   }
 
   /**
@@ -1699,20 +1743,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]);
     }
@@ -2015,17 +2058,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);
@@ -2050,30 +2102,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);
@@ -2127,19 +2182,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;
@@ -2228,39 +2280,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;
 
@@ -2381,30 +2433,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) {
@@ -2424,17 +2476,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);
@@ -2459,17 +2511,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;
@@ -2542,20 +2594,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");
 
@@ -2619,22 +2671,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,
@@ -2663,18 +2715,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;
@@ -2731,26 +2783,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");
 
@@ -2789,35 +2841,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;
@@ -2903,18 +2957,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);
   },
@@ -2930,22 +2984,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 {
@@ -2964,17 +3018,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++;
 
@@ -3001,19 +3055,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);
     }
@@ -3039,17 +3093,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;
@@ -3060,32 +3114,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);
@@ -3174,18 +3231,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;
@@ -3244,17 +3301,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 {
@@ -3278,16 +3336,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;
       }
 
@@ -3493,16 +3556,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();
     }
 
@@ -3518,17 +3584,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;
@@ -3539,21 +3605,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 {
@@ -3850,51 +3917,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.
@@ -3922,28 +3993,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,
@@ -3960,66 +4032,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 });
@@ -4125,22 +4201,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)
@@ -4260,18 +4336,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)