diff --git a/Tests/LibWeb/Ref/nested-boxes-with-hidden-overflow-and-border-radius.html b/Tests/LibWeb/Ref/nested-boxes-with-hidden-overflow-and-border-radius.html
new file mode 100644
index 0000000000..4201da1ab0
--- /dev/null
+++ b/Tests/LibWeb/Ref/nested-boxes-with-hidden-overflow-and-border-radius.html
@@ -0,0 +1,30 @@
+
+
+
+
diff --git a/Tests/LibWeb/Ref/reference/images/nested-boxes-with-hidden-overflow-and-border-radius-ref.png b/Tests/LibWeb/Ref/reference/images/nested-boxes-with-hidden-overflow-and-border-radius-ref.png
new file mode 100644
index 0000000000..84b09d71a6
Binary files /dev/null and b/Tests/LibWeb/Ref/reference/images/nested-boxes-with-hidden-overflow-and-border-radius-ref.png differ
diff --git a/Tests/LibWeb/Ref/reference/nested-boxes-with-hidden-overflow-and-border-radius-ref.html b/Tests/LibWeb/Ref/reference/nested-boxes-with-hidden-overflow-and-border-radius-ref.html
new file mode 100644
index 0000000000..c9a882ba78
--- /dev/null
+++ b/Tests/LibWeb/Ref/reference/nested-boxes-with-hidden-overflow-and-border-radius-ref.html
@@ -0,0 +1,10 @@
+
+
diff --git a/Userland/Libraries/LibWeb/Painting/BorderRadiiData.h b/Userland/Libraries/LibWeb/Painting/BorderRadiiData.h
index 8ebd81d26d..625cd31948 100644
--- a/Userland/Libraries/LibWeb/Painting/BorderRadiiData.h
+++ b/Userland/Libraries/LibWeb/Painting/BorderRadiiData.h
@@ -31,6 +31,12 @@ struct BorderRadiusData {
if (vertical_radius != 0)
vertical_radius = max(CSSPixels(0), vertical_radius - vertical);
}
+
+ inline void union_max_radii(BorderRadiusData const& other)
+ {
+ horizontal_radius = max(horizontal_radius, other.horizontal_radius);
+ vertical_radius = max(vertical_radius, other.vertical_radius);
+ }
};
using CornerRadius = Gfx::AntiAliasingPainter::CornerRadius;
@@ -58,6 +64,14 @@ struct BorderRadiiData {
return top_left || top_right || bottom_right || bottom_left;
}
+ inline void union_max_radii(BorderRadiiData const& other)
+ {
+ top_left.union_max_radii(other.top_left);
+ top_right.union_max_radii(other.top_right);
+ bottom_right.union_max_radii(other.bottom_right);
+ bottom_left.union_max_radii(other.bottom_left);
+ }
+
inline void shrink(CSSPixels top, CSSPixels right, CSSPixels bottom, CSSPixels left)
{
top_left.shrink(left, top);
diff --git a/Userland/Libraries/LibWeb/Painting/ClipFrame.h b/Userland/Libraries/LibWeb/Painting/ClipFrame.h
new file mode 100644
index 0000000000..43e34b70b1
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Painting/ClipFrame.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2024, Aliaksandr Kalenik
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace Web::Painting {
+
+struct BorderRadiiClip {
+ CSSPixelRect rect;
+ BorderRadiiData radii;
+};
+
+struct ClipFrame : public RefCounted {
+ Vector const& border_radii_clips() const { return m_border_radii_clips; }
+ void add_border_radii_clip(BorderRadiiClip border_radii_clip)
+ {
+ for (auto& existing_clip : m_border_radii_clips) {
+ if (border_radii_clip.rect == existing_clip.rect) {
+ existing_clip.radii.union_max_radii(border_radii_clip.radii);
+ return;
+ }
+ }
+ m_border_radii_clips.append(border_radii_clip);
+ }
+
+ CSSPixelRect rect() const { return m_rect; }
+ void set_rect(CSSPixelRect rect) { m_rect = rect; }
+
+private:
+ CSSPixelRect m_rect;
+ Vector m_border_radii_clips;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp b/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp
index 0de7ec5e91..7a965240be 100644
--- a/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp
+++ b/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp
@@ -44,7 +44,7 @@ Optional InlinePaintable::enclosing_scroll_frame_offset() const
Optional InlinePaintable::clip_rect() const
{
if (m_enclosing_clip_frame)
- return m_enclosing_clip_frame->rect;
+ return m_enclosing_clip_frame->rect();
return {};
}
diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp
index cfc279cc1e..26ba547947 100644
--- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp
+++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp
@@ -214,14 +214,14 @@ Optional PaintableBox::enclosing_scroll_frame_offset() const
Optional PaintableBox::clip_rect() const
{
if (m_enclosing_clip_frame)
- return m_enclosing_clip_frame->rect;
+ return m_enclosing_clip_frame->rect();
return {};
}
-Optional PaintableBox::corner_clip_radii() const
+Span PaintableBox::border_radii_clips() const
{
if (m_enclosing_clip_frame)
- return m_enclosing_clip_frame->corner_clip_radii;
+ return m_enclosing_clip_frame->border_radii_clips();
return {};
}
@@ -444,12 +444,17 @@ void PaintableBox::apply_clip_overflow_rect(PaintContext& context, PaintPhase ph
m_clipping_overflow = true;
context.recording_painter().save();
context.recording_painter().add_clip_rect(context.enclosing_device_rect(overflow_clip_rect).to_type());
- if (corner_clip_radii().has_value()) {
- VERIFY(!m_corner_clipper_id.has_value());
- m_corner_clipper_id = context.allocate_corner_clipper_id();
- auto corner_radii = corner_clip_radii()->as_corners(context);
- if (corner_radii.has_any_radius())
- context.recording_painter().sample_under_corners(*m_corner_clipper_id, corner_clip_radii()->as_corners(context), context.rounded_device_rect(overflow_clip_rect).to_type(), CornerClip::Outside);
+ auto const& border_radii_clips = this->border_radii_clips();
+ m_corner_clipper_ids.resize(border_radii_clips.size());
+ for (size_t corner_clip_index = 0; corner_clip_index < border_radii_clips.size(); ++corner_clip_index) {
+ auto const& corner_clip = border_radii_clips[corner_clip_index];
+ auto corners = corner_clip.radii.as_corners(context);
+ if (!corners.has_any_radius())
+ continue;
+ auto corner_clipper_id = context.allocate_corner_clipper_id();
+ m_corner_clipper_ids[corner_clip_index] = corner_clipper_id;
+ auto rect = corner_clip.rect.translated(-combined_transform.translation().to_type());
+ context.recording_painter().sample_under_corners(corner_clipper_id, corner_clip.radii.as_corners(context), context.rounded_device_rect(rect).to_type(), CornerClip::Outside);
}
}
}
@@ -461,12 +466,17 @@ void PaintableBox::clear_clip_overflow_rect(PaintContext& context, PaintPhase ph
if (m_clipping_overflow) {
m_clipping_overflow = false;
- if (corner_clip_radii().has_value()) {
- VERIFY(m_corner_clipper_id.has_value());
- auto corner_radii = corner_clip_radii()->as_corners(context);
- if (corner_radii.has_any_radius())
- context.recording_painter().blit_corner_clipping(*m_corner_clipper_id, context.rounded_device_rect(*clip_rect()).to_type());
- m_corner_clipper_id = {};
+ auto combined_transform = compute_combined_css_transform();
+ auto const& border_radii_clips = this->border_radii_clips();
+ for (size_t corner_clip_index = 0; corner_clip_index < border_radii_clips.size(); ++corner_clip_index) {
+ auto const& corner_clip = border_radii_clips[corner_clip_index];
+ auto corners = corner_clip.radii.as_corners(context);
+ if (!corners.has_any_radius())
+ continue;
+ auto corner_clipper_id = m_corner_clipper_ids[corner_clip_index];
+ m_corner_clipper_ids[corner_clip_index] = corner_clipper_id;
+ auto rect = corner_clip.rect.translated(-combined_transform.translation().to_type());
+ context.recording_painter().blit_corner_clipping(corner_clipper_id, context.rounded_device_rect(rect).to_type());
}
context.recording_painter().restore();
}
diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.h b/Userland/Libraries/LibWeb/Painting/PaintableBox.h
index 4ce58e87a2..bd7458e689 100644
--- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h
+++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
#include
#include
@@ -19,11 +20,6 @@ struct ScrollFrame : public RefCounted {
CSSPixelPoint offset;
};
-struct ClipFrame : public RefCounted {
- CSSPixelRect rect;
- Optional corner_clip_radii;
-};
-
class PaintableBox : public Paintable {
JS_CELL(PaintableBox, Paintable);
@@ -210,7 +206,7 @@ public:
Optional scroll_frame_id() const;
Optional enclosing_scroll_frame_offset() const;
Optional clip_rect() const;
- Optional corner_clip_radii() const;
+ Span border_radii_clips() const;
protected:
explicit PaintableBox(Layout::Box const&);
@@ -235,7 +231,7 @@ private:
Optional mutable m_absolute_paint_rect;
mutable bool m_clipping_overflow { false };
- mutable Optional m_corner_clipper_id;
+ mutable Vector m_corner_clipper_ids;
RefPtr m_enclosing_scroll_frame;
RefPtr m_enclosing_clip_frame;
diff --git a/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp b/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp
index 5ee40397ca..c157cd8d61 100644
--- a/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp
+++ b/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp
@@ -149,21 +149,22 @@ void ViewportPaintable::refresh_clip_state()
auto const& block_paintable_box = *block->paintable_box();
auto block_overflow_x = block_paintable_box.computed_values().overflow_x();
auto block_overflow_y = block_paintable_box.computed_values().overflow_y();
- if (block_overflow_x != CSS::Overflow::Visible && block_overflow_y != CSS::Overflow::Visible)
- overflow_clip_rect.intersect(block_paintable_box.compute_absolute_padding_rect_with_css_transform_applied());
+ if (block_overflow_x != CSS::Overflow::Visible && block_overflow_y != CSS::Overflow::Visible) {
+ auto rect = block_paintable_box.compute_absolute_padding_rect_with_css_transform_applied();
+ overflow_clip_rect.intersect(rect);
+ auto border_radii_data = block_paintable_box.normalized_border_radii_data(ShrinkRadiiForBorders::Yes);
+ if (border_radii_data.has_any_radius()) {
+ BorderRadiiClip border_radii_clip { .rect = rect, .radii = border_radii_data };
+ clip_frame.add_border_radii_clip(border_radii_clip);
+ }
+ }
if (auto css_clip_property_rect = block->paintable_box()->get_clip_rect(); css_clip_property_rect.has_value())
overflow_clip_rect.intersect(css_clip_property_rect.value());
}
clip_rect = overflow_clip_rect;
}
- auto border_radii_data = paintable_box.normalized_border_radii_data(ShrinkRadiiForBorders::Yes);
- if (border_radii_data.has_any_radius()) {
- // FIXME: Border radii of all boxes in containing block chain should be taken into account.
- clip_frame.corner_clip_radii = border_radii_data;
- }
-
- clip_frame.rect = *clip_rect;
+ clip_frame.set_rect(*clip_rect);
}
}