Diff
Modified: trunk/LayoutTests/ChangeLog (279194 => 279195)
--- trunk/LayoutTests/ChangeLog 2021-06-23 22:37:09 UTC (rev 279194)
+++ trunk/LayoutTests/ChangeLog 2021-06-23 23:00:51 UTC (rev 279195)
@@ -1,3 +1,16 @@
+2021-06-23 Martin Robinson <[email protected]>
+
+ [iOS 15 Regression]: scroll position resets when using scroll snap
+ https://bugs.webkit.org/show_bug.cgi?id=226816
+ <rdar://problem/79081301>
+
+ Reviewed by Simon Fraser.
+
+ * fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-main-frame-expected.txt: Added.
+ * fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-main-frame.html: Added.
+ * fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-overflow-scroll-area-expected.txt: Added.
+ * fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-overflow-scroll-area.html: Added.
+
2021-06-23 Fujii Hironori <[email protected]>
[WinCairo] Unreviewed test gardening
Added: trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-main-frame-expected.txt (0 => 279195)
--- trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-main-frame-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-main-frame-expected.txt 2021-06-23 23:00:51 UTC (rev 279195)
@@ -0,0 +1,13 @@
+Verifies that a momentum scroll always moves to the next or previous snap position no matter how far away it is.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+FAIL document.body.scrollTop should be 500. Was 0.
+FAIL document.body.scrollTop should be 1000. Was 0.
+FAIL document.body.scrollTop should be 500. Was 0.
+PASS successfullyParsed is true
+Some tests failed.
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-main-frame.html (0 => 279195)
--- trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-main-frame.html (rev 0)
+++ trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-main-frame.html 2021-06-23 23:00:51 UTC (rev 279195)
@@ -0,0 +1,57 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true AsyncOverflowScrollingEnabled=true AsyncFrameScrollingEnabled=true ] -->
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+<script src=""
+<script src=""
+<style>
+body {
+ scroll-snap-type: both mandatory;
+}
+
+.block {
+ height: 500px;
+ border: 1px solid black;
+ box-sizing: border-box;
+ scroll-snap-align: start;
+}
+</style>
+
+<body _onload_="runTest()">
+ <div class="block"></div>
+ <div class="block"></div>
+ <div class="block"></div>
+ <div class="block"></div>
+ <div class="block"></div>
+ <div class="block"></div>
+ <div class="block"></div>
+ <div class="block"></div>
+ <div class="block"></div>
+ <div class="block"></div>
+</body>
+
+<script>
+jsTestIsAsync = true;
+
+async function runTest()
+{
+ description("Verifies that a momentum scroll always moves to the next or previous snap position no matter how far away it is.");
+
+ if (!window.testRunner)
+ return;
+
+ await UIHelper.dragFromPointToPoint(150, 50, 150, 300, 0.1);
+ await UIHelper.waitForTargetScrollAnimationToSettle(document.scrollingElement);
+ shouldBe("document.body.scrollTop", "500");
+
+ await UIHelper.dragFromPointToPoint(150, 50, 150, 300, 0.1);
+ await UIHelper.waitForTargetScrollAnimationToSettle(document.scrollingElement);
+ shouldBe("document.body.scrollTop", "1000");
+
+ await UIHelper.dragFromPointToPoint(150, 10, 150, 350, 0.1);
+ await UIHelper.waitForTargetScrollAnimationToSettle(document.scrollingElement);
+ shouldBe("document.body.scrollTop", "500");
+ finishJSTest();
+}
+</script>
+</html>
Added: trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-overflow-scroll-area-expected.txt (0 => 279195)
--- trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-overflow-scroll-area-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-overflow-scroll-area-expected.txt 2021-06-23 23:00:51 UTC (rev 279195)
@@ -0,0 +1,12 @@
+Verifies that a momentum scroll always moves to the next or previous snap position no matter how far away it is.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS scroller.scrollTop is 1000
+PASS scroller.scrollTop is 6500
+PASS scroller.scrollTop is 1000
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-overflow-scroll-area.html (0 => 279195)
--- trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-overflow-scroll-area.html (rev 0)
+++ trunk/LayoutTests/fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-overflow-scroll-area.html 2021-06-23 23:00:51 UTC (rev 279195)
@@ -0,0 +1,58 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true AsyncOverflowScrollingEnabled=true AsyncFrameScrollingEnabled=true ] -->
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+<script src=""
+<script src=""
+<style>
+#scroller {
+ overflow: scroll;
+ scroll-snap-type: both mandatory;
+ width: 500px;
+ height: 500px;
+}
+
+.block {
+ height: 500px;
+ border: 1px solid black;
+ box-sizing: border-box;
+ scroll-snap-align: start;
+}
+</style>
+
+<body _onload_="runTest()">
+ <div id="scroller">
+ <div class="block"></div>
+ <div style="height: 500px"></div>
+ <div class="block"></div>
+ <div style="height: 5000px"></div>
+ <div class="block"></div>
+ <div class="block"></div>
+ </div>
+</body>
+
+<script>
+jsTestIsAsync = true;
+
+async function runTest()
+{
+ description("Verifies that a momentum scroll always moves to the next or previous snap position no matter how far away it is.");
+
+ if (!window.testRunner)
+ return;
+
+ await UIHelper.dragFromPointToPoint(150, 320, 150, 300, 0.01);
+ await UIHelper.waitForTargetScrollAnimationToSettle(scroller);
+ shouldBe("scroller.scrollTop", "1000");
+
+ await UIHelper.dragFromPointToPoint(150, 320, 150, 300, 0.01);
+ await UIHelper.waitForTargetScrollAnimationToSettle(scroller);
+ shouldBe("scroller.scrollTop", "6500");
+
+ await UIHelper.dragFromPointToPoint(150, 300, 150, 320, 0.01);
+ await UIHelper.waitForTargetScrollAnimationToSettle(scroller);
+ shouldBe("scroller.scrollTop", "1000");
+ finishJSTest();
+}
+</script>
+</html>
Modified: trunk/Source/WebKit/ChangeLog (279194 => 279195)
--- trunk/Source/WebKit/ChangeLog 2021-06-23 22:37:09 UTC (rev 279194)
+++ trunk/Source/WebKit/ChangeLog 2021-06-23 23:00:51 UTC (rev 279195)
@@ -1,3 +1,31 @@
+2021-06-23 Martin Robinson <[email protected]>
+
+ [iOS 15 Regression]: scroll position resets when using scroll snap
+ https://bugs.webkit.org/show_bug.cgi?id=226816
+ <rdar://problem/79081301>
+
+ Reviewed by Simon Fraser.
+
+ To improve scrolling fluidity on iOS, have any sort of momentum scroll trigger directional
+ scroll snapping. This means that any momentum scroll will move to the next or previous scroll
+ position and not snap back. This improves behavior on tesla.com and also on the scroll
+ snapping demo on the WebKit page.
+
+ Tests: fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-main-frame.html
+ fast/scrolling/ios/scroll-snap-with-momentum-scroll-in-overflow-scroll-area.html
+
+ * UIProcess/API/ios/WKWebViewIOS.mm:
+ (-[WKWebView scrollViewWillEndDragging:withVelocity:targetContentOffset:]): Pass the current scroll offset to
+ the RemoteScrollingCoordinatorProxy. Passing a value for this offset is the trigger for directional scroll
+ snapping.
+ * UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h: Update method definitions.
+ * UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm:
+ (WebKit::RemoteScrollingCoordinatorProxy::adjustTargetContentOffsetForSnapping): Pass the current offset.
+ (WebKit::RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling const): Ditto.
+ * UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm:
+ (-[WKScrollingNodeScrollViewDelegate scrollViewWillEndDragging:withVelocity:targetContentOffset:]): Enable
+ directional scroll snapping for RenderLayers.
+
2021-06-23 Jer Noble <[email protected]>
[Cocoa] Make the hostTime parameter to playSession a Monotonic time
Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm (279194 => 279195)
--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm 2021-06-23 22:37:09 UTC (rev 279194)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm 2021-06-23 23:00:51 UTC (rev 279195)
@@ -1586,7 +1586,7 @@
CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInset);
- coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, targetContentOffset);
+ coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, scrollView.contentOffset, targetContentOffset);
}
#endif
}
Modified: trunk/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h (279194 => 279195)
--- trunk/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h 2021-06-23 22:37:09 UTC (rev 279194)
+++ trunk/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h 2021-06-23 23:00:51 UTC (rev 279195)
@@ -100,7 +100,7 @@
void scrollingTreeNodeWillStartScroll(WebCore::ScrollingNodeID);
void scrollingTreeNodeDidEndScroll(WebCore::ScrollingNodeID);
#if ENABLE(CSS_SCROLL_SNAP)
- void adjustTargetContentOffsetForSnapping(CGSize maxScrollDimensions, CGPoint velocity, CGFloat topInset, CGPoint* targetContentOffset);
+ void adjustTargetContentOffsetForSnapping(CGSize maxScrollDimensions, CGPoint velocity, CGFloat topInset, CGPoint currentContentOffset, CGPoint* targetContentOffset);
bool hasActiveSnapPoint() const;
CGPoint nearestActiveContentInsetAdjustedSnapOffset(CGFloat topInset, const CGPoint&) const;
bool shouldSetScrollViewDecelerationRateFast() const;
@@ -121,7 +121,7 @@
#if ENABLE(CSS_SCROLL_SNAP)
bool shouldSnapForMainFrameScrolling(WebCore::ScrollEventAxis) const;
- std::pair<float, std::optional<unsigned>> closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis, float scrollDestination, float velocity) const;
+ std::pair<float, std::optional<unsigned>> closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis, float currentScrollOffset, float scrollDestination, float velocity) const;
#endif
void sendUIStateChangedIfNecessary();
Modified: trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm (279194 => 279195)
--- trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm 2021-06-23 22:37:09 UTC (rev 279194)
+++ trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm 2021-06-23 23:00:51 UTC (rev 279195)
@@ -196,12 +196,12 @@
}
#if ENABLE(CSS_SCROLL_SNAP)
-void RemoteScrollingCoordinatorProxy::adjustTargetContentOffsetForSnapping(CGSize maxScrollOffsets, CGPoint velocity, CGFloat topInset, CGPoint* targetContentOffset)
+void RemoteScrollingCoordinatorProxy::adjustTargetContentOffsetForSnapping(CGSize maxScrollOffsets, CGPoint velocity, CGFloat topInset, CGPoint currentContentOffset, CGPoint* targetContentOffset)
{
// The bounds checking with maxScrollOffsets is to ensure that we won't interfere with rubber-banding when scrolling to the edge of the page.
if (shouldSnapForMainFrameScrolling(WebCore::ScrollEventAxis::Horizontal)) {
float potentialSnapPosition;
- std::tie(potentialSnapPosition, m_currentHorizontalSnapPointIndex) = closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis::Horizontal, targetContentOffset->x, velocity.x);
+ std::tie(potentialSnapPosition, m_currentHorizontalSnapPointIndex) = closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis::Horizontal, currentContentOffset.x, targetContentOffset->x, velocity.x);
if (targetContentOffset->x > 0 && targetContentOffset->x < maxScrollOffsets.width)
targetContentOffset->x = std::min<float>(maxScrollOffsets.width, potentialSnapPosition);
}
@@ -208,7 +208,7 @@
if (shouldSnapForMainFrameScrolling(WebCore::ScrollEventAxis::Vertical)) {
float potentialSnapPosition;
- std::tie(potentialSnapPosition, m_currentVerticalSnapPointIndex) = closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis::Vertical, targetContentOffset->y, velocity.y);
+ std::tie(potentialSnapPosition, m_currentVerticalSnapPointIndex) = closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis::Vertical, currentContentOffset.y + topInset, targetContentOffset->y, velocity.y);
if (m_currentVerticalSnapPointIndex)
potentialSnapPosition -= topInset;
@@ -232,7 +232,7 @@
return false;
}
-std::pair<float, std::optional<unsigned>> RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling(ScrollEventAxis axis, float scrollDestination, float velocity) const
+std::pair<float, std::optional<unsigned>> RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling(ScrollEventAxis axis, float currentScrollOffset, float scrollDestination, float velocity) const
{
ScrollingTreeNode* root = m_scrollingTree->rootNode();
ASSERT(root && root->isFrameScrollingNode());
@@ -240,7 +240,8 @@
const auto& snapOffsetsInfo = rootScrollingNode->snapOffsetsInfo();
float scaledScrollDestination = scrollDestination / m_webPageProxy.displayedContentScale();
- auto [rawClosestSnapOffset, newIndex] = snapOffsetsInfo.closestSnapOffset(axis, rootScrollingNode->layoutViewport().size(), scaledScrollDestination, velocity);
+ float scaledCurrentScrollOffset = currentScrollOffset / m_webPageProxy.displayedContentScale();
+ auto [rawClosestSnapOffset, newIndex] = snapOffsetsInfo.closestSnapOffset(axis, rootScrollingNode->layoutViewport().size(), scaledScrollDestination, velocity, scaledCurrentScrollOffset);
return std::make_pair(rawClosestSnapOffset * m_webPageProxy.displayedContentScale(), newIndex);
}
Modified: trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm (279194 => 279195)
--- trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm 2021-06-23 22:37:09 UTC (rev 279194)
+++ trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm 2021-06-23 23:00:51 UTC (rev 279195)
@@ -109,7 +109,7 @@
WebCore::FloatSize viewportSize(static_cast<float>(CGRectGetWidth([scrollView bounds])), static_cast<float>(CGRectGetHeight([scrollView bounds])));
const auto& snapOffsetsInfo = _scrollingTreeNodeDelegate->scrollingNode().snapOffsetsInfo();
if (!snapOffsetsInfo.horizontalSnapOffsets.isEmpty()) {
- auto [potentialSnapPosition, index] = snapOffsetsInfo.closestSnapOffset(WebCore::ScrollEventAxis::Horizontal, viewportSize, horizontalTarget, velocity.x);
+ auto [potentialSnapPosition, index] = snapOffsetsInfo.closestSnapOffset(WebCore::ScrollEventAxis::Horizontal, viewportSize, horizontalTarget, velocity.x, scrollView.contentOffset.x);
_scrollingTreeNodeDelegate->scrollingNode().setCurrentHorizontalSnapPointIndex(index);
if (horizontalTarget >= 0 && horizontalTarget <= scrollView.contentSize.width)
targetContentOffset->x = potentialSnapPosition;
@@ -116,7 +116,7 @@
}
if (!snapOffsetsInfo.verticalSnapOffsets.isEmpty()) {
- auto [potentialSnapPosition, index] = snapOffsetsInfo.closestSnapOffset(WebCore::ScrollEventAxis::Vertical, viewportSize, verticalTarget, velocity.x);
+ auto [potentialSnapPosition, index] = snapOffsetsInfo.closestSnapOffset(WebCore::ScrollEventAxis::Vertical, viewportSize, verticalTarget, velocity.y, scrollView.contentOffset.y);
_scrollingTreeNodeDelegate->scrollingNode().setCurrentVerticalSnapPointIndex(index);
if (verticalTarget >= 0 && verticalTarget <= scrollView.contentSize.height)
targetContentOffset->y = potentialSnapPosition;