LibWeb: Add paint containment clipping

The contain-paint-stacking-context-001a.html test has been removed
for now because it has a 1px tall blue line at the top that should
not be there. With paint containment, this line is removed only in
the actual test case, but not in the reference. This is because of
the font that we use in testing and happens in Chromium as well if
the test is run with that font.
This commit is contained in:
Psychpsyo 2025-05-03 11:46:39 +02:00 committed by Alexander Kalenik
commit 4989c5c793
Notes: github-actions[bot] 2025-05-13 12:32:31 +00:00
4 changed files with 84 additions and 67 deletions

View file

@ -129,7 +129,8 @@ void ViewportPaintable::assign_clip_frames()
auto overflow_y = paintable_box.computed_values().overflow_y(); auto overflow_y = paintable_box.computed_values().overflow_y();
// Note: Overflow may be clip on one axis and visible on the other. // Note: Overflow may be clip on one axis and visible on the other.
auto has_hidden_overflow = overflow_x != CSS::Overflow::Visible || overflow_y != CSS::Overflow::Visible; auto has_hidden_overflow = overflow_x != CSS::Overflow::Visible || overflow_y != CSS::Overflow::Visible;
if (has_hidden_overflow || paintable_box.get_clip_rect().has_value()) { auto element = as_if<DOM::Element>(paintable_box.layout_node().dom_node());
if (has_hidden_overflow || paintable_box.get_clip_rect().has_value() || (element && element->has_paint_containment())) {
auto clip_frame = adopt_ref(*new ClipFrame()); auto clip_frame = adopt_ref(*new ClipFrame());
clip_state.set(paintable_box, move(clip_frame)); clip_state.set(paintable_box, move(clip_frame));
} }
@ -177,6 +178,23 @@ void ViewportPaintable::assign_clip_frames()
clip_y = true; clip_y = true;
} }
// https://drafts.csswg.org/css-contain-2/#paint-containment
// 1. The contents of the element including any ink or scrollable overflow must be clipped to the overflow clip
// edge of the paint containment box, taking corner clipping into account. This does not include the creation of
// any mechanism to access or indicate the presence of the clipped content; nor does it inhibit the creation of
// any such mechanism through other properties, such as overflow, resize, or text-overflow.
// NOTE: This clipping shape respects overflow-clip-margin, allowing an element with paint containment
// to still slightly overflow its normal bounds.
if (auto element = as_if<DOM::Element>(block->dom_node())) {
if (element->has_paint_containment()) {
// NOTE: Note: The behavior is described in this paragraph is equivalent to changing 'overflow-x: visible' into
// 'overflow-x: clip' and 'overflow-y: visible' into 'overflow-y: clip' at used value time, while leaving other
// values of 'overflow-x' and 'overflow-y' unchanged.
clip_x = true;
clip_y = true;
}
}
if (clip_x || clip_y) { if (clip_x || clip_y) {
// https://drafts.csswg.org/css-overflow-3/#corner-clipping // https://drafts.csswg.org/css-overflow-3/#corner-clipping
// As mentioned in CSS Backgrounds 3 §4.3 Corner Clipping, the clipping region established by overflow can be // As mentioned in CSS Backgrounds 3 §4.3 Corner Clipping, the clipping region established by overflow can be

View file

@ -0,0 +1,26 @@
<!doctype html>
<html lang=en>
<meta charset=utf-8>
<title>CSS-contain test: paint containment use the padding edge</title>
<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net">
<style>
div {
width: 100px;
height: 100px;
background: blue;
padding: 50px;
border-radius: 100px;
overflow: hidden
}
div::before {
content:"";
display: block;
background: green;
width: 100px;
height: 100px;
}
</style>
<p>Test passes if there is a green square in a rounded blue box, and no red.
<div></div>

View file

@ -0,0 +1,39 @@
<!doctype html>
<html lang=en>
<meta charset=utf-8>
<title>CSS-contain test: paint containment use the padding edge</title>
<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net">
<meta name=flags content="">
<meta name=assert content="paint containment clips at the padding edge, not content edge, and takes corner clipping into account">
<link rel="match" href="../../../../expected/wpt-import/css/css-contain/reference/contain-paint-001-ref.html">
<link rel=help href="https://drafts.csswg.org/css-contain-1/#containment-paint">
<style>
div {
width: 100px;
height: 100px;
background: blue;
padding: 50px;
border-radius: 100px;
contain: paint;
}
div::before {
content:"";
display: block;
background: green;
width: 100px;
height: 100px;
}
div::after {
content:"";
display: block;
background: red;
width: 50px;
height: 50px;
margin-top: 38px;
margin-left: -50px;
}
</style>
<p>Test passes if there is a green square in a rounded blue box, and no red.
<div></div>

View file

@ -1,66 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>CSS Test: 'contain: paint' with stacking contents. Z-index is defined only for siblings and children.</title>
<link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#x43">
<link rel="help" href="https://drafts.csswg.org/css-contain/#containment-paint">
<link rel="match" href="../../../../expected/wpt-import/css/css-contain/contain-paint-stacking-context-001-ref.html">
<style>
div {
position: relative;
width: 100px;
}
#div1,
#div3 {
background-color: #cfc;
}
#div1 {
z-index: 5;
}
#div2 {
contain: paint;
background-color: #fdd;
height: 100px;
top: -20px;
}
#div2_1 {
background-color: #ffc;
z-index: 6;
top: -10px;
}
#div2_2 {
z-index: 3;
position: absolute;
top: -15px;
width: 40px;
height: 100px;
background-color: #ddf;
}
#div3 {
z-index: 2;
top: -50px;
}
</style>
</head>
<body>
<div id="div1">
<br/><br/>
</div>
<div id="div2">
<div id="div2_1">
<br/><br/>
</div>
<div id="div2_2">
</div>
</div>
<div id="div3">
<br/><br/>
</div>
</body>
</html>