Skip to main content

blitz_dom/
traversal.rs

1use blitz_traits::node_id::NodeId;
2use std::cmp::Ordering;
3
4use style::dom::TNode as _;
5
6use crate::{BaseDocument, Node};
7
8macro_rules! iter_children {
9    ($node_expr:expr, $cb:expr) => {{
10        let node = &mut $node_expr;
11        let children = core::mem::take(&mut node.children);
12        for child_id in children.iter().copied() {
13            $cb(child_id)
14        }
15        $node_expr.children = children;
16    }};
17}
18pub(crate) use iter_children;
19
20macro_rules! iter_children_and_pseudos {
21    ($node_expr:expr, $cb:expr) => {{
22        // Load node
23        let node = &mut $node_expr;
24
25        // Copy before, after, and take children
26        let before = node.before();
27        let after = node.after();
28        let children = core::mem::take(&mut node.children);
29
30        if let Some(before) = before {
31            $cb(before)
32        }
33        for child_id in children.iter().copied() {
34            $cb(child_id)
35        }
36        if let Some(after) = after {
37            $cb(after)
38        }
39
40        // Reload node and put children back
41        $node_expr.children = children;
42    }};
43}
44pub(crate) use iter_children_and_pseudos;
45
46#[derive(Clone)]
47/// An pre-order tree traverser for a [BaseDocument](crate::document::BaseDocument).
48pub struct TreeTraverser<'a> {
49    doc: &'a BaseDocument,
50    stack: Vec<NodeId>,
51}
52
53impl<'a> TreeTraverser<'a> {
54    /// Creates a new tree traverser for the given document which starts at the root node.
55    pub fn new(doc: &'a BaseDocument) -> Self {
56        Self::new_with_root(doc, doc.root_node().id)
57    }
58
59    /// Creates a new tree traverser for the given document which starts at the specified node.
60    pub fn new_with_root(doc: &'a BaseDocument, root: NodeId) -> Self {
61        let mut stack = Vec::with_capacity(32);
62        stack.push(root);
63        TreeTraverser { doc, stack }
64    }
65}
66impl Iterator for TreeTraverser<'_> {
67    type Item = NodeId;
68
69    fn next(&mut self) -> Option<Self::Item> {
70        let id = self.stack.pop()?;
71        let node = self.doc.get_node(id)?;
72        self.stack.extend(node.children.iter().rev());
73        Some(id)
74    }
75}
76
77#[derive(Clone)]
78/// An ancestor traverser for a [BaseDocument](crate::document::BaseDocument).
79pub struct AncestorTraverser<'a> {
80    doc: &'a BaseDocument,
81    current: NodeId,
82}
83impl<'a> AncestorTraverser<'a> {
84    /// Creates a new ancestor traverser for the given document and node ID.
85    pub fn new(doc: &'a BaseDocument, node_id: NodeId) -> Self {
86        AncestorTraverser {
87            doc,
88            current: node_id,
89        }
90    }
91}
92impl Iterator for AncestorTraverser<'_> {
93    type Item = NodeId;
94
95    fn next(&mut self) -> Option<Self::Item> {
96        let current_node = self.doc.get_node(self.current)?;
97        self.current = current_node.parent?;
98        Some(self.current)
99    }
100}
101
102impl BaseDocument {
103    /// Collect the nodes into a chain by traversing upwards
104    pub fn node_chain(&self, node_id: NodeId) -> Vec<NodeId> {
105        let mut chain = Vec::with_capacity(16);
106        chain.push(node_id);
107        chain.extend(
108            AncestorTraverser::new(self, node_id).filter(|id| self.nodes[*id].is_element()),
109        );
110        chain
111    }
112
113    pub fn visit<F>(&self, mut visit: F)
114    where
115        F: FnMut(NodeId, &Node),
116    {
117        TreeTraverser::new(self).for_each(|node_id| visit(node_id, &self.nodes[node_id]));
118    }
119
120    /// If the node is non-anonymous then returns the node's id
121    /// Else find's the first non-anonymous ancester of the node
122    pub fn non_anon_ancestor_if_anon(&self, mut node_id: NodeId) -> NodeId {
123        loop {
124            let node = &self.nodes[node_id];
125
126            if !node.is_anonymous() {
127                return node.id;
128            }
129
130            let Some(parent_id) = node.layout_parent.get() else {
131                // Shouldn't be reachable unless invalid node_id is passed
132                // as root node is always non-anonymous
133                panic!("Node does not exist or does not have a non-anonymous parent");
134            };
135
136            node_id = parent_id;
137        }
138    }
139
140    pub fn iter_children_mut(
141        &mut self,
142        node_id: NodeId,
143        mut cb: impl FnMut(NodeId, &mut BaseDocument),
144    ) {
145        let children = std::mem::take(&mut self.nodes[node_id].children);
146        for child_id in children.iter().cloned() {
147            cb(child_id, self);
148        }
149        self.nodes[node_id].children = children;
150    }
151
152    pub fn iter_subtree_mut(
153        &mut self,
154        node_id: NodeId,
155        mut cb: impl FnMut(NodeId, &mut BaseDocument),
156    ) {
157        cb(node_id, self);
158        iter_subtree_mut_inner(self, node_id, &mut cb);
159        fn iter_subtree_mut_inner(
160            doc: &mut BaseDocument,
161            node_id: NodeId,
162            cb: &mut impl FnMut(NodeId, &mut BaseDocument),
163        ) {
164            let children = std::mem::take(&mut doc.nodes[node_id].children);
165            for child_id in children.iter().cloned() {
166                cb(child_id, doc);
167                iter_subtree_mut_inner(doc, child_id, cb);
168            }
169            doc.nodes[node_id].children = children;
170        }
171    }
172
173    pub fn iter_children_and_pseudos_mut(
174        &mut self,
175        node_id: NodeId,
176        mut cb: impl FnMut(NodeId, &mut BaseDocument),
177    ) {
178        let before = self.nodes[node_id].before();
179        self.nodes[node_id].set_pe_by_index(1, None);
180        if let Some(before_node_id) = before {
181            cb(before_node_id, self)
182        }
183        self.nodes[node_id].set_pe_by_index(1, before);
184
185        self.iter_children_mut(node_id, &mut cb);
186
187        let after = self.nodes[node_id].after();
188        self.nodes[node_id].set_pe_by_index(0, None);
189        if let Some(after_node_id) = after {
190            cb(after_node_id, self)
191        }
192        self.nodes[node_id].set_pe_by_index(0, after);
193    }
194
195    pub fn next_node(&self, start: &Node, mut filter: impl FnMut(&Node) -> bool) -> Option<NodeId> {
196        let start_id = start.id;
197        let mut node = start;
198        let mut look_in_children = true;
199        loop {
200            // Next is first child
201            let next = if look_in_children && !node.children.is_empty() {
202                let node_id = node.children[0];
203                &self.nodes[node_id]
204            }
205            // Next is next sibling or parent
206            else if let Some(parent) = node.parent_node() {
207                let self_idx = parent
208                    .children
209                    .iter()
210                    .position(|id| *id == node.id)
211                    .unwrap();
212                // Next is next sibling
213                if let Some(sibling_id) = parent.children.get(self_idx + 1) {
214                    look_in_children = true;
215                    &self.nodes[*sibling_id]
216                }
217                // Next is parent
218                else {
219                    look_in_children = false;
220                    node = parent;
221                    continue;
222                }
223            }
224            // Continue search from the root
225            else {
226                look_in_children = true;
227                self.root_node()
228            };
229
230            if filter(next) {
231                return Some(next.id);
232            } else if next.id == start_id {
233                return None;
234            }
235
236            node = next;
237        }
238    }
239
240    /// The node that comes last within `node`'s subtree in document order,
241    /// which is what precedes `node`'s successor in reverse order.
242    fn deepest_last_descendant<'a>(&'a self, mut node: &'a Node) -> &'a Node {
243        while let Some(last_child_id) = node.children.last() {
244            node = &self.nodes[*last_child_id];
245        }
246        node
247    }
248
249    /// Mirror of [`Self::next_node`]: walks the tree in reverse document
250    /// order, wrapping around to the end of the document.
251    pub fn prev_node(&self, start: &Node, mut filter: impl FnMut(&Node) -> bool) -> Option<NodeId> {
252        let start_id = start.id;
253        let mut node = start;
254        loop {
255            let prev = if let Some(parent) = node.parent_node() {
256                let self_idx = parent
257                    .children
258                    .iter()
259                    .position(|id| *id == node.id)
260                    .unwrap();
261                // Previous is the deepest last descendant of the previous
262                // sibling, or the parent when there is no previous sibling
263                if self_idx > 0 {
264                    self.deepest_last_descendant(&self.nodes[parent.children[self_idx - 1]])
265                } else {
266                    parent
267                }
268            }
269            // Continue the search from the end of the document
270            else {
271                self.deepest_last_descendant(self.root_node())
272            };
273
274            if filter(prev) {
275                return Some(prev.id);
276            } else if prev.id == start_id {
277                return None;
278            }
279
280            node = prev;
281        }
282    }
283
284    /// `node_id` and its ancestor elements in the DOM, root first. These are
285    /// the elements that match `:hover`/`:active` when `node_id` does.
286    pub fn node_element_ancestors(&self, node_id: NodeId) -> Vec<NodeId> {
287        let mut ancestors = Vec::with_capacity(12);
288        let mut maybe_id = Some(node_id);
289        while let Some(id) = maybe_id {
290            let node = &self.nodes[id];
291            if !node.is_element() {
292                break;
293            }
294            ancestors.push(id);
295            maybe_id = node.parent;
296        }
297        ancestors.reverse();
298        ancestors
299    }
300
301    pub fn maybe_node_element_ancestors(&self, node_id: Option<NodeId>) -> Vec<NodeId> {
302        node_id
303            .map(|id| self.node_element_ancestors(id))
304            .unwrap_or_default()
305    }
306
307    /// Compare the document order of two nodes.
308    /// Returns Ordering::Less if node_a comes before node_b in document order.
309    /// Returns Ordering::Greater if node_a comes after node_b.
310    /// Returns Ordering::Equal if they are the same node.
311    pub fn compare_document_order(&self, node_a: NodeId, node_b: NodeId) -> Ordering {
312        if node_a == node_b {
313            return Ordering::Equal;
314        }
315
316        // Build ancestor chains from root to node (inclusive)
317        let chain_a = self.ancestor_chain_from_root(node_a);
318        let chain_b = self.ancestor_chain_from_root(node_b);
319
320        // Find where the chains diverge
321        let mut common_depth = 0;
322        for (a, b) in chain_a.iter().zip(chain_b.iter()) {
323            if a != b {
324                break;
325            }
326            common_depth += 1;
327        }
328
329        // If one is an ancestor of the other
330        if common_depth == chain_a.len() {
331            return Ordering::Less; // node_a is ancestor of node_b
332        }
333        if common_depth == chain_b.len() {
334            return Ordering::Greater; // node_b is ancestor of node_a
335        }
336
337        // Safety: common_depth must be > 0 here because both chains start from the same
338        // root node (node 0), so they share at least that node. If common_depth were 0,
339        // chain_a[0] != chain_b[0], but both start from root, so this is impossible.
340        debug_assert!(
341            common_depth > 0,
342            "nodes must share a common ancestor (the root)"
343        );
344
345        // Compare position among siblings at the divergence point
346        let divergent_a = chain_a[common_depth];
347        let divergent_b = chain_b[common_depth];
348        let parent_id = chain_a[common_depth - 1];
349        let parent = &self.nodes[parent_id];
350
351        for &child_id in &parent.children {
352            if child_id == divergent_a {
353                return Ordering::Less;
354            }
355            if child_id == divergent_b {
356                return Ordering::Greater;
357            }
358        }
359
360        // Should not reach here if tree is well-formed
361        Ordering::Equal
362    }
363
364    /// Build ancestor chain from root to node (inclusive), ordered [root, ..., node].
365    fn ancestor_chain_from_root(&self, node_id: NodeId) -> Vec<NodeId> {
366        let mut ancestors = Vec::with_capacity(16);
367        let mut current = Some(node_id);
368        while let Some(id) = current {
369            ancestors.push(id);
370            current = self.nodes[id].parent;
371        }
372        ancestors.reverse();
373        ancestors
374    }
375
376    /// Collect all inline root nodes between start_node and end_node in document order.
377    /// Both start and end are assumed to be inline roots.
378    /// Returns the nodes in document order (from first to last).
379    pub fn collect_inline_roots_in_range(
380        &self,
381        start_node: NodeId,
382        end_node: NodeId,
383    ) -> Vec<NodeId> {
384        // Resolve nodes: for anonymous blocks, get (parent_id, Some(anon_id)); for regular, (node_id, None)
385        let (start_anchor, start_anon) = self.resolve_for_traversal(start_node);
386        let (end_anchor, end_anon) = self.resolve_for_traversal(end_node);
387
388        // If both are anonymous blocks with the same parent, just collect from layout_children
389        if start_anon.is_some() && end_anon.is_some() && start_anchor == end_anchor {
390            return self.collect_anonymous_siblings(start_anchor, start_node, end_node);
391        }
392
393        // Determine first/last based on document order (using anchors for comparison)
394        let (first_anchor, first_anon, last_anchor, last_anon) = match self
395            .compare_document_order(start_anchor, end_anchor)
396        {
397            Ordering::Less | Ordering::Equal => (start_anchor, start_anon, end_anchor, end_anon),
398            Ordering::Greater => (end_anchor, end_anon, start_anchor, start_anon),
399        };
400
401        let mut result = Vec::new();
402        let mut found_first = false;
403
404        // Traverse tree in document order
405        for node_id in TreeTraverser::new(self) {
406            if !found_first {
407                if node_id == first_anchor {
408                    found_first = true;
409                    if let Some(anon_id) = first_anon {
410                        // First is anonymous: collect from this parent starting at anon_id
411                        // Stop at last_anchor if different parent, or last_anon if same parent
412                        let stop_at = if first_anchor == last_anchor {
413                            // Same parent: stop at last_anon
414                            last_anon
415                        } else {
416                            // Different parents: stop at last_anchor (which is a child of first_anchor)
417                            Some(last_anchor)
418                        };
419                        self.collect_layout_children_inline_roots(
420                            node_id,
421                            Some(anon_id),
422                            stop_at,
423                            &mut result,
424                        );
425                        // If we collected up to last, we're done
426                        if result.last() == Some(&last_anchor)
427                            || last_anon.is_some_and(|la| result.last() == Some(&la))
428                        {
429                            break;
430                        }
431                        continue;
432                    }
433                }
434            }
435
436            if found_first {
437                if node_id == last_anchor {
438                    if let Some(anon_id) = last_anon {
439                        // Last is anonymous: collect up to anon_id (exclusive), then include anon_id
440                        self.collect_layout_children_inline_roots(
441                            node_id,
442                            None,
443                            Some(anon_id),
444                            &mut result,
445                        );
446                        // Include the last_anon itself (until is exclusive, so we add it here)
447                        if !result.contains(&anon_id) {
448                            result.push(anon_id);
449                        }
450                    } else {
451                        // Last is regular: include it if it's an inline root and not already collected
452                        let node = &self.nodes[node_id];
453                        if node.flags.is_inline_root() && !result.contains(&node_id) {
454                            result.push(node_id);
455                        }
456                    }
457                    break;
458                }
459
460                let node = &self.nodes[node_id];
461                if node.flags.is_inline_root() && !result.contains(&node_id) {
462                    result.push(node_id);
463                } else {
464                    // For non-inline-root nodes, collect any inline roots from their layout_children
465                    // This handles intermediate block containers with anonymous block children
466                    self.collect_layout_children_inline_roots(
467                        node_id,
468                        None,
469                        Some(last_anchor),
470                        &mut result,
471                    );
472                }
473            }
474        }
475
476        result
477    }
478
479    /// Resolve a node for traversal purposes.
480    /// For anonymous blocks: returns (parent_id, Some(node_id))
481    /// For regular nodes: returns (node_id, None)
482    fn resolve_for_traversal(&self, node_id: NodeId) -> (NodeId, Option<NodeId>) {
483        let node = &self.nodes[node_id];
484        if node.is_anonymous() {
485            (node.parent.unwrap_or(node_id), Some(node_id))
486        } else {
487            (node_id, None)
488        }
489    }
490
491    /// Collect anonymous block siblings between start and end (inclusive)
492    /// Also recursively collects inline roots from any block children in between
493    fn collect_anonymous_siblings(
494        &self,
495        parent_id: NodeId,
496        start: NodeId,
497        end: NodeId,
498    ) -> Vec<NodeId> {
499        let parent = &self.nodes[parent_id];
500        let layout_children = parent.layout_children.borrow();
501        let Some(children) = layout_children.as_ref() else {
502            return Vec::new();
503        };
504
505        let start_idx = children.iter().position(|&id| id == start);
506        let end_idx = children.iter().position(|&id| id == end);
507
508        let (first_idx, last_idx) = match (start_idx, end_idx) {
509            (Some(s), Some(e)) if s <= e => (s, e),
510            (Some(s), Some(e)) => (e, s),
511            _ => return Vec::new(),
512        };
513
514        let mut result = Vec::new();
515        for &child_id in &children[first_idx..=last_idx] {
516            let child = &self.nodes[child_id];
517            if child.flags.is_inline_root() {
518                result.push(child_id);
519            } else {
520                // For non-inline-root children (block containers), collect all their inline roots
521                self.collect_all_inline_roots_in_subtree(child_id, &mut result);
522            }
523        }
524        result
525    }
526
527    /// Recursively collect all inline roots from a node's layout_children subtree
528    fn collect_all_inline_roots_in_subtree(&self, node_id: NodeId, result: &mut Vec<NodeId>) {
529        let node = &self.nodes[node_id];
530        let layout_children = node.layout_children.borrow();
531        let Some(children) = layout_children.as_ref() else {
532            return;
533        };
534
535        for &child_id in children.iter() {
536            let child = &self.nodes[child_id];
537            if child.flags.is_inline_root() {
538                result.push(child_id);
539            } else {
540                // Recurse into block children
541                self.collect_all_inline_roots_in_subtree(child_id, result);
542            }
543        }
544    }
545
546    /// Collect inline roots from a parent's layout_children.
547    /// - `from`: If Some, start collecting from this node; if None, start from beginning
548    /// - `until`: If Some, stop when we reach this node OR a node that contains it; if None, collect to end
549    fn collect_layout_children_inline_roots(
550        &self,
551        parent_id: NodeId,
552        from: Option<NodeId>,
553        until: Option<NodeId>,
554        result: &mut Vec<NodeId>,
555    ) {
556        let parent = &self.nodes[parent_id];
557        let layout_children = parent.layout_children.borrow();
558        let Some(children) = layout_children.as_ref() else {
559            return;
560        };
561
562        let mut collecting = from.is_none(); // Start immediately if no 'from' specified
563        for &child_id in children.iter() {
564            if from == Some(child_id) {
565                collecting = true;
566            }
567            if collecting {
568                // Stop without adding if this child contains the 'until' node (it will be processed later)
569                if let Some(until_id) = until {
570                    if self.is_ancestor_of(child_id, until_id) {
571                        break;
572                    }
573                }
574                // Stop before processing if this child IS the 'until' node
575                if until == Some(child_id) {
576                    break;
577                }
578                let child = &self.nodes[child_id];
579                if child.flags.is_inline_root() {
580                    result.push(child_id);
581                } else {
582                    // For non-inline-root children (block containers), recursively collect their inline roots
583                    self.collect_all_inline_roots_in_subtree(child_id, result);
584                }
585            }
586        }
587    }
588
589    /// Check if `ancestor_id` is an ancestor of `descendant_id`
590    fn is_ancestor_of(&self, ancestor_id: NodeId, descendant_id: NodeId) -> bool {
591        let mut current = descendant_id;
592        while let Some(parent) = self.nodes[current].parent {
593            if parent == ancestor_id {
594                return true;
595            }
596            current = parent;
597        }
598        false
599    }
600}