aboutsummaryrefslogtreecommitdiffstats
path: root/components/style/selectors.rs
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2014-12-11 09:58:00 -0800
committerPatrick Walton <pcwalton@mimiga.net>2014-12-15 18:09:44 -0800
commita1ea44b294e73f4397887fe806fc5bc95499efda (patch)
treea32b122e99c87a1f98e6b05c087fee00411cef36 /components/style/selectors.rs
parent17835ba0cb446e1d542c7e5347210c03158a9fec (diff)
downloadservo-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.rs422
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 {