Add functional pseudo-element support to selectors crate. r?heycam draft
authorXidorn Quan <me@upsuper.org>
Mon, 10 Jul 2017 11:34:32 +1000
changeset 606464 6791b53f12b3003ebe503e2e33f1e1c24dfb6624
parent 606463 4e8681fbf177c61a0fecca671b42fb4ac9920eb0
child 606465 8a621554e75b0615f741c22c1b937a8c4a94a361
push id67706
push userxquan@mozilla.com
push dateMon, 10 Jul 2017 23:54:14 +0000
reviewersheycam
milestone56.0a1
Add functional pseudo-element support to selectors crate. r?heycam MozReview-Commit-ID: 5WTIIlkbeg3
servo/components/selectors/parser.rs
--- a/servo/components/selectors/parser.rs
+++ b/servo/components/selectors/parser.rs
@@ -148,16 +148,23 @@ pub trait Parser<'i> {
     }
 
     fn parse_pseudo_element(&self, name: CompactCowStr<'i>)
                             -> Result<<Self::Impl as SelectorImpl>::PseudoElement,
                                       ParseError<'i, SelectorParseError<'i, Self::Error>>> {
         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> {
         None
     }
 
     fn namespace_for_prefix(&self, _prefix: &<Self::Impl as SelectorImpl>::NamespacePrefix)
                             -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
         None
     }
@@ -1465,16 +1472,27 @@ fn parse_nth_pseudo_class<'i, 't, Impl, 
                                               -> Result<Component<Impl>,
                                                         ParseError<'i, SelectorParseError<'i, E>>>
 where Impl: SelectorImpl, F: FnOnce(i32, i32) -> Component<Impl> {
     let (a, b) = parse_nth(input)?;
     Ok(selector(a, b))
 }
 
 
+/// 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
 /// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed.
 /// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
 fn parse_one_simple_selector<'i, 't, P, E, Impl>(parser: &P,
                                                  input: &mut CssParser<'i, 't>,
                                                  inside_negation: bool)
@@ -1497,47 +1515,44 @@ fn parse_one_simple_selector<'i, 't, P, 
                 t => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
             }
         }
         Ok(Token::SquareBracketBlock) => {
             let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?;
             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_class = if !is_functional {
+                    parse_simple_pseudo_class(parser, name)?
+                } else {
+                    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))),
+                    })?
+                };
+                Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo_class)))
+            } else {
+                let pseudo_element = if !is_functional {
+                    P::parse_pseudo_element(parser, name)?
+                } else {
+                    input.parse_nested_block(|input| {
+                        P::parse_functional_pseudo_element(parser, name, input)
+                    })?
+                };
+                Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element)))
             }
         }
         _ => {
             input.reset(start_position);
             Ok(None)
         }
     }
 }