@@ -428,6 +428,49 @@ impl<'i, Impl: SelectorImpl<'i>> SelectorList<'i, Impl> {
428428 }
429429 }
430430
431+ #[ inline]
432+ fn parse_relative < ' t , P > (
433+ parser : & P ,
434+ input : & mut CssParser < ' i , ' t > ,
435+ state : & mut SelectorParsingState ,
436+ recovery : ParseErrorRecovery ,
437+ ) -> Result < Self , ParseError < ' i , P :: Error > >
438+ where
439+ P : Parser < ' i , Impl = Impl > ,
440+ {
441+ let original_state = * state;
442+ let mut values = SmallVec :: new ( ) ;
443+ loop {
444+ let selector = input. parse_until_before ( Delimiter :: Comma , |input| {
445+ let mut selector_state = original_state;
446+ let result = parse_relative_selector ( parser, input, & mut selector_state) ;
447+ if selector_state. contains ( SelectorParsingState :: AFTER_NESTING ) {
448+ state. insert ( SelectorParsingState :: AFTER_NESTING )
449+ }
450+ result
451+ } ) ;
452+
453+ let was_ok = selector. is_ok ( ) ;
454+ match selector {
455+ Ok ( selector) => values. push ( selector) ,
456+ Err ( err) => match recovery {
457+ ParseErrorRecovery :: DiscardList => return Err ( err) ,
458+ ParseErrorRecovery :: IgnoreInvalidSelector => { } ,
459+ } ,
460+ }
461+
462+ loop {
463+ match input. next ( ) {
464+ Err ( _) => return Ok ( SelectorList ( values) ) ,
465+ Ok ( & Token :: Comma ) => break ,
466+ Ok ( _) => {
467+ debug_assert ! ( !was_ok, "Shouldn't have got a selector if getting here" ) ;
468+ } ,
469+ }
470+ }
471+ }
472+ }
473+
431474 /// Creates a SelectorList from a Vec of selectors. Used in tests.
432475 pub fn from_vec ( v : Vec < Selector < ' i , Impl > > ) -> Self {
433476 SelectorList ( SmallVec :: from_vec ( v) )
@@ -1114,6 +1157,10 @@ pub enum Component<'i, Impl: SelectorImpl<'i>> {
11141157 ///
11151158 /// Same comment as above re. the argument.
11161159 Is ( Box < [ Selector < ' i , Impl > ] > ) ,
1160+ /// The `:has` pseudo-class.
1161+ ///
1162+ /// https://www.w3.org/TR/selectors/#relational
1163+ Has ( Box < [ Selector < ' i , Impl > ] > ) ,
11171164 /// An implementation-dependent pseudo-element selector.
11181165 PseudoElement ( Impl :: PseudoElement ) ,
11191166 /// A nesting selector:
@@ -1587,11 +1634,12 @@ impl<'i, Impl: SelectorImpl<'i>> ToCss for Component<'i, Impl> {
15871634 write_affine ( dest, a, b) ?;
15881635 dest. write_char ( ')' )
15891636 } ,
1590- Is ( ref list) | Where ( ref list) | Negation ( ref list) => {
1637+ Is ( ref list) | Where ( ref list) | Negation ( ref list) | Has ( ref list ) => {
15911638 match * self {
15921639 Where ( ..) => dest. write_str ( ":where(" ) ?,
15931640 Is ( ..) => dest. write_str ( ":is(" ) ?,
15941641 Negation ( ..) => dest. write_str ( ":not(" ) ?,
1642+ Has ( ..) => dest. write_str ( ":has(" ) ?,
15951643 _ => unreachable ! ( ) ,
15961644 }
15971645 serialize_selector_list ( list. iter ( ) , dest) ?;
@@ -1757,6 +1805,37 @@ impl<'i, Impl: SelectorImpl<'i>> Selector<'i, Impl> {
17571805 }
17581806}
17591807
1808+ fn parse_relative_selector < ' i , ' t , P , Impl > (
1809+ parser : & P ,
1810+ input : & mut CssParser < ' i , ' t > ,
1811+ state : & mut SelectorParsingState ,
1812+ ) -> Result < Selector < ' i , Impl > , ParseError < ' i , P :: Error > >
1813+ where
1814+ P : Parser < ' i , Impl = Impl > ,
1815+ Impl : SelectorImpl < ' i > ,
1816+ {
1817+ // https://www.w3.org/TR/selectors-4/#parse-relative-selector
1818+ let s = input. state ( ) ;
1819+ let combinator = match input. next ( ) ? {
1820+ Token :: Delim ( '>' ) => Some ( Combinator :: Child ) ,
1821+ Token :: Delim ( '+' ) => Some ( Combinator :: NextSibling ) ,
1822+ Token :: Delim ( '~' ) => Some ( Combinator :: LaterSibling ) ,
1823+ _ => {
1824+ input. reset ( & s) ;
1825+ None
1826+ }
1827+ } ;
1828+
1829+ let mut selector = parse_selector ( parser, input, state, NestingRequirement :: None ) ?;
1830+ if let Some ( combinator) = combinator {
1831+ // https://www.w3.org/TR/selectors/#absolutizing
1832+ selector. 1 . push ( Component :: Combinator ( combinator) ) ;
1833+ selector. 1 . push ( Component :: Scope ) ;
1834+ }
1835+
1836+ Ok ( selector)
1837+ }
1838+
17601839/// * `Err(())`: Invalid selector, abort
17611840/// * `Ok(false)`: Not a type selector, could be something else. `input` was not consumed.
17621841/// * `Ok(true)`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`)
@@ -2315,6 +2394,27 @@ where
23152394 Ok ( component ( inner. 0 . into_vec ( ) . into_boxed_slice ( ) ) )
23162395}
23172396
2397+ fn parse_has < ' i , ' t , P , Impl > (
2398+ parser : & P ,
2399+ input : & mut CssParser < ' i , ' t > ,
2400+ state : & mut SelectorParsingState ,
2401+ ) -> Result < Component < ' i , Impl > , ParseError < ' i , P :: Error > >
2402+ where
2403+ P : Parser < ' i , Impl = Impl > ,
2404+ Impl : SelectorImpl < ' i > ,
2405+ {
2406+ let mut child_state = * state;
2407+ let inner = SelectorList :: parse_relative (
2408+ parser,
2409+ input,
2410+ & mut child_state,
2411+ parser. is_and_where_error_recovery ( ) ,
2412+ ) ?;
2413+ if child_state. contains ( SelectorParsingState :: AFTER_NESTING ) {
2414+ state. insert ( SelectorParsingState :: AFTER_NESTING )
2415+ }
2416+ Ok ( Component :: Has ( inner. 0 . into_vec ( ) . into_boxed_slice ( ) ) )
2417+ }
23182418fn parse_functional_pseudo_class < ' i , ' t , P , Impl > (
23192419 parser : & P ,
23202420 input : & mut CssParser < ' i , ' t > ,
@@ -2332,6 +2432,7 @@ where
23322432 "nth-last-of-type" => return parse_nth_pseudo_class( parser, input, * state, Component :: NthLastOfType ) ,
23332433 "is" if parser. parse_is_and_where( ) => return parse_is_or_where( parser, input, state, Component :: Is ) ,
23342434 "where" if parser. parse_is_and_where( ) => return parse_is_or_where( parser, input, state, Component :: Where ) ,
2435+ "has" => return parse_has( parser, input, state) ,
23352436 "host" => {
23362437 if !state. allows_tree_structural_pseudo_classes( ) {
23372438 return Err ( input. new_custom_error( SelectorParseErrorKind :: InvalidState ) ) ;
0 commit comments