Index: mojo/public/dart/third_party/csslib/lib/src/polyfill.dart |
diff --git a/mojo/public/dart/third_party/csslib/lib/src/polyfill.dart b/mojo/public/dart/third_party/csslib/lib/src/polyfill.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9b682ce4cb127126f0589830647683cd481c3f65 |
--- /dev/null |
+++ b/mojo/public/dart/third_party/csslib/lib/src/polyfill.dart |
@@ -0,0 +1,256 @@ |
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+part of csslib.parser; |
+ |
+/** |
+ * CSS polyfill emits CSS to be understood by older parsers that which do not |
+ * understand (var, calc, etc.). |
+ */ |
+class PolyFill { |
+ final Messages _messages; |
+ final bool _warningsAsErrors; |
+ Map<String, VarDefinition> _allVarDefinitions = |
+ new Map<String, VarDefinition>(); |
+ |
+ Set<StyleSheet> allStyleSheets = new Set<StyleSheet>(); |
+ |
+ /** |
+ * [_pseudoElements] list of known pseudo attributes found in HTML, any |
+ * CSS pseudo-elements 'name::custom-element' is mapped to the manged name |
+ * associated with the pseudo-element key. |
+ */ |
+ PolyFill(this._messages, this._warningsAsErrors); |
+ |
+ /** |
+ * Run the analyzer on every file that is a style sheet or any component that |
+ * has a style tag. |
+ */ |
+ void process(StyleSheet styleSheet, {List<StyleSheet> includes: null}) { |
+ if (includes != null) { |
+ processVarDefinitions(includes); |
+ } |
+ processVars(styleSheet); |
+ |
+ // Remove all var definitions for this style sheet. |
+ new _RemoveVarDefinitions().visitTree(styleSheet); |
+ } |
+ |
+ /** Process all includes looking for var definitions. */ |
+ void processVarDefinitions(List<StyleSheet> includes) { |
+ for (var include in includes) { |
+ _allVarDefinitions = (new _VarDefinitionsIncludes(_allVarDefinitions) |
+ ..visitTree(include)).varDefs; |
+ } |
+ } |
+ |
+ void processVars(StyleSheet styleSheet) { |
+ // Build list of all var definitions. |
+ var mainStyleSheetVarDefs = (new _VarDefAndUsage( |
+ this._messages, _allVarDefinitions)..visitTree(styleSheet)).varDefs; |
+ |
+ // Resolve all definitions to a non-VarUsage (terminal expression). |
+ mainStyleSheetVarDefs.forEach((key, value) { |
+ for (var _ in (value.expression as Expressions).expressions) { |
+ mainStyleSheetVarDefs[key] = |
+ _findTerminalVarDefinition(_allVarDefinitions, value); |
+ } |
+ }); |
+ } |
+} |
+ |
+/** Build list of all var definitions in all includes. */ |
+class _VarDefinitionsIncludes extends Visitor { |
+ final Map<String, VarDefinition> varDefs; |
+ |
+ _VarDefinitionsIncludes(this.varDefs); |
+ |
+ void visitTree(StyleSheet tree) { |
+ visitStyleSheet(tree); |
+ } |
+ |
+ visitVarDefinition(VarDefinition node) { |
+ // Replace with latest variable definition. |
+ varDefs[node.definedName] = node; |
+ super.visitVarDefinition(node); |
+ } |
+ |
+ void visitVarDefinitionDirective(VarDefinitionDirective node) { |
+ visitVarDefinition(node.def); |
+ } |
+} |
+ |
+/** |
+ * Find var- definitions in a style sheet. |
+ * [found] list of known definitions. |
+ */ |
+class _VarDefAndUsage extends Visitor { |
+ final Messages _messages; |
+ final Map<String, VarDefinition> _knownVarDefs; |
+ final Map<String, VarDefinition> varDefs = new Map<String, VarDefinition>(); |
+ |
+ VarDefinition currVarDefinition; |
+ List<Expression> currentExpressions; |
+ |
+ _VarDefAndUsage(this._messages, this._knownVarDefs); |
+ |
+ void visitTree(StyleSheet tree) { |
+ visitStyleSheet(tree); |
+ } |
+ |
+ visitVarDefinition(VarDefinition node) { |
+ // Replace with latest variable definition. |
+ currVarDefinition = node; |
+ |
+ _knownVarDefs[node.definedName] = node; |
+ varDefs[node.definedName] = node; |
+ |
+ super.visitVarDefinition(node); |
+ |
+ currVarDefinition = null; |
+ } |
+ |
+ void visitVarDefinitionDirective(VarDefinitionDirective node) { |
+ visitVarDefinition(node.def); |
+ } |
+ |
+ void visitExpressions(Expressions node) { |
+ currentExpressions = node.expressions; |
+ super.visitExpressions(node); |
+ currentExpressions = null; |
+ } |
+ |
+ void visitVarUsage(VarUsage node) { |
+ if (currVarDefinition != null && currVarDefinition.badUsage) return; |
+ |
+ // Don't process other var() inside of a varUsage. That implies that the |
+ // default is a var() too. Also, don't process any var() inside of a |
+ // varDefinition (they're just place holders until we've resolved all real |
+ // usages. |
+ var expressions = currentExpressions; |
+ var index = expressions.indexOf(node); |
+ assert(index >= 0); |
+ var def = _knownVarDefs[node.name]; |
+ if (def != null) { |
+ if (def.badUsage) { |
+ // Remove any expressions pointing to a bad var definition. |
+ expressions.removeAt(index); |
+ return; |
+ } |
+ _resolveVarUsage(currentExpressions, index, |
+ _findTerminalVarDefinition(_knownVarDefs, def)); |
+ } else if (node.defaultValues.any((e) => e is VarUsage)) { |
+ // Don't have a VarDefinition need to use default values resolve all |
+ // default values. |
+ var terminalDefaults = <Expression>[]; |
+ for (var defaultValue in node.defaultValues) { |
+ terminalDefaults.addAll(resolveUsageTerminal(defaultValue)); |
+ } |
+ expressions.replaceRange(index, index + 1, terminalDefaults); |
+ } else if (node.defaultValues.isNotEmpty) { |
+ // No VarDefinition but default value is a terminal expression; use it. |
+ expressions.replaceRange(index, index + 1, node.defaultValues); |
+ } else { |
+ if (currVarDefinition != null) { |
+ currVarDefinition.badUsage = true; |
+ var mainStyleSheetDef = varDefs[node.name]; |
+ if (mainStyleSheetDef != null) { |
+ varDefs.remove(currVarDefinition.property); |
+ } |
+ } |
+ // Remove var usage that points at an undefined definition. |
+ expressions.removeAt(index); |
+ _messages.warning("Variable is not defined.", node.span); |
+ } |
+ |
+ var oldExpressions = currentExpressions; |
+ currentExpressions = node.defaultValues; |
+ super.visitVarUsage(node); |
+ currentExpressions = oldExpressions; |
+ } |
+ |
+ List<Expression> resolveUsageTerminal(VarUsage usage) { |
+ var result = []; |
+ |
+ var varDef = _knownVarDefs[usage.name]; |
+ var expressions; |
+ if (varDef == null) { |
+ // VarDefinition not found try the defaultValues. |
+ expressions = usage.defaultValues; |
+ } else { |
+ // Use the VarDefinition found. |
+ expressions = (varDef.expression as Expressions).expressions; |
+ } |
+ |
+ for (var expr in expressions) { |
+ if (expr is VarUsage) { |
+ // Get terminal value. |
+ result.addAll(resolveUsageTerminal(expr)); |
+ } |
+ } |
+ |
+ // We're at a terminal just return the VarDefinition expression. |
+ if (result.isEmpty && varDef != null) { |
+ result = (varDef.expression as Expressions).expressions; |
+ } |
+ |
+ return result; |
+ } |
+ |
+ _resolveVarUsage(List<Expression> expressions, int index, VarDefinition def) { |
+ var defExpressions = (def.expression as Expressions).expressions; |
+ expressions.replaceRange(index, index + 1, defExpressions); |
+ } |
+} |
+ |
+/** Remove all var definitions. */ |
+class _RemoveVarDefinitions extends Visitor { |
+ void visitTree(StyleSheet tree) { |
+ visitStyleSheet(tree); |
+ } |
+ |
+ void visitStyleSheet(StyleSheet ss) { |
+ ss.topLevels.removeWhere((e) => e is VarDefinitionDirective); |
+ super.visitStyleSheet(ss); |
+ } |
+ |
+ void visitDeclarationGroup(DeclarationGroup node) { |
+ node.declarations.removeWhere((e) => e is VarDefinition); |
+ super.visitDeclarationGroup(node); |
+ } |
+} |
+ |
+/** Find terminal definition (non VarUsage implies real CSS value). */ |
+VarDefinition _findTerminalVarDefinition( |
+ Map<String, VarDefinition> varDefs, VarDefinition varDef) { |
+ var expressions = varDef.expression as Expressions; |
+ for (var expr in expressions.expressions) { |
+ if (expr is VarUsage) { |
+ var usageName = (expr as VarUsage).name; |
+ var foundDef = varDefs[usageName]; |
+ |
+ // If foundDef is unknown check if defaultValues; if it exist then resolve |
+ // to terminal value. |
+ if (foundDef == null) { |
+ // We're either a VarUsage or terminal definition if in varDefs; |
+ // either way replace VarUsage with it's default value because the |
+ // VarDefinition isn't found. |
+ var defaultValues = (expr as VarUsage).defaultValues; |
+ var replaceExprs = expressions.expressions; |
+ assert(replaceExprs.length == 1); |
+ replaceExprs.replaceRange(0, 1, defaultValues); |
+ return varDef; |
+ } |
+ if (foundDef is VarDefinition) { |
+ return _findTerminalVarDefinition(varDefs, foundDef); |
+ } |
+ } else { |
+ // Return real CSS property. |
+ return varDef; |
+ } |
+ } |
+ |
+ // Didn't point to a var definition that existed. |
+ return varDef; |
+} |