<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[209477] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/209477">209477</a></dd>
<dt>Author</dt> <dd>wenson_hsieh@apple.com</dd>
<dt>Date</dt> <dd>2016-12-07 13:50:11 -0800 (Wed, 07 Dec 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Scroll position jumps to the origin when scrolling without momentum at the end of a scroll snapping container
https://bugs.webkit.org/show_bug.cgi?id=165474
&lt;rdar://problem/29534305&gt;

Reviewed by Simon Fraser.

Source/WebCore:

When initializing an AppKit _NSScrollingMomentumCalculator, if the initial and target positions are the same and
the initial velocity is (0, 0), the momentum calculator will output (0, 0) as the animated scroll position when
animating. This causes the scroll position to jump to the top left in some cases when scrolling in scroll snap
containers. To fix this, we teach the ScrollingMomentumCalculatorMac to return an animation duration of 0 and
an animated scroll position equal to the final scroll position when this is the case.

Test: tiled-drawing/scrolling/scroll-snap/scrolling-jumps-to-top.html

* page/scrolling/mac/ScrollingMomentumCalculatorMac.h:
* page/scrolling/mac/ScrollingMomentumCalculatorMac.mm:
(WebCore::ScrollingMomentumCalculatorMac::ScrollingMomentumCalculatorMac):
(WebCore::ScrollingMomentumCalculatorMac::scrollOffsetAfterElapsedTime):
(WebCore::ScrollingMomentumCalculatorMac::animationDuration):

LayoutTests:

Added a new test verifying that if a scroll gesture ends without momentum at the bottom of a scroll snapping
container, the scroll position won't jump to the top.

* tiled-drawing/scrolling/scroll-snap/scrolling-jumps-to-top-expected.txt: Added.
* tiled-drawing/scrolling/scroll-snap/scrolling-jumps-to-top.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorepagescrollingmacScrollingMomentumCalculatorMach">trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h</a></li>
<li><a href="#trunkSourceWebCorepagescrollingmacScrollingMomentumCalculatorMacmm">trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.mm</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapscrollingjumpstotophtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapscrollingjumpstotoptxt">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.txt</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (209476 => 209477)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-12-07 21:43:58 UTC (rev 209476)
+++ trunk/LayoutTests/ChangeLog        2016-12-07 21:50:11 UTC (rev 209477)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2016-12-07  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Scroll position jumps to the origin when scrolling without momentum at the end of a scroll snapping container
+        https://bugs.webkit.org/show_bug.cgi?id=165474
+        &lt;rdar://problem/29534305&gt;
+
+        Reviewed by Simon Fraser.
+
+        Added a new test verifying that if a scroll gesture ends without momentum at the bottom of a scroll snapping
+        container, the scroll position won't jump to the top.
+
+        * tiled-drawing/scrolling/scroll-snap/scrolling-jumps-to-top-expected.txt: Added.
+        * tiled-drawing/scrolling/scroll-snap/scrolling-jumps-to-top.html: Added.
+
</ins><span class="cx"> 2016-12-07  Simon Fraser  &lt;simon.fraser@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         REGRESSION (r209447): LayoutTests compositing/layer-creation/fixed-position-out-of-view-scaled.html and compositing/layer-creation/fixed-position-out-of-view-scaled-scroll.html failing
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapscrollingjumpstotophtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.html (0 => 209477)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.html                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.html        2016-12-07 21:50:11 UTC (rev 209477)
</span><span class="lines">@@ -0,0 +1,68 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;style&gt;
+            body {
+                overflow: hidden;
+                margin: 0;
+            }
+
+            #container {
+                width: 300px;
+                height: 300px;
+                display: inline-block;
+                overflow-x: hidden;
+                overflow-y: auto;
+                scroll-snap-type: y mandatory;
+                -webkit-scroll-snap-type: mandatory;
+            }
+
+            .child {
+                height: 300px;
+                width: 300px;
+                float: left;
+                scroll-snap-align: start;
+                -webkit-scroll-snap-coordinate: 0 0;
+            }
+        &lt;/style&gt;
+        &lt;script&gt;
+        let write = s =&gt; output.innerHTML += s + &quot;&lt;br&gt;&quot;;
+        function run() {
+            container.scrollTop = 300;
+            write(`The scroll position is: ${container.scrollTop}`);
+            if (!window.eventSender || !window.testRunner)
+                return;
+
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+            eventSender.monitorWheelEvents();
+            eventSender.mouseMoveTo(container.offsetLeft + container.clientWidth / 2, container.offsetTop + container.clientHeight / 2);
+
+            write(&quot;Scrolling without momentum to the same position several times&quot;)
+            for (let i = 0; i &lt; 10; i++) {
+                eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, &quot;began&quot;, &quot;none&quot;);
+                eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 1, &quot;changed&quot;, &quot;none&quot;);
+                eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, &quot;changed&quot;, &quot;none&quot;);
+                eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, &quot;changed&quot;, &quot;none&quot;);
+                eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, &quot;ended&quot;, &quot;none&quot;);
+            }
+
+            setTimeout(() =&gt; {
+                eventSender.callAfterScrollingCompletes(() =&gt; {
+                    write(`The scroll position is now: ${container.scrollTop}`);
+                    testRunner.notifyDone();
+                });
+            }, 0);
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+    &lt;body onload=run()&gt;
+        &lt;p&gt;To manually test, scroll so the green box exactly fills the container, and let go.&lt;/p&gt;
+        &lt;p&gt;The scroll position should not jump to the top.&lt;/p&gt;
+        &lt;div id=&quot;container&quot;&gt;
+            &lt;div style=&quot;background-color: red;&quot; class=&quot;child&quot;&gt;&lt;/div&gt;
+            &lt;div style=&quot;background-color: green;&quot; class=&quot;child&quot;&gt;&lt;/div&gt;
+        &lt;/div&gt;
+        &lt;div id=&quot;output&quot;&gt;&lt;/div&gt;
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapscrollingjumpstotoptxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.txt (0 => 209477)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.txt                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.txt        2016-12-07 21:50:11 UTC (rev 209477)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+To manually test, scroll so the green box exactly fills the container, and let go.
+
+The scroll position should not jump to the top.
+
+The scroll position is: 300
+Scrolling without momentum to the same position several times
+The scroll position is now: 300
+
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (209476 => 209477)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-12-07 21:43:58 UTC (rev 209476)
+++ trunk/Source/WebCore/ChangeLog        2016-12-07 21:50:11 UTC (rev 209477)
</span><span class="lines">@@ -1,3 +1,25 @@
</span><ins>+2016-12-07  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Scroll position jumps to the origin when scrolling without momentum at the end of a scroll snapping container
+        https://bugs.webkit.org/show_bug.cgi?id=165474
+        &lt;rdar://problem/29534305&gt;
+
+        Reviewed by Simon Fraser.
+
+        When initializing an AppKit _NSScrollingMomentumCalculator, if the initial and target positions are the same and
+        the initial velocity is (0, 0), the momentum calculator will output (0, 0) as the animated scroll position when
+        animating. This causes the scroll position to jump to the top left in some cases when scrolling in scroll snap
+        containers. To fix this, we teach the ScrollingMomentumCalculatorMac to return an animation duration of 0 and
+        an animated scroll position equal to the final scroll position when this is the case.
+
+        Test: tiled-drawing/scrolling/scroll-snap/scrolling-jumps-to-top.html
+
+        * page/scrolling/mac/ScrollingMomentumCalculatorMac.h:
+        * page/scrolling/mac/ScrollingMomentumCalculatorMac.mm:
+        (WebCore::ScrollingMomentumCalculatorMac::ScrollingMomentumCalculatorMac):
+        (WebCore::ScrollingMomentumCalculatorMac::scrollOffsetAfterElapsedTime):
+        (WebCore::ScrollingMomentumCalculatorMac::animationDuration):
+
</ins><span class="cx"> 2016-12-07  Nan Wang  &lt;n_wang@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         AX: menu type toolbar should be mapped correctly on Mac
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingmacScrollingMomentumCalculatorMach"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h (209476 => 209477)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h        2016-12-07 21:43:58 UTC (rev 209476)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h        2016-12-07 21:50:11 UTC (rev 209477)
</span><span class="lines">@@ -44,6 +44,7 @@
</span><span class="cx">     _NSScrollingMomentumCalculator *ensurePlatformMomentumCalculator();
</span><span class="cx"> 
</span><span class="cx">     RetainPtr&lt;_NSScrollingMomentumCalculator&gt; m_platformMomentumCalculator;
</span><ins>+    bool m_requiresMomentumScrolling { true };
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingmacScrollingMomentumCalculatorMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.mm (209476 => 209477)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.mm        2016-12-07 21:43:58 UTC (rev 209476)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.mm        2016-12-07 21:50:11 UTC (rev 209477)
</span><span class="lines">@@ -39,16 +39,23 @@
</span><span class="cx"> 
</span><span class="cx"> ScrollingMomentumCalculatorMac::ScrollingMomentumCalculatorMac(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity)
</span><span class="cx">     : ScrollingMomentumCalculator(viewportSize, contentSize, initialOffset, targetOffset, initialDelta, initialVelocity)
</span><ins>+    , m_requiresMomentumScrolling(initialOffset != targetOffset || initialVelocity.area())
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> FloatPoint ScrollingMomentumCalculatorMac::scrollOffsetAfterElapsedTime(Seconds elapsedTime)
</span><span class="cx"> {
</span><ins>+    if (!m_requiresMomentumScrolling)
+        return { m_targetScrollOffset.width(), m_targetScrollOffset.height() };
+
</ins><span class="cx">     return [ensurePlatformMomentumCalculator() positionAfterDuration:elapsedTime.value()];
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> Seconds ScrollingMomentumCalculatorMac::animationDuration()
</span><span class="cx"> {
</span><ins>+    if (!m_requiresMomentumScrolling)
+        return 0_s;
+
</ins><span class="cx">     return Seconds([ensurePlatformMomentumCalculator() durationUntilStop]);
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>