Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012, 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 | |
| 15 Set<StyleSheet> allStyleSheets = new Set<StyleSheet>(); | |
| 16 | |
| 17 /** | |
| 18 * [_pseudoElements] list of known pseudo attributes found in HTML, any | |
| 19 * CSS pseudo-elements 'name::custom-element' is mapped to the manged name | |
| 20 * associated with the pseudo-element key. | |
| 21 */ | |
| 22 PolyFill(this._messages, this._warningsAsErrors); | |
| 23 | |
| 24 /** | |
| 25 * Run the analyzer on every file that is a style sheet or any component that | |
| 26 * has a style tag. | |
| 27 */ | |
| 28 void process(StyleSheet stylesheet) { | |
| 29 // TODO(terry): Process all imported stylesheets. | |
| 30 | |
| 31 var styleSheets = processVars([stylesheet]); | |
| 32 allStyleSheets.addAll(styleSheets); | |
| 33 | |
| 34 normalize(); | |
| 35 } | |
| 36 | |
| 37 void normalize() { | |
| 38 // Remove all var definitions for all style sheets analyzed. | |
| 39 for (var tree in allStyleSheets) | |
| 40 new _RemoveVarDefinitions().visitTree(tree); | |
| 41 } | |
| 42 | |
| 43 List<StyleSheet> processVars(List<StyleSheet> styleSheets) { | |
| 44 // TODO(terry): Process all dependencies. | |
| 45 // Build list of all var definitions. | |
| 46 Map varDefs = new Map(); | |
| 47 for (var tree in styleSheets) { | |
| 48 var allDefs = (new _VarDefinitions()..visitTree(tree)).found; | |
| 49 allDefs.forEach((key, value) { | |
| 50 varDefs[key] = value; | |
| 51 }); | |
| 52 } | |
| 53 | |
| 54 // Resolve all definitions to a non-VarUsage (terminal expression). | |
| 55 varDefs.forEach((key, value) { | |
| 56 for (var expr in (value.expression as Expressions).expressions) { | |
| 57 var def = _findTerminalVarDefinition(varDefs, value); | |
| 58 varDefs[key] = def; | |
| 59 } | |
| 60 }); | |
| 61 | |
| 62 // Resolve all var usages. | |
| 63 for (var tree in styleSheets) new _ResolveVarUsages(varDefs).visitTree(tree) ; | |
|
Siggi Cherem (dart-lang)
2013/09/09 19:53:38
style: 80 col and add { } (we only skip them for f
terry
2013/09/09 20:04:50
Done.
| |
| 64 | |
| 65 return styleSheets; | |
| 66 } | |
| 67 } | |
| 68 | |
| 69 /** | |
| 70 * Find var- definitions in a style sheet. | |
| 71 * [found] list of known definitions. | |
| 72 */ | |
| 73 class _VarDefinitions extends Visitor { | |
| 74 final Map<String, VarDefinition> found = new Map(); | |
| 75 | |
| 76 void visitTree(StyleSheet tree) { | |
| 77 visitStyleSheet(tree); | |
| 78 } | |
| 79 | |
| 80 visitVarDefinition(VarDefinition node) { | |
| 81 // Replace with latest variable definition. | |
| 82 found[node.definedName] = node; | |
| 83 super.visitVarDefinition(node); | |
| 84 } | |
| 85 | |
| 86 void visitVarDefinitionDirective(VarDefinitionDirective node) { | |
| 87 visitVarDefinition(node.def); | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 /** | |
| 92 * Resolve any CSS expression which contains a var() usage to the ultimate real | |
| 93 * CSS expression value e.g., | |
| 94 * | |
| 95 * var-one: var(two); | |
| 96 * var-two: #ff00ff; | |
| 97 * | |
| 98 * .test { | |
| 99 * color: var(one); | |
| 100 * } | |
| 101 * | |
| 102 * then .test's color would be #ff00ff | |
| 103 */ | |
| 104 class _ResolveVarUsages extends Visitor { | |
| 105 final Map<String, VarDefinition> varDefs; | |
| 106 bool inVarDefinition = false; | |
| 107 bool inUsage = false; | |
| 108 Expressions currentExpressions; | |
| 109 | |
| 110 _ResolveVarUsages(this.varDefs); | |
| 111 | |
| 112 void visitTree(StyleSheet tree) { | |
| 113 visitStyleSheet(tree); | |
| 114 } | |
| 115 | |
| 116 void visitVarDefinition(VarDefinition varDef) { | |
| 117 inVarDefinition = true; | |
| 118 super.visitVarDefinition(varDef); | |
| 119 inVarDefinition = false; | |
| 120 } | |
| 121 | |
| 122 void visitExpressions(Expressions node) { | |
| 123 currentExpressions = node; | |
| 124 super.visitExpressions(node); | |
| 125 currentExpressions = null; | |
| 126 } | |
| 127 | |
| 128 void visitVarUsage(VarUsage node) { | |
| 129 // Don't process other var() inside of a varUsage. That implies that the | |
| 130 // default is a var() too. Also, don't process any var() inside of a | |
| 131 // varDefinition (they're just place holders until we've resolved all real | |
| 132 // usages. | |
| 133 if (!inUsage && !inVarDefinition && currentExpressions != null) { | |
| 134 var expressions = currentExpressions.expressions; | |
| 135 var index = expressions.indexOf(node); | |
| 136 assert(index >= 0); | |
| 137 var def = varDefs[node.name]; | |
| 138 if (def != null) { | |
| 139 // Found a VarDefinition use it. | |
| 140 _resolveVarUsage(currentExpressions.expressions, index, def); | |
| 141 } else if (node.defaultValues.any((e) => e is VarUsage)) { | |
| 142 // Don't have a VarDefinition need to use default values resolve all | |
| 143 // default values. | |
| 144 var terminalDefaults = []; | |
| 145 for (var defaultValue in node.defaultValues) { | |
| 146 terminalDefaults.addAll(resolveUsageTerminal(defaultValue)); | |
| 147 } | |
| 148 expressions.replaceRange(index, index + 1, terminalDefaults); | |
| 149 } else { | |
| 150 // No VarDefinition but default value is a terminal expression; use it. | |
| 151 expressions.replaceRange(index, index + 1, node.defaultValues); | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 inUsage = true; | |
| 156 super.visitVarUsage(node); | |
| 157 inUsage = false; | |
| 158 } | |
| 159 | |
| 160 List<Expression> resolveUsageTerminal(VarUsage usage) { | |
| 161 var result = []; | |
| 162 | |
| 163 var varDef = varDefs[usage.name]; | |
| 164 var expressions; | |
| 165 if (varDef == null) { | |
| 166 // VarDefinition not found try the defaultValues. | |
| 167 expressions = usage.defaultValues; | |
| 168 } else { | |
| 169 // Use the VarDefinition found. | |
| 170 expressions = (varDef.expression as Expressions).expressions; | |
| 171 } | |
| 172 | |
| 173 for (var expr in expressions) { | |
| 174 if (expr is VarUsage) { | |
| 175 // Get terminal value. | |
| 176 result.addAll(resolveUsageTerminal(expr)); | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 // We're at a terminal just return the VarDefinition expression. | |
| 181 if (result.isEmpty && varDef != null) { | |
| 182 result = (varDef.expression as Expressions).expressions; | |
| 183 } | |
| 184 | |
| 185 return result; | |
| 186 } | |
| 187 | |
| 188 _resolveVarUsage(List<Expressions> expressions, int index, | |
| 189 VarDefinition def) { | |
| 190 var defExpressions = (def.expression as Expressions).expressions; | |
| 191 expressions.replaceRange(index, index + 1, defExpressions); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 /** Remove all var definitions. */ | |
| 196 class _RemoveVarDefinitions extends Visitor { | |
| 197 void visitTree(StyleSheet tree) { | |
| 198 visitStyleSheet(tree); | |
| 199 } | |
| 200 | |
| 201 void visitStyleSheet(StyleSheet ss) { | |
| 202 ss.topLevels.removeWhere((e) => e is VarDefinitionDirective); | |
| 203 super.visitStyleSheet(ss); | |
| 204 } | |
| 205 | |
| 206 void visitDeclarationGroup(DeclarationGroup node) { | |
| 207 node.declarations.removeWhere((e) => e is VarDefinition); | |
| 208 super.visitDeclarationGroup(node); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 /** Find terminal definition (non VarUsage implies real CSS value). */ | |
| 213 VarDefinition _findTerminalVarDefinition(Map<String, VarDefinition> varDefs, | |
| 214 VarDefinition varDef) { | |
|
Siggi Cherem (dart-lang)
2013/09/09 19:53:38
fix indentation: missing a space, or only indent +
terry
2013/09/09 20:04:50
+4 Done.
| |
| 215 var expressions = varDef.expression as Expressions; | |
| 216 for (var expr in expressions.expressions) { | |
| 217 if (expr is VarUsage) { | |
| 218 var usageName = (expr as VarUsage).name; | |
| 219 var foundDef = varDefs[usageName]; | |
| 220 | |
| 221 // If foundDef is unknown check if defaultValues; if it exist then resolve | |
| 222 // to terminal value. | |
| 223 if (foundDef == null) { | |
| 224 // We're either a VarUsage or terminal definition if in varDefs; | |
| 225 // either way replace VarUsage with it's default value because the | |
| 226 // VarDefinition isn't found. | |
| 227 var defaultValues = (expr as VarUsage).defaultValues; | |
| 228 var replaceExprs = expressions.expressions; | |
| 229 assert(replaceExprs.length == 1); | |
| 230 replaceExprs.replaceRange(0, 1, defaultValues); | |
| 231 return varDef; | |
| 232 } | |
| 233 if (foundDef is VarDefinition) { | |
| 234 return _findTerminalVarDefinition(varDefs, foundDef); | |
| 235 } | |
| 236 } else { | |
| 237 // Return real CSS property. | |
| 238 return varDef; | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 // Didn't point to a var definition that existed. | |
| 243 return varDef; | |
| 244 } | |
| OLD | NEW |