From 9d4a1ac2b31a0d2cb472439856106dadf8353f5c Mon Sep 17 00:00:00 2001 From: Zaggy1024 Date: Thu, 31 Aug 2023 17:08:45 -0500 Subject: [PATCH] LibWeb: Apply CSS `clip` property to an element as well as its children d06d4eb made the `clip` property apply to children of an absolute- positioned element, but caused it not to be applied to the element the property was applied to directly. To fix this, apply the clip in new `before_paint()` and `after_paint()` functions. Doing so keeps painter state from leaking from `paint()`, but still allows subclasses of `PaintableBox` clip their contents correctly without repeating the application of the clip rectangle. --- .../Libraries/LibWeb/Painting/Paintable.h | 3 ++ .../LibWeb/Painting/PaintableBox.cpp | 46 +++++++++++++++---- .../Libraries/LibWeb/Painting/PaintableBox.h | 5 ++ .../LibWeb/Painting/StackingContext.cpp | 2 + 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/Userland/Libraries/LibWeb/Painting/Paintable.h b/Userland/Libraries/LibWeb/Painting/Paintable.h index bbef8388b0..49e97f5316 100644 --- a/Userland/Libraries/LibWeb/Painting/Paintable.h +++ b/Userland/Libraries/LibWeb/Painting/Paintable.h @@ -109,6 +109,9 @@ public: return TraversalDecision::Continue; } + virtual void before_paint(PaintContext&, PaintPhase) const { } + virtual void after_paint(PaintContext&, PaintPhase) const { } + virtual void paint(PaintContext&, PaintPhase) const { } virtual void before_children_paint(PaintContext&, PaintPhase) const { } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index 790a5a0216..e0b24e9737 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -176,6 +176,37 @@ StackingContext* PaintableBox::enclosing_stacking_context() VERIFY_NOT_REACHED(); } +Optional PaintableBox::get_clip_rect() const +{ + auto clip = computed_values().clip(); + if (clip.is_rect() && layout_box().is_absolutely_positioned()) { + auto border_box = absolute_border_box_rect(); + return clip.to_rect().resolved(layout_node(), border_box.to_type()).to_type(); + } + return {}; +} + +void PaintableBox::before_paint(PaintContext& context, [[maybe_unused]] PaintPhase phase) const +{ + if (!is_visible()) + return; + + auto clip_rect = get_clip_rect(); + if (clip_rect.has_value()) { + context.painter().save(); + context.painter().add_clip_rect(clip_rect->to_type()); + } +} + +void PaintableBox::after_paint(PaintContext& context, [[maybe_unused]] PaintPhase phase) const +{ + if (!is_visible()) + return; + + if (get_clip_rect().has_value()) + context.painter().restore(); +} + void PaintableBox::paint(PaintContext& context, PaintPhase phase) const { if (!is_visible()) @@ -424,15 +455,12 @@ void PaintableBox::apply_clip_overflow_rect(PaintContext& context, PaintPhase ph auto overflow_x = computed_values().overflow_x(); auto overflow_y = computed_values().overflow_y(); - auto clip = computed_values().clip(); - if (clip.is_rect() && layout_box().is_absolutely_positioned()) { - auto border_box = absolute_border_box_rect(); - auto resolved_clip_rect = clip.to_rect().resolved(layout_node(), border_box.to_type()).to_type(); - if (clip_rect.has_value()) { - clip_rect->intersect(resolved_clip_rect); - } else { - clip_rect = resolved_clip_rect; - } + auto css_clip_property = get_clip_rect(); + if (css_clip_property.has_value()) { + if (clip_rect.has_value()) + clip_rect->intersect(css_clip_property.value()); + else + clip_rect = css_clip_property.value(); } if (!clip_rect.has_value()) diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.h b/Userland/Libraries/LibWeb/Painting/PaintableBox.h index 93dadf055c..b20d38d7c7 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h @@ -20,6 +20,9 @@ public: static JS::NonnullGCPtr create(Layout::Box const&); virtual ~PaintableBox(); + virtual void before_paint(PaintContext&, PaintPhase) const override; + virtual void after_paint(PaintContext&, PaintPhase) const override; + virtual void paint(PaintContext&, PaintPhase) const override; bool is_visible() const { return layout_box().is_visible(); } @@ -186,6 +189,8 @@ public: protected: explicit PaintableBox(Layout::Box const&); + Optional get_clip_rect() const; + virtual void paint_border(PaintContext&) const; virtual void paint_backdrop_filter(PaintContext&) const; virtual void paint_background(PaintContext&) const; diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp index ec41ec7dfd..5772adf34a 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp @@ -26,7 +26,9 @@ namespace Web::Painting { static void paint_node(Paintable const& paintable, PaintContext& context, PaintPhase phase) { + paintable.before_paint(context, phase); paintable.paint(context, phase); + paintable.after_paint(context, phase); } StackingContext::StackingContext(PaintableBox& paintable_box, StackingContext* parent, size_t index_in_tree_order)