| 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) { |
| 64 new _ResolveVarUsages(varDefs).visitTree(tree); |
| 65 } |
| 66 |
| 67 return styleSheets; |
| 68 } |
| 69 } |
| 70 |
| 71 /** |
| 72 * Find var- definitions in a style sheet. |
| 73 * [found] list of known definitions. |
| 74 */ |
| 75 class _VarDefinitions extends Visitor { |
| 76 final Map<String, VarDefinition> found = new Map(); |
| 77 |
| 78 void visitTree(StyleSheet tree) { |
| 79 visitStyleSheet(tree); |
| 80 } |
| 81 |
| 82 visitVarDefinition(VarDefinition node) { |
| 83 // Replace with latest variable definition. |
| 84 found[node.definedName] = node; |
| 85 super.visitVarDefinition(node); |
| 86 } |
| 87 |
| 88 void visitVarDefinitionDirective(VarDefinitionDirective node) { |
| 89 visitVarDefinition(node.def); |
| 90 } |
| 91 } |
| 92 |
| 93 /** |
| 94 * Resolve any CSS expression which contains a var() usage to the ultimate real |
| 95 * CSS expression value e.g., |
| 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 */ |
| 106 class _ResolveVarUsages extends Visitor { |
| 107 final Map<String, VarDefinition> varDefs; |
| 108 bool inVarDefinition = false; |
| 109 bool inUsage = false; |
| 110 Expressions currentExpressions; |
| 111 |
| 112 _ResolveVarUsages(this.varDefs); |
| 113 |
| 114 void visitTree(StyleSheet tree) { |
| 115 visitStyleSheet(tree); |
| 116 } |
| 117 |
| 118 void visitVarDefinition(VarDefinition varDef) { |
| 119 inVarDefinition = true; |
| 120 super.visitVarDefinition(varDef); |
| 121 inVarDefinition = false; |
| 122 } |
| 123 |
| 124 void visitExpressions(Expressions node) { |
| 125 currentExpressions = node; |
| 126 super.visitExpressions(node); |
| 127 currentExpressions = null; |
| 128 } |
| 129 |
| 130 void visitVarUsage(VarUsage node) { |
| 131 // 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 |
| 133 // varDefinition (they're just place holders until we've resolved all real |
| 134 // usages. |
| 135 if (!inUsage && !inVarDefinition && currentExpressions != null) { |
| 136 var expressions = currentExpressions.expressions; |
| 137 var index = expressions.indexOf(node); |
| 138 assert(index >= 0); |
| 139 var def = varDefs[node.name]; |
| 140 if (def != null) { |
| 141 // Found a VarDefinition use it. |
| 142 _resolveVarUsage(currentExpressions.expressions, index, 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 = []; |
| 147 for (var defaultValue in node.defaultValues) { |
| 148 terminalDefaults.addAll(resolveUsageTerminal(defaultValue)); |
| 149 } |
| 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 } |
| 155 } |
| 156 |
| 157 inUsage = true; |
| 158 super.visitVarUsage(node); |
| 159 inUsage = false; |
| 160 } |
| 161 |
| 162 List<Expression> resolveUsageTerminal(VarUsage usage) { |
| 163 var result = []; |
| 164 |
| 165 var varDef = varDefs[usage.name]; |
| 166 var expressions; |
| 167 if (varDef == null) { |
| 168 // VarDefinition not found try the defaultValues. |
| 169 expressions = usage.defaultValues; |
| 170 } else { |
| 171 // Use the VarDefinition found. |
| 172 expressions = (varDef.expression as Expressions).expressions; |
| 173 } |
| 174 |
| 175 for (var expr in expressions) { |
| 176 if (expr is VarUsage) { |
| 177 // Get terminal value. |
| 178 result.addAll(resolveUsageTerminal(expr)); |
| 179 } |
| 180 } |
| 181 |
| 182 // We're at a terminal just return the VarDefinition expression. |
| 183 if (result.isEmpty && varDef != null) { |
| 184 result = (varDef.expression as Expressions).expressions; |
| 185 } |
| 186 |
| 187 return result; |
| 188 } |
| 189 |
| 190 _resolveVarUsage(List<Expressions> expressions, int index, |
| 191 VarDefinition def) { |
| 192 var defExpressions = (def.expression as Expressions).expressions; |
| 193 expressions.replaceRange(index, index + 1, defExpressions); |
| 194 } |
| 195 } |
| 196 |
| 197 /** Remove all var definitions. */ |
| 198 class _RemoveVarDefinitions extends Visitor { |
| 199 void visitTree(StyleSheet tree) { |
| 200 visitStyleSheet(tree); |
| 201 } |
| 202 |
| 203 void visitStyleSheet(StyleSheet ss) { |
| 204 ss.topLevels.removeWhere((e) => e is VarDefinitionDirective); |
| 205 super.visitStyleSheet(ss); |
| 206 } |
| 207 |
| 208 void visitDeclarationGroup(DeclarationGroup node) { |
| 209 node.declarations.removeWhere((e) => e is VarDefinition); |
| 210 super.visitDeclarationGroup(node); |
| 211 } |
| 212 } |
| 213 |
| 214 /** Find terminal definition (non VarUsage implies real CSS value). */ |
| 215 VarDefinition _findTerminalVarDefinition(Map<String, VarDefinition> varDefs, |
| 216 VarDefinition varDef) { |
| 217 var expressions = varDef.expression as Expressions; |
| 218 for (var expr in expressions.expressions) { |
| 219 if (expr is VarUsage) { |
| 220 var usageName = (expr as VarUsage).name; |
| 221 var foundDef = varDefs[usageName]; |
| 222 |
| 223 // If foundDef is unknown check if defaultValues; if it exist then resolve |
| 224 // to terminal value. |
| 225 if (foundDef == null) { |
| 226 // We're either a VarUsage or terminal definition if in varDefs; |
| 227 // either way replace VarUsage with it's default value because the |
| 228 // VarDefinition isn't found. |
| 229 var defaultValues = (expr as VarUsage).defaultValues; |
| 230 var replaceExprs = expressions.expressions; |
| 231 assert(replaceExprs.length == 1); |
| 232 replaceExprs.replaceRange(0, 1, defaultValues); |
| 233 return varDef; |
| 234 } |
| 235 if (foundDef is VarDefinition) { |
| 236 return _findTerminalVarDefinition(varDefs, foundDef); |
| 237 } |
| 238 } else { |
| 239 // Return real CSS property. |
| 240 return varDef; |
| 241 } |
| 242 } |
| 243 |
| 244 // Didn't point to a var definition that existed. |
| 245 return varDef; |
| 246 } |
| OLD | NEW |