script/dom/treewalker.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::TreeWalkerBinding::TreeWalkerMethods;
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// https://dom.spec.whatwg.org/#interface-treewalker
22#[dom_struct]
23pub(crate) struct TreeWalker {
24 reflector_: Reflector,
25 root_node: Dom<Node>,
26 current_node: MutDom<Node>,
27 what_to_show: u32,
28 #[ignore_malloc_size_of = "function pointers and Rc<T> are hard"]
29 filter: Filter,
30 active: Cell<bool>,
31}
32
33impl TreeWalker {
34 fn new_inherited(root_node: &Node, what_to_show: u32, filter: Filter) -> TreeWalker {
35 TreeWalker {
36 reflector_: Reflector::new(),
37 root_node: Dom::from_ref(root_node),
38 current_node: MutDom::new(root_node),
39 what_to_show,
40 filter,
41 active: Cell::new(false),
42 }
43 }
44
45 pub(crate) fn new_with_filter(
46 document: &Document,
47 root_node: &Node,
48 what_to_show: u32,
49 filter: Filter,
50 can_gc: CanGc,
51 ) -> DomRoot<TreeWalker> {
52 reflect_dom_object(
53 Box::new(TreeWalker::new_inherited(root_node, what_to_show, filter)),
54 document.window(),
55 can_gc,
56 )
57 }
58
59 pub(crate) fn new(
60 document: &Document,
61 root_node: &Node,
62 what_to_show: u32,
63 node_filter: Option<Rc<NodeFilter>>,
64 ) -> DomRoot<TreeWalker> {
65 let filter = match node_filter {
66 None => Filter::None,
67 Some(jsfilter) => Filter::Dom(jsfilter),
68 };
69 TreeWalker::new_with_filter(document, root_node, what_to_show, filter, CanGc::note())
70 }
71}
72
73impl TreeWalkerMethods<crate::DomTypeHolder> for TreeWalker {
74 // https://dom.spec.whatwg.org/#dom-treewalker-root
75 fn Root(&self) -> DomRoot<Node> {
76 DomRoot::from_ref(&*self.root_node)
77 }
78
79 // https://dom.spec.whatwg.org/#dom-treewalker-whattoshow
80 fn WhatToShow(&self) -> u32 {
81 self.what_to_show
82 }
83
84 // https://dom.spec.whatwg.org/#dom-treewalker-filter
85 fn GetFilter(&self) -> Option<Rc<NodeFilter>> {
86 match self.filter {
87 Filter::None => None,
88 Filter::Dom(ref nf) => Some(nf.clone()),
89 }
90 }
91
92 // https://dom.spec.whatwg.org/#dom-treewalker-currentnode
93 fn CurrentNode(&self) -> DomRoot<Node> {
94 self.current_node.get()
95 }
96
97 // https://dom.spec.whatwg.org/#dom-treewalker-currentnode
98 fn SetCurrentNode(&self, node: &Node) {
99 self.current_node.set(node);
100 }
101
102 // https://dom.spec.whatwg.org/#dom-treewalker-parentnode
103 fn ParentNode(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
104 // "1. Let node be the value of the currentNode attribute."
105 let mut node = self.current_node.get();
106 // "2. While node is not null and is not root, run these substeps:"
107 while !self.is_root_node(&node) {
108 // "1. Let node be node's parent."
109 match node.GetParentNode() {
110 Some(n) => {
111 node = n;
112 // "2. If node is not null and filtering node returns FILTER_ACCEPT,
113 // then set the currentNode attribute to node, return node."
114 if NodeFilterConstants::FILTER_ACCEPT == self.accept_node(&node, can_gc)? {
115 self.current_node.set(&node);
116 return Ok(Some(node));
117 }
118 },
119 None => break,
120 }
121 }
122 // "3. Return null."
123 Ok(None)
124 }
125
126 // https://dom.spec.whatwg.org/#dom-treewalker-firstchild
127 fn FirstChild(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
128 // "The firstChild() method must traverse children of type first."
129 self.traverse_children(
130 |node| node.GetFirstChild(),
131 |node| node.GetNextSibling(),
132 can_gc,
133 )
134 }
135
136 // https://dom.spec.whatwg.org/#dom-treewalker-lastchild
137 fn LastChild(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
138 // "The lastChild() method must traverse children of type last."
139 self.traverse_children(
140 |node| node.GetLastChild(),
141 |node| node.GetPreviousSibling(),
142 can_gc,
143 )
144 }
145
146 // https://dom.spec.whatwg.org/#dom-treewalker-previoussibling
147 fn PreviousSibling(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
148 // "The nextSibling() method must traverse siblings of type next."
149 self.traverse_siblings(
150 |node| node.GetLastChild(),
151 |node| node.GetPreviousSibling(),
152 can_gc,
153 )
154 }
155
156 // https://dom.spec.whatwg.org/#dom-treewalker-nextsibling
157 fn NextSibling(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
158 // "The previousSibling() method must traverse siblings of type previous."
159 self.traverse_siblings(
160 |node| node.GetFirstChild(),
161 |node| node.GetNextSibling(),
162 can_gc,
163 )
164 }
165
166 // https://dom.spec.whatwg.org/#dom-treewalker-previousnode
167 fn PreviousNode(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
168 // "1. Let node be the value of the currentNode attribute."
169 let mut node = self.current_node.get();
170 // "2. While node is not root, run these substeps:"
171 while !self.is_root_node(&node) {
172 // "1. Let sibling be the previous sibling of node."
173 let mut sibling_op = node.GetPreviousSibling();
174 // "2. While sibling is not null, run these subsubsteps:"
175 while sibling_op.is_some() {
176 // "1. Set node to sibling."
177 node = sibling_op.unwrap();
178 // "2. Filter node and let result be the return value."
179 // "3. While result is not FILTER_REJECT and node has a child,
180 // set node to its last child and then filter node and
181 // set result to the return value."
182 // "4. If result is FILTER_ACCEPT, then
183 // set the currentNode attribute to node and return node."
184 loop {
185 let result = self.accept_node(&node, can_gc)?;
186 match result {
187 NodeFilterConstants::FILTER_REJECT => break,
188 _ if node.GetFirstChild().is_some() => node = node.GetLastChild().unwrap(),
189 NodeFilterConstants::FILTER_ACCEPT => {
190 self.current_node.set(&node);
191 return Ok(Some(node));
192 },
193 _ => break,
194 }
195 }
196 // "5. Set sibling to the previous sibling of node."
197 sibling_op = node.GetPreviousSibling()
198 }
199 // "3. If node is root or node's parent is null, return null."
200 if self.is_root_node(&node) || node.GetParentNode().is_none() {
201 return Ok(None);
202 }
203 // "4. Set node to its parent."
204 match node.GetParentNode() {
205 None =>
206 // This can happen if the user set the current node to somewhere
207 // outside of the tree rooted at the original root.
208 {
209 return Ok(None);
210 },
211 Some(n) => node = n,
212 }
213 // "5. Filter node and if the return value is FILTER_ACCEPT, then
214 // set the currentNode attribute to node and return node."
215 if NodeFilterConstants::FILTER_ACCEPT == self.accept_node(&node, can_gc)? {
216 self.current_node.set(&node);
217 return Ok(Some(node));
218 }
219 }
220 // "6. Return null."
221 Ok(None)
222 }
223
224 // https://dom.spec.whatwg.org/#dom-treewalker-nextnode
225 fn NextNode(&self, can_gc: CanGc) -> Fallible<Option<DomRoot<Node>>> {
226 // "1. Let node be the value of the currentNode attribute."
227 let mut node = self.current_node.get();
228 // "2. Let result be FILTER_ACCEPT."
229 let mut result = NodeFilterConstants::FILTER_ACCEPT;
230 // "3. Run these substeps:"
231 loop {
232 // "1. While result is not FILTER_REJECT and node has a child, run these subsubsteps:"
233 loop {
234 if NodeFilterConstants::FILTER_REJECT == result {
235 break;
236 }
237 match node.GetFirstChild() {
238 None => break,
239 Some(child) => {
240 // "1. Set node to its first child."
241 node = child;
242 // "2. Filter node and set result to the return value."
243 result = self.accept_node(&node, can_gc)?;
244 // "3. If result is FILTER_ACCEPT, then
245 // set the currentNode attribute to node and return node."
246 if NodeFilterConstants::FILTER_ACCEPT == result {
247 self.current_node.set(&node);
248 return Ok(Some(node));
249 }
250 },
251 }
252 }
253 // "2. If a node is following node and is not following root,
254 // set node to the first such node."
255 // "Otherwise, return null."
256 match self.first_following_node_not_following_root(&node) {
257 None => return Ok(None),
258 Some(n) => {
259 node = n;
260 // "3. Filter node and set result to the return value."
261 result = self.accept_node(&node, can_gc)?;
262 // "4. If result is FILTER_ACCEPT, then
263 // set the currentNode attribute to node and return node."
264 if NodeFilterConstants::FILTER_ACCEPT == result {
265 self.current_node.set(&node);
266 return Ok(Some(node));
267 }
268 },
269 }
270 // "5. Run these substeps again."
271 }
272 }
273}
274
275impl TreeWalker {
276 // https://dom.spec.whatwg.org/#concept-traverse-children
277 fn traverse_children<F, G>(
278 &self,
279 next_child: F,
280 next_sibling: G,
281 can_gc: CanGc,
282 ) -> Fallible<Option<DomRoot<Node>>>
283 where
284 F: Fn(&Node) -> Option<DomRoot<Node>>,
285 G: Fn(&Node) -> Option<DomRoot<Node>>,
286 {
287 // "To **traverse children** of type *type*, run these steps:"
288 // "1. Let node be the value of the currentNode attribute."
289 let cur = self.current_node.get();
290
291 // "2. Set node to node's first child if type is first, and node's last child if type is last."
292 // "3. If node is null, return null."
293 let mut node = match next_child(&cur) {
294 Some(node) => node,
295 None => return Ok(None),
296 };
297
298 // 4. Main: Repeat these substeps:
299 'main: loop {
300 // "1. Filter node and let result be the return value."
301 let result = self.accept_node(&node, can_gc)?;
302 match result {
303 // "2. If result is FILTER_ACCEPT, then set the currentNode
304 // attribute to node and return node."
305 NodeFilterConstants::FILTER_ACCEPT => {
306 self.current_node.set(&node);
307 return Ok(Some(DomRoot::from_ref(&node)));
308 },
309 // "3. If result is FILTER_SKIP, run these subsubsteps:"
310 NodeFilterConstants::FILTER_SKIP => {
311 // "1. Let child be node's first child if type is first,
312 // and node's last child if type is last."
313 if let Some(child) = next_child(&node) {
314 // "2. If child is not null, set node to child and goto Main."
315 node = child;
316 continue 'main;
317 }
318 },
319 _ => {},
320 }
321 // "4. Repeat these subsubsteps:"
322 loop {
323 // "1. Let sibling be node's next sibling if type is next,
324 // and node's previous sibling if type is previous."
325 match next_sibling(&node) {
326 // "2. If sibling is not null,
327 // set node to sibling and goto Main."
328 Some(sibling) => {
329 node = sibling;
330 continue 'main;
331 },
332 None => {
333 // "3. Let parent be node's parent."
334 match node.GetParentNode() {
335 // "4. If parent is null, parent is root,
336 // or parent is currentNode attribute's value,
337 // return null."
338 None => return Ok(None),
339 Some(ref parent)
340 if self.is_root_node(parent) || self.is_current_node(parent) =>
341 {
342 return Ok(None);
343 },
344 // "5. Otherwise, set node to parent."
345 Some(parent) => node = parent,
346 }
347 },
348 }
349 }
350 }
351 }
352
353 // https://dom.spec.whatwg.org/#concept-traverse-siblings
354 fn traverse_siblings<F, G>(
355 &self,
356 next_child: F,
357 next_sibling: G,
358 can_gc: CanGc,
359 ) -> Fallible<Option<DomRoot<Node>>>
360 where
361 F: Fn(&Node) -> Option<DomRoot<Node>>,
362 G: Fn(&Node) -> Option<DomRoot<Node>>,
363 {
364 // "To **traverse siblings** of type *type* run these steps:"
365 // "1. Let node be the value of the currentNode attribute."
366 let mut node = self.current_node.get();
367 // "2. If node is root, return null."
368 if self.is_root_node(&node) {
369 return Ok(None);
370 }
371 // "3. Run these substeps:"
372 loop {
373 // "1. Let sibling be node's next sibling if type is next,
374 // and node's previous sibling if type is previous."
375 let mut sibling_op = next_sibling(&node);
376 // "2. While sibling is not null, run these subsubsteps:"
377 while sibling_op.is_some() {
378 // "1. Set node to sibling."
379 node = sibling_op.unwrap();
380 // "2. Filter node and let result be the return value."
381 let result = self.accept_node(&node, can_gc)?;
382 // "3. If result is FILTER_ACCEPT, then set the currentNode
383 // attribute to node and return node."
384 if NodeFilterConstants::FILTER_ACCEPT == result {
385 self.current_node.set(&node);
386 return Ok(Some(node));
387 }
388
389 // "4. Set sibling to node's first child if type is next,
390 // and node's last child if type is previous."
391 sibling_op = next_child(&node);
392 // "5. If result is FILTER_REJECT or sibling is null,
393 // then set sibling to node's next sibling if type is next,
394 // and node's previous sibling if type is previous."
395 match (result, &sibling_op) {
396 (NodeFilterConstants::FILTER_REJECT, _) | (_, &None) => {
397 sibling_op = next_sibling(&node)
398 },
399 _ => {},
400 }
401 }
402 // "3. Set node to its parent."
403 match node.GetParentNode() {
404 // "4. If node is null or is root, return null."
405 None => return Ok(None),
406 Some(ref n) if self.is_root_node(n) => return Ok(None),
407 // "5. Filter node and if the return value is FILTER_ACCEPT, then return null."
408 Some(n) => {
409 node = n;
410 if NodeFilterConstants::FILTER_ACCEPT == self.accept_node(&node, can_gc)? {
411 return Ok(None);
412 }
413 },
414 }
415 // "6. Run these substeps again."
416 }
417 }
418
419 // https://dom.spec.whatwg.org/#concept-tree-following
420 fn first_following_node_not_following_root(&self, node: &Node) -> Option<DomRoot<Node>> {
421 // "An object A is following an object B if A and B are in the same tree
422 // and A comes after B in tree order."
423 match node.GetNextSibling() {
424 None => {
425 let mut candidate = DomRoot::from_ref(node);
426 while !self.is_root_node(&candidate) && candidate.GetNextSibling().is_none() {
427 // This can return None if the user set the current node to somewhere
428 // outside of the tree rooted at the original root.
429 candidate = candidate.GetParentNode()?;
430 }
431 if self.is_root_node(&candidate) {
432 None
433 } else {
434 candidate.GetNextSibling()
435 }
436 },
437 it => it,
438 }
439 }
440
441 // https://dom.spec.whatwg.org/#concept-node-filter
442 fn accept_node(&self, node: &Node, can_gc: CanGc) -> Fallible<u16> {
443 // Step 1.
444 if self.active.get() {
445 return Err(Error::InvalidState);
446 }
447 // Step 2.
448 let n = node.NodeType() - 1;
449 // Step 3.
450 if (self.what_to_show & (1 << n)) == 0 {
451 return Ok(NodeFilterConstants::FILTER_SKIP);
452 }
453 match self.filter {
454 // Step 4.
455 Filter::None => Ok(NodeFilterConstants::FILTER_ACCEPT),
456 Filter::Dom(ref callback) => {
457 // Step 5.
458 self.active.set(true);
459 // Step 6.
460 let result = callback.AcceptNode_(self, node, Rethrow, can_gc);
461 // Step 7.
462 self.active.set(false);
463 // Step 8.
464 result
465 },
466 }
467 }
468
469 fn is_root_node(&self, node: &Node) -> bool {
470 Dom::from_ref(node) == self.root_node
471 }
472
473 fn is_current_node(&self, node: &Node) -> bool {
474 node == &*self.current_node.get()
475 }
476}
477
478impl Iterator for &TreeWalker {
479 type Item = DomRoot<Node>;
480
481 fn next(&mut self) -> Option<DomRoot<Node>> {
482 match self.NextNode(CanGc::note()) {
483 Ok(node) => node,
484 Err(_) =>
485 // The Err path happens only when a JavaScript
486 // NodeFilter throws an exception. This iterator
487 // is meant for internal use from Rust code, which
488 // will probably be using a native Rust filter,
489 // which cannot produce an Err result.
490 {
491 unreachable!()
492 },
493 }
494 }
495}
496
497#[derive(JSTraceable)]
498pub(crate) enum Filter {
499 None,
500 Dom(Rc<NodeFilter>),
501}