Fix IS_IN_SHADOW_TREE flag for descendants after Node::remove call (#34803)

* Consider a UnbindContext to be tree-connected if its in a shadow root

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Properly track whether a node is in a shadow tree after removing subtree

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Update WPT expectations

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2025-01-04 00:27:43 +01:00 committed by GitHub
parent e8f75c9aea
commit 1ab55e6b11
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 34 additions and 15 deletions

View file

@ -3665,7 +3665,11 @@ impl VirtualMethods for Element {
} }
if let Some(ref value) = *self.id_attribute.borrow() { if let Some(ref value) = *self.id_attribute.borrow() {
if let Some(ref shadow_root) = self.containing_shadow_root() { if let Some(ref shadow_root) = self.containing_shadow_root() {
// Only unregister the element id if the node was disconnected from it's shadow root
// (as opposed to the whole shadow tree being disconnected as a whole)
if !self.upcast::<Node>().is_in_shadow_tree() {
shadow_root.unregister_element_id(self, value.clone()); shadow_root.unregister_element_id(self, value.clone());
}
} else { } else {
doc.unregister_element_id(self, value.clone()); doc.unregister_element_id(self, value.clone());
} }

View file

@ -316,17 +316,28 @@ impl Node {
/// Clean up flags and unbind from tree. /// Clean up flags and unbind from tree.
pub fn complete_remove_subtree(root: &Node, context: &UnbindContext) { pub fn complete_remove_subtree(root: &Node, context: &UnbindContext) {
for node in root.traverse_preorder(ShadowIncluding::Yes) { // Flags that reset when a node is disconnected
// Out-of-document elements never have the descendants flag set. const RESET_FLAGS: NodeFlags = NodeFlags::IS_IN_DOC
node.set_flag( .union(NodeFlags::IS_CONNECTED)
NodeFlags::IS_IN_DOC | .union(NodeFlags::HAS_DIRTY_DESCENDANTS)
NodeFlags::IS_CONNECTED | .union(NodeFlags::HAS_SNAPSHOT)
NodeFlags::HAS_DIRTY_DESCENDANTS | .union(NodeFlags::HANDLED_SNAPSHOT);
NodeFlags::HAS_SNAPSHOT |
NodeFlags::HANDLED_SNAPSHOT, for node in root.traverse_preorder(ShadowIncluding::No) {
false, node.set_flag(RESET_FLAGS | NodeFlags::IS_IN_SHADOW_TREE, false);
);
// If the element has a shadow root attached to it then we traverse that as well,
// but without touching the IS_IN_SHADOW_TREE flags of the children
if let Some(shadow_root) = node.downcast::<Element>().and_then(Element::shadow_root) {
for node in shadow_root
.upcast::<Node>()
.traverse_preorder(ShadowIncluding::Yes)
{
node.set_flag(RESET_FLAGS, false);
} }
}
}
for node in root.traverse_preorder(ShadowIncluding::Yes) { for node in root.traverse_preorder(ShadowIncluding::Yes) {
node.clean_up_style_and_layout_data(); node.clean_up_style_and_layout_data();
@ -608,6 +619,11 @@ impl Node {
self.flags.get().contains(NodeFlags::IS_CONNECTED) self.flags.get().contains(NodeFlags::IS_CONNECTED)
} }
/// Return true iff the node's root is a Document or a ShadowRoot
pub fn is_connected_to_tree(&self) -> bool {
self.is_connected() || self.is_in_shadow_tree()
}
/// Returns the type ID of this node. /// Returns the type ID of this node.
pub fn type_id(&self) -> NodeTypeId { pub fn type_id(&self) -> NodeTypeId {
match *self.eventtarget.type_id() { match *self.eventtarget.type_id() {
@ -3559,6 +3575,8 @@ pub struct UnbindContext<'a> {
/// The next sibling of the inclusive ancestor that was removed. /// The next sibling of the inclusive ancestor that was removed.
pub next_sibling: Option<&'a Node>, pub next_sibling: Option<&'a Node>,
/// Whether the tree is connected. /// Whether the tree is connected.
///
/// A tree is connected iff it's root is a Document or a ShadowRoot.
pub tree_connected: bool, pub tree_connected: bool,
/// Whether the tree is in doc. /// Whether the tree is in doc.
pub tree_in_doc: bool, pub tree_in_doc: bool,
@ -3577,7 +3595,7 @@ impl<'a> UnbindContext<'a> {
parent, parent,
prev_sibling, prev_sibling,
next_sibling, next_sibling,
tree_connected: parent.is_connected(), tree_connected: parent.is_connected_to_tree(),
tree_in_doc: parent.is_in_doc(), tree_in_doc: parent.is_in_doc(),
} }
} }

View file

@ -1,3 +0,0 @@
[getElementById-dynamic-001.html]
[ShadowRoot.getElementById keeps working after host has been removed]
expected: FAIL