script/dom/
xpathexpression.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::rc::Rc;
6
7use dom_struct::dom_struct;
8use js::rust::HandleObject;
9use xpath::{Error as XPathError, Expression, evaluate_parsed_xpath};
10
11use crate::dom::bindings::codegen::Bindings::XPathExpressionBinding::XPathExpressionMethods;
12use crate::dom::bindings::codegen::Bindings::XPathNSResolverBinding::XPathNSResolver;
13use crate::dom::bindings::error::{Error, Fallible};
14use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
15use crate::dom::bindings::root::{Dom, DomRoot};
16use crate::dom::node::Node;
17use crate::dom::window::Window;
18use crate::dom::xpathresult::{XPathResult, XPathResultType};
19use crate::script_runtime::CanGc;
20use crate::xpath::{XPathImplementation, XPathWrapper};
21
22#[dom_struct]
23pub(crate) struct XPathExpression {
24    reflector_: Reflector,
25    window: Dom<Window>,
26    #[no_trace]
27    parsed_expression: Expression,
28}
29
30impl XPathExpression {
31    fn new_inherited(window: &Window, parsed_expression: Expression) -> XPathExpression {
32        XPathExpression {
33            reflector_: Reflector::new(),
34            window: Dom::from_ref(window),
35            parsed_expression,
36        }
37    }
38
39    pub(crate) fn new(
40        window: &Window,
41        proto: Option<HandleObject>,
42        can_gc: CanGc,
43        parsed_expression: Expression,
44    ) -> DomRoot<XPathExpression> {
45        reflect_dom_object_with_proto(
46            Box::new(XPathExpression::new_inherited(window, parsed_expression)),
47            window,
48            proto,
49            can_gc,
50        )
51    }
52
53    pub(crate) fn evaluate_internal(
54        &self,
55        context_node: &Node,
56        result_type_num: u16,
57        result: Option<&XPathResult>,
58        resolver: Option<Rc<XPathNSResolver>>,
59        can_gc: CanGc,
60    ) -> Fallible<DomRoot<XPathResult>> {
61        let result_type = XPathResultType::try_from(result_type_num)
62            .map_err(|()| Error::Type("Invalid XPath result type".to_string()))?;
63
64        let global = self.global();
65        let window = global.as_window();
66
67        let result_value = evaluate_parsed_xpath::<XPathImplementation>(
68            &self.parsed_expression,
69            DomRoot::from_ref(context_node).into(),
70            resolver.map(XPathWrapper),
71        )
72        .map_err(|error| match error {
73            XPathError::JsException(exception) => exception,
74            _ => Error::Operation,
75        })?
76        .into();
77
78        if let Some(result) = result {
79            // According to https://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator-evaluate, reusing
80            // the provided result object is optional. We choose to do it here because thats what other browsers do.
81            result.reinitialize_with(result_type, result_value);
82            Ok(DomRoot::from_ref(result))
83        } else {
84            Ok(XPathResult::new(
85                window,
86                None,
87                can_gc,
88                result_type,
89                result_value,
90            ))
91        }
92    }
93}
94
95impl XPathExpressionMethods<crate::DomTypeHolder> for XPathExpression {
96    /// <https://dom.spec.whatwg.org/#dom-xpathexpression-evaluate>
97    fn Evaluate(
98        &self,
99        context_node: &Node,
100        result_type_num: u16,
101        result: Option<&XPathResult>,
102        can_gc: CanGc,
103    ) -> Fallible<DomRoot<XPathResult>> {
104        self.evaluate_internal(context_node, result_type_num, result, None, can_gc)
105    }
106}