| 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 |