Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rcdom's node's destructor clears all children for nodes that can have strong references outside of the tree #410

Open
jdm opened this issue Jan 24, 2020 · 5 comments

Comments

@jdm
Copy link
Member

jdm commented Jan 24, 2020

#409 contains this code:

            // If this is not the only strong reference, don't drop any children, because
            // apparently somebody else wants them. Just let our reference drop instead.
            if Rc::strong_count(&root) > 1 {
                continue;
            }

Our current implementation does not perform this check, but presumably this isn't a case that affects our tests which are largely concerned with the tree structure.

@asquared31415
Copy link

This can definitely cause weird and hard to debug behavior. I ran into a case in practice where a parent node can be dropped but a child node might still have a strong reference. This caused usage of the child node to be incorrect as that node's children had been removed by the current Drop impl. I believe that simply changing line 133 which extends the list of nodes to remove to something like nodes.extend(children.into_iter().filter(|node| Rc::strong_count(node) <= 1)) would fix this issue.

@jdm
Copy link
Member Author

jdm commented May 2, 2021

Per the description in https://github.com/servo/html5ever/tree/master/rcdom#readme, I recommend forking rcdom to make whatever changes you want to it.

@skanfd
Copy link

skanfd commented Feb 14, 2022

So why is there a manually implemented Drop for Node ?
I've thought Node and it's children will be automatically dropped when strong count reach zero.
And these codes just flatten the tree?

impl Drop for Node {
    fn drop(&mut self) {
        let mut nodes = mem::replace(&mut *self.children.borrow_mut(), vec![]);
        while let Some(node) = nodes.pop() {
            let children = mem::replace(&mut *node.children.borrow_mut(), vec![]);
            nodes.extend(children.into_iter());
            if let NodeData::Element { ref template_contents, .. } = node.data {
                if let Some(template_contents) = template_contents.borrow_mut().take() {
                    nodes.push(template_contents);
                }
            }
        }
    }
}

@froydnj
Copy link

froydnj commented Feb 14, 2022

So why is there a manually implemented Drop for Node ?

So that the children are dropped iteratively, not recursively. See PR #384.

@skanfd
Copy link

skanfd commented Feb 14, 2022

Thank you for your time! @froydnj

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants