Bug 1367776 - Calculate the maximum height of panelmultiview before the panel is displayed. r=Gijs
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Thu, 01 Jun 2017 15:49:35 +0100
changeset 361864 7af15f9b3cfa367e6de34215a484d02204699301
parent 361863 dc7327ab555ab30167e81d9201c046ca8baf797f
child 361865 c26decc58822e20a6f263ede2f6168bb830555ad
push id31946
push userryanvm@gmail.com
push dateThu, 01 Jun 2017 20:43:38 +0000
treeherdermozilla-central@b138d2f271fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1367776
milestone55.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 1367776 - Calculate the maximum height of panelmultiview before the panel is displayed. r=Gijs This allows the panel to open correctly when scrollable views like History are displayed as the main view, for example when the History button is moved to the toolbar. MozReview-Commit-ID: ELSbC0RpuaK
browser/components/customizableui/PanelMultiView.jsm
--- a/browser/components/customizableui/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -197,16 +197,22 @@ this.PanelMultiView = class {
     return this._panelViews;
   }
   get _dwu() {
     if (this.__dwu)
       return this.__dwu;
     return this.__dwu = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                                    .getInterface(Ci.nsIDOMWindowUtils);
   }
+  get _screenManager() {
+    if (this.__screenManager)
+      return this.__screenManager;
+    return this.__screenManager = Cc["@mozilla.org/gfx/screenmanager;1"]
+                                    .getService(Ci.nsIScreenManager);
+  }
   get _currentSubView() {
     return this.panelViews ? this.panelViews.currentView : this.__currentSubView;
   }
   set _currentSubView(panel) {
     if (this.panelViews)
       this.panelViews.currentView = panel;
     else
       this.__currentSubView = panel;
@@ -769,50 +775,57 @@ this.PanelMultiView = class {
         // subview causes the panel to exceed the dimensions of the screen in the
         // direction that the panel originally opened in. This property resets
         // every time the popup closes, which is why we have to set it each time.
         this._panel.autoPosition = false;
         if (this.panelViews) {
           this.window.addEventListener("keydown", this);
           this._panel.addEventListener("mousemove", this);
         }
+
+        // Before opening the panel, we have to limit the maximum height of any
+        // view based on the space that will be available. We cannot just use
+        // window.screen.availTop and availHeight because these may return an
+        // incorrect value when the window spans multiple screens.
+        let anchorBox = this._panel.anchorNode.boxObject;
+        let screen = this._screenManager.screenForRect(anchorBox.screenX,
+                                                       anchorBox.screenY,
+                                                       anchorBox.width,
+                                                       anchorBox.height);
+        let availTop = {}, availHeight = {};
+        screen.GetAvailRect({}, availTop, {}, availHeight);
+        let cssAvailTop = availTop.value / screen.defaultCSSScaleFactor;
+
+        // The distance from the anchor to the available margin of the screen is
+        // based on whether the panel will open towards the top or the bottom.
+        let maxHeight;
+        if (this._panel.alignmentPosition.startsWith("before_")) {
+          maxHeight = anchorBox.screenY - cssAvailTop;
+        } else {
+          let anchorScreenBottom = anchorBox.screenY + anchorBox.height;
+          let cssAvailHeight = availHeight.value / screen.defaultCSSScaleFactor;
+          maxHeight = cssAvailTop + cssAvailHeight - anchorScreenBottom;
+        }
+
+        // To go from the maximum height of the panel to the maximum height of
+        // the view stack, we need to subtract the height of the arrow and the
+        // height of the opposite margin, but we cannot get their actual values
+        // because the panel is not visible yet. However, we know that this is
+        // currently 11px on Mac, 13px on Windows, and 13px on Linux. We also
+        // want an extra margin, both for visual reasons and to prevent glitches
+        // due to small rounding errors. So, we just use a value that makes
+        // sense for all platforms. If the arrow visuals change significantly,
+        // this value will be easy to adjust.
+        const EXTRA_MARGIN_PX = 20;
+        this._viewStack.style.maxHeight = (maxHeight - EXTRA_MARGIN_PX) + "px";
         break;
       case "popupshown":
         // Now that the main view is visible, we can check the height of the
         // description elements it contains.
         this.descriptionHeightWorkaround();
-
-        if (!this.panelViews) {
-          // Now that the panel has opened, we can compute the distance from its
-          // anchor to the available margin of the screen, based on whether the
-          // panel actually opened towards the top or the bottom. We use this to
-          // limit its maximum height, which is relevant when opening a subview.
-          let maxHeight;
-          if (this._panel.alignmentPosition.startsWith("before_")) {
-            maxHeight = this._panel.getOuterScreenRect().bottom -
-                        this.window.screen.availTop;
-          } else {
-            maxHeight = this.window.screen.availTop +
-                        this.window.screen.availHeight -
-                        this._panel.getOuterScreenRect().top;
-          }
-          // To go from the maximum height of the panel to the maximum height of
-          // the view stack, we start by subtracting the height of the arrow box.
-          // We don't need to trigger a new layout because this does not change.
-          let arrowBox = this.document.getAnonymousElementByAttribute(
-                                          this._panel, "anonid", "arrowbox");
-          maxHeight -= this._dwu.getBoundsWithoutFlushing(arrowBox).height;
-          // We subtract a fixed margin to account for variable borders. We don't
-          // try to measure this accurately so it's faster, we don't depend on
-          // the arrowpanel structure, and we don't hit rounding errors. Instead,
-          // we use a value that is much greater than the typical borders and
-          // makes sense visually.
-          const EXTRA_MARGIN_PX = 8;
-          this._viewStack.style.maxHeight = (maxHeight - EXTRA_MARGIN_PX) + "px";
-        }
         break;
       case "popuphidden":
         this.node.removeAttribute("panelopen");
         this.showMainView();
         if (this.panelViews) {
           this.window.removeEventListener("keydown", this);
           this._panel.removeEventListener("mousemove", this);
           this._resetKeyNavigation();