Diff
Modified: trunk/LayoutTests/ChangeLog (242996 => 242997)
--- trunk/LayoutTests/ChangeLog 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/LayoutTests/ChangeLog 2019-03-15 16:26:09 UTC (rev 242997)
@@ -1,3 +1,22 @@
+2019-03-15 Simon Fraser <[email protected]>
+
+ [Async overflow Scrolling] Update positioned node layers when overflows are scrolled
+ https://bugs.webkit.org/show_bug.cgi?id=195733
+ rdar://problem/11642295
+
+ Reviewed by Antti Koivisto.
+
+ New tests that use uiController.scrollUpdatesDisabled, and are the two move/stationary
+ cases.
+
+ * platform/ios-wk2/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt:
+ * scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll-expected.html: Added.
+ * scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html: Added.
+ * scrollingcoordinator/ios/relative-layer-should-move-with-scroll-expected.html: Added.
+ * scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html: Added.
+ * scrollingcoordinator/ios/ui-scrolling-tree-expected.txt: Fixed the last paren showing up.
+ * scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt: We make positioned nodes now.
+
2019-03-15 Fujii Hironori <[email protected]>
Unreviewed test gardening for WinCairo port
Modified: trunk/LayoutTests/platform/ios-wk2/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt (242996 => 242997)
--- trunk/LayoutTests/platform/ios-wk2/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/LayoutTests/platform/ios-wk2/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt 2019-03-15 16:26:09 UTC (rev 242997)
@@ -17,7 +17,7 @@
(min layout viewport origin (0,0))
(max layout viewport origin (0,141))
(behavior for fixed 0)
- (children 4
+ (children 5
(Overflow scrolling node
(scrollable area size 220 170)
(contents size 220 1020)
@@ -54,6 +54,12 @@
)
)
)
+ (Positioned node
+ (layout constraints
+ (layer-position-at-last-layout (10,30))
+ (positioning-behavior moves))
+ (related overflow nodes 1)
+ )
(Overflow scrolling node
(scrollable area size 220 170)
(contents size 220 1020)
Added: trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll-expected.html (0 => 242997)
--- trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll-expected.html (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll-expected.html 2019-03-15 16:26:09 UTC (rev 242997)
@@ -0,0 +1,61 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+ <meta name="viewport" content="initial-scale=1.0">
+ <style>
+ #scroller {
+ margin: 10px;
+ height: 300px;
+ width: 300px;
+ border: 1px solid black;
+ overflow: scroll;
+ opacity: 0.999; /* Make this a stacking context */
+ }
+
+ .box {
+ width: 200px;
+ height: 200px;
+ background-color: gray;
+ }
+
+ .absolute {
+ position: absolute;
+ top: 200px;
+ background-color: blue;
+ transform: translateZ(0); /* Currently need to trigger compositing */
+ }
+
+ .scroll-content {
+ height: 2000px;
+ }
+ </style>
+ <script src=""
+ <script>
+ if (window.testRunner)
+ testRunner.waitUntilDone();
+
+ if (window.internals)
+ window.internals.settings.setAsyncOverflowScrollingEnabled(true);
+
+ async function doTest()
+ {
+ await UIHelper.ensurePresentationUpdate(); // Not sure why this is necessary, but it is.
+ scroller.scrollTo(0, 100);
+ await UIHelper.ensurePresentationUpdate();
+
+ if (window.testRunner)
+ testRunner.notifyDone();
+ }
+
+ window.addEventListener('load', doTest, false);
+ </script>
+</head>
+<body>
+ <div id="scroller">
+ <div class="scroll-content">
+ <div class="box"></div>
+ <div class="absolute box"></div>
+ </div>
+ </div>
+</body>
+</html>
Added: trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html (0 => 242997)
--- trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html 2019-03-15 16:26:09 UTC (rev 242997)
@@ -0,0 +1,71 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+ <meta name="viewport" content="initial-scale=1.0">
+ <style>
+ #scroller {
+ margin: 10px;
+ height: 300px;
+ width: 300px;
+ border: 1px solid black;
+ overflow: scroll;
+ opacity: 0.999; /* Make this a stacking context */
+ }
+
+ .box {
+ width: 200px;
+ height: 200px;
+ background-color: gray;
+ }
+
+ .absolute {
+ position: absolute;
+ top: 200px;
+ background-color: blue;
+ transform: translateZ(0); /* Currently need to trigger compositing */
+ }
+
+ .scroll-content {
+ height: 2000px;
+ }
+ </style>
+ <script>
+ if (window.testRunner)
+ testRunner.waitUntilDone();
+
+ if (window.internals)
+ window.internals.settings.setAsyncOverflowScrollingEnabled(true);
+
+ function getUnstableScrollScript(x, y, scrollX, scrollY)
+ {
+ return `(function() {
+ uiController.scrollUpdatesDisabled = true;
+ uiController.immediateScrollElementAtContentPointToOffset(${x}, ${y}, ${scrollX}, ${scrollY});
+ uiController.doAfterPresentationUpdate(function() {
+ uiController.uiScriptComplete();
+ });
+ })();`;
+ }
+
+ function doTest()
+ {
+ if (!testRunner.runUIScript)
+ return
+
+ testRunner.runUIScript(getUnstableScrollScript(50, 50, 0, 100), () => {
+ testRunner.notifyDone();
+ });
+ }
+
+ window.addEventListener('load', doTest, false);
+ </script>
+</head>
+<body>
+ <div id="scroller">
+ <div class="scroll-content">
+ <div class="box"></div>
+ <div class="absolute box"></div>
+ </div>
+ </div>
+</body>
+</html>
Added: trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll-expected.html (0 => 242997)
--- trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll-expected.html (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll-expected.html 2019-03-15 16:26:09 UTC (rev 242997)
@@ -0,0 +1,55 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+ <meta name="viewport" content="initial-scale=1.0">
+ <style>
+ #scroller {
+ margin: 10px;
+ height: 300px;
+ width: 300px;
+ border: 1px solid black;
+ overflow: scroll;
+ }
+
+ .box {
+ position: relative;
+ z-index: 0; /* stacking context ancestor is the root */
+ top: 250px;
+ width: 200px;
+ height: 200px;
+ background-color: blue;
+ }
+
+ .scroll-content {
+ height: 2000px;
+ }
+ </style>
+ <script src=""
+ <script>
+ if (window.testRunner)
+ testRunner.waitUntilDone();
+
+ if (window.internals)
+ window.internals.settings.setAsyncOverflowScrollingEnabled(true);
+
+ async function doTest()
+ {
+ await UIHelper.ensurePresentationUpdate(); // Not sure why this is necessary, but it is.
+ scroller.scrollTo(0, 250);
+ await UIHelper.ensurePresentationUpdate();
+
+ if (window.testRunner)
+ testRunner.notifyDone();
+ }
+
+ window.addEventListener('load', doTest, false);
+ </script>
+</head>
+<body>
+ <div id="scroller">
+ <div class="scroll-content">
+ <div class="box"></div>
+ </div>
+ </div>
+</body>
+</html>
Added: trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html (0 => 242997)
--- trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html 2019-03-15 16:26:09 UTC (rev 242997)
@@ -0,0 +1,65 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+ <meta name="viewport" content="initial-scale=1.0">
+ <style>
+ #scroller {
+ margin: 10px;
+ height: 300px;
+ width: 300px;
+ border: 1px solid black;
+ overflow: scroll;
+ }
+
+ .box {
+ position: relative;
+ z-index: 0; /* stacking context ancestor is the root */
+ top: 250px;
+ width: 200px;
+ height: 200px;
+ background-color: blue;
+ }
+
+ .scroll-content {
+ height: 2000px;
+ }
+ </style>
+ <script>
+ if (window.testRunner)
+ testRunner.waitUntilDone();
+
+ if (window.internals)
+ window.internals.settings.setAsyncOverflowScrollingEnabled(true);
+
+ function getUnstableScrollScript(x, y, scrollX, scrollY)
+ {
+ return `(function() {
+ uiController.scrollUpdatesDisabled = true;
+ uiController.immediateScrollElementAtContentPointToOffset(${x}, ${y}, ${scrollX}, ${scrollY});
+ uiController.doAfterPresentationUpdate(function() {
+ uiController.uiScriptComplete();
+ });
+ })();`;
+ }
+
+ function doTest()
+ {
+ if (!testRunner.runUIScript)
+ return
+
+ testRunner.runUIScript(getUnstableScrollScript(50, 50, 0, 250), () => {
+ testRunner.notifyDone();
+ });
+ }
+
+ window.addEventListener('load', doTest, false);
+ </script>
+</head>
+<body>
+ <div id="scroller">
+ <div class="scroll-content">
+ <div class="box"></div>
+ </div>
+ </div>
+</body>
+</html>
Modified: trunk/LayoutTests/scrollingcoordinator/ios/ui-scrolling-tree-expected.txt (242996 => 242997)
--- trunk/LayoutTests/scrollingcoordinator/ios/ui-scrolling-tree-expected.txt 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/LayoutTests/scrollingcoordinator/ios/ui-scrolling-tree-expected.txt 2019-03-15 16:26:09 UTC (rev 242997)
@@ -17,4 +17,4 @@
(fixed constraints
(viewport-rect-at-last-layout (0,0) width=320 height=548)
(layer-position-at-last-layout (12,10)))
- (layer top left (12,10))))
+ (layer top left (12,10)))))
Modified: trunk/LayoutTests/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt (242996 => 242997)
--- trunk/LayoutTests/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/LayoutTests/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt 2019-03-15 16:26:09 UTC (rev 242997)
@@ -18,7 +18,7 @@
(min layout viewport origin (0,0))
(max layout viewport origin (0,141))
(behavior for fixed 0)
- (children 4
+ (children 5
(Overflow scrolling node
(scrollable area size 205 155)
(contents size 205 1020)
@@ -55,6 +55,12 @@
)
)
)
+ (Positioned node
+ (layout constraints
+ (layer-position-at-last-layout (10,28))
+ (positioning-behavior moves))
+ (related overflow nodes 1)
+ )
(Overflow scrolling node
(scrollable area size 205 155)
(contents size 205 1020)
Modified: trunk/Source/WebCore/ChangeLog (242996 => 242997)
--- trunk/Source/WebCore/ChangeLog 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/ChangeLog 2019-03-15 16:26:09 UTC (rev 242997)
@@ -1,3 +1,47 @@
+2019-03-15 Simon Fraser <[email protected]>
+
+ [Async overflow Scrolling] Update positioned node layers when overflows are scrolled
+ https://bugs.webkit.org/show_bug.cgi?id=195733
+ rdar://problem/11642295
+
+ Reviewed by Antti Koivisto.
+
+ Make ScrollingTree positioned nodes in the two cases where we need them, as
+ detected by RenderLayerCompositor::computeCoordinatedPositioningForLayer().
+
+ For "Moves" layers we know that the overflow is not in the z-order ancestor chain,
+ so ScrollingTree needs a map of overflow node -> affected positioned nodes which
+ notifyRelatedNodesAfterScrollPositionChange() uses to find nodes to update after
+ a scroll. Computing these dependent nodes in RenderLayerCompositor() would require
+ correct dependency analysis between an overflow layers and "positioned" layers which
+ is hard. It's easier to have "positioned" layers figure out which overflow nodes
+ affect them, then compute the inverse relationship when the scrolling tree is updated
+ which happens in ScrollingTreePositionedNode::commitStateBeforeChildren().
+
+ Tests: scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html
+ scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html
+
+ * page/scrolling/ScrollingTree.cpp:
+ (WebCore::ScrollingTree::commitTreeState):
+ (WebCore::ScrollingTree::applyLayerPositions):
+ (WebCore::ScrollingTree::notifyRelatedNodesAfterScrollPositionChange):
+ (WebCore::ScrollingTree::scrollingTreeAsText):
+ * page/scrolling/ScrollingTree.h:
+ (WebCore::ScrollingTree::overflowRelatedNodes):
+ * page/scrolling/ScrollingTreeOverflowScrollingNode.cpp:
+ (WebCore::ScrollingTreeOverflowScrollingNode::dumpProperties const):
+ * page/scrolling/ScrollingTreeOverflowScrollingNode.h:
+ * page/scrolling/cocoa/ScrollingTreePositionedNode.mm:
+ (WebCore::ScrollingTreePositionedNode::commitStateBeforeChildren):
+ (WebCore::ScrollingTreePositionedNode::applyLayerPositions):
+ (WebCore::ScrollingTreePositionedNode::relatedNodeScrollPositionDidChange):
+ * rendering/RenderLayerCompositor.cpp:
+ (WebCore::layerContainingBlockCrossesCoordinatedScrollingBoundary):
+ (WebCore::layerParentedAcrossCoordinatedScrollingBoundary):
+ (WebCore::RenderLayerCompositor::computeCoordinatedPositioningForLayer const):
+ (WebCore::collectRelatedCoordinatedScrollingNodes):
+ (WebCore::RenderLayerCompositor::updateScrollingNodeForPositioningRole):
+
2019-03-15 Antti Koivisto <[email protected]>
Optimize Region for single rectangle case
Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp (242996 => 242997)
--- trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp 2019-03-15 16:26:09 UTC (rev 242997)
@@ -174,6 +174,8 @@
for (auto nodeID : m_nodeMap.keys())
unvisitedNodes.add(nodeID);
+ m_overflowRelatedNodesMap.clear();
+
// orphanNodes keeps child nodes alive while we rebuild child lists.
OrphanScrollingNodeMap orphanNodes;
updateTreeFromStateNode(rootNode, orphanNodes, unvisitedNodes);
@@ -185,6 +187,8 @@
LOG(Scrolling, "ScrollingTree::commitTreeState - removing unvisited node %" PRIu64, nodeID);
m_nodeMap.remove(nodeID);
}
+
+ LOG(Scrolling, "committed ScrollingTree\n%s", scrollingTreeAsText(ScrollingStateTreeAsTextBehaviorDebug).utf8().data());
}
void ScrollingTree::updateTreeFromStateNode(const ScrollingStateNode* stateNode, OrphanScrollingNodeMap& orphanNodes, HashSet<ScrollingNodeID>& unvisitedNodes)
@@ -263,7 +267,11 @@
if (!m_rootNode)
return;
+ LOG(Scrolling, "\nScrollingTree %p applyLayerPositions", this);
+
applyLayerPositionsRecursive(*m_rootNode, { }, { });
+
+ LOG(Scrolling, "ScrollingTree %p applyLayerPositions - done\n", this);
}
void ScrollingTree::applyLayerPositionsRecursive(ScrollingTreeNode& currNode, FloatRect layoutViewport, FloatSize cumulativeDelta)
@@ -291,6 +299,8 @@
void ScrollingTree::notifyRelatedNodesAfterScrollPositionChange(ScrollingTreeScrollingNode& changedNode)
{
+ Vector<ScrollingNodeID> additionalUpdateRoots;
+
FloatSize deltaFromLastCommittedScrollPosition;
FloatRect currentFrameLayoutViewport;
if (is<ScrollingTreeFrameScrollingNode>(changedNode))
@@ -300,9 +310,17 @@
if (auto* frameScrollingNode = changedNode.enclosingFrameNodeIncludingSelf())
currentFrameLayoutViewport = frameScrollingNode->layoutViewport();
+
+ additionalUpdateRoots = overflowRelatedNodes().get(changedNode.scrollingNodeID());
}
notifyRelatedNodesRecursive(changedNode, changedNode, currentFrameLayoutViewport, deltaFromLastCommittedScrollPosition);
+
+ for (auto positionedNodeID : additionalUpdateRoots) {
+ auto* positionedNode = nodeForID(positionedNodeID);
+ if (positionedNode)
+ notifyRelatedNodesRecursive(changedNode, *positionedNode, currentFrameLayoutViewport, deltaFromLastCommittedScrollPosition);
+ }
}
void ScrollingTree::notifyRelatedNodesRecursive(ScrollingTreeScrollingNode& changedNode, ScrollingTreeNode& currNode, const FloatRect& layoutViewport, FloatSize cumulativeDelta)
@@ -451,26 +469,37 @@
m_treeState.latchedNodeID = 0;
}
-String ScrollingTree::scrollingTreeAsText()
+String ScrollingTree::scrollingTreeAsText(ScrollingStateTreeAsTextBehavior behavior)
{
TextStream ts(TextStream::LineMode::MultipleLine);
- TextStream::GroupScope scope(ts);
- ts << "scrolling tree";
+ {
+ TextStream::GroupScope scope(ts);
+ ts << "scrolling tree";
- LockHolder locker(m_treeStateMutex);
+ LockHolder locker(m_treeStateMutex);
- if (m_treeState.latchedNodeID)
- ts.dumpProperty("latched node", m_treeState.latchedNodeID);
+ if (m_treeState.latchedNodeID)
+ ts.dumpProperty("latched node", m_treeState.latchedNodeID);
- if (!m_treeState.mainFrameScrollPosition.isZero())
- ts.dumpProperty("main frame scroll position", m_treeState.mainFrameScrollPosition);
-
- if (m_rootNode) {
- TextStream::GroupScope scope(ts);
- m_rootNode->dump(ts, ScrollingStateTreeAsTextBehaviorIncludeLayerPositions);
+ if (!m_treeState.mainFrameScrollPosition.isZero())
+ ts.dumpProperty("main frame scroll position", m_treeState.mainFrameScrollPosition);
+
+ if (m_rootNode) {
+ TextStream::GroupScope scope(ts);
+ m_rootNode->dump(ts, behavior | ScrollingStateTreeAsTextBehaviorIncludeLayerPositions);
+ }
+
+ if (behavior & ScrollingStateTreeAsTextBehaviorIncludeNodeIDs && !m_overflowRelatedNodesMap.isEmpty()) {
+ TextStream::GroupScope scope(ts);
+ ts << "overflow related nodes";
+ {
+ TextStream::IndentScope indentScope(ts);
+ for (auto& it : m_overflowRelatedNodesMap)
+ ts << "\n" << indent << it.key << " -> " << it.value;
+ }
+ }
}
-
return ts.release();
}
Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.h (242996 => 242997)
--- trunk/Source/WebCore/page/scrolling/ScrollingTree.h 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.h 2019-03-15 16:26:09 UTC (rev 242997)
@@ -144,9 +144,14 @@
ASSERT(m_fixedOrStickyNodeCount);
--m_fixedOrStickyNodeCount;
}
+
+ // A map of overflow scrolling nodes to positioned nodes which need to be updated
+ // when the scroller changes, but are not descendants.
+ using RelatedNodesMap = HashMap<ScrollingNodeID, Vector<ScrollingNodeID>>;
+ RelatedNodesMap& overflowRelatedNodes() { return m_overflowRelatedNodesMap; }
+
+ WEBCORE_EXPORT String scrollingTreeAsText(ScrollingStateTreeAsTextBehavior = ScrollingStateTreeAsTextBehaviorNormal);
- WEBCORE_EXPORT String scrollingTreeAsText();
-
protected:
void setMainFrameScrollPosition(FloatPoint);
@@ -167,6 +172,8 @@
using ScrollingTreeNodeMap = HashMap<ScrollingNodeID, ScrollingTreeNode*>;
ScrollingTreeNodeMap m_nodeMap;
+ RelatedNodesMap m_overflowRelatedNodesMap;
+
struct TreeState {
ScrollingNodeID latchedNodeID { 0 };
EventTrackingRegions eventTrackingRegions;
Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.cpp (242996 => 242997)
--- trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.cpp 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.cpp 2019-03-15 16:26:09 UTC (rev 242997)
@@ -40,6 +40,12 @@
ScrollingTreeOverflowScrollingNode::~ScrollingTreeOverflowScrollingNode() = default;
+void ScrollingTreeOverflowScrollingNode::dumpProperties(TextStream& ts, ScrollingStateTreeAsTextBehavior behavior) const
+{
+ ts << "overflow scrolling node";
+ ScrollingTreeNode::dumpProperties(ts, behavior);
+}
+
} // namespace WebCore
#endif // ENABLE(ASYNC_SCROLLING)
Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.h (242996 => 242997)
--- trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.h 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.h 2019-03-15 16:26:09 UTC (rev 242997)
@@ -37,6 +37,8 @@
protected:
WEBCORE_EXPORT ScrollingTreeOverflowScrollingNode(ScrollingTree&, ScrollingNodeID);
+
+ WEBCORE_EXPORT void dumpProperties(TextStream&, ScrollingStateTreeAsTextBehavior) const;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.h (242996 => 242997)
--- trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.h 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.h 2019-03-15 16:26:09 UTC (rev 242997)
@@ -49,7 +49,7 @@
void applyLayerPositions(const FloatRect& layoutViewport, FloatSize& cumulativeDelta) override;
- void dumpProperties(WTF::TextStream&, ScrollingStateTreeAsTextBehavior) const override;
+ WEBCORE_EXPORT void dumpProperties(WTF::TextStream&, ScrollingStateTreeAsTextBehavior) const override;
Vector<ScrollingNodeID> m_relatedOverflowScrollingNodes;
LayoutConstraints m_constraints;
Modified: trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.mm (242996 => 242997)
--- trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.mm 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.mm 2019-03-15 16:26:09 UTC (rev 242997)
@@ -31,6 +31,7 @@
#import "Logging.h"
#import "ScrollingStatePositionedNode.h"
#import "ScrollingTree.h"
+#import "ScrollingTreeOverflowScrollingNode.h"
#import "ScrollingTreeScrollingNode.h"
#import <QuartzCore/CALayer.h>
#import <wtf/text/TextStream.h>
@@ -61,30 +62,51 @@
if (positionedStateNode.hasChangedProperty(ScrollingStatePositionedNode::LayoutConstraintData))
m_constraints = positionedStateNode.layoutConstraints();
+
+ // Tell the ScrollingTree about non-ancestor overflow nodes which affect this node.
+ if (m_constraints.scrollPositioningBehavior() == ScrollPositioningBehavior::Moves) {
+ auto& relatedNodes = scrollingTree().overflowRelatedNodes();
+ for (auto overflowNodeID : m_relatedOverflowScrollingNodes) {
+ relatedNodes.ensure(overflowNodeID, [] {
+ return Vector<ScrollingNodeID>();
+ }).iterator->value.append(scrollingNodeID());
+ }
+ }
}
void ScrollingTreePositionedNode::applyLayerPositions(const FloatRect&, FloatSize& cumulativeDelta)
{
- FloatSize layerOffset; // FIXME: layerOffset needs to be computed by looking at scrolling tree deltas
- // in the overflow nodes that affect this node. Some of that may come in via cumulativeDelta.
+ // Note that we ignore cumulativeDelta because it will contain the delta for ancestor scrollers,
+ // but not non-ancestor ones, so it's simpler to just recompute from the scrollers we know about here.
+ FloatSize scrollOffsetSinceLastCommit;
+ for (auto nodeID : m_relatedOverflowScrollingNodes) {
+ if (auto* node = scrollingTree().nodeForID(nodeID)) {
+ if (is<ScrollingTreeOverflowScrollingNode>(node)) {
+ auto& overflowNode = downcast<ScrollingTreeOverflowScrollingNode>(*node);
+ scrollOffsetSinceLastCommit += overflowNode.lastCommittedScrollPosition() - overflowNode.currentScrollPosition();
+ }
+ }
+ }
+ LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreePositionedNode " << scrollingNodeID() << " applyLayerPositions: overflow delta " << scrollOffsetSinceLastCommit);
- LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreePositionedNode " << scrollingNodeID() << " applyLayerPositions: total overflow delta " << layerOffset);
-
- layerOffset += cumulativeDelta;
- // Stationary nodes move in the opposite direction.
- if (m_constraints.scrollPositioningBehavior() == ScrollPositioningBehavior::Stationary)
+ auto layerOffset = -scrollOffsetSinceLastCommit;
+ if (m_constraints.scrollPositioningBehavior() == ScrollPositioningBehavior::Stationary) {
+ // Stationary nodes move in the opposite direction.
layerOffset = -layerOffset;
+ }
FloatPoint layerPosition = m_constraints.layerPositionAtLastLayout() - layerOffset;
[m_layer _web_setLayerTopLeftPosition:layerPosition - m_constraints.alignmentOffset()];
- cumulativeDelta += layerPosition - m_constraints.layerPositionAtLastLayout();
+ // FIXME: Should our scroller deltas propagate to descendants?
+ cumulativeDelta = layerPosition - m_constraints.layerPositionAtLastLayout();
}
void ScrollingTreePositionedNode::relatedNodeScrollPositionDidChange(const ScrollingTreeScrollingNode& changedNode, const FloatRect& layoutViewport, FloatSize& cumulativeDelta)
{
- UNUSED_PARAM(changedNode);
- // FIXME: This will avoid doing work if we can determine that changedNode doesn't affect this positioned node.
+ if (!m_relatedOverflowScrollingNodes.contains(changedNode.scrollingNodeID()))
+ return;
+
applyLayerPositions(layoutViewport, cumulativeDelta);
}
Modified: trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp (242996 => 242997)
--- trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp 2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp 2019-03-15 16:26:09 UTC (rev 242997)
@@ -2869,17 +2869,117 @@
return false;
}
+// Is this layer's containingBlock an ancestor of scrollable overflow, and is the layer's compositing ancestor inside that overflow?
+static bool layerContainingBlockCrossesCoordinatedScrollingBoundary(const RenderLayer& layer, const RenderLayer& compositedAncestor)
+{
+ ASSERT(layer.isComposited());
+ ASSERT(layer.renderer().style().position() == PositionType::Absolute);
+
+ bool sawCompositingAncestor = false;
+ for (const auto* currLayer = layer.parent(); currLayer; currLayer = currLayer->parent()) {
+ if (currLayer->renderer().canContainAbsolutelyPositionedObjects())
+ return false;
+
+ if (currLayer == &compositedAncestor)
+ sawCompositingAncestor = true;
+
+ if (currLayer->hasCompositedScrollableOverflow())
+ return sawCompositingAncestor;
+ }
+
+ return false;
+}
+
+// Is there scrollable overflow between this layer and its composited ancestor?
+static bool layerParentedAcrossCoordinatedScrollingBoundary(const RenderLayer& layer, const RenderLayer& compositedAncestor)
+{
+ ASSERT(layer.isComposited());
+
+ for (const auto* currLayer = layer.parent(); currLayer != &compositedAncestor; currLayer = currLayer->parent()) {
+ if (currLayer->hasCompositedScrollableOverflow())
+ return true;
+ }
+
+ return false;
+}
+
ScrollPositioningBehavior RenderLayerCompositor::computeCoordinatedPositioningForLayer(const RenderLayer& layer) const
{
if (layer.isRenderViewLayer())
return ScrollPositioningBehavior::None;
- // FIXME: This will look at the containing block and stacking context ancestor chains and determine
- // whether this layer needs to be repositioned when a composited overflow scroll scrolls.
+ auto* scrollingCoordinator = this->scrollingCoordinator();
+ if (!scrollingCoordinator)
+ return ScrollPositioningBehavior::None;
+ // There are two cases we have to deal with here:
+ // 1. There's a composited overflow:scroll in the parent chain between the renderer and its containing block, and the layer's
+ // composited (z-order) ancestor is inside the scroller or is the scroller. In this case, we have to compensate for scroll position
+ // changes to make the positioned layer stay in the same place. This only applies to position:absolute (since we handle fixed elsewhere).
+ auto* compositedAncestor = layer.ancestorCompositingLayer();
+
+ auto& renderer = layer.renderer();
+ if (renderer.isOutOfFlowPositioned() && renderer.style().position() == PositionType::Absolute) {
+ if (layerContainingBlockCrossesCoordinatedScrollingBoundary(layer, *compositedAncestor))
+ return ScrollPositioningBehavior::Stationary;
+
+ return ScrollPositioningBehavior::None;
+ }
+
+ // 2. The layer's containing block is the overflow or inside the overflow:scroll, but its z-order ancestor is
+ // outside the overflow:scroll. In that case, we have to move the layer via the scrolling tree to make
+ // it move along with the overflow scrolling.
+ if (layerParentedAcrossCoordinatedScrollingBoundary(layer, *compositedAncestor))
+ return ScrollPositioningBehavior::Moves;
+
return ScrollPositioningBehavior::None;
}
+static Vector<ScrollingNodeID> collectRelatedCoordinatedScrollingNodes(const RenderLayer& layer, ScrollPositioningBehavior positioningBehavior)
+{
+ Vector<ScrollingNodeID> overflowNodeData;
+
+ switch (positioningBehavior) {
+ case ScrollPositioningBehavior::Moves: {
+ // Collect all the composited scrollers between this layer and its composited ancestor.
+ auto* compositedAncestor = layer.ancestorCompositingLayer();
+ for (const auto* currLayer = layer.parent(); currLayer != compositedAncestor; currLayer = currLayer->parent()) {
+ if (currLayer->hasCompositedScrollableOverflow()) {
+ auto scrollingNodeID = currLayer->backing()->scrollingNodeIDForRole(ScrollCoordinationRole::Scrolling);
+ if (scrollingNodeID)
+ overflowNodeData.append(scrollingNodeID);
+ else
+ LOG(Scrolling, "Layer %p doesn't have scrolling node ID yet", &layer);
+ }
+ }
+ break;
+ }
+ case ScrollPositioningBehavior::Stationary: {
+ // Collect all the composited scrollers between this layer and its containing block.
+ ASSERT(layer.renderer().style().position() == PositionType::Absolute);
+ for (const auto* currLayer = layer.parent(); currLayer; currLayer = currLayer->parent()) {
+ if (currLayer->renderer().canContainAbsolutelyPositionedObjects())
+ break;
+
+ if (currLayer->hasCompositedScrollableOverflow()) {
+ auto scrollingNodeID = currLayer->backing()->scrollingNodeIDForRole(ScrollCoordinationRole::Scrolling);
+ if (scrollingNodeID)
+ overflowNodeData.append(scrollingNodeID);
+ else
+ LOG(Scrolling, "Layer %p doesn't have scrolling node ID yet", &layer);
+ }
+ }
+ // Don't need to do anything because the layer is a descendant of the overflow in stacking.
+ break;
+ }
+ case ScrollPositioningBehavior::None:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ return overflowNodeData;
+}
+
bool RenderLayerCompositor::isLayerForIFrameWithScrollCoordinatedContents(const RenderLayer& layer) const
{
if (!is<RenderWidget>(layer.renderer()))
@@ -4078,7 +4178,9 @@
}
if (changes & ScrollingNodeChangeFlags::LayerGeometry && treeState.parentNodeID) {
- Vector<ScrollingNodeID> relatedNodeIDs; // FIXME: This will do a tree walk to figure out which composited overflows affect this positioned node.
+ // Would be nice to avoid calling computeCoordinatedPositioningForLayer() again.
+ auto positioningBehavior = computeCoordinatedPositioningForLayer(layer);
+ auto relatedNodeIDs = collectRelatedCoordinatedScrollingNodes(layer, positioningBehavior);
scrollingCoordinator->setRelatedOverflowScrollingNodes(newNodeID, WTFMove(relatedNodeIDs));
auto* graphicsLayer = layer.backing()->graphicsLayer();
@@ -4085,8 +4187,7 @@
LayoutConstraints constraints;
constraints.setAlignmentOffset(graphicsLayer->pixelAlignmentOffset());
constraints.setLayerPositionAtLastLayout(graphicsLayer->position());
- // Would be nice to avoid calling computeCoordinatedPositioningForLayer() again.
- constraints.setScrollPositioningBehavior(computeCoordinatedPositioningForLayer(layer));
+ constraints.setScrollPositioningBehavior(positioningBehavior);
scrollingCoordinator->setPositionedNodeGeometry(newNodeID, constraints);
}