Bug 1251987 - Support by-page scroll deltas in XUL scrollboxes. r=Enn, a=lizzard
authorMarkus Stange <mstange@themasta.com>
Thu, 05 May 2016 13:39:54 -0400
changeset 333014 80c89a78cae57e319a7b27b30b26200064b351d5
parent 333013 59a8c40a2a17aca0435d8ee4a7af52d0adf5648a
child 333015 b01ee9309ba0d1d2ac65e4f2d8e25899ca817ec4
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersEnn, lizzard
bugs1251987
milestone48.0a2
Bug 1251987 - Support by-page scroll deltas in XUL scrollboxes. r=Enn, a=lizzard MozReview-Commit-ID: JL0s4Pum5P5
toolkit/content/widgets/scrollbox.xml
--- a/toolkit/content/widgets/scrollbox.xml
+++ b/toolkit/content/widgets/scrollbox.xml
@@ -356,16 +356,70 @@
           }
           if (!targetElement)
             return;
 
           this.ensureElementIsVisible(targetElement, aSmoothScroll);
         ]]></body>
       </method>
 
+      <method name="scrollByPage">
+        <parameter name="pageDelta"/>
+        <parameter name="aSmoothScroll"/>
+        <body><![CDATA[
+          if (pageDelta == 0)
+            return;
+
+          // If a previous call is still in progress because of smooth
+          // scrolling, we need to complete it before starting a new one.
+          if (this._scrollTarget) {
+            let elements = this._getScrollableElements();
+            if (this._scrollTarget != elements[0] &&
+                this._scrollTarget != elements[elements.length - 1])
+              this.ensureElementIsVisible(this._scrollTarget, false);
+          }
+
+          var [start, end] = this._startEndProps;
+          var rect = this.scrollClientRect;
+          var containerEdge = pageDelta > 0 ? rect[end] + 1 : rect[start] - 1;
+          var pixelDelta = pageDelta * (rect[end] - rect[start]);
+          var destinationPosition = containerEdge + pixelDelta;
+          var nextElement = this._elementFromPoint(containerEdge, pageDelta);
+          if (!nextElement)
+            return;
+
+          // We need to iterate over our elements in the direction of pageDelta.
+          // pageDelta is the physical direction, so in a horizontal scroll box,
+          // positive values scroll to the right no matter if the scrollbox is
+          // LTR or RTL. But RTL changes how we need to advance the iteration
+          // (whether to get the next or the previous sibling of the current
+          // element).
+          var logicalAdvanceDir = pageDelta * (this._isRTLScrollbox ? -1 : 1);
+          var advance = logicalAdvanceDir > 0 ? (e => e.nextSibling) : (e => e.previousSibling);
+
+          var extendsPastTarget = (pageDelta > 0)
+            ? (e => e.getBoundingClientRect()[end] > destinationPosition)
+            : (e => e.getBoundingClientRect()[start] < destinationPosition);
+
+          // We want to scroll to the last element we encounter before we find
+          // an element which extends past destinationPosition.
+          var targetElement;
+          do {
+            if (this._canScrollToElement(nextElement))
+              targetElement = nextElement;
+            nextElement = advance(nextElement);
+          } while (nextElement && !extendsPastTarget(nextElement));
+
+          if (!targetElement)
+            return;
+
+          this.ensureElementIsVisible(targetElement, aSmoothScroll);
+        ]]></body>
+      </method>
+
       <method name="_getScrollableElements">
         <body><![CDATA[
           var nodes = this.childNodes;
           if (nodes.length == 1 &&
               nodes[0].localName == "children" &&
               nodes[0].namespaceURI == "http://www.mozilla.org/xbl") {
             nodes = document.getBindingParent(this).childNodes;
           }
@@ -496,16 +550,18 @@
       </method>
     </implementation>
 
     <handlers>
       <handler event="wheel"><![CDATA[
         if (this.orient == "vertical") {
           if (event.deltaMode == event.DOM_DELTA_PIXEL)
             this.scrollByPixels(event.deltaY);
+          else if (event.deltaMode == event.DOM_DELTA_PAGE)
+            this.scrollByPage(event.deltaY);
           else
             this.scrollByIndex(event.deltaY);
         }
         // We allow vertical scrolling to scroll a horizontal scrollbox
         // because many users have a vertical scroll wheel but no
         // horizontal support.
         // Because of this, we need to avoid scrolling chaos on trackpads
         // and mouse wheels that support simultaneous scrolling in both axes.
@@ -515,16 +571,18 @@
         else {
           let isVertical = Math.abs(event.deltaY) > Math.abs(event.deltaX);
           let delta = isVertical ? event.deltaY : event.deltaX;
           let scrollByDelta = isVertical && this._isRTLScrollbox ? -delta : delta;
 
           if (this._prevMouseScrolls.every(prev => prev == isVertical)) {
             if (event.deltaMode == event.DOM_DELTA_PIXEL)
               this.scrollByPixels(scrollByDelta);
+            else if (event.deltaMode == event.DOM_DELTA_PAGE)
+              this.scrollByPage(scrollByDelta);
             else
               this.scrollByIndex(scrollByDelta);
           }
 
           if (this._prevMouseScrolls.length > 1)
             this._prevMouseScrolls.shift();
           this._prevMouseScrolls.push(isVertical);
         }