Skip to content

Commit 0c19070

Browse files
Merge pull request #920 from SrikanthKumarC/main
Fix: Properly handle node ownership when using appendChild and insertBefore
2 parents 07e37b2 + 0a5f060 commit 0c19070

File tree

1 file changed

+79
-0
lines changed

1 file changed

+79
-0
lines changed

src/browser/dom/node.zig

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,23 @@ pub const Node = struct {
198198
// Methods
199199

200200
pub fn _appendChild(self: *parser.Node, child: *parser.Node) !Union {
201+
const self_owner = try parser.nodeOwnerDocument(self);
202+
const child_owner = try parser.nodeOwnerDocument(child);
203+
204+
// If the node to be inserted has a different ownerDocument than the parent node,
205+
// modern browsers automatically adopt the node and its descendants into
206+
// the parent's ownerDocument.
207+
// This process is known as adoption.
208+
// (7.1) https://dom.spec.whatwg.org/#concept-node-insert
209+
if (child_owner == null or (child_owner.? != self_owner.?)) {
210+
const w = Walker{};
211+
var current = child;
212+
while (true) {
213+
current.owner = self_owner;
214+
current = try w.get_next(child, current) orelse break;
215+
}
216+
}
217+
201218
// TODO: DocumentFragment special case
202219
const res = try parser.nodeAppendChild(self, child);
203220
return try Node.toInterface(res);
@@ -290,6 +307,23 @@ pub const Node = struct {
290307
}
291308

292309
pub fn _insertBefore(self: *parser.Node, new_node: *parser.Node, ref_node_: ?*parser.Node) !Union {
310+
const self_owner = try parser.nodeOwnerDocument(self);
311+
const new_node_owner = try parser.nodeOwnerDocument(new_node);
312+
313+
// If the node to be inserted has a different ownerDocument than the parent node,
314+
// modern browsers automatically adopt the node and its descendants into
315+
// the parent's ownerDocument.
316+
// This process is known as adoption.
317+
// (7.1) https://dom.spec.whatwg.org/#concept-node-insert
318+
if (new_node_owner == null or (new_node_owner.? != self_owner.?)) {
319+
const w = Walker{};
320+
var current = new_node;
321+
while(true) {
322+
current.owner = self_owner;
323+
current = try w.get_next(new_node, current) orelse break;
324+
}
325+
}
326+
293327
if (ref_node_) |ref_node| {
294328
return Node.toInterface(try parser.nodeInsertBefore(self, new_node, ref_node));
295329
}
@@ -720,3 +754,48 @@ test "Browser.DOM.node" {
720754
.{ "Node.NOTATION_NODE", "12" },
721755
}, .{});
722756
}
757+
758+
test "Browser.DOM.node.owner" {
759+
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .html=
760+
\\ <div id="target-container">
761+
\\ <p id="reference-node">
762+
\\ I am the original reference node.
763+
\\ </p>
764+
\\ </div>"
765+
});
766+
767+
defer runner.deinit();
768+
769+
try runner.testCases(&.{
770+
.{
771+
\\ const parser = new DOMParser();
772+
\\ const newDoc = parser.parseFromString('<div id="new-node"><p>Hey</p><span>Marked</span></div>', 'text/html');
773+
774+
\\ const newNode = newDoc.getElementById('new-node');
775+
776+
\\ const parent = document.getElementById('target-container');
777+
\\ const referenceNode = document.getElementById('reference-node');
778+
779+
\\ parent.insertBefore(newNode, referenceNode);
780+
\\ const k = document.getElementById('new-node');
781+
\\ const ptag = k.querySelector('p');
782+
\\ const spanTag = k.querySelector('span');
783+
\\ const anotherDoc = parser.parseFromString('<div id="another-new-node"></div>', 'text/html');
784+
\\ const anotherNewNode = anotherDoc.getElementById('another-new-node');
785+
\\
786+
\\ parent.appendChild(anotherNewNode)
787+
,
788+
"[object HTMLDivElement]",
789+
},
790+
791+
.{"parent.ownerDocument === newNode.ownerDocument", "true" },
792+
.{"parent.ownerDocument === anotherNewNode.ownerDocument", "true"},
793+
.{"newNode.firstChild.nodeName", "P"},
794+
.{"ptag.ownerDocument === parent.ownerDocument", "true"},
795+
.{"spanTag.ownerDocument === parent.ownerDocument", "true"},
796+
.{"parent.contains(newNode)", "true"},
797+
.{"parent.contains(anotherNewNode)", "true"},
798+
.{"anotherDoc.contains(anotherNewNode)", "false"},
799+
.{"newDoc.contains(newNode)", "false"},
800+
}, .{});
801+
}

0 commit comments

Comments
 (0)