Skip to main content

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