@@ -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