<!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>[209070] 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/209070">209070</a></dd>
<dt>Author</dt> <dd>wenson_hsieh@apple.com</dd>
<dt>Date</dt> <dd>2016-11-29 08:10:55 -0800 (Tue, 29 Nov 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Scroll snapping on Mac should use AppKit animations
https://bugs.webkit.org/show_bug.cgi?id=147261
&lt;rdar://problem/29395293&gt;

Reviewed by Brent Fulgham.

Source/WebCore:

Refactors the scroll snapping animation logic to support arbitrary scrolling momentum calculators and
introduces ScrollingMomentumCalculatorMac, which wraps AppKit's _NSScrollingMomentumCalculator. On macOS El
Capitan and later, we use the platform scrolling momentum calculator and for all other cases, we fall back to
the preexissting platform-invariant momentum calculator.

Previously, the scroll snapping animation logic was shared between the ScrollSnapAnimatorState and
ScrollController -- namely, the ScrollController would update various parameters of the ScrollSnapAnimatorState
and then tell it to compute animation-specific constants and coefficients. After this patch, ScrollController
will no longer directly set the ScrollSnapAnimatorState's member variables. Instead, it will tell the animator
state to transition to a new ScrollSnapState with the necessary parameters, and the ScrollSnapAnimatorState is
responsible for modifying itself accordingly. Furthermore, logic pertaining to computing animated scroll offsets
is now split out into a new ScrollingMomentumCalculator, which may have different platform-dependent
implementations. The correct calculator is initialized via ScrollingMomentumCalculator::create, which currently
returns a ScrollingMomentumCalculatorMac on El Capitan and later, and a BasicScrollingMomentumCalculator
otherwise.

The new abstracted ScrollingMomentumCalculator is initialized with various parameters describing the scrolled
content and viewport, as well as the initial and target scrolling offsets. The momentum calculator is then able
to compute the animated scroll offset at any given elapsed time, as well as the total duration of the snapping
animation. The ScrollController's scroll snap timer uses this information (via the ScrollSnapAnimatorState) to
animate its client's scroll offset during a snap or glide.

Also reenables 8 failing and/or flaky scroll snapping tests and adds a new layout test. This patch addresses
two causes for failures and flakiness in these scroll snapping tests:

1.  When starting or stopping the scroll snap animation timer, we call deferTestsForReason and
    removeTestDeferralForReason, respectively. These were actually noops for the first simulated scroll gesture
    on each of the failing mainframe scrolling tests due to m_expectsWheelEventTestTrigger being false. This
    member variable is updated when AsyncScrollingCoordinator::frameViewLayoutUpdated is invoked, wherein we
    call ScrollingStateFrameScrollingNode::setExpectsWheelEventTestTrigger(true) when the test has started
    monitoring wheel events. However, if this does not happen before scrolling begins in the test (which is the
    case here), then the mainframe scrolling node will not expect a wheel event test trigger even though
    eventSender.monitorWheelEvents() has been called. To fix this, we simply make the Page trigger a layout of
    the main FrameView when first ensuring the wheel event test trigger on the Page.

2.  The second reason for flakiness affects both overflow and mainframe scrolling. Previously, due to the way
    we would wait for multiple momentum scroll events before starting to glide, we would end up starting the
    scroll snap timer for a snapping animation, stopping it, and then starting it again for the glide animation.
    Thus, if the wheel event test trigger's timer fires right after the scroll snap timer stops and before it
    starts again due to a glide animation, it will erroneously think that scroll snapping is complete, even
    though it's only just about to begin! Now that we know scrolling velocity when we receive the initial
    &quot;momentum begin&quot;, we now directly transition the scroll snap state from a snapping state to a gliding state
    and no longer stop and start the timer during this transition, which means that the test trigger will be
    deferred for at least the entire duration of the scroll snapping animation (starting right after the first
    &quot;drag end&quot; wheel event).

Test: tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-vertical-then-horizontal.html

* WebCore.xcodeproj/project.pbxproj:
* page/EventHandler.cpp:
(WebCore::handleWheelEventInAppropriateEnclosingBox):
(WebCore::EventHandler::defaultWheelEventHandler):
* page/Page.cpp:
(WebCore::Page::ensureTestTrigger):

Addresses test failures by forcing the mainframe scrolling node to expect wheel event test triggers.

* page/WheelEventDeltaFilter.cpp:
(WebCore::WheelEventDeltaFilter::create):
(WebCore::WheelEventDeltaFilter::filteredVelocity):
* page/WheelEventDeltaFilter.h:
* page/mac/WheelEventDeltaFilterMac.mm:
(WebCore::WheelEventDeltaFilterMac::updateFromDelta):

Add support for plumbing filtered scrolling velocity over to the ScrollController.

* page/scrolling/ScrollingMomentumCalculator.cpp: Copied from Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm.
(WebCore::ScrollingMomentumCalculator::ScrollingMomentumCalculator):
(WebCore::ScrollingMomentumCalculator::create):

Creates a platform-independent BasicScrollingMomentumCalculator.

(WebCore::BasicScrollingMomentumCalculator::BasicScrollingMomentumCalculator):
(WebCore::BasicScrollingMomentumCalculator::linearlyInterpolatedOffsetAtProgress):
(WebCore::BasicScrollingMomentumCalculator::cubicallyInterpolatedOffsetAtProgress):
(WebCore::BasicScrollingMomentumCalculator::scrollOffsetAfterElapsedTime):
(WebCore::BasicScrollingMomentumCalculator::animationDuration):
(WebCore::BasicScrollingMomentumCalculator::initializeInterpolationCoefficientsIfNecessary):
(WebCore::BasicScrollingMomentumCalculator::initializeSnapProgressCurve):
(WebCore::BasicScrollingMomentumCalculator::animationProgressAfterElapsedTime):

Interpolation logic ported over from ScrollSnapAnimatorState.

* page/scrolling/ScrollingMomentumCalculator.h: Added.
(WebCore::ScrollingMomentumCalculator::~ScrollingMomentumCalculator):
* page/scrolling/mac/ScrollingMomentumCalculatorMac.h: Copied from Source/WebCore/page/WheelEventDeltaFilter.h.
* page/scrolling/mac/ScrollingMomentumCalculatorMac.mm: Added.
(WebCore::ScrollingMomentumCalculator::create):

Creates a ScrollingMomentumCalculatorMac.

(WebCore::ScrollingMomentumCalculatorMac::ScrollingMomentumCalculatorMac):
(WebCore::ScrollingMomentumCalculatorMac::scrollOffsetAfterElapsedTime):
(WebCore::ScrollingMomentumCalculatorMac::animationDuration):
(WebCore::ScrollingMomentumCalculatorMac::ensurePlatformMomentumCalculator):
* page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h:
* page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm:
(WebCore::ScrollingTreeFrameScrollingNodeMac::scrollOffset):
(WebCore::ScrollingTreeFrameScrollingNodeMac::viewportSize):
(WebCore::ScrollingTreeFrameScrollingNodeMac::scrollOffsetOnAxis): Deleted.
* platform/PlatformWheelEvent.h:
(WebCore::PlatformWheelEvent::copyWithDeltasAndVelocity):
(WebCore::PlatformWheelEvent::scrollingVelocity):
(WebCore::PlatformWheelEvent::copyWithDeltas): Deleted.
* platform/ScrollAnimator.cpp:
(WebCore::ScrollAnimator::scrollOffset):
(WebCore::ScrollAnimator::viewportSize):
(WebCore::ScrollAnimator::scrollOffsetOnAxis): Deleted.
* platform/ScrollAnimator.h:
* platform/cocoa/ScrollController.h:
* platform/cocoa/ScrollController.mm:
(WebCore::otherScrollEventAxis):
(WebCore::ScrollController::ScrollController):
(WebCore::ScrollController::shouldOverrideInertialScrolling):
(WebCore::ScrollController::scheduleStatelessScrollSnap):
(WebCore::ScrollController::statelessSnapTransitionTimerFired):
(WebCore::ScrollController::startDeferringTestsDueToScrollSnapping):
(WebCore::ScrollController::stopDeferringTestsDueToScrollSnapping):
(WebCore::ScrollController::processWheelEventForScrollSnap):
(WebCore::ScrollController::updateScrollSnapState):
(WebCore::ScrollController::updateScrollSnapPoints):

Update the ScrollController's ScrollSnapAnimationState for both vertical and horizontal axes. If both axes lack
any snap points, the pointer to the animation state will be nulled out; otherwise, the animation state will
exist.

(WebCore::ScrollController::startScrollSnapTimer):
(WebCore::ScrollController::stopScrollSnapTimer):
(WebCore::ScrollController::scrollSnapTimerFired):
(WebCore::ScrollController::activeScrollSnapIndexForAxis):
(WebCore::ScrollController::setActiveScrollSnapIndexForAxis):
(WebCore::ScrollController::setNearestScrollSnapIndexForAxisAndOffset):
(WebCore::ScrollController::setActiveScrollSnapIndicesForOffset):
(WebCore::ScrollController::scrollSnapPointState): Deleted.
(WebCore::ScrollController::processWheelEventForScrollSnapOnAxis): Deleted.
(WebCore::ScrollController::shouldOverrideWheelEvent): Deleted.
(WebCore::projectedInertialScrollDistance): Deleted.
(WebCore::ScrollController::beginScrollSnapAnimation): Deleted.
(WebCore::ScrollController::endScrollSnapAnimation): Deleted.
(WebCore::ScrollController::initializeScrollSnapAnimationParameters): Deleted.
(WebCore::ScrollController::isSnappingOnAxis): Deleted.
* platform/cocoa/ScrollSnapAnimatorState.h:
(WebCore::ScrollSnapAnimatorState::snapOffsetsForAxis):
(WebCore::ScrollSnapAnimatorState::setSnapOffsetsForAxis):
(WebCore::ScrollSnapAnimatorState::currentState):
(WebCore::ScrollSnapAnimatorState::activeSnapIndexForAxis):
(WebCore::ScrollSnapAnimatorState::setActiveSnapIndexForAxis):
* platform/cocoa/ScrollSnapAnimatorState.mm:
(WebCore::projectedInertialScrollDistance):
(WebCore::ScrollSnapAnimatorState::transitionToSnapAnimationState):
(WebCore::ScrollSnapAnimatorState::transitionToGlideAnimationState):
(WebCore::ScrollSnapAnimatorState::transitionToUserInteractionState):
(WebCore::ScrollSnapAnimatorState::transitionToDestinationReachedState):

These methods are used to update the ScrollSnapAnimationState. These state transitions should (and do)
encapsulate all changes that need to be made to the animation state; in other words, the ScrollController should
no longer be reaching directly into the ScrollSnapAnimatorState to change member variables.

(WebCore::ScrollSnapAnimatorState::setupAnimationForState):
(WebCore::ScrollSnapAnimatorState::teardownAnimationForState):
(WebCore::ScrollSnapAnimatorState::currentAnimatedScrollOffset):
(WebCore::ScrollSnapAnimatorState::targetOffsetForStartOffset):
(WebCore::ScrollSnapAnimatorState::ScrollSnapAnimatorState): Deleted.
(WebCore::ScrollSnapAnimatorState::pushInitialWheelDelta): Deleted.
(WebCore::ScrollSnapAnimatorState::averageInitialWheelDelta): Deleted.
(WebCore::ScrollSnapAnimatorState::clearInitialWheelDeltaWindow): Deleted.
(WebCore::ScrollSnapAnimatorState::isSnapping): Deleted.
(WebCore::ScrollSnapAnimatorState::canReachTargetWithCurrentInitialScrollDelta): Deleted.
(WebCore::ScrollSnapAnimatorState::wheelDeltaTrackingIsInProgress): Deleted.
(WebCore::ScrollSnapAnimatorState::hasFinishedTrackingWheelDeltas): Deleted.
(WebCore::ScrollSnapAnimatorState::interpolatedOffsetAtProgress): Deleted.
(WebCore::ScrollSnapAnimationCurveState::initializeSnapProgressCurve): Deleted.
(WebCore::ScrollSnapAnimationCurveState::initializeInterpolationCoefficientsIfNecessary): Deleted.
(WebCore::ScrollSnapAnimationCurveState::interpolatedPositionAtProgress): Deleted.
(WebCore::ScrollSnapAnimationCurveState::shouldCompleteSnapAnimationImmediatelyAtTime): Deleted.
(WebCore::ScrollSnapAnimationCurveState::animationProgressAtTime): Deleted.

The ScrollSnapAnimatorState now tracks state across both axes. This simplifies coordinating scroll snapping in
both horizontal and vertical axes and fixes the issue of the scroll offset not snapping when performing a scroll
in one direction without momentum, then scrolling with momentum in the other direction in a single gesture.

* platform/spi/mac/NSScrollingMomentumCalculatorSPI.h: Added.

Source/WebKit2:

Add some logic to plumb filtered wheel velocity over to WebCore in the case of mainframe scrolling. See
WebCore/ChangeLog for more details.

* WebProcess/WebPage/EventDispatcher.cpp:
(WebKit::EventDispatcher::wheelEvent):

Source/WTF:

Introduce HAVE(NSSCROLLING_FILTERS), which is on for macOS El Capitan and later.

* wtf/Platform.h:

LayoutTests:

Fixes 8 previously failing scroll snapping tests in the tiled-drawing/scrolling/scroll-snap directory and
removes them from TestExpectations. Also adds a new layout test. See WebCore/ChangeLog for more details.

* platform/mac-wk2/TestExpectations:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow-expected.txt:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-horizontal.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-vertical.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal-expected.txt: Added.
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html: Added.
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsplatformmacwk2TestExpectations">trunk/LayoutTests/platform/mac-wk2/TestExpectations</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatory2doverflowexpectedtxt">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow-expected.txt</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorybordersexpectedtxt">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeslowhorizontalhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-horizontal.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeslowverticalhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-vertical.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeverticalhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html</a></li>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFwtfPlatformh">trunk/Source/WTF/wtf/Platform.h</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceWebCorepageEventHandlercpp">trunk/Source/WebCore/page/EventHandler.cpp</a></li>
<li><a href="#trunkSourceWebCorepagePagecpp">trunk/Source/WebCore/page/Page.cpp</a></li>
<li><a href="#trunkSourceWebCorepageWheelEventDeltaFiltercpp">trunk/Source/WebCore/page/WheelEventDeltaFilter.cpp</a></li>
<li><a href="#trunkSourceWebCorepageWheelEventDeltaFilterh">trunk/Source/WebCore/page/WheelEventDeltaFilter.h</a></li>
<li><a href="#trunkSourceWebCorepagemacWheelEventDeltaFilterMacmm">trunk/Source/WebCore/page/mac/WheelEventDeltaFilterMac.mm</a></li>
<li><a href="#trunkSourceWebCorepagescrollingmacScrollingTreeFrameScrollingNodeMach">trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h</a></li>
<li><a href="#trunkSourceWebCorepagescrollingmacScrollingTreeFrameScrollingNodeMacmm">trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformPlatformWheelEventh">trunk/Source/WebCore/platform/PlatformWheelEvent.h</a></li>
<li><a href="#trunkSourceWebCoreplatformScrollAnimatorcpp">trunk/Source/WebCore/platform/ScrollAnimator.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformScrollAnimatorh">trunk/Source/WebCore/platform/ScrollAnimator.h</a></li>
<li><a href="#trunkSourceWebCoreplatformcocoaScrollControllerh">trunk/Source/WebCore/platform/cocoa/ScrollController.h</a></li>
<li><a href="#trunkSourceWebCoreplatformcocoaScrollControllermm">trunk/Source/WebCore/platform/cocoa/ScrollController.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformcocoaScrollSnapAnimatorStateh">trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h</a></li>
<li><a href="#trunkSourceWebCoreplatformcocoaScrollSnapAnimatorStatemm">trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm</a></li>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPageEventDispatchercpp">trunk/Source/WebKit2/WebProcess/WebPage/EventDispatcher.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeverticalthenhorizontalexpectedtxt">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal-expected.txt</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeverticalthenhorizontalhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html</a></li>
<li><a href="#trunkSourceWebCorepagescrollingScrollingMomentumCalculatorcpp">trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp</a></li>
<li><a href="#trunkSourceWebCorepagescrollingScrollingMomentumCalculatorh">trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h</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>
<li><a href="#trunkSourceWebCoreplatformspimacNSScrollingMomentumCalculatorSPIh">trunk/Source/WebCore/platform/spi/mac/NSScrollingMomentumCalculatorSPI.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/LayoutTests/ChangeLog        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -1,3 +1,23 @@
</span><ins>+2016-11-29  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Scroll snapping on Mac should use AppKit animations
+        https://bugs.webkit.org/show_bug.cgi?id=147261
+        &lt;rdar://problem/29395293&gt;
+
+        Reviewed by Brent Fulgham.
+
+        Fixes 8 previously failing scroll snapping tests in the tiled-drawing/scrolling/scroll-snap directory and
+        removes them from TestExpectations. Also adds a new layout test. See WebCore/ChangeLog for more details.
+
+        * platform/mac-wk2/TestExpectations:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow-expected.txt:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-horizontal.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-vertical.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal-expected.txt: Added.
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html: Added.
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html:
+
</ins><span class="cx"> 2016-11-29  Brady Eidson  &lt;beidson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         IndexedDB 2.0: Queue up completed requests in the client, handle them one by one.
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmacwk2TestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac-wk2/TestExpectations (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac-wk2/TestExpectations        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/LayoutTests/platform/mac-wk2/TestExpectations        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -264,19 +264,9 @@
</span><span class="cx"> webkit.org/b/139901 tiled-drawing/scrolling/frames/frameset-frame-scrollability.html [ Pass Failure ]
</span><span class="cx"> 
</span><span class="cx"> webkit.org/b/162505 tiled-drawing/scrolling/latched-div-with-scroll-snap.html [ Pass Failure ]
</span><del>-
-webkit.org/b/148405 tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow.html [ Pass Failure ]
-
-webkit.org/b/148407 tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-horizontal.html [ Pass Failure ]
</del><span class="cx"> webkit.org/b/148407 tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-horizontal.html [ Pass Failure ]
</span><span class="cx"> webkit.org/b/148407 tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-vertical.html [ Pass Failure ]
</span><del>-webkit.org/b/148407 tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html [ Pass Failure ]
</del><span class="cx"> 
</span><del>-webkit.org/b/148408 tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html [ Pass Failure ]
-webkit.org/b/148408 tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html [ Pass Failure ]
-webkit.org/b/148408 tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html [ Pass Failure ]
-webkit.org/b/148408 tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html [ Pass Failure ]
-webkit.org/b/148408 tiled-drawing/scrolling/scroll-snap/scroll-snap-iframe.html [ Pass Failure ]
</del><span class="cx"> webkit.org/b/148408 tiled-drawing/scrolling/root-overflow-with-mousewheel.html [ Pass Failure Timeout ]
</span><span class="cx"> 
</span><span class="cx"> webkit.org/b/139820 fast/frames/lots-of-objects.html [ Timeout ]
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatory2doverflowexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow-expected.txt (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow-expected.txt        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow-expected.txt        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -1,6 +1,6 @@
</span><span class="cx"> PASS div successfully scrolled diagonally.
</span><span class="cx"> PASS div successfully snapped diagonally.
</span><del>-FAIL div did not honor 2D snap points. (single axis scroll followed by flick on other axis)
</del><ins>+PASS div successfully snapped after dragging along one axis and then scrolling in the other.
</ins><span class="cx"> PASS successfullyParsed is true
</span><span class="cx"> 
</span><span class="cx"> TEST COMPLETE
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorybordersexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders-expected.txt        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -4,9 +4,9 @@
</span><span class="cx"> Testing scroll-snap snap for horizontalTarget:
</span><span class="cx"> PASS div honored snap points.
</span><span class="cx"> Testing scroll-snap glide for verticalTarget:
</span><del>-FAIL div did not honor snap points. Expected 300, but got 50
</del><ins>+PASS div scrolled to next window.
</ins><span class="cx"> Testing scroll-snap snap for verticalTarget:
</span><del>-FAIL div did not snap back to proper location for verticalTarget. Expected 50, but got 0
</del><ins>+PASS div honored snap points.
</ins><span class="cx"> PASS successfullyParsed is true
</span><span class="cx"> 
</span><span class="cx"> TEST COMPLETE
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeslowhorizontalhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-horizontal.html (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-horizontal.html        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-horizontal.html        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -33,7 +33,7 @@
</span><span class="cx">         function checkForScrollSnap() {
</span><span class="cx">             // The div should have snapped back to the previous position
</span><span class="cx">             if (divTarget.scrollLeft != divScrollPositionBeforeSnap)
</span><del>-                testFailed(&quot;div did not snap back to proper location.&quot;);
</del><ins>+                testFailed(`div did not snap back to proper location. ${divTarget.scrollLeft} vs. ${divScrollPositionBeforeSnap}`);
</ins><span class="cx">             else
</span><span class="cx">                 testPassed(&quot;div honored snap points.&quot;);
</span><span class="cx"> 
</span><span class="lines">@@ -63,7 +63,7 @@
</span><span class="cx">             if (divTarget.scrollLeft == window.innerWidth)
</span><span class="cx">                 testPassed(&quot;div scrolled to next window.&quot;);
</span><span class="cx">             else
</span><del>-                testFailed(&quot;div did not honor snap points.&quot;);
</del><ins>+                testFailed(`div did not honor snap points. ${divTarget.scrollLeft} vs. ${window.innerWidth}`);
</ins><span class="cx"> 
</span><span class="cx">             setTimeout(scrollSnapTest, 0);
</span><span class="cx">         }
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeslowverticalhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-vertical.html (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-vertical.html        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-vertical.html        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -33,7 +33,7 @@
</span><span class="cx">         function checkForScrollSnap() {
</span><span class="cx">             // The div should have snapped back to the previous position
</span><span class="cx">             if (divTarget.scrollTop != divScrollPositionBeforeSnap)
</span><del>-                testFailed(&quot;div did not snap back to proper location.&quot;);
</del><ins>+                testFailed(`div did not snap back to proper location. (${divTarget.scrollTop} vs. ${divScrollPositionBeforeSnap})`);
</ins><span class="cx">             else
</span><span class="cx">                 testPassed(&quot;div honored snap points.&quot;);
</span><span class="cx"> 
</span><span class="lines">@@ -63,7 +63,7 @@
</span><span class="cx">             if (divTarget.scrollTop == window.innerHeight)
</span><span class="cx">                 testPassed(&quot;div scrolled to next window.&quot;);
</span><span class="cx">             else
</span><del>-                testFailed(&quot;div did not honor snap points.&quot;);
</del><ins>+                testFailed(`div did not honor snap points. (${divTarget.scrollTop} vs. ${window.innerHeight})`);
</ins><span class="cx"> 
</span><span class="cx">             setTimeout(scrollSnapTest, 0);
</span><span class="cx">         }
</span><span class="lines">@@ -88,7 +88,6 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         function onLoad() {
</span><del>-
</del><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><span class="cx">                 setTimeout(scrollGlideTest, 0);
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeverticalthenhorizontalexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal-expected.txt (0 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal-expected.txt                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal-expected.txt        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -0,0 +1,5 @@
</span><ins>+PASS scroll offset snapped back to top.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeverticalthenhorizontalhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html (0 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -0,0 +1,75 @@
</span><ins>+&lt;!DOCTYPE HTML&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;style&gt;
+            .verticalGallery {
+                width: 100vw;
+                height: 600vh;
+                margin: 0;
+                padding: 0;
+                -webkit-scroll-snap-points-y: repeat(100vh);
+                -webkit-scroll-snap-type: mandatory;
+            }
+            .colorBox {
+                height: 100vh;
+                width: 100vw;
+                float: left;
+            }
+            #item0 { background-color: red; }
+            #item1 { background-color: green; }
+            #item2 { background-color: blue; }
+            #item3 { background-color: aqua; }
+            #item4 { background-color: yellow; }
+            #item5 { background-color: fuchsia; }
+        &lt;/style&gt;
+        &lt;script src=&quot;../../../resources/js-test.js&quot;&gt;&lt;/script&gt;
+        &lt;script&gt;
+        window.jsTestIsAsync = true;
+
+        function scrollSnapTest() {
+            var startPosX = document.body.offsetLeft + 20;
+            var startPosY = document.body.offsetTop + 20;
+            eventSender.mouseMoveTo(startPosX, startPosY);
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'began', 'none');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'changed', 'none');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(-1, 0, 'changed', 'none');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(-1, 0, 'changed', 'none');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(-1, 0, 'changed', 'none');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(-1, 0, 'none', 'begin');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(-4, 0, 'none', 'continue');
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end');
+            eventSender.callAfterScrollingCompletes(() =&gt; {
+                if (document.body.scrollTop == 0)
+                    testPassed(&quot;scroll offset snapped back to top.&quot;);
+                else
+                    testFailed(`did not honor snap points (expected 0, observed ${document.body.scrollTop}).`);
+
+                finishJSTest();
+            });
+        }
+
+        function onLoad() {
+            if (window.eventSender) {
+                eventSender.monitorWheelEvents();
+                setTimeout(scrollSnapTest, 0);
+            } else {
+                var messageLocation = document.getElementById('item0');
+                var message = document.createElement('div');
+                message.innerHTML = `&lt;p&gt;This test is better run under DumpRenderTree. To manually test it, scroll down
+                slightly, and then directly to the left or right with momentum without lifting your fingers from the
+                trackpad. The scroll offset should animate to the nearest snap offset.&lt;/p&gt;`;
+                messageLocation.appendChild(message);
+            }
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+    &lt;body onload=&quot;onLoad();&quot; class=&quot;verticalGallery&quot;&gt;
+        &lt;div id=&quot;item0&quot; class=&quot;colorBox&quot;&gt;&lt;div id=&quot;console&quot;&gt;&lt;/div&gt;&lt;/div&gt;
+        &lt;div id=&quot;item1&quot; class=&quot;colorBox&quot;&gt;&lt;/div&gt;
+        &lt;div id=&quot;item2&quot; class=&quot;colorBox&quot;&gt;&lt;/div&gt;
+        &lt;div id=&quot;item3&quot; class=&quot;colorBox&quot;&gt;&lt;/div&gt;
+        &lt;div id=&quot;item4&quot; class=&quot;colorBox&quot;&gt;&lt;/div&gt;
+        &lt;div id=&quot;item5&quot; class=&quot;colorBox&quot;&gt;&lt;/div&gt;
+    &lt;/body&gt;
+&lt;/html&gt;
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeverticalhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -33,7 +33,7 @@
</span><span class="cx">         function checkForScrollSnap() {
</span><span class="cx">             // The div should have snapped back to the previous position
</span><span class="cx">             if (divTarget.scrollTop != divScrollPositionBeforeSnap)
</span><del>-                testFailed(&quot;div did not snap back to proper location.&quot;);
</del><ins>+                testFailed(`div did not snap back to proper location. (${divTarget.scrollTop} vs. ${divScrollPositionBeforeSnap})`);
</ins><span class="cx">             else
</span><span class="cx">                 testPassed(&quot;div honored snap points.&quot;);
</span><span class="cx"> 
</span><span class="lines">@@ -63,7 +63,7 @@
</span><span class="cx">             if (divTarget.scrollTop == window.innerHeight)
</span><span class="cx">                 testPassed(&quot;div scrolled to next window.&quot;);
</span><span class="cx">             else
</span><del>-                testFailed(&quot;div did not honor snap points.&quot;);
</del><ins>+                testFailed(`div did not honor snap points. (${divTarget.scrollTop} vs. ${window.innerHeight})`);
</ins><span class="cx"> 
</span><span class="cx">             setTimeout(scrollSnapTest, 0);
</span><span class="cx">         }
</span></span></pre></div>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WTF/ChangeLog        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2016-11-29  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Scroll snapping on Mac should use AppKit animations
+        https://bugs.webkit.org/show_bug.cgi?id=147261
+        &lt;rdar://problem/29395293&gt;
+
+        Reviewed by Brent Fulgham.
+
+        Introduce HAVE(NSSCROLLING_FILTERS), which is on for macOS El Capitan and later.
+
+        * wtf/Platform.h:
+
</ins><span class="cx"> 2016-11-28  Darin Adler  &lt;darin@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Streamline and speed up tokenizer and segmented string classes
</span></span></pre></div>
<a id="trunkSourceWTFwtfPlatformh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/Platform.h (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/Platform.h        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WTF/wtf/Platform.h        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -551,6 +551,12 @@
</span><span class="cx"> #define USE_PLUGIN_HOST_PROCESS 1
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#if __MAC_OS_X_VERSION_MIN_REQUIRED &gt;= 101100
+#define HAVE_NSSCROLLING_FILTERS 1
+#else
+#define HAVE_NSSCROLLING_FILTERS 0
+#endif
+
</ins><span class="cx"> /* OS X defines a series of platform macros for debugging. */
</span><span class="cx"> /* Some of them are really annoying because they use common names (e.g. check()). */
</span><span class="cx"> /* Disable those macros so that we are not limited in how we name methods and functions. */
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/ChangeLog        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -1,3 +1,194 @@
</span><ins>+2016-11-29  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Scroll snapping on Mac should use AppKit animations
+        https://bugs.webkit.org/show_bug.cgi?id=147261
+        &lt;rdar://problem/29395293&gt;
+
+        Reviewed by Brent Fulgham.
+
+        Refactors the scroll snapping animation logic to support arbitrary scrolling momentum calculators and
+        introduces ScrollingMomentumCalculatorMac, which wraps AppKit's _NSScrollingMomentumCalculator. On macOS El
+        Capitan and later, we use the platform scrolling momentum calculator and for all other cases, we fall back to
+        the preexissting platform-invariant momentum calculator.
+
+        Previously, the scroll snapping animation logic was shared between the ScrollSnapAnimatorState and
+        ScrollController -- namely, the ScrollController would update various parameters of the ScrollSnapAnimatorState
+        and then tell it to compute animation-specific constants and coefficients. After this patch, ScrollController
+        will no longer directly set the ScrollSnapAnimatorState's member variables. Instead, it will tell the animator
+        state to transition to a new ScrollSnapState with the necessary parameters, and the ScrollSnapAnimatorState is
+        responsible for modifying itself accordingly. Furthermore, logic pertaining to computing animated scroll offsets
+        is now split out into a new ScrollingMomentumCalculator, which may have different platform-dependent
+        implementations. The correct calculator is initialized via ScrollingMomentumCalculator::create, which currently
+        returns a ScrollingMomentumCalculatorMac on El Capitan and later, and a BasicScrollingMomentumCalculator
+        otherwise.
+
+        The new abstracted ScrollingMomentumCalculator is initialized with various parameters describing the scrolled
+        content and viewport, as well as the initial and target scrolling offsets. The momentum calculator is then able
+        to compute the animated scroll offset at any given elapsed time, as well as the total duration of the snapping
+        animation. The ScrollController's scroll snap timer uses this information (via the ScrollSnapAnimatorState) to
+        animate its client's scroll offset during a snap or glide.
+
+        Also reenables 8 failing and/or flaky scroll snapping tests and adds a new layout test. This patch addresses
+        two causes for failures and flakiness in these scroll snapping tests:
+
+        1.  When starting or stopping the scroll snap animation timer, we call deferTestsForReason and
+            removeTestDeferralForReason, respectively. These were actually noops for the first simulated scroll gesture
+            on each of the failing mainframe scrolling tests due to m_expectsWheelEventTestTrigger being false. This
+            member variable is updated when AsyncScrollingCoordinator::frameViewLayoutUpdated is invoked, wherein we
+            call ScrollingStateFrameScrollingNode::setExpectsWheelEventTestTrigger(true) when the test has started
+            monitoring wheel events. However, if this does not happen before scrolling begins in the test (which is the
+            case here), then the mainframe scrolling node will not expect a wheel event test trigger even though
+            eventSender.monitorWheelEvents() has been called. To fix this, we simply make the Page trigger a layout of
+            the main FrameView when first ensuring the wheel event test trigger on the Page.
+
+        2.  The second reason for flakiness affects both overflow and mainframe scrolling. Previously, due to the way
+            we would wait for multiple momentum scroll events before starting to glide, we would end up starting the
+            scroll snap timer for a snapping animation, stopping it, and then starting it again for the glide animation.
+            Thus, if the wheel event test trigger's timer fires right after the scroll snap timer stops and before it
+            starts again due to a glide animation, it will erroneously think that scroll snapping is complete, even
+            though it's only just about to begin! Now that we know scrolling velocity when we receive the initial
+            &quot;momentum begin&quot;, we now directly transition the scroll snap state from a snapping state to a gliding state
+            and no longer stop and start the timer during this transition, which means that the test trigger will be
+            deferred for at least the entire duration of the scroll snapping animation (starting right after the first
+            &quot;drag end&quot; wheel event).
+
+        Test: tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-vertical-then-horizontal.html
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * page/EventHandler.cpp:
+        (WebCore::handleWheelEventInAppropriateEnclosingBox):
+        (WebCore::EventHandler::defaultWheelEventHandler):
+        * page/Page.cpp:
+        (WebCore::Page::ensureTestTrigger):
+
+        Addresses test failures by forcing the mainframe scrolling node to expect wheel event test triggers.
+
+        * page/WheelEventDeltaFilter.cpp:
+        (WebCore::WheelEventDeltaFilter::create):
+        (WebCore::WheelEventDeltaFilter::filteredVelocity):
+        * page/WheelEventDeltaFilter.h:
+        * page/mac/WheelEventDeltaFilterMac.mm:
+        (WebCore::WheelEventDeltaFilterMac::updateFromDelta):
+
+        Add support for plumbing filtered scrolling velocity over to the ScrollController.
+
+        * page/scrolling/ScrollingMomentumCalculator.cpp: Copied from Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm.
+        (WebCore::ScrollingMomentumCalculator::ScrollingMomentumCalculator):
+        (WebCore::ScrollingMomentumCalculator::create):
+
+        Creates a platform-independent BasicScrollingMomentumCalculator.
+
+        (WebCore::BasicScrollingMomentumCalculator::BasicScrollingMomentumCalculator):
+        (WebCore::BasicScrollingMomentumCalculator::linearlyInterpolatedOffsetAtProgress):
+        (WebCore::BasicScrollingMomentumCalculator::cubicallyInterpolatedOffsetAtProgress):
+        (WebCore::BasicScrollingMomentumCalculator::scrollOffsetAfterElapsedTime):
+        (WebCore::BasicScrollingMomentumCalculator::animationDuration):
+        (WebCore::BasicScrollingMomentumCalculator::initializeInterpolationCoefficientsIfNecessary):
+        (WebCore::BasicScrollingMomentumCalculator::initializeSnapProgressCurve):
+        (WebCore::BasicScrollingMomentumCalculator::animationProgressAfterElapsedTime):
+
+        Interpolation logic ported over from ScrollSnapAnimatorState.
+
+        * page/scrolling/ScrollingMomentumCalculator.h: Added.
+        (WebCore::ScrollingMomentumCalculator::~ScrollingMomentumCalculator):
+        * page/scrolling/mac/ScrollingMomentumCalculatorMac.h: Copied from Source/WebCore/page/WheelEventDeltaFilter.h.
+        * page/scrolling/mac/ScrollingMomentumCalculatorMac.mm: Added.
+        (WebCore::ScrollingMomentumCalculator::create):
+
+        Creates a ScrollingMomentumCalculatorMac.
+
+        (WebCore::ScrollingMomentumCalculatorMac::ScrollingMomentumCalculatorMac):
+        (WebCore::ScrollingMomentumCalculatorMac::scrollOffsetAfterElapsedTime):
+        (WebCore::ScrollingMomentumCalculatorMac::animationDuration):
+        (WebCore::ScrollingMomentumCalculatorMac::ensurePlatformMomentumCalculator):
+        * page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h:
+        * page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm:
+        (WebCore::ScrollingTreeFrameScrollingNodeMac::scrollOffset):
+        (WebCore::ScrollingTreeFrameScrollingNodeMac::viewportSize):
+        (WebCore::ScrollingTreeFrameScrollingNodeMac::scrollOffsetOnAxis): Deleted.
+        * platform/PlatformWheelEvent.h:
+        (WebCore::PlatformWheelEvent::copyWithDeltasAndVelocity):
+        (WebCore::PlatformWheelEvent::scrollingVelocity):
+        (WebCore::PlatformWheelEvent::copyWithDeltas): Deleted.
+        * platform/ScrollAnimator.cpp:
+        (WebCore::ScrollAnimator::scrollOffset):
+        (WebCore::ScrollAnimator::viewportSize):
+        (WebCore::ScrollAnimator::scrollOffsetOnAxis): Deleted.
+        * platform/ScrollAnimator.h:
+        * platform/cocoa/ScrollController.h:
+        * platform/cocoa/ScrollController.mm:
+        (WebCore::otherScrollEventAxis):
+        (WebCore::ScrollController::ScrollController):
+        (WebCore::ScrollController::shouldOverrideInertialScrolling):
+        (WebCore::ScrollController::scheduleStatelessScrollSnap):
+        (WebCore::ScrollController::statelessSnapTransitionTimerFired):
+        (WebCore::ScrollController::startDeferringTestsDueToScrollSnapping):
+        (WebCore::ScrollController::stopDeferringTestsDueToScrollSnapping):
+        (WebCore::ScrollController::processWheelEventForScrollSnap):
+        (WebCore::ScrollController::updateScrollSnapState):
+        (WebCore::ScrollController::updateScrollSnapPoints):
+
+        Update the ScrollController's ScrollSnapAnimationState for both vertical and horizontal axes. If both axes lack
+        any snap points, the pointer to the animation state will be nulled out; otherwise, the animation state will
+        exist.
+
+        (WebCore::ScrollController::startScrollSnapTimer):
+        (WebCore::ScrollController::stopScrollSnapTimer):
+        (WebCore::ScrollController::scrollSnapTimerFired):
+        (WebCore::ScrollController::activeScrollSnapIndexForAxis):
+        (WebCore::ScrollController::setActiveScrollSnapIndexForAxis):
+        (WebCore::ScrollController::setNearestScrollSnapIndexForAxisAndOffset):
+        (WebCore::ScrollController::setActiveScrollSnapIndicesForOffset):
+        (WebCore::ScrollController::scrollSnapPointState): Deleted.
+        (WebCore::ScrollController::processWheelEventForScrollSnapOnAxis): Deleted.
+        (WebCore::ScrollController::shouldOverrideWheelEvent): Deleted.
+        (WebCore::projectedInertialScrollDistance): Deleted.
+        (WebCore::ScrollController::beginScrollSnapAnimation): Deleted.
+        (WebCore::ScrollController::endScrollSnapAnimation): Deleted.
+        (WebCore::ScrollController::initializeScrollSnapAnimationParameters): Deleted.
+        (WebCore::ScrollController::isSnappingOnAxis): Deleted.
+        * platform/cocoa/ScrollSnapAnimatorState.h:
+        (WebCore::ScrollSnapAnimatorState::snapOffsetsForAxis):
+        (WebCore::ScrollSnapAnimatorState::setSnapOffsetsForAxis):
+        (WebCore::ScrollSnapAnimatorState::currentState):
+        (WebCore::ScrollSnapAnimatorState::activeSnapIndexForAxis):
+        (WebCore::ScrollSnapAnimatorState::setActiveSnapIndexForAxis):
+        * platform/cocoa/ScrollSnapAnimatorState.mm:
+        (WebCore::projectedInertialScrollDistance):
+        (WebCore::ScrollSnapAnimatorState::transitionToSnapAnimationState):
+        (WebCore::ScrollSnapAnimatorState::transitionToGlideAnimationState):
+        (WebCore::ScrollSnapAnimatorState::transitionToUserInteractionState):
+        (WebCore::ScrollSnapAnimatorState::transitionToDestinationReachedState):
+
+        These methods are used to update the ScrollSnapAnimationState. These state transitions should (and do)
+        encapsulate all changes that need to be made to the animation state; in other words, the ScrollController should
+        no longer be reaching directly into the ScrollSnapAnimatorState to change member variables.
+
+        (WebCore::ScrollSnapAnimatorState::setupAnimationForState):
+        (WebCore::ScrollSnapAnimatorState::teardownAnimationForState):
+        (WebCore::ScrollSnapAnimatorState::currentAnimatedScrollOffset):
+        (WebCore::ScrollSnapAnimatorState::targetOffsetForStartOffset):
+        (WebCore::ScrollSnapAnimatorState::ScrollSnapAnimatorState): Deleted.
+        (WebCore::ScrollSnapAnimatorState::pushInitialWheelDelta): Deleted.
+        (WebCore::ScrollSnapAnimatorState::averageInitialWheelDelta): Deleted.
+        (WebCore::ScrollSnapAnimatorState::clearInitialWheelDeltaWindow): Deleted.
+        (WebCore::ScrollSnapAnimatorState::isSnapping): Deleted.
+        (WebCore::ScrollSnapAnimatorState::canReachTargetWithCurrentInitialScrollDelta): Deleted.
+        (WebCore::ScrollSnapAnimatorState::wheelDeltaTrackingIsInProgress): Deleted.
+        (WebCore::ScrollSnapAnimatorState::hasFinishedTrackingWheelDeltas): Deleted.
+        (WebCore::ScrollSnapAnimatorState::interpolatedOffsetAtProgress): Deleted.
+        (WebCore::ScrollSnapAnimationCurveState::initializeSnapProgressCurve): Deleted.
+        (WebCore::ScrollSnapAnimationCurveState::initializeInterpolationCoefficientsIfNecessary): Deleted.
+        (WebCore::ScrollSnapAnimationCurveState::interpolatedPositionAtProgress): Deleted.
+        (WebCore::ScrollSnapAnimationCurveState::shouldCompleteSnapAnimationImmediatelyAtTime): Deleted.
+        (WebCore::ScrollSnapAnimationCurveState::animationProgressAtTime): Deleted.
+
+        The ScrollSnapAnimatorState now tracks state across both axes. This simplifies coordinating scroll snapping in
+        both horizontal and vertical axes and fixes the issue of the scroll offset not snapping when performing a scroll
+        in one direction without momentum, then scrolling with momentum in the other direction in a single gesture.
+
+        * platform/spi/mac/NSScrollingMomentumCalculatorSPI.h: Added.
+
</ins><span class="cx"> 2016-11-29  Brady Eidson  &lt;beidson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         IndexedDB 2.0: Queue up completed requests in the client, handle them one by one.
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -2159,6 +2159,8 @@
</span><span class="cx">                 517A63C61B74319200E7DCDC /* KeyedEncoderCF.h in Headers */ = {isa = PBXBuildFile; fileRef = 517A63C21B74317E00E7DCDC /* KeyedEncoderCF.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 517B25A91CC82B2A0061C011 /* IDBConnectionProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 517B25A71CC820320061C011 /* IDBConnectionProxy.cpp */; };
</span><span class="cx">                 517B25AA1CC82B2A0061C011 /* IDBConnectionProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 517B25A81CC820320061C011 /* IDBConnectionProxy.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+                517DEEE51DE94ADC00B91644 /* ScrollingMomentumCalculatorMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 517DEEE31DE94ADC00B91644 /* ScrollingMomentumCalculatorMac.mm */; };
+                517DEEE81DE94B0800B91644 /* ScrollingMomentumCalculatorMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 517DEEE71DE94B0800B91644 /* ScrollingMomentumCalculatorMac.h */; settings = {ATTRIBUTES = (Private, ); }; };
</ins><span class="cx">                 517FBA1E151AB17C00B57959 /* DOMWindowExtension.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 517FBA17151AA71B00B57959 /* DOMWindowExtension.cpp */; };
</span><span class="cx">                 5185FC741BB4C4E80012898F /* DOMWindowIndexedDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51D7196C181106DF0016DC51 /* DOMWindowIndexedDatabase.cpp */; };
</span><span class="cx">                 5185FC751BB4C4E80012898F /* DOMWindowIndexedDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 51D7196D181106DF0016DC51 /* DOMWindowIndexedDatabase.h */; };
</span><span class="lines">@@ -2257,6 +2259,8 @@
</span><span class="cx">                 51BE37E00DAEE00E001085FC /* StorageArea.h in Headers */ = {isa = PBXBuildFile; fileRef = 51BE37DE0DAEE00E001085FC /* StorageArea.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 51C0AA390F2AA10A001648C2 /* CachedFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 51C0AA380F2AA10A001648C2 /* CachedFrame.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 51C0AA410F2AA15E001648C2 /* CachedFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51C0AA400F2AA15E001648C2 /* CachedFrame.cpp */; };
</span><ins>+                51C61B0A1DE536E7008A212D /* ScrollingMomentumCalculator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51C61B081DE536E7008A212D /* ScrollingMomentumCalculator.cpp */; };
+                51C61B0B1DE536E7008A212D /* ScrollingMomentumCalculator.h in Headers */ = {isa = PBXBuildFile; fileRef = 51C61B091DE536E7008A212D /* ScrollingMomentumCalculator.h */; settings = {ATTRIBUTES = (Private, ); }; };
</ins><span class="cx">                 51C81B890C4422F70019ECE3 /* FTPDirectoryParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51C81B870C4422F70019ECE3 /* FTPDirectoryParser.cpp */; };
</span><span class="cx">                 51C81B8A0C4422F70019ECE3 /* FTPDirectoryParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 51C81B880C4422F70019ECE3 /* FTPDirectoryParser.h */; };
</span><span class="cx">                 51CBFC990D10E483002DBF51 /* CachedFramePlatformData.h in Headers */ = {isa = PBXBuildFile; fileRef = 51CBFC980D10E483002DBF51 /* CachedFramePlatformData.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="lines">@@ -9396,6 +9400,8 @@
</span><span class="cx">                 517A63C21B74317E00E7DCDC /* KeyedEncoderCF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyedEncoderCF.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 517B25A71CC820320061C011 /* IDBConnectionProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDBConnectionProxy.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 517B25A81CC820320061C011 /* IDBConnectionProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IDBConnectionProxy.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                517DEEE31DE94ADC00B91644 /* ScrollingMomentumCalculatorMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollingMomentumCalculatorMac.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
+                517DEEE71DE94B0800B91644 /* ScrollingMomentumCalculatorMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollingMomentumCalculatorMac.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 517FBA17151AA71B00B57959 /* DOMWindowExtension.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMWindowExtension.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 517FBA18151AA71B00B57959 /* DOMWindowExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMWindowExtension.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 5185FCBC1BB5CB770012898F /* IDBConnectionToServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDBConnectionToServer.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -9456,6 +9462,9 @@
</span><span class="cx">                 51BE37DE0DAEE00E001085FC /* StorageArea.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StorageArea.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 51C0AA380F2AA10A001648C2 /* CachedFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedFrame.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 51C0AA400F2AA15E001648C2 /* CachedFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CachedFrame.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                51C61B081DE536E7008A212D /* ScrollingMomentumCalculator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollingMomentumCalculator.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
+                51C61B091DE536E7008A212D /* ScrollingMomentumCalculator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollingMomentumCalculator.h; sourceTree = &quot;&lt;group&gt;&quot;; };
+                51C61B0C1DE5383D008A212D /* NSScrollingMomentumCalculatorSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSScrollingMomentumCalculatorSPI.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 51C81B870C4422F70019ECE3 /* FTPDirectoryParser.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = FTPDirectoryParser.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 51C81B880C4422F70019ECE3 /* FTPDirectoryParser.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = FTPDirectoryParser.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 51CBFC980D10E483002DBF51 /* CachedFramePlatformData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedFramePlatformData.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -15945,6 +15954,8 @@
</span><span class="cx">                                 0FFD4D5F18651FA300512F6E /* AsyncScrollingCoordinator.h */,
</span><span class="cx">                                 F45C231B1995B73B00A6E2E3 /* AxisScrollSnapOffsets.cpp */,
</span><span class="cx">                                 F45C231C1995B73B00A6E2E3 /* AxisScrollSnapOffsets.h */,
</span><ins>+                                51C61B081DE536E7008A212D /* ScrollingMomentumCalculator.cpp */,
+                                51C61B091DE536E7008A212D /* ScrollingMomentumCalculator.h */,
</ins><span class="cx">                                 0F605AEA15F94848004DF0C0 /* ScrollingConstraints.cpp */,
</span><span class="cx">                                 0F605AEB15F94848004DF0C0 /* ScrollingConstraints.h */,
</span><span class="cx">                                 1AF62EE414DA22A70041556C /* ScrollingCoordinator.cpp */,
</span><span class="lines">@@ -15986,6 +15997,8 @@
</span><span class="cx">                 1AF62EE214DA22A70041556C /* mac */ = {
</span><span class="cx">                         isa = PBXGroup;
</span><span class="cx">                         children = (
</span><ins>+                                517DEEE71DE94B0800B91644 /* ScrollingMomentumCalculatorMac.h */,
+                                517DEEE31DE94ADC00B91644 /* ScrollingMomentumCalculatorMac.mm */,
</ins><span class="cx">                                 9391A990162746CB00297330 /* ScrollingCoordinatorMac.h */,
</span><span class="cx">                                 1AF62EE314DA22A70041556C /* ScrollingCoordinatorMac.mm */,
</span><span class="cx">                                 0FA88EBC16A8D1BD00F99984 /* ScrollingStateFrameScrollingNodeMac.mm */,
</span><span class="lines">@@ -18503,6 +18516,7 @@
</span><span class="cx">                                 93F1E1EB1A40FDDC00348D13 /* NSPopoverSPI.h */,
</span><span class="cx">                                 7C0406121C66EE9C00AF0711 /* NSScrollerImpSPI.h */,
</span><span class="cx">                                 F40EA8AA1B867D6500CE5581 /* NSScrollingInputFilterSPI.h */,
</span><ins>+                                51C61B0C1DE5383D008A212D /* NSScrollingMomentumCalculatorSPI.h */,
</ins><span class="cx">                                 2DCB837719F99BBA00A7FBE4 /* NSSharingServicePickerSPI.h */,
</span><span class="cx">                                 2DCB837819F99BBA00A7FBE4 /* NSSharingServiceSPI.h */,
</span><span class="cx">                                 933C7A741C0FBC440034FB97 /* NSSpellCheckerSPI.h */,
</span><span class="lines">@@ -25773,6 +25787,7 @@
</span><span class="cx">                                 5126E6BC0A2E3B12005C29FA /* IconDatabase.h in Headers */,
</span><span class="cx">                                 516953981329A3C800B92D04 /* IconDatabaseBase.h in Headers */,
</span><span class="cx">                                 51E1ECBE0C91C90400DC255B /* IconDatabaseClient.h in Headers */,
</span><ins>+                                517DEEE81DE94B0800B91644 /* ScrollingMomentumCalculatorMac.h in Headers */,
</ins><span class="cx">                                 513F14540AB634C400094DDF /* IconLoader.h in Headers */,
</span><span class="cx">                                 51E1ECC10C91C90400DC255B /* IconRecord.h in Headers */,
</span><span class="cx">                                 51714EA81CF4E4B1004723C4 /* IDBActiveDOMObject.h in Headers */,
</span><span class="lines">@@ -26334,6 +26349,7 @@
</span><span class="cx">                                 9BDA64D81B975CF2009C4387 /* JSShadowRoot.h in Headers */,
</span><span class="cx">                                 46DFF49C1DC2620B00B80B48 /* JSShadowRootMode.h in Headers */,
</span><span class="cx">                                 CD9DE17B17AAC75B00EA386D /* JSSourceBuffer.h in Headers */,
</span><ins>+                                51C61B0B1DE536E7008A212D /* ScrollingMomentumCalculator.h in Headers */,
</ins><span class="cx">                                 CD9DE17D17AAC75B00EA386D /* JSSourceBufferList.h in Headers */,
</span><span class="cx">                                 AA7FEEAD16A4E74B004C0C33 /* JSSpeechSynthesis.h in Headers */,
</span><span class="cx">                                 AA2A5AD216A4860A00976A25 /* JSSpeechSynthesisEvent.h in Headers */,
</span><span class="lines">@@ -29355,6 +29371,7 @@
</span><span class="cx">                                 46C83EFD1A9BBE2900A79A41 /* GeoNotifier.cpp in Sources */,
</span><span class="cx">                                 2D5036681BCDDDC400E20BB3 /* GestureEvents.cpp in Sources */,
</span><span class="cx">                                 B2AFFC830D00A5C10030074D /* GlyphPageMac.cpp in Sources */,
</span><ins>+                                51C61B0A1DE536E7008A212D /* ScrollingMomentumCalculator.cpp in Sources */,
</ins><span class="cx">                                 BC53C6080DA56C570021EB5D /* Gradient.cpp in Sources */,
</span><span class="cx">                                 BC53C60B0DA56CF10021EB5D /* GradientCG.cpp in Sources */,
</span><span class="cx">                                 2D481F03146B5C6500AA7834 /* GradientImage.cpp in Sources */,
</span><span class="lines">@@ -30925,6 +30942,7 @@
</span><span class="cx">                                 AB67D1A8097F3AE300F9392E /* RenderTextControl.cpp in Sources */,
</span><span class="cx">                                 083DAEA60F01A7FB00342754 /* RenderTextControlMultiLine.cpp in Sources */,
</span><span class="cx">                                 083DAEA80F01A7FB00342754 /* RenderTextControlSingleLine.cpp in Sources */,
</span><ins>+                                517DEEE51DE94ADC00B91644 /* ScrollingMomentumCalculatorMac.mm in Sources */,
</ins><span class="cx">                                 BCEA488D097D93020094C9E4 /* RenderTextFragment.cpp in Sources */,
</span><span class="cx">                                 E4C91A18180999FB00A17F6D /* RenderTextLineBoxes.cpp in Sources */,
</span><span class="cx">                                 BCEA4889097D93020094C9E4 /* RenderTheme.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceWebCorepageEventHandlercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/EventHandler.cpp (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/EventHandler.cpp        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/page/EventHandler.cpp        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -303,7 +303,7 @@
</span><span class="cx">     return didHandleWheelEvent;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static inline bool handleWheelEventInAppropriateEnclosingBox(Node* startNode, WheelEvent&amp; wheelEvent, Element** stopElement, const FloatSize&amp; filteredPlatformDelta)
</del><ins>+static inline bool handleWheelEventInAppropriateEnclosingBox(Node* startNode, WheelEvent&amp; wheelEvent, Element** stopElement, const FloatSize&amp; filteredPlatformDelta, const FloatPoint&amp; filteredVelocity)
</ins><span class="cx"> {
</span><span class="cx">     bool shouldHandleEvent = wheelEvent.deltaX() || wheelEvent.deltaY();
</span><span class="cx"> #if PLATFORM(MAC)
</span><span class="lines">@@ -324,9 +324,10 @@
</span><span class="cx">         if (RenderLayer* boxLayer = currentEnclosingBox-&gt;layer()) {
</span><span class="cx">             const PlatformWheelEvent* platformEvent = wheelEvent.wheelEvent();
</span><span class="cx">             bool scrollingWasHandled;
</span><del>-            if (platformEvent != nullptr)
-                scrollingWasHandled = boxLayer-&gt;handleWheelEvent(platformEvent-&gt;copyWithDeltas(filteredPlatformDelta.width(), filteredPlatformDelta.height()));
-            else
</del><ins>+            if (platformEvent != nullptr) {
+                auto copiedEvent = platformEvent-&gt;copyWithDeltasAndVelocity(filteredPlatformDelta.width(), filteredPlatformDelta.height(), filteredVelocity);
+                scrollingWasHandled = boxLayer-&gt;handleWheelEvent(copiedEvent);
+            } else
</ins><span class="cx">                 scrollingWasHandled = didScrollInScrollableArea(boxLayer, wheelEvent);
</span><span class="cx"> 
</span><span class="cx">             if (scrollingWasHandled) {
</span><span class="lines">@@ -2757,6 +2758,7 @@
</span><span class="cx">     Ref&lt;Frame&gt; protectedFrame(m_frame);
</span><span class="cx"> 
</span><span class="cx">     FloatSize filteredPlatformDelta(wheelEvent.deltaX(), wheelEvent.deltaY());
</span><ins>+    FloatPoint filteredVelocity;
</ins><span class="cx">     if (const PlatformWheelEvent* platformWheelEvent = wheelEvent.wheelEvent()) {
</span><span class="cx">         filteredPlatformDelta.setWidth(platformWheelEvent-&gt;deltaX());
</span><span class="cx">         filteredPlatformDelta.setHeight(platformWheelEvent-&gt;deltaY());
</span><span class="lines">@@ -2766,14 +2768,16 @@
</span><span class="cx">     ScrollLatchingState* latchedState = m_frame.mainFrame().latchingState();
</span><span class="cx">     Element* stopElement = latchedState ? latchedState-&gt;previousWheelScrolledElement() : nullptr;
</span><span class="cx"> 
</span><del>-    if (m_frame.mainFrame().wheelEventDeltaFilter()-&gt;isFilteringDeltas())
</del><ins>+    if (m_frame.mainFrame().wheelEventDeltaFilter()-&gt;isFilteringDeltas()) {
</ins><span class="cx">         filteredPlatformDelta = m_frame.mainFrame().wheelEventDeltaFilter()-&gt;filteredDelta();
</span><ins>+        filteredVelocity = m_frame.mainFrame().wheelEventDeltaFilter()-&gt;filteredVelocity();
+    }
</ins><span class="cx"> #else
</span><span class="cx">     Element* stopElement = nullptr;
</span><span class="cx"> #endif
</span><span class="cx">     
</span><span class="cx">     
</span><del>-    if (handleWheelEventInAppropriateEnclosingBox(startNode, wheelEvent, &amp;stopElement, filteredPlatformDelta))
</del><ins>+    if (handleWheelEventInAppropriateEnclosingBox(startNode, wheelEvent, &amp;stopElement, filteredPlatformDelta, filteredVelocity))
</ins><span class="cx">         wheelEvent.setDefaultHandled();
</span><span class="cx">     
</span><span class="cx"> #if PLATFORM(MAC)
</span></span></pre></div>
<a id="trunkSourceWebCorepagePagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/Page.cpp (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/Page.cpp        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/page/Page.cpp        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -1993,8 +1993,11 @@
</span><span class="cx"> 
</span><span class="cx"> WheelEventTestTrigger&amp; Page::ensureTestTrigger()
</span><span class="cx"> {
</span><del>-    if (!m_testTrigger)
</del><ins>+    if (!m_testTrigger) {
</ins><span class="cx">         m_testTrigger = adoptRef(new WheelEventTestTrigger());
</span><ins>+        if (auto* frameView = mainFrame().view())
+            frameView-&gt;layout();
+    }
</ins><span class="cx"> 
</span><span class="cx">     return *m_testTrigger;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorepageWheelEventDeltaFiltercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/WheelEventDeltaFilter.cpp (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/WheelEventDeltaFilter.cpp        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/page/WheelEventDeltaFilter.cpp        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -26,7 +26,7 @@
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> #include &quot;WheelEventDeltaFilter.h&quot;
</span><span class="cx"> 
</span><del>-#if PLATFORM(MAC) &amp;&amp; __MAC_OS_X_VERSION_MIN_REQUIRED &gt;= 101100
</del><ins>+#if HAVE(NSSCROLLING_FILTERS)
</ins><span class="cx"> #include &quot;WheelEventDeltaFilterMac.h&quot;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="lines">@@ -46,7 +46,7 @@
</span><span class="cx"> 
</span><span class="cx"> std::unique_ptr&lt;WheelEventDeltaFilter&gt; WheelEventDeltaFilter::create()
</span><span class="cx"> {
</span><del>-#if PLATFORM(MAC) &amp;&amp; __MAC_OS_X_VERSION_MIN_REQUIRED &gt;= 101100
</del><ins>+#if HAVE(NSSCROLLING_FILTERS)
</ins><span class="cx">     return std::make_unique&lt;WheelEventDeltaFilterMac&gt;();
</span><span class="cx"> #else
</span><span class="cx">     return std::make_unique&lt;BasicWheelEventDeltaFilter&gt;();
</span><span class="lines">@@ -63,6 +63,11 @@
</span><span class="cx">     return m_currentFilteredDelta;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+FloatPoint WheelEventDeltaFilter::filteredVelocity() const
+{
+    return m_currentFilteredVelocity;
+}
+
</ins><span class="cx"> BasicWheelEventDeltaFilter::BasicWheelEventDeltaFilter()
</span><span class="cx">     : WheelEventDeltaFilter()
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceWebCorepageWheelEventDeltaFilterh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/WheelEventDeltaFilter.h (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/WheelEventDeltaFilter.h        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/page/WheelEventDeltaFilter.h        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -25,6 +25,7 @@
</span><span class="cx"> 
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><ins>+#include &quot;FloatPoint.h&quot;
</ins><span class="cx"> #include &quot;FloatSize.h&quot;
</span><span class="cx"> #include &lt;wtf/Deque.h&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -39,11 +40,13 @@
</span><span class="cx">     WEBCORE_EXPORT virtual void updateFromDelta(const FloatSize&amp;) = 0;
</span><span class="cx">     WEBCORE_EXPORT virtual void beginFilteringDeltas() = 0;
</span><span class="cx">     WEBCORE_EXPORT virtual void endFilteringDeltas() = 0;
</span><ins>+    WEBCORE_EXPORT FloatPoint filteredVelocity() const;
</ins><span class="cx">     WEBCORE_EXPORT bool isFilteringDeltas() const;
</span><span class="cx">     WEBCORE_EXPORT FloatSize filteredDelta() const;
</span><span class="cx"> 
</span><span class="cx"> protected:
</span><span class="cx">     FloatSize m_currentFilteredDelta;
</span><ins>+    FloatPoint m_currentFilteredVelocity;
</ins><span class="cx">     bool m_isFilteringDeltas { false };
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorepagemacWheelEventDeltaFilterMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/mac/WheelEventDeltaFilterMac.mm (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/mac/WheelEventDeltaFilterMac.mm        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/page/mac/WheelEventDeltaFilterMac.mm        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -25,7 +25,7 @@
</span><span class="cx"> 
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> 
</span><del>-#if PLATFORM(MAC) &amp;&amp; __MAC_OS_X_VERSION_MIN_REQUIRED &gt;= 101100
</del><ins>+#if HAVE(NSSCROLLING_FILTERS)
</ins><span class="cx"> 
</span><span class="cx"> #include &quot;WheelEventDeltaFilterMac.h&quot;
</span><span class="cx"> #include &quot;FloatPoint.h&quot;
</span><span class="lines">@@ -55,6 +55,7 @@
</span><span class="cx">     NSPoint filteredDeltaResult;
</span><span class="cx">     NSPoint filteredVelocityResult;
</span><span class="cx">     [m_predominantAxisFilter filterInputDelta:NSPoint(FloatPoint(delta.width(), delta.height())) timestamp:monotonicallyIncreasingTime() - m_beginFilteringDeltasTime outputDelta:&amp;filteredDeltaResult velocity:&amp;filteredVelocityResult];
</span><ins>+    m_currentFilteredVelocity = FloatPoint(filteredVelocityResult);
</ins><span class="cx">     m_currentFilteredDelta = FloatSize(filteredDeltaResult.x, filteredDeltaResult.y);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -68,4 +69,4 @@
</span><span class="cx"> 
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-#endif /* PLATFORM(MAC) &amp;&amp; __MAC_OS_X_VERSION_MIN_REQUIRED &gt;= 101100 */
</del><ins>+#endif /* HAVE(NSSCROLLING_FILTERS) */
</ins></span></pre></div>
<a id="trunkSourceWebCorepagescrollingScrollingMomentumCalculatorcpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp (0 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp                                (rev 0)
+++ trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -0,0 +1,208 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include &quot;config.h&quot;
+#include &quot;ScrollingMomentumCalculator.h&quot;
+
+#include &quot;FloatPoint.h&quot;
+#include &quot;FloatSize.h&quot;
+#include &lt;wtf/CurrentTime.h&gt;
+
+namespace WebCore {
+
+static const double scrollSnapAnimationDuration = 1;
+
+ScrollingMomentumCalculator::ScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatPoint&amp; initialVelocity)
+    : m_initialDelta(initialDelta)
+    , m_initialVelocity(initialVelocity)
+    , m_initialScrollOffset(initialOffset.x(), initialOffset.y())
+    , m_targetScrollOffset(targetOffset.x(), targetOffset.y())
+    , m_viewportSize(viewportSize)
+    , m_contentSize(contentSize)
+{
+}
+
+#if !HAVE(NSSCROLLING_FILTERS)
+
+std::unique_ptr&lt;ScrollingMomentumCalculator&gt; ScrollingMomentumCalculator::create(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatPoint&amp; initialVelocity)
+{
+    return std::make_unique&lt;BasicScrollingMomentumCalculator&gt;(viewportSize, contentSize, initialOffset, targetOffset, initialDelta, initialVelocity);
+}
+
+#endif
+
+BasicScrollingMomentumCalculator::BasicScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatPoint&amp; initialVelocity)
+    : ScrollingMomentumCalculator(viewportSize, contentSize, initialOffset, targetOffset, initialDelta, initialVelocity)
+{
+}
+
+FloatSize BasicScrollingMomentumCalculator::linearlyInterpolatedOffsetAtProgress(float progress) const
+{
+    return m_initialScrollOffset + progress * (m_targetScrollOffset - m_initialScrollOffset);
+}
+
+FloatSize BasicScrollingMomentumCalculator::cubicallyInterpolatedOffsetAtProgress(float progress) const
+{
+    ASSERT(!m_forceLinearAnimationCurve);
+    FloatSize interpolatedPoint;
+    for (int i = 0; i &lt; 4; ++i)
+        interpolatedPoint += std::pow(progress, i) * m_snapAnimationCurveCoefficients[i];
+
+    return interpolatedPoint;
+}
+
+FloatPoint BasicScrollingMomentumCalculator::scrollOffsetAfterElapsedTime(double seconds)
+{
+    if (m_momentumCalculatorRequiresInitialization) {
+        initializeSnapProgressCurve();
+        initializeInterpolationCoefficientsIfNecessary();
+        m_momentumCalculatorRequiresInitialization = false;
+    }
+
+    float progress = animationProgressAfterElapsedTime(seconds);
+    auto offsetAsSize = m_forceLinearAnimationCurve ? linearlyInterpolatedOffsetAtProgress(progress) : cubicallyInterpolatedOffsetAtProgress(progress);
+    return FloatPoint(offsetAsSize.width(), offsetAsSize.height());
+}
+
+double BasicScrollingMomentumCalculator::animationDuration()
+{
+    return scrollSnapAnimationDuration;
+}
+
+/**
+ * Computes and sets coefficients required for interpolated snapping when scrolling in 2 dimensions, given
+ * initial conditions (the initial and target vectors, along with the initial wheel delta as a vector). The
+ * path is a cubic Bezier curve of the form p(s) = INITIAL + (C_1 * s) + (C_2 * s^2) + (C_3 * s^3) where each
+ * C_i is a 2D vector and INITIAL is the vector representing the initial scroll offset. s is a real in the
+ * interval [0, 1] indicating the &quot;progress&quot; of the curve (i.e. how much of the curve has been traveled).
+ *
+ * The curve has 4 control points, the first and last of which are the initial and target points, respectively.
+ * The distances between adjacent control points are constrained to be the same, making the convex hull an
+ * isosceles trapezoid with 3 sides of equal length. Additionally, the vector from the first control point to
+ * the second points in the same direction as the initial scroll delta. These constraints ensure two properties:
+ *     1. The direction of the snap animation at s=0 will be equal to the direction of the initial scroll delta.
+ *     2. Points at regular intervals of s will be evenly spread out.
+ *
+ * If the initial scroll direction is orthogonal to or points in the opposite direction as the vector from the
+ * initial point to the target point, initialization returns early and sets the curve to animate directly to the
+ * snap point without cubic interpolation.
+ *
+ * FIXME: This should be refactored to use UnitBezier.
+ */
+void BasicScrollingMomentumCalculator::initializeInterpolationCoefficientsIfNecessary()
+{
+    m_forceLinearAnimationCurve = true;
+    float initialDeltaMagnitude = m_initialDelta.diagonalLength();
+    if (initialDeltaMagnitude &lt; 1) {
+        // The initial wheel delta is so insignificant that we're better off considering this to have the same effect as finishing a scroll gesture with no momentum.
+        // Thus, cubic interpolation isn't needed here.
+        return;
+    }
+
+    FloatSize startToEndVector = m_targetScrollOffset - m_initialScrollOffset;
+    float startToEndDistance = startToEndVector.diagonalLength();
+    if (!startToEndDistance) {
+        // The start and end positions are the same, so we shouldn't try to interpolate a path.
+        return;
+    }
+
+    float cosTheta = (m_initialDelta.width() * startToEndVector.width() + m_initialDelta.height() * startToEndVector.height()) / (initialDeltaMagnitude * startToEndDistance);
+    if (cosTheta &lt;= 0) {
+        // It's possible that the user is not scrolling towards the target snap offset (for instance, scrolling against a corner when 2D scroll snapping).
+        // In this case, just let the scroll offset animate to the target without computing a cubic curve.
+        return;
+    }
+
+    float sideLength = startToEndDistance / (2.0f * cosTheta + 1.0f);
+    FloatSize controlVector1 = m_initialScrollOffset + sideLength * m_initialDelta / initialDeltaMagnitude;
+    FloatSize controlVector2 = controlVector1 + (sideLength * startToEndVector / startToEndDistance);
+    m_snapAnimationCurveCoefficients[0] = m_initialScrollOffset;
+    m_snapAnimationCurveCoefficients[1] = 3 * (controlVector1 - m_initialScrollOffset);
+    m_snapAnimationCurveCoefficients[2] = 3 * (m_initialScrollOffset - 2 * controlVector1 + controlVector2);
+    m_snapAnimationCurveCoefficients[3] = 3 * (controlVector1 - controlVector2) - m_initialScrollOffset + m_targetScrollOffset;
+    m_forceLinearAnimationCurve = false;
+}
+
+static const float framesPerSecond = 60.0f;
+
+/**
+ * Computes and sets parameters required for tracking the progress of a snap animation curve, interpolated
+ * or linear. The progress curve s(t) maps time t to progress s; both variables are in the interval [0, 1].
+ * The time input t is 0 when the current time is the start of the animation, t = 0, and 1 when the current
+ * time is at or after the end of the animation, t = m_scrollSnapAnimationDuration.
+ *
+ * In this exponential progress model, s(t) = A - A * b^(-kt), where k = 60T is the number of frames in the
+ * animation (assuming 60 FPS and an animation duration of T) and A, b are reals greater than or equal to 1.
+ * Also note that we are given the initial progress, a value indicating the portion of the curve which our
+ * initial scroll delta takes us. This is important when matching the initial speed of the animation to the
+ * user's initial momentum scrolling speed. Let this initial progress amount equal v_0. I clamp this initial
+ * progress amount to a minimum or maximum value.
+ *
+ * A is referred to as the curve magnitude, while b is referred to as the decay factor. We solve for A and b,
+ * keeping the following constraints in mind:
+ *     1. s(0) = 0
+ *     2. s(1) = 1
+ *     3. s(1/k) = v_0
+ *
+ * First, observe that s(0) = 0 holds for appropriate values of A, b. Solving for the remaining constraints
+ * yields a nonlinear system of two equations. In lieu of a purely analytical solution, an alternating
+ * optimization scheme is used to approximate A and b. This technique converges quickly (within 5 iterations
+ * or so) for appropriate values of v_0. The optimization terminates early when the decay factor changes by
+ * less than a threshold between one iteration and the next.
+ */
+void BasicScrollingMomentumCalculator::initializeSnapProgressCurve()
+{
+    static const int maxNumScrollSnapParameterEstimationIterations = 10;
+    static const float scrollSnapDecayFactorConvergenceThreshold = 0.001;
+    static const float initialScrollSnapCurveMagnitude = 1.1;
+    static const float minScrollSnapInitialProgress = 0.1;
+    static const float maxScrollSnapInitialProgress = 0.5;
+
+    FloatSize alignmentVector = m_initialDelta * (m_targetScrollOffset - m_initialScrollOffset);
+    float initialProgress;
+    if (alignmentVector.width() + alignmentVector.height() &gt; 0)
+        initialProgress = clampTo(m_initialDelta.diagonalLength() / (m_targetScrollOffset - m_initialScrollOffset).diagonalLength(), minScrollSnapInitialProgress, maxScrollSnapInitialProgress);
+    else
+        initialProgress = minScrollSnapInitialProgress;
+
+    float previousDecayFactor = 1.0f;
+    m_snapAnimationCurveMagnitude = initialScrollSnapCurveMagnitude;
+    for (int i = 0; i &lt; maxNumScrollSnapParameterEstimationIterations; ++i) {
+        m_snapAnimationDecayFactor = m_snapAnimationCurveMagnitude / (m_snapAnimationCurveMagnitude - initialProgress);
+        m_snapAnimationCurveMagnitude = 1.0f / (1.0f - std::pow(m_snapAnimationDecayFactor, -framesPerSecond * scrollSnapAnimationDuration));
+        if (std::abs(m_snapAnimationDecayFactor - previousDecayFactor) &lt; scrollSnapDecayFactorConvergenceThreshold)
+            break;
+
+        previousDecayFactor = m_snapAnimationDecayFactor;
+    }
+}
+
+float BasicScrollingMomentumCalculator::animationProgressAfterElapsedTime(double seconds) const
+{
+    float timeProgress = clampTo&lt;float&gt;(seconds / scrollSnapAnimationDuration, 0, 1);
+    return std::min(1.0, m_snapAnimationCurveMagnitude * (1.0 - std::pow(m_snapAnimationDecayFactor, -framesPerSecond * scrollSnapAnimationDuration * timeProgress)));
+}
+
+}; // namespace WebCore
</ins></span></pre></div>
<a id="trunkSourceWebCorepagescrollingScrollingMomentumCalculatorh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h (0 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h                                (rev 0)
+++ trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -0,0 +1,79 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(CSS_SCROLL_SNAP)
+
+#include &quot;AxisScrollSnapOffsets.h&quot;
+#include &quot;PlatformWheelEvent.h&quot;
+#include &quot;ScrollTypes.h&quot;
+
+namespace WebCore {
+
+class FloatPoint;
+class FloatSize;
+
+class ScrollingMomentumCalculator {
+public:
+    ScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatPoint&amp; initialVelocity);
+    static std::unique_ptr&lt;ScrollingMomentumCalculator&gt; create(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatPoint&amp; initialVelocity);
+    virtual ~ScrollingMomentumCalculator() { }
+
+    virtual FloatPoint scrollOffsetAfterElapsedTime(double time) = 0;
+    virtual double animationDuration() = 0;
+
+protected:
+    FloatSize m_initialDelta;
+    FloatPoint m_initialVelocity;
+    FloatSize m_initialScrollOffset;
+    FloatSize m_targetScrollOffset;
+    FloatSize m_viewportSize;
+    FloatSize m_contentSize;
+};
+
+class BasicScrollingMomentumCalculator final : public ScrollingMomentumCalculator {
+public:
+    BasicScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatPoint&amp; initialVelocity);
+
+private:
+    FloatPoint scrollOffsetAfterElapsedTime(double seconds) final;
+    double animationDuration() final;
+    void initializeInterpolationCoefficientsIfNecessary();
+    void initializeSnapProgressCurve();
+    float animationProgressAfterElapsedTime(double time) const;
+    FloatSize linearlyInterpolatedOffsetAtProgress(float progress) const;
+    FloatSize cubicallyInterpolatedOffsetAtProgress(float progress) const;
+
+    float m_snapAnimationCurveMagnitude { 0 };
+    float m_snapAnimationDecayFactor { 0 };
+    FloatSize m_snapAnimationCurveCoefficients[4] { };
+    bool m_forceLinearAnimationCurve { false };
+    bool m_momentumCalculatorRequiresInitialization { true };
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(CSS_SCROLL_SNAP)
</ins></span></pre></div>
<a id="trunkSourceWebCorepagescrollingmacScrollingMomentumCalculatorMachfromrev209069trunkSourceWebCorepageWheelEventDeltaFilterh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h (from rev 209069, trunk/Source/WebCore/page/WheelEventDeltaFilter.h) (0 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h                                (rev 0)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -0,0 +1,51 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include &quot;ScrollingMomentumCalculator.h&quot;
+#include &lt;wtf/RetainPtr.h&gt;
+
+#if HAVE(NSSCROLLING_FILTERS)
+
+@class _NSScrollingMomentumCalculator;
+
+namespace WebCore {
+
+class ScrollingMomentumCalculatorMac final : public ScrollingMomentumCalculator {
+public:
+    ScrollingMomentumCalculatorMac(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatPoint&amp; initialVelocity);
+
+private:
+    FloatPoint scrollOffsetAfterElapsedTime(double time) final;
+    double animationDuration() final;
+    _NSScrollingMomentumCalculator *ensurePlatformMomentumCalculator();
+
+    RetainPtr&lt;_NSScrollingMomentumCalculator&gt; m_platformMomentumCalculator;
+};
+
+} // namespace WebCore
+
+#endif // HAVE(NSSCROLLING_FILTERS)
</ins></span></pre></div>
<a id="trunkSourceWebCorepagescrollingmacScrollingMomentumCalculatorMacmm"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.mm (0 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.mm                                (rev 0)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.mm        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -0,0 +1,70 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include &quot;config.h&quot;
+#include &quot;ScrollingMomentumCalculatorMac.h&quot;
+
+#if HAVE(NSSCROLLING_FILTERS)
+
+#include &quot;NSScrollingMomentumCalculatorSPI.h&quot;
+
+namespace WebCore {
+
+std::unique_ptr&lt;ScrollingMomentumCalculator&gt; ScrollingMomentumCalculator::create(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatPoint&amp; initialVelocity)
+{
+    return std::make_unique&lt;ScrollingMomentumCalculatorMac&gt;(viewportSize, contentSize, initialOffset, targetOffset, initialDelta, initialVelocity);
+}
+
+ScrollingMomentumCalculatorMac::ScrollingMomentumCalculatorMac(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatPoint&amp; initialVelocity)
+    : ScrollingMomentumCalculator(viewportSize, contentSize, initialOffset, targetOffset, initialDelta, initialVelocity)
+{
+}
+
+FloatPoint ScrollingMomentumCalculatorMac::scrollOffsetAfterElapsedTime(double time)
+{
+    return [ensurePlatformMomentumCalculator() positionAfterDuration:time];
+}
+
+double ScrollingMomentumCalculatorMac::animationDuration()
+{
+    return [ensurePlatformMomentumCalculator() durationUntilStop];
+}
+
+_NSScrollingMomentumCalculator *ScrollingMomentumCalculatorMac::ensurePlatformMomentumCalculator()
+{
+    if (m_platformMomentumCalculator)
+        return m_platformMomentumCalculator.get();
+
+    NSPoint origin = NSMakePoint(m_initialScrollOffset.width(), m_initialScrollOffset.height());
+    NSRect contentFrame = NSMakeRect(0, 0, m_contentSize.width(), m_contentSize.height());
+    m_platformMomentumCalculator = adoptNS([[_NSScrollingMomentumCalculator alloc] initWithInitialOrigin:origin velocity:m_initialVelocity documentFrame:contentFrame constrainedClippingOrigin:NSZeroPoint clippingSize:m_viewportSize tolerance:NSMakeSize(1, 1)]);
+    [m_platformMomentumCalculator setDestinationOrigin:NSMakePoint(m_targetScrollOffset.width(), m_targetScrollOffset.height())];
+    [m_platformMomentumCalculator calculateToReachDestination];
+    return m_platformMomentumCalculator.get();
+}
+
+} // namespace WebCore
+
+#endif // HAVE(NSSCROLLING_FILTERS)
</ins></span></pre></div>
<a id="trunkSourceWebCorepagescrollingmacScrollingTreeFrameScrollingNodeMach"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -84,12 +84,13 @@
</span><span class="cx">     void removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier, WheelEventTestTrigger::DeferTestTriggerReason) const override;
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP) &amp;&amp; PLATFORM(MAC)
</span><del>-    LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) const override;
</del><ins>+    FloatPoint scrollOffset() const override;
</ins><span class="cx">     void immediateScrollOnAxis(ScrollEventAxis, float delta) override;
</span><span class="cx">     float pageScaleFactor() const override;
</span><span class="cx">     void startScrollSnapTimer() override;
</span><span class="cx">     void stopScrollSnapTimer() override;
</span><span class="cx">     LayoutSize scrollExtent() const override;
</span><ins>+    FloatSize viewportSize() const override;
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     void logExposedUnfilledArea();
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingmacScrollingTreeFrameScrollingNodeMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -581,10 +581,9 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-LayoutUnit ScrollingTreeFrameScrollingNodeMac::scrollOffsetOnAxis(ScrollEventAxis axis) const
</del><ins>+FloatPoint ScrollingTreeFrameScrollingNodeMac::scrollOffset() const
</ins><span class="cx"> {
</span><del>-    const FloatPoint&amp; currentPosition = scrollPosition();
-    return axis == ScrollEventAxis::Horizontal ? currentPosition.x() : currentPosition.y();
</del><ins>+    return scrollPosition();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollingTreeFrameScrollingNodeMac::immediateScrollOnAxis(ScrollEventAxis axis, float delta)
</span><span class="lines">@@ -618,6 +617,12 @@
</span><span class="cx"> {
</span><span class="cx">     return LayoutSize(totalContentsSize());
</span><span class="cx"> }
</span><ins>+
+FloatSize ScrollingTreeFrameScrollingNodeMac::viewportSize() const
+{
+    return scrollableAreaSize();
+}
+
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> void ScrollingTreeFrameScrollingNodeMac::deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformPlatformWheelEventh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/PlatformWheelEvent.h (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/PlatformWheelEvent.h        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/platform/PlatformWheelEvent.h        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #ifndef PlatformWheelEvent_h
</span><span class="cx"> #define PlatformWheelEvent_h
</span><span class="cx"> 
</span><ins>+#include &quot;FloatPoint.h&quot;
</ins><span class="cx"> #include &quot;IntPoint.h&quot;
</span><span class="cx"> #include &quot;PlatformEvent.h&quot;
</span><span class="cx"> #include &lt;wtf/WindowsExtras.h&gt;
</span><span class="lines">@@ -119,11 +120,12 @@
</span><span class="cx">             return copy;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        PlatformWheelEvent copyWithDeltas(float deltaX, float deltaY) const
</del><ins>+        PlatformWheelEvent copyWithDeltasAndVelocity(float deltaX, float deltaY, FloatPoint velocity) const
</ins><span class="cx">         {
</span><span class="cx">             PlatformWheelEvent copy = *this;
</span><span class="cx">             copy.m_deltaX = deltaX;
</span><span class="cx">             copy.m_deltaY = deltaY;
</span><ins>+            copy.m_scrollingVelocity = velocity;
</ins><span class="cx">             return copy;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="lines">@@ -166,6 +168,8 @@
</span><span class="cx">         bool useLatchedEventElement() const { return false; }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+        FloatPoint scrollingVelocity() const { return m_scrollingVelocity; }
+
</ins><span class="cx"> #if PLATFORM(WIN)
</span><span class="cx">         PlatformWheelEvent(HWND, WPARAM, LPARAM, bool isMouseHWheel);
</span><span class="cx">         PlatformWheelEvent(HWND, const FloatSize&amp; delta, const FloatPoint&amp; location);
</span><span class="lines">@@ -180,6 +184,7 @@
</span><span class="cx">         float m_wheelTicksY;
</span><span class="cx">         PlatformWheelEventGranularity m_granularity;
</span><span class="cx">         bool m_directionInvertedFromDevice;
</span><ins>+        FloatPoint m_scrollingVelocity;
</ins><span class="cx"> #if PLATFORM(COCOA)
</span><span class="cx">         bool m_hasPreciseScrollingDeltas;
</span><span class="cx">         PlatformWheelEventPhase m_phase;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformScrollAnimatorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/ScrollAnimator.cpp (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/ScrollAnimator.cpp        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/platform/ScrollAnimator.cpp        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -201,9 +201,9 @@
</span><span class="cx">     m_scrollController.updateScrollSnapState(m_scrollableArea);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-LayoutUnit ScrollAnimator::scrollOffsetOnAxis(ScrollEventAxis axis) const
</del><ins>+FloatPoint ScrollAnimator::scrollOffset() const
</ins><span class="cx"> {
</span><del>-    return axis == ScrollEventAxis::Horizontal ? m_currentPosition.x() : m_currentPosition.y();
</del><ins>+    return m_currentPosition;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollAnimator::immediateScrollOnAxis(ScrollEventAxis axis, float delta)
</span><span class="lines">@@ -221,6 +221,12 @@
</span><span class="cx"> {
</span><span class="cx">     return m_scrollableArea.contentsSize();
</span><span class="cx"> }
</span><ins>+
+FloatSize ScrollAnimator::viewportSize() const
+{
+    return m_scrollableArea.visibleSize();
+}
+
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> #if (ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)) &amp;&amp; PLATFORM(MAC)
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformScrollAnimatorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/ScrollAnimator.h (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/ScrollAnimator.h        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/platform/ScrollAnimator.h        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -134,11 +134,12 @@
</span><span class="cx">     bool processWheelEventForScrollSnap(const PlatformWheelEvent&amp;);
</span><span class="cx"> #endif
</span><span class="cx">     void updateScrollSnapState();
</span><del>-    LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) const override;
</del><ins>+    FloatPoint scrollOffset() const override;
</ins><span class="cx">     void immediateScrollOnAxis(ScrollEventAxis, float delta) override;
</span><span class="cx">     bool activeScrollSnapIndexDidChange() const;
</span><span class="cx">     unsigned activeScrollSnapIndexForAxis(ScrollEventAxis) const;
</span><span class="cx">     LayoutSize scrollExtent() const override;
</span><ins>+    FloatSize viewportSize() const override;
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> protected:
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaScrollControllerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ScrollController.h (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ScrollController.h        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/platform/cocoa/ScrollController.h        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -81,7 +81,7 @@
</span><span class="cx">     virtual void removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier, WheelEventTestTrigger::DeferTestTriggerReason) const { /* Do nothing */ }
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    virtual LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) const = 0;
</del><ins>+    virtual FloatPoint scrollOffset() const = 0;
</ins><span class="cx">     virtual void immediateScrollOnAxis(ScrollEventAxis, float delta) = 0;
</span><span class="cx">     virtual void startScrollSnapTimer()
</span><span class="cx">     {
</span><span class="lines">@@ -104,9 +104,21 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     virtual LayoutSize scrollExtent() const = 0;
</span><ins>+    virtual FloatSize viewportSize() const = 0;
</ins><span class="cx"> #endif
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+enum class WheelEventStatus {
+    UserScrollBegin,
+    UserScrolling,
+    UserScrollEnd,
+    InertialScrollBegin,
+    InertialScrolling,
+    InertialScrollEnd,
+    StatelessScrollEvent,
+    Unknown
+};
+
</ins><span class="cx"> class ScrollController {
</span><span class="cx">     WTF_MAKE_NONCOPYABLE(ScrollController);
</span><span class="cx"> 
</span><span class="lines">@@ -144,30 +156,25 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) const;
</del><span class="cx">     void setNearestScrollSnapIndexForAxisAndOffset(ScrollEventAxis, int);
</span><del>-    ScrollSnapAnimatorState&amp; scrollSnapPointState(ScrollEventAxis);
-    const ScrollSnapAnimatorState&amp; scrollSnapPointState(ScrollEventAxis) const;
</del><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx">     void scrollSnapTimerFired();
</span><span class="cx">     void startScrollSnapTimer();
</span><span class="cx">     void stopScrollSnapTimer();
</span><span class="cx"> 
</span><del>-    void processWheelEventForScrollSnapOnAxis(ScrollEventAxis, const PlatformWheelEvent&amp;);
-    bool shouldOverrideWheelEvent(ScrollEventAxis, const PlatformWheelEvent&amp;) const;
-
-    void beginScrollSnapAnimation(ScrollEventAxis, ScrollSnapState);
-    
-    void endScrollSnapAnimation(ScrollSnapState);
-    void initializeScrollSnapAnimationParameters();
-    bool isSnappingOnAxis(ScrollEventAxis) const;
-    
</del><ins>+    bool shouldOverrideInertialScrolling() const;
+    void statelessSnapTransitionTimerFired();
+    void startDeferringTestsDueToScrollSnapping();
+    void stopDeferringTestsDueToScrollSnapping();
+    void scheduleStatelessScrollSnap();
</ins><span class="cx"> #endif
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     ScrollControllerClient&amp; m_client;
</span><del>-    
</del><ins>+
+#if PLATFORM(MAC)
</ins><span class="cx">     CFTimeInterval m_lastMomentumScrollTimestamp { 0 };
</span><ins>+#endif
</ins><span class="cx">     FloatSize m_overflowScrollDelta;
</span><span class="cx">     FloatSize m_stretchScrollForce;
</span><span class="cx">     FloatSize m_momentumVelocity;
</span><span class="lines">@@ -181,20 +188,21 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    bool m_expectingHorizontalStatelessScrollSnap { false };
-    bool m_expectingVerticalStatelessScrollSnap { false };
-    std::unique_ptr&lt;ScrollSnapAnimatorState&gt; m_horizontalScrollSnapState;
-    std::unique_ptr&lt;ScrollSnapAnimatorState&gt; m_verticalScrollSnapState;
-    std::unique_ptr&lt;ScrollSnapAnimationCurveState&gt; m_scrollSnapCurveState;
</del><ins>+    std::unique_ptr&lt;ScrollSnapAnimatorState&gt; m_scrollSnapState;
</ins><span class="cx"> #if PLATFORM(MAC)
</span><ins>+    FloatPoint m_dragEndedScrollingVelocity;
+    RunLoop::Timer&lt;ScrollController&gt; m_statelessSnapTransitionTimer;
</ins><span class="cx">     RunLoop::Timer&lt;ScrollController&gt; m_scrollSnapTimer;
</span><span class="cx"> #endif
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#if PLATFORM(MAC)
</ins><span class="cx">     bool m_inScrollGesture { false };
</span><span class="cx">     bool m_momentumScrollInProgress { false };
</span><span class="cx">     bool m_ignoreMomentumScrolls { false };
</span><span class="cx">     bool m_snapRubberbandTimerIsActive { false };
</span><ins>+#endif
+
</ins><span class="cx">     bool m_activeScrollSnapIndexDidChange { false };
</span><span class="cx"> };
</span><span class="cx">     
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaScrollControllermm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ScrollController.mm (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ScrollController.mm        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/platform/cocoa/ScrollController.mm        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -74,23 +74,7 @@
</span><span class="cx"> static const float rubberbandMinimumRequiredDeltaBeforeStretch = 10;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-#if ENABLE(CSS_SCROLL_SNAP) &amp;&amp; PLATFORM(MAC)
-static const float inertialScrollPredictionFactor = 16.7;
-static const double statelessScrollSnapDelay = 0.5;
-#endif
-
</del><span class="cx"> #if PLATFORM(MAC)
</span><del>-enum class WheelEventStatus {
-    UserScrollBegin,
-    UserScrolling,
-    UserScrollEnd,
-    InertialScrollBegin,
-    InertialScrolling,
-    InertialScrollEnd,
-    StatelessScrollEvent,
-    Unknown
-};
-
</del><span class="cx"> static float elasticDeltaForTimeDelta(float initialPosition, float initialVelocity, float elapsedTime)
</span><span class="cx"> {
</span><span class="cx">     return wkNSElasticDeltaForTimeDelta(initialPosition, initialVelocity, elapsedTime);
</span><span class="lines">@@ -116,6 +100,11 @@
</span><span class="cx">     }
</span><span class="cx">     return multiplier;
</span><span class="cx"> }
</span><ins>+
+static ScrollEventAxis otherScrollEventAxis(ScrollEventAxis axis)
+{
+    return axis == ScrollEventAxis::Horizontal ? ScrollEventAxis::Vertical : ScrollEventAxis::Horizontal;
+}
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> ScrollController::ScrollController(ScrollControllerClient&amp; client)
</span><span class="lines">@@ -124,6 +113,7 @@
</span><span class="cx">     , m_snapRubberbandTimer(RunLoop::current(), this, &amp;ScrollController::snapRubberBandTimerFired)
</span><span class="cx"> #endif
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP) &amp;&amp; PLATFORM(MAC)
</span><ins>+    , m_statelessSnapTransitionTimer(RunLoop::current(), this, &amp;ScrollController::statelessSnapTransitionTimerFired)
</ins><span class="cx">     , m_scrollSnapTimer(RunLoop::current(), this, &amp;ScrollController::scrollSnapTimerFired)
</span><span class="cx"> #endif
</span><span class="cx"> {
</span><span class="lines">@@ -466,22 +456,7 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-ScrollSnapAnimatorState&amp; ScrollController::scrollSnapPointState(ScrollEventAxis axis)
-{
-    ASSERT(axis != ScrollEventAxis::Horizontal || m_horizontalScrollSnapState);
-    ASSERT(axis != ScrollEventAxis::Vertical || m_verticalScrollSnapState);
</del><span class="cx"> 
</span><del>-    return (axis == ScrollEventAxis::Horizontal) ? *m_horizontalScrollSnapState : *m_verticalScrollSnapState;
-}
-
-const ScrollSnapAnimatorState&amp; ScrollController::scrollSnapPointState(ScrollEventAxis axis) const
-{
-    ASSERT(axis != ScrollEventAxis::Horizontal || m_horizontalScrollSnapState);
-    ASSERT(axis != ScrollEventAxis::Vertical || m_verticalScrollSnapState);
-    
-    return (axis == ScrollEventAxis::Horizontal) ? *m_horizontalScrollSnapState : *m_verticalScrollSnapState;
-}
-
</del><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx"> static inline WheelEventStatus toWheelEventStatus(PlatformWheelEventPhase phase, PlatformWheelEventPhase momentumPhase)
</span><span class="cx"> {
</span><span class="lines">@@ -523,356 +498,202 @@
</span><span class="cx">     return WheelEventStatus::Unknown;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ScrollController::processWheelEventForScrollSnapOnAxis(ScrollEventAxis axis, const PlatformWheelEvent&amp; event)
</del><ins>+bool ScrollController::shouldOverrideInertialScrolling() const
</ins><span class="cx"> {
</span><del>-    ScrollSnapAnimatorState&amp; snapState = scrollSnapPointState(axis);
</del><ins>+    if (!m_scrollSnapState)
+        return false;
</ins><span class="cx"> 
</span><del>-    float wheelDelta = axis == ScrollEventAxis::Horizontal ? -event.deltaX() : -event.deltaY();
-    WheelEventStatus wheelStatus = toWheelEventStatus(event.phase(), event.momentumPhase());
-    
-    switch (wheelStatus) {
</del><ins>+    ScrollSnapState scrollSnapState = m_scrollSnapState-&gt;currentState();
+    return scrollSnapState == ScrollSnapState::Gliding || scrollSnapState == ScrollSnapState::DestinationReached;
+}
+
+void ScrollController::scheduleStatelessScrollSnap()
+{
+    stopScrollSnapTimer();
+    m_statelessSnapTransitionTimer.stop();
+    if (!m_scrollSnapState)
+        return;
+
+    static const double statelessScrollSnapDelay = 0.75;
+    m_statelessSnapTransitionTimer.startOneShot(statelessScrollSnapDelay);
+    startDeferringTestsDueToScrollSnapping();
+}
+
+void ScrollController::statelessSnapTransitionTimerFired()
+{
+    if (!m_scrollSnapState)
+        return;
+
+    m_scrollSnapState-&gt;transitionToSnapAnimationState(m_client.scrollExtent(), m_client.viewportSize(), m_client.pageScaleFactor(), m_client.scrollOffset());
+    startScrollSnapTimer();
+}
+
+void ScrollController::startDeferringTestsDueToScrollSnapping()
+{
+    m_client.deferTestsForReason(reinterpret_cast&lt;WheelEventTestTrigger::ScrollableAreaIdentifier&gt;(this), WheelEventTestTrigger::ScrollSnapInProgress);
+}
+
+void ScrollController::stopDeferringTestsDueToScrollSnapping()
+{
+    m_client.removeTestDeferralForReason(reinterpret_cast&lt;WheelEventTestTrigger::ScrollableAreaIdentifier&gt;(this), WheelEventTestTrigger::ScrollSnapInProgress);
+}
+
+bool ScrollController::processWheelEventForScrollSnap(const PlatformWheelEvent&amp; wheelEvent)
+{
+    if (!m_scrollSnapState)
+        return true;
+
+    if (m_scrollSnapState-&gt;snapOffsetsForAxis(ScrollEventAxis::Horizontal).isEmpty() &amp;&amp; m_scrollSnapState-&gt;snapOffsetsForAxis(ScrollEventAxis::Vertical).isEmpty())
+        return true;
+
+    WheelEventStatus status = toWheelEventStatus(wheelEvent.phase(), wheelEvent.momentumPhase());
+    bool isInertialScrolling = false;
+    switch (status) {
</ins><span class="cx">     case WheelEventStatus::UserScrollBegin:
</span><span class="cx">     case WheelEventStatus::UserScrolling:
</span><del>-        endScrollSnapAnimation(ScrollSnapState::UserInteraction);
</del><ins>+        stopScrollSnapTimer();
+        m_scrollSnapState-&gt;transitionToUserInteractionState();
</ins><span class="cx">         break;
</span><del>-            
</del><span class="cx">     case WheelEventStatus::UserScrollEnd:
</span><del>-        beginScrollSnapAnimation(axis, ScrollSnapState::Snapping);
</del><ins>+        m_dragEndedScrollingVelocity = -wheelEvent.scrollingVelocity();
+        m_scrollSnapState-&gt;transitionToSnapAnimationState(m_client.scrollExtent(), m_client.viewportSize(), m_client.pageScaleFactor(), m_client.scrollOffset());
+        startScrollSnapTimer();
</ins><span class="cx">         break;
</span><del>-        
</del><span class="cx">     case WheelEventStatus::InertialScrollBegin:
</span><del>-        // Begin tracking wheel deltas for glide prediction.
-        endScrollSnapAnimation(ScrollSnapState::UserInteraction);
-        snapState.pushInitialWheelDelta(wheelDelta);
-        snapState.m_beginTrackingWheelDeltaOffset = m_client.scrollOffsetOnAxis(axis);
</del><ins>+        m_scrollSnapState-&gt;transitionToGlideAnimationState(m_client.scrollExtent(), m_client.viewportSize(), m_client.pageScaleFactor(), m_client.scrollOffset(), m_dragEndedScrollingVelocity, FloatSize(-wheelEvent.deltaX(), -wheelEvent.deltaY()));
+        isInertialScrolling = true;
</ins><span class="cx">         break;
</span><del>-            
</del><span class="cx">     case WheelEventStatus::InertialScrolling:
</span><del>-        // This check for DestinationReached ensures that we don't receive another set of momentum events after ending the last glide.
-        if (snapState.m_currentState != ScrollSnapState::Gliding &amp;&amp; snapState.m_currentState != ScrollSnapState::DestinationReached) {
-            if (snapState.wheelDeltaTrackingIsInProgress() &amp;&amp; wheelDelta)
-                snapState.pushInitialWheelDelta(wheelDelta);
-            
-            if (snapState.hasFinishedTrackingWheelDeltas() &amp;&amp; snapState.averageInitialWheelDelta())
-                beginScrollSnapAnimation(axis, ScrollSnapState::Gliding);
-        }
-        break;
-        
</del><span class="cx">     case WheelEventStatus::InertialScrollEnd:
</span><del>-        if (snapState.wheelDeltaTrackingIsInProgress() &amp;&amp; snapState.averageInitialWheelDelta())
-            beginScrollSnapAnimation(axis, ScrollSnapState::Gliding);
-
-        snapState.clearInitialWheelDeltaWindow();
-        snapState.m_shouldOverrideWheelEvent = false;
</del><ins>+        isInertialScrolling = true;
</ins><span class="cx">         break;
</span><del>-
</del><span class="cx">     case WheelEventStatus::StatelessScrollEvent:
</span><del>-        endScrollSnapAnimation(ScrollSnapState::UserInteraction);
-        snapState.clearInitialWheelDeltaWindow();
-        snapState.m_shouldOverrideWheelEvent = false;
-        m_scrollSnapTimer.startOneShot(statelessScrollSnapDelay);
-        if (axis == ScrollEventAxis::Horizontal)
-            m_expectingHorizontalStatelessScrollSnap = true;
-        else
-            m_expectingVerticalStatelessScrollSnap = true;
</del><ins>+        m_scrollSnapState-&gt;transitionToUserInteractionState();
+        scheduleStatelessScrollSnap();
</ins><span class="cx">         break;
</span><del>-
</del><span class="cx">     case WheelEventStatus::Unknown:
</span><span class="cx">         ASSERT_NOT_REACHED();
</span><span class="cx">         break;
</span><span class="cx">     }
</span><del>-}
</del><span class="cx"> 
</span><del>-bool ScrollController::shouldOverrideWheelEvent(ScrollEventAxis axis, const PlatformWheelEvent&amp; event) const
-{
-    const ScrollSnapAnimatorState&amp; snapState = scrollSnapPointState(axis);
-
-    return snapState.m_shouldOverrideWheelEvent &amp;&amp; toWheelEventStatus(event.phase(), event.momentumPhase()) == WheelEventStatus::InertialScrolling;
</del><ins>+    return !(isInertialScrolling &amp;&amp; shouldOverrideInertialScrolling());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool ScrollController::processWheelEventForScrollSnap(const PlatformWheelEvent&amp; wheelEvent)
-{
-    bool shouldAllowWheelEventToPropagate = true;
-    if (m_verticalScrollSnapState) {
-        processWheelEventForScrollSnapOnAxis(ScrollEventAxis::Vertical, wheelEvent);
-        shouldAllowWheelEventToPropagate &amp;= !shouldOverrideWheelEvent(ScrollEventAxis::Vertical, wheelEvent);
-    }
-    if (m_horizontalScrollSnapState) {
-        processWheelEventForScrollSnapOnAxis(ScrollEventAxis::Horizontal, wheelEvent);
-        shouldAllowWheelEventToPropagate &amp;= !shouldOverrideWheelEvent(ScrollEventAxis::Horizontal, wheelEvent);
-    }
-    return shouldAllowWheelEventToPropagate;
-}
-#endif
-
</del><span class="cx"> void ScrollController::updateScrollSnapState(const ScrollableArea&amp; scrollableArea)
</span><span class="cx"> {
</span><del>-    // FIXME: Currently, scroll snap animators are recreated even though the snap offsets alone can be updated.
-    if (scrollableArea.horizontalSnapOffsets())
-        m_horizontalScrollSnapState = std::make_unique&lt;ScrollSnapAnimatorState&gt;(ScrollEventAxis::Horizontal, *scrollableArea.horizontalSnapOffsets());
-    else if (m_horizontalScrollSnapState)
-        m_horizontalScrollSnapState = nullptr;
</del><ins>+    if (auto* snapOffsets = scrollableArea.horizontalSnapOffsets())
+        updateScrollSnapPoints(ScrollEventAxis::Horizontal, *snapOffsets);
</ins><span class="cx"> 
</span><del>-    if (scrollableArea.verticalSnapOffsets())
-        m_verticalScrollSnapState = std::make_unique&lt;ScrollSnapAnimatorState&gt;(ScrollEventAxis::Vertical, *scrollableArea.verticalSnapOffsets());
-    else if (m_verticalScrollSnapState)
-        m_verticalScrollSnapState = nullptr;
</del><ins>+    if (auto* snapOffsets = scrollableArea.verticalSnapOffsets())
+        updateScrollSnapPoints(ScrollEventAxis::Vertical, *snapOffsets);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollController::updateScrollSnapPoints(ScrollEventAxis axis, const Vector&lt;LayoutUnit&gt;&amp; snapPoints)
</span><span class="cx"> {
</span><del>-    // FIXME: Currently, scroll snap animators are recreated even though the snap offsets alone can be updated.
-    if (axis == ScrollEventAxis::Horizontal)
-        m_horizontalScrollSnapState = !snapPoints.isEmpty() ? std::make_unique&lt;ScrollSnapAnimatorState&gt;(ScrollEventAxis::Horizontal, snapPoints) : nullptr;
</del><ins>+    if (!m_scrollSnapState) {
+        if (snapPoints.isEmpty())
+            return;
</ins><span class="cx"> 
</span><del>-    if (axis == ScrollEventAxis::Vertical)
-        m_verticalScrollSnapState = !snapPoints.isEmpty() ? std::make_unique&lt;ScrollSnapAnimatorState&gt;(ScrollEventAxis::Vertical, snapPoints) : nullptr;
</del><ins>+        m_scrollSnapState = std::make_unique&lt;ScrollSnapAnimatorState&gt;();
+    }
+
+    if (snapPoints.isEmpty() &amp;&amp; m_scrollSnapState-&gt;snapOffsetsForAxis(otherScrollEventAxis(axis)).isEmpty())
+        m_scrollSnapState = nullptr;
+    else
+        m_scrollSnapState-&gt;setSnapOffsetsForAxis(axis, snapPoints);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-#if PLATFORM(MAC)
</del><span class="cx"> void ScrollController::startScrollSnapTimer()
</span><span class="cx"> {
</span><del>-    if (!m_scrollSnapTimer.isActive()) {
-        m_client.startScrollSnapTimer();
-        m_scrollSnapTimer.startRepeating(1.0 / 60.0);
-    }
-
-    if (!m_scrollSnapTimer.isActive())
</del><ins>+    if (m_scrollSnapTimer.isActive())
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    m_client.deferTestsForReason(reinterpret_cast&lt;WheelEventTestTrigger::ScrollableAreaIdentifier&gt;(this), WheelEventTestTrigger::ScrollSnapInProgress);
</del><ins>+    startDeferringTestsDueToScrollSnapping();
+    m_client.startScrollSnapTimer();
+    m_scrollSnapTimer.startRepeating(1.0 / 60.0);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollController::stopScrollSnapTimer()
</span><span class="cx"> {
</span><ins>+    if (!m_scrollSnapTimer.isActive())
+        return;
+
+    stopDeferringTestsDueToScrollSnapping();
</ins><span class="cx">     m_client.stopScrollSnapTimer();
</span><span class="cx">     m_scrollSnapTimer.stop();
</span><del>-    
-    if (m_scrollSnapTimer.isActive())
-        return;
-
-    m_client.removeTestDeferralForReason(reinterpret_cast&lt;WheelEventTestTrigger::ScrollableAreaIdentifier&gt;(this), WheelEventTestTrigger::ScrollSnapInProgress);
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollController::scrollSnapTimerFired()
</span><span class="cx"> {
</span><del>-    if (m_expectingHorizontalStatelessScrollSnap || m_expectingVerticalStatelessScrollSnap) {
-        if (m_expectingHorizontalStatelessScrollSnap)
-            beginScrollSnapAnimation(ScrollEventAxis::Horizontal, ScrollSnapState::Snapping);
-        if (m_expectingVerticalStatelessScrollSnap)
-            beginScrollSnapAnimation(ScrollEventAxis::Vertical, ScrollSnapState::Snapping);
</del><ins>+    if (!m_scrollSnapState) {
+        ASSERT_NOT_REACHED();
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><del>-        
-    bool snapOnHorizontalAxis = isSnappingOnAxis(ScrollEventAxis::Horizontal);
-    bool snapOnVerticalAxis = isSnappingOnAxis(ScrollEventAxis::Vertical);
-    if (snapOnHorizontalAxis &amp;&amp; !m_horizontalScrollSnapState-&gt;canReachTargetWithCurrentInitialScrollDelta()) {
-        m_horizontalScrollSnapState-&gt;m_currentState = ScrollSnapState::DestinationReached;
-        snapOnHorizontalAxis = false;
-    }
-    if (snapOnVerticalAxis &amp;&amp; !m_verticalScrollSnapState-&gt;canReachTargetWithCurrentInitialScrollDelta()) {
-        m_verticalScrollSnapState-&gt;m_currentState = ScrollSnapState::DestinationReached;
-        snapOnVerticalAxis = false;
-    }
-    if (!snapOnHorizontalAxis &amp;&amp; !snapOnVerticalAxis) {
-        endScrollSnapAnimation(ScrollSnapState::DestinationReached);
-        return;
-    }
-    
-    double currentTime = monotonicallyIncreasingTime();
-    if (m_scrollSnapCurveState-&gt;shouldCompleteSnapAnimationImmediatelyAtTime(currentTime)) {
-        float finalHorizontalDelta = 0;
-        float finalVerticalDelta = 0;
-        if (snapOnHorizontalAxis)
-            finalHorizontalDelta = m_horizontalScrollSnapState-&gt;m_targetOffset - m_client.scrollOffsetOnAxis(ScrollEventAxis::Horizontal);
-        if (snapOnVerticalAxis)
-            finalVerticalDelta = m_verticalScrollSnapState-&gt;m_targetOffset - m_client.scrollOffsetOnAxis(ScrollEventAxis::Vertical);
</del><span class="cx"> 
</span><del>-        if (finalHorizontalDelta || finalVerticalDelta)
-            m_client.immediateScrollBy(FloatSize(finalHorizontalDelta, finalVerticalDelta));
-
-        endScrollSnapAnimation(ScrollSnapState::DestinationReached);
-        return;
</del><ins>+    bool isAnimationComplete;
+    auto animationOffset = m_scrollSnapState-&gt;currentAnimatedScrollOffset(isAnimationComplete);
+    auto currentOffset = m_client.scrollOffset();
+    m_client.immediateScrollByWithoutContentEdgeConstraints(FloatSize(animationOffset.x() - currentOffset.x(), animationOffset.y() - currentOffset.y()));
+    if (isAnimationComplete) {
+        m_scrollSnapState-&gt;transitionToDestinationReachedState();
+        stopScrollSnapTimer();
</ins><span class="cx">     }
</span><del>-    
-    float animationProgress = m_scrollSnapCurveState-&gt;animationProgressAtTime(currentTime);
-    float horizontalDelta = 0;
-    float verticalDelta = 0;
-    if (m_scrollSnapCurveState-&gt;shouldAnimateDirectlyToSnapPoint) {
-        if (snapOnHorizontalAxis)
-            horizontalDelta = m_horizontalScrollSnapState-&gt;interpolatedOffsetAtProgress(animationProgress) - m_client.scrollOffsetOnAxis(ScrollEventAxis::Horizontal);
-        if (snapOnVerticalAxis)
-            verticalDelta = m_verticalScrollSnapState-&gt;interpolatedOffsetAtProgress(animationProgress) - m_client.scrollOffsetOnAxis(ScrollEventAxis::Vertical);
-
-    } else {
-        FloatPoint interpolatedPoint = m_scrollSnapCurveState-&gt;interpolatedPositionAtProgress(animationProgress);
-        horizontalDelta = interpolatedPoint.x() - m_client.scrollOffsetOnAxis(ScrollEventAxis::Horizontal);
-        verticalDelta = interpolatedPoint.y() - m_client.scrollOffsetOnAxis(ScrollEventAxis::Vertical);
-    }
-    
-    if (horizontalDelta || verticalDelta)
-        m_client.immediateScrollBy(FloatSize(horizontalDelta, verticalDelta));
</del><span class="cx"> }
</span><del>-
-static inline float projectedInertialScrollDistance(float initialWheelDelta)
</del><ins>+#else
+void ScrollController::updateScrollSnapState(const ScrollableArea&amp;)
</ins><span class="cx"> {
</span><del>-    // FIXME: Experiments with inertial scrolling show a fairly consistent linear relationship between initial wheel delta and total distance scrolled.
-    // In the future, we'll want to find a more accurate way of inertial scroll prediction.
-    return inertialScrollPredictionFactor * initialWheelDelta;
</del><span class="cx"> }
</span><del>-#endif
</del><ins>+#endif // PLATFORM(MAC)
</ins><span class="cx"> 
</span><span class="cx"> unsigned ScrollController::activeScrollSnapIndexForAxis(ScrollEventAxis axis) const
</span><span class="cx"> {
</span><del>-    if ((axis == ScrollEventAxis::Horizontal) &amp;&amp; !m_horizontalScrollSnapState)
</del><ins>+    if (!m_scrollSnapState)
</ins><span class="cx">         return 0;
</span><del>-    if ((axis == ScrollEventAxis::Vertical) &amp;&amp; !m_verticalScrollSnapState)
-        return 0;
-    
-    const ScrollSnapAnimatorState&amp; snapState = scrollSnapPointState(axis);
-    return snapState.m_activeSnapIndex;
</del><ins>+
+    return m_scrollSnapState-&gt;activeSnapIndexForAxis(axis);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollController::setActiveScrollSnapIndexForAxis(ScrollEventAxis axis, unsigned index)
</span><span class="cx"> {
</span><del>-    auto* snapState = (axis == ScrollEventAxis::Horizontal) ? m_horizontalScrollSnapState.get() : m_verticalScrollSnapState.get();
-    if (!snapState)
</del><ins>+    if (!m_scrollSnapState)
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    snapState-&gt;m_activeSnapIndex = index;
</del><ins>+    m_scrollSnapState-&gt;setActiveSnapIndexForAxis(axis, index);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollController::setNearestScrollSnapIndexForAxisAndOffset(ScrollEventAxis axis, int offset)
</span><span class="cx"> {
</span><ins>+    if (!m_scrollSnapState)
+        return;
+
</ins><span class="cx">     float scaleFactor = m_client.pageScaleFactor();
</span><del>-    ScrollSnapAnimatorState&amp; snapState = scrollSnapPointState(axis);
-    
-    LayoutUnit clampedOffset = std::min(std::max(LayoutUnit(offset / scaleFactor), snapState.m_snapOffsets.first()), snapState.m_snapOffsets.last());
</del><ins>+    ScrollSnapAnimatorState&amp; snapState = *m_scrollSnapState;
</ins><span class="cx"> 
</span><ins>+    auto snapOffsets = snapState.snapOffsetsForAxis(axis);
+    if (!snapOffsets.size())
+        return;
+
+    LayoutUnit clampedOffset = std::min(std::max(LayoutUnit(offset / scaleFactor), snapOffsets.first()), snapOffsets.last());
+
</ins><span class="cx">     unsigned activeIndex = 0;
</span><del>-    (void)closestSnapOffset&lt;LayoutUnit, float&gt;(snapState.m_snapOffsets, clampedOffset, 0, activeIndex);
</del><ins>+    closestSnapOffset&lt;LayoutUnit, float&gt;(snapState.snapOffsetsForAxis(axis), clampedOffset, 0, activeIndex);
</ins><span class="cx"> 
</span><del>-    if (activeIndex == snapState.m_activeSnapIndex)
</del><ins>+    if (activeIndex == activeScrollSnapIndexForAxis(axis))
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     m_activeScrollSnapIndexDidChange = true;
</span><del>-    snapState.m_activeSnapIndex = activeIndex;
</del><ins>+    setActiveScrollSnapIndexForAxis(axis, activeIndex);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollController::setActiveScrollSnapIndicesForOffset(int x, int y)
</span><span class="cx"> {
</span><del>-    if (m_horizontalScrollSnapState)
-        setNearestScrollSnapIndexForAxisAndOffset(ScrollEventAxis::Horizontal, x);
-    if (m_verticalScrollSnapState)
-        setNearestScrollSnapIndexForAxisAndOffset(ScrollEventAxis::Vertical, y);
-}
-
-#if PLATFORM(MAC)
-void ScrollController::beginScrollSnapAnimation(ScrollEventAxis axis, ScrollSnapState newState)
-{
-    ASSERT(newState == ScrollSnapState::Gliding || newState == ScrollSnapState::Snapping);
-    if (m_expectingHorizontalStatelessScrollSnap || m_expectingVerticalStatelessScrollSnap) {
-        m_expectingHorizontalStatelessScrollSnap = false;
-        m_expectingVerticalStatelessScrollSnap = false;
-        stopScrollSnapTimer();
-    }
-    ScrollSnapAnimatorState&amp; snapState = scrollSnapPointState(axis);
-
-    LayoutUnit offset = m_client.scrollOffsetOnAxis(axis);
-    float initialWheelDelta = newState == ScrollSnapState::Gliding ? snapState.averageInitialWheelDelta() : 0;
-    LayoutUnit scaledProjectedScrollDestination = newState == ScrollSnapState::Gliding ? snapState.m_beginTrackingWheelDeltaOffset + LayoutUnit(projectedInertialScrollDistance(initialWheelDelta)) : offset;
-    if (snapState.m_snapOffsets.isEmpty())
</del><ins>+    if (!m_scrollSnapState)
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    float scaleFactor = m_client.pageScaleFactor();
-    LayoutUnit originalProjectedScrollDestination = scaledProjectedScrollDestination / scaleFactor;
-    
-    LayoutUnit clampedScrollDestination = std::min(std::max(originalProjectedScrollDestination, snapState.m_snapOffsets.first()), snapState.m_snapOffsets.last());
-    snapState.m_initialOffset = offset;
-    m_activeScrollSnapIndexDidChange = false;
-    snapState.m_targetOffset = scaleFactor * closestSnapOffset&lt;LayoutUnit, float&gt;(snapState.m_snapOffsets, clampedScrollDestination, initialWheelDelta, snapState.m_activeSnapIndex);
-    if (snapState.m_initialOffset == snapState.m_targetOffset)
-        return;
-
-    LayoutUnit scrollExtent = (axis == ScrollEventAxis::Horizontal) ? m_client.scrollExtent().width() : m_client.scrollExtent().height();
-    LayoutUnit projectedScrollDestination = clampedScrollDestination;
-    if (originalProjectedScrollDestination &lt; 0 || originalProjectedScrollDestination &gt; scrollExtent)
-        projectedScrollDestination = originalProjectedScrollDestination;
-    
-    m_activeScrollSnapIndexDidChange = true;
-    snapState.m_currentState = newState;
-    if (newState == ScrollSnapState::Gliding) {
-        // Check if the other scroll axis needs to animate to the nearest snap point.
-        snapState.m_initialScrollDelta = initialWheelDelta;
-        snapState.m_shouldOverrideWheelEvent = true;
-        snapState.clearInitialWheelDeltaWindow();
-        ScrollEventAxis otherAxis = axis == ScrollEventAxis::Horizontal ? ScrollEventAxis::Vertical : ScrollEventAxis::Horizontal;
-        if ((otherAxis == ScrollEventAxis::Horizontal &amp;&amp; m_horizontalScrollSnapState &amp;&amp; m_horizontalScrollSnapState-&gt;m_currentState == ScrollSnapState::UserInteraction)
-            || (otherAxis == ScrollEventAxis::Vertical &amp;&amp; m_verticalScrollSnapState &amp;&amp; m_verticalScrollSnapState-&gt;m_currentState == ScrollSnapState::UserInteraction)) {
-            
-            ScrollSnapAnimatorState&amp; otherState = scrollSnapPointState(otherAxis);
-            if (!otherState.averageInitialWheelDelta()) {
-                float offsetOnOtherAxis = m_client.scrollOffsetOnAxis(otherAxis);
-                float snapOffsetForOtherAxis = scaleFactor * closestSnapOffset&lt;LayoutUnit, float&gt;(otherState.m_snapOffsets, offsetOnOtherAxis, 0, otherState.m_activeSnapIndex);
-                if (offsetOnOtherAxis != snapOffsetForOtherAxis) {
-                    otherState.m_initialOffset = offsetOnOtherAxis;
-                    otherState.m_targetOffset = snapOffsetForOtherAxis;
-                    otherState.m_initialScrollDelta = 0;
-                    otherState.m_currentState = ScrollSnapState::Gliding;
-                }
-            }
-        }
-        
-    } else {
-        snapState.m_initialScrollDelta = initialWheelDelta;
-    }
-    initializeScrollSnapAnimationParameters();
-    startScrollSnapTimer();
</del><ins>+    setNearestScrollSnapIndexForAxisAndOffset(ScrollEventAxis::Horizontal, x);
+    setNearestScrollSnapIndexForAxisAndOffset(ScrollEventAxis::Vertical, y);
</ins><span class="cx"> }
</span><del>-
-void ScrollController::endScrollSnapAnimation(ScrollSnapState newState)
-{
-    ASSERT(newState == ScrollSnapState::DestinationReached || newState == ScrollSnapState::UserInteraction);
-    if (m_horizontalScrollSnapState)
-        m_horizontalScrollSnapState-&gt;m_currentState = newState;
-
-    if (m_verticalScrollSnapState)
-        m_verticalScrollSnapState-&gt;m_currentState = newState;
-
-    stopScrollSnapTimer();
-}
-
-void ScrollController::initializeScrollSnapAnimationParameters()
-{
-    if (!m_scrollSnapCurveState)
-        m_scrollSnapCurveState = std::make_unique&lt;ScrollSnapAnimationCurveState&gt;();
-    
-    bool isSnappingOnHorizontalAxis = isSnappingOnAxis(ScrollEventAxis::Horizontal);
-    bool isSnappingOnVerticalAxis = isSnappingOnAxis(ScrollEventAxis::Vertical);
-    FloatSize initialVector(isSnappingOnHorizontalAxis ? m_horizontalScrollSnapState-&gt;m_initialOffset : m_client.scrollOffsetOnAxis(ScrollEventAxis::Horizontal),
-        isSnappingOnVerticalAxis ? m_verticalScrollSnapState-&gt;m_initialOffset : m_client.scrollOffsetOnAxis(ScrollEventAxis::Vertical));
-    FloatSize targetVector(isSnappingOnHorizontalAxis ? m_horizontalScrollSnapState-&gt;m_targetOffset : m_client.scrollOffsetOnAxis(ScrollEventAxis::Horizontal),
-        isSnappingOnVerticalAxis ? m_verticalScrollSnapState-&gt;m_targetOffset : m_client.scrollOffsetOnAxis(ScrollEventAxis::Vertical));
-    FloatSize initialDelta(isSnappingOnHorizontalAxis ? m_horizontalScrollSnapState-&gt;m_initialScrollDelta : 0,
-        isSnappingOnVerticalAxis ? m_verticalScrollSnapState-&gt;m_initialScrollDelta : 0);
-
-    // Animate directly by default. This flag will be changed as necessary if interpolation is possible.
-    m_scrollSnapCurveState-&gt;shouldAnimateDirectlyToSnapPoint = true;
-    m_scrollSnapCurveState-&gt;initializeSnapProgressCurve(initialVector, targetVector, initialDelta);
-    if (isSnappingOnHorizontalAxis &amp;&amp; isSnappingOnVerticalAxis)
-        m_scrollSnapCurveState-&gt;initializeInterpolationCoefficientsIfNecessary(initialVector, targetVector, initialDelta);
-}
-    
-bool ScrollController::isSnappingOnAxis(ScrollEventAxis axis) const
-{
-    if (axis == ScrollEventAxis::Horizontal)
-        return m_horizontalScrollSnapState &amp;&amp; m_horizontalScrollSnapState-&gt;isSnapping();
-
-    return m_verticalScrollSnapState &amp;&amp; m_verticalScrollSnapState-&gt;isSnapping();
-}
-    
</del><span class="cx"> #endif
</span><del>-#endif
</del><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaScrollSnapAnimatorStateh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -31,9 +31,10 @@
</span><span class="cx"> #include &quot;AxisScrollSnapOffsets.h&quot;
</span><span class="cx"> #include &quot;FloatPoint.h&quot;
</span><span class="cx"> #include &quot;FloatSize.h&quot;
</span><del>-#include &quot;LayoutUnit.h&quot;
</del><ins>+#include &quot;LayoutPoint.h&quot;
</ins><span class="cx"> #include &quot;PlatformWheelEvent.h&quot;
</span><span class="cx"> #include &quot;ScrollTypes.h&quot;
</span><ins>+#include &quot;ScrollingMomentumCalculator.h&quot;
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="lines">@@ -44,57 +45,60 @@
</span><span class="cx">     UserInteraction
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-struct ScrollSnapAnimatorState {
-    ScrollSnapAnimatorState(ScrollEventAxis, const Vector&lt;LayoutUnit&gt;&amp;);
</del><ins>+class ScrollSnapAnimatorState {
+public:
+    Vector&lt;LayoutUnit&gt; snapOffsetsForAxis(ScrollEventAxis axis) const
+    {
+        return axis == ScrollEventAxis::Horizontal ? m_snapOffsetsX : m_snapOffsetsY;
+    }
</ins><span class="cx"> 
</span><del>-    void pushInitialWheelDelta(float);
-    float averageInitialWheelDelta() const;
-    void clearInitialWheelDeltaWindow();
-    bool isSnapping() const;
-    bool canReachTargetWithCurrentInitialScrollDelta() const;
-    bool wheelDeltaTrackingIsInProgress() const;
-    bool hasFinishedTrackingWheelDeltas() const;
-    float interpolatedOffsetAtProgress(float) const;
-    
-    static const int wheelDeltaWindowSize = 3;
</del><ins>+    void setSnapOffsetsForAxis(ScrollEventAxis axis, const Vector&lt;LayoutUnit&gt;&amp; snapOffsets)
+    {
+        if (axis == ScrollEventAxis::Horizontal)
+            m_snapOffsetsX = snapOffsets;
+        else
+            m_snapOffsetsY = snapOffsets;
+    }
</ins><span class="cx"> 
</span><del>-    Vector&lt;LayoutUnit&gt; m_snapOffsets;
-    ScrollEventAxis m_axis;
-    // Used to track both snapping and gliding behaviors.
-    ScrollSnapState m_currentState;
-    LayoutUnit m_initialOffset;
-    LayoutUnit m_targetOffset;
-    // Used to track gliding behavior.
-    LayoutUnit m_beginTrackingWheelDeltaOffset;
-    int m_numWheelDeltasTracked { 0 };
-    unsigned m_activeSnapIndex { 0 };
-    float m_wheelDeltaWindow[wheelDeltaWindowSize];
-    float m_initialScrollDelta { 0 };
-    bool m_shouldOverrideWheelEvent { false };
-};
-    
-/**
- * Stores state variables necessary to coordinate snapping animations between
- * horizontal and vertical axes.
- */
-struct ScrollSnapAnimationCurveState {
-    
-    void initializeSnapProgressCurve(const FloatSize&amp;, const FloatSize&amp;, const FloatSize&amp;);
-    void initializeInterpolationCoefficientsIfNecessary(const FloatSize&amp;, const FloatSize&amp;, const FloatSize&amp;);
-    FloatPoint interpolatedPositionAtProgress(float) const;
-    bool shouldCompleteSnapAnimationImmediatelyAtTime(double) const;
-    float animationProgressAtTime(double) const;
</del><ins>+    ScrollSnapState currentState() const { return m_currentState; }
</ins><span class="cx"> 
</span><del>-    bool shouldAnimateDirectlyToSnapPoint { false };
-    
</del><ins>+    unsigned activeSnapIndexForAxis(ScrollEventAxis axis) const
+    {
+        return axis == ScrollEventAxis::Horizontal ? m_activeSnapIndexX : m_activeSnapIndexY;
+    }
+
+    void setActiveSnapIndexForAxis(ScrollEventAxis axis, unsigned index)
+    {
+        if (axis == ScrollEventAxis::Horizontal)
+            m_activeSnapIndexX = index;
+        else
+            m_activeSnapIndexY = index;
+    }
+
+    FloatPoint currentAnimatedScrollOffset(bool&amp; isAnimationComplete) const;
+
+    // State transition helpers.
+    void transitionToSnapAnimationState(const FloatSize&amp; contentSize, const FloatSize&amp; viewportSize, float pageScale, const FloatPoint&amp; initialOffset);
+    void transitionToGlideAnimationState(const FloatSize&amp; contentSize, const FloatSize&amp; viewportSize, float pageScale, const FloatPoint&amp; initialOffset, const FloatPoint&amp; initialVelocity, const FloatSize&amp; initialDelta);
+    void transitionToUserInteractionState();
+    void transitionToDestinationReachedState();
+
</ins><span class="cx"> private:
</span><ins>+    float targetOffsetForStartOffset(ScrollEventAxis, float maxScrollOffset, float startOffset, float pageScale, float delta, unsigned&amp; outActiveSnapIndex) const;
+    void teardownAnimationForState(ScrollSnapState);
+    void setupAnimationForState(ScrollSnapState, const FloatSize&amp; contentSize, const FloatSize&amp; viewportSize, float pageScale, const FloatPoint&amp; initialOffset, const FloatPoint&amp; initialVelocity, const FloatSize&amp; initialDelta);
+
+    ScrollSnapState m_currentState { ScrollSnapState::UserInteraction };
+
+    Vector&lt;LayoutUnit&gt; m_snapOffsetsX;
+    unsigned m_activeSnapIndexX { 0 };
+    Vector&lt;LayoutUnit&gt; m_snapOffsetsY;
+    unsigned m_activeSnapIndexY { 0 };
+
</ins><span class="cx">     double m_startTime { 0 };
</span><del>-    float m_snapAnimationCurveMagnitude { 0 };
-    float m_snapAnimationDecayFactor { 0 };
-    FloatSize m_snapAnimationCurveCoefficients[4] { };
</del><ins>+    std::unique_ptr&lt;ScrollingMomentumCalculator&gt; m_momentumCalculator;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> #endif // ENABLE(CSS_SCROLL_SNAP)
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaScrollSnapAnimatorStatemm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -26,185 +26,87 @@
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> #include &quot;ScrollSnapAnimatorState.h&quot;
</span><span class="cx"> #include &lt;wtf/CurrentTime.h&gt;
</span><ins>+#include &lt;wtf/MathExtras.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-ScrollSnapAnimatorState::ScrollSnapAnimatorState(ScrollEventAxis axis, const Vector&lt;LayoutUnit&gt;&amp; snapOffsets)
-    : m_snapOffsets(snapOffsets)
-    , m_axis(axis)
-    , m_currentState(ScrollSnapState::DestinationReached)
-    , m_initialOffset(0)
-    , m_targetOffset(0)
-    , m_beginTrackingWheelDeltaOffset(0)
</del><ins>+static const float inertialScrollPredictionFactor = 10;
+static inline float projectedInertialScrollDistance(float initialWheelDelta)
</ins><span class="cx"> {
</span><ins>+    return inertialScrollPredictionFactor * initialWheelDelta;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ScrollSnapAnimatorState::pushInitialWheelDelta(float wheelDelta)
</del><ins>+void ScrollSnapAnimatorState::transitionToSnapAnimationState(const FloatSize&amp; contentSize, const FloatSize&amp; viewportSize, float pageScale, const FloatPoint&amp; initialOffset)
</ins><span class="cx"> {
</span><del>-    if (m_numWheelDeltasTracked &lt; wheelDeltaWindowSize)
-        m_wheelDeltaWindow[m_numWheelDeltasTracked++] = wheelDelta;
</del><ins>+    setupAnimationForState(ScrollSnapState::Snapping, contentSize, viewportSize, pageScale, initialOffset, { }, { });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-float ScrollSnapAnimatorState::averageInitialWheelDelta() const
</del><ins>+void ScrollSnapAnimatorState::transitionToGlideAnimationState(const FloatSize&amp; contentSize, const FloatSize&amp; viewportSize, float pageScale, const FloatPoint&amp; initialOffset, const FloatPoint&amp; initialVelocity, const FloatSize&amp; initialDelta)
</ins><span class="cx"> {
</span><del>-    if (!m_numWheelDeltasTracked)
-        return 0;
-
-    float sum = 0;
-    int numZeroDeltas = 0;
-    for (int i = 0; i &lt; m_numWheelDeltasTracked; ++i) {
-        sum += m_wheelDeltaWindow[i];
-        if (!m_wheelDeltaWindow[i])
-            numZeroDeltas++;
-    }
-
-    return m_numWheelDeltasTracked == numZeroDeltas ? 0 : sum / (m_numWheelDeltasTracked - numZeroDeltas);
</del><ins>+    setupAnimationForState(ScrollSnapState::Gliding, contentSize, viewportSize, pageScale, initialOffset, initialVelocity, initialDelta);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ScrollSnapAnimatorState::clearInitialWheelDeltaWindow()
</del><ins>+void ScrollSnapAnimatorState::setupAnimationForState(ScrollSnapState state, const FloatSize&amp; contentSize, const FloatSize&amp; viewportSize, float pageScale, const FloatPoint&amp; initialOffset, const FloatPoint&amp; initialVelocity, const FloatSize&amp; initialDelta)
</ins><span class="cx"> {
</span><del>-    for (int i = 0; i &lt; m_numWheelDeltasTracked; ++i)
-        m_wheelDeltaWindow[i] = 0;
</del><ins>+    ASSERT(state == ScrollSnapState::Snapping || state == ScrollSnapState::Gliding);
+    if (m_currentState == state)
+        return;
</ins><span class="cx"> 
</span><del>-    m_numWheelDeltasTracked = 0;
</del><ins>+    float targetOffsetX = targetOffsetForStartOffset(ScrollEventAxis::Horizontal, contentSize.width() - viewportSize.width(), initialOffset.x(), pageScale, initialDelta.width(), m_activeSnapIndexX);
+    float targetOffsetY = targetOffsetForStartOffset(ScrollEventAxis::Vertical, contentSize.height() - viewportSize.height(), initialOffset.y(), pageScale, initialDelta.height(), m_activeSnapIndexY);
+    m_momentumCalculator = ScrollingMomentumCalculator::create(viewportSize, contentSize, initialOffset, FloatPoint(targetOffsetX, targetOffsetY), initialDelta, initialVelocity);
+    m_startTime = monotonicallyIncreasingTime();
+    m_currentState = state;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool ScrollSnapAnimatorState::isSnapping() const
</del><ins>+void ScrollSnapAnimatorState::transitionToUserInteractionState()
</ins><span class="cx"> {
</span><del>-    return m_currentState == ScrollSnapState::Gliding || m_currentState == ScrollSnapState::Snapping;
</del><ins>+    teardownAnimationForState(ScrollSnapState::UserInteraction);
</ins><span class="cx"> }
</span><del>-    
-bool ScrollSnapAnimatorState::canReachTargetWithCurrentInitialScrollDelta() const
</del><ins>+
+void ScrollSnapAnimatorState::transitionToDestinationReachedState()
</ins><span class="cx"> {
</span><del>-    if (m_initialOffset == m_targetOffset || !m_initialScrollDelta)
-        return true;
-    
-    return m_initialOffset &lt; m_targetOffset ? m_initialScrollDelta &gt; 0 : m_initialScrollDelta &lt; 0;
</del><ins>+    teardownAnimationForState(ScrollSnapState::DestinationReached);
</ins><span class="cx"> }
</span><del>-    
-bool ScrollSnapAnimatorState::wheelDeltaTrackingIsInProgress() const
-{
-    return m_numWheelDeltasTracked &amp;&amp; m_numWheelDeltasTracked &lt; wheelDeltaWindowSize;
-}
</del><span class="cx"> 
</span><del>-bool ScrollSnapAnimatorState::hasFinishedTrackingWheelDeltas() const
</del><ins>+void ScrollSnapAnimatorState::teardownAnimationForState(ScrollSnapState state)
</ins><span class="cx"> {
</span><del>-    return m_numWheelDeltasTracked == wheelDeltaWindowSize;
-}
</del><ins>+    ASSERT(state == ScrollSnapState::UserInteraction || state == ScrollSnapState::DestinationReached);
+    if (m_currentState == state)
+        return;
</ins><span class="cx"> 
</span><del>-float ScrollSnapAnimatorState::interpolatedOffsetAtProgress(float progress) const
-{
-    progress = std::max(0.0f, std::min(1.0f, progress));
-    return m_initialOffset + progress * (m_targetOffset - m_initialOffset);
</del><ins>+    m_momentumCalculator = nullptr;
+    m_startTime = 0;
+    m_currentState = state;
</ins><span class="cx"> }
</span><del>-    
-static const int maxNumScrollSnapParameterEstimationIterations = 10;
-static const float scrollSnapDecayFactorConvergenceThreshold = 0.001;
-static const float initialScrollSnapCurveMagnitude = 1.1;
-static const float minScrollSnapInitialProgress = 0.15;
-static const float maxScrollSnapInitialProgress = 0.5;
-static const double scrollSnapAnimationDuration = 0.5;
</del><span class="cx"> 
</span><del>-/**
- * Computes and sets parameters required for tracking the progress of a snap animation curve, interpolated
- * or linear. The progress curve s(t) maps time t to progress s; both variables are in the interval [0, 1].
- * The time input t is 0 when the current time is the start of the animation, t = m_startTime, and 1 when the
- * current time is at or after the end of the animation, t = m_startTime + m_scrollSnapAnimationDuration.
- *
- * In this exponential progress model, s(t) = A - A * b^(-kt), where k = 60T is the number of frames in the
- * animation (assuming 60 FPS and an animation duration of T) and A, b are reals greater than or equal to 1.
- * Also note that we are given the initial progress, a value indicating the portion of the curve which our
- * initial scroll delta takes us. This is important when matching the initial speed of the animation to the
- * user's initial momentum scrolling speed. Let this initial progress amount equal v_0. I clamp this initial
- * progress amount to a minimum or maximum value.
- *
- * A is referred to as the curve magnitude, while b is referred to as the decay factor. We solve for A and b,
- * keeping the following constraints in mind:
- *     1. s(0) = 0
- *     2. s(1) = 1
- *     3. s(1/k) = v_0
- *
- * First, observe that s(0) = 0 holds for appropriate values of A, b. Solving for the remaining constraints
- * yields a nonlinear system of two equations. In lieu of a purely analytical solution, an alternating
- * optimization scheme is used to approximate A and b. This technique converges quickly (within 5 iterations
- * or so) for appropriate values of v_0. The optimization terminates early when the decay factor changes by
- * less than a threshold between one iteration and the next.
- */
-void ScrollSnapAnimationCurveState::initializeSnapProgressCurve(const FloatSize&amp; initialVector, const FloatSize&amp; targetVector, const FloatSize&amp; initialDelta)
</del><ins>+FloatPoint ScrollSnapAnimatorState::currentAnimatedScrollOffset(bool&amp; isAnimationComplete) const
</ins><span class="cx"> {
</span><del>-    float initialProgress = std::max(minScrollSnapInitialProgress, std::min(initialDelta.diagonalLength() / (targetVector - initialVector).diagonalLength(), maxScrollSnapInitialProgress));
-    float previousDecayFactor = 1.0f;
-    m_snapAnimationCurveMagnitude = initialScrollSnapCurveMagnitude;
-    for (int i = 0; i &lt; maxNumScrollSnapParameterEstimationIterations; ++i) {
-        m_snapAnimationDecayFactor = m_snapAnimationCurveMagnitude / (m_snapAnimationCurveMagnitude - initialProgress);
-        m_snapAnimationCurveMagnitude = 1.0f / (1.0f - std::pow(m_snapAnimationDecayFactor, -60.0f * scrollSnapAnimationDuration));
-        if (std::abs(m_snapAnimationDecayFactor - previousDecayFactor) &lt; scrollSnapDecayFactorConvergenceThreshold)
-            break;
-        
-        previousDecayFactor = m_snapAnimationDecayFactor;
</del><ins>+    if (!m_momentumCalculator) {
+        isAnimationComplete = true;
+        return { };
</ins><span class="cx">     }
</span><del>-    m_startTime = monotonicallyIncreasingTime();
</del><ins>+
+    double elapsedTime = monotonicallyIncreasingTime() - m_startTime;
+    isAnimationComplete = elapsedTime &gt;= m_momentumCalculator-&gt;animationDuration();
+    return m_momentumCalculator-&gt;scrollOffsetAfterElapsedTime(elapsedTime);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-/**
- * Computes and sets coefficients required for interpolated snapping when scrolling in 2 dimensions, given
- * initial conditions (the initial and target vectors, along with the initial wheel delta as a vector). The
- * path is a cubic Bezier curve of the form p(s) = INITIAL + (C_1 * s) + (C_2 * s^2) + (C_3 * s^3) where each
- * C_i is a 2D vector and INITIAL is the vector representing the initial scroll offset. s is a real in the
- * interval [0, 1] indicating the &quot;progress&quot; of the curve (i.e. how much of the curve has been traveled).
- *
- * The curve has 4 control points, the first and last of which are the initial and target points, respectively.
- * The distances between adjacent control points are constrained to be the same, making the convex hull an
- * isosceles trapezoid with 3 sides of equal length. Additionally, the vector from the first control point to
- * the second points in the same direction as the initial scroll delta. These constraints ensure two properties:
- *     1. The direction of the snap animation at s=0 will be equal to the direction of the initial scroll delta.
- *     2. Points at regular intervals of s will be evenly spread out.
- *
- * If the initial scroll direction is orthogonal to or points in the opposite direction as the vector from the
- * initial point to the target point, initialization returns early and sets the curve to animate directly to the
- * snap point without interpolation.
- */
-void ScrollSnapAnimationCurveState::initializeInterpolationCoefficientsIfNecessary(const FloatSize&amp; initialVector, const FloatSize&amp; targetVector, const FloatSize&amp; initialDelta)
</del><ins>+float ScrollSnapAnimatorState::targetOffsetForStartOffset(ScrollEventAxis axis, float maxScrollOffset, float startOffset, float pageScale, float initialDelta, unsigned&amp; outActiveSnapIndex) const
</ins><span class="cx"> {
</span><del>-    FloatSize startToEndVector = targetVector - initialVector;
-    float startToEndDistance = startToEndVector.diagonalLength();
-    float initialDeltaMagnitude = initialDelta.diagonalLength();
-    float cosTheta = initialDelta.isZero() ? 0 : (initialDelta.width() * startToEndVector.width() + initialDelta.height() * startToEndVector.height()) / (std::max(1.0f, initialDeltaMagnitude) * startToEndDistance);
-    if (cosTheta &lt;= 0)
-        return;
-    
-    float sideLength = startToEndDistance / (2.0f * cosTheta + 1.0f);
-    FloatSize controlVector1 = initialVector + sideLength * initialDelta / initialDeltaMagnitude;
-    FloatSize controlVector2 = controlVector1 + (sideLength * startToEndVector / startToEndDistance);
-    m_snapAnimationCurveCoefficients[0] = initialVector;
-    m_snapAnimationCurveCoefficients[1] = 3 * (controlVector1 - initialVector);
-    m_snapAnimationCurveCoefficients[2] = 3 * (initialVector - 2 * controlVector1 + controlVector2);
-    m_snapAnimationCurveCoefficients[3] = 3 * (controlVector1 - controlVector2) - initialVector + targetVector;
-    shouldAnimateDirectlyToSnapPoint = false;
-}
-    
-FloatPoint ScrollSnapAnimationCurveState::interpolatedPositionAtProgress(float progress) const
-{
-    ASSERT(!shouldAnimateDirectlyToSnapPoint);
-    progress = std::max(0.0f, std::min(1.0f, progress));
-    FloatPoint interpolatedPoint(0.0f, 0.0f);
-    for (int i = 0; i &lt; 4; ++i)
-        interpolatedPoint += std::pow(progress, i) * m_snapAnimationCurveCoefficients[i];
-    
-    return interpolatedPoint;
-}
-    
-bool ScrollSnapAnimationCurveState::shouldCompleteSnapAnimationImmediatelyAtTime(double time) const
-{
-    return m_startTime + scrollSnapAnimationDuration &lt; time;
-}
</del><ins>+    auto snapOffsets = snapOffsetsForAxis(axis);
+    if (!snapOffsets.size()) {
+        outActiveSnapIndex = 0;
+        return clampTo&lt;float&gt;(startOffset, 0, maxScrollOffset);
+    }
</ins><span class="cx"> 
</span><del>-float ScrollSnapAnimationCurveState::animationProgressAtTime(double time) const
-{
-    float timeProgress = std::max(0.0, std::min(1.0, (time - m_startTime) / scrollSnapAnimationDuration));
-    return std::min(1.0, m_snapAnimationCurveMagnitude * (1.0 - std::pow(m_snapAnimationDecayFactor, -60.0f * scrollSnapAnimationDuration * timeProgress)));
</del><ins>+    float projectedDestination = (startOffset + projectedInertialScrollDistance(initialDelta)) / pageScale;
+    float targetOffset = closestSnapOffset&lt;LayoutUnit, float&gt;(snapOffsets, projectedDestination, initialDelta, outActiveSnapIndex);
+    targetOffset = clampTo&lt;float&gt;(targetOffset, snapOffsets.first(), snapOffsets.last());
+    targetOffset = clampTo&lt;float&gt;(targetOffset, 0, maxScrollOffset);
+    return pageScale * targetOffset;
</ins><span class="cx"> }
</span><span class="cx">     
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformspimacNSScrollingMomentumCalculatorSPIh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/platform/spi/mac/NSScrollingMomentumCalculatorSPI.h (0 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/spi/mac/NSScrollingMomentumCalculatorSPI.h                                (rev 0)
+++ trunk/Source/WebCore/platform/spi/mac/NSScrollingMomentumCalculatorSPI.h        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -0,0 +1,46 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if HAVE(NSSCROLLING_FILTERS)
+
+#if USE(APPLE_INTERNAL_SDK)
+
+#import &lt;AppKit/NSScrollingMomentumCalculator_Private.h&gt;
+
+#else
+
+@interface _NSScrollingMomentumCalculator : NSObject;
+
+- (instancetype)initWithInitialOrigin:(NSPoint)origin velocity:(NSPoint)velocity documentFrame:(NSRect)docFrame constrainedClippingOrigin:(NSPoint)constrainedClippingOrigin clippingSize:(NSSize)clipViewSize tolerance:(NSSize)tolerance;
+- (NSPoint)positionAfterDuration:(NSTimeInterval)duration;
+
+@property (atomic) NSPoint destinationOrigin;
+@property (readonly) NSTimeInterval durationUntilStop;
+
+@end
+
+#endif /* USE(APPLE_INTERNAL_SDK) */
+
+#endif /* HAVE(NSSCROLLING_FILTERS) */
</ins></span></pre></div>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebKit2/ChangeLog        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2016-11-29  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Scroll snapping on Mac should use AppKit animations
+        https://bugs.webkit.org/show_bug.cgi?id=147261
+        &lt;rdar://problem/29395293&gt;
+
+        Reviewed by Brent Fulgham.
+
+        Add some logic to plumb filtered wheel velocity over to WebCore in the case of mainframe scrolling. See
+        WebCore/ChangeLog for more details.
+
+        * WebProcess/WebPage/EventDispatcher.cpp:
+        (WebKit::EventDispatcher::wheelEvent):
+
</ins><span class="cx"> 2016-11-21  Brian Burg  &lt;bburg@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Web Automation: add ObjC SPI to set whether a page is controlled by automation
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPageEventDispatchercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/EventDispatcher.cpp (209069 => 209070)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/EventDispatcher.cpp        2016-11-29 16:05:17 UTC (rev 209069)
+++ trunk/Source/WebKit2/WebProcess/WebPage/EventDispatcher.cpp        2016-11-29 16:10:55 UTC (rev 209070)
</span><span class="lines">@@ -107,7 +107,7 @@
</span><span class="cx">     if (m_recentWheelEventDeltaFilter-&gt;isFilteringDeltas()) {
</span><span class="cx">         m_recentWheelEventDeltaFilter-&gt;updateFromDelta(FloatSize(platformWheelEvent.deltaX(), platformWheelEvent.deltaY()));
</span><span class="cx">         FloatSize filteredDelta = m_recentWheelEventDeltaFilter-&gt;filteredDelta();
</span><del>-        platformWheelEvent = platformWheelEvent.copyWithDeltas(filteredDelta.width(), filteredDelta.height());
</del><ins>+        platformWheelEvent = platformWheelEvent.copyWithDeltasAndVelocity(filteredDelta.width(), filteredDelta.height(), m_recentWheelEventDeltaFilter-&gt;filteredVelocity());
</ins><span class="cx">     }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>