LibWeb: Apply clip rect before painting background and foreground items

This commit is contained in:
Tim Ledbetter 2025-06-23 13:49:57 +01:00 committed by Sam Atkins
commit 212d748ded
Notes: github-actions[bot] 2025-06-24 11:58:05 +00:00
13 changed files with 217 additions and 12 deletions

View file

@ -79,6 +79,9 @@ public:
virtual void apply_scroll_offset(PaintContext&, PaintPhase) const { } virtual void apply_scroll_offset(PaintContext&, PaintPhase) const { }
virtual void reset_scroll_offset(PaintContext&, PaintPhase) const { } virtual void reset_scroll_offset(PaintContext&, PaintPhase) const { }
virtual void apply_own_clip_rect(PaintContext&, PaintPhase) const { }
virtual void clear_own_clip_rect(PaintContext&, PaintPhase) const { }
virtual void apply_clip_overflow_rect(PaintContext&, PaintPhase) const { } virtual void apply_clip_overflow_rect(PaintContext&, PaintPhase) const { }
virtual void clear_clip_overflow_rect(PaintContext&, PaintPhase) const { } virtual void clear_clip_overflow_rect(PaintContext&, PaintPhase) const { }

View file

@ -326,6 +326,8 @@ void PaintableBox::before_paint(PaintContext& context, [[maybe_unused]] PaintPha
if (!is_visible()) if (!is_visible())
return; return;
apply_own_clip_rect(context, phase);
if (!has_css_transform()) { if (!has_css_transform()) {
apply_clip_overflow_rect(context, phase); apply_clip_overflow_rect(context, phase);
} }
@ -337,6 +339,8 @@ void PaintableBox::after_paint(PaintContext& context, [[maybe_unused]] PaintPhas
if (!is_visible()) if (!is_visible())
return; return;
clear_own_clip_rect(context, phase);
reset_scroll_offset(context, phase); reset_scroll_offset(context, phase);
if (!has_css_transform()) { if (!has_css_transform()) {
clear_clip_overflow_rect(context, phase); clear_clip_overflow_rect(context, phase);
@ -651,6 +655,38 @@ void PaintableBox::clear_clip_overflow_rect(PaintContext& context, PaintPhase ph
restore_clip(context, enclosing_clip_frame()); restore_clip(context, enclosing_clip_frame());
} }
void PaintableBox::apply_own_clip_rect(PaintContext& context, PaintPhase phase) const
{
if (!own_clip_frame())
return;
// FIXME: This should apply to SVGs
if (is<SVGPaintable>(*this) || is<SVGSVGPaintable>(*this))
return;
// FIXME: This should also apply to borders and outlines.
if (!first_is_one_of(phase, PaintPhase::Background, PaintPhase::Foreground))
return;
apply_clip(context, own_clip_frame());
}
void PaintableBox::clear_own_clip_rect(PaintContext& context, PaintPhase phase) const
{
if (!own_clip_frame())
return;
// FIXME: This should apply to SVGs
if (is<SVGPaintable>(*this) || is<SVGSVGPaintable>(*this))
return;
// FIXME: This should also apply to borders and outlines.
if (!first_is_one_of(phase, PaintPhase::Background, PaintPhase::Foreground))
return;
restore_clip(context, own_clip_frame());
}
void paint_cursor_if_needed(PaintContext& context, TextPaintable const& paintable, PaintableFragment const& fragment) void paint_cursor_if_needed(PaintContext& context, TextPaintable const& paintable, PaintableFragment const& fragment)
{ {
auto const& navigable = *paintable.navigable(); auto const& navigable = *paintable.navigable();
@ -870,12 +906,7 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
PaintableBox::paint(context, phase); PaintableBox::paint(context, phase);
if (own_clip_frame()) { apply_own_clip_rect(context, phase);
apply_clip(context, own_clip_frame());
if (own_scroll_frame_id().has_value()) {
context.display_list_recorder().push_scroll_frame_id(own_scroll_frame_id().value());
}
}
// Text shadows // Text shadows
// This is yet another loop, but done here because all shadows should appear under all text. // This is yet another loop, but done here because all shadows should appear under all text.
@ -899,12 +930,7 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
paint_text_fragment(context, static_cast<TextPaintable const&>(fragment.paintable()), fragment, phase); paint_text_fragment(context, static_cast<TextPaintable const&>(fragment.paintable()), fragment, phase);
} }
if (own_clip_frame()) { clear_own_clip_rect(context, phase);
if (own_scroll_frame_id().has_value()) {
context.display_list_recorder().pop_scroll_frame_id();
}
restore_clip(context, own_clip_frame());
}
} }
Paintable::DispatchEventOfSameName PaintableBox::handle_mousedown(Badge<EventHandler>, CSSPixelPoint position, unsigned, unsigned) Paintable::DispatchEventOfSameName PaintableBox::handle_mousedown(Badge<EventHandler>, CSSPixelPoint position, unsigned, unsigned)

View file

@ -142,6 +142,9 @@ public:
virtual void apply_clip_overflow_rect(PaintContext&, PaintPhase) const override; virtual void apply_clip_overflow_rect(PaintContext&, PaintPhase) const override;
virtual void clear_clip_overflow_rect(PaintContext&, PaintPhase) const override; virtual void clear_clip_overflow_rect(PaintContext&, PaintPhase) const override;
virtual void apply_own_clip_rect(PaintContext&, PaintPhase) const override;
virtual void clear_own_clip_rect(PaintContext&, PaintPhase) const override;
[[nodiscard]] virtual TraversalDecision hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const override; [[nodiscard]] virtual TraversalDecision hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const override;
Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const; Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const;
[[nodiscard]] TraversalDecision hit_test_continuation(Function<TraversalDecision(HitTestResult)> const& callback) const; [[nodiscard]] TraversalDecision hit_test_continuation(Function<TraversalDecision(HitTestResult)> const& callback) const;

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS Reftest Reference</title>
<link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com">
</head>
<body>
<p>The test passes if there is a green square and no red.</p>
<div style="width: 200px; height: 200px; background-color: green;"></div>
</body>
</html>

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS Reftest Reference</title>
<link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com">
</head>
<body>
<p>The test passes if there is a green square with a blue border.</p>
<div style="width: 100px; height: 100px; border: solid blue 50px; background-color: green;"></div>
</body>
</html>

View file

@ -0,0 +1,64 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>CSS Test: clip - auto value</title>
<link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
<link rel="author" title="Vasil Dinkov" href="http://www.vadikom.com/" />
<link rel="help" href="http://www.w3.org/TR/CSS21/visufx.html#clipping" />
<link rel="match" href="../../../../../expected/wpt-import/css/CSS2/visufx/../reference/ref-filled-green-100px-square.xht" />
<meta content="dom" name="flags" />
<meta content="An element must not clip when clip is set to auto." name="assert" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<style type="text/css"><![CDATA[
#red-overlapped-layer
{
background-color: red;
height: 100px;
position: absolute;
width: 100px;
}
#red-parent
{
background-color: red;
clip: rect(0, 1px, 1px, 0);
height: 2px;
position: absolute;
width: 2px;
}
#green-child
{
background-color: green;
height: 100px;
width: 100px;
}
]]></style>
<script type="text/javascript"><![CDATA[
function startTest()
{
document.getElementById("red-parent").style.clip = "auto";
}
]]></script>
</head>
<body onload="startTest();">
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div id="red-overlapped-layer"></div>
<div id="red-parent">
<div id="green-child"></div>
</div>
</body>
</html>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>CSS Test: Clip using pixels with a zero value, 0px</title>
<link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
<link rel="help" href="http://www.w3.org/TR/CSS21/visufx.html#propdef-clip" />
<link rel="help" href="http://www.w3.org/TR/CSS21/visufx.html#clipping" />
<link rel="match" href="../../../../../expected/wpt-import/css/CSS2/visufx/../reference/no-red-on-blank-page-ref.xht"/>
<meta name="assert" content="The 'clip' property sets a zero value, in pixels, for all sides of the clipping rectangle." />
<style type="text/css">
div
{
background: red;
clip: rect(0px, 0px, 0px, 0px);
height: 1in;
position: absolute;
width: 1in;
}
</style>
</head>
<body>
<p>Test passes if there is no red visible on the page.</p>
<div></div>
</body>
</html>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>CSS Test: Clip using pixels with a nominal value, 96px</title>
<link rel="author" title="Microsoft" href="http://www.microsoft.com/" />
<link rel="help" href="http://www.w3.org/TR/CSS21/visufx.html#propdef-clip" />
<link rel="help" href="http://www.w3.org/TR/CSS21/visufx.html#clipping" />
<link rel="match" href="../../../../../expected/wpt-import/css/CSS2/visufx/../reference/no-red-on-blank-page-ref.xht"/>
<meta name="assert" content="The 'clip' property sets a value of ninety-six pixels, for all sides of the clipping rectangle." />
<style type="text/css">
div
{
background: red;
clip: rect(96px, 96px, 96px, 96px);
height: 3in;
position: absolute;
width: 3in;
}
</style>
</head>
<body>
<p>Test passes if there is no red visible on the page.</p>
<div></div>
</body>
</html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS Masking: Test clip property does not clip on with negative values - 1</title>
<link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com">
<link rel="help" href="http://www.w3.org/TR/css-masking-1/#clipping-paths">
<link rel="help" href="http://www.w3.org/TR/css-masking-1/#clip-property">
<link rel="match" href="../../../../../expected/wpt-import/css/css-masking/clip/reference/clip-full-ref.html">
<meta name="assert" content="Negative values are permitted on rect function
for clip. Test that whole element is clipped if bottom edge is before top
edge. On pass you should see a green square and no red.">
</head>
<body>
<p>The test passes if there is a green square and no red.</p>
<div style="background-color: green; width: 200px; height: 200px;">
<div style="width: 200px; height: 200px; background-color: red; position: absolute; clip: rect(0, -50px, 200px, 50px);"></div>
</div>
</body>
</html>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS Masking: Test clip property with rect function and auto values clip to border box - 2</title>
<link rel="author" title="Dirk Schulze" href="mailto:dschulze@adobe.com">
<link rel="help" href="http://www.w3.org/TR/css-masking-1/#clipping-paths">
<link rel="help" href="http://www.w3.org/TR/css-masking-1/#clip-property">
<link rel="match" href="../../../../../expected/wpt-import/css/css-masking/clip/reference/clip-no-clipping-ref.html">
<meta name="assert" content="A value of 'auto' in the rect function is
equal to the certain edge of the border box. The box shadow should be
clipped, since it is painted outside the border box. On pass you should see
a green square with a blue border.">
</head>
<body>
<p>The test passes if there is a green square with a blue border.</p>
<div style="width: 100px; height: 100px; border: solid blue 50px; background-color: green; position: absolute; clip: rect(auto, auto, auto, auto); box-shadow: 0 0 10px red;"></div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Before After
Before After