Title: [283626] trunk
Revision
283626
Author
[email protected]
Date
2021-10-06 09:06:36 -0700 (Wed, 06 Oct 2021)

Log Message

[GTK][WPE] CSS scroll snapping interacts badly with smooth scrolling
https://bugs.webkit.org/show_bug.cgi?id=229037

Reviewed by Simon Fraser.

Source/WebCore:

Make CSS scroll snapping behave in a predictable way when scrolling
with touchpads/screens on GTK/WPE.

Test: fast/scrolling/gtk/user-scroll-snapping-interaction.html

* platform/ScrollAnimator.h:
(WebCore::ScrollAnimator::isUserScrollInProgress const):
(WebCore::ScrollAnimator::isScrollSnapInProgress const):
* platform/ScrollingEffectsController.cpp:
(WebCore::ScrollingEffectsController::processWheelEventForKineticScrolling):
(WebCore::ScrollingEffectsController::adjustDeltaForSnappingIfNeeded):
(WebCore::ScrollingEffectsController::handleWheelEvent):
(WebCore::ScrollingEffectsController::isUserScrollInProgress const):
(WebCore::ScrollingEffectsController::isScrollSnapInProgress const):
* platform/ScrollingEffectsController.h:
* platform/mac/ScrollAnimatorMac.h:
* platform/mac/ScrollAnimatorMac.mm:
(WebCore::ScrollAnimatorMac::isUserScrollInProgress const): Deleted.
(WebCore::ScrollAnimatorMac::isScrollSnapInProgress const): Deleted.

LayoutTests:

Add a GTK-only test to verify CSS scroll snapping and kinetic/touchpad
scrolling behave correctly.

* TestExpectations:
* fast/scrolling/gtk/user-scroll-snapping-interaction-expected.txt: Added.
* fast/scrolling/gtk/user-scroll-snapping-interaction.html: Added.
* platform/gtk/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (283625 => 283626)


--- trunk/LayoutTests/ChangeLog	2021-10-06 15:57:51 UTC (rev 283625)
+++ trunk/LayoutTests/ChangeLog	2021-10-06 16:06:36 UTC (rev 283626)
@@ -1,3 +1,18 @@
+2021-10-06  Chris Lord  <[email protected]>
+
+        [GTK][WPE] CSS scroll snapping interacts badly with smooth scrolling
+        https://bugs.webkit.org/show_bug.cgi?id=229037
+
+        Reviewed by Simon Fraser.
+
+        Add a GTK-only test to verify CSS scroll snapping and kinetic/touchpad
+        scrolling behave correctly.
+
+        * TestExpectations:
+        * fast/scrolling/gtk/user-scroll-snapping-interaction-expected.txt: Added.
+        * fast/scrolling/gtk/user-scroll-snapping-interaction.html: Added.
+        * platform/gtk/TestExpectations:
+
 2021-10-06  Ayumi Kojima  <[email protected]>
 
         [ iOS 15 ] 2 http/tests/app-privacy-report tests are timing out.

Modified: trunk/LayoutTests/TestExpectations (283625 => 283626)


--- trunk/LayoutTests/TestExpectations	2021-10-06 15:57:51 UTC (rev 283625)
+++ trunk/LayoutTests/TestExpectations	2021-10-06 16:06:36 UTC (rev 283626)
@@ -59,6 +59,7 @@
 fast/events/pointer/ios [ Skip ]
 fast/events/touch/ios [ Skip ]
 fast/history/ios [ Skip ]
+fast/scrolling/gtk [ Skip ]
 fast/scrolling/ios [ Skip ]
 fast/scrolling/mac [ Skip ]
 fast/scrolling/ipad [ Skip ]

Added: trunk/LayoutTests/fast/scrolling/gtk/user-scroll-snapping-interaction-expected.txt (0 => 283626)


--- trunk/LayoutTests/fast/scrolling/gtk/user-scroll-snapping-interaction-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/gtk/user-scroll-snapping-interaction-expected.txt	2021-10-06 16:06:36 UTC (rev 283626)
@@ -0,0 +1,9 @@
+PASS Scroll downwards by 25 pixels
+PASS Scroll downwards by another 25 pixels
+PASS End scroll snaps back to top
+PASS Scroll downwards by 200 pixels
+PASS End scroll should snap to next element
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/scrolling/gtk/user-scroll-snapping-interaction.html (0 => 283626)


--- trunk/LayoutTests/fast/scrolling/gtk/user-scroll-snapping-interaction.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/gtk/user-scroll-snapping-interaction.html	2021-10-06 16:06:36 UTC (rev 283626)
@@ -0,0 +1,120 @@
+<!-- webkit-test-runner [ useThreadedScrolling=false ] -->
+<!DOCTYPE html>
+
+<html>
+<head>
+  <script src=""
+  <title>CSS scroll snapping should not conflict with user scrolling</title>
+  <style>
+    html {
+      scroll-snap-type: y mandatory;
+    }
+
+    body {
+      margin: 0;
+    }
+
+    div {
+      width: 100%;
+      height: 250px;
+      scroll-snap-align: start;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+  <div></div>
+</body>
+<script>
+var expectedScrollTop = 0;
+var testsPassed = 0;
+var testDescription;
+
+function startScroll(startx, starty, deltaX, deltaY)
+{
+    eventSender.mouseMoveTo(startx, starty);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "began", "none");
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(-deltaX, -deltaY, "changed", "none");
+}
+
+function continueScroll(deltaX, deltaY)
+{
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(-deltaX, -deltaY, "changed", "none");
+}
+
+function endScroll(deltaX, deltaY)
+{
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(-deltaX, -deltaY, "ended", "none");
+}
+
+function triggerNextTest() {
+    if (testsPassed)
+        debug("PASS " + testDescription);
+
+    switch (testsPassed) {
+    case 0:
+        testDescription = "Scroll downwards by 25 pixels"
+        expectedScrollTop = 25;
+        startScroll(50, 50, 0, 25);
+        return;
+
+    case 1:
+        testDescription = "Scroll downwards by another 25 pixels"
+        expectedScrollTop = 50;
+        continueScroll(0, 25);
+        return;
+
+    case 2:
+        testDescription = "End scroll snaps back to top"
+        expectedScrollTop = 0;
+        endScroll(0, 0);
+        return;
+
+    case 3:
+        testDescription = "Scroll downwards by 200 pixels"
+        expectedScrollTop = 200;
+        startScroll(50, 50, 0, 200);
+        return;
+
+    case 4:
+        testDescription = "End scroll should snap to next element"
+        expectedScrollTop = 250;
+        endScroll(0, 0);
+        return;
+    }
+
+    isSuccessfullyParsed();
+    testRunner.notifyDone();
+
+    return;
+}
+
+function scrollEventCallback() {
+    if (document.scrollingElement.scrollTop == expectedScrollTop) {
+        ++testsPassed;
+        if (window.testRunner)
+            triggerNextTest();
+    }
+}
+
+
+document.addEventListener("scroll", scrollEventCallback, false);
+
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+}
+
+if (window.eventSender) {
+    eventSender.setWheelHasPreciseDeltas(true);
+    triggerNextTest();
+}
+</script>
+</html>

Modified: trunk/LayoutTests/platform/gtk/TestExpectations (283625 => 283626)


--- trunk/LayoutTests/platform/gtk/TestExpectations	2021-10-06 15:57:51 UTC (rev 283625)
+++ trunk/LayoutTests/platform/gtk/TestExpectations	2021-10-06 16:06:36 UTC (rev 283626)
@@ -19,6 +19,7 @@
 # Platform-specific directories.
 accessibility/gtk [ Pass ]
 editing/pasteboard/gtk [ Pass ]
+fast/scrolling/gtk [ Pass ]
 swipe [ Pass ]
 
 ietestcenter/css3/flexbox/flexbox-layout-001.htm [ Pass ]

Modified: trunk/Source/WebCore/ChangeLog (283625 => 283626)


--- trunk/Source/WebCore/ChangeLog	2021-10-06 15:57:51 UTC (rev 283625)
+++ trunk/Source/WebCore/ChangeLog	2021-10-06 16:06:36 UTC (rev 283626)
@@ -1,3 +1,30 @@
+2021-10-06  Chris Lord  <[email protected]>
+
+        [GTK][WPE] CSS scroll snapping interacts badly with smooth scrolling
+        https://bugs.webkit.org/show_bug.cgi?id=229037
+
+        Reviewed by Simon Fraser.
+
+        Make CSS scroll snapping behave in a predictable way when scrolling
+        with touchpads/screens on GTK/WPE.
+
+        Test: fast/scrolling/gtk/user-scroll-snapping-interaction.html
+
+        * platform/ScrollAnimator.h:
+        (WebCore::ScrollAnimator::isUserScrollInProgress const):
+        (WebCore::ScrollAnimator::isScrollSnapInProgress const):
+        * platform/ScrollingEffectsController.cpp:
+        (WebCore::ScrollingEffectsController::processWheelEventForKineticScrolling):
+        (WebCore::ScrollingEffectsController::adjustDeltaForSnappingIfNeeded):
+        (WebCore::ScrollingEffectsController::handleWheelEvent):
+        (WebCore::ScrollingEffectsController::isUserScrollInProgress const):
+        (WebCore::ScrollingEffectsController::isScrollSnapInProgress const):
+        * platform/ScrollingEffectsController.h:
+        * platform/mac/ScrollAnimatorMac.h:
+        * platform/mac/ScrollAnimatorMac.mm:
+        (WebCore::ScrollAnimatorMac::isUserScrollInProgress const): Deleted.
+        (WebCore::ScrollAnimatorMac::isScrollSnapInProgress const): Deleted.
+
 2021-10-06  Antti Koivisto  <[email protected]>
 
         [LFC] Layout box should own its children

Modified: trunk/Source/WebCore/platform/ScrollAnimator.h (283625 => 283626)


--- trunk/Source/WebCore/platform/ScrollAnimator.h	2021-10-06 15:57:51 UTC (rev 283625)
+++ trunk/Source/WebCore/platform/ScrollAnimator.h	2021-10-06 16:06:36 UTC (rev 283626)
@@ -93,10 +93,11 @@
 
     virtual void cancelAnimations();
 
-    virtual bool isUserScrollInProgress() const { return false; }
     virtual bool isRubberBandInProgress() const { return false; }
-    virtual bool isScrollSnapInProgress() const { return false; }
 
+    bool isUserScrollInProgress() const { return m_scrollController.isUserScrollInProgress(); }
+    bool isScrollSnapInProgress() const { return m_scrollController.isScrollSnapInProgress(); }
+
     void contentsSizeChanged();
 
     enum NotifyScrollableArea : bool {

Modified: trunk/Source/WebCore/platform/ScrollingEffectsController.cpp (283625 => 283626)


--- trunk/Source/WebCore/platform/ScrollingEffectsController.cpp	2021-10-06 15:57:51 UTC (rev 283625)
+++ trunk/Source/WebCore/platform/ScrollingEffectsController.cpp	2021-10-06 16:06:36 UTC (rev 283626)
@@ -240,11 +240,16 @@
     if (!event.isEndOfNonMomentumScroll() && !event.isTransitioningToMomentumScroll())
         return false;
 
+    m_inScrollGesture = false;
+
     if (m_currentAnimation && !is<ScrollAnimationKinetic>(m_currentAnimation.get())) {
         m_currentAnimation->stop();
         m_currentAnimation = nullptr;
     }
 
+    if (usesScrollSnap())
+        return false;
+
     if (!m_currentAnimation)
         m_currentAnimation = makeUnique<ScrollAnimationKinetic>(*this);
 
@@ -267,9 +272,29 @@
 }
 #endif
 
+void ScrollingEffectsController::adjustDeltaForSnappingIfNeeded(float& deltaX, float& deltaY)
+{
+    if (snapOffsetsInfo() && !snapOffsetsInfo()->isEmpty()) {
+        float scale = m_client.pageScaleFactor();
+        auto scrollOffset = m_client.scrollOffset();
+        auto extents = m_client.scrollExtents();
+
+        auto originalOffset = LayoutPoint(scrollOffset.x() / scale, scrollOffset.y() / scale);
+        auto newOffset = LayoutPoint((scrollOffset.x() + deltaX) / scale, (scrollOffset.y() + deltaY) / scale);
+
+        auto offsetX = snapOffsetsInfo()->closestSnapOffset(ScrollEventAxis::Horizontal, LayoutSize(extents.contentsSize), newOffset, deltaX, originalOffset.x()).first;
+        auto offsetY = snapOffsetsInfo()->closestSnapOffset(ScrollEventAxis::Vertical, LayoutSize(extents.contentsSize), newOffset, deltaY, originalOffset.y()).first;
+
+        deltaX = (offsetX - originalOffset.x()) * scale;
+        deltaY = (offsetY - originalOffset.y()) * scale;
+    }
+}
+
 bool ScrollingEffectsController::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
 {
 #if ENABLE(KINETIC_SCROLLING)
+    m_inScrollGesture = wheelEvent.hasPreciseScrollingDeltas() && !wheelEvent.isEndOfNonMomentumScroll() && !wheelEvent.isTransitioningToMomentumScroll();
+
     if (processWheelEventForKineticScrolling(wheelEvent))
         return true;
 #endif
@@ -288,9 +313,6 @@
         || (deltaY > 0 && scrollOffset.y() <= minPosition.y()))
         deltaY = 0;
 
-    if (!deltaX && !deltaY)
-        return false;
-
     if (wheelEvent.granularity() == ScrollByPageWheelEvent) {
         if (deltaX) {
             bool negative = deltaX < 0;
@@ -309,20 +331,14 @@
     deltaX = -deltaX;
     deltaY = -deltaY;
 
-    if (snapOffsetsInfo() && !snapOffsetsInfo()->isEmpty()) {
-        float scale = m_client.pageScaleFactor();
-        auto originalOffset = LayoutPoint(scrollOffset.x() / scale, scrollOffset.y() / scale);
-        auto newOffset = LayoutPoint((scrollOffset.x() + deltaX) / scale, (scrollOffset.y() + deltaY) / scale);
+    if (!m_inScrollGesture)
+        adjustDeltaForSnappingIfNeeded(deltaX, deltaY);
 
-        auto offsetX = snapOffsetsInfo()->closestSnapOffset(ScrollEventAxis::Horizontal, LayoutSize(extents.contentsSize), newOffset, deltaX, originalOffset.x()).first;
-        auto offsetY = snapOffsetsInfo()->closestSnapOffset(ScrollEventAxis::Vertical, LayoutSize(extents.contentsSize), newOffset, deltaY, originalOffset.y()).first;
+    if (!deltaX && !deltaY)
+        return false;
 
-        deltaX = (offsetX - originalOffset.x()) * scale;
-        deltaY = (offsetY - originalOffset.y()) * scale;
-    }
-
 #if ENABLE(SMOOTH_SCROLLING)
-    if (m_client.scrollAnimationEnabled() && !wheelEvent.hasPreciseScrollingDeltas()) {
+    if (m_client.scrollAnimationEnabled() && !m_inScrollGesture) {
         if (is<ScrollAnimationSmooth>(m_currentAnimation.get())) {
             auto lastDestinationOffset = downcast<ScrollAnimationSmooth>(*m_currentAnimation).destinationOffset();
             retargetAnimatedScroll(lastDestinationOffset + FloatSize { deltaX, deltaY });
@@ -336,6 +352,22 @@
 
     return true;
 }
+
+bool ScrollingEffectsController::isUserScrollInProgress() const
+{
+    return m_inScrollGesture;
+}
+
+bool ScrollingEffectsController::isScrollSnapInProgress() const
+{
+    if (!usesScrollSnap())
+        return false;
+
+    if (m_inScrollGesture || (m_currentAnimation && m_currentAnimation->isActive()))
+        return true;
+
+    return false;
+}
 #endif
 
 void ScrollingEffectsController::updateActiveScrollSnapIndexForClientOffset()

Modified: trunk/Source/WebCore/platform/ScrollingEffectsController.h (283625 => 283626)


--- trunk/Source/WebCore/platform/ScrollingEffectsController.h	2021-10-06 15:57:51 UTC (rev 283625)
+++ trunk/Source/WebCore/platform/ScrollingEffectsController.h	2021-10-06 16:06:36 UTC (rev 283626)
@@ -163,12 +163,12 @@
     // Returns true if handled.
     bool handleWheelEvent(const PlatformWheelEvent&);
 
+    bool isScrollSnapInProgress() const;
+    bool isUserScrollInProgress() const;
+
 #if PLATFORM(MAC)
     static FloatSize wheelDeltaBiasingTowardsVertical(const PlatformWheelEvent&);
 
-    bool isScrollSnapInProgress() const;
-    bool isUserScrollInProgress() const;
-
     // Returns true if handled.
     bool processWheelEventForScrollSnap(const PlatformWheelEvent&);
 
@@ -216,6 +216,8 @@
     void scrollAnimationDidEnd(ScrollAnimation&) final;
     ScrollExtents scrollExtentsForAnimation(ScrollAnimation&) final;
 
+    void adjustDeltaForSnappingIfNeeded(float& deltaX, float& deltaY);
+
 #if ENABLE(KINETIC_SCROLLING) && !PLATFORM(MAC)
     // Returns true if handled.
     bool processWheelEventForKineticScrolling(const PlatformWheelEvent&);
@@ -234,6 +236,7 @@
     bool m_isAnimatingRubberBand { false };
     bool m_isAnimatingScrollSnap { false };
     bool m_isAnimatingKeyboardScrolling { false };
+    bool m_inScrollGesture { false };
 
 #if PLATFORM(MAC)
     WallTime m_lastMomentumScrollTimestamp;
@@ -241,7 +244,6 @@
     FloatSize m_stretchScrollForce;
     FloatSize m_momentumVelocity;
 
-    bool m_inScrollGesture { false };
     bool m_momentumScrollInProgress { false };
     bool m_ignoreMomentumScrolls { false };
     bool m_isRubberBanding { false };

Modified: trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.h (283625 => 283626)


--- trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.h	2021-10-06 15:57:51 UTC (rev 283625)
+++ trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.h	2021-10-06 16:06:36 UTC (rev 283626)
@@ -49,10 +49,8 @@
     bool platformAllowsScrollAnimation() const;
 
     void handleWheelEventPhase(PlatformWheelEventPhase) final;
-    
-    bool isUserScrollInProgress() const final;
+
     bool isRubberBandInProgress() const final;
-    bool isScrollSnapInProgress() const final;
 
     bool processWheelEventForScrollSnap(const PlatformWheelEvent&) final;
 

Modified: trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.mm (283625 => 283626)


--- trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.mm	2021-10-06 15:57:51 UTC (rev 283625)
+++ trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.mm	2021-10-06 16:06:36 UTC (rev 283626)
@@ -73,21 +73,11 @@
     return enabled;
 }
 
-bool ScrollAnimatorMac::isUserScrollInProgress() const
-{
-    return m_scrollController.isUserScrollInProgress();
-}
-
 bool ScrollAnimatorMac::isRubberBandInProgress() const
 {
     return m_scrollController.isRubberBandInProgress();
 }
 
-bool ScrollAnimatorMac::isScrollSnapInProgress() const
-{
-    return m_scrollController.isScrollSnapInProgress();
-}
-
 void ScrollAnimatorMac::handleWheelEventPhase(PlatformWheelEventPhase phase)
 {
     LOG_WITH_STREAM(OverlayScrollbars, stream << "ScrollAnimatorMac " << this << " scrollableArea " << m_scrollableArea << " handleWheelEventPhase " << phase);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to