Bug 1354079 - add sticky / permanent part to overflow panel, r?mikedeboer draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Sat, 22 Apr 2017 10:41:03 +0100
changeset 571411 49280a3a1ed505e8d61ae38c3f0903c3c5fd8767
parent 571272 48c0fd9c9ec5d68061ea7b59358874ae8da72572
child 626753 d71bb492738bd78bd2ab7a5aed16cce9c5f434b8
push id56780
push usergijskruitbosch@gmail.com
push dateTue, 02 May 2017 15:40:13 +0000
reviewersmikedeboer
bugs1354079
milestone55.0a1
Bug 1354079 - add sticky / permanent part to overflow panel, r?mikedeboer MozReview-Commit-ID: 7wv43JWLx8Q
browser/components/customizableui/CustomizableUI.jsm
browser/components/customizableui/CustomizableWidgets.jsm
browser/components/customizableui/DragPositionManager.jsm
browser/components/customizableui/content/panelUI.inc.xul
browser/components/customizableui/content/panelUI.js
browser/themes/shared/customizableui/panelUI.inc.css
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -229,16 +229,23 @@ var CustomizableUIInternal = {
 
     this.registerArea(CustomizableUI.AREA_PANEL, {
       anchor: "PanelUI-menu-button",
       type: CustomizableUI.TYPE_MENU_PANEL,
       defaultPlacements: panelPlacements
     }, true);
     PanelWideWidgetTracker.init();
 
+    if (Services.prefs.getBoolPref("browser.photon.structure.enabled")) {
+      this.registerArea(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL, {
+        type: CustomizableUI.TYPE_MENU_PANEL,
+        defaultPlacements: [],
+      }, true);
+    }
+
     let navbarPlacements = [
       "urlbar-container",
       "search-container",
       "bookmarks-menu-button",
       "downloads-button",
       "home-button",
     ];
 
@@ -892,45 +899,44 @@ var CustomizableUIInternal = {
     if (node) {
       return [ CustomizableUI.PROVIDER_XUL, node ];
     }
 
     log.debug("No node for " + aWidgetId + " found.");
     return [null, null];
   },
 
-  registerMenuPanel(aPanelContents) {
-    if (gBuildAreas.has(CustomizableUI.AREA_PANEL) &&
-        gBuildAreas.get(CustomizableUI.AREA_PANEL).has(aPanelContents)) {
+  registerMenuPanel(aPanelContents, aArea) {
+    if (gBuildAreas.has(aArea) && gBuildAreas.get(aArea).has(aPanelContents)) {
       return;
     }
 
     let document = aPanelContents.ownerDocument;
 
     aPanelContents.toolbox = document.getElementById("navigator-toolbox");
     aPanelContents.customizationTarget = aPanelContents;
 
     this.addPanelCloseListeners(this._getPanelForNode(aPanelContents));
 
-    let placements = gPlacements.get(CustomizableUI.AREA_PANEL);
-    this.buildArea(CustomizableUI.AREA_PANEL, placements, aPanelContents);
-    this.notifyListeners("onAreaNodeRegistered", CustomizableUI.AREA_PANEL, aPanelContents);
+    let placements = gPlacements.get(aArea);
+    this.buildArea(aArea, placements, aPanelContents);
+    this.notifyListeners("onAreaNodeRegistered", aArea, aPanelContents);
 
     for (let child of aPanelContents.children) {
       if (child.localName != "toolbarbutton") {
         if (child.localName == "toolbaritem") {
           this.ensureButtonContextMenu(child, aPanelContents);
         }
         continue;
       }
       this.ensureButtonContextMenu(child, aPanelContents);
       child.setAttribute("wrap", "true");
     }
 
-    this.registerBuildArea(CustomizableUI.AREA_PANEL, aPanelContents);
+    this.registerBuildArea(aArea, aPanelContents);
   },
 
   onWidgetAdded(aWidgetId, aArea, aPosition) {
     this.insertNode(aWidgetId, aArea, aPosition, true);
 
     if (!gResetting) {
       this._clearPreviousUIState();
     }
@@ -1484,18 +1490,19 @@ var CustomizableUIInternal = {
         // XXXunf Need to think this through more, and formalize.
         Services.obs.notifyObservers(aNode,
                                      "customizedui-widget-command",
                                      aWidget.id);
       }
     } else if (aWidget.type == "view") {
       let ownerWindow = aNode.ownerGlobal;
       let area = this.getPlacementOfWidget(aNode.id).area;
+      let areaType = CustomizableUI.getAreaType(area);
       let anchor = aNode;
-      if (area != CustomizableUI.AREA_PANEL) {
+      if (areaType != CustomizableUI.TYPE_MENU_PANEL) {
         let wrapper = this.wrapWidget(aWidget.id).forWindow(ownerWindow);
 
         if (wrapper && !wrapper.overflowed && wrapper.anchor) {
           this.hidePanelForNode(aNode);
           anchor = wrapper.anchor;
         }
       }
       ownerWindow.PanelUI.showSubView(aWidget.viewId, anchor, area);
@@ -2146,17 +2153,18 @@ var CustomizableUIInternal = {
     }
 
     this.notifyListeners("onWidgetCreated", widget.id);
 
     if (widget.defaultArea) {
       let addToDefaultPlacements = false;
       let area = gAreas.get(widget.defaultArea);
       if (!CustomizableUI.isBuiltinToolbar(widget.defaultArea) &&
-          widget.defaultArea != CustomizableUI.AREA_PANEL) {
+          widget.defaultArea != CustomizableUI.AREA_PANEL &&
+          widget.defaultArea != CustomizableUI.AREA_FIXED_OVERFLOW_PANEL) {
         addToDefaultPlacements = true;
       }
 
       if (addToDefaultPlacements) {
         if (area.has("defaultPlacements")) {
           area.get("defaultPlacements").push(widget.id);
         } else {
           area.set("defaultPlacements", [widget.id]);
@@ -2820,16 +2828,21 @@ this.CustomizableUI = {
   AREA_BOOKMARKS: "PersonalToolbar",
   /**
    * Constant reference to the ID of the addon-bar toolbar shim.
    * Do not use, this will be removed as soon as reasonably possible.
    * @deprecated
    */
   AREA_ADDONBAR: "addon-bar",
   /**
+   * Constant reference to the ID of the non-dymanic (fixed) list in the overflow panel.
+   */
+  AREA_FIXED_OVERFLOW_PANEL: "widget-overflow-fixed-list",
+
+  /**
    * Constant indicating the area is a menu panel.
    */
   TYPE_MENU_PANEL: "menu-panel",
   /**
    * Constant indicating the area is a toolbar.
    */
   TYPE_TOOLBAR: "toolbar",
 
@@ -3040,20 +3053,21 @@ this.CustomizableUI = {
    * with it, until the area has been registered.
    */
   registerToolbarNode(aToolbar, aExistingChildren) {
     CustomizableUIInternal.registerToolbarNode(aToolbar, aExistingChildren);
   },
   /**
    * Register the menu panel node. This method should not be called by anyone
    * apart from the built-in PanelUI.
-   * @param aPanel the panel DOM node being registered.
+   * @param aPanelContents the panel contents DOM node being registered.
+   * @param aArea the area for which to register this node.
    */
-  registerMenuPanel(aPanel) {
-    CustomizableUIInternal.registerMenuPanel(aPanel);
+  registerMenuPanel(aPanelContents, aArea) {
+    CustomizableUIInternal.registerMenuPanel(aPanelContents, aArea);
   },
   /**
    * Unregister a customizable area. The inverse of registerArea.
    *
    * Unregistering an area will remove all the (removable) widgets in the
    * area, which will return to the panel, and destroy all other traces
    * of the area within CustomizableUI. Note that this means the *contents*
    * of the area's DOM nodes will be moved to the panel or removed, but
@@ -4145,18 +4159,21 @@ OverflowableToolbar.prototype = {
     }
   },
 
   _onPanelHiding(aEvent) {
     this._chevron.open = false;
     this._panel.removeEventListener("dragover", this);
     this._panel.removeEventListener("dragend", this);
     let doc = aEvent.target.ownerDocument;
-    let contextMenu = doc.getElementById(this._panel.getAttribute("context"));
-    gELS.removeSystemEventListener(contextMenu, "command", this, true);
+    let contextMenuId = this._panel.getAttribute("context");
+    if (contextMenuId) {
+      let contextMenu = doc.getElementById(contextMenuId);
+      gELS.removeSystemEventListener(contextMenu, "command", this, true);
+    }
   },
 
   onOverflow(aEvent) {
     // The rangeParent check is here because of bug 1111986 and ensuring that
     // overflow events from the bookmarks toolbar items or similar things that
     // manage their own overflow don't trigger an overflow on the entire toolbar
     if (!this._enabled ||
         (aEvent && aEvent.target != this._toolbar.customizationTarget) ||
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -1001,56 +1001,59 @@ const CustomizableWidgets = [
         } catch (e) {
           Cu.reportError("Failed to set the intl.charset.detector preference.");
         }
         // Prepare a browser page reload with a changed charset.
         window.BrowserCharsetReload();
       }
     },
     onCreated(aNode) {
-      const kPanelId = "PanelUI-popup";
       let document = aNode.ownerDocument;
 
       let updateButton = () => {
         if (this.maybeDisableMenu(document))
           aNode.setAttribute("disabled", "true");
         else
           aNode.removeAttribute("disabled");
       };
 
-      if (this.currentArea == CustomizableUI.AREA_PANEL) {
-        let panel = document.getElementById(kPanelId);
-        panel.addEventListener("popupshowing", updateButton);
+      let getPanel = () => {
+        let {PanelUI} = document.ownerGlobal;
+        if (PanelUI.overflowContents) {
+          return document.getElementById("widget-overflow");
+        }
+        return PanelUI.panel;
+      }
+
+      if (CustomizableUI.getAreaType(this.currentArea) == CustomizableUI.TYPE_MENU_PANEL) {
+        getPanel().addEventListener("popupshowing", updateButton);
       }
 
       let listener = {
         onWidgetAdded: (aWidgetId, aArea) => {
           if (aWidgetId != this.id)
             return;
-          if (aArea == CustomizableUI.AREA_PANEL) {
-            let panel = document.getElementById(kPanelId);
-            panel.addEventListener("popupshowing", updateButton);
+          if (CustomizableUI.getAreaType(aArea) == CustomizableUI.TYPE_MENU_PANEL) {
+            getPanel().addEventListener("popupshowing", updateButton);
           }
         },
         onWidgetRemoved: (aWidgetId, aPrevArea) => {
           if (aWidgetId != this.id)
             return;
           aNode.removeAttribute("disabled");
-          if (aPrevArea == CustomizableUI.AREA_PANEL) {
-            let panel = document.getElementById(kPanelId);
-            panel.removeEventListener("popupshowing", updateButton);
+          if (CustomizableUI.getAreaType(aPrevArea) == CustomizableUI.TYPE_MENU_PANEL) {
+            getPanel().removeEventListener("popupshowing", updateButton);
           }
         },
         onWidgetInstanceRemoved: (aWidgetId, aDoc) => {
           if (aWidgetId != this.id || aDoc != document)
             return;
 
           CustomizableUI.removeListener(listener);
-          let panel = aDoc.getElementById(kPanelId);
-          panel.removeEventListener("popupshowing", updateButton);
+          getPanel().removeEventListener("popupshowing", updateButton);
         }
       };
       CustomizableUI.addListener(listener);
       if (!this.charsetInfo) {
         this.charsetInfo = CharsetMenu.getData();
       }
     }
   }, {
--- a/browser/components/customizableui/DragPositionManager.jsm
+++ b/browser/components/customizableui/DragPositionManager.jsm
@@ -374,39 +374,39 @@ AreaPositionManager.prototype = {
       rv = rv[aDirection + "Sibling"];
     } while (rv && rv.getAttribute("hidden") == "true")
     return rv;
   }
 }
 
 var DragPositionManager = {
   start(aWindow) {
-    let areas = CustomizableUI.areas.filter((area) => CustomizableUI.getAreaType(area) != "toolbar");
+    let areas = [CustomizableUI.AREA_PANEL];
     areas = areas.map((area) => CustomizableUI.getCustomizeTargetForArea(area, aWindow));
     areas.push(aWindow.document.getElementById(kPaletteId));
     for (let areaNode of areas) {
       let positionManager = gManagers.get(areaNode);
       if (positionManager) {
         positionManager.update(areaNode);
       } else {
         gManagers.set(areaNode, new AreaPositionManager(areaNode));
       }
     }
   },
 
   add(aWindow, aArea, aContainer) {
-    if (CustomizableUI.getAreaType(aArea) != "toolbar") {
+    if (aArea != CustomizableUI.AREA_PANEL) {
       return;
     }
 
     gManagers.set(aContainer, new AreaPositionManager(aContainer));
   },
 
   remove(aWindow, aArea, aContainer) {
-    if (CustomizableUI.getAreaType(aArea) != "toolbar") {
+    if (aArea != CustomizableUI.AREA_PANEL) {
       return;
     }
 
     gManagers.delete(aContainer);
   },
 
   stop() {
     gManagers = new WeakMap();
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -378,16 +378,18 @@
        type="arrow"
        noautofocus="true"
        position="bottomcenter topright"
        hidden="true">
   <panelmultiview mainViewId="widget-overflow-mainView">
     <panelview id="widget-overflow-mainView"
                context="toolbar-context-menu">
       <vbox id="widget-overflow-scroller">
+        <vbox id="widget-overflow-fixed-list" class="widget-overflow-list" hidden="true"/>
+        <toolbarseparator id="widget-overflow-fixed-separator" hidden="true"/>
         <vbox id="widget-overflow-list" class="widget-overflow-list"
               overflowfortoolbar="nav-bar"/>
       </vbox>
     </panelview>
   </panelmultiview>
 </panel>
 
 <panel id="customization-tipPanel"
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -32,23 +32,28 @@ const PanelUI = {
       contents: "PanelUI-contents",
       mainView: gPhotonStructure ? "appMenu-mainView" : "PanelUI-mainView",
       multiView: gPhotonStructure ? "appMenu-multiView" : "PanelUI-multiView",
       helpView: "PanelUI-helpView",
       menuButton: "PanelUI-menu-button",
       panel: gPhotonStructure ? "appMenu-popup" : "PanelUI-popup",
       notificationPanel: "PanelUI-notification-popup",
       scroller: "PanelUI-contents-scroller",
-      footer: "PanelUI-footer"
+      footer: "PanelUI-footer",
+
+      overflowFixedList: gPhotonStructure ? "widget-overflow-fixed-list" : "",
     };
   },
 
   _initialized: false,
   init() {
     for (let [k, v] of Object.entries(this.kElements)) {
+      if (!v) {
+        continue;
+      }
       // Need to do fresh let-bindings per iteration
       let getKey = k;
       let id = v;
       this.__defineGetter__(getKey, function() {
         delete this[getKey];
         return this[getKey] = document.getElementById(id);
       });
     }
@@ -65,16 +70,22 @@ const PanelUI = {
     window.addEventListener("fullscreen", this);
     window.matchMedia("(-moz-overlay-scrollbars)").addListener(this._overlayScrollListenerBoundFn);
     CustomizableUI.addListener(this);
 
     for (let event of this.kEvents) {
       this.notificationPanel.addEventListener(event, this);
     }
 
+    if (gPhotonStructure) {
+      this.overflowFixedList.hidden = false;
+      this.overflowFixedList.nextSibling.hidden = false;
+      CustomizableUI.registerMenuPanel(this.overflowFixedList, CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
+    }
+
     this._initialized = true;
   },
 
   _eventListenersAdded: false,
   _ensureEventListenersAdded() {
     if (this._eventListenersAdded)
       return;
     this._addEventListeners();
@@ -380,21 +391,21 @@ const PanelUI = {
         let paddingLeft = cstyle.paddingLeft;
         let paddingRight = cstyle.paddingRight;
         let calcStr = [widthStr, this._scrollWidth,
                        paddingLeft, paddingRight].join(" + ");
         this.scroller.style.width = "calc(" + calcStr + ")";
       }
 
       if (aCustomizing) {
-        CustomizableUI.registerMenuPanel(this.contents);
+        CustomizableUI.registerMenuPanel(this.contents, CustomizableUI.AREA_PANEL);
       } else {
         this.beginBatchUpdate();
         try {
-          CustomizableUI.registerMenuPanel(this.contents);
+          CustomizableUI.registerMenuPanel(this.contents, CustomizableUI.AREA_PANEL);
         } finally {
           this.endBatchUpdate();
         }
       }
       this._updateQuitTooltip();
       this.panel.hidden = false;
       this._isReady = true;
     }.bind(this)).then(null, Cu.reportError);
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -1198,16 +1198,24 @@ menuitem.panel-subview-footer@menuStateA
   padding-bottom: 4px;
 }
 
 /* Disabled (empty) item is always alone and never has an icon, so fix its left padding */
 #BMB_bookmarksPopup menupopup[emptyplacesresult] .bookmark-item.subviewbutton {
   padding-left: 6px;
 }
 
+/* Yeah, the ids are ugly, but this should be reasonably performant, and
+ * using a tagname as the last item would be less so.
+ */
+#widget-overflow-fixed-list:empty + #widget-overflow-fixed-separator {
+  display: none;
+}
+
+#widget-overflow-scroller > toolbarseparator,
 .PanelUI-subView menuseparator,
 .PanelUI-subView toolbarseparator,
 .cui-widget-panelview menuseparator {
   -moz-appearance: none;
   min-height: 0;
   border-top: 1px solid var(--panel-separator-color);
   border-bottom: none;
   margin: 6px 0;
@@ -1423,17 +1431,17 @@ toolbarpaletteitem[haswideitem][place="p
 }
 
 #widget-overflow-scroller {
   max-height: 30em;
   margin-top: 10px;
   margin-bottom: 10px;
 }
 
-#widget-overflow-list {
+.widget-overflow-list {
   width: @menuPanelWidth@;
   padding-left: 10px;
   padding-right: 10px;
 }
 
 toolbaritem[overflowedItem=true],
 .widget-overflow-list .toolbarbutton-1 {
   width: 100%;
@@ -1450,17 +1458,17 @@ toolbaritem[overflowedItem=true],
 }
 
 .widget-overflow-list .toolbarbutton-1:not(.toolbarbutton-combined) > .toolbarbutton-text,
 .widget-overflow-list .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-text {
   text-align: start;
   padding-inline-start: .5em;
 }
 
-#widget-overflow-list > .toolbaritem-combined-buttons {
+.widget-overflow-list > .toolbaritem-combined-buttons {
   min-height: 28px;
 }
 
 .widget-overflow-list .toolbarbutton-1 > .toolbarbutton-menubutton-button::after {
   content: "";
   display: -moz-box;
   width: 1px;
   height: 18px;