mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
Pass all xml_xpath_tests.xml
tests
Signed-off-by: Ville Lindholm <ville@lindholm.dev>
This commit is contained in:
parent
6d99c09499
commit
a97131fc57
3 changed files with 65 additions and 30 deletions
|
@ -5,10 +5,14 @@
|
||||||
use std::iter::Enumerate;
|
use std::iter::Enumerate;
|
||||||
use std::vec::IntoIter;
|
use std::vec::IntoIter;
|
||||||
|
|
||||||
|
use script_bindings::str::DOMString;
|
||||||
|
|
||||||
use super::Node;
|
use super::Node;
|
||||||
use crate::dom::bindings::root::DomRoot;
|
use crate::dom::bindings::root::DomRoot;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||||
|
|
||||||
/// The context during evaluation of an XPath expression.
|
/// The context during evaluation of an XPath expression.
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) struct EvaluationCtx {
|
pub(crate) struct EvaluationCtx {
|
||||||
/// Where we started at
|
/// Where we started at
|
||||||
pub(crate) starting_node: DomRoot<Node>,
|
pub(crate) starting_node: DomRoot<Node>,
|
||||||
|
@ -20,7 +24,7 @@ pub(crate) struct EvaluationCtx {
|
||||||
pub(crate) predicate_nodes: Option<Vec<DomRoot<Node>>>,
|
pub(crate) predicate_nodes: Option<Vec<DomRoot<Node>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub(crate) struct PredicateCtx {
|
pub(crate) struct PredicateCtx {
|
||||||
pub(crate) index: usize,
|
pub(crate) index: usize,
|
||||||
pub(crate) size: usize,
|
pub(crate) size: usize,
|
||||||
|
@ -68,6 +72,11 @@ impl EvaluationCtx {
|
||||||
size,
|
size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a namespace prefix using the context node's document
|
||||||
|
pub(crate) fn resolve_namespace(&self, prefix: Option<&str>) -> Option<DOMString> {
|
||||||
|
self.context_node.LookupNamespaceURI(prefix.map(DOMString::from))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When evaluating predicates, we need to keep track of the current node being evaluated and
|
/// When evaluating predicates, we need to keep track of the current node being evaluated and
|
||||||
|
|
|
@ -12,6 +12,7 @@ use super::parser::{
|
||||||
QName as ParserQualName, RelationalOp, StepExpr, UnaryOp,
|
QName as ParserQualName, RelationalOp, StepExpr, UnaryOp,
|
||||||
};
|
};
|
||||||
use super::{EvaluationCtx, Value};
|
use super::{EvaluationCtx, Value};
|
||||||
|
use crate::dom::attr::Attr;
|
||||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||||
use crate::dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId};
|
use crate::dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId};
|
||||||
use crate::dom::bindings::root::DomRoot;
|
use crate::dom::bindings::root::DomRoot;
|
||||||
|
@ -246,21 +247,29 @@ impl Evaluatable for PathExpr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&ParserQualName> for QualName {
|
pub(crate) struct QualNameConverter<'a> {
|
||||||
|
qname: &'a ParserQualName,
|
||||||
|
context: &'a EvaluationCtx,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<QualNameConverter<'a>> for QualName {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(qname: &ParserQualName) -> Result<Self, Self::Error> {
|
fn try_from(converter: QualNameConverter<'a>) -> Result<Self, Self::Error> {
|
||||||
let qname_as_str = qname.to_string();
|
let qname_as_str = converter.qname.to_string();
|
||||||
if let Ok((ns, prefix, local)) = validate_and_extract(None, &qname_as_str) {
|
let namespace = converter.context.resolve_namespace(converter.qname.prefix.as_deref());
|
||||||
|
|
||||||
|
if let Ok((ns, prefix, local)) = validate_and_extract(namespace, &qname_as_str) {
|
||||||
Ok(QualName { prefix, ns, local })
|
Ok(QualName { prefix, ns, local })
|
||||||
} else {
|
} else {
|
||||||
Err(Error::InvalidQName {
|
Err(Error::InvalidQName {
|
||||||
qname: qname.clone(),
|
qname: converter.qname.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) enum NameTestComparisonMode {
|
pub(crate) enum NameTestComparisonMode {
|
||||||
/// Namespaces must match exactly
|
/// Namespaces must match exactly
|
||||||
XHtml,
|
XHtml,
|
||||||
|
@ -310,17 +319,18 @@ pub(crate) fn element_name_test(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_node_test(test: &NodeTest, node: &Node) -> Result<bool, Error> {
|
fn apply_node_test(context: &EvaluationCtx, test: &NodeTest, node: &Node) -> Result<bool, Error> {
|
||||||
let result = match test {
|
let result = match test {
|
||||||
NodeTest::Name(qname) => {
|
NodeTest::Name(qname) => {
|
||||||
// Convert the unvalidated "parser QualName" into the proper QualName structure
|
// Convert the unvalidated "parser QualName" into the proper QualName structure
|
||||||
let wanted_name: QualName = qname.try_into()?;
|
let wanted_name: QualName = QualNameConverter { qname, context }.try_into()?;
|
||||||
if matches!(node.type_id(), NodeTypeId::Element(_)) {
|
match node.type_id() {
|
||||||
|
NodeTypeId::Element(_) => {
|
||||||
let element = node.downcast::<Element>().unwrap();
|
let element = node.downcast::<Element>().unwrap();
|
||||||
let comparison_mode = if node.owner_doc().is_xhtml_document() {
|
let comparison_mode = if node.owner_doc().is_html_document() {
|
||||||
|
NameTestComparisonMode::Html
|
||||||
|
} else {
|
||||||
NameTestComparisonMode::XHtml
|
NameTestComparisonMode::XHtml
|
||||||
} else {
|
|
||||||
NameTestComparisonMode::Html
|
|
||||||
};
|
};
|
||||||
let element_qualname = QualName::new(
|
let element_qualname = QualName::new(
|
||||||
element.prefix().as_ref().cloned(),
|
element.prefix().as_ref().cloned(),
|
||||||
|
@ -328,11 +338,22 @@ fn apply_node_test(test: &NodeTest, node: &Node) -> Result<bool, Error> {
|
||||||
element.local_name().clone(),
|
element.local_name().clone(),
|
||||||
);
|
);
|
||||||
element_name_test(wanted_name, element_qualname, comparison_mode)
|
element_name_test(wanted_name, element_qualname, comparison_mode)
|
||||||
} else {
|
},
|
||||||
false
|
NodeTypeId::Attr => {
|
||||||
|
let attr = node.downcast::<Attr>().unwrap();
|
||||||
|
let attr_qualname = QualName::new(
|
||||||
|
attr.prefix().cloned(),
|
||||||
|
attr.namespace().clone(),
|
||||||
|
attr.local_name().clone(),
|
||||||
|
);
|
||||||
|
// attributes are always compared with strict namespace matching
|
||||||
|
let comparison_mode = NameTestComparisonMode::XHtml;
|
||||||
|
element_name_test(wanted_name, attr_qualname, comparison_mode)
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NodeTest::Wildcard => true,
|
NodeTest::Wildcard => matches!(node.type_id(), NodeTypeId::Element(_)),
|
||||||
NodeTest::Kind(kind) => match kind {
|
NodeTest::Kind(kind) => match kind {
|
||||||
KindTest::PI(target) => {
|
KindTest::PI(target) => {
|
||||||
if NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) ==
|
if NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) ==
|
||||||
|
@ -427,7 +448,7 @@ impl Evaluatable for StepExpr {
|
||||||
let filtered_nodes: Vec<DomRoot<Node>> = nodes
|
let filtered_nodes: Vec<DomRoot<Node>> = nodes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|node| {
|
.map(|node| {
|
||||||
apply_node_test(&axis_step.node_test, &node)
|
apply_node_test(context, &axis_step.node_test, &node)
|
||||||
.map(|matches| matches.then_some(node))
|
.map(|matches| matches.then_some(node))
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
|
@ -505,6 +526,7 @@ impl Evaluatable for PredicateExpr {
|
||||||
|
|
||||||
let v = match eval_result {
|
let v = match eval_result {
|
||||||
Ok(Value::Number(v)) => Ok(predicate_ctx.index == v as usize),
|
Ok(Value::Number(v)) => Ok(predicate_ctx.index == v as usize),
|
||||||
|
Ok(Value::Boolean(v)) => Ok(v),
|
||||||
Ok(v) => Ok(v.boolean()),
|
Ok(v) => Ok(v.boolean()),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
};
|
};
|
||||||
|
|
|
@ -510,7 +510,7 @@ fn union_expr(input: &str) -> IResult<&str, Expr> {
|
||||||
fn path_expr(input: &str) -> IResult<&str, Expr> {
|
fn path_expr(input: &str) -> IResult<&str, Expr> {
|
||||||
alt((
|
alt((
|
||||||
// "//" RelativePathExpr
|
// "//" RelativePathExpr
|
||||||
map(pair(tag("//"), relative_path_expr), |(_, rel_path)| {
|
map(pair(tag("//"), move |i| relative_path_expr(true, i)), |(_, rel_path)| {
|
||||||
Expr::Path(PathExpr {
|
Expr::Path(PathExpr {
|
||||||
is_absolute: true,
|
is_absolute: true,
|
||||||
is_descendant: true,
|
is_descendant: true,
|
||||||
|
@ -521,7 +521,7 @@ fn path_expr(input: &str) -> IResult<&str, Expr> {
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
// "/" RelativePathExpr?
|
// "/" RelativePathExpr?
|
||||||
map(pair(char('/'), opt(relative_path_expr)), |(_, rel_path)| {
|
map(pair(char('/'), opt(move |i| relative_path_expr(false, i))), |(_, rel_path)| {
|
||||||
Expr::Path(PathExpr {
|
Expr::Path(PathExpr {
|
||||||
is_absolute: true,
|
is_absolute: true,
|
||||||
is_descendant: false,
|
is_descendant: false,
|
||||||
|
@ -534,16 +534,15 @@ fn path_expr(input: &str) -> IResult<&str, Expr> {
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
// RelativePathExpr
|
// RelativePathExpr
|
||||||
relative_path_expr,
|
move |i| relative_path_expr(false, i),
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn relative_path_expr(input: &str) -> IResult<&str, Expr> {
|
fn relative_path_expr(is_descendant: bool, input: &str) -> IResult<&str, Expr> {
|
||||||
let (input, first) = step_expr(input)?;
|
let (input, first) = step_expr(is_descendant, input)?;
|
||||||
let (input, steps) = many0(pair(
|
let (input, steps) = many0(pair(
|
||||||
// ("/" | "//")
|
|
||||||
ws(alt((value(true, tag("//")), value(false, char('/'))))),
|
ws(alt((value(true, tag("//")), value(false, char('/'))))),
|
||||||
step_expr,
|
move |i| step_expr(is_descendant, i),
|
||||||
))(input)?;
|
))(input)?;
|
||||||
|
|
||||||
let mut all_steps = vec![first];
|
let mut all_steps = vec![first];
|
||||||
|
@ -569,16 +568,16 @@ fn relative_path_expr(input: &str) -> IResult<&str, Expr> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_expr(input: &str) -> IResult<&str, StepExpr> {
|
fn step_expr(is_descendant: bool, input: &str) -> IResult<&str, StepExpr> {
|
||||||
alt((
|
alt((
|
||||||
map(filter_expr, StepExpr::Filter),
|
map(filter_expr, StepExpr::Filter),
|
||||||
map(axis_step, StepExpr::Axis),
|
map(|i| axis_step(is_descendant, i), StepExpr::Axis),
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn axis_step(input: &str) -> IResult<&str, AxisStep> {
|
fn axis_step(is_descendant: bool, input: &str) -> IResult<&str, AxisStep> {
|
||||||
let (input, (step, predicates)) =
|
let (input, (step, predicates)) =
|
||||||
pair(alt((forward_step, reverse_step)), predicate_list)(input)?;
|
pair(alt((move |i| forward_step(is_descendant, i), reverse_step)), predicate_list)(input)?;
|
||||||
|
|
||||||
let (axis, node_test) = step;
|
let (axis, node_test) = step;
|
||||||
Ok((
|
Ok((
|
||||||
|
@ -591,8 +590,11 @@ fn axis_step(input: &str) -> IResult<&str, AxisStep> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn forward_step(input: &str) -> IResult<&str, (Axis, NodeTest)> {
|
fn forward_step(is_descendant: bool, input: &str) -> IResult<&str, (Axis, NodeTest)> {
|
||||||
alt((pair(forward_axis, node_test), abbrev_forward_step))(input)
|
alt((
|
||||||
|
pair(forward_axis, node_test),
|
||||||
|
move |i| abbrev_forward_step(is_descendant, i),
|
||||||
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn forward_axis(input: &str) -> IResult<&str, Axis> {
|
fn forward_axis(input: &str) -> IResult<&str, Axis> {
|
||||||
|
@ -610,7 +612,7 @@ fn forward_axis(input: &str) -> IResult<&str, Axis> {
|
||||||
Ok((input, axis))
|
Ok((input, axis))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn abbrev_forward_step(input: &str) -> IResult<&str, (Axis, NodeTest)> {
|
fn abbrev_forward_step(is_descendant: bool, input: &str) -> IResult<&str, (Axis, NodeTest)> {
|
||||||
let (input, attr) = opt(char('@'))(input)?;
|
let (input, attr) = opt(char('@'))(input)?;
|
||||||
let (input, test) = node_test(input)?;
|
let (input, test) = node_test(input)?;
|
||||||
|
|
||||||
|
@ -619,6 +621,8 @@ fn abbrev_forward_step(input: &str) -> IResult<&str, (Axis, NodeTest)> {
|
||||||
(
|
(
|
||||||
if attr.is_some() {
|
if attr.is_some() {
|
||||||
Axis::Attribute
|
Axis::Attribute
|
||||||
|
} else if is_descendant {
|
||||||
|
Axis::DescendantOrSelf
|
||||||
} else {
|
} else {
|
||||||
Axis::Child
|
Axis::Child
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue