use std::cell::Cell;
use std::rc::Rc;
use dom_struct::dom_struct;
use crate::dom::bindings::callback::ExceptionHandling::Rethrow;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use crate::dom::bindings::codegen::Bindings::NodeFilterBinding::{NodeFilter, NodeFilterConstants};
use crate::dom::bindings::codegen::Bindings::NodeIteratorBinding::NodeIteratorMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot, MutDom};
use crate::dom::document::Document;
use crate::dom::node::Node;
use crate::script_runtime::CanGc;
#[dom_struct]
pub struct NodeIterator {
reflector_: Reflector,
root_node: Dom<Node>,
#[ignore_malloc_size_of = "Defined in rust-mozjs"]
reference_node: MutDom<Node>,
pointer_before_reference_node: Cell<bool>,
what_to_show: u32,
#[ignore_malloc_size_of = "Rc<NodeFilter> has shared ownership, so its size cannot be measured accurately"]
filter: Filter,
active: Cell<bool>,
}
impl NodeIterator {
fn new_inherited(root_node: &Node, what_to_show: u32, filter: Filter) -> NodeIterator {
NodeIterator {
reflector_: Reflector::new(),
root_node: Dom::from_ref(root_node),
reference_node: MutDom::new(root_node),
pointer_before_reference_node: Cell::new(true),
what_to_show,
filter,
active: Cell::new(false),
}
}
pub fn new_with_filter(
document: &Document,
root_node: &Node,
what_to_show: u32,
filter: Filter,
) -> DomRoot<NodeIterator> {
reflect_dom_object(
Box::new(NodeIterator::new_inherited(root_node, what_to_show, filter)),
document.window(),
CanGc::note(),
)
}
pub fn new(
document: &Document,
root_node: &Node,
what_to_show: u32,
node_filter: Option<Rc<NodeFilter>>,
) -> DomRoot<NodeIterator> {
let filter = match node_filter {
None => Filter::None,
Some(jsfilter) => Filter::Callback(jsfilter),
};
NodeIterator::new_with_filter(document, root_node, what_to_show, filter)
}
}
impl NodeIteratorMethods<crate::DomTypeHolder> for NodeIterator {
fn Root(&self) -> DomRoot<Node> {
DomRoot::from_ref(&*self.root_node)
}
fn WhatToShow(&self) -> u32 {
self.what_to_show
}
fn GetFilter(&self) -> Option<Rc<NodeFilter>> {
match self.filter {
Filter::None => None,
Filter::Callback(ref nf) => Some((*nf).clone()),
}
}
fn ReferenceNode(&self) -> DomRoot<Node> {
self.reference_node.get()
}
fn PointerBeforeReferenceNode(&self) -> bool {
self.pointer_before_reference_node.get()
}
fn NextNode(&self) -> Fallible<Option<DomRoot<Node>>> {
let node = self.reference_node.get();
let mut before_node = self.pointer_before_reference_node.get();
if before_node {
before_node = false;
let result = self.accept_node(&node)?;
if result == NodeFilterConstants::FILTER_ACCEPT {
self.reference_node.set(&node);
self.pointer_before_reference_node.set(before_node);
return Ok(Some(node));
}
}
for following_node in node.following_nodes(&self.root_node) {
let result = self.accept_node(&following_node)?;
if result == NodeFilterConstants::FILTER_ACCEPT {
self.reference_node.set(&following_node);
self.pointer_before_reference_node.set(before_node);
return Ok(Some(following_node));
}
}
Ok(None)
}
fn PreviousNode(&self) -> Fallible<Option<DomRoot<Node>>> {
let node = self.reference_node.get();
let mut before_node = self.pointer_before_reference_node.get();
if !before_node {
before_node = true;
let result = self.accept_node(&node)?;
if result == NodeFilterConstants::FILTER_ACCEPT {
self.reference_node.set(&node);
self.pointer_before_reference_node.set(before_node);
return Ok(Some(node));
}
}
for preceding_node in node.preceding_nodes(&self.root_node) {
let result = self.accept_node(&preceding_node)?;
if result == NodeFilterConstants::FILTER_ACCEPT {
self.reference_node.set(&preceding_node);
self.pointer_before_reference_node.set(before_node);
return Ok(Some(preceding_node));
}
}
Ok(None)
}
fn Detach(&self) {
}
}
impl NodeIterator {
fn accept_node(&self, node: &Node) -> Fallible<u16> {
if self.active.get() {
return Err(Error::InvalidState);
}
let n = node.NodeType() - 1;
if (self.what_to_show & (1 << n)) == 0 {
return Ok(NodeFilterConstants::FILTER_SKIP);
}
match self.filter {
Filter::None => Ok(NodeFilterConstants::FILTER_ACCEPT),
Filter::Callback(ref callback) => {
self.active.set(true);
let result = callback.AcceptNode_(self, node, Rethrow);
self.active.set(false);
result
},
}
}
}
#[derive(JSTraceable)]
pub enum Filter {
None,
Callback(Rc<NodeFilter>),
}