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