OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of csslib.parser; | |
6 | |
7 /** | |
8 * CSS polyfill emits CSS to be understood by older parsers that which do not | |
9 * understand (var, calc, etc.). | |
10 */ | |
11 class PolyFill { | |
12 final Messages _messages; | |
13 final bool _warningsAsErrors; | |
14 Map<String, VarDefinition> _allVarDefinitions = | |
15 new Map<String, VarDefinition>(); | |
16 | |
17 Set<StyleSheet> allStyleSheets = new Set<StyleSheet>(); | |
18 | |
19 /** | |
20 * [_pseudoElements] list of known pseudo attributes found in HTML, any | |
21 * CSS pseudo-elements 'name::custom-element' is mapped to the manged name | |
22 * associated with the pseudo-element key. | |
23 */ | |
24 PolyFill(this._messages, this._warningsAsErrors); | |
25 | |
26 /** | |
27 * Run the analyzer on every file that is a style sheet or any component that | |
28 * has a style tag. | |
29 */ | |
30 void process(StyleSheet styleSheet, {List<StyleSheet> includes: null}) { | |
31 if (includes != null) { | |
32 processVarDefinitions(includes); | |
33 } | |
34 processVars(styleSheet); | |
35 | |
36 // Remove all var definitions for this style sheet. | |
37 new _RemoveVarDefinitions().visitTree(styleSheet); | |
38 } | |
39 | |
40 /** Process all includes looking for var definitions. */ | |
41 void processVarDefinitions(List<StyleSheet> includes) { | |
42 for (var include in includes) { | |
43 _allVarDefinitions = (new _VarDefinitionsIncludes(_allVarDefinitions) | |
44 ..visitTree(include)).varDefs; | |
45 } | |
46 } | |
47 | |
48 void processVars(StyleSheet styleSheet) { | |
49 // Build list of all var definitions. | |
50 var mainStyleSheetVarDefs = (new _VarDefAndUsage( | |
51 this._messages, _allVarDefinitions)..visitTree(styleSheet)).varDefs; | |
52 | |
53 // Resolve all definitions to a non-VarUsage (terminal expression). | |
54 mainStyleSheetVarDefs.forEach((key, value) { | |
55 for (var _ in (value.expression as Expressions).expressions) { | |
56 mainStyleSheetVarDefs[key] = | |
57 _findTerminalVarDefinition(_allVarDefinitions, value); | |
58 } | |
59 }); | |
60 } | |
61 } | |
62 | |
63 /** Build list of all var definitions in all includes. */ | |
64 class _VarDefinitionsIncludes extends Visitor { | |
65 final Map<String, VarDefinition> varDefs; | |
66 | |
67 _VarDefinitionsIncludes(this.varDefs); | |
68 | |
69 void visitTree(StyleSheet tree) { | |
70 visitStyleSheet(tree); | |
71 } | |
72 | |
73 visitVarDefinition(VarDefinition node) { | |
74 // Replace with latest variable definition. | |
75 varDefs[node.definedName] = node; | |
76 super.visitVarDefinition(node); | |
77 } | |
78 | |
79 void visitVarDefinitionDirective(VarDefinitionDirective node) { | |
80 visitVarDefinition(node.def); | |
81 } | |
82 } | |
83 | |
84 /** | |
85 * Find var- definitions in a style sheet. | |
86 * [found] list of known definitions. | |
87 */ | |
88 class _VarDefAndUsage extends Visitor { | |
89 final Messages _messages; | |
90 final Map<String, VarDefinition> _knownVarDefs; | |
91 final Map<String, VarDefinition> varDefs = new Map<String, VarDefinition>(); | |
92 | |
93 VarDefinition currVarDefinition; | |
94 List<Expression> currentExpressions; | |
95 | |
96 _VarDefAndUsage(this._messages, this._knownVarDefs); | |
97 | |
98 void visitTree(StyleSheet tree) { | |
99 visitStyleSheet(tree); | |
100 } | |
101 | |
102 visitVarDefinition(VarDefinition node) { | |
103 // Replace with latest variable definition. | |
104 currVarDefinition = node; | |
105 | |
106 _knownVarDefs[node.definedName] = node; | |
107 varDefs[node.definedName] = node; | |
108 | |
109 super.visitVarDefinition(node); | |
110 | |
111 currVarDefinition = null; | |
112 } | |
113 | |
114 void visitVarDefinitionDirective(VarDefinitionDirective node) { | |
115 visitVarDefinition(node.def); | |
116 } | |
117 | |
118 void visitExpressions(Expressions node) { | |
119 currentExpressions = node.expressions; | |
120 super.visitExpressions(node); | |
121 currentExpressions = null; | |
122 } | |
123 | |
124 void visitVarUsage(VarUsage node) { | |
125 if (currVarDefinition != null && currVarDefinition.badUsage) return; | |
126 | |
127 // Don't process other var() inside of a varUsage. That implies that the | |
128 // default is a var() too. Also, don't process any var() inside of a | |
129 // varDefinition (they're just place holders until we've resolved all real | |
130 // usages. | |
131 var expressions = currentExpressions; | |
132 var index = expressions.indexOf(node); | |
133 assert(index >= 0); | |
134 var def = _knownVarDefs[node.name]; | |
135 if (def != null) { | |
136 if (def.badUsage) { | |
137 // Remove any expressions pointing to a bad var definition. | |
138 expressions.removeAt(index); | |
139 return; | |
140 } | |
141 _resolveVarUsage(currentExpressions, index, | |
142 _findTerminalVarDefinition(_knownVarDefs, def)); | |
143 } else if (node.defaultValues.any((e) => e is VarUsage)) { | |
144 // Don't have a VarDefinition need to use default values resolve all | |
145 // default values. | |
146 var terminalDefaults = <Expression>[]; | |
147 for (var defaultValue in node.defaultValues) { | |
148 terminalDefaults.addAll(resolveUsageTerminal(defaultValue)); | |
149 } | |
150 expressions.replaceRange(index, index + 1, terminalDefaults); | |
151 } else if (node.defaultValues.isNotEmpty) { | |
152 // No VarDefinition but default value is a terminal expression; use it. | |
153 expressions.replaceRange(index, index + 1, node.defaultValues); | |
154 } else { | |
155 if (currVarDefinition != null) { | |
156 currVarDefinition.badUsage = true; | |
157 var mainStyleSheetDef = varDefs[node.name]; | |
158 if (mainStyleSheetDef != null) { | |
159 varDefs.remove(currVarDefinition.property); | |
160 } | |
161 } | |
162 // Remove var usage that points at an undefined definition. | |
163 expressions.removeAt(index); | |
164 _messages.warning("Variable is not defined.", node.span); | |
165 } | |
166 | |
167 var oldExpressions = currentExpressions; | |
168 currentExpressions = node.defaultValues; | |
169 super.visitVarUsage(node); | |
170 currentExpressions = oldExpressions; | |
171 } | |
172 | |
173 List<Expression> resolveUsageTerminal(VarUsage usage) { | |
174 var result = []; | |
175 | |
176 var varDef = _knownVarDefs[usage.name]; | |
177 var expressions; | |
178 if (varDef == null) { | |
179 // VarDefinition not found try the defaultValues. | |
180 expressions = usage.defaultValues; | |
181 } else { | |
182 // Use the VarDefinition found. | |
183 expressions = (varDef.expression as Expressions).expressions; | |
184 } | |
185 | |
186 for (var expr in expressions) { | |
187 if (expr is VarUsage) { | |
188 // Get terminal value. | |
189 result.addAll(resolveUsageTerminal(expr)); | |
190 } | |
191 } | |
192 | |
193 // We're at a terminal just return the VarDefinition expression. | |
194 if (result.isEmpty && varDef != null) { | |
195 result = (varDef.expression as Expressions).expressions; | |
196 } | |
197 | |
198 return result; | |
199 } | |
200 | |
201 _resolveVarUsage(List<Expression> expressions, int index, VarDefinition def) { | |
202 var defExpressions = (def.expression as Expressions).expressions; | |
203 expressions.replaceRange(index, index + 1, defExpressions); | |
204 } | |
205 } | |
206 | |
207 /** Remove all var definitions. */ | |
208 class _RemoveVarDefinitions extends Visitor { | |
209 void visitTree(StyleSheet tree) { | |
210 visitStyleSheet(tree); | |
211 } | |
212 | |
213 void visitStyleSheet(StyleSheet ss) { | |
214 ss.topLevels.removeWhere((e) => e is VarDefinitionDirective); | |
215 super.visitStyleSheet(ss); | |
216 } | |
217 | |
218 void visitDeclarationGroup(DeclarationGroup node) { | |
219 node.declarations.removeWhere((e) => e is VarDefinition); | |
220 super.visitDeclarationGroup(node); | |
221 } | |
222 } | |
223 | |
224 /** Find terminal definition (non VarUsage implies real CSS value). */ | |
225 VarDefinition _findTerminalVarDefinition( | |
226 Map<String, VarDefinition> varDefs, VarDefinition varDef) { | |
227 var expressions = varDef.expression as Expressions; | |
228 for (var expr in expressions.expressions) { | |
229 if (expr is VarUsage) { | |
230 var usageName = (expr as VarUsage).name; | |
231 var foundDef = varDefs[usageName]; | |
232 | |
233 // If foundDef is unknown check if defaultValues; if it exist then resolve | |
234 // to terminal value. | |
235 if (foundDef == null) { | |
236 // We're either a VarUsage or terminal definition if in varDefs; | |
237 // either way replace VarUsage with it's default value because the | |
238 // VarDefinition isn't found. | |
239 var defaultValues = (expr as VarUsage).defaultValues; | |
240 var replaceExprs = expressions.expressions; | |
241 assert(replaceExprs.length == 1); | |
242 replaceExprs.replaceRange(0, 1, defaultValues); | |
243 return varDef; | |
244 } | |
245 if (foundDef is VarDefinition) { | |
246 return _findTerminalVarDefinition(varDefs, foundDef); | |
247 } | |
248 } else { | |
249 // Return real CSS property. | |
250 return varDef; | |
251 } | |
252 } | |
253 | |
254 // Didn't point to a var definition that existed. | |
255 return varDef; | |
256 } | |
OLD | NEW |