script/dom/node/
nodeiterator.rs1use 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 fn Root(&self) -> DomRoot<Node> {
86 DomRoot::from_ref(&*self.root_node)
87 }
88
89 fn WhatToShow(&self) -> u32 {
91 self.what_to_show
92 }
93
94 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 fn ReferenceNode(&self) -> DomRoot<Node> {
104 self.reference_node.get()
105 }
106
107 fn PointerBeforeReferenceNode(&self) -> bool {
109 self.pointer_before_reference_node.get()
110 }
111
112 fn NextNode(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
114 let node = self.reference_node.get();
117
118 let mut before_node = self.pointer_before_reference_node.get();
120
121 if before_node {
123 before_node = false;
124
125 let result = self.accept_node(cx, &node)?;
127
128 if result == NodeFilterConstants::FILTER_ACCEPT {
130 self.reference_node.set(&node);
132 self.pointer_before_reference_node.set(before_node);
133
134 return Ok(Some(node));
135 }
136 }
137
138 for following_node in node.following_nodes(&self.root_node, ShadowIncluding::No) {
140 let result = self.accept_node(cx, &following_node)?;
142
143 if result == NodeFilterConstants::FILTER_ACCEPT {
145 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 fn PreviousNode(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
158 let node = self.reference_node.get();
161
162 let mut before_node = self.pointer_before_reference_node.get();
164
165 if !before_node {
167 before_node = true;
168
169 let result = self.accept_node(cx, &node)?;
171
172 if result == NodeFilterConstants::FILTER_ACCEPT {
174 self.reference_node.set(&node);
176 self.pointer_before_reference_node.set(before_node);
177
178 return Ok(Some(node));
179 }
180 }
181
182 for preceding_node in node.preceding_nodes(&self.root_node) {
184 let result = self.accept_node(cx, &preceding_node)?;
186
187 if result == NodeFilterConstants::FILTER_ACCEPT {
189 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 fn Detach(&self) {
202 }
204}
205
206impl NodeIterator {
207 fn accept_node(&self, cx: &mut JSContext, node: &Node) -> Fallible<u16> {
209 if self.active.get() {
211 return Err(Error::InvalidState(Some(
212 "Node iterator cannot be active".into(),
213 )));
214 }
215 let n = node.NodeType() - 1;
217 if (self.what_to_show & (1 << n)) == 0 {
219 return Ok(NodeFilterConstants::FILTER_SKIP);
220 }
221
222 match self.filter {
223 Filter::None => Ok(NodeFilterConstants::FILTER_ACCEPT),
225 Filter::Callback(ref callback) => {
226 self.active.set(true);
228 let result = callback.AcceptNode_(cx, self, node, Rethrow);
230 self.active.set(false);
232 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 {}