Skip to content

Conversation

@SrikanthKumarC
Copy link
Contributor

Fixes a bug where using appendChild or insertBefore with a node from a different ownerDocument would throw a WrongDocument Error. According to the specification, the new node being added should be implicitly adopted by the parent's ownerDocument.

This change updates these methods to first set the ownerDocument of the node being added, and recursively update the ownerDocument of its children to match that of the parent.


Currently, the behavior is as follows: an error is thrown when a node is inserted using appendChild or insertBefore if its ownerDocument differs from that of the parent. This PR fixes that.

Debug log:

js : function call error . . . . . . . . . . . . . . .  �[0m[+0ms]
      name = browser.dom.node.Node._insertBefore
      err = WrongDocument
      args = 
        1: #<HTMLDivElement> (object)
        2: #<HTMLParagraphElement> (object)
      stack = 
        <anonymous>:28

Javascript used:

console.log("--- Starting Automatic Cross-Document Insertion ---");


const parser = new DOMParser();
const newDoc = parser.parseFromString('<div id="new-node"><p>Hey</p></div>', 'text/html');
console.log("A new document has been created in memory using DOMParser:", newDoc);


const newNode = newDoc.getElementById('new-node');

console.log("The new node's initial ownerDocument is:", newNode.ownerDocument);
console.log("The main document is:", document);


const parent = document.getElementById('target-container');
const referenceNode = document.getElementById('reference-node');


parent.insertBefore(newNode, referenceNode);

console.log("\n--- Insertion Complete ---");
console.log("The new node has been inserted into the main document.");
console.log("The new node's updated ownerDocument is:", newNode.ownerDocument);
console.log("Is the new node now a child of the main document's parent?", parent.contains(newNode));

const k = document.getElementById('new-node');
const ptag = k.querySelector('p');
console.log("The new node's p tag owner is: ", ptag.ownerDocument);
console.log("P tags owner is same as main document?", ptag?.ownerDocument === document);

const statusMessage = document.getElementById('status-message');
statusMessage.textContent = "Success! The node was inserted automatically on load. Check the console for details.";

@github-actions
Copy link

github-actions bot commented Aug 3, 2025

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@SrikanthKumarC
Copy link
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@krichprollsch krichprollsch self-requested a review August 4, 2025 07:46
Copy link
Member

@krichprollsch krichprollsch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @SrikanthKumarC

Thank you very much for you PR 🙏

I would like to avoid the usage of an arena for traversing children.
Can you consider using the walker instead?

const w = Walker{};
while (true) {
    next = try w.get_next(child, next) orelse break;
    next.owner = self_owner;
}

existing example: https://github.com/lightpanda-io/browser/blob/main/src/browser/dom/node.zig#L224-L234

@SrikanthKumarC
Copy link
Contributor Author

Oh yeah that's better. Just refactored it to use the walker instead. thanks

Copy link
Member

@krichprollsch krichprollsch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the change.

Here is a little non-blocking suggestion.

It would be also great to:

Comment on lines 206 to 211
child.owner = self_owner;
var current = child;
while (try w.get_next(child, current)) |current_node| {
current_node.owner = self_owner;
current = current_node;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about:

Suggested change
child.owner = self_owner;
var current = child;
while (try w.get_next(child, current)) |current_node| {
current_node.owner = self_owner;
current = current_node;
}
var current = child;
while (true) {
current.owner = self_owner;
current = try w.get_next(child, next) orelse break;
}

@SrikanthKumarC
Copy link
Contributor Author

Thanks for the suggestion. I’ve added the tests and simplified the walker as you recommended. Sorry for the trouble, I'm still getting the hang of Zig

@krichprollsch
Copy link
Member

No worries, thank you very much for your contribution 🙏
I think a will create a little additional PR to avoid getting the owner twice when calling __insertBefore with a null ref_node_.

@krichprollsch krichprollsch merged commit 0c19070 into lightpanda-io:main Aug 5, 2025
10 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Aug 5, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants