Auto merge of #17687 - upsuper:tree-pseudo, r=heycam

Support parsing ::-moz-tree-* pseudo-elements selector

This is the Servo side change of [bug 1348488](https://bugzilla.mozilla.org/show_bug.cgi?id=1348488).

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17687)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-07-12 03:35:37 -07:00 committed by GitHub
commit ce52c16a8f
11 changed files with 765 additions and 788 deletions

View file

@ -17,6 +17,7 @@ pub enum PseudoClass {
pub enum PseudoElement {
A,
B,
Tree(Box<[String]>),
}
#[derive(Eq, PartialEq, Clone, Debug, Default)]

View file

@ -131,6 +131,12 @@ pub trait Parser<'i> {
type Impl: SelectorImpl;
type Error: 'i;
/// Whether the name is a pseudo-element that can be specified with
/// the single colon syntax in addition to the double-colon syntax.
fn is_pseudo_element_allows_single_colon(name: &CompactCowStr<'i>) -> bool {
is_css2_pseudo_element(name)
}
/// This function can return an "Err" pseudo-element in order to support CSS2.1
/// pseudo-elements.
fn parse_non_ts_pseudo_class(&self, name: CompactCowStr<'i>)
@ -153,6 +159,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<<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
}
@ -1471,6 +1484,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).
pub 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
@ -1490,13 +1514,12 @@ fn parse_one_simple_selector<'i, 't, P, E, Impl>(parser: &P,
Ok(Some(SimpleSelectorParseResult::SimpleSelector(id)))
}
Ok(Token::Delim('.')) => {
match input.next_including_whitespace() {
Ok(Token::Ident(class)) => {
match input.next_including_whitespace()? {
Token::Ident(class) => {
let class = Component::Class(from_cow_str(class.into()));
Ok(Some(SimpleSelectorParseResult::SimpleSelector(class)))
}
Ok(t) => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
Err(e) => Err(ParseError::Basic(e)),
t => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
}
}
Ok(Token::SquareBracketBlock) => {
@ -1504,39 +1527,35 @@ 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() {
Ok(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)))
}
}
Ok(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 ||
P::is_pseudo_element_allows_single_colon(&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)))
}
Ok(Token::Colon) => {
match input.next_including_whitespace() {
Ok(Token::Ident(name)) => {
let pseudo = P::parse_pseudo_element(parser, name)?;
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo)))
}
Ok(t) => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
Err(e) => Err(ParseError::Basic(e)),
}
}
Ok(t) => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
Err(e) => Err(ParseError::Basic(e)),
})?
} else {
parse_simple_pseudo_class(parser, name)?
};
Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo_class)))
}
}
_ => {

View file

@ -12,7 +12,7 @@ use std::fmt;
use visitor::SelectorVisitor;
size_of_test!(size_of_selector, Selector<Impl>, 8);
size_of_test!(size_of_pseudo_element, gecko_like_types::PseudoElement, 1);
size_of_test!(size_of_pseudo_element, gecko_like_types::PseudoElement, 24);
size_of_test!(size_of_component, Component<Impl>, 32);
size_of_test!(size_of_pseudo_class, PseudoClass, 24);