OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of csslib.parser; | 5 part of csslib.parser; |
6 | 6 |
7 /** | 7 /** |
8 * CSS polyfill emits CSS to be understood by older parsers that which do not | 8 * CSS polyfill emits CSS to be understood by older parsers that which do not |
9 * understand (var, calc, etc.). | 9 * understand (var, calc, etc.). |
10 */ | 10 */ |
11 class PolyFill { | 11 class PolyFill { |
12 final Messages _messages; | 12 final Messages _messages; |
13 final bool _warningsAsErrors; | 13 final bool _warningsAsErrors; |
| 14 Map<String, VarDefinition> _allVarDefinitions = |
| 15 new Map<String, VarDefinition>(); |
14 | 16 |
15 Set<StyleSheet> allStyleSheets = new Set<StyleSheet>(); | 17 Set<StyleSheet> allStyleSheets = new Set<StyleSheet>(); |
16 | 18 |
17 /** | 19 /** |
18 * [_pseudoElements] list of known pseudo attributes found in HTML, any | 20 * [_pseudoElements] list of known pseudo attributes found in HTML, any |
19 * CSS pseudo-elements 'name::custom-element' is mapped to the manged name | 21 * CSS pseudo-elements 'name::custom-element' is mapped to the manged name |
20 * associated with the pseudo-element key. | 22 * associated with the pseudo-element key. |
21 */ | 23 */ |
22 PolyFill(this._messages, this._warningsAsErrors); | 24 PolyFill(this._messages, this._warningsAsErrors); |
23 | 25 |
24 /** | 26 /** |
25 * Run the analyzer on every file that is a style sheet or any component that | 27 * Run the analyzer on every file that is a style sheet or any component that |
26 * has a style tag. | 28 * has a style tag. |
27 */ | 29 */ |
28 void process(StyleSheet stylesheet) { | 30 void process(StyleSheet styleSheet, {List<StyleSheet> includes: null}) { |
29 // TODO(terry): Process all imported stylesheets. | 31 if (includes != null) { |
| 32 processVarDefinitions(includes); |
| 33 } |
| 34 processVars(styleSheet); |
30 | 35 |
31 var styleSheets = processVars([stylesheet]); | 36 // Remove all var definitions for this style sheet. |
32 allStyleSheets.addAll(styleSheets); | 37 new _RemoveVarDefinitions().visitTree(styleSheet); |
33 | |
34 normalize(); | |
35 } | 38 } |
36 | 39 |
37 void normalize() { | 40 /** Process all includes looking for var definitions. */ |
38 // Remove all var definitions for all style sheets analyzed. | 41 void processVarDefinitions(List<StyleSheet> includes) { |
39 for (var tree in allStyleSheets) | 42 for (var include in includes) { |
40 new _RemoveVarDefinitions().visitTree(tree); | 43 _allVarDefinitions = (new _VarDefinitionsIncludes(_allVarDefinitions) |
| 44 ..visitTree(include)).varDefs; |
| 45 } |
41 } | 46 } |
42 | 47 |
43 List<StyleSheet> processVars(List<StyleSheet> styleSheets) { | 48 void processVars(StyleSheet styleSheet) { |
44 // TODO(terry): Process all dependencies. | |
45 // Build list of all var definitions. | 49 // Build list of all var definitions. |
46 Map varDefs = new Map(); | 50 var mainStyleSheetVarDefs = |
47 for (var tree in styleSheets) { | 51 (new _VarDefAndUsage(this._messages, _allVarDefinitions) |
48 var allDefs = (new _VarDefinitions()..visitTree(tree)).found; | 52 ..visitTree(styleSheet)).varDefs; |
49 allDefs.forEach((key, value) { | |
50 varDefs[key] = value; | |
51 }); | |
52 } | |
53 | 53 |
54 // Resolve all definitions to a non-VarUsage (terminal expression). | 54 // Resolve all definitions to a non-VarUsage (terminal expression). |
55 varDefs.forEach((key, value) { | 55 mainStyleSheetVarDefs.forEach((key, value) { |
56 for (var expr in (value.expression as Expressions).expressions) { | 56 for (Expression expr in (value.expression as Expressions).expressions) { |
57 var def = _findTerminalVarDefinition(varDefs, value); | 57 mainStyleSheetVarDefs[key] = |
58 varDefs[key] = def; | 58 _findTerminalVarDefinition(_allVarDefinitions, value); |
59 } | 59 } |
60 }); | 60 }); |
61 | |
62 // Resolve all var usages. | |
63 for (var tree in styleSheets) { | |
64 new _ResolveVarUsages(varDefs).visitTree(tree); | |
65 } | |
66 | |
67 return styleSheets; | |
68 } | 61 } |
69 } | 62 } |
70 | 63 |
71 /** | 64 /** Build list of all var definitions in all includes. */ |
72 * Find var- definitions in a style sheet. | 65 class _VarDefinitionsIncludes extends Visitor { |
73 * [found] list of known definitions. | 66 final Map<String, VarDefinition> varDefs; |
74 */ | 67 |
75 class _VarDefinitions extends Visitor { | 68 _VarDefinitionsIncludes(this.varDefs); |
76 final Map<String, VarDefinition> found = new Map(); | |
77 | 69 |
78 void visitTree(StyleSheet tree) { | 70 void visitTree(StyleSheet tree) { |
79 visitStyleSheet(tree); | 71 visitStyleSheet(tree); |
80 } | 72 } |
81 | 73 |
82 visitVarDefinition(VarDefinition node) { | 74 visitVarDefinition(VarDefinition node) { |
83 // Replace with latest variable definition. | 75 // Replace with latest variable definition. |
84 found[node.definedName] = node; | 76 varDefs[node.definedName] = node; |
85 super.visitVarDefinition(node); | 77 super.visitVarDefinition(node); |
86 } | 78 } |
87 | 79 |
88 void visitVarDefinitionDirective(VarDefinitionDirective node) { | 80 void visitVarDefinitionDirective(VarDefinitionDirective node) { |
89 visitVarDefinition(node.def); | 81 visitVarDefinition(node.def); |
90 } | 82 } |
91 } | 83 } |
92 | 84 |
93 /** | 85 /** |
94 * Resolve any CSS expression which contains a var() usage to the ultimate real | 86 * Find var- definitions in a style sheet. |
95 * CSS expression value e.g., | 87 * [found] list of known definitions. |
96 * | |
97 * var-one: var(two); | |
98 * var-two: #ff00ff; | |
99 * | |
100 * .test { | |
101 * color: var(one); | |
102 * } | |
103 * | |
104 * then .test's color would be #ff00ff | |
105 */ | 88 */ |
106 class _ResolveVarUsages extends Visitor { | 89 class _VarDefAndUsage extends Visitor { |
107 final Map<String, VarDefinition> varDefs; | 90 final Messages _messages; |
108 bool inVarDefinition = false; | 91 final Map<String, VarDefinition> _knownVarDefs; |
109 bool inUsage = false; | 92 final Map<String, VarDefinition> varDefs = new Map<String, VarDefinition>(); |
110 Expressions currentExpressions; | |
111 | 93 |
112 _ResolveVarUsages(this.varDefs); | 94 VarDefinition currVarDefinition; |
| 95 List<Expression> currentExpressions; |
| 96 |
| 97 _VarDefAndUsage(this._messages, this._knownVarDefs); |
113 | 98 |
114 void visitTree(StyleSheet tree) { | 99 void visitTree(StyleSheet tree) { |
115 visitStyleSheet(tree); | 100 visitStyleSheet(tree); |
116 } | 101 } |
117 | 102 |
118 void visitVarDefinition(VarDefinition varDef) { | 103 visitVarDefinition(VarDefinition node) { |
119 inVarDefinition = true; | 104 // Replace with latest variable definition. |
120 super.visitVarDefinition(varDef); | 105 currVarDefinition = node; |
121 inVarDefinition = false; | 106 |
| 107 _knownVarDefs[node.definedName] = node; |
| 108 varDefs[node.definedName] = node; |
| 109 |
| 110 super.visitVarDefinition(node); |
| 111 |
| 112 currVarDefinition = null; |
| 113 } |
| 114 |
| 115 void visitVarDefinitionDirective(VarDefinitionDirective node) { |
| 116 visitVarDefinition(node.def); |
122 } | 117 } |
123 | 118 |
124 void visitExpressions(Expressions node) { | 119 void visitExpressions(Expressions node) { |
125 currentExpressions = node; | 120 currentExpressions = node.expressions; |
126 super.visitExpressions(node); | 121 super.visitExpressions(node); |
127 currentExpressions = null; | 122 currentExpressions = null; |
128 } | 123 } |
129 | 124 |
130 void visitVarUsage(VarUsage node) { | 125 void visitVarUsage(VarUsage node) { |
| 126 if (currVarDefinition != null && currVarDefinition.badUsage) return; |
| 127 |
131 // Don't process other var() inside of a varUsage. That implies that the | 128 // Don't process other var() inside of a varUsage. That implies that the |
132 // default is a var() too. Also, don't process any var() inside of a | 129 // default is a var() too. Also, don't process any var() inside of a |
133 // varDefinition (they're just place holders until we've resolved all real | 130 // varDefinition (they're just place holders until we've resolved all real |
134 // usages. | 131 // usages. |
135 if (!inUsage && !inVarDefinition && currentExpressions != null) { | 132 var expressions = currentExpressions; |
136 var expressions = currentExpressions.expressions; | 133 var index = expressions.indexOf(node); |
137 var index = expressions.indexOf(node); | 134 assert(index >= 0); |
138 assert(index >= 0); | 135 var def = _knownVarDefs[node.name]; |
139 var def = varDefs[node.name]; | 136 if (def != null) { |
140 if (def != null) { | 137 if (def.badUsage) { |
141 // Found a VarDefinition use it. | 138 // Remove any expressions pointing to a bad var definition. |
142 _resolveVarUsage(currentExpressions.expressions, index, def); | 139 expressions.removeAt(index); |
143 } else if (node.defaultValues.any((e) => e is VarUsage)) { | 140 return; |
144 // Don't have a VarDefinition need to use default values resolve all | 141 } |
145 // default values. | 142 _resolveVarUsage(currentExpressions, index, |
146 var terminalDefaults = []; | 143 _findTerminalVarDefinition(_knownVarDefs, def)); |
147 for (var defaultValue in node.defaultValues) { | 144 } else if (node.defaultValues.any((e) => e is VarUsage)) { |
148 terminalDefaults.addAll(resolveUsageTerminal(defaultValue)); | 145 // Don't have a VarDefinition need to use default values resolve all |
| 146 // default values. |
| 147 var terminalDefaults = []; |
| 148 for (var defaultValue in node.defaultValues) { |
| 149 terminalDefaults.addAll(resolveUsageTerminal(defaultValue)); |
| 150 } |
| 151 expressions.replaceRange(index, index + 1, terminalDefaults); |
| 152 } else if (node.defaultValues.isNotEmpty){ |
| 153 // No VarDefinition but default value is a terminal expression; use it. |
| 154 expressions.replaceRange(index, index + 1, node.defaultValues); |
| 155 } else { |
| 156 if (currVarDefinition != null) { |
| 157 currVarDefinition.badUsage = true; |
| 158 var mainStyleSheetDef = varDefs[node.name]; |
| 159 if (mainStyleSheetDef != null) { |
| 160 varDefs.remove(currVarDefinition.property); |
149 } | 161 } |
150 expressions.replaceRange(index, index + 1, terminalDefaults); | |
151 } else { | |
152 // No VarDefinition but default value is a terminal expression; use it. | |
153 expressions.replaceRange(index, index + 1, node.defaultValues); | |
154 } | 162 } |
| 163 // Remove var usage that points at an undefined definition. |
| 164 expressions.removeAt(index); |
| 165 _messages.warning("Variable is not defined.", node.span); |
155 } | 166 } |
156 | 167 |
157 inUsage = true; | 168 var oldExpressions = currentExpressions; |
| 169 currentExpressions = node.defaultValues; |
158 super.visitVarUsage(node); | 170 super.visitVarUsage(node); |
159 inUsage = false; | 171 currentExpressions = oldExpressions; |
160 } | 172 } |
161 | 173 |
162 List<Expression> resolveUsageTerminal(VarUsage usage) { | 174 List<Expression> resolveUsageTerminal(VarUsage usage) { |
163 var result = []; | 175 var result = []; |
164 | 176 |
165 var varDef = varDefs[usage.name]; | 177 var varDef = _knownVarDefs[usage.name]; |
166 var expressions; | 178 var expressions; |
167 if (varDef == null) { | 179 if (varDef == null) { |
168 // VarDefinition not found try the defaultValues. | 180 // VarDefinition not found try the defaultValues. |
169 expressions = usage.defaultValues; | 181 expressions = usage.defaultValues; |
170 } else { | 182 } else { |
171 // Use the VarDefinition found. | 183 // Use the VarDefinition found. |
172 expressions = (varDef.expression as Expressions).expressions; | 184 expressions = (varDef.expression as Expressions).expressions; |
173 } | 185 } |
174 | 186 |
175 for (var expr in expressions) { | 187 for (var expr in expressions) { |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 } | 249 } |
238 } else { | 250 } else { |
239 // Return real CSS property. | 251 // Return real CSS property. |
240 return varDef; | 252 return varDef; |
241 } | 253 } |
242 } | 254 } |
243 | 255 |
244 // Didn't point to a var definition that existed. | 256 // Didn't point to a var definition that existed. |
245 return varDef; | 257 return varDef; |
246 } | 258 } |
OLD | NEW |