Skip to main content

script/layout_dom/
iterators.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::iter::FusedIterator;
6
7use layout_api::{DangerousStyleNode, LayoutElement, LayoutNode};
8use style::dom::{DomChildren, TElement, TShadowRoot};
9
10use crate::layout_dom::{ServoDangerousStyleElement, ServoDangerousStyleNode, ServoLayoutNode};
11
12pub struct ReverseChildrenIterator<'dom> {
13    current: Option<ServoLayoutNode<'dom>>,
14}
15
16impl<'dom> Iterator for ReverseChildrenIterator<'dom> {
17    type Item = ServoLayoutNode<'dom>;
18
19    #[expect(unsafe_code)]
20    fn next(&mut self) -> Option<Self::Item> {
21        let node = self.current;
22        self.current = node.and_then(|node| unsafe { node.dangerous_previous_sibling() });
23        node
24    }
25
26    fn size_hint(&self) -> (usize, Option<usize>) {
27        if let Some(node) = self.current {
28            (node.node.children_count() as usize, None)
29        } else {
30            (0, None)
31        }
32    }
33}
34
35pub enum ServoLayoutNodeChildrenIterator<'dom> {
36    /// Iterating over the children of a node
37    Node(Option<ServoLayoutNode<'dom>>),
38    /// Iterating over the assigned nodes of a `HTMLSlotElement`
39    Slottables(<Vec<ServoDangerousStyleNode<'dom>> as IntoIterator>::IntoIter),
40}
41
42impl<'dom> ServoLayoutNodeChildrenIterator<'dom> {
43    #[expect(unsafe_code)]
44    pub(super) fn new_for_flat_tree(parent: ServoLayoutNode<'dom>) -> Self {
45        if let Some(element) = parent.as_element() {
46            if let Some(shadow) = element.shadow_root() {
47                return Self::new_for_flat_tree(shadow.as_node().layout_node());
48            };
49
50            let element = unsafe { element.dangerous_style_element() };
51            let slotted_nodes = element.slotted_nodes();
52            if !slotted_nodes.is_empty() {
53                #[expect(clippy::unnecessary_to_owned)] // Clippy is wrong.
54                return Self::Slottables(slotted_nodes.to_owned().into_iter());
55            }
56        }
57
58        Self::Node(unsafe { parent.dangerous_first_child() })
59    }
60
61    #[expect(unsafe_code)]
62    pub(super) fn new_for_dom_tree(parent: ServoLayoutNode<'dom>) -> Self {
63        Self::Node(unsafe { parent.dangerous_first_child() })
64    }
65}
66
67impl<'dom> Iterator for ServoLayoutNodeChildrenIterator<'dom> {
68    type Item = ServoLayoutNode<'dom>;
69
70    fn next(&mut self) -> Option<Self::Item> {
71        match self {
72            Self::Node(node) => {
73                #[expect(unsafe_code)]
74                let next_sibling = unsafe { (*node)?.dangerous_next_sibling() };
75                std::mem::replace(node, next_sibling)
76            },
77            Self::Slottables(slots) => slots.next().map(|node| node.layout_node()),
78        }
79    }
80
81    fn size_hint(&self) -> (usize, Option<usize>) {
82        match self {
83            Self::Node(node) if node.is_some() => {
84                (node.unwrap().node.children_count() as usize, None)
85            },
86            _ => (0, None),
87        }
88    }
89}
90
91impl FusedIterator for ServoLayoutNodeChildrenIterator<'_> {}
92
93pub enum DOMDescendantIterator<'dom> {
94    /// Iterating over the children of a node, including children of a potential
95    /// [ShadowRoot](crate::dom::shadow_root::ShadowRoot)
96    Children(DomChildren<ServoDangerousStyleNode<'dom>>),
97    /// Iterating over the content's of a [`<slot>`](HTMLSlotElement) element.
98    Slottables {
99        slot: ServoDangerousStyleElement<'dom>,
100        index: usize,
101    },
102}
103
104impl<'dom> Iterator for DOMDescendantIterator<'dom> {
105    type Item = ServoDangerousStyleNode<'dom>;
106
107    fn next(&mut self) -> Option<Self::Item> {
108        match self {
109            Self::Children(children) => children.next(),
110            Self::Slottables { slot, index } => {
111                let slottables = slot.slotted_nodes();
112                let slot = slottables.get(*index)?;
113                *index += 1;
114                Some(*slot)
115            },
116        }
117    }
118}