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