Add functional pseudo-element support to selectors crate.

This commit is contained in:
Xidorn Quan 2017-07-12 15:07:24 +10:00
parent d138fd1a32
commit 706d54401e

View file

@ -153,6 +153,13 @@ pub trait Parser<'i> {
Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name))) Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name)))
} }
fn parse_functional_pseudo_element<'t>
(&self, name: CompactCowStr<'i>, _arguments: &mut CssParser<'i, 't>)
-> Result<<Self::Impl as SelectorImpl>::PseudoElement,
ParseError<'i, SelectorParseError<'i, Self::Error>>> {
Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name)))
}
fn default_namespace(&self) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> { fn default_namespace(&self) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
None None
} }
@ -1471,6 +1478,17 @@ where Impl: SelectorImpl, F: FnOnce(i32, i32) -> Component<Impl> {
} }
/// Returns whether the name corresponds to a CSS2 pseudo-element that
/// can be specified with the single colon syntax (in addition to the
/// double-colon syntax, which can be used for all pseudo-elements).
fn is_css2_pseudo_element<'i>(name: &CompactCowStr<'i>) -> bool {
// ** Do not add to this list! **
return name.eq_ignore_ascii_case("before") ||
name.eq_ignore_ascii_case("after") ||
name.eq_ignore_ascii_case("first-line") ||
name.eq_ignore_ascii_case("first-letter");
}
/// Parse a simple selector other than a type selector. /// Parse a simple selector other than a type selector.
/// ///
/// * `Err(())`: Invalid selector, abort /// * `Err(())`: Invalid selector, abort
@ -1503,37 +1521,34 @@ fn parse_one_simple_selector<'i, 't, P, E, Impl>(parser: &P,
Ok(Some(SimpleSelectorParseResult::SimpleSelector(attr))) Ok(Some(SimpleSelectorParseResult::SimpleSelector(attr)))
} }
Ok(Token::Colon) => { Ok(Token::Colon) => {
match input.next_including_whitespace()? { let (is_single_colon, next_token) = match input.next_including_whitespace()? {
Token::Ident(name) => { Token::Colon => (false, input.next_including_whitespace()?),
// Supported CSS 2.1 pseudo-elements only. t => (true, t),
// ** Do not add to this list! ** };
if name.eq_ignore_ascii_case("before") || let (name, is_functional) = match next_token {
name.eq_ignore_ascii_case("after") || Token::Ident(name) => (name, false),
name.eq_ignore_ascii_case("first-line") || Token::Function(name) => (name, true),
name.eq_ignore_ascii_case("first-letter") { t => return Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
let pseudo_element = P::parse_pseudo_element(parser, name)?; };
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element))) let is_pseudo_element = !is_single_colon || is_css2_pseudo_element(&name);
} else { if is_pseudo_element {
let pseudo_class = parse_simple_pseudo_class(parser, name)?; let pseudo_element = if is_functional {
Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo_class))) input.parse_nested_block(|input| {
} P::parse_functional_pseudo_element(parser, name, input)
} })?
Token::Function(name) => { } else {
let pseudo = input.parse_nested_block(|input| { P::parse_pseudo_element(parser, name)?
};
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element)))
} else {
let pseudo_class = if is_functional {
input.parse_nested_block(|input| {
parse_functional_pseudo_class(parser, input, name, inside_negation) parse_functional_pseudo_class(parser, input, name, inside_negation)
})?; })?
Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo))) } else {
} parse_simple_pseudo_class(parser, name)?
Token::Colon => { };
match input.next_including_whitespace()? { Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo_class)))
Token::Ident(name) => {
let pseudo = P::parse_pseudo_element(parser, name)?;
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo)))
}
t => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
}
}
t => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
} }
} }
_ => { _ => {