use dom_struct::dom_struct;
use js::context::JSContext;
use js::rust::HandleObject;
use script_bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId};
use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
use xpath::{Expression, evaluate_parsed_xpath};
use crate::dom::bindings::codegen::Bindings::XPathExpressionBinding::XPathExpressionMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::DomGlobal;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::node::Node;
use crate::dom::window::Window;
use crate::dom::xpathresult::{XPathResult, XPathResultType};
use crate::xpath::{Value, XPathImplementation};
#[dom_struct]
pub(crate) struct XPathExpression {
reflector_: Reflector,
window: Dom<Window>,
#[no_trace]
parsed_expression: Expression,
}
impl XPathExpression {
fn new_inherited(window: &Window, parsed_expression: Expression) -> XPathExpression {
XPathExpression {
reflector_: Reflector::new(),
window: Dom::from_ref(window),
parsed_expression,
}
}
pub(crate) fn new(
cx: &mut JSContext,
window: &Window,
proto: Option<HandleObject>,
parsed_expression: Expression,
) -> DomRoot<XPathExpression> {
reflect_dom_object_with_proto(
cx,
Box::new(XPathExpression::new_inherited(window, parsed_expression)),
window,
proto,
)
}
pub(crate) fn evaluate_internal(
&self,
cx: &mut JSContext,
context_node: &Node,
result_type_num: u16,
result: Option<&XPathResult>,
) -> Fallible<DomRoot<XPathResult>> {
let is_allowed_context_node_type = matches!(
context_node.type_id(),
NodeTypeId::Attr |
NodeTypeId::CharacterData(
CharacterDataTypeId::Comment |
CharacterDataTypeId::Text(_) |
CharacterDataTypeId::ProcessingInstruction
) |
NodeTypeId::Document(_) |
NodeTypeId::Element(_)
);
if !is_allowed_context_node_type {
return Err(Error::NotSupported(None));
}
let result_type = XPathResultType::try_from(result_type_num)
.map_err(|()| Error::Type(c"Invalid XPath result type".to_owned()))?;
let global = self.global();
let window = global.as_window();
let result_value = evaluate_parsed_xpath::<XPathImplementation>(
cx,
&self.parsed_expression,
DomRoot::from_ref(context_node).into(),
)
.map_err(|_| Error::Operation(None))?;
let result_value: Value = match result_type {
XPathResultType::Boolean => result_value.convert_to_boolean().into(),
XPathResultType::Number => result_value.convert_to_number().into(),
XPathResultType::String => result_value.convert_to_string().into(),
_ => result_value,
};
let inferred_result_type = if result_type == XPathResultType::Any {
match result_value {
Value::Boolean(_) => XPathResultType::Boolean,
Value::Number(_) => XPathResultType::Number,
Value::String(_) => XPathResultType::String,
Value::NodeSet(_) => XPathResultType::UnorderedNodeIterator,
}
} else {
result_type
};
if let Some(result) = result {
result.reinitialize_with(cx.no_gc(), inferred_result_type, result_value);
Ok(DomRoot::from_ref(result))
} else {
Ok(XPathResult::new(
cx,
window,
inferred_result_type,
result_value,
))
}
}
}
impl XPathExpressionMethods<crate::DomTypeHolder> for XPathExpression {
fn Evaluate(
&self,
cx: &mut JSContext,
context_node: &Node,
result_type_num: u16,
result: Option<&XPathResult>,
) -> Fallible<DomRoot<XPathResult>> {
self.evaluate_internal(cx, context_node, result_type_num, result)
}
}