diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2014-12-11 09:58:00 -0800 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2014-12-15 18:09:44 -0800 |
commit | a1ea44b294e73f4397887fe806fc5bc95499efda (patch) | |
tree | a32b122e99c87a1f98e6b05c087fee00411cef36 /components/style/selectors.rs | |
parent | 17835ba0cb446e1d542c7e5347210c03158a9fec (diff) | |
download | servo-a1ea44b294e73f4397887fe806fc5bc95499efda.tar.gz servo-a1ea44b294e73f4397887fe806fc5bc95499efda.zip |
style: Address review comments relating to `bgcolor` and column spans
Diffstat (limited to 'components/style/selectors.rs')
-rw-r--r-- | components/style/selectors.rs | 422 |
1 files changed, 230 insertions, 192 deletions
diff --git a/components/style/selectors.rs b/components/style/selectors.rs index 9cb3f240dd4..1d72a530383 100644 --- a/components/style/selectors.rs +++ b/components/style/selectors.rs @@ -9,10 +9,16 @@ use sync::Arc; use cssparser::ast::*; use cssparser::{tokenize, parse_nth}; +use selector_matching::{StylesheetOrigin, UserAgentOrigin}; use string_cache::{Atom, Namespace}; use namespaces::NamespaceMap; +/// Ambient data used by the parser. +pub struct ParserContext { + /// The origin of this stylesheet. + pub origin: StylesheetOrigin, +} #[deriving(PartialEq, Clone)] pub struct Selector { @@ -112,12 +118,6 @@ pub enum NamespaceConstraint { } -pub fn parse_selector_list_from_str(input: &str) -> Result<SelectorList, ()> { - let namespaces = NamespaceMap::new(); - let iter = tokenize(input).map(|(token, _)| token); - parse_selector_list(iter, &namespaces).map(|s| SelectorList { selectors: s }) -} - /// Re-exported to script, but opaque. pub struct SelectorList { selectors: Vec<Selector> @@ -128,70 +128,9 @@ pub fn get_selector_list_selectors<'a>(selector_list: &'a SelectorList) -> &'a [ selector_list.selectors.as_slice() } -/// Parse a comma-separated list of Selectors. -/// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping -/// -/// Return the Selectors or None if there is an invalid selector. -pub fn parse_selector_list<I: Iterator<ComponentValue>>( - iter: I, namespaces: &NamespaceMap) - -> Result<Vec<Selector>, ()> { - let iter = &mut iter.peekable(); - let mut results = vec![try!(parse_selector(iter, namespaces))]; - - loop { - skip_whitespace(iter); - match iter.peek() { - None => break, // EOF - Some(&Comma) => { - iter.next(); - } - _ => return Err(()), - } - results.push(try!(parse_selector(iter, namespaces))); - } - Ok(results) -} - type Iter<I> = iter::Peekable<ComponentValue, I>; -/// Build up a Selector. -/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ; -/// -/// `Err` means invalid selector. -fn parse_selector<I: Iterator<ComponentValue>>( - iter: &mut Iter<I>, namespaces: &NamespaceMap) - -> Result<Selector, ()> { - let (first, mut pseudo_element) = try!(parse_simple_selectors(iter, namespaces)); - let mut compound = CompoundSelector{ simple_selectors: first, next: None }; - - while pseudo_element.is_none() { - let any_whitespace = skip_whitespace(iter); - let combinator = match iter.peek() { - None => break, // EOF - Some(&Comma) => break, - Some(&Delim('>')) => { iter.next(); Child }, - Some(&Delim('+')) => { iter.next(); NextSibling }, - Some(&Delim('~')) => { iter.next(); LaterSibling }, - Some(_) => { - if any_whitespace { Descendant } - else { return Err(()) } - } - }; - let (simple_selectors, pseudo) = try!(parse_simple_selectors(iter, namespaces)); - compound = CompoundSelector { - simple_selectors: simple_selectors, - next: Some((box compound, combinator)) - }; - pseudo_element = pseudo; - } - Ok(Selector { - specificity: compute_specificity(&compound, &pseudo_element), - compound_selectors: Arc::new(compound), - pseudo_element: pseudo_element, - }) -} - fn compute_specificity(mut selector: &CompoundSelector, pseudo_element: &Option<PseudoElement>) -> u32 { @@ -248,32 +187,6 @@ fn compute_specificity(mut selector: &CompoundSelector, } -/// simple_selector_sequence -/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]* -/// | [ HASH | class | attrib | pseudo | negation ]+ -/// -/// `Err(())` means invalid selector -fn parse_simple_selectors<I: Iterator<ComponentValue>>( - iter: &mut Iter<I>, namespaces: &NamespaceMap) - -> Result<(Vec<SimpleSelector>, Option<PseudoElement>), ()> { - let mut empty = true; - let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) { - None => vec![], - Some(s) => { empty = false; s } - }; - - let mut pseudo_element = None; - loop { - match try!(parse_one_simple_selector(iter, namespaces, /* inside_negation = */ false)) { - None => break, - Some(SimpleSelectorResult(s)) => { simple_selectors.push(s); empty = false }, - Some(PseudoElementResult(p)) => { pseudo_element = Some(p); empty = false; break }, - } - } - if empty { Err(()) } // An empty selector is invalid - else { Ok((simple_selectors, pseudo_element)) } -} - /// * `Err(())`: Invalid selector, abort /// * `Ok(None)`: Not a type selector, could be something else. `iter` was not consumed. @@ -310,67 +223,6 @@ enum SimpleSelectorParseResult { PseudoElementResult(PseudoElement), } -/// Parse a simple selector other than a type selector. -/// -/// * `Err(())`: Invalid selector, abort -/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed. -/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element -fn parse_one_simple_selector<I: Iterator<ComponentValue>>( - iter: &mut Iter<I>, namespaces: &NamespaceMap, inside_negation: bool) - -> Result<Option<SimpleSelectorParseResult>, ()> { - match iter.peek() { - Some(&IDHash(_)) => match iter.next() { - Some(IDHash(id)) => Ok(Some(SimpleSelectorResult( - IDSelector(Atom::from_slice(id.as_slice()))))), - _ => panic!("Implementation error, this should not happen."), - }, - Some(&Delim('.')) => { - iter.next(); - match iter.next() { - Some(Ident(class)) => Ok(Some(SimpleSelectorResult( - ClassSelector(Atom::from_slice(class.as_slice()))))), - _ => Err(()), - } - } - Some(&SquareBracketBlock(_)) => match iter.next() { - Some(SquareBracketBlock(content)) - => Ok(Some(SimpleSelectorResult(try!(parse_attribute_selector(content, namespaces))))), - _ => panic!("Implementation error, this should not happen."), - }, - Some(&Colon) => { - iter.next(); - match iter.next() { - Some(Ident(name)) => match parse_simple_pseudo_class(name.as_slice()) { - Err(()) => { - match name.as_slice().to_ascii_lower().as_slice() { - // Supported CSS 2.1 pseudo-elements only. - // ** Do not add to this list! ** - "before" => Ok(Some(PseudoElementResult(Before))), - "after" => Ok(Some(PseudoElementResult(After))), -// "first-line" => PseudoElementResult(FirstLine), -// "first-letter" => PseudoElementResult(FirstLetter), - _ => Err(()) - } - }, - Ok(result) => Ok(Some(SimpleSelectorResult(result))), - }, - Some(Function(name, arguments)) - => Ok(Some(SimpleSelectorResult(try!(parse_functional_pseudo_class( - name, arguments, namespaces, inside_negation))))), - Some(Colon) => { - match iter.next() { - Some(Ident(name)) - => Ok(Some(PseudoElementResult(try!(parse_pseudo_element(name))))), - _ => Err(()), - } - } - _ => Err(()), - } - } - _ => Ok(None), - } -} - /// * `Err(())`: Invalid selector, abort /// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed. @@ -490,8 +342,224 @@ fn parse_attribute_flags<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) } } +pub fn parse_selector_list_from_str(context: &ParserContext, input: &str) + -> Result<SelectorList,()> { + let namespaces = NamespaceMap::new(); + let iter = tokenize(input).map(|(token, _)| token); + parse_selector_list(context, iter, &namespaces).map(|s| SelectorList { selectors: s }) +} + +/// Parse a comma-separated list of Selectors. +/// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping +/// +/// Return the Selectors or None if there is an invalid selector. +pub fn parse_selector_list<I>(context: &ParserContext, iter: I, namespaces: &NamespaceMap) + -> Result<Vec<Selector>,()> + where I: Iterator<ComponentValue> { + let iter = &mut iter.peekable(); + let mut results = vec![try!(parse_selector(context, iter, namespaces))]; + + loop { + skip_whitespace(iter); + match iter.peek() { + None => break, // EOF + Some(&Comma) => { + iter.next(); + } + _ => return Err(()), + } + results.push(try!(parse_selector(context, iter, namespaces))); + } + Ok(results) +} +/// Build up a Selector. +/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ; +/// +/// `Err` means invalid selector. +fn parse_selector<I>(context: &ParserContext, iter: &mut Iter<I>, namespaces: &NamespaceMap) + -> Result<Selector,()> + where I: Iterator<ComponentValue> { + let (first, mut pseudo_element) = try!(parse_simple_selectors(context, iter, namespaces)); + let mut compound = CompoundSelector{ simple_selectors: first, next: None }; + + while pseudo_element.is_none() { + let any_whitespace = skip_whitespace(iter); + let combinator = match iter.peek() { + None => break, // EOF + Some(&Comma) => break, + Some(&Delim('>')) => { iter.next(); Child }, + Some(&Delim('+')) => { iter.next(); NextSibling }, + Some(&Delim('~')) => { iter.next(); LaterSibling }, + Some(_) => { + if any_whitespace { Descendant } + else { return Err(()) } + } + }; + let (simple_selectors, pseudo) = try!(parse_simple_selectors(context, iter, namespaces)); + compound = CompoundSelector { + simple_selectors: simple_selectors, + next: Some((box compound, combinator)) + }; + pseudo_element = pseudo; + } + Ok(Selector { + specificity: compute_specificity(&compound, &pseudo_element), + compound_selectors: Arc::new(compound), + pseudo_element: pseudo_element, + }) +} + +/// Level 3: Parse **one** simple_selector +fn parse_negation(context: &ParserContext, + arguments: Vec<ComponentValue>, + namespaces: &NamespaceMap) + -> Result<SimpleSelector,()> { + let iter = &mut arguments.into_iter().peekable(); + match try!(parse_type_selector(iter, namespaces)) { + Some(type_selector) => Ok(Negation(type_selector)), + None => { + match try!(parse_one_simple_selector(context, + iter, + namespaces, + /* inside_negation = */ true)) { + Some(SimpleSelectorResult(simple_selector)) => { + Ok(Negation(vec![simple_selector])) + } + _ => Err(()) + } + }, + } +} + +/// simple_selector_sequence +/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]* +/// | [ HASH | class | attrib | pseudo | negation ]+ +/// +/// `Err(())` means invalid selector +fn parse_simple_selectors<I>(context: &ParserContext, + iter: &mut Iter<I>, + namespaces: &NamespaceMap) + -> Result<(Vec<SimpleSelector>, Option<PseudoElement>),()> + where I: Iterator<ComponentValue> { + let mut empty = true; + let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) { + None => vec![], + Some(s) => { empty = false; s } + }; + + let mut pseudo_element = None; + loop { + match try!(parse_one_simple_selector(context, + iter, + namespaces, + /* inside_negation = */ false)) { + None => break, + Some(SimpleSelectorResult(s)) => { simple_selectors.push(s); empty = false }, + Some(PseudoElementResult(p)) => { pseudo_element = Some(p); empty = false; break }, + } + } + if empty { + // An empty selector is invalid. + Err(()) + } else { + Ok((simple_selectors, pseudo_element)) + } +} + +fn parse_functional_pseudo_class(context: &ParserContext, + name: String, + arguments: Vec<ComponentValue>, + namespaces: &NamespaceMap, + inside_negation: bool) + -> Result<SimpleSelector,()> { + match name.as_slice().to_ascii_lower().as_slice() { +// "lang" => parse_lang(arguments), + "nth-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthChild(a, b)), + "nth-last-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastChild(a, b)), + "nth-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthOfType(a, b)), + "nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastOfType(a, b)), + "not" => { + if inside_negation { + Err(()) + } else { + parse_negation(context, arguments, namespaces) + } + } + _ => Err(()) + } +} + +/// Parse a simple selector other than a type selector. +/// +/// * `Err(())`: Invalid selector, abort +/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed. +/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element +fn parse_one_simple_selector<I>(context: &ParserContext, + iter: &mut Iter<I>, + namespaces: &NamespaceMap, + inside_negation: bool) + -> Result<Option<SimpleSelectorParseResult>,()> + where I: Iterator<ComponentValue> { + match iter.peek() { + Some(&IDHash(_)) => match iter.next() { + Some(IDHash(id)) => Ok(Some(SimpleSelectorResult( + IDSelector(Atom::from_slice(id.as_slice()))))), + _ => panic!("Implementation error, this should not happen."), + }, + Some(&Delim('.')) => { + iter.next(); + match iter.next() { + Some(Ident(class)) => Ok(Some(SimpleSelectorResult( + ClassSelector(Atom::from_slice(class.as_slice()))))), + _ => Err(()), + } + } + Some(&SquareBracketBlock(_)) => match iter.next() { + Some(SquareBracketBlock(content)) + => Ok(Some(SimpleSelectorResult(try!(parse_attribute_selector(content, namespaces))))), + _ => panic!("Implementation error, this should not happen."), + }, + Some(&Colon) => { + iter.next(); + match iter.next() { + Some(Ident(name)) => match parse_simple_pseudo_class(context, name.as_slice()) { + Err(()) => { + match name.as_slice().to_ascii_lower().as_slice() { + // Supported CSS 2.1 pseudo-elements only. + // ** Do not add to this list! ** + "before" => Ok(Some(PseudoElementResult(Before))), + "after" => Ok(Some(PseudoElementResult(After))), +// "first-line" => PseudoElementResult(FirstLine), +// "first-letter" => PseudoElementResult(FirstLetter), + _ => Err(()) + } + }, + Ok(result) => Ok(Some(SimpleSelectorResult(result))), + }, + Some(Function(name, arguments)) + => { + Ok(Some(SimpleSelectorResult(try!(parse_functional_pseudo_class( + context, + name, + arguments, + namespaces, + inside_negation))))) + } + Some(Colon) => { + match iter.next() { + Some(Ident(name)) + => Ok(Some(PseudoElementResult(try!(parse_pseudo_element(name))))), + _ => Err(()), + } + } + _ => Err(()), + } + } + _ => Ok(None), + } +} -fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> { +fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<SimpleSelector,()> { match name.to_ascii_lower().as_slice() { "any-link" => Ok(AnyLink), "link" => Ok(Link), @@ -507,31 +575,12 @@ fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> { "first-of-type" => Ok(FirstOfType), "last-of-type" => Ok(LastOfType), "only-of-type" => Ok(OnlyOfType), - "-servo-nonzero-border" => { - // TODO(pcwalton): Have some mechanism whereby we forbid Web content from using this. - Ok(ServoNonzeroBorder) - } -// "empty" => Ok(Empty), - _ => Err(()) - } -} - - -fn parse_functional_pseudo_class(name: String, arguments: Vec<ComponentValue>, - namespaces: &NamespaceMap, inside_negation: bool) - -> Result<SimpleSelector, ()> { - match name.as_slice().to_ascii_lower().as_slice() { -// "lang" => parse_lang(arguments), - "nth-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthChild(a, b)), - "nth-last-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastChild(a, b)), - "nth-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthOfType(a, b)), - "nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastOfType(a, b)), - "not" => if inside_negation { Err(()) } else { parse_negation(arguments, namespaces) }, + "-servo-nonzero-border" if context.origin == UserAgentOrigin => Ok(ServoNonzeroBorder), +// "empty" => Ok(Empty), _ => Err(()) } } - fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> { match name.as_slice().to_ascii_lower().as_slice() { // All supported pseudo-elements @@ -556,21 +605,6 @@ fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> { //} -/// Level 3: Parse **one** simple_selector -fn parse_negation(arguments: Vec<ComponentValue>, namespaces: &NamespaceMap) - -> Result<SimpleSelector, ()> { - let iter = &mut arguments.into_iter().peekable(); - match try!(parse_type_selector(iter, namespaces)) { - Some(type_selector) => Ok(Negation(type_selector)), - None => { - match try!(parse_one_simple_selector(iter, namespaces, /* inside_negation = */ true)) { - Some(SimpleSelectorResult(simple_selector)) => Ok(Negation(vec![simple_selector])), - _ => Err(()) - } - }, - } -} - /// Assuming the next token is an ident, consume it and return its value #[inline] @@ -598,6 +632,7 @@ mod tests { use sync::Arc; use cssparser; use namespaces::NamespaceMap; + use selector_matching::AuthorOrigin; use string_cache::Atom; use super::*; @@ -606,7 +641,10 @@ mod tests { } fn parse_ns(input: &str, namespaces: &NamespaceMap) -> Result<Vec<Selector>, ()> { - parse_selector_list(cssparser::tokenize(input).map(|(v, _)| v), namespaces) + let context = ParserContext { + origin: AuthorOrigin, + }; + parse_selector_list(&context, cssparser::tokenize(input).map(|(v, _)| v), namespaces) } fn specificity(a: u32, b: u32, c: u32) -> u32 { |