aboutsummaryrefslogtreecommitdiffstats
path: root/components/selectors/parser.rs
diff options
context:
space:
mode:
authorZach Hoffman <zach@zrhoffman.net>2023-01-07 14:32:42 +0000
committerMartin Robinson <mrobinson@igalia.com>2023-11-04 08:17:09 +0100
commit1c8408e97e7254b3493666007e3e4299a849b36e (patch)
tree69694e062d638d71716ae1dd491cebae21f1c481 /components/selectors/parser.rs
parentb7d64ee6a4579ae38877abd0b2fc163ea09d76cc (diff)
downloadservo-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.rs90
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
}