Bug 972359 - Australis - use margins and border for measuring panel heights to avoid unnecessary scrollbars, r=mconley
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Mon, 24 Feb 2014 17:29:02 +0000
changeset 171096 b89988fc261d6f63f59d7d98f1c25bc1b425f20e
parent 171095 9adf062bc5a344d2314498ce168d915632367c75
child 171097 1ac1efebb5a5e416ed2fbbdde7bf3b4cfc9d9512
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersmconley
bugs972359
milestone30.0a1
Bug 972359 - Australis - use margins and border for measuring panel heights to avoid unnecessary scrollbars, r=mconley
browser/components/customizableui/content/panelUI.xml
--- a/browser/components/customizableui/content/panelUI.xml
+++ b/browser/components/customizableui/content/panelUI.xml
@@ -212,17 +212,17 @@
           this._mainViewHeight = this._viewStack.clientHeight;
 
           this._transitioning = true;
           this._viewContainer.addEventListener("transitionend", function trans() {
             this._viewContainer.removeEventListener("transitionend", trans);
             this._transitioning = false;
           }.bind(this));
 
-          let newHeight = this._heightOfSubview(viewNode);
+          let newHeight = this._heightOfSubview(viewNode, this._subViews);
           this._viewContainer.style.height = newHeight + "px";
 
           this._subViewObserver.observe(viewNode, {
             attributes: true,
             characterData: true,
             childList: true,
             subtree: true
           });
@@ -319,17 +319,17 @@
           this._mainView.style.height =
             this.getBoundingClientRect().height + "px";
           this.ignoreMutations = false;
         ]]></body>
       </method>
       <method name="_syncContainerWithSubView">
         <body><![CDATA[
           if (!this.ignoreMutations && this.showingSubView) {
-            let newHeight = this._heightOfSubview(this._currentSubView);
+            let newHeight = this._heightOfSubview(this._currentSubView, this._subViews);
             this._viewContainer.style.height = newHeight + "px";
           }
         ]]></body>
       </method>
       <method name="_syncContainerWithMainView">
         <body><![CDATA[
           if (!this.ignoreMutations && !this.showingSubView && !this._transitioning) {
             let height;
@@ -340,26 +340,60 @@
             }
             this._viewContainer.style.height = height + "px";
           }
         ]]></body>
       </method>
 
       <method name="_heightOfSubview">
         <parameter name="aSubview"/>
+        <parameter name="aContainerToCheck"/>
         <body><![CDATA[
+          function getFullHeight(element) {
+            //XXXgijs: unfortunately, scrollHeight rounds values, and there's no alternative
+            // that works with overflow: auto elements. Fortunately for us,
+            // we have exactly 1 (potentially) scrolling element in here (the subview body),
+            // and rounding 1 value is OK - rounding more than 1 and adding them means we get
+            // off-by-1 errors. Now we might be off by a subpixel, but we care less about that.
+            // So, use scrollHeight *only* if the element is vertically scrollable.
+            let height;
+            let elementCS;
+            if (element.scrollTopMax) {
+              height = element.scrollHeight;
+              // Bounding client rects include borders, scrollHeight doesn't:
+              elementCS = win.getComputedStyle(element);
+              height += parseFloat(elementCS.borderTopWidth) +
+                        parseFloat(elementCS.borderBottomWidth);
+            } else {
+              height = element.getBoundingClientRect().height;
+              if (height > 0) {
+                elementCS = win.getComputedStyle(element);
+              }
+            }
+            if (elementCS) {
+              // Include margins - but not borders or paddings because they
+              // were dealt with above.
+              height += parseFloat(elementCS.marginTop) + parseFloat(elementCS.marginBottom);
+            }
+            return height;
+          }
+          let win = aSubview.ownerDocument.defaultView;
           let body = aSubview.querySelector(".panel-subview-body");
-          let height = body ? body.scrollHeight : aSubview.scrollHeight;
+          let height = getFullHeight(body || aSubview);
           if (body) {
             let header = aSubview.querySelector(".panel-subview-header");
             let footer = aSubview.querySelector(".panel-subview-footer");
-            height += (header ? header.scrollHeight : 0) +
-                      (footer ? footer.scrollHeight : 0);
+            height += (header ? getFullHeight(header) : 0) +
+                      (footer ? getFullHeight(footer) : 0);
           }
-          return height;
+          if (aContainerToCheck) {
+            let containerCS = win.getComputedStyle(aContainerToCheck);
+            height += parseFloat(containerCS.paddingTop) + parseFloat(containerCS.paddingBottom);
+          }
+          return Math.round(height);
         ]]></body>
       </method>
 
     </implementation>
   </binding>
 
   <binding id="panelview">
     <implementation>