1313import 'dart:collection' ;
1414
1515import 'package:collection/collection.dart' ;
16+ import 'package:tuple/tuple.dart' ;
1617
1718import '../ast/selector.dart' ;
1819import '../utils.dart' ;
1920
20- /// Names of pseudo selectors that take selectors as arguments, and that are
21- /// subselectors of their arguments.
22- ///
23- /// For example, `.foo` is a superselector of `:matches(.foo)` .
24- final _subselectorPseudos = {
25- 'is' ,
26- 'matches' ,
27- 'where' ,
28- 'any' ,
29- 'nth-child' ,
30- 'nth-last-child'
31- };
32-
3321/// Returns the contents of a [SelectorList] that matches only elements that are
3422/// matched by every complex selector in [complexes] .
3523///
@@ -689,6 +677,29 @@ bool complexIsSuperselector(List<ComplexSelectorComponent> complex1,
689677bool compoundIsSuperselector (
690678 CompoundSelector compound1, CompoundSelector compound2,
691679 {Iterable <ComplexSelectorComponent >? parents}) {
680+ // Pseudo elements effectively change the target of a compound selector rather
681+ // than narrowing the set of elements to which it applies like other
682+ // selectors. As such, if either selector has a pseudo element, they both must
683+ // have the _same_ pseudo element.
684+ //
685+ // In addition, order matters when pseudo-elements are involved. The selectors
686+ // before them must
687+ var tuple1 = _findPseudoElementIndexed (compound1);
688+ var tuple2 = _findPseudoElementIndexed (compound2);
689+ if (tuple1 != null && tuple2 != null ) {
690+ return tuple1.item1.isSuperselector (tuple2.item1) &&
691+ _compoundComponentsIsSuperselector (
692+ compound1.components.take (tuple1.item2),
693+ compound2.components.take (tuple2.item2),
694+ parents: parents) &&
695+ _compoundComponentsIsSuperselector (
696+ compound1.components.skip (tuple1.item2 + 1 ),
697+ compound2.components.skip (tuple2.item2 + 1 ),
698+ parents: parents);
699+ } else if (tuple1 != null || tuple2 != null ) {
700+ return false ;
701+ }
702+
692703 // Every selector in [compound1.components] must have a matching selector in
693704 // [compound2.components].
694705 for (var simple1 in compound1.components) {
@@ -697,49 +708,44 @@ bool compoundIsSuperselector(
697708 parents: parents)) {
698709 return false ;
699710 }
700- } else if (! _simpleIsSuperselectorOfCompound (simple1, compound2)) {
701- return false ;
702- }
703- }
704-
705- // [compound1] can't be a superselector of a selector with non-selector
706- // pseudo-elements that [compound2] doesn't share.
707- for (var simple2 in compound2.components) {
708- if (simple2 is PseudoSelector &&
709- simple2.isElement &&
710- simple2.selector == null &&
711- ! _simpleIsSuperselectorOfCompound (simple2, compound1)) {
711+ } else if (! compound2.components.any (simple1.isSuperselector)) {
712712 return false ;
713713 }
714714 }
715715
716716 return true ;
717717}
718718
719- /// Returns whether [simple] is a superselector of [compound] .
719+ /// If [compound] contains a pseudo-element, returns it and its index in
720+ /// [compound.components] .
721+ Tuple2 <PseudoSelector , int >? _findPseudoElementIndexed (
722+ CompoundSelector compound) {
723+ for (var i = 0 ; i < compound.components.length; i++ ) {
724+ var simple = compound.components[i];
725+ if (simple is PseudoSelector && simple.isElement) return Tuple2 (simple, i);
726+ }
727+ return null ;
728+ }
729+
730+ /// Like [compoundIsSuperselector] but operates on the underlying lists of
731+ /// simple selectors.
720732///
721- /// That is, whether [simple] matches every element that [compound] matches, as
722- /// well as possibly additional elements.
723- bool _simpleIsSuperselectorOfCompound (
724- SimpleSelector simple, CompoundSelector compound) {
725- return compound.components.any ((theirSimple) {
726- if (simple == theirSimple) return true ;
727-
728- // Some selector pseudoclasses can match normal selectors.
729- if (theirSimple is ! PseudoSelector ) return false ;
730- var selector = theirSimple.selector;
731- if (selector == null ) return false ;
732- if (! _subselectorPseudos.contains (theirSimple.normalizedName)) return false ;
733-
734- return selector.components.every ((complex) =>
735- complex.singleCompound? .components.contains (simple) ?? false );
736- });
733+ /// The [compound1] and [compound2] are expected to have efficient
734+ /// [Iterable.length] fields.
735+ bool _compoundComponentsIsSuperselector (
736+ Iterable <SimpleSelector > compound1, Iterable <SimpleSelector > compound2,
737+ {Iterable <ComplexSelectorComponent >? parents}) {
738+ if (compound1.isEmpty) return true ;
739+ if (compound2.isEmpty) compound2 = [UniversalSelector (namespace: '*' )];
740+ return compoundIsSuperselector (
741+ CompoundSelector (compound1), CompoundSelector (compound2),
742+ parents: parents);
737743}
738744
739745/// Returns whether [pseudo1] is a superselector of [compound2] .
740746///
741- /// That is, whether [pseudo1] matches every element that [compound2] matches, as well
742- /// as possibly additional elements.
747+ /// That is, whether [pseudo1] matches every element that [compound2] matches,
748+ /// as well as possibly additional elements.
743749///
744750/// This assumes that [pseudo1] 's `selector` argument is not `null` .
745751///
0 commit comments