Bug 1392081 - Use a document fragment to populate toolbar and menu Places views. r=gijs draft
authorMarco Bonardo <mbonardo@mozilla.com>
Fri, 25 Aug 2017 15:20:00 +0200
changeset 658998 584744131ae801c7cbfad2962a52416232510773
parent 658813 1401e3eec44df87963d3af329ef8a4183ab0483f
child 658999 c08f9ce1611895f5ad6c7b330bb9adbc67e70404
push id77973
push usermak77@bonardo.net
push dateTue, 05 Sep 2017 09:47:43 +0000
reviewersgijs
bugs1392081
milestone57.0a1
Bug 1392081 - Use a document fragment to populate toolbar and menu Places views. r=gijs MozReview-Commit-ID: 1Q4U3xXfF4Y
browser/components/places/content/browserPlacesViews.js
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -278,21 +278,22 @@ PlacesViewBase.prototype = {
       return;
     }
 
     this._cleanPopup(aPopup);
 
     let cc = resultNode.childCount;
     if (cc > 0) {
       this._setEmptyPopupStatus(aPopup, false);
-
+      let fragment = document.createDocumentFragment();
       for (let i = 0; i < cc; ++i) {
         let child = resultNode.getChild(i);
-        this._insertNewItemToPopup(child, aPopup, null);
+        this._insertNewItemToPopup(child, fragment);
       }
+      aPopup.insertBefore(fragment, aPopup._endMarker);
     } else {
       this._setEmptyPopupStatus(aPopup, true);
     }
     aPopup._built = true;
   },
 
   _removeChild: function PVB__removeChild(aChild) {
     // If document.popupNode pointed to this child, null it out,
@@ -399,26 +400,25 @@ PlacesViewBase.prototype = {
     element._placesNode = aPlacesNode;
     if (!this._domNodes.has(aPlacesNode))
       this._domNodes.set(aPlacesNode, element);
 
     return element;
   },
 
   _insertNewItemToPopup:
-  function PVB__insertNewItemToPopup(aNewChild, aPopup, aBefore) {
+  function PVB__insertNewItemToPopup(aNewChild, aInsertionNode, aBefore = null) {
     let element = this._createDOMNodeForPlacesNode(aNewChild);
-    let before = aBefore || aPopup._endMarker;
 
     if (element.localName == "menuitem" || element.localName == "menu") {
       if (typeof this.options.extraClasses.entry == "string")
         element.classList.add(this.options.extraClasses.entry);
     }
 
-    aPopup.insertBefore(element, before);
+    aInsertionNode.insertBefore(element, aBefore);
     return element;
   },
 
   _setLivemarkSiteURIMenuItem:
   function PVB__setLivemarkSiteURIMenuItem(aPopup) {
     let livemarkInfo = this.controller.getCachedLivemarkInfo(aPopup._placesNode);
     let siteUrl = livemarkInfo && livemarkInfo.siteURI ?
                   livemarkInfo.siteURI.spec : null;
@@ -630,17 +630,17 @@ PlacesViewBase.prototype = {
   function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
     if (!parentElt._built)
       return;
 
     let index = Array.prototype.indexOf.call(parentElt.childNodes, parentElt._startMarker) +
                 aIndex + 1;
     this._insertNewItemToPopup(aPlacesNode, parentElt,
-                               parentElt.childNodes[index]);
+                               parentElt.childNodes[index] || parentElt._endMarker);
     this._setEmptyPopupStatus(parentElt, false);
   },
 
   nodeMoved:
   function PBV_nodeMoved(aPlacesNode,
                          aOldParentPlacesNode, aOldIndex,
                          aNewParentPlacesNode, aNewIndex) {
     // Note: the current implementation of moveItem does not actually
@@ -1031,31 +1031,33 @@ PlacesToolbar.prototype = {
     if (this._overFolder.elt)
       this._clearOverFolder();
 
     this._openedMenuButton = null;
     while (this._rootElt.hasChildNodes()) {
       this._rootElt.firstChild.remove();
     }
 
+    let fragment = document.createDocumentFragment();
     let cc = this._resultNode.childCount;
     for (let i = 0; i < cc; ++i) {
-      this._insertNewItem(this._resultNode.getChild(i), null);
+      this._insertNewItem(this._resultNode.getChild(i), fragment);
     }
+    this._rootElt.appendChild(fragment);
 
     if (this._chevronPopup.hasAttribute("type")) {
       // Chevron has already been initialized, but since we are forcing
       // a rebuild of the toolbar, it has to be rebuilt.
       // Otherwise, it will be initialized when the toolbar overflows.
       this._chevronPopup.place = this.place;
     }
   },
 
   _insertNewItem:
-  function PT__insertNewItem(aChild, aBefore) {
+  function PT__insertNewItem(aChild, aInsertionNode, aBefore = null) {
     this._domNodes.delete(aChild);
 
     let type = aChild.type;
     let button;
     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
       button = document.createElement("toolbarseparator");
     } else {
       button = document.createElement("toolbarbutton");
@@ -1094,19 +1096,19 @@ PlacesToolbar.prototype = {
       }
     }
 
     button._placesNode = aChild;
     if (!this._domNodes.has(aChild))
       this._domNodes.set(aChild, button);
 
     if (aBefore)
-      this._rootElt.insertBefore(button, aBefore);
+      aInsertionNode.insertBefore(button, aBefore);
     else
-      this._rootElt.appendChild(button);
+      aInsertionNode.appendChild(button);
   },
 
   _updateChevronPopupNodesVisibility:
   function PT__updateChevronPopupNodesVisibility() {
     for (let i = 0, node = this._chevronPopup._startMarker.nextSibling;
          node != this._chevronPopup._endMarker;
          i++, node = node.nextSibling) {
       node.hidden = this._rootElt.childNodes[i].style.visibility != "hidden";
@@ -1248,17 +1250,17 @@ PlacesToolbar.prototype = {
       this._updateChevronPopupNodesVisibility();
   },
 
   nodeInserted:
   function PT_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
     if (parentElt == this._rootElt) {
       let children = this._rootElt.childNodes;
-      this._insertNewItem(aPlacesNode,
+      this._insertNewItem(aPlacesNode, this._rootElt,
         aIndex < children.length ? children[aIndex] : null);
       this.updateChevron();
       return;
     }
 
     PlacesViewBase.prototype.nodeInserted.apply(this, arguments);
   },
 
@@ -1872,17 +1874,17 @@ PlacesPanelMenuView.prototype = {
     return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
   },
 
   uninit: function PAMV_uninit() {
     PlacesViewBase.prototype.uninit.apply(this, arguments);
   },
 
   _insertNewItem:
-  function PAMV__insertNewItem(aChild, aBefore) {
+  function PAMV__insertNewItem(aChild, aInsertionNode, aBefore = null) {
     this._domNodes.delete(aChild);
 
     let type = aChild.type;
     let button;
     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
       button = document.createElement("toolbarseparator");
       button.setAttribute("class", "small-separator");
     } else {
@@ -1914,27 +1916,27 @@ PlacesPanelMenuView.prototype = {
                             PlacesUIUtils.guessUrlSchemeForUI(aChild.uri));
       }
     }
 
     button._placesNode = aChild;
     if (!this._domNodes.has(aChild))
       this._domNodes.set(aChild, button);
 
-    this._rootElt.insertBefore(button, aBefore);
+    aInsertionNode.insertBefore(button, aBefore);
   },
 
   nodeInserted:
   function PAMV_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
     if (parentElt != this._rootElt)
       return;
 
     let children = this._rootElt.childNodes;
-    this._insertNewItem(aPlacesNode,
+    this._insertNewItem(aPlacesNode, this._rootElt,
       aIndex < children.length ? children[aIndex] : null);
   },
 
   nodeRemoved:
   function PAMV_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
     if (parentElt != this._rootElt)
       return;
@@ -1993,19 +1995,21 @@ PlacesPanelMenuView.prototype = {
     if (elt != this._rootElt)
       return;
 
     // Container is the toolbar itself.
     while (this._rootElt.hasChildNodes()) {
       this._rootElt.firstChild.remove();
     }
 
+    let fragment = document.createDocumentFragment();
     for (let i = 0; i < this._resultNode.childCount; ++i) {
-      this._insertNewItem(this._resultNode.getChild(i), null);
+      this._insertNewItem(this._resultNode.getChild(i), fragment);
     }
+    this._rootElt.appendChild(fragment);
   }
 };
 
 this.PlacesPanelview = class extends PlacesViewBase {
   constructor(container, panelview, place, options = {}) {
     options.rootElt = container;
     options.viewElt = panelview;
     super(place, options);