script/dom/xpath/
xpathexpression.rs1use dom_struct::dom_struct;
6use js::rust::HandleObject;
7use script_bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId};
8use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
9use xpath::{Expression, evaluate_parsed_xpath};
10
11use crate::dom::bindings::codegen::Bindings::XPathExpressionBinding::XPathExpressionMethods;
12use crate::dom::bindings::error::{Error, Fallible};
13use crate::dom::bindings::reflector::DomGlobal;
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::{Value, XPathImplementation};
20
21#[dom_struct]
22pub(crate) struct XPathExpression {
23 reflector_: Reflector,
24 window: Dom<Window>,
25 #[no_trace]
26 parsed_expression: Expression,
27}
28
29impl XPathExpression {
30 fn new_inherited(window: &Window, parsed_expression: Expression) -> 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: Expression,
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 can_gc: CanGc,
58 ) -> Fallible<DomRoot<XPathResult>> {
59 let is_allowed_context_node_type = matches!(
60 context_node.type_id(),
61 NodeTypeId::Attr |
62 NodeTypeId::CharacterData(
63 CharacterDataTypeId::Comment |
64 CharacterDataTypeId::Text(_) |
65 CharacterDataTypeId::ProcessingInstruction
66 ) |
67 NodeTypeId::Document(_) |
68 NodeTypeId::Element(_)
69 );
70 if !is_allowed_context_node_type {
71 return Err(Error::NotSupported(None));
72 }
73
74 let result_type = XPathResultType::try_from(result_type_num)
75 .map_err(|()| Error::Type(c"Invalid XPath result type".to_owned()))?;
76
77 let global = self.global();
78 let window = global.as_window();
79
80 let result_value = evaluate_parsed_xpath::<XPathImplementation>(
81 &self.parsed_expression,
82 DomRoot::from_ref(context_node).into(),
83 )
84 .map_err(|_| Error::Operation(None))?;
85
86 let result_value: Value = match result_type {
88 XPathResultType::Boolean => result_value.convert_to_boolean().into(),
89 XPathResultType::Number => result_value.convert_to_number().into(),
90 XPathResultType::String => result_value.convert_to_string().into(),
91 _ => result_value,
92 };
93
94 let inferred_result_type = if result_type == XPathResultType::Any {
97 match result_value {
98 Value::Boolean(_) => XPathResultType::Boolean,
99 Value::Number(_) => XPathResultType::Number,
100 Value::String(_) => XPathResultType::String,
101 Value::NodeSet(_) => XPathResultType::UnorderedNodeIterator,
102 }
103 } else {
104 result_type
105 };
106
107 if let Some(result) = result {
108 result.reinitialize_with(inferred_result_type, result_value.into());
111 Ok(DomRoot::from_ref(result))
112 } else {
113 Ok(XPathResult::new(
114 window,
115 None,
116 can_gc,
117 inferred_result_type,
118 result_value.into(),
119 ))
120 }
121 }
122}
123
124impl XPathExpressionMethods<crate::DomTypeHolder> for XPathExpression {
125 fn Evaluate(
127 &self,
128 context_node: &Node,
129 result_type_num: u16,
130 result: Option<&XPathResult>,
131 can_gc: CanGc,
132 ) -> Fallible<DomRoot<XPathResult>> {
133 self.evaluate_internal(context_node, result_type_num, result, can_gc)
134 }
135}