Title: [293260] trunk
Revision
293260
Author
[email protected]
Date
2022-04-22 15:52:23 -0700 (Fri, 22 Apr 2022)

Log Message

Focusing scroll container before scrolling breaks smooth scrolling
https://bugs.webkit.org/show_bug.cgi?id=239605

Reviewed by Wenson Hsieh.

Source/WebCore:

If script calls focus() and then scrollTo({ behavior: smooth }) in the same runloop, the
scrollToFocusedElementInternal() that happens on a zero-delay timer in FrameView will stop
the animated scroll.

Fix by having programmatic scrolls on Element and DOMWindow cancel any pending focus-related
scroll.

Test: fast/scrolling/programmatic-smooth-scroll-after-focus.html

* dom/Element.cpp:
(WebCore::Element::scrollTo):
* page/DOMWindow.cpp:
(WebCore::DOMWindow::scrollTo const):
* page/FrameView.cpp:
(WebCore::FrameView::maintainScrollPositionAtAnchor):
(WebCore::FrameView::setScrollPosition):
(WebCore::FrameView::cancelScheduledScrollToFocusedElement):
(WebCore::FrameView::scrollToAnchor):
(WebCore::FrameView::setWasScrolledByUser):
* page/FrameView.h:

LayoutTests:

* fast/scrolling/programmatic-smooth-scroll-after-focus-expected.txt: Added.
* fast/scrolling/programmatic-smooth-scroll-after-focus.html: Added.
* platform/win/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (293259 => 293260)


--- trunk/LayoutTests/ChangeLog	2022-04-22 22:45:41 UTC (rev 293259)
+++ trunk/LayoutTests/ChangeLog	2022-04-22 22:52:23 UTC (rev 293260)
@@ -1,3 +1,14 @@
+2022-04-22  Simon Fraser  <[email protected]>
+
+        Focusing scroll container before scrolling breaks smooth scrolling
+        https://bugs.webkit.org/show_bug.cgi?id=239605
+
+        Reviewed by Wenson Hsieh.
+
+        * fast/scrolling/programmatic-smooth-scroll-after-focus-expected.txt: Added.
+        * fast/scrolling/programmatic-smooth-scroll-after-focus.html: Added.
+        * platform/win/TestExpectations:
+
 2022-04-22  Matteo Flores  <[email protected]>
 
         REBASLINE: [ Monterey ] fast/text/khmer-lao-font.html is a constant text failure

Added: trunk/LayoutTests/fast/scrolling/programmatic-smooth-scroll-after-focus-expected.txt (0 => 293260)


--- trunk/LayoutTests/fast/scrolling/programmatic-smooth-scroll-after-focus-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/programmatic-smooth-scroll-after-focus-expected.txt	2022-04-22 22:52:23 UTC (rev 293260)
@@ -0,0 +1,5 @@
+PASS scrollContainer.scrollLeft is 500
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/scrolling/programmatic-smooth-scroll-after-focus.html (0 => 293260)


--- trunk/LayoutTests/fast/scrolling/programmatic-smooth-scroll-after-focus.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/programmatic-smooth-scroll-after-focus.html	2022-04-22 22:52:23 UTC (rev 293260)
@@ -0,0 +1,54 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        .scroll-container {
+            height: 250px;
+            width: 300px;
+            border: 1px solid black;
+            overflow-y: scroll;
+        }
+        
+        .contents {
+            height: 100%;
+            width: 400%;
+            background-image: repeating-linear-gradient(to right, white, silver 250px);
+        }
+    </style>
+    <script src=""
+    <script src=""
+    <script>
+        jsTestIsAsync = true;
+
+        async function runTest()
+        {
+            scrollContainer = document.querySelector('.scroll-container');
+
+            eventSender.monitorWheelEvents();
+            
+            scrollContainer.focus();
+            scrollContainer.scrollTo({
+                top: 0,
+                left: 500,
+                behavior: 'smooth'
+            });
+            
+            await UIHelper.waitForScrollCompletion();
+            
+            shouldBe('scrollContainer.scrollLeft', '500');
+            finishJSTest();
+        }
+    
+        window.addEventListener('load', () => {
+            setTimeout(runTest, 0);
+        }, false);
+    </script>
+</head>
+<body>
+    <div class="scroll-container" tabindex="0">
+        <div class="contents"></div>
+    </div>
+    <div id="console"></div>
+    <script src=""
+</body>
+</html>

Modified: trunk/LayoutTests/platform/win/TestExpectations (293259 => 293260)


--- trunk/LayoutTests/platform/win/TestExpectations	2022-04-22 22:45:41 UTC (rev 293259)
+++ trunk/LayoutTests/platform/win/TestExpectations	2022-04-22 22:52:23 UTC (rev 293260)
@@ -534,6 +534,9 @@
 webkit.org/b/208559 fast/scrolling/rtl-point-in-iframe.html [ Skip ]
 fast/scrolling/rtl-scrollbars-alternate-iframe-body-dir-attr-does-not-update-scrollbar-placement.html [ ImageOnlyFailure ]
 
+# waitForScrollCompletion() not supported
+fast/scrolling/programmatic-smooth-scroll-after-focus.html [ Skip ]
+
 # TODO Needs testRunner.enableAutoResizeMode()
 fast/autoresize/ [ Skip ]
 

Modified: trunk/Source/WebCore/ChangeLog (293259 => 293260)


--- trunk/Source/WebCore/ChangeLog	2022-04-22 22:45:41 UTC (rev 293259)
+++ trunk/Source/WebCore/ChangeLog	2022-04-22 22:52:23 UTC (rev 293260)
@@ -1,3 +1,31 @@
+2022-04-22  Simon Fraser  <[email protected]>
+
+        Focusing scroll container before scrolling breaks smooth scrolling
+        https://bugs.webkit.org/show_bug.cgi?id=239605
+
+        Reviewed by Wenson Hsieh.
+
+        If script calls focus() and then scrollTo({ behavior: smooth }) in the same runloop, the
+        scrollToFocusedElementInternal() that happens on a zero-delay timer in FrameView will stop
+        the animated scroll.
+
+        Fix by having programmatic scrolls on Element and DOMWindow cancel any pending focus-related
+        scroll.
+
+        Test: fast/scrolling/programmatic-smooth-scroll-after-focus.html
+
+        * dom/Element.cpp:
+        (WebCore::Element::scrollTo):
+        * page/DOMWindow.cpp:
+        (WebCore::DOMWindow::scrollTo const):
+        * page/FrameView.cpp:
+        (WebCore::FrameView::maintainScrollPositionAtAnchor):
+        (WebCore::FrameView::setScrollPosition):
+        (WebCore::FrameView::cancelScheduledScrollToFocusedElement):
+        (WebCore::FrameView::scrollToAnchor):
+        (WebCore::FrameView::setWasScrolledByUser):
+        * page/FrameView.h:
+
 2022-04-22  Alan Bujtas  <[email protected]>
 
         [LFC][Integration] Add blank LayoutIntegration::FlexLayout

Modified: trunk/Source/WebCore/dom/Element.cpp (293259 => 293260)


--- trunk/Source/WebCore/dom/Element.cpp	2022-04-22 22:45:41 UTC (rev 293259)
+++ trunk/Source/WebCore/dom/Element.cpp	2022-04-22 22:52:23 UTC (rev 293260)
@@ -1100,6 +1100,9 @@
         if (this == document().documentElement())
             return;
     }
+    
+    if (auto* view = document().view())
+        view->cancelScheduledScrollToFocusedElement();
 
     document().updateLayoutIgnorePendingStylesheets();
 

Modified: trunk/Source/WebCore/page/DOMWindow.cpp (293259 => 293260)


--- trunk/Source/WebCore/page/DOMWindow.cpp	2022-04-22 22:45:41 UTC (rev 293259)
+++ trunk/Source/WebCore/page/DOMWindow.cpp	2022-04-22 22:52:23 UTC (rev 293260)
@@ -1738,6 +1738,7 @@
         return;
     }
 
+    view->cancelScheduledScrollToFocusedElement();
     document()->updateLayoutIgnorePendingStylesheets();
 
     IntPoint layoutPos(view->mapFromCSSToLayoutUnits(scrollToOptions.left.value()), view->mapFromCSSToLayoutUnits(scrollToOptions.top.value()));

Modified: trunk/Source/WebCore/page/FrameView.cpp (293259 => 293260)


--- trunk/Source/WebCore/page/FrameView.cpp	2022-04-22 22:45:41 UTC (rev 293259)
+++ trunk/Source/WebCore/page/FrameView.cpp	2022-04-22 22:52:23 UTC (rev 293260)
@@ -2329,9 +2329,9 @@
     m_maintainScrollPositionAnchor = anchorNode;
     if (!m_maintainScrollPositionAnchor)
         return;
-    m_shouldScrollToFocusedElement = false;
-    m_delayedScrollToFocusedElementTimer.stop();
 
+    cancelScheduledScrollToFocusedElement();
+
     // We need to update the layout before scrolling, otherwise we could
     // really mess things up if an anchor scroll comes at a bad moment.
     frame().document()->updateStyleIfNeeded();
@@ -2363,8 +2363,8 @@
     setCurrentScrollType(options.type);
 
     m_maintainScrollPositionAnchor = nullptr;
-    m_shouldScrollToFocusedElement = false;
-    m_delayedScrollToFocusedElementTimer.stop();
+    cancelScheduledScrollToFocusedElement();
+
     Page* page = frame().page();
     if (page && page->isMonitoringWheelEvents())
         scrollAnimator().setWheelEventTestMonitor(page->wheelEventTestMonitor());
@@ -2412,6 +2412,12 @@
     m_delayedScrollToFocusedElementTimer.startOneShot(0_s);
 }
 
+void FrameView::cancelScheduledScrollToFocusedElement()
+{
+    m_shouldScrollToFocusedElement = false;
+    m_delayedScrollToFocusedElementTimer.stop();
+}
+
 void FrameView::scrollToFocusedElementImmediatelyIfNeeded()
 {
     if (!m_shouldScrollToFocusedElement)
@@ -3288,8 +3294,7 @@
     if (!anchorNode->renderer())
         return;
 
-    m_shouldScrollToFocusedElement = false;
-    m_delayedScrollToFocusedElementTimer.stop();
+    cancelScheduledScrollToFocusedElement();
 
     LayoutRect rect;
     bool insideFixed = false;
@@ -3313,8 +3318,7 @@
     // scrollRectToVisible can call into setScrollPosition(), which resets m_maintainScrollPositionAnchor.
     LOG_WITH_STREAM(Scrolling, stream << " restoring anchor node to " << anchorNode.get());
     m_maintainScrollPositionAnchor = anchorNode;
-    m_shouldScrollToFocusedElement = false;
-    m_delayedScrollToFocusedElementTimer.stop();
+    cancelScheduledScrollToFocusedElement();
 }
 
 void FrameView::updateEmbeddedObject(RenderEmbeddedObject& embeddedObject)
@@ -4322,8 +4326,7 @@
 {
     LOG(Scrolling, "FrameView::setWasScrolledByUser at %d", wasScrolledByUser);
 
-    m_shouldScrollToFocusedElement = false;
-    m_delayedScrollToFocusedElementTimer.stop();
+    cancelScheduledScrollToFocusedElement();
     if (currentScrollType() == ScrollType::Programmatic)
         return;
     m_maintainScrollPositionAnchor = nullptr;

Modified: trunk/Source/WebCore/page/FrameView.h (293259 => 293260)


--- trunk/Source/WebCore/page/FrameView.h	2022-04-22 22:45:41 UTC (rev 293259)
+++ trunk/Source/WebCore/page/FrameView.h	2022-04-22 22:52:23 UTC (rev 293260)
@@ -266,6 +266,7 @@
     WEBCORE_EXPORT void setScrollPosition(const ScrollPosition&, const ScrollPositionChangeOptions& = ScrollPositionChangeOptions::createProgrammatic()) final;
     void restoreScrollbar();
     void scheduleScrollToFocusedElement(SelectionRevealMode);
+    void cancelScheduledScrollToFocusedElement();
     void scrollToFocusedElementImmediatelyIfNeeded();
     void updateLayerPositionsAfterScrolling() final;
     void updateCompositingLayersAfterScrolling() final;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to