77 * Copyright (C) 2022-2024 The WebF authors. All rights reserved.
88 */
99
10- import 'package:quiver/core.dart' ;
11- import 'package:collection/collection.dart' ;
1210import 'package:webf/css.dart' ;
1311import 'package:webf/src/foundation/logger.dart' ;
1412
13+ bool cssRuleListsStructurallyEqual (List <CSSRule > left, List <CSSRule > right) {
14+ if (identical (left, right)) return true ;
15+ if (left.length != right.length) return false ;
16+ for (int index = 0 ; index < left.length; index++ ) {
17+ if (! cssRulesStructurallyEqual (left[index], right[index])) {
18+ return false ;
19+ }
20+ }
21+ return true ;
22+ }
23+
24+ int cssRuleListStructuralHash (Iterable <CSSRule > rules) {
25+ return Object .hashAll (rules.map (cssRuleStructuralHash));
26+ }
27+
28+ bool cssRulesStructurallyEqual (CSSRule ? left, CSSRule ? right) {
29+ if (identical (left, right)) return true ;
30+ if (left == null || right == null ) return left == right;
31+ if (left.runtimeType != right.runtimeType) return false ;
32+
33+ if (left is CSSStyleRule && right is CSSStyleRule ) {
34+ return left.selectorGroup.structurallyEquals (right.selectorGroup) &&
35+ left.layerPath.length == right.layerPath.length &&
36+ _stringListsEqual (left.layerPath, right.layerPath) &&
37+ left.declaration.structurallyEquals (right.declaration);
38+ }
39+
40+ if (left is CSSLayerStatementRule && right is CSSLayerStatementRule ) {
41+ return _layerNamePathsEqual (left.layerNamePaths, right.layerNamePaths);
42+ }
43+
44+ if (left is CSSLayerBlockRule && right is CSSLayerBlockRule ) {
45+ return left.name == right.name &&
46+ _stringListsEqual (left.layerNamePath, right.layerNamePath) &&
47+ cssRuleListsStructurallyEqual (left.cssRules, right.cssRules);
48+ }
49+
50+ if (left is CSSImportRule && right is CSSImportRule ) {
51+ return left.href == right.href && left.media == right.media;
52+ }
53+
54+ if (left is CSSKeyframesRule && right is CSSKeyframesRule ) {
55+ return left.name == right.name &&
56+ _keyframesEqual (left.keyframes, right.keyframes);
57+ }
58+
59+ if (left is CSSFontFaceRule && right is CSSFontFaceRule ) {
60+ return left.declarations.structurallyEquals (right.declarations);
61+ }
62+
63+ if (left is CSSMediaDirective && right is CSSMediaDirective ) {
64+ return _mediaQueriesEqual (left.cssMediaQuery, right.cssMediaQuery) &&
65+ cssRuleListsStructurallyEqual (
66+ left.rules ?? const < CSSRule > [], right.rules ?? const < CSSRule > []);
67+ }
68+
69+ return left.cssText == right.cssText;
70+ }
71+
72+ int cssRuleStructuralHash (CSSRule rule) {
73+ if (rule is CSSStyleRule ) {
74+ return Object .hash (
75+ rule.type,
76+ rule.selectorGroup.structuralHashCode,
77+ Object .hashAll (rule.layerPath),
78+ rule.declaration.structuralHashCode,
79+ );
80+ }
81+
82+ if (rule is CSSLayerStatementRule ) {
83+ return Object .hash (
84+ rule.type,
85+ Object .hashAll (rule.layerNamePaths.map ((path) => Object .hashAll (path))),
86+ );
87+ }
88+
89+ if (rule is CSSLayerBlockRule ) {
90+ return Object .hash (
91+ rule.type,
92+ rule.name,
93+ Object .hashAll (rule.layerNamePath),
94+ cssRuleListStructuralHash (rule.cssRules),
95+ );
96+ }
97+
98+ if (rule is CSSImportRule ) {
99+ return Object .hash (rule.type, rule.href, rule.media);
100+ }
101+
102+ if (rule is CSSKeyframesRule ) {
103+ return Object .hash (
104+ rule.type,
105+ rule.name,
106+ Object .hashAll (rule.keyframes.map ((keyframe) => Object .hash (
107+ keyframe.property,
108+ keyframe.value,
109+ keyframe.offset,
110+ keyframe.easing,
111+ ))),
112+ );
113+ }
114+
115+ if (rule is CSSFontFaceRule ) {
116+ return Object .hash (rule.type, rule.declarations.structuralHashCode);
117+ }
118+
119+ if (rule is CSSMediaDirective ) {
120+ return Object .hash (
121+ rule.type,
122+ _mediaQueryStructuralHash (rule.cssMediaQuery),
123+ cssRuleListStructuralHash (rule.rules ?? const < CSSRule > []),
124+ );
125+ }
126+
127+ return Object .hash (rule.type, rule.cssText);
128+ }
129+
130+ bool _layerNamePathsEqual (List <List <String >> left, List <List <String >> right) {
131+ if (identical (left, right)) return true ;
132+ if (left.length != right.length) return false ;
133+ for (int index = 0 ; index < left.length; index++ ) {
134+ if (! _stringListsEqual (left[index], right[index])) return false ;
135+ }
136+ return true ;
137+ }
138+
139+ bool _stringListsEqual (List <String > left, List <String > right) {
140+ if (identical (left, right)) return true ;
141+ if (left.length != right.length) return false ;
142+ for (int index = 0 ; index < left.length; index++ ) {
143+ if (left[index] != right[index]) return false ;
144+ }
145+ return true ;
146+ }
147+
148+ bool _keyframesEqual (List <Keyframe > left, List <Keyframe > right) {
149+ if (identical (left, right)) return true ;
150+ if (left.length != right.length) return false ;
151+ for (int index = 0 ; index < left.length; index++ ) {
152+ final Keyframe leftKeyframe = left[index];
153+ final Keyframe rightKeyframe = right[index];
154+ if (leftKeyframe.property != rightKeyframe.property ||
155+ leftKeyframe.value != rightKeyframe.value ||
156+ leftKeyframe.offset != rightKeyframe.offset ||
157+ leftKeyframe.easing != rightKeyframe.easing) {
158+ return false ;
159+ }
160+ }
161+ return true ;
162+ }
163+
164+ bool _mediaQueriesEqual (CSSMediaQuery ? left, CSSMediaQuery ? right) {
165+ if (identical (left, right)) return true ;
166+ if (left == null || right == null ) return left == right;
167+ if (left._mediaUnary != right._mediaUnary) return false ;
168+ if (left._mediaType? .name != right._mediaType? .name) return false ;
169+ if (left.expressions.length != right.expressions.length) return false ;
170+ for (int index = 0 ; index < left.expressions.length; index++ ) {
171+ final CSSMediaExpression leftExpression = left.expressions[index];
172+ final CSSMediaExpression rightExpression = right.expressions[index];
173+ if (leftExpression.op != rightExpression.op) return false ;
174+ final Map <String , String >? leftStyle = leftExpression.mediaStyle;
175+ final Map <String , String >? rightStyle = rightExpression.mediaStyle;
176+ if (leftStyle == null || rightStyle == null ) {
177+ if (leftStyle != rightStyle) return false ;
178+ continue ;
179+ }
180+ if (leftStyle.length != rightStyle.length) return false ;
181+ final List <String > keys = leftStyle.keys.toList (growable: false )..sort ();
182+ final List <String > otherKeys = rightStyle.keys.toList (growable: false )
183+ ..sort ();
184+ if (! _stringListsEqual (keys, otherKeys)) return false ;
185+ for (final String key in keys) {
186+ if (leftStyle[key] != rightStyle[key]) return false ;
187+ }
188+ }
189+ return true ;
190+ }
191+
192+ int _mediaQueryStructuralHash (CSSMediaQuery ? mediaQuery) {
193+ if (mediaQuery == null ) return 0 ;
194+ return Object .hash (
195+ mediaQuery._mediaUnary,
196+ mediaQuery._mediaType? .name,
197+ Object .hashAll (mediaQuery.expressions.map ((expression) {
198+ final Map <String , String >? mediaStyle = expression.mediaStyle;
199+ if (mediaStyle == null ) {
200+ return Object .hash (expression.op, null );
201+ }
202+ final List <String > keys = mediaStyle.keys.toList (growable: false )..sort ();
203+ return Object .hash (
204+ expression.op,
205+ Object .hashAll (keys.map ((key) => Object .hash (key, mediaStyle[key]))),
206+ );
207+ })),
208+ );
209+ }
210+
15211/// https://drafts.csswg.org/cssom/#the-cssstylerule-interface
16212class CSSStyleRule extends CSSRule {
17213 @override
18- String get cssText => '${selectorGroup .selectorText } {${declaration .cssText }}' ;
214+ String get cssText =>
215+ '${selectorGroup .selectorText } {${declaration .cssText }}' ;
19216
20217 @override
21218 int get type => CSSRule .STYLE_RULE ;
@@ -25,12 +222,10 @@ class CSSStyleRule extends CSSRule {
25222
26223 CSSStyleRule (this .selectorGroup, this .declaration) : super ();
27224
28- @override
29- int get hashCode => hash2 (selectorGroup, declaration);
225+ int get structuralHashCode => cssRuleStructuralHash (this );
30226
31- @override
32- bool operator == (Object other) {
33- return hashCode == other.hashCode;
227+ bool structurallyEquals (CSSStyleRule other) {
228+ return cssRulesStructurallyEqual (this , other);
34229 }
35230}
36231
@@ -58,29 +253,6 @@ class CSSLayerStatementRule extends CSSRule {
58253
59254 @override
60255 int get type => CSSRule .LAYER_STATEMENT_RULE ;
61-
62- static bool _deepEquals (List <List <String >> a, List <List <String >> b) {
63- if (identical (a, b)) return true ;
64- if (a.length != b.length) return false ;
65- for (var i = 0 ; i < a.length; i++ ) {
66- final ai = a[i];
67- final bi = b[i];
68- if (ai.length != bi.length) return false ;
69- for (var j = 0 ; j < ai.length; j++ ) {
70- if (ai[j] != bi[j]) return false ;
71- }
72- }
73- return true ;
74- }
75-
76- @override
77- int get hashCode => hashObjects (layerNamePaths.map ((p) => hashObjects (p)));
78-
79- @override
80- bool operator == (Object other) {
81- return other is CSSLayerStatementRule &&
82- _deepEquals (layerNamePaths, other.layerNamePaths);
83- }
84256}
85257
86258/// Represents a CSS Cascade Layer block: `@layer name { ... }` .
@@ -107,30 +279,6 @@ class CSSLayerBlockRule extends CSSRule {
107279 @override
108280 int get type => CSSRule .LAYER_BLOCK_RULE ;
109281
110- @override
111- int get hashCode => hashObjects (< Object ? > [
112- name,
113- hashObjects (layerNamePath),
114- hashObjects (cssRules),
115- ]);
116-
117- static bool _listEquals (List <String > a, List <String > b) {
118- if (identical (a, b)) return true ;
119- if (a.length != b.length) return false ;
120- for (int i = 0 ; i < a.length; i++ ) {
121- if (a[i] != b[i]) return false ;
122- }
123- return true ;
124- }
125-
126- @override
127- bool operator == (Object other) {
128- return other is CSSLayerBlockRule &&
129- other.name == name &&
130- _listEquals (other.layerNamePath, layerNamePath) &&
131- const ListEquality <CSSRule >().equals (other.cssRules, cssRules);
132- }
133-
134282 int insertRule (CSSRule rule, int index) {
135283 if (index < 0 || index > cssRules.length) {
136284 throw RangeError .index (index, cssRules, 'index' );
0 commit comments