Files
ladybird/Tests/LibWeb/Text/input/SVG/svg-foreignObject-abspos-size-change-during-partial-relayout.html
Aliaksandr Kalenik b3231ea2a0 LibWeb: Make foreignObject establish a containing block for abspos
Absolutely positioned elements inside SVG foreignObject were being
positioned relative to an ancestor containing block outside the SVG,
instead of relative to the foreignObject itself. Per a W3C resolution
and the behavior of other browsers, foreignObject should establish a
containing block for absolutely and fixed positioned elements.

With this fix, the `has_abspos_with_external_containing_block` check
in `set_needs_layout_update()` and the abspos preservation loop in
`relayout_svg_root()` become dead code — remove both and simplify the
ancestor loops. Rename related tests to reflect the new behavior.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/3241
2026-02-17 15:59:59 +01:00

61 lines
2.3 KiB
HTML

<!DOCTYPE html>
<script src="../include.js"></script>
<style>
.container {
position: relative;
width: 200px;
height: 200px;
}
.abspos {
position: absolute;
top: 10px;
left: 10px;
width: 50px;
height: 50px;
background: green;
}
</style>
<div class="container">
<svg width="100" height="100">
<foreignObject width="100" height="100">
<div class="abspos" id="abspos"></div>
</foreignObject>
</svg>
</div>
<script>
asyncTest(done => {
// Force initial layout and capture "before" values without any DOM mutations.
// (println modifies the DOM, which would trigger full relayout instead of partial SVG relayout)
document.body.offsetWidth;
const absposBefore = document.getElementById("abspos").getBoundingClientRect();
// 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.
// 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";
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const absposAfter = document.getElementById("abspos").getBoundingClientRect();
// Now it's safe to print (layout observations are done)
println(`Before SVG relayout:`);
println(` abspos width: ${absposBefore.width}`);
println(` abspos height: ${absposBefore.height}`);
println(`After SVG relayout:`);
println(` abspos width: ${absposAfter.width}`);
println(` abspos height: ${absposAfter.height}`);
println(`Abspos size correctly updated:`);
println(` width changed to 100: ${absposAfter.width === 100}`);
println(` height changed to 100: ${absposAfter.height === 100}`);
done();
});
});
});
</script>