Files
ladybird/Tests/LibWeb/Text/input/SVG/use-element-shadow-tree-removal.html
Andreas Kling a64ea670fd LibWeb: Fix excessive recursion when removing SVG elements from use tree
When an SVGElement is removed from a <use> element's shadow tree, we
need to check if it was in a use element's shadow root to avoid
notifying use elements about the removal of their own clones.

The check was incorrectly using root() instead of old_root. Since the
element has already been detached when removed_from() is called,
root() no longer returns the shadow root, causing the early-return
check to fail.

This led to O(n) recursion depth when clearing a use element's shadow
tree, as each removed clone would trigger another round of
remove_all_children() on use elements referencing the same ID.
2026-01-14 11:46:23 +01:00

48 lines
1.5 KiB
HTML

<!DOCTYPE html>
<script src="../include.js"></script>
<svg id="defs" style="display: none;"></svg>
<svg id="testSvg"></svg>
<script>
// Generate a deeply nested SVG structure that would cause stack overflow
// if the recursion bug in SVGElement::removed_from is present.
const depth = 100;
let inner = document.createElementNS("http://www.w3.org/2000/svg", "g");
inner.id = "level0";
for (let i = 1; i < depth; i++) {
let g = document.createElementNS("http://www.w3.org/2000/svg", "g");
g.id = `level${i}`;
g.appendChild(inner);
inner = g;
}
let symbol = document.createElementNS("http://www.w3.org/2000/svg", "symbol");
symbol.id = "deepSymbol";
symbol.appendChild(inner);
defs.appendChild(symbol);
let use = document.createElementNS("http://www.w3.org/2000/svg", "use");
use.id = "deepUse";
use.setAttribute("href", "#deepSymbol");
testSvg.appendChild(use);
function shadowChildCount(use) {
let sr = internals.getShadowRoot(use);
return sr ? sr.childNodes.length : 0;
}
asyncTest((done) => {
setTimeout(() => {
println(`Initial shadow children: ${shadowChildCount(deepUse)}`);
// This would cause stack overflow before the fix due to O(depth) recursion
document.getElementById("deepSymbol").remove();
println(`After removal shadow children: ${shadowChildCount(deepUse)}`);
println("PASS: No stack overflow from deep recursion");
done();
}, 10);
});
</script>