mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +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
|
@ -3,7 +3,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
use bloom::BloomFilter;
|
use bloom::BloomFilter;
|
||||||
use parser::{CaseSensitivity, Combinator, ComplexSelector, Component, LocalName};
|
use parser::{CaseSensitivity, Combinator, ComplexSelector, Component, LocalName};
|
||||||
use parser::{Selector, SelectorInner, SelectorIter};
|
use parser::{Selector, SelectorInner, SelectorIter, SelectorImpl};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use tree::Element;
|
use tree::Element;
|
||||||
|
|
||||||
|
@ -299,8 +299,18 @@ fn matches_simple_selector<E, F>(
|
||||||
let name = if element.is_html_element_in_html_document() { lower_name } else { name };
|
let name = if element.is_html_element_in_html_document() { lower_name } else { name };
|
||||||
element.get_local_name() == name.borrow()
|
element.get_local_name() == name.borrow()
|
||||||
}
|
}
|
||||||
Component::Namespace(ref namespace) => {
|
Component::ExplicitUniversalType |
|
||||||
element.get_namespace() == namespace.url.borrow()
|
Component::ExplicitAnyNamespace => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Component::Namespace(_, ref url) |
|
||||||
|
Component::DefaultNamespace(ref url) => {
|
||||||
|
element.get_namespace() == url.borrow()
|
||||||
|
}
|
||||||
|
Component::ExplicitNoNamespace => {
|
||||||
|
// Rust type’s default, not default namespace
|
||||||
|
let empty_string = <E::Impl as SelectorImpl>::NamespaceUrl::default();
|
||||||
|
element.get_namespace() == empty_string.borrow()
|
||||||
}
|
}
|
||||||
// TODO: case-sensitivity depends on the document type and quirks mode
|
// TODO: case-sensitivity depends on the document type and quirks mode
|
||||||
Component::ID(ref id) => {
|
Component::ID(ref id) => {
|
||||||
|
|
|
@ -431,10 +431,17 @@ impl Combinator {
|
||||||
#[derive(Eq, PartialEq, Clone)]
|
#[derive(Eq, PartialEq, Clone)]
|
||||||
pub enum Component<Impl: SelectorImpl> {
|
pub enum Component<Impl: SelectorImpl> {
|
||||||
Combinator(Combinator),
|
Combinator(Combinator),
|
||||||
|
|
||||||
|
ExplicitAnyNamespace,
|
||||||
|
ExplicitNoNamespace,
|
||||||
|
DefaultNamespace(Impl::NamespaceUrl),
|
||||||
|
Namespace(Impl::NamespacePrefix, Impl::NamespaceUrl),
|
||||||
|
|
||||||
|
ExplicitUniversalType,
|
||||||
|
LocalName(LocalName<Impl>),
|
||||||
|
|
||||||
ID(Impl::Identifier),
|
ID(Impl::Identifier),
|
||||||
Class(Impl::ClassName),
|
Class(Impl::ClassName),
|
||||||
LocalName(LocalName<Impl>),
|
|
||||||
Namespace(Namespace<Impl>),
|
|
||||||
|
|
||||||
// Attribute selectors
|
// Attribute selectors
|
||||||
AttrExists(AttrSelector<Impl>), // [foo]
|
AttrExists(AttrSelector<Impl>), // [foo]
|
||||||
|
@ -489,8 +496,9 @@ impl<Impl: SelectorImpl> Component<Impl> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Component::Namespace(ref namespace) => {
|
Component::DefaultNamespace(ref url) |
|
||||||
Some(namespace.url.precomputed_hash())
|
Component::Namespace(_, ref url) => {
|
||||||
|
Some(url.precomputed_hash())
|
||||||
},
|
},
|
||||||
Component::ID(ref id) => {
|
Component::ID(ref id) => {
|
||||||
Some(id.precomputed_hash())
|
Some(id.precomputed_hash())
|
||||||
|
@ -646,7 +654,15 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
|
||||||
display_to_css_identifier(s, dest)
|
display_to_css_identifier(s, dest)
|
||||||
}
|
}
|
||||||
LocalName(ref s) => s.to_css(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
|
// Attribute selectors
|
||||||
AttrExists(ref a) => {
|
AttrExists(ref a) => {
|
||||||
|
@ -673,7 +689,7 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
|
||||||
// Pseudo-classes
|
// Pseudo-classes
|
||||||
Negation(ref arg) => {
|
Negation(ref arg) => {
|
||||||
dest.write_str(":not(")?;
|
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() {
|
for component in arg.iter() {
|
||||||
component.to_css(dest)?;
|
component.to_css(dest)?;
|
||||||
}
|
}
|
||||||
|
@ -832,10 +848,12 @@ fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>)
|
||||||
where Impl: SelectorImpl {
|
where Impl: SelectorImpl {
|
||||||
match *simple_selector {
|
match *simple_selector {
|
||||||
Component::Combinator(..) => unreachable!(),
|
Component::Combinator(..) => unreachable!(),
|
||||||
Component::LocalName(..) =>
|
Component::LocalName(..) => {
|
||||||
specificity.element_selectors += 1,
|
specificity.element_selectors += 1
|
||||||
Component::ID(..) =>
|
}
|
||||||
specificity.id_selectors += 1,
|
Component::ID(..) => {
|
||||||
|
specificity.id_selectors += 1
|
||||||
|
}
|
||||||
Component::Class(..) |
|
Component::Class(..) |
|
||||||
Component::AttrExists(..) |
|
Component::AttrExists(..) |
|
||||||
Component::AttrEqual(..) |
|
Component::AttrEqual(..) |
|
||||||
|
@ -859,10 +877,16 @@ fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>)
|
||||||
Component::NthLastOfType(..) |
|
Component::NthLastOfType(..) |
|
||||||
Component::FirstOfType | Component::LastOfType |
|
Component::FirstOfType | Component::LastOfType |
|
||||||
Component::OnlyOfType |
|
Component::OnlyOfType |
|
||||||
Component::NonTSPseudoClass(..) =>
|
Component::NonTSPseudoClass(..) => {
|
||||||
specificity.class_like_selectors += 1,
|
specificity.class_like_selectors += 1
|
||||||
|
}
|
||||||
Component::Namespace(..) => (),
|
Component::ExplicitUniversalType |
|
||||||
|
Component::ExplicitAnyNamespace |
|
||||||
|
Component::ExplicitNoNamespace |
|
||||||
|
Component::DefaultNamespace(..) |
|
||||||
|
Component::Namespace(..) => {
|
||||||
|
// Does not affect specificity
|
||||||
|
}
|
||||||
Component::Negation(ref negated) => {
|
Component::Negation(ref negated) => {
|
||||||
for ss in negated.iter() {
|
for ss in negated.iter() {
|
||||||
simple_selector_specificity(&ss, specificity);
|
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),
|
None => Ok(false),
|
||||||
Some((namespace, local_name)) => {
|
Some((namespace, local_name)) => {
|
||||||
match namespace {
|
match namespace {
|
||||||
NamespaceConstraint::Specific(ns) => {
|
QNamePrefix::ImplicitAnyNamespace => {}
|
||||||
sequence.push(Component::Namespace(ns))
|
QNamePrefix::ImplicitDefaultNamespace(url) => {
|
||||||
},
|
sequence.push(Component::DefaultNamespace(url))
|
||||||
NamespaceConstraint::Any => (),
|
}
|
||||||
|
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 {
|
match local_name {
|
||||||
Some(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),
|
name: from_cow_str(name),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
None => (),
|
None => {
|
||||||
|
sequence.push(Component::ExplicitUniversalType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
@ -1015,22 +1053,29 @@ enum SimpleSelectorParseResult<Impl: SelectorImpl> {
|
||||||
PseudoElement(Impl::PseudoElementSelector),
|
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
|
/// * `Err(())`: Invalid selector, abort
|
||||||
/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed.
|
/// * `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
|
/// * `Ok(Some((namespace, local_name)))`: `None` for the local name means a `*` universal selector
|
||||||
fn parse_qualified_name<'i, 't, P, Impl>
|
fn parse_qualified_name<'i, 't, P, Impl>
|
||||||
(parser: &P, input: &mut CssParser<'i, 't>,
|
(parser: &P, input: &mut CssParser<'i, 't>,
|
||||||
in_attr_selector: bool)
|
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
|
where P: Parser<Impl=Impl>, Impl: SelectorImpl
|
||||||
{
|
{
|
||||||
let default_namespace = |local_name| {
|
let default_namespace = |local_name| {
|
||||||
let namespace = match parser.default_namespace() {
|
let namespace = match parser.default_namespace() {
|
||||||
Some(url) => NamespaceConstraint::Specific(Namespace {
|
Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),
|
||||||
prefix: None,
|
None => QNamePrefix::ImplicitAnyNamespace,
|
||||||
url: url
|
|
||||||
}),
|
|
||||||
None => NamespaceConstraint::Any,
|
|
||||||
};
|
};
|
||||||
Ok(Some((namespace, local_name)))
|
Ok(Some((namespace, local_name)))
|
||||||
};
|
};
|
||||||
|
@ -1056,15 +1101,12 @@ fn parse_qualified_name<'i, 't, P, Impl>
|
||||||
let prefix = from_cow_str(value);
|
let prefix = from_cow_str(value);
|
||||||
let result = parser.namespace_for_prefix(&prefix);
|
let result = parser.namespace_for_prefix(&prefix);
|
||||||
let url = result.ok_or(())?;
|
let url = result.ok_or(())?;
|
||||||
explicit_namespace(input, NamespaceConstraint::Specific(Namespace {
|
explicit_namespace(input, QNamePrefix::ExplicitNamespace(prefix, url))
|
||||||
prefix: Some(prefix),
|
|
||||||
url: url
|
|
||||||
}))
|
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
input.reset(position);
|
input.reset(position);
|
||||||
if in_attr_selector {
|
if in_attr_selector {
|
||||||
Ok(Some((NamespaceConstraint::Specific(Default::default()), Some(value))))
|
Ok(Some((QNamePrefix::ImplicitNoNamespace, Some(value))))
|
||||||
} else {
|
} else {
|
||||||
default_namespace(Some(value))
|
default_namespace(Some(value))
|
||||||
}
|
}
|
||||||
|
@ -1074,7 +1116,9 @@ fn parse_qualified_name<'i, 't, P, Impl>
|
||||||
Ok(Token::Delim('*')) => {
|
Ok(Token::Delim('*')) => {
|
||||||
let position = input.position();
|
let position = input.position();
|
||||||
match input.next_including_whitespace() {
|
match input.next_including_whitespace() {
|
||||||
Ok(Token::Delim('|')) => explicit_namespace(input, NamespaceConstraint::Any),
|
Ok(Token::Delim('|')) => {
|
||||||
|
explicit_namespace(input, QNamePrefix::ExplicitAnyNamespace)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
input.reset(position);
|
input.reset(position);
|
||||||
if in_attr_selector {
|
if in_attr_selector {
|
||||||
|
@ -1086,7 +1130,7 @@ fn parse_qualified_name<'i, 't, P, Impl>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Ok(Token::Delim('|')) => {
|
Ok(Token::Delim('|')) => {
|
||||||
explicit_namespace(input, NamespaceConstraint::Specific(Default::default()))
|
explicit_namespace(input, QNamePrefix::ExplicitNoNamespace)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
input.reset(position);
|
input.reset(position);
|
||||||
|
@ -1104,7 +1148,28 @@ fn parse_attribute_selector<P, Impl>(parser: &P, input: &mut CssParser)
|
||||||
None => return Err(()),
|
None => return Err(()),
|
||||||
Some((_, None)) => unreachable!(),
|
Some((_, None)) => unreachable!(),
|
||||||
Some((namespace, Some(local_name))) => AttrSelector {
|
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),
|
lower_name: from_ascii_lowercase(&local_name),
|
||||||
name: from_cow_str(local_name),
|
name: from_cow_str(local_name),
|
||||||
},
|
},
|
||||||
|
@ -1187,17 +1252,33 @@ fn parse_negation<P, Impl>(parser: &P,
|
||||||
let mut v = ParseVec::new();
|
let mut v = ParseVec::new();
|
||||||
parse_compound_selector(parser, input, &mut v, /* inside_negation = */ true)?;
|
parse_compound_selector(parser, input, &mut v, /* inside_negation = */ true)?;
|
||||||
|
|
||||||
let allow = v.len() <= 1 ||
|
if single_simple_selector(&v) {
|
||||||
(v.len() == 2 && matches!(v[0], Component::Namespace(_)) &&
|
|
||||||
matches!(v[1], Component::LocalName(_)));
|
|
||||||
|
|
||||||
if allow {
|
|
||||||
Ok(Component::Negation(v.into_vec().into_boxed_slice()))
|
Ok(Component::Negation(v.into_vec().into_boxed_slice()))
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
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
|
/// simple_selector_sequence
|
||||||
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
|
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
|
||||||
/// | [ 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.
|
// Note that this doesn't apply to :not() and :matches() per spec.
|
||||||
if !inside_negation {
|
if !inside_negation {
|
||||||
sequence.push(Component::Namespace(Namespace {
|
sequence.push(Component::DefaultNamespace(url))
|
||||||
prefix: None,
|
|
||||||
url: url
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1554,13 +1632,61 @@ pub mod tests {
|
||||||
assert_eq!(parse(":lang(4)"), Err(())) ;
|
assert_eq!(parse(":lang(4)"), Err(())) ;
|
||||||
assert_eq!(parse(":lang(en US)"), Err(())) ;
|
assert_eq!(parse(":lang(en US)"), Err(())) ;
|
||||||
assert_eq!(parse("EeÉ"), Ok(SelectorList(vec!(Selector {
|
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É"),
|
name: DummyAtom::from("EeÉ"),
|
||||||
lower_name: DummyAtom::from("eeÉ") })),
|
lower_name: DummyAtom::from("eeÉ")
|
||||||
),
|
}),
|
||||||
|
)),
|
||||||
pseudo_element: None,
|
pseudo_element: None,
|
||||||
specificity: specificity(0, 0, 1),
|
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 {
|
assert_eq!(parse(".foo:lang(en-US)"), Ok(SelectorList(vec!(Selector {
|
||||||
inner: SelectorInner::from_vec(vec![
|
inner: SelectorInner::from_vec(vec![
|
||||||
Component::Class(DummyAtom::from("foo")),
|
Component::Class(DummyAtom::from("foo")),
|
||||||
|
@ -1616,10 +1742,7 @@ pub mod tests {
|
||||||
assert_eq!(parse_ns("svg|circle", &parser), Ok(SelectorList(vec![Selector {
|
assert_eq!(parse_ns("svg|circle", &parser), Ok(SelectorList(vec![Selector {
|
||||||
inner: SelectorInner::from_vec(
|
inner: SelectorInner::from_vec(
|
||||||
vec![
|
vec![
|
||||||
Component::Namespace(Namespace {
|
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
|
||||||
prefix: Some(DummyAtom("svg".into())),
|
|
||||||
url: SVG.into(),
|
|
||||||
}),
|
|
||||||
Component::LocalName(LocalName {
|
Component::LocalName(LocalName {
|
||||||
name: DummyAtom::from("circle"),
|
name: DummyAtom::from("circle"),
|
||||||
lower_name: DummyAtom::from("circle"),
|
lower_name: DummyAtom::from("circle"),
|
||||||
|
@ -1628,6 +1751,15 @@ pub mod tests {
|
||||||
pseudo_element: None,
|
pseudo_element: None,
|
||||||
specificity: specificity(0, 0, 1),
|
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
|
// Default namespace does not apply to attribute selectors
|
||||||
// https://github.com/mozilla/servo/pull/1652
|
// https://github.com/mozilla/servo/pull/1652
|
||||||
// but it does apply to implicit type selectors
|
// 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 {
|
assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector {
|
||||||
inner: SelectorInner::from_vec(
|
inner: SelectorInner::from_vec(
|
||||||
vec![
|
vec![
|
||||||
Component::Namespace(Namespace {
|
Component::DefaultNamespace(MATHML.into()),
|
||||||
prefix: None,
|
|
||||||
url: MATHML.into(),
|
|
||||||
}),
|
|
||||||
Component::AttrExists(AttrSelector {
|
Component::AttrExists(AttrSelector {
|
||||||
name: DummyAtom::from("Foo"),
|
name: DummyAtom::from("Foo"),
|
||||||
lower_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 {
|
assert_eq!(parse_ns("e", &parser), Ok(SelectorList(vec!(Selector {
|
||||||
inner: SelectorInner::from_vec(
|
inner: SelectorInner::from_vec(
|
||||||
vec!(
|
vec!(
|
||||||
Component::Namespace(Namespace {
|
Component::DefaultNamespace(MATHML.into()),
|
||||||
prefix: None,
|
|
||||||
url: MATHML.into(),
|
|
||||||
}),
|
|
||||||
Component::LocalName(LocalName {
|
Component::LocalName(LocalName {
|
||||||
name: DummyAtom::from("e"),
|
name: DummyAtom::from("e"),
|
||||||
lower_name: DummyAtom::from("e") }),
|
lower_name: DummyAtom::from("e") }),
|
||||||
|
@ -1667,6 +1793,61 @@ pub mod tests {
|
||||||
pseudo_element: None,
|
pseudo_element: None,
|
||||||
specificity: specificity(0, 0, 1),
|
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 {
|
assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector {
|
||||||
inner: SelectorInner::from_vec(
|
inner: SelectorInner::from_vec(
|
||||||
vec![
|
vec![
|
||||||
|
@ -1727,10 +1908,7 @@ pub mod tests {
|
||||||
assert_eq!(parse_ns(":not(svg|circle)", &parser), Ok(SelectorList(vec!(Selector {
|
assert_eq!(parse_ns(":not(svg|circle)", &parser), Ok(SelectorList(vec!(Selector {
|
||||||
inner: SelectorInner::from_vec(vec!(Component::Negation(
|
inner: SelectorInner::from_vec(vec!(Component::Negation(
|
||||||
vec![
|
vec![
|
||||||
Component::Namespace(Namespace {
|
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
|
||||||
prefix: Some(DummyAtom("svg".into())),
|
|
||||||
url: SVG.into(),
|
|
||||||
}),
|
|
||||||
Component::LocalName(LocalName {
|
Component::LocalName(LocalName {
|
||||||
name: DummyAtom::from("circle"),
|
name: DummyAtom::from("circle"),
|
||||||
lower_name: DummyAtom::from("circle")
|
lower_name: DummyAtom::from("circle")
|
||||||
|
@ -1740,6 +1918,46 @@ pub mod tests {
|
||||||
pseudo_element: None,
|
pseudo_element: None,
|
||||||
specificity: specificity(0, 0, 1),
|
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 {
|
struct TestVisitor {
|
||||||
|
|
|
@ -1366,9 +1366,20 @@ impl SelectorMap<Rule> {
|
||||||
|
|
||||||
let mut rules_list = vec![];
|
let mut rules_list = vec![];
|
||||||
for rule in self.other.iter() {
|
for rule in self.other.iter() {
|
||||||
if rule.selector.inner.complex.iter_raw().next().is_none() {
|
let mut iter = rule.selector.inner.complex.iter_raw();
|
||||||
rules_list.push(rule.to_applicable_declaration_block(cascade_level));
|
match iter.next() {
|
||||||
|
None => {}
|
||||||
|
Some(&Component::ExplicitUniversalType) => match iter.next() {
|
||||||
|
None => {}
|
||||||
|
Some(&Component::ExplicitAnyNamespace) => match iter.next() {
|
||||||
|
None => {}
|
||||||
|
_ => continue
|
||||||
|
},
|
||||||
|
_ => continue
|
||||||
|
},
|
||||||
|
_ => continue
|
||||||
}
|
}
|
||||||
|
rules_list.push(rule.to_applicable_declaration_block(cascade_level))
|
||||||
}
|
}
|
||||||
|
|
||||||
sort_by_key(&mut rules_list,
|
sort_by_key(&mut rules_list,
|
||||||
|
|
|
@ -92,10 +92,7 @@ fn test_parse_stylesheet() {
|
||||||
selectors: SelectorList(vec![
|
selectors: SelectorList(vec![
|
||||||
Selector {
|
Selector {
|
||||||
inner: SelectorInner::from_vec(vec![
|
inner: SelectorInner::from_vec(vec![
|
||||||
Component::Namespace(Namespace {
|
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
||||||
prefix: None,
|
|
||||||
url: NsAtom::from("http://www.w3.org/1999/xhtml")
|
|
||||||
}),
|
|
||||||
Component::LocalName(LocalName {
|
Component::LocalName(LocalName {
|
||||||
name: local_name!("input"),
|
name: local_name!("input"),
|
||||||
lower_name: local_name!("input"),
|
lower_name: local_name!("input"),
|
||||||
|
@ -129,10 +126,7 @@ fn test_parse_stylesheet() {
|
||||||
selectors: SelectorList(vec![
|
selectors: SelectorList(vec![
|
||||||
Selector {
|
Selector {
|
||||||
inner: SelectorInner::from_vec(vec![
|
inner: SelectorInner::from_vec(vec![
|
||||||
Component::Namespace(Namespace {
|
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
||||||
prefix: None,
|
|
||||||
url: NsAtom::from("http://www.w3.org/1999/xhtml")
|
|
||||||
}),
|
|
||||||
Component::LocalName(LocalName {
|
Component::LocalName(LocalName {
|
||||||
name: local_name!("html"),
|
name: local_name!("html"),
|
||||||
lower_name: local_name!("html"),
|
lower_name: local_name!("html"),
|
||||||
|
@ -143,10 +137,7 @@ fn test_parse_stylesheet() {
|
||||||
},
|
},
|
||||||
Selector {
|
Selector {
|
||||||
inner: SelectorInner::from_vec(vec![
|
inner: SelectorInner::from_vec(vec![
|
||||||
Component::Namespace(Namespace {
|
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
||||||
prefix: None,
|
|
||||||
url: NsAtom::from("http://www.w3.org/1999/xhtml")
|
|
||||||
}),
|
|
||||||
Component::LocalName(LocalName {
|
Component::LocalName(LocalName {
|
||||||
name: local_name!("body"),
|
name: local_name!("body"),
|
||||||
lower_name: local_name!("body"),
|
lower_name: local_name!("body"),
|
||||||
|
@ -169,16 +160,10 @@ fn test_parse_stylesheet() {
|
||||||
selectors: SelectorList(vec![
|
selectors: SelectorList(vec![
|
||||||
Selector {
|
Selector {
|
||||||
inner: SelectorInner::from_vec(vec![
|
inner: SelectorInner::from_vec(vec![
|
||||||
Component::Namespace(Namespace {
|
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
||||||
prefix: None,
|
|
||||||
url: NsAtom::from("http://www.w3.org/1999/xhtml")
|
|
||||||
}),
|
|
||||||
Component::ID(Atom::from("d1")),
|
Component::ID(Atom::from("d1")),
|
||||||
Component::Combinator(Combinator::Child),
|
Component::Combinator(Combinator::Child),
|
||||||
Component::Namespace(Namespace {
|
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
|
||||||
prefix: None,
|
|
||||||
url: NsAtom::from("http://www.w3.org/1999/xhtml")
|
|
||||||
}),
|
|
||||||
Component::Class(Atom::from("ok")),
|
Component::Class(Atom::from("ok")),
|
||||||
]),
|
]),
|
||||||
pseudo_element: None,
|
pseudo_element: None,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue