mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Fix serialization of namespace and universal selectors
Fix #16017 Fix #16020
This commit is contained in:
parent
ababebcced
commit
390e688058
4 changed files with 310 additions and 86 deletions
|
@ -431,10 +431,17 @@ impl Combinator {
|
|||
#[derive(Eq, PartialEq, Clone)]
|
||||
pub enum Component<Impl: SelectorImpl> {
|
||||
Combinator(Combinator),
|
||||
|
||||
ExplicitAnyNamespace,
|
||||
ExplicitNoNamespace,
|
||||
DefaultNamespace(Impl::NamespaceUrl),
|
||||
Namespace(Impl::NamespacePrefix, Impl::NamespaceUrl),
|
||||
|
||||
ExplicitUniversalType,
|
||||
LocalName(LocalName<Impl>),
|
||||
|
||||
ID(Impl::Identifier),
|
||||
Class(Impl::ClassName),
|
||||
LocalName(LocalName<Impl>),
|
||||
Namespace(Namespace<Impl>),
|
||||
|
||||
// Attribute selectors
|
||||
AttrExists(AttrSelector<Impl>), // [foo]
|
||||
|
@ -489,8 +496,9 @@ impl<Impl: SelectorImpl> Component<Impl> {
|
|||
None
|
||||
}
|
||||
},
|
||||
Component::Namespace(ref namespace) => {
|
||||
Some(namespace.url.precomputed_hash())
|
||||
Component::DefaultNamespace(ref url) |
|
||||
Component::Namespace(_, ref url) => {
|
||||
Some(url.precomputed_hash())
|
||||
},
|
||||
Component::ID(ref id) => {
|
||||
Some(id.precomputed_hash())
|
||||
|
@ -646,7 +654,15 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
|
|||
display_to_css_identifier(s, dest)
|
||||
}
|
||||
LocalName(ref s) => s.to_css(dest),
|
||||
Namespace(ref ns) => ns.to_css(dest),
|
||||
ExplicitUniversalType => dest.write_char('*'),
|
||||
|
||||
DefaultNamespace(_) => Ok(()),
|
||||
ExplicitNoNamespace => dest.write_char('|'),
|
||||
ExplicitAnyNamespace => dest.write_str("*|"),
|
||||
Namespace(ref prefix, _) => {
|
||||
display_to_css_identifier(prefix, dest)?;
|
||||
dest.write_char('|')
|
||||
}
|
||||
|
||||
// Attribute selectors
|
||||
AttrExists(ref a) => {
|
||||
|
@ -673,7 +689,7 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
|
|||
// Pseudo-classes
|
||||
Negation(ref arg) => {
|
||||
dest.write_str(":not(")?;
|
||||
debug_assert!(arg.len() <= 1 || (arg.len() == 2 && matches!(arg[0], Component::Namespace(_))));
|
||||
debug_assert!(single_simple_selector(arg));
|
||||
for component in arg.iter() {
|
||||
component.to_css(dest)?;
|
||||
}
|
||||
|
@ -832,10 +848,12 @@ fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>)
|
|||
where Impl: SelectorImpl {
|
||||
match *simple_selector {
|
||||
Component::Combinator(..) => unreachable!(),
|
||||
Component::LocalName(..) =>
|
||||
specificity.element_selectors += 1,
|
||||
Component::ID(..) =>
|
||||
specificity.id_selectors += 1,
|
||||
Component::LocalName(..) => {
|
||||
specificity.element_selectors += 1
|
||||
}
|
||||
Component::ID(..) => {
|
||||
specificity.id_selectors += 1
|
||||
}
|
||||
Component::Class(..) |
|
||||
Component::AttrExists(..) |
|
||||
Component::AttrEqual(..) |
|
||||
|
@ -859,10 +877,16 @@ fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>)
|
|||
Component::NthLastOfType(..) |
|
||||
Component::FirstOfType | Component::LastOfType |
|
||||
Component::OnlyOfType |
|
||||
Component::NonTSPseudoClass(..) =>
|
||||
specificity.class_like_selectors += 1,
|
||||
|
||||
Component::Namespace(..) => (),
|
||||
Component::NonTSPseudoClass(..) => {
|
||||
specificity.class_like_selectors += 1
|
||||
}
|
||||
Component::ExplicitUniversalType |
|
||||
Component::ExplicitAnyNamespace |
|
||||
Component::ExplicitNoNamespace |
|
||||
Component::DefaultNamespace(..) |
|
||||
Component::Namespace(..) => {
|
||||
// Does not affect specificity
|
||||
}
|
||||
Component::Negation(ref negated) => {
|
||||
for ss in negated.iter() {
|
||||
simple_selector_specificity(&ss, specificity);
|
||||
|
@ -990,10 +1014,22 @@ fn parse_type_selector<P, Impl>(parser: &P, input: &mut CssParser, sequence: &mu
|
|||
None => Ok(false),
|
||||
Some((namespace, local_name)) => {
|
||||
match namespace {
|
||||
NamespaceConstraint::Specific(ns) => {
|
||||
sequence.push(Component::Namespace(ns))
|
||||
},
|
||||
NamespaceConstraint::Any => (),
|
||||
QNamePrefix::ImplicitAnyNamespace => {}
|
||||
QNamePrefix::ImplicitDefaultNamespace(url) => {
|
||||
sequence.push(Component::DefaultNamespace(url))
|
||||
}
|
||||
QNamePrefix::ExplicitNamespace(prefix, url) => {
|
||||
sequence.push(Component::Namespace(prefix, url))
|
||||
}
|
||||
QNamePrefix::ExplicitNoNamespace => {
|
||||
sequence.push(Component::ExplicitNoNamespace)
|
||||
}
|
||||
QNamePrefix::ExplicitAnyNamespace => {
|
||||
sequence.push(Component::ExplicitAnyNamespace)
|
||||
}
|
||||
QNamePrefix::ImplicitNoNamespace => {
|
||||
unreachable!() // Not returned with in_attr_selector = false
|
||||
}
|
||||
}
|
||||
match local_name {
|
||||
Some(name) => {
|
||||
|
@ -1002,7 +1038,9 @@ fn parse_type_selector<P, Impl>(parser: &P, input: &mut CssParser, sequence: &mu
|
|||
name: from_cow_str(name),
|
||||
}))
|
||||
}
|
||||
None => (),
|
||||
None => {
|
||||
sequence.push(Component::ExplicitUniversalType)
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
@ -1015,22 +1053,29 @@ enum SimpleSelectorParseResult<Impl: SelectorImpl> {
|
|||
PseudoElement(Impl::PseudoElementSelector),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum QNamePrefix<Impl: SelectorImpl> {
|
||||
ImplicitNoNamespace, // `foo` in attr selectors
|
||||
ImplicitAnyNamespace, // `foo` in type selectors, without a default ns
|
||||
ImplicitDefaultNamespace(Impl::NamespaceUrl), // `foo` in type selectors, with a default ns
|
||||
ExplicitNoNamespace, // `|foo`
|
||||
ExplicitAnyNamespace, // `*|foo`
|
||||
ExplicitNamespace(Impl::NamespacePrefix, Impl::NamespaceUrl), // `prefix|foo`
|
||||
}
|
||||
|
||||
/// * `Err(())`: Invalid selector, abort
|
||||
/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed.
|
||||
/// * `Ok(Some((namespace, local_name)))`: `None` for the local name means a `*` universal selector
|
||||
fn parse_qualified_name<'i, 't, P, Impl>
|
||||
(parser: &P, input: &mut CssParser<'i, 't>,
|
||||
in_attr_selector: bool)
|
||||
-> Result<Option<(NamespaceConstraint<Impl>, Option<Cow<'i, str>>)>, ()>
|
||||
-> Result<Option<(QNamePrefix<Impl>, Option<Cow<'i, str>>)>, ()>
|
||||
where P: Parser<Impl=Impl>, Impl: SelectorImpl
|
||||
{
|
||||
let default_namespace = |local_name| {
|
||||
let namespace = match parser.default_namespace() {
|
||||
Some(url) => NamespaceConstraint::Specific(Namespace {
|
||||
prefix: None,
|
||||
url: url
|
||||
}),
|
||||
None => NamespaceConstraint::Any,
|
||||
Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),
|
||||
None => QNamePrefix::ImplicitAnyNamespace,
|
||||
};
|
||||
Ok(Some((namespace, local_name)))
|
||||
};
|
||||
|
@ -1056,15 +1101,12 @@ fn parse_qualified_name<'i, 't, P, Impl>
|
|||
let prefix = from_cow_str(value);
|
||||
let result = parser.namespace_for_prefix(&prefix);
|
||||
let url = result.ok_or(())?;
|
||||
explicit_namespace(input, NamespaceConstraint::Specific(Namespace {
|
||||
prefix: Some(prefix),
|
||||
url: url
|
||||
}))
|
||||
explicit_namespace(input, QNamePrefix::ExplicitNamespace(prefix, url))
|
||||
},
|
||||
_ => {
|
||||
input.reset(position);
|
||||
if in_attr_selector {
|
||||
Ok(Some((NamespaceConstraint::Specific(Default::default()), Some(value))))
|
||||
Ok(Some((QNamePrefix::ImplicitNoNamespace, Some(value))))
|
||||
} else {
|
||||
default_namespace(Some(value))
|
||||
}
|
||||
|
@ -1074,7 +1116,9 @@ fn parse_qualified_name<'i, 't, P, Impl>
|
|||
Ok(Token::Delim('*')) => {
|
||||
let position = input.position();
|
||||
match input.next_including_whitespace() {
|
||||
Ok(Token::Delim('|')) => explicit_namespace(input, NamespaceConstraint::Any),
|
||||
Ok(Token::Delim('|')) => {
|
||||
explicit_namespace(input, QNamePrefix::ExplicitAnyNamespace)
|
||||
}
|
||||
_ => {
|
||||
input.reset(position);
|
||||
if in_attr_selector {
|
||||
|
@ -1086,7 +1130,7 @@ fn parse_qualified_name<'i, 't, P, Impl>
|
|||
}
|
||||
},
|
||||
Ok(Token::Delim('|')) => {
|
||||
explicit_namespace(input, NamespaceConstraint::Specific(Default::default()))
|
||||
explicit_namespace(input, QNamePrefix::ExplicitNoNamespace)
|
||||
}
|
||||
_ => {
|
||||
input.reset(position);
|
||||
|
@ -1104,7 +1148,28 @@ fn parse_attribute_selector<P, Impl>(parser: &P, input: &mut CssParser)
|
|||
None => return Err(()),
|
||||
Some((_, None)) => unreachable!(),
|
||||
Some((namespace, Some(local_name))) => AttrSelector {
|
||||
namespace: namespace,
|
||||
namespace: match namespace {
|
||||
QNamePrefix::ImplicitNoNamespace |
|
||||
QNamePrefix::ExplicitNoNamespace => {
|
||||
NamespaceConstraint::Specific(Namespace {
|
||||
prefix: None,
|
||||
url: Impl::NamespaceUrl::default(),
|
||||
})
|
||||
}
|
||||
QNamePrefix::ExplicitNamespace(prefix, url) => {
|
||||
NamespaceConstraint::Specific(Namespace {
|
||||
prefix: Some(prefix),
|
||||
url: url,
|
||||
})
|
||||
}
|
||||
QNamePrefix::ExplicitAnyNamespace => {
|
||||
NamespaceConstraint::Any
|
||||
}
|
||||
QNamePrefix::ImplicitAnyNamespace |
|
||||
QNamePrefix::ImplicitDefaultNamespace(_) => {
|
||||
unreachable!() // Not returned with in_attr_selector = true
|
||||
}
|
||||
},
|
||||
lower_name: from_ascii_lowercase(&local_name),
|
||||
name: from_cow_str(local_name),
|
||||
},
|
||||
|
@ -1187,17 +1252,33 @@ fn parse_negation<P, Impl>(parser: &P,
|
|||
let mut v = ParseVec::new();
|
||||
parse_compound_selector(parser, input, &mut v, /* inside_negation = */ true)?;
|
||||
|
||||
let allow = v.len() <= 1 ||
|
||||
(v.len() == 2 && matches!(v[0], Component::Namespace(_)) &&
|
||||
matches!(v[1], Component::LocalName(_)));
|
||||
|
||||
if allow {
|
||||
if single_simple_selector(&v) {
|
||||
Ok(Component::Negation(v.into_vec().into_boxed_slice()))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
// A single type selector can be represented as two components
|
||||
fn single_simple_selector<Impl: SelectorImpl>(v: &[Component<Impl>]) -> bool {
|
||||
v.len() == 1 || (
|
||||
v.len() == 2 &&
|
||||
match v[1] {
|
||||
Component::LocalName(_) | Component::ExplicitUniversalType => {
|
||||
debug_assert!(matches!(v[0],
|
||||
Component::ExplicitAnyNamespace |
|
||||
Component::ExplicitNoNamespace |
|
||||
Component::DefaultNamespace(_) |
|
||||
Component::Namespace(..)
|
||||
));
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/// simple_selector_sequence
|
||||
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
|
||||
/// | [ HASH | class | attrib | pseudo | negation ]+
|
||||
|
@ -1228,10 +1309,7 @@ fn parse_compound_selector<P, Impl>(
|
|||
//
|
||||
// Note that this doesn't apply to :not() and :matches() per spec.
|
||||
if !inside_negation {
|
||||
sequence.push(Component::Namespace(Namespace {
|
||||
prefix: None,
|
||||
url: url
|
||||
}));
|
||||
sequence.push(Component::DefaultNamespace(url))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1554,13 +1632,61 @@ pub mod tests {
|
|||
assert_eq!(parse(":lang(4)"), Err(())) ;
|
||||
assert_eq!(parse(":lang(en US)"), Err(())) ;
|
||||
assert_eq!(parse("EeÉ"), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(Component::LocalName(LocalName {
|
||||
inner: SelectorInner::from_vec(vec!(
|
||||
Component::LocalName(LocalName {
|
||||
name: DummyAtom::from("EeÉ"),
|
||||
lower_name: DummyAtom::from("eeÉ") })),
|
||||
),
|
||||
lower_name: DummyAtom::from("eeÉ")
|
||||
}),
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 1),
|
||||
}))));
|
||||
assert_eq!(parse("|e"), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(
|
||||
Component::ExplicitNoNamespace,
|
||||
Component::LocalName(LocalName {
|
||||
name: DummyAtom::from("e"),
|
||||
lower_name: DummyAtom::from("e")
|
||||
}),
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 1),
|
||||
}))));
|
||||
// https://github.com/servo/servo/issues/16020
|
||||
assert_eq!(parse("*|e"), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(
|
||||
Component::ExplicitAnyNamespace,
|
||||
Component::LocalName(LocalName {
|
||||
name: DummyAtom::from("e"),
|
||||
lower_name: DummyAtom::from("e")
|
||||
}),
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 1),
|
||||
}))));
|
||||
assert_eq!(parse("*"), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(
|
||||
Component::ExplicitUniversalType,
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}))));
|
||||
assert_eq!(parse("|*"), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(
|
||||
Component::ExplicitNoNamespace,
|
||||
Component::ExplicitUniversalType,
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}))));
|
||||
assert_eq!(parse("*|*"), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(
|
||||
Component::ExplicitAnyNamespace,
|
||||
Component::ExplicitUniversalType,
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}))));
|
||||
assert_eq!(parse(".foo:lang(en-US)"), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec![
|
||||
Component::Class(DummyAtom::from("foo")),
|
||||
|
@ -1616,10 +1742,7 @@ pub mod tests {
|
|||
assert_eq!(parse_ns("svg|circle", &parser), Ok(SelectorList(vec![Selector {
|
||||
inner: SelectorInner::from_vec(
|
||||
vec![
|
||||
Component::Namespace(Namespace {
|
||||
prefix: Some(DummyAtom("svg".into())),
|
||||
url: SVG.into(),
|
||||
}),
|
||||
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
|
||||
Component::LocalName(LocalName {
|
||||
name: DummyAtom::from("circle"),
|
||||
lower_name: DummyAtom::from("circle"),
|
||||
|
@ -1628,6 +1751,15 @@ pub mod tests {
|
|||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 1),
|
||||
}])));
|
||||
assert_eq!(parse_ns("svg|*", &parser), Ok(SelectorList(vec![Selector {
|
||||
inner: SelectorInner::from_vec(
|
||||
vec![
|
||||
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
|
||||
Component::ExplicitUniversalType,
|
||||
]),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}])));
|
||||
// Default namespace does not apply to attribute selectors
|
||||
// https://github.com/mozilla/servo/pull/1652
|
||||
// but it does apply to implicit type selectors
|
||||
|
@ -1636,10 +1768,7 @@ pub mod tests {
|
|||
assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(
|
||||
vec![
|
||||
Component::Namespace(Namespace {
|
||||
prefix: None,
|
||||
url: MATHML.into(),
|
||||
}),
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::AttrExists(AttrSelector {
|
||||
name: DummyAtom::from("Foo"),
|
||||
lower_name: DummyAtom::from("foo"),
|
||||
|
@ -1656,10 +1785,7 @@ pub mod tests {
|
|||
assert_eq!(parse_ns("e", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(
|
||||
vec!(
|
||||
Component::Namespace(Namespace {
|
||||
prefix: None,
|
||||
url: MATHML.into(),
|
||||
}),
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::LocalName(LocalName {
|
||||
name: DummyAtom::from("e"),
|
||||
lower_name: DummyAtom::from("e") }),
|
||||
|
@ -1667,6 +1793,61 @@ pub mod tests {
|
|||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 1),
|
||||
}))));
|
||||
assert_eq!(parse_ns("*", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(
|
||||
vec!(
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::ExplicitUniversalType,
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}))));
|
||||
assert_eq!(parse_ns("*|*", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(
|
||||
vec!(
|
||||
Component::ExplicitAnyNamespace,
|
||||
Component::ExplicitUniversalType,
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}))));
|
||||
// Default namespace applies to universal and type selectors inside :not and :matches,
|
||||
// but not otherwise.
|
||||
assert_eq!(parse_ns(":not(.cl)", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::Negation(vec![
|
||||
Component::Class(DummyAtom::from("cl"))
|
||||
].into_boxed_slice()),
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 1, 0),
|
||||
}))));
|
||||
assert_eq!(parse_ns(":not(*)", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::Negation(vec![
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::ExplicitUniversalType,
|
||||
].into_boxed_slice()),
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}))));
|
||||
assert_eq!(parse_ns(":not(e)", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::Negation(vec![
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::LocalName(LocalName {
|
||||
name: DummyAtom::from("e"),
|
||||
lower_name: DummyAtom::from("e")
|
||||
}),
|
||||
].into_boxed_slice())
|
||||
)),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 1),
|
||||
}))));
|
||||
assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector {
|
||||
inner: SelectorInner::from_vec(
|
||||
vec![
|
||||
|
@ -1727,10 +1908,7 @@ pub mod tests {
|
|||
assert_eq!(parse_ns(":not(svg|circle)", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(Component::Negation(
|
||||
vec![
|
||||
Component::Namespace(Namespace {
|
||||
prefix: Some(DummyAtom("svg".into())),
|
||||
url: SVG.into(),
|
||||
}),
|
||||
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
|
||||
Component::LocalName(LocalName {
|
||||
name: DummyAtom::from("circle"),
|
||||
lower_name: DummyAtom::from("circle")
|
||||
|
@ -1740,6 +1918,46 @@ pub mod tests {
|
|||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 1),
|
||||
}))));
|
||||
// https://github.com/servo/servo/issues/16017
|
||||
assert_eq!(parse_ns(":not(*)", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(Component::Negation(
|
||||
vec![
|
||||
Component::ExplicitUniversalType,
|
||||
].into_boxed_slice()
|
||||
))),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}))));
|
||||
assert_eq!(parse_ns(":not(|*)", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(Component::Negation(
|
||||
vec![
|
||||
Component::ExplicitNoNamespace,
|
||||
Component::ExplicitUniversalType,
|
||||
].into_boxed_slice()
|
||||
))),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}))));
|
||||
assert_eq!(parse_ns(":not(*|*)", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(Component::Negation(
|
||||
vec![
|
||||
Component::ExplicitAnyNamespace,
|
||||
Component::ExplicitUniversalType,
|
||||
].into_boxed_slice()
|
||||
))),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}))));
|
||||
assert_eq!(parse_ns(":not(svg|*)", &parser), Ok(SelectorList(vec!(Selector {
|
||||
inner: SelectorInner::from_vec(vec!(Component::Negation(
|
||||
vec![
|
||||
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
|
||||
Component::ExplicitUniversalType,
|
||||
].into_boxed_slice()
|
||||
))),
|
||||
pseudo_element: None,
|
||||
specificity: specificity(0, 0, 0),
|
||||
}))));
|
||||
}
|
||||
|
||||
struct TestVisitor {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue