<!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>[210560] 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/210560">210560</a></dd>
<dt>Author</dt> <dd>wenson_hsieh@apple.com</dd>
<dt>Date</dt> <dd>2017-01-10 14:26:56 -0800 (Tue, 10 Jan 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Implement &quot;proximity&quot; scroll snapping
https://bugs.webkit.org/show_bug.cgi?id=135994
&lt;rdar://problem/18162418&gt;

Reviewed by Dean Jackson.

Source/WebCore:

Adds support for proximity scroll snapping. To do this, we introduce scroll offset ranges, a list of scroll
offset ranges that are plumbed alongside the list of scroll snap offsets. Similar to a snap offset, a snap
offset range contains scroll offsets on which scrolling is allowed to come to a rest within a scroll snapping
container. However, unlike normal snap offsets, scrolling may only come to rest within a snap offset range if
the predicted scroll offset already lies within the range. The new algorithm for selecting a target scroll snap
position given a destination offset is now:

-   If the scroll destination lies within a snap offset range, return the scroll destination
-   Otherwise, compute the nearest lower/upper snap offsets and lower/upper snap offset ranges
-   If scrolling ended with no velocity, return the nearest snap offset
-   If scrolling ended with positive velocity, choose the upper snap offset only if there is no snap offset
    range in between the scroll destination and the snap offset; else, choose the lower snap offset
-   If scrolling ended with negative velocity, choose the lower snap offset only if there is no snap offset
    range in between the scroll destination and the snap offset; else, choose the upper snap offset

The extra rule accounting for scroll offset ranges in between the scroll destination and a potential snap offset
handles the corner case where the user scrolls with momentum very lightly away from a snap offset, such that the
predicted scroll destination is still within proximity of the snap offset. In this case, the regular (mandatory
scroll snapping) behavior would be to snap to the next offset in the direction of momentum scrolling, but
instead, it is more intuitive to return to the original snap position.

We also move scrolling prediction logic into ScrollingMomentumCalculator and adopt the platform
_NSScrollingMomentumCalculator's destinationOrigin property when computing the predicted scroll destination.
Previously, we were simply multiplying by an empirically-derived constant to approximate the scroll destination,
but now that we are supporting proximity scroll snapping, we need more exact scroll destinaton prediction in
order to make sure that scrolling to a snap offset range feels natural.

Tests: tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity.html
       tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe.html
       tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html

* WebCore.xcodeproj/project.pbxproj:
* page/scrolling/AsyncScrollingCoordinator.cpp:
(WebCore::setStateScrollingNodeSnapOffsetsAsFloat):
(WebCore::AsyncScrollingCoordinator::updateOverflowScrollingNode):
(WebCore::AsyncScrollingCoordinator::updateScrollSnapPropertiesWithFrameView):

Make boilerplate changes to plumb lists of horizontal and vertical snap offset ranges alongside the lists of
horizontal and vertical snap offsets.

* page/scrolling/AxisScrollSnapOffsets.cpp:
(WebCore::snapOffsetRangesToString):
(WebCore::indicesOfNearestSnapOffsetRanges):
(WebCore::indicesOfNearestSnapOffsets):
(WebCore::adjustAxisSnapOffsetsForScrollExtent):
(WebCore::computeAxisProximitySnapOffsetRanges):
(WebCore::updateSnapOffsetsForScrollableArea):
(WebCore::closestSnapOffset):

Adjust the snap offset selection algorithm to take snap offset ranges into account. See above for more details.
Additionally, augment snap offset update logic to emit snap offset ranges for proximity scroll snapping. To do
this, we run the following steps on the final list of processed snap offsets:
-   Compute the proximity distance, which (for now) is arbitrarily 0.3 * the length or width of the scroll snap
    port, depending on whether scroll snapping is taking place in the X or Y axis.
-   For each pair of adjacent snap offsets, if they are more than 2 * proximity distance away from each other,
    emit a snap offset range starting from (lower snap offset + proximity distance) and ending on (upper snap
    offset + proximity distance).

* page/scrolling/AxisScrollSnapOffsets.h:
(WebCore::closestSnapOffset): Deleted.
* page/scrolling/ScrollSnapOffsetsInfo.h:

Introduce ScrollSnapOffsetsInfo, a struct which contains data relevant to scroll snapping. This includes
vertical and horizontal snap offsets, as well as vertical and horizontal snap offset ranges. Snap offset ranges
consist of a vector of ranges of scroll offsets.

* page/scrolling/ScrollingCoordinator.h:
* page/scrolling/ScrollingMomentumCalculator.cpp:
(WebCore::projectedInertialScrollDistance):
(WebCore::ScrollingMomentumCalculator::ScrollingMomentumCalculator):
(WebCore::ScrollingMomentumCalculator::setRetargetedScrollOffset):
(WebCore::ScrollingMomentumCalculator::predictedDestinationOffset):
(WebCore::ScrollingMomentumCalculator::create):
(WebCore::ScrollingMomentumCalculator::setPlatformMomentumScrollingPredictionEnabled):
(WebCore::BasicScrollingMomentumCalculator::BasicScrollingMomentumCalculator):
(WebCore::BasicScrollingMomentumCalculator::linearlyInterpolatedOffsetAtProgress):
(WebCore::BasicScrollingMomentumCalculator::initializeInterpolationCoefficientsIfNecessary):
(WebCore::BasicScrollingMomentumCalculator::initializeSnapProgressCurve):
* page/scrolling/ScrollingMomentumCalculator.h:
(WebCore::ScrollingMomentumCalculator::retargetedScrollOffset):
(WebCore::ScrollingMomentumCalculator::retargetedScrollOffsetDidChange):

Currently, the ScrollingMomentumCalculator is responsible for taking an initial position, initial velocity, and
target position and animating the scroll offset from the initial to target position. Now, we refactor the
ScrollingMomentumCalculator interface to no longer take a target offset upon initialization, and instead compute
the predicted scroll destination given initial position and velocity; clients of the ScrollingMomentumCalculator
then use this predicted scroll destination to compute a retargeted scroll offset and then call
setRetargetedScrollOffset on the calculator, which sets up an animation curve to the new retargeted offset. This
allows both the AppKit-based scrolling momentum calculator and platform-invariant momentum calculator to be used
interchangeably, while still allowing them to compute a destination offset from initial parameters of the
scroll.

* page/scrolling/ScrollingStateScrollingNode.cpp:
(WebCore::ScrollingStateScrollingNode::ScrollingStateScrollingNode):
(WebCore::ScrollingStateScrollingNode::setHorizontalSnapOffsets):
(WebCore::ScrollingStateScrollingNode::setVerticalSnapOffsets):
(WebCore::ScrollingStateScrollingNode::setHorizontalSnapOffsetRanges):
(WebCore::ScrollingStateScrollingNode::setVerticalSnapOffsetRanges):
* page/scrolling/ScrollingStateScrollingNode.h:
(WebCore::ScrollingStateScrollingNode::horizontalSnapOffsets):
(WebCore::ScrollingStateScrollingNode::verticalSnapOffsets):
(WebCore::ScrollingStateScrollingNode::horizontalSnapOffsetRanges):
(WebCore::ScrollingStateScrollingNode::verticalSnapOffsetRanges):
* page/scrolling/ScrollingTreeScrollingNode.cpp:
(WebCore::ScrollingTreeScrollingNode::commitStateBeforeChildren):
(WebCore::ScrollingTreeScrollingNode::dumpProperties):
* page/scrolling/ScrollingTreeScrollingNode.h:
(WebCore::ScrollingTreeScrollingNode::horizontalSnapOffsets):
(WebCore::ScrollingTreeScrollingNode::verticalSnapOffsets):
(WebCore::ScrollingTreeScrollingNode::horizontalSnapOffsetRanges):
(WebCore::ScrollingTreeScrollingNode::verticalSnapOffsetRanges):

Add more boilerplate support for snap offset ranges.

* page/scrolling/mac/ScrollingMomentumCalculatorMac.h:
* page/scrolling/mac/ScrollingMomentumCalculatorMac.mm:
(WebCore::ScrollingMomentumCalculator::create):
(WebCore::ScrollingMomentumCalculator::setPlatformMomentumScrollingPredictionEnabled):
(WebCore::ScrollingMomentumCalculatorMac::ScrollingMomentumCalculatorMac):
(WebCore::ScrollingMomentumCalculatorMac::scrollOffsetAfterElapsedTime):
(WebCore::ScrollingMomentumCalculatorMac::predictedDestinationOffset):
(WebCore::ScrollingMomentumCalculatorMac::retargetedScrollOffsetDidChange):
(WebCore::ScrollingMomentumCalculatorMac::animationDuration):
(WebCore::ScrollingMomentumCalculatorMac::requiresMomentumScrolling):
(WebCore::ScrollingMomentumCalculatorMac::ensurePlatformMomentumCalculator):

Hook into AppKit momentum scroll offset prediction.

* page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm:
(WebCore::convertToLayoutUnits):
(WebCore::ScrollingTreeFrameScrollingNodeMac::commitStateBeforeChildren):
* platform/ScrollableArea.cpp:
(WebCore::ScrollableArea::ensureSnapOffsetsInfo):
(WebCore::ScrollableArea::horizontalSnapOffsets):
(WebCore::ScrollableArea::horizontalSnapOffsetRanges):
(WebCore::ScrollableArea::verticalSnapOffsetRanges):
(WebCore::ScrollableArea::verticalSnapOffsets):
(WebCore::ScrollableArea::setHorizontalSnapOffsets):
(WebCore::ScrollableArea::setVerticalSnapOffsets):
(WebCore::ScrollableArea::setHorizontalSnapOffsetRanges):
(WebCore::ScrollableArea::setVerticalSnapOffsetRanges):
(WebCore::ScrollableArea::clearHorizontalSnapOffsets):
(WebCore::ScrollableArea::clearVerticalSnapOffsets):
* platform/ScrollableArea.h:
(WebCore::ScrollableArea::horizontalSnapOffsets): Deleted.
(WebCore::ScrollableArea::verticalSnapOffsets): Deleted.
* platform/cocoa/ScrollController.h:
* platform/cocoa/ScrollController.mm:
(WebCore::ScrollController::processWheelEventForScrollSnap):

Fix an issue where initial scrolling velocity would be set to zero at the end of a drag gesture.

(WebCore::ScrollController::updateScrollSnapState):
(WebCore::ScrollController::updateScrollSnapPoints):
(WebCore::ScrollController::setNearestScrollSnapIndexForAxisAndOffset):
* platform/cocoa/ScrollSnapAnimatorState.h:
(WebCore::ScrollSnapAnimatorState::snapOffsetsForAxis):
(WebCore::ScrollSnapAnimatorState::snapOffsetRangesForAxis):
(WebCore::ScrollSnapAnimatorState::setSnapOffsetsAndPositionRangesForAxis):
(WebCore::ScrollSnapAnimatorState::setSnapOffsetsForAxis): Deleted.
* platform/cocoa/ScrollSnapAnimatorState.mm:
(WebCore::ScrollSnapAnimatorState::setupAnimationForState):
(WebCore::ScrollSnapAnimatorState::targetOffsetForStartOffset):
(WebCore::projectedInertialScrollDistance): Deleted.
* rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::updateScrollCoordinatedLayer):
* testing/Internals.cpp:
(WebCore::Internals::setPlatformMomentumScrollingPredictionEnabled):

Add a new hook for layout tests to force scrolling momentum calculators to use the platform-invariant momentum
scrolling prediction heuristic instead of the platform-dependent one.

(WebCore::Internals::scrollSnapOffsets):
* testing/Internals.h:
* testing/Internals.idl:

Source/WebKit2:

Adds boilerplate support for plumbing lists of snap offset ranges from the web process to the UI process
alongside the list of snap offsets.

* Shared/Scrolling/RemoteScrollingCoordinatorTransaction.cpp:
(ArgumentCoder&lt;ScrollingStateScrollingNode&gt;::encode):
(ArgumentCoder&lt;ScrollingStateScrollingNode&gt;::decode):
* Shared/WebCoreArgumentCoders.cpp:
(IPC::ArgumentCoder&lt;ScrollOffsetRange&lt;float&gt;&gt;::encode):
(IPC::ArgumentCoder&lt;ScrollOffsetRange&lt;float&gt;&gt;::decode):
* Shared/WebCoreArgumentCoders.h:
* UIProcess/Scrolling/ios/ScrollingTreeOverflowScrollingNodeIOS.mm:
(-[WKOverflowScrollViewDelegate scrollViewWillEndDragging:withVelocity:targetContentOffset:]):
* UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm:

Adjust mainframe proximity scroll snapping logic to not subtract out the top content inset when there is no
active snap offset (i.e. when snapping rests in a snap offset range). Attempting to subtract out the top inset
in this case caused the scroll offset to jump after ending a drag with no momentum in a snap offset range.

(WebKit::RemoteScrollingCoordinatorProxy::adjustTargetContentOffsetForSnapping):
(WebKit::RemoteScrollingCoordinatorProxy::shouldSnapForMainFrameScrolling):
(WebKit::RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling):

LayoutTests:

Adds 3 new layout tests for proximity scroll snapping. Also tweaks some existing tests that test scroll snapping
after scrolling with momentum to use the custom heuristic for predicting scroll destination instead of platform
momentum scrolling. This ensures that the results of our layout tests that depend on predicting momentum scroll
destination are consistent across runs.

* tiled-drawing/scrolling/latched-div-with-scroll-snap.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-iframe.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-hidden-scrollbars.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-horizontal.html:
* 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.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow-stateless.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html:
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html:

Force these tests to use platform-independent scrolling momentum prediction, by multiplying the last scroll
delta upon release by a constant factor.

* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity-expected.txt: Added.
* tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity.html: Added.

Tests that after changing scroll-snap-type from mandatory to proximity, swiping downwards no longer snaps the
scroll offset to the second box, but instead leaves the scroll offset somewhere in the middle of the first box.

* tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe-expected.txt: Added.
* tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe.html: Added.

Tests that when scroll-snap-type is proximity in the mainframe, scrolling slightly downwards snaps the scroll
offset back up to the top; scrolling somewhere in the middle of the first box does not snap the scroll offset;
and scrolling near the end of the first box snaps the scroll offset to the second box.

* tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow-expected.txt: Added.
* tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html: Added.

Similar to scroll-snap-proximity-mainframe.html, except for overflow scrolling instead of the mainframe.

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

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollinglatcheddivwithscrollsnaphtml">trunk/LayoutTests/tiled-drawing/scrolling/latched-div-with-scroll-snap.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapiframehtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-iframe.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatory2doverflowhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorybordershtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatoryhiddenscrollbarshtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-hidden-scrollbars.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframehorizontalhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-horizontal.html</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="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeverticalthenhorizontalhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.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="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatoryoverflowstatelesshtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow-stateless.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatoryoverflowhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorypaddinghtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatoryrotatedhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapscrollingjumpstotophtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.html</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="#trunkSourceWebCorepagescrollingAsyncScrollingCoordinatorcpp">trunk/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp</a></li>
<li><a href="#trunkSourceWebCorepagescrollingAxisScrollSnapOffsetscpp">trunk/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.cpp</a></li>
<li><a href="#trunkSourceWebCorepagescrollingAxisScrollSnapOffsetsh">trunk/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.h</a></li>
<li><a href="#trunkSourceWebCorepagescrollingScrollingCoordinatorh">trunk/Source/WebCore/page/scrolling/ScrollingCoordinator.h</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="#trunkSourceWebCorepagescrollingScrollingStateScrollingNodecpp">trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp</a></li>
<li><a href="#trunkSourceWebCorepagescrollingScrollingStateScrollingNodeh">trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h</a></li>
<li><a href="#trunkSourceWebCorepagescrollingScrollingTreeScrollingNodecpp">trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp</a></li>
<li><a href="#trunkSourceWebCorepagescrollingScrollingTreeScrollingNodeh">trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.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="#trunkSourceWebCorepagescrollingmacScrollingTreeFrameScrollingNodeMacmm">trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformScrollableAreacpp">trunk/Source/WebCore/platform/ScrollableArea.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformScrollableAreah">trunk/Source/WebCore/platform/ScrollableArea.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="#trunkSourceWebCorerenderingRenderLayerCompositorcpp">trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp</a></li>
<li><a href="#trunkSourceWebCoretestingInternalscpp">trunk/Source/WebCore/testing/Internals.cpp</a></li>
<li><a href="#trunkSourceWebCoretestingInternalsh">trunk/Source/WebCore/testing/Internals.h</a></li>
<li><a href="#trunkSourceWebCoretestingInternalsidl">trunk/Source/WebCore/testing/Internals.idl</a></li>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2SharedScrollingRemoteScrollingCoordinatorTransactioncpp">trunk/Source/WebKit2/Shared/Scrolling/RemoteScrollingCoordinatorTransaction.cpp</a></li>
<li><a href="#trunkSourceWebKit2SharedWebCoreArgumentCoderscpp">trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp</a></li>
<li><a href="#trunkSourceWebKit2SharedWebCoreArgumentCodersh">trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessScrollingiosScrollingTreeOverflowScrollingNodeIOSmm">trunk/Source/WebKit2/UIProcess/Scrolling/ios/ScrollingTreeOverflowScrollingNodeIOS.mm</a></li>
<li><a href="#trunkSourceWebKit2UIProcessiosRemoteScrollingCoordinatorProxyIOSmm">trunk/Source/WebKit2/UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorythenproximityexpectedtxt">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity-expected.txt</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorythenproximityhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapproximitymainframeexpectedtxt">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe-expected.txt</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapproximitymainframehtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe.html</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapproximityoverflowexpectedtxt">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow-expected.txt</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapproximityoverflowhtml">trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html</a></li>
<li><a href="#trunkSourceWebCorepagescrollingScrollSnapOffsetsInfoh">trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/ChangeLog        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -1,3 +1,54 @@
</span><ins>+2017-01-10  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Implement &quot;proximity&quot; scroll snapping
+        https://bugs.webkit.org/show_bug.cgi?id=135994
+        &lt;rdar://problem/18162418&gt;
+
+        Reviewed by Dean Jackson.
+
+        Adds 3 new layout tests for proximity scroll snapping. Also tweaks some existing tests that test scroll snapping
+        after scrolling with momentum to use the custom heuristic for predicting scroll destination instead of platform
+        momentum scrolling. This ensures that the results of our layout tests that depend on predicting momentum scroll
+        destination are consistent across runs.
+
+        * tiled-drawing/scrolling/latched-div-with-scroll-snap.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-iframe.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-hidden-scrollbars.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-horizontal.html:
+        * 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.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow-stateless.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html:
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html:
+
+        Force these tests to use platform-independent scrolling momentum prediction, by multiplying the last scroll
+        delta upon release by a constant factor.
+
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity-expected.txt: Added.
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity.html: Added.
+
+        Tests that after changing scroll-snap-type from mandatory to proximity, swiping downwards no longer snaps the
+        scroll offset to the second box, but instead leaves the scroll offset somewhere in the middle of the first box.
+
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe-expected.txt: Added.
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe.html: Added.
+
+        Tests that when scroll-snap-type is proximity in the mainframe, scrolling slightly downwards snaps the scroll
+        offset back up to the top; scrolling somewhere in the middle of the first box does not snap the scroll offset;
+        and scrolling near the end of the first box snaps the scroll offset to the second box.
+
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow-expected.txt: Added.
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html: Added.
+
+        Similar to scroll-snap-proximity-mainframe.html, except for overflow scrolling instead of the mainframe.
+
+        * tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.html:
+
</ins><span class="cx"> 2017-01-10  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Make Event.initEvent()'s first parameter mandatory
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollinglatcheddivwithscrollsnaphtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/latched-div-with-scroll-snap.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/latched-div-with-scroll-snap.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/latched-div-with-scroll-snap.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -74,6 +74,7 @@
</span><span class="cx">             return new Promise(resolve =&gt; {
</span><span class="cx">                 write(`* Swiping ${momentum ? &quot;with&quot; : &quot;without&quot;} momentum in ${element.id} with scroll offset ${element.scrollLeft}`);
</span><span class="cx">                 let location = locationInWindowCoordinates(element);
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 eventSender.monitorWheelEvents();
</span><span class="cx">                 eventSender.mouseMoveTo(location.x, location.y);
</span><span class="cx">                 eventSender.mouseScrollByWithWheelAndMomentumPhases(-1, 0, &quot;began&quot;, &quot;none&quot;);
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapiframehtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-iframe.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-iframe.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-iframe.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -147,6 +147,7 @@
</span><span class="cx">         {
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(function() { scrollGlideTest('horizontalTarget') }, 0);
</span><span class="cx">             }
</span><span class="cx">         }
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatory2doverflowhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-2d-overflow.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -123,6 +123,7 @@
</span><span class="cx">         function onLoad() {
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(scrollGlideTest, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocation = document.getElementById(&quot;snap-from&quot;);
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorybordershtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-borders.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -177,6 +177,7 @@
</span><span class="cx">         {
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(function() { scrollGlideTest('horizontalTarget') }, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocationH = document.getElementById('itemH0');
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatoryhiddenscrollbarshtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-hidden-scrollbars.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-hidden-scrollbars.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-hidden-scrollbars.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -44,6 +44,7 @@
</span><span class="cx">                 return;
</span><span class="cx">             }
</span><span class="cx"> 
</span><ins>+            internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">             eventSender.monitorWheelEvents();
</span><span class="cx">             eventSender.mouseMoveTo(250, 250);
</span><span class="cx">             eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, &quot;began&quot;, &quot;none&quot;);
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframehorizontalhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-horizontal.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-horizontal.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-horizontal.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -91,6 +91,7 @@
</span><span class="cx"> 
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(scrollGlideTest, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocation = document.getElementById('item0');
</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 (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-horizontal.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-horizontal.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -91,6 +91,7 @@
</span><span class="cx"> 
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(scrollGlideTest, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocation = document.getElementById('item0');
</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 (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-vertical.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-slow-vertical.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -90,6 +90,7 @@
</span><span class="cx">         function onLoad() {
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(scrollGlideTest, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocation = document.getElementById('item0');
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorymainframeverticalthenhorizontalhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical-then-horizontal.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -52,6 +52,7 @@
</span><span class="cx">         function onLoad() {
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(scrollSnapTest, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocation = document.getElementById('item0');
</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 (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -91,6 +91,7 @@
</span><span class="cx"> 
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(scrollGlideTest, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocation = document.getElementById('item0');
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatoryoverflowstatelesshtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow-stateless.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow-stateless.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow-stateless.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -68,6 +68,7 @@
</span><span class="cx">         {
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(scrollSnapTest, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocationH = document.getElementById(&quot;item0&quot;);
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatoryoverflowhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-overflow.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -167,6 +167,7 @@
</span><span class="cx">         {
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(function() { scrollGlideTest('horizontalTarget') }, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocationH = document.getElementById('itemH0');
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorypaddinghtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-padding.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -177,6 +177,7 @@
</span><span class="cx">         {
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(function() { scrollGlideTest('horizontalTarget') }, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocationH = document.getElementById('itemH0');
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatoryrotatedhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-rotated.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -169,6 +169,7 @@
</span><span class="cx">         {
</span><span class="cx">             if (window.eventSender) {
</span><span class="cx">                 eventSender.monitorWheelEvents();
</span><ins>+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">                 setTimeout(function() { scrollGlideTest('horizontalTarget') }, 0);
</span><span class="cx">             } else {
</span><span class="cx">                 var messageLocationH = document.getElementById('itemH0');
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorythenproximityexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity-expected.txt (0 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity-expected.txt                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity-expected.txt        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -0,0 +1,7 @@
</span><ins>+Scrolling in container with scroll-snap-type: y mandatory
+- Did the scrolling snap to the top? NO
+- Did scrolling snap to the second box? YES
+Scrolling in container with scroll-snap-type: y proximity
+- Did the scrolling snap to the top? NO
+- Did scrolling snap to the second box? NO
+
</ins></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapmandatorythenproximityhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity.html (0 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity.html                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -0,0 +1,82 @@
</span><ins>+&lt;!DOCTYPE HTML&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;style&gt;
+            body {
+                margin: 0;
+                overflow: hidden;
+            }
+
+            #container {
+                width: 600px;
+                height: 600px;
+                position: absolute;
+                top: 0;
+                left: 0;
+                overflow-x: none;
+                overflow-y: scroll;
+                scroll-snap-type: y mandatory;
+                opacity: 0.5;
+            }
+
+            .area {
+                height: 600px;
+                width: 600px;
+                float: left;
+                scroll-snap-align: start;
+            }
+        &lt;/style&gt;
+        &lt;script&gt;
+        let write = s =&gt; output.innerHTML += s + &quot;&lt;br&gt;&quot;;
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        function verticalScrollInContainer(dragDeltas, momentumDeltas)
+        {
+            return new Promise(resolve =&gt; {
+                write(`Scrolling in ${container.id} with scroll-snap-type: ${getComputedStyle(container).scrollSnapType}`);
+                eventSender.monitorWheelEvents();
+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
+                eventSender.mouseMoveTo(300, 300);
+                dragDeltas.forEach((delta, i) =&gt; eventSender.mouseScrollByWithWheelAndMomentumPhases(0, delta, i == 0 ? &quot;began&quot; : &quot;changed&quot;, &quot;none&quot;));
+                eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, &quot;ended&quot;, &quot;none&quot;);
+                if (momentumDeltas &amp;&amp; momentumDeltas.length) {
+                    momentumDeltas.forEach((delta, i) =&gt; eventSender.mouseScrollByWithWheelAndMomentumPhases(0, delta, &quot;none&quot;, i == 0 ? &quot;begin&quot; : &quot;continue&quot;));
+                    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, &quot;none&quot;, &quot;end&quot;);
+                }
+                eventSender.callAfterScrollingCompletes(() =&gt; {
+                    write(`- Did the scrolling snap to the top? ${container.scrollTop == 0 ? &quot;YES&quot; : &quot;NO&quot;}`);
+                    write(`- Did scrolling snap to the second box? ${container.scrollTop == 600 ? &quot;YES&quot; : &quot;NO&quot;}`);
+                    container.style.scrollSnapType = &quot;y proximity&quot;
+                    container.scrollTop = 0;
+                    setTimeout(resolve, 0);
+                });
+            });
+        }
+
+        function run() {
+            if (!window.testRunner || !window.eventSender) {
+                write(&quot;This test requires EventSender support.&quot;);
+                return;
+            }
+
+            verticalScrollInContainer(new Array(16).fill(-1), new Array(3).fill(-1))
+                .then(() =&gt; verticalScrollInContainer(new Array(16).fill(-1), new Array(3).fill(-1)))
+                .then(() =&gt; testRunner.notifyDone());
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+    &lt;body onload=run()&gt;
+        &lt;div id=&quot;container&quot;&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: red;&quot;&gt;&lt;/div&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: green;&quot;&gt;&lt;/div&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: blue;&quot;&gt;&lt;/div&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: aqua;&quot;&gt;&lt;/div&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: yellow;&quot;&gt;&lt;/div&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: fuchsia;&quot;&gt;&lt;/div&gt;
+        &lt;/div&gt;
+        &lt;div id=&quot;output&quot;&gt;&lt;/div&gt;
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapproximitymainframeexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe-expected.txt (0 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe-expected.txt                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe-expected.txt        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+Scrolling body with 2 drag ticks
+- Did the scrolling snap to the top? YES
+- Did scrolling snap to the second box? NO
+Scrolling body with 30 drag ticks
+- Did the scrolling snap to the top? NO
+- Did scrolling snap to the second box? NO
+Scrolling body with 60 drag ticks
+- Did the scrolling snap to the top? NO
+- Did scrolling snap to the second box? YES
+
</ins></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapproximitymainframehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe.html (0 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe.html                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -0,0 +1,82 @@
</span><ins>+&lt;!DOCTYPE HTML&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;style&gt;
+            body {
+                margin: 0;
+                width: 100%;
+                height: 100%;
+                overflow-x: none;
+                overflow-y: scroll;
+                position: absolute;
+                scroll-snap-type: y proximity;
+                -webkit-scroll-snap-type: proximity;
+                scroll-snap-type: proximity;
+            }
+
+            .area {
+                width: 100%;
+                height: 100%;
+                float: left;
+                opacity: 0.5;
+                scroll-snap-align: start;
+                -webkit-scroll-snap-coordinate: 0 0;
+                scroll-snap-coordinate: 0 0;
+            }
+
+            #output {
+                position: fixed;
+            }
+        &lt;/style&gt;
+        &lt;script&gt;
+        let write = s =&gt; output.innerHTML += s + &quot;&lt;br&gt;&quot;;
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        function verticalScrollInBody(dragDeltas)
+        {
+            return new Promise(resolve =&gt; {
+                write(`Scrolling body with ${dragDeltas.length} drag ticks`);
+                eventSender.monitorWheelEvents();
+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
+                eventSender.mouseMoveTo(window.innerWidth / 2, window.innerHeight / 2);
+                dragDeltas.forEach((delta, i) =&gt; eventSender.mouseScrollByWithWheelAndMomentumPhases(0, delta, i == 0 ? &quot;began&quot; : &quot;changed&quot;, &quot;none&quot;));
+                eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, &quot;ended&quot;, &quot;none&quot;);
+                eventSender.callAfterScrollingCompletes(() =&gt; {
+                    let areaHeight = document.querySelector(&quot;.area&quot;).clientHeight;
+                    write(`- Did the scrolling snap to the top? ${document.body.scrollTop == 0 ? &quot;YES&quot; : &quot;NO&quot;}`);
+                    write(`- Did scrolling snap to the second box? ${document.body.scrollTop == areaHeight ? &quot;YES&quot; : &quot;NO&quot;}`);
+                    document.body.scrollTop = 0;
+                    setTimeout(resolve, 0);
+                });
+            });
+        }
+
+        function run() {
+            if (!window.testRunner || !window.eventSender) {
+                write(&quot;To manually test, verify that scrolling near one of the boundaries between the colored boxes&quot;);
+                write(&quot;snaps to the edge of the nearest colored box, but scrolling somewhere near the middle of two&quot;);
+                write(&quot;boxes does not cause the scroll offset to snap.&quot;);
+                return;
+            }
+
+            let areaHeight = document.querySelector(&quot;.area&quot;).clientHeight;
+            verticalScrollInBody(new Array(2).fill(-1))
+                .then(() =&gt; verticalScrollInBody(new Array(Math.round(areaHeight / 20)).fill(-1)))
+                .then(() =&gt; verticalScrollInBody(new Array(Math.round(areaHeight / 10)).fill(-1)))
+                .then(() =&gt; testRunner.notifyDone());
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+    &lt;body onload=run()&gt;
+        &lt;div class=&quot;area&quot; style=&quot;background-color: red;&quot;&gt;&lt;/div&gt;
+        &lt;div class=&quot;area&quot; style=&quot;background-color: green;&quot;&gt;&lt;/div&gt;
+        &lt;div class=&quot;area&quot; style=&quot;background-color: blue;&quot;&gt;&lt;/div&gt;
+        &lt;div class=&quot;area&quot; style=&quot;background-color: aqua;&quot;&gt;&lt;/div&gt;
+        &lt;div class=&quot;area&quot; style=&quot;background-color: yellow;&quot;&gt;&lt;/div&gt;
+        &lt;div class=&quot;area&quot; style=&quot;background-color: fuchsia;&quot;&gt;&lt;/div&gt;
+    &lt;/body&gt;
+    &lt;div id=&quot;output&quot;&gt;&lt;/div&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapproximityoverflowexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow-expected.txt (0 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow-expected.txt                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow-expected.txt        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+Scrolling in container with 2 drag ticks
+- Did the scrolling snap to the top? YES
+- Did scrolling snap to the second box? NO
+Scrolling in container with 31 drag ticks
+- Did the scrolling snap to the top? NO
+- Did scrolling snap to the second box? NO
+Scrolling in container with 59 drag ticks
+- Did the scrolling snap to the top? NO
+- Did scrolling snap to the second box? YES
+
</ins></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapproximityoverflowhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html (0 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -0,0 +1,84 @@
</span><ins>+&lt;!DOCTYPE HTML&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;style&gt;
+            body {
+                margin: 0;
+                overflow: hidden;
+            }
+
+            #container {
+                width: 600px;
+                height: 600px;
+                position: absolute;
+                top: 0;
+                left: 0;
+                overflow-x: none;
+                overflow-y: scroll;
+                scroll-snap-type: y proximity;
+                -webkit-scroll-snap-type: proximity;
+                scroll-snap-type: proximity;
+                opacity: 0.5;
+            }
+
+            .area {
+                height: 600px;
+                width: 600px;
+                float: left;
+                scroll-snap-align: start;
+                -webkit-scroll-snap-coordinate: 0 0;
+                scroll-snap-coordinate: 0 0;
+            }
+        &lt;/style&gt;
+        &lt;script&gt;
+        let write = s =&gt; output.innerHTML += s + &quot;&lt;br&gt;&quot;;
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        function verticalScrollInContainer(dragDeltas)
+        {
+            return new Promise(resolve =&gt; {
+                write(`Scrolling in ${container.id} with ${dragDeltas.length} drag ticks`);
+                eventSender.monitorWheelEvents();
+                internals.setPlatformMomentumScrollingPredictionEnabled(false);
+                eventSender.mouseMoveTo(300, 300);
+                dragDeltas.forEach((delta, i) =&gt; eventSender.mouseScrollByWithWheelAndMomentumPhases(0, delta, i == 0 ? &quot;began&quot; : &quot;changed&quot;, &quot;none&quot;));
+                eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, &quot;ended&quot;, &quot;none&quot;);
+                eventSender.callAfterScrollingCompletes(() =&gt; {
+                    write(`- Did the scrolling snap to the top? ${container.scrollTop == 0 ? &quot;YES&quot; : &quot;NO&quot;}`);
+                    write(`- Did scrolling snap to the second box? ${container.scrollTop == 600 ? &quot;YES&quot; : &quot;NO&quot;}`);
+                    container.scrollTop = 0;
+                    setTimeout(resolve, 0);
+                });
+            });
+        }
+
+        function run() {
+            if (!window.testRunner || !window.eventSender) {
+                write(&quot;To manually test, verify that scrolling near one of the boundaries between the colored boxes&quot;);
+                write(&quot;snaps to the edge of the nearest colored box, but scrolling somewhere near the middle of two&quot;);
+                write(&quot;boxes does not cause the scroll offset to snap.&quot;);
+                return;
+            }
+
+            verticalScrollInContainer(new Array(2).fill(-1))
+                .then(() =&gt; verticalScrollInContainer(new Array(31).fill(-1)))
+                .then(() =&gt; verticalScrollInContainer(new Array(59).fill(-1)))
+                .then(() =&gt; testRunner.notifyDone());
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+    &lt;body onload=run()&gt;
+        &lt;div id=&quot;container&quot;&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: red;&quot;&gt;&lt;/div&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: green;&quot;&gt;&lt;/div&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: blue;&quot;&gt;&lt;/div&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: aqua;&quot;&gt;&lt;/div&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: yellow;&quot;&gt;&lt;/div&gt;
+            &lt;div class=&quot;area&quot; style=&quot;background-color: fuchsia;&quot;&gt;&lt;/div&gt;
+        &lt;/div&gt;
+        &lt;div id=&quot;output&quot;&gt;&lt;/div&gt;
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollingscrollsnapscrollsnapscrollingjumpstotophtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.html (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.html        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scroll-snap/scroll-snap-scrolling-jumps-to-top.html        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx">             testRunner.dumpAsText();
</span><span class="cx">             testRunner.waitUntilDone();
</span><span class="cx">             eventSender.monitorWheelEvents();
</span><ins>+            internals.setPlatformMomentumScrollingPredictionEnabled(false);
</ins><span class="cx">             eventSender.mouseMoveTo(container.offsetLeft + container.clientWidth / 2, container.offsetTop + container.clientHeight / 2);
</span><span class="cx"> 
</span><span class="cx">             write(&quot;Scrolling without momentum to the same position several times&quot;)
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/ChangeLog        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -1,3 +1,187 @@
</span><ins>+2017-01-10  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Implement &quot;proximity&quot; scroll snapping
+        https://bugs.webkit.org/show_bug.cgi?id=135994
+        &lt;rdar://problem/18162418&gt;
+
+        Reviewed by Dean Jackson.
+
+        Adds support for proximity scroll snapping. To do this, we introduce scroll offset ranges, a list of scroll
+        offset ranges that are plumbed alongside the list of scroll snap offsets. Similar to a snap offset, a snap
+        offset range contains scroll offsets on which scrolling is allowed to come to a rest within a scroll snapping
+        container. However, unlike normal snap offsets, scrolling may only come to rest within a snap offset range if
+        the predicted scroll offset already lies within the range. The new algorithm for selecting a target scroll snap
+        position given a destination offset is now:
+
+        -   If the scroll destination lies within a snap offset range, return the scroll destination
+        -   Otherwise, compute the nearest lower/upper snap offsets and lower/upper snap offset ranges
+        -   If scrolling ended with no velocity, return the nearest snap offset
+        -   If scrolling ended with positive velocity, choose the upper snap offset only if there is no snap offset
+            range in between the scroll destination and the snap offset; else, choose the lower snap offset
+        -   If scrolling ended with negative velocity, choose the lower snap offset only if there is no snap offset
+            range in between the scroll destination and the snap offset; else, choose the upper snap offset
+
+        The extra rule accounting for scroll offset ranges in between the scroll destination and a potential snap offset
+        handles the corner case where the user scrolls with momentum very lightly away from a snap offset, such that the
+        predicted scroll destination is still within proximity of the snap offset. In this case, the regular (mandatory
+        scroll snapping) behavior would be to snap to the next offset in the direction of momentum scrolling, but
+        instead, it is more intuitive to return to the original snap position.
+
+        We also move scrolling prediction logic into ScrollingMomentumCalculator and adopt the platform
+        _NSScrollingMomentumCalculator's destinationOrigin property when computing the predicted scroll destination.
+        Previously, we were simply multiplying by an empirically-derived constant to approximate the scroll destination,
+        but now that we are supporting proximity scroll snapping, we need more exact scroll destinaton prediction in
+        order to make sure that scrolling to a snap offset range feels natural.
+
+        Tests: tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-then-proximity.html
+               tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-mainframe.html
+               tiled-drawing/scrolling/scroll-snap/scroll-snap-proximity-overflow.html
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * page/scrolling/AsyncScrollingCoordinator.cpp:
+        (WebCore::setStateScrollingNodeSnapOffsetsAsFloat):
+        (WebCore::AsyncScrollingCoordinator::updateOverflowScrollingNode):
+        (WebCore::AsyncScrollingCoordinator::updateScrollSnapPropertiesWithFrameView):
+
+        Make boilerplate changes to plumb lists of horizontal and vertical snap offset ranges alongside the lists of
+        horizontal and vertical snap offsets.
+
+        * page/scrolling/AxisScrollSnapOffsets.cpp:
+        (WebCore::snapOffsetRangesToString):
+        (WebCore::indicesOfNearestSnapOffsetRanges):
+        (WebCore::indicesOfNearestSnapOffsets):
+        (WebCore::adjustAxisSnapOffsetsForScrollExtent):
+        (WebCore::computeAxisProximitySnapOffsetRanges):
+        (WebCore::updateSnapOffsetsForScrollableArea):
+        (WebCore::closestSnapOffset):
+
+        Adjust the snap offset selection algorithm to take snap offset ranges into account. See above for more details.
+        Additionally, augment snap offset update logic to emit snap offset ranges for proximity scroll snapping. To do
+        this, we run the following steps on the final list of processed snap offsets:
+        -   Compute the proximity distance, which (for now) is arbitrarily 0.3 * the length or width of the scroll snap
+            port, depending on whether scroll snapping is taking place in the X or Y axis.
+        -   For each pair of adjacent snap offsets, if they are more than 2 * proximity distance away from each other,
+            emit a snap offset range starting from (lower snap offset + proximity distance) and ending on (upper snap
+            offset + proximity distance).
+
+        * page/scrolling/AxisScrollSnapOffsets.h:
+        (WebCore::closestSnapOffset): Deleted.
+        * page/scrolling/ScrollSnapOffsetsInfo.h:
+
+        Introduce ScrollSnapOffsetsInfo, a struct which contains data relevant to scroll snapping. This includes
+        vertical and horizontal snap offsets, as well as vertical and horizontal snap offset ranges. Snap offset ranges
+        consist of a vector of ranges of scroll offsets.
+
+        * page/scrolling/ScrollingCoordinator.h:
+        * page/scrolling/ScrollingMomentumCalculator.cpp:
+        (WebCore::projectedInertialScrollDistance):
+        (WebCore::ScrollingMomentumCalculator::ScrollingMomentumCalculator):
+        (WebCore::ScrollingMomentumCalculator::setRetargetedScrollOffset):
+        (WebCore::ScrollingMomentumCalculator::predictedDestinationOffset):
+        (WebCore::ScrollingMomentumCalculator::create):
+        (WebCore::ScrollingMomentumCalculator::setPlatformMomentumScrollingPredictionEnabled):
+        (WebCore::BasicScrollingMomentumCalculator::BasicScrollingMomentumCalculator):
+        (WebCore::BasicScrollingMomentumCalculator::linearlyInterpolatedOffsetAtProgress):
+        (WebCore::BasicScrollingMomentumCalculator::initializeInterpolationCoefficientsIfNecessary):
+        (WebCore::BasicScrollingMomentumCalculator::initializeSnapProgressCurve):
+        * page/scrolling/ScrollingMomentumCalculator.h:
+        (WebCore::ScrollingMomentumCalculator::retargetedScrollOffset):
+        (WebCore::ScrollingMomentumCalculator::retargetedScrollOffsetDidChange):
+
+        Currently, the ScrollingMomentumCalculator is responsible for taking an initial position, initial velocity, and
+        target position and animating the scroll offset from the initial to target position. Now, we refactor the
+        ScrollingMomentumCalculator interface to no longer take a target offset upon initialization, and instead compute
+        the predicted scroll destination given initial position and velocity; clients of the ScrollingMomentumCalculator
+        then use this predicted scroll destination to compute a retargeted scroll offset and then call
+        setRetargetedScrollOffset on the calculator, which sets up an animation curve to the new retargeted offset. This
+        allows both the AppKit-based scrolling momentum calculator and platform-invariant momentum calculator to be used
+        interchangeably, while still allowing them to compute a destination offset from initial parameters of the
+        scroll.
+
+        * page/scrolling/ScrollingStateScrollingNode.cpp:
+        (WebCore::ScrollingStateScrollingNode::ScrollingStateScrollingNode):
+        (WebCore::ScrollingStateScrollingNode::setHorizontalSnapOffsets):
+        (WebCore::ScrollingStateScrollingNode::setVerticalSnapOffsets):
+        (WebCore::ScrollingStateScrollingNode::setHorizontalSnapOffsetRanges):
+        (WebCore::ScrollingStateScrollingNode::setVerticalSnapOffsetRanges):
+        * page/scrolling/ScrollingStateScrollingNode.h:
+        (WebCore::ScrollingStateScrollingNode::horizontalSnapOffsets):
+        (WebCore::ScrollingStateScrollingNode::verticalSnapOffsets):
+        (WebCore::ScrollingStateScrollingNode::horizontalSnapOffsetRanges):
+        (WebCore::ScrollingStateScrollingNode::verticalSnapOffsetRanges):
+        * page/scrolling/ScrollingTreeScrollingNode.cpp:
+        (WebCore::ScrollingTreeScrollingNode::commitStateBeforeChildren):
+        (WebCore::ScrollingTreeScrollingNode::dumpProperties):
+        * page/scrolling/ScrollingTreeScrollingNode.h:
+        (WebCore::ScrollingTreeScrollingNode::horizontalSnapOffsets):
+        (WebCore::ScrollingTreeScrollingNode::verticalSnapOffsets):
+        (WebCore::ScrollingTreeScrollingNode::horizontalSnapOffsetRanges):
+        (WebCore::ScrollingTreeScrollingNode::verticalSnapOffsetRanges):
+
+        Add more boilerplate support for snap offset ranges.
+
+        * page/scrolling/mac/ScrollingMomentumCalculatorMac.h:
+        * page/scrolling/mac/ScrollingMomentumCalculatorMac.mm:
+        (WebCore::ScrollingMomentumCalculator::create):
+        (WebCore::ScrollingMomentumCalculator::setPlatformMomentumScrollingPredictionEnabled):
+        (WebCore::ScrollingMomentumCalculatorMac::ScrollingMomentumCalculatorMac):
+        (WebCore::ScrollingMomentumCalculatorMac::scrollOffsetAfterElapsedTime):
+        (WebCore::ScrollingMomentumCalculatorMac::predictedDestinationOffset):
+        (WebCore::ScrollingMomentumCalculatorMac::retargetedScrollOffsetDidChange):
+        (WebCore::ScrollingMomentumCalculatorMac::animationDuration):
+        (WebCore::ScrollingMomentumCalculatorMac::requiresMomentumScrolling):
+        (WebCore::ScrollingMomentumCalculatorMac::ensurePlatformMomentumCalculator):
+
+        Hook into AppKit momentum scroll offset prediction.
+
+        * page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm:
+        (WebCore::convertToLayoutUnits):
+        (WebCore::ScrollingTreeFrameScrollingNodeMac::commitStateBeforeChildren):
+        * platform/ScrollableArea.cpp:
+        (WebCore::ScrollableArea::ensureSnapOffsetsInfo):
+        (WebCore::ScrollableArea::horizontalSnapOffsets):
+        (WebCore::ScrollableArea::horizontalSnapOffsetRanges):
+        (WebCore::ScrollableArea::verticalSnapOffsetRanges):
+        (WebCore::ScrollableArea::verticalSnapOffsets):
+        (WebCore::ScrollableArea::setHorizontalSnapOffsets):
+        (WebCore::ScrollableArea::setVerticalSnapOffsets):
+        (WebCore::ScrollableArea::setHorizontalSnapOffsetRanges):
+        (WebCore::ScrollableArea::setVerticalSnapOffsetRanges):
+        (WebCore::ScrollableArea::clearHorizontalSnapOffsets):
+        (WebCore::ScrollableArea::clearVerticalSnapOffsets):
+        * platform/ScrollableArea.h:
+        (WebCore::ScrollableArea::horizontalSnapOffsets): Deleted.
+        (WebCore::ScrollableArea::verticalSnapOffsets): Deleted.
+        * platform/cocoa/ScrollController.h:
+        * platform/cocoa/ScrollController.mm:
+        (WebCore::ScrollController::processWheelEventForScrollSnap):
+
+        Fix an issue where initial scrolling velocity would be set to zero at the end of a drag gesture.
+
+        (WebCore::ScrollController::updateScrollSnapState):
+        (WebCore::ScrollController::updateScrollSnapPoints):
+        (WebCore::ScrollController::setNearestScrollSnapIndexForAxisAndOffset):
+        * platform/cocoa/ScrollSnapAnimatorState.h:
+        (WebCore::ScrollSnapAnimatorState::snapOffsetsForAxis):
+        (WebCore::ScrollSnapAnimatorState::snapOffsetRangesForAxis):
+        (WebCore::ScrollSnapAnimatorState::setSnapOffsetsAndPositionRangesForAxis):
+        (WebCore::ScrollSnapAnimatorState::setSnapOffsetsForAxis): Deleted.
+        * platform/cocoa/ScrollSnapAnimatorState.mm:
+        (WebCore::ScrollSnapAnimatorState::setupAnimationForState):
+        (WebCore::ScrollSnapAnimatorState::targetOffsetForStartOffset):
+        (WebCore::projectedInertialScrollDistance): Deleted.
+        * rendering/RenderLayerCompositor.cpp:
+        (WebCore::RenderLayerCompositor::updateScrollCoordinatedLayer):
+        * testing/Internals.cpp:
+        (WebCore::Internals::setPlatformMomentumScrollingPredictionEnabled):
+
+        Add a new hook for layout tests to force scrolling momentum calculators to use the platform-invariant momentum
+        scrolling prediction heuristic instead of the platform-dependent one.
+
+        (WebCore::Internals::scrollSnapOffsets):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
</ins><span class="cx"> 2017-01-10  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Make Event.initEvent()'s first parameter mandatory
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -6487,6 +6487,7 @@
</span><span class="cx">                 F44EBBDB1DB5DD9D00277334 /* StaticRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F44EBBDA1DB5DD9D00277334 /* StaticRange.cpp */; };
</span><span class="cx">                 F45C231D1995B73B00A6E2E3 /* AxisScrollSnapOffsets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45C231B1995B73B00A6E2E3 /* AxisScrollSnapOffsets.cpp */; };
</span><span class="cx">                 F45C231E1995B73B00A6E2E3 /* AxisScrollSnapOffsets.h in Headers */ = {isa = PBXBuildFile; fileRef = F45C231C1995B73B00A6E2E3 /* AxisScrollSnapOffsets.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+                F46729281E0DE68500ACC3D8 /* ScrollSnapOffsetsInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = F46729251E0DE5AB00ACC3D8 /* ScrollSnapOffsetsInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
</ins><span class="cx">                 F478755419983AFF0024A287 /* ScrollSnapAnimatorState.h in Headers */ = {isa = PBXBuildFile; fileRef = F478755219983AFF0024A287 /* ScrollSnapAnimatorState.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 F478755519983AFF0024A287 /* ScrollSnapAnimatorState.mm in Sources */ = {isa = PBXBuildFile; fileRef = F478755319983AFF0024A287 /* ScrollSnapAnimatorState.mm */; };
</span><span class="cx">                 F47A5E3E195B8C8A00483100 /* StyleScrollSnapPoints.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A5E3B195B8C8A00483100 /* StyleScrollSnapPoints.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="lines">@@ -14509,6 +14510,7 @@
</span><span class="cx">                 F44EBBDA1DB5DD9D00277334 /* StaticRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticRange.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 F45C231B1995B73B00A6E2E3 /* AxisScrollSnapOffsets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AxisScrollSnapOffsets.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 F45C231C1995B73B00A6E2E3 /* AxisScrollSnapOffsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AxisScrollSnapOffsets.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                F46729251E0DE5AB00ACC3D8 /* ScrollSnapOffsetsInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollSnapOffsetsInfo.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 F478755219983AFF0024A287 /* ScrollSnapAnimatorState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollSnapAnimatorState.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 F478755319983AFF0024A287 /* ScrollSnapAnimatorState.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollSnapAnimatorState.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 F47A5E3A195B8C8A00483100 /* StyleScrollSnapPoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StyleScrollSnapPoints.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -16039,6 +16041,7 @@
</span><span class="cx">                                 0FEA3E7E191B3169000F1B55 /* ScrollingTreeOverflowScrollingNode.h */,
</span><span class="cx">                                 9391A99A1629D6FF00297330 /* ScrollingTreeScrollingNode.cpp */,
</span><span class="cx">                                 9391A99B1629D70000297330 /* ScrollingTreeScrollingNode.h */,
</span><ins>+                                F46729251E0DE5AB00ACC3D8 /* ScrollSnapOffsetsInfo.h */,
</ins><span class="cx">                                 7AAFE8CD19CB8672000F56D8 /* ScrollLatchingState.cpp */,
</span><span class="cx">                                 7AAFE8CE19CB8672000F56D8 /* ScrollLatchingState.h */,
</span><span class="cx">                                 0F6383DB18615B29003E5DB5 /* ThreadedScrollingTree.cpp */,
</span><span class="lines">@@ -26183,6 +26186,7 @@
</span><span class="cx">                                 0F4710C01DB56BE8002DCEC3 /* JSDOMRectReadOnly.h in Headers */,
</span><span class="cx">                                 BC5A86B60C3367E800EEA649 /* JSDOMSelection.h in Headers */,
</span><span class="cx">                                 C5137CF311A58378004ADB99 /* JSDOMStringList.h in Headers */,
</span><ins>+                                F46729281E0DE68500ACC3D8 /* ScrollSnapOffsetsInfo.h in Headers */,
</ins><span class="cx">                                 BC64649811D82349006455B0 /* JSDOMStringMap.h in Headers */,
</span><span class="cx">                                 7694563D1214D97C0007CBAE /* JSDOMTokenList.h in Headers */,
</span><span class="cx">                                 2E37E00612DBC5A400A6B233 /* JSDOMURL.h in Headers */,
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingAsyncScrollingCoordinatorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -65,7 +65,7 @@
</span><span class="cx">     scheduleTreeStateCommit();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static inline void setStateScrollingNodeSnapOffsetsAsFloat(ScrollingStateScrollingNode&amp; node, ScrollEventAxis axis, const Vector&lt;LayoutUnit&gt;* snapOffsets, float deviceScaleFactor)
</del><ins>+static inline void setStateScrollingNodeSnapOffsetsAsFloat(ScrollingStateScrollingNode&amp; node, ScrollEventAxis axis, const Vector&lt;LayoutUnit&gt;* snapOffsets, const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;* snapOffsetRanges, float deviceScaleFactor)
</ins><span class="cx"> {
</span><span class="cx">     // FIXME: Incorporate current page scale factor in snapping to device pixel. Perhaps we should just convert to float here and let UI process do the pixel snapping?
</span><span class="cx">     Vector&lt;float&gt; snapOffsetsAsFloat;
</span><span class="lines">@@ -74,10 +74,20 @@
</span><span class="cx">         for (auto&amp; offset : *snapOffsets)
</span><span class="cx">             snapOffsetsAsFloat.uncheckedAppend(roundToDevicePixel(offset, deviceScaleFactor, false));
</span><span class="cx">     }
</span><del>-    if (axis == ScrollEventAxis::Horizontal)
</del><ins>+
+    Vector&lt;ScrollOffsetRange&lt;float&gt;&gt; snapOffsetRangesAsFloat;
+    if (snapOffsetRanges) {
+        snapOffsetRangesAsFloat.reserveInitialCapacity(snapOffsetRanges-&gt;size());
+        for (auto&amp; range : *snapOffsetRanges)
+            snapOffsetRangesAsFloat.uncheckedAppend({ roundToDevicePixel(range.start, deviceScaleFactor, false), roundToDevicePixel(range.end, deviceScaleFactor, false) });
+    }
+    if (axis == ScrollEventAxis::Horizontal) {
</ins><span class="cx">         node.setHorizontalSnapOffsets(snapOffsetsAsFloat);
</span><del>-    else
</del><ins>+        node.setHorizontalSnapOffsetRanges(snapOffsetRangesAsFloat);
+    } else {
</ins><span class="cx">         node.setVerticalSnapOffsets(snapOffsetsAsFloat);
</span><ins>+        node.setVerticalSnapOffsetRanges(snapOffsetRangesAsFloat);
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void AsyncScrollingCoordinator::setEventTrackingRegionsDirty()
</span><span class="lines">@@ -537,8 +547,8 @@
</span><span class="cx">         node-&gt;setReachableContentsSize(scrollingGeometry-&gt;reachableContentSize);
</span><span class="cx">         node-&gt;setScrollableAreaSize(scrollingGeometry-&gt;scrollableAreaSize);
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-        setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Horizontal, &amp;scrollingGeometry-&gt;horizontalSnapOffsets, m_page-&gt;deviceScaleFactor());
-        setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Vertical, &amp;scrollingGeometry-&gt;verticalSnapOffsets, m_page-&gt;deviceScaleFactor());
</del><ins>+        setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Horizontal, &amp;scrollingGeometry-&gt;horizontalSnapOffsets, &amp;scrollingGeometry-&gt;horizontalSnapOffsetRanges, m_page-&gt;deviceScaleFactor());
+        setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Vertical, &amp;scrollingGeometry-&gt;verticalSnapOffsets, &amp;scrollingGeometry-&gt;verticalSnapOffsetRanges, m_page-&gt;deviceScaleFactor());
</ins><span class="cx">         node-&gt;setCurrentHorizontalSnapPointIndex(scrollingGeometry-&gt;currentHorizontalSnapPointIndex);
</span><span class="cx">         node-&gt;setCurrentVerticalSnapPointIndex(scrollingGeometry-&gt;currentVerticalSnapPointIndex);
</span><span class="cx"> #endif
</span><span class="lines">@@ -682,8 +692,8 @@
</span><span class="cx"> void AsyncScrollingCoordinator::updateScrollSnapPropertiesWithFrameView(const FrameView&amp; frameView)
</span><span class="cx"> {
</span><span class="cx">     if (auto node = downcast&lt;ScrollingStateFrameScrollingNode&gt;(m_scrollingStateTree-&gt;stateNodeForID(frameView.scrollLayerID()))) {
</span><del>-        setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Horizontal, frameView.horizontalSnapOffsets(), m_page-&gt;deviceScaleFactor());
-        setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Vertical, frameView.verticalSnapOffsets(), m_page-&gt;deviceScaleFactor());
</del><ins>+        setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Horizontal, frameView.horizontalSnapOffsets(), frameView.horizontalSnapOffsetRanges(), m_page-&gt;deviceScaleFactor());
+        setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Vertical, frameView.verticalSnapOffsets(), frameView.verticalSnapOffsetRanges(), m_page-&gt;deviceScaleFactor());
</ins><span class="cx">         node-&gt;setCurrentHorizontalSnapPointIndex(frameView.currentHorizontalSnapPointIndex());
</span><span class="cx">         node-&gt;setCurrentVerticalSnapPointIndex(frameView.currentVerticalSnapPointIndex());
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingAxisScrollSnapOffsetscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.cpp (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.cpp        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.cpp        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -84,6 +84,16 @@
</span><span class="cx">     return s.toString();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static String snapOffsetRangesToString(const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; ranges)
+{
+    StringBuilder s;
+    s.append(&quot;[ &quot;);
+    for (auto range : ranges)
+        s.append(String::format(&quot;(%.1f, %.1f) &quot;, range.start.toFloat(), range.end.toFloat()));
+    s.append(&quot;]&quot;);
+    return s.toString();
+}
+
</ins><span class="cx"> static String snapPortOrAreaToString(const LayoutRect&amp; rect)
</span><span class="cx"> {
</span><span class="cx">     return String::format(&quot;{{%.1f, %.1f} {%.1f, %.1f}}&quot;, rect.x().toFloat(), rect.y().toFloat(), rect.width().toFloat(), rect.height().toFloat());
</span><span class="lines">@@ -91,6 +101,100 @@
</span><span class="cx"> 
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+template &lt;typename LayoutType&gt;
+static void indicesOfNearestSnapOffsetRanges(LayoutType offset, const Vector&lt;ScrollOffsetRange&lt;LayoutType&gt;&gt;&amp; snapOffsetRanges, unsigned&amp; lowerIndex, unsigned&amp; upperIndex)
+{
+    if (snapOffsetRanges.isEmpty()) {
+        lowerIndex = invalidSnapOffsetIndex;
+        upperIndex = invalidSnapOffsetIndex;
+        return;
+    }
+
+    int lowerIndexAsInt = -1;
+    int upperIndexAsInt = snapOffsetRanges.size();
+    do {
+        int middleIndex = (lowerIndexAsInt + upperIndexAsInt) / 2;
+        auto&amp; range = snapOffsetRanges[middleIndex];
+        if (range.start &lt; offset &amp;&amp; offset &lt; range.end) {
+            lowerIndexAsInt = middleIndex;
+            upperIndexAsInt = middleIndex;
+            break;
+        }
+
+        if (offset &gt; range.end)
+            lowerIndexAsInt = middleIndex;
+        else
+            upperIndexAsInt = middleIndex;
+    } while (lowerIndexAsInt &lt; upperIndexAsInt - 1);
+
+    if (offset &lt;= snapOffsetRanges.first().start)
+        lowerIndex = invalidSnapOffsetIndex;
+    else
+        lowerIndex = lowerIndexAsInt;
+
+    if (offset &gt;= snapOffsetRanges.last().end)
+        upperIndex = invalidSnapOffsetIndex;
+    else
+        upperIndex = upperIndexAsInt;
+}
+
+template &lt;typename LayoutType&gt;
+static void indicesOfNearestSnapOffsets(LayoutType offset, const Vector&lt;LayoutType&gt;&amp; snapOffsets, unsigned&amp; lowerIndex, unsigned&amp; upperIndex)
+{
+    lowerIndex = 0;
+    upperIndex = snapOffsets.size() - 1;
+    while (lowerIndex &lt; upperIndex - 1) {
+        int middleIndex = (lowerIndex + upperIndex) / 2;
+        auto middleOffset = snapOffsets[middleIndex];
+        if (offset == middleOffset) {
+            upperIndex = middleIndex;
+            lowerIndex = middleIndex;
+            break;
+        }
+
+        if (offset &gt; middleOffset)
+            lowerIndex = middleIndex;
+        else
+            upperIndex = middleIndex;
+    }
+}
+
+static void adjustAxisSnapOffsetsForScrollExtent(Vector&lt;LayoutUnit&gt;&amp; snapOffsets, float maxScrollExtent)
+{
+    if (snapOffsets.isEmpty())
+        return;
+
+    std::sort(snapOffsets.begin(), snapOffsets.end());
+    if (snapOffsets.last() != maxScrollExtent)
+        snapOffsets.append(maxScrollExtent);
+    if (snapOffsets.first())
+        snapOffsets.insert(0, 0);
+}
+
+static void computeAxisProximitySnapOffsetRanges(const Vector&lt;LayoutUnit&gt;&amp; snapOffsets, Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; offsetRanges, LayoutUnit scrollPortAxisLength)
+{
+    // This is an arbitrary choice for what it means to be &quot;in proximity&quot; of a snap offset. We should play around with
+    // this and see what feels best.
+    static const float ratioOfScrollPortAxisLengthToBeConsideredForProximity = 0.3;
+    if (snapOffsets.size() &lt; 2)
+        return;
+
+    // The extra rule accounting for scroll offset ranges in between the scroll destination and a potential snap offset
+    // handles the corner case where the user scrolls with momentum very lightly away from a snap offset, such that the
+    // predicted scroll destination is still within proximity of the snap offset. In this case, the regular (mandatory
+    // scroll snapping) behavior would be to snap to the next offset in the direction of momentum scrolling, but
+    // instead, it is more intuitive to either return to the original snap position (which we arbitrarily choose here)
+    // or scroll just outside of the snap offset range. This is another minor behavior tweak that we should play around
+    // with to see what feels best.
+    LayoutUnit proximityDistance = ratioOfScrollPortAxisLengthToBeConsideredForProximity * scrollPortAxisLength;
+    for (size_t index = 1; index &lt; snapOffsets.size(); ++index) {
+        auto startOffset = snapOffsets[index - 1] + proximityDistance;
+        auto endOffset = snapOffsets[index] - proximityDistance;
+        if (startOffset &lt; endOffset)
+            offsetRanges.append({ startOffset, endOffset });
+    }
+}
+
</ins><span class="cx"> void updateSnapOffsetsForScrollableArea(ScrollableArea&amp; scrollableArea, HTMLElement&amp; scrollingElement, const RenderBox&amp; scrollingElementBox, const RenderStyle&amp; scrollingElementStyle)
</span><span class="cx"> {
</span><span class="cx">     auto* scrollContainer = scrollingElement.renderer();
</span><span class="lines">@@ -101,8 +205,10 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    auto verticalSnapOffsets = std::make_unique&lt;Vector&lt;LayoutUnit&gt;&gt;();
-    auto horizontalSnapOffsets = std::make_unique&lt;Vector&lt;LayoutUnit&gt;&gt;();
</del><ins>+    Vector&lt;LayoutUnit&gt; verticalSnapOffsets;
+    Vector&lt;LayoutUnit&gt; horizontalSnapOffsets;
+    Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt; verticalSnapOffsetRanges;
+    Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt; horizontalSnapOffsetRanges;
</ins><span class="cx">     HashSet&lt;float&gt; seenVerticalSnapOffsets;
</span><span class="cx">     HashSet&lt;float&gt; seenHorizontalSnapOffsets;
</span><span class="cx">     bool hasHorizontalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::XAxis || scrollSnapType.axis == ScrollSnapAxis::Inline;
</span><span class="lines">@@ -133,7 +239,7 @@
</span><span class="cx">             auto absoluteScrollOffset = clampTo&lt;LayoutUnit&gt;(computeScrollSnapAlignOffset(scrollSnapArea.x(), scrollSnapArea.width(), alignment.x) - computeScrollSnapAlignOffset(scrollSnapPort.x(), scrollSnapPort.width(), alignment.x), 0, maxScrollLeft);
</span><span class="cx">             if (!seenHorizontalSnapOffsets.contains(absoluteScrollOffset)) {
</span><span class="cx">                 seenHorizontalSnapOffsets.add(absoluteScrollOffset);
</span><del>-                horizontalSnapOffsets-&gt;append(absoluteScrollOffset);
</del><ins>+                horizontalSnapOffsets.append(absoluteScrollOffset);
</ins><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx">         if (hasVerticalSnapOffsets &amp;&amp; alignment.y != ScrollSnapAxisAlignType::None) {
</span><span class="lines">@@ -140,38 +246,101 @@
</span><span class="cx">             auto absoluteScrollOffset = clampTo&lt;LayoutUnit&gt;(computeScrollSnapAlignOffset(scrollSnapArea.y(), scrollSnapArea.height(), alignment.y) - computeScrollSnapAlignOffset(scrollSnapPort.y(), scrollSnapPort.height(), alignment.y), 0, maxScrollTop);
</span><span class="cx">             if (!seenVerticalSnapOffsets.contains(absoluteScrollOffset)) {
</span><span class="cx">                 seenVerticalSnapOffsets.add(absoluteScrollOffset);
</span><del>-                verticalSnapOffsets-&gt;append(absoluteScrollOffset);
</del><ins>+                verticalSnapOffsets.append(absoluteScrollOffset);
</ins><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    std::sort(horizontalSnapOffsets-&gt;begin(), horizontalSnapOffsets-&gt;end());
-    if (horizontalSnapOffsets-&gt;size()) {
-        if (horizontalSnapOffsets-&gt;last() != maxScrollLeft)
-            horizontalSnapOffsets-&gt;append(maxScrollLeft);
-        if (horizontalSnapOffsets-&gt;first())
-            horizontalSnapOffsets-&gt;insert(0, 0);
</del><ins>+    if (!horizontalSnapOffsets.isEmpty()) {
+        adjustAxisSnapOffsetsForScrollExtent(horizontalSnapOffsets, maxScrollLeft);
</ins><span class="cx"> #if !LOG_DISABLED
</span><del>-        LOG(Scrolling, &quot; =&gt; Computed horizontal scroll snap offsets: %s&quot;, snapOffsetsToString(*horizontalSnapOffsets).utf8().data());
</del><ins>+        LOG(Scrolling, &quot; =&gt; Computed horizontal scroll snap offsets: %s&quot;, snapOffsetsToString(horizontalSnapOffsets).utf8().data());
+        LOG(Scrolling, &quot; =&gt; Computed horizontal scroll snap offset ranges: %s&quot;, snapOffsetRangesToString(horizontalSnapOffsetRanges).utf8().data());
</ins><span class="cx"> #endif
</span><del>-        scrollableArea.setHorizontalSnapOffsets(WTFMove(horizontalSnapOffsets));
</del><ins>+        if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity)
+            computeAxisProximitySnapOffsetRanges(horizontalSnapOffsets, horizontalSnapOffsetRanges, scrollSnapPort.width());
+
+        scrollableArea.setHorizontalSnapOffsets(horizontalSnapOffsets);
+        scrollableArea.setHorizontalSnapOffsetRanges(horizontalSnapOffsetRanges);
</ins><span class="cx">     } else
</span><span class="cx">         scrollableArea.clearHorizontalSnapOffsets();
</span><span class="cx"> 
</span><del>-    std::sort(verticalSnapOffsets-&gt;begin(), verticalSnapOffsets-&gt;end());
-    if (verticalSnapOffsets-&gt;size()) {
-        if (verticalSnapOffsets-&gt;last() != maxScrollTop)
-            verticalSnapOffsets-&gt;append(maxScrollTop);
-        if (verticalSnapOffsets-&gt;first())
-            verticalSnapOffsets-&gt;insert(0, 0);
</del><ins>+    if (!verticalSnapOffsets.isEmpty()) {
+        adjustAxisSnapOffsetsForScrollExtent(verticalSnapOffsets, maxScrollTop);
</ins><span class="cx"> #if !LOG_DISABLED
</span><del>-        LOG(Scrolling, &quot; =&gt; Computed vertical scroll snap offsets: %s&quot;, snapOffsetsToString(*verticalSnapOffsets).utf8().data());
</del><ins>+        LOG(Scrolling, &quot; =&gt; Computed vertical scroll snap offsets: %s&quot;, snapOffsetsToString(verticalSnapOffsets).utf8().data());
+        LOG(Scrolling, &quot; =&gt; Computed vertical scroll snap offset ranges: %s&quot;, snapOffsetRangesToString(verticalSnapOffsetRanges).utf8().data());
</ins><span class="cx"> #endif
</span><del>-        scrollableArea.setVerticalSnapOffsets(WTFMove(verticalSnapOffsets));
</del><ins>+        if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity)
+            computeAxisProximitySnapOffsetRanges(verticalSnapOffsets, verticalSnapOffsetRanges, scrollSnapPort.height());
+
+        scrollableArea.setVerticalSnapOffsets(verticalSnapOffsets);
+        scrollableArea.setVerticalSnapOffsetRanges(verticalSnapOffsetRanges);
</ins><span class="cx">     } else
</span><span class="cx">         scrollableArea.clearVerticalSnapOffsets();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+template &lt;typename LayoutType&gt;
+LayoutType closestSnapOffset(const Vector&lt;LayoutType&gt;&amp; snapOffsets, const Vector&lt;ScrollOffsetRange&lt;LayoutType&gt;&gt;&amp; snapOffsetRanges, LayoutType scrollDestination, float velocity, unsigned&amp; activeSnapIndex)
+{
+    ASSERT(snapOffsets.size());
+    activeSnapIndex = 0;
+
+    unsigned lowerSnapOffsetRangeIndex;
+    unsigned upperSnapOffsetRangeIndex;
+    indicesOfNearestSnapOffsetRanges&lt;LayoutType&gt;(scrollDestination, snapOffsetRanges, lowerSnapOffsetRangeIndex, upperSnapOffsetRangeIndex);
+    if (lowerSnapOffsetRangeIndex == upperSnapOffsetRangeIndex &amp;&amp; upperSnapOffsetRangeIndex != invalidSnapOffsetIndex) {
+        activeSnapIndex = invalidSnapOffsetIndex;
+        return scrollDestination;
+    }
+
+    if (scrollDestination &lt;= snapOffsets.first())
+        return snapOffsets.first();
+
+    activeSnapIndex = snapOffsets.size() - 1;
+    if (scrollDestination &gt;= snapOffsets.last())
+        return snapOffsets.last();
+
+    unsigned lowerIndex;
+    unsigned upperIndex;
+    indicesOfNearestSnapOffsets&lt;LayoutType&gt;(scrollDestination, snapOffsets, lowerIndex, upperIndex);
+    LayoutType lowerSnapPosition = snapOffsets[lowerIndex];
+    LayoutType upperSnapPosition = snapOffsets[upperIndex];
+    if (!std::abs(velocity)) {
+        bool isCloserToLowerSnapPosition = scrollDestination - lowerSnapPosition &lt;= upperSnapPosition - scrollDestination;
+        activeSnapIndex = isCloserToLowerSnapPosition ? lowerIndex : upperIndex;
+        return isCloserToLowerSnapPosition ? lowerSnapPosition : upperSnapPosition;
+    }
+
+    // Non-zero velocity indicates a flick gesture. Even if another snap point is closer, we should choose the one in the direction of the flick gesture
+    // as long as a scroll snap offset range does not lie between the scroll destination and the targeted snap offset.
+    if (velocity &lt; 0) {
+        if (lowerSnapOffsetRangeIndex != invalidSnapOffsetIndex &amp;&amp; lowerSnapPosition &lt; snapOffsetRanges[lowerSnapOffsetRangeIndex].end) {
+            activeSnapIndex = upperIndex;
+            return upperSnapPosition;
+        }
+        activeSnapIndex = lowerIndex;
+        return lowerSnapPosition;
+    }
+
+    if (upperSnapOffsetRangeIndex != invalidSnapOffsetIndex &amp;&amp; snapOffsetRanges[upperSnapOffsetRangeIndex].start &lt; upperSnapPosition) {
+        activeSnapIndex = lowerIndex;
+        return lowerSnapPosition;
+    }
+    activeSnapIndex = upperIndex;
+    return upperSnapPosition;
+}
+
+LayoutUnit closestSnapOffset(const Vector&lt;LayoutUnit&gt;&amp; snapOffsets, const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; snapOffsetRanges, LayoutUnit scrollDestination, float velocity, unsigned&amp; activeSnapIndex)
+{
+    return closestSnapOffset&lt;LayoutUnit&gt;(snapOffsets, snapOffsetRanges, scrollDestination, velocity, activeSnapIndex);
+}
+
+float closestSnapOffset(const Vector&lt;float&gt;&amp; snapOffsets, const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp; snapOffsetRanges, float scrollDestination, float velocity, unsigned&amp; activeSnapIndex)
+{
+    return closestSnapOffset&lt;float&gt;(snapOffsets, snapOffsetRanges, scrollDestination, velocity, activeSnapIndex);
+}
+
</ins><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> #endif // CSS_SCROLL_SNAP
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingAxisScrollSnapOffsetsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -27,6 +27,8 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx"> 
</span><ins>+#include &quot;LayoutUnit.h&quot;
+#include &quot;ScrollSnapOffsetsInfo.h&quot;
</ins><span class="cx"> #include &quot;ScrollTypes.h&quot;
</span><span class="cx"> #include &lt;wtf/Vector.h&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -39,48 +41,10 @@
</span><span class="cx"> 
</span><span class="cx"> void updateSnapOffsetsForScrollableArea(ScrollableArea&amp;, HTMLElement&amp; scrollingElement, const RenderBox&amp; scrollingElementBox, const RenderStyle&amp; scrollingElementStyle);
</span><span class="cx"> 
</span><del>-// closestSnapOffset is a templated function that takes in a Vector representing snap offsets as LayoutTypes (e.g. LayoutUnit or float) and
-// as well as a VelocityType indicating the velocity (e.g. float, CGFloat, etc.) This function is templated because the UI process will now
-// use pixel snapped floats to represent snap offsets rather than LayoutUnits.
-template &lt;typename LayoutType, typename VelocityType&gt;
-LayoutType closestSnapOffset(const Vector&lt;LayoutType&gt;&amp; snapOffsets, LayoutType scrollDestination, VelocityType velocity, unsigned&amp; activeSnapIndex)
-{
-    ASSERT(snapOffsets.size());
-    activeSnapIndex = 0;
-    if (scrollDestination &lt;= snapOffsets.first())
-        return snapOffsets.first();
</del><ins>+const unsigned invalidSnapOffsetIndex = UINT_MAX;
+WEBCORE_EXPORT LayoutUnit closestSnapOffset(const Vector&lt;LayoutUnit&gt;&amp; snapOffsets, const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; snapOffsetRanges, LayoutUnit scrollDestination, float velocity, unsigned&amp; activeSnapIndex);
+WEBCORE_EXPORT float closestSnapOffset(const Vector&lt;float&gt;&amp; snapOffsets, const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp; snapOffsetRanges, float scrollDestination, float velocity, unsigned&amp; activeSnapIndex);
</ins><span class="cx"> 
</span><del>-    activeSnapIndex = snapOffsets.size() - 1;
-    if (scrollDestination &gt;= snapOffsets.last())
-        return snapOffsets.last();
-
-    size_t lowerIndex = 0;
-    size_t upperIndex = snapOffsets.size() - 1;
-    while (lowerIndex &lt; upperIndex - 1) {
-        size_t middleIndex = (lowerIndex + upperIndex) / 2;
-        if (scrollDestination &lt; snapOffsets[middleIndex])
-            upperIndex = middleIndex;
-        else if (scrollDestination &gt; snapOffsets[middleIndex])
-            lowerIndex = middleIndex;
-        else {
-            upperIndex = middleIndex;
-            lowerIndex = middleIndex;
-            break;
-        }
-    }
-    LayoutType lowerSnapPosition = snapOffsets[lowerIndex];
-    LayoutType upperSnapPosition = snapOffsets[upperIndex];
-    // Nonzero velocity indicates a flick gesture. Even if another snap point is closer, snap to the one in the direction of the flick gesture.
-    if (velocity) {
-        activeSnapIndex = (velocity &lt; 0) ? lowerIndex : upperIndex;
-        return velocity &lt; 0 ? lowerSnapPosition : upperSnapPosition;
-    }
-
-    bool isCloserToLowerSnapPosition = scrollDestination - lowerSnapPosition &lt;= upperSnapPosition - scrollDestination;
-    activeSnapIndex = isCloserToLowerSnapPosition ? lowerIndex : upperIndex;
-    return isCloserToLowerSnapPosition ? lowerSnapPosition : upperSnapPosition;
-}
-
</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="trunkSourceWebCorepagescrollingScrollSnapOffsetsInfohfromrev210559trunkSourceWebCorepagescrollingmacScrollingMomentumCalculatorMach"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h (from rev 210559, trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h) (0 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h                                (rev 0)
+++ trunk/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h        2017-01-10 22:26:56 UTC (rev 210560)
</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 &lt;wtf/Vector.h&gt;
+
+namespace WebCore {
+
+template &lt;typename T&gt;
+struct ScrollOffsetRange {
+    T start;
+    T end;
+};
+
+template &lt;typename T&gt;
+struct ScrollSnapOffsetsInfo {
+    Vector&lt;T&gt; horizontalSnapOffsets;
+    Vector&lt;T&gt; verticalSnapOffsets;
+
+    // Snap offset ranges represent non-empty ranges of scroll offsets in which scrolling may rest after scroll snapping.
+    // These are used in two cases: (1) for proximity scroll snapping, where portions of areas between adjacent snap offsets
+    // may emit snap offset ranges, and (2) in the case where the snap area is larger than the snap port, in which case areas
+    // where the snap port fits within the snap area are considered to be valid snap positions.
+    Vector&lt;ScrollOffsetRange&lt;T&gt;&gt; horizontalSnapOffsetRanges;
+    Vector&lt;ScrollOffsetRange&lt;T&gt;&gt; verticalSnapOffsetRanges;
+};
+
+}; // namespace WebCore
</ins></span></pre></div>
<a id="trunkSourceWebCorepagescrollingScrollingCoordinatorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/ScrollingCoordinator.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/ScrollingCoordinator.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/ScrollingCoordinator.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> #include &quot;IntRect.h&quot;
</span><span class="cx"> #include &quot;LayoutRect.h&quot;
</span><span class="cx"> #include &quot;PlatformWheelEvent.h&quot;
</span><ins>+#include &quot;ScrollSnapOffsetsInfo.h&quot;
</ins><span class="cx"> #include &quot;ScrollTypes.h&quot;
</span><span class="cx"> #include &lt;wtf/Forward.h&gt;
</span><span class="cx"> #include &lt;wtf/ThreadSafeRefCounted.h&gt;
</span><span class="lines">@@ -170,6 +171,8 @@
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx">         Vector&lt;LayoutUnit&gt; horizontalSnapOffsets;
</span><span class="cx">         Vector&lt;LayoutUnit&gt; verticalSnapOffsets;
</span><ins>+        Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt; horizontalSnapOffsetRanges;
+        Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt; verticalSnapOffsetRanges;
</ins><span class="cx">         unsigned currentHorizontalSnapPointIndex;
</span><span class="cx">         unsigned currentVerticalSnapPointIndex;
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingScrollingMomentumCalculatorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -32,34 +32,62 @@
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> static const Seconds scrollSnapAnimationDuration = 1_s;
</span><ins>+static inline float projectedInertialScrollDistance(float initialWheelDelta)
+{
+    // On macOS 10.10 and earlier, we don't have a platform scrolling momentum calculator, so we instead approximate the scroll destination
+    // by multiplying the initial wheel delta by a constant factor. By running a few experiments (i.e. logging scroll destination and initial
+    // wheel delta for many scroll gestures) we determined that this is a reasonable way to approximate where scrolling will take us without
+    // using _NSScrollingMomentumCalculator.
+    const static double inertialScrollPredictionFactor = 16.7;
+    return inertialScrollPredictionFactor * initialWheelDelta;
+}
</ins><span class="cx"> 
</span><del>-ScrollingMomentumCalculator::ScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity)
</del><ins>+ScrollingMomentumCalculator::ScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity)
</ins><span class="cx">     : m_initialDelta(initialDelta)
</span><span class="cx">     , m_initialVelocity(initialVelocity)
</span><span class="cx">     , m_initialScrollOffset(initialOffset.x(), initialOffset.y())
</span><del>-    , m_targetScrollOffset(targetOffset.x(), targetOffset.y())
</del><span class="cx">     , m_viewportSize(viewportSize)
</span><span class="cx">     , m_contentSize(contentSize)
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void ScrollingMomentumCalculator::setRetargetedScrollOffset(const FloatSize&amp; target)
+{
+    if (m_retargetedScrollOffset &amp;&amp; m_retargetedScrollOffset == target)
+        return;
+
+    m_retargetedScrollOffset = target;
+    retargetedScrollOffsetDidChange();
+}
+
+FloatSize ScrollingMomentumCalculator::predictedDestinationOffset()
+{
+    float initialOffsetX = clampTo&lt;float&gt;(m_initialScrollOffset.width() + projectedInertialScrollDistance(m_initialDelta.width()), 0, m_contentSize.width() - m_viewportSize.width());
+    float initialOffsetY = clampTo&lt;float&gt;(m_initialScrollOffset.height() + projectedInertialScrollDistance(m_initialDelta.height()), 0, m_contentSize.height() - m_viewportSize.height());
+    return { initialOffsetX, initialOffsetY };
+}
+
</ins><span class="cx"> #if !HAVE(NSSCROLLING_FILTERS)
</span><span class="cx"> 
</span><del>-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 FloatSize&amp; initialVelocity)
</del><ins>+std::unique_ptr&lt;ScrollingMomentumCalculator&gt; ScrollingMomentumCalculator::create(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity)
</ins><span class="cx"> {
</span><del>-    return std::make_unique&lt;BasicScrollingMomentumCalculator&gt;(viewportSize, contentSize, initialOffset, targetOffset, initialDelta, initialVelocity);
</del><ins>+    return std::make_unique&lt;BasicScrollingMomentumCalculator&gt;(viewportSize, contentSize, initialOffset, initialDelta, initialVelocity);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void ScrollingMomentumCalculator::setPlatformMomentumScrollingPredictionEnabled(bool)
+{
+}
+
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-BasicScrollingMomentumCalculator::BasicScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity)
-    : ScrollingMomentumCalculator(viewportSize, contentSize, initialOffset, targetOffset, initialDelta, initialVelocity)
</del><ins>+BasicScrollingMomentumCalculator::BasicScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity)
+    : ScrollingMomentumCalculator(viewportSize, contentSize, initialOffset, initialDelta, initialVelocity)
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-FloatSize BasicScrollingMomentumCalculator::linearlyInterpolatedOffsetAtProgress(float progress) const
</del><ins>+FloatSize BasicScrollingMomentumCalculator::linearlyInterpolatedOffsetAtProgress(float progress)
</ins><span class="cx"> {
</span><del>-    return m_initialScrollOffset + progress * (m_targetScrollOffset - m_initialScrollOffset);
</del><ins>+    return m_initialScrollOffset + progress * (retargetedScrollOffset() - m_initialScrollOffset);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> FloatSize BasicScrollingMomentumCalculator::cubicallyInterpolatedOffsetAtProgress(float progress) const
</span><span class="lines">@@ -120,7 +148,7 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    FloatSize startToEndVector = m_targetScrollOffset - m_initialScrollOffset;
</del><ins>+    FloatSize startToEndVector = retargetedScrollOffset() - m_initialScrollOffset;
</ins><span class="cx">     float startToEndDistance = startToEndVector.diagonalLength();
</span><span class="cx">     if (!startToEndDistance) {
</span><span class="cx">         // The start and end positions are the same, so we shouldn't try to interpolate a path.
</span><span class="lines">@@ -140,7 +168,7 @@
</span><span class="cx">     m_snapAnimationCurveCoefficients[0] = m_initialScrollOffset;
</span><span class="cx">     m_snapAnimationCurveCoefficients[1] = 3 * (controlVector1 - m_initialScrollOffset);
</span><span class="cx">     m_snapAnimationCurveCoefficients[2] = 3 * (m_initialScrollOffset - 2 * controlVector1 + controlVector2);
</span><del>-    m_snapAnimationCurveCoefficients[3] = 3 * (controlVector1 - controlVector2) - m_initialScrollOffset + m_targetScrollOffset;
</del><ins>+    m_snapAnimationCurveCoefficients[3] = 3 * (controlVector1 - controlVector2) - m_initialScrollOffset + retargetedScrollOffset();
</ins><span class="cx">     m_forceLinearAnimationCurve = false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -179,10 +207,10 @@
</span><span class="cx">     static const float minScrollSnapInitialProgress = 0.1;
</span><span class="cx">     static const float maxScrollSnapInitialProgress = 0.5;
</span><span class="cx"> 
</span><del>-    FloatSize alignmentVector = m_initialDelta * (m_targetScrollOffset - m_initialScrollOffset);
</del><ins>+    FloatSize alignmentVector = m_initialDelta * (retargetedScrollOffset() - m_initialScrollOffset);
</ins><span class="cx">     float initialProgress;
</span><span class="cx">     if (alignmentVector.width() + alignmentVector.height() &gt; 0)
</span><del>-        initialProgress = clampTo(m_initialDelta.diagonalLength() / (m_targetScrollOffset - m_initialScrollOffset).diagonalLength(), minScrollSnapInitialProgress, maxScrollSnapInitialProgress);
</del><ins>+        initialProgress = clampTo(m_initialDelta.diagonalLength() / (retargetedScrollOffset() - m_initialScrollOffset).diagonalLength(), minScrollSnapInitialProgress, maxScrollSnapInitialProgress);
</ins><span class="cx">     else
</span><span class="cx">         initialProgress = minScrollSnapInitialProgress;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingScrollingMomentumCalculatorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx"> #include &quot;AxisScrollSnapOffsets.h&quot;
</span><span class="cx"> #include &quot;PlatformWheelEvent.h&quot;
</span><span class="cx"> #include &quot;ScrollTypes.h&quot;
</span><ins>+#include &lt;wtf/Optional.h&gt;
</ins><span class="cx"> #include &lt;wtf/Seconds.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="lines">@@ -39,25 +40,33 @@
</span><span class="cx"> 
</span><span class="cx"> class ScrollingMomentumCalculator {
</span><span class="cx"> public:
</span><del>-    ScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatSize&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 FloatSize&amp; initialVelocity);
</del><ins>+    ScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity);
+    static std::unique_ptr&lt;ScrollingMomentumCalculator&gt; create(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity);
+    WEBCORE_EXPORT static void setPlatformMomentumScrollingPredictionEnabled(bool);
</ins><span class="cx">     virtual ~ScrollingMomentumCalculator() { }
</span><span class="cx"> 
</span><span class="cx">     virtual FloatPoint scrollOffsetAfterElapsedTime(Seconds) = 0;
</span><span class="cx">     virtual Seconds animationDuration() = 0;
</span><ins>+    virtual FloatSize predictedDestinationOffset();
+    void setRetargetedScrollOffset(const FloatSize&amp;);
</ins><span class="cx"> 
</span><span class="cx"> protected:
</span><ins>+    const FloatSize&amp; retargetedScrollOffset() const { return m_retargetedScrollOffset.value(); }
+    virtual void retargetedScrollOffsetDidChange() { }
+
</ins><span class="cx">     FloatSize m_initialDelta;
</span><span class="cx">     FloatSize m_initialVelocity;
</span><span class="cx">     FloatSize m_initialScrollOffset;
</span><del>-    FloatSize m_targetScrollOffset;
</del><span class="cx">     FloatSize m_viewportSize;
</span><span class="cx">     FloatSize m_contentSize;
</span><ins>+
+private:
+    std::optional&lt;FloatSize&gt; m_retargetedScrollOffset;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> class BasicScrollingMomentumCalculator final : public ScrollingMomentumCalculator {
</span><span class="cx"> public:
</span><del>-    BasicScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity);
</del><ins>+    BasicScrollingMomentumCalculator(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity);
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     FloatPoint scrollOffsetAfterElapsedTime(Seconds) final;
</span><span class="lines">@@ -65,7 +74,7 @@
</span><span class="cx">     void initializeInterpolationCoefficientsIfNecessary();
</span><span class="cx">     void initializeSnapProgressCurve();
</span><span class="cx">     float animationProgressAfterElapsedTime(Seconds) const;
</span><del>-    FloatSize linearlyInterpolatedOffsetAtProgress(float progress) const;
</del><ins>+    FloatSize linearlyInterpolatedOffsetAtProgress(float progress);
</ins><span class="cx">     FloatSize cubicallyInterpolatedOffsetAtProgress(float progress) const;
</span><span class="cx"> 
</span><span class="cx">     float m_snapAnimationCurveMagnitude { 0 };
</span><span class="lines">@@ -73,6 +82,7 @@
</span><span class="cx">     FloatSize m_snapAnimationCurveCoefficients[4] { };
</span><span class="cx">     bool m_forceLinearAnimationCurve { false };
</span><span class="cx">     bool m_momentumCalculatorRequiresInitialization { true };
</span><ins>+    std::optional&lt;FloatSize&gt; m_predictedDestinationOffset;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingScrollingStateScrollingNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -47,8 +47,7 @@
</span><span class="cx">     , m_requestedScrollPosition(stateNode.requestedScrollPosition())
</span><span class="cx">     , m_scrollOrigin(stateNode.scrollOrigin())
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    , m_horizontalSnapOffsets(stateNode.horizontalSnapOffsets())
-    , m_verticalSnapOffsets(stateNode.verticalSnapOffsets())
</del><ins>+    , m_snapOffsetsInfo(stateNode.m_snapOffsetsInfo)
</ins><span class="cx"> #endif
</span><span class="cx">     , m_scrollableAreaParameters(stateNode.scrollableAreaParameters())
</span><span class="cx">     , m_requestedScrollPositionRepresentsProgrammaticScroll(stateNode.requestedScrollPositionRepresentsProgrammaticScroll())
</span><span class="lines">@@ -108,22 +107,40 @@
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx"> void ScrollingStateScrollingNode::setHorizontalSnapOffsets(const Vector&lt;float&gt;&amp; snapOffsets)
</span><span class="cx"> {
</span><del>-    if (m_horizontalSnapOffsets == snapOffsets)
</del><ins>+    if (m_snapOffsetsInfo.horizontalSnapOffsets == snapOffsets)
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    m_horizontalSnapOffsets = snapOffsets;
</del><ins>+    m_snapOffsetsInfo.horizontalSnapOffsets = snapOffsets;
</ins><span class="cx">     setPropertyChanged(HorizontalSnapOffsets);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollingStateScrollingNode::setVerticalSnapOffsets(const Vector&lt;float&gt;&amp; snapOffsets)
</span><span class="cx"> {
</span><del>-    if (m_verticalSnapOffsets == snapOffsets)
</del><ins>+    if (m_snapOffsetsInfo.verticalSnapOffsets == snapOffsets)
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    m_verticalSnapOffsets = snapOffsets;
</del><ins>+    m_snapOffsetsInfo.verticalSnapOffsets = snapOffsets;
</ins><span class="cx">     setPropertyChanged(VerticalSnapOffsets);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void ScrollingStateScrollingNode::setHorizontalSnapOffsetRanges(const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp; scrollOffsetRanges)
+{
+    if (m_snapOffsetsInfo.horizontalSnapOffsetRanges == scrollOffsetRanges)
+        return;
+
+    m_snapOffsetsInfo.horizontalSnapOffsetRanges = scrollOffsetRanges;
+    setPropertyChanged(HorizontalSnapOffsetRanges);
+}
+
+void ScrollingStateScrollingNode::setVerticalSnapOffsetRanges(const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp; scrollOffsetRanges)
+{
+    if (m_snapOffsetsInfo.verticalSnapOffsetRanges == scrollOffsetRanges)
+        return;
+
+    m_snapOffsetsInfo.verticalSnapOffsetRanges = scrollOffsetRanges;
+    setPropertyChanged(VerticalSnapOffsetRanges);
+}
+
</ins><span class="cx"> void ScrollingStateScrollingNode::setCurrentHorizontalSnapPointIndex(unsigned index)
</span><span class="cx"> {
</span><span class="cx">     if (m_currentHorizontalSnapPointIndex == index)
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingScrollingStateScrollingNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS)
</span><span class="cx"> 
</span><ins>+#include &quot;ScrollSnapOffsetsInfo.h&quot;
</ins><span class="cx"> #include &quot;ScrollTypes.h&quot;
</span><span class="cx"> #include &quot;ScrollingCoordinator.h&quot;
</span><span class="cx"> #include &quot;ScrollingStateNode.h&quot;
</span><span class="lines">@@ -49,6 +50,8 @@
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx">         HorizontalSnapOffsets,
</span><span class="cx">         VerticalSnapOffsets,
</span><ins>+        HorizontalSnapOffsetRanges,
+        VerticalSnapOffsetRanges,
</ins><span class="cx">         CurrentHorizontalSnapOffsetIndex,
</span><span class="cx">         CurrentVerticalSnapOffsetIndex,
</span><span class="cx"> #endif
</span><span class="lines">@@ -71,12 +74,18 @@
</span><span class="cx">     WEBCORE_EXPORT void setScrollOrigin(const IntPoint&amp;);
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    const Vector&lt;float&gt;&amp; horizontalSnapOffsets() const { return m_horizontalSnapOffsets; }
</del><ins>+    const Vector&lt;float&gt;&amp; horizontalSnapOffsets() const { return m_snapOffsetsInfo.horizontalSnapOffsets; }
</ins><span class="cx">     WEBCORE_EXPORT void setHorizontalSnapOffsets(const Vector&lt;float&gt;&amp;);
</span><span class="cx"> 
</span><del>-    const Vector&lt;float&gt;&amp; verticalSnapOffsets() const { return m_verticalSnapOffsets; }
</del><ins>+    const Vector&lt;float&gt;&amp; verticalSnapOffsets() const { return m_snapOffsetsInfo.verticalSnapOffsets; }
</ins><span class="cx">     WEBCORE_EXPORT void setVerticalSnapOffsets(const Vector&lt;float&gt;&amp;);
</span><span class="cx"> 
</span><ins>+    const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp; horizontalSnapOffsetRanges() const { return m_snapOffsetsInfo.horizontalSnapOffsetRanges; }
+    WEBCORE_EXPORT void setHorizontalSnapOffsetRanges(const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp;);
+
+    const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp; verticalSnapOffsetRanges() const { return m_snapOffsetsInfo.verticalSnapOffsetRanges; }
+    WEBCORE_EXPORT void setVerticalSnapOffsetRanges(const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp;);
+
</ins><span class="cx">     unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
</span><span class="cx">     WEBCORE_EXPORT void setCurrentHorizontalSnapPointIndex(unsigned);
</span><span class="cx"> 
</span><span class="lines">@@ -108,8 +117,7 @@
</span><span class="cx">     FloatPoint m_requestedScrollPosition;
</span><span class="cx">     IntPoint m_scrollOrigin;
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    Vector&lt;float&gt; m_horizontalSnapOffsets;
-    Vector&lt;float&gt; m_verticalSnapOffsets;
</del><ins>+    ScrollSnapOffsetsInfo&lt;float&gt; m_snapOffsetsInfo;
</ins><span class="cx">     unsigned m_currentHorizontalSnapPointIndex { 0 };
</span><span class="cx">     unsigned m_currentVerticalSnapPointIndex { 0 };
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingScrollingTreeScrollingNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -70,11 +70,17 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx">     if (state.hasChangedProperty(ScrollingStateScrollingNode::HorizontalSnapOffsets))
</span><del>-        m_horizontalSnapOffsets = state.horizontalSnapOffsets();
</del><ins>+        m_snapOffsetsInfo.horizontalSnapOffsets = state.horizontalSnapOffsets();
</ins><span class="cx"> 
</span><span class="cx">     if (state.hasChangedProperty(ScrollingStateScrollingNode::VerticalSnapOffsets))
</span><del>-        m_verticalSnapOffsets = state.verticalSnapOffsets();
</del><ins>+        m_snapOffsetsInfo.verticalSnapOffsets = state.verticalSnapOffsets();
</ins><span class="cx"> 
</span><ins>+    if (state.hasChangedProperty(ScrollingStateScrollingNode::HorizontalSnapOffsetRanges))
+        m_snapOffsetsInfo.horizontalSnapOffsetRanges = state.horizontalSnapOffsetRanges();
+
+    if (state.hasChangedProperty(ScrollingStateScrollingNode::VerticalSnapOffsetRanges))
+        m_snapOffsetsInfo.verticalSnapOffsetRanges = state.verticalSnapOffsetRanges();
+
</ins><span class="cx">     if (state.hasChangedProperty(ScrollingStateScrollingNode::CurrentHorizontalSnapOffsetIndex))
</span><span class="cx">         m_currentHorizontalSnapPointIndex = state.currentHorizontalSnapPointIndex();
</span><span class="cx"> 
</span><span class="lines">@@ -139,14 +145,14 @@
</span><span class="cx">         ts.dumpProperty(&quot;scrollable area size&quot;, m_scrollOrigin);
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    if (m_horizontalSnapOffsets.size())
-        ts.dumpProperty(&quot;horizontal snap offsets&quot;, m_horizontalSnapOffsets);
</del><ins>+    if (m_snapOffsetsInfo.horizontalSnapOffsets.size())
+        ts.dumpProperty(&quot;horizontal snap offsets&quot;, m_snapOffsetsInfo.horizontalSnapOffsets);
</ins><span class="cx"> 
</span><del>-    if (m_verticalSnapOffsets.size())
-        ts.dumpProperty(&quot;horizontal snap offsets&quot;, m_verticalSnapOffsets);
</del><ins>+    if (m_snapOffsetsInfo.verticalSnapOffsets.size())
+        ts.dumpProperty(&quot;horizontal snap offsets&quot;, m_snapOffsetsInfo.verticalSnapOffsets);
</ins><span class="cx"> 
</span><span class="cx">     if (m_currentHorizontalSnapPointIndex)
</span><del>-        ts.dumpProperty(&quot;current horizontal snap point index&quot;, m_verticalSnapOffsets);
</del><ins>+        ts.dumpProperty(&quot;current horizontal snap point index&quot;, m_currentHorizontalSnapPointIndex);
</ins><span class="cx"> 
</span><span class="cx">     if (m_currentVerticalSnapPointIndex)
</span><span class="cx">         ts.dumpProperty(&quot;current vertical snap point index&quot;, m_currentVerticalSnapPointIndex);
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingScrollingTreeScrollingNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> #if ENABLE(ASYNC_SCROLLING)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;IntRect.h&quot;
</span><ins>+#include &quot;ScrollSnapOffsetsInfo.h&quot;
</ins><span class="cx"> #include &quot;ScrollTypes.h&quot;
</span><span class="cx"> #include &quot;ScrollingCoordinator.h&quot;
</span><span class="cx"> #include &quot;ScrollingTreeNode.h&quot;
</span><span class="lines">@@ -56,8 +57,10 @@
</span><span class="cx">     virtual FloatPoint scrollPosition() const = 0;
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    const Vector&lt;float&gt;&amp; horizontalSnapOffsets() const { return m_horizontalSnapOffsets; }
-    const Vector&lt;float&gt;&amp; verticalSnapOffsets() const { return m_verticalSnapOffsets; }
</del><ins>+    const Vector&lt;float&gt;&amp; horizontalSnapOffsets() const { return m_snapOffsetsInfo.horizontalSnapOffsets; }
+    const Vector&lt;float&gt;&amp; verticalSnapOffsets() const { return m_snapOffsetsInfo.verticalSnapOffsets; }
+    const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp; horizontalSnapOffsetRanges() const { return m_snapOffsetsInfo.horizontalSnapOffsetRanges; }
+    const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp; verticalSnapOffsetRanges() const { return m_snapOffsetsInfo.verticalSnapOffsetRanges; }
</ins><span class="cx">     unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
</span><span class="cx">     unsigned currentVerticalSnapPointIndex() const { return m_currentVerticalSnapPointIndex; }
</span><span class="cx">     void setCurrentHorizontalSnapPointIndex(unsigned index) { m_currentHorizontalSnapPointIndex = index; }
</span><span class="lines">@@ -102,8 +105,7 @@
</span><span class="cx">     FloatPoint m_lastCommittedScrollPosition;
</span><span class="cx">     IntPoint m_scrollOrigin;
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    Vector&lt;float&gt; m_horizontalSnapOffsets;
-    Vector&lt;float&gt; m_verticalSnapOffsets;
</del><ins>+    ScrollSnapOffsetsInfo&lt;float&gt; m_snapOffsetsInfo;
</ins><span class="cx">     unsigned m_currentHorizontalSnapPointIndex { 0 };
</span><span class="cx">     unsigned m_currentVerticalSnapPointIndex { 0 };
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingmacScrollingMomentumCalculatorMach"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -36,15 +36,19 @@
</span><span class="cx"> 
</span><span class="cx"> class ScrollingMomentumCalculatorMac final : public ScrollingMomentumCalculator {
</span><span class="cx"> public:
</span><del>-    ScrollingMomentumCalculatorMac(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity);
</del><ins>+    ScrollingMomentumCalculatorMac(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity);
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     FloatPoint scrollOffsetAfterElapsedTime(Seconds) final;
</span><span class="cx">     Seconds animationDuration() final;
</span><ins>+    FloatSize predictedDestinationOffset() final;
+    void retargetedScrollOffsetDidChange() final;
</ins><span class="cx">     _NSScrollingMomentumCalculator *ensurePlatformMomentumCalculator();
</span><ins>+    bool requiresMomentumScrolling();
</ins><span class="cx"> 
</span><span class="cx">     RetainPtr&lt;_NSScrollingMomentumCalculator&gt; m_platformMomentumCalculator;
</span><del>-    bool m_requiresMomentumScrolling { true };
</del><ins>+    std::optional&lt;bool&gt; m_requiresMomentumScrolling;
+    FloatPoint m_initialDestinationOrigin;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingmacScrollingMomentumCalculatorMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.mm (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.mm        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingMomentumCalculatorMac.mm        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -32,33 +32,64 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-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 FloatSize&amp; initialVelocity)
</del><ins>+static bool gEnablePlatformMomentumScrollingPrediction = true;
+
+std::unique_ptr&lt;ScrollingMomentumCalculator&gt; ScrollingMomentumCalculator::create(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity)
</ins><span class="cx"> {
</span><del>-    return std::make_unique&lt;ScrollingMomentumCalculatorMac&gt;(viewportSize, contentSize, initialOffset, targetOffset, initialDelta, initialVelocity);
</del><ins>+    return std::make_unique&lt;ScrollingMomentumCalculatorMac&gt;(viewportSize, contentSize, initialOffset, initialDelta, initialVelocity);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-ScrollingMomentumCalculatorMac::ScrollingMomentumCalculatorMac(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatPoint&amp; targetOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity)
-    : ScrollingMomentumCalculator(viewportSize, contentSize, initialOffset, targetOffset, initialDelta, initialVelocity)
-    , m_requiresMomentumScrolling(initialOffset != targetOffset || initialVelocity.area())
</del><ins>+void ScrollingMomentumCalculator::setPlatformMomentumScrollingPredictionEnabled(bool enabled)
</ins><span class="cx"> {
</span><ins>+    gEnablePlatformMomentumScrollingPrediction = enabled;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+ScrollingMomentumCalculatorMac::ScrollingMomentumCalculatorMac(const FloatSize&amp; viewportSize, const FloatSize&amp; contentSize, const FloatPoint&amp; initialOffset, const FloatSize&amp; initialDelta, const FloatSize&amp; initialVelocity)
+    : ScrollingMomentumCalculator(viewportSize, contentSize, initialOffset, initialDelta, initialVelocity)
+{
+}
+
</ins><span class="cx"> FloatPoint ScrollingMomentumCalculatorMac::scrollOffsetAfterElapsedTime(Seconds elapsedTime)
</span><span class="cx"> {
</span><del>-    if (!m_requiresMomentumScrolling)
-        return { m_targetScrollOffset.width(), m_targetScrollOffset.height() };
</del><ins>+    if (!requiresMomentumScrolling())
+        return { retargetedScrollOffset().width(), retargetedScrollOffset().height() };
</ins><span class="cx"> 
</span><span class="cx">     return [ensurePlatformMomentumCalculator() positionAfterDuration:elapsedTime.value()];
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+FloatSize ScrollingMomentumCalculatorMac::predictedDestinationOffset()
+{
+    if (!gEnablePlatformMomentumScrollingPrediction)
+        return ScrollingMomentumCalculator::predictedDestinationOffset();
+
+    ensurePlatformMomentumCalculator();
+    return { m_initialDestinationOrigin.x(), m_initialDestinationOrigin.y() };
+}
+
+void ScrollingMomentumCalculatorMac::retargetedScrollOffsetDidChange()
+{
+    _NSScrollingMomentumCalculator *calculator = ensurePlatformMomentumCalculator();
+    calculator.destinationOrigin = NSMakePoint(retargetedScrollOffset().width(), retargetedScrollOffset().height());
+#if __MAC_OS_X_VERSION_MIN_REQUIRED &gt;= 101200
+    [calculator calculateToReachDestination];
+#endif
+}
+
</ins><span class="cx"> Seconds ScrollingMomentumCalculatorMac::animationDuration()
</span><span class="cx"> {
</span><del>-    if (!m_requiresMomentumScrolling)
</del><ins>+    if (!requiresMomentumScrolling())
</ins><span class="cx">         return 0_s;
</span><span class="cx"> 
</span><span class="cx">     return Seconds([ensurePlatformMomentumCalculator() durationUntilStop]);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool ScrollingMomentumCalculatorMac::requiresMomentumScrolling()
+{
+    if (m_requiresMomentumScrolling == std::nullopt)
+        m_requiresMomentumScrolling = m_initialScrollOffset != retargetedScrollOffset() || m_initialVelocity.area();
+    return m_requiresMomentumScrolling.value();
+}
+
</ins><span class="cx"> _NSScrollingMomentumCalculator *ScrollingMomentumCalculatorMac::ensurePlatformMomentumCalculator()
</span><span class="cx"> {
</span><span class="cx">     if (m_platformMomentumCalculator)
</span><span class="lines">@@ -68,10 +99,7 @@
</span><span class="cx">     NSRect contentFrame = NSMakeRect(0, 0, m_contentSize.width(), m_contentSize.height());
</span><span class="cx">     NSPoint velocity = NSMakePoint(m_initialVelocity.width(), m_initialVelocity.height());
</span><span class="cx">     m_platformMomentumCalculator = adoptNS([[_NSScrollingMomentumCalculator alloc] initWithInitialOrigin:origin velocity:velocity documentFrame:contentFrame constrainedClippingOrigin:NSZeroPoint clippingSize:m_viewportSize tolerance:NSMakeSize(1, 1)]);
</span><del>-    [m_platformMomentumCalculator setDestinationOrigin:NSMakePoint(m_targetScrollOffset.width(), m_targetScrollOffset.height())];
-#if __MAC_OS_X_VERSION_MIN_REQUIRED &gt;= 101200
-    [m_platformMomentumCalculator calculateToReachDestination];
-#endif
</del><ins>+    m_initialDestinationOrigin = [m_platformMomentumCalculator destinationOrigin];
</ins><span class="cx">     return m_platformMomentumCalculator.get();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorepagescrollingmacScrollingTreeFrameScrollingNodeMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -90,6 +90,16 @@
</span><span class="cx"> 
</span><span class="cx">     return snapOffsets;
</span><span class="cx"> }
</span><ins>+
+static inline Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt; convertToLayoutUnits(const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp; snapOffsetRangesAsFloat)
+{
+    Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt; snapOffsetRanges;
+    snapOffsetRanges.reserveInitialCapacity(snapOffsetRangesAsFloat.size());
+    for (auto range : snapOffsetRangesAsFloat)
+        snapOffsetRanges.uncheckedAppend({ range.start, range.end });
+
+    return snapOffsetRanges;
+}
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> void ScrollingTreeFrameScrollingNodeMac::commitStateBeforeChildren(const ScrollingStateNode&amp; stateNode)
</span><span class="lines">@@ -146,11 +156,11 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HorizontalSnapOffsets))
-        m_scrollController.updateScrollSnapPoints(ScrollEventAxis::Horizontal, convertToLayoutUnits(scrollingStateNode.horizontalSnapOffsets()));
</del><ins>+    if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HorizontalSnapOffsets) || scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HorizontalSnapOffsetRanges))
+        m_scrollController.updateScrollSnapPoints(ScrollEventAxis::Horizontal, convertToLayoutUnits(scrollingStateNode.horizontalSnapOffsets()), convertToLayoutUnits(scrollingStateNode.horizontalSnapOffsetRanges()));
</ins><span class="cx"> 
</span><del>-    if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::VerticalSnapOffsets))
-        m_scrollController.updateScrollSnapPoints(ScrollEventAxis::Vertical, convertToLayoutUnits(scrollingStateNode.verticalSnapOffsets()));
</del><ins>+    if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::VerticalSnapOffsets) || scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::VerticalSnapOffsetRanges))
+        m_scrollController.updateScrollSnapPoints(ScrollEventAxis::Vertical, convertToLayoutUnits(scrollingStateNode.verticalSnapOffsets()), convertToLayoutUnits(scrollingStateNode.verticalSnapOffsetRanges()));
</ins><span class="cx"> 
</span><span class="cx">     if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::CurrentHorizontalSnapOffsetIndex))
</span><span class="cx">         m_scrollController.setActiveScrollSnapIndexForAxis(ScrollEventAxis::Horizontal, scrollingStateNode.currentHorizontalSnapPointIndex());
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformScrollableAreacpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/ScrollableArea.cpp (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/ScrollableArea.cpp        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/platform/ScrollableArea.cpp        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -48,7 +48,7 @@
</span><span class="cx"> struct SameSizeAsScrollableArea {
</span><span class="cx">     virtual ~SameSizeAsScrollableArea();
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    void* pointers[4];
</del><ins>+    void* pointers[3];
</ins><span class="cx">     unsigned currentIndices[2];
</span><span class="cx"> #else
</span><span class="cx">     void* pointer[2];
</span><span class="lines">@@ -425,35 +425,90 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-void ScrollableArea::setHorizontalSnapOffsets(std::unique_ptr&lt;Vector&lt;LayoutUnit&gt;&gt; horizontalSnapOffsets)
</del><ins>+ScrollSnapOffsetsInfo&lt;LayoutUnit&gt;&amp; ScrollableArea::ensureSnapOffsetsInfo()
</ins><span class="cx"> {
</span><del>-    ASSERT(horizontalSnapOffsets);
</del><ins>+    if (!m_snapOffsetsInfo)
+        m_snapOffsetsInfo = std::make_unique&lt;ScrollSnapOffsetsInfo&lt;LayoutUnit&gt;&gt;();
+    return *m_snapOffsetsInfo;
+}
+
+const Vector&lt;LayoutUnit&gt;* ScrollableArea::horizontalSnapOffsets() const
+{
+    if (!m_snapOffsetsInfo)
+        return nullptr;
+
+    return &amp;m_snapOffsetsInfo-&gt;horizontalSnapOffsets;
+}
+
+const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;* ScrollableArea::horizontalSnapOffsetRanges() const
+{
+    if (!m_snapOffsetsInfo)
+        return nullptr;
+
+    return &amp;m_snapOffsetsInfo-&gt;horizontalSnapOffsetRanges;
+}
+
+const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;* ScrollableArea::verticalSnapOffsetRanges() const
+{
+    if (!m_snapOffsetsInfo)
+        return nullptr;
+
+    return &amp;m_snapOffsetsInfo-&gt;verticalSnapOffsetRanges;
+}
+
+const Vector&lt;LayoutUnit&gt;* ScrollableArea::verticalSnapOffsets() const
+{
+    if (!m_snapOffsetsInfo)
+        return nullptr;
+
+    return &amp;m_snapOffsetsInfo-&gt;verticalSnapOffsets;
+}
+
+void ScrollableArea::setHorizontalSnapOffsets(const Vector&lt;LayoutUnit&gt;&amp; horizontalSnapOffsets)
+{
</ins><span class="cx">     // Consider having a non-empty set of snap offsets as a cue to initialize the ScrollAnimator.
</span><del>-    if (horizontalSnapOffsets-&gt;size())
</del><ins>+    if (horizontalSnapOffsets.size())
</ins><span class="cx">         scrollAnimator();
</span><span class="cx"> 
</span><del>-    m_horizontalSnapOffsets = WTFMove(horizontalSnapOffsets);
</del><ins>+    ensureSnapOffsetsInfo().horizontalSnapOffsets = horizontalSnapOffsets;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ScrollableArea::setVerticalSnapOffsets(std::unique_ptr&lt;Vector&lt;LayoutUnit&gt;&gt; verticalSnapOffsets)
</del><ins>+void ScrollableArea::setVerticalSnapOffsets(const Vector&lt;LayoutUnit&gt;&amp; verticalSnapOffsets)
</ins><span class="cx"> {
</span><del>-    ASSERT(verticalSnapOffsets);
</del><span class="cx">     // Consider having a non-empty set of snap offsets as a cue to initialize the ScrollAnimator.
</span><del>-    if (verticalSnapOffsets-&gt;size())
</del><ins>+    if (verticalSnapOffsets.size())
</ins><span class="cx">         scrollAnimator();
</span><span class="cx"> 
</span><del>-    m_verticalSnapOffsets = WTFMove(verticalSnapOffsets);
</del><ins>+    ensureSnapOffsetsInfo().verticalSnapOffsets = verticalSnapOffsets;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void ScrollableArea::setHorizontalSnapOffsetRanges(const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; horizontalRanges)
+{
+    ensureSnapOffsetsInfo().horizontalSnapOffsetRanges = horizontalRanges;
+}
+
+void ScrollableArea::setVerticalSnapOffsetRanges(const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; verticalRanges)
+{
+    ensureSnapOffsetsInfo().verticalSnapOffsetRanges = verticalRanges;
+}
+
</ins><span class="cx"> void ScrollableArea::clearHorizontalSnapOffsets()
</span><span class="cx"> {
</span><del>-    m_horizontalSnapOffsets = nullptr;
</del><ins>+    if (!m_snapOffsetsInfo)
+        return;
+
+    m_snapOffsetsInfo-&gt;horizontalSnapOffsets = { };
+    m_snapOffsetsInfo-&gt;horizontalSnapOffsetRanges = { };
</ins><span class="cx">     m_currentHorizontalSnapPointIndex = 0;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollableArea::clearVerticalSnapOffsets()
</span><span class="cx"> {
</span><del>-    m_verticalSnapOffsets = nullptr;
</del><ins>+    if (!m_snapOffsetsInfo)
+        return;
+
+    m_snapOffsetsInfo-&gt;verticalSnapOffsets = { };
+    m_snapOffsetsInfo-&gt;verticalSnapOffsetRanges = { };
</ins><span class="cx">     m_currentVerticalSnapPointIndex = 0;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformScrollableAreah"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/ScrollableArea.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/ScrollableArea.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/platform/ScrollableArea.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #ifndef ScrollableArea_h
</span><span class="cx"> #define ScrollableArea_h
</span><span class="cx"> 
</span><ins>+#include &quot;ScrollSnapOffsetsInfo.h&quot;
</ins><span class="cx"> #include &quot;Scrollbar.h&quot;
</span><span class="cx"> #include &lt;wtf/Vector.h&gt;
</span><span class="cx"> #include &lt;wtf/WeakPtr.h&gt;
</span><span class="lines">@@ -67,11 +68,15 @@
</span><span class="cx">     WeakPtr&lt;ScrollableArea&gt; createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    const Vector&lt;LayoutUnit&gt;* horizontalSnapOffsets() const { return m_horizontalSnapOffsets.get(); };
-    const Vector&lt;LayoutUnit&gt;* verticalSnapOffsets() const { return m_verticalSnapOffsets.get(); };
</del><ins>+    WEBCORE_EXPORT const Vector&lt;LayoutUnit&gt;* horizontalSnapOffsets() const;
+    WEBCORE_EXPORT const Vector&lt;LayoutUnit&gt;* verticalSnapOffsets() const;
+    WEBCORE_EXPORT const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;* horizontalSnapOffsetRanges() const;
+    WEBCORE_EXPORT const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;* verticalSnapOffsetRanges() const;
</ins><span class="cx">     virtual void updateSnapOffsets() { };
</span><del>-    void setHorizontalSnapOffsets(std::unique_ptr&lt;Vector&lt;LayoutUnit&gt;&gt;);
-    void setVerticalSnapOffsets(std::unique_ptr&lt;Vector&lt;LayoutUnit&gt;&gt;);
</del><ins>+    void setHorizontalSnapOffsets(const Vector&lt;LayoutUnit&gt;&amp;);
+    void setVerticalSnapOffsets(const Vector&lt;LayoutUnit&gt;&amp;);
+    void setHorizontalSnapOffsetRanges(const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp;);
+    void setVerticalSnapOffsetRanges(const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp;);
</ins><span class="cx">     void clearHorizontalSnapOffsets();
</span><span class="cx">     void clearVerticalSnapOffsets();
</span><span class="cx">     unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; }
</span><span class="lines">@@ -346,6 +351,7 @@
</span><span class="cx">     // This function should be overriden by subclasses to perform the actual
</span><span class="cx">     // scroll of the content.
</span><span class="cx">     virtual void setScrollOffset(const ScrollOffset&amp;) = 0;
</span><ins>+    ScrollSnapOffsetsInfo&lt;LayoutUnit&gt;&amp; ensureSnapOffsetsInfo();
</ins><span class="cx"> 
</span><span class="cx">     mutable std::unique_ptr&lt;ScrollAnimator&gt; m_scrollAnimator;
</span><span class="cx"> 
</span><span class="lines">@@ -352,8 +358,7 @@
</span><span class="cx">     WeakPtrFactory&lt;ScrollableArea&gt; m_weakPtrFactory { this };
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    std::unique_ptr&lt;Vector&lt;LayoutUnit&gt;&gt; m_horizontalSnapOffsets;
-    std::unique_ptr&lt;Vector&lt;LayoutUnit&gt;&gt; m_verticalSnapOffsets;
</del><ins>+    std::unique_ptr&lt;ScrollSnapOffsetsInfo&lt;LayoutUnit&gt;&gt; m_snapOffsetsInfo;
</ins><span class="cx">     unsigned m_currentHorizontalSnapPointIndex { 0 };
</span><span class="cx">     unsigned m_currentVerticalSnapPointIndex { 0 };
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaScrollControllerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ScrollController.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ScrollController.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/platform/cocoa/ScrollController.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx"> #include &quot;ScrollSnapAnimatorState.h&quot;
</span><ins>+#include &quot;ScrollSnapOffsetsInfo.h&quot;
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="lines">@@ -133,7 +134,7 @@
</span><span class="cx">     bool isScrollSnapInProgress() const;
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><del>-    void updateScrollSnapPoints(ScrollEventAxis, const Vector&lt;LayoutUnit&gt;&amp;);
</del><ins>+    void updateScrollSnapPoints(ScrollEventAxis, const Vector&lt;LayoutUnit&gt;&amp;, const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp;);
</ins><span class="cx">     void setActiveScrollSnapIndexForAxis(ScrollEventAxis, unsigned);
</span><span class="cx">     void setActiveScrollSnapIndicesForOffset(int x, int y);
</span><span class="cx">     bool activeScrollSnapIndexDidChange() const { return m_activeScrollSnapIndexDidChange; }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaScrollControllermm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ScrollController.mm (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ScrollController.mm        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/platform/cocoa/ScrollController.mm        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -553,14 +553,15 @@
</span><span class="cx">     case WheelEventStatus::UserScrolling:
</span><span class="cx">         stopScrollSnapTimer();
</span><span class="cx">         m_scrollSnapState-&gt;transitionToUserInteractionState();
</span><ins>+        m_dragEndedScrollingVelocity = -wheelEvent.scrollingVelocity();
</ins><span class="cx">         break;
</span><span class="cx">     case WheelEventStatus::UserScrollEnd:
</span><del>-        m_dragEndedScrollingVelocity = -wheelEvent.scrollingVelocity();
</del><span class="cx">         m_scrollSnapState-&gt;transitionToSnapAnimationState(m_client.scrollExtent(), m_client.viewportSize(), m_client.pageScaleFactor(), m_client.scrollOffset());
</span><span class="cx">         startScrollSnapTimer();
</span><span class="cx">         break;
</span><span class="cx">     case WheelEventStatus::InertialScrollBegin:
</span><span class="cx">         m_scrollSnapState-&gt;transitionToGlideAnimationState(m_client.scrollExtent(), m_client.viewportSize(), m_client.pageScaleFactor(), m_client.scrollOffset(), m_dragEndedScrollingVelocity, FloatSize(-wheelEvent.deltaX(), -wheelEvent.deltaY()));
</span><ins>+        m_dragEndedScrollingVelocity = { };
</ins><span class="cx">         isInertialScrolling = true;
</span><span class="cx">         break;
</span><span class="cx">     case WheelEventStatus::InertialScrolling:
</span><span class="lines">@@ -581,18 +582,24 @@
</span><span class="cx"> 
</span><span class="cx"> void ScrollController::updateScrollSnapState(const ScrollableArea&amp; scrollableArea)
</span><span class="cx"> {
</span><del>-    if (auto* snapOffsets = scrollableArea.horizontalSnapOffsets())
-        updateScrollSnapPoints(ScrollEventAxis::Horizontal, *snapOffsets);
-    else
-        updateScrollSnapPoints(ScrollEventAxis::Horizontal, { });
</del><ins>+    if (auto* snapOffsets = scrollableArea.horizontalSnapOffsets()) {
+        if (auto* snapOffsetRanges = scrollableArea.horizontalSnapOffsetRanges())
+            updateScrollSnapPoints(ScrollEventAxis::Horizontal, *snapOffsets, *snapOffsetRanges);
+        else
+            updateScrollSnapPoints(ScrollEventAxis::Horizontal, *snapOffsets, { });
+    } else
+        updateScrollSnapPoints(ScrollEventAxis::Horizontal, { }, { });
</ins><span class="cx"> 
</span><del>-    if (auto* snapOffsets = scrollableArea.verticalSnapOffsets())
-        updateScrollSnapPoints(ScrollEventAxis::Vertical, *snapOffsets);
-    else
-        updateScrollSnapPoints(ScrollEventAxis::Vertical, { });
</del><ins>+    if (auto* snapOffsets = scrollableArea.verticalSnapOffsets()) {
+        if (auto* snapOffsetRanges = scrollableArea.verticalSnapOffsetRanges())
+            updateScrollSnapPoints(ScrollEventAxis::Vertical, *snapOffsets, *snapOffsetRanges);
+        else
+            updateScrollSnapPoints(ScrollEventAxis::Vertical, *snapOffsets, { });
+    } else
+        updateScrollSnapPoints(ScrollEventAxis::Vertical, { }, { });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ScrollController::updateScrollSnapPoints(ScrollEventAxis axis, const Vector&lt;LayoutUnit&gt;&amp; snapPoints)
</del><ins>+void ScrollController::updateScrollSnapPoints(ScrollEventAxis axis, const Vector&lt;LayoutUnit&gt;&amp; snapPoints, const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; snapRanges)
</ins><span class="cx"> {
</span><span class="cx">     if (!m_scrollSnapState) {
</span><span class="cx">         if (snapPoints.isEmpty())
</span><span class="lines">@@ -604,7 +611,7 @@
</span><span class="cx">     if (snapPoints.isEmpty() &amp;&amp; m_scrollSnapState-&gt;snapOffsetsForAxis(otherScrollEventAxis(axis)).isEmpty())
</span><span class="cx">         m_scrollSnapState = nullptr;
</span><span class="cx">     else
</span><del>-        m_scrollSnapState-&gt;setSnapOffsetsForAxis(axis, snapPoints);
</del><ins>+        m_scrollSnapState-&gt;setSnapOffsetsAndPositionRangesForAxis(axis, snapPoints, snapRanges);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ScrollController::startScrollSnapTimer()
</span><span class="lines">@@ -680,7 +687,7 @@
</span><span class="cx">     LayoutUnit clampedOffset = std::min(std::max(LayoutUnit(offset / scaleFactor), snapOffsets.first()), snapOffsets.last());
</span><span class="cx"> 
</span><span class="cx">     unsigned activeIndex = 0;
</span><del>-    closestSnapOffset&lt;LayoutUnit, float&gt;(snapState.snapOffsetsForAxis(axis), clampedOffset, 0, activeIndex);
</del><ins>+    closestSnapOffset(snapState.snapOffsetsForAxis(axis), snapState.snapOffsetRangesForAxis(axis), clampedOffset, 0, activeIndex);
</ins><span class="cx"> 
</span><span class="cx">     if (activeIndex == activeScrollSnapIndexForAxis(axis))
</span><span class="cx">         return;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaScrollSnapAnimatorStateh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -33,6 +33,7 @@
</span><span class="cx"> #include &quot;FloatSize.h&quot;
</span><span class="cx"> #include &quot;LayoutPoint.h&quot;
</span><span class="cx"> #include &quot;PlatformWheelEvent.h&quot;
</span><ins>+#include &quot;ScrollSnapOffsetsInfo.h&quot;
</ins><span class="cx"> #include &quot;ScrollTypes.h&quot;
</span><span class="cx"> #include &quot;ScrollingMomentumCalculator.h&quot;
</span><span class="cx"> #include &lt;wtf/MonotonicTime.h&gt;
</span><span class="lines">@@ -48,17 +49,25 @@
</span><span class="cx"> 
</span><span class="cx"> class ScrollSnapAnimatorState {
</span><span class="cx"> public:
</span><del>-    Vector&lt;LayoutUnit&gt; snapOffsetsForAxis(ScrollEventAxis axis) const
</del><ins>+    const Vector&lt;LayoutUnit&gt;&amp; snapOffsetsForAxis(ScrollEventAxis axis) const
</ins><span class="cx">     {
</span><span class="cx">         return axis == ScrollEventAxis::Horizontal ? m_snapOffsetsX : m_snapOffsetsY;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void setSnapOffsetsForAxis(ScrollEventAxis axis, const Vector&lt;LayoutUnit&gt;&amp; snapOffsets)
</del><ins>+    const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; snapOffsetRangesForAxis(ScrollEventAxis axis) const
</ins><span class="cx">     {
</span><del>-        if (axis == ScrollEventAxis::Horizontal)
</del><ins>+        return axis == ScrollEventAxis::Horizontal ? m_snapOffsetRangesX : m_snapOffsetRangesY;
+    }
+
+    void setSnapOffsetsAndPositionRangesForAxis(ScrollEventAxis axis, const Vector&lt;LayoutUnit&gt;&amp; snapOffsets, const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; snapOffsetRanges)
+    {
+        if (axis == ScrollEventAxis::Horizontal) {
</ins><span class="cx">             m_snapOffsetsX = snapOffsets;
</span><del>-        else
</del><ins>+            m_snapOffsetRangesX = snapOffsetRanges;
+        } else {
</ins><span class="cx">             m_snapOffsetsY = snapOffsets;
</span><ins>+            m_snapOffsetRangesY = snapOffsetRanges;
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     ScrollSnapState currentState() const { return m_currentState; }
</span><span class="lines">@@ -85,7 +94,7 @@
</span><span class="cx">     void transitionToDestinationReachedState();
</span><span class="cx"> 
</span><span class="cx"> private:
</span><del>-    float targetOffsetForStartOffset(ScrollEventAxis, float maxScrollOffset, float startOffset, float pageScale, float delta, unsigned&amp; outActiveSnapIndex) const;
</del><ins>+    float targetOffsetForStartOffset(const Vector&lt;LayoutUnit&gt;&amp; snapOffsets, const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; snapOffsetRanges, float maxScrollOffset, float startOffset, float predictedOffset, float pageScale, float delta, unsigned&amp; outActiveSnapIndex) const;
</ins><span class="cx">     void teardownAnimationForState(ScrollSnapState);
</span><span class="cx">     void setupAnimationForState(ScrollSnapState, const FloatSize&amp; contentSize, const FloatSize&amp; viewportSize, float pageScale, const FloatPoint&amp; initialOffset, const FloatSize&amp; initialVelocity, const FloatSize&amp; initialDelta);
</span><span class="cx"> 
</span><span class="lines">@@ -92,8 +101,10 @@
</span><span class="cx">     ScrollSnapState m_currentState { ScrollSnapState::UserInteraction };
</span><span class="cx"> 
</span><span class="cx">     Vector&lt;LayoutUnit&gt; m_snapOffsetsX;
</span><ins>+    Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt; m_snapOffsetRangesX;
</ins><span class="cx">     unsigned m_activeSnapIndexX { 0 };
</span><span class="cx">     Vector&lt;LayoutUnit&gt; m_snapOffsetsY;
</span><ins>+    Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt; m_snapOffsetRangesY;
</ins><span class="cx">     unsigned m_activeSnapIndexY { 0 };
</span><span class="cx"> 
</span><span class="cx">     MonotonicTime m_startTime;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaScrollSnapAnimatorStatemm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -31,12 +31,6 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-static const float inertialScrollPredictionFactor = 10;
-static inline float projectedInertialScrollDistance(float initialWheelDelta)
-{
-    return inertialScrollPredictionFactor * initialWheelDelta;
-}
-
</del><span class="cx"> void ScrollSnapAnimatorState::transitionToSnapAnimationState(const FloatSize&amp; contentSize, const FloatSize&amp; viewportSize, float pageScale, const FloatPoint&amp; initialOffset)
</span><span class="cx"> {
</span><span class="cx">     setupAnimationForState(ScrollSnapState::Snapping, contentSize, viewportSize, pageScale, initialOffset, { }, { });
</span><span class="lines">@@ -53,9 +47,11 @@
</span><span class="cx">     if (m_currentState == state)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    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);
</del><ins>+    m_momentumCalculator = ScrollingMomentumCalculator::create(viewportSize, contentSize, initialOffset, initialDelta, initialVelocity);
+    auto predictedScrollTarget = m_momentumCalculator-&gt;predictedDestinationOffset();
+    float targetOffsetX = targetOffsetForStartOffset(m_snapOffsetsX, m_snapOffsetRangesX, contentSize.width() - viewportSize.width(), initialOffset.x(), predictedScrollTarget.width(), pageScale, initialDelta.width(), m_activeSnapIndexX);
+    float targetOffsetY = targetOffsetForStartOffset(m_snapOffsetsY, m_snapOffsetRangesY, contentSize.height() - viewportSize.height(), initialOffset.y(), predictedScrollTarget.height(), pageScale, initialDelta.height(), m_activeSnapIndexY);
+    m_momentumCalculator-&gt;setRetargetedScrollOffset({ targetOffsetX, targetOffsetY });
</ins><span class="cx">     m_startTime = MonotonicTime::now();
</span><span class="cx">     m_currentState = state;
</span><span class="cx"> }
</span><span class="lines">@@ -93,18 +89,17 @@
</span><span class="cx">     return m_momentumCalculator-&gt;scrollOffsetAfterElapsedTime(elapsedTime);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-float ScrollSnapAnimatorState::targetOffsetForStartOffset(ScrollEventAxis axis, float maxScrollOffset, float startOffset, float pageScale, float initialDelta, unsigned&amp; outActiveSnapIndex) const
</del><ins>+float ScrollSnapAnimatorState::targetOffsetForStartOffset(const Vector&lt;LayoutUnit&gt;&amp; snapOffsets, const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;&amp; snapOffsetRanges, float maxScrollOffset, float startOffset, float predictedOffset, float pageScale, float initialDelta, unsigned&amp; outActiveSnapIndex) const
</ins><span class="cx"> {
</span><del>-    auto snapOffsets = snapOffsetsForAxis(axis);
-    if (!snapOffsets.size()) {
-        outActiveSnapIndex = 0;
</del><ins>+    if (snapOffsets.isEmpty()) {
+        outActiveSnapIndex = invalidSnapOffsetIndex;
</ins><span class="cx">         return clampTo&lt;float&gt;(startOffset, 0, maxScrollOffset);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    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);
</del><ins>+    float targetOffset = closestSnapOffset(snapOffsets, snapOffsetRanges, predictedOffset / pageScale, initialDelta, outActiveSnapIndex);
+    float minimumTargetOffset = std::max&lt;float&gt;(0, snapOffsets.first());
+    float maximumTargetOffset = std::min&lt;float&gt;(maxScrollOffset, snapOffsets.last());
+    targetOffset = clampTo&lt;float&gt;(targetOffset, minimumTargetOffset, maximumTargetOffset);
</ins><span class="cx">     return pageScale * targetOffset;
</span><span class="cx"> }
</span><span class="cx">     
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderLayerCompositorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -3907,6 +3907,10 @@
</span><span class="cx">                 scrollingGeometry.horizontalSnapOffsets = *offsets;
</span><span class="cx">             if (const Vector&lt;LayoutUnit&gt;* offsets = layer.verticalSnapOffsets())
</span><span class="cx">                 scrollingGeometry.verticalSnapOffsets = *offsets;
</span><ins>+            if (const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;* ranges = layer.horizontalSnapOffsetRanges())
+                scrollingGeometry.horizontalSnapOffsetRanges = *ranges;
+            if (const Vector&lt;ScrollOffsetRange&lt;LayoutUnit&gt;&gt;* ranges = layer.verticalSnapOffsetRanges())
+                scrollingGeometry.verticalSnapOffsetRanges = *ranges;
</ins><span class="cx">             scrollingGeometry.currentHorizontalSnapPointIndex = layer.currentHorizontalSnapPointIndex();
</span><span class="cx">             scrollingGeometry.currentVerticalSnapPointIndex = layer.currentVerticalSnapPointIndex();
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.cpp (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.cpp        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/testing/Internals.cpp        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -117,6 +117,7 @@
</span><span class="cx"> #include &quot;SchemeRegistry.h&quot;
</span><span class="cx"> #include &quot;ScriptedAnimationController.h&quot;
</span><span class="cx"> #include &quot;ScrollingCoordinator.h&quot;
</span><ins>+#include &quot;ScrollingMomentumCalculator.h&quot;
</ins><span class="cx"> #include &quot;SerializedScriptValue.h&quot;
</span><span class="cx"> #include &quot;Settings.h&quot;
</span><span class="cx"> #include &quot;ShadowRoot.h&quot;
</span><span class="lines">@@ -3340,6 +3341,11 @@
</span><span class="cx">     builder.appendLiteral(&quot; }&quot;);
</span><span class="cx"> }
</span><span class="cx">     
</span><ins>+void Internals::setPlatformMomentumScrollingPredictionEnabled(bool enabled)
+{
+    ScrollingMomentumCalculator::setPlatformMomentumScrollingPredictionEnabled(enabled);
+}
+
</ins><span class="cx"> ExceptionOr&lt;String&gt; Internals::scrollSnapOffsets(Element&amp; element)
</span><span class="cx"> {
</span><span class="cx">     if (!element.renderBox())
</span><span class="lines">@@ -3367,17 +3373,21 @@
</span><span class="cx">     
</span><span class="cx">     StringBuilder result;
</span><span class="cx"> 
</span><del>-    if (scrollableArea-&gt;horizontalSnapOffsets()) {
-        result.appendLiteral(&quot;horizontal = &quot;);
-        appendOffsets(result, *scrollableArea-&gt;horizontalSnapOffsets());
</del><ins>+    if (auto* offsets = scrollableArea-&gt;horizontalSnapOffsets()) {
+        if (offsets-&gt;size()) {
+            result.appendLiteral(&quot;horizontal = &quot;);
+            appendOffsets(result, *offsets);
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (scrollableArea-&gt;verticalSnapOffsets()) {
-        if (result.length())
-            result.appendLiteral(&quot;, &quot;);
</del><ins>+    if (auto* offsets = scrollableArea-&gt;verticalSnapOffsets()) {
+        if (offsets-&gt;size()) {
+            if (result.length())
+                result.appendLiteral(&quot;, &quot;);
</ins><span class="cx"> 
</span><del>-        result.appendLiteral(&quot;vertical = &quot;);
-        appendOffsets(result, *scrollableArea-&gt;verticalSnapOffsets());
</del><ins>+            result.appendLiteral(&quot;vertical = &quot;);
+            appendOffsets(result, *offsets);
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     return result.toString();
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/testing/Internals.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -479,6 +479,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx">     ExceptionOr&lt;String&gt; scrollSnapOffsets(Element&amp;);
</span><ins>+    void setPlatformMomentumScrollingPredictionEnabled(bool);
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     ExceptionOr&lt;String&gt; pathStringWithShrinkWrappedRects(const Vector&lt;double&gt;&amp; rectComponents, double radius);
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalsidl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.idl (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.idl        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebCore/testing/Internals.idl        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -456,6 +456,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if defined(ENABLE_CSS_SCROLL_SNAP) &amp;&amp; ENABLE_CSS_SCROLL_SNAP
</span><span class="cx">     [MayThrowException] DOMString scrollSnapOffsets(Element element);
</span><ins>+    void setPlatformMomentumScrollingPredictionEnabled(boolean enabled);
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     [MayThrowException] DOMString pathStringWithShrinkWrappedRects(sequence&lt;double&gt; rectComponents, double radius);
</span></span></pre></div>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebKit2/ChangeLog        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -1,3 +1,33 @@
</span><ins>+2017-01-10  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Implement &quot;proximity&quot; scroll snapping
+        https://bugs.webkit.org/show_bug.cgi?id=135994
+        &lt;rdar://problem/18162418&gt;
+
+        Reviewed by Dean Jackson.
+
+        Adds boilerplate support for plumbing lists of snap offset ranges from the web process to the UI process
+        alongside the list of snap offsets.
+
+        * Shared/Scrolling/RemoteScrollingCoordinatorTransaction.cpp:
+        (ArgumentCoder&lt;ScrollingStateScrollingNode&gt;::encode):
+        (ArgumentCoder&lt;ScrollingStateScrollingNode&gt;::decode):
+        * Shared/WebCoreArgumentCoders.cpp:
+        (IPC::ArgumentCoder&lt;ScrollOffsetRange&lt;float&gt;&gt;::encode):
+        (IPC::ArgumentCoder&lt;ScrollOffsetRange&lt;float&gt;&gt;::decode):
+        * Shared/WebCoreArgumentCoders.h:
+        * UIProcess/Scrolling/ios/ScrollingTreeOverflowScrollingNodeIOS.mm:
+        (-[WKOverflowScrollViewDelegate scrollViewWillEndDragging:withVelocity:targetContentOffset:]):
+        * UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm:
+
+        Adjust mainframe proximity scroll snapping logic to not subtract out the top content inset when there is no
+        active snap offset (i.e. when snapping rests in a snap offset range). Attempting to subtract out the top inset
+        in this case caused the scroll offset to jump after ending a drag with no momentum in a snap offset range.
+
+        (WebKit::RemoteScrollingCoordinatorProxy::adjustTargetContentOffsetForSnapping):
+        (WebKit::RemoteScrollingCoordinatorProxy::shouldSnapForMainFrameScrolling):
+        (WebKit::RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling):
+
</ins><span class="cx"> 2017-01-10  Zan Dobersek  &lt;zdobersek@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         ThreadedCoordinatedLayerTreeHost::renderNextFrame() should short-cut to layer flushing
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedScrollingRemoteScrollingCoordinatorTransactioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/Scrolling/RemoteScrollingCoordinatorTransaction.cpp (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/Scrolling/RemoteScrollingCoordinatorTransaction.cpp        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebKit2/Shared/Scrolling/RemoteScrollingCoordinatorTransaction.cpp        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -127,6 +127,8 @@
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx">     SCROLLING_NODE_ENCODE(ScrollingStateScrollingNode::HorizontalSnapOffsets, horizontalSnapOffsets)
</span><span class="cx">     SCROLLING_NODE_ENCODE(ScrollingStateScrollingNode::VerticalSnapOffsets, verticalSnapOffsets)
</span><ins>+    SCROLLING_NODE_ENCODE(ScrollingStateScrollingNode::HorizontalSnapOffsetRanges, horizontalSnapOffsetRanges)
+    SCROLLING_NODE_ENCODE(ScrollingStateScrollingNode::VerticalSnapOffsetRanges, verticalSnapOffsetRanges)
</ins><span class="cx">     SCROLLING_NODE_ENCODE(ScrollingStateScrollingNode::CurrentHorizontalSnapOffsetIndex, currentHorizontalSnapPointIndex)
</span><span class="cx">     SCROLLING_NODE_ENCODE(ScrollingStateScrollingNode::CurrentVerticalSnapOffsetIndex, currentVerticalSnapPointIndex)
</span><span class="cx"> #endif
</span><span class="lines">@@ -202,6 +204,8 @@
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx">     SCROLLING_NODE_DECODE(ScrollingStateScrollingNode::HorizontalSnapOffsets, Vector&lt;float&gt;, setHorizontalSnapOffsets);
</span><span class="cx">     SCROLLING_NODE_DECODE(ScrollingStateScrollingNode::VerticalSnapOffsets, Vector&lt;float&gt;, setVerticalSnapOffsets);
</span><ins>+    SCROLLING_NODE_DECODE(ScrollingStateScrollingNode::HorizontalSnapOffsetRanges, Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;, setHorizontalSnapOffsetRanges)
+    SCROLLING_NODE_DECODE(ScrollingStateScrollingNode::VerticalSnapOffsetRanges, Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;, setVerticalSnapOffsetRanges)
</ins><span class="cx">     SCROLLING_NODE_DECODE(ScrollingStateScrollingNode::CurrentHorizontalSnapOffsetIndex, unsigned, setCurrentHorizontalSnapPointIndex);
</span><span class="cx">     SCROLLING_NODE_DECODE(ScrollingStateScrollingNode::CurrentVerticalSnapOffsetIndex, unsigned, setCurrentVerticalSnapPointIndex);
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedWebCoreArgumentCoderscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -2472,4 +2472,29 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#if ENABLE(CSS_SCROLL_SNAP)
+
+void ArgumentCoder&lt;ScrollOffsetRange&lt;float&gt;&gt;::encode(Encoder&amp; encoder, const ScrollOffsetRange&lt;float&gt;&amp; range)
+{
+    encoder &lt;&lt; range.start;
+    encoder &lt;&lt; range.end;
+}
+
+bool ArgumentCoder&lt;ScrollOffsetRange&lt;float&gt;&gt;::decode(Decoder&amp; decoder, ScrollOffsetRange&lt;float&gt;&amp; range)
+{
+    float start;
+    if (!decoder.decode(start))
+        return false;
+
+    float end;
+    if (!decoder.decode(end))
+        return false;
+
+    range.start = start;
+    range.end = end;
+    return true;
+}
+
+#endif
+
</ins><span class="cx"> } // namespace IPC
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedWebCoreArgumentCodersh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.h (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.h        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.h        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx"> #include &lt;WebCore/FrameLoaderTypes.h&gt;
</span><span class="cx"> #include &lt;WebCore/IndexedDB.h&gt;
</span><span class="cx"> #include &lt;WebCore/PaymentHeaders.h&gt;
</span><ins>+#include &lt;WebCore/ScrollSnapOffsetsInfo.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace WTF {
</span><span class="cx"> class MonotonicTime;
</span><span class="lines">@@ -599,6 +600,15 @@
</span><span class="cx"> 
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#if ENABLE(CSS_SCROLL_SNAP)
+
+template&lt;&gt; struct ArgumentCoder&lt;WebCore::ScrollOffsetRange&lt;float&gt;&gt; {
+    static void encode(Encoder&amp;, const WebCore::ScrollOffsetRange&lt;float&gt;&amp;);
+    static bool decode(Decoder&amp;, WebCore::ScrollOffsetRange&lt;float&gt;&amp;);
+};
+
+#endif
+
</ins><span class="cx"> } // namespace IPC
</span><span class="cx"> 
</span><span class="cx"> namespace WTF {
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessScrollingiosScrollingTreeOverflowScrollingNodeIOSmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/Scrolling/ios/ScrollingTreeOverflowScrollingNodeIOS.mm (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/Scrolling/ios/ScrollingTreeOverflowScrollingNodeIOS.mm        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebKit2/UIProcess/Scrolling/ios/ScrollingTreeOverflowScrollingNodeIOS.mm        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -39,6 +39,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx"> #import &lt;WebCore/AxisScrollSnapOffsets.h&gt;
</span><ins>+#import &lt;WebCore/ScrollSnapOffsetsInfo.h&gt;
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> using namespace WebCore;
</span><span class="lines">@@ -88,7 +89,7 @@
</span><span class="cx"> 
</span><span class="cx">     if (!_scrollingTreeNode-&gt;horizontalSnapOffsets().isEmpty()) {
</span><span class="cx">         unsigned index;
</span><del>-        float potentialSnapPosition = closestSnapOffset&lt;float, CGFloat&gt;(_scrollingTreeNode-&gt;horizontalSnapOffsets(), horizontalTarget, velocity.x, index);
</del><ins>+        float potentialSnapPosition = closestSnapOffset(_scrollingTreeNode-&gt;horizontalSnapOffsets(), _scrollingTreeNode-&gt;horizontalSnapOffsetRanges(), horizontalTarget, velocity.x, index);
</ins><span class="cx">         _scrollingTreeNode-&gt;setCurrentHorizontalSnapPointIndex(index);
</span><span class="cx">         if (horizontalTarget &gt;= 0 &amp;&amp; horizontalTarget &lt;= scrollView.contentSize.width)
</span><span class="cx">             targetContentOffset-&gt;x = potentialSnapPosition;
</span><span class="lines">@@ -96,7 +97,7 @@
</span><span class="cx"> 
</span><span class="cx">     if (!_scrollingTreeNode-&gt;verticalSnapOffsets().isEmpty()) {
</span><span class="cx">         unsigned index;
</span><del>-        float potentialSnapPosition = closestSnapOffset&lt;float, CGFloat&gt;(_scrollingTreeNode-&gt;verticalSnapOffsets(), verticalTarget, velocity.y, index);
</del><ins>+        float potentialSnapPosition = closestSnapOffset(_scrollingTreeNode-&gt;verticalSnapOffsets(), _scrollingTreeNode-&gt;verticalSnapOffsetRanges(), verticalTarget, velocity.y, index);
</ins><span class="cx">         _scrollingTreeNode-&gt;setCurrentVerticalSnapPointIndex(index);
</span><span class="cx">         if (verticalTarget &gt;= 0 &amp;&amp; verticalTarget &lt;= scrollView.contentSize.height)
</span><span class="cx">             targetContentOffset-&gt;y = potentialSnapPosition;
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessiosRemoteScrollingCoordinatorProxyIOSmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm (210559 => 210560)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm        2017-01-10 21:59:15 UTC (rev 210559)
+++ trunk/Source/WebKit2/UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm        2017-01-10 22:26:56 UTC (rev 210560)
</span><span class="lines">@@ -39,6 +39,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CSS_SCROLL_SNAP)
</span><span class="cx"> #import &lt;WebCore/AxisScrollSnapOffsets.h&gt;
</span><ins>+#import &lt;WebCore/ScrollSnapOffsetsInfo.h&gt;
</ins><span class="cx"> #import &lt;WebCore/ScrollTypes.h&gt;
</span><span class="cx"> #import &lt;WebCore/ScrollingTreeFrameScrollingNode.h&gt;
</span><span class="cx"> #endif
</span><span class="lines">@@ -124,7 +125,9 @@
</span><span class="cx"> 
</span><span class="cx">     if (shouldSnapForMainFrameScrolling(WebCore::ScrollEventAxis::Vertical)) {
</span><span class="cx">         float potentialSnapPosition = closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis::Vertical, targetContentOffset-&gt;y, velocity.y, m_currentVerticalSnapPointIndex);
</span><del>-        potentialSnapPosition -= topInset;
</del><ins>+        if (m_currentVerticalSnapPointIndex != invalidSnapOffsetIndex)
+            potentialSnapPosition -= topInset;
+
</ins><span class="cx">         if (targetContentOffset-&gt;y &gt; 0 &amp;&amp; targetContentOffset-&gt;y &lt; maxScrollOffsets.height)
</span><span class="cx">             targetContentOffset-&gt;y = std::min&lt;float&gt;(maxScrollOffsets.height, potentialSnapPosition);
</span><span class="cx">     }
</span><span class="lines">@@ -142,7 +145,7 @@
</span><span class="cx">         ScrollingTreeFrameScrollingNode* rootFrame = static_cast&lt;ScrollingTreeFrameScrollingNode*&gt;(root);
</span><span class="cx">         const Vector&lt;float&gt;&amp; snapOffsets = axis == ScrollEventAxis::Horizontal ? rootFrame-&gt;horizontalSnapOffsets() : rootFrame-&gt;verticalSnapOffsets();
</span><span class="cx">         unsigned currentIndex = axis == ScrollEventAxis::Horizontal ? m_currentHorizontalSnapPointIndex : m_currentVerticalSnapPointIndex;
</span><del>-        return (snapOffsets.size() &gt; 0) &amp;&amp; (currentIndex &lt; snapOffsets.size());
</del><ins>+        return snapOffsets.size() &amp;&amp; (currentIndex &lt; snapOffsets.size() || currentIndex == invalidSnapOffsetIndex);
</ins><span class="cx">     }
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="lines">@@ -153,9 +156,10 @@
</span><span class="cx">     ASSERT(root &amp;&amp; root-&gt;isFrameScrollingNode());
</span><span class="cx">     ScrollingTreeFrameScrollingNode* rootFrame = static_cast&lt;ScrollingTreeFrameScrollingNode*&gt;(root);
</span><span class="cx">     const Vector&lt;float&gt;&amp; snapOffsets = axis == ScrollEventAxis::Horizontal ? rootFrame-&gt;horizontalSnapOffsets() : rootFrame-&gt;verticalSnapOffsets();
</span><ins>+    const Vector&lt;ScrollOffsetRange&lt;float&gt;&gt;&amp; snapOffsetRanges = axis == ScrollEventAxis::Horizontal ? rootFrame-&gt;horizontalSnapOffsetRanges() : rootFrame-&gt;verticalSnapOffsetRanges();
</ins><span class="cx"> 
</span><span class="cx">     float scaledScrollDestination = scrollDestination / m_webPageProxy.displayedContentScale();
</span><del>-    float rawClosestSnapOffset = closestSnapOffset&lt;float, float&gt;(snapOffsets, scaledScrollDestination, velocity, currentIndex);
</del><ins>+    float rawClosestSnapOffset = closestSnapOffset(snapOffsets, snapOffsetRanges, scaledScrollDestination, velocity, currentIndex);
</ins><span class="cx">     return rawClosestSnapOffset * m_webPageProxy.displayedContentScale();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>