Bug 1510321 - avoid calling buildArea for all the builtin toolbars, r=jaws,bgrins
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Wed, 28 Nov 2018 23:23:31 +0000
changeset 507864 da642018d1c67b613d25a17208e83a5d2ab09063
parent 507863 c4fe963f4eb4167dbb2149cfc7444a688de8bf66
child 507865 1aa87e9e3c71eb756c446b285f098d0dc267b38e
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws, bgrins
bugs1510321
milestone65.0a1
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 1510321 - avoid calling buildArea for all the builtin toolbars, r=jaws,bgrins Differential Revision: https://phabricator.services.mozilla.com/D13259
browser/components/customizableui/CustomizableUI.jsm
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -658,18 +658,18 @@ var CustomizableUIInternal = {
         }
       } else {
         this.restoreStateForArea(aName);
       }
 
       // If we have pending build area nodes, register all of them
       if (gPendingBuildAreas.has(aName)) {
         let pendingNodes = gPendingBuildAreas.get(aName);
-        for (let [pendingNode, existingChildren] of pendingNodes) {
-          this.registerToolbarNode(pendingNode, existingChildren);
+        for (let pendingNode of pendingNodes) {
+          this.registerToolbarNode(pendingNode);
         }
         gPendingBuildAreas.delete(aName);
       }
     }
   },
 
   unregisterArea(aName, aDestroyPlacements) {
     if (typeof aName != "string" || !/^[a-z0-9-_]{1,}$/i.test(aName)) {
@@ -709,45 +709,45 @@ var CustomizableUIInternal = {
         }
       }
       gBuildAreas.delete(aName);
     } finally {
       this.endBatchUpdate(true);
     }
   },
 
-  registerToolbarNode(aToolbar, aExistingChildren) {
+  registerToolbarNode(aToolbar) {
     let area = aToolbar.id;
     if (gBuildAreas.has(area) && gBuildAreas.get(area).has(aToolbar)) {
       return;
     }
     let areaProperties = gAreas.get(area);
 
     // If this area is not registered, try to do it automatically:
     if (!areaProperties) {
       if (!gPendingBuildAreas.has(area)) {
-        gPendingBuildAreas.set(area, new Map());
+        gPendingBuildAreas.set(area, []);
       }
-      let pendingNodes = gPendingBuildAreas.get(area);
-      pendingNodes.set(aToolbar, aExistingChildren);
+      gPendingBuildAreas.get(area).push(aToolbar);
       return;
     }
 
     this.beginBatchUpdate();
     try {
       let placements = gPlacements.get(area);
       if (!placements && areaProperties.get("type") == CustomizableUI.TYPE_TOOLBAR) {
         this.restoreStateForArea(area);
         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])) {
+      // For toolbars that need it, mark as dirty.
+      let defaultPlacements = areaProperties.get("defaultPlacements");
+      if (!this._builtinToolbars.has(area) ||
+          placements.length != defaultPlacements.length ||
+          !placements.every((id, i) => id == defaultPlacements[i])) {
         gDirtyAreaCache.add(area);
       }
 
       if (areaProperties.has("overflowable")) {
         aToolbar.overflowable = new OverflowableToolbar(aToolbar);
       }
 
       this.registerBuildArea(area, aToolbar);
@@ -757,24 +757,45 @@ var CustomizableUIInternal = {
       // 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);
+      } else {
+        // We must have a builtin toolbar that's in the default state. We need
+        // to only make sure that all the special nodes are correct.
+        let specials = placements.filter(p => this.isSpecialWidget(p));
+        if (specials.length) {
+          this.updateSpecialsForBuiltinToolbar(aToolbar, specials);
+        }
       }
       this.notifyListeners("onAreaNodeRegistered", area,
                            this.getCustomizationTarget(aToolbar));
     } finally {
       this.endBatchUpdate();
     }
   },
 
+  updateSpecialsForBuiltinToolbar(aToolbar, aSpecialIDs) {
+    // Nodes are going to be in the correct order, so we can do this straightforwardly:
+    let {children} = this.getCustomizationTarget(aToolbar);
+    for (let kid of children) {
+      if (this.matchingSpecials(aSpecialIDs[0], kid) &&
+          kid.getAttribute("skipintoolbarset") != "true") {
+        kid.id = aSpecialIDs.shift();
+      }
+      if (!aSpecialIDs.length) {
+        return;
+      }
+    }
+  },
+
   buildArea(aArea, aPlacements, aAreaNode) {
     let document = aAreaNode.ownerDocument;
     let window = document.defaultView;
     let inPrivateWindow = PrivateBrowsingUtils.isWindowPrivate(window);
     let container = this.getCustomizationTarget(aAreaNode);
     let areaIsPanel = gAreas.get(aArea).get("type") == CustomizableUI.TYPE_MENU_PANEL;
 
     if (!container) {
@@ -1155,26 +1176,22 @@ var CustomizableUIInternal = {
       }
     }
 
     for (let [, widget] of gPalette) {
       widget.instances.delete(document);
       this.notifyListeners("onWidgetInstanceRemoved", widget.id, document);
     }
 
-    for (let [, areaMap] of gPendingBuildAreas) {
-      let toDelete = [];
-      for (let [areaNode ] of areaMap) {
-        if (areaNode.ownerDocument == document) {
-          toDelete.push(areaNode);
+    for (let [, pendingNodes] of gPendingBuildAreas) {
+      for (let i = pendingNodes.length - 1; i >= 0; i--) {
+        if (pendingNodes[i].ownerDocument == document) {
+          pendingNodes.splice(i, 1);
         }
       }
-      for (let areaNode of toDelete) {
-        areaMap.delete(areaNode);
-      }
     }
 
     this.notifyListeners("onWindowClosed", aWindow);
   },
 
   setLocationAttributes(aNode, aArea) {
     let props = gAreas.get(aArea);
     if (!props) {
@@ -3137,20 +3154,17 @@ var CustomizableUI = {
    *
    * Note that ideally, you should register your toolbar using registerArea
    * before calling this. If you don't, the node will be saved for processing when
    * you call registerArea. Note that CustomizableUI won't restore state in the area,
    * allow the user to customize it in customize mode, or otherwise deal
    * with it, until the area has been registered.
    */
   registerToolbarNode(aToolbar) {
-    let children = Array.from(aToolbar.children)
-                        .filter(child => child.getAttribute("skipintoolbarset") != "true" && child.id)
-                        .map(child => child.id);
-    CustomizableUIInternal.registerToolbarNode(aToolbar, children);
+    CustomizableUIInternal.registerToolbarNode(aToolbar);
   },
   /**
    * Register the menu panel node. This method should not be called by anyone
    * apart from the built-in PanelUI.
    * @param aPanelContents the panel contents DOM node being registered.
    * @param aArea the area for which to register this node.
    */
   registerMenuPanel(aPanelContents, aArea) {