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