Merge pull request #1578 from lightpanda-io/detach_attached_parsed_node

Detach attached nodes on appendBeforeSibling callback
This commit is contained in:
Karl Seguin
2026-02-18 15:42:13 +08:00
committed by GitHub
3 changed files with 41 additions and 2 deletions

View File

@@ -421,7 +421,16 @@ fn appendBeforeSiblingCallback(ctx: *anyopaque, sibling_ref: *anyopaque, node_or
fn _appendBeforeSiblingCallback(self: *Parser, sibling: *Node, node_or_text: h5e.NodeOrText) !void {
const parent = sibling.parentNode() orelse return error.NoParent;
const node: *Node = switch (node_or_text.toUnion()) {
.node => |cpn| getNode(cpn),
.node => |cpn| blk: {
const child = getNode(cpn);
if (child._parent) |previous_parent| {
// A custom element constructor may have inserted the node into the
// DOM before the parser officially places it (e.g. via foster
// parenting). Detach it first so insertNodeRelative's assertion holds.
self.page.removeNode(previous_parent, child, .{ .will_be_reconnected = parent.isConnected() });
}
break :blk child;
},
.text => |txt| try self.page.createTextNode(txt),
};
try self.page.insertNodeRelative(parent, node, .{ .before = sibling }, .{});

View File

@@ -119,3 +119,33 @@
}
</script>
<script id="constructor_self_insert_foster_parent">
{
// Regression: custom element constructor inserting itself (via appendChild) during
// innerHTML parsing. When the element is not valid table content, the HTML5 parser
// foster-parents it before the <table> via appendBeforeSiblingCallback. That callback
// previously didn't check for an existing _parent before calling insertNodeRelative,
// causing the "Page.insertNodeRelative parent" assertion to fire.
let constructorCalled = 0;
let container;
class CtorSelfInsert extends HTMLElement {
constructor() {
super();
constructorCalled++;
// Insert self into container so _parent is set before the parser
// officially places this element via appendBeforeSiblingCallback.
if (container) container.appendChild(this);
}
}
customElements.define('ctor-self-insert', CtorSelfInsert);
container = document.createElement('div');
// ctor-self-insert is not valid table content; the parser foster-parents it
// before the <table>, calling appendBeforeSiblingCallback(sibling=table, node=element).
// At that point the element already has _parent=container from the constructor.
container.innerHTML = '<table><ctor-self-insert></ctor-self-insert></table>';
testing.expectEqual(1, constructorCalled);
}
</script>

View File

@@ -1417,7 +1417,7 @@ pub const Transfer = struct {
header_len = buf_len - 1;
}
const header = buffer[0 .. header_len];
const header = buffer[0..header_len];
// We need to parse the first line headers for each request b/c curl's
// CURLINFO_RESPONSE_CODE returns the status code of the final request.