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