aboutsummaryrefslogtreecommitdiffstats
path: root/components/selectors/parser.rs
diff options
context:
space:
mode:
authorTiaan Louw <tlouw@mozilla.com>2022-06-20 08:53:02 +0000
committerMartin Robinson <mrobinson@igalia.com>2023-10-02 14:37:19 +0000
commit3d0cf4dbf9cb40bc120be3173ce1b3afcbc9c956 (patch)
tree9ead13ff59495c7782284d91de5e467c1d61f281 /components/selectors/parser.rs
parentdcdf9f33d552a17cd37459a2da28a361e215c14f (diff)
downloadservo-3d0cf4dbf9cb40bc120be3173ce1b3afcbc9c956.tar.gz
servo-3d0cf4dbf9cb40bc120be3173ce1b3afcbc9c956.zip
style: Add simple parsing and matching support for :has
Parsing is behind a config value `layout.css.has-selectors.enabled`. This change does not support p:has(> a) combinators, but will handle them gracefully, just not matching on them. Differential Revision: https://phabricator.services.mozilla.com/D149515
Diffstat (limited to 'components/selectors/parser.rs')
-rw-r--r--components/selectors/parser.rs27
1 files changed, 22 insertions, 5 deletions
diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs
index 2720d723568..2fc5ad02d39 100644
--- a/components/selectors/parser.rs
+++ b/components/selectors/parser.rs
@@ -261,6 +261,11 @@ pub trait Parser<'i> {
false
}
+ /// Whether to parse the :has pseudo-class.
+ fn parse_has(&self) -> bool {
+ false
+ }
+
/// Whether the given function name is an alias for the `:is()` function.
fn is_is_alias(&self, _name: &str) -> bool {
false
@@ -1116,6 +1121,12 @@ pub enum Component<Impl: SelectorImpl> {
///
/// Same comment as above re. the argument.
Is(Box<[Selector<Impl>]>),
+ /// The `:has` pseudo-class.
+ ///
+ /// https://drafts.csswg.org/selectors/#has-pseudo
+ ///
+ /// Same comment as above re. the argument.
+ Has(Box<[Selector<Impl>]>),
/// An implementation-dependent pseudo-element selector.
PseudoElement(#[cfg_attr(feature = "shmem", shmem(field_bound))] Impl::PseudoElement),
@@ -1589,11 +1600,12 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
write_affine(dest, a, b)?;
dest.write_char(')')
},
- Is(ref list) | Where(ref list) | Negation(ref list) => {
+ Is(ref list) | Where(ref list) | Negation(ref list) | Has(ref list) => {
match *self {
Where(..) => dest.write_str(":where(")?,
Is(..) => dest.write_str(":is(")?,
Negation(..) => dest.write_str(":not(")?,
+ Has(..) => dest.write_str(":has(")?,
_ => unreachable!(),
}
serialize_selector_list(list.iter(), dest)?;
@@ -2253,7 +2265,7 @@ where
Ok(empty)
}
-fn parse_is_or_where<'i, 't, P, Impl>(
+fn parse_is_where_has<'i, 't, P, Impl>(
parser: &P,
input: &mut CssParser<'i, 't>,
state: SelectorParsingState,
@@ -2295,8 +2307,9 @@ where
"nth-of-type" => return parse_nth_pseudo_class(parser, input, state, Component::NthOfType),
"nth-last-child" => return parse_nth_pseudo_class(parser, input, state, Component::NthLastChild),
"nth-last-of-type" => return parse_nth_pseudo_class(parser, input, state, Component::NthLastOfType),
- "is" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, state, Component::Is),
- "where" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, state, Component::Where),
+ "is" if parser.parse_is_and_where() => return parse_is_where_has(parser, input, state, Component::Is),
+ "where" if parser.parse_is_and_where() => return parse_is_where_has(parser, input, state, Component::Where),
+ "has" if parser.parse_has() => return parse_is_where_has(parser, input, state, Component::Has),
"host" => {
if !state.allows_tree_structural_pseudo_classes() {
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
@@ -2310,7 +2323,7 @@ where
}
if parser.parse_is_and_where() && parser.is_is_alias(&name) {
- return parse_is_or_where(parser, input, state, Component::Is);
+ return parse_is_where_has(parser, input, state, Component::Is);
}
if !state.allows_custom_functional_pseudo_classes() {
@@ -2684,6 +2697,10 @@ pub mod tests {
true
}
+ fn parse_has(&self) -> bool {
+ true
+ }
+
fn parse_part(&self) -> bool {
true
}