mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-31 17:39:46 +00:00
`appendAllChildren` iterates through the children, but when a child is appended it can mutate the DOM (only via a custom element connected callback AFAIK) which can render the iterator invalid. Constantly get parent.firstChild() as the target.
68 lines
2.0 KiB
HTML
68 lines
2.0 KiB
HTML
<!DOCTYPE html>
|
|
<script src="../testing.js"></script>
|
|
<div id=d1></div>
|
|
<div id=d2><p id=p1></p></div>
|
|
|
|
<script id=appendChild>
|
|
function assertChildren(expected, parent) {
|
|
const actual = Array.from(parent.childNodes).map((n) => n.id);
|
|
testing.expectEqual(expected, actual)
|
|
for (let child of parent.childNodes) {
|
|
testing.expectEqual(parent, child.parentNode);
|
|
}
|
|
}
|
|
const d1 = $('#d1');
|
|
const d2 = $('#d2');
|
|
const p1 = $('#p1');
|
|
|
|
assertChildren([], d1);
|
|
assertChildren(['p1'], d2);
|
|
|
|
d1.appendChild(p1);
|
|
|
|
assertChildren(['p1'], d1);
|
|
assertChildren([], d2);
|
|
|
|
const p2 = document.createElement('p');
|
|
p2.id = 'p2';
|
|
d1.appendChild(p2);
|
|
assertChildren(['p1', 'p2'], d1);
|
|
</script>
|
|
|
|
<div id=d3></div>
|
|
<script id=appendChild_fragment_mutation>
|
|
// Test that appendChild with DocumentFragment handles synchronous callbacks
|
|
// (like custom element connectedCallback) that modify the fragment during iteration.
|
|
// This reproduces a bug where the iterator captures "next" node pointers
|
|
// before processing, but callbacks can remove those nodes from the fragment.
|
|
const d3 = $('#d3');
|
|
const fragment = document.createDocumentFragment();
|
|
|
|
// Create custom element whose connectedCallback modifies the fragment
|
|
let bElement = null;
|
|
class ModifyingElement extends HTMLElement {
|
|
connectedCallback() {
|
|
// When this element is connected, remove 'b' from the fragment
|
|
if (bElement && bElement.parentNode === fragment) {
|
|
fragment.removeChild(bElement);
|
|
}
|
|
}
|
|
}
|
|
customElements.define('modifying-element', ModifyingElement);
|
|
|
|
const a = document.createElement('modifying-element');
|
|
a.id = 'a';
|
|
const b = document.createElement('span');
|
|
b.id = 'b';
|
|
bElement = b;
|
|
fragment.appendChild(a);
|
|
fragment.appendChild(b);
|
|
|
|
// This should not crash - appendChild should handle the modification gracefully
|
|
d3.appendChild(fragment);
|
|
|
|
// 'a' should be in d3, 'b' was removed by connectedCallback and is now detached
|
|
assertChildren(['a'], d3);
|
|
testing.expectEqual(null, b.parentNode);
|
|
</script>
|