Bug 597397: Install Updates only installs the first update in the list. r=Unfocused, a=blocks-betaN
authorDave Townsend <dtownsend@oxymoronical.com>
Mon, 08 Nov 2010 10:00:13 -0800
changeset 57120 6f359bbf2b7b613649de9f4a2abca639aab071a4
parent 57119 25f346a28ba8b268fb7a3ebac93d1a27f4fbae9b
child 57121 413f719c1d73ce749922f33e257428e530022b9e
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersUnfocused, blocks-betaN
bugs597397
milestone2.0b8pre
Bug 597397: Install Updates only installs the first update in the list. r=Unfocused, a=blocks-betaN
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/test/browser/browser_bug587970.js
toolkit/mozapps/extensions/test/browser/head.js
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -2403,17 +2403,18 @@ var gUpdatesView = {
   _showAvailableUpdates: function(aIsRefresh, aRequest) {
     /* Disable the Update Selected button so it can't get clicked
        before everything is initialized asynchronously.
        It will get re-enabled by maybeDisableUpdateSelected(). */
     this._updateSelected.disabled = true;
 
     var self = this;
     AddonManager.getAllInstalls(function(aInstallsList) {
-      if (!aIsRefresh && gViewController && aRequest != gViewController.currentViewRequest)
+      if (!aIsRefresh && gViewController && aRequest &&
+          aRequest != gViewController.currentViewRequest)
         return;
 
       if (aIsRefresh) {
         self.showEmptyNotice(false);
         self._updateSelected.hidden = true;
 
         while (self._listBox.itemCount > 0)
           self._listBox.removeItemAt(0);
@@ -2522,26 +2523,23 @@ var gUpdatesView = {
         this._updateSelected.disabled = false;
         return;
       }
     }
     this._updateSelected.disabled = true;
   },
 
   installSelected: function() {
-    /* Starting the update of one item will refresh the list,
-       which can cause problems while we're iterating over it.
-       So we update only after we've finished iterating over the list. */
-    var toUpgrade = [];
     for (let i = 0; i < this._listBox.childNodes.length; i++) {
       let item = this._listBox.childNodes[i];
       if (item.includeUpdate)
-        toUpgrade.push(item);
+        item.upgrade();
     }
-    toUpgrade.forEach(function(aItem) aItem.upgrade());
+
+    this._updateSelected.disabled = true;
   },
 
   getSelectedAddon: function() {
     var item = this._listBox.selectedItem;
     if (item)
       return item.mAddon;
     return null;
   },
@@ -2574,35 +2572,29 @@ var gUpdatesView = {
 
   onExternalInstall: function(aAddon) {
     if (!shouldAutoUpdate(aAddon)) {
       this._numManualUpdaters++;
       this.maybeShowCategory();
     }
   },
 
-  onDownloadStarted: function(aInstall) {
-    if (!this.isManualUpdate(aInstall))
-      return;
-    this.maybeRefresh();
-  },
-
-  onInstallStarted: function(aInstall) {
-    if (!this.isManualUpdate(aInstall))
-      return;
-    this.maybeRefresh();
-  },
-
   onInstallEnded: function(aAddon) {
     if (!shouldAutoUpdate(aAddon)) {
       this._numManualUpdaters++;
       this.maybeShowCategory();
     }
   },
 
+  onInstallCancelled: function(aInstall) {
+    if (!this.isManualUpdate(aInstall))
+      return;
+    this.maybeRefresh();
+  },
+
   onPropertyChanged: function(aAddon, aProperties) {
     if (aProperties.indexOf("applyBackgroundUpdates") != -1)
       this.updateManualUpdatersCount();
   }
 };
 
 
 var gDragDrop = {
--- a/toolkit/mozapps/extensions/test/browser/browser_bug587970.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug587970.js
@@ -17,16 +17,21 @@ function test() {
     name: "addon 1",
     version: "1.0",
     applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE
   }, {
     id: "addon2@tests.mozilla.org",
     name: "addon 2",
     version: "2.0",
     applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE
+  }, {
+    id: "addon3@tests.mozilla.org",
+    name: "addon 3",
+    version: "3.0",
+    applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE
   }]);
   
 
   open_manager("addons://updates/available", function(aWindow) {
     gManagerWindow = aWindow;
     run_next_test();
   });
 }
@@ -51,102 +56,125 @@ add_test(function() {
   gProvider.createInstalls([{
     name: "addon 1",
     version: "1.1",
     existingAddon: gProvider.addons[0]
   }, {
     name: "addon 2",
     version: "2.1",
     existingAddon: gProvider.addons[1]
+  }, {
+    name: "addon 3",
+    version: "3.1",
+    existingAddon: gProvider.addons[2]
   }]);
 
   function wait_for_refresh() {
-    if (list.childNodes.length == 2 &&
+    if (list.childNodes.length == 3 &&
         list.childNodes[0].mManualUpdate &&
-        list.childNodes[1].mManualUpdate) {
+        list.childNodes[1].mManualUpdate &&
+        list.childNodes[2].mManualUpdate) {
       run_next_test();
     } else {
       info("Waiting for pane to refresh");
       setTimeout(wait_for_refresh, 10);
     }
   }
   info("Waiting for pane to refresh");
   setTimeout(wait_for_refresh, 10);
 });
 
 
 add_test(function() {
   var list = gManagerWindow.document.getElementById("updates-list");
-  is(list.childNodes.length, 2, "Available updates list should have 2 items");
+  is(list.childNodes.length, 3, "Available updates list should have 2 items");
   
   var item1 = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
   isnot(item1, null, "Item for addon1@tests.mozilla.org should be in list");
   var item2 = get_addon_element(gManagerWindow, "addon2@tests.mozilla.org");
   isnot(item2, null, "Item for addon2@tests.mozilla.org should be in list");
+  var item3 = get_addon_element(gManagerWindow, "addon3@tests.mozilla.org");
+  isnot(item3, null, "Item for addon3@tests.mozilla.org should be in list");
 
   var emptyNotice = gManagerWindow.document.getElementById("empty-availableUpdates-msg");
   is_element_hidden(emptyNotice, "Empty notice should be hidden");
 
   var updateSelected = gManagerWindow.document.getElementById("update-selected-btn");
   is_element_visible(updateSelected, "Update Selected button should be visible");
   is(updateSelected.disabled, false, "Update Selected button should be enabled by default");
 
   is(item1._includeUpdate.checked, true, "Include Update checkbox should be checked by default for addon1");
   is(item2._includeUpdate.checked, true, "Include Update checkbox should be checked by default for addon2");
+  is(item3._includeUpdate.checked, true, "Include Update checkbox should be checked by default for addon3");
   
   info("Unchecking Include Update checkbox for addon1");
   EventUtils.synthesizeMouse(item1._includeUpdate, 2, 2, { }, gManagerWindow);
   is(item1._includeUpdate.checked, false, "Include Update checkbox should now be be unchecked for addon1");
   is(updateSelected.disabled, false, "Update Selected button should still be enabled");
 
   info("Unchecking Include Update checkbox for addon2");
   EventUtils.synthesizeMouse(item2._includeUpdate, 2, 2, { }, gManagerWindow);
   is(item2._includeUpdate.checked, false, "Include Update checkbox should now be be unchecked for addon2");
+  is(updateSelected.disabled, false, "Update Selected button should still be enabled");
+
+  info("Unchecking Include Update checkbox for addon3");
+  EventUtils.synthesizeMouse(item3._includeUpdate, 2, 2, { }, gManagerWindow);
+  is(item3._includeUpdate.checked, false, "Include Update checkbox should now be be unchecked for addon3");
   is(updateSelected.disabled, true, "Update Selected button should now be disabled");
 
   info("Checking Include Update checkbox for addon2");
   EventUtils.synthesizeMouse(item2._includeUpdate, 2, 2, { }, gManagerWindow);
   is(item2._includeUpdate.checked, true, "Include Update checkbox should now be be checked for addon2");
   is(updateSelected.disabled, false, "Update Selected button should now be enabled");
 
+  info("Checking Include Update checkbox for addon3");
+  EventUtils.synthesizeMouse(item3._includeUpdate, 2, 2, { }, gManagerWindow);
+  is(item3._includeUpdate.checked, true, "Include Update checkbox should now be be checked for addon3");
+  is(updateSelected.disabled, false, "Update Selected button should now be enabled");
+
+  var installCount = 0;
   var listener = {
-    onInstallEnded: function() {
+    onDownloadStarted: function(aInstall) {
+      isnot(aInstall.existingAddon.id, "addon1@tests.mozilla.org", "Should not have seen a download start for addon1");
+    },
+
+    onInstallEnded: function(aInstall) {
+      if (++installCount < 2)
+        return;
+
+      gProvider.installs[0].removeTestListener(listener);
       gProvider.installs[1].removeTestListener(listener);
+      gProvider.installs[2].removeTestListener(listener);
 
-      is(gProvider.installs[0].state, AddonManager.STATE_AVAILABLE, "addon1 should not have been upgraded");
-      is(gProvider.installs[1].state, AddonManager.STATE_INSTALLED, "addon2 should have been upgraded");
+      // Installs are started synchronously so by the time an executeSoon is
+      // executed all installs that are going to start will have started
+      executeSoon(function() {
+        is(gProvider.installs[0].state, AddonManager.STATE_AVAILABLE, "addon1 should not have been upgraded");
+        is(gProvider.installs[1].state, AddonManager.STATE_INSTALLED, "addon2 should have been upgraded");
+        is(gProvider.installs[2].state, AddonManager.STATE_INSTALLED, "addon3 should have been upgraded");
+
+        run_next_test();
+      });
     }
   }
+  gProvider.installs[0].addTestListener(listener);
   gProvider.installs[1].addTestListener(listener);
+  gProvider.installs[2].addTestListener(listener);
   info("Clicking Update Selected button");
-  EventUtils.synthesizeMouse(updateSelected, 2, 2, { }, gManagerWindow);
-
-  wait_for_view_load(gManagerWindow, function() {
-    function wait_for_refresh() {
-      var item = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
-      if (item.mManualUpdate) {
-        run_next_test();
-      } else {
-        info("Waiting for pane to refresh");
-        setTimeout(wait_for_refresh, 10);
-      }
-    }
-    info("Waiting for pane to refresh");
-    setTimeout(wait_for_refresh, 10);
-  }, true);
+  EventUtils.synthesizeMouseAtCenter(updateSelected, { }, gManagerWindow);
 });
 
 
 add_test(function() {
   var updateSelected = gManagerWindow.document.getElementById("update-selected-btn");
-  is(updateSelected.disabled, false, "Update Selected button should now be enabled");
+  is(updateSelected.disabled, true, "Update Selected button should be disabled");
 
   var item1 = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
   isnot(item1, null, "Item for addon1@tests.mozilla.org should be in list");
-  is(item1._includeUpdate.checked, true, "Include Update checkbox should be checked by default for addon1");
+  is(item1._includeUpdate.checked, false, "Include Update checkbox should not have changed");
 
-  info("Unchecking Include Update checkbox for addon1");
+  info("Checking Include Update checkbox for addon1");
   EventUtils.synthesizeMouse(item1._includeUpdate, 2, 2, { }, gManagerWindow);
-  is(item1._includeUpdate.checked, false, "Include Update checkbox should now be be unchecked for addon1");
-  is(updateSelected.disabled, true, "Update Selected button should now be disabled");
+  is(item1._includeUpdate.checked, true, "Include Update checkbox should now be be checked for addon1");
+  is(updateSelected.disabled, false, "Update Selected button should now not be disabled");
 
   run_next_test();
 });
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -970,22 +970,29 @@ function MockInstall(aName, aType, aAddo
   // of code run from standard install listeners
   this.testListeners = [];
 }
 
 MockInstall.prototype = {
   install: function() {
     switch (this.state) {
       case AddonManager.STATE_AVAILABLE:
+        this.state = AddonManager.STATE_DOWNLOADING;
+        if (!this.callListeners("onDownloadStarted")) {
+          this.state = AddonManager.STATE_CANCELLED;
+          this.callListeners("onDownloadCancelled");
+          return;
+        }
+
+        this.state = AddonManager.STATE_DOWNLOADED;
+        this.callListeners("onDownloadEnded");
+
       case AddonManager.STATE_DOWNLOADED:
-        // Downloading to be implemented when needed
-
         this.state = AddonManager.STATE_INSTALLING;
         if (!this.callListeners("onInstallStarted")) {
-          // Reverting to STATE_DOWNLOADED instead to be implemented when needed
           this.state = AddonManager.STATE_CANCELLED;
           this.callListeners("onInstallCancelled");
           return;
         }
 
         // Adding addon to MockProvider to be implemented when needed
         if (this._addonToInstall)
           this.addon = this._addonToInstall;
@@ -1043,17 +1050,22 @@ MockInstall.prototype = {
 
   callListeners: function(aMethod) {
     var result = AddonManagerPrivate.callInstallListeners(aMethod, this.listeners,
                                                           this, this.addon);
 
     // Call test listeners after standard listeners to remove race condition
     // between standard and test listeners
     this.testListeners.forEach(function(aListener) {
-      if (aMethod in aListener)
-        if (aListener[aMethod].call(aListener, this, this.addon) === false)
-          result = false;
-    });
+      try {
+        if (aMethod in aListener)
+          if (aListener[aMethod].call(aListener, this, this.addon) === false)
+            result = false;
+      }
+      catch (e) {
+        ok(false, "Test listener threw exception: " + e);
+      }
+    }, this);
 
     return result;
   }
 };