bug 1323129 part 3: clean up the old amIWebInstallListener interfaces draft
authorAndrew Swan <aswan@mozilla.com>
Tue, 03 Jan 2017 20:37:31 -0800
changeset 456020 ccf1595d802f2cfb698011be1863700e2b1f059f
parent 456019 d0474c301e33ef0b3253baa407508f35a722d293
child 456616 bc07133845f52b60da45dbc88d6c7713ba472cd7
push id40356
push useraswan@mozilla.com
push dateWed, 04 Jan 2017 18:19:38 +0000
bugs1323129
milestone53.0a1
bug 1323129 part 3: clean up the old amIWebInstallListener interfaces MozReview-Commit-ID: 27IuXEXbGvq
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/test/browser/browser_bug567127.js
toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -443,264 +443,235 @@ BrowserListener.prototype = {
     this.unregister();
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
                                          Ci.nsIWebProgressListener,
                                          Ci.nsIObserver])
 };
 
-function installNotifyObservers(aTopic, aBrowser, aUri, aInstalls, aInstallFn) {
+function installNotifyObservers(aTopic, aBrowser, aUri, aInstall, aInstallFn) {
   let info = {
     wrappedJSObject: {
       browser: aBrowser,
       originatingURI: aUri,
-      installs: aInstalls,
+      installs: [aInstall],
       install: aInstallFn,
     },
   };
   Services.obs.notifyObservers(info, aTopic, null);
 }
 
 /**
- * Creates a new installer to monitor downloads and prompt to install when
- * ready
- *
- * @param  aBrowser
- *         The browser that started the installations
- * @param  aUrl
- *         The URL that started the installations
- * @param  aInstalls
- *         An array of AddonInstalls
+ * Helper to monitor a download and prompt to install when ready
  */
-function Installer(aBrowser, aUrl, aInstalls) {
-  this.browser = aBrowser;
-  this.url = aUrl;
-  this.downloads = aInstalls;
-  this.installed = [];
-
-  installNotifyObservers("addon-install-started", aBrowser, aUrl, aInstalls);
-
-  const READY_STATES = [
-    AddonManager.STATE_AVAILABLE,
-    AddonManager.STATE_DOWNLOAD_FAILED,
-    AddonManager.STATE_INSTALL_FAILED,
-    AddonManager.STATE_CANCELLED,
-  ];
-  for (let install of aInstalls) {
-    install.addListener(this);
+class Installer {
+  /**
+   *
+   * @param  aBrowser
+   *         The browser that started the installations
+   * @param  aUrl
+   *         The URL that started the installations
+   * @param  aInstall
+   *         An AddonInstall
+   */
+  constructor(aBrowser, aUrl, aInstall) {
+    this.browser = aBrowser;
+    this.url = aUrl;
+    this.install = aInstall;
+    this.isDownloading = true;
+
+    installNotifyObservers("addon-install-started", aBrowser, aUrl, aInstall);
+
+    this.install.addListener(this);
 
     // Start downloading if it hasn't already begun
-    if (READY_STATES.indexOf(install.state) != -1)
-      install.install();
+    const READY_STATES = [
+      AddonManager.STATE_AVAILABLE,
+      AddonManager.STATE_DOWNLOAD_FAILED,
+      AddonManager.STATE_INSTALL_FAILED,
+      AddonManager.STATE_CANCELLED,
+    ];
+    if (READY_STATES.includes(this.install.state)) {
+      this.install.install();
+    }
+
+    this.checkDownloaded();
   }
 
-  this.checkAllDownloaded();
-}
-
-Installer.prototype = {
-  URI_XPINSTALL_DIALOG: "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul",
-  browser: null,
-  downloads: null,
-  installed: null,
-  isDownloading: true,
+  get URI_XPINSTALL_DIALOG() {
+    return "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
+  }
 
   /**
-   * Checks if all downloads are now complete and if so prompts to install.
+   * Checks if the download is complete and if so prompts to install.
    */
-  checkAllDownloaded: function() {
-    // Prevent re-entrancy caused by the confirmation dialog cancelling unwanted
-    // installs.
+  checkDownloaded() {
+    // Prevent re-entrancy caused by the confirmation dialog cancelling
+    // unwanted installs.
     if (!this.isDownloading)
       return;
 
-    var failed = [];
-    var installs = [];
-
-    for (let install of this.downloads) {
-      switch (install.state) {
-        case AddonManager.STATE_AVAILABLE:
-        case AddonManager.STATE_DOWNLOADING:
-          // Exit early if any add-ons haven't started downloading yet or are
-          // still downloading
-          return;
-        case AddonManager.STATE_DOWNLOAD_FAILED:
-          failed.push(install);
-          break;
-        case AddonManager.STATE_DOWNLOADED:
-          // App disabled items are not compatible and so fail to install
-          if (install.addon.appDisabled)
-            failed.push(install);
-          else
-            installs.push(install);
-          break;
-        case AddonManager.STATE_CANCELLED:
-          // Just ignore cancelled downloads
-          break;
-        default:
-          logger.warn("Download of " + install.sourceURI.spec + " in unexpected state " +
-                      install.state);
-      }
+    let failed = false;
+
+    switch (this.install.state) {
+      case AddonManager.STATE_AVAILABLE:
+      case AddonManager.STATE_DOWNLOADING:
+        // Exit early if the add-on hasn't started downloading yet or is
+        // still downloading
+        return;
+      case AddonManager.STATE_DOWNLOAD_FAILED:
+        failed = true;
+        break;
+      case AddonManager.STATE_DOWNLOADED:
+        // App disabled items are not compatible and so fail to install
+        failed = this.install.addon.appDisabled
+        break;
+      case AddonManager.STATE_CANCELLED:
+        // Just ignore cancelled downloads
+        return;
+      default:
+        logger.warn(`Download of ${this.install.sourceURI.spec} in unexpected state ${this.install.state}`);
+        return;
     }
 
     this.isDownloading = false;
-    this.downloads = installs;
-
-    if (failed.length > 0) {
+
+    if (failed) {
       // Stop listening and cancel any installs that are failed because of
       // compatibility reasons.
-      for (let install of failed) {
-        if (install.state == AddonManager.STATE_DOWNLOADED) {
-          install.removeListener(this);
-          install.cancel();
-        }
+      if (this.install.state == AddonManager.STATE_DOWNLOADED) {
+        this.install.removeListener(this);
+        this.install.cancel();
       }
-      installNotifyObservers("addon-install-failed", this.browser, this.url, failed);
+      installNotifyObservers("addon-install-failed", this.browser, this.url, this.install);
+      return;
     }
 
-    // If none of the downloads were successful then exit early
-    if (this.downloads.length == 0)
-      return;
-
     // Check for a custom installation prompt that may be provided by the
     // applicaton
     if ("@mozilla.org/addons/web-install-prompt;1" in Cc) {
       try {
         let prompt = Cc["@mozilla.org/addons/web-install-prompt;1"].
                                   getService(Ci.amIWebInstallPrompt);
-        prompt.confirm(this.browser, this.url, this.downloads, this.downloads.length);
+        prompt.confirm(this.browser, this.url, [this.install]);
         return;
       } catch (e) {}
     }
 
     if (Preferences.get("xpinstall.customConfirmationUI", false)) {
-      installNotifyObservers("addon-install-confirmation", this.browser, this.url, this.downloads);
+      installNotifyObservers("addon-install-confirmation", this.browser, this.url, this.install);
       return;
     }
 
     let args = {};
     args.url = this.url;
-    args.installs = this.downloads;
+    args.installs = [this.install];
     args.wrappedJSObject = args;
 
     try {
       Cc["@mozilla.org/base/telemetry;1"].
                    getService(Ci.nsITelemetry).
                    getHistogramById("SECURITY_UI").
                    add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
       let parentWindow = null;
       if (this.browser) {
         parentWindow = this.browser.ownerDocument.defaultView;
         PromptUtils.fireDialogEvent(parentWindow, "DOMWillOpenModalDialog", this.browser);
       }
       Services.ww.openWindow(parentWindow, this.URI_XPINSTALL_DIALOG,
                              null, "chrome,modal,centerscreen", args);
     } catch (e) {
       logger.warn("Exception showing install confirmation dialog", e);
-      for (let install of this.downloads) {
-        install.removeListener(this);
-        // Cancel the installs, as currently there is no way to make them fail
-        // from here.
-        install.cancel();
-      }
+      this.install.removeListener(this);
+      // Cancel the install, as currently there is no way to make it fail
+      // from here.
+      this.install.cancel();
+
       installNotifyObservers("addon-install-cancelled", this.browser, this.url,
-                      this.downloads);
+                             this.install);
     }
-  },
+  }
 
   /**
-   * Checks if all installs are now complete and if so notifies observers.
+   * Checks if install is now complete and if so notifies observers.
    */
-  checkAllInstalled: function() {
-    var failed = [];
-
-    for (let install of this.downloads) {
-      switch (install.state) {
-        case AddonManager.STATE_DOWNLOADED:
-        case AddonManager.STATE_INSTALLING:
-          // Exit early if any add-ons haven't started installing yet or are
-          // still installing
-          return;
-        case AddonManager.STATE_INSTALL_FAILED:
-          failed.push(install);
-          break;
-      }
+  checkInstalled() {
+    switch (this.install.state) {
+      case AddonManager.STATE_DOWNLOADED:
+      case AddonManager.STATE_INSTALLING:
+        // Exit early if the add-on hasn't started installing yet or is
+        // still installing
+        return;
+      case AddonManager.STATE_INSTALL_FAILED:
+        installNotifyObservers("addon-install-failed", this.browser, this.url, this.install);
+        break;
+      default:
+        installNotifyObservers("addon-install-complete", this.browser, this.url, this.install);
     }
-
-    this.downloads = null;
-
-    if (failed.length > 0)
-      installNotifyObservers("addon-install-failed", this.browser, this.url, failed);
-
-    if (this.installed.length > 0)
-      installNotifyObservers("addon-install-complete", this.browser, this.url, this.installed);
-    this.installed = null;
-  },
-
-  onDownloadCancelled: function(aInstall) {
+  }
+
+  // InstallListener methods:
+  onDownloadCancelled(aInstall) {
     aInstall.removeListener(this);
-    this.checkAllDownloaded();
-  },
-
-  onDownloadFailed: function(aInstall) {
+    this.checkDownloaded();
+  }
+
+  onDownloadFailed(aInstall) {
     aInstall.removeListener(this);
-    this.checkAllDownloaded();
-  },
-
-  onDownloadEnded: function(aInstall) {
-    this.checkAllDownloaded();
+    this.checkDownloaded();
+  }
+
+  onDownloadEnded(aInstall) {
+    this.checkDownloaded();
     return false;
-  },
-
-  onInstallCancelled: function(aInstall) {
+  }
+
+  onInstallCancelled(aInstall) {
     aInstall.removeListener(this);
-    this.checkAllInstalled();
-  },
-
-  onInstallFailed: function(aInstall) {
+    this.checkInstalled();
+  }
+
+  onInstallFailed(aInstall) {
     aInstall.removeListener(this);
-    this.checkAllInstalled();
-  },
-
-  onInstallEnded: function(aInstall) {
+    this.checkInstalled();
+  }
+
+  onInstallEnded(aInstall) {
     aInstall.removeListener(this);
-    this.installed.push(aInstall);
 
     // If installing a theme that is disabled and can be enabled then enable it
     if (aInstall.addon.type == "theme" &&
         aInstall.addon.userDisabled == true &&
         aInstall.addon.appDisabled == false) {
           aInstall.addon.userDisabled = false;
     }
 
-    this.checkAllInstalled();
+    this.checkInstalled();
   }
-};
+}
 
 const weblistener = {
-  onWebInstallDisabled: function(aBrowser, aUri, aInstalls) {
-    installNotifyObservers("addon-install-disabled", aBrowser, aUri, aInstalls);
+  onWebInstallDisabled(aBrowser, aUri, aInstall) {
+    installNotifyObservers("addon-install-disabled", aBrowser, aUri, aInstall);
   },
 
-  onWebInstallOriginBlocked: function(aBrowser, aUri, aInstalls) {
-    installNotifyObservers("addon-install-origin-blocked", aBrowser, aUri, aInstalls);
+  onWebInstallOriginBlocked(aBrowser, aUri, aInstall) {
+    installNotifyObservers("addon-install-origin-blocked", aBrowser, aUri, aInstall);
     return false;
   },
 
-  onWebInstallBlocked: function(aBrowser, aUri, aInstalls) {
-    installNotifyObservers("addon-install-blocked", aBrowser, aUri, aInstalls,
-                           function() { new Installer(this.browser, this.originatingURI, this.installs); });
+  onWebInstallBlocked(aBrowser, aUri, aInstall) {
+    installNotifyObservers("addon-install-blocked", aBrowser, aUri, aInstall,
+                           function() { new Installer(this.browser, this.originatingURI, aInstall); });
     return false;
   },
 
-  onWebInstallRequested: function(aBrowser, aUri, aInstalls) {
-    new Installer(aBrowser, aUri, aInstalls);
-
-    // We start the installs ourself
-    return false;
+  onWebInstallRequested(aBrowser, aUri, aInstall) {
+    new Installer(aBrowser, aUri, aInstall);
   },
 };
 
 /**
  * This represents an author of an add-on (e.g. creator or developer)
  *
  * @param  aName
  *         The name of the author
@@ -2313,39 +2284,37 @@ var AddonManagerInternal = {
     if (docShell.itemType == Ci.nsIDocShellTreeItem.typeContent)
       topBrowser = docShell.chromeEventHandler;
 
     try {
       if (!this.isInstallEnabled(aMimetype)) {
         aInstall.cancel();
 
         weblistener.onWebInstallDisabled(topBrowser, aInstallingPrincipal.URI,
-                                         [aInstall], 1);
+                                         aInstall);
         return;
       } else if (!aBrowser.contentPrincipal || !aInstallingPrincipal.subsumes(aBrowser.contentPrincipal)) {
         aInstall.cancel();
 
         weblistener.onWebInstallOriginBlocked(topBrowser, aInstallingPrincipal.URI,
-                                              [aInstall], 1);
+                                              aInstall);
         return;
       }
 
       // The install may start now depending on the web install listener,
       // listen for the browser navigating to a new origin and cancel the
       // install in that case.
       new BrowserListener(aBrowser, aInstallingPrincipal, aInstall);
 
       if (!this.isInstallAllowed(aMimetype, aInstallingPrincipal)) {
-        if (weblistener.onWebInstallBlocked(topBrowser, aInstallingPrincipal.URI,
-                                            [aInstall], 1)) {
-          aInstall.install();
-        }
-      } else if (weblistener.onWebInstallRequested(topBrowser, aInstallingPrincipal.URI,
-                                                 [aInstall], 1)) {
-        aInstall.install();
+        weblistener.onWebInstallBlocked(topBrowser, aInstallingPrincipal.URI,
+                                        aInstall);
+      } else {
+        weblistener.onWebInstallRequested(topBrowser, aInstallingPrincipal.URI,
+                                          aInstall);
       }
     } catch (e) {
       // In the event that the weblistener throws during instantiation or when
       // calling onWebInstallBlocked or onWebInstallRequested the
       // install should get cancelled.
       logger.warn("Failure calling web installer", e);
       aInstall.cancel();
     }
@@ -2362,17 +2331,17 @@ var AddonManagerInternal = {
    * @param  install
    *         The AddonInstall to be installed
    */
   installAddonFromAOM(browser, uri, install) {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
-    weblistener.onWebInstallRequested(browser, uri, [install]);
+    weblistener.onWebInstallRequested(browser, uri, install);
   },
 
   /**
    * Adds a new InstallListener if the listener is not already registered.
    *
    * @param  aListener
    *         The InstallListener to add
    */
--- a/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
@@ -10,17 +10,17 @@ MockFilePicker.init(window);
 var gManagerWindow;
 
 function checkInstallConfirmation(...urls) {
   return new Promise(resolve => {
     let nurls = urls.length;
 
     let notificationCount = 0;
     let observer = {
-      observe: function(aSubject, aTopic, aData) {
+      observe(aSubject, aTopic, aData) {
         var installInfo = aSubject.wrappedJSObject;
         if (gTestInWindow)
           is(installInfo.browser, null, "Notification should have a null browser");
         else
           isnot(installInfo.browser, null, "Notification should have non-null browser");
         notificationCount++;
       }
     };
--- a/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
@@ -16,17 +16,17 @@ this._scriptLoader = Cc["@mozilla.org/mo
                      getService(Ci.mozIJSSubScriptLoader);
 this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
 
 function checkInstallConfirmation(...urls) {
   let nurls = urls.length;
 
   let notificationCount = 0;
   let observer = {
-    observe: function(aSubject, aTopic, aData) {
+    observe(aSubject, aTopic, aData) {
       var installInfo = aSubject.wrappedJSObject;
       if (gTestInWindow)
         is(installInfo.browser, null, "Notification should have a null browser");
       else
         isnot(installInfo.browser, null, "Notification should have non-null browser");
       notificationCount++;
     }
   };