Report invalid selectors (bug 1384216).

This commit is contained in:
Josh Matthews 2017-08-28 17:21:07 -07:00
parent 408c34a76d
commit 9a7cceb0a1
3 changed files with 51 additions and 14 deletions

View file

@ -90,6 +90,12 @@ impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
self.simple_selectors.is_empty() self.simple_selectors.is_empty()
} }
/// Returns true if combinators have ever been pushed to this builder.
#[inline(always)]
pub fn has_combinators(&self) -> bool {
!self.combinators.is_empty()
}
/// Consumes the builder, producing a Selector. /// Consumes the builder, producing a Selector.
#[inline(always)] #[inline(always)]
pub fn build(&mut self, parsed_pseudo: bool) -> ThinArc<SpecificityAndFlags, Component<Impl>> { pub fn build(&mut self, parsed_pseudo: bool) -> ThinArc<SpecificityAndFlags, Component<Impl>> {

View file

@ -54,16 +54,19 @@ pub enum SelectorParseError<'i, T> {
NegationSelectorComponentNotNamespace, NegationSelectorComponentNotNamespace,
NegationSelectorComponentNotLocalName, NegationSelectorComponentNotLocalName,
EmptySelector, EmptySelector,
DanglingCombinator,
NonSimpleSelectorInNegation, NonSimpleSelectorInNegation,
UnexpectedTokenInAttributeSelector(Token<'i>), UnexpectedTokenInAttributeSelector(Token<'i>),
PseudoElementExpectedColon, PseudoElementExpectedColon(Token<'i>),
PseudoElementExpectedIdent, PseudoElementExpectedIdent(Token<'i>),
NoIdentForPseudo(Token<'i>),
UnsupportedPseudoClassOrElement(CowRcStr<'i>), UnsupportedPseudoClassOrElement(CowRcStr<'i>),
UnexpectedIdent(CowRcStr<'i>), UnexpectedIdent(CowRcStr<'i>),
ExpectedNamespace(CowRcStr<'i>), ExpectedNamespace(CowRcStr<'i>),
ExpectedBarInAttr(Token<'i>), ExpectedBarInAttr(Token<'i>),
BadValueInAttr(Token<'i>), BadValueInAttr(Token<'i>),
InvalidQualNameInAttr(Token<'i>), InvalidQualNameInAttr(Token<'i>),
ExplicitNamespaceUnexpectedToken(Token<'i>),
Custom(T), Custom(T),
} }
@ -1044,7 +1047,12 @@ fn parse_selector<'i, 't, P, E, Impl>(
let mut parsed_pseudo_element; let mut parsed_pseudo_element;
'outer_loop: loop { 'outer_loop: loop {
// Parse a sequence of simple selectors. // Parse a sequence of simple selectors.
parsed_pseudo_element = parse_compound_selector(parser, input, &mut builder)?; parsed_pseudo_element = match parse_compound_selector(parser, input, &mut builder) {
Ok(result) => result,
Err(ParseError::Custom(SelectorParseError::EmptySelector)) if builder.has_combinators() =>
return Err(SelectorParseError::DanglingCombinator.into()),
Err(e) => return Err(e),
};
if parsed_pseudo_element { if parsed_pseudo_element {
break; break;
} }
@ -1109,9 +1117,10 @@ fn parse_type_selector<'i, 't, P, E, Impl, S>(parser: &P, input: &mut CssParser<
Impl: SelectorImpl, Impl: SelectorImpl,
S: Push<Component<Impl>>, S: Push<Component<Impl>>,
{ {
match parse_qualified_name(parser, input, /* in_attr_selector = */ false)? { match parse_qualified_name(parser, input, /* in_attr_selector = */ false) {
OptionalQName::None(_) => Ok(false), Err(ParseError::Basic(BasicParseError::EndOfInput)) |
OptionalQName::Some(namespace, local_name) => { Ok(OptionalQName::None(_)) => Ok(false),
Ok(OptionalQName::Some(namespace, local_name)) => {
match namespace { match namespace {
QNamePrefix::ImplicitAnyNamespace => {} QNamePrefix::ImplicitAnyNamespace => {}
QNamePrefix::ImplicitDefaultNamespace(url) => { QNamePrefix::ImplicitDefaultNamespace(url) => {
@ -1160,6 +1169,7 @@ fn parse_type_selector<'i, 't, P, E, Impl, S>(parser: &P, input: &mut CssParser<
} }
Ok(true) Ok(true)
} }
Err(e) => Err(e)
} }
} }
@ -1212,7 +1222,7 @@ fn parse_qualified_name<'i, 't, P, E, Impl>
Ok(OptionalQName::Some(namespace, Some(local_name.clone()))) Ok(OptionalQName::Some(namespace, Some(local_name.clone())))
}, },
Ok(t) if in_attr_selector => Err(SelectorParseError::InvalidQualNameInAttr(t.clone()).into()), Ok(t) if in_attr_selector => Err(SelectorParseError::InvalidQualNameInAttr(t.clone()).into()),
Ok(t) => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t.clone()))), Ok(t) => Err(SelectorParseError::ExplicitNamespaceUnexpectedToken(t.clone()).into()),
Err(e) => Err(ParseError::Basic(e)), Err(e) => Err(ParseError::Basic(e)),
} }
}; };
@ -1499,14 +1509,15 @@ fn parse_compound_selector<'i, 't, P, E, Impl>(
match input.next_including_whitespace() { match input.next_including_whitespace() {
Ok(&Token::Colon) => {}, Ok(&Token::Colon) => {},
Ok(&Token::WhiteSpace(_)) | Err(_) => break, Ok(&Token::WhiteSpace(_)) | Err(_) => break,
_ => return Err(SelectorParseError::PseudoElementExpectedColon.into()), Ok(t) =>
return Err(SelectorParseError::PseudoElementExpectedColon(t.clone()).into()),
} }
// TODO(emilio): Functional pseudo-classes too? // TODO(emilio): Functional pseudo-classes too?
// We don't need it for now. // We don't need it for now.
let name = match input.next_including_whitespace() { let name = match input.next_including_whitespace()? {
Ok(&Token::Ident(ref name)) => name.clone(), &Token::Ident(ref name) => name.clone(),
_ => return Err(SelectorParseError::PseudoElementExpectedIdent.into()), t => return Err(SelectorParseError::NoIdentForPseudo(t.clone()).into()),
}; };
let pseudo_class = let pseudo_class =
@ -1626,7 +1637,7 @@ fn parse_one_simple_selector<'i, 't, P, E, Impl>(parser: &P,
let (name, is_functional) = match next_token { let (name, is_functional) = match next_token {
Token::Ident(name) => (name, false), Token::Ident(name) => (name, false),
Token::Function(name) => (name, true), Token::Function(name) => (name, true),
t => return Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))), t => return Err(SelectorParseError::PseudoElementExpectedIdent(t).into()),
}; };
let is_pseudo_element = !is_single_colon || let is_pseudo_element = !is_single_colon ||
P::is_pseudo_element_allows_single_colon(&name); P::is_pseudo_element_allows_single_colon(&name);

View file

@ -161,7 +161,7 @@ fn token_to_str<'a>(t: Token<'a>) -> String {
format!("{}{}", i, escape_css_ident(&*unit)), format!("{}{}", i, escape_css_ident(&*unit)),
Token::Dimension { value, ref unit, .. } => Token::Dimension { value, ref unit, .. } =>
format!("{}{}", value, escape_css_ident(&*unit)), format!("{}{}", value, escape_css_ident(&*unit)),
Token::WhiteSpace(_) => "whitespace".into(), Token::WhiteSpace(s) => s.into(),
Token::Comment(_) => "comment".into(), Token::Comment(_) => "comment".into(),
Token::Colon => ":".into(), Token::Colon => ":".into(),
Token::Semicolon => ";".into(), Token::Semicolon => ";".into(),
@ -251,7 +251,11 @@ fn extract_error_params<'a>(err: ParseError<'a>) -> Option<ErrorParams<'a>> {
CssParseError::Custom(SelectorParseError::BadValueInAttr(t)) | CssParseError::Custom(SelectorParseError::BadValueInAttr(t)) |
CssParseError::Custom(SelectorParseError::ExpectedBarInAttr(t)) | CssParseError::Custom(SelectorParseError::ExpectedBarInAttr(t)) |
CssParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(t)) | CssParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(t)) |
CssParseError::Custom(SelectorParseError::InvalidQualNameInAttr(t)) => CssParseError::Custom(SelectorParseError::InvalidQualNameInAttr(t)) |
CssParseError::Custom(SelectorParseError::ExplicitNamespaceUnexpectedToken(t)) |
CssParseError::Custom(SelectorParseError::PseudoElementExpectedIdent(t)) |
CssParseError::Custom(SelectorParseError::NoIdentForPseudo(t)) |
CssParseError::Custom(SelectorParseError::PseudoElementExpectedColon(t)) =>
(None, Some(ErrorString::UnexpectedToken(t))), (None, Some(ErrorString::UnexpectedToken(t))),
CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) => CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) =>
@ -260,6 +264,10 @@ fn extract_error_params<'a>(err: ParseError<'a>) -> Option<ErrorParams<'a>> {
CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(p)) => CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(p)) =>
(None, Some(ErrorString::Ident(p))), (None, Some(ErrorString::Ident(p))),
CssParseError::Custom(SelectorParseError::EmptySelector) |
CssParseError::Custom(SelectorParseError::DanglingCombinator) =>
(None, None),
err => match extract_error_param(err) { err => match extract_error_param(err) {
Some(e) => (Some(e), None), Some(e) => (Some(e), None),
None => return None, None => return None,
@ -344,10 +352,22 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
Some(&b"PEAttributeNameOrNamespaceExpected\0"[..]), Some(&b"PEAttributeNameOrNamespaceExpected\0"[..]),
CssParseError::Custom(SelectorParseError::InvalidQualNameInAttr(_)) => CssParseError::Custom(SelectorParseError::InvalidQualNameInAttr(_)) =>
Some(&b"PEAttributeNameExpected\0"[..]), Some(&b"PEAttributeNameExpected\0"[..]),
CssParseError::Custom(SelectorParseError::ExplicitNamespaceUnexpectedToken(_)) =>
Some(&b"PETypeSelNotType\0"[..]),
CssParseError::Custom(SelectorParseError::ExpectedNamespace(_)) => CssParseError::Custom(SelectorParseError::ExpectedNamespace(_)) =>
Some(&b"PEUnknownNamespacePrefix\0"[..]), Some(&b"PEUnknownNamespacePrefix\0"[..]),
CssParseError::Custom(SelectorParseError::EmptySelector) =>
Some(&b"PESelectorGroupNoSelector\0"[..]),
CssParseError::Custom(SelectorParseError::DanglingCombinator) =>
Some(&b"PESelectorGroupExtraCombinator\0"[..]),
CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(_)) => CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(_)) =>
Some(&b"PEPseudoSelUnknown\0"[..]), Some(&b"PEPseudoSelUnknown\0"[..]),
CssParseError::Custom(SelectorParseError::PseudoElementExpectedColon(_)) =>
Some(&b"PEPseudoSelEndOrUserActionPC\0"[..]),
CssParseError::Custom(SelectorParseError::NoIdentForPseudo(_)) =>
Some(&b"PEPseudoClassArgNotIdent\0"[..]),
CssParseError::Custom(SelectorParseError::PseudoElementExpectedIdent(_)) =>
Some(&b"PEPseudoSelBadName\0"[..]),
_ => None, _ => None,
}; };
return (prefix, b"PEBadSelectorRSIgnored\0", Action::Nothing); return (prefix, b"PEBadSelectorRSIgnored\0", Action::Nothing);