Title: [271786] trunk
Revision
271786
Author
[email protected]
Date
2021-01-24 16:47:32 -0800 (Sun, 24 Jan 2021)

Log Message

[iOS WK2] theverge.com - rubber band scrolling at the top of the page causes an abrupt jump
https://bugs.webkit.org/show_bug.cgi?id=220886
<rdar://71177566>

Reviewed by Sam Weinig.
Source/WebCore:

theverge.com on iOS is a page that has long main thread stalls with forced layouts on a timer
that alter the page height; this caused the post-layout updateScrollbars() called from
FrameView::adjustViewSize() to call scrollToPosition() after adjusting the scroll position
to the allowed range.

If the page laid out while rubberbanding was happening, the current scroll position would
be negative, then clamped to 0, then sent to the UI process as a requested scroll to 0,
triggering the jump to top in the UI process.

There's existing code to prevent this from happening if we know that rubberbanding is
happening; this patch makes isRubberBandInProgress() work for iOS WK2. It does so
by having updateVisibleContentRects() push information about rubberbanding nodes onto
RemoteScrollingCoordinator.

We remove an unnecessary shouldUpdateScrollLayerPositionSynchronously() check in
FrameView::isRubberBandInProgress() - if it's true, then the scrolling coordinator
won't see any rubberbanding nodes anyway.

Test: fast/scrolling/ios/content-size-change-during-rubberband.html

* page/FrameView.cpp:
(WebCore::FrameView::isRubberBandInProgress const):
* page/FrameView.h:
* platform/ScrollView.cpp:
(WebCore::ScrollView::updateScrollbars):

Source/WebKit:

theverge.com on iOS is a page that has long main thread stalls with forced layouts on a timer
that alter the page height; this caused the post-layout updateScrollbars() called from
FrameView::adjustViewSize() to call scrollToPosition() after adjusting the scroll position
to the allowed range.

If the page laid out while rubberbanding was happening, the current scroll position would
be negative, then clamped to 0, then sent to the UI process as a requested scroll to 0,
triggering the jump to top in the UI process.

There's existing code to prevent this from happening if we know that rubberbanding is
happening; this patch makes isRubberBandInProgress() work for iOS WK2. It does so
by having updateVisibleContentRects() push information about rubberbanding nodes onto
RemoteScrollingCoordinator.

We remove an unnecessary shouldUpdateScrollLayerPositionSynchronously() check in
FrameView::isRubberBandInProgress() - if it's true, then the scrolling coordinator
won't see any rubberbanding nodes anyway.

* UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm:
* WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.h:
* WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.mm:
(WebKit::RemoteScrollingCoordinator::addNodeWithActiveRubberBanding):
(WebKit::RemoteScrollingCoordinator::removeNodeWithActiveRubberBanding):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::updateVisibleContentRects):

Tools:

Add test infrastructure to allow UIScriptController::scrollToOffset() and
UIScriptController::immediateScrollToOffset() to take an options argument with
a 'unconstrained' property, which allows scrolling to unstable offset to simulate
rubberbanding.

* DumpRenderTree/ios/UIScriptControllerIOS.h:
* DumpRenderTree/ios/UIScriptControllerIOS.mm:
(WTR::contentOffsetBoundedIfNecessary):
(WTR::UIScriptControllerIOS::scrollToOffset):
(WTR::UIScriptControllerIOS::immediateScrollToOffset):
(WTR::contentOffsetBoundedInValidRange): Deleted.
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptController.h:
(WTR::UIScriptController::scrollToOffset):
(WTR::UIScriptController::immediateScrollToOffset):
* TestRunnerShared/UIScriptContext/UIScriptControllerShared.cpp:
(WTR::toScrollToOptions):
* WebKitTestRunner/ios/UIScriptControllerIOS.h:
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::contentOffsetBoundedIfNecessary):
(WTR::UIScriptControllerIOS::scrollToOffset):
(WTR::UIScriptControllerIOS::immediateScrollToOffset):
(WTR::contentOffsetBoundedInValidRange): Deleted.

LayoutTests:

Add test infrastructure to allow UIScriptController::scrollToOffset() and
UIScriptController::immediateScrollToOffset() to take an options argument with
a 'unconstrained' property, which allows scrolling to unstable offset to simulate
rubberbanding.

* fast/scrolling/ios/content-size-change-during-rubberband-expected.txt: Added.
* fast/scrolling/ios/content-size-change-during-rubberband.html: Added.
* resources/ui-helper.js:
(window.UIHelper.scrollTo.return.new.Promise.):
(window.UIHelper.scrollTo.return.new.Promise):
(window.UIHelper.scrollTo):
(window.UIHelper.immediateScrollTo):
(window.UIHelper.immediateUnstableScrollTo):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (271785 => 271786)


--- trunk/LayoutTests/ChangeLog	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/LayoutTests/ChangeLog	2021-01-25 00:47:32 UTC (rev 271786)
@@ -1,5 +1,27 @@
 2021-01-24  Simon Fraser  <[email protected]>
 
+        [iOS WK2] theverge.com - rubber band scrolling at the top of the page causes an abrupt jump
+        https://bugs.webkit.org/show_bug.cgi?id=220886
+        <rdar://71177566>
+
+        Reviewed by Sam Weinig.
+
+        Add test infrastructure to allow UIScriptController::scrollToOffset() and
+        UIScriptController::immediateScrollToOffset() to take an options argument with
+        a 'unconstrained' property, which allows scrolling to unstable offset to simulate
+        rubberbanding.
+
+        * fast/scrolling/ios/content-size-change-during-rubberband-expected.txt: Added.
+        * fast/scrolling/ios/content-size-change-during-rubberband.html: Added.
+        * resources/ui-helper.js:
+        (window.UIHelper.scrollTo.return.new.Promise.):
+        (window.UIHelper.scrollTo.return.new.Promise):
+        (window.UIHelper.scrollTo):
+        (window.UIHelper.immediateScrollTo):
+        (window.UIHelper.immediateUnstableScrollTo):
+
+2021-01-24  Simon Fraser  <[email protected]>
+
         SVG reference filter chain with errors applies only some of the filters, producing incorrect output
         https://bugs.webkit.org/show_bug.cgi?id=220893
 

Added: trunk/LayoutTests/fast/scrolling/ios/content-size-change-during-rubberband-expected.txt (0 => 271786)


--- trunk/LayoutTests/fast/scrolling/ios/content-size-change-during-rubberband-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/ios/content-size-change-during-rubberband-expected.txt	2021-01-25 00:47:32 UTC (rev 271786)
@@ -0,0 +1,6 @@
+Top
+PASS window.pageYOffset is -100
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/scrolling/ios/content-size-change-during-rubberband.html (0 => 271786)


--- trunk/LayoutTests/fast/scrolling/ios/content-size-change-during-rubberband.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/ios/content-size-change-during-rubberband.html	2021-01-25 00:47:32 UTC (rev 271786)
@@ -0,0 +1,41 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        .content {
+            height: 2000px;
+        }
+
+        body.changed .content {
+            height: 2100px;
+        }
+    </style>
+    <script src=""
+    <script src=""
+    <script>
+        var jsTestIsAsync = true;
+
+        async function doTest()
+        {
+            const unconstrained = true;
+            await UIHelper.scrollTo(0, -100, unconstrained);
+            
+            document.body.classList.add('changed');
+            
+            await UIHelper.renderingUpdate();
+            shouldBe('window.pageYOffset', '-100');
+            finishJSTest();
+        }
+
+        window.addEventListener('load', () => {
+            doTest();
+        }, false);
+    </script>
+</head>
+<body>
+    Top
+    <div class="content"></div>
+    <div id="console"></div>
+    <script src=""
+</body>
+</html>

Modified: trunk/LayoutTests/resources/ui-helper.js (271785 => 271786)


--- trunk/LayoutTests/resources/ui-helper.js	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/LayoutTests/resources/ui-helper.js	2021-01-25 00:47:32 UTC (rev 271786)
@@ -423,8 +423,26 @@
     {
         return new Promise(resolve => setTimeout(resolve, ms));
     }
+
+    static scrollTo(x, y, unconstrained)
+    {
+        if (!this.isWebKit2()) {
+            window.scrollTo(x, y);
+            return Promise.resolve();
+        }
+
+        return new Promise(resolve => {
+            testRunner.runUIScript(`
+                (function() {
+                    uiController.didEndScrollingCallback = function() {
+                        uiController.uiScriptComplete();
+                    }
+                    uiController.scrollToOffset(${x}, ${y}, { unconstrained: ${unconstrained} });
+                })()`, resolve);
+        });
+    }
     
-    static immediateScrollTo(x, y)
+    static immediateScrollTo(x, y, unconstrained)
     {
         if (!this.isWebKit2()) {
             window.scrollTo(x, y);
@@ -433,11 +451,11 @@
 
         return new Promise(resolve => {
             testRunner.runUIScript(`
-                uiController.immediateScrollToOffset(${x}, ${y});`, resolve);
+                uiController.immediateScrollToOffset(${x}, ${y}, { unconstrained: ${unconstrained} });`, resolve);
         });
     }
 
-    static immediateUnstableScrollTo(x, y)
+    static immediateUnstableScrollTo(x, y, unconstrained)
     {
         if (!this.isWebKit2()) {
             window.scrollTo(x, y);
@@ -447,7 +465,7 @@
         return new Promise(resolve => {
             testRunner.runUIScript(`
                 uiController.stableStateOverride = false;
-                uiController.immediateScrollToOffset(${x}, ${y});`, resolve);
+                uiController.immediateScrollToOffset(${x}, ${y}, { unconstrained: ${unconstrained} });`, resolve);
         });
     }
 

Modified: trunk/Source/WebCore/ChangeLog (271785 => 271786)


--- trunk/Source/WebCore/ChangeLog	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Source/WebCore/ChangeLog	2021-01-25 00:47:32 UTC (rev 271786)
@@ -1,5 +1,39 @@
 2021-01-24  Simon Fraser  <[email protected]>
 
+        [iOS WK2] theverge.com - rubber band scrolling at the top of the page causes an abrupt jump
+        https://bugs.webkit.org/show_bug.cgi?id=220886
+        <rdar://71177566>
+
+        Reviewed by Sam Weinig.
+
+        theverge.com on iOS is a page that has long main thread stalls with forced layouts on a timer
+        that alter the page height; this caused the post-layout updateScrollbars() called from
+        FrameView::adjustViewSize() to call scrollToPosition() after adjusting the scroll position
+        to the allowed range.
+
+        If the page laid out while rubberbanding was happening, the current scroll position would
+        be negative, then clamped to 0, then sent to the UI process as a requested scroll to 0,
+        triggering the jump to top in the UI process.
+
+        There's existing code to prevent this from happening if we know that rubberbanding is
+        happening; this patch makes isRubberBandInProgress() work for iOS WK2. It does so
+        by having updateVisibleContentRects() push information about rubberbanding nodes onto
+        RemoteScrollingCoordinator.
+
+        We remove an unnecessary shouldUpdateScrollLayerPositionSynchronously() check in
+        FrameView::isRubberBandInProgress() - if it's true, then the scrolling coordinator
+        won't see any rubberbanding nodes anyway.
+        
+        Test: fast/scrolling/ios/content-size-change-during-rubberband.html
+
+        * page/FrameView.cpp:
+        (WebCore::FrameView::isRubberBandInProgress const):
+        * page/FrameView.h:
+        * platform/ScrollView.cpp:
+        (WebCore::ScrollView::updateScrollbars):
+
+2021-01-24  Simon Fraser  <[email protected]>
+
         SVG reference filter chain with errors applies only some of the filters, producing incorrect output
         https://bugs.webkit.org/show_bug.cgi?id=220893
 

Modified: trunk/Source/WebCore/page/FrameView.cpp (271785 => 271786)


--- trunk/Source/WebCore/page/FrameView.cpp	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Source/WebCore/page/FrameView.cpp	2021-01-25 00:47:32 UTC (rev 271786)
@@ -2665,10 +2665,8 @@
     if (scrollbarsSuppressed())
         return false;
 
-    if (auto scrollingCoordinator = this->scrollingCoordinator()) {
-        if (!scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this))
-            return scrollingCoordinator->isRubberBandInProgress(scrollingNodeID());
-    }
+    if (auto scrollingCoordinator = this->scrollingCoordinator())
+        return scrollingCoordinator->isRubberBandInProgress(scrollingNodeID());
 
     if (auto scrollAnimator = existingScrollAnimator())
         return scrollAnimator->isRubberBandInProgress();

Modified: trunk/Source/WebCore/page/FrameView.h (271785 => 271786)


--- trunk/Source/WebCore/page/FrameView.h	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Source/WebCore/page/FrameView.h	2021-01-25 00:47:32 UTC (rev 271786)
@@ -167,7 +167,7 @@
 
     WEBCORE_EXPORT TiledBacking* tiledBacking() const;
 
-    ScrollingNodeID scrollingNodeID() const override;
+    WEBCORE_EXPORT ScrollingNodeID scrollingNodeID() const override;
     ScrollableArea* scrollableAreaForScrollingNodeID(ScrollingNodeID) const;
     bool usesAsyncScrolling() const final;
 

Modified: trunk/Source/WebCore/platform/ScrollView.cpp (271785 => 271786)


--- trunk/Source/WebCore/platform/ScrollView.cpp	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Source/WebCore/platform/ScrollView.cpp	2021-01-25 00:47:32 UTC (rev 271786)
@@ -586,7 +586,7 @@
 
 void ScrollView::updateScrollbars(const ScrollPosition& desiredPosition)
 {
-    LOG_WITH_STREAM(Scrolling, stream << "ScrollView::updateScrollbars " << desiredPosition);
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollView::updateScrollbars " << desiredPosition << " isRubberBandInProgress " << isRubberBandInProgress());
 
     if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget())
         return;

Modified: trunk/Source/WebKit/ChangeLog (271785 => 271786)


--- trunk/Source/WebKit/ChangeLog	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Source/WebKit/ChangeLog	2021-01-25 00:47:32 UTC (rev 271786)
@@ -1,3 +1,37 @@
+2021-01-24  Simon Fraser  <[email protected]>
+
+        [iOS WK2] theverge.com - rubber band scrolling at the top of the page causes an abrupt jump
+        https://bugs.webkit.org/show_bug.cgi?id=220886
+        <rdar://71177566>
+
+        Reviewed by Sam Weinig.
+        
+        theverge.com on iOS is a page that has long main thread stalls with forced layouts on a timer
+        that alter the page height; this caused the post-layout updateScrollbars() called from
+        FrameView::adjustViewSize() to call scrollToPosition() after adjusting the scroll position
+        to the allowed range.
+
+        If the page laid out while rubberbanding was happening, the current scroll position would
+        be negative, then clamped to 0, then sent to the UI process as a requested scroll to 0,
+        triggering the jump to top in the UI process.
+
+        There's existing code to prevent this from happening if we know that rubberbanding is
+        happening; this patch makes isRubberBandInProgress() work for iOS WK2. It does so
+        by having updateVisibleContentRects() push information about rubberbanding nodes onto
+        RemoteScrollingCoordinator.
+
+        We remove an unnecessary shouldUpdateScrollLayerPositionSynchronously() check in
+        FrameView::isRubberBandInProgress() - if it's true, then the scrolling coordinator
+        won't see any rubberbanding nodes anyway.
+        
+        * UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm:
+        * WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.h:
+        * WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.mm:
+        (WebKit::RemoteScrollingCoordinator::addNodeWithActiveRubberBanding):
+        (WebKit::RemoteScrollingCoordinator::removeNodeWithActiveRubberBanding):
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::updateVisibleContentRects):
+
 2021-01-23  Per Arne Vollan  <[email protected]>
 
         [macOS] Deny mach lookup access to "com.apple.iconservices" in the WebContent process

Modified: trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm (271785 => 271786)


--- trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm	2021-01-25 00:47:32 UTC (rev 271786)
@@ -125,6 +125,7 @@
     m_webPageProxy.scrollingNodeScrollViewWillStartPanGesture();
 }
 
+// This is not called for the main scroll view.
 void RemoteScrollingCoordinatorProxy::scrollingTreeNodeWillStartScroll(ScrollingNodeID nodeID)
 {
     m_webPageProxy.scrollingNodeScrollWillStartScroll();
@@ -133,6 +134,7 @@
     sendUIStateChangedIfNecessary();
 }
 
+// This is not called for the main scroll view.
 void RemoteScrollingCoordinatorProxy::scrollingTreeNodeDidEndScroll(ScrollingNodeID nodeID)
 {
     m_webPageProxy.scrollingNodeScrollDidEndScroll();

Modified: trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.h (271785 => 271786)


--- trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.h	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.h	2021-01-25 00:47:32 UTC (rev 271786)
@@ -55,6 +55,9 @@
 
     void scrollingStateInUIProcessChanged(const RemoteScrollingUIState&);
 
+    void addNodeWithActiveRubberBanding(WebCore::ScrollingNodeID);
+    void removeNodeWithActiveRubberBanding(WebCore::ScrollingNodeID);
+
 private:
     RemoteScrollingCoordinator(WebPage*);
     virtual ~RemoteScrollingCoordinator();

Modified: trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.mm (271785 => 271786)


--- trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.mm	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.mm	2021-01-25 00:47:32 UTC (rev 271786)
@@ -125,6 +125,16 @@
         m_nodesWithActiveUserScrolls = uiState.nodesWithActiveUserScrolls();
 }
 
+void RemoteScrollingCoordinator::addNodeWithActiveRubberBanding(ScrollingNodeID nodeID)
+{
+    m_nodesWithActiveRubberBanding.add(nodeID);
+}
+
+void RemoteScrollingCoordinator::removeNodeWithActiveRubberBanding(ScrollingNodeID nodeID)
+{
+    m_nodesWithActiveRubberBanding.remove(nodeID);
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(ASYNC_SCROLLING)

Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (271785 => 271786)


--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2021-01-25 00:47:32 UTC (rev 271786)
@@ -40,6 +40,7 @@
 #import "PluginView.h"
 #import "PrintInfo.h"
 #import "RemoteLayerTreeDrawingArea.h"
+#import "RemoteScrollingCoordinator.h"
 #import "SandboxUtilities.h"
 #import "SharedMemory.h"
 #import "SyntheticEditingCommandType.h"
@@ -3835,6 +3836,19 @@
     FloatRect adjustedExposedContentRect = adjustExposedRectForNewScale(exposedContentRect, visibleContentRectUpdateInfo.scale(), scaleToUse);
     m_drawingArea->setExposedContentRect(adjustedExposedContentRect);
 
+    auto& frame = m_page->mainFrame();
+    FrameView& frameView = *frame.view();
+
+    if (auto* scrollingCoordinator = this->scrollingCoordinator()) {
+        auto& remoteScrollingCoordinator = downcast<RemoteScrollingCoordinator>(*scrollingCoordinator);
+        if (auto mainFrameScrollingNodeID = frameView.scrollingNodeID()) {
+            if (visibleContentRectUpdateInfo.viewStability().contains(ViewStabilityFlag::ScrollViewRubberBanding))
+                remoteScrollingCoordinator.addNodeWithActiveRubberBanding(mainFrameScrollingNodeID);
+            else
+                remoteScrollingCoordinator.removeNodeWithActiveRubberBanding(mainFrameScrollingNodeID);
+        }
+    }
+
     IntPoint scrollPosition = roundedIntPoint(visibleContentRectUpdateInfo.unobscuredContentRect().location());
 
     bool pageHasBeenScaledSinceLastLayerTreeCommitThatChangedPageScale = ([&] {
@@ -3870,8 +3884,6 @@
         }
     }
 
-    auto& frame = m_page->mainFrame();
-    FrameView& frameView = *frame.view();
     if (scrollPosition != frameView.scrollPosition())
         m_dynamicSizeUpdateHistory.clear();
 
@@ -3924,9 +3936,9 @@
     if (!isChangingObscuredInsetsInteractively)
         frameView.setCustomSizeForResizeEvent(expandedIntSize(visibleContentRectUpdateInfo.unobscuredRectInScrollViewCoordinates().size()));
 
-    if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) {
-        ViewportRectStability viewportStability = ViewportRectStability::Stable;
-        ScrollingLayerPositionAction layerAction = ScrollingLayerPositionAction::Sync;
+    if (auto* scrollingCoordinator = this->scrollingCoordinator()) {
+        auto viewportStability = ViewportRectStability::Stable;
+        auto layerAction = ScrollingLayerPositionAction::Sync;
         
         if (isChangingObscuredInsetsInteractively) {
             viewportStability = ViewportRectStability::ChangingObscuredInsetsInteractively;

Modified: trunk/Tools/ChangeLog (271785 => 271786)


--- trunk/Tools/ChangeLog	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Tools/ChangeLog	2021-01-25 00:47:32 UTC (rev 271786)
@@ -1,3 +1,35 @@
+2021-01-24  Simon Fraser  <[email protected]>
+
+        [iOS WK2] theverge.com - rubber band scrolling at the top of the page causes an abrupt jump
+        https://bugs.webkit.org/show_bug.cgi?id=220886
+        <rdar://71177566>
+
+        Reviewed by Sam Weinig.
+
+        Add test infrastructure to allow UIScriptController::scrollToOffset() and
+        UIScriptController::immediateScrollToOffset() to take an options argument with
+        a 'unconstrained' property, which allows scrolling to unstable offset to simulate
+        rubberbanding.
+
+        * DumpRenderTree/ios/UIScriptControllerIOS.h:
+        * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+        (WTR::contentOffsetBoundedIfNecessary):
+        (WTR::UIScriptControllerIOS::scrollToOffset):
+        (WTR::UIScriptControllerIOS::immediateScrollToOffset):
+        (WTR::contentOffsetBoundedInValidRange): Deleted.
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+        (WTR::UIScriptController::scrollToOffset):
+        (WTR::UIScriptController::immediateScrollToOffset):
+        * TestRunnerShared/UIScriptContext/UIScriptControllerShared.cpp:
+        (WTR::toScrollToOptions):
+        * WebKitTestRunner/ios/UIScriptControllerIOS.h:
+        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+        (WTR::contentOffsetBoundedIfNecessary):
+        (WTR::UIScriptControllerIOS::scrollToOffset):
+        (WTR::UIScriptControllerIOS::immediateScrollToOffset):
+        (WTR::contentOffsetBoundedInValidRange): Deleted.
+
 2021-01-23  Xan Lopez  <[email protected]>
 
         [JSC] Allow to build WebAssembly without B3

Modified: trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.h (271785 => 271786)


--- trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.h	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.h	2021-01-25 00:47:32 UTC (rev 271786)
@@ -43,8 +43,8 @@
     double zoomScale() const override;
     double contentOffsetX() const override;
     double contentOffsetY() const override;
-    void scrollToOffset(long x, long y) override;
-    void immediateScrollToOffset(long x, long y) override;
+    void scrollToOffset(long x, long y, ScrollToOptions*) override;
+    void immediateScrollToOffset(long x, long y, ScrollToOptions*) override;
     void immediateZoomToScale(double scale) override;
     double minimumZoomScale() const override;
     double maximumZoomScale() const override;

Modified: trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm (271785 => 271786)


--- trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm	2021-01-25 00:47:32 UTC (rev 271786)
@@ -75,19 +75,24 @@
     return gWebScrollView.zoomScale;
 }
 
-static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
+static CGPoint contentOffsetBoundedIfNecessary(UIScrollView *scrollView, long x, long y, ScrollToOptions* options)
 {
-    UIEdgeInsets contentInsets = scrollView.contentInset;
-    CGSize contentSize = scrollView.contentSize;
-    CGSize scrollViewSize = scrollView.bounds.size;
+    auto contentOffset = CGPointMake(x, y);
+    bool constrain = !options || !options->unconstrained;
+    if (constrain) {
+        UIEdgeInsets contentInsets = scrollView.contentInset;
+        CGSize contentSize = scrollView.contentSize;
+        CGSize scrollViewSize = scrollView.bounds.size;
 
-    CGFloat maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
-    contentOffset.x = std::min(maxHorizontalOffset, contentOffset.x);
-    contentOffset.x = std::max(-contentInsets.left, contentOffset.x);
+        CGFloat maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
+        contentOffset.x = std::min(maxHorizontalOffset, contentOffset.x);
+        contentOffset.x = std::max(-contentInsets.left, contentOffset.x);
 
-    CGFloat maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
-    contentOffset.y = std::min(maxVerticalOffset, contentOffset.y);
-    contentOffset.y = std::max(-contentInsets.top, contentOffset.y);
+        CGFloat maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
+        contentOffset.y = std::min(maxVerticalOffset, contentOffset.y);
+        contentOffset.y = std::max(-contentInsets.top, contentOffset.y);
+    }
+
     return contentOffset;
 }
 
@@ -101,14 +106,16 @@
     return [gWebScrollView contentOffset].y;
 }
 
-void UIScriptControllerIOS::scrollToOffset(long x, long y)
+void UIScriptControllerIOS::scrollToOffset(long x, long y, ScrollToOptions* options)
 {
-    [gWebScrollView setContentOffset:contentOffsetBoundedInValidRange(gWebScrollView, CGPointMake(x, y)) animated:YES];
+    auto offset = contentOffsetBoundedIfNecessary(gWebScrollView, x, y, options);
+    [gWebScrollView setContentOffset:offset animated:YES];
 }
 
-void UIScriptControllerIOS::immediateScrollToOffset(long x, long y)
+void UIScriptControllerIOS::immediateScrollToOffset(long x, long y, ScrollToOptions* options)
 {
-    [gWebScrollView setContentOffset:contentOffsetBoundedInValidRange(gWebScrollView, CGPointMake(x, y)) animated:NO];
+    auto offset = contentOffsetBoundedIfNecessary(gWebScrollView, x, y, options);
+    [gWebScrollView setContentOffset:offset animated:NO];
 }
 
 void UIScriptControllerIOS::immediateZoomToScale(double scale)

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl (271785 => 271786)


--- trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl	2021-01-25 00:47:32 UTC (rev 271786)
@@ -38,6 +38,10 @@
     "shift"
 };
 
+dictionary ScrollToOptions {
+    boolean unconstrained = false;
+};
+
 interface UIScriptController {
 
     undefined doAsyncTask(object callback); // Used to test the harness.
@@ -282,10 +286,10 @@
 
     attribute boolean scrollUpdatesDisabled; // Turns off notifications back to the web process after scrolls (used for testing scrolling tree).
 
-    undefined scrollToOffset(long x, long y); // Initiate an animated scroll in the UI process.
+    undefined scrollToOffset(long x, long y, ScrollToOptions options); // Initiate an animated scroll in the UI process.
     attribute object didEndScrollingCallback;
 
-    undefined immediateScrollToOffset(long x, long y); // Set the scroll position in the UI process without animation.
+    undefined immediateScrollToOffset(long x, long y, ScrollToOptions options); // Set the scroll position in the UI process without animation.
     undefined immediateZoomToScale(double scale); // Set the zoom scale in the UI process without animation.
 
     // Find the scroller for the given point in content ("absolute") coordinates, and do an immediate scroll.

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h (271785 => 271786)


--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h	2021-01-25 00:47:32 UTC (rev 271786)
@@ -23,8 +23,7 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef UIScriptController_h
-#define UIScriptController_h
+#pragma once
 
 #include "JSWrappable.h"
 #include <_javascript_Core/JSRetainPtr.h>
@@ -52,6 +51,12 @@
 
 DeviceOrientation* toDeviceOrientation(JSContextRef, JSValueRef);
 
+struct ScrollToOptions {
+    bool unconstrained { false };
+};
+
+ScrollToOptions* toScrollToOptions(JSContextRef, JSValueRef);
+
 class UIScriptController : public JSWrappable {
 public:
     static Ref<UIScriptController> create(UIScriptContext&);
@@ -133,9 +138,9 @@
     virtual bool scrollUpdatesDisabled() const { notImplemented(); return false; }
     virtual void setScrollUpdatesDisabled(bool) { notImplemented(); }
 
-    virtual void scrollToOffset(long, long) { notImplemented(); }
+    virtual void scrollToOffset(long, long, ScrollToOptions*) { notImplemented(); }
 
-    virtual void immediateScrollToOffset(long, long) { notImplemented(); }
+    virtual void immediateScrollToOffset(long, long, ScrollToOptions*) { notImplemented(); }
     virtual void immediateScrollElementAtContentPointToOffset(long, long, long, long) { notImplemented(); }
 
     virtual double contentOffsetX() const { notImplemented(); return 0; }
@@ -360,5 +365,3 @@
 };
 
 }
-
-#endif // UIScriptController_h

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptControllerShared.cpp (271785 => 271786)


--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptControllerShared.cpp	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptControllerShared.cpp	2021-01-25 00:47:32 UTC (rev 271786)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "UIScriptController.h"
 
+#include "JSBasics.h"
 #include "JSUIScriptController.h"
 #include "UIScriptContext.h"
 #include <_javascript_Core/JSValueRef.h>
@@ -53,6 +54,16 @@
     return nullptr;
 }
 
+ScrollToOptions* toScrollToOptions(JSContextRef context, JSValueRef argument)
+{
+    if (!JSValueIsObject(context, argument))
+        return nullptr;
+
+    static ScrollToOptions options;
+    options.unconstrained = booleanProperty(context, (JSObjectRef)argument, "unconstrained", false);
+    return &options;
+}
+
 UIScriptController::UIScriptController(UIScriptContext& context)
     : m_context(&context)
 {

Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h (271785 => 271786)


--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h	2021-01-25 00:47:32 UTC (rev 271786)
@@ -94,8 +94,8 @@
     double contentOffsetY() const override;
     bool scrollUpdatesDisabled() const override;
     void setScrollUpdatesDisabled(bool) override;
-    void scrollToOffset(long x, long y) override;
-    void immediateScrollToOffset(long x, long y) override;
+    void scrollToOffset(long x, long y, ScrollToOptions*) override;
+    void immediateScrollToOffset(long x, long y, ScrollToOptions*) override;
     void immediateScrollElementAtContentPointToOffset(long x, long y, long xScrollOffset, long yScrollOffset) override;
     void immediateZoomToScale(double scale) override;
     void keyboardAccessoryBarNext() override;

Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm (271785 => 271786)


--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm	2021-01-25 00:28:45 UTC (rev 271785)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm	2021-01-25 00:47:32 UTC (rev 271786)
@@ -612,19 +612,24 @@
     return !!webView().window.rootViewController.presentedViewController;
 }
 
-static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
+static CGPoint contentOffsetBoundedIfNecessary(UIScrollView *scrollView, long x, long y, ScrollToOptions* options)
 {
-    UIEdgeInsets contentInsets = scrollView.contentInset;
-    CGSize contentSize = scrollView.contentSize;
-    CGSize scrollViewSize = scrollView.bounds.size;
+    auto contentOffset = CGPointMake(x, y);
+    bool constrain = !options || !options->unconstrained;
+    if (constrain) {
+        UIEdgeInsets contentInsets = scrollView.contentInset;
+        CGSize contentSize = scrollView.contentSize;
+        CGSize scrollViewSize = scrollView.bounds.size;
 
-    CGFloat maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
-    contentOffset.x = std::min(maxHorizontalOffset, contentOffset.x);
-    contentOffset.x = std::max(-contentInsets.left, contentOffset.x);
+        CGFloat maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
+        contentOffset.x = std::min(maxHorizontalOffset, contentOffset.x);
+        contentOffset.x = std::max(-contentInsets.left, contentOffset.x);
 
-    CGFloat maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
-    contentOffset.y = std::min(maxVerticalOffset, contentOffset.y);
-    contentOffset.y = std::max(-contentInsets.top, contentOffset.y);
+        CGFloat maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
+        contentOffset.y = std::min(maxVerticalOffset, contentOffset.y);
+        contentOffset.y = std::max(-contentInsets.top, contentOffset.y);
+    }
+
     return contentOffset;
 }
 
@@ -648,16 +653,18 @@
     webView()._scrollingUpdatesDisabledForTesting = disabled;
 }
 
-void UIScriptControllerIOS::scrollToOffset(long x, long y)
+void UIScriptControllerIOS::scrollToOffset(long x, long y, ScrollToOptions* options)
 {
     TestRunnerWKWebView *webView = this->webView();
-    [webView.scrollView setContentOffset:contentOffsetBoundedInValidRange(webView.scrollView, CGPointMake(x, y)) animated:YES];
+    auto offset = contentOffsetBoundedIfNecessary(webView.scrollView, x, y, options);
+    [webView.scrollView setContentOffset:offset animated:YES];
 }
 
-void UIScriptControllerIOS::immediateScrollToOffset(long x, long y)
+void UIScriptControllerIOS::immediateScrollToOffset(long x, long y, ScrollToOptions* options)
 {
     TestRunnerWKWebView *webView = this->webView();
-    [webView.scrollView setContentOffset:contentOffsetBoundedInValidRange(webView.scrollView, CGPointMake(x, y)) animated:NO];
+    auto offset = contentOffsetBoundedIfNecessary(webView.scrollView, x, y, options);
+    [webView.scrollView setContentOffset:offset animated:NO];
 }
 
 static UIScrollView *enclosingScrollViewIncludingSelf(UIView *view)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to