Skip to content
Permalink
Browse files
Elements with position: sticky jitter inside a container with `over…
…flow-x: clip;`

https://bugs.webkit.org/show_bug.cgi?id=247130
rdar://101645570

Reviewed by Alan Baradlay.

`overflow:clip` on just one axis can output a clip rect which is infinite in just
x or y. In that case, we'd create a clipping layer with large floating point
geometry values which, when moved around by the scrolling tree, had low precision
resulting in visible stutters.

Fix by shrinking the infinite rect values by a factor of 16, which is sufficient
to provide enough device pixel precision for 3x displays.

* LayoutTests/compositing/clipping/sticky-inside-overflow-x-clip-expected.txt: Added.
* LayoutTests/compositing/clipping/sticky-inside-overflow-x-clip.html: Added.
* LayoutTests/platform/gtk/compositing/clipping/sticky-inside-overflow-x-clip-expected.txt: Added.
* LayoutTests/platform/ios-wk2/compositing/clipping/sticky-inside-overflow-x-clip-expected.txt: Added.
* LayoutTests/platform/mac-wk1/compositing/clipping/sticky-inside-overflow-x-clip-expected.txt: Added.
* Source/WebCore/platform/graphics/LayoutRect.h:
* Source/WebCore/rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::computeAncestorClippingStack const):

Canonical link: https://commits.webkit.org/256618@main
  • Loading branch information
smfr committed Nov 13, 2022
1 parent 5a9504a commit 854716970c0750581f75ee43bb91578d9ac52451
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 1 deletion.
@@ -0,0 +1,30 @@
(GraphicsLayer
(anchor 0.00 0.00)
(bounds 785.00 2202.00)
(children 1
(GraphicsLayer
(bounds 785.00 2202.00)
(contentsOpaque 1)
(children 1
(GraphicsLayer
(position 109.00 -1048576.00)
(bounds 600.00 2097152.00)
(clips 1)
(children 1
(GraphicsLayer
(position 0.00 1048677.00)
(preserves3D 1)
(children 1
(GraphicsLayer
(bounds 600.00 100.00)
(contentsOpaque 1)
)
)
)
)
)
)
)
)
)

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<style>
.container {
position: relative;
margin: 100px;
width: 600px;
min-height: 2000px;
border: 1px solid black;
overflow-x: clip;
}

.sticky {
position: sticky;
top: 20px;
left: 0;
width: 100%;
background: blue;
height: 100px;
}
</style>
<script>
if (window.testRunner)
testRunner.dumpAsText();

window.addEventListener('load', () => {
if (window.testRunner)
document.getElementById('layers').innerText = window.internals.layerTreeAsText(document, internals.LAYER_TREE_INCLUDES_CLIPPING);
}, false);
</script>
</head>
<body>
<div class="container">
<div class="sticky"></div>
</div>
<pre id="layers"></pre>
</body>
</html>
@@ -0,0 +1 @@

@@ -0,0 +1,30 @@
(GraphicsLayer
(anchor 0.00 0.00)
(bounds 800.00 2202.00)
(children 1
(GraphicsLayer
(bounds 800.00 2202.00)
(contentsOpaque 1)
(children 1
(GraphicsLayer
(position 109.00 -1048576.00)
(bounds 600.00 2097152.00)
(clips 1)
(children 1
(GraphicsLayer
(position 0.00 1048677.00)
(preserves3D 1)
(children 1
(GraphicsLayer
(bounds 600.00 100.00)
(contentsOpaque 1)
)
)
)
)
)
)
)
)
)

@@ -0,0 +1 @@

@@ -199,7 +199,7 @@ class LayoutRect {
// Return a rect that is slightly smaller than the true max rect to allow pixelSnapping to round up to the nearest IntRect without overflowing.
return LayoutRect(LayoutUnit::nearlyMin() / 2, LayoutUnit::nearlyMin() / 2, LayoutUnit::nearlyMax(), LayoutUnit::nearlyMax());
}

operator FloatRect() const { return FloatRect(m_location, m_size); }

private:
@@ -2954,6 +2954,23 @@ Vector<CompositedClipData> RenderLayerCompositor::computeAncestorClippingStack(c
auto clipRect = backgroundClip.rect();
if (clipRect.isInfinite())
return;

auto infiniteRect = LayoutRect::infiniteRect();
auto renderableInfiniteRect = [] {
// Return a infinite-like rect whose values are such that, when converted to float pixel values, they can reasonably represent device pixels.
return LayoutRect(LayoutUnit::nearlyMin() / 32, LayoutUnit::nearlyMin() / 32, LayoutUnit::nearlyMax() / 16, LayoutUnit::nearlyMax() / 16);
}();

if (clipRect.width() == infiniteRect.width()) {
clipRect.setX(renderableInfiniteRect.x());
clipRect.setWidth(renderableInfiniteRect.width());
}

if (clipRect.height() == infiniteRect.height()) {
clipRect.setY(renderableInfiniteRect.y());
clipRect.setHeight(renderableInfiniteRect.height());
}

auto offset = layer.convertToLayerCoords(&clippingRoot, { }, RenderLayer::AdjustForColumns);
clipRect.moveBy(-offset);

0 comments on commit 8547169

Please sign in to comment.