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 id16795
push userdtownsend@mozilla.com
push dateMon, 08 Nov 2010 18:58:13 +0000
treeherdermozilla-central@6f359bbf2b7b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersUnfocused, blocks-betaN
bugs597397
milestone2.0b8pre
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 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;
   }
 };