diff options
author | Zach Hoffman <zach@zrhoffman.net> | 2023-01-07 14:32:42 +0000 |
---|---|---|
committer | Martin Robinson <mrobinson@igalia.com> | 2023-11-04 08:17:09 +0100 |
commit | 1c8408e97e7254b3493666007e3e4299a849b36e (patch) | |
tree | 69694e062d638d71716ae1dd491cebae21f1c481 /components/selectors/parser.rs | |
parent | b7d64ee6a4579ae38877abd0b2fc163ea09d76cc (diff) | |
download | servo-1c8408e97e7254b3493666007e3e4299a849b36e.tar.gz servo-1c8408e97e7254b3493666007e3e4299a849b36e.zip |
style: Implement parsing and serialization for nth-child(An+B of selector list) and :nth-last-child(An+B of selector list)
:nth-{,last-}child parsing is disabled by default for now by pref
layout.css.nth-child-of.enabled.
Differential Revision: https://phabricator.services.mozilla.com/D165895
Diffstat (limited to 'components/selectors/parser.rs')
-rw-r--r-- | components/selectors/parser.rs | 90 |
1 files changed, 87 insertions, 3 deletions
diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index bb5f9155d0f..3098cb0e454 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -256,6 +256,11 @@ pub trait Parser<'i> { false } + /// Whether to parse the selector list of nth-child() or nth-last-child(). + fn parse_nth_child_of(&self) -> bool { + false + } + /// Whether to parse the `:where` pseudo-class. fn parse_is_and_where(&self) -> bool { false @@ -1121,6 +1126,38 @@ impl NthSelectorData { } } +/// The properties that comprise an :nth- pseudoclass as of Selectors 4 (e.g., +/// nth-child(An+B [of S]?)). +/// https://www.w3.org/TR/selectors-4/#nth-child-pseudo +#[derive(Clone, Eq, PartialEq, ToShmem)] +#[shmem(no_bounds)] +pub struct NthOfSelectorData<Impl: SelectorImpl>( + #[shmem(field_bound)] ThinArc<NthSelectorData, Selector<Impl>>, +); + +impl<Impl: SelectorImpl> NthOfSelectorData<Impl> { + /// Returns selector data for :nth-{,last-}{child,of-type}(An+B [of S]) + #[inline] + pub fn new(nth_data: &NthSelectorData, mut selectors: SelectorList<Impl>) -> Self { + Self(ThinArc::from_header_and_iter( + *nth_data, + selectors.0.drain(..), + )) + } + + /// Returns the An+B part of the selector + #[inline] + pub fn nth_data(&self) -> &NthSelectorData { + &self.0.header.header + } + + /// Returns the selector list part of the selector + #[inline] + pub fn selectors(&self) -> &[Selector<Impl>] { + &self.0.slice + } +} + /// A CSS simple selector or combinator. We store both in the same enum for /// optimal packing and cache performance, see [1]. /// @@ -1167,6 +1204,7 @@ pub enum Component<Impl: SelectorImpl> { Empty, Scope, Nth(NthSelectorData), + NthOf(NthOfSelectorData<Impl>), NonTSPseudoClass(#[cfg_attr(feature = "shmem", shmem(field_bound))] Impl::NonTSPseudoClass), /// The ::slotted() pseudo-element: /// @@ -1358,6 +1396,11 @@ impl<Impl: SelectorImpl> Component<Impl> { return false; } }, + NthOf(ref nth_of_data) => { + if !visitor.visit_selector_list(nth_of_data.selectors()) { + return false; + } + }, _ => {}, } @@ -1687,6 +1730,22 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> { } Ok(()) }, + NthOf(ref nth_of_data) => { + let nth_data = nth_of_data.nth_data(); + dest.write_str(match nth_data.ty { + NthType::Child => ":nth-child(", + NthType::LastChild => ":nth-last-child(", + _ => unreachable!(), + })?; + write_affine(dest, nth_data.a, nth_data.b)?; + debug_assert!( + !nth_of_data.selectors().is_empty(), + "The selector list should not be empty" + ); + dest.write_str(" of ")?; + serialize_selector_list(nth_of_data.selectors().iter(), dest)?; + dest.write_char(')') + }, Is(ref list) | Where(ref list) | Negation(ref list) | Has(ref list) => { match *self { Where(..) => dest.write_str(":where(")?, @@ -2421,7 +2480,7 @@ where } fn parse_nth_pseudo_class<'i, 't, P, Impl>( - _: &P, + parser: &P, input: &mut CssParser<'i, 't>, state: SelectorParsingState, ty: NthType, @@ -2434,12 +2493,33 @@ where return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); } let (a, b) = parse_nth(input)?; - Ok(Component::Nth(NthSelectorData { + let nth_data = NthSelectorData { ty, is_function: true, a, b, - })) + }; + if !parser.parse_nth_child_of() || ty.is_of_type() { + return Ok(Component::Nth(nth_data)); + } + + // Try to parse "of <selector-list>". + if input.try_parse(|i| i.expect_ident_matching("of")).is_err() { + return Ok(Component::Nth(nth_data)); + } + // Whitespace between "of" and the selector list is optional + // https://github.com/w3c/csswg-drafts/issues/8285 + let selectors = SelectorList::parse_with_state( + parser, + input, + state | + SelectorParsingState::SKIP_DEFAULT_NAMESPACE | + SelectorParsingState::DISALLOW_PSEUDOS, + ParseErrorRecovery::DiscardList, + )?; + Ok(Component::NthOf(NthOfSelectorData::new( + &nth_data, selectors, + ))) } /// Returns whether the name corresponds to a CSS2 pseudo-element that @@ -2784,6 +2864,10 @@ pub mod tests { true } + fn parse_nth_child_of(&self) -> bool { + true + } + fn parse_is_and_where(&self) -> bool { true } |