diff --git a/src/browser/Frame.zig b/src/browser/Frame.zig index 12bb8c38..1369cde4 100644 --- a/src/browser/Frame.zig +++ b/src/browser/Frame.zig @@ -2885,12 +2885,24 @@ pub fn insertAllChildrenBefore(self: *Frame, fragment: *Node, parent: *Node, ref // Check if child was connected BEFORE removing it from fragment const child_was_connected = child.isConnected(); self.removeNode(fragment, child, .{ .will_be_reconnected = dest_connected }); - try self.insertNodeRelative( - parent, - child, - .{ .before = ref_node }, - .{ .child_already_connected = child_was_connected }, - ); + // A callback fired by a previous iteration's insert (e.g. a custom + // element's connectedCallback) may have detached ref_node from + // parent. In that case, fall back to append so the remaining + // children still land in `parent` in source order. + if (ref_node._parent == parent) { + try self.insertNodeRelative( + parent, + child, + .{ .before = ref_node }, + .{ .child_already_connected = child_was_connected }, + ); + } else { + try self.appendNode( + parent, + child, + .{ .child_already_connected = child_was_connected }, + ); + } } } diff --git a/src/browser/tests/custom_elements/mutation_during_callback.html b/src/browser/tests/custom_elements/mutation_during_callback.html new file mode 100644 index 00000000..bca44926 --- /dev/null +++ b/src/browser/tests/custom_elements/mutation_during_callback.html @@ -0,0 +1,86 @@ + + + + + + + + + + + diff --git a/src/browser/tests/document/write.html b/src/browser/tests/document/write.html index 6cb5bbb6..7322882f 100644 --- a/src/browser/tests/document/write.html +++ b/src/browser/tests/document/write.html @@ -120,6 +120,28 @@ } + +
+ +
+ + + diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig index a30d32cd..8ffec5ce 100644 --- a/src/browser/webapi/Document.zig +++ b/src/browser/webapi/Document.zig @@ -713,13 +713,20 @@ fn writeInternal(self: *Document, text: []const []const u8, append_newline: bool // Determine insertion point: // - If _write_insertion_point is set and still parented correctly, continue from there // - Otherwise, start after the script (first write, or previous insertion point was removed) + // parseFragment above can synchronously execute a parser-blocking script + // (e.g.