Bug 1354082 - part 2: make overflow panel show up in customize mode and make dragging work correctly, r?mikedeboer
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Fri, 05 May 2017 19:26:55 +0100
changeset 575579 57c413620aff496069b8cf371b8980a1fa432d72
parent 575578 2b6df54d24d35294cc547be0b7abe5396ddec6b6
push id58111
push userbmo:gijskruitbosch+bugs@gmail.com
push dateWed, 10 May 2017 17:46:21 +0000
reviewersmikedeboer
bugs1354082
milestone55.0a1
Bug 1354082 - part 2: make overflow panel show up in customize mode and make dragging work correctly, r?mikedeboer This reuses the logic we have for toolbar dragging and uses it for the photon panel (ie overflow panel) to do vertical space-making animations. It explicitly doesn't address the styling or permanently showing the overflow button (for which we have other bugs on file). MozReview-Commit-ID: 4qrWC0H30xi
browser/components/customizableui/CustomizableUI.jsm
browser/components/customizableui/CustomizeMode.jsm
browser/themes/shared/customizableui/customizeMode.inc.css
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -24,16 +24,21 @@ XPCOMUtils.defineLazyGetter(this, "gWidg
   return Services.strings.createBundle(kUrl);
 });
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
   "resource://gre/modules/ShortcutUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "gELS",
   "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
+XPCOMUtils.defineLazyPreferenceGetter(this, "gPhotonStructure", "browser.photon.structure.enabled", false,
+  (pref, oldValue, newValue) => {
+    CustomizableUIInternal._updateAreasForPhoton();
+    CustomizableUIInternal.notifyListeners("onPhotonChanged");
+  });
 
 const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const kSpecialWidgetPfx = "customizableui-special-";
 
 const kPrefCustomizationState        = "browser.uiCustomization.state";
 const kPrefCustomizationAutoAdd      = "browser.uiCustomization.autoAdd";
 const kPrefCustomizationDebug        = "browser.uiCustomization.debug";
@@ -225,17 +230,16 @@ var CustomizableUIInternal = {
     if (AppConstants.NIGHTLY_BUILD) {
       if (Services.prefs.getBoolPref("extensions.webcompat-reporter.enabled")) {
         panelPlacements.push("webcompat-reporter-button");
       }
     }
 
     gDefaultPanelPlacements = panelPlacements;
     this._updateAreasForPhoton();
-    Services.prefs.addObserver("browser.photon.structure.enabled", this);
 
     let navbarPlacements = [
       "urlbar-container",
       "search-container",
       "bookmarks-menu-button",
       "downloads-button",
       "home-button",
     ];
@@ -293,17 +297,17 @@ var CustomizableUIInternal = {
       type: CustomizableUI.TYPE_TOOLBAR,
       legacy: true,
       defaultPlacements: ["addonbar-closebutton", "status-bar"],
       defaultCollapsed: false,
     }, true);
   },
 
   _updateAreasForPhoton() {
-    if (Services.prefs.getBoolPref("browser.photon.structure.enabled")) {
+    if (gPhotonStructure) {
       if (gAreas.has(CustomizableUI.AREA_PANEL)) {
         this.unregisterArea(CustomizableUI.AREA_PANEL, true);
       }
       this.registerArea(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL, {
         type: CustomizableUI.TYPE_MENU_PANEL,
         defaultPlacements: [],
       }, true);
     } else {
@@ -320,24 +324,16 @@ var CustomizableUIInternal = {
         anchor: "PanelUI-menu-button",
         type: CustomizableUI.TYPE_MENU_PANEL,
         defaultPlacements: placementsToUse,
       }, true);
       PanelWideWidgetTracker.init();
     }
   },
 
-  observe(subject, topic, data) {
-    // Currently only used for pref change observers.
-    if (data == "browser.photon.structure.enabled") {
-      this._updateAreasForPhoton();
-      this.notifyListeners("onPhotonChanged");
-    }
-  },
-
   get _builtinToolbars() {
     let toolbars = new Set([
       CustomizableUI.AREA_NAVBAR,
       CustomizableUI.AREA_BOOKMARKS,
       CustomizableUI.AREA_TABSTRIP,
       CustomizableUI.AREA_ADDONBAR,
     ]);
     if (AppConstants.platform != "macosx") {
@@ -3750,17 +3746,17 @@ this.CustomizableUI = {
    *         palette, undefined otherwise.
    */
   getPlaceForItem(aElement) {
     let place;
     let node = aElement;
     while (node && !place) {
       if (node.localName == "toolbar")
         place = "toolbar";
-      else if (node.id == CustomizableUI.AREA_PANEL)
+      else if (node.id == CustomizableUI.AREA_PANEL || node.id == CustomizableUI.AREA_FIXED_OVERFLOW_PANEL)
         place = "panel";
       else if (node.id == "customization-palette")
         place = "palette";
 
       node = node.parentNode;
     }
     return place;
   },
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -33,16 +33,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/DragPositionManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
                                   "resource:///modules/BrowserUITelemetry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
                                   "resource:///modules/sessionstore/SessionStore.jsm");
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "gPhotonStructure",
+  "browser.photon.structure.enabled", false);
+
 let gDebug;
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let scope = {};
   Cu.import("resource://gre/modules/Console.jsm", scope);
   gDebug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
   let consoleOptions = {
     maxLogLevel: gDebug ? "all" : "log",
     prefix: "CustomizeMode",
@@ -273,49 +276,60 @@ CustomizeMode.prototype = {
 
       // 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 click on the
       // menu button means a quick exit from customization mode.
       window.PanelUI.hide();
-      window.PanelUI.menuButton.addEventListener("command", this);
-      window.PanelUI.menuButton.open = true;
-      window.PanelUI.beginBatchUpdate();
 
-      // The menu panel is lazy, and registers itself when the popup shows. We
-      // need to force the menu panel to register itself, or else customization
-      // is really not going to work. We pass "true" to ensureReady to
-      // indicate that we're handling calling startBatchUpdate and
-      // endBatchUpdate.
-      if (!window.PanelUI.isReady) {
-        yield window.PanelUI.ensureReady(true);
-      }
+      let panelHolder = document.getElementById("customization-panelHolder");
+      if (gPhotonStructure) {
+        panelHolder.appendChild(window.PanelUI.overflowFixedList);
+        window.PanelUI.overflowFixedList.setAttribute("customizing", true);
+        window.PanelUI.menuButton.disabled = true;
+      } else {
+        window.PanelUI.menuButton.addEventListener("command", this);
+        window.PanelUI.menuButton.open = true;
+        window.PanelUI.beginBatchUpdate();
 
-      // Hide the palette before starting the transition for increased perf.
-      this.visiblePalette.hidden = true;
-      this.visiblePalette.removeAttribute("showing");
+        // The menu panel is lazy, and registers itself when the popup shows. We
+        // need to force the menu panel to register itself, or else customization
+        // is really not going to work. We pass "true" to ensureReady to
+        // indicate that we're handling calling startBatchUpdate and
+        // endBatchUpdate.
+        if (!window.PanelUI.isReady) {
+          yield window.PanelUI.ensureReady(true);
+        }
 
-      // Disable the button-text fade-out mask
-      // during the transition for increased perf.
-      let panelContents = window.PanelUI.contents;
-      panelContents.setAttribute("customize-transitioning", "true");
+        // Hide the palette before starting the transition for increased perf.
+        this.visiblePalette.hidden = true;
+        this.visiblePalette.removeAttribute("showing");
+
+        // Disable the button-text fade-out mask
+        // during the transition for increased perf.
+        window.PanelUI.contents.setAttribute("customize-transitioning", "true");
 
-      // Move the mainView in the panel to the holder so that we can see it
-      // while customizing.
-      let mainView = window.PanelUI.mainView;
-      let panelHolder = document.getElementById("customization-panelHolder");
-      panelHolder.appendChild(mainView);
+        // Move the mainView in the panel to the holder so that we can see it
+        // while customizing.
+        let mainView = window.PanelUI.mainView;
+        panelHolder.appendChild(mainView);
 
-      let customizeButton = document.getElementById("PanelUI-customize");
-      customizeButton.setAttribute("enterLabel", customizeButton.getAttribute("label"));
-      customizeButton.setAttribute("label", customizeButton.getAttribute("exitLabel"));
-      customizeButton.setAttribute("enterTooltiptext", customizeButton.getAttribute("tooltiptext"));
-      customizeButton.setAttribute("tooltiptext", customizeButton.getAttribute("exitTooltiptext"));
+        let customizeButton = document.getElementById("PanelUI-customize");
+        customizeButton.setAttribute("enterLabel", customizeButton.getAttribute("label"));
+        customizeButton.setAttribute("label", customizeButton.getAttribute("exitLabel"));
+        customizeButton.setAttribute("enterTooltiptext", customizeButton.getAttribute("tooltiptext"));
+        customizeButton.setAttribute("tooltiptext", customizeButton.getAttribute("exitTooltiptext"));
+
+        this._mainViewContext = mainView.getAttribute("context");
+        if (this._mainViewContext) {
+          mainView.removeAttribute("context");
+        }
+      }
 
       this._transitioning = true;
 
       let customizer = document.getElementById("customization-container");
       customizer.parentNode.selectedPanel = customizer;
       customizer.hidden = false;
 
       this._wrapToolbarItemSync(CustomizableUI.AREA_TABSTRIP);
@@ -326,41 +340,42 @@ CustomizeMode.prototype = {
 
       yield this._doTransition(true);
 
       Services.obs.addObserver(this, "lightweight-theme-window-updated");
 
       // Let everybody in this window know that we're about to customize.
       CustomizableUI.dispatchToolboxEvent("customizationstarting", {}, window);
 
-      this._mainViewContext = mainView.getAttribute("context");
-      if (this._mainViewContext) {
-        mainView.removeAttribute("context");
+      if (!gPhotonStructure) {
+        this._showPanelCustomizationPlaceholders();
       }
 
-      this._showPanelCustomizationPlaceholders();
-
       yield this._wrapToolbarItems();
       this.populatePalette();
 
       this._addDragHandlers(this.visiblePalette);
 
       window.gNavToolbox.addEventListener("toolbarvisibilitychange", this);
 
-      document.getElementById("PanelUI-help").setAttribute("disabled", true);
-      document.getElementById("PanelUI-quit").setAttribute("disabled", true);
+      if (!gPhotonStructure) {
+        document.getElementById("PanelUI-help").setAttribute("disabled", true);
+        document.getElementById("PanelUI-quit").setAttribute("disabled", true);
+      }
 
       this._updateResetButton();
       this._updateUndoResetButton();
 
       this._skipSourceNodeCheck = Services.prefs.getPrefType(kSkipSourceNodePref) == Ci.nsIPrefBranch.PREF_BOOL &&
                                   Services.prefs.getBoolPref(kSkipSourceNodePref);
 
       CustomizableUI.addListener(this);
-      window.PanelUI.endBatchUpdate();
+      if (!gPhotonStructure) {
+        window.PanelUI.endBatchUpdate();
+      }
       this._customizing = true;
       this._transitioning = false;
 
       // Show the palette now that the transition has finished.
       this.visiblePalette.hidden = false;
       window.setTimeout(() => {
         // Force layout reflow to ensure the animation runs,
         // and make it async so it doesn't affect the timing.
@@ -368,22 +383,26 @@ CustomizeMode.prototype = {
         this.visiblePalette.setAttribute("showing", "true");
       }, 0);
       this._updateEmptyPaletteNotice();
 
       this._updateLWThemeButtonIcon();
       this.maybeShowTip(panelHolder);
 
       this._handler.isEnteringCustomizeMode = false;
-      panelContents.removeAttribute("customize-transitioning");
+      if (!gPhotonStructure) {
+        window.PanelUI.contents.removeAttribute("customize-transitioning");
+      }
 
       CustomizableUI.dispatchToolboxEvent("customizationready", {}, window);
       this._enableOutlinesTimeout = window.setTimeout(() => {
         this.document.getElementById("nav-bar").setAttribute("showoutline", "true");
-        this.panelUIContents.setAttribute("showoutline", "true");
+        if (!gPhotonStructure) {
+          this.panelUIContents.setAttribute("showoutline", "true");
+        }
         delete this._enableOutlinesTimeout;
       }, 0);
 
       if (!this._wantToBeInCustomizeMode) {
         this.exit();
       }
     }.bind(this)).then(null, e => {
       log.error("Error entering customize mode", e);
@@ -426,35 +445,39 @@ CustomizeMode.prototype = {
       this.panelUIContents.removeAttribute("showoutline");
     }
 
     this._removeExtraToolbarsIfEmpty();
 
     CustomizableUI.removeListener(this);
 
     this.document.removeEventListener("keypress", this);
-    this.window.PanelUI.menuButton.removeEventListener("command", this);
-    this.window.PanelUI.menuButton.open = false;
+    if (!gPhotonStructure) {
+      this.window.PanelUI.menuButton.removeEventListener("command", this);
+      this.window.PanelUI.menuButton.open = false;
 
-    this.window.PanelUI.beginBatchUpdate();
+      this.window.PanelUI.beginBatchUpdate();
 
-    this._removePanelCustomizationPlaceholders();
+      this._removePanelCustomizationPlaceholders();
+    }
 
     let window = this.window;
     let document = this.document;
 
     // Hide the palette before starting the transition for increased perf.
     this.visiblePalette.hidden = true;
     this.visiblePalette.removeAttribute("showing");
     this.paletteEmptyNotice.hidden = true;
 
-    // Disable the button-text fade-out mask
-    // during the transition for increased perf.
-    let panelContents = window.PanelUI.contents;
-    panelContents.setAttribute("customize-transitioning", "true");
+    if (!gPhotonStructure) {
+      // Disable the button-text fade-out mask
+      // during the transition for increased perf.
+      let panelContents = window.PanelUI.contents;
+      panelContents.setAttribute("customize-transitioning", "true");
+    }
 
     // Disable the reset and undo reset buttons while transitioning:
     let resetButton = this.document.getElementById("customization-reset-button");
     let undoResetButton = this.document.getElementById("customization-undo-reset-button");
     undoResetButton.hidden = resetButton.disabled = true;
 
     this._transitioning = true;
 
@@ -495,62 +518,72 @@ CustomizeMode.prototype = {
 
       // And drop all area references.
       this.areas.clear();
 
       // Let everybody in this window know that we're starting to
       // exit customization mode.
       CustomizableUI.dispatchToolboxEvent("customizationending", {}, window);
 
-      window.PanelUI.setMainView(window.PanelUI.mainView);
       window.PanelUI.menuButton.disabled = false;
+      if (gPhotonStructure) {
+        let overflowContainer = document.getElementById("widget-overflow-scroller");
+        overflowContainer.insertBefore(window.PanelUI.overflowFixedList, overflowContainer.firstElementChild);
+      } else {
+        window.PanelUI.setMainView(window.PanelUI.mainView);
 
-      let customizeButton = document.getElementById("PanelUI-customize");
-      customizeButton.setAttribute("exitLabel", customizeButton.getAttribute("label"));
-      customizeButton.setAttribute("label", customizeButton.getAttribute("enterLabel"));
-      customizeButton.setAttribute("exitTooltiptext", customizeButton.getAttribute("tooltiptext"));
-      customizeButton.setAttribute("tooltiptext", customizeButton.getAttribute("enterTooltiptext"));
+        let customizeButton = document.getElementById("PanelUI-customize");
+        customizeButton.setAttribute("exitLabel", customizeButton.getAttribute("label"));
+        customizeButton.setAttribute("label", customizeButton.getAttribute("enterLabel"));
+        customizeButton.setAttribute("exitTooltiptext", customizeButton.getAttribute("tooltiptext"));
+        customizeButton.setAttribute("tooltiptext", customizeButton.getAttribute("enterTooltiptext"));
 
-      // We have to use setAttribute/removeAttribute here instead of the
-      // property because the XBL property will be set later, and right
-      // now we'd be setting an expando, which breaks the XBL property.
-      document.getElementById("PanelUI-help").removeAttribute("disabled");
-      document.getElementById("PanelUI-quit").removeAttribute("disabled");
-
-      panelContents.removeAttribute("customize-transitioning");
+        // We have to use setAttribute/removeAttribute here instead of the
+        // property because the XBL property will be set later, and right
+        // now we'd be setting an expando, which breaks the XBL property.
+        document.getElementById("PanelUI-help").removeAttribute("disabled");
+        document.getElementById("PanelUI-quit").removeAttribute("disabled");
+        this.panelUIContents.removeAttribute("customize-transitioning");
+      }
 
       // We need to set this._customizing to false before removing the tab
       // or the TabSelect event handler will think that we are exiting
       // customization mode for a second time.
       this._customizing = false;
 
-      let mainView = window.PanelUI.mainView;
-      if (this._mainViewContext) {
-        mainView.setAttribute("context", this._mainViewContext);
+      if (!gPhotonStructure) {
+        let mainView = window.PanelUI.mainView;
+        if (this._mainViewContext) {
+          mainView.setAttribute("context", this._mainViewContext);
+        }
       }
 
       let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true])");
       for (let toolbar of customizableToolbars)
         toolbar.removeAttribute("customizing");
 
-      this.window.PanelUI.endBatchUpdate();
+      if (!gPhotonStructure) {
+        this.window.PanelUI.endBatchUpdate();
+      }
       delete this._lastLightweightTheme;
       this._changed = false;
       this._transitioning = false;
       this._handler.isExitingCustomizeMode = false;
       CustomizableUI.dispatchToolboxEvent("aftercustomization", {}, window);
       CustomizableUI.notifyEndCustomizing(window);
 
       if (this._wantToBeInCustomizeMode) {
         this.enter();
       }
     }.bind(this)).then(null, e => {
       log.error("Error exiting customize mode", e);
-      // We should ensure this has been called, and calling it again doesn't hurt:
-      window.PanelUI.endBatchUpdate();
+      if (!gPhotonStructure) {
+        // We should ensure this has been called, and calling it again doesn't hurt:
+        window.PanelUI.endBatchUpdate();
+      }
       this._handler.isExitingCustomizeMode = false;
     });
   },
 
   /**
    * The customize mode transition has 4 phases when entering:
    * 1) Pre-customization mode
    *    This is the starting phase of the browser.
@@ -1523,16 +1556,30 @@ CustomizeMode.prototype = {
   toggleTitlebar(aShouldShowTitlebar) {
     if (!AppConstants.CAN_DRAW_IN_TITLEBAR) {
       return;
     }
     // Drawing in the titlebar means not showing the titlebar, hence the negation:
     Services.prefs.setBoolPref(kDrawInTitlebarPref, !aShouldShowTitlebar);
   },
 
+  get _dwu() {
+    if (!this.__dwu) {
+      this.__dwu = this.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+    }
+    return this.__dwu;
+  },
+
+  get _dir() {
+    if (!this.__dir) {
+      this.__dir = this.window.getComputedStyle(this.document.documentElement).direction;
+    }
+    return this.__dir;
+  },
+
   _onDragStart(aEvent) {
     __dumpDragData(aEvent);
     let item = aEvent.target;
     while (item && item.localName != "toolbarpaletteitem") {
       if (item.localName == "toolbar") {
         return;
       }
       item = item.parentNode;
@@ -1543,22 +1590,21 @@ CustomizeMode.prototype = {
     let isRemovable = placeForItem == "palette" ||
                       CustomizableUI.isWidgetRemovable(draggedItem);
     if (item.classList.contains(kPlaceholderClass) || !isRemovable) {
       return;
     }
 
     let dt = aEvent.dataTransfer;
     let documentId = aEvent.target.ownerDocument.documentElement.id;
-    let isInToolbar = placeForItem == "toolbar";
 
     dt.mozSetDataAt(kDragDataTypePrefix + documentId, draggedItem.id, 0);
     dt.effectAllowed = "move";
 
-    let itemRect = draggedItem.getBoundingClientRect();
+    let itemRect = this._dwu.getBoundsWithoutFlushing(draggedItem);
     let itemCenter = {x: itemRect.left + itemRect.width / 2,
                       y: itemRect.top + itemRect.height / 2};
     this._dragOffset = {x: aEvent.clientX - itemCenter.x,
                         y: aEvent.clientY - itemCenter.y};
 
     gDraggingInToolbars = new Set();
 
     // Hack needed so that the dragimage will still show the
@@ -1566,21 +1612,23 @@ CustomizeMode.prototype = {
     this._initializeDragAfterMove = () => {
       // For automated tests, we sometimes start exiting customization mode
       // before this fires, which leaves us with placeholders inserted after
       // we've exited. So we need to check that we are indeed customizing.
       if (this._customizing && !this._transitioning) {
         item.hidden = true;
         this._showPanelCustomizationPlaceholders();
         DragPositionManager.start(this.window);
+        let canUsePrevSibling = placeForItem == "toolbar" ||
+                                (placeForItem == "panel" && gPhotonStructure);
         if (item.nextSibling) {
-          this._setDragActive(item.nextSibling, "before", draggedItem.id, isInToolbar);
+          this._setDragActive(item.nextSibling, "before", draggedItem.id, placeForItem);
           this._dragOverItem = item.nextSibling;
-        } else if (isInToolbar && item.previousSibling) {
-          this._setDragActive(item.previousSibling, "after", draggedItem.id, isInToolbar);
+        } else if (canUsePrevSibling && item.previousSibling) {
+          this._setDragActive(item.previousSibling, "after", draggedItem.id, placeForItem);
           this._dragOverItem = item.previousSibling;
         }
       }
       this._initializeDragAfterMove = null;
       this.window.clearTimeout(this._dragInitializeTimeout);
     };
     this._dragInitializeTimeout = this.window.setTimeout(this._initializeDragAfterMove, 0);
   },
@@ -1619,65 +1667,76 @@ CustomizeMode.prototype = {
     }
 
     // Do nothing if the widget is not allowed to move to the target area.
     if (targetArea.id != kPaletteId &&
         !CustomizableUI.canWidgetMoveToArea(draggedItemId, targetArea.id)) {
       return;
     }
 
-    let targetIsToolbar = CustomizableUI.getAreaType(targetArea.id) == "toolbar";
-    let targetNode = this._getDragOverNode(aEvent, targetArea, targetIsToolbar, draggedItemId);
+    let targetAreaType = CustomizableUI.getAreaType(targetArea.id);
+    let targetNode = this._getDragOverNode(aEvent, targetArea, targetAreaType, draggedItemId);
 
     // We need to determine the place that the widget is being dropped in
     // the target.
     let dragOverItem, dragValue;
     if (targetNode == targetArea.customizationTarget) {
       // We'll assume if the user is dragging directly over the target, that
       // they're attempting to append a child to that target.
-      dragOverItem = (targetIsToolbar ? this._findVisiblePreviousSiblingNode(targetNode.lastChild) :
-                                        targetNode.lastChild) || targetNode;
+      dragOverItem = (targetAreaType == "toolbar"
+                        ? this._findVisiblePreviousSiblingNode(targetNode.lastChild)
+                        : targetNode.lastChild) ||
+                     targetNode;
       dragValue = "after";
     } else {
       let targetParent = targetNode.parentNode;
       let position = Array.indexOf(targetParent.children, targetNode);
       if (position == -1) {
-        dragOverItem = targetIsToolbar ? this._findVisiblePreviousSiblingNode(targetNode.lastChild) :
-                                         targetParent.lastChild;
+        dragOverItem = (targetAreaType == "toolbar"
+                          ? this._findVisiblePreviousSiblingNode(targetNode.lastChild)
+                          : targetNode.lastChild);
         dragValue = "after";
       } else {
         dragOverItem = targetParent.children[position];
-        if (!targetIsToolbar) {
-          dragValue = "before";
-        } else {
+        if (targetAreaType == "toolbar") {
           // Check if the aDraggedItem is hovered past the first half of dragOverItem
-          let window = dragOverItem.ownerGlobal;
-          let direction = window.getComputedStyle(dragOverItem).direction;
-          let itemRect = dragOverItem.getBoundingClientRect();
+          let itemRect = this._dwu.getBoundsWithoutFlushing(dragOverItem);
           let dropTargetCenter = itemRect.left + (itemRect.width / 2);
           let existingDir = dragOverItem.getAttribute("dragover");
-          if ((existingDir == "before") == (direction == "ltr")) {
-            dropTargetCenter += (parseInt(dragOverItem.style.borderLeftWidth) || 0) / 2;
+          if (existingDir == "before") {
+            dropTargetCenter += (parseInt(dragOverItem.style.borderInlineStartWidth) || 0) / 2;
           } else {
-            dropTargetCenter -= (parseInt(dragOverItem.style.borderRightWidth) || 0) / 2;
+            dropTargetCenter -= (parseInt(dragOverItem.style.borderInlineEndWidth) || 0) / 2;
           }
-          let before = direction == "ltr" ? aEvent.clientX < dropTargetCenter : aEvent.clientX > dropTargetCenter;
+          let before = this._dir == "ltr" ? aEvent.clientX < dropTargetCenter : aEvent.clientX > dropTargetCenter;
           dragValue = before ? "before" : "after";
+        } else if (targetAreaType == "menu-panel" && gPhotonStructure) {
+          let itemRect = this._dwu.getBoundsWithoutFlushing(dragOverItem);
+          let dropTargetCenter = itemRect.top + (itemRect.height / 2);
+          let existingDir = dragOverItem.getAttribute("dragover");
+          if (existingDir == "before") {
+            dropTargetCenter += (parseInt(dragOverItem.style.borderBlockStartWidth) || 0) / 2;
+          } else {
+            dropTargetCenter -= (parseInt(dragOverItem.style.borderBlockEndWidth) || 0) / 2;
+          }
+          dragValue = aEvent.clientY < dropTargetCenter ? "before" : "after";
+        } else {
+          dragValue = "before";
         }
       }
     }
 
     if (this._dragOverItem && dragOverItem != this._dragOverItem) {
       this._cancelDragActive(this._dragOverItem, dragOverItem);
     }
 
     if (dragOverItem != this._dragOverItem || dragValue != dragOverItem.getAttribute("dragover")) {
       if (dragOverItem != targetArea.customizationTarget) {
-        this._setDragActive(dragOverItem, dragValue, draggedItemId, targetIsToolbar);
-      } else if (targetIsToolbar) {
+        this._setDragActive(dragOverItem, dragValue, draggedItemId, targetAreaType);
+      } else if (targetAreaType == "toolbar") {
         this._updateToolbarCustomizationOutline(this.window, targetArea);
       }
       this._dragOverItem = dragOverItem;
     }
 
     aEvent.preventDefault();
     aEvent.stopPropagation();
   },
@@ -1936,80 +1995,87 @@ CustomizeMode.prototype = {
        prevent content->chrome privilege escalations. */
     let mozSourceNode = aEvent.dataTransfer.mozSourceNode;
     // mozSourceNode is null in the dragStart event handler or if
     // the drag event originated in an external application.
     return !mozSourceNode ||
            mozSourceNode.ownerGlobal != this.window;
   },
 
-  _setDragActive(aItem, aValue, aDraggedItemId, aInToolbar) {
+  _setDragActive(aItem, aValue, aDraggedItemId, aAreaType) {
     if (!aItem) {
       return;
     }
 
+    // getPlaceForItem and getAreaType return different things. Hack-around
+    // rather than having to update every. single. consumer. (and break add-ons)
+    if (aAreaType == "panel") {
+      aAreaType = "menu-panel";
+    }
     if (aItem.getAttribute("dragover") != aValue) {
       aItem.setAttribute("dragover", aValue);
 
       let window = aItem.ownerGlobal;
       let draggedItem = window.document.getElementById(aDraggedItemId);
-      if (!aInToolbar) {
+      if (aAreaType == "palette" || (aAreaType == "menu-panel" && !gPhotonStructure)) {
         this._setGridDragActive(aItem, draggedItem, aValue);
       } else {
         let targetArea = this._getCustomizableParent(aItem);
         this._updateToolbarCustomizationOutline(window, targetArea);
         let makeSpaceImmediately = false;
         if (!gDraggingInToolbars.has(targetArea.id)) {
           gDraggingInToolbars.add(targetArea.id);
           let draggedWrapper = this.document.getElementById("wrapper-" + aDraggedItemId);
           let originArea = this._getCustomizableParent(draggedWrapper);
           makeSpaceImmediately = originArea == targetArea;
         }
-        // Calculate width of the item when it'd be dropped in this position
-        let width = this._getDragItemSize(aItem, draggedItem).width;
-        let direction = window.getComputedStyle(aItem).direction;
+        let propertyToMeasure = aAreaType == "toolbar" ? "width" : "height";
+        // Calculate width/height of the item when it'd be dropped in this position.
+        let borderWidth = this._getDragItemSize(aItem, draggedItem)[propertyToMeasure];
+        let layoutSide = aAreaType == "toolbar" ? "Inline" : "Block";
         let prop, otherProp;
-        // If we're inserting before in ltr, or after in rtl:
-        if ((aValue == "before") == (direction == "ltr")) {
-          prop = "borderLeftWidth";
-          otherProp = "border-right-width";
+        if (aValue == "before") {
+          prop = "border" + layoutSide + "StartWidth";
+          otherProp = "border-" + layoutSide.toLowerCase() + "-end-width";
         } else {
-          // otherwise:
-          prop = "borderRightWidth";
-          otherProp = "border-left-width";
+          prop = "border" + layoutSide + "EndWidth";
+          otherProp = "border-" + layoutSide.toLowerCase() + "-start-width";
         }
         if (makeSpaceImmediately) {
           aItem.setAttribute("notransition", "true");
         }
-        aItem.style[prop] = width + "px";
+        aItem.style[prop] = borderWidth + "px";
         aItem.style.removeProperty(otherProp);
         if (makeSpaceImmediately) {
           // Force a layout flush:
           aItem.getBoundingClientRect();
           aItem.removeAttribute("notransition");
         }
       }
     }
   },
   _cancelDragActive(aItem, aNextItem, aNoTransition) {
     this._updateToolbarCustomizationOutline(aItem.ownerGlobal);
     let currentArea = this._getCustomizableParent(aItem);
     if (!currentArea) {
       return;
     }
-    let isToolbar = CustomizableUI.getAreaType(currentArea.id) == "toolbar";
-    if (isToolbar) {
+    let areaType = CustomizableUI.getAreaType(currentArea.id);
+    let needPositionManager = !areaType || (areaType == "menu-panel" && !gPhotonStructure);
+    if (!needPositionManager) {
       if (aNoTransition) {
         aItem.setAttribute("notransition", "true");
       }
       aItem.removeAttribute("dragover");
-      // Remove both property values in the case that the end padding
+      // Remove all property values in the case that the end padding
       // had been set.
-      aItem.style.removeProperty("border-left-width");
-      aItem.style.removeProperty("border-right-width");
+      aItem.style.removeProperty("border-inline-start-width");
+      aItem.style.removeProperty("border-inline-end-width");
+      aItem.style.removeProperty("border-block-start-width");
+      aItem.style.removeProperty("border-block-end-width");
       if (aNoTransition) {
         // Force a layout flush:
         aItem.getBoundingClientRect();
         aItem.removeAttribute("notransition");
       }
     } else {
       aItem.removeAttribute("dragover");
       if (aNextItem) {
@@ -2103,17 +2169,17 @@ CustomizeMode.prototype = {
       if (areas.indexOf(aElement.id) != -1) {
         return aElement;
       }
       aElement = aElement.parentNode;
     }
     return null;
   },
 
-  _getDragOverNode(aEvent, aAreaElement, aInToolbar, aDraggedItemId) {
+  _getDragOverNode(aEvent, aAreaElement, aAreaType, aDraggedItemId) {
     let expectedParent = aAreaElement.customizationTarget || aAreaElement;
     // Our tests are stupid. Cope:
     if (!aEvent.clientX && !aEvent.clientY) {
       return aEvent.target;
     }
     // Offset the drag event's position with the offset to the center of
     // the thing we're dragging
     let dragX = aEvent.clientX - this._dragOffset.x;
@@ -2128,17 +2194,17 @@ CustomizeMode.prototype = {
     if (boundsContainer == this.panelUIContents) {
       boundsContainer = boundsContainer.parentNode;
     }
     let bounds = boundsContainer.getBoundingClientRect();
     dragX = Math.min(bounds.right, Math.max(dragX, bounds.left));
     dragY = Math.min(bounds.bottom, Math.max(dragY, bounds.top));
 
     let targetNode;
-    if (aInToolbar) {
+    if (aAreaType == "toolbar" || (aAreaType == "menu-panel" && gPhotonStructure)) {
       targetNode = aAreaElement.ownerDocument.elementFromPoint(dragX, dragY);
       while (targetNode && targetNode.parentNode != expectedParent) {
         targetNode = targetNode.parentNode;
       }
     } else {
       let positionManager = DragPositionManager.getManagerForArea(aAreaElement);
       // Make it relative to the container:
       dragX -= bounds.left;
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -247,16 +247,17 @@
                     url("chrome://browser/skin/customizableui/background-noise-toolbar.png"),
                     linear-gradient(to bottom, #3e86ce, #3878ba);
   background-position: center top, left center, left top, left top, left top;
   background-repeat: no-repeat, no-repeat, repeat, repeat, no-repeat;
   background-size: auto 12px, 12px 100%, auto, auto, auto;
   background-attachment: scroll, scroll, fixed, fixed, scroll;
 }
 
+#widget-overflow-fixed-list > toolbarpaletteitem[place="panel"],
 toolbarpaletteitem[place="toolbar"] {
   transition: border-width 250ms ease-in-out;
 }
 
 toolbarpaletteitem[mousedown] {
   cursor: -moz-grabbing;
 }
 
@@ -274,16 +275,17 @@ toolbarpaletteitem[place="panel"] {
 #customization-palette[showing="true"] {
   opacity: 1;
 }
 
 toolbarpaletteitem toolbarbutton[disabled] {
   color: inherit !important;
 }
 
+#widget-overflow-fixed-list > toolbarpaletteitem[notransition][place="panel"],
 toolbarpaletteitem[notransition].panel-customization-placeholder,
 toolbarpaletteitem[notransition][place="toolbar"],
 toolbarpaletteitem[notransition][place="palette"],
 toolbarpaletteitem[notransition][place="panel"] {
   transition: none;
 }
 
 toolbarpaletteitem > toolbarbutton > .toolbarbutton-icon,
@@ -305,16 +307,20 @@ toolbarpaletteitem[mousedown] > toolbarb
 
 /* Override the toolkit styling for items being dragged over. */
 toolbarpaletteitem[place="toolbar"] {
   border-left-width: 0;
   border-right-width: 0;
   margin-right: 0;
   margin-left: 0;
 }
+#widget-overflow-fixed-list > toolbarpaletteitem[place="panel"] {
+  border-top: 0px solid transparent;
+  border-bottom: 0px solid transparent;
+}
 
 #customization-palette:not([hidden]) {
   margin-bottom: 25px;
 }
 
 toolbarpaletteitem[place="palette"]:-moz-focusring,
 toolbarpaletteitem[place="panel"]:-moz-focusring,
 toolbarpaletteitem[place="toolbar"]:-moz-focusring {