From ddcb87fb40725eec75632de304bd9883c786f8b4 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 6 Jul 2025 15:19:57 +0200 Subject: [PATCH] LibWeb: Make TreeBuilder nicer to SVG foreignObject This patch does two things: 1. Makes TreeBuilder never cross the foreignObject boundary when looking for an appropriate insertion parent. Before this change, we would sometimes make things inside the foreignObject DOM subtree have layout nodes outside the foreignObject. 2. Makes foreignObject boxes participate in the anonymous wrapping of inline-level boxes. This is particularly imporant for absolutely positioned elements inside foreignObject, which were previously getting incorrectly wrapped if there was any text (even empty) preceding the abspos element. --- Libraries/LibWeb/Layout/TreeBuilder.cpp | 8 +++++- .../foreignObject-with-abspos-descendant.txt | 26 +++++++++++++++++++ .../foreignObject-with-abspos-descendant.html | 14 ++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 Tests/LibWeb/Layout/expected/svg/foreignObject-with-abspos-descendant.txt create mode 100644 Tests/LibWeb/Layout/input/svg/foreignObject-with-abspos-descendant.html diff --git a/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Libraries/LibWeb/Layout/TreeBuilder.cpp index a084a250b2e..c5feb56f12e 100644 --- a/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2022, Andreas Kling + * Copyright (c) 2018-2025, Andreas Kling * Copyright (c) 2022-2023, Sam Atkins * Copyright (c) 2022, MacDue * Copyright (c) 2025, Jelle Raaijmakers @@ -77,6 +77,9 @@ static Layout::Node& insertion_parent_for_inline_node(Layout::NodeWithStyle& lay if (is(layout_parent)) return last_child_creating_anonymous_wrapper_if_needed(layout_parent); + if (layout_parent.is_svg_foreign_object_box()) + return last_child_creating_anonymous_wrapper_if_needed(layout_parent); + if (layout_parent.display().is_inline_outside() && layout_parent.display().is_flow_inside()) return layout_parent; @@ -151,6 +154,9 @@ void TreeBuilder::insert_node_into_inline_or_block_ancestor(Layout::Node& node, // Find the nearest ancestor that can host the node. auto& nearest_insertion_ancestor = [&]() -> NodeWithStyle& { for (auto& ancestor : m_ancestor_stack.in_reverse()) { + if (ancestor->is_svg_foreign_object_box()) + return ancestor; + auto const& ancestor_display = ancestor->display(); // Out-of-flow nodes cannot be hosted in inline flow nodes. diff --git a/Tests/LibWeb/Layout/expected/svg/foreignObject-with-abspos-descendant.txt b/Tests/LibWeb/Layout/expected/svg/foreignObject-with-abspos-descendant.txt new file mode 100644 index 00000000000..f59e9297f6b --- /dev/null +++ b/Tests/LibWeb/Layout/expected/svg/foreignObject-with-abspos-descendant.txt @@ -0,0 +1,26 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x166 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x150 children: not-inline + SVGSVGBox at (8,8) content-size 300x150 [SVG] children: inline + TextNode <#text> + SVGForeignObjectBox at (8,8) content-size 100x200 [BFC] children: not-inline + BlockContainer <(anonymous)> at (8,8) content-size 300x0 children: inline + TextNode <#text> + BlockContainer at (8,8) content-size 50x60 positioned [BFC] children: not-inline + TextNode <#text> + TextNode <#text> + BlockContainer <(anonymous)> at (8,158) content-size 784x0 children: inline + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x166] + PaintableWithLines (BlockContainer) [8,8 784x150] + SVGSVGPaintable (SVGSVGBox) [8,8 300x150] overflow: [8,8 300x200] + SVGForeignObjectPaintable (SVGForeignObjectBox) [8,8 100x200] + PaintableWithLines (BlockContainer(anonymous)) [8,8 300x0] + PaintableWithLines (BlockContainer
.el) [8,8 50x60] + PaintableWithLines (BlockContainer(anonymous)) [8,158 784x0] + +SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto) + SC for BlockContainer [0,0 800x166] [children: 1] (z-index: auto) + SC for SVGForeignObjectBox [8,8 100x200] [children: 0] (z-index: auto) diff --git a/Tests/LibWeb/Layout/input/svg/foreignObject-with-abspos-descendant.html b/Tests/LibWeb/Layout/input/svg/foreignObject-with-abspos-descendant.html new file mode 100644 index 00000000000..cd8621a4661 --- /dev/null +++ b/Tests/LibWeb/Layout/input/svg/foreignObject-with-abspos-descendant.html @@ -0,0 +1,14 @@ + + + + +
+
+