Bug 553022: Remove cancelled and failed installs from the active installs list. r=robstrong
authorDave Townsend <dtownsend@oxymoronical.com>
Wed, 07 Apr 2010 11:16:10 -0700
changeset 41582 8bbe856ee60e3ea6ecee2ced52181193e8d2ce2e
parent 41581 3d6481a52850768a4e7e72d4a2fa3567da71f64a
child 41583 2b6722d6b864e8e618d4de243d01fb1d3e6de29d
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersrobstrong
bugs553022
milestone1.9.3a5pre
Bug 553022: Remove cancelled and failed installs from the active installs list. r=robstrong
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_install.js
toolkit/mozapps/extensions/test/xpcshell/test_startup.js
toolkit/mozapps/plugins/tests/browser_bug435788.js
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -1639,16 +1639,20 @@ var XPIProvider = {
     AddonInstall.createInstall(function(install) {
       if (install)
         callback(install.wrapper);
       else
         callback(null);
     }, file);
   },
 
+  removeActiveInstall: function XPI_removeActiveInstall(install) {
+    this.installs = this.installs.filter(function(i) i != install);
+  },
+
   /**
    * Called to get an Addon with a particular ID.
    *
    * @param   id
    *          The ID of the add-on to retrieve
    * @param   callback
    *          A callback to pass the Addon to
    */
@@ -3348,32 +3352,34 @@ AddonInstall.prototype = {
   cancel: function AI_cancel() {
     switch (this.state) {
     case AddonManager.STATE_DOWNLOADING:
       this.channel.cancel(Cr.NS_BINDING_ABORTED);
     case AddonManager.STATE_AVAILABLE:
     case AddonManager.STATE_DOWNLOADED:
       LOG("Cancelling download of " + this.sourceURL.spec);
       this.state = AddonManager.STATE_CANCELLED;
+      XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
                                                this.listeners, this.wrapper);
       if (this.file && !(this.sourceURL instanceof Ci.nsIFileURL))
         this.file.remove(true);
       break;
     case AddonManager.STATE_INSTALLED:
       LOG("Cancelling install of " + this.addon.id);
       let stagedAddon = this.installLocation.getStagingDir();
       let stagedJSON = stagedAddon.clone();
       stagedAddon.append(this.addon.id);
       stagedJSON.append(this.addon.id + ".json");
       if (stagedAddon.exists())
         stagedAddon.remove(true);
       if (stagedJSON.exists())
         stagedJSON.remove(true);
       this.state = AddonManager.STATE_CANCELLED;
+      XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onInstallCancelled",
                                                this.listeners, this.wrapper);
       break;
     default:
       throw new Error("Cannot cancel from this state");
     }
   },
 
@@ -3467,32 +3473,34 @@ AddonInstall.prototype = {
    */
   startDownload: function AI_startDownload() {
     Components.utils.import("resource://gre/modules/CertUtils.jsm");
 
     this.state = AddonManager.STATE_DOWNLOADING;
     if (!AddonManagerPrivate.callInstallListeners("onDownloadStarted",
                                                   this.listeners, this.wrapper)) {
       this.state = AddonManager.STATE_CANCELLED;
+      XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
                                                this.listeners, this.wrapper)
       return;
     }
 
     this.crypto = Cc["@mozilla.org/security/hash;1"].
                   createInstance(Ci.nsICryptoHash);
     if (this.hash) {
       [alg, this.hash] = this.hash.split(":", 2);
 
       try {
         this.crypto.initWithString(alg);
       }
       catch (e) {
         WARN("Unknown hash algorithm " + alg);
         this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+        XPIProvider.removeActiveInstall(this);
         AddonManagerPrivate.callInstallListeners("onDownloadFailed",
                                                  this.listeners, this.wrapper,
                                                  AddonManager.ERROR_INCORRECT_HASH);
         return;
       }
     }
     else {
       // We always need something to consume data from the inputstream passed
@@ -3523,16 +3531,17 @@ AddonInstall.prototype = {
       // hash to verify with (see bug 537761 for discussion)
       if (!this.hash)
         this.channel.notificationCallbacks = new BadCertHandler();
       this.channel.asyncOpen(listener, null);
     }
     catch (e) {
       WARN("Failed to start download: " + e);
       this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+      XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onDownloadFailed",
                                                this.listeners, this.wrapper,
                                                AddonManager.ERROR_NETWORK_FAILURE);
     }
   },
 
   /**
    * Update the crypto hasher with the new data and call the progress listeners.
@@ -3659,16 +3668,17 @@ AddonInstall.prototype = {
    * @param   reason
    *          Something to log about the failure
    * @param   error
    *          The error code to pass to the listeners
    */
   downloadFailed: function(reason, error) {
     WARN("Download failed: " + error + "\n");
     this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+    XPIProvider.removeActiveInstall(this);
     AddonManagerPrivate.callInstallListeners("onDownloadFailed", this.listeners,
                                              this.wrapper, reason);
     this.file.remove(true);
   },
 
   /**
    * Notify listeners that the download completed.
    */
@@ -3690,16 +3700,17 @@ AddonInstall.prototype = {
   /**
    * Installs the add-on into the install location.
    */
   startInstall: function AI_startInstall() {
     this.state = AddonManager.STATE_INSTALLING;
     if (!AddonManagerPrivate.callInstallListeners("onInstallStarted",
                                                   this.listeners, this.wrapper)) {
       this.state = AddonManager.STATE_DOWNLOADED;
+      XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onInstallCancelled",
                                                this.listeners, this.wrapper)
       return;
     }
 
     let isUpgrade = this.existingAddon &&
                     this.existingAddon._installLocation == this.installLocation;
     let requiresRestart = XPIProvider.installRequiresRestart(this.addon);
@@ -3825,16 +3836,17 @@ AddonInstall.prototype = {
         });
       }
     }
     catch (e) {
       WARN("Failed to install: " + e);
       if (stagedAddon.exists())
         stagedAddon.remove(true);
       this.state = AddonManager.STATE_INSTALL_FAILED;
+      XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onInstallFailed",
                                                this.listeners,
                                                this.wrapper, e);
     }
     finally {
       // If the file was downloaded then delete it
       if (!(this.sourceURL instanceof Ci.nsIFileURL))
         this.file.remove(true);
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -448,26 +448,31 @@ const InstallListener = {
     do_check_eq(install.state, AddonManager.STATE_DOWNLOADING);
     do_check_eq("onDownloadStarted", gExpectedInstalls.shift());
     return check_test_completed(arguments);
   },
 
   onDownloadEnded: function(install) {
     do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
     do_check_eq("onDownloadEnded", gExpectedInstalls.shift());
-    // gNext should determine whether installation continues
     return check_test_completed(arguments);
   },
 
   onDownloadFailed: function(install, status) {
     do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
     do_check_eq("onDownloadFailed", gExpectedInstalls.shift());
     return check_test_completed(arguments);
   },
 
+  onDownloadCancelled: function(install) {
+    do_check_eq(install.state, AddonManager.STATE_CANCELLED);
+    do_check_eq("onDownloadCancelled", gExpectedInstalls.shift());
+    return check_test_completed(arguments);
+  },
+
   onInstallStarted: function(install) {
     do_check_eq(install.state, AddonManager.STATE_INSTALLING);
     do_check_eq("onInstallStarted", gExpectedInstalls.shift());
     return check_test_completed(arguments);
   },
 
   onInstallEnded: function(install, newAddon) {
     do_check_eq(install.state, AddonManager.STATE_INSTALLED);
@@ -476,16 +481,22 @@ const InstallListener = {
   },
 
   onInstallFailed: function(install, status) {
     do_check_eq(install.state, AddonManager.STATE_INSTALL_FAILED);
     do_check_eq("onInstallFailed", gExpectedInstalls.shift());
     return check_test_completed(arguments);
   },
 
+  onInstallCancelled: function(install) {
+    do_check_eq(install.state, AddonManager.STATE_CANCELLED);
+    do_check_eq("onInstallCancelled", gExpectedInstalls.shift());
+    return check_test_completed(arguments);
+  },
+
   onExternalInstall: function(addon, existingAddon, requiresRestart) {
     do_check_eq("onExternalInstall", gExpectedInstalls.shift());
     do_check_false(requiresRestart);
     return check_test_completed(arguments);
   }
 };
 
 function hasFlag(bits, flag) {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_install.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_install.js
@@ -436,11 +436,106 @@ function check_test_8() {
     do_check_eq(a3.name, "Real Test 4");
     do_check_true(a3.isActive);
     do_check_false(a3.appDisabled);
     do_check_true(isExtensionInAddonsList(profileDir, a3.id));
     do_check_true(do_get_addon("test_install3").exists());
     a3.uninstall();
     restartManager(0);
 
+    run_test_9();
+  });
+}
+
+// Test that after cancelling a download it is removed from the active installs
+function run_test_9() {
+  prepare_test({ }, [
+    "onNewInstall"
+  ]);
+
+  let url = "http://localhost:4444/addons/test_install3.xpi";
+  AddonManager.getInstallForURL(url, function(install) {
+    ensure_test_completed();
+
+    do_check_neq(install, null);
+    do_check_eq(install.version, "1.0");
+    do_check_eq(install.name, "Real Test 4");
+    do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+    AddonManager.getInstalls(null, function(activeInstalls) {
+      do_check_eq(activeInstalls.length, 1);
+      do_check_eq(activeInstalls[0], install);
+
+      prepare_test({}, [
+        "onDownloadStarted",
+        "onDownloadEnded",
+      ], check_test_9);
+      install.install();
+    });
+  }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_9(install) {
+  prepare_test({}, [
+    "onDownloadCancelled"
+  ]);
+
+  install.cancel();
+
+  ensure_test_completed();
+
+  AddonManager.getInstalls(null, function(activeInstalls) {
+    do_check_eq(activeInstalls.length, 0);
+
+    run_test_10();
+  });
+}
+
+// Tests that after cancelling a pending install it is removed from the active
+// installs
+function run_test_10() {
+  prepare_test({ }, [
+    "onNewInstall"
+  ]);
+
+  let url = "http://localhost:4444/addons/test_install3.xpi";
+  AddonManager.getInstallForURL(url, function(install) {
+    ensure_test_completed();
+
+    do_check_neq(install, null);
+    do_check_eq(install.version, "1.0");
+    do_check_eq(install.name, "Real Test 4");
+    do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+    AddonManager.getInstalls(null, function(activeInstalls) {
+      do_check_eq(activeInstalls.length, 1);
+      do_check_eq(activeInstalls[0], install);
+
+      prepare_test({
+        "addon3@tests.mozilla.org": [
+          "onInstalling"
+        ]
+      }, [
+        "onDownloadStarted",
+        "onDownloadEnded",
+        "onInstallStarted",
+        "onInstallEnded"
+      ], check_test_10);
+      install.install();
+    });
+  }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_10(install) {
+  prepare_test({}, [
+    "onInstallCancelled"
+  ]);
+
+  install.cancel();
+
+  ensure_test_completed();
+
+  AddonManager.getInstalls(null, function(activeInstalls) {
+    do_check_eq(activeInstalls.length, 0);
+
     end_test();
   });
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
@@ -175,16 +175,17 @@ function run_test_1() {
     do_check_eq(a5, null);
     do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
     dest = profileDir.clone();
     dest.append("addon5@tests.mozilla.org");
     do_check_false(dest.exists());
 
     AddonManager.getAddonsByTypes(["extension"], function(extensionAddons) {
       do_check_eq(extensionAddons.length, 3);
+
       run_test_2();
     });
   });
 }
 
 // Test that modified items are detected and items in other install locations
 // are ignored
 function run_test_2() {
--- a/toolkit/mozapps/plugins/tests/browser_bug435788.js
+++ b/toolkit/mozapps/plugins/tests/browser_bug435788.js
@@ -412,20 +412,18 @@ function test_7_complete() {
   var item = getResultItem("Test extension 1", null);
   ok(item, "Should have seen the installed item");
   is(item.status, "Installed", "Should have been a failed install");
   item = getResultItem("Test extension 2", null);
   ok(item, "Should have seen the installed item");
   is(item.status, "Failed", "Should have been a failed install");
 
   AddonManager.getInstalls(null, function(installs) {
-    is(installs.length, 4, "Should be just one install");
-    is(installs[3].state, AddonManager.STATE_DOWNLOAD_FAILED, "Should have failed to download");
-    is(installs[3].addon.id, "bug435788_1@tests.mozilla.org", "Should have installed the extension");
-    installs[3].cancel();
+    is(installs.length, 1, "Should be one active installs");
+    installs[0].cancel();
 
     gPFS.document.documentElement.getButton("finish").click();
   });
 
   gPFS.document.documentElement.getButton("finish").click();
 }
 
 // Test an xpi with a bad hash
@@ -468,17 +466,17 @@ function test_8_available() {
 function test_8_complete() {
   ok(gSeenAvailable, "Should have seen the list of available plugins");
   is(getResultCount(), 1, "Should have attempted to install 1 plugin");
   var item = getResultItem("Test extension 3", null);
   ok(item, "Should have seen the installed item");
   is(item.status, "Failed", "Should have not been a successful install");
 
   AddonManager.getInstalls(null, function(installs) {
-    is(installs.length, 5, "Should not be any installs");
+    is(installs.length, 0, "Should not be any installs");
 
     gPFS.document.documentElement.getButton("finish").click();
   });
 
   gPFS.document.documentElement.getButton("finish").click();
 }
 
 // Test when no plugin exists in the datasource