script/
xpath.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
5//! Bindings to the `xpath` crate
6
7use std::cell::Ref;
8use std::cmp::Ordering;
9use std::fmt::Debug;
10use std::rc::Rc;
11
12use html5ever::{LocalName, Namespace, Prefix};
13use script_bindings::callback::ExceptionHandling;
14use script_bindings::codegen::GenericBindings::AttrBinding::AttrMethods;
15use script_bindings::codegen::GenericBindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
16use script_bindings::root::Dom;
17use script_bindings::script_runtime::CanGc;
18use script_bindings::str::DOMString;
19use style::Atom;
20use style::dom::OpaqueNode;
21
22use crate::dom::attr::Attr;
23use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
24use crate::dom::bindings::error::{Error, Fallible};
25use crate::dom::bindings::inheritance::Castable;
26use crate::dom::bindings::root::DomRoot;
27use crate::dom::comment::Comment;
28use crate::dom::document::Document;
29use crate::dom::element::Element;
30use crate::dom::node::{Node, NodeTraits, PrecedingNodeIterator, ShadowIncluding};
31use crate::dom::processinginstruction::ProcessingInstruction;
32use crate::dom::text::Text;
33
34pub(crate) type Value = xpath::Value<XPathWrapper<DomRoot<Node>>>;
35
36/// Wrapper type that allows us to define xpath traits on the relevant types,
37/// since they're not defined in `script`.
38#[derive(Clone, Debug, Eq, PartialEq)]
39pub(crate) struct XPathWrapper<T>(pub T);
40
41pub(crate) struct XPathImplementation;
42
43impl xpath::Dom for XPathImplementation {
44    type Node = XPathWrapper<DomRoot<Node>>;
45    type NamespaceResolver = XPathWrapper<Rc<XPathNSResolver>>;
46}
47
48impl xpath::Node for XPathWrapper<DomRoot<Node>> {
49    type ProcessingInstruction = XPathWrapper<DomRoot<ProcessingInstruction>>;
50    type Document = XPathWrapper<DomRoot<Document>>;
51    type Attribute = XPathWrapper<DomRoot<Attr>>;
52    type Element = XPathWrapper<DomRoot<Element>>;
53    /// A opaque handle to a node with the sole purpose of comparing one node with another.
54    type Opaque = OpaqueNode;
55
56    fn is_comment(&self) -> bool {
57        self.0.is::<Comment>()
58    }
59
60    fn is_text(&self) -> bool {
61        self.0.is::<Text>()
62    }
63
64    fn text_content(&self) -> String {
65        self.0.GetTextContent().unwrap_or_default().into()
66    }
67
68    fn language(&self) -> Option<String> {
69        self.0.get_lang()
70    }
71
72    fn parent(&self) -> Option<Self> {
73        // The parent of an attribute node is its owner, see
74        // https://www.w3.org/TR/1999/REC-xpath-19991116/#attribute-nodes
75        if let Some(attribute) = self.0.downcast::<Attr>() {
76            return attribute
77                .GetOwnerElement()
78                .map(DomRoot::upcast)
79                .map(XPathWrapper);
80        }
81
82        self.0.GetParentNode().map(XPathWrapper)
83    }
84
85    fn children(&self) -> impl Iterator<Item = Self> {
86        self.0.children().map(XPathWrapper)
87    }
88
89    fn compare_tree_order(&self, other: &Self) -> Ordering {
90        if self == other {
91            Ordering::Equal
92        } else if self.0.is_before(&other.0) {
93            Ordering::Less
94        } else {
95            Ordering::Greater
96        }
97    }
98
99    fn traverse_preorder(&self) -> impl Iterator<Item = Self> {
100        self.0
101            .traverse_preorder(ShadowIncluding::No)
102            .map(XPathWrapper)
103    }
104
105    fn inclusive_ancestors(&self) -> impl Iterator<Item = Self> {
106        self.0
107            .inclusive_ancestors(ShadowIncluding::No)
108            .map(XPathWrapper)
109    }
110
111    fn preceding_nodes(&self) -> impl Iterator<Item = Self> {
112        PrecedingNodeIteratorWithoutAncestors::new(&self.0).map(XPathWrapper)
113    }
114
115    fn following_nodes(&self) -> impl Iterator<Item = Self> {
116        let owner_document = self.0.owner_document();
117        let next_non_descendant_node = self
118            .0
119            .following_nodes(owner_document.upcast())
120            .next_skipping_children();
121        let following_nodes = next_non_descendant_node
122            .clone()
123            .map(|node| node.following_nodes(owner_document.upcast()))
124            .into_iter()
125            .flatten();
126        next_non_descendant_node
127            .into_iter()
128            .chain(following_nodes)
129            .map(XPathWrapper)
130    }
131
132    fn preceding_siblings(&self) -> impl Iterator<Item = Self> {
133        self.0.preceding_siblings().map(XPathWrapper)
134    }
135
136    fn following_siblings(&self) -> impl Iterator<Item = Self> {
137        self.0.following_siblings().map(XPathWrapper)
138    }
139
140    fn owner_document(&self) -> Self::Document {
141        XPathWrapper(self.0.owner_document())
142    }
143
144    fn to_opaque(&self) -> Self::Opaque {
145        self.0.to_opaque()
146    }
147
148    fn as_processing_instruction(&self) -> Option<Self::ProcessingInstruction> {
149        self.0
150            .downcast::<ProcessingInstruction>()
151            .map(DomRoot::from_ref)
152            .map(XPathWrapper)
153    }
154
155    fn as_attribute(&self) -> Option<Self::Attribute> {
156        self.0
157            .downcast::<Attr>()
158            .map(DomRoot::from_ref)
159            .map(XPathWrapper)
160    }
161
162    fn as_element(&self) -> Option<Self::Element> {
163        self.0
164            .downcast::<Element>()
165            .map(DomRoot::from_ref)
166            .map(XPathWrapper)
167    }
168
169    fn get_root_node(&self) -> Self {
170        XPathWrapper(self.0.GetRootNode(&GetRootNodeOptions::empty()))
171    }
172}
173
174impl xpath::Document for XPathWrapper<DomRoot<Document>> {
175    type Node = XPathWrapper<DomRoot<Node>>;
176
177    fn get_elements_with_id(
178        &self,
179        id: &str,
180    ) -> impl Iterator<Item = XPathWrapper<DomRoot<Element>>> {
181        struct ElementIterator<'a> {
182            elements: Ref<'a, [Dom<Element>]>,
183            position: usize,
184        }
185
186        impl<'a> Iterator for ElementIterator<'a> {
187            type Item = XPathWrapper<DomRoot<Element>>;
188
189            fn next(&mut self) -> Option<Self::Item> {
190                let element = self.elements.get(self.position)?;
191                self.position += 1;
192                Some(element.as_rooted().into())
193            }
194        }
195
196        ElementIterator {
197            elements: self.0.get_elements_with_id(&Atom::from(id)),
198            position: 0,
199        }
200    }
201}
202
203impl xpath::Element for XPathWrapper<DomRoot<Element>> {
204    type Node = XPathWrapper<DomRoot<Node>>;
205    type Attribute = XPathWrapper<DomRoot<Attr>>;
206
207    fn as_node(&self) -> Self::Node {
208        DomRoot::from_ref(self.0.upcast::<Node>()).into()
209    }
210
211    fn attributes(&self) -> impl Iterator<Item = Self::Attribute> {
212        struct AttributeIterator<'a> {
213            attributes: Ref<'a, [Dom<Attr>]>,
214            position: usize,
215        }
216
217        impl<'a> Iterator for AttributeIterator<'a> {
218            type Item = XPathWrapper<DomRoot<Attr>>;
219
220            fn next(&mut self) -> Option<Self::Item> {
221                let attribute = self.attributes.get(self.position)?;
222                self.position += 1;
223                Some(attribute.as_rooted().into())
224            }
225
226            fn size_hint(&self) -> (usize, Option<usize>) {
227                let exact_length = self.attributes.len() - self.position;
228                (exact_length, Some(exact_length))
229            }
230        }
231
232        AttributeIterator {
233            attributes: self.0.attrs(),
234            position: 0,
235        }
236    }
237
238    fn prefix(&self) -> Option<Prefix> {
239        self.0.prefix().clone()
240    }
241
242    fn namespace(&self) -> Namespace {
243        self.0.namespace().clone()
244    }
245
246    fn local_name(&self) -> LocalName {
247        self.0.local_name().clone()
248    }
249
250    fn is_html_element_in_html_document(&self) -> bool {
251        self.0.is_html_element() && self.0.owner_document().is_html_document()
252    }
253}
254
255impl xpath::Attribute for XPathWrapper<DomRoot<Attr>> {
256    type Node = XPathWrapper<DomRoot<Node>>;
257
258    fn as_node(&self) -> Self::Node {
259        XPathWrapper(DomRoot::from_ref(self.0.upcast::<Node>()))
260    }
261
262    fn prefix(&self) -> Option<Prefix> {
263        self.0.prefix().cloned()
264    }
265
266    fn namespace(&self) -> Namespace {
267        self.0.namespace().clone()
268    }
269
270    fn local_name(&self) -> LocalName {
271        self.0.local_name().clone()
272    }
273}
274
275impl xpath::NamespaceResolver for XPathWrapper<Rc<XPathNSResolver>> {
276    fn resolve_namespace_prefix(&self, prefix: &str) -> Option<String> {
277        self.0
278            .LookupNamespaceURI__(
279                Some(DOMString::from(prefix)),
280                ExceptionHandling::Report,
281                CanGc::note(),
282            )
283            .ok()
284            .flatten()
285            .map(String::from)
286    }
287}
288
289impl xpath::ProcessingInstruction for XPathWrapper<DomRoot<ProcessingInstruction>> {
290    fn target(&self) -> String {
291        self.0.target().to_owned().into()
292    }
293}
294
295impl<T> From<T> for XPathWrapper<T> {
296    fn from(value: T) -> Self {
297        Self(value)
298    }
299}
300
301impl<T> XPathWrapper<T> {
302    pub(crate) fn into_inner(self) -> T {
303        self.0
304    }
305}
306
307pub(crate) fn parse_expression(
308    expression: &str,
309    resolver: Option<Rc<XPathNSResolver>>,
310    is_in_html_document: bool,
311) -> Fallible<xpath::Expression> {
312    xpath::parse(expression, resolver.map(XPathWrapper), is_in_html_document).map_err(|error| {
313        match error {
314            xpath::ParserError::FailedToResolveNamespacePrefix => Error::Namespace,
315            _ => Error::Syntax(Some(format!("Failed to parse XPath expression: {error:?}"))),
316        }
317    })
318}
319
320enum PrecedingNodeIteratorWithoutAncestors {
321    Done,
322    NotDone {
323        current: DomRoot<Node>,
324        /// When we're currently walking over the subtree of a node in reverse tree order
325        /// then this is the iterator for doing that.
326        subtree_iterator: Option<PrecedingNodeIterator>,
327    },
328}
329
330/// Returns the previous element (in tree order) that is not an ancestor of `node`.
331fn previous_non_ancestor_node(node: &Node) -> Option<DomRoot<Node>> {
332    let mut current = DomRoot::from_ref(node);
333    loop {
334        if let Some(previous_sibling) = current.GetPreviousSibling() {
335            return Some(previous_sibling);
336        }
337
338        current = current.GetParentNode()?;
339    }
340}
341
342impl PrecedingNodeIteratorWithoutAncestors {
343    fn new(node: &Node) -> Self {
344        let Some(current) = previous_non_ancestor_node(node) else {
345            return Self::Done;
346        };
347
348        Self::NotDone {
349            subtree_iterator: current
350                .descending_last_children()
351                .last()
352                .map(|node| node.preceding_nodes(&current)),
353            current,
354        }
355    }
356}
357
358impl Iterator for PrecedingNodeIteratorWithoutAncestors {
359    type Item = DomRoot<Node>;
360
361    fn next(&mut self) -> Option<Self::Item> {
362        let Self::NotDone {
363            current,
364            subtree_iterator,
365        } = self
366        else {
367            return None;
368        };
369
370        if let Some(next_node) = subtree_iterator
371            .as_mut()
372            .and_then(|iterator| iterator.next())
373        {
374            return Some(next_node);
375        }
376
377        // Our current subtree is exhausted. Return the root of the subtree and move on to the next one
378        // in inverse tree order.
379        let Some(next_subtree) = previous_non_ancestor_node(current) else {
380            *self = Self::Done;
381            return None;
382        };
383
384        *current = next_subtree;
385        *subtree_iterator = current
386            .descending_last_children()
387            .last()
388            .map(|node| node.preceding_nodes(current));
389
390        self.next()
391    }
392}