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::iter::Enumerate;
6use std::vec::IntoIter;
7
8use script_bindings::str::DOMString;
9
10use super::Node;
11use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
12use crate::dom::bindings::root::DomRoot;
13
14/// The context during evaluation of an XPath expression.
15#[derive(Debug)]
16pub(crate) struct EvaluationCtx {
17    /// Where we started at
18    pub(crate) starting_node: DomRoot<Node>,
19    /// The "current" node in the evaluation
20    pub(crate) context_node: DomRoot<Node>,
21    /// Details needed for evaluating a predicate list
22    pub(crate) predicate_ctx: Option<PredicateCtx>,
23    /// The nodes we're currently matching against
24    pub(crate) predicate_nodes: Option<Vec<DomRoot<Node>>>,
25}
26
27#[derive(Clone, Copy, Debug)]
28pub(crate) struct PredicateCtx {
29    pub(crate) index: usize,
30    pub(crate) size: usize,
31}
32
33impl EvaluationCtx {
34    /// Prepares the context used while evaluating the XPath expression
35    pub(crate) fn new(context_node: &Node) -> EvaluationCtx {
36        EvaluationCtx {
37            starting_node: DomRoot::from_ref(context_node),
38            context_node: DomRoot::from_ref(context_node),
39            predicate_ctx: None,
40            predicate_nodes: None,
41        }
42    }
43
44    /// Creates a new context using the provided node as the context node
45    pub(crate) fn subcontext_for_node(&self, node: &Node) -> EvaluationCtx {
46        EvaluationCtx {
47            starting_node: self.starting_node.clone(),
48            context_node: DomRoot::from_ref(node),
49            predicate_ctx: self.predicate_ctx,
50            predicate_nodes: self.predicate_nodes.clone(),
51        }
52    }
53
54    pub(crate) fn update_predicate_nodes(&self, nodes: Vec<&Node>) -> EvaluationCtx {
55        EvaluationCtx {
56            starting_node: self.starting_node.clone(),
57            context_node: self.context_node.clone(),
58            predicate_ctx: None,
59            predicate_nodes: Some(nodes.into_iter().map(DomRoot::from_ref).collect()),
60        }
61    }
62
63    pub(crate) fn subcontext_iter_for_nodes(&self) -> EvalNodesetIter<'_> {
64        let size = self.predicate_nodes.as_ref().map_or(0, |v| v.len());
65        EvalNodesetIter {
66            ctx: self,
67            nodes_iter: self
68                .predicate_nodes
69                .as_ref()
70                .map_or_else(|| Vec::new().into_iter(), |v| v.clone().into_iter())
71                .enumerate(),
72            size,
73        }
74    }
75
76    /// Resolve a namespace prefix using the context node's document
77    pub(crate) fn resolve_namespace(&self, prefix: Option<&str>) -> Option<DOMString> {
78        self.context_node
79            .LookupNamespaceURI(prefix.map(DOMString::from))
80    }
81}
82
83/// When evaluating predicates, we need to keep track of the current node being evaluated and
84/// the index of that node in the nodeset we're operating on.
85pub(crate) struct EvalNodesetIter<'a> {
86    ctx: &'a EvaluationCtx,
87    nodes_iter: Enumerate<IntoIter<DomRoot<Node>>>,
88    size: usize,
89}
90
91impl Iterator for EvalNodesetIter<'_> {
92    type Item = EvaluationCtx;
93
94    fn next(&mut self) -> Option<EvaluationCtx> {
95        self.nodes_iter.next().map(|(idx, node)| EvaluationCtx {
96            starting_node: self.ctx.starting_node.clone(),
97            context_node: node.clone(),
98            predicate_nodes: self.ctx.predicate_nodes.clone(),
99            predicate_ctx: Some(PredicateCtx {
100                index: idx + 1,
101                size: self.size,
102            }),
103        })
104    }
105}