From 613cd6104d0679d2bbc30614e9428d2a87241a6b Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Thu, 25 Apr 2024 21:10:30 +0200 Subject: [PATCH] LibWeb: Support masking of SVGForeignObjectPaintable --- .../Ref/reference/svg-foreign-object-mask-ref.html | 4 ++++ Tests/LibWeb/Ref/svg-foreign-object-mask.html | 10 ++++++++++ .../Libraries/LibWeb/Layout/BlockFormattingContext.cpp | 5 +++++ .../Libraries/LibWeb/Layout/InlineLevelIterator.cpp | 5 +++++ Userland/Libraries/LibWeb/Layout/Node.h | 1 + .../Libraries/LibWeb/Layout/SVGFormattingContext.cpp | 3 +++ Userland/Libraries/LibWeb/Layout/SVGMaskBox.h | 5 +++++ .../LibWeb/Painting/SVGForeignObjectPaintable.h | 9 ++++++++- 8 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 Tests/LibWeb/Ref/reference/svg-foreign-object-mask-ref.html create mode 100644 Tests/LibWeb/Ref/svg-foreign-object-mask.html diff --git a/Tests/LibWeb/Ref/reference/svg-foreign-object-mask-ref.html b/Tests/LibWeb/Ref/reference/svg-foreign-object-mask-ref.html new file mode 100644 index 00000000000..de67586e853 --- /dev/null +++ b/Tests/LibWeb/Ref/reference/svg-foreign-object-mask-ref.html @@ -0,0 +1,4 @@ + + + + diff --git a/Tests/LibWeb/Ref/svg-foreign-object-mask.html b/Tests/LibWeb/Ref/svg-foreign-object-mask.html new file mode 100644 index 00000000000..c12c9e266d6 --- /dev/null +++ b/Tests/LibWeb/Ref/svg-foreign-object-mask.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 74988594503..ecb34e65991 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -623,6 +623,11 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain auto independent_formatting_context = create_independent_formatting_context_if_needed(m_state, box); + // NOTE: It is possible to encounter SVGMaskBox nodes while doing layout of formatting context established by with a mask. + // We should skip and let SVGFormattingContext take care of them. + if (box.is_svg_mask_box()) + return; + if (!independent_formatting_context && !is(box)) { dbgln("FIXME: Block-level box is not BlockContainer but does not create formatting context: {}", box.debug_description()); return; diff --git a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp index 9e97f3cd995..1f76c1b1d38 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp +++ b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp @@ -111,6 +111,11 @@ void InlineLevelIterator::compute_next() return; do { m_next_node = next_inline_node_in_pre_order(*m_next_node, m_containing_block); + if (m_next_node && m_next_node->is_svg_mask_box()) { + // NOTE: It is possible to encounter SVGMaskBox nodes while doing layout of formatting context established by with a mask. + // We should skip and let SVGFormattingContext take care of them. + m_next_node = m_next_node->next_sibling(); + } } while (m_next_node && (!m_next_node->is_inline() && !m_next_node->is_out_of_flow(m_inline_formatting_context))); } diff --git a/Userland/Libraries/LibWeb/Layout/Node.h b/Userland/Libraries/LibWeb/Layout/Node.h index ba721228137..f2f27e50908 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.h +++ b/Userland/Libraries/LibWeb/Layout/Node.h @@ -107,6 +107,7 @@ public: virtual bool is_viewport() const { return false; } virtual bool is_svg_box() const { return false; } virtual bool is_svg_geometry_box() const { return false; } + virtual bool is_svg_mask_box() const { return false; } virtual bool is_svg_svg_box() const { return false; } virtual bool is_label() const { return false; } virtual bool is_replaced_box() const { return false; } diff --git a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp index 9fdb80086ce..0424b923665 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp @@ -262,6 +262,9 @@ void SVGFormattingContext::layout_svg_element(Box const& child) bfc.run(child, LayoutMode::Normal, *m_available_space); auto& child_state = m_state.get_mutable(child); child_state.set_content_offset(child_state.offset.translated(m_svg_offset)); + child.for_each_child_of_type([&](SVGMaskBox const& child) { + layout_svg_element(child); + }); } else if (is(child)) { layout_graphics_element(static_cast(child)); } diff --git a/Userland/Libraries/LibWeb/Layout/SVGMaskBox.h b/Userland/Libraries/LibWeb/Layout/SVGMaskBox.h index d925f8815d0..4c66eb28b6c 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGMaskBox.h +++ b/Userland/Libraries/LibWeb/Layout/SVGMaskBox.h @@ -20,10 +20,15 @@ public: SVGMaskBox(DOM::Document&, SVG::SVGMaskElement&, NonnullRefPtr); virtual ~SVGMaskBox() override = default; + virtual bool is_svg_mask_box() const override { return true; } + SVG::SVGMaskElement& dom_node() { return verify_cast(SVGGraphicsBox::dom_node()); } SVG::SVGMaskElement const& dom_node() const { return verify_cast(SVGGraphicsBox::dom_node()); } virtual JS::GCPtr create_paintable() const override; }; +template<> +inline bool Node::fast_is() const { return is_svg_mask_box(); } + } diff --git a/Userland/Libraries/LibWeb/Painting/SVGForeignObjectPaintable.h b/Userland/Libraries/LibWeb/Painting/SVGForeignObjectPaintable.h index 88f6dc2e29d..6457f249403 100644 --- a/Userland/Libraries/LibWeb/Painting/SVGForeignObjectPaintable.h +++ b/Userland/Libraries/LibWeb/Painting/SVGForeignObjectPaintable.h @@ -8,10 +8,12 @@ #include #include +#include namespace Web::Painting { -class SVGForeignObjectPaintable final : public PaintableWithLines { +class SVGForeignObjectPaintable final : public PaintableWithLines + , public SVGMaskable { JS_CELL(SVGForeignObjectPaintable, PaintableWithLines); JS_DECLARE_ALLOCATOR(SVGForeignObjectPaintable); @@ -24,6 +26,11 @@ public: Layout::SVGForeignObjectBox const& layout_box() const; + virtual JS::GCPtr dom_node_of_svg() const override { return dom_node(); } + virtual Optional get_masking_area() const override { return get_masking_area_of_svg(); } + virtual Optional get_mask_type() const override { return get_mask_type_of_svg(); } + virtual RefPtr calculate_mask(PaintContext& paint_context, CSSPixelRect const& masking_area) const override { return calculate_mask_of_svg(paint_context, masking_area); } + protected: SVGForeignObjectPaintable(Layout::SVGForeignObjectBox const&); };