aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/layout_dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/layout_dom')
-rw-r--r--components/script/layout_dom/element.rs91
-rw-r--r--components/script/layout_dom/node.rs1
2 files changed, 84 insertions, 8 deletions
diff --git a/components/script/layout_dom/element.rs b/components/script/layout_dom/element.rs
index 45ff4450542..1abd64e1fbf 100644
--- a/components/script/layout_dom/element.rs
+++ b/components/script/layout_dom/element.rs
@@ -2,9 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::fmt;
use std::hash::Hash;
use std::sync::atomic::Ordering;
+use std::{fmt, slice};
use atomic_refcell::{AtomicRef, AtomicRefMut};
use html5ever::{local_name, namespace_url, ns, LocalName, Namespace};
@@ -40,12 +40,13 @@ use style_dom::ElementState;
use crate::dom::attr::AttrHelpersForLayout;
use crate::dom::bindings::inheritance::{
- CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId, HTMLElementTypeId, NodeTypeId,
- TextTypeId,
+ Castable, CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId, HTMLElementTypeId,
+ NodeTypeId, TextTypeId,
};
use crate::dom::bindings::root::LayoutDom;
use crate::dom::characterdata::LayoutCharacterDataHelpers;
use crate::dom::element::{Element, LayoutElementHelpers};
+use crate::dom::htmlslotelement::HTMLSlotElement;
use crate::dom::node::{LayoutNodeHelpers, Node, NodeFlags};
use crate::layout_dom::{ServoLayoutNode, ServoShadowRoot, ServoThreadSafeLayoutNode};
@@ -138,26 +139,77 @@ impl<'dom> ServoLayoutElement<'dom> {
Some(node) => matches!(node.script_type_id(), NodeTypeId::Document(_)),
}
}
+
+ fn assigned_slot(&self) -> Option<Self> {
+ let slot = self.element.get_assigned_slot()?;
+ Some(Self::from_layout_js(slot.upcast()))
+ }
+}
+
+pub enum DOMDescendantIterator<E>
+where
+ E: TElement,
+{
+ /// Iterating over the children of a node, including children of a potential
+ /// [ShadowRoot](crate::dom::shadow_root::ShadowRoot)
+ Children(DomChildren<E::ConcreteNode>),
+ /// Iterating over the content's of a [`<slot>`](HTMLSlotElement) element.
+ Slottables { slot: E, index: usize },
+}
+
+impl<E> Iterator for DOMDescendantIterator<E>
+where
+ E: TElement,
+{
+ type Item = E::ConcreteNode;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self {
+ Self::Children(children) => children.next(),
+ Self::Slottables { slot, index } => {
+ let slottables = slot.slotted_nodes();
+ let slot = slottables.get(*index)?;
+ *index += 1;
+ Some(*slot)
+ },
+ }
+ }
}
impl<'dom> style::dom::TElement for ServoLayoutElement<'dom> {
type ConcreteNode = ServoLayoutNode<'dom>;
- type TraversalChildrenIterator = DomChildren<Self::ConcreteNode>;
+ type TraversalChildrenIterator = DOMDescendantIterator<Self>;
fn as_node(&self) -> ServoLayoutNode<'dom> {
ServoLayoutNode::from_layout_js(self.element.upcast())
}
fn traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator> {
- let iterator = if let Some(shadow_root) = self.shadow_root() {
- shadow_root.as_node().dom_children()
+ let iterator = if self.slotted_nodes().is_empty() {
+ let children = if let Some(shadow_root) = self.shadow_root() {
+ shadow_root.as_node().dom_children()
+ } else {
+ self.as_node().dom_children()
+ };
+ DOMDescendantIterator::Children(children)
} else {
- self.as_node().dom_children()
+ DOMDescendantIterator::Slottables {
+ slot: *self,
+ index: 0,
+ }
};
LayoutIterator(iterator)
}
+ fn traversal_parent(&self) -> Option<Self> {
+ if let Some(assigned_slot) = self.assigned_slot() {
+ Some(assigned_slot)
+ } else {
+ self.as_node().traversal_parent()
+ }
+ }
+
fn is_html_element(&self) -> bool {
ServoLayoutElement::is_html_element(self)
}
@@ -457,6 +509,24 @@ impl<'dom> style::dom::TElement for ServoLayoutElement<'dom> {
servo_layout_node.as_element().unwrap()
}
}
+
+ fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
+ let Some(slot_element) = self.element.unsafe_get().downcast::<HTMLSlotElement>() else {
+ return &[];
+ };
+ let assigned_nodes = slot_element.assigned_nodes();
+
+ // SAFETY:
+ // Self::ConcreteNode (aka ServoLayoutNode) and Slottable are guaranteed to have the same
+ // layout and alignment as ptr::NonNull<T>. Lifetimes are not an issue because the
+ // slottables are being kept alive by the slot element.
+ unsafe {
+ slice::from_raw_parts(
+ assigned_nodes.as_ptr() as *const Self::ConcreteNode,
+ assigned_nodes.len(),
+ )
+ }
+ }
}
impl<'dom> ::selectors::Element for ServoLayoutElement<'dom> {
@@ -670,7 +740,12 @@ impl<'dom> ::selectors::Element for ServoLayoutElement<'dom> {
}
fn is_html_slot_element(&self) -> bool {
- self.element.is_html_element() && self.local_name() == &local_name!("slot")
+ self.element.is::<HTMLSlotElement>()
+ }
+
+ #[allow(unsafe_code)]
+ fn assigned_slot(&self) -> Option<Self> {
+ self.assigned_slot()
}
fn is_html_element_in_html_document(&self) -> bool {
diff --git a/components/script/layout_dom/node.rs b/components/script/layout_dom/node.rs
index 2ef11841ca6..1d9d5310a3b 100644
--- a/components/script/layout_dom/node.rs
+++ b/components/script/layout_dom/node.rs
@@ -45,6 +45,7 @@ use crate::dom::text::Text;
/// should only be used on a single thread. If you need to use nodes across
/// threads use ServoThreadSafeLayoutNode.
#[derive(Clone, Copy, PartialEq)]
+#[repr(transparent)]
pub struct ServoLayoutNode<'dom> {
/// The wrapped private DOM node.
pub(super) node: LayoutDom<'dom, Node>,