Skip to main content

ps_blitz_dom/
traversal.rs

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