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 0000000000..de67586e85
--- /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 0000000000..c12c9e266d
--- /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 7498859450..ecb34e6599 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 9e97f3cd99..1f76c1b1d3 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 ba72122813..f2f27e5090 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 9fdb80086c..0424b92366 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 d925f8815d..4c66eb28b6 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 88f6dc2e29..6457f24940 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&);
};