Title: [279195] trunk
Revision
279195
Author
[email protected]
Date
2021-06-23 16:00:51 -0700 (Wed, 23 Jun 2021)

Log Message

[iOS 15 Regression]: scroll position resets when using scroll snap
https://bugs.webkit.org/show_bug.cgi?id=226816
<rdar://problem/79081301>

Patch by Martin Robinson <[email protected]> on 2021-06-23
Reviewed by Simon Fraser.

Source/WebKit:

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.

LayoutTests:

* 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.

Modified Paths

Added Paths

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;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to