aboutsummaryrefslogtreecommitdiffstats
path: root/components/style/selectors.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/style/selectors.rs')
-rw-r--r--components/style/selectors.rs644
1 files changed, 320 insertions, 324 deletions
diff --git a/components/style/selectors.rs b/components/style/selectors.rs
index 92570409552..55b2fc687d0 100644
--- a/components/style/selectors.rs
+++ b/components/style/selectors.rs
@@ -2,49 +2,42 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use std::{cmp, iter};
+use std::cmp;
use std::ascii::{AsciiExt, OwnedAsciiExt};
use std::sync::Arc;
+use std::str::CowString;
-use cssparser::ast::*;
-use cssparser::ast::ComponentValue::*;
-use cssparser::{tokenize, parse_nth};
-
-use selector_matching::StylesheetOrigin;
+use cssparser::{Token, Parser, parse_nth};
use string_cache::{Atom, Namespace};
+use url::Url;
+use parser::ParserContext;
use namespaces::NamespaceMap;
+use stylesheets::Origin;
-/// Ambient data used by the parser.
-#[deriving(Copy)]
-pub struct ParserContext {
- /// The origin of this stylesheet.
- pub origin: StylesheetOrigin,
-}
-#[deriving(PartialEq, Clone)]
+#[deriving(PartialEq, Clone, Show)]
pub struct Selector {
pub compound_selectors: Arc<CompoundSelector>,
pub pseudo_element: Option<PseudoElement>,
pub specificity: u32,
}
-#[deriving(Eq, PartialEq, Clone, Hash, Copy)]
+#[deriving(Eq, PartialEq, Clone, Hash, Copy, Show)]
pub enum PseudoElement {
Before,
After,
-// FirstLine,
-// FirstLetter,
+ // ...
}
-#[deriving(PartialEq, Clone)]
+#[deriving(PartialEq, Clone, Show)]
pub struct CompoundSelector {
pub simple_selectors: Vec<SimpleSelector>,
pub next: Option<(Box<CompoundSelector>, Combinator)>, // c.next is left of c
}
-#[deriving(PartialEq, Clone, Copy)]
+#[deriving(PartialEq, Clone, Copy, Show)]
pub enum Combinator {
Child, // >
Descendant, // space
@@ -52,7 +45,7 @@ pub enum Combinator {
LaterSibling, // ~
}
-#[deriving(Eq, PartialEq, Clone, Hash)]
+#[deriving(Eq, PartialEq, Clone, Hash, Show)]
pub enum SimpleSelector {
ID(Atom),
Class(Atom),
@@ -79,9 +72,7 @@ pub enum SimpleSelector {
Checked,
Indeterminate,
FirstChild, LastChild, OnlyChild,
-// Empty,
Root,
-// Lang(String),
NthChild(i32, i32),
NthLastChild(i32, i32),
NthOfType(i32, i32),
@@ -94,27 +85,27 @@ pub enum SimpleSelector {
}
-#[deriving(Eq, PartialEq, Clone, Hash, Copy)]
+#[deriving(Eq, PartialEq, Clone, Hash, Copy, Show)]
pub enum CaseSensitivity {
CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive.
CaseInsensitive,
}
-#[deriving(Eq, PartialEq, Clone, Hash)]
+#[deriving(Eq, PartialEq, Clone, Hash, Show)]
pub struct LocalName {
pub name: Atom,
pub lower_name: Atom,
}
-#[deriving(Eq, PartialEq, Clone, Hash)]
+#[deriving(Eq, PartialEq, Clone, Hash, Show)]
pub struct AttrSelector {
pub name: Atom,
pub lower_name: Atom,
pub namespace: NamespaceConstraint,
}
-#[deriving(Eq, PartialEq, Clone, Hash)]
+#[deriving(Eq, PartialEq, Clone, Hash, Show)]
pub enum NamespaceConstraint {
Any,
Specific(Namespace),
@@ -132,9 +123,6 @@ pub fn get_selector_list_selectors<'a>(selector_list: &'a SelectorList) -> &'a [
}
-type Iter<I> = iter::Peekable<ComponentValue, I>;
-
-
fn compute_specificity(mut selector: &CompoundSelector,
pseudo_element: &Option<PseudoElement>) -> u32 {
struct Specificity {
@@ -183,7 +171,6 @@ fn compute_specificity(mut selector: &CompoundSelector,
&SimpleSelector::OnlyChild | &SimpleSelector::Root |
&SimpleSelector::Checked |
&SimpleSelector::Indeterminate |
-// &SimpleSelector::Empty | &SimpleSelector::Lang(*) |
&SimpleSelector::NthChild(..) |
&SimpleSelector::NthLastChild(..) |
&SimpleSelector::NthOfType(..) |
@@ -207,14 +194,87 @@ fn compute_specificity(mut selector: &CompoundSelector,
+pub fn parse_author_origin_selector_list_from_str(input: &str)
+ -> Result<SelectorList,()> {
+ let context = ParserContext {
+ stylesheet_origin: Origin::Author,
+ namespaces: NamespaceMap::new(),
+ base_url: &Url::parse("about:blank").unwrap(),
+ };
+ parse_selector_list(&context, &mut Parser::new(input))
+ .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(context: &ParserContext, input: &mut Parser)
+ -> Result<Vec<Selector>,()> {
+ input.parse_comma_separated(|input| parse_selector(context, input))
+}
+
+
+/// Build up a Selector.
+/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
+///
+/// `Err` means invalid selector.
+fn parse_selector(context: &ParserContext, input: &mut Parser) -> Result<Selector,()> {
+ let (first, mut pseudo_element) = try!(parse_simple_selectors(context, input));
+ let mut compound = CompoundSelector{ simple_selectors: first, next: None };
+
+ 'outer_loop: while pseudo_element.is_none() {
+ let combinator;
+ let mut any_whitespace = false;
+ loop {
+ let position = input.position();
+ match input.next_including_whitespace() {
+ Err(()) => break 'outer_loop,
+ Ok(Token::WhiteSpace(_)) => any_whitespace = true,
+ Ok(Token::Delim('>')) => {
+ combinator = Combinator::Child;
+ break
+ }
+ Ok(Token::Delim('+')) => {
+ combinator = Combinator::NextSibling;
+ break
+ }
+ Ok(Token::Delim('~')) => {
+ combinator = Combinator::LaterSibling;
+ break
+ }
+ Ok(_) => {
+ input.reset(position);
+ if any_whitespace {
+ combinator = Combinator::Descendant;
+ break
+ } else {
+ break 'outer_loop
+ }
+ }
+ }
+ }
+ let (simple_selectors, pseudo) = try!(parse_simple_selectors(context, input));
+ 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,
+ })
+}
+
+
/// * `Err(())`: Invalid selector, abort
-/// * `Ok(None)`: Not a type selector, could be something else. `iter` was not consumed.
+/// * `Ok(None)`: Not a type selector, could be something else. `input` was not consumed.
/// * `Ok(Some(vec))`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`)
-fn parse_type_selector<I: Iterator<ComponentValue>>(
- iter: &mut Iter<I>, namespaces: &NamespaceMap)
+fn parse_type_selector(context: &ParserContext, input: &mut Parser)
-> Result<Option<Vec<SimpleSelector>>, ()> {
- skip_whitespace(iter);
- match try!(parse_qualified_name(iter, /* in_attr_selector = */ false, namespaces)) {
+ match try!(parse_qualified_name(context, input, /* in_attr_selector = */ false)) {
None => Ok(None),
Some((namespace, local_name)) => {
let mut simple_selectors = vec!();
@@ -228,7 +288,7 @@ fn parse_type_selector<I: Iterator<ComponentValue>>(
Some(name) => {
simple_selectors.push(SimpleSelector::LocalName(LocalName {
name: Atom::from_slice(name.as_slice()),
- lower_name: Atom::from_slice(name.into_ascii_lower().as_slice())
+ lower_name: Atom::from_slice(name.into_owned().into_ascii_lower().as_slice())
}))
}
None => (),
@@ -239,6 +299,7 @@ fn parse_type_selector<I: Iterator<ComponentValue>>(
}
+#[deriving(Show)]
enum SimpleSelectorParseResult {
SimpleSelector(SimpleSelector),
PseudoElement(PseudoElement),
@@ -246,219 +307,145 @@ enum SimpleSelectorParseResult {
/// * `Err(())`: Invalid selector, abort
-/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed.
+/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed.
/// * `Ok(Some((namespace, local_name)))`: `None` for the local name means a `*` universal selector
-fn parse_qualified_name<I: Iterator<ComponentValue>>(
- iter: &mut Iter<I>, in_attr_selector: bool, namespaces: &NamespaceMap)
- -> Result<Option<(NamespaceConstraint, Option<String>)>, ()> {
+fn parse_qualified_name<'i, 't>
+ (context: &ParserContext, input: &mut Parser<'i, 't>,
+ in_attr_selector: bool)
+ -> Result<Option<(NamespaceConstraint, Option<CowString<'i>>)>, ()> {
let default_namespace = |local_name| {
- let namespace = match namespaces.default {
+ let namespace = match context.namespaces.default {
Some(ref ns) => NamespaceConstraint::Specific(ns.clone()),
None => NamespaceConstraint::Any,
};
Ok(Some((namespace, local_name)))
};
- let explicit_namespace = |iter: &mut Iter<I>, namespace| {
- assert!(iter.next() == Some(Delim('|')),
- "Implementation error, this should not happen.");
- match iter.peek() {
- Some(&Delim('*')) if !in_attr_selector => {
- iter.next();
+ let explicit_namespace = |input: &mut Parser<'i, 't>, namespace| {
+ match input.next_including_whitespace() {
+ Ok(Token::Delim('*')) if !in_attr_selector => {
Ok(Some((namespace, None)))
},
- Some(&Ident(_)) => {
- let local_name = get_next_ident(iter);
+ Ok(Token::Ident(local_name)) => {
Ok(Some((namespace, Some(local_name))))
},
_ => Err(()),
}
};
- match iter.peek() {
- Some(&Ident(_)) => {
- let value = get_next_ident(iter);
- match iter.peek() {
- Some(&Delim('|')) => {
- let namespace = match namespaces.prefix_map.get(&value) {
- None => return Err(()), // Undeclared namespace prefix
- Some(ref ns) => (*ns).clone(),
- };
- explicit_namespace(iter, NamespaceConstraint::Specific(namespace))
+ let position = input.position();
+ match input.next_including_whitespace() {
+ Ok(Token::Ident(value)) => {
+ let position = input.position();
+ match input.next_including_whitespace() {
+ Ok(Token::Delim('|')) => {
+ let result = context.namespaces.prefix_map.get(value.as_slice());
+ let namespace = try!(result.ok_or(()));
+ explicit_namespace(input, NamespaceConstraint::Specific(namespace.clone()))
},
- _ if in_attr_selector => Ok(Some(
- (NamespaceConstraint::Specific(ns!("")), Some(value)))),
- _ => default_namespace(Some(value)),
+ _ => {
+ input.reset(position);
+ if in_attr_selector {
+ Ok(Some((NamespaceConstraint::Specific(ns!("")), Some(value))))
+ } else {
+ default_namespace(Some(value))
+ }
+ }
}
},
- Some(&Delim('*')) => {
- iter.next(); // Consume '*'
- match iter.peek() {
- Some(&Delim('|')) => explicit_namespace(iter, NamespaceConstraint::Any),
+ Ok(Token::Delim('*')) => {
+ let position = input.position();
+ match input.next_including_whitespace() {
+ Ok(Token::Delim('|')) => explicit_namespace(input, NamespaceConstraint::Any),
_ => {
- if !in_attr_selector { default_namespace(None) }
- else { Err(()) }
+ input.reset(position);
+ if in_attr_selector {
+ Err(())
+ } else {
+ default_namespace(None)
+ }
},
}
},
- Some(&Delim('|')) => explicit_namespace(iter, NamespaceConstraint::Specific(ns!(""))),
- _ => Ok(None),
+ Ok(Token::Delim('|')) => explicit_namespace(input, NamespaceConstraint::Specific(ns!(""))),
+ _ => {
+ input.reset(position);
+ Ok(None)
+ }
}
}
-fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &NamespaceMap)
+fn parse_attribute_selector(context: &ParserContext, input: &mut Parser)
-> Result<SimpleSelector, ()> {
- let iter = &mut content.into_iter().peekable();
- let attr = match try!(parse_qualified_name(iter, /* in_attr_selector = */ true, namespaces)) {
+ let attr = match try!(parse_qualified_name(context, input, /* in_attr_selector = */ true)) {
None => return Err(()),
- Some((_, None)) => panic!("Implementation error, this should not happen."),
+ Some((_, None)) => unreachable!(),
Some((namespace, Some(local_name))) => AttrSelector {
namespace: namespace,
lower_name: Atom::from_slice(local_name.as_slice().to_ascii_lower().as_slice()),
name: Atom::from_slice(local_name.as_slice()),
},
};
- skip_whitespace(iter);
+
+ fn parse_value(input: &mut Parser) -> Result<String, ()> {
+ Ok((try!(input.expect_ident_or_string())).into_owned())
+ }
// TODO: deal with empty value or value containing whitespace (see spec)
- let result = match iter.next() {
+ match input.next() {
// [foo]
- None => SimpleSelector::AttrExists(attr),
+ Err(()) => Ok(SimpleSelector::AttrExists(attr)),
// [foo=bar]
- Some(Delim('=')) =>
- SimpleSelector::AttrEqual(attr, try!(parse_attribute_value(iter)),
- try!(parse_attribute_flags(iter))),
-
+ Ok(Token::Delim('=')) => {
+ Ok(SimpleSelector::AttrEqual(attr, try!(parse_value(input)),
+ try!(parse_attribute_flags(input))))
+ }
// [foo~=bar]
- Some(IncludeMatch) =>
- SimpleSelector::AttrIncludes(attr, try!(parse_attribute_value(iter))),
-
+ Ok(Token::IncludeMatch) => {
+ Ok(SimpleSelector::AttrIncludes(attr, try!(parse_value(input))))
+ }
// [foo|=bar]
- Some(DashMatch) => {
- let value = try!(parse_attribute_value(iter));
+ Ok(Token::DashMatch) => {
+ let value = try!(parse_value(input));
let dashing_value = format!("{}-", value);
- SimpleSelector::AttrDashMatch(attr, value, dashing_value)
- },
-
+ Ok(SimpleSelector::AttrDashMatch(attr, value, dashing_value))
+ }
// [foo^=bar]
- Some(PrefixMatch) =>
- SimpleSelector::AttrPrefixMatch(attr, try!(parse_attribute_value(iter))),
-
+ Ok(Token::PrefixMatch) => {
+ Ok(SimpleSelector::AttrPrefixMatch(attr, try!(parse_value(input))))
+ }
// [foo*=bar]
- Some(SubstringMatch) =>
- SimpleSelector::AttrSubstringMatch(attr, try!(parse_attribute_value(iter))),
-
+ Ok(Token::SubstringMatch) => {
+ Ok(SimpleSelector::AttrSubstringMatch(attr, try!(parse_value(input))))
+ }
// [foo$=bar]
- Some(SuffixMatch) =>
- SimpleSelector::AttrSuffixMatch(attr, try!(parse_attribute_value(iter))),
-
- _ => return Err(())
- };
- skip_whitespace(iter);
- if iter.next().is_none() { Ok(result) } else { Err(()) }
-}
-
-
-fn parse_attribute_value<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) -> Result<String, ()> {
- skip_whitespace(iter);
- match iter.next() {
- Some(Ident(value)) | Some(QuotedString(value)) => Ok(value),
- _ => Err(())
- }
-}
-
-
-fn parse_attribute_flags<I: Iterator<ComponentValue>>(iter: &mut Iter<I>)
- -> Result<CaseSensitivity, ()> {
- skip_whitespace(iter);
- match iter.next() {
- None => Ok(CaseSensitivity::CaseSensitive),
- Some(Ident(ref value)) if value.as_slice().eq_ignore_ascii_case("i")
- => Ok(CaseSensitivity::CaseInsensitive),
+ Ok(Token::SuffixMatch) => {
+ Ok(SimpleSelector::AttrSuffixMatch(attr, try!(parse_value(input))))
+ }
_ => Err(())
}
}
-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(()),
+fn parse_attribute_flags(input: &mut Parser) -> Result<CaseSensitivity, ()> {
+ match input.next() {
+ Err(()) => Ok(CaseSensitivity::CaseSensitive),
+ Ok(Token::Ident(ref value)) if value.eq_ignore_ascii_case("i") => {
+ Ok(CaseSensitivity::CaseInsensitive)
}
- results.push(try!(parse_selector(context, iter, namespaces)));
+ _ => Err(())
}
- 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(); Combinator::Child },
- Some(&Delim('+')) => { iter.next(); Combinator::NextSibling },
- Some(&Delim('~')) => { iter.next(); Combinator::LaterSibling },
- Some(_) => {
- if any_whitespace { Combinator::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)) {
+fn parse_negation(context: &ParserContext, input: &mut Parser) -> Result<SimpleSelector,()> {
+ match try!(parse_type_selector(context, input)) {
Some(type_selector) => Ok(SimpleSelector::Negation(type_selector)),
None => {
match try!(parse_one_simple_selector(context,
- iter,
- namespaces,
+ input,
/* inside_negation = */ true)) {
Some(SimpleSelectorParseResult::SimpleSelector(simple_selector)) => {
Ok(SimpleSelector::Negation(vec![simple_selector]))
@@ -474,13 +461,18 @@ fn parse_negation(context: &ParserContext,
/// | [ 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> {
+fn parse_simple_selectors(context: &ParserContext, input: &mut Parser)
+ -> Result<(Vec<SimpleSelector>, Option<PseudoElement>),()> {
+ // Consume any leading whitespace.
+ loop {
+ let position = input.position();
+ if !matches!(input.next_including_whitespace(), Ok(Token::WhiteSpace(_))) {
+ input.reset(position);
+ break
+ }
+ }
let mut empty = true;
- let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) {
+ let mut simple_selectors = match try!(parse_type_selector(context, input)) {
None => vec![],
Some(s) => { empty = false; s }
};
@@ -488,12 +480,18 @@ fn parse_simple_selectors<I>(context: &ParserContext,
let mut pseudo_element = None;
loop {
match try!(parse_one_simple_selector(context,
- iter,
- namespaces,
+ input,
/* inside_negation = */ false)) {
None => break,
- Some(SimpleSelectorParseResult::SimpleSelector(s)) => { simple_selectors.push(s); empty = false },
- Some(SimpleSelectorParseResult::PseudoElement(p)) => { pseudo_element = Some(p); empty = false; break },
+ Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
+ simple_selectors.push(s);
+ empty = false
+ }
+ Some(SimpleSelectorParseResult::PseudoElement(p)) => {
+ pseudo_element = Some(p);
+ empty = false;
+ break
+ }
}
}
if empty {
@@ -505,100 +503,111 @@ fn parse_simple_selectors<I>(context: &ParserContext,
}
fn parse_functional_pseudo_class(context: &ParserContext,
- name: String,
- arguments: Vec<ComponentValue>,
- namespaces: &NamespaceMap,
+ input: &mut Parser,
+ name: &str,
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)| SimpleSelector::NthChild(a, b)),
- "nth-last-child" => parse_nth(arguments.as_slice()).map(|(a, b)| SimpleSelector::NthLastChild(a, b)),
- "nth-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| SimpleSelector::NthOfType(a, b)),
- "nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| SimpleSelector::NthLastOfType(a, b)),
+ match_ignore_ascii_case! { name:
+ "nth-child" => parse_nth_pseudo_class(input, SimpleSelector::NthChild),
+ "nth-of-type" => parse_nth_pseudo_class(input, SimpleSelector::NthOfType),
+ "nth-last-child" => parse_nth_pseudo_class(input, SimpleSelector::NthLastChild),
+ "nth-last-of-type" => parse_nth_pseudo_class(input, SimpleSelector::NthLastOfType),
"not" => {
if inside_negation {
Err(())
} else {
- parse_negation(context, arguments, namespaces)
+ parse_negation(context, input)
}
}
_ => Err(())
}
}
+
+fn parse_nth_pseudo_class(input: &mut Parser, selector: |i32, i32| -> SimpleSelector)
+ -> Result<SimpleSelector, ()> {
+ let (a, b) = try!(parse_nth(input));
+ Ok(selector(a, b))
+}
+
+
/// 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(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>(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(SimpleSelectorParseResult::SimpleSelector(
- SimpleSelector::ID(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(SimpleSelectorParseResult::SimpleSelector(
- SimpleSelector::Class(Atom::from_slice(class.as_slice()))))),
+fn parse_one_simple_selector(context: &ParserContext,
+ input: &mut Parser,
+ inside_negation: bool)
+ -> Result<Option<SimpleSelectorParseResult>,()> {
+ let start_position = input.position();
+ match input.next_including_whitespace() {
+ Ok(Token::IDHash(id)) => {
+ let id = SimpleSelector::ID(Atom::from_slice(id.as_slice()));
+ Ok(Some(SimpleSelectorParseResult::SimpleSelector(id)))
+ }
+ Ok(Token::Delim('.')) => {
+ match input.next_including_whitespace() {
+ Ok(Token::Ident(class)) => {
+ let class = SimpleSelector::Class(Atom::from_slice(class.as_slice()));
+ Ok(Some(SimpleSelectorParseResult::SimpleSelector(class)))
+ }
_ => Err(()),
}
}
- Some(&SquareBracketBlock(_)) => match iter.next() {
- Some(SquareBracketBlock(content))
- => Ok(Some(SimpleSelectorParseResult::SimpleSelector(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(SimpleSelectorParseResult::PseudoElement(PseudoElement::Before))),
- "after" => Ok(Some(SimpleSelectorParseResult::PseudoElement(PseudoElement::After))),
-// "first-line" => SimpleSelectorParseResult::PseudoElement(FirstLine),
-// "first-letter" => SimpleSelectorParseResult::PseudoElement(FirstLetter),
- _ => Err(())
- }
- },
- Ok(result) => Ok(Some(SimpleSelectorParseResult::SimpleSelector(result))),
- },
- Some(Function(name, arguments))
- => {
- Ok(Some(SimpleSelectorParseResult::SimpleSelector(try!(parse_functional_pseudo_class(
- context,
- name,
- arguments,
- namespaces,
- inside_negation)))))
+ Ok(Token::SquareBracketBlock) => {
+ let attr = try!(input.parse_nested_block(|input| {
+ parse_attribute_selector(context, input)
+ }));
+ Ok(Some(SimpleSelectorParseResult::SimpleSelector(attr)))
+ }
+ Ok(Token::Colon) => {
+ match input.next_including_whitespace() {
+ Ok(Token::Ident(name)) => {
+ match parse_simple_pseudo_class(context, name.as_slice()) {
+ Err(()) => {
+ let pseudo_element = match_ignore_ascii_case! { name:
+ // Supported CSS 2.1 pseudo-elements only.
+ // ** Do not add to this list! **
+ "before" => PseudoElement::Before,
+ "after" => PseudoElement::After,
+ "first-line" => return Err(()),
+ "first-letter" => return Err(())
+ _ => return Err(())
+ };
+ Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element)))
+ },
+ Ok(result) => Ok(Some(SimpleSelectorParseResult::SimpleSelector(result))),
+ }
+ }
+ Ok(Token::Function(name)) => {
+ let name = name.as_slice();
+ let pseudo = try!(input.parse_nested_block(|input| {
+ parse_functional_pseudo_class(context, input, name, inside_negation)
+ }));
+ Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo)))
}
- Some(Colon) => {
- match iter.next() {
- Some(Ident(name))
- => Ok(Some(SimpleSelectorParseResult::PseudoElement(try!(parse_pseudo_element(name))))),
- _ => Err(()),
+ Ok(Token::Colon) => {
+ match input.next() {
+ Ok(Token::Ident(name)) => {
+ let pseudo = try!(parse_pseudo_element(name.as_slice()));
+ Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo)))
+ }
+ _ => Err(())
}
}
- _ => Err(()),
+ _ => Err(())
}
}
- _ => Ok(None),
+ _ => {
+ input.reset(start_position);
+ Ok(None)
+ }
}
}
fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<SimpleSelector,()> {
- match name.to_ascii_lower().as_slice() {
+ match_ignore_ascii_case! { name:
"any-link" => Ok(SimpleSelector::AnyLink),
"link" => Ok(SimpleSelector::Link),
"visited" => Ok(SimpleSelector::Visited),
@@ -614,76 +623,48 @@ fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<Simp
"first-of-type" => Ok(SimpleSelector::FirstOfType),
"last-of-type" => Ok(SimpleSelector::LastOfType),
"only-of-type" => Ok(SimpleSelector::OnlyOfType),
- "-servo-nonzero-border" if context.origin == StylesheetOrigin::UserAgent => Ok(SimpleSelector::ServoNonzeroBorder),
-// "empty" => Ok(Empty),
+ "-servo-nonzero-border" => {
+ if context.in_user_agent_stylesheet() {
+ Ok(SimpleSelector::ServoNonzeroBorder)
+ } else {
+ Err(())
+ }
+ }
_ => Err(())
}
}
-fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> {
- match name.as_slice().to_ascii_lower().as_slice() {
- // All supported pseudo-elements
+fn parse_pseudo_element(name: &str) -> Result<PseudoElement, ()> {
+ match_ignore_ascii_case! { name:
"before" => Ok(PseudoElement::Before),
- "after" => Ok(PseudoElement::After),
-// "first-line" => Some(FirstLine),
-// "first-letter" => Some(FirstLetter),
+ "after" => Ok(PseudoElement::After)
_ => Err(())
}
}
-//fn parse_lang(arguments: vec!(ComponentValue)) -> Result<SimpleSelector, ()> {
-// let mut iter = arguments.move_skip_whitespace();
-// match iter.next() {
-// Some(Ident(value)) => {
-// if "" == value || iter.next().is_some() { None }
-// else { Ok(Lang(value)) }
-// },
-// _ => Err(()),
-// }
-//}
-
-
-
-/// Assuming the next token is an ident, consume it and return its value
-#[inline]
-fn get_next_ident<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) -> String {
- match iter.next() {
- Some(Ident(value)) => value,
- _ => panic!("Implementation error, this should not happen."),
- }
-}
-
-
-#[inline]
-fn skip_whitespace<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) -> bool {
- let mut any_whitespace = false;
- loop {
- if iter.peek() != Some(&WhiteSpace) { return any_whitespace }
- any_whitespace = true;
- iter.next();
- }
-}
-
-
#[cfg(test)]
mod tests {
use std::sync::Arc;
- use cssparser;
+ use cssparser::Parser;
use namespaces::NamespaceMap;
- use selector_matching::StylesheetOrigin;
+ use stylesheets::Origin;
use string_cache::Atom;
+ use parser::ParserContext;
+ use url::Url;
use super::*;
fn parse(input: &str) -> Result<Vec<Selector>, ()> {
- parse_ns(input, &NamespaceMap::new())
+ parse_ns(input, NamespaceMap::new())
}
- fn parse_ns(input: &str, namespaces: &NamespaceMap) -> Result<Vec<Selector>, ()> {
+ fn parse_ns(input: &str, namespaces: NamespaceMap) -> Result<Vec<Selector>, ()> {
let context = ParserContext {
- origin: StylesheetOrigin::Author,
+ stylesheet_origin: Origin::Author,
+ namespaces: namespaces,
+ base_url: &Url::parse("about:blank").unwrap(),
};
- parse_selector_list(&context, cssparser::tokenize(input).map(|(v, _)| v), namespaces)
+ parse_selector_list(&context, &mut Parser::new(input))
}
fn specificity(a: u32, b: u32, c: u32) -> u32 {
@@ -692,8 +673,8 @@ mod tests {
#[test]
fn test_parsing() {
- assert!(parse("") == Err(()))
- assert!(parse("EeÉ") == Ok(vec!(Selector {
+ assert_eq!(parse(""), Err(()))
+ assert_eq!(parse("EeÉ"), Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(SimpleSelector::LocalName(LocalName {
name: Atom::from_slice("EeÉ"),
@@ -703,7 +684,7 @@ mod tests {
pseudo_element: None,
specificity: specificity(0, 0, 1),
})))
- assert!(parse(".foo") == Ok(vec!(Selector {
+ assert_eq!(parse(".foo"), Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(SimpleSelector::Class(Atom::from_slice("foo"))),
next: None,
@@ -711,7 +692,7 @@ mod tests {
pseudo_element: None,
specificity: specificity(0, 1, 0),
})))
- assert!(parse("#bar") == Ok(vec!(Selector {
+ assert_eq!(parse("#bar"), Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(SimpleSelector::ID(Atom::from_slice("bar"))),
next: None,
@@ -719,7 +700,7 @@ mod tests {
pseudo_element: None,
specificity: specificity(1, 0, 0),
})))
- assert!(parse("e.foo#bar") == Ok(vec!(Selector {
+ assert_eq!(parse("e.foo#bar"), Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(SimpleSelector::LocalName(LocalName {
name: Atom::from_slice("e"),
@@ -731,7 +712,7 @@ mod tests {
pseudo_element: None,
specificity: specificity(1, 1, 1),
})))
- assert!(parse("e.foo #bar") == Ok(vec!(Selector {
+ assert_eq!(parse("e.foo #bar"), Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(SimpleSelector::ID(Atom::from_slice("bar"))),
next: Some((box CompoundSelector {
@@ -748,7 +729,7 @@ mod tests {
// Default namespace does not apply to attribute selectors
// https://github.com/mozilla/servo/pull/1652
let mut namespaces = NamespaceMap::new();
- assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector {
+ assert_eq!(parse_ns("[Foo]", namespaces.clone()), Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(SimpleSelector::AttrExists(AttrSelector {
name: Atom::from_slice("Foo"),
@@ -763,7 +744,7 @@ mod tests {
// Default namespace does not apply to attribute selectors
// https://github.com/mozilla/servo/pull/1652
namespaces.default = Some(ns!(MathML));
- assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector {
+ assert_eq!(parse_ns("[Foo]", namespaces.clone()), Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(SimpleSelector::AttrExists(AttrSelector {
name: Atom::from_slice("Foo"),
@@ -776,7 +757,7 @@ mod tests {
specificity: specificity(0, 1, 0),
})))
// Default namespace does apply to type selectors
- assert!(parse_ns("e", &namespaces) == Ok(vec!(Selector {
+ assert_eq!(parse_ns("e", namespaces), Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(
SimpleSelector::Namespace(ns!(MathML)),
@@ -790,7 +771,7 @@ mod tests {
specificity: specificity(0, 0, 1),
})))
// https://github.com/mozilla/servo/issues/1723
- assert!(parse("::before") == Ok(vec!(Selector {
+ assert_eq!(parse("::before"), Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(),
next: None,
@@ -798,7 +779,7 @@ mod tests {
pseudo_element: Some(PseudoElement::Before),
specificity: specificity(0, 0, 1),
})))
- assert!(parse("div :after") == Ok(vec!(Selector {
+ assert_eq!(parse("div :after"), Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(),
next: Some((box CompoundSelector {
@@ -811,5 +792,20 @@ mod tests {
pseudo_element: Some(PseudoElement::After),
specificity: specificity(0, 0, 2),
})))
+ assert_eq!(parse("#d1 > .ok"), Ok(vec![Selector {
+ compound_selectors: Arc::new(CompoundSelector {
+ simple_selectors: vec![
+ SimpleSelector::Class(Atom::from_slice("ok")),
+ ],
+ next: Some((box CompoundSelector {
+ simple_selectors: vec![
+ SimpleSelector::ID(Atom::from_slice("d1")),
+ ],
+ next: None,
+ }, Combinator::Child)),
+ }),
+ pseudo_element: None,
+ specificity: (1 << 20) + (1 << 10) + (0 << 0),
+ }]))
}
}