script/dom/
nodeiterator.rs1use 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 fn Root(&self) -> DomRoot<Node> {
79 DomRoot::from_ref(&*self.root_node)
80 }
81
82 fn WhatToShow(&self) -> u32 {
84 self.what_to_show
85 }
86
87 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 fn ReferenceNode(&self) -> DomRoot<Node> {
97 self.reference_node.get()
98 }
99
100 fn PointerBeforeReferenceNode(&self) -> bool {
102 self.pointer_before_reference_node.get()
103 }
104
105 fn NextNode(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
107 let node = self.reference_node.get();
110
111 let mut before_node = self.pointer_before_reference_node.get();
113
114 if before_node {
116 before_node = false;
117
118 let result = self.accept_node(&node, can_gc)?;
120
121 if result == NodeFilterConstants::FILTER_ACCEPT {
123 self.reference_node.set(&node);
125 self.pointer_before_reference_node.set(before_node);
126
127 return Ok(Some(node));
128 }
129 }
130
131 for following_node in node.following_nodes(&self.root_node) {
133 let result = self.accept_node(&following_node, can_gc)?;
135
136 if result == NodeFilterConstants::FILTER_ACCEPT {
138 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 fn PreviousNode(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
151 let node = self.reference_node.get();
154
155 let mut before_node = self.pointer_before_reference_node.get();
157
158 if !before_node {
160 before_node = true;
161
162 let result = self.accept_node(&node, can_gc)?;
164
165 if result == NodeFilterConstants::FILTER_ACCEPT {
167 self.reference_node.set(&node);
169 self.pointer_before_reference_node.set(before_node);
170
171 return Ok(Some(node));
172 }
173 }
174
175 for preceding_node in node.preceding_nodes(&self.root_node) {
177 let result = self.accept_node(&preceding_node, can_gc)?;
179
180 if result == NodeFilterConstants::FILTER_ACCEPT {
182 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 fn Detach(&self) {
195 }
197}
198
199impl NodeIterator {
200 fn accept_node(&self, node: &Node, can_gc: CanGc) -> Fallible<u16> {
202 if self.active.get() {
204 return Err(Error::InvalidState);
205 }
206 let n = node.NodeType() - 1;
208 if (self.what_to_show & (1 << n)) == 0 {
210 return Ok(NodeFilterConstants::FILTER_SKIP);
211 }
212
213 match self.filter {
214 Filter::None => Ok(NodeFilterConstants::FILTER_ACCEPT),
216 Filter::Callback(ref callback) => {
217 self.active.set(true);
219 let result = callback.AcceptNode_(self, node, Rethrow, can_gc);
221 self.active.set(false);
223 result
225 },
226 }
227 }
228}
229
230#[derive(JSTraceable)]
231pub(crate) enum Filter {
232 None,
233 Callback(Rc<NodeFilter>),
234}