From 37634b1251a10cdb873750456026ff3bf68693c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20W=C3=BClker?= Date: Thu, 6 Mar 2025 10:24:14 +0100 Subject: [PATCH] Let layout invalidations happen in the flat tree (#35769) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When invalidating layout, computing dirty roots and such, we want to work in the flat tree. That means that the children of slots are their assigned slottables, parents of assigned slottables are their slots and the children of an element include children of a potential shadow dom. Signed-off-by: Simon Wülker --- components/script/dom/document.rs | 14 +++++------- components/script/dom/node.rs | 37 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 63e465ccae8..f1e11a38a7d 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -528,7 +528,7 @@ impl Document { return; } - let parent = match node.inclusive_ancestors(ShadowIncluding::Yes).nth(1) { + let parent = match node.parent_in_flat_tree() { Some(parent) => parent, None => { // There is no parent so this is the Document node, so we @@ -542,7 +542,7 @@ impl Document { // ancestors as dirty until the document element. for ancestor in dirty_root .upcast::() - .inclusive_ancestors(ShadowIncluding::Yes) + .inclusive_ancestors_in_flat_tree() { if ancestor.is::() { ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true); @@ -596,13 +596,11 @@ impl Document { Some(root) => root, }; - for ancestor in element - .upcast::() - .inclusive_ancestors(ShadowIncluding::Yes) - { + for ancestor in element.upcast::().inclusive_ancestors_in_flat_tree() { if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) { return; } + if ancestor.is::() { ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true); } @@ -610,13 +608,13 @@ impl Document { let new_dirty_root = element .upcast::() - .common_ancestor(dirty_root.upcast(), ShadowIncluding::Yes) + .common_ancestor_in_flat_tree(dirty_root.upcast()) .expect("Couldn't find common ancestor"); let mut has_dirty_descendants = true; for ancestor in dirty_root .upcast::() - .inclusive_ancestors(ShadowIncluding::Yes) + .inclusive_ancestors_in_flat_tree() { ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, has_dirty_descendants); has_dirty_descendants &= *ancestor != *new_dirty_root; diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 4653139ed12..428a8d075b3 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -777,6 +777,14 @@ impl Node { }) } + pub(crate) fn common_ancestor_in_flat_tree(&self, other: &Node) -> Option> { + self.inclusive_ancestors_in_flat_tree().find(|ancestor| { + other + .inclusive_ancestors_in_flat_tree() + .any(|node| node == *ancestor) + }) + } + pub(crate) fn is_inclusive_ancestor_of(&self, parent: &Node) -> bool { self == parent || self.is_ancestor_of(parent) } @@ -1395,6 +1403,35 @@ impl Node { .slottable_data .manual_slot_assignment = manually_assigned_slot.map(Dom::from_ref); } + + /// Gets the parent of this node from the perspective of layout and style. + /// + /// The returned node is the node's assigned slot, if any, or the + /// shadow host if it's a shadow root. Otherwise, it is the node's + /// parent. + pub(crate) fn parent_in_flat_tree(&self) -> Option> { + if let Some(assigned_slot) = self.assigned_slot() { + return Some(DomRoot::upcast(assigned_slot)); + } + + let parent_or_none = self.GetParentNode(); + if let Some(parent) = parent_or_none.as_deref() { + if let Some(shadow_root) = parent.downcast::() { + return Some(DomRoot::from_ref(shadow_root.Host().upcast::())); + } + } + + parent_or_none + } + + pub(crate) fn inclusive_ancestors_in_flat_tree( + &self, + ) -> impl Iterator> + use<> { + SimpleNodeIterator { + current: Some(DomRoot::from_ref(self)), + next_node: move |n| n.parent_in_flat_tree(), + } + } } /// Iterate through `nodes` until we find a `Node` that is not in `not_in`