script/xpath/
context.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::fmt;
6use std::iter::Enumerate;
7use std::rc::Rc;
8use std::vec::IntoIter;
9
10use script_bindings::error::Fallible;
11use script_bindings::script_runtime::CanGc;
12use script_bindings::str::DOMString;
13
14use super::Node;
15use crate::dom::bindings::callback::ExceptionHandling;
16use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
17use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
18use crate::dom::bindings::root::DomRoot;
19
20/// The context during evaluation of an XPath expression.
21pub(crate) struct EvaluationCtx {
22    /// Where we started at.
23    pub(crate) starting_node: DomRoot<Node>,
24    /// The "current" node in the evaluation.
25    pub(crate) context_node: DomRoot<Node>,
26    /// Details needed for evaluating a predicate list.
27    pub(crate) predicate_ctx: Option<PredicateCtx>,
28    /// The nodes we're currently matching against.
29    pub(crate) predicate_nodes: Option<Vec<DomRoot<Node>>>,
30    /// A list of known namespace prefixes.
31    pub(crate) resolver: Option<Rc<XPathNSResolver>>,
32}
33
34#[derive(Clone, Copy, Debug)]
35pub(crate) struct PredicateCtx {
36    pub(crate) index: usize,
37    pub(crate) size: usize,
38}
39
40impl EvaluationCtx {
41    /// Prepares the context used while evaluating the XPath expression
42    pub(crate) fn new(context_node: &Node, resolver: Option<Rc<XPathNSResolver>>) -> EvaluationCtx {
43        EvaluationCtx {
44            starting_node: DomRoot::from_ref(context_node),
45            context_node: DomRoot::from_ref(context_node),
46            predicate_ctx: None,
47            predicate_nodes: None,
48            resolver,
49        }
50    }
51
52    /// Creates a new context using the provided node as the context node
53    pub(crate) fn subcontext_for_node(&self, node: &Node) -> EvaluationCtx {
54        EvaluationCtx {
55            starting_node: self.starting_node.clone(),
56            context_node: DomRoot::from_ref(node),
57            predicate_ctx: self.predicate_ctx,
58            predicate_nodes: self.predicate_nodes.clone(),
59            resolver: self.resolver.clone(),
60        }
61    }
62
63    pub(crate) fn update_predicate_nodes(&self, nodes: Vec<&Node>) -> EvaluationCtx {
64        EvaluationCtx {
65            starting_node: self.starting_node.clone(),
66            context_node: self.context_node.clone(),
67            predicate_ctx: None,
68            predicate_nodes: Some(nodes.into_iter().map(DomRoot::from_ref).collect()),
69            resolver: self.resolver.clone(),
70        }
71    }
72
73    pub(crate) fn subcontext_iter_for_nodes(&self) -> EvalNodesetIter<'_> {
74        let size = self.predicate_nodes.as_ref().map_or(0, |v| v.len());
75        EvalNodesetIter {
76            ctx: self,
77            nodes_iter: self
78                .predicate_nodes
79                .as_ref()
80                .map_or_else(|| Vec::new().into_iter(), |v| v.clone().into_iter())
81                .enumerate(),
82            size,
83        }
84    }
85
86    /// Resolve a namespace prefix using the context node's document
87    pub(crate) fn resolve_namespace(
88        &self,
89        prefix: Option<&str>,
90        can_gc: CanGc,
91    ) -> Fallible<Option<DOMString>> {
92        // First check if the prefix is known by our resolver function
93        if let Some(resolver) = self.resolver.as_ref() {
94            if let Some(namespace_uri) = resolver.LookupNamespaceURI__(
95                prefix.map(DOMString::from),
96                ExceptionHandling::Rethrow,
97                can_gc,
98            )? {
99                return Ok(Some(namespace_uri));
100            }
101        }
102
103        // Then, see if it's defined on the context node
104        Ok(self
105            .context_node
106            .LookupNamespaceURI(prefix.map(DOMString::from)))
107    }
108}
109
110/// When evaluating predicates, we need to keep track of the current node being evaluated and
111/// the index of that node in the nodeset we're operating on.
112pub(crate) struct EvalNodesetIter<'a> {
113    ctx: &'a EvaluationCtx,
114    nodes_iter: Enumerate<IntoIter<DomRoot<Node>>>,
115    size: usize,
116}
117
118impl Iterator for EvalNodesetIter<'_> {
119    type Item = EvaluationCtx;
120
121    fn next(&mut self) -> Option<EvaluationCtx> {
122        self.nodes_iter.next().map(|(idx, node)| EvaluationCtx {
123            starting_node: self.ctx.starting_node.clone(),
124            context_node: node.clone(),
125            predicate_nodes: self.ctx.predicate_nodes.clone(),
126            predicate_ctx: Some(PredicateCtx {
127                index: idx + 1,
128                size: self.size,
129            }),
130            resolver: self.ctx.resolver.clone(),
131        })
132    }
133}
134
135impl fmt::Debug for EvaluationCtx {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        f.debug_struct("EvaluationCtx")
138            .field("starting_node", &self.starting_node)
139            .field("context_node", &self.context_node)
140            .field("predicate_ctx", &self.predicate_ctx)
141            .field("predicate_nodes", &self.predicate_nodes)
142            .field("resolver", &"<callback function>")
143            .finish()
144    }
145}