Use Result/Err(()) in Selector parsing.

… get rid of some custom `enum` types for parsing return values.
`Option<Option<T>>` was ridiculous, but `Result<Option<T>, ()>`
make perfect sense.

Also, we can now take advantage of the `try!()` macro.
This commit is contained in:
Simon Sapin 2014-08-11 21:50:47 +01:00
parent d9278e3f6a
commit 9564d91b5e
5 changed files with 145 additions and 173 deletions

View file

@ -29,7 +29,7 @@ servo-test-$(1): $$(DEPS_$(1))
.PHONY: check-servo-$(1) .PHONY: check-servo-$(1)
check-servo-$(1): servo-test-$(1) check-servo-$(1): servo-test-$(1)
@$$(call E, check: $(1)) @$$(call E, check: $(1))
$$(Q)./servo-test-$(1) $$(Q)./servo-test-$(1) $(TESTNAME)
endef endef
$(foreach lib_crate,$(SERVO_LIB_CRATES),\ $(foreach lib_crate,$(SERVO_LIB_CRATES),\

View file

@ -778,8 +778,8 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
let namespace = NamespaceMap::new(); let namespace = NamespaceMap::new();
match parse_selector_list(tokenize(selectors.as_slice()).map(|(token, _)| token).collect(), match parse_selector_list(tokenize(selectors.as_slice()).map(|(token, _)| token).collect(),
&namespace) { &namespace) {
None => return Err(Syntax), Err(()) => return Err(Syntax),
Some(ref selectors) => { Ok(ref selectors) => {
let root: &JSRef<Node> = NodeCast::from_ref(self); let root: &JSRef<Node> = NodeCast::from_ref(self);
for selector in selectors.iter() { for selector in selectors.iter() {
let mut shareable = false; let mut shareable = false;

View file

@ -614,9 +614,9 @@ impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> {
let namespace = NamespaceMap::new(); let namespace = NamespaceMap::new();
match parse_selector_list(tokenize(selectors.as_slice()).map(|(token, _)| token).collect(), &namespace) { match parse_selector_list(tokenize(selectors.as_slice()).map(|(token, _)| token).collect(), &namespace) {
// Step 2. // Step 2.
None => return Err(Syntax), Err(()) => return Err(Syntax),
// Step 3. // Step 3.
Some(ref selectors) => { Ok(ref selectors) => {
let root = self.ancestors().last().unwrap_or(self.clone()); let root = self.ancestors().last().unwrap_or(self.clone());
for selector in selectors.iter() { for selector in selectors.iter() {
assert!(selector.pseudo_element.is_none()); assert!(selector.pseudo_element.is_none());
@ -641,9 +641,9 @@ impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> {
let namespace = NamespaceMap::new(); let namespace = NamespaceMap::new();
match parse_selector_list(tokenize(selectors.as_slice()).map(|(token, _)| token).collect(), &namespace) { match parse_selector_list(tokenize(selectors.as_slice()).map(|(token, _)| token).collect(), &namespace) {
// Step 2. // Step 2.
None => return Err(Syntax), Err(()) => return Err(Syntax),
// Step 3. // Step 3.
Some(ref selectors) => { Ok(ref selectors) => {
for selector in selectors.iter() { for selector in selectors.iter() {
assert!(selector.pseudo_element.is_none()); assert!(selector.pseudo_element.is_none());
for node in root.traverse_preorder().filter(|node| node.is_element()) { for node in root.traverse_preorder().filter(|node| node.is_element()) {

View file

@ -115,13 +115,9 @@ type Iter = iter::Peekable<ComponentValue, vec::MoveItems<ComponentValue>>;
/// ///
/// Return the Selectors or None if there is an invalid selector. /// Return the Selectors or None if there is an invalid selector.
pub fn parse_selector_list(input: Vec<ComponentValue>, namespaces: &NamespaceMap) pub fn parse_selector_list(input: Vec<ComponentValue>, namespaces: &NamespaceMap)
-> Option<Vec<Selector>> { -> Result<Vec<Selector>, ()> {
let iter = &mut input.move_iter().peekable(); let iter = &mut input.move_iter().peekable();
let first = match parse_selector(iter, namespaces) { let mut results = vec![try!(parse_selector(iter, namespaces))];
None => return None,
Some(result) => result
};
let mut results = vec!(first);
loop { loop {
skip_whitespace(iter); skip_whitespace(iter);
@ -130,14 +126,11 @@ pub fn parse_selector_list(input: Vec<ComponentValue>, namespaces: &NamespaceMap
Some(&Comma) => { Some(&Comma) => {
iter.next(); iter.next();
} }
_ => return None, _ => return Err(()),
}
match parse_selector(iter, namespaces) {
Some(selector) => results.push(selector),
None => return None,
} }
results.push(try!(parse_selector(iter, namespaces)));
} }
Some(results) Ok(results)
} }
@ -146,13 +139,9 @@ pub fn parse_selector_list(input: Vec<ComponentValue>, namespaces: &NamespaceMap
/// ///
/// None means invalid selector. /// None means invalid selector.
fn parse_selector(iter: &mut Iter, namespaces: &NamespaceMap) fn parse_selector(iter: &mut Iter, namespaces: &NamespaceMap)
-> Option<Selector> { -> Result<Selector, ()> {
let (first, pseudo_element) = match parse_simple_selectors(iter, namespaces) { let (first, mut pseudo_element) = try!(parse_simple_selectors(iter, namespaces));
None => return None,
Some(result) => result
};
let mut compound = CompoundSelector{ simple_selectors: first, next: None }; let mut compound = CompoundSelector{ simple_selectors: first, next: None };
let mut pseudo_element = pseudo_element;
while pseudo_element.is_none() { while pseudo_element.is_none() {
let any_whitespace = skip_whitespace(iter); let any_whitespace = skip_whitespace(iter);
@ -164,21 +153,17 @@ fn parse_selector(iter: &mut Iter, namespaces: &NamespaceMap)
Some(&Delim('~')) => { iter.next(); LaterSibling }, Some(&Delim('~')) => { iter.next(); LaterSibling },
Some(_) => { Some(_) => {
if any_whitespace { Descendant } if any_whitespace { Descendant }
else { return None } else { return Err(()) }
} }
}; };
match parse_simple_selectors(iter, namespaces) { let (simple_selectors, pseudo) = try!(parse_simple_selectors(iter, namespaces));
None => return None, compound = CompoundSelector {
Some((simple_selectors, pseudo)) => { simple_selectors: simple_selectors,
compound = CompoundSelector { next: Some((box compound, combinator))
simple_selectors: simple_selectors, };
next: Some((box compound, combinator)) pseudo_element = pseudo;
};
pseudo_element = pseudo;
}
}
} }
Some(Selector { Ok(Selector {
specificity: compute_specificity(&compound, &pseudo_element), specificity: compute_specificity(&compound, &pseudo_element),
compound_selectors: Arc::new(compound), compound_selectors: Arc::new(compound),
pseudo_element: pseudo_element, pseudo_element: pseudo_element,
@ -245,43 +230,37 @@ fn compute_specificity(mut selector: &CompoundSelector,
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]* /// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
/// | [ HASH | class | attrib | pseudo | negation ]+ /// | [ HASH | class | attrib | pseudo | negation ]+
/// ///
/// None means invalid selector /// `Err(())` means invalid selector
fn parse_simple_selectors(iter: &mut Iter, namespaces: &NamespaceMap) fn parse_simple_selectors(iter: &mut Iter, namespaces: &NamespaceMap)
-> Option<(Vec<SimpleSelector>, Option<PseudoElement>)> { -> Result<(Vec<SimpleSelector>, Option<PseudoElement>), ()> {
let mut empty = true; let mut empty = true;
let mut simple_selectors = match parse_type_selector(iter, namespaces) { let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) {
InvalidTypeSelector => return None, None => vec![],
NotATypeSelector => vec!(), Some(s) => { empty = false; s }
TypeSelector(s) => { empty = false; s }
}; };
let mut pseudo_element = None; let mut pseudo_element = None;
loop { loop {
match parse_one_simple_selector(iter, namespaces, /* inside_negation = */ false) { match try!(parse_one_simple_selector(iter, namespaces, /* inside_negation = */ false)) {
InvalidSimpleSelector => return None, None => break,
NotASimpleSelector => break, Some(SimpleSelectorResult(s)) => { simple_selectors.push(s); empty = false },
SimpleSelectorResult(s) => { simple_selectors.push(s); empty = false }, Some(PseudoElementResult(p)) => { pseudo_element = Some(p); empty = false; break },
PseudoElementResult(p) => { pseudo_element = Some(p); empty = false; break },
} }
} }
if empty { None } // An empty selector is invalid if empty { Err(()) } // An empty selector is invalid
else { Some((simple_selectors, pseudo_element)) } else { Ok((simple_selectors, pseudo_element)) }
} }
enum TypeSelectorParseResult { /// * `Err(())`: Invalid selector, abort
InvalidTypeSelector, /// * `Ok(None)`: Not a type selector, could be something else. `iter` was not consumed.
NotATypeSelector, /// * `Ok(Some(vec))`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`)
TypeSelector(Vec<SimpleSelector>), // Length 0 (*|*), 1 (*|E or ns|*) or 2 (|E or ns|E)
}
fn parse_type_selector(iter: &mut Iter, namespaces: &NamespaceMap) fn parse_type_selector(iter: &mut Iter, namespaces: &NamespaceMap)
-> TypeSelectorParseResult { -> Result<Option<Vec<SimpleSelector>>, ()> {
skip_whitespace(iter); skip_whitespace(iter);
match parse_qualified_name(iter, /* in_attr_selector = */ false, namespaces) { match try!(parse_qualified_name(iter, /* in_attr_selector = */ false, namespaces)) {
InvalidQualifiedName => InvalidTypeSelector, None => Ok(None),
NotAQualifiedName => NotATypeSelector, Some((namespace, local_name)) => {
QualifiedName(namespace, local_name) => {
let mut simple_selectors = vec!(); let mut simple_selectors = vec!();
match namespace { match namespace {
SpecificNamespace(ns) => simple_selectors.push(NamespaceSelector(ns)), SpecificNamespace(ns) => simple_selectors.push(NamespaceSelector(ns)),
@ -294,114 +273,108 @@ fn parse_type_selector(iter: &mut Iter, namespaces: &NamespaceMap)
} }
None => (), None => (),
} }
TypeSelector(simple_selectors) Ok(Some(simple_selectors))
} }
} }
} }
enum SimpleSelectorParseResult { enum SimpleSelectorParseResult {
InvalidSimpleSelector,
NotASimpleSelector,
SimpleSelectorResult(SimpleSelector), SimpleSelectorResult(SimpleSelector),
PseudoElementResult(PseudoElement), PseudoElementResult(PseudoElement),
} }
// Parse a simple selector other than a type selector /// 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(iter: &mut Iter, namespaces: &NamespaceMap, inside_negation: bool) fn parse_one_simple_selector(iter: &mut Iter, namespaces: &NamespaceMap, inside_negation: bool)
-> SimpleSelectorParseResult { -> Result<Option<SimpleSelectorParseResult>, ()> {
match iter.peek() { match iter.peek() {
Some(&IDHash(_)) => match iter.next() { Some(&IDHash(_)) => match iter.next() {
Some(IDHash(id)) => SimpleSelectorResult(IDSelector(Atom::from_slice(id.as_slice()))), Some(IDHash(id)) => Ok(Some(SimpleSelectorResult(
IDSelector(Atom::from_slice(id.as_slice()))))),
_ => fail!("Implementation error, this should not happen."), _ => fail!("Implementation error, this should not happen."),
}, },
Some(&Delim('.')) => { Some(&Delim('.')) => {
iter.next(); iter.next();
match iter.next() { match iter.next() {
Some(Ident(class)) => SimpleSelectorResult(ClassSelector(Atom::from_slice(class.as_slice()))), Some(Ident(class)) => Ok(Some(SimpleSelectorResult(
_ => InvalidSimpleSelector, ClassSelector(Atom::from_slice(class.as_slice()))))),
_ => Err(()),
} }
} }
Some(&SquareBracketBlock(_)) => match iter.next() { Some(&SquareBracketBlock(_)) => match iter.next() {
Some(SquareBracketBlock(content)) Some(SquareBracketBlock(content))
=> match parse_attribute_selector(content, namespaces) { => Ok(Some(SimpleSelectorResult(try!(parse_attribute_selector(content, namespaces))))),
None => InvalidSimpleSelector,
Some(simple_selector) => SimpleSelectorResult(simple_selector),
},
_ => fail!("Implementation error, this should not happen."), _ => fail!("Implementation error, this should not happen."),
}, },
Some(&Colon) => { Some(&Colon) => {
iter.next(); iter.next();
match iter.next() { match iter.next() {
Some(Ident(name)) => match parse_simple_pseudo_class(name.as_slice()) { Some(Ident(name)) => match parse_simple_pseudo_class(name.as_slice()) {
None => { Err(()) => {
match name.as_slice().to_ascii_lower().as_slice() { match name.as_slice().to_ascii_lower().as_slice() {
// Supported CSS 2.1 pseudo-elements only. // Supported CSS 2.1 pseudo-elements only.
// ** Do not add to this list! ** // ** Do not add to this list! **
"before" => PseudoElementResult(Before), "before" => Ok(Some(PseudoElementResult(Before))),
"after" => PseudoElementResult(After), "after" => Ok(Some(PseudoElementResult(After))),
// "first-line" => PseudoElementResult(FirstLine), // "first-line" => PseudoElementResult(FirstLine),
// "first-letter" => PseudoElementResult(FirstLetter), // "first-letter" => PseudoElementResult(FirstLetter),
_ => InvalidSimpleSelector _ => Err(())
} }
}, },
Some(result) => SimpleSelectorResult(result), Ok(result) => Ok(Some(SimpleSelectorResult(result))),
},
Some(Function(name, arguments)) => match parse_functional_pseudo_class(
name, arguments, namespaces, inside_negation) {
None => InvalidSimpleSelector,
Some(simple_selector) => SimpleSelectorResult(simple_selector),
}, },
Some(Function(name, arguments))
=> Ok(Some(SimpleSelectorResult(try!(parse_functional_pseudo_class(
name, arguments, namespaces, inside_negation))))),
Some(Colon) => { Some(Colon) => {
match iter.next() { match iter.next() {
Some(Ident(name)) => match parse_pseudo_element(name) { Some(Ident(name))
Some(pseudo_element) => PseudoElementResult(pseudo_element), => Ok(Some(PseudoElementResult(try!(parse_pseudo_element(name))))),
_ => InvalidSimpleSelector, _ => Err(()),
},
_ => InvalidSimpleSelector,
} }
} }
_ => InvalidSimpleSelector, _ => Err(()),
} }
} }
_ => NotASimpleSelector, _ => Ok(None),
} }
} }
enum QualifiedNameParseResult { /// * `Err(())`: Invalid selector, abort
InvalidQualifiedName, /// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed.
NotAQualifiedName, /// * `Ok(Some((namespace, local_name)))`: `None` for the local name means a `*` universal selector
// Namespace URL, local name. None means '*'
QualifiedName(NamespaceConstraint, Option<String>)
}
fn parse_qualified_name(iter: &mut Iter, in_attr_selector: bool, namespaces: &NamespaceMap) fn parse_qualified_name(iter: &mut Iter, in_attr_selector: bool, namespaces: &NamespaceMap)
-> QualifiedNameParseResult { -> Result<Option<(NamespaceConstraint, Option<String>)>, ()> {
#[inline] #[inline]
fn default_namespace(namespaces: &NamespaceMap, local_name: Option<String>) fn default_namespace(namespaces: &NamespaceMap, local_name: Option<String>)
-> QualifiedNameParseResult { -> Result<Option<(NamespaceConstraint, Option<String>)>, ()> {
QualifiedName(match namespaces.default { let namespace = match namespaces.default {
Some(ref ns) => SpecificNamespace(ns.clone()), Some(ref ns) => SpecificNamespace(ns.clone()),
None => AnyNamespace, None => AnyNamespace,
}, local_name) };
Ok(Some((namespace, local_name)))
} }
#[inline] #[inline]
fn explicit_namespace(iter: &mut Iter, in_attr_selector: bool, namespace: NamespaceConstraint) fn explicit_namespace(iter: &mut Iter, in_attr_selector: bool, namespace: NamespaceConstraint)
-> QualifiedNameParseResult { -> Result<Option<(NamespaceConstraint, Option<String>)>, ()> {
assert!(iter.next() == Some(Delim('|')), assert!(iter.next() == Some(Delim('|')),
"Implementation error, this should not happen."); "Implementation error, this should not happen.");
match iter.peek() { match iter.peek() {
Some(&Delim('*')) if !in_attr_selector => { Some(&Delim('*')) if !in_attr_selector => {
iter.next(); iter.next();
QualifiedName(namespace, None) Ok(Some((namespace, None)))
}, },
Some(&Ident(_)) => { Some(&Ident(_)) => {
let local_name = get_next_ident(iter); let local_name = get_next_ident(iter);
QualifiedName(namespace, Some(local_name)) Ok(Some((namespace, Some(local_name))))
}, },
_ => InvalidQualifiedName, _ => Err(()),
} }
} }
@ -411,13 +384,13 @@ fn parse_qualified_name(iter: &mut Iter, in_attr_selector: bool, namespaces: &Na
match iter.peek() { match iter.peek() {
Some(&Delim('|')) => { Some(&Delim('|')) => {
let namespace = match namespaces.prefix_map.find(&value) { let namespace = match namespaces.prefix_map.find(&value) {
None => return InvalidQualifiedName, // Undeclared namespace prefix None => return Err(()), // Undeclared namespace prefix
Some(ref ns) => (*ns).clone(), Some(ref ns) => (*ns).clone(),
}; };
explicit_namespace(iter, in_attr_selector, SpecificNamespace(namespace)) explicit_namespace(iter, in_attr_selector, SpecificNamespace(namespace))
}, },
_ if in_attr_selector => QualifiedName( _ if in_attr_selector => Ok(Some(
SpecificNamespace(namespace::Null), Some(value)), (SpecificNamespace(namespace::Null), Some(value)))),
_ => default_namespace(namespaces, Some(value)), _ => default_namespace(namespaces, Some(value)),
} }
}, },
@ -427,24 +400,24 @@ fn parse_qualified_name(iter: &mut Iter, in_attr_selector: bool, namespaces: &Na
Some(&Delim('|')) => explicit_namespace(iter, in_attr_selector, AnyNamespace), Some(&Delim('|')) => explicit_namespace(iter, in_attr_selector, AnyNamespace),
_ => { _ => {
if !in_attr_selector { default_namespace(namespaces, None) } if !in_attr_selector { default_namespace(namespaces, None) }
else { InvalidQualifiedName } else { Err(()) }
}, },
} }
}, },
Some(&Delim('|')) => explicit_namespace( Some(&Delim('|')) => explicit_namespace(
iter, in_attr_selector, SpecificNamespace(namespace::Null)), iter, in_attr_selector, SpecificNamespace(namespace::Null)),
_ => NotAQualifiedName, _ => Ok(None),
} }
} }
fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &NamespaceMap) fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &NamespaceMap)
-> Option<SimpleSelector> { -> Result<SimpleSelector, ()> {
let iter = &mut content.move_iter().peekable(); let iter = &mut content.move_iter().peekable();
let attr = match parse_qualified_name(iter, /* in_attr_selector = */ true, namespaces) { let attr = match try!(parse_qualified_name(iter, /* in_attr_selector = */ true, namespaces)) {
InvalidQualifiedName | NotAQualifiedName => return None, None => return Err(()),
QualifiedName(_, None) => fail!("Implementation error, this should not happen."), Some((_, None)) => fail!("Implementation error, this should not happen."),
QualifiedName(namespace, Some(local_name)) => AttrSelector { Some((namespace, Some(local_name))) => AttrSelector {
namespace: namespace, namespace: namespace,
lower_name: local_name.as_slice().to_ascii_lower(), lower_name: local_name.as_slice().to_ascii_lower(),
name: local_name, name: local_name,
@ -456,7 +429,7 @@ fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &Namespace
skip_whitespace(iter); skip_whitespace(iter);
match iter.next() { match iter.next() {
Some(Ident(value)) | Some(String(value)) => value, Some(Ident(value)) | Some(String(value)) => value,
_ => return None, _ => return Err(())
} }
}};) }};)
let result = match iter.next() { let result = match iter.next() {
@ -471,87 +444,86 @@ fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &Namespace
Some(PrefixMatch) => AttrPrefixMatch(attr, (get_value!())), // [foo^=bar] Some(PrefixMatch) => AttrPrefixMatch(attr, (get_value!())), // [foo^=bar]
Some(SubstringMatch) => AttrSubstringMatch(attr, (get_value!())), // [foo*=bar] Some(SubstringMatch) => AttrSubstringMatch(attr, (get_value!())), // [foo*=bar]
Some(SuffixMatch) => AttrSuffixMatch(attr, (get_value!())), // [foo$=bar] Some(SuffixMatch) => AttrSuffixMatch(attr, (get_value!())), // [foo$=bar]
_ => return None _ => return Err(())
}; };
skip_whitespace(iter); skip_whitespace(iter);
if iter.next().is_none() { Some(result) } else { None } if iter.next().is_none() { Ok(result) } else { Err(()) }
} }
fn parse_simple_pseudo_class(name: &str) -> Option<SimpleSelector> { fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> {
match name.to_ascii_lower().as_slice() { match name.to_ascii_lower().as_slice() {
"any-link" => Some(AnyLink), "any-link" => Ok(AnyLink),
"link" => Some(Link), "link" => Ok(Link),
"visited" => Some(Visited), "visited" => Ok(Visited),
"hover" => Some(Hover), "hover" => Ok(Hover),
"disabled" => Some(Disabled), "disabled" => Ok(Disabled),
"enabled" => Some(Enabled), "enabled" => Ok(Enabled),
"first-child" => Some(FirstChild), "first-child" => Ok(FirstChild),
"last-child" => Some(LastChild), "last-child" => Ok(LastChild),
"only-child" => Some(OnlyChild), "only-child" => Ok(OnlyChild),
"root" => Some(Root), "root" => Ok(Root),
"first-of-type" => Some(FirstOfType), "first-of-type" => Ok(FirstOfType),
"last-of-type" => Some(LastOfType), "last-of-type" => Ok(LastOfType),
"only-of-type" => Some(OnlyOfType), "only-of-type" => Ok(OnlyOfType),
// "empty" => Some(Empty), // "empty" => Ok(Empty),
_ => None _ => Err(())
} }
} }
fn parse_functional_pseudo_class(name: String, arguments: Vec<ComponentValue>, fn parse_functional_pseudo_class(name: String, arguments: Vec<ComponentValue>,
namespaces: &NamespaceMap, inside_negation: bool) namespaces: &NamespaceMap, inside_negation: bool)
-> Option<SimpleSelector> { -> Result<SimpleSelector, ()> {
match name.as_slice().to_ascii_lower().as_slice() { match name.as_slice().to_ascii_lower().as_slice() {
// "lang" => parse_lang(arguments), // "lang" => parse_lang(arguments),
"nth-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthChild(a, b)).ok(), "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)).ok(), "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)).ok(), "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)).ok(), "nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastOfType(a, b)),
"not" => if inside_negation { None } else { parse_negation(arguments, namespaces) }, "not" => if inside_negation { Err(()) } else { parse_negation(arguments, namespaces) },
_ => None _ => Err(())
} }
} }
fn parse_pseudo_element(name: String) -> Option<PseudoElement> { fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> {
match name.as_slice().to_ascii_lower().as_slice() { match name.as_slice().to_ascii_lower().as_slice() {
// All supported pseudo-elements // All supported pseudo-elements
"before" => Some(Before), "before" => Ok(Before),
"after" => Some(After), "after" => Ok(After),
// "first-line" => Some(FirstLine), // "first-line" => Some(FirstLine),
// "first-letter" => Some(FirstLetter), // "first-letter" => Some(FirstLetter),
_ => None _ => Err(())
} }
} }
//fn parse_lang(arguments: vec!(ComponentValue)) -> Option<SimpleSelector> { //fn parse_lang(arguments: vec!(ComponentValue)) -> Result<SimpleSelector, ()> {
// let mut iter = arguments.move_skip_whitespace(); // let mut iter = arguments.move_skip_whitespace();
// match iter.next() { // match iter.next() {
// Some(Ident(value)) => { // Some(Ident(value)) => {
// if "" == value || iter.next().is_some() { None } // if "" == value || iter.next().is_some() { None }
// else { Some(Lang(value)) } // else { Ok(Lang(value)) }
// }, // },
// _ => None, // _ => Err(()),
// } // }
//} //}
// Level 3: Parse ONE simple_selector /// Level 3: Parse **one** simple_selector
fn parse_negation(arguments: Vec<ComponentValue>, namespaces: &NamespaceMap) fn parse_negation(arguments: Vec<ComponentValue>, namespaces: &NamespaceMap)
-> Option<SimpleSelector> { -> Result<SimpleSelector, ()> {
let iter = &mut arguments.move_iter().peekable(); let iter = &mut arguments.move_iter().peekable();
Some(Negation(match parse_type_selector(iter, namespaces) { match try!(parse_type_selector(iter, namespaces)) {
InvalidTypeSelector => return None, Some(type_selector) => Ok(Negation(type_selector)),
TypeSelector(s) => s, None => {
NotATypeSelector => { match try!(parse_one_simple_selector(iter, namespaces, /* inside_negation = */ true)) {
match parse_one_simple_selector(iter, namespaces, /* inside_negation = */ true) { Some(SimpleSelectorResult(simple_selector)) => Ok(Negation(vec![simple_selector])),
SimpleSelectorResult(s) => vec!(s), _ => Err(())
_ => return None
} }
}, },
})) }
} }
@ -585,11 +557,11 @@ mod tests {
use namespaces::NamespaceMap; use namespaces::NamespaceMap;
use super::*; use super::*;
fn parse(input: &str) -> Option<Vec<Selector>> { fn parse(input: &str) -> Result<Vec<Selector>, ()> {
parse_ns(input, &NamespaceMap::new()) parse_ns(input, &NamespaceMap::new())
} }
fn parse_ns(input: &str, namespaces: &NamespaceMap) -> Option<Vec<Selector>> { fn parse_ns(input: &str, namespaces: &NamespaceMap) -> Result<Vec<Selector>, ()> {
parse_selector_list( parse_selector_list(
cssparser::tokenize(input).map(|(v, _)| v).collect(), cssparser::tokenize(input).map(|(v, _)| v).collect(),
namespaces) namespaces)
@ -601,8 +573,8 @@ mod tests {
#[test] #[test]
fn test_parsing() { fn test_parsing() {
assert!(parse("") == None) assert!(parse("") == Err(()))
assert!(parse("e") == Some(vec!(Selector{ assert!(parse("e") == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(LocalNameSelector(Atom::from_slice("e"))), simple_selectors: vec!(LocalNameSelector(Atom::from_slice("e"))),
next: None, next: None,
@ -610,7 +582,7 @@ mod tests {
pseudo_element: None, pseudo_element: None,
specificity: specificity(0, 0, 1), specificity: specificity(0, 0, 1),
}))) })))
assert!(parse(".foo") == Some(vec!(Selector{ assert!(parse(".foo") == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(ClassSelector(Atom::from_slice("foo"))), simple_selectors: vec!(ClassSelector(Atom::from_slice("foo"))),
next: None, next: None,
@ -618,7 +590,7 @@ mod tests {
pseudo_element: None, pseudo_element: None,
specificity: specificity(0, 1, 0), specificity: specificity(0, 1, 0),
}))) })))
assert!(parse("#bar") == Some(vec!(Selector{ assert!(parse("#bar") == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(IDSelector(Atom::from_slice("bar"))), simple_selectors: vec!(IDSelector(Atom::from_slice("bar"))),
next: None, next: None,
@ -626,7 +598,7 @@ mod tests {
pseudo_element: None, pseudo_element: None,
specificity: specificity(1, 0, 0), specificity: specificity(1, 0, 0),
}))) })))
assert!(parse("e.foo#bar") == Some(vec!(Selector{ assert!(parse("e.foo#bar") == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(LocalNameSelector(Atom::from_slice("e")), simple_selectors: vec!(LocalNameSelector(Atom::from_slice("e")),
ClassSelector(Atom::from_slice("foo")), ClassSelector(Atom::from_slice("foo")),
@ -636,7 +608,7 @@ mod tests {
pseudo_element: None, pseudo_element: None,
specificity: specificity(1, 1, 1), specificity: specificity(1, 1, 1),
}))) })))
assert!(parse("e.foo #bar") == Some(vec!(Selector{ assert!(parse("e.foo #bar") == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(IDSelector(Atom::from_slice("bar"))), simple_selectors: vec!(IDSelector(Atom::from_slice("bar"))),
next: Some((box CompoundSelector { next: Some((box CompoundSelector {
@ -651,7 +623,7 @@ mod tests {
// Default namespace does not apply to attribute selectors // Default namespace does not apply to attribute selectors
// https://github.com/mozilla/servo/pull/1652 // https://github.com/mozilla/servo/pull/1652
let mut namespaces = NamespaceMap::new(); let mut namespaces = NamespaceMap::new();
assert!(parse_ns("[Foo]", &namespaces) == Some(vec!(Selector{ assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(AttrExists(AttrSelector { simple_selectors: vec!(AttrExists(AttrSelector {
name: "Foo".to_string(), name: "Foo".to_string(),
@ -666,7 +638,7 @@ mod tests {
// Default namespace does not apply to attribute selectors // Default namespace does not apply to attribute selectors
// https://github.com/mozilla/servo/pull/1652 // https://github.com/mozilla/servo/pull/1652
namespaces.default = Some(namespace::MathML); namespaces.default = Some(namespace::MathML);
assert!(parse_ns("[Foo]", &namespaces) == Some(vec!(Selector{ assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(AttrExists(AttrSelector { simple_selectors: vec!(AttrExists(AttrSelector {
name: "Foo".to_string(), name: "Foo".to_string(),
@ -679,7 +651,7 @@ mod tests {
specificity: specificity(0, 1, 0), specificity: specificity(0, 1, 0),
}))) })))
// Default namespace does apply to type selectors // Default namespace does apply to type selectors
assert!(parse_ns("e", &namespaces) == Some(vec!(Selector{ assert!(parse_ns("e", &namespaces) == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!( simple_selectors: vec!(
NamespaceSelector(namespace::MathML), NamespaceSelector(namespace::MathML),
@ -691,7 +663,7 @@ mod tests {
specificity: specificity(0, 0, 1), specificity: specificity(0, 0, 1),
}))) })))
// https://github.com/mozilla/servo/issues/1723 // https://github.com/mozilla/servo/issues/1723
assert!(parse("::before") == Some(vec!(Selector{ assert!(parse("::before") == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(), simple_selectors: vec!(),
next: None, next: None,
@ -699,7 +671,7 @@ mod tests {
pseudo_element: Some(Before), pseudo_element: Some(Before),
specificity: specificity(0, 0, 1), specificity: specificity(0, 0, 1),
}))) })))
assert!(parse("div :after") == Some(vec!(Selector{ assert!(parse("div :after") == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(), simple_selectors: vec!(),
next: Some((box CompoundSelector { next: Some((box CompoundSelector {

View file

@ -130,11 +130,11 @@ pub fn parse_style_rule(rule: QualifiedRule, parent_rules: &mut Vec<CSSRule>,
// FIXME: avoid doing this for valid selectors // FIXME: avoid doing this for valid selectors
let serialized = prelude.iter().to_css(); let serialized = prelude.iter().to_css();
match selectors::parse_selector_list(prelude, namespaces) { match selectors::parse_selector_list(prelude, namespaces) {
Some(selectors) => parent_rules.push(CSSStyleRule(StyleRule{ Ok(selectors) => parent_rules.push(CSSStyleRule(StyleRule{
selectors: selectors, selectors: selectors,
declarations: properties::parse_property_declaration_list(block.move_iter(), base_url) declarations: properties::parse_property_declaration_list(block.move_iter(), base_url)
})), })),
None => log_css_error(location, format!( Err(()) => log_css_error(location, format!(
"Invalid/unsupported selector: {}", serialized).as_slice()), "Invalid/unsupported selector: {}", serialized).as_slice()),
} }
} }