script/dom/node/
nodeiterator.rs1use 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 fn Root(&self) -> DomRoot<Node> {
80 DomRoot::from_ref(&*self.root_node)
81 }
82
83 fn WhatToShow(&self) -> u32 {
85 self.what_to_show
86 }
87
88 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 fn ReferenceNode(&self) -> DomRoot<Node> {
98 self.reference_node.get()
99 }
100
101 fn PointerBeforeReferenceNode(&self) -> bool {
103 self.pointer_before_reference_node.get()
104 }
105
106 fn NextNode(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
108 let node = self.reference_node.get();
111
112 let mut before_node = self.pointer_before_reference_node.get();
114
115 if before_node {
117 before_node = false;
118
119 let result = self.accept_node(cx, &node)?;
121
122 if result == NodeFilterConstants::FILTER_ACCEPT {
124 self.reference_node.set(&node);
126 self.pointer_before_reference_node.set(before_node);
127
128 return Ok(Some(node));
129 }
130 }
131
132 for following_node in node.following_nodes(&self.root_node, ShadowIncluding::No) {
134 let result = self.accept_node(cx, &following_node)?;
136
137 if result == NodeFilterConstants::FILTER_ACCEPT {
139 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 fn PreviousNode(&self, cx: &mut JSContext) -> Fallible<Option<DomRoot<Node>>> {
152 let node = self.reference_node.get();
155
156 let mut before_node = self.pointer_before_reference_node.get();
158
159 if !before_node {
161 before_node = true;
162
163 let result = self.accept_node(cx, &node)?;
165
166 if result == NodeFilterConstants::FILTER_ACCEPT {
168 self.reference_node.set(&node);
170 self.pointer_before_reference_node.set(before_node);
171
172 return Ok(Some(node));
173 }
174 }
175
176 for preceding_node in node.preceding_nodes(&self.root_node) {
178 let result = self.accept_node(cx, &preceding_node)?;
180
181 if result == NodeFilterConstants::FILTER_ACCEPT {
183 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 fn Detach(&self) {
196 }
198}
199
200impl NodeIterator {
201 fn accept_node(&self, cx: &mut JSContext, node: &Node) -> Fallible<u16> {
203 if self.active.get() {
205 return Err(Error::InvalidState(None));
206 }
207 let n = node.NodeType() - 1;
209 if (self.what_to_show & (1 << n)) == 0 {
211 return Ok(NodeFilterConstants::FILTER_SKIP);
212 }
213
214 match self.filter {
215 Filter::None => Ok(NodeFilterConstants::FILTER_ACCEPT),
217 Filter::Callback(ref callback) => {
218 self.active.set(true);
220 let result = callback.AcceptNode_(cx, self, node, Rethrow);
222 self.active.set(false);
224 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}