Skip to main content

bliss_dom/
traversal.rs

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