@@ -47,12 +47,24 @@ pub trait PseudoElement: Sized + ToCss {
4747 false
4848 }
4949
50+ /// Whether this pseudo-element is valid when directly after a ::before/::after pseudo.
51+ fn valid_after_before_or_after ( & self ) -> bool {
52+ false
53+ }
54+
5055 /// Whether this pseudo-element is element-backed.
5156 /// https://drafts.csswg.org/css-pseudo-4/#element-like
5257 fn is_element_backed ( & self ) -> bool {
5358 false
5459 }
5560
61+ /// Whether this pseudo-element is ::before or ::after pseudo element,
62+ /// which are treated specially when deciding what can come after them.
63+ /// https://drafts.csswg.org/css-pseudo-4/#generated-content
64+ fn is_before_or_after ( & self ) -> bool {
65+ false
66+ }
67+
5668 /// The count we contribute to the specificity from this pseudo-element.
5769 fn specificity_count ( & self ) -> u32 {
5870 1
@@ -129,32 +141,30 @@ bitflags! {
129141 /// disallowed. If this flag is set, `AFTER_NON_ELEMENT_BACKED_PSEUDO` must be set
130142 /// as well.
131143 const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 4 ;
144+ // Whether we've parsed a generated pseudo-element (as in ::before, ::after).
145+ // If so then some other pseudo elements are disallowed (e.g. another generated pseudo)
146+ // while others allowed (e.g. ::marker).
147+ const AFTER_BEFORE_OR_AFTER_PSEUDO = 1 << 5 ;
132148
133149 /// Whether we are after any of the pseudo-like things.
134- const AFTER_PSEUDO = Self :: AFTER_PART_LIKE . bits( ) | Self :: AFTER_SLOTTED . bits( ) | Self :: AFTER_NON_ELEMENT_BACKED_PSEUDO . bits( ) ;
150+ const AFTER_PSEUDO = Self :: AFTER_PART_LIKE . bits( ) | Self :: AFTER_SLOTTED . bits( ) | Self :: AFTER_NON_ELEMENT_BACKED_PSEUDO . bits( ) | Self :: AFTER_BEFORE_OR_AFTER_PSEUDO . bits ( ) ;
135151
136152 /// Whether we explicitly disallow combinators.
137- const DISALLOW_COMBINATORS = 1 << 5 ;
153+ const DISALLOW_COMBINATORS = 1 << 6 ;
138154
139155 /// Whether we explicitly disallow pseudo-element-like things.
140- const DISALLOW_PSEUDOS = 1 << 6 ;
156+ const DISALLOW_PSEUDOS = 1 << 7 ;
141157
142158 /// Whether we explicitly disallow relative selectors (i.e. `:has()`).
143- const DISALLOW_RELATIVE_SELECTOR = 1 << 7 ;
159+ const DISALLOW_RELATIVE_SELECTOR = 1 << 8 ;
144160
145161 /// Whether we've parsed a pseudo-element which is in a pseudo-element tree (i.e. it is a
146162 /// descendant pseudo of a pseudo-element root).
147- const IN_PSEUDO_ELEMENT_TREE = 1 << 8 ;
163+ const IN_PSEUDO_ELEMENT_TREE = 1 << 9 ;
148164 }
149165}
150166
151167impl SelectorParsingState {
152- #[ inline]
153- fn allows_pseudos ( self ) -> bool {
154- // NOTE(emilio): We allow pseudos after ::part and such.
155- !self . intersects ( Self :: AFTER_NON_ELEMENT_BACKED_PSEUDO | Self :: DISALLOW_PSEUDOS )
156- }
157-
158168 #[ inline]
159169 fn allows_slotted ( self ) -> bool {
160170 !self . intersects ( Self :: AFTER_PSEUDO | Self :: DISALLOW_PSEUDOS )
@@ -2772,6 +2782,7 @@ where
27722782 if state. intersects ( SelectorParsingState :: AFTER_PSEUDO ) {
27732783 debug_assert ! ( state. intersects(
27742784 SelectorParsingState :: AFTER_NON_ELEMENT_BACKED_PSEUDO |
2785+ SelectorParsingState :: AFTER_BEFORE_OR_AFTER_PSEUDO |
27752786 SelectorParsingState :: AFTER_SLOTTED |
27762787 SelectorParsingState :: AFTER_PART_LIKE
27772788 ) ) ;
@@ -3310,6 +3321,9 @@ where
33103321 state. insert ( SelectorParsingState :: AFTER_PART_LIKE ) ;
33113322 } else {
33123323 state. insert ( SelectorParsingState :: AFTER_NON_ELEMENT_BACKED_PSEUDO ) ;
3324+ if p. is_before_or_after ( ) {
3325+ state. insert ( SelectorParsingState :: AFTER_BEFORE_OR_AFTER_PSEUDO ) ;
3326+ }
33133327 }
33143328 if !p. accepts_state_pseudo_classes ( ) {
33153329 state. insert ( SelectorParsingState :: AFTER_NON_STATEFUL_PSEUDO_ELEMENT ) ;
@@ -3552,7 +3566,15 @@ where
35523566 } ;
35533567 let is_pseudo_element = !is_single_colon || is_css2_pseudo_element ( & name) ;
35543568 if is_pseudo_element {
3555- if !state. allows_pseudos ( ) {
3569+ // Pseudos after pseudo elements are not allowed in some cases:
3570+ // - Some states will disallow pseudos, such as the interiors of
3571+ // :has/:is/:where/:not (DISALLOW_PSEUDOS).
3572+ // - Non-element backed pseudos do not allow other pseudos to follow (AFTER_NON_ELEMENT_BACKED_PSEUDO)...
3573+ // - ... except ::before and ::after, which allow _some_ pseudos.
3574+ if state. intersects ( SelectorParsingState :: DISALLOW_PSEUDOS )
3575+ || ( state. intersects ( SelectorParsingState :: AFTER_NON_ELEMENT_BACKED_PSEUDO )
3576+ && !state. intersects ( SelectorParsingState :: AFTER_BEFORE_OR_AFTER_PSEUDO ) )
3577+ {
35563578 return Err ( input. new_custom_error ( SelectorParseErrorKind :: InvalidState ) ) ;
35573579 }
35583580 let pseudo_element = if is_functional {
@@ -3590,6 +3612,12 @@ where
35903612 P :: parse_pseudo_element ( parser, location, name) ?
35913613 } ;
35923614
3615+ if state. intersects ( SelectorParsingState :: AFTER_BEFORE_OR_AFTER_PSEUDO ) &&
3616+ !pseudo_element. valid_after_before_or_after ( )
3617+ {
3618+ return Err ( input. new_custom_error ( SelectorParseErrorKind :: InvalidState ) ) ;
3619+ }
3620+
35933621 if state. intersects ( SelectorParsingState :: AFTER_SLOTTED ) &&
35943622 !pseudo_element. valid_after_slotted ( )
35953623 {
@@ -3688,6 +3716,7 @@ pub mod tests {
36883716 pub enum PseudoElement {
36893717 Before ,
36903718 After ,
3719+ Marker ,
36913720 Highlight ( String ) ,
36923721 }
36933722
@@ -3702,8 +3731,16 @@ pub mod tests {
37023731 true
37033732 }
37043733
3734+ fn valid_after_before_or_after ( & self ) -> bool {
3735+ matches ! ( self , Self :: Marker )
3736+ }
3737+
3738+ fn is_before_or_after ( & self ) -> bool {
3739+ matches ! ( self , Self :: Before | Self :: After )
3740+ }
3741+
37053742 fn is_element_backed ( & self ) -> bool {
3706- true
3743+ false
37073744 }
37083745 }
37093746
@@ -3746,6 +3783,7 @@ pub mod tests {
37463783 match * self {
37473784 PseudoElement :: Before => dest. write_str ( "::before" ) ,
37483785 PseudoElement :: After => dest. write_str ( "::after" ) ,
3786+ PseudoElement :: Marker => dest. write_str ( "::marker" ) ,
37493787 PseudoElement :: Highlight ( ref name) => {
37503788 dest. write_str ( "::highlight(" ) ?;
37513789 serialize_identifier ( & name, dest) ?;
@@ -3915,6 +3953,7 @@ pub mod tests {
39153953 match_ignore_ascii_case ! { & name,
39163954 "before" => return Ok ( PseudoElement :: Before ) ,
39173955 "after" => return Ok ( PseudoElement :: After ) ,
3956+ "marker" => return Ok ( PseudoElement :: Marker ) ,
39183957 _ => { }
39193958 }
39203959 Err (
@@ -4579,6 +4618,33 @@ pub mod tests {
45794618 assert_eq ! ( iter. next_sequence( ) , None ) ;
45804619 }
45814620
4621+ #[ test]
4622+ fn test_pseudo_before_marker ( ) {
4623+ let list = parse ( "::before::marker" ) . unwrap ( ) ;
4624+ let selector = & list. slice ( ) [ 0 ] ;
4625+ let mut iter = selector. iter ( ) ;
4626+ assert_eq ! (
4627+ iter. next( ) ,
4628+ Some ( & Component :: PseudoElement ( PseudoElement :: Marker ) )
4629+ ) ;
4630+ assert_eq ! ( iter. next( ) , None ) ;
4631+ let combinator = iter. next_sequence ( ) ;
4632+ assert_eq ! ( combinator, Some ( Combinator :: PseudoElement ) ) ;
4633+ assert ! ( matches!( iter. next( ) , Some ( & Component :: PseudoElement ( PseudoElement :: Before ) ) ) ) ;
4634+ assert_eq ! ( iter. next( ) , None ) ;
4635+ let combinator = iter. next_sequence ( ) ;
4636+ assert_eq ! ( combinator, Some ( Combinator :: PseudoElement ) ) ;
4637+ assert_eq ! ( iter. next( ) , None ) ;
4638+ assert_eq ! ( iter. next_sequence( ) , None ) ;
4639+ }
4640+
4641+ #[ test]
4642+ fn test_pseudo_duplicate_before_after_or_marker ( ) {
4643+ assert ! ( parse( "::before::before" ) . is_err( ) ) ;
4644+ assert ! ( parse( "::after::after" ) . is_err( ) ) ;
4645+ assert ! ( parse( "::marker::marker" ) . is_err( ) ) ;
4646+ }
4647+
45824648 #[ test]
45834649 fn test_universal ( ) {
45844650 let list = parse_ns (
0 commit comments