diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs index 7195d63f14c..99a8f086915 100644 --- a/components/selectors/builder.rs +++ b/components/selectors/builder.rs @@ -90,6 +90,12 @@ impl SelectorBuilder { 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. #[inline(always)] pub fn build(&mut self, parsed_pseudo: bool) -> ThinArc> { diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 08ad801781d..ac5d184cb9b 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -54,16 +54,19 @@ pub enum SelectorParseError<'i, T> { NegationSelectorComponentNotNamespace, NegationSelectorComponentNotLocalName, EmptySelector, + DanglingCombinator, NonSimpleSelectorInNegation, UnexpectedTokenInAttributeSelector(Token<'i>), - PseudoElementExpectedColon, - PseudoElementExpectedIdent, + PseudoElementExpectedColon(Token<'i>), + PseudoElementExpectedIdent(Token<'i>), + NoIdentForPseudo(Token<'i>), UnsupportedPseudoClassOrElement(CowRcStr<'i>), UnexpectedIdent(CowRcStr<'i>), ExpectedNamespace(CowRcStr<'i>), ExpectedBarInAttr(Token<'i>), BadValueInAttr(Token<'i>), InvalidQualNameInAttr(Token<'i>), + ExplicitNamespaceUnexpectedToken(Token<'i>), Custom(T), } @@ -1044,7 +1047,12 @@ fn parse_selector<'i, 't, P, E, Impl>( let mut parsed_pseudo_element; 'outer_loop: loop { // 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 { break; } @@ -1109,9 +1117,10 @@ fn parse_type_selector<'i, 't, P, E, Impl, S>(parser: &P, input: &mut CssParser< Impl: SelectorImpl, S: Push>, { - match parse_qualified_name(parser, input, /* in_attr_selector = */ false)? { - OptionalQName::None(_) => Ok(false), - OptionalQName::Some(namespace, local_name) => { + match parse_qualified_name(parser, input, /* in_attr_selector = */ false) { + Err(ParseError::Basic(BasicParseError::EndOfInput)) | + Ok(OptionalQName::None(_)) => Ok(false), + Ok(OptionalQName::Some(namespace, local_name)) => { match namespace { QNamePrefix::ImplicitAnyNamespace => {} QNamePrefix::ImplicitDefaultNamespace(url) => { @@ -1160,6 +1169,7 @@ fn parse_type_selector<'i, 't, P, E, Impl, S>(parser: &P, input: &mut CssParser< } 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(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)), } }; @@ -1499,14 +1509,15 @@ fn parse_compound_selector<'i, 't, P, E, Impl>( match input.next_including_whitespace() { Ok(&Token::Colon) => {}, 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? // We don't need it for now. - let name = match input.next_including_whitespace() { - Ok(&Token::Ident(ref name)) => name.clone(), - _ => return Err(SelectorParseError::PseudoElementExpectedIdent.into()), + let name = match input.next_including_whitespace()? { + &Token::Ident(ref name) => name.clone(), + t => return Err(SelectorParseError::NoIdentForPseudo(t.clone()).into()), }; 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 { Token::Ident(name) => (name, false), 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 || P::is_pseudo_element_allows_single_colon(&name); diff --git a/ports/geckolib/error_reporter.rs b/ports/geckolib/error_reporter.rs index 0518750271b..1f2c683aca3 100644 --- a/ports/geckolib/error_reporter.rs +++ b/ports/geckolib/error_reporter.rs @@ -161,7 +161,7 @@ fn token_to_str<'a>(t: Token<'a>) -> String { format!("{}{}", i, escape_css_ident(&*unit)), Token::Dimension { value, ref unit, .. } => format!("{}{}", value, escape_css_ident(&*unit)), - Token::WhiteSpace(_) => "whitespace".into(), + Token::WhiteSpace(s) => s.into(), Token::Comment(_) => "comment".into(), Token::Colon => ":".into(), Token::Semicolon => ";".into(), @@ -251,7 +251,11 @@ fn extract_error_params<'a>(err: ParseError<'a>) -> Option> { CssParseError::Custom(SelectorParseError::BadValueInAttr(t)) | CssParseError::Custom(SelectorParseError::ExpectedBarInAttr(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))), CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) => @@ -260,6 +264,10 @@ fn extract_error_params<'a>(err: ParseError<'a>) -> Option> { CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(p)) => (None, Some(ErrorString::Ident(p))), + CssParseError::Custom(SelectorParseError::EmptySelector) | + CssParseError::Custom(SelectorParseError::DanglingCombinator) => + (None, None), + err => match extract_error_param(err) { Some(e) => (Some(e), None), None => return None, @@ -344,10 +352,22 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> { Some(&b"PEAttributeNameOrNamespaceExpected\0"[..]), CssParseError::Custom(SelectorParseError::InvalidQualNameInAttr(_)) => Some(&b"PEAttributeNameExpected\0"[..]), + CssParseError::Custom(SelectorParseError::ExplicitNamespaceUnexpectedToken(_)) => + Some(&b"PETypeSelNotType\0"[..]), CssParseError::Custom(SelectorParseError::ExpectedNamespace(_)) => Some(&b"PEUnknownNamespacePrefix\0"[..]), + CssParseError::Custom(SelectorParseError::EmptySelector) => + Some(&b"PESelectorGroupNoSelector\0"[..]), + CssParseError::Custom(SelectorParseError::DanglingCombinator) => + Some(&b"PESelectorGroupExtraCombinator\0"[..]), CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(_)) => 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, }; return (prefix, b"PEBadSelectorRSIgnored\0", Action::Nothing);