mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
style: Allow :is() and :where() to have better error recovery.
Adjust is-where-parsing.html to work with both the new and old behavior, and add a test for the new behavior. Depends on D90049 Differential Revision: https://phabricator.services.mozilla.com/D90050
This commit is contained in:
parent
b1dbbb2969
commit
7e6d70f314
1 changed files with 50 additions and 13 deletions
|
@ -251,6 +251,11 @@ pub trait Parser<'i> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The error recovery that selector lists inside :is() and :where() have.
|
||||||
|
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
|
||||||
|
ParseErrorRecovery::IgnoreInvalidSelector
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the given function name is an alias for the `:is()` function.
|
/// Whether the given function name is an alias for the `:is()` function.
|
||||||
fn is_is_alias(&self, _name: &str) -> bool {
|
fn is_is_alias(&self, _name: &str) -> bool {
|
||||||
false
|
false
|
||||||
|
@ -329,6 +334,17 @@ pub struct SelectorList<Impl: SelectorImpl>(
|
||||||
#[shmem(field_bound)] pub SmallVec<[Selector<Impl>; 1]>,
|
#[shmem(field_bound)] pub SmallVec<[Selector<Impl>; 1]>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// How to treat invalid selectors in a selector list.
|
||||||
|
pub enum ParseErrorRecovery {
|
||||||
|
/// Discard the entire selector list, this is the default behavior for
|
||||||
|
/// almost all of CSS.
|
||||||
|
DiscardList,
|
||||||
|
/// Ignore invalid selectors, potentially creating an empty selector list.
|
||||||
|
///
|
||||||
|
/// This is the error recovery mode of :is() and :where()
|
||||||
|
IgnoreInvalidSelector,
|
||||||
|
}
|
||||||
|
|
||||||
impl<Impl: SelectorImpl> SelectorList<Impl> {
|
impl<Impl: SelectorImpl> SelectorList<Impl> {
|
||||||
/// Parse a comma-separated list of Selectors.
|
/// Parse a comma-separated list of Selectors.
|
||||||
/// <https://drafts.csswg.org/selectors/#grouping>
|
/// <https://drafts.csswg.org/selectors/#grouping>
|
||||||
|
@ -341,26 +357,42 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
{
|
{
|
||||||
Self::parse_with_state(parser, input, SelectorParsingState::empty())
|
Self::parse_with_state(parser, input, SelectorParsingState::empty(), ParseErrorRecovery::DiscardList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn parse_with_state<'i, 't, P>(
|
fn parse_with_state<'i, 't, P>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
state: SelectorParsingState,
|
state: SelectorParsingState,
|
||||||
|
recovery: ParseErrorRecovery,
|
||||||
) -> Result<Self, ParseError<'i, P::Error>>
|
) -> Result<Self, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
{
|
{
|
||||||
let mut values = SmallVec::new();
|
let mut values = SmallVec::new();
|
||||||
loop {
|
loop {
|
||||||
values.push(input.parse_until_before(Delimiter::Comma, |input| {
|
let selector = input.parse_until_before(Delimiter::Comma, |input| {
|
||||||
parse_selector(parser, input, state)
|
parse_selector(parser, input, state)
|
||||||
})?);
|
});
|
||||||
match input.next() {
|
|
||||||
Err(_) => return Ok(SelectorList(values)),
|
let was_ok = selector.is_ok();
|
||||||
Ok(&Token::Comma) => continue,
|
match selector {
|
||||||
Ok(_) => unreachable!(),
|
Ok(selector) => values.push(selector),
|
||||||
|
Err(err) => match recovery {
|
||||||
|
ParseErrorRecovery::DiscardList => return Err(err),
|
||||||
|
ParseErrorRecovery::IgnoreInvalidSelector => {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match input.next() {
|
||||||
|
Err(_) => return Ok(SelectorList(values)),
|
||||||
|
Ok(&Token::Comma) => break,
|
||||||
|
Ok(_) => {
|
||||||
|
debug_assert!(!was_ok, "Shouldn't have got a selector if getting here");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1251,18 +1283,18 @@ impl<Impl: SelectorImpl> Debug for LocalName<Impl> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_selector_list<'a, Impl, I, W>(mut iter: I, dest: &mut W) -> fmt::Result
|
fn serialize_selector_list<'a, Impl, I, W>(iter: I, dest: &mut W) -> fmt::Result
|
||||||
where
|
where
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
I: Iterator<Item = &'a Selector<Impl>>,
|
I: Iterator<Item = &'a Selector<Impl>>,
|
||||||
W: fmt::Write,
|
W: fmt::Write,
|
||||||
{
|
{
|
||||||
let first = iter
|
let mut first = true;
|
||||||
.next()
|
|
||||||
.expect("Empty SelectorList, should contain at least one selector");
|
|
||||||
first.to_css(dest)?;
|
|
||||||
for selector in iter {
|
for selector in iter {
|
||||||
dest.write_str(", ")?;
|
if !first {
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
selector.to_css(dest)?;
|
selector.to_css(dest)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2266,6 +2298,7 @@ where
|
||||||
parser,
|
parser,
|
||||||
input,
|
input,
|
||||||
state | SelectorParsingState::DISALLOW_PSEUDOS,
|
state | SelectorParsingState::DISALLOW_PSEUDOS,
|
||||||
|
parser.is_and_where_error_recovery(),
|
||||||
)?;
|
)?;
|
||||||
Ok(component(inner.0.into_vec().into_boxed_slice()))
|
Ok(component(inner.0.into_vec().into_boxed_slice()))
|
||||||
}
|
}
|
||||||
|
@ -2658,6 +2691,10 @@ pub mod tests {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
|
||||||
|
ParseErrorRecovery::DiscardList
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_part(&self) -> bool {
|
fn parse_part(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue