Bug 1450834: Remove AddonManagerTesting.jsm for it is monumentally silly. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 15 Apr 2018 19:01:19 -0700
changeset 782441 e88fa613920eee9a719d569e744c44f3523eaf34
parent 782440 ecafbf3bfe2380d87138c13b896e318215b2bd84
push id106536
push usermaglione.k@gmail.com
push dateMon, 16 Apr 2018 02:06:13 +0000
reviewersaswan
bugs1450834
milestone61.0a1
Bug 1450834: Remove AddonManagerTesting.jsm for it is monumentally silly. r?aswan MozReview-Commit-ID: HWGELfaFEx4
toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
toolkit/mozapps/extensions/internal/XPIInstall.jsm
toolkit/mozapps/extensions/test/AddonManagerTesting.jsm
toolkit/mozapps/extensions/test/moz.build
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -3,17 +3,16 @@
 
 const {AddonManager, AddonManagerPrivate} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
 ChromeUtils.import("resource://gre/modules/TelemetryEnvironment.jsm", this);
 ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm", this);
 ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm", this);
 ChromeUtils.import("resource://gre/modules/Timer.jsm", this);
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
-ChromeUtils.import("resource://testing-common/AddonManagerTesting.jsm");
 ChromeUtils.import("resource://testing-common/httpd.js");
 ChromeUtils.import("resource://testing-common/MockRegistrar.jsm", this);
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 
 // AttributionCode is only needed for Firefox
 ChromeUtils.defineModuleGetter(this, "AttributionCode",
                                "resource:///modules/AttributionCode.jsm");
 
@@ -22,16 +21,21 @@ ChromeUtils.defineModuleGetter(this, "Li
                                "resource://gre/modules/LightweightThemeManager.jsm");
 
 ChromeUtils.defineModuleGetter(this, "ProfileAge",
                                "resource://gre/modules/ProfileAge.jsm");
 
 ChromeUtils.defineModuleGetter(this, "ExtensionTestUtils",
                                "resource://testing-common/ExtensionXPCShellUtils.jsm");
 
+async function installXPIFromURL(url) {
+  let install = await AddonManager.getInstallForURL(url, "application/x-xpinstall");
+  return install.install();
+}
+
 // The webserver hosting the addons.
 var gHttpServer = null;
 // The URL of the webserver root.
 var gHttpRoot = null;
 // The URL of the data directory, on the webserver.
 var gDataRoot = null;
 
 const PLATFORM_VERSION = "1.9.2";
@@ -1085,38 +1089,38 @@ add_task(async function test_addonsWatch
 
   let assertCheckpoint = (aExpected) => {
     Assert.equal(receivedNotifications, aExpected);
     TelemetryEnvironment.unregisterChangeListener("testWatchAddons_Changes" + aExpected);
   };
 
   // Test for receiving one notification after each change.
   let checkpointPromise = registerCheckpointPromise(1);
-  await AddonManagerTesting.installXPIFromURL(ADDON_INSTALL_URL);
+  await installXPIFromURL(ADDON_INSTALL_URL);
   await checkpointPromise;
   assertCheckpoint(1);
   Assert.ok(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons);
 
   checkpointPromise = registerCheckpointPromise(2);
-  let addon = await AddonManagerTesting.getAddonById(ADDON_ID);
+  let addon = await AddonManager.getAddonByID(ADDON_ID);
   addon.userDisabled = true;
   await checkpointPromise;
   assertCheckpoint(2);
   Assert.ok(!(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons));
 
   checkpointPromise = registerCheckpointPromise(3);
   let startupPromise = AddonTestUtils.promiseWebExtensionStartup(ADDON_ID);
   addon.userDisabled = false;
   await checkpointPromise;
   assertCheckpoint(3);
   Assert.ok(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons);
   await startupPromise;
 
   checkpointPromise = registerCheckpointPromise(4);
-  await AddonManagerTesting.uninstallAddonByID(ADDON_ID);
+  (await AddonManager.getAddonByID(ADDON_ID)).uninstall();
   await checkpointPromise;
   assertCheckpoint(4);
   Assert.ok(!(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons));
 
   Assert.equal(receivedNotifications, EXPECTED_NOTIFICATIONS,
                "We must only receive the notifications we expect.");
 });
 
@@ -1188,26 +1192,28 @@ add_task(async function test_addonsWatch
   let deferred = PromiseUtils.defer();
   TelemetryEnvironment.registerChangeListener("testNotInteresting",
     () => {
       Assert.ok(!receivedNotification, "Should not receive multiple notifications");
       receivedNotification = true;
       deferred.resolve();
     });
 
-  let dictionaryAddon = await AddonManagerTesting.installXPIFromURL(DICTIONARY_ADDON_INSTALL_URL);
-  let interestingAddon = await AddonManagerTesting.installXPIFromURL(INTERESTING_ADDON_INSTALL_URL);
+  let dictionaryAddon = await installXPIFromURL(DICTIONARY_ADDON_INSTALL_URL);
+  let interestingAddon = await installXPIFromURL(INTERESTING_ADDON_INSTALL_URL);
 
   await deferred.promise;
   Assert.ok(!("telemetry-dictionary@tests.mozilla.org" in
               TelemetryEnvironment.currentEnvironment.addons.activeAddons),
             "Dictionaries should not appear in active addons.");
 
   TelemetryEnvironment.unregisterChangeListener("testNotInteresting");
 
+  dump(`DICTIONARY ADDON ${dictionaryAddon}\n`);
+  dump(`           ADDON ${uneval(dictionaryAddon)}\n`);
   dictionaryAddon.uninstall();
   await interestingAddon.startupPromise;
   interestingAddon.uninstall();
 });
 
 add_task(async function test_addonsAndPlugins() {
   const ADDON_INSTALL_URL = gDataRoot + "restartless.xpi";
   const ADDON_ID = "tel-restartless-webext@tests.mozilla.org";
@@ -1284,17 +1290,17 @@ add_task(async function test_addonsAndPl
   TelemetryEnvironment.registerChangeListener("test_WebExtension",
     (reason, data) => {
       Assert.equal(reason, "addons-changed");
       deferred.resolve();
     }
   );
 
   // Install an add-on so we have some data.
-  let addon = await AddonManagerTesting.installXPIFromURL(ADDON_INSTALL_URL);
+  let addon = await installXPIFromURL(ADDON_INSTALL_URL);
 
   // Install a webextension as well.
   ExtensionTestUtils.init(this);
 
   let webextension = ExtensionTestUtils.loadExtension({
     useAddonManager: "permanent",
     manifest: {
       "name": "XPI Telemetry WebExtension Add-on Test",
@@ -1384,17 +1390,17 @@ add_task(async function test_signedAddon
     updateDay: ADDON_INSTALL_DATE,
     signedState: mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED,
   };
 
   let deferred = PromiseUtils.defer();
   TelemetryEnvironment.registerChangeListener("test_signedAddon", deferred.resolve);
 
   // Install the addon.
-  let addon = await AddonManagerTesting.installXPIFromURL(ADDON_INSTALL_URL);
+  let addon = await installXPIFromURL(ADDON_INSTALL_URL);
 
   await deferred.promise;
   // Unregister the listener.
   TelemetryEnvironment.unregisterChangeListener("test_signedAddon");
 
   let data = TelemetryEnvironment.currentEnvironment;
   checkEnvironmentData(data);
 
@@ -1412,17 +1418,17 @@ add_task(async function test_signedAddon
 
 add_task(async function test_addonsFieldsLimit() {
   const ADDON_INSTALL_URL = gDataRoot + "long-fields.xpi";
   const ADDON_ID = "tel-longfields-webext@tests.mozilla.org";
 
   // Install the addon and wait for the TelemetryEnvironment to pick it up.
   let deferred = PromiseUtils.defer();
   TelemetryEnvironment.registerChangeListener("test_longFieldsAddon", deferred.resolve);
-  let addon = await AddonManagerTesting.installXPIFromURL(ADDON_INSTALL_URL);
+  let addon = await installXPIFromURL(ADDON_INSTALL_URL);
   await deferred.promise;
   TelemetryEnvironment.unregisterChangeListener("test_longFieldsAddon");
 
   let data = TelemetryEnvironment.currentEnvironment;
   checkEnvironmentData(data);
 
   // Check that the addon is available and that the string fields are limited.
   Assert.ok(ADDON_ID in data.addons.activeAddons, "Add-on should be in the environment.");
@@ -1492,17 +1498,17 @@ add_task(async function test_collectionW
   let brokenAddonProvider = createMockAddonProvider("Broken Extensions Provider");
   AddonManagerPrivate.registerProvider(brokenAddonProvider);
   brokenAddonProvider.addAddon(BROKEN_MANIFEST);
   await checkpointPromise;
   assertCheckpoint(1);
 
   // Now install an addon which returns the correct information.
   checkpointPromise = registerCheckpointPromise(2);
-  let addon = await AddonManagerTesting.installXPIFromURL(ADDON_INSTALL_URL);
+  let addon = await installXPIFromURL(ADDON_INSTALL_URL);
   await checkpointPromise;
   assertCheckpoint(2);
 
   // Check that the new environment contains the Social addon installed with the broken
   // manifest and the rest of the data.
   let data = TelemetryEnvironment.currentEnvironment;
   checkEnvironmentData(data, {expectBrokenAddons: true});
 
--- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
@@ -1267,34 +1267,19 @@ var AddonTestUtils = {
    * A helper method to install AddonInstall and wait for completion.
    *
    * @param {AddonInstall} install
    *        The add-on to install.
    * @returns {Promise<AddonInstall>}
    *        Resolves when the install completes, either successfully or
    *        in failure.
    */
-  promiseCompleteInstall(install) {
-    let listener;
-    return new Promise(resolve => {
-      listener = {
-        onDownloadFailed: resolve,
-        onDownloadCancelled: resolve,
-        onInstallFailed: resolve,
-        onInstallCancelled: resolve,
-        onInstallEnded: resolve,
-        onInstallPostponed: resolve,
-      };
-
-      install.addListener(listener);
-      install.install();
-    }).then(() => {
-      install.removeListener(listener);
-      return install;
-    });
+  async promiseCompleteInstall(install) {
+    await install.install().catch(() => {});
+    return install;
   },
 
   /**
    * A helper method to install a file.
    *
    * @param {nsIFile} file
    *        The file to install
    * @param {boolean} [ignoreIncompatible = false]
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -1227,16 +1227,23 @@ class AddonInstall {
         data: hashSplit[1]
       };
     }
     this.hash = this.originalHash;
     this.existingAddon = options.existingAddon || null;
     this.promptHandler = options.promptHandler || (() => Promise.resolve());
     this.releaseNotesURI = null;
 
+    this._installPromise = new Promise(resolve => {
+      this._resolveInstallPromise = resolve;
+    });
+    // Ignore uncaught rejections for this promise, since they're
+    // handled by install listeners.
+    this._installPromise.catch(() => {});
+
     this.listeners = [];
     this.icons = options.icons || {};
     this.error = 0;
 
     this.progress = 0;
     this.maxProgress = -1;
 
     // Giving each instance of AddonInstall a reference to the logger.
@@ -1277,20 +1284,21 @@ class AddonInstall {
       break;
     case AddonManager.STATE_POSTPONED:
       logger.debug(`Postponing install of ${this.addon.id}`);
       break;
     case AddonManager.STATE_DOWNLOADING:
     case AddonManager.STATE_CHECKING:
     case AddonManager.STATE_INSTALLING:
       // Installation is already running
-      return;
+      break;
     default:
       throw new Error("Cannot start installing from this state");
     }
+    return this._installPromise;
   }
 
   /**
    * Called during XPIProvider shutdown so that we can do any necessary
    * pre-shutdown cleanup.
    */
   onShutdown() {
     switch (this.state) {
@@ -1310,18 +1318,17 @@ class AddonInstall {
    */
   cancel() {
     switch (this.state) {
     case AddonManager.STATE_AVAILABLE:
     case AddonManager.STATE_DOWNLOADED:
       logger.debug("Cancelling download of " + this.sourceURI.spec);
       this.state = AddonManager.STATE_CANCELLED;
       XPIProvider.removeActiveInstall(this);
-      AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
-                                               this.listeners, this.wrapper);
+      this._callInstallListeners("onDownloadCancelled");
       this.removeTemporaryFile();
       break;
     case AddonManager.STATE_INSTALLED:
       logger.debug("Cancelling install of " + this.addon.id);
       let xpi = getFile(`${this.addon.id}.xpi`, this.installLocation.getStagingDir());
       flushJarCache(xpi);
       this.installLocation.cleanStagingDir([this.addon.id, this.addon.id + ".xpi"]);
       this.state = AddonManager.STATE_CANCELLED;
@@ -1329,25 +1336,23 @@ class AddonInstall {
 
       if (this.existingAddon) {
         delete this.existingAddon.pendingUpgrade;
         this.existingAddon.pendingUpgrade = null;
       }
 
       AddonManagerPrivate.callAddonListeners("onOperationCancelled", this.addon.wrapper);
 
-      AddonManagerPrivate.callInstallListeners("onInstallCancelled",
-                                               this.listeners, this.wrapper);
+      this._callInstallListeners("onInstallCancelled");
       break;
     case AddonManager.STATE_POSTPONED:
       logger.debug(`Cancelling postponed install of ${this.addon.id}`);
       this.state = AddonManager.STATE_CANCELLED;
       XPIProvider.removeActiveInstall(this);
-      AddonManagerPrivate.callInstallListeners("onInstallCancelled",
-                                               this.listeners, this.wrapper);
+      this._callInstallListeners("onInstallCancelled");
       this.removeTemporaryFile();
 
       let stagingDir = this.installLocation.getStagingDir();
       let stagedAddon = stagingDir.clone();
 
       this.unstageInstall(stagedAddon);
     default:
       throw new Error("Cannot cancel install of " + this.sourceURI.spec +
@@ -1529,18 +1534,17 @@ class AddonInstall {
         };
 
         try {
           await this.promptHandler(info);
         } catch (err) {
           logger.info(`Install of ${this.addon.id} cancelled by user`);
           this.state = AddonManager.STATE_CANCELLED;
           XPIProvider.removeActiveInstall(this);
-          AddonManagerPrivate.callInstallListeners("onInstallCancelled",
-                                                   this.listeners, this.wrapper);
+          this._callInstallListeners("onInstallCancelled");
           return;
         }
       }
       this.state = AddonManager.STATE_PROMPTS_DONE;
       this.install();
     })();
   }
 
@@ -1571,23 +1575,21 @@ class AddonInstall {
   // TODO This relies on the assumption that we are always installing into the
   // highest priority install location so the resulting add-on will be visible
   // overriding any existing copy in another install location (bug 557710).
   /**
    * Installs the add-on into the install location.
    */
   startInstall() {
     this.state = AddonManager.STATE_INSTALLING;
-    if (!AddonManagerPrivate.callInstallListeners("onInstallStarted",
-                                                  this.listeners, this.wrapper)) {
+    if (!this._callInstallListeners("onInstallStarted")) {
       this.state = AddonManager.STATE_DOWNLOADED;
       this.removeTemporaryFile();
       XPIProvider.removeActiveInstall(this);
-      AddonManagerPrivate.callInstallListeners("onInstallCancelled",
-                                               this.listeners, this.wrapper);
+      this._callInstallListeners("onInstallCancelled");
       return;
     }
 
     // Find and cancel any pending installs for the same add-on in the same
     // install location
     for (let aInstall of XPIProvider.installs) {
       if (aInstall.state == AddonManager.STATE_INSTALLED &&
           aInstall.installLocation == this.installLocation &&
@@ -1694,19 +1696,17 @@ class AddonInstall {
                                         reason, extraParams);
       }
 
       AddonManagerPrivate.callAddonListeners("onInstalled",
                                              this.addon.wrapper);
 
       logger.debug("Install of " + this.sourceURI.spec + " completed.");
       this.state = AddonManager.STATE_INSTALLED;
-      AddonManagerPrivate.callInstallListeners("onInstallEnded",
-                                               this.listeners, this.wrapper,
-                                               this.addon.wrapper);
+      this._callInstallListeners("onInstallEnded", this.addon.wrapper);
 
       if (this.addon.bootstrap) {
         if (this.addon.active) {
           XPIProvider.callBootstrapMethod(this.addon, file, "startup",
                                           reason, extraParams);
         } else {
           // XXX this makes it dangerous to do some things in onInstallEnded
           // listeners because important cleanup hasn't been done yet
@@ -1723,19 +1723,17 @@ class AddonInstall {
 
       if (stagedAddon.exists())
         recursiveRemove(stagedAddon);
       this.state = AddonManager.STATE_INSTALL_FAILED;
       this.error = AddonManager.ERROR_FILE_ACCESS;
       XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callAddonListeners("onOperationCancelled",
                                              this.addon.wrapper);
-      AddonManagerPrivate.callInstallListeners("onInstallFailed",
-                                               this.listeners,
-                                               this.wrapper);
+      this._callInstallListeners("onInstallFailed");
     }).then(() => {
       this.removeTemporaryFile();
       return this.installLocation.releaseStagingDir();
     });
   }
 
   /**
    * Stages an upgrade for next application restart.
@@ -1795,18 +1793,17 @@ class AddonInstall {
 
     await this.installLocation.requestStagingDir();
     await this.unstageInstall(stagingDir);
 
     let stagedAddon = getFile(`${this.addon.id}.xpi`, stagingDir);
 
     await this.stageInstall(true, stagedAddon, true);
 
-    AddonManagerPrivate.callInstallListeners("onInstallPostponed",
-                                             this.listeners, this.wrapper);
+    this._callInstallListeners("onInstallPostponed");
 
     // upgrade has been staged for restart, provide a way for it to call the
     // resume function.
     let callback = AddonManagerPrivate.getUpgradeListener(this.addon.id);
     if (callback) {
       callback({
         version: this.version,
         install: () => {
@@ -1823,16 +1820,34 @@ class AddonInstall {
         },
       });
     }
     // Release the staging directory lock, but since the staging dir is populated
     // it will not be removed until resumed or installed by restart.
     // See also cleanStagingDir()
     this.installLocation.releaseStagingDir();
   }
+
+  _callInstallListeners(event, ...args) {
+    switch (event) {
+      case "onDownloadCancelled":
+      case "onDownloadFailed":
+      case "onInstallCancelled":
+      case "onInstallFailed":
+        let rej = Promise.reject(new Error(`Install failed: ${event}`));
+        rej.catch(() => {});
+        this._resolveInstallPromise(rej);
+        break;
+      case "onInstallEnded":
+        this._resolveInstallPromise(args[0]);
+        break;
+    }
+    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.
@@ -1881,19 +1896,17 @@ var LocalAddonInstall = class extends Ad
 
     try {
       await this.loadManifest(this.file);
     } catch ([error, message]) {
       logger.warn("Invalid XPI", message);
       this.state = AddonManager.STATE_DOWNLOAD_FAILED;
       this.error = error;
       XPIProvider.removeActiveInstall(this);
-      AddonManagerPrivate.callInstallListeners("onNewInstall",
-                                               this.listeners,
-                                               this.wrapper);
+      this._callInstallListeners("onNewInstall");
       flushJarCache(this.file);
       return;
     }
 
     let addon = await XPIDatabase.getVisibleAddonForID(this.addon.id);
 
     this.existingAddon = addon;
     this.addon.updateBlocklistState({oldAddon: this.existingAddon});
@@ -1902,43 +1915,38 @@ var LocalAddonInstall = class extends Ad
 
     if (!this.addon.isCompatible) {
       this.state = AddonManager.STATE_CHECKING;
 
       await new Promise(resolve => {
         new UpdateChecker(this.addon, {
           onUpdateFinished: aAddon => {
             this.state = AddonManager.STATE_DOWNLOADED;
-            AddonManagerPrivate.callInstallListeners("onNewInstall",
-                                                     this.listeners,
-                                                     this.wrapper);
+            this._callInstallListeners("onNewInstall");
             resolve();
           }
         }, AddonManager.UPDATE_WHEN_ADDON_INSTALLED);
       });
     } else {
-      AddonManagerPrivate.callInstallListeners("onNewInstall",
-                                               this.listeners,
-                                               this.wrapper);
+      this._callInstallListeners("onNewInstall");
 
     }
   }
 
   install() {
     if (this.state == AddonManager.STATE_DOWNLOAD_FAILED) {
       // For a local install, this state means that verification of the
       // file failed (e.g., the hash or signature or manifest contents
       // were invalid).  It doesn't make sense to retry anything in this
       // case but we have callers who don't know if their AddonInstall
       // object is a local file or a download so accommodate them here.
-      AddonManagerPrivate.callInstallListeners("onDownloadFailed",
-                                               this.listeners, this.wrapper);
-      return;
+      this._callInstallListeners("onDownloadFailed");
+      return this._installPromise;
     }
-    super.install();
+    return super.install();
   }
 };
 
 var DownloadAddonInstall = class extends AddonInstall {
   /**
    * Instantiates a DownloadAddonInstall
    *
    * @param  installLocation
@@ -1972,18 +1980,17 @@ var DownloadAddonInstall = class extends
 
     this.state = AddonManager.STATE_AVAILABLE;
 
     this.stream = null;
     this.crypto = null;
     this.badCertHandler = null;
     this.restartDownload = false;
 
-    AddonManagerPrivate.callInstallListeners("onNewInstall", this.listeners,
-                                            this.wrapper);
+    this._callInstallListeners("onNewInstall", this.listeners, this.wrapper);
   }
 
   install() {
     switch (this.state) {
     case AddonManager.STATE_AVAILABLE:
       this.startDownload();
       break;
     case AddonManager.STATE_DOWNLOAD_FAILED:
@@ -1993,18 +2000,19 @@ var DownloadAddonInstall = class extends
       this.state = AddonManager.STATE_AVAILABLE;
       this.error = 0;
       this.progress = 0;
       this.maxProgress = -1;
       this.hash = this.originalHash;
       this.startDownload();
       break;
     default:
-      super.install();
+      return super.install();
     }
+    return this._installPromise;
   }
 
   cancel() {
     if (this.state == AddonManager.STATE_DOWNLOADING) {
       if (this.channel) {
         logger.debug("Cancelling download of " + this.sourceURI.spec);
         this.channel.cancel(Cr.NS_BINDING_ABORTED);
       }
@@ -2018,23 +2026,21 @@ var DownloadAddonInstall = class extends
     this.cancel();
   }
 
   /**
    * Starts downloading the add-on's XPI file.
    */
   startDownload() {
     this.state = AddonManager.STATE_DOWNLOADING;
-    if (!AddonManagerPrivate.callInstallListeners("onDownloadStarted",
-                                                  this.listeners, this.wrapper)) {
+    if (!this._callInstallListeners("onDownloadStarted")) {
       logger.debug("onDownloadStarted listeners cancelled installation of addon " + this.sourceURI.spec);
       this.state = AddonManager.STATE_CANCELLED;
       XPIProvider.removeActiveInstall(this);
-      AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
-                                               this.listeners, this.wrapper);
+      this._callInstallListeners("onDownloadCancelled");
       return;
     }
 
     // If a listener changed our state then do not proceed with the download
     if (this.state != AddonManager.STATE_DOWNLOADING)
       return;
 
     if (this.channel) {
@@ -2058,18 +2064,17 @@ var DownloadAddonInstall = class extends
                     createInstance(Ci.nsIFileOutputStream);
       this.stream.init(this.file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
                        FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE, 0);
     } catch (e) {
       logger.warn("Failed to start download for addon " + this.sourceURI.spec, e);
       this.state = AddonManager.STATE_DOWNLOAD_FAILED;
       this.error = AddonManager.ERROR_FILE_ACCESS;
       XPIProvider.removeActiveInstall(this);
-      AddonManagerPrivate.callInstallListeners("onDownloadFailed",
-                                               this.listeners, this.wrapper);
+      this._callInstallListeners("onDownloadFailed");
       return;
     }
 
     let listener = Cc["@mozilla.org/network/stream-listener-tee;1"].
                    createInstance(Ci.nsIStreamListenerTee);
     listener.init(this, this.stream);
     try {
       let requireBuiltIn = Services.prefs.getBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, true);
@@ -2088,31 +2093,29 @@ var DownloadAddonInstall = class extends
       this.channel.asyncOpen2(listener);
 
       Services.obs.addObserver(this, "network:offline-about-to-go-offline");
     } catch (e) {
       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);
-      AddonManagerPrivate.callInstallListeners("onDownloadFailed",
-                                               this.listeners, this.wrapper);
+      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 (!AddonManagerPrivate.callInstallListeners("onDownloadProgress",
-                                                  this.listeners, this.wrapper)) {
+    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.
    *
@@ -2151,18 +2154,17 @@ var DownloadAddonInstall = class extends
     if (this.hash) {
       try {
         this.crypto = CryptoHash(this.hash.algorithm);
       } catch (e) {
         logger.warn("Unknown hash algorithm '" + this.hash.algorithm + "' for addon " + this.sourceURI.spec, e);
         this.state = AddonManager.STATE_DOWNLOAD_FAILED;
         this.error = AddonManager.ERROR_INCORRECT_HASH;
         XPIProvider.removeActiveInstall(this);
-        AddonManagerPrivate.callInstallListeners("onDownloadFailed",
-                                                 this.listeners, this.wrapper);
+        this._callInstallListeners("onDownloadFailed");
         aRequest.cancel(Cr.NS_BINDING_ABORTED);
         return;
       }
     } else {
       // We always need something to consume data from the inputstream passed
       // to onDataAvailable so just create a dummy cryptohasher to do that.
       this.crypto = CryptoHash("sha1");
     }
@@ -2190,18 +2192,17 @@ var DownloadAddonInstall = class extends
     Services.obs.removeObserver(this, "network:offline-about-to-go-offline");
 
     // If the download was cancelled then update the state and send events
     if (aStatus == Cr.NS_BINDING_ABORTED) {
       if (this.state == AddonManager.STATE_DOWNLOADING) {
         logger.debug("Cancelled download of " + this.sourceURI.spec);
         this.state = AddonManager.STATE_CANCELLED;
         XPIProvider.removeActiveInstall(this);
-        AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
-                                                 this.listeners, this.wrapper);
+        this._callInstallListeners("onDownloadCancelled");
         // If a listener restarted the download then there is no need to
         // remove the temporary file
         if (this.state != AddonManager.STATE_CANCELLED)
           return;
       }
 
       this.removeTemporaryFile();
       if (this.restartDownload)
@@ -2267,18 +2268,17 @@ var DownloadAddonInstall = class extends
    * @param  error
    *         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);
-    AddonManagerPrivate.callInstallListeners("onDownloadFailed", this.listeners,
-                                             this.wrapper);
+    this._callInstallListeners("onDownloadFailed");
 
     // If the listener hasn't restarted the download then remove any temporary
     // file
     if (this.state == AddonManager.STATE_DOWNLOAD_FAILED) {
       logger.debug("downloadFailed: removing temp file for " + this.sourceURI.spec);
       this.removeTemporaryFile();
     } else
       logger.debug("downloadFailed: listener changed AddonInstall state for " +
@@ -2299,19 +2299,17 @@ var DownloadAddonInstall = class extends
     if (this.existingAddon) {
       this.addon.existingAddonID = this.existingAddon.id;
       this.addon.installDate = this.existingAddon.installDate;
     } else {
       this.addon.installDate = this.addon.updateDate;
     }
     this.addon.updateBlocklistState({oldAddon: this.existingAddon});
 
-    if (AddonManagerPrivate.callInstallListeners("onDownloadEnded",
-                                                 this.listeners,
-                                                 this.wrapper)) {
+    if (this._callInstallListeners("onDownloadEnded")) {
       // If a listener changed our state then do not proceed with the install
       if (this.state != AddonManager.STATE_DOWNLOADED)
         return;
 
       // proceed with the install state machine.
       this.install();
     }
   }
@@ -2420,17 +2418,17 @@ AddonInstallWrapper.prototype = {
     return installFor(this).sourceURI;
   },
 
   set promptHandler(handler) {
     installFor(this).promptHandler = handler;
   },
 
   install() {
-    installFor(this).install();
+    return installFor(this).install();
   },
 
   cancel() {
     installFor(this).cancel();
   },
 
   addListener(listener) {
     installFor(this).addListener(listener);
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/AddonManagerTesting.jsm
+++ /dev/null
@@ -1,110 +0,0 @@
-/* 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/. */
-
-// This file is a test-only JSM containing utility methods for
-// interacting with the add-ons manager.
-
-"use strict";
-
-var EXPORTED_SYMBOLS = [
-  "AddonManagerTesting",
-];
-
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-ChromeUtils.defineModuleGetter(this, "AddonManager",
-                               "resource://gre/modules/AddonManager.jsm");
-
-var AddonManagerTesting = {
-  /**
-   * Get the add-on that is specified by its ID.
-   *
-   * @return {Promise<Object>} A promise that resolves returning the found addon or null
-   *         if it is not found.
-   */
-  getAddonById(id) {
-    return AddonManager.getAddonByID(id);
-  },
-
-  /**
-   * Uninstall an add-on that is specified by its ID.
-   *
-   * The returned promise resolves on successful uninstall and rejects
-   * if the add-on is not unknown.
-   *
-   * @return Promise<restartRequired>
-   */
-  uninstallAddonByID(id) {
-    return new Promise(async (resolve, reject) => {
-
-      let addon = await AddonManager.getAddonByID(id);
-      if (!addon) {
-        reject(new Error("Add-on is not known: " + id));
-        return;
-      }
-
-      let listener = {
-        onUninstalling(addon, needsRestart) {
-          if (addon.id != id) {
-            return;
-          }
-
-          if (needsRestart) {
-            AddonManager.removeAddonListener(listener);
-            resolve(true);
-          }
-        },
-
-        onUninstalled(addon) {
-          if (addon.id != id) {
-            return;
-          }
-
-          AddonManager.removeAddonListener(listener);
-          resolve(false);
-        },
-
-        onOperationCancelled(addon) {
-          if (addon.id != id) {
-            return;
-          }
-
-          AddonManager.removeAddonListener(listener);
-          reject(new Error("Uninstall cancelled."));
-        },
-      };
-
-      AddonManager.addAddonListener(listener);
-      addon.uninstall();
-
-    });
-  },
-
-  /**
-   * Install an XPI add-on from a URL.
-   *
-   * @return Promise<addon>
-   */
-  installXPIFromURL(url, hash, name, iconURL, version) {
-    return new Promise(async (resolve, reject) => {
-
-      let install = await AddonManager.getInstallForURL(url, "application/x-xpinstall", hash, name, iconURL, version);
-      let fail = () => { reject(new Error("Add-on install failed.")); };
-
-      let listener = {
-        onDownloadCancelled: fail,
-        onDownloadFailed: fail,
-        onInstallCancelled: fail,
-        onInstallFailed: fail,
-        onInstallEnded(install, addon) {
-          resolve(addon);
-        },
-      };
-
-      install.addListener(listener);
-      install.install();
-
-    });
-  },
-};
--- a/toolkit/mozapps/extensions/test/moz.build
+++ b/toolkit/mozapps/extensions/test/moz.build
@@ -4,16 +4,12 @@
 # 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/.
 
 DIRS += ['browser']
 
 BROWSER_CHROME_MANIFESTS += ['xpinstall/browser.ini']
 MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
 
-TESTING_JS_MODULES += [
-    'AddonManagerTesting.jsm',
-]
-
 XPCSHELL_TESTS_MANIFESTS += [
     'xpcshell/xpcshell-unpack.ini',
     'xpcshell/xpcshell.ini',
 ]