diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index a12ce97c9f..020040e7c9 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -1354,33 +1354,14 @@ static void relayout_svg_root(Layout::SVGSVGBox& svg_root) if (auto const* paintable = svg_root.paintable_box()) layout_state.populate_from_paintable(svg_root, *paintable); - // Pre-populate ancestors outside the subtree: - // - SVGGraphicsBox ancestors (up to outer SVG) for get_parent_svg_transform() - // - Abspos boxes inside the subtree whose layout is managed by ancestor formatting contexts (not re-laid-out - // during SVG relayout, so their existing paintable state must be preserved) - bool found_outer_svg = false; + // Pre-populate SVGGraphicsBox ancestors (up to outer SVG) for get_parent_svg_transform(). for (auto* ancestor = svg_root.parent(); ancestor; ancestor = ancestor->parent()) { - if (!found_outer_svg) { - if (auto const* svg_graphics_ancestor = as_if(*ancestor)) { - if (auto const* paintable = svg_graphics_ancestor->paintable_box()) - layout_state.populate_from_paintable(*svg_graphics_ancestor, *paintable); - } - if (is(*ancestor)) - found_outer_svg = true; - } - - if (auto const* box = as_if(*ancestor)) { - // Pre-populate abspos boxes that are inside the SVG subtree but whose containing block is outside it. - // These boxes are laid out by ancestor formatting contexts (not during SVG relayout), so we must preserve - // their existing paintable state to prevent commit() from destroying their paintables. - for (auto const& abspos_child : box->contained_abspos_children()) { - if (svg_root.is_inclusive_ancestor_of(abspos_child) && is(*abspos_child)) { - auto const& abspos_box = static_cast(*abspos_child); - if (auto const* abspos_paintable = abspos_box.paintable_box()) - layout_state.populate_from_paintable(abspos_box, *abspos_paintable); - } - } + if (auto const* svg_graphics_ancestor = as_if(*ancestor)) { + if (auto const* paintable = svg_graphics_ancestor->paintable_box()) + layout_state.populate_from_paintable(*svg_graphics_ancestor, *paintable); } + if (is(*ancestor)) + break; } auto const& svg_state = layout_state.get(svg_root); diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index 5c10f5f59f..f70a4cf5c2 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -160,6 +160,11 @@ bool Node::establishes_an_absolute_positioning_containing_block() const if (is(*this)) return true; + // https://github.com/w3c/fxtf-drafts/issues/307#issuecomment-499612420 + // foreignObject establishes a containing block for absolutely and fixed positioned elements. + if (is_svg_foreign_object_box()) + return true; + return computed_values_establish_absolute_positioning_containing_block(); } @@ -1467,29 +1472,11 @@ void Node::set_needs_layout_update(DOM::SetNeedsLayoutReason reason) return IterationDecision::Continue; }); - auto has_abspos_with_external_containing_block = [](SVGSVGBox const& svg_box) { - for (auto const* ancestor = svg_box.parent(); ancestor; ancestor = ancestor->parent()) { - auto const* box = as_if(ancestor); - if (!box) - continue; - for (auto const& abspos_child : box->contained_abspos_children()) { - if (svg_box.is_inclusive_ancestor_of(abspos_child)) - return true; - } - } - return false; - }; - for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { if (ancestor->m_needs_layout_update) break; ancestor->m_needs_layout_update = true; if (auto* svg_box = as_if(ancestor)) { - // Absolutely positioned elements inside the SVG subtree whose containing - // block is outside the SVG can't be properly relaid out during partial SVG - // relayout — their layout depends on formatting contexts outside the subtree. - if (has_abspos_with_external_containing_block(*svg_box)) - continue; document().mark_svg_root_as_needing_relayout(*svg_box); break; } diff --git a/Tests/LibWeb/Layout/expected/svg-foreignobject-absolute-positioning.txt b/Tests/LibWeb/Layout/expected/svg-foreignobject-absolute-positioning.txt new file mode 100644 index 0000000000..60d232fd87 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/svg-foreignobject-absolute-positioning.txt @@ -0,0 +1,29 @@ +Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 216 0+0+0] [BFC] children: not-inline + BlockContainer at [8,8] [8+0+0 784 0+0+8] [8+0+0 200 0+0+8] children: inline + frag 0 from SVGSVGBox start: 0, length: 0, rect: [8,8 200x200] baseline: 200 + SVGSVGBox at [8,8] [0+0+0 200 0+0+0] [0+0+0 200 0+0+0] [SVG] children: inline + TextNode <#text> (not painted) + SVGForeignObjectBox at [58,58] [0+0+0 100 0+0+0] [0+0+0 100 0+0+0] [BFC] children: not-inline + BlockContainer <(anonymous)> at [58,58] [0+0+0 100 0+0+0] [0+0+0 0 0+0+0] children: inline + TextNode <#text> (not painted) + BlockContainer
at [58,58] positioned [0+0+0 100 0+0+0] [0+0+0 100 0+0+0] [BFC] children: inline + frag 0 from TextNode start: 0, length: 12, rect: [58,58 94.15625x18] baseline: 13.796875 + "I am offset!" + TextNode <#text> (not painted) + TextNode <#text> (not painted) + TextNode <#text> (not painted) + TextNode <#text> (not painted) + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x216] + PaintableWithLines (BlockContainer) [8,8 784x200] + SVGSVGPaintable (SVGSVGBox) [8,8 200x200] + SVGForeignObjectPaintable (SVGForeignObjectBox) [58,58 100x100] + PaintableWithLines (BlockContainer(anonymous)) [58,58 100x0] + PaintableWithLines (BlockContainer
) [58,58 100x100] + TextPaintable (TextNode<#text>) + +SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto) + SC for BlockContainer [0,0 800x216] [children: 1] (z-index: auto) + SC for SVGForeignObjectBox [58,58 100x100] [children: 0] (z-index: auto) diff --git a/Tests/LibWeb/Layout/input/svg-foreignobject-absolute-positioning.html b/Tests/LibWeb/Layout/input/svg-foreignobject-absolute-positioning.html new file mode 100644 index 0000000000..e29c4b9849 --- /dev/null +++ b/Tests/LibWeb/Layout/input/svg-foreignobject-absolute-positioning.html @@ -0,0 +1,15 @@ + + + + +
I am offset!
+
+
diff --git a/Tests/LibWeb/Text/expected/SVG/svg-foreignObject-abspos-with-ancestor-containing-block.txt b/Tests/LibWeb/Text/expected/SVG/svg-foreignObject-abspos-preserved-during-partial-relayout.txt similarity index 74% rename from Tests/LibWeb/Text/expected/SVG/svg-foreignObject-abspos-with-ancestor-containing-block.txt rename to Tests/LibWeb/Text/expected/SVG/svg-foreignObject-abspos-preserved-during-partial-relayout.txt index ae4e21167e..2102dd0e22 100644 --- a/Tests/LibWeb/Text/expected/SVG/svg-foreignObject-abspos-with-ancestor-containing-block.txt +++ b/Tests/LibWeb/Text/expected/SVG/svg-foreignObject-abspos-preserved-during-partial-relayout.txt @@ -1,11 +1,11 @@ Before SVG relayout: - abspos width: 50 - abspos height: 50 + abspos width: 25 + abspos height: 25 abspos x: 18 abspos y: 18 After SVG relayout: - abspos width: 50 - abspos height: 50 + abspos width: 25 + abspos height: 25 abspos x: 18 abspos y: 18 Abspos preserved after SVG relayout: diff --git a/Tests/LibWeb/Text/expected/SVG/svg-foreignObject-abspos-size-change-with-ancestor-containing-block.txt b/Tests/LibWeb/Text/expected/SVG/svg-foreignObject-abspos-size-change-during-partial-relayout.txt similarity index 100% rename from Tests/LibWeb/Text/expected/SVG/svg-foreignObject-abspos-size-change-with-ancestor-containing-block.txt rename to Tests/LibWeb/Text/expected/SVG/svg-foreignObject-abspos-size-change-during-partial-relayout.txt diff --git a/Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-with-ancestor-containing-block.html b/Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-preserved-during-partial-relayout.html similarity index 100% rename from Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-with-ancestor-containing-block.html rename to Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-preserved-during-partial-relayout.html diff --git a/Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-size-change-with-ancestor-containing-block.html b/Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-size-change-during-partial-relayout.html similarity index 88% rename from Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-size-change-with-ancestor-containing-block.html rename to Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-size-change-during-partial-relayout.html index 9120940017..579512d896 100644 --- a/Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-size-change-with-ancestor-containing-block.html +++ b/Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-size-change-during-partial-relayout.html @@ -32,9 +32,8 @@ // Change the abspos element's size via inline style. // Since this element is inside the SVG subtree (via foreignObject), // set_needs_layout_update() breaks at SVGSVGBox and triggers partial SVG relayout. - // However, the abspos element's containing block is the .container div OUTSIDE - // the SVG subtree, so the formatting context responsible for laying out the abspos - // won't run during partial SVG relayout. This means the size change won't take effect. + // The abspos element's containing block is the foreignObject itself, so the size + // change should take effect during partial SVG relayout. const abspos = document.getElementById("abspos"); abspos.style.width = "100px"; abspos.style.height = "100px";