script/dom/
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;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use script_bindings::script_runtime::temp_cx;
11
12use crate::dom::bindings::callback::ExceptionHandling::Rethrow;
13use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
14use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::{NodeFilter, NodeFilterConstants};
15use crate::dom::bindings::codegen::Bindings::TreeWalkerBinding::TreeWalkerMethods;
16use crate::dom::bindings::error::{Error, Fallible};
17use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
18use crate::dom::bindings::root::{Dom, DomRoot, MutDom};
19use crate::dom::document::Document;
20use crate::dom::node::Node;
21use crate::script_runtime::CanGc;
22
23// https://dom.spec.whatwg.org/#interface-treewalker
24#[dom_struct]
25pub(crate) struct TreeWalker {
26    reflector_: Reflector,
27    root_node: Dom<Node>,
28    current_node: MutDom<Node>,
29    what_to_show: u32,
30    #[ignore_malloc_size_of = "function pointers and Rc<T> are hard"]
31    filter: Filter,
32    active: Cell<bool>,
33}
34
35impl TreeWalker {
36    fn new_inherited(root_node: &Node, what_to_show: u32, filter: Filter) -> TreeWalker {
37        TreeWalker {
38            reflector_: Reflector::new(),
39            root_node: Dom::from_ref(root_node),
40            current_node: MutDom::new(root_node),
41            what_to_show,
42            filter,
43            active: Cell::new(false),
44        }
45    }
46
47    pub(crate) fn new_with_filter(
48        document: &Document,
49        root_node: &Node,
50        what_to_show: u32,
51        filter: Filter,
52        can_gc: CanGc,
53    ) -> DomRoot<TreeWalker> {
54        reflect_dom_object(
55            Box::new(TreeWalker::new_inherited(root_node, what_to_show, filter)),
56            document.window(),
57            can_gc,
58        )
59    }
60
61    pub(crate) fn new(
62        document: &Document,
63        root_node: &Node,
64        what_to_show: u32,
65        node_filter: Option<Rc<NodeFilter>>,
66    ) -> DomRoot<TreeWalker> {
67        let filter = match node_filter {
68            None => Filter::None,
69            Some(jsfilter) => Filter::Dom(jsfilter),
70        };
71        TreeWalker::new_with_filter(
72            document,
73            root_node,
74            what_to_show,
75            filter,
76            CanGc::deprecated_note(),
77        )
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) -> Option<Rc<NodeFilter>> {
94        match self.filter {
95            Filter::None => None,
96            Filter::Dom(ref nf) => Some(nf.clone()),
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(None));
454        }
455        // Step 2.
456        let n = node.NodeType() - 1;
457        // Step 3.
458        if (self.what_to_show & (1 << n)) == 0 {
459            return Ok(NodeFilterConstants::FILTER_SKIP);
460        }
461        match self.filter {
462            // Step 4.
463            Filter::None => Ok(NodeFilterConstants::FILTER_ACCEPT),
464            Filter::Dom(ref callback) => {
465                // Step 5.
466                self.active.set(true);
467                // Step 6.
468                let result = callback.AcceptNode_(self, node, Rethrow, CanGc::from_cx(cx));
469                // Step 7.
470                self.active.set(false);
471                // Step 8.
472                result
473            },
474        }
475    }
476
477    fn is_root_node(&self, node: &Node) -> bool {
478        Dom::from_ref(node) == self.root_node
479    }
480
481    fn is_current_node(&self, node: &Node) -> bool {
482        node == &*self.current_node.get()
483    }
484}
485
486impl Iterator for &TreeWalker {
487    type Item = DomRoot<Node>;
488
489    #[expect(unsafe_code)]
490    fn next(&mut self) -> Option<DomRoot<Node>> {
491        // TODO: https://github.com/servo/servo/issues/43311
492        let mut cx = unsafe { temp_cx() };
493        match self.NextNode(&mut cx) {
494            Ok(node) => node,
495            Err(_) =>
496            // The Err path happens only when a JavaScript
497            // NodeFilter throws an exception. This iterator
498            // is meant for internal use from Rust code, which
499            // will probably be using a native Rust filter,
500            // which cannot produce an Err result.
501            {
502                unreachable!()
503            },
504        }
505    }
506}
507
508#[derive(JSTraceable)]
509pub(crate) enum Filter {
510    None,
511    Dom(Rc<NodeFilter>),
512}