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