diff options
author | Simon Wülker <simon.wuelker@arcor.de> | 2025-02-07 02:05:27 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-02-07 01:05:27 +0000 |
commit | 5a5d796988789ce6fe0bc3f0d72bcb80506208ab (patch) | |
tree | 08852e8f24f3c766a2e3f9085b86a0ed26a63622 /components/script/dom/node.rs | |
parent | 9faa7be302e97e33d66853843bd79272bab240d0 (diff) | |
download | servo-5a5d796988789ce6fe0bc3f0d72bcb80506208ab.tar.gz servo-5a5d796988789ce6fe0bc3f0d72bcb80506208ab.zip |
Implement ServoLayoutNode::traversal_parent (#35338)
This fixes common crash related to slottables, currently present on wpt.fyi.
Previously, the traversal parent of `Text` nodes was incorrectly
assumed to always be the parent or shadow host. That caused crashes
inside stylo's bloom filter. Now the traversal parent is the slot
that the node is assigned to, if any, and the parent/shadow host otherwise.
The slottable data for Text/Element nodes is now stored in NodeRareData.
This is very cheap, because NodeRareData will already be instantiated
for assigned slottables anyways, because the containing_shadow_root
field will be set (since assigned slottables are always in a shadow
tree). This change is necessary because we need to hand out references
to the assigned slot to stylo and that is not possible to do (without
unsafe code) if we need to downcast the node first.
As a side effect, this reduces the size of `Text` from 256 to 232 bytes,
because the slottable data is no longer stored there.
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Diffstat (limited to 'components/script/dom/node.rs')
-rw-r--r-- | components/script/dom/node.rs | 67 |
1 files changed, 54 insertions, 13 deletions
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 77be215cccb..b623ae18973 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1361,6 +1361,43 @@ impl Node { } } } + + pub(crate) fn assigned_slot(&self) -> Option<DomRoot<HTMLSlotElement>> { + let assigned_slot = self + .rare_data + .borrow() + .as_ref()? + .slottable_data + .assigned_slot + .as_ref()? + .as_rooted(); + Some(assigned_slot) + } + + pub(crate) fn set_assigned_slot(&self, assigned_slot: Option<&HTMLSlotElement>) { + self.ensure_rare_data().slottable_data.assigned_slot = assigned_slot.map(Dom::from_ref); + } + + pub(crate) fn manual_slot_assignment(&self) -> Option<DomRoot<HTMLSlotElement>> { + let manually_assigned_slot = self + .rare_data + .borrow() + .as_ref()? + .slottable_data + .manual_slot_assignment + .as_ref()? + .as_rooted(); + Some(manually_assigned_slot) + } + + pub(crate) fn set_manual_slot_assignment( + &self, + manually_assigned_slot: Option<&HTMLSlotElement>, + ) { + self.ensure_rare_data() + .slottable_data + .manual_slot_assignment = manually_assigned_slot.map(Dom::from_ref); + } } /// Iterate through `nodes` until we find a `Node` that is not in `not_in` @@ -1395,6 +1432,7 @@ pub(crate) trait LayoutNodeHelpers<'dom> { fn owner_doc_for_layout(self) -> LayoutDom<'dom, Document>; fn containing_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>>; + fn assigned_slot_for_layout(self) -> Option<LayoutDom<'dom, HTMLSlotElement>>; fn is_element_for_layout(&self) -> bool; unsafe fn get_flag(self, flag: NodeFlags) -> bool; @@ -1517,6 +1555,21 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> { } } + #[inline] + #[allow(unsafe_code)] + fn assigned_slot_for_layout(self) -> Option<LayoutDom<'dom, HTMLSlotElement>> { + unsafe { + self.unsafe_get() + .rare_data + .borrow_for_layout() + .as_ref()? + .slottable_data + .assigned_slot + .as_ref() + .map(|assigned_slot| assigned_slot.to_layout()) + } + } + // FIXME(nox): get_flag/set_flag (especially the latter) are not safe because // they mutate stuff while values of this type can be used from multiple // threads at once, this should be revisited. @@ -2379,19 +2432,7 @@ impl Node { parent.remove_child(node, cached_index); // Step 12. If node is assigned, then run assign slottables for node’s assigned slot. - let assigned_slot = node - .downcast::<Element>() - .and_then(|e| e.assigned_slot()) - .or_else(|| { - node.downcast::<Text>().and_then(|text| { - text.slottable_data() - .borrow() - .assigned_slot - .as_ref() - .map(Dom::as_rooted) - }) - }); - if let Some(slot) = assigned_slot { + if let Some(slot) = node.assigned_slot() { slot.assign_slottables(); } |