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