script/dom/
xpathexpression.rs1use dom_struct::dom_struct;
6use js::rust::HandleObject;
7use script_bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId};
8use xpath::{Expression, evaluate_parsed_xpath};
9
10use crate::dom::bindings::codegen::Bindings::XPathExpressionBinding::XPathExpressionMethods;
11use crate::dom::bindings::error::{Error, Fallible};
12use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
13use crate::dom::bindings::root::{Dom, DomRoot};
14use crate::dom::node::Node;
15use crate::dom::window::Window;
16use crate::dom::xpathresult::{XPathResult, XPathResultType};
17use crate::script_runtime::CanGc;
18use crate::xpath::{Value, XPathImplementation};
19
20#[dom_struct]
21pub(crate) struct XPathExpression {
22 reflector_: Reflector,
23 window: Dom<Window>,
24 #[no_trace]
25 parsed_expression: Expression,
26}
27
28impl XPathExpression {
29 fn new_inherited(window: &Window, parsed_expression: Expression) -> XPathExpression {
30 XPathExpression {
31 reflector_: Reflector::new(),
32 window: Dom::from_ref(window),
33 parsed_expression,
34 }
35 }
36
37 pub(crate) fn new(
38 window: &Window,
39 proto: Option<HandleObject>,
40 can_gc: CanGc,
41 parsed_expression: Expression,
42 ) -> DomRoot<XPathExpression> {
43 reflect_dom_object_with_proto(
44 Box::new(XPathExpression::new_inherited(window, parsed_expression)),
45 window,
46 proto,
47 can_gc,
48 )
49 }
50
51 pub(crate) fn evaluate_internal(
52 &self,
53 context_node: &Node,
54 result_type_num: u16,
55 result: Option<&XPathResult>,
56 can_gc: CanGc,
57 ) -> Fallible<DomRoot<XPathResult>> {
58 let is_allowed_context_node_type = matches!(
59 context_node.type_id(),
60 NodeTypeId::Attr |
61 NodeTypeId::CharacterData(
62 CharacterDataTypeId::Comment |
63 CharacterDataTypeId::Text(_) |
64 CharacterDataTypeId::ProcessingInstruction
65 ) |
66 NodeTypeId::Document(_) |
67 NodeTypeId::Element(_)
68 );
69 if !is_allowed_context_node_type {
70 return Err(Error::NotSupported);
71 }
72
73 let result_type = XPathResultType::try_from(result_type_num)
74 .map_err(|()| Error::Type("Invalid XPath result type".to_string()))?;
75
76 let global = self.global();
77 let window = global.as_window();
78
79 let result_value = evaluate_parsed_xpath::<XPathImplementation>(
80 &self.parsed_expression,
81 DomRoot::from_ref(context_node).into(),
82 )
83 .map_err(|_| Error::Operation)?;
84
85 let result_value: Value = match result_type {
87 XPathResultType::Boolean => result_value.convert_to_boolean().into(),
88 XPathResultType::Number => result_value.convert_to_number().into(),
89 XPathResultType::String => result_value.convert_to_string().into(),
90 _ => result_value,
91 };
92
93 let inferred_result_type = if result_type == XPathResultType::Any {
96 match result_value {
97 Value::Boolean(_) => XPathResultType::Boolean,
98 Value::Number(_) => XPathResultType::Number,
99 Value::String(_) => XPathResultType::String,
100 Value::NodeSet(_) => XPathResultType::UnorderedNodeIterator,
101 }
102 } else {
103 result_type
104 };
105
106 if let Some(result) = result {
107 result.reinitialize_with(inferred_result_type, result_value.into());
110 Ok(DomRoot::from_ref(result))
111 } else {
112 Ok(XPathResult::new(
113 window,
114 None,
115 can_gc,
116 inferred_result_type,
117 result_value.into(),
118 ))
119 }
120 }
121}
122
123impl XPathExpressionMethods<crate::DomTypeHolder> for XPathExpression {
124 fn Evaluate(
126 &self,
127 context_node: &Node,
128 result_type_num: u16,
129 result: Option<&XPathResult>,
130 can_gc: CanGc,
131 ) -> Fallible<DomRoot<XPathResult>> {
132 self.evaluate_internal(context_node, result_type_num, result, can_gc)
133 }
134}