Support for clone custom elements that attach them self in their constructor

When we createElement, we assume the element is detached. This is usually true
except for Custom Elements where the constructor can do anything, including
connecting the element. This broken assumption results in cloneNode crashing.
This commit is contained in:
Karl Seguin committed 2026-03-04 14:54:34 +08:00
1 parent bc8c44f62f
commit ca0f77bdee
2 files changed
+30 -2

No files matched your search

@@ -53,3 +53,22 @@
testing.expectEqual('NO-CONSTRUCTOR-ELEMENT', el.tagName);
}
</script>
<div id=clone_container></div>
<script id=clone>
{
let calls = 0;
class MyCloneElementA extends HTMLElement {
constructor() {
super();
calls += 1;
$('#clone_container').appendChild(this);
}
}
customElements.define('my-clone_element_a', MyCloneElementA);
const original = document.createElement('my-clone_element_a');
$('#clone_container').cloneNode(true);
testing.expectEqual(2, calls);
}
</script>
+11 -2
View File
@@ -1329,9 +1329,18 @@ pub fn clone(self: *Element, deep: bool, page: *Page) !*Node {
var child_it = self.asNode().childrenIterator();
while (child_it.next()) |child| {
const cloned_child = try child.cloneNode(true, page);
if (cloned_child._parent != null) {
// This is almost always false, the only case where a cloned
// node would already have a parent is with a custom element
// that has a constructor (which is called during cloning) which
// inserts it somewhere. In that case, whatever parent was set
// in the constructor should not be changed.
continue;
}
// We pass `true` to `child_already_connected` as a hacky optimization
// We _know_ this child isn't connected (Becasue the parent isn't connected)
// setting this to `true` skips all connection checks and just assumes t
// We _know_ this child isn't connected (Because the parent isn't connected)
// setting this to `true` skips all connection checks.
try page.appendNode(node, cloned_child, .{ .child_already_connected = true });
}
}