blitz_dom/
traversal.rs

1use style::{dom::TNode as _, values::specified::box_::DisplayInside};
2
3use crate::{BaseDocument, Node};
4
5#[derive(Clone)]
6/// An pre-order tree traverser for a [BaseDocument](crate::document::BaseDocument).
7pub struct TreeTraverser<'a> {
8    doc: &'a BaseDocument,
9    stack: Vec<usize>,
10}
11
12impl<'a> TreeTraverser<'a> {
13    /// Creates a new tree traverser for the given document which starts at the root node.
14    pub fn new(doc: &'a BaseDocument) -> Self {
15        Self::new_with_root(doc, 0)
16    }
17
18    /// Creates a new tree traverser for the given document which starts at the specified node.
19    pub fn new_with_root(doc: &'a BaseDocument, root: usize) -> Self {
20        let mut stack = Vec::with_capacity(32);
21        stack.push(root);
22        TreeTraverser { doc, stack }
23    }
24}
25impl Iterator for TreeTraverser<'_> {
26    type Item = usize;
27
28    fn next(&mut self) -> Option<Self::Item> {
29        let id = self.stack.pop()?;
30        let node = self.doc.get_node(id)?;
31        self.stack.extend(node.children.iter().rev());
32        Some(id)
33    }
34}
35
36#[derive(Clone)]
37/// An ancestor traverser for a [BaseDocument](crate::document::BaseDocument).
38pub struct AncestorTraverser<'a> {
39    doc: &'a BaseDocument,
40    current: usize,
41}
42impl<'a> AncestorTraverser<'a> {
43    /// Creates a new ancestor traverser for the given document and node ID.
44    pub fn new(doc: &'a BaseDocument, node_id: usize) -> Self {
45        AncestorTraverser {
46            doc,
47            current: node_id,
48        }
49    }
50}
51impl Iterator for AncestorTraverser<'_> {
52    type Item = usize;
53
54    fn next(&mut self) -> Option<Self::Item> {
55        let current_node = self.doc.get_node(self.current)?;
56        self.current = current_node.parent?;
57        Some(self.current)
58    }
59}
60
61impl Node {
62    #[allow(dead_code)]
63    pub(crate) fn should_traverse_layout_children(&mut self) -> bool {
64        let prefer_layout_children = match self.display_constructed_as.inside() {
65            DisplayInside::None => return false,
66            DisplayInside::Contents => false,
67            DisplayInside::Flow | DisplayInside::FlowRoot | DisplayInside::TableCell => {
68                // Prefer layout children for "block" but not "inline" contexts
69                self.element_data()
70                    .is_none_or(|el| el.inline_layout_data.is_none())
71            }
72            DisplayInside::Flex | DisplayInside::Grid => true,
73            DisplayInside::Table => false,
74            DisplayInside::TableRowGroup => false,
75            DisplayInside::TableColumn => false,
76            DisplayInside::TableColumnGroup => false,
77            DisplayInside::TableHeaderGroup => false,
78            DisplayInside::TableFooterGroup => false,
79            DisplayInside::TableRow => false,
80        };
81        let has_layout_children = self.layout_children.get_mut().is_some();
82        prefer_layout_children & has_layout_children
83    }
84}
85
86impl BaseDocument {
87    /// Collect the nodes into a chain by traversing upwards
88    pub fn node_chain(&self, node_id: usize) -> Vec<usize> {
89        let mut chain = Vec::with_capacity(16);
90        chain.push(node_id);
91        chain.extend(
92            AncestorTraverser::new(self, node_id).filter(|id| self.nodes[*id].is_element()),
93        );
94        chain
95    }
96
97    pub fn visit<F>(&self, mut visit: F)
98    where
99        F: FnMut(usize, &Node),
100    {
101        TreeTraverser::new(self).for_each(|node_id| visit(node_id, &self.nodes[node_id]));
102    }
103
104    /// If the node is non-anonymous then returns the node's id
105    /// Else find's the first non-anonymous ancester of the node
106    pub fn non_anon_ancestor_if_anon(&self, mut node_id: usize) -> usize {
107        loop {
108            let node = &self.nodes[node_id];
109
110            if !node.is_anonymous() {
111                return node.id;
112            }
113
114            let Some(parent_id) = node.layout_parent.get() else {
115                // Shouldn't be reachable unless invalid node_id is passed
116                // as root node is always non-anonymous
117                panic!("Node does not exist or does not have a non-anonymous parent");
118            };
119
120            node_id = parent_id;
121        }
122    }
123
124    pub fn iter_children_mut(
125        &mut self,
126        node_id: usize,
127        mut cb: impl FnMut(usize, &mut BaseDocument),
128    ) {
129        let children = std::mem::take(&mut self.nodes[node_id].children);
130        for child_id in children.iter().cloned() {
131            cb(child_id, self);
132        }
133        self.nodes[node_id].children = children;
134    }
135
136    pub fn iter_subtree_mut(
137        &mut self,
138        node_id: usize,
139        mut cb: impl FnMut(usize, &mut BaseDocument),
140    ) {
141        cb(node_id, self);
142        iter_subtree_mut_inner(self, node_id, &mut cb);
143        fn iter_subtree_mut_inner(
144            doc: &mut BaseDocument,
145            node_id: usize,
146            cb: &mut impl FnMut(usize, &mut BaseDocument),
147        ) {
148            let children = std::mem::take(&mut doc.nodes[node_id].children);
149            for child_id in children.iter().cloned() {
150                cb(child_id, doc);
151                iter_subtree_mut_inner(doc, child_id, cb);
152            }
153            doc.nodes[node_id].children = children;
154        }
155    }
156
157    pub fn iter_children_and_pseudos_mut(
158        &mut self,
159        node_id: usize,
160        mut cb: impl FnMut(usize, &mut BaseDocument),
161    ) {
162        let before = self.nodes[node_id].before.take();
163        if let Some(before_node_id) = before {
164            cb(before_node_id, self)
165        }
166        self.nodes[node_id].before = before;
167
168        self.iter_children_mut(node_id, &mut cb);
169
170        let after = self.nodes[node_id].after.take();
171        if let Some(after_node_id) = after {
172            cb(after_node_id, self)
173        }
174        self.nodes[node_id].after = after;
175    }
176
177    pub fn next_node(&self, start: &Node, mut filter: impl FnMut(&Node) -> bool) -> Option<usize> {
178        let start_id = start.id;
179        let mut node = start;
180        let mut look_in_children = true;
181        loop {
182            // Next is first child
183            let next = if look_in_children && !node.children.is_empty() {
184                let node_id = node.children[0];
185                &self.nodes[node_id]
186            }
187            // Next is next sibling or parent
188            else if let Some(parent) = node.parent_node() {
189                let self_idx = parent
190                    .children
191                    .iter()
192                    .position(|id| *id == node.id)
193                    .unwrap();
194                // Next is next sibling
195                if let Some(sibling_id) = parent.children.get(self_idx + 1) {
196                    look_in_children = true;
197                    &self.nodes[*sibling_id]
198                }
199                // Next is parent
200                else {
201                    look_in_children = false;
202                    node = parent;
203                    continue;
204                }
205            }
206            // Continue search from the root
207            else {
208                look_in_children = true;
209                self.root_node()
210            };
211
212            if filter(next) {
213                return Some(next.id);
214            } else if next.id == start_id {
215                return None;
216            }
217
218            node = next;
219        }
220    }
221
222    pub fn node_layout_ancestors(&self, node_id: usize) -> Vec<usize> {
223        let mut ancestors = Vec::with_capacity(12);
224        let mut maybe_id = Some(node_id);
225        while let Some(id) = maybe_id {
226            ancestors.push(id);
227            maybe_id = self.nodes[id].layout_parent.get();
228        }
229        ancestors.reverse();
230        ancestors
231    }
232
233    pub fn maybe_node_layout_ancestors(&self, node_id: Option<usize>) -> Vec<usize> {
234        node_id
235            .map(|id| self.node_layout_ancestors(id))
236            .unwrap_or_default()
237    }
238}