mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #18290 - jdm:selectorerr, r=heycam
Report more specific CSS selector errors Report more specific errors for invalid CSS selectors. Reviewed by @heycam in https://bugzilla.mozilla.org/show_bug.cgi?id=1384216. --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] There are tests for these changes <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18290) <!-- Reviewable:end -->
This commit is contained in:
commit
b2f8974ab8
4 changed files with 211 additions and 103 deletions
|
@ -90,6 +90,12 @@ impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
|
|||
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<SpecificityAndFlags, Component<Impl>> {
|
||||
|
|
|
@ -49,18 +49,23 @@ fn to_ascii_lowercase(s: &str) -> Cow<str> {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum SelectorParseError<'i, T> {
|
||||
PseudoElementInComplexSelector,
|
||||
NoQualifiedNameInAttributeSelector,
|
||||
TooManyCompoundSelectorComponentsInNegation,
|
||||
NegationSelectorComponentNotNamespace,
|
||||
NegationSelectorComponentNotLocalName,
|
||||
NoQualifiedNameInAttributeSelector(Token<'i>),
|
||||
EmptySelector,
|
||||
DanglingCombinator,
|
||||
NonSimpleSelectorInNegation,
|
||||
UnexpectedTokenInAttributeSelector,
|
||||
PseudoElementExpectedColon,
|
||||
PseudoElementExpectedIdent,
|
||||
UnsupportedPseudoClass,
|
||||
UnexpectedTokenInAttributeSelector(Token<'i>),
|
||||
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>),
|
||||
ClassNeedsIdent(Token<'i>),
|
||||
EmptyNegation,
|
||||
Custom(T),
|
||||
}
|
||||
|
||||
|
@ -136,7 +141,7 @@ pub trait Parser<'i> {
|
|||
fn parse_non_ts_pseudo_class(&self, name: CowRcStr<'i>)
|
||||
-> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass,
|
||||
ParseError<'i, SelectorParseError<'i, Self::Error>>> {
|
||||
Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name)))
|
||||
Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
|
||||
}
|
||||
|
||||
fn parse_non_ts_functional_pseudo_class<'t>
|
||||
|
@ -144,20 +149,20 @@ pub trait Parser<'i> {
|
|||
-> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass,
|
||||
ParseError<'i, SelectorParseError<'i, Self::Error>>>
|
||||
{
|
||||
Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name)))
|
||||
Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
|
||||
}
|
||||
|
||||
fn parse_pseudo_element(&self, name: CowRcStr<'i>)
|
||||
-> Result<<Self::Impl as SelectorImpl>::PseudoElement,
|
||||
ParseError<'i, SelectorParseError<'i, Self::Error>>> {
|
||||
Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name)))
|
||||
Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
|
||||
}
|
||||
|
||||
fn parse_functional_pseudo_element<'t>
|
||||
(&self, name: CowRcStr<'i>, _arguments: &mut CssParser<'i, 't>)
|
||||
-> Result<<Self::Impl as SelectorImpl>::PseudoElement,
|
||||
ParseError<'i, SelectorParseError<'i, Self::Error>>> {
|
||||
Err(ParseError::Custom(SelectorParseError::UnexpectedIdent(name)))
|
||||
Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
|
||||
}
|
||||
|
||||
fn default_namespace(&self) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
|
||||
|
@ -1041,7 +1046,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;
|
||||
}
|
||||
|
@ -1106,9 +1116,10 @@ fn parse_type_selector<'i, 't, P, E, Impl, S>(parser: &P, input: &mut CssParser<
|
|||
Impl: SelectorImpl,
|
||||
S: Push<Component<Impl>>,
|
||||
{
|
||||
match parse_qualified_name(parser, input, /* in_attr_selector = */ false)? {
|
||||
None => Ok(false),
|
||||
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) => {
|
||||
|
@ -1157,6 +1168,7 @@ fn parse_type_selector<'i, 't, P, E, Impl, S>(parser: &P, input: &mut CssParser<
|
|||
}
|
||||
Ok(true)
|
||||
}
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1176,13 +1188,19 @@ enum QNamePrefix<Impl: SelectorImpl> {
|
|||
ExplicitNamespace(Impl::NamespacePrefix, Impl::NamespaceUrl), // `prefix|foo`
|
||||
}
|
||||
|
||||
enum OptionalQName<'i, Impl: SelectorImpl> {
|
||||
Some(QNamePrefix<Impl>, Option<CowRcStr<'i>>),
|
||||
None(Token<'i>),
|
||||
}
|
||||
|
||||
/// * `Err(())`: Invalid selector, abort
|
||||
/// * `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
|
||||
/// * `Ok(None(token))`: Not a simple selector, could be something else. `input` was not consumed,
|
||||
/// but the token is still returned.
|
||||
/// * `Ok(Some(namespace, local_name))`: `None` for the local name means a `*` universal selector
|
||||
fn parse_qualified_name<'i, 't, P, E, Impl>
|
||||
(parser: &P, input: &mut CssParser<'i, 't>,
|
||||
in_attr_selector: bool)
|
||||
-> Result<Option<(QNamePrefix<Impl>, Option<CowRcStr<'i>>)>,
|
||||
-> Result<OptionalQName<'i, Impl>,
|
||||
ParseError<'i, SelectorParseError<'i, E>>>
|
||||
where P: Parser<'i, Impl=Impl, Error=E>, Impl: SelectorImpl
|
||||
{
|
||||
|
@ -1191,18 +1209,19 @@ fn parse_qualified_name<'i, 't, P, E, Impl>
|
|||
Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),
|
||||
None => QNamePrefix::ImplicitAnyNamespace,
|
||||
};
|
||||
Ok(Some((namespace, local_name)))
|
||||
Ok(OptionalQName::Some(namespace, local_name))
|
||||
};
|
||||
|
||||
let explicit_namespace = |input: &mut CssParser<'i, 't>, namespace| {
|
||||
match input.next_including_whitespace() {
|
||||
Ok(&Token::Delim('*')) if !in_attr_selector => {
|
||||
Ok(Some((namespace, None)))
|
||||
Ok(OptionalQName::Some(namespace, None))
|
||||
},
|
||||
Ok(&Token::Ident(ref local_name)) => {
|
||||
Ok(Some((namespace, Some(local_name.clone()))))
|
||||
Ok(OptionalQName::Some(namespace, Some(local_name.clone())))
|
||||
},
|
||||
Ok(t) => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t.clone()))),
|
||||
Ok(t) if in_attr_selector => Err(SelectorParseError::InvalidQualNameInAttr(t.clone()).into()),
|
||||
Ok(t) => Err(SelectorParseError::ExplicitNamespaceUnexpectedToken(t.clone()).into()),
|
||||
Err(e) => Err(ParseError::Basic(e)),
|
||||
}
|
||||
};
|
||||
|
@ -1223,7 +1242,7 @@ fn parse_qualified_name<'i, 't, P, E, Impl>
|
|||
_ => {
|
||||
input.reset(&after_ident);
|
||||
if in_attr_selector {
|
||||
Ok(Some((QNamePrefix::ImplicitNoNamespace, Some(value))))
|
||||
Ok(OptionalQName::Some(QNamePrefix::ImplicitNoNamespace, Some(value)))
|
||||
} else {
|
||||
default_namespace(Some(value))
|
||||
}
|
||||
|
@ -1241,7 +1260,7 @@ fn parse_qualified_name<'i, 't, P, E, Impl>
|
|||
input.reset(&after_star);
|
||||
if in_attr_selector {
|
||||
match result {
|
||||
Ok(t) => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t))),
|
||||
Ok(t) => Err(SelectorParseError::ExpectedBarInAttr(t).into()),
|
||||
Err(e) => Err(ParseError::Basic(e)),
|
||||
}
|
||||
} else {
|
||||
|
@ -1253,9 +1272,13 @@ fn parse_qualified_name<'i, 't, P, E, Impl>
|
|||
Ok(Token::Delim('|')) => {
|
||||
explicit_namespace(input, QNamePrefix::ExplicitNoNamespace)
|
||||
}
|
||||
_ => {
|
||||
Ok(t) => {
|
||||
input.reset(&start);
|
||||
Ok(None)
|
||||
Ok(OptionalQName::None(t))
|
||||
}
|
||||
Err(e) => {
|
||||
input.reset(&start);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1269,9 +1292,10 @@ fn parse_attribute_selector<'i, 't, P, E, Impl>(parser: &P, input: &mut CssParse
|
|||
let namespace;
|
||||
let local_name;
|
||||
match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? {
|
||||
None => return Err(ParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector)),
|
||||
Some((_, None)) => unreachable!(),
|
||||
Some((ns, Some(ln))) => {
|
||||
OptionalQName::None(t) =>
|
||||
return Err(ParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(t))),
|
||||
OptionalQName::Some(_, None) => unreachable!(),
|
||||
OptionalQName::Some(ns, Some(ln)) => {
|
||||
local_name = ln;
|
||||
namespace = match ns {
|
||||
QNamePrefix::ImplicitNoNamespace |
|
||||
|
@ -1292,10 +1316,7 @@ fn parse_attribute_selector<'i, 't, P, E, Impl>(parser: &P, input: &mut CssParse
|
|||
}
|
||||
}
|
||||
|
||||
let operator;
|
||||
let value;
|
||||
let never_matches;
|
||||
match input.next() {
|
||||
let operator = match input.next() {
|
||||
// [foo]
|
||||
Err(_) => {
|
||||
let local_name_lower = to_ascii_lowercase(&local_name).as_ref().into();
|
||||
|
@ -1317,43 +1338,38 @@ fn parse_attribute_selector<'i, 't, P, E, Impl>(parser: &P, input: &mut CssParse
|
|||
}
|
||||
|
||||
// [foo=bar]
|
||||
Ok(&Token::Delim('=')) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = false;
|
||||
operator = AttrSelectorOperator::Equal;
|
||||
}
|
||||
Ok(&Token::Delim('=')) => AttrSelectorOperator::Equal,
|
||||
// [foo~=bar]
|
||||
Ok(&Token::IncludeMatch) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = value.is_empty() || value.contains(SELECTOR_WHITESPACE);
|
||||
operator = AttrSelectorOperator::Includes;
|
||||
}
|
||||
Ok(&Token::IncludeMatch) => AttrSelectorOperator::Includes,
|
||||
// [foo|=bar]
|
||||
Ok(&Token::DashMatch) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = false;
|
||||
operator = AttrSelectorOperator::DashMatch;
|
||||
}
|
||||
Ok(&Token::DashMatch) => AttrSelectorOperator::DashMatch,
|
||||
// [foo^=bar]
|
||||
Ok(&Token::PrefixMatch) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = value.is_empty();
|
||||
operator = AttrSelectorOperator::Prefix;
|
||||
}
|
||||
Ok(&Token::PrefixMatch) => AttrSelectorOperator::Prefix,
|
||||
// [foo*=bar]
|
||||
Ok(&Token::SubstringMatch) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = value.is_empty();
|
||||
operator = AttrSelectorOperator::Substring;
|
||||
}
|
||||
Ok(&Token::SubstringMatch) => AttrSelectorOperator::Substring,
|
||||
// [foo$=bar]
|
||||
Ok(&Token::SuffixMatch) => {
|
||||
value = input.expect_ident_or_string()?.clone();
|
||||
never_matches = value.is_empty();
|
||||
operator = AttrSelectorOperator::Suffix;
|
||||
Ok(&Token::SuffixMatch) => AttrSelectorOperator::Suffix,
|
||||
Ok(t) => return Err(SelectorParseError::UnexpectedTokenInAttributeSelector(t.clone()).into())
|
||||
};
|
||||
|
||||
let value = match input.expect_ident_or_string() {
|
||||
Ok(t) => t.clone(),
|
||||
Err(BasicParseError::UnexpectedToken(t)) =>
|
||||
return Err(SelectorParseError::BadValueInAttr(t.clone()).into()),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
let never_matches = match operator {
|
||||
AttrSelectorOperator::Equal |
|
||||
AttrSelectorOperator::DashMatch => false,
|
||||
|
||||
AttrSelectorOperator::Includes => {
|
||||
value.is_empty() || value.contains(SELECTOR_WHITESPACE)
|
||||
}
|
||||
_ => return Err(SelectorParseError::UnexpectedTokenInAttributeSelector.into())
|
||||
}
|
||||
|
||||
AttrSelectorOperator::Prefix |
|
||||
AttrSelectorOperator::Substring |
|
||||
AttrSelectorOperator::Suffix => value.is_empty()
|
||||
};
|
||||
|
||||
let mut case_sensitivity = parse_attribute_flags(input)?;
|
||||
|
||||
|
@ -1429,13 +1445,19 @@ fn parse_negation<'i, 't, P, E, Impl>(parser: &P,
|
|||
|
||||
// Get exactly one simple selector. The parse logic in the caller will verify
|
||||
// that there are no trailing tokens after we're done.
|
||||
if !parse_type_selector(parser, input, &mut sequence)? {
|
||||
let is_type_sel = match parse_type_selector(parser, input, &mut sequence) {
|
||||
Ok(result) => result,
|
||||
Err(ParseError::Basic(BasicParseError::EndOfInput)) =>
|
||||
return Err(SelectorParseError::EmptyNegation.into()),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
if !is_type_sel {
|
||||
match parse_one_simple_selector(parser, input, /* inside_negation = */ true)? {
|
||||
Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
|
||||
sequence.push(s);
|
||||
},
|
||||
None => {
|
||||
return Err(ParseError::Custom(SelectorParseError::EmptySelector));
|
||||
return Err(ParseError::Custom(SelectorParseError::EmptyNegation));
|
||||
},
|
||||
Some(SimpleSelectorParseResult::PseudoElement(_)) => {
|
||||
return Err(ParseError::Custom(SelectorParseError::NonSimpleSelectorInNegation));
|
||||
|
@ -1492,20 +1514,21 @@ 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 =
|
||||
P::parse_non_ts_pseudo_class(parser, name)?;
|
||||
P::parse_non_ts_pseudo_class(parser, name.clone())?;
|
||||
if !p.supports_pseudo_class(&pseudo_class) {
|
||||
return Err(SelectorParseError::UnsupportedPseudoClass.into());
|
||||
return Err(SelectorParseError::UnsupportedPseudoClassOrElement(name).into());
|
||||
}
|
||||
state_selectors.push(Component::NonTSPseudoClass(pseudo_class));
|
||||
}
|
||||
|
@ -1604,7 +1627,7 @@ fn parse_one_simple_selector<'i, 't, P, E, Impl>(parser: &P,
|
|||
let class = Component::Class(class.as_ref().into());
|
||||
Ok(Some(SimpleSelectorParseResult::SimpleSelector(class)))
|
||||
}
|
||||
ref t => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t.clone()))),
|
||||
ref t => Err(SelectorParseError::ClassNeedsIdent(t.clone()).into()),
|
||||
}
|
||||
}
|
||||
Ok(Token::SquareBracketBlock) => {
|
||||
|
@ -1619,7 +1642,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);
|
||||
|
|
|
@ -308,7 +308,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => {
|
||||
match_ignore_ascii_case! { &name,
|
||||
$($css => NonTSPseudoClass::$name,)*
|
||||
_ => return Err(::selectors::parser::SelectorParseError::UnexpectedIdent(
|
||||
_ => return Err(::selectors::parser::SelectorParseError::UnsupportedPseudoClassOrElement(
|
||||
name.clone()).into())
|
||||
}
|
||||
}
|
||||
|
@ -317,7 +317,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
if self.is_pseudo_class_enabled(&pseudo_class) {
|
||||
Ok(pseudo_class)
|
||||
} else {
|
||||
Err(SelectorParseError::UnexpectedIdent(name).into())
|
||||
Err(SelectorParseError::UnsupportedPseudoClassOrElement(name).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,7 +354,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
}
|
||||
NonTSPseudoClass::MozAny(selectors.into_boxed_slice())
|
||||
}
|
||||
_ => return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
|
||||
_ => return Err(SelectorParseError::UnsupportedPseudoClassOrElement(name.clone()).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -362,13 +362,13 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
if self.is_pseudo_class_enabled(&pseudo_class) {
|
||||
Ok(pseudo_class)
|
||||
} else {
|
||||
Err(SelectorParseError::UnexpectedIdent(name).into())
|
||||
Err(SelectorParseError::UnsupportedPseudoClassOrElement(name).into())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_pseudo_element(&self, name: CowRcStr<'i>) -> Result<PseudoElement, ParseError<'i>> {
|
||||
PseudoElement::from_slice(&name, self.in_user_agent_stylesheet())
|
||||
.ok_or(SelectorParseError::UnexpectedIdent(name.clone()).into())
|
||||
.ok_or(SelectorParseError::UnsupportedPseudoClassOrElement(name.clone()).into())
|
||||
}
|
||||
|
||||
fn parse_functional_pseudo_element<'t>(&self, name: CowRcStr<'i>,
|
||||
|
@ -392,7 +392,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
return Ok(pseudo);
|
||||
}
|
||||
}
|
||||
Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
|
||||
Err(SelectorParseError::UnsupportedPseudoClassOrElement(name.clone()).into())
|
||||
}
|
||||
|
||||
fn default_namespace(&self) -> Option<Namespace> {
|
||||
|
|
|
@ -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(),
|
||||
|
@ -194,7 +194,7 @@ enum Action {
|
|||
|
||||
trait ErrorHelpers<'a> {
|
||||
fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>);
|
||||
fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>);
|
||||
fn error_params(self) -> ErrorParams<'a>;
|
||||
fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,9 @@ fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
|
|||
CssParseError::Basic(BasicParseError::UnexpectedToken(t)) =>
|
||||
ErrorString::UnexpectedToken(t),
|
||||
|
||||
CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) =>
|
||||
CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) |
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::UnsupportedAtRule(i))) =>
|
||||
ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
|
@ -214,9 +216,6 @@ fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
|
|||
CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident)) =>
|
||||
ErrorString::Ident(ident),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) =>
|
||||
ErrorString::Ident(namespace),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::PropertyDeclaration(
|
||||
PropertyDeclarationParseError::UnknownProperty(property)))) =>
|
||||
|
@ -236,17 +235,54 @@ fn extract_value_error_param<'a>(err: ValueParseError<'a>) -> ErrorString<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
struct ErrorParams<'a> {
|
||||
prefix_param: Option<ErrorString<'a>>,
|
||||
main_param: Option<ErrorString<'a>>,
|
||||
}
|
||||
|
||||
/// If an error parameter is present in the given error, return it. Additionally return
|
||||
/// a second parameter if it exists, for use in the prefix for the eventual error message.
|
||||
fn extract_error_params<'a>(err: ParseError<'a>) -> Option<(ErrorString<'a>, Option<ErrorString<'a>>)> {
|
||||
match err {
|
||||
fn extract_error_params<'a>(err: ParseError<'a>) -> Option<ErrorParams<'a>> {
|
||||
let (main, prefix) = match err {
|
||||
CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::PropertyDeclaration(
|
||||
PropertyDeclarationParseError::InvalidValue(property, Some(e))))) =>
|
||||
Some((ErrorString::Snippet(property.into()), Some(extract_value_error_param(e)))),
|
||||
(Some(ErrorString::Snippet(property.into())), Some(extract_value_error_param(e))),
|
||||
|
||||
err => extract_error_param(err).map(|e| (e, None)),
|
||||
}
|
||||
CssParseError::Custom(SelectorParseError::UnexpectedTokenInAttributeSelector(t)) |
|
||||
CssParseError::Custom(SelectorParseError::BadValueInAttr(t)) |
|
||||
CssParseError::Custom(SelectorParseError::ExpectedBarInAttr(t)) |
|
||||
CssParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(t)) |
|
||||
CssParseError::Custom(SelectorParseError::InvalidQualNameInAttr(t)) |
|
||||
CssParseError::Custom(SelectorParseError::ExplicitNamespaceUnexpectedToken(t)) |
|
||||
CssParseError::Custom(SelectorParseError::PseudoElementExpectedIdent(t)) |
|
||||
CssParseError::Custom(SelectorParseError::NoIdentForPseudo(t)) |
|
||||
CssParseError::Custom(SelectorParseError::ClassNeedsIdent(t)) |
|
||||
CssParseError::Custom(SelectorParseError::PseudoElementExpectedColon(t)) =>
|
||||
(None, Some(ErrorString::UnexpectedToken(t))),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) =>
|
||||
(None, Some(ErrorString::Ident(namespace))),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(p)) =>
|
||||
(None, Some(ErrorString::Ident(p))),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::EmptySelector) |
|
||||
CssParseError::Custom(SelectorParseError::DanglingCombinator) =>
|
||||
(None, None),
|
||||
|
||||
CssParseError::Custom(SelectorParseError::EmptyNegation) =>
|
||||
(None, Some(ErrorString::Snippet(")".into()))),
|
||||
|
||||
err => match extract_error_param(err) {
|
||||
Some(e) => (Some(e), None),
|
||||
None => return None,
|
||||
}
|
||||
};
|
||||
Some(ErrorParams {
|
||||
main_param: main,
|
||||
prefix_param: prefix,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
||||
|
@ -273,9 +309,12 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>) {
|
||||
fn error_params(self) -> ErrorParams<'a> {
|
||||
let (s, error) = self.error_data();
|
||||
extract_error_params(error).unwrap_or((ErrorString::Snippet(s), None))
|
||||
extract_error_params(error).unwrap_or_else(|| ErrorParams {
|
||||
main_param: Some(ErrorString::Snippet(s)),
|
||||
prefix_param: None
|
||||
})
|
||||
}
|
||||
|
||||
fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action) {
|
||||
|
@ -303,15 +342,52 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
|||
(b"PEKeyframeBadName\0", Action::Nothing),
|
||||
ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
|
||||
(b"PEBadSelectorKeyframeRuleIgnored\0", Action::Nothing),
|
||||
ContextualParseError::InvalidRule(
|
||||
_, CssParseError::Custom(SelectorParseError::ExpectedNamespace(_))) =>
|
||||
(b"PEUnknownNamespacePrefix\0", Action::Nothing),
|
||||
ContextualParseError::InvalidRule(
|
||||
_, CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::UnexpectedTokenWithinNamespace(_)))) =>
|
||||
(b"PEAtNSUnexpected\0", Action::Nothing),
|
||||
ContextualParseError::InvalidRule(..) =>
|
||||
(b"PEBadSelectorRSIgnored\0", Action::Nothing),
|
||||
ContextualParseError::InvalidRule(
|
||||
_, CssParseError::Basic(BasicParseError::AtRuleInvalid(_))) |
|
||||
ContextualParseError::InvalidRule(
|
||||
_, CssParseError::Custom(SelectorParseError::Custom(
|
||||
StyleParseError::UnsupportedAtRule(_)))) =>
|
||||
(b"PEUnknownAtRule\0", Action::Nothing),
|
||||
ContextualParseError::InvalidRule(_, ref err) => {
|
||||
let prefix = match *err {
|
||||
CssParseError::Custom(SelectorParseError::UnexpectedTokenInAttributeSelector(_)) =>
|
||||
Some(&b"PEAttSelUnexpected\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::ExpectedBarInAttr(_)) =>
|
||||
Some(&b"PEAttSelNoBar\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::BadValueInAttr(_)) =>
|
||||
Some(&b"PEAttSelBadValue\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(_)) =>
|
||||
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"[..]),
|
||||
CssParseError::Custom(SelectorParseError::ClassNeedsIdent(_)) =>
|
||||
Some(&b"PEClassSelNotIdent\0"[..]),
|
||||
CssParseError::Custom(SelectorParseError::EmptyNegation) =>
|
||||
Some(&b"PENegationBadArg\0"[..]),
|
||||
_ => None,
|
||||
};
|
||||
return (prefix, b"PEBadSelectorRSIgnored\0", Action::Nothing);
|
||||
}
|
||||
ContextualParseError::UnsupportedRule(..) =>
|
||||
(b"PEDeclDropped\0", Action::Nothing),
|
||||
ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
|
||||
|
@ -340,17 +416,20 @@ impl ParseErrorReporter for ErrorReporter {
|
|||
Action::Skip => b"PEDeclSkipped\0".as_ptr(),
|
||||
Action::Drop => b"PEDeclDropped\0".as_ptr(),
|
||||
};
|
||||
let (param, pre_param) = error.error_params();
|
||||
let param = param.into_str();
|
||||
let params = error.error_params();
|
||||
let param = params.main_param;
|
||||
let pre_param = params.prefix_param;
|
||||
let param = param.map(|p| p.into_str());
|
||||
let pre_param = pre_param.map(|p| p.into_str());
|
||||
let param_ptr = param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
|
||||
let pre_param_ptr = pre_param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
|
||||
// The CSS source text is unused and will be removed in bug 1381188.
|
||||
let source = "";
|
||||
unsafe {
|
||||
Gecko_ReportUnexpectedCSSError(self.0,
|
||||
name.as_ptr() as *const _,
|
||||
param.as_ptr() as *const _,
|
||||
param.len() as u32,
|
||||
param_ptr as *const _,
|
||||
param.as_ref().map_or(0, |p| p.len()) as u32,
|
||||
pre.map_or(ptr::null(), |p| p.as_ptr()) as *const _,
|
||||
pre_param_ptr as *const _,
|
||||
pre_param.as_ref().map_or(0, |p| p.len()) as u32,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue