Skip to content
Permalink
Browse files
border-radius clipping of composited layers doesn't work
https://bugs.webkit.org/show_bug.cgi?id=68196
<rdar://10133719>

Reviewed by Alan Bujtas.

In CSS it's possible for clipping (via overflow:scroll, overflow:hidden and the `clip`
property) to apply to elements that are descendants in containing-block order, but
siblings in z-order, for example:
    <div style="position: relative; overflow: hidden">
      <div style="position: absolute"></div
    </div>
If a non-z-order descendant here has a composited layer, we'd fail to take border-radius
into account when clipping it.

In this layer configuration we fall into the "AncestorClippingStack" code path: the
descendant layer owns a stack of clips that represent clipping by non-paint-order
ancestors. Previously, all overflow:hidden clips were simply intersected and represented
as a single rectangular clipping GraphicsLayer, but when border-radius is present on
some or all of them, we need to maintain a layer for each, since you can't trivially
generate a path that is the intersection of rounded rects. Code was added to
RenderLayerCompositor::computeAncestorClippingStack() to do this.

When an intermediate clip with border radius represents a scrollable elements (i.e.
scrollable overflow), its corresponding entry in the ancestor clipping stack needs two
layers; one to apply the clipping, which doesn't scroll with the content, and one that
applies the scroll offset, so ClippingStackEntry gains a scrollingLayer, which is the
one handed off to the scrolling tree, and the rounded clip is applied to the clipping
layer. Logic is added to build the hierarchy of these layers.

When style changes we have to ensure that layers are updated with the new rounded rects;
notably, style can change on a non-composited RenderLayer (e.g. an overflow:hidden layer
with border-radius) which requires that non-z-order descendants need to update their
composited layer geometry; new code in RenderLayerCompositor::layerStyleChanged() takes
care of this. Tested by
compositing/clipping/border-radius-with-composited-descendant-dynamic.html

* LayoutTests/compositing/clipping/border-radius-async-overflow-non-stacking.html:
* LayoutTests/compositing/clipping/border-radius-with-composited-descendant-dynamic-expected.html: Added.
* LayoutTests/compositing/clipping/border-radius-with-composited-descendant-dynamic.html: Added.
* LayoutTests/compositing/clipping/border-radius-with-composited-descendant-expected.html: Added.
* LayoutTests/compositing/clipping/border-radius-with-composited-descendant-nested-expected.html: Added.
* LayoutTests/compositing/clipping/border-radius-with-composited-descendant-nested.html: Added.
* LayoutTests/compositing/clipping/border-radius-with-composited-descendant.html: Added.
* LayoutTests/compositing/layer-creation/clipping-scope/nested-scroller-overlap-expected.txt:
* LayoutTests/compositing/layer-creation/clipping-scope/overlap-constrained-inside-scroller-expected.txt:
* LayoutTests/compositing/layer-creation/clipping-scope/scroller-with-negative-z-children-expected.txt:
* LayoutTests/compositing/overflow/scrolling-content-clip-to-viewport-expected.txt:
* LayoutTests/compositing/rtl/rtl-scrolling-with-transformed-descendants-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/clipped-layer-in-overflow-clipped-by-scroll-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/clipped-layer-in-overflow-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/clipped-layer-in-overflow-nested-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/layer-for-negative-z-in-scroller-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-clip-to-hidden-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-clip-to-visible-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-gain-clipping-layer-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-in-clipped-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-lose-clipping-layer-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/nested-scrollers-backing-attachment-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/overlapped-overlay-scrollbar-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/overlapped-overlay-scrollbar-inside-hidden-expected.txt:
* LayoutTests/compositing/scrolling/async-overflow-scrolling/overlapped-overlay-scrollbar-nested-expected.txt:
* LayoutTests/compositing/shared-backing/overflow-scroll/composited-absolute-in-absolute-in-relative-in-scroller-expected.txt:
* LayoutTests/compositing/shared-backing/overflow-scroll/previous-sibling-prevents-inclusiveness-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/layer-creation/clipping-scope/nested-scroller-overlap-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/layer-creation/clipping-scope/overlap-constrained-inside-scroller-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/layer-creation/clipping-scope/scroller-with-negative-z-children-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/clipped-layer-in-overflow-clipped-by-scroll-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/clipped-layer-in-overflow-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/clipped-layer-in-overflow-nested-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/layer-for-negative-z-in-scroller-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-clip-to-hidden-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-clip-to-visible-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-gain-clipping-layer-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-in-clipped-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/layer-in-overflow-lose-clipping-layer-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/scrolling/async-overflow-scrolling/nested-scrollers-backing-attachment-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/shared-backing/overflow-scroll/composited-absolute-in-absolute-in-relative-in-scroller-expected.txt:
* LayoutTests/platform/ios-wk2/compositing/shared-backing/overflow-scroll/previous-sibling-prevents-inclusiveness-expected.txt:
* Source/WebCore/platform/graphics/RoundedRect.cpp:
(WebCore::operator<<):
* Source/WebCore/platform/graphics/RoundedRect.h:
* Source/WebCore/rendering/LayerAncestorClippingStack.cpp:
(WebCore::LayerAncestorClippingStack::LayerAncestorClippingStack):
(WebCore::LayerAncestorClippingStack::firstLayer const):
(WebCore::LayerAncestorClippingStack::lastLayer const):
(WebCore::LayerAncestorClippingStack::updateScrollingNodeLayers):
(WebCore::LayerAncestorClippingStack::updateWithClipData):
(WebCore::operator<<):
(WebCore::LayerAncestorClippingStack::firstClippingLayer const): Deleted.
(WebCore::LayerAncestorClippingStack::lastClippingLayer const): Deleted.
* Source/WebCore/rendering/LayerAncestorClippingStack.h:
(WebCore::CompositedClipData::CompositedClipData):
(WebCore::LayerAncestorClippingStack::ClippingStackEntry::parentForSublayers const):
(WebCore::LayerAncestorClippingStack::ClippingStackEntry::childForSuperlayers const):
* Source/WebCore/rendering/RenderLayer.h:
* Source/WebCore/rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::updateInternalHierarchy):
(WebCore::RenderLayerBacking::ensureClippingStackLayers):
(WebCore::RenderLayerBacking::removeClippingStackLayers):
(WebCore::RenderLayerBacking::connectClippingStackLayers):
(WebCore::RenderLayerBacking::updateClippingStackLayerGeometry):
(WebCore::RenderLayerBacking::childForSuperlayers const):
* Source/WebCore/rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::adjustOverflowScrollbarContainerLayers):
(WebCore::RenderLayerCompositor::layerStyleChanged):
(WebCore::RenderLayerCompositor::computeAncestorClippingStack const):
(WebCore::RenderLayerCompositor::parentRelativeScrollableRect const):
(WebCore::RenderLayerCompositor::updateScrollingNodeForScrollingProxyRole):
* Source/WebCore/rendering/RenderLayerCompositor.h:

Canonical link: https://commits.webkit.org/254253@main
  • Loading branch information
smfr committed Sep 7, 2022
1 parent c4b49d2 commit 97910542b25d8ccbabb97dc9f230461fd4a592e2
Show file tree
Hide file tree
Showing 52 changed files with 1,072 additions and 436 deletions.
@@ -1,7 +1,7 @@
<!DOCTYPE html> <!-- webkit-test-runner [ AsyncOverflowScrollingEnabled=true ] -->
<html>
<head>
<meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-140" />
<meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-180" />
<style>
.scroller {
margin: 10px;
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<style>
.clipping {
margin: 10px;
width: 300px;
height: 200px;
overflow: hidden;
border: 30px solid green;
border-top-right-radius: 150px 100px;
border-bottom-left-radius: 150px 100px;
}

.composited {
transform: translateZ(0);
width: 100%;
height: 100%;
background-color: blue;
}
</style>
</head>
<body>
<div class="clipping">
<div class="composited"></div>
</div>
</body>
</html>
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<style>
.clipping {
margin: 10px;
width: 300px;
height: 200px;
overflow: hidden;
border: 30px solid green;
}

.composited {
transform: translateZ(0);
width: 100%;
height: 100%;
background-color: blue;
}

body.changed .clipping {
border-top-right-radius: 150px 100px;
border-bottom-left-radius: 150px 100px;
}
</style>
<script>
if (window.testRunner)
testRunner.waitUntilDone();

window.addEventListener('load', () => {
setTimeout(() => {
document.body.classList.add('changed');
setTimeout(() => {
if (window.testRunner)
testRunner.notifyDone();
}, 0);
}, 0);
}, false);
</script>
</head>
<body>
<div class="clipping">
<div class="composited"></div>
</div>
</body>
</html>
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<style>
.clipping {
margin: 10px;
width: 300px;
height: 200px;
overflow: hidden;
border: 30px solid green;
border-top-right-radius: 150px 100px;
border-bottom-left-radius: 150px 100px;
}

.composited {
width: 100%;
height: 100%;
background-color: blue;
}
</style>
</head>
<body>
<div class="clipping">
<div class="composited"></div>
</div>
</body>
</html>
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<style>
.container {
width: 400px;
height: 300px;
border: 20px solid black;
border-radius: 120px 50px;
overflow: hidden;
}

.intermediate {
background-color: rgba(0, 0, 0, 0.4);
width: 100%;
height: 100%;
margin: 50px 0 0 50px;;
border-radius: 100px;
border: 30px solid green;
overflow: hidden;
}

.child {
height: 100%;
width: 100%;
background-color: blue;
}
</style>
</head>
<body>
<div class="container">
<div class="intermediate">
<div class="child"></div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<head>
<meta name="fuzzy" content="maxDifference=0-62; totalPixels=0-360" />
<style>
.container {
width: 400px;
height: 300px;
border: 20px solid black;
border-radius: 120px 50px;
overflow: hidden;
}

.intermediate {
background-color: rgba(0, 0, 0, 0.4);
width: 100%;
height: 100%;
margin: 50px 0 0 50px;;
border-radius: 100px;
border: 30px solid green;
overflow: hidden;
}

.child {
height: 100%;
width: 100%;
background-color: blue;
}

.composited {
transform: translateZ(0);
}
</style>
</head>
<body>
<div class="container">
<div class="intermediate">
<div class="composited child"></div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<meta name="fuzzy" content="maxDifference=0-42; totalPixels=0-380" />
<style>
.clipping {
margin: 10px;
width: 300px;
height: 200px;
overflow: hidden;
border: 30px solid green;
border-top-right-radius: 150px 100px;
border-bottom-left-radius: 150px 100px;
}

.composited {
transform: translateZ(0);
width: 100%;
height: 100%;
background-color: blue;
}
</style>
</head>
<body>
<div class="clipping">
<div class="composited"></div>
</div>
</body>
</html>
@@ -68,9 +68,13 @@
(clips 1)
(children 1
(GraphicsLayer
(position 10.00 10.00)
(bounds 100.00 80.00)
(contentsOpaque 1)
(children 1
(GraphicsLayer
(position 10.00 10.00)
(bounds 100.00 80.00)
(contentsOpaque 1)
)
)
)
)
)
@@ -84,14 +88,22 @@
(clips 1)
(children 1
(GraphicsLayer
(position 21.00 111.00)
(bounds 285.00 200.00)
(clips 1)
(children 1
(GraphicsLayer
(position 10.00 10.00)
(bounds 100.00 80.00)
(contentsOpaque 1)
(position 21.00 111.00)
(bounds 285.00 200.00)
(clips 1)
(children 1
(GraphicsLayer
(children 1
(GraphicsLayer
(position 10.00 10.00)
(bounds 100.00 80.00)
(contentsOpaque 1)
)
)
)
)
)
)
)
@@ -103,14 +115,22 @@
(clips 1)
(children 1
(GraphicsLayer
(position 21.00 111.00)
(bounds 285.00 200.00)
(clips 1)
(children 1
(GraphicsLayer
(position 10.00 100.00)
(bounds 100.00 80.00)
(contentsOpaque 1)
(position 21.00 111.00)
(bounds 285.00 200.00)
(clips 1)
(children 1
(GraphicsLayer
(children 1
(GraphicsLayer
(position 10.00 100.00)
(bounds 100.00 80.00)
(contentsOpaque 1)
)
)
)
)
)
)
)
@@ -122,14 +142,22 @@
(clips 1)
(children 1
(GraphicsLayer
(position 21.00 111.00)
(bounds 285.00 200.00)
(clips 1)
(children 1
(GraphicsLayer
(position 10.00 190.00)
(bounds 100.00 80.00)
(contentsOpaque 1)
(position 21.00 111.00)
(bounds 285.00 200.00)
(clips 1)
(children 1
(GraphicsLayer
(children 1
(GraphicsLayer
(position 10.00 190.00)
(bounds 100.00 80.00)
(contentsOpaque 1)
)
)
)
)
)
)
)

0 comments on commit 9791054

Please sign in to comment.