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 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 fn Root(&self) -> DomRoot<Node> {
78 DomRoot::from_ref(&*self.root_node)
79 }
80
81 fn WhatToShow(&self) -> u32 {
83 self.what_to_show
84 }
85
86 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 fn ReferenceNode(&self) -> DomRoot<Node> {
96 self.reference_node.get()
97 }
98
99 fn PointerBeforeReferenceNode(&self) -> bool {
101 self.pointer_before_reference_node.get()
102 }
103
104 fn NextNode(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
106 let node = self.reference_node.get();
109
110 let mut before_node = self.pointer_before_reference_node.get();
112
113 if before_node {
115 before_node = false;
116
117 let result = self.accept_node(&node, can_gc)?;
119
120 if result == NodeFilterConstants::FILTER_ACCEPT {
122 self.reference_node.set(&node);
124 self.pointer_before_reference_node.set(before_node);
125
126 return Ok(Some(node));
127 }
128 }
129
130 for following_node in node.following_nodes(&self.root_node) {
132 let result = self.accept_node(&following_node, can_gc)?;
134
135 if result == NodeFilterConstants::FILTER_ACCEPT {
137 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 fn PreviousNode(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
150 let node = self.reference_node.get();
153
154 let mut before_node = self.pointer_before_reference_node.get();
156
157 if !before_node {
159 before_node = true;
160
161 let result = self.accept_node(&node, can_gc)?;
163
164 if result == NodeFilterConstants::FILTER_ACCEPT {
166 self.reference_node.set(&node);
168 self.pointer_before_reference_node.set(before_node);
169
170 return Ok(Some(node));
171 }
172 }
173
174 for preceding_node in node.preceding_nodes(&self.root_node) {
176 let result = self.accept_node(&preceding_node, can_gc)?;
178
179 if result == NodeFilterConstants::FILTER_ACCEPT {
181 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 fn Detach(&self) {
194 }
196}
197
198impl NodeIterator {
199 fn accept_node(&self, node: &Node, can_gc: CanGc) -> Fallible<u16> {
201 if self.active.get() {
203 return Err(Error::InvalidState(None));
204 }
205 let n = node.NodeType() - 1;
207 if (self.what_to_show & (1 << n)) == 0 {
209 return Ok(NodeFilterConstants::FILTER_SKIP);
210 }
211
212 match self.filter {
213 Filter::None => Ok(NodeFilterConstants::FILTER_ACCEPT),
215 Filter::Callback(ref callback) => {
216 self.active.set(true);
218 let result = callback.AcceptNode_(self, node, Rethrow, can_gc);
220 self.active.set(false);
222 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}