Skip to main content

blitz_dom/
traversal.rs

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