Skip to main content

script/dom/node/
treewalker.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use script_bindings::callback::{OwnerWindow, RootedCallback, TracedCallback};
10use script_bindings::reflector::{Reflector, reflect_dom_object};
11use script_bindings::script_runtime::temp_cx;
12
13use crate::dom::bindings::callback::ExceptionHandling::Rethrow;
14use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
15use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::{NodeFilter, NodeFilterConstants};
16use crate::dom::bindings::codegen::Bindings::TreeWalkerBinding::TreeWalkerMethods;
17use crate::dom::bindings::error::{Error, Fallible};
18use crate::dom::bindings::root::{Dom, DomRoot, MutDom};
19use crate::dom::document::Document;
20use crate::dom::node::Node;
21
22// https://dom.spec.whatwg.org/#interface-treewalker
23#[dom_struct]
24pub(crate) struct TreeWalker {
25    reflector_: Reflector,
26    root_node: Dom<Node>,
27    current_node: MutDom<Node>,
28    what_to_show: u32,
29    #[ignore_malloc_size_of = "function pointers and Rc<T> are hard"]
30    filter: Filter,
31    active: Cell<bool>,
32}
33
34impl TreeWalker {
35    fn new_inherited(
36        root_node: &Node,
37        what_to_show: u32,
38        node_filter: Option<RootedCallback<NodeFilter>>,
39    ) -> TreeWalker {
40        TreeWalker {
41            reflector_: Reflector::new(),
42            root_node: Dom::from_ref(root_node),
43            current_node: MutDom::new(root_node),
44            what_to_show,
45            filter: match node_filter {
46                None => Filter::None,
47                Some(jsfilter) => Filter::Dom(jsfilter.to_traced()),
48            },
49            active: Cell::new(false),
50        }
51    }
52
53    pub(crate) fn new_with_filter(
54        cx: &mut JSContext,
55        document: &Document,
56        root_node: &Node,
57        what_to_show: u32,
58        node_filter: Option<RootedCallback<NodeFilter>>,
59    ) -> DomRoot<TreeWalker> {
60        reflect_dom_object(
61            cx,
62            Box::new(TreeWalker::new_inherited(
63                root_node,
64                what_to_show,
65                node_filter,
66            )),
67            document.window(),
68        )
69    }
70
71    pub(crate) fn new(
72        cx: &mut JSContext,
73        document: &Document,
74        root_node: &Node,
75        what_to_show: u32,
76        node_filter: Option<RootedCallback<NodeFilter>>,
77    ) -> DomRoot<TreeWalker> {
78        TreeWalker::new_with_filter(cx, document, root_node, what_to_show, node_filter)
79    }
80}
81
82impl TreeWalkerMethods<crate::DomTypeHolder> for TreeWalker {
83    /// <https://dom.spec.whatwg.org/#dom-treewalker-root>
84    fn Root(&self) -> DomRoot<Node> {
85        DomRoot::from_ref(&*self.root_node)
86    }
87
88    /// <https://dom.spec.whatwg.org/#dom-treewalker-whattoshow>
89    fn WhatToShow(&self) -> u32 {
90        self.what_to_show
91    }
92
93    /// <https://dom.spec.whatwg.org/#dom-treewalker-filter>
94    fn GetFilter(&self, cx: &JSContext) -> Option<RootedCallback<NodeFilter>> {
95        match self.filter {
96            Filter::None => None,
97            Filter::Dom(ref nf) => Some(nf.root(cx)),
98        }
99    }
100
101    /// <https://dom.spec.whatwg.org/#dom-treewalker-currentnode>
102    fn CurrentNode(&self) -> DomRoot<Node> {
103        self.current_node.get()
104    }
105
106    /// <https://dom.spec.whatwg.org/#dom-treewalker-currentnode>
107    fn SetCurrentNode(&self, node: &Node) {
108        self.current_node.set(node);
109    }
110
111    /// <https://dom.spec.whatwg.org/#dom-treewalker-parentnode>
112    fn ParentNode(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
113        // "1. Let node be the value of the currentNode attribute."
114        let mut node = self.current_node.get();
115        // "2. While node is not null and is not root, run these substeps:"
116        while !self.is_root_node(&node) {
117            // "1. Let node be node's parent."
118            match node.GetParentNode() {
119                Some(n) => {
120                    node = n;
121                    // "2. If node is not null and filtering node returns FILTER_ACCEPT,
122                    //     then set the currentNode attribute to node, return node."
123                    if NodeFilterConstants::FILTER_ACCEPT == self.accept_node(cx, &node)? {
124                        self.current_node.set(&node);
125                        return Ok(Some(node));
126                    }
127                },
128                None => break,
129            }
130        }
131        // "3. Return null."
132        Ok(None)
133    }
134
135    /// <https://dom.spec.whatwg.org/#dom-treewalker-firstchild>
136    fn FirstChild(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
137        // "The firstChild() method must traverse children of type first."
138        self.traverse_children(
139            cx,
140            |node| node.GetFirstChild(),
141            |node| node.GetNextSibling(),
142        )
143    }
144
145    /// <https://dom.spec.whatwg.org/#dom-treewalker-lastchild>
146    fn LastChild(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
147        // "The lastChild() method must traverse children of type last."
148        self.traverse_children(
149            cx,
150            |node| node.GetLastChild(),
151            |node| node.GetPreviousSibling(),
152        )
153    }
154
155    /// <https://dom.spec.whatwg.org/#dom-treewalker-previoussibling>
156    fn PreviousSibling(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
157        // "The nextSibling() method must traverse siblings of type next."
158        self.traverse_siblings(
159            cx,
160            |node| node.GetLastChild(),
161            |node| node.GetPreviousSibling(),
162        )
163    }
164
165    /// <https://dom.spec.whatwg.org/#dom-treewalker-nextsibling>
166    fn NextSibling(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
167        // "The previousSibling() method must traverse siblings of type previous."
168        self.traverse_siblings(
169            cx,
170            |node| node.GetFirstChild(),
171            |node| node.GetNextSibling(),
172        )
173    }
174
175    /// <https://dom.spec.whatwg.org/#dom-treewalker-previousnode>
176    fn PreviousNode(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
177        // "1. Let node be the value of the currentNode attribute."
178        let mut node = self.current_node.get();
179        // "2. While node is not root, run these substeps:"
180        while !self.is_root_node(&node) {
181            // "1. Let sibling be the previous sibling of node."
182            let mut sibling_op = node.GetPreviousSibling();
183            // "2. While sibling is not null, run these subsubsteps:"
184            while sibling_op.is_some() {
185                // "1. Set node to sibling."
186                node = sibling_op.unwrap();
187                // "2. Filter node and let result be the return value."
188                // "3. While result is not FILTER_REJECT and node has a child,
189                //     set node to its last child and then filter node and
190                //     set result to the return value."
191                // "4. If result is FILTER_ACCEPT, then
192                //     set the currentNode attribute to node and return node."
193                loop {
194                    let result = self.accept_node(cx, &node)?;
195                    match result {
196                        NodeFilterConstants::FILTER_REJECT => break,
197                        _ if node.GetFirstChild().is_some() => node = node.GetLastChild().unwrap(),
198                        NodeFilterConstants::FILTER_ACCEPT => {
199                            self.current_node.set(&node);
200                            return Ok(Some(node));
201                        },
202                        _ => break,
203                    }
204                }
205                // "5. Set sibling to the previous sibling of node."
206                sibling_op = node.GetPreviousSibling()
207            }
208            // "3. If node is root or node's parent is null, return null."
209            if self.is_root_node(&node) || node.GetParentNode().is_none() {
210                return Ok(None);
211            }
212            // "4. Set node to its parent."
213            match node.GetParentNode() {
214                None =>
215                // This can happen if the user set the current node to somewhere
216                // outside of the tree rooted at the original root.
217                {
218                    return Ok(None);
219                },
220                Some(n) => node = n,
221            }
222            // "5. Filter node and if the return value is FILTER_ACCEPT, then
223            //     set the currentNode attribute to node and return node."
224            if NodeFilterConstants::FILTER_ACCEPT == self.accept_node(cx, &node)? {
225                self.current_node.set(&node);
226                return Ok(Some(node));
227            }
228        }
229        // "6. Return null."
230        Ok(None)
231    }
232
233    /// <https://dom.spec.whatwg.org/#dom-treewalker-nextnode>
234    fn NextNode(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
235        // "1. Let node be the value of the currentNode attribute."
236        let mut node = self.current_node.get();
237        // "2. Let result be FILTER_ACCEPT."
238        let mut result = NodeFilterConstants::FILTER_ACCEPT;
239        // "3. Run these substeps:"
240        loop {
241            // "1. While result is not FILTER_REJECT and node has a child, run these subsubsteps:"
242            loop {
243                if NodeFilterConstants::FILTER_REJECT == result {
244                    break;
245                }
246                match node.GetFirstChild() {
247                    None => break,
248                    Some(child) => {
249                        // "1. Set node to its first child."
250                        node = child;
251                        // "2. Filter node and set result to the return value."
252                        result = self.accept_node(cx, &node)?;
253                        // "3. If result is FILTER_ACCEPT, then
254                        //     set the currentNode attribute to node and return node."
255                        if NodeFilterConstants::FILTER_ACCEPT == result {
256                            self.current_node.set(&node);
257                            return Ok(Some(node));
258                        }
259                    },
260                }
261            }
262            // "2. If a node is following node and is not following root,
263            //     set node to the first such node."
264            // "Otherwise, return null."
265            match self.first_following_node_not_following_root(&node) {
266                None => return Ok(None),
267                Some(n) => {
268                    node = n;
269                    // "3. Filter node and set result to the return value."
270                    result = self.accept_node(cx, &node)?;
271                    // "4. If result is FILTER_ACCEPT, then
272                    //     set the currentNode attribute to node and return node."
273                    if NodeFilterConstants::FILTER_ACCEPT == result {
274                        self.current_node.set(&node);
275                        return Ok(Some(node));
276                    }
277                },
278            }
279            // "5. Run these substeps again."
280        }
281    }
282}
283
284impl TreeWalker {
285    /// <https://dom.spec.whatwg.org/#concept-traverse-children>
286    fn traverse_children<F, G>(
287        &self,
288        cx: &mut JSContext,
289        next_child: F,
290        next_sibling: G,
291    ) -> Fallible<Option<DomRoot<Node>>>
292    where
293        F: Fn(&Node) -> Option<DomRoot<Node>>,
294        G: Fn(&Node) -> Option<DomRoot<Node>>,
295    {
296        // "To **traverse children** of type *type*, run these steps:"
297        // "1. Let node be the value of the currentNode attribute."
298        let cur = self.current_node.get();
299
300        // "2. Set node to node's first child if type is first, and node's last child if type is last."
301        // "3. If node is null, return null."
302        let mut node = match next_child(&cur) {
303            Some(node) => node,
304            None => return Ok(None),
305        };
306
307        // 4. Main: Repeat these substeps:
308        'main: loop {
309            // "1. Filter node and let result be the return value."
310            let result = self.accept_node(cx, &node)?;
311            match result {
312                // "2. If result is FILTER_ACCEPT, then set the currentNode
313                //     attribute to node and return node."
314                NodeFilterConstants::FILTER_ACCEPT => {
315                    self.current_node.set(&node);
316                    return Ok(Some(DomRoot::from_ref(&node)));
317                },
318                // "3. If result is FILTER_SKIP, run these subsubsteps:"
319                NodeFilterConstants::FILTER_SKIP => {
320                    // "1. Let child be node's first child if type is first,
321                    //     and node's last child if type is last."
322                    if let Some(child) = next_child(&node) {
323                        // "2. If child is not null, set node to child and goto Main."
324                        node = child;
325                        continue 'main;
326                    }
327                },
328                _ => {},
329            }
330            // "4. Repeat these subsubsteps:"
331            loop {
332                // "1. Let sibling be node's next sibling if type is next,
333                //     and node's previous sibling if type is previous."
334                match next_sibling(&node) {
335                    // "2. If sibling is not null,
336                    //     set node to sibling and goto Main."
337                    Some(sibling) => {
338                        node = sibling;
339                        continue 'main;
340                    },
341                    None => {
342                        // "3. Let parent be node's parent."
343                        match node.GetParentNode() {
344                            // "4. If parent is null, parent is root,
345                            //     or parent is currentNode attribute's value,
346                            //     return null."
347                            None => return Ok(None),
348                            Some(ref parent)
349                                if self.is_root_node(parent) || self.is_current_node(parent) =>
350                            {
351                                return Ok(None);
352                            },
353                            // "5. Otherwise, set node to parent."
354                            Some(parent) => node = parent,
355                        }
356                    },
357                }
358            }
359        }
360    }
361
362    /// <https://dom.spec.whatwg.org/#concept-traverse-siblings>
363    fn traverse_siblings<F, G>(
364        &self,
365        cx: &mut JSContext,
366        next_child: F,
367        next_sibling: G,
368    ) -> Fallible<Option<DomRoot<Node>>>
369    where
370        F: Fn(&Node) -> Option<DomRoot<Node>>,
371        G: Fn(&Node) -> Option<DomRoot<Node>>,
372    {
373        // "To **traverse siblings** of type *type* run these steps:"
374        // "1. Let node be the value of the currentNode attribute."
375        let mut node = self.current_node.get();
376        // "2. If node is root, return null."
377        if self.is_root_node(&node) {
378            return Ok(None);
379        }
380        // "3. Run these substeps:"
381        loop {
382            // "1. Let sibling be node's next sibling if type is next,
383            //  and node's previous sibling if type is previous."
384            let mut sibling_op = next_sibling(&node);
385            // "2. While sibling is not null, run these subsubsteps:"
386            while sibling_op.is_some() {
387                // "1. Set node to sibling."
388                node = sibling_op.unwrap();
389                // "2. Filter node and let result be the return value."
390                let result = self.accept_node(cx, &node)?;
391                // "3. If result is FILTER_ACCEPT, then set the currentNode
392                //     attribute to node and return node."
393                if NodeFilterConstants::FILTER_ACCEPT == result {
394                    self.current_node.set(&node);
395                    return Ok(Some(node));
396                }
397
398                // "4. Set sibling to node's first child if type is next,
399                //     and node's last child if type is previous."
400                sibling_op = next_child(&node);
401                // "5. If result is FILTER_REJECT or sibling is null,
402                //     then set sibling to node's next sibling if type is next,
403                //     and node's previous sibling if type is previous."
404                match (result, &sibling_op) {
405                    (NodeFilterConstants::FILTER_REJECT, _) | (_, &None) => {
406                        sibling_op = next_sibling(&node)
407                    },
408                    _ => {},
409                }
410            }
411            // "3. Set node to its parent."
412            match node.GetParentNode() {
413                // "4. If node is null or is root, return null."
414                None => return Ok(None),
415                Some(ref n) if self.is_root_node(n) => return Ok(None),
416                // "5. Filter node and if the return value is FILTER_ACCEPT, then return null."
417                Some(n) => {
418                    node = n;
419                    if NodeFilterConstants::FILTER_ACCEPT == self.accept_node(cx, &node)? {
420                        return Ok(None);
421                    }
422                },
423            }
424            // "6. Run these substeps again."
425        }
426    }
427
428    /// <https://dom.spec.whatwg.org/#concept-tree-following>
429    fn first_following_node_not_following_root(&self, node: &Node) -> Option<DomRoot<Node>> {
430        // "An object A is following an object B if A and B are in the same tree
431        //  and A comes after B in tree order."
432        match node.GetNextSibling() {
433            None => {
434                let mut candidate = DomRoot::from_ref(node);
435                while !self.is_root_node(&candidate) && candidate.GetNextSibling().is_none() {
436                    // This can return None if the user set the current node to somewhere
437                    // outside of the tree rooted at the original root.
438                    candidate = candidate.GetParentNode()?;
439                }
440                if self.is_root_node(&candidate) {
441                    None
442                } else {
443                    candidate.GetNextSibling()
444                }
445            },
446            it => it,
447        }
448    }
449
450    /// <https://dom.spec.whatwg.org/#concept-node-filter>
451    fn accept_node(&self, cx: &mut JSContext, node: &Node) -> Fallible<u16> {
452        // Step 1.
453        if self.active.get() {
454            return Err(Error::InvalidState(Some(
455                "TreeWalker cannot be active".into(),
456            )));
457        }
458        // Step 2.
459        let n = node.NodeType() - 1;
460        // Step 3.
461        if (self.what_to_show & (1 << n)) == 0 {
462            return Ok(NodeFilterConstants::FILTER_SKIP);
463        }
464        match self.filter {
465            // Step 4.
466            Filter::None => Ok(NodeFilterConstants::FILTER_ACCEPT),
467            Filter::Dom(ref callback) => {
468                // Step 5.
469                self.active.set(true);
470                // Step 6.
471                let result = callback.AcceptNode_(cx, self, node, Rethrow);
472                // Step 7.
473                self.active.set(false);
474                // Step 8.
475                result
476            },
477        }
478    }
479
480    fn is_root_node(&self, node: &Node) -> bool {
481        Dom::from_ref(node) == self.root_node
482    }
483
484    fn is_current_node(&self, node: &Node) -> bool {
485        node == &*self.current_node.get()
486    }
487}
488
489impl Iterator for &TreeWalker {
490    type Item = DomRoot<Node>;
491
492    #[expect(unsafe_code)]
493    fn next(&mut self) -> Option<DomRoot<Node>> {
494        // TODO: https://github.com/servo/servo/issues/43311
495        let mut cx = unsafe { temp_cx() };
496        match self.NextNode(&mut cx) {
497            Ok(node) => node,
498            Err(_) =>
499            // The Err path happens only when a JavaScript
500            // NodeFilter throws an exception. This iterator
501            // is meant for internal use from Rust code, which
502            // will probably be using a native Rust filter,
503            // which cannot produce an Err result.
504            {
505                unreachable!()
506            },
507        }
508    }
509}
510
511#[derive(JSTraceable)]
512#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
513pub(crate) enum Filter {
514    None,
515    Dom(TracedCallback<NodeFilter>),
516}
517
518impl OwnerWindow<crate::DomTypeHolder> for TreeWalker {}