--- a/toolkit/content/widgets/scrollbox.xml
+++ b/toolkit/content/widgets/scrollbox.xml
@@ -38,37 +38,38 @@
anonid="scrollbutton-up"
xbl:inherits="orient,collapsed=notoverflowing,disabled=scrolledtostart"
oncommand="_autorepeatbuttonScroll(event);"/>
<xul:spacer class="arrowscrollbox-overflow-start-indicator"
xbl:inherits="collapsed=scrolledtostart"/>
<xul:scrollbox class="arrowscrollbox-scrollbox"
anonid="scrollbox"
flex="1"
- xbl:inherits="orient,align,pack,dir">
+ xbl:inherits="orient,align,pack,dir,smoothscroll">
<children/>
</xul:scrollbox>
<xul:spacer class="arrowscrollbox-overflow-end-indicator"
xbl:inherits="collapsed=scrolledtoend"/>
<xul:autorepeatbutton class="autorepeatbutton-down"
anonid="scrollbutton-down"
xbl:inherits="orient,collapsed=notoverflowing,disabled=scrolledtoend"
oncommand="_autorepeatbuttonScroll(event);"/>
</content>
<implementation>
<constructor><![CDATA[
+ if (!this.hasAttribute("smoothscroll")) {
+ this.smoothScroll = this._prefBranch
+ .getBoolPref("toolkit.scrollbox.smoothScroll", true);
+ }
+
this.setAttribute("notoverflowing", "true");
this._updateScrollButtonsDisabledState();
]]></constructor>
- <destructor><![CDATA[
- this._stopSmoothScroll();
- ]]></destructor>
-
<field name="_scrollbox">
document.getAnonymousElementByAttribute(this, "anonid", "scrollbox");
</field>
<field name="_scrollButtonUp">
document.getAnonymousElementByAttribute(this, "anonid", "scrollbutton-up");
</field>
<field name="_scrollButtonDown">
document.getAnonymousElementByAttribute(this, "anonid", "scrollbutton-down");
@@ -91,31 +92,22 @@
if (this._scrollIncrement === null) {
this._scrollIncrement = this._prefBranch
.getIntPref("toolkit.scrollbox.scrollIncrement", 20);
}
return this._scrollIncrement;
]]></getter>
</property>
- <field name="_smoothScroll">null</field>
<property name="smoothScroll">
<getter><![CDATA[
- if (this._smoothScroll === null) {
- if (this.hasAttribute("smoothscroll")) {
- this._smoothScroll = (this.getAttribute("smoothscroll") == "true");
- } else {
- this._smoothScroll = this._prefBranch
- .getBoolPref("toolkit.scrollbox.smoothScroll", true);
- }
- }
- return this._smoothScroll;
+ return this.getAttribute("smoothscroll") == "true";
]]></getter>
<setter><![CDATA[
- this._smoothScroll = val;
+ this.setAttribute("smoothscroll", !!val);
return val;
]]></setter>
</property>
<field name="_scrollBoxObject">null</field>
<property name="scrollBoxObject" readonly="true">
<getter><![CDATA[
if (!this._scrollBoxObject) {
@@ -249,187 +241,20 @@
<method name="ensureElementIsVisible">
<parameter name="element"/>
<parameter name="aSmoothScroll"/>
<body><![CDATA[
if (!this._canScrollToElement(element))
return;
- var vertical = this.orient == "vertical";
- var rect = this.scrollClientRect;
- var containerStart = vertical ? rect.top : rect.left;
- var containerEnd = vertical ? rect.bottom : rect.right;
- rect = element.getBoundingClientRect();
- var elementStart = vertical ? rect.top : rect.left;
- var elementEnd = vertical ? rect.bottom : rect.right;
-
- var scrollPaddingRect = this.scrollPaddingRect;
- let style = window.getComputedStyle(this._scrollbox);
- var scrollContentRect = {
- left: scrollPaddingRect.left + parseFloat(style.paddingLeft),
- top: scrollPaddingRect.top + parseFloat(style.paddingTop),
- right: scrollPaddingRect.right - parseFloat(style.paddingRight),
- bottom: scrollPaddingRect.bottom - parseFloat(style.paddingBottom)
- };
-
- if (elementStart <= (vertical ? scrollContentRect.top : scrollContentRect.left)) {
- elementStart = vertical ? scrollPaddingRect.top : scrollPaddingRect.left;
- }
- if (elementEnd >= (vertical ? scrollContentRect.bottom : scrollContentRect.right)) {
- elementEnd = vertical ? scrollPaddingRect.bottom : scrollPaddingRect.right;
- }
-
- var amountToScroll;
-
- if (elementStart < containerStart) {
- amountToScroll = elementStart - containerStart;
- } else if (containerEnd < elementEnd) {
- amountToScroll = elementEnd - containerEnd;
- } else if (this._isScrolling) {
- // decelerate if a currently-visible element is selected during the scroll
- const STOP_DISTANCE = 15;
- if (this._isScrolling == -1 && elementStart - STOP_DISTANCE < containerStart)
- amountToScroll = elementStart - containerStart;
- else if (this._isScrolling == 1 && containerEnd - STOP_DISTANCE < elementEnd)
- amountToScroll = elementEnd - containerEnd;
- else
- amountToScroll = this._isScrolling * STOP_DISTANCE;
- } else {
- return;
- }
-
- this._stopSmoothScroll();
-
- if (aSmoothScroll != false && this.smoothScroll) {
- this._smoothScrollByPixels(amountToScroll, element);
- } else {
- this.scrollByPixels(amountToScroll);
- }
- ]]></body>
- </method>
-
- <method name="_smoothScrollByPixels">
- <parameter name="amountToScroll"/>
- <parameter name="element"/><!-- optional -->
- <body><![CDATA[
- if (amountToScroll == 0)
- return;
-
- // Shouldn't forget pending scroll amount if the scroll direction
- // isn't changed because this may be called high frequency with very
- // small pixel values.
- var scrollDirection = 0;
- if (amountToScroll) {
- // Positive amountToScroll makes us scroll right (elements fly left),
- // negative scrolls left.
- scrollDirection = amountToScroll < 0 ? -1 : 1;
- }
-
- // However, if the scroll direction is changed, let's cancel the
- // pending scroll because user must want to scroll from current
- // position.
- if (this._isScrolling && this._isScrolling != scrollDirection)
- this._stopSmoothScroll();
-
- this._scrollTarget = element;
- this._isScrolling = scrollDirection;
-
- this._scrollAnim.start(amountToScroll, !this._scrollTarget);
+ element.scrollIntoView({ behavior: aSmoothScroll == false ? "instant" : "auto" });
]]></body>
</method>
- <field name="_scrollAnim"><![CDATA[({
- scrollbox: this,
- distance: 0.0,
- requestHandle: 0, /* 0 indicates there is no pending request */
-
- // Be aware, |distance| may be dounble. I.e., the absolute value of it can
- // be less than 1. Set |isContinuousScroll| to true when the scroll may be
- // a part of continous scroll, for example, it's caused by turning mosue wheel.
- start: function scrollAnim_start(distance, isContinuousScroll) {
- // When it's a continous scroll and the scroll was started, this needs to
- // respect preceding scroll requests. For example, 1.5px scroll occurs 2 times,
- // 3px should be scrolled. So, fractional values shouldn't be discarded.
- if (isContinuousScroll && this.distance) {
- // |this.startPos| is integer due to cache of |.scrollPosition|. Therefore,
- // we need to manage actual destination with |this.destination|.
- var oldDestination = this.destination;
- this.destination = this._clampPosition(this.destination + distance);
-
- // If scroll position has already reached the ends, we need to do nothing.
- if (oldDestination == this.destination)
- return;
-
- // If the integer part of the destination isn't changed, we need to do
- // nothing now, wait next event.
- if (Math.trunc(this.destination) == Math.trunc(this.destination - distance))
- return;
-
- // Let's restart animation from current position to the new destination.
- if (this.requestHandle) {
- this.stop();
- this.startPos = this.scrollbox.scrollPosition;
- // The call of |.stop()| causes clearing |this.distance| but let's recover it
- // for keeping continuous scroll.
- this.distance = this.destination - this.startPos;
- }
- } else {
- this.startPos = this.scrollbox.scrollPosition;
- this.destination = this._clampPosition(this.startPos + distance);
- this.distance = this.destination - this.startPos;
-
- // If absolute value of |this.distance| is less than 1px and this call is
- // start of a continous scroll, should wait to scroll until accumulated
- // scroll amount becomes 1px or greater.
- if (isContinuousScroll && Math.abs(this.distance) < 1)
- return;
- }
- this.duration = Math.min(1000, Math.round(50 * Math.sqrt(Math.abs(distance))));
- this.startTime = window.performance.now();
-
- if (!this.requestHandle)
- this.requestHandle = window.requestAnimationFrame(this.sample.bind(this));
- },
-
- stop: function scrollAnim_stop() {
- window.cancelAnimationFrame(this.requestHandle);
- this.requestHandle = 0;
- // Reset continouos scroll transaction at stopping the scroll animation.
- this.distance = 0;
- },
-
- sample: function scrollAnim_handleEvent(timeStamp) {
- // Note that timeStamp sometimes older than start time. If we use
- // native value below, it causes scrolling revese direction.
- // So, if the timeStamp is older, let's treat it as same as the start time.
- const timePassed = Math.max(0, timeStamp - this.startTime);
- const pos = timePassed >= this.duration ? 1 :
- 1 - Math.pow(1 - timePassed / this.duration, 4);
-
- this.scrollbox.scrollPosition = this.startPos + (this.distance * pos);
-
- if (pos == 1)
- this.scrollbox._stopSmoothScroll();
- else
- this.requestHandle = window.requestAnimationFrame(this.sample.bind(this));
- },
-
- _clampPosition: function scrollAnim_clampPosition(aScrollPosition) {
- if (aScrollPosition < 0) {
- return 0;
- }
- var maxPos = this.scrollbox.scrollSize - this.scrollbox.scrollClientSize;
- if (aScrollPosition > maxPos) {
- return maxPos;
- }
- return aScrollPosition;
- }
- })]]></field>
-
<method name="scrollByIndex">
<parameter name="index"/>
<parameter name="aSmoothScroll"/>
<body><![CDATA[
if (index == 0)
return;
// Each scrollByIndex call is expected to scroll the given number of
@@ -600,34 +425,20 @@
<method name="scrollByPixels">
<parameter name="px"/>
<body><![CDATA[
this.scrollPosition += px;
]]></body>
</method>
- <!-- 0: idle
- 1: scrolling right
- -1: scrolling left -->
- <field name="_isScrolling">0</field>
<field name="_prevMouseScrolls">[null, null]</field>
<field name="_touchStart">-1</field>
- <method name="_stopSmoothScroll">
- <body><![CDATA[
- if (this._isScrolling) {
- this._scrollAnim.stop();
- this._isScrolling = 0;
- this._scrollTarget = null;
- }
- ]]></body>
- </method>
-
<field name="_scrollButtonUpdatePending">false</field>
<method name="_updateScrollButtonsDisabledState">
<body><![CDATA[
if (this.hasAttribute("notoverflowing")) {
this.setAttribute("scrolledtoend", "true");
this.setAttribute("scrolledtostart", "true");
return;
}
@@ -686,17 +497,16 @@
});
]]></body>
</method>
</implementation>
<handlers>
<handler event="wheel"><![CDATA[
let doScroll = false;
- let useSmoothScroll = event.deltaMode != event.DOM_DELTA_PIXEL && this.smoothScroll;
let scrollAmount = 0;
if (this.orient == "vertical") {
doScroll = true;
if (event.deltaMode == event.DOM_DELTA_PIXEL)
scrollAmount = event.deltaY;
else if (event.deltaMode == event.DOM_DELTA_PAGE)
scrollAmount = event.deltaY * this.scrollClientSize;
else
@@ -725,20 +535,17 @@
}
if (this._prevMouseScrolls.length > 1)
this._prevMouseScrolls.shift();
this._prevMouseScrolls.push(isVertical);
}
if (doScroll) {
- if (useSmoothScroll)
- this._smoothScrollByPixels(scrollAmount);
- else
- this.scrollByPixels(scrollAmount);
+ this.scrollByPixels(scrollAmount);
}
event.stopPropagation();
event.preventDefault();
]]></handler>
<handler event="touchstart"><![CDATA[
if (event.touches.length > 1) {
@@ -857,17 +664,17 @@
onmouseup="if (event.button == 0) _stopScroll();"
onmouseover="_continueScroll(-1);"
onmouseout="_pauseScroll();"/>
<xul:spacer class="arrowscrollbox-overflow-start-indicator"
xbl:inherits="collapsed=scrolledtostart"/>
<xul:scrollbox class="arrowscrollbox-scrollbox"
anonid="scrollbox"
flex="1"
- xbl:inherits="orient,align,pack,dir">
+ xbl:inherits="orient,align,pack,dir,smoothscroll">
<children/>
</xul:scrollbox>
<xul:spacer class="arrowscrollbox-overflow-end-indicator"
xbl:inherits="collapsed=scrolledtoend"/>
<xul:toolbarbutton class="scrollbutton-down"
xbl:inherits="orient,collapsed=notoverflowing,disabled=scrolledtoend"
anonid="scrollbutton-down"
onclick="_distanceScroll(event);"
@@ -901,51 +708,23 @@
if (!document)
aTimer.cancel();
this.scrollByIndex(this._scrollIndex);
]]>
</body>
</method>
- <field name="_arrowScrollAnim"><![CDATA[({
- scrollbox: this,
- requestHandle: 0, /* 0 indicates there is no pending request */
- start: function arrowSmoothScroll_start() {
- this.lastFrameTime = window.performance.now();
- if (!this.requestHandle)
- this.requestHandle = window.requestAnimationFrame(this.sample.bind(this));
- },
- stop: function arrowSmoothScroll_stop() {
- window.cancelAnimationFrame(this.requestHandle);
- this.requestHandle = 0;
- },
- sample: function arrowSmoothScroll_handleEvent(timeStamp) {
- const scrollIndex = this.scrollbox._scrollIndex;
- const timePassed = timeStamp - this.lastFrameTime;
- this.lastFrameTime = timeStamp;
-
- const scrollDelta = 0.5 * timePassed * scrollIndex;
- this.scrollbox.scrollPosition += scrollDelta;
-
- this.requestHandle = window.requestAnimationFrame(this.sample.bind(this));
- }
- })]]></field>
-
<method name="_startScroll">
<parameter name="index"/>
<body><![CDATA[
if (this._isRTLScrollbox)
index *= -1;
this._scrollIndex = index;
this._mousedown = true;
- if (this.smoothScroll) {
- this._arrowScrollAnim.start();
- return;
- }
if (!this._scrollTimer)
this._scrollTimer =
Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
else
this._scrollTimer.cancel();
@@ -956,22 +735,17 @@
</body>
</method>
<method name="_stopScroll">
<body><![CDATA[
if (this._scrollTimer)
this._scrollTimer.cancel();
this._mousedown = false;
- if (!this._scrollIndex || !this.smoothScroll)
- return;
-
- this.scrollByIndex(this._scrollIndex);
this._scrollIndex = 0;
- this._arrowScrollAnim.stop();
]]></body>
</method>
<method name="_pauseScroll">
<body><![CDATA[
if (this._mousedown) {
this._stopScroll();
this._mousedown = true;