style: Represent FirstChild, FirstOfType, LastChild, LastOfType, OnlyChild, and OnlyOfType as functionless Nth variants

Like bug 1808226, this doesn't change any behavior, it just simplifies matching.

I also added a WPT JavaScript test for :only-of-type, since dedicated WPT
JavScript tests already exist for the other pseudo-classes this patch
touches.

Differential Revision: https://phabricator.services.mozilla.com/D165859
This commit is contained in:
Zach Hoffman 2023-01-03 15:30:19 +00:00 committed by Martin Robinson
parent dc225e0b2f
commit 211761ad88
5 changed files with 76 additions and 74 deletions

View file

@ -711,16 +711,10 @@ where
Component::Class(..) | Component::Class(..) |
Component::AttributeInNoNamespaceExists { .. } | Component::AttributeInNoNamespaceExists { .. } |
Component::AttributeInNoNamespace { .. } | Component::AttributeInNoNamespace { .. } |
Component::FirstChild |
Component::LastChild |
Component::OnlyChild |
Component::Root | Component::Root |
Component::Empty | Component::Empty |
Component::Scope | Component::Scope |
Component::Nth(..) | Component::Nth(..) |
Component::FirstOfType |
Component::LastOfType |
Component::OnlyOfType |
Component::Host(None) => 0, Component::Host(None) => 0,
} }
} }

View file

@ -316,16 +316,10 @@ where
Component::AttributeInNoNamespace { .. } | Component::AttributeInNoNamespace { .. } |
Component::AttributeInNoNamespaceExists { .. } | Component::AttributeInNoNamespaceExists { .. } |
Component::AttributeOther(..) | Component::AttributeOther(..) |
Component::FirstChild |
Component::LastChild |
Component::OnlyChild |
Component::Root | Component::Root |
Component::Empty | Component::Empty |
Component::Scope | Component::Scope |
Component::Nth(..) | Component::Nth(..) |
Component::FirstOfType |
Component::LastOfType |
Component::OnlyOfType |
Component::NonTSPseudoClass(..) => { Component::NonTSPseudoClass(..) => {
specificity.class_like_selectors += 1; specificity.class_like_selectors += 1;
}, },

View file

@ -387,14 +387,8 @@ fn hover_and_active_quirk_applies<Impl: SelectorImpl>(
Component::Class(_) | Component::Class(_) |
Component::PseudoElement(_) | Component::PseudoElement(_) |
Component::Negation(_) | Component::Negation(_) |
Component::FirstChild |
Component::LastChild |
Component::OnlyChild |
Component::Empty | Component::Empty |
Component::Nth(_) | Component::Nth(_) => false,
Component::FirstOfType |
Component::LastOfType |
Component::OnlyOfType => false,
Component::NonTSPseudoClass(ref pseudo_class) => pseudo_class.is_active_or_hover(), Component::NonTSPseudoClass(ref pseudo_class) => pseudo_class.is_active_or_hover(),
_ => true, _ => true,
}) })
@ -784,12 +778,6 @@ where
} }
element.match_non_ts_pseudo_class(pc, &mut context.shared) element.match_non_ts_pseudo_class(pc, &mut context.shared)
}, },
Component::FirstChild => matches_first_child(element, context.shared),
Component::LastChild => matches_last_child(element, context.shared),
Component::OnlyChild => {
matches_first_child(element, context.shared) &&
matches_last_child(element, context.shared)
},
Component::Root => element.is_root(), Component::Root => element.is_root(),
Component::Empty => { Component::Empty => {
if context.shared.needs_selector_flags() { if context.shared.needs_selector_flags() {
@ -812,23 +800,46 @@ where
Some(ref scope_element) => element.opaque() == *scope_element, Some(ref scope_element) => element.opaque() == *scope_element,
None => element.is_root(), None => element.is_root(),
}, },
Component::Nth(nth_data) => matches_generic_nth_child( Component::Nth(nth_data) => {
element, if nth_data.is_function ||
context.shared, (match nth_data.ty {
nth_data.a, NthType::Child => return matches_first_child(element, context.shared),
nth_data.b, NthType::LastChild => return matches_last_child(element, context.shared),
nth_data.ty == NthType::OfType || nth_data.ty == NthType::LastOfType, NthType::OnlyChild => {
nth_data.ty == NthType::LastChild || nth_data.ty == NthType::LastOfType, return matches_first_child(element, context.shared) &&
), matches_last_child(element, context.shared)
Component::FirstOfType => { },
matches_generic_nth_child(element, context.shared, 0, 1, true, false) NthType::OnlyOfType => {
}, return matches_generic_nth_child(
Component::LastOfType => { element,
matches_generic_nth_child(element, context.shared, 0, 1, true, true) context.shared,
}, nth_data.a,
Component::OnlyOfType => { nth_data.b,
matches_generic_nth_child(element, context.shared, 0, 1, true, false) && true,
matches_generic_nth_child(element, context.shared, 0, 1, true, true) false,
) && matches_generic_nth_child(
element,
context.shared,
nth_data.a,
nth_data.b,
true,
true,
)
},
_ => true,
})
{
matches_generic_nth_child(
element,
context.shared,
nth_data.a,
nth_data.b,
nth_data.ty == NthType::OfType || nth_data.ty == NthType::LastOfType,
nth_data.ty == NthType::LastChild || nth_data.ty == NthType::LastOfType,
)
} else {
unreachable!()
}
}, },
Component::Is(ref list) | Component::Where(ref list) => context.shared.nest(|context| { Component::Is(ref list) | Component::Where(ref list) => context.shared.nest(|context| {
for selector in &**list { for selector in &**list {

View file

@ -1042,8 +1042,10 @@ impl Combinator {
pub enum NthType { pub enum NthType {
Child, Child,
LastChild, LastChild,
OnlyChild,
OfType, OfType,
LastOfType, LastOfType,
OnlyOfType,
} }
/// The properties that comprise an :nth- pseudoclass as of Selectors 3 (e.g., /// The properties that comprise an :nth- pseudoclass as of Selectors 3 (e.g.,
@ -1053,6 +1055,7 @@ pub enum NthType {
#[shmem(no_bounds)] #[shmem(no_bounds)]
pub struct NthSelectorData { pub struct NthSelectorData {
pub ty: NthType, pub ty: NthType,
pub is_function: bool,
pub a: i32, pub a: i32,
pub b: i32, pub b: i32,
} }
@ -1099,16 +1102,10 @@ pub enum Component<Impl: SelectorImpl> {
/// Pseudo-classes /// Pseudo-classes
Negation(Box<[Selector<Impl>]>), Negation(Box<[Selector<Impl>]>),
FirstChild,
LastChild,
OnlyChild,
Root, Root,
Empty, Empty,
Scope, Scope,
Nth(NthSelectorData), Nth(NthSelectorData),
FirstOfType,
LastOfType,
OnlyOfType,
NonTSPseudoClass(#[cfg_attr(feature = "shmem", shmem(field_bound))] Impl::NonTSPseudoClass), NonTSPseudoClass(#[cfg_attr(feature = "shmem", shmem(field_bound))] Impl::NonTSPseudoClass),
/// The ::slotted() pseudo-element: /// The ::slotted() pseudo-element:
/// ///
@ -1598,9 +1595,6 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
AttributeOther(ref attr_selector) => attr_selector.to_css(dest), AttributeOther(ref attr_selector) => attr_selector.to_css(dest),
// Pseudo-classes // Pseudo-classes
FirstChild => dest.write_str(":first-child"),
LastChild => dest.write_str(":last-child"),
OnlyChild => dest.write_str(":only-child"),
Root => dest.write_str(":root"), Root => dest.write_str(":root"),
Empty => dest.write_str(":empty"), Empty => dest.write_str(":empty"),
Scope => dest.write_str(":scope"), Scope => dest.write_str(":scope"),
@ -1613,17 +1607,27 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
} }
Ok(()) Ok(())
}, },
FirstOfType => dest.write_str(":first-of-type"),
LastOfType => dest.write_str(":last-of-type"),
OnlyOfType => dest.write_str(":only-of-type"),
Nth(nth_data) => { Nth(nth_data) => {
match nth_data.ty { if nth_data.is_function {
NthType::Child => dest.write_str(":nth-child(")?, match nth_data.ty {
NthType::LastChild => dest.write_str(":nth-last-child(")?, NthType::Child => dest.write_str(":nth-child("),
NthType::OfType => dest.write_str(":nth-of-type(")?, NthType::LastChild => dest.write_str(":nth-last-child("),
NthType::LastOfType => dest.write_str(":nth-last-of-type(")?, NthType::OfType => dest.write_str(":nth-of-type("),
NthType::LastOfType => dest.write_str(":nth-last-of-type("),
_ => unreachable!(),
}?;
write_affine(dest, nth_data.a, nth_data.b)?;
} else {
match nth_data.ty {
NthType::Child => dest.write_str(":first-child"),
NthType::LastChild => dest.write_str(":last-child"),
NthType::OnlyChild => dest.write_str(":only-child"),
NthType::OfType => dest.write_str(":first-of-type"),
NthType::LastOfType => dest.write_str(":last-of-type"),
NthType::OnlyOfType => dest.write_str(":only-of-type"),
}?;
} }
write_affine(dest, nth_data.a, nth_data.b)?;
dest.write_char(')') dest.write_char(')')
}, },
Is(ref list) | Where(ref list) | Negation(ref list) | Has(ref list) => { Is(ref list) | Where(ref list) | Negation(ref list) | Has(ref list) => {
@ -2373,7 +2377,12 @@ where
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
} }
let (a, b) = parse_nth(input)?; let (a, b) = parse_nth(input)?;
Ok(Component::Nth(NthSelectorData { ty, a, b })) Ok(Component::Nth(NthSelectorData {
ty,
is_function: true,
a,
b,
}))
} }
/// Returns whether the name corresponds to a CSS2 pseudo-element that /// Returns whether the name corresponds to a CSS2 pseudo-element that
@ -2534,16 +2543,16 @@ where
if state.allows_tree_structural_pseudo_classes() { if state.allows_tree_structural_pseudo_classes() {
match_ignore_ascii_case! { &name, match_ignore_ascii_case! { &name,
"first-child" => return Ok(Component::FirstChild), "first-child" => return Ok(Component::Nth(NthSelectorData{ ty: NthType:: Child, is_function: false, a: 0, b: 1})),
"last-child" => return Ok(Component::LastChild), "last-child" => return Ok(Component::Nth(NthSelectorData{ ty: NthType:: LastChild, is_function: false, a: 0, b: 1})),
"only-child" => return Ok(Component::OnlyChild), "only-child" => return Ok(Component::Nth(NthSelectorData{ ty: NthType:: OnlyChild, is_function: false, a: 0, b: 1})),
"root" => return Ok(Component::Root), "root" => return Ok(Component::Root),
"empty" => return Ok(Component::Empty), "empty" => return Ok(Component::Empty),
"scope" => return Ok(Component::Scope), "scope" => return Ok(Component::Scope),
"host" if P::parse_host(parser) => return Ok(Component::Host(None)), "host" if P::parse_host(parser) => return Ok(Component::Host(None)),
"first-of-type" => return Ok(Component::FirstOfType), "first-of-type" => return Ok(Component::Nth(NthSelectorData{ ty: NthType:: OfType, is_function: false, a: 0, b: 1})),
"last-of-type" => return Ok(Component::LastOfType), "last-of-type" => return Ok(Component::Nth(NthSelectorData{ ty: NthType:: LastOfType, is_function: false, a: 0, b: 1})),
"only-of-type" => return Ok(Component::OnlyOfType), "only-of-type" => return Ok(Component::Nth(NthSelectorData{ ty: NthType:: OnlyOfType, is_function: false, a: 0, b: 1})),
_ => {}, _ => {},
} }
} }

View file

@ -1918,13 +1918,7 @@ fn component_needs_revalidation(
Component::AttributeInNoNamespace { .. } | Component::AttributeInNoNamespace { .. } |
Component::AttributeOther(_) | Component::AttributeOther(_) |
Component::Empty | Component::Empty |
Component::FirstChild | Component::Nth(..) => true,
Component::LastChild |
Component::OnlyChild |
Component::Nth(..) |
Component::FirstOfType |
Component::LastOfType |
Component::OnlyOfType => true,
Component::NonTSPseudoClass(ref p) => p.needs_cache_revalidation(), Component::NonTSPseudoClass(ref p) => p.needs_cache_revalidation(),
_ => false, _ => false,
} }