Bug 893446 - Try to avoid moving the page contents when the find bar appears. r=dao
authorMarkus Stange <mstange@themasta.com>
Tue, 16 Jul 2013 11:57:20 +0200
changeset 141873 452ca8b779f3ddf979af9fdf42d9f5241b9d80f5
parent 141872 678dd0508c822e467eea8e5348ebfa2aa92429f4
child 141874 676b8b1ea011128abc8ead148ba2264327aa634a
push id2123
push usermstange@themasta.com
push dateFri, 09 Aug 2013 17:59:43 +0000
treeherderfx-team@452ca8b779f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdao
bugs893446
milestone26.0a1
Bug 893446 - Try to avoid moving the page contents when the find bar appears. r=dao
toolkit/content/jar.mn
toolkit/content/widgets/findbar.css
toolkit/content/widgets/findbar.xml
toolkit/themes/linux/global/findBar.css
toolkit/themes/osx/global/findBar.css
toolkit/themes/windows/global/findBar.css
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -57,16 +57,17 @@ toolkit.jar:
    content/global/bindings/checkbox.xml        (widgets/checkbox.xml)
    content/global/bindings/colorpicker.xml     (widgets/colorpicker.xml)
    content/global/bindings/datetimepicker.xml  (widgets/datetimepicker.xml)
 *+ content/global/bindings/dialog.xml          (widgets/dialog.xml)
    content/global/bindings/editor.xml          (widgets/editor.xml)
    content/global/bindings/expander.xml        (widgets/expander.xml)
 *  content/global/bindings/filefield.xml       (widgets/filefield.xml)
 *+ content/global/bindings/findbar.xml         (widgets/findbar.xml)
+   content/global/bindings/findbar.css         (widgets/findbar.css)
    content/global/bindings/general.xml         (widgets/general.xml)
    content/global/bindings/groupbox.xml        (widgets/groupbox.xml)
 *+ content/global/bindings/listbox.xml         (widgets/listbox.xml)
    content/global/bindings/menu.xml            (widgets/menu.xml)
    content/global/bindings/menulist.xml        (widgets/menulist.xml)
    content/global/bindings/notification.xml    (widgets/notification.xml)
    content/global/bindings/numberbox.xml       (widgets/numberbox.xml)
    content/global/bindings/popup.xml           (widgets/popup.xml)
new file mode 100644
--- /dev/null
+++ b/toolkit/content/widgets/findbar.css
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+findbar {
+  transition-property: transform, opacity, visibility;
+  transition-duration: 120ms, 120ms, 0s;
+  transition-timing-function: ease-in-out, ease-in-out, linear;
+
+  /* The following positioning properties only take an effect during findbar
+   * transitions. The findbar binding sets position:absolute during that time
+   * on the findbar.
+   */
+  left: 0;
+  right: 0;
+  bottom: 0;
+}
+
+findbar[position="top"] {
+  top: 0;
+  bottom: auto;
+}
+
+findbar > hbox {
+  width: 100%;
+}
+
+findbar[hidden] {
+  /* Override display:none to make the transition work. */
+  display: -moz-box;
+  visibility: collapse;
+  opacity: 0;
+  transition-delay: 0s, 0s, 120ms;
+  transform: translateY(2em);
+}
+
+findbar[position="top"][hidden] {
+  transform: translateY(-2em);
+}
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -168,16 +168,17 @@
         event.preventDefault();
       ]]></handler>
     </handlers>
   </binding>
 
   <binding id="findbar"
            extends="chrome://global/content/bindings/toolbar.xml#toolbar">
     <resources>
+      <stylesheet src="chrome://global/content/bindings/findbar.css"/>
       <stylesheet src="chrome://global/skin/findBar.css"/>
     </resources>
 
     <content hidden="true">
     <xul:hbox anonid="findbar-container" class="findbar-container" flex="1" align="center">
       <xul:hbox anonid="findbar-textbox-wrapper" align="stretch">
         <xul:textbox anonid="findbar-textbox"
                      class="findbar-textbox findbar-find-fast"
@@ -236,16 +237,18 @@
       <field name="_tmpOutlineOffset">"0"</field>
       <field name="_drawOutline">false</field>
       <field name="_editors">null</field>
       <field name="_stateListeners">null</field>
 
       <field name="_flashFindBar">0</field>
       <field name="_initialFlashFindBarCount">6</field>
 
+      <field name="_contentScrollOffset">0</field>
+
       <property name="_foundLink"
                 onget="return this._foundLinkRef.get();"
                 onset="this._foundLinkRef = Components.utils.getWeakReference(val); return val;"/>
       <property name="_foundEditable"
                 onget="return this._foundEditableRef.get();"
                 onset="this._foundEditableRef = Components.utils.getWeakReference(val); return val;"/>
       <property name="_currentWindow"
                 onget="return this._currentWindowRef.get();"
@@ -1129,18 +1132,40 @@
             this._caseSensitiveStr =
               stringsBundle.GetStringFromName("CaseSensitive");
           }
 
           this._findFailedString = null;
 
           this._updateFindUI();
           if (this.hidden) {
+            // Use position:absolute during the transition.
+            this.style.position = "absolute";
+            this.parentNode.style.position = "relative";
+
+            // Apparently a flush is necessary after setting position:relative
+            // on our parentNode, otherwise setting hidden to false won't
+            // animate the transform change.
+            this.getBoundingClientRect();
+
             this.hidden = false;
 
+            // Set a height on the findbar that's at least as much as the
+            // current height, but guaranteed to be an integer number of
+            // screen pixels.
+            // This way, reapplying position:static on the findbar after the
+            // fade in animation won't cause the browser contents to wiggle.
+            let [chromeOffset, contentScrollOffset] = this._findOffsets();
+            this.style.height = chromeOffset + "px";
+            this._contentScrollOffset = contentScrollOffset;
+
+            // Wait for the findbar appearance animation to end before
+            // changing the browser size.
+            this.addEventListener("transitionend", this);
+
             this._updateStatusUI(this.nsITypeAheadFind.FIND_FOUND);
 
             let event = document.createEvent("Events");
             event.initEvent("findbaropen", true, false);
             this.dispatchEvent(event);
 
             return true;
           }
@@ -1173,16 +1198,26 @@
                     this._currentWindow.focus();
                 else
                   this.browser.contentWindow.focus();
               }
             }
           }
 
           this.hidden = true;
+
+          this.addEventListener("transitionend", this);
+
+          // Revert browser scroll shift + findbar static positioning.
+          if (this.getAttribute("position") == "top" &&
+              this.style.position != "absolute") {
+            this._browser.contentWindow.scrollBy(0, -this._contentScrollOffset);
+          }
+          this.style.position = "absolute";
+
           var fastFind = this.browser.fastFind;
           fastFind.setSelectionModeAndRepaint
             (this.nsISelectionController.SELECTION_ON);
           this._setFoundLink(null);
           this._foundEditable = null;
           this._currentWindow = null;
           if (this._quickFindTimeout) {
             clearTimeout(this._quickFindTimeout);
@@ -1474,20 +1509,68 @@
             case "mouseup":
               if (!this.hidden && this._findMode != this.FIND_NORMAL)
                 this.close();
 
               break;
             case "keypress":
               this._onBrowserKeypress(aEvent);
               break;
+            case "transitionend":
+              if (aEvent.target == this &&
+                  aEvent.propertyName == "transform") {
+                this.removeEventListener("transitionend", this);
+
+                // Change the browser size in such a way that the region that's
+                // overlapped by the findbar can be scrolled to, but try to
+                // avoid a visual shift of the browser contents.
+                this.style.removeProperty("position");
+                if (this.getAttribute("position") == "top" &&
+                    !this.hidden) {
+                  this._browser.contentWindow.scrollBy(0, this._contentScrollOffset);
+                }
+
+                // We'd like to remove position:relative from this.parentNode,
+                // but that unfortunately causes unnecessary repainting.
+              }
+              break;
           }
         ]]></body>
       </method>
 
+      <method name="_screenPixelsPerCSSPixel">
+        <parameter name="aWindow"/>
+        <body><![CDATA[
+          return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                        .getInterface(Components.interfaces.nsIDOMWindowUtils)
+                        .screenPixelsPerCSSPixel;
+        ]]></body>
+      </method>
+
+      <!--
+        - Find two numbers, one in chrome CSS pixels and one in integer
+        - content CSS pixels, that are about the same as (but not less than)
+        - the height of the findbar. These two numbers hopefully map to the
+        - same number of integer screen pixels.
+        - We want to avoid shifting of the page even when chrome and content
+        - have different zoom factors, and scrollBy() only accepts integers.
+        -->
+      <method name="_findOffsets">
+        <body><![CDATA[
+          let chromeFactor = this._screenPixelsPerCSSPixel(window);
+          let contentFactor = this._screenPixelsPerCSSPixel(this._browser.contentWindow);
+
+          let findbarHeightScreen = this.getBoundingClientRect().height * chromeFactor;
+          let contentScrollOffset = Math.ceil(findbarHeightScreen / contentFactor);
+          let estimatedScrollOffsetInScreenPixels = Math.round(contentScrollOffset * contentFactor);
+          let chromeOffset = estimatedScrollOffsetInScreenPixels / chromeFactor;
+          return [chromeOffset, contentScrollOffset];
+        ]]></body>
+      </method>
+
       <method name="_enableFindButtons">
         <parameter name="aEnable"/>
         <body><![CDATA[
           this.getElement("find-next").disabled =
             this.getElement("find-previous").disabled = !aEnable;
         ]]></body>
       </method>
 
--- a/toolkit/themes/linux/global/findBar.css
+++ b/toolkit/themes/linux/global/findBar.css
@@ -4,39 +4,21 @@
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 findbar {
   padding: 4px 8px;
   border-top: 2px solid;
   -moz-border-top-colors: ThreeDShadow ThreeDHighlight;
   min-width: 1px;
-  transition-property: margin-bottom, opacity, visibility;
-  transition-duration: 150ms, 150ms, 0s;
-  transition-timing-function: ease-in-out, ease-in-out, linear;
 }
 
 findbar[position="top"] {
   border-top-style: none;
   border-bottom: 1px solid ThreeDShadow;
-  transition-property: margin-top, opacity, visibility;
-}
-
-findbar[hidden] {
-  /* Override display:none to make the transition work. */
-  display: -moz-box;
-  visibility: collapse;
-  margin-bottom: -1em;
-  opacity: 0;
-  transition-delay: 0s, 0s, 150ms;
-}
-
-findbar[position="top"][hidden] {
-  margin-bottom: auto;
-  margin-top: -1em;
 }
 
 .findbar-closebutton {
   -moz-margin-start: 4px;
   list-style-image: url("moz-icon://stock/gtk-close?size=menu");
 }
 
 /* Search field */
--- a/toolkit/themes/osx/global/findBar.css
+++ b/toolkit/themes/osx/global/findBar.css
@@ -5,39 +5,21 @@
 %include shared.inc
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 findbar {
   background: @scopeBarBackground@;
   border-top: @scopeBarSeparatorBorder@;
   min-width: 1px;
   padding: 4px 2px;
-  transition-property: margin-bottom, opacity, visibility;
-  transition-duration: 150ms, 150ms, 0s;
-  transition-timing-function: ease-in-out, ease-in-out, linear;
 }
 
 findbar[position="top"] {
   border-top: none;
   border-bottom: @scopeBarSeparatorBorder@;
-  transition-property: margin-top, opacity, visibility;
-}
-
-findbar[hidden] {
-  /* Override display:none to make the transition work. */
-  display: -moz-box;
-  visibility: collapse;
-  margin-bottom: -1em;
-  opacity: 0;
-  transition-delay: 0s, 0s, 150ms;
-}
-
-findbar[position="top"][hidden] {
-  margin-bottom: auto;
-  margin-top: -1em;
 }
 
 findbar:-moz-lwtheme {
   -moz-appearance: none;
   background: none;
   border-style: none;
 }
 
--- a/toolkit/themes/windows/global/findBar.css
+++ b/toolkit/themes/windows/global/findBar.css
@@ -6,39 +6,21 @@
 
 findbar {
   padding: 4px 8px;
   box-shadow: 0 1px 1px rgba(0,0,0,.1) inset;
   background-image: linear-gradient(rgba(0,0,0,.15) 1px, rgba(255,255,255,.15) 1px);
   background-size: 100% 2px;
   background-repeat: no-repeat;
   min-width: 1px;
-  transition-property: margin-bottom, opacity, visibility;
-  transition-duration: 150ms, 150ms, 0s;
-  transition-timing-function: ease-in-out, ease-in-out, linear;
 }
 
 findbar[position="top"] {
   background-image: none;
   box-shadow: 0 -1px 0 rgba(0,0,0,.1) inset;
-  transition-property: margin-top, opacity, visibility;
-}
-
-findbar[hidden] {
-  /* Override display:none to make the transition work. */
-  display: -moz-box;
-  visibility: collapse;
-  margin-bottom: -1em;
-  opacity: 0;
-  transition-delay: 0s, 0s, 150ms;
-}
-
-findbar[position="top"][hidden] {
-  margin-bottom: auto;
-  margin-top: -1em;
 }
 
 .findbar-closebutton {
   -moz-margin-start: 4px;
   border: none;
   padding: 0;
   list-style-image: url("chrome://global/skin/icons/close.png");
   -moz-appearance: none;