Bug 589777: Cannot remove several backgrounds in a row. r=Unfocused, a=blocking-betaN
authorDave Townsend <dtownsend@oxymoronical.com>
Mon, 13 Sep 2010 10:36:12 -0700
changeset 53709 12c016c708e33c088c0e512e1f7e4727be9556dc
parent 53708 fd5ff14bc45abbedf9a9c4c789dff5d57c88f7ed
child 53710 473e497f56767bc1772424881e13ccd32d6e92fe
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersUnfocused, blocking-betaN
bugs589777
milestone2.0b6pre
Bug 589777: Cannot remove several backgrounds in a row. r=Unfocused, a=blocking-betaN
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/test/browser/browser_uninstalling.js
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -1440,23 +1440,28 @@ var gSearchView = {
       this.showEmptyNotice(isEmpty);
       this.showAllResultsLink(this._lastRemoteTotal);
     }
 
     gViewController.updateCommands();
   },
 
   hide: function() {
+    // Uninstalling add-ons can mutate the list so find the add-ons first then
+    // uninstall them
+    var items = [];
     var listitem = this._listBox.firstChild;
     while (listitem) {
       if (listitem.getAttribute("pending") == "uninstall" &&
           !listitem.isPending("uninstall"))
-        listitem.mAddon.uninstall();
+        items.push(listitem.mAddon);
       listitem = listitem.nextSibling;
     }
+
+    items.forEach(function(aAddon) { aAddon.uninstall(); });
   },
 
   getMatchScore: function(aObj, aQuery) {
     var score = 0;
     score += this.calculateMatchScore(aObj.name, aQuery,
                                       SEARCH_SCORE_MULTIPLIER_NAME);
     score += this.calculateMatchScore(aObj.description, aQuery,
                                       SEARCH_SCORE_MULTIPLIER_DESCRIPTION);
@@ -1606,23 +1611,28 @@ var gListView = {
 
     this._types = types.addon;
     this._installTypes = types.install;
   },
 
   hide: function() {
     gEventManager.unregisterInstallListener(this);
 
+    // Uninstalling add-ons can mutate the list so find the add-ons first then
+    // uninstall them
+    var items = [];
     var listitem = this._listBox.firstChild;
     while (listitem) {
       if (listitem.getAttribute("pending") == "uninstall" &&
           !listitem.isPending("uninstall"))
-        listitem.mAddon.uninstall();
+        items.push(listitem.mAddon);
       listitem = listitem.nextSibling;
     }
+
+    items.forEach(function(aAddon) { aAddon.uninstall(); });
   },
 
   showEmptyNotice: function(aShow) {
     this._emptyNotice.hidden = !aShow;
   },
 
   onSortChanged: function(aSortBy, aAscending) {
     var hints = aAscending ? "ascending" : "descending";
--- a/toolkit/mozapps/extensions/test/browser/browser_uninstalling.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_uninstalling.js
@@ -35,16 +35,36 @@ function test() {
     name: "Uninstall doesn't need restart 3",
     type: "extension",
     operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
   }, {
     id: "addon5@tests.mozilla.org",
     name: "Uninstall doesn't need restart 4",
     type: "extension",
     operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+  }, {
+    id: "addon6@tests.mozilla.org",
+    name: "Uninstall doesn't need restart 5",
+    type: "extension",
+    operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+  }, {
+    id: "addon7@tests.mozilla.org",
+    name: "Uninstall doesn't need restart 6",
+    type: "extension",
+    operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+  }, {
+    id: "addon8@tests.mozilla.org",
+    name: "Uninstall doesn't need restart 7",
+    type: "extension",
+    operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
+  }, {
+    id: "addon9@tests.mozilla.org",
+    name: "Uninstall doesn't need restart 8",
+    type: "extension",
+    operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
   }]);
 
   open_manager(null, function(aWindow) {
     gManagerWindow = aWindow;
     gDocument = gManagerWindow.document;
     gCategoryUtilities = new CategoryUtilities(gManagerWindow);
     run_next_test();
   });
@@ -707,17 +727,17 @@ add_test(function() {
 
       // Force XBL to apply
       item.clientTop;
 
       is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
 
       ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
 
-      var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
       isnot(button, null, "Should have a restart button");
       ok(!button.hidden, "Restart button should not be hidden");
       button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
       isnot(button, null, "Should have an undo button");
 
       gCategoryUtilities.openType("plugin", function() {
         is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to plugin");
         searchBox.value = "Uninstall";
@@ -751,20 +771,21 @@ add_test(function() {
 
           run_next_test();
         });
       });
     });
   });
 });
 
-// Tests that switching away from the list view finalises the uninstall of a
-// restartless add-on
+// Tests that switching away from the list view finalises the uninstall of
+// multiple restartless add-ons
 add_test(function() {
   var ID = "addon2@tests.mozilla.org";
+  var ID2 = "addon6@tests.mozilla.org";
   var list = gDocument.getElementById("addon-list");
 
   // Select the extensions category
   gCategoryUtilities.openType("extension", function() {
     is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
 
     AddonManager.getAddonByID(ID, function(aAddon) {
       ok(aAddon.isActive, "Add-on should be active");
@@ -783,46 +804,217 @@ add_test(function() {
       // Force XBL to apply
       item.clientTop;
 
       is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
 
       ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
       ok(!aAddon.isActive, "Add-on should be inactive");
 
-      var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
       isnot(button, null, "Should have a restart button");
       ok(button.hidden, "Restart button should be hidden");
       button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
       isnot(button, null, "Should have an undo button");
 
+      item = get_item_in_list(ID2, list);
+      isnot(item, null, "Should have found the add-on in the list");
+
+      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+      isnot(button, null, "Should have a remove button");
+      ok(!button.disabled, "Button should not be disabled");
+
+      EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow);
+
       gCategoryUtilities.openType("plugin", function() {
         is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to extension");
 
-        AddonManager.getAddonByID(ID, function(aAddon) {
+        AddonManager.getAddonsByIDs([ID, ID2], function([aAddon, aAddon2]) {
           is(aAddon, null, "Add-on should no longer be installed");
+          is(aAddon2, null, "Second add-on should no longer be installed");
 
           gCategoryUtilities.openType("extension", function() {
             is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
 
             var item = get_item_in_list(ID, list);
             is(item, null, "Should not have found the add-on in the list");
+            item = get_item_in_list(ID2, list);
+            is(item, null, "Should not have found the second add-on in the list");
 
             run_next_test();
           });
         });
       });
     });
   });
 });
 
-// Tests that switching away from the search view finalises the uninstall of a
-// restartless add-on
+// Tests that switching away from the search view finalises the uninstall of
+// multiple restartless add-ons
 add_test(function() {
   var ID = "addon3@tests.mozilla.org";
+  var ID2 = "addon7@tests.mozilla.org";
+  var list = gDocument.getElementById("search-list");
+
+  var searchBox = gManagerWindow.document.getElementById("header-search");
+  searchBox.value = "Uninstall";
+
+  EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow);
+  EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+  wait_for_view_load(gManagerWindow, function() {
+    is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+    // Make sure to show local add-ons
+    EventUtils.synthesizeMouse(gDocument.getElementById("search-filter-local"), 2, 2, { }, gManagerWindow);
+
+    AddonManager.getAddonByID(ID, function(aAddon) {
+      ok(aAddon.isActive, "Add-on should be active");
+      ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+      ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+      var item = get_item_in_list(ID, list);
+      isnot(item, null, "Should have found the add-on in the list");
+
+      var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+      isnot(button, null, "Should have a remove button");
+      ok(!button.disabled, "Button should not be disabled");
+
+      EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow);
+
+      // Force XBL to apply
+      item.clientTop;
+
+      is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+      ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+      ok(!aAddon.isActive, "Add-on should be inactive");
+
+      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+      isnot(button, null, "Should have a restart button");
+      ok(button.hidden, "Restart button should be hidden");
+      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+      isnot(button, null, "Should have an undo button");
+
+      item = get_item_in_list(ID2, list);
+      isnot(item, null, "Should have found the add-on in the list");
+
+      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+      isnot(button, null, "Should have a remove button");
+      ok(!button.disabled, "Button should not be disabled");
+
+      EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow);
+
+      gCategoryUtilities.openType("plugin", function() {
+        is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to extension");
+
+        AddonManager.getAddonsByIDs([ID, ID2], function([aAddon, aAddon2]) {
+          is(aAddon, null, "Add-on should no longer be installed");
+          is(aAddon2, null, "Second add-on should no longer be installed");
+
+          searchBox.value = "Uninstall";
+
+          EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow);
+          EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
+
+          wait_for_view_load(gManagerWindow, function() {
+            is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
+
+            var item = get_item_in_list(ID, list);
+            is(item, null, "Should not have found the add-on in the list");
+            item = get_item_in_list(ID2, list);
+            is(item, null, "Should not have found the second add-on in the list");
+
+            run_next_test();
+          });
+        });
+      });
+    });
+  });
+});
+
+// Tests that closing the manager from the list view finalises the uninstall of
+// multiple restartless add-ons
+add_test(function() {
+  var ID = "addon4@tests.mozilla.org";
+  var ID2 = "addon8@tests.mozilla.org";
+  var list = gDocument.getElementById("addon-list");
+
+  // Select the extensions category
+  gCategoryUtilities.openType("extension", function() {
+    is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+    AddonManager.getAddonByID(ID, function(aAddon) {
+      ok(aAddon.isActive, "Add-on should be active");
+      ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
+      ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+
+      var item = get_item_in_list(ID, list);
+      isnot(item, null, "Should have found the add-on in the list");
+
+      var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+      isnot(button, null, "Should have a remove button");
+      ok(!button.disabled, "Button should not be disabled");
+
+      EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow);
+
+      // Force XBL to apply
+      item.clientTop;
+
+      is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+      ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
+      ok(!aAddon.isActive, "Add-on should be inactive");
+
+      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
+      isnot(button, null, "Should have a restart button");
+      ok(button.hidden, "Restart button should be hidden");
+      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
+      isnot(button, null, "Should have an undo button");
+
+      item = get_item_in_list(ID2, list);
+      isnot(item, null, "Should have found the add-on in the list");
+
+      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+      isnot(button, null, "Should have a remove button");
+      ok(!button.disabled, "Button should not be disabled");
+
+      EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow);
+
+      close_manager(gManagerWindow, function() {
+        AddonManager.getAddonsByIDs([ID, ID2], function([aAddon, aAddon2]) {
+          is(aAddon, null, "Add-on should no longer be installed");
+          is(aAddon2, null, "Second add-on should no longer be installed");
+
+          open_manager(null, function(aWindow) {
+            gManagerWindow = aWindow;
+            gDocument = gManagerWindow.document;
+            gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+            var list = gDocument.getElementById("addon-list");
+
+            is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
+
+            var item = get_item_in_list(ID, list);
+            is(item, null, "Should not have found the add-on in the list");
+            item = get_item_in_list(ID2, list);
+            is(item, null, "Should not have found the second add-on in the list");
+
+            run_next_test();
+          });
+        });
+      });
+    });
+  });
+});
+
+// Tests that closing the manager from the search view finalises the uninstall
+// of multiple restartless add-ons
+add_test(function() {
+  var ID = "addon5@tests.mozilla.org";
+  var ID2 = "addon9@tests.mozilla.org";
   var list = gDocument.getElementById("search-list");
 
   var searchBox = gManagerWindow.document.getElementById("header-search");
   searchBox.value = "Uninstall";
 
   EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow);
   EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
 
@@ -855,151 +1047,29 @@ add_test(function() {
       ok(!aAddon.isActive, "Add-on should be inactive");
 
       var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
       isnot(button, null, "Should have a restart button");
       ok(button.hidden, "Restart button should be hidden");
       button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
       isnot(button, null, "Should have an undo button");
 
-      gCategoryUtilities.openType("plugin", function() {
-        is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to extension");
-
-        AddonManager.getAddonByID(ID, function(aAddon) {
-          is(aAddon, null, "Add-on should no longer be installed");
-
-          searchBox.value = "Uninstall";
-
-          EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow);
-          EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
-
-          wait_for_view_load(gManagerWindow, function() {
-            is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
-
-            var item = get_item_in_list(ID, list);
-            is(item, null, "Should not have found the add-on in the list");
-
-            run_next_test();
-          });
-        });
-      });
-    });
-  });
-});
-
-// Tests that closing the manager from the list view finalises the uninstall of
-// a restartless add-on
-add_test(function() {
-  var ID = "addon4@tests.mozilla.org";
-  var list = gDocument.getElementById("addon-list");
-
-  // Select the extensions category
-  gCategoryUtilities.openType("extension", function() {
-    is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
-
-    AddonManager.getAddonByID(ID, function(aAddon) {
-      ok(aAddon.isActive, "Add-on should be active");
-      ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
-      ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
-
-      var item = get_item_in_list(ID, list);
+      item = get_item_in_list(ID2, list);
       isnot(item, null, "Should have found the add-on in the list");
 
-      var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
       isnot(button, null, "Should have a remove button");
       ok(!button.disabled, "Button should not be disabled");
 
       EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow);
 
-      // Force XBL to apply
-      item.clientTop;
-
-      is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
-
-      ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
-      ok(!aAddon.isActive, "Add-on should be inactive");
-
-      var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
-      isnot(button, null, "Should have a restart button");
-      ok(button.hidden, "Restart button should be hidden");
-      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
-      isnot(button, null, "Should have an undo button");
-
       close_manager(gManagerWindow, function() {
-        AddonManager.getAddonByID(ID, function(aAddon) {
+        AddonManager.getAddonsByIDs([ID, ID2], function([aAddon, aAddon2]) {
           is(aAddon, null, "Add-on should no longer be installed");
-
-          open_manager(null, function(aWindow) {
-            gManagerWindow = aWindow;
-            gDocument = gManagerWindow.document;
-            gCategoryUtilities = new CategoryUtilities(gManagerWindow);
-            var list = gDocument.getElementById("addon-list");
-
-            is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
-
-            var item = get_item_in_list(ID, list);
-            is(item, null, "Should not have found the add-on in the list");
-
-            run_next_test();
-          });
-        });
-      });
-    });
-  });
-});
-
-// Tests that closing the manager from the search view finalises the uninstall
-// of a restartless add-on
-add_test(function() {
-  var ID = "addon5@tests.mozilla.org";
-  var list = gDocument.getElementById("search-list");
-
-  var searchBox = gManagerWindow.document.getElementById("header-search");
-  searchBox.value = "Uninstall";
-
-  EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow);
-  EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
-
-  wait_for_view_load(gManagerWindow, function() {
-    is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
-
-    // Make sure to show local add-ons
-    EventUtils.synthesizeMouse(gDocument.getElementById("search-filter-local"), 2, 2, { }, gManagerWindow);
-
-    AddonManager.getAddonByID(ID, function(aAddon) {
-      ok(aAddon.isActive, "Add-on should be active");
-      ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
-      ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
-
-      var item = get_item_in_list(ID, list);
-      isnot(item, null, "Should have found the add-on in the list");
-
-      var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
-      isnot(button, null, "Should have a remove button");
-      ok(!button.disabled, "Button should not be disabled");
-
-      EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow);
-
-      // Force XBL to apply
-      item.clientTop;
-
-      is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
-
-      ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
-      ok(!aAddon.isActive, "Add-on should be inactive");
-
-      var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
-      isnot(button, null, "Should have a restart button");
-      ok(button.hidden, "Restart button should be hidden");
-      button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn");
-      isnot(button, null, "Should have an undo button");
-
-      close_manager(gManagerWindow, function() {
-        AddonManager.getAddonByID(ID, function(aAddon) {
-          is(aAddon, null, "Add-on should no longer be installed");
+          is(aAddon2, null, "Second add-on should no longer be installed");
 
           open_manager(null, function(aWindow) {
             gManagerWindow = aWindow;
             gDocument = gManagerWindow.document;
             gCategoryUtilities = new CategoryUtilities(gManagerWindow);
             var list = gDocument.getElementById("search-list");
             var searchBox = gManagerWindow.document.getElementById("header-search");
 
@@ -1008,16 +1078,18 @@ add_test(function() {
             EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow);
             EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow);
 
             wait_for_view_load(gManagerWindow, function() {
               is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search");
 
               var item = get_item_in_list(ID, list);
               is(item, null, "Should not have found the add-on in the list");
+              item = get_item_in_list(ID2, list);
+              is(item, null, "Should not have found the second add-on in the list");
 
               run_next_test();
             });
           });
         });
       });
     });
   });