+
diff --git a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp
index 74cd4ee329b..ebce36ed4bd 100644
--- a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp
@@ -26,8 +26,9 @@
namespace Web::Layout {
-SVGFormattingContext::SVGFormattingContext(LayoutState& state, Box const& box, FormattingContext* parent)
+SVGFormattingContext::SVGFormattingContext(LayoutState& state, Box const& box, FormattingContext* parent, Gfx::AffineTransform parent_viewbox_transform)
: FormattingContext(Type::SVG, state, box, parent)
+ , m_parent_viewbox_transform(parent_viewbox_transform)
{
}
@@ -156,27 +157,56 @@ static bool is_container_element(Node const& node)
return false;
}
+enum class TraversalDecision {
+ Continue,
+ SkipChildrenAndContinue,
+ Break,
+};
+
+// FIXME: Add TraversalDecision::SkipChildrenAndContinue to TreeNode's implementation.
+template
+static TraversalDecision for_each_in_inclusive_subtree(Layout::Node const& node, Callback callback)
+{
+ if (auto decision = callback(node); decision != TraversalDecision::Continue)
+ return decision;
+ for (auto* child = node.first_child(); child; child = child->next_sibling()) {
+ if (for_each_in_inclusive_subtree(*child, callback) == TraversalDecision::Break)
+ return TraversalDecision::Break;
+ }
+ return TraversalDecision::Continue;
+}
+
+// FIXME: Add TraversalDecision::SkipChildrenAndContinue to TreeNode's implementation.
+template
+static TraversalDecision for_each_in_subtree(Layout::Node const& node, Callback callback)
+{
+ for (auto* child = node.first_child(); child; child = child->next_sibling()) {
+ if (for_each_in_inclusive_subtree(*child, callback) == TraversalDecision::Break)
+ return TraversalDecision::Break;
+ }
+ return TraversalDecision::Continue;
+}
+
void SVGFormattingContext::run(Box const& box, LayoutMode layout_mode, AvailableSpace const& available_space)
{
- auto& svg_svg_element = verify_cast(*box.dom_node());
+ auto& svg_viewport = dynamic_cast(*box.dom_node());
+ auto& svg_box_state = m_state.get_mutable(box);
- auto svg_box_state = m_state.get(box);
- auto root_offset = svg_box_state.offset;
-
- box.for_each_child_of_type([&](BlockContainer const& child_box) {
- if (is(child_box.dom_node())) {
- Layout::BlockFormattingContext bfc(m_state, child_box, this);
- bfc.run(child_box, LayoutMode::Normal, available_space);
-
- auto& child_state = m_state.get_mutable(child_box);
- child_state.set_content_offset(child_state.offset.translated(root_offset));
+ auto viewbox = svg_viewport.view_box();
+ // https://svgwg.org/svg2-draft/coords.html#ViewBoxAttribute
+ if (viewbox.has_value()) {
+ if (viewbox->width < 0 || viewbox->height < 0) {
+ // A negative value for or is an error and invalidates the ‘viewBox’ attribute.
+ viewbox = {};
+ } else if (viewbox->width == 0 || viewbox->height == 0) {
+ // A value of zero disables rendering of the element.
+ return;
}
- return IterationDecision::Continue;
- });
+ }
- auto compute_viewbox_transform = [&](auto const& viewbox) -> Gfx::AffineTransform {
+ auto viewbox_transform = [&] {
if (!viewbox.has_value())
- return {};
+ return m_parent_viewbox_transform;
// FIXME: This should allow just one of width or height to be specified.
// E.g. We should be able to layout