From ca0f77bdee340f058438621c06ef2f30009d15cf Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 4 Mar 2026 14:54:34 +0800 Subject: [PATCH] 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. --- .../tests/custom_elements/constructor.html | 19 +++++++++++++++++++ src/browser/webapi/Element.zig | 13 +++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/browser/tests/custom_elements/constructor.html b/src/browser/tests/custom_elements/constructor.html index eba9c1f4..0241b242 100644 --- a/src/browser/tests/custom_elements/constructor.html +++ b/src/browser/tests/custom_elements/constructor.html @@ -53,3 +53,22 @@ testing.expectEqual('NO-CONSTRUCTOR-ELEMENT', el.tagName); } + +
+ + diff --git a/src/browser/webapi/Element.zig b/src/browser/webapi/Element.zig index b922eb4c..ef2386da 100644 --- a/src/browser/webapi/Element.zig +++ b/src/browser/webapi/Element.zig @@ -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 }); } }