diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index b500fdfad50..422f7eb5c05 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -153,6 +153,13 @@ pub trait Parser<'i> { Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name))) } + fn parse_functional_pseudo_element<'t> + (&self, name: CompactCowStr<'i>, _arguments: &mut CssParser<'i, 't>) + -> Result<::PseudoElement, + ParseError<'i, SelectorParseError<'i, Self::Error>>> { + Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name))) + } + fn default_namespace(&self) -> Option<::NamespaceUrl> { None } @@ -1471,6 +1478,17 @@ where Impl: SelectorImpl, F: FnOnce(i32, i32) -> Component { } +/// 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. /// /// * `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(Token::Colon) => { - match input.next_including_whitespace()? { - Token::Ident(name) => { - // Supported CSS 2.1 pseudo-elements only. - // ** Do not add to this list! ** - if 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") { - let pseudo_element = P::parse_pseudo_element(parser, name)?; - Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element))) - } else { - let pseudo_class = parse_simple_pseudo_class(parser, name)?; - Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo_class))) - } - } - Token::Function(name) => { - let pseudo = input.parse_nested_block(|input| { + let (is_single_colon, next_token) = match input.next_including_whitespace()? { + Token::Colon => (false, input.next_including_whitespace()?), + t => (true, t), + }; + let (name, is_functional) = match next_token { + Token::Ident(name) => (name, false), + Token::Function(name) => (name, true), + t => return Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))), + }; + let is_pseudo_element = !is_single_colon || is_css2_pseudo_element(&name); + if is_pseudo_element { + let pseudo_element = if is_functional { + input.parse_nested_block(|input| { + P::parse_functional_pseudo_element(parser, name, input) + })? + } else { + 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) - })?; - Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo))) - } - Token::Colon => { - match input.next_including_whitespace()? { - 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))), + })? + } else { + parse_simple_pseudo_class(parser, name)? + }; + Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo_class))) } } _ => {