Title: [284654] trunk
Revision
284654
Author
[email protected]
Date
2021-10-21 16:58:35 -0700 (Thu, 21 Oct 2021)

Log Message

Content offset in this codepen when switching tabs
https://bugs.webkit.org/show_bug.cgi?id=231989

Reviewed by Tim Horton.

Source/WebCore:

There were two problems that occurred with async-scrollable iframes when their associated
WKWebView was removed and re-added to the view hierarchy (e.g. when switching tabs).
These resulted in misplaced position:fixed content, and the first user scroll in the
iframe causing the scroll position to jump back to the top.

The positon:fixed issue was caused by an ordering problem in
ScrollingTreeFrameScrollingNode::commitStateBeforeChildren() which resulted in the layout
viewport being computed incorrectly; we called updateViewportForCurrentScrollPosition()
before setting the min and max scroll position, so we'd always clamp the layout viewport to
a location of 0,0.

The second scroll position reset issue was caused by the ScrollingTreeScrollingNode's
m_currentScrollPosition reverting to a stale after re-attaching the iframe's scrolling
subtree. ScrollingTreeScrollingNode::commitStateBeforeChildren() has code to set
m_currentScrollPosition from the state tree node's scroll position on first commit;
the issue was that ScrollingStateScrollingNode's scrollPosition() was not updated on every
scroll, only when something triggered a scrolling tree commit.

Fix by silently updating ScrollingStateScrollingNode's scrollPosition() when we get
notifications back from the scrolling thread that a scroll happened.

Both fixes are tested by the ScrollingCoordinatorTests.ScrollingTreeAfterDetachReattach API test.

* page/scrolling/AsyncScrollingCoordinator.cpp:
(WebCore::AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll):
* page/scrolling/ScrollingStateScrollingNode.cpp:
(WebCore::ScrollingStateScrollingNode::syncScrollPosition):
(WebCore::ScrollingStateScrollingNode::hasScrollPositionRequest const):
* page/scrolling/ScrollingStateScrollingNode.h:
* page/scrolling/ScrollingStateTree.cpp:
(WebCore::ScrollingStateTree::insertNode):
* page/scrolling/ScrollingTreeFrameScrollingNode.cpp:
(WebCore::ScrollingTreeFrameScrollingNode::commitStateBeforeChildren): We need to check
state.hasScrollPositionRequest(), otherwise the "cancel animated scroll request" that comes
out of Page::stopKeyboardScrollAnimation() prevents scroll position restoration.
* page/scrolling/ScrollingTreeScrollingNode.cpp:
(WebCore::ScrollingTreeScrollingNode::commitStateBeforeChildren):
(WebCore::ScrollingTreeScrollingNode::dumpProperties const):
* page/scrolling/mac/ScrollingCoordinatorMac.mm:
(WebCore::ScrollingCoordinatorMac::commitTreeStateIfNeeded):

Tools:

API test that scrolls an iframe via wheel events, then detached and re-attaches the view.

The two wheel scrolls are necessary to exercise the "stale ScrollingStateScrollingNode
scroll position" issue.

The scrolling tree dumps validate the layout viewport part of the fix.

Also correct some functions where the sense of 'isWaitingForJavaScript' was flipped.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/mac/ScrollingCoordinatorTests.mm: Added.
(TestWebKitAPI::synthesizeWheelEvents):
(TestWebKitAPI::waitForScrollEventAndReturnScrollY):
(TestWebKitAPI::scrollingTreeElidingLastCommittedScrollPosition):
(TestWebKitAPI::TEST):
* TestWebKitAPI/cocoa/TestWKWebView.mm:
(-[WKWebView objectByEvaluatingJavaScript:]):
(-[WKWebView objectByEvaluatingJavaScriptWithUserGesture:]):
(-[WKWebView objectByCallingAsyncFunction:withArguments:error:]):

LayoutTests:

New baselines.

* tiled-drawing/scrolling/clamp-out-of-bounds-scrolls-expected.txt:
* tiled-drawing/scrolling/scrolling-tree-after-scroll-expected.txt:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (284653 => 284654)


--- trunk/LayoutTests/ChangeLog	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/LayoutTests/ChangeLog	2021-10-21 23:58:35 UTC (rev 284654)
@@ -1,3 +1,15 @@
+2021-10-21  Simon Fraser  <[email protected]>
+
+        Content offset in this codepen when switching tabs
+        https://bugs.webkit.org/show_bug.cgi?id=231989
+
+        Reviewed by Tim Horton.
+
+        New baselines.
+
+        * tiled-drawing/scrolling/clamp-out-of-bounds-scrolls-expected.txt:
+        * tiled-drawing/scrolling/scrolling-tree-after-scroll-expected.txt:
+
 2021-10-21  John Wilander  <[email protected]>
 
         PCM: Change expectation to pass so we can investigate http/tests/privateClickMeasurement/attribution-conversion-through-image-redirect-in-new-window.html

Modified: trunk/LayoutTests/tiled-drawing/scrolling/clamp-out-of-bounds-scrolls-expected.txt (284653 => 284654)


--- trunk/LayoutTests/tiled-drawing/scrolling/clamp-out-of-bounds-scrolls-expected.txt	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/LayoutTests/tiled-drawing/scrolling/clamp-out-of-bounds-scrolls-expected.txt	2021-10-21 23:58:35 UTC (rev 284654)
@@ -55,6 +55,7 @@
 Attempted scroll to 10000, 0
 
 (Frame scrolling node
+  (scroll position 4223 0)
   (scrollable area size 785 585)
   (contents size 5008 5021)
   (requested scroll position 4223 0)
@@ -75,7 +76,7 @@
 Attempted scroll to 0, 10000
 
 (Frame scrolling node
-  (scroll position 4223 0)
+  (scroll position 0 4436)
   (scrollable area size 785 585)
   (contents size 5008 5021)
   (requested scroll position 0 4436)
@@ -96,7 +97,7 @@
 Attempted scroll to 10000, 10000
 
 (Frame scrolling node
-  (scroll position 0 4436)
+  (scroll position 4223 4436)
   (scrollable area size 785 585)
   (contents size 5008 5021)
   (requested scroll position 4223 4436)

Modified: trunk/LayoutTests/tiled-drawing/scrolling/scrolling-tree-after-scroll-expected.txt (284653 => 284654)


--- trunk/LayoutTests/tiled-drawing/scrolling/scrolling-tree-after-scroll-expected.txt	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/LayoutTests/tiled-drawing/scrolling/scrolling-tree-after-scroll-expected.txt	2021-10-21 23:58:35 UTC (rev 284654)
@@ -1,5 +1,6 @@
 
 (Frame scrolling node
+  (scroll position 0 3000)
   (scrollable area size 785 600)
   (contents size 785 5021)
   (requested scroll position 0 3000)

Modified: trunk/Source/WebCore/ChangeLog (284653 => 284654)


--- trunk/Source/WebCore/ChangeLog	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Source/WebCore/ChangeLog	2021-10-21 23:58:35 UTC (rev 284654)
@@ -1,3 +1,51 @@
+2021-10-21  Simon Fraser  <[email protected]>
+
+        Content offset in this codepen when switching tabs
+        https://bugs.webkit.org/show_bug.cgi?id=231989
+
+        Reviewed by Tim Horton.
+
+        There were two problems that occurred with async-scrollable iframes when their associated
+        WKWebView was removed and re-added to the view hierarchy (e.g. when switching tabs).
+        These resulted in misplaced position:fixed content, and the first user scroll in the 
+        iframe causing the scroll position to jump back to the top.
+
+        The positon:fixed issue was caused by an ordering problem in
+        ScrollingTreeFrameScrollingNode::commitStateBeforeChildren() which resulted in the layout
+        viewport being computed incorrectly; we called updateViewportForCurrentScrollPosition()
+        before setting the min and max scroll position, so we'd always clamp the layout viewport to
+        a location of 0,0.
+
+        The second scroll position reset issue was caused by the ScrollingTreeScrollingNode's
+        m_currentScrollPosition reverting to a stale after re-attaching the iframe's scrolling
+        subtree. ScrollingTreeScrollingNode::commitStateBeforeChildren() has code to set
+        m_currentScrollPosition from the state tree node's scroll position on first commit;
+        the issue was that ScrollingStateScrollingNode's scrollPosition() was not updated on every
+        scroll, only when something triggered a scrolling tree commit.
+
+        Fix by silently updating ScrollingStateScrollingNode's scrollPosition() when we get
+        notifications back from the scrolling thread that a scroll happened.
+
+        Both fixes are tested by the ScrollingCoordinatorTests.ScrollingTreeAfterDetachReattach API test.
+
+        * page/scrolling/AsyncScrollingCoordinator.cpp:
+        (WebCore::AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll):
+        * page/scrolling/ScrollingStateScrollingNode.cpp:
+        (WebCore::ScrollingStateScrollingNode::syncScrollPosition):
+        (WebCore::ScrollingStateScrollingNode::hasScrollPositionRequest const):
+        * page/scrolling/ScrollingStateScrollingNode.h:
+        * page/scrolling/ScrollingStateTree.cpp:
+        (WebCore::ScrollingStateTree::insertNode):
+        * page/scrolling/ScrollingTreeFrameScrollingNode.cpp:
+        (WebCore::ScrollingTreeFrameScrollingNode::commitStateBeforeChildren): We need to check
+        state.hasScrollPositionRequest(), otherwise the "cancel animated scroll request" that comes
+        out of Page::stopKeyboardScrollAnimation() prevents scroll position restoration.
+        * page/scrolling/ScrollingTreeScrollingNode.cpp:
+        (WebCore::ScrollingTreeScrollingNode::commitStateBeforeChildren):
+        (WebCore::ScrollingTreeScrollingNode::dumpProperties const):
+        * page/scrolling/mac/ScrollingCoordinatorMac.mm:
+        (WebCore::ScrollingCoordinatorMac::commitTreeStateIfNeeded):
+
 2021-10-21  Sihui Liu  <[email protected]>
 
         FileSystemSyncAccessHandle should close platform file handle on close()

Modified: trunk/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp (284653 => 284654)


--- trunk/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp	2021-10-21 23:58:35 UTC (rev 284654)
@@ -451,6 +451,12 @@
             m_page->editorClient().subFrameScrollPositionChanged();
     }
 
+    auto* node = m_scrollingStateTree->stateNodeForID(scrollingNodeID);
+    if (is<ScrollingStateScrollingNode>(node)) {
+        auto& scrollingNode = downcast<ScrollingStateScrollingNode>(*node);
+        scrollingNode.syncScrollPosition(scrollPosition);
+    }
+
     if (scrollingNodeID == frameView.scrollingNodeID()) {
         reconcileScrollingState(frameView, scrollPosition, layoutViewportOrigin, scrollType, ViewportRectStability::Stable, scrollingLayerPositionAction);
         return;

Modified: trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp (284653 => 284654)


--- trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp	2021-10-21 23:58:35 UTC (rev 284654)
@@ -143,6 +143,11 @@
     setPropertyChanged(Property::ScrollPosition);
 }
 
+void ScrollingStateScrollingNode::syncScrollPosition(const FloatPoint& scrollPosition)
+{
+    m_scrollPosition = scrollPosition;
+}
+
 void ScrollingStateScrollingNode::setScrollOrigin(const IntPoint& scrollOrigin)
 {
     if (m_scrollOrigin == scrollOrigin)
@@ -206,6 +211,11 @@
     setPropertyChanged(Property::RequestedScrollPosition);
 }
 
+bool ScrollingStateScrollingNode::hasScrollPositionRequest() const
+{
+    return hasChangedProperty(Property::RequestedScrollPosition) && m_requestedScrollData.requestType == ScrollRequestType::PositionUpdate;
+}
+
 void ScrollingStateScrollingNode::setIsMonitoringWheelEvents(bool isMonitoringWheelEvents)
 {
     if (isMonitoringWheelEvents == m_isMonitoringWheelEvents)

Modified: trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h (284653 => 284654)


--- trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h	2021-10-21 23:58:35 UTC (rev 284654)
@@ -54,6 +54,9 @@
     const FloatPoint& scrollPosition() const { return m_scrollPosition; }
     WEBCORE_EXPORT void setScrollPosition(const FloatPoint&);
 
+    // Does not trigger a scrolling tree commit.
+    WEBCORE_EXPORT void syncScrollPosition(const FloatPoint&);
+
     const IntPoint& scrollOrigin() const { return m_scrollOrigin; }
     WEBCORE_EXPORT void setScrollOrigin(const IntPoint&);
 
@@ -78,6 +81,8 @@
     const RequestedScrollData& requestedScrollData() const { return m_requestedScrollData; }
     WEBCORE_EXPORT void setRequestedScrollData(const RequestedScrollData&);
 
+    WEBCORE_EXPORT bool hasScrollPositionRequest() const;
+
     bool isMonitoringWheelEvents() const { return m_isMonitoringWheelEvents; }
     WEBCORE_EXPORT void setIsMonitoringWheelEvents(bool);
 

Modified: trunk/Source/WebCore/page/scrolling/ScrollingStateTree.cpp (284653 => 284654)


--- trunk/Source/WebCore/page/scrolling/ScrollingStateTree.cpp	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Source/WebCore/page/scrolling/ScrollingStateTree.cpp	2021-10-21 23:58:35 UTC (rev 284654)
@@ -176,7 +176,7 @@
 
         if (parentID) {
             if (auto unparentedNode = m_unparentedNodes.take(newNodeID)) {
-                LOG_WITH_STREAM(ScrollingTree, stream << "ScrollingStateTree " << this << " insertNode " << newNodeID << " getting node from unparented nodes");
+                LOG_WITH_STREAM(ScrollingTree, stream << "ScrollingStateTree " << this << " insertNode reattaching node " << newNodeID);
                 newNode = unparentedNode.get();
                 nodeWasReattachedRecursive(*unparentedNode);
 

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.cpp (284653 => 284654)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.cpp	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.cpp	2021-10-21 23:58:35 UTC (rev 284654)
@@ -72,10 +72,8 @@
     if (state.hasChangedProperty(ScrollingStateNode::Property::FixedElementsLayoutRelativeToFrame))
         m_fixedElementsLayoutRelativeToFrame = state.fixedElementsLayoutRelativeToFrame();
 
-    if (state.hasChangedProperty(ScrollingStateNode::Property::LayoutViewport)) {
+    if (state.hasChangedProperty(ScrollingStateNode::Property::LayoutViewport))
         m_layoutViewport = state.layoutViewport();
-        updateViewportForCurrentScrollPosition({ });
-    }
 
     if (state.hasChangedProperty(ScrollingStateNode::Property::MinLayoutViewportOrigin))
         m_minLayoutViewportOrigin = state.minLayoutViewportOrigin();
@@ -85,6 +83,11 @@
 
     if (state.hasChangedProperty(ScrollingStateNode::Property::OverrideVisualViewportSize))
         m_overrideVisualViewportSize = state.overrideVisualViewportSize();
+
+    if (state.hasChangedProperty(ScrollingStateNode::Property::LayoutViewport)) {
+        // This requires that minLayoutViewportOrigin and maxLayoutViewportOrigin have been updated.
+        updateViewportForCurrentScrollPosition({ });
+    }
 }
 
 bool ScrollingTreeFrameScrollingNode::scrollPositionAndLayoutViewportMatch(const FloatPoint& position, std::optional<FloatRect> overrideLayoutViewport)

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp (284653 => 284654)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp	2021-10-21 23:58:35 UTC (rev 284654)
@@ -67,7 +67,7 @@
 
     if (state.hasChangedProperty(ScrollingStateNode::Property::ScrollPosition)) {
         m_lastCommittedScrollPosition = state.scrollPosition();
-        if (m_isFirstCommit && !state.hasChangedProperty(ScrollingStateNode::Property::RequestedScrollPosition))
+        if (m_isFirstCommit && !state.hasScrollPositionRequest())
             m_currentScrollPosition = m_lastCommittedScrollPosition;
     }
 
@@ -336,7 +336,10 @@
         ts.dumpProperty("reachable content size", m_reachableContentsSize);
     ts.dumpProperty("last committed scroll position", m_lastCommittedScrollPosition);
 
-    if (m_scrollOrigin != IntPoint())
+    if (!m_currentScrollPosition.isZero())
+        ts.dumpProperty("scroll position", m_currentScrollPosition);
+
+    if (!m_scrollOrigin.isZero())
         ts.dumpProperty("scroll origin", m_scrollOrigin);
 
     if (m_snapOffsetsInfo.horizontalSnapOffsets.size())

Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm (284653 => 284654)


--- trunk/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm	2021-10-21 23:58:35 UTC (rev 284654)
@@ -114,7 +114,7 @@
     if (!scrollingStateTree()->hasChangedProperties())
         return;
 
-    LOG_WITH_STREAM(ScrollingTree, stream << scrollingStateTreeAsText(debugScrollingStateTreeAsTextBehaviors));
+    LOG_WITH_STREAM(ScrollingTree, stream << "ScrollingCoordinatorMac::commitTreeState: state tree " << scrollingStateTreeAsText(debugScrollingStateTreeAsTextBehaviors));
 
     auto stateTree = scrollingStateTree()->commit(LayerRepresentation::PlatformLayerRepresentation);
     scrollingTree()->commitTreeState(WTFMove(stateTree));

Modified: trunk/Tools/ChangeLog (284653 => 284654)


--- trunk/Tools/ChangeLog	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Tools/ChangeLog	2021-10-21 23:58:35 UTC (rev 284654)
@@ -1,3 +1,30 @@
+2021-10-21  Simon Fraser  <[email protected]>
+
+        Content offset in this codepen when switching tabs
+        https://bugs.webkit.org/show_bug.cgi?id=231989
+
+        Reviewed by Tim Horton.
+
+        API test that scrolls an iframe via wheel events, then detached and re-attaches the view.
+
+        The two wheel scrolls are necessary to exercise the "stale ScrollingStateScrollingNode
+        scroll position" issue.
+
+        The scrolling tree dumps validate the layout viewport part of the fix.
+
+        Also correct some functions where the sense of 'isWaitingForJavaScript' was flipped.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/mac/ScrollingCoordinatorTests.mm: Added.
+        (TestWebKitAPI::synthesizeWheelEvents):
+        (TestWebKitAPI::waitForScrollEventAndReturnScrollY):
+        (TestWebKitAPI::scrollingTreeElidingLastCommittedScrollPosition):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/cocoa/TestWKWebView.mm:
+        (-[WKWebView objectByEvaluatingJavaScript:]):
+        (-[WKWebView objectByEvaluatingJavaScriptWithUserGesture:]):
+        (-[WKWebView objectByCallingAsyncFunction:withArguments:error:]):
+
 2021-10-21  Fujii Hironori  <[email protected]>
 
         [Win] TestWTF.FileSystemTest.* are timing out if run-api-tests is run by a normal user

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (284653 => 284654)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2021-10-21 23:58:35 UTC (rev 284654)
@@ -86,6 +86,7 @@
 		0F4FFA9E1ED3AA8500F7111F /* SnapshotViaRenderInContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F4FFA9D1ED3AA8500F7111F /* SnapshotViaRenderInContext.mm */; };
 		0F5651F71FCE4DDC00310FBC /* NoHistoryItemScrollToFragment.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F5651F61FCE4DDB00310FBC /* NoHistoryItemScrollToFragment.mm */; };
 		0F5651F91FCE513500310FBC /* scroll-to-anchor.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 0F5651F81FCE50E800310FBC /* scroll-to-anchor.html */; };
+		0FEFAF64271FC2CD005704D7 /* ScrollingCoordinatorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0FEFAF63271FC2CD005704D7 /* ScrollingCoordinatorTests.mm */; };
 		0FF1134E22D68679009A81DA /* ScrollViewScrollabilityTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0FF1134D22D68679009A81DA /* ScrollViewScrollabilityTests.mm */; };
 		115EB3431EE0BA03003C2C0A /* ViewportSizeForViewportUnits.mm in Sources */ = {isa = PBXBuildFile; fileRef = 115EB3421EE0B720003C2C0A /* ViewportSizeForViewportUnits.mm */; };
 		1171B24F219F49CD00CB897D /* FirstMeaningfulPaintMilestone_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 11B7FD21219F46DD0069B27F /* FirstMeaningfulPaintMilestone_Bundle.cpp */; };
@@ -1860,6 +1861,7 @@
 		0FC6C4CE141034AD005B7F0C /* MetaAllocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MetaAllocator.cpp; sourceTree = "<group>"; };
 		0FE447971B76F1E3009498EB /* ParkingLot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParkingLot.cpp; sourceTree = "<group>"; };
 		0FEAE3671B7D19CB00CE17F2 /* Condition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Condition.cpp; sourceTree = "<group>"; };
+		0FEFAF63271FC2CD005704D7 /* ScrollingCoordinatorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollingCoordinatorTests.mm; sourceTree = "<group>"; };
 		0FF1134D22D68679009A81DA /* ScrollViewScrollabilityTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollViewScrollabilityTests.mm; sourceTree = "<group>"; };
 		0FFC45A41B73EBE20085BD62 /* Lock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Lock.cpp; sourceTree = "<group>"; };
 		115EB3421EE0B720003C2C0A /* ViewportSizeForViewportUnits.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewportSizeForViewportUnits.mm; sourceTree = "<group>"; };
@@ -3092,7 +3094,6 @@
 		F44A530F21B8976900DBB99C /* ClassMethodSwizzler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClassMethodSwizzler.h; path = ../TestRunnerShared/cocoa/ClassMethodSwizzler.h; sourceTree = "<group>"; };
 		F44A531021B8976900DBB99C /* InstanceMethodSwizzler.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = InstanceMethodSwizzler.mm; path = ../TestRunnerShared/cocoa/InstanceMethodSwizzler.mm; sourceTree = "<group>"; };
 		F44A7D1F268D5C6900B49BB8 /* ImageAnalysisTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ImageAnalysisTests.mm; sourceTree = "<group>"; };
-		F44A9AF52649BBDD00E7CB16 /* ImmediateActionTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImmediateActionTests.h; sourceTree = "<group>"; };
 		F44A9AF62649BBDD00E7CB16 /* ImmediateActionTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ImmediateActionTests.mm; sourceTree = "<group>"; };
 		F44C79FB20F9E50C0014478C /* ParserYieldTokenPlugIn.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ParserYieldTokenPlugIn.mm; sourceTree = "<group>"; };
 		F44C79FD20F9E8710014478C /* ParserYieldTokenTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ParserYieldTokenTests.h; sourceTree = "<group>"; };
@@ -4828,7 +4829,6 @@
 				51EB125824C68589000CB030 /* HIDGamepads.mm */,
 				9B4F8FA3159D52B1002D9F94 /* HTMLCollectionNamedItem.mm */,
 				9B26FC6B159D061000CC3765 /* HTMLFormCollectionNamedItem.mm */,
-				F44A9AF52649BBDD00E7CB16 /* ImmediateActionTests.h */,
 				F44A9AF62649BBDD00E7CB16 /* ImmediateActionTests.mm */,
 				C507E8A614C6545B005D6B3B /* InspectorBar.mm */,
 				57F10D921C7E7B3800ECDF30 /* IsNavigationActionTrusted.mm */,
@@ -4848,6 +4848,7 @@
 				A57A34EF16AF677200C2501F /* PageVisibilityStateWithWindowChanges.mm */,
 				37C784DE197C8F2E0010A496 /* RenderedImageFromDOMNode.mm */,
 				3722C8681461E03E00C45D00 /* RenderedImageFromDOMRange.mm */,
+				0FEFAF63271FC2CD005704D7 /* ScrollingCoordinatorTests.mm */,
 				261516D515B0E60500A2C201 /* SetAndUpdateCacheModel.mm */,
 				52B8CF9515868CF000281053 /* SetDocumentURI.mm */,
 				C540F775152E4DA000A40C8C /* SimplifyMarkup.mm */,
@@ -5845,6 +5846,7 @@
 				95095F20262FFFA50000D920 /* SampledPageTopColor.mm in Sources */,
 				CDCFA7AA1E45183200C2433D /* SampleMap.cpp in Sources */,
 				CE0947372063223B003C9BA0 /* SchemeRegistry.mm in Sources */,
+				0FEFAF64271FC2CD005704D7 /* ScrollingCoordinatorTests.mm in Sources */,
 				CDC0932B21C872C10030C4B0 /* ScrollingDoesNotPauseMedia.mm in Sources */,
 				7CCE7F121A411AE600447C4C /* ScrollPinningBehaviors.cpp in Sources */,
 				F434CA1A22E65BCA005DDB26 /* ScrollToRevealSelection.mm in Sources */,

Added: trunk/Tools/TestWebKitAPI/Tests/mac/ScrollingCoordinatorTests.mm (0 => 284654)


--- trunk/Tools/TestWebKitAPI/Tests/mac/ScrollingCoordinatorTests.mm	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/mac/ScrollingCoordinatorTests.mm	2021-10-21 23:58:35 UTC (rev 284654)
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "Test.h"
+
+#if PLATFORM(MAC)
+
+#import "PlatformUtilities.h"
+#import "TestWKWebView.h"
+#import "WKWebViewConfigurationExtras.h"
+#import <WebKit/WKWebViewPrivate.h>
+#import <wtf/RetainPtr.h>
+
+namespace TestWebKitAPI {
+
+static float waitForScrollEventAndReturnScrollY(WKWebView* webView, const std::function<void(WKWebView*)>& scrollTrigger)
+{
+    bool receivedScrollEvent = false;
+    RetainPtr<id> evalResult;
+    RetainPtr<NSError> strongError;
+    
+    NSString *scriptString = @"return new Promise((resolve) => {" \
+        "   let iframe = document.getElementsByTagName('iframe')[0];" \
+        "   iframe.contentWindow.addEventListener(\"scroll\", (event) => resolve(iframe.contentWindow.scrollY)); " \
+        "})";
+
+    [webView callAsyncJavaScript:scriptString arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+        evalResult = result;
+        strongError = error;
+        receivedScrollEvent = true;
+    }];
+
+    scrollTrigger(webView);
+
+    TestWebKitAPI::Util::run(&receivedScrollEvent);
+
+    return [evalResult floatValue];
+}
+
+// Remove "last committed scroll position" for the iframe's node, which we expect to be different.
+static NSString *scrollingTreeElidingLastCommittedScrollPosition(NSString *scrollingTree)
+{
+    NSMutableArray *lines = [[[scrollingTree componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] mutableCopy] autorelease];
+    
+    NSIndexSet* lastCommittedLineIndices = [lines indexesOfObjectsPassingTest:^BOOL(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+        return [obj hasPrefix:@"        (last committed scroll position"];
+    }];
+    [lines removeObjectsAtIndexes:lastCommittedLineIndices];
+    return [lines componentsJoinedByString:@"\n"];
+}
+
+TEST(ScrollingCoordinatorTests, ScrollingTreeAfterDetachReattach)
+{
+    WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 500, 500) configuration:configuration addToWindow:YES]);
+    
+    NSString *documentString = @"<style>body { height: 5000px; }</style>" \
+        "<iframe srcdoc=\"<style>body { height: 5000px; }</style><div style='position:fixed; width: 100px; height: 50px; background: blue'></div>\"></iframe>";
+    
+    [webView synchronouslyLoadHTMLString:documentString];
+    [webView waitForNextPresentationUpdate];
+
+    CGPoint eventLocationInWindow = [webView convertPoint:CGPointMake(50, 50) toView:nil];
+
+    auto scrollY = waitForScrollEventAndReturnScrollY(webView.get(), [eventLocationInWindow](TestWKWebView *webView) {
+        [webView wheelEventAtPoint:eventLocationInWindow wheelDelta:CGSizeMake(0, -100)];
+    });
+    EXPECT_EQ(scrollY, 100);
+
+    // Send a second wheel event to trigger reconcileScrollingState() that does not end up in setScrollingNodeScrollableAreaGeometry() (some compositing code early returns).
+    // This would leave the scrolling state tree with a stale m_scrollPosition.
+    scrollY = waitForScrollEventAndReturnScrollY(webView.get(), [eventLocationInWindow](TestWKWebView *webView) {
+        [webView wheelEventAtPoint:eventLocationInWindow wheelDelta:CGSizeMake(0, -100)];
+    });
+    EXPECT_EQ(scrollY, 200);
+
+    [webView waitForNextPresentationUpdate];
+
+    NSString *scrollingTreeBefore = scrollingTreeElidingLastCommittedScrollPosition([webView stringByEvaluatingJavaScript:@"internals.scrollingTreeAsText()"]);
+
+    NSWindow *hostWindow = [webView window];
+    [webView removeFromSuperview];
+    [webView waitForNextPresentationUpdate];
+    [[hostWindow contentView] addSubview:webView.get()];
+    [webView waitForNextPresentationUpdate];
+
+    NSString *scrollingTreeAfter = scrollingTreeElidingLastCommittedScrollPosition([webView stringByEvaluatingJavaScript:@"internals.scrollingTreeAsText()"]);
+
+    EXPECT_TRUE([scrollingTreeBefore isEqualToString:scrollingTreeAfter]);
+
+    scrollY = waitForScrollEventAndReturnScrollY(webView.get(), [eventLocationInWindow](TestWKWebView *webView) {
+        [webView wheelEventAtPoint:eventLocationInWindow wheelDelta:CGSizeMake(0, -101)];
+    });
+    EXPECT_EQ(scrollY, 301);
+}
+
+} // namespace TestWebKitAPI
+
+#endif // PLATFORM(MAC)

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h (284653 => 284654)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h	2021-10-21 23:58:35 UTC (rev 284654)
@@ -139,6 +139,7 @@
 - (void)mouseMoveToPoint:(NSPoint)pointInWindow withFlags:(NSEventModifierFlags)flags;
 - (void)sendClicksAtPoint:(NSPoint)pointInWindow numberOfClicks:(NSUInteger)numberOfClicks;
 - (void)sendClickAtPoint:(NSPoint)pointInWindow;
+- (void)wheelEventAtPoint:(CGPoint)pointInWindow wheelDelta:(CGSize)delta;
 - (NSWindow *)hostWindow;
 - (void)typeCharacter:(char)character modifiers:(NSEventModifierFlags)modifiers;
 - (void)typeCharacter:(char)character;

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm (284653 => 284654)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2021-10-21 23:46:00 UTC (rev 284653)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2021-10-21 23:58:35 UTC (rev 284654)
@@ -204,37 +204,37 @@
 
 - (id)objectByEvaluatingJavaScript:(NSString *)script
 {
-    bool isWaitingForJavaScript = false;
+    bool callbackComplete = false;
     RetainPtr<id> evalResult;
     [self _evaluateJavaScriptWithoutUserGesture:script completionHandler:[&] (id result, NSError *error) {
         evalResult = result;
-        isWaitingForJavaScript = true;
+        callbackComplete = true;
         EXPECT_TRUE(!error);
         if (error)
             NSLog(@"Encountered error: %@ while evaluating script: %@", error, script);
     }];
-    TestWebKitAPI::Util::run(&isWaitingForJavaScript);
+    TestWebKitAPI::Util::run(&callbackComplete);
     return evalResult.autorelease();
 }
 
 - (id)objectByEvaluatingJavaScriptWithUserGesture:(NSString *)script
 {
-    bool isWaitingForJavaScript = false;
+    bool callbackComplete = false;
     RetainPtr<id> evalResult;
     [self evaluateJavaScript:script completionHandler:[&] (id result, NSError *error) {
         evalResult = result;
-        isWaitingForJavaScript = true;
+        callbackComplete = true;
         EXPECT_TRUE(!error);
         if (error)
             NSLog(@"Encountered error: %@ while evaluating script: %@", error, script);
     }];
-    TestWebKitAPI::Util::run(&isWaitingForJavaScript);
+    TestWebKitAPI::Util::run(&callbackComplete);
     return evalResult.autorelease();
 }
 
 - (id)objectByCallingAsyncFunction:(NSString *)script withArguments:(NSDictionary *)arguments error:(NSError **)errorOut
 {
-    bool isWaitingForJavaScript = false;
+    bool callbackComplete = false;
     if (errorOut)
         *errorOut = nil;
 
@@ -243,9 +243,9 @@
     [self callAsyncJavaScript:script arguments:arguments inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         evalResult = result;
         strongError = error;
-        isWaitingForJavaScript = true;
+        callbackComplete = true;
     }];
-    TestWebKitAPI::Util::run(&isWaitingForJavaScript);
+    TestWebKitAPI::Util::run(&callbackComplete);
 
     if (errorOut)
         *errorOut = strongError.autorelease();
@@ -862,6 +862,18 @@
     }
 }
 
+- (void)wheelEventAtPoint:(CGPoint)pointInWindow wheelDelta:(CGSize)delta
+{
+    RetainPtr<CGEventRef> cgScrollEvent = adoptCF(CGEventCreateScrollWheelEvent(nullptr, kCGScrollEventUnitPixel, 2, delta.height, delta.width, 0));
+
+    CGPoint locationInGlobalScreenCoordinates = [[self window] convertPointToScreen:pointInWindow];
+    locationInGlobalScreenCoordinates.y = [[[NSScreen screens] objectAtIndex:0] frame].size.height - locationInGlobalScreenCoordinates.y;
+    CGEventSetLocation(cgScrollEvent.get(), locationInGlobalScreenCoordinates);
+    
+    NSEvent* event = [NSEvent eventWithCGEvent:cgScrollEvent.get()];
+    [self scrollWheel:event];
+}
+
 - (NSWindow *)hostWindow
 {
     return _hostWindow.get();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to