script/dom/
nodeiterator.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::NodeIteratorBinding::NodeIteratorMethods;
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#[dom_struct]
22pub(crate) struct NodeIterator {
23    reflector_: Reflector,
24    root_node: Dom<Node>,
25    #[ignore_malloc_size_of = "Defined in rust-mozjs"]
26    reference_node: MutDom<Node>,
27    pointer_before_reference_node: Cell<bool>,
28    what_to_show: u32,
29    #[ignore_malloc_size_of = "Rc<NodeFilter> has shared ownership, so its size cannot be measured accurately"]
30    filter: Filter,
31    active: Cell<bool>,
32}
33
34impl NodeIterator {
35    fn new_inherited(root_node: &Node, what_to_show: u32, filter: Filter) -> NodeIterator {
36        NodeIterator {
37            reflector_: Reflector::new(),
38            root_node: Dom::from_ref(root_node),
39            reference_node: MutDom::new(root_node),
40            pointer_before_reference_node: Cell::new(true),
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<NodeIterator> {
54        reflect_dom_object(
55            Box::new(NodeIterator::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        can_gc: CanGc,
67    ) -> DomRoot<NodeIterator> {
68        let filter = match node_filter {
69            None => Filter::None,
70            Some(jsfilter) => Filter::Callback(jsfilter),
71        };
72        NodeIterator::new_with_filter(document, root_node, what_to_show, filter, can_gc)
73    }
74}
75
76impl NodeIteratorMethods<crate::DomTypeHolder> for NodeIterator {
77    // https://dom.spec.whatwg.org/#dom-nodeiterator-root
78    fn Root(&self) -> DomRoot<Node> {
79        DomRoot::from_ref(&*self.root_node)
80    }
81
82    // https://dom.spec.whatwg.org/#dom-nodeiterator-whattoshow
83    fn WhatToShow(&self) -> u32 {
84        self.what_to_show
85    }
86
87    // https://dom.spec.whatwg.org/#dom-nodeiterator-filter
88    fn GetFilter(&self) -> Option<Rc<NodeFilter>> {
89        match self.filter {
90            Filter::None => None,
91            Filter::Callback(ref nf) => Some((*nf).clone()),
92        }
93    }
94
95    // https://dom.spec.whatwg.org/#dom-nodeiterator-referencenode
96    fn ReferenceNode(&self) -> DomRoot<Node> {
97        self.reference_node.get()
98    }
99
100    // https://dom.spec.whatwg.org/#dom-nodeiterator-pointerbeforereferencenode
101    fn PointerBeforeReferenceNode(&self) -> bool {
102        self.pointer_before_reference_node.get()
103    }
104
105    // https://dom.spec.whatwg.org/#dom-nodeiterator-nextnode
106    fn NextNode(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
107        // https://dom.spec.whatwg.org/#concept-NodeIterator-traverse
108        // Step 1.
109        let node = self.reference_node.get();
110
111        // Step 2.
112        let mut before_node = self.pointer_before_reference_node.get();
113
114        // Step 3-1.
115        if before_node {
116            before_node = false;
117
118            // Step 3-2.
119            let result = self.accept_node(&node, can_gc)?;
120
121            // Step 3-3.
122            if result == NodeFilterConstants::FILTER_ACCEPT {
123                // Step 4.
124                self.reference_node.set(&node);
125                self.pointer_before_reference_node.set(before_node);
126
127                return Ok(Some(node));
128            }
129        }
130
131        // Step 3-1.
132        for following_node in node.following_nodes(&self.root_node) {
133            // Step 3-2.
134            let result = self.accept_node(&following_node, can_gc)?;
135
136            // Step 3-3.
137            if result == NodeFilterConstants::FILTER_ACCEPT {
138                // Step 4.
139                self.reference_node.set(&following_node);
140                self.pointer_before_reference_node.set(before_node);
141
142                return Ok(Some(following_node));
143            }
144        }
145
146        Ok(None)
147    }
148
149    // https://dom.spec.whatwg.org/#dom-nodeiterator-previousnode
150    fn PreviousNode(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
151        // https://dom.spec.whatwg.org/#concept-NodeIterator-traverse
152        // Step 1.
153        let node = self.reference_node.get();
154
155        // Step 2.
156        let mut before_node = self.pointer_before_reference_node.get();
157
158        // Step 3-1.
159        if !before_node {
160            before_node = true;
161
162            // Step 3-2.
163            let result = self.accept_node(&node, can_gc)?;
164
165            // Step 3-3.
166            if result == NodeFilterConstants::FILTER_ACCEPT {
167                // Step 4.
168                self.reference_node.set(&node);
169                self.pointer_before_reference_node.set(before_node);
170
171                return Ok(Some(node));
172            }
173        }
174
175        // Step 3-1.
176        for preceding_node in node.preceding_nodes(&self.root_node) {
177            // Step 3-2.
178            let result = self.accept_node(&preceding_node, can_gc)?;
179
180            // Step 3-3.
181            if result == NodeFilterConstants::FILTER_ACCEPT {
182                // Step 4.
183                self.reference_node.set(&preceding_node);
184                self.pointer_before_reference_node.set(before_node);
185
186                return Ok(Some(preceding_node));
187            }
188        }
189
190        Ok(None)
191    }
192
193    // https://dom.spec.whatwg.org/#dom-nodeiterator-detach
194    fn Detach(&self) {
195        // This method intentionally left blank.
196    }
197}
198
199impl NodeIterator {
200    // https://dom.spec.whatwg.org/#concept-node-filter
201    fn accept_node(&self, node: &Node, can_gc: CanGc) -> Fallible<u16> {
202        // Step 1.
203        if self.active.get() {
204            return Err(Error::InvalidState);
205        }
206        // Step 2.
207        let n = node.NodeType() - 1;
208        // Step 3.
209        if (self.what_to_show & (1 << n)) == 0 {
210            return Ok(NodeFilterConstants::FILTER_SKIP);
211        }
212
213        match self.filter {
214            // Step 4.
215            Filter::None => Ok(NodeFilterConstants::FILTER_ACCEPT),
216            Filter::Callback(ref callback) => {
217                // Step 5.
218                self.active.set(true);
219                // Step 6.
220                let result = callback.AcceptNode_(self, node, Rethrow, can_gc);
221                // Step 7.
222                self.active.set(false);
223                // Step 8.
224                result
225            },
226        }
227    }
228}
229
230#[derive(JSTraceable)]
231pub(crate) enum Filter {
232    None,
233    Callback(Rc<NodeFilter>),
234}