Backed out changeset de713c4fc4e1 (bug 941321)
authorMike Conley <mconley@mozilla.com>
Fri, 29 Nov 2013 11:19:26 -0500
changeset 172710 a99c95838e44674abbea7b6d18a12fd20e725b94
parent 172709 25c1fc075d51fb8450d0b0b63ac072c6caf78ec1
child 172711 33c5883b1a899cac022e53236ed993448a075fc1
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs941321
milestone28.0a1
backs outde713c4fc4e104dab3fbee1219feba5327ed5fa0
Backed out changeset de713c4fc4e1 (bug 941321)
browser/components/customizableui/content/panelUI.js
browser/components/customizableui/content/toolbar.xml
browser/components/customizableui/src/CustomizableUI.jsm
browser/components/customizableui/src/CustomizeMode.jsm
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -215,21 +215,18 @@ const PanelUI = {
                        paddingLeft, paddingRight].join(" + ");
         this.scroller.style.width = "calc(" + calcStr + ")";
       }
 
       if (aCustomizing) {
         CustomizableUI.registerMenuPanel(this.contents);
       } else {
         this.beginBatchUpdate();
-        try {
-          CustomizableUI.registerMenuPanel(this.contents);
-        } finally {
-          this.endBatchUpdate();
-        }
+        CustomizableUI.registerMenuPanel(this.contents);
+        this.endBatchUpdate();
       }
       this.panel.hidden = false;
     }.bind(this)).then(null, Cu.reportError);
 
     return this._readyPromise;
   },
 
   /**
--- a/browser/components/customizableui/content/toolbar.xml
+++ b/browser/components/customizableui/content/toolbar.xml
@@ -188,42 +188,39 @@
         <setter><![CDATA[
           // Get list of new and old ids:
           let newVal = (val || '').split(',').filter(x => x);
           let oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
 
           // Get a list of items only in the new list
           let newIds = [id for (id of newVal) if (oldIds.indexOf(id) == -1)];
           CustomizableUI.beginBatchUpdate();
-          try {
-            for (let newId of newIds) {
-              oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
-              let nextId = newId;
-              let pos;
-              do {
-                // Get the next item
-                nextId = newVal[newVal.indexOf(nextId) + 1];
-                // Figure out where it is in the old list
-                pos = oldIds.indexOf(nextId);
-                // If it's not in the old list, repeat:
-              } while (pos == -1 && nextId);
-              if (pos == -1) {
-                pos = null; // We didn't find anything, insert at the end
-              }
-              CustomizableUI.addWidgetToArea(newId, this.id, pos);
+          for (let newId of newIds) {
+            oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
+            let nextId = newId;
+            let pos;
+            do {
+              // Get the next item
+              nextId = newVal[newVal.indexOf(nextId) + 1];
+              // Figure out where it is in the old list
+              pos = oldIds.indexOf(nextId);
+              // If it's not in the old list, repeat:
+            } while (pos == -1 && nextId);
+            if (pos == -1) {
+              pos = null; // We didn't find anything, insert at the end
             }
+            CustomizableUI.addWidgetToArea(newId, this.id, pos);
+          }
 
-            let currentIds = this.currentSet.split(',');
-            let removedIds = [id for (id of currentIds) if (newIds.indexOf(id) == -1 && newVal.indexOf(id) == -1)];
-            for (let removedId of removedIds) {
-              CustomizableUI.removeWidgetFromArea(removedId);
-            }
-          } finally {
-            CustomizableUI.endBatchUpdate();
+          let currentIds = this.currentSet.split(',');
+          let removedIds = [id for (id of currentIds) if (newIds.indexOf(id) == -1 && newVal.indexOf(id) == -1)];
+          for (let removedId of removedIds) {
+            CustomizableUI.removeWidgetFromArea(removedId);
           }
+          CustomizableUI.endBatchUpdate();
         ]]></setter>
       </property>
 
 
     </implementation>
   </binding>
 
   <binding id="toolbar-menubar-stub">
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -274,203 +274,195 @@ let CustomizableUIInternal = {
       throw new Error("Invalid area name");
     }
     if (!gAreas.has(aName)) {
       throw new Error("Area not registered");
     }
 
     // Move all the widgets out
     this.beginBatchUpdate();
-    try {
-      let placements = gPlacements.get(aName);
-      placements.forEach(this.removeWidgetFromArea, this);
+    let placements = gPlacements.get(aName);
+    placements.forEach(this.removeWidgetFromArea, this);
 
-      // Delete all remaining traces.
-      gAreas.delete(aName);
-      gPlacements.delete(aName);
-      gFuturePlacements.delete(aName);
-      gBuildAreas.delete(aName);
-    } finally {
-      this.endBatchUpdate(true);
-    }
+    // Delete all remaining traces.
+    gAreas.delete(aName);
+    gPlacements.delete(aName);
+    gFuturePlacements.delete(aName);
+    gBuildAreas.delete(aName);
+    this.endBatchUpdate(true);
   },
 
   registerToolbarNode: function(aToolbar, aExistingChildren) {
     let area = aToolbar.id;
     if (gBuildAreas.has(area) && gBuildAreas.get(area).has(aToolbar)) {
       return;
     }
     let document = aToolbar.ownerDocument;
     let areaProperties = gAreas.get(area);
 
     if (!areaProperties) {
       throw new Error("Unknown customization area: " + area);
     }
 
     this.beginBatchUpdate();
-    try {
-      let placements = gPlacements.get(area);
-      if (!placements && areaProperties.has("legacy")) {
-        let legacyState = aToolbar.getAttribute("currentset");
-        if (legacyState) {
-          legacyState = legacyState.split(",").filter(s => s);
-        }
-
-        // Manually restore the state here, so the legacy state can be converted. 
-        this.restoreStateForArea(area, legacyState);
-        placements = gPlacements.get(area);
-      }
-
-      // Check that the current children and the current placements match. If
-      // not, mark it as dirty:
-      if (aExistingChildren.length != placements.length ||
-          aExistingChildren.every((id, i) => id == placements[i])) {
-        gDirtyAreaCache.add(area);
+    let placements = gPlacements.get(area);
+    if (!placements && areaProperties.has("legacy")) {
+      let legacyState = aToolbar.getAttribute("currentset");
+      if (legacyState) {
+        legacyState = legacyState.split(",").filter(s => s);
       }
 
-      if (areaProperties.has("overflowable")) {
-        aToolbar.overflowable = new OverflowableToolbar(aToolbar);
-      }
+      // Manually restore the state here, so the legacy state can be converted. 
+      this.restoreStateForArea(area, legacyState);
+      placements = gPlacements.get(area);
+    }
 
-      this.registerBuildArea(area, aToolbar);
+    // Check that the current children and the current placements match. If
+    // not, mark it as dirty:
+    if (aExistingChildren.length != placements.length ||
+        aExistingChildren.every((id, i) => id == placements[i])) {
+      gDirtyAreaCache.add(area);
+    }
+
+    if (areaProperties.has("overflowable")) {
+      aToolbar.overflowable = new OverflowableToolbar(aToolbar);
+    }
 
-      // We only build the toolbar if it's been marked as "dirty". Dirty means
-      // one of the following things:
-      // 1) Items have been added, moved or removed from this toolbar before.
-      // 2) The number of children of the toolbar does not match the length of
-      //    the placements array for that area.
-      //
-      // This notion of being "dirty" is stored in a cache which is persisted
-      // in the saved state.
-      if (gDirtyAreaCache.has(area)) {
-        this.buildArea(area, placements, aToolbar);
-      }
-      aToolbar.setAttribute("currentset", placements.join(","));
-    } finally {
-      this.endBatchUpdate();
+    this.registerBuildArea(area, aToolbar);
+
+    // We only build the toolbar if it's been marked as "dirty". Dirty means
+    // one of the following things:
+    // 1) Items have been added, moved or removed from this toolbar before.
+    // 2) The number of children of the toolbar does not match the length of
+    //    the placements array for that area.
+    //
+    // This notion of being "dirty" is stored in a cache which is persisted
+    // in the saved state.
+    if (gDirtyAreaCache.has(area)) {
+      this.buildArea(area, placements, aToolbar);
     }
+    aToolbar.setAttribute("currentset", placements.join(","));
+    this.endBatchUpdate();
   },
 
   buildArea: function(aArea, aPlacements, aAreaNode) {
     let document = aAreaNode.ownerDocument;
     let window = document.defaultView;
     let inPrivateWindow = PrivateBrowsingUtils.isWindowPrivate(window);
     let container = aAreaNode.customizationTarget;
 
     if (!container) {
       throw new Error("Expected area " + aArea
                       + " to have a customizationTarget attribute.");
     }
 
+    this.beginBatchUpdate();
+
     // Restore nav-bar visibility since it may have been hidden
     // through a migration path (bug 938980) or an add-on.
     if (aArea == CustomizableUI.AREA_NAVBAR) {
       aAreaNode.collapsed = false;
     }
 
-    this.beginBatchUpdate();
+    let currentNode = container.firstChild;
+    let placementsToRemove = new Set();
+    for (let id of aPlacements) {
+      while (currentNode && currentNode.getAttribute("skipintoolbarset") == "true") {
+        currentNode = currentNode.nextSibling;
+      }
+
+      if (currentNode && currentNode.id == id) {
+        currentNode = currentNode.nextSibling;
+        continue;
+      }
 
-    try {
-      let currentNode = container.firstChild;
-      let placementsToRemove = new Set();
-      for (let id of aPlacements) {
-        while (currentNode && currentNode.getAttribute("skipintoolbarset") == "true") {
-          currentNode = currentNode.nextSibling;
-        }
+      let [provider, node] = this.getWidgetNode(id, window);
+      if (!node) {
+        LOG("Unknown widget: " + id);
+        continue;
+      }
 
-        if (currentNode && currentNode.id == id) {
-          currentNode = currentNode.nextSibling;
-          continue;
-        }
+      // If the placements have items in them which are (now) no longer removable,
+      // we shouldn't be moving them:
+      if (node.parentNode != container && !this.isWidgetRemovable(node)) {
+        placementsToRemove.add(id);
+        continue;
+      }
 
-        let [provider, node] = this.getWidgetNode(id, window);
-        if (!node) {
-          LOG("Unknown widget: " + id);
+      if (inPrivateWindow && provider == CustomizableUI.PROVIDER_API) {
+        let widget = gPalette.get(id);
+        if (!widget.showInPrivateBrowsing && inPrivateWindow) {
           continue;
         }
-
-        // If the placements have items in them which are (now) no longer removable,
-        // we shouldn't be moving them:
-        if (node.parentNode != container && !this.isWidgetRemovable(node)) {
-          placementsToRemove.add(id);
-          continue;
-        }
+      }
 
-        if (inPrivateWindow && provider == CustomizableUI.PROVIDER_API) {
-          let widget = gPalette.get(id);
-          if (!widget.showInPrivateBrowsing && inPrivateWindow) {
-            continue;
-          }
-        }
-
-        this.ensureButtonContextMenu(node, aAreaNode);
-        if (node.localName == "toolbarbutton" && aArea == CustomizableUI.AREA_PANEL) {
-          node.setAttribute("tabindex", "0");
-          if (!node.hasAttribute("type")) {
-            node.setAttribute("type", "wrap");
-          }
-        }
-
-        this.insertWidgetBefore(node, currentNode, container, aArea);
-        if (gResetting) {
-          this.notifyListeners("onWidgetReset", id, aArea);
+      this.ensureButtonContextMenu(node, aAreaNode);
+      if (node.localName == "toolbarbutton" && aArea == CustomizableUI.AREA_PANEL) {
+        node.setAttribute("tabindex", "0");
+        if (!node.hasAttribute("type")) {
+          node.setAttribute("type", "wrap");
         }
       }
 
-      if (currentNode) {
-        let palette = aAreaNode.toolbox ? aAreaNode.toolbox.palette : null;
-        let limit = currentNode.previousSibling;
-        let node = container.lastChild;
-        while (node && node != limit) {
-          let previousSibling = node.previousSibling;
-          // Nodes opt-in to removability. If they're removable, and we haven't
-          // seen them in the placements array, then we toss them into the palette
-          // if one exists. If no palette exists, we just remove the node. If the
-          // node is not removable, we leave it where it is. However, we can only
-          // safely touch elements that have an ID - both because we depend on
-          // IDs, and because such elements are not intended to be widgets
-          // (eg, titlebar-placeholder elements).
-          if (node.id && node.getAttribute("skipintoolbarset") != "true") {
-            if (this.isWidgetRemovable(node)) {
-              if (palette && !this.isSpecialWidget(node.id)) {
-                palette.appendChild(node);
-                this.removeLocationAttributes(node);
-              } else {
-                container.removeChild(node);
-              }
+      this.insertWidgetBefore(node, currentNode, container, aArea);
+      if (gResetting) {
+        this.notifyListeners("onWidgetReset", id, aArea);
+      }
+    }
+
+    if (currentNode) {
+      let palette = aAreaNode.toolbox ? aAreaNode.toolbox.palette : null;
+      let limit = currentNode.previousSibling;
+      let node = container.lastChild;
+      while (node && node != limit) {
+        let previousSibling = node.previousSibling;
+        // Nodes opt-in to removability. If they're removable, and we haven't
+        // seen them in the placements array, then we toss them into the palette
+        // if one exists. If no palette exists, we just remove the node. If the
+        // node is not removable, we leave it where it is. However, we can only
+        // safely touch elements that have an ID - both because we depend on
+        // IDs, and because such elements are not intended to be widgets
+        // (eg, titlebar-placeholder elements).
+        if (node.id && node.getAttribute("skipintoolbarset") != "true") {
+          if (this.isWidgetRemovable(node)) {
+            if (palette && !this.isSpecialWidget(node.id)) {
+              palette.appendChild(node);
+              this.removeLocationAttributes(node);
             } else {
-              this.setLocationAttributes(currentNode, aArea);
-              node.setAttribute("removable", false);
-              LOG("Adding non-removable widget to placements of " + aArea + ": " +
-                  node.id);
-              gPlacements.get(aArea).push(node.id);
-              gDirty = true;
+              container.removeChild(node);
             }
+          } else {
+            this.setLocationAttributes(currentNode, aArea);
+            node.setAttribute("removable", false);
+            LOG("Adding non-removable widget to placements of " + aArea + ": " +
+                node.id);
+            gPlacements.get(aArea).push(node.id);
+            gDirty = true;
           }
-          node = previousSibling;
         }
+        node = previousSibling;
       }
+    }
 
-      // If there are placements in here which aren't removable from their original area,
-      // we remove them from this area's placement array. They will (have) be(en) added
-      // to their original area's placements array in the block above this one.
-      if (placementsToRemove.size) {
-        let placementAry = gPlacements.get(aArea);
-        for (let id of placementsToRemove) {
-          let index = placementAry.indexOf(id);
-          placementAry.splice(index, 1);
-        }
+    // If there are placements in here which aren't removable from their original area,
+    // we remove them from this area's placement array. They will (have) be(en) added
+    // to their original area's placements array in the block above this one.
+    if (placementsToRemove.size) {
+      let placementAry = gPlacements.get(aArea);
+      for (let id of placementsToRemove) {
+        let index = placementAry.indexOf(id);
+        placementAry.splice(index, 1);
       }
+    }
 
-      if (gResetting) {
-        this.notifyListeners("onAreaReset", aArea);
-      }
-    } finally {
-      this.endBatchUpdate();
+    if (gResetting) {
+      this.notifyListeners("onAreaReset", aArea);
     }
+
+    this.endBatchUpdate();
   },
 
   addPanelCloseListeners: function(aPanel) {
     gELS.addSystemEventListener(aPanel, "click", this, false);
     gELS.addSystemEventListener(aPanel, "keypress", this, false);
     let win = aPanel.ownerDocument.defaultView;
     if (!gPanelsForWindow.has(win)) {
       gPanelsForWindow.set(win, new Set());
@@ -1385,64 +1377,61 @@ let CustomizableUIInternal = {
 
   restoreStateForArea: function(aArea, aLegacyState) {
     if (gPlacements.has(aArea)) {
       // Already restored.
       return;
     }
 
     this.beginBatchUpdate();
-    try {
-      gRestoring = true;
+    gRestoring = true;
 
-      let restored = false;
-      gPlacements.set(aArea, []);
+    let restored = false;
+    gPlacements.set(aArea, []);
 
-      if (gSavedState && aArea in gSavedState.placements) {
-        LOG("Restoring " + aArea + " from saved state");
-        let placements = gSavedState.placements[aArea];
-        for (let id of placements)
-          this.addWidgetToArea(id, aArea);
-        gDirty = false;
-        restored = true;
-      }
+    if (gSavedState && aArea in gSavedState.placements) {
+      LOG("Restoring " + aArea + " from saved state");
+      let placements = gSavedState.placements[aArea];
+      for (let id of placements)
+        this.addWidgetToArea(id, aArea);
+      gDirty = false;
+      restored = true;
+    }
 
-      if (!restored && aLegacyState) {
-        LOG("Restoring " + aArea + " from legacy state");
-        for (let id of aLegacyState)
-          this.addWidgetToArea(id, aArea);
-        // Don't override dirty state, to ensure legacy state is saved here and
-        // therefore only used once.
-        restored = true;
-      }
+    if (!restored && aLegacyState) {
+      LOG("Restoring " + aArea + " from legacy state");
+      for (let id of aLegacyState)
+        this.addWidgetToArea(id, aArea);
+      // Don't override dirty state, to ensure legacy state is saved here and
+      // therefore only used once.
+      restored = true;
+    }
 
-      if (!restored) {
-        LOG("Restoring " + aArea + " from default state");
-        let defaults = gAreas.get(aArea).get("defaultPlacements");
-        if (defaults) {
-          for (let id of defaults)
-            this.addWidgetToArea(id, aArea, null, true);
-        }
-        gDirty = false;
+    if (!restored) {
+      LOG("Restoring " + aArea + " from default state");
+      let defaults = gAreas.get(aArea).get("defaultPlacements");
+      if (defaults) {
+        for (let id of defaults)
+          this.addWidgetToArea(id, aArea, null, true);
       }
+      gDirty = false;
+    }
 
-      // Finally, add widgets to the area that were added before the it was able
-      // to be restored. This can occur when add-ons register widgets for a
-      // lazily-restored area before it's been restored.
-      if (gFuturePlacements.has(aArea)) {
-        for (let id of gFuturePlacements.get(aArea))
-          this.addWidgetToArea(id, aArea);
-      }
+    // Finally, add widgets to the area that were added before the it was able
+    // to be restored. This can occur when add-ons register widgets for a
+    // lazily-restored area before it's been restored.
+    if (gFuturePlacements.has(aArea)) {
+      for (let id of gFuturePlacements.get(aArea))
+        this.addWidgetToArea(id, aArea);
+    }
 
-      LOG("Placements for " + aArea + ":\n\t" + gPlacements.get(aArea).join("\n\t"));
+    LOG("Placements for " + aArea + ":\n\t" + gPlacements.get(aArea).join("\n\t"));
 
-      gRestoring = false;
-    } finally {
-      this.endBatchUpdate();
-    }
+    gRestoring = false;
+    this.endBatchUpdate();
   },
 
   saveState: function() {
     if (gInBatchStack || !gDirty) {
       return;
     }
     let state = { placements: gPlacements,
                   seen: gSeenWidgets,
@@ -1587,29 +1576,27 @@ let CustomizableUIInternal = {
       try {
         autoAdd = Services.prefs.getBoolPref(kPrefCustomizationAutoAdd);
       } catch (e) {}
 
       // If the widget doesn't have an existing placement, and it hasn't been
       // seen before, then add it to its default area so it can be used.
       if (autoAdd && !widget.currentArea && !gSeenWidgets.has(widget.id)) {
         this.beginBatchUpdate();
-        try {
-          gSeenWidgets.add(widget.id);
+        gSeenWidgets.add(widget.id);
 
-          if (widget.defaultArea) {
-            if (this.isAreaLazy(widget.defaultArea)) {
-              gFuturePlacements.get(widget.defaultArea).add(widget.id);
-            } else {
-              this.addWidgetToArea(widget.id, widget.defaultArea);
-            }
+        if (widget.defaultArea) {
+          if (this.isAreaLazy(widget.defaultArea)) {
+            gFuturePlacements.get(widget.defaultArea).add(widget.id);
+          } else {
+            this.addWidgetToArea(widget.id, widget.defaultArea);
           }
-        } finally {
-          this.endBatchUpdate(true);
         }
+
+        this.endBatchUpdate(true);
       }
     }
 
     this.notifyListeners("onWidgetAfterCreation", widget.id, widget.currentArea);
     return widget.id;
   },
 
   createBuiltinWidget: function(aData) {
--- a/browser/components/customizableui/src/CustomizeMode.jsm
+++ b/browser/components/customizableui/src/CustomizeMode.jsm
@@ -88,19 +88,16 @@ CustomizeMode.prototype = {
 
     // We don't need to switch to kAboutURI, or open a new tab at
     // kAboutURI if we're already on it.
     if (this.browser.selectedBrowser.currentURI.spec != kAboutURI) {
       this.window.switchToTabHavingURI(kAboutURI, true);
       return;
     }
 
-    let window = this.window;
-    let document = this.document;
-
     Task.spawn(function() {
       // We shouldn't start customize mode until after browser-delayed-startup has finished:
       if (!this.window.gBrowserInit.delayedStartupFinished) {
         let delayedStartupDeferred = Promise.defer();
         let delayedStartupObserver = function(aSubject) {
           if (aSubject == this.window) {
             Services.obs.removeObserver(delayedStartupObserver, "browser-delayed-startup-finished");
             delayedStartupDeferred.resolve();
@@ -113,16 +110,19 @@ CustomizeMode.prototype = {
       // Disable lightweight themes while in customization mode since
       // they don't have large enough images to pad the full browser window.
       if (this.document.documentElement._lightweightTheme)
         this.document.documentElement._lightweightTheme.disable();
 
       this.dispatchToolboxEvent("beforecustomization");
       CustomizableUI.notifyStartCustomizing(this.window);
 
+      let window = this.window;
+      let document = this.document;
+
       // Add a keypress listener to the document so that we can quickly exit
       // customization mode when pressing ESC.
       document.addEventListener("keypress", this);
 
       // Same goes for the menu button - if we're customizing, a mousedown to the
       // menu button means a quick exit from customization mode.
       window.PanelUI.hide();
       window.PanelUI.menuButton.addEventListener("mousedown", this);
@@ -154,16 +154,17 @@ CustomizeMode.prototype = {
       yield window.PanelUI.ensureReady(true);
 
       this._mainViewContext = mainView.getAttribute("context");
       if (this._mainViewContext) {
         mainView.removeAttribute("context");
       }
 
       this._showPanelCustomizationPlaceholders();
+      CustomizableUI.addListener(this);
 
       yield this._wrapToolbarItems();
       yield this.populatePalette();
 
       this.visiblePalette.addEventListener("dragstart", this, true);
       this.visiblePalette.addEventListener("dragover", this, true);
       this.visiblePalette.addEventListener("dragexit", this, true);
       this.visiblePalette.addEventListener("drop", this, true);
@@ -178,26 +179,21 @@ CustomizeMode.prototype = {
 
       this._skipSourceNodeCheck = Services.prefs.getPrefType(kSkipSourceNodePref) == Ci.nsIPrefBranch.PREF_BOOL &&
                                   Services.prefs.getBoolPref(kSkipSourceNodePref);
 
       let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true]):not([collapsed=true])");
       for (let toolbar of customizableToolbars)
         toolbar.setAttribute("customizing", true);
 
-      CustomizableUI.addListener(this);
       window.PanelUI.endBatchUpdate();
       this._customizing = true;
       this._transitioning = false;
       this.dispatchToolboxEvent("customizationready");
-    }.bind(this)).then(null, function(e) {
-      ERROR(e);
-      // We should ensure this has been called, and calling it again doesn't hurt:
-      window.PanelUI.endBatchUpdate();
-    });
+    }.bind(this)).then(null, ERROR);
   },
 
   exit: function() {
     if (!this._customizing || this._transitioning) {
       return;
     }
 
     CustomizableUI.removeListener(this);
@@ -297,21 +293,17 @@ CustomizeMode.prototype = {
       for (let toolbar of customizableToolbars)
         toolbar.removeAttribute("customizing");
 
       this.window.PanelUI.endBatchUpdate();
       this._changed = false;
       this._transitioning = false;
       this.dispatchToolboxEvent("aftercustomization");
       CustomizableUI.notifyEndCustomizing(this.window);
-    }.bind(this)).then(null, function(e) {
-      ERROR(e);
-      // We should ensure this has been called, and calling it again doesn't hurt:
-      window.PanelUI.endBatchUpdate();
-    });
+    }.bind(this)).then(null, ERROR);
   },
 
   /**
    * The customize mode transition has 3 phases when entering:
    * 1) Pre-customization mode
    *    This is the starting phase of the browser.
    * 2) customize-entering
    *    This phase is a transition, optimized for smoothness.