@@ -36,7 +36,7 @@ List<ComplexSelector>? unifyComplex(
36
36
List <ComplexSelector > complexes, FileSpan span) {
37
37
if (complexes.length == 1 ) return complexes;
38
38
39
- List < SimpleSelector > ? unifiedBase;
39
+ CompoundSelector ? unifiedBase;
40
40
CssValue <Combinator >? leadingCombinator;
41
41
CssValue <Combinator >? trailingCombinator;
42
42
for (var complex in complexes) {
@@ -67,12 +67,10 @@ List<ComplexSelector>? unifyComplex(
67
67
}
68
68
69
69
if (unifiedBase == null ) {
70
- unifiedBase = base .selector.components ;
70
+ unifiedBase = base .selector;
71
71
} else {
72
- for (var simple in base .selector.components) {
73
- unifiedBase = simple.unify (unifiedBase! ); // dart-lang/sdk#45348
74
- if (unifiedBase == null ) return null ;
75
- }
72
+ unifiedBase = unifyCompound (unifiedBase, base .selector);
73
+ if (unifiedBase == null ) return null ;
76
74
}
77
75
}
78
76
@@ -87,7 +85,7 @@ List<ComplexSelector>? unifyComplex(
87
85
var base = ComplexSelector (
88
86
leadingCombinator == null ? const [] : [leadingCombinator],
89
87
[
90
- ComplexSelectorComponent (CompoundSelector ( unifiedBase! , span) ,
88
+ ComplexSelectorComponent (unifiedBase! ,
91
89
trailingCombinator == null ? const [] : [trailingCombinator], span)
92
90
],
93
91
span,
@@ -106,19 +104,44 @@ List<ComplexSelector>? unifyComplex(
106
104
/// Returns a [CompoundSelector] that matches only elements that are matched by
107
105
/// both [compound1] and [compound2] .
108
106
///
109
- /// The [span] will be used for the new unified selector.
107
+ /// The [compound1] 's `span` will be used for the new unified selector.
108
+ ///
109
+ /// This function ensures that the relative order of pseudo-classes (`:` ) and
110
+ /// pseudo-elements (`::` ) within each input selector is preserved in the
111
+ /// resulting combined selector.
112
+ ///
113
+ /// This function enforces a general rule that pseudo-classes (`:` ) should come
114
+ /// before pseudo-elements (`::` ), but it won't change their order if they were
115
+ /// originally interleaved within a single input selector. This prevents
116
+ /// unintended changes to the selector's meaning. For example, unifying
117
+ /// `::foo:bar` and `:baz` results in `:baz::foo:bar` . `:baz` is a pseudo-class,
118
+ /// so it is moved before the pseudo-class `::foo` . Meanwhile, `:bar` is not
119
+ /// moved before `::foo` because it appeared after `::foo` in the original
120
+ /// selector.
110
121
///
111
122
/// If no such selector can be produced, returns `null` .
112
123
CompoundSelector ? unifyCompound (
113
124
CompoundSelector compound1, CompoundSelector compound2) {
114
- var result = compound2.components;
115
- for (var simple in compound1.components) {
116
- var unified = simple.unify (result);
117
- if (unified == null ) return null ;
118
- result = unified;
125
+ var result = compound1.components;
126
+ var pseudoResult = < SimpleSelector > [];
127
+ var pseudoElementFound = false ;
128
+
129
+ for (var simple in compound2.components) {
130
+ // All pseudo-classes are unified separately after a pseudo-element to
131
+ // preserve their relative order with the pseudo-element.
132
+ if (pseudoElementFound && simple is PseudoSelector ) {
133
+ var unified = simple.unify (pseudoResult);
134
+ if (unified == null ) return null ;
135
+ pseudoResult = unified;
136
+ } else {
137
+ pseudoElementFound | = simple is PseudoSelector && simple.isElement;
138
+ var unified = simple.unify (result);
139
+ if (unified == null ) return null ;
140
+ result = unified;
141
+ }
119
142
}
120
143
121
- return CompoundSelector (result, compound1.span);
144
+ return CompoundSelector ([... result, ...pseudoResult] , compound1.span);
122
145
}
123
146
124
147
/// Returns a [SimpleSelector] that matches only elements that are matched by
0 commit comments