Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(87)

Side by Side Diff: lib/src/css_analyzer.dart

Issue 22962005: Merge pull request #581 from kevmoo/polymer (Closed) Base URL: https://github.com/dart-lang/web-ui.git@polymer
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/compiler_options.dart ('k') | lib/src/css_emitters.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, 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 library html_css_fixup; 5 /** Portion of the analyzer dealing with CSS sources. */
6 6 library polymer.src.css_analyzer;
7 import 'dart:json' as json;
8 7
9 import 'package:csslib/parser.dart' as css; 8 import 'package:csslib/parser.dart' as css;
10 import 'package:csslib/visitor.dart'; 9 import 'package:csslib/visitor.dart';
11 import 'package:html5lib/dom.dart'; 10 import 'package:html5lib/dom.dart';
12 import 'package:html5lib/dom_parsing.dart'; 11 import 'package:html5lib/dom_parsing.dart';
13 12
14 import 'compiler.dart';
15 import 'emitters.dart';
16 import 'info.dart'; 13 import 'info.dart';
14 import 'files.dart' show SourceFile;
17 import 'messages.dart'; 15 import 'messages.dart';
18 import 'compiler_options.dart'; 16 import 'compiler_options.dart';
19 import 'paths.dart';
20 import 'utils.dart';
21 17
22 /** Enum for type of polyfills supported. */ 18 void analyzeCss(String packageRoot, List<SourceFile> files,
23 class CssPolyfillKind { 19 Map<String, FileInfo> info, Map<String, String> pseudoElements,
24 final _index; 20 Messages messages, {warningsAsErrors: false}) {
25 const CssPolyfillKind._internal(this._index); 21 var analyzer = new _AnalyzerCss(packageRoot, info, pseudoElements, messages,
26 22 warningsAsErrors);
27 /** Emit CSS selectors as seen (no polyfill). */ 23 for (var file in files) analyzer.process(file);
28 static const NO_POLYFILL = const CssPolyfillKind._internal(0); 24 analyzer.normalize();
29
30 /** Emit CSS selectors scoped to the "is" attribute of the component. */
31 static const SCOPED_POLYFILL = const CssPolyfillKind._internal(1);
32
33 /** Emit CSS selectors mangled. */
34 static const MANGLED_POLYFILL = const CssPolyfillKind._internal(2);
35
36 static CssPolyfillKind of(CompilerOptions options, ComponentInfo component) {
37 if (!options.processCss || !component.scoped) return NO_POLYFILL;
38 if (options.mangleCss) return MANGLED_POLYFILL;
39 if (!component.hasAuthorStyles && !options.hasCssReset) {
40 return MANGLED_POLYFILL;
41 }
42 return SCOPED_POLYFILL;
43 }
44 } 25 }
45 26
27 class _AnalyzerCss {
28 final String packageRoot;
29 final Map<String, FileInfo> info;
30 final Map<String, String> _pseudoElements;
31 final Messages _messages;
32 final bool _warningsAsErrors;
46 33
47 /** 34 Set<StyleSheet> allStyleSheets = new Set<StyleSheet>();
48 * If processCss is enabled, prefix any component's HTML attributes for id or
49 * class to reference the mangled CSS class name or id.
50 */
51 void fixupHtmlCss(FileInfo fileInfo, CompilerOptions options) {
52 // Walk the HTML tree looking for class names or id that are in our parsed
53 // stylesheet selectors and making those CSS classes and ids unique to that
54 // component.
55 if (options.verbose) {
56 print(" CSS fixup ${path.basename(fileInfo.inputUrl.resolvedPath)}");
57 }
58 for (var component in fileInfo.declaredComponents) {
59 // Mangle class names and element ids in the HTML to match the stylesheet.
60 // TODO(terry): Allow more than one style sheet per component.
61 if (component.styleSheets.length == 1) {
62 // For components only 1 stylesheet allowed.
63 var styleSheet = component.styleSheets[0];
64 var prefix = CssPolyfillKind.of(options, component) ==
65 CssPolyfillKind.MANGLED_POLYFILL ? component.tagName : null;
66 35
67 // List of referenced #id and .class in CSS. 36 /**
68 var knownCss = new IdClassVisitor()..visitTree(styleSheet); 37 * [_pseudoElements] list of known pseudo attributes found in HTML, any
69 // Prefix all id and class refs in CSS selectors and HTML attributes. 38 * CSS pseudo-elements 'name::custom-element' is mapped to the manged name
70 new _ScopedStyleRenamer(knownCss, prefix, options.debugCss) 39 * associated with the pseudo-element key.
71 .visit(component.element); 40 */
41 _AnalyzerCss(this.packageRoot, this.info, this._pseudoElements,
42 this._messages, this._warningsAsErrors);
43
44 /**
45 * Run the analyzer on every file that is a style sheet or any component that
46 * has a style tag.
47 */
48 void process(SourceFile file) {
49 var fileInfo = info[file.path];
50 if (file.isStyleSheet || fileInfo.styleSheets.length > 0) {
51 var styleSheets = processVars(fileInfo);
52
53 // Add to list of all style sheets analyzed.
54 allStyleSheets.addAll(styleSheets);
72 } 55 }
73 }
74 }
75 56
76 /** Build list of every CSS class name and id selector in a stylesheet. */ 57 // Process any components.
77 class IdClassVisitor extends Visitor { 58 for (var component in fileInfo.declaredComponents) {
78 final Set<String> classes = new Set(); 59 var all = processVars(component);
79 final Set<String> ids = new Set();
80 60
81 void visitClassSelector(ClassSelector node) { 61 // Add to list of all style sheets analyzed.
82 classes.add(node.name); 62 allStyleSheets.addAll(all);
63 }
64
65 processCustomPseudoElements();
83 } 66 }
84 67
85 void visitIdSelector(IdSelector node) { 68 void normalize() {
86 ids.add(node.name); 69 // Remove all var definitions for all style sheets analyzed.
87 } 70 for (var tree in allStyleSheets) new _RemoveVarDefinitions().visitTree(tree) ;
88 }
89
90 /** Build the Dart map of managled class/id names and component tag name. */
91 Map _createCssSimpleSelectors(IdClassVisitor visitedCss, ComponentInfo info,
92 CssPolyfillKind kind) {
93 bool mangleNames = kind == CssPolyfillKind.MANGLED_POLYFILL;
94 Map selectors = {};
95 if (visitedCss != null) {
96 for (var cssClass in visitedCss.classes) {
97 selectors['.$cssClass'] =
98 mangleNames ? '${info.tagName}_$cssClass' : cssClass;
99 }
100 for (var id in visitedCss.ids) {
101 selectors['#$id'] = mangleNames ? '${info.tagName}_$id' : id;
102 }
103 } 71 }
104 72
105 // Add tag name selector x-comp == [is="x-comp"]. 73 List<StyleSheet> processVars(var libraryInfo) {
106 var componentName = info.tagName; 74 // Get list of all stylesheet(s) dependencies referenced from this file.
107 selectors['$componentName'] = '[is="$componentName"]'; 75 var styleSheets = _dependencies(libraryInfo).toList();
108 76
109 return selectors; 77 var errors = [];
110 } 78 css.analyze(styleSheets, errors: errors, options:
79 [_warningsAsErrors ? '--warnings_as_errors' : '', 'memory']);
111 80
112 /** 81 // Print errors as warnings.
113 * Return a map of simple CSS selectors (class and id selectors) as a Dart map 82 for (var e in errors) {
114 * definition. 83 _messages.warning(e.message, e.span);
115 */ 84 }
116 String createCssSelectorsExpression(ComponentInfo info, CssPolyfillKind kind) {
117 var cssVisited = new IdClassVisitor();
118 85
119 // For components only 1 stylesheet allowed. 86 // Build list of all var definitions.
120 if (!info.styleSheets.isEmpty && info.styleSheets.length == 1) { 87 Map varDefs = new Map();
121 var styleSheet = info.styleSheets[0]; 88 for (var tree in styleSheets) {
122 cssVisited..visitTree(styleSheet); 89 var allDefs = (new _VarDefinitions()..visitTree(tree)).found;
90 allDefs.forEach((key, value) {
91 varDefs[key] = value;
92 });
93 }
94
95 // Resolve all definitions to a non-VarUsage (terminal expression).
96 varDefs.forEach((key, value) {
97 for (var expr in (value.expression as Expressions).expressions) {
98 var def = _findTerminalVarDefinition(varDefs, value);
99 varDefs[key] = def;
100 }
101 });
102
103 // Resolve all var usages.
104 for (var tree in styleSheets) new _ResolveVarUsages(varDefs).visitTree(tree) ;
105
106 return styleSheets;
123 } 107 }
124 108
125 return json.stringify(_createCssSimpleSelectors(cssVisited, info, kind)); 109 processCustomPseudoElements() {
126 } 110 var polyFiller = new _PseudoElementExpander(_pseudoElements);
127 111 for (var tree in allStyleSheets) {
128 // TODO(terry): Need to handle other selectors than IDs/classes like tag name 112 polyFiller.visitTree(tree);
129 // e.g., DIV { color: red; }
130 // TODO(terry): Would be nice if we didn't need to mangle names; requires users
131 // to be careful in their code and makes it more than a "polyfill".
132 // Maybe mechanism that generates CSS class name for scoping. This
133 // would solve tag name selectors (see above TODO).
134 /**
135 * Fix a component's HTML to implement scoped stylesheets.
136 *
137 * We do this by renaming all element class and id attributes to be globally
138 * unique to a component.
139 */
140 class _ScopedStyleRenamer extends TreeVisitor {
141 final bool _debugCss;
142
143 /** Set of classes and ids defined for this component. */
144 final IdClassVisitor _knownCss;
145
146 /** Prefix to apply to each class/id reference. */
147 final String _prefix;
148
149 _ScopedStyleRenamer(this._knownCss, this._prefix, this._debugCss);
150
151 void visitElement(Element node) {
152 // Walk the HTML elements mangling any references to id or class attributes.
153 _mangleClassAttribute(node, _knownCss.classes, _prefix);
154 _mangleIdAttribute(node, _knownCss.ids, _prefix);
155
156 super.visitElement(node);
157 }
158
159 /**
160 * Mangles HTML class reference that matches a CSS class name defined in the
161 * component's style sheet.
162 */
163 void _mangleClassAttribute(Node node, Set<String> classes, String prefix) {
164 if (node.attributes.containsKey('class')) {
165 var refClasses = node.attributes['class'].trim().split(" ");
166
167 bool changed = false;
168 var len = refClasses.length;
169 for (var i = 0; i < len; i++) {
170 var refClass = refClasses[i];
171 if (classes.contains(refClass)) {
172 if (prefix != null) {
173 refClasses[i] = '${prefix}_$refClass';
174 changed = true;
175 }
176 }
177 }
178
179 if (changed) {
180 StringBuffer newClasses = new StringBuffer();
181 refClasses.forEach((String className) {
182 newClasses.write("${(newClasses.length > 0) ? ' ' : ''}$className");
183 });
184 var mangledClasses = newClasses.toString();
185 if (_debugCss) {
186 print(" class = ${node.attributes['class'].trim()} => "
187 "$mangledClasses");
188 }
189 node.attributes['class'] = mangledClasses;
190 }
191 } 113 }
192 } 114 }
193 115
194 /** 116 /**
195 * Mangles an HTML id reference that matches a CSS id selector name defined 117 * Given a component or file check if any stylesheets referenced. If so then
196 * in the component's style sheet. 118 * return a list of all referenced stylesheet dependencies (@imports or <link
119 * rel="stylesheet" ..>).
197 */ 120 */
198 void _mangleIdAttribute(Node node, Set<String> ids, String prefix) { 121 Set<StyleSheet> _dependencies(var libraryInfo, {Set<StyleSheet> seen}) {
199 if (prefix != null) { 122 if (seen == null) seen = new Set();
200 var id = node.attributes['id']; 123
201 if (id != null && ids.contains(id)) { 124 // Used to resolve all pathing information.
202 var mangledName = '${prefix}_$id'; 125 var inputUrl = libraryInfo is FileInfo
203 if (_debugCss) { 126 ? libraryInfo.inputUrl
204 print(" id = ${node.attributes['id'].toString()} => $mangledName"); 127 : (libraryInfo as ComponentInfo).declaringFile.inputUrl;
128
129 for (var styleSheet in libraryInfo.styleSheets) {
130 if (!seen.contains(styleSheet)) {
131 // TODO(terry): VM uses expandos to implement hashes. Currently, it's a
132 // linear (not constant) time cost (see dartbug.com/5746).
133 // If this bug isn't fixed and performance show's this a
134 // a problem we'll need to implement our own hashCode or
135 // use a different key for better perf.
136 // Add the stylesheet.
137 seen.add(styleSheet);
138
139 // Any other imports in this stylesheet?
140 var urlInfos = findImportsInStyleSheet(styleSheet, packageRoot,
141 inputUrl, _messages);
142
143 // Process other imports in this stylesheets.
144 for (var importSS in urlInfos) {
145 var importInfo = info[importSS.resolvedPath];
146 if (importInfo != null) {
147 // Add all known stylesheets processed.
148 seen.addAll(importInfo.styleSheets);
149 // Find dependencies for stylesheet referenced with a
150 // @import
151 for (var ss in importInfo.styleSheets) {
152 var urls = findImportsInStyleSheet(ss, packageRoot, inputUrl,
153 _messages);
154 for (var url in urls) {
155 _dependencies(info[url.resolvedPath], seen: seen);
156 }
157 }
158 }
205 } 159 }
206 node.attributes['id'] = mangledName;
207 } 160 }
208 } 161 }
162
163 return seen;
209 } 164 }
210 } 165 }
211 166
212
213 /** 167 /**
214 * Find var- definitions in a style sheet. 168 * Find var- definitions in a style sheet.
215 * [found] list of known definitions. 169 * [found] list of known definitions.
216 */ 170 */
217 class VarDefinitions extends Visitor { 171 class _VarDefinitions extends Visitor {
218 final Map<String, VarDefinition> found = new Map(); 172 final Map<String, VarDefinition> found = new Map();
219 173
220 void visitTree(StyleSheet tree) { 174 void visitTree(StyleSheet tree) {
221 visitStyleSheet(tree); 175 visitStyleSheet(tree);
222 } 176 }
223 177
224 visitVarDefinition(VarDefinition node) { 178 visitVarDefinition(VarDefinition node) {
225 // Replace with latest variable definition. 179 // Replace with latest variable definition.
226 found[node.definedName] = node; 180 found[node.definedName] = node;
227 super.visitVarDefinition(node); 181 super.visitVarDefinition(node);
(...skipping 10 matching lines...) Expand all
238 * 192 *
239 * var-one: var(two); 193 * var-one: var(two);
240 * var-two: #ff00ff; 194 * var-two: #ff00ff;
241 * 195 *
242 * .test { 196 * .test {
243 * color: var(one); 197 * color: var(one);
244 * } 198 * }
245 * 199 *
246 * then .test's color would be #ff00ff 200 * then .test's color would be #ff00ff
247 */ 201 */
248 class ResolveVarUsages extends Visitor { 202 class _ResolveVarUsages extends Visitor {
249 final Map<String, VarDefinition> varDefs; 203 final Map<String, VarDefinition> varDefs;
250 bool inVarDefinition = false; 204 bool inVarDefinition = false;
251 bool inUsage = false; 205 bool inUsage = false;
252 Expressions currentExpressions; 206 Expressions currentExpressions;
253 207
254 ResolveVarUsages(this.varDefs); 208 _ResolveVarUsages(this.varDefs);
255 209
256 void visitTree(StyleSheet tree) { 210 void visitTree(StyleSheet tree) {
257 visitStyleSheet(tree); 211 visitStyleSheet(tree);
258 } 212 }
259 213
260 void visitVarDefinition(VarDefinition varDef) { 214 void visitVarDefinition(VarDefinition varDef) {
261 inVarDefinition = true; 215 inVarDefinition = true;
262 super.visitVarDefinition(varDef); 216 super.visitVarDefinition(varDef);
263 inVarDefinition = false; 217 inVarDefinition = false;
264 } 218 }
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 } 284 }
331 285
332 _resolveVarUsage(List<Expressions> expressions, int index, 286 _resolveVarUsage(List<Expressions> expressions, int index,
333 VarDefinition def) { 287 VarDefinition def) {
334 var defExpressions = (def.expression as Expressions).expressions; 288 var defExpressions = (def.expression as Expressions).expressions;
335 expressions.replaceRange(index, index + 1, defExpressions); 289 expressions.replaceRange(index, index + 1, defExpressions);
336 } 290 }
337 } 291 }
338 292
339 /** Remove all var definitions. */ 293 /** Remove all var definitions. */
340 class RemoveVarDefinitions extends Visitor { 294 class _RemoveVarDefinitions extends Visitor {
341 void visitTree(StyleSheet tree) { 295 void visitTree(StyleSheet tree) {
342 visitStyleSheet(tree); 296 visitStyleSheet(tree);
343 } 297 }
344 298
345 void visitStyleSheet(StyleSheet ss) { 299 void visitStyleSheet(StyleSheet ss) {
346 ss.topLevels.removeWhere((e) => e is VarDefinitionDirective); 300 ss.topLevels.removeWhere((e) => e is VarDefinitionDirective);
347 super.visitStyleSheet(ss); 301 super.visitStyleSheet(ss);
348 } 302 }
349 303
350 void visitDeclarationGroup(DeclarationGroup node) { 304 void visitDeclarationGroup(DeclarationGroup node) {
(...skipping 13 matching lines...) Expand all
364 * 318 *
365 * Change the custom pseudo-element to be a child of the pseudo attribute's 319 * Change the custom pseudo-element to be a child of the pseudo attribute's
366 * mangled custom pseudo element name. e.g, 320 * mangled custom pseudo element name. e.g,
367 * 321 *
368 * .test::x-box 322 * .test::x-box
369 * 323 *
370 * would become: 324 * would become:
371 * 325 *
372 * .test > *[pseudo="x-box_2"] 326 * .test > *[pseudo="x-box_2"]
373 */ 327 */
374 class PseudoElementExpander extends Visitor { 328 class _PseudoElementExpander extends Visitor {
375 final Map<String, String> _pseudoElements; 329 final Map<String, String> _pseudoElements;
376 330
377 PseudoElementExpander(this._pseudoElements); 331 _PseudoElementExpander(this._pseudoElements);
378 332
379 void visitTree(StyleSheet tree) => visitStyleSheet(tree); 333 void visitTree(StyleSheet tree) => visitStyleSheet(tree);
380 334
381 visitSelector(Selector node) { 335 visitSelector(Selector node) {
382 var selectors = node.simpleSelectorSequences; 336 var selectors = node.simpleSelectorSequences;
383 for (var index = 0; index < selectors.length; index++) { 337 for (var index = 0; index < selectors.length; index++) {
384 var selector = selectors[index].simpleSelector; 338 var selector = selectors[index].simpleSelector;
385 if (selector is PseudoElementSelector) { 339 if (selector is PseudoElementSelector) {
386 if (_pseudoElements.containsKey(selector.name)) { 340 if (_pseudoElements.containsKey(selector.name)) {
387 // Pseudo Element is a custom element. 341 // Pseudo Element is a custom element.
388 var mangledName = _pseudoElements[selector.name]; 342 var mangledName = _pseudoElements[selector.name];
389 343
390 var span = selectors[index].span; 344 var span = selectors[index].span;
391 345
392 var attrSelector = new AttributeSelector( 346 var attrSelector = new AttributeSelector(
393 new Identifier('pseudo', span), css.TokenKind.EQUALS, 347 new Identifier('pseudo', span), css.TokenKind.EQUALS,
394 mangledName, span); 348 mangledName, span);
395 // The wildcard * namespace selector. 349 // The wildcard * namespace selector.
396 var wildCard = new ElementSelector(new Wildcard(span), span); 350 var wildCard = new ElementSelector(new Wildcard(span), span);
397 selectors[index] = new SimpleSelectorSequence(wildCard, span, 351 selectors[index] = new SimpleSelectorSequence(wildCard, span,
398 css.TokenKind.COMBINATOR_GREATER); 352 css.TokenKind.COMBINATOR_GREATER);
399 selectors.insert(++index, 353 selectors.insert(++index,
400 new SimpleSelectorSequence(attrSelector, span)); 354 new SimpleSelectorSequence(attrSelector, span));
401 } 355 }
402 } 356 }
403 } 357 }
404 } 358 }
405 } 359 }
406 360
407 /** Compute each CSS URI resource relative from the generated CSS file. */
408 class UriVisitor extends Visitor {
409 /**
410 * Relative path from the output css file to the location of the original
411 * css file that contained the URI to each resource.
412 */
413 final String _pathToOriginalCss;
414
415 factory UriVisitor(PathMapper pathMapper, String cssPath, bool rewriteUrl) {
416 var cssDir = path.dirname(cssPath);
417 var outCssDir = rewriteUrl ? pathMapper.outputDirPath(cssPath)
418 : path.dirname(cssPath);
419 return new UriVisitor._internal(path.relative(cssDir, from: outCssDir));
420 }
421
422 UriVisitor._internal(this._pathToOriginalCss);
423
424 void visitUriTerm(UriTerm node) {
425 // Don't touch URIs that have any scheme (http, etc.).
426 var uri = Uri.parse(node.text);
427 if (uri.host != '') return;
428 if (uri.scheme != '' && uri.scheme != 'package') return;
429
430 node.text = pathToUrl(
431 path.normalize(path.join(_pathToOriginalCss, node.text)));
432 }
433 }
434
435 List<UrlInfo> findImportsInStyleSheet(StyleSheet styleSheet, 361 List<UrlInfo> findImportsInStyleSheet(StyleSheet styleSheet,
436 String packageRoot, UrlInfo inputUrl, Messages messages) { 362 String packageRoot, UrlInfo inputUrl, Messages messages) {
437 var visitor = new CssImports(packageRoot, inputUrl, messages); 363 var visitor = new _CssImports(packageRoot, inputUrl, messages);
438 visitor.visitTree(styleSheet); 364 visitor.visitTree(styleSheet);
439 return visitor.urlInfos; 365 return visitor.urlInfos;
440 } 366 }
441 367
442 /** 368 /**
443 * Find any imports in the style sheet; normalize the style sheet href and 369 * Find any imports in the style sheet; normalize the style sheet href and
444 * return a list of all fully qualified CSS files. 370 * return a list of all fully qualified CSS files.
445 */ 371 */
446 class CssImports extends Visitor { 372 class _CssImports extends Visitor {
447 final String packageRoot; 373 final String packageRoot;
448 374
449 /** Input url of the css file, used to normalize relative import urls. */ 375 /** Input url of the css file, used to normalize relative import urls. */
450 final UrlInfo inputUrl; 376 final UrlInfo inputUrl;
451 377
452 /** List of all imported style sheets. */ 378 /** List of all imported style sheets. */
453 final List<UrlInfo> urlInfos = []; 379 final List<UrlInfo> urlInfos = [];
454 380
455 final Messages _messages; 381 final Messages _messages;
456 382
457 CssImports(this.packageRoot, this.inputUrl, this._messages); 383 _CssImports(this.packageRoot, this.inputUrl, this._messages);
458 384
459 void visitTree(StyleSheet tree) { 385 void visitTree(StyleSheet tree) {
460 visitStyleSheet(tree); 386 visitStyleSheet(tree);
461 } 387 }
462 388
463 void visitImportDirective(ImportDirective node) { 389 void visitImportDirective(ImportDirective node) {
464 var urlInfo = UrlInfo.resolve(node.import, inputUrl, 390 var urlInfo = UrlInfo.resolve(node.import, inputUrl,
465 node.span, packageRoot, _messages, ignoreAbsolute: true); 391 node.span, packageRoot, _messages, ignoreAbsolute: true);
466 if (urlInfo == null) return; 392 if (urlInfo == null) return;
467 urlInfos.add(urlInfo); 393 urlInfos.add(urlInfo);
(...skipping 13 matching lines...) Expand all
481 // Note: errors aren't fatal in HTML (unless strict mode is on). 407 // Note: errors aren't fatal in HTML (unless strict mode is on).
482 // So just print them as warnings. 408 // So just print them as warnings.
483 for (var e in errors) { 409 for (var e in errors) {
484 messages.warning(e.message, e.span); 410 messages.warning(e.message, e.span);
485 } 411 }
486 412
487 return stylesheet; 413 return stylesheet;
488 } 414 }
489 415
490 /** Find terminal definition (non VarUsage implies real CSS value). */ 416 /** Find terminal definition (non VarUsage implies real CSS value). */
491 VarDefinition findTerminalVarDefinition(Map<String, VarDefinition> varDefs, 417 VarDefinition _findTerminalVarDefinition(Map<String, VarDefinition> varDefs,
492 VarDefinition varDef) { 418 VarDefinition varDef) {
493 var expressions = varDef.expression as Expressions; 419 var expressions = varDef.expression as Expressions;
494 for (var expr in expressions.expressions) { 420 for (var expr in expressions.expressions) {
495 if (expr is VarUsage) { 421 if (expr is VarUsage) {
496 var usageName = (expr as VarUsage).name; 422 var usageName = (expr as VarUsage).name;
497 var foundDef = varDefs[usageName]; 423 var foundDef = varDefs[usageName];
498 424
499 // If foundDef is unknown check if defaultValues; if it exist then resolve 425 // If foundDef is unknown check if defaultValues; if it exist then resolve
500 // to terminal value. 426 // to terminal value.
501 if (foundDef == null) { 427 if (foundDef == null) {
502 // We're either a VarUsage or terminal definition if in varDefs; 428 // We're either a VarUsage or terminal definition if in varDefs;
503 // either way replace VarUsage with it's default value because the 429 // either way replace VarUsage with it's default value because the
504 // VarDefinition isn't found. 430 // VarDefinition isn't found.
505 var defaultValues = (expr as VarUsage).defaultValues; 431 var defaultValues = (expr as VarUsage).defaultValues;
506 var replaceExprs = expressions.expressions; 432 var replaceExprs = expressions.expressions;
507 assert(replaceExprs.length == 1); 433 assert(replaceExprs.length == 1);
508 replaceExprs.replaceRange(0, 1, defaultValues); 434 replaceExprs.replaceRange(0, 1, defaultValues);
509 return varDef; 435 return varDef;
510 } 436 }
511 if (foundDef is VarDefinition) { 437 if (foundDef is VarDefinition) {
512 return findTerminalVarDefinition(varDefs, foundDef); 438 return _findTerminalVarDefinition(varDefs, foundDef);
513 } 439 }
514 } else { 440 } else {
515 // Return real CSS property. 441 // Return real CSS property.
516 return varDef; 442 return varDef;
517 } 443 }
518 } 444 }
519 445
520 // Didn't point to a var definition that existed. 446 // Didn't point to a var definition that existed.
521 return varDef; 447 return varDef;
522 } 448 }
523 449
524 /** 450 /**
525 * Find urls imported inside style tags under [info]. If [info] is a FileInfo 451 * Find urls imported inside style tags under [info]. If [info] is a FileInfo
526 * then process only style tags in the body (don't process any style tags in a 452 * then process only style tags in the body (don't process any style tags in a
527 * component). If [info] is a ComponentInfo only process style tags inside of 453 * component). If [info] is a ComponentInfo only process style tags inside of
528 * the element are processed. For an [info] of type FileInfo [node] is the 454 * the element are processed. For an [info] of type FileInfo [node] is the
529 * file's document and for an [info] of type ComponentInfo then [node] is the 455 * file's document and for an [info] of type ComponentInfo then [node] is the
530 * component's element tag. 456 * component's element tag.
531 */ 457 */
532 List<UrlInfo> findUrlsImported(LibraryInfo info, UrlInfo inputUrl, 458 List<UrlInfo> findUrlsImported(LibraryInfo info, UrlInfo inputUrl,
533 String packageRoot, Node node, Messages messages, CompilerOptions options) { 459 String packageRoot, Node node, Messages messages, CompilerOptions options) {
534 // Process any @imports inside of the <style> tag. 460 // Process any @imports inside of the <style> tag.
535 var styleProcessor = 461 var styleProcessor =
536 new CssStyleTag(packageRoot, info, inputUrl, messages, options); 462 new _CssStyleTag(packageRoot, info, inputUrl, messages, options);
537 styleProcessor.visit(node); 463 styleProcessor.visit(node);
538 return styleProcessor.imports; 464 return styleProcessor.imports;
539 } 465 }
540 466
541 /* Process CSS inside of a style tag. */ 467 /* Process CSS inside of a style tag. */
542 class CssStyleTag extends TreeVisitor { 468 class _CssStyleTag extends TreeVisitor {
543 final String _packageRoot; 469 final String _packageRoot;
544 470
545 /** Either a FileInfo or ComponentInfo. */ 471 /** Either a FileInfo or ComponentInfo. */
546 final LibraryInfo _info; 472 final LibraryInfo _info;
547 final Messages _messages; 473 final Messages _messages;
548 final CompilerOptions _options; 474 final CompilerOptions _options;
549 475
550 /** 476 /**
551 * Path of the declaring file, for a [_info] of type FileInfo it's the file's 477 * Path of the declaring file, for a [_info] of type FileInfo it's the file's
552 * path for a type ComponentInfo it's the declaring file path. 478 * path for a type ComponentInfo it's the declaring file path.
553 */ 479 */
554 final UrlInfo _inputUrl; 480 final UrlInfo _inputUrl;
555 481
556 /** List of @imports found. */ 482 /** List of @imports found. */
557 List<UrlInfo> imports = []; 483 List<UrlInfo> imports = [];
558 484
559 CssStyleTag(this._packageRoot, this._info, this._inputUrl, this._messages, 485 _CssStyleTag(this._packageRoot, this._info, this._inputUrl, this._messages,
560 this._options); 486 this._options);
561 487
562 void visitElement(Element node) { 488 void visitElement(Element node) {
563 // Don't process any style tags inside of element if we're processing a 489 // Don't process any style tags inside of element if we're processing a
564 // FileInfo. The style tags inside of a component defintion will be 490 // FileInfo. The style tags inside of a component defintion will be
565 // processed when _info is a ComponentInfo. 491 // processed when _info is a ComponentInfo.
566 if (node.tagName == 'polymer-element' && _info is FileInfo) return; 492 if (node.tagName == 'polymer-element' && _info is FileInfo) return;
567 if (node.tagName == 'style') { 493 if (node.tagName == 'style') {
568 // Parse the contents of the scoped style tag. 494 // Parse the contents of the scoped style tag.
569 var styleSheet = parseCss(node.nodes.single.value, _messages, _options); 495 var styleSheet = parseCss(node.nodes.single.value, _messages, _options);
570 if (styleSheet != null) { 496 if (styleSheet != null) {
571 _info.styleSheets.add(styleSheet); 497 _info.styleSheets.add(styleSheet);
572 498
573 // TODO(terry): Check on scoped attribute there's a rumor that styles
574 // might always be scoped in a component.
575 // TODO(terry): May need to handle multiple style tags some with scoped
576 // and some without for now first style tag determines how
577 // CSS is emitted.
578 if (node.attributes.containsKey('scoped') && _info is ComponentInfo) {
579 (_info as ComponentInfo).scoped = true;
580 }
581
582 // Find all imports return list of @imports in this style tag. 499 // Find all imports return list of @imports in this style tag.
583 var urlInfos = findImportsInStyleSheet(styleSheet, _packageRoot, 500 var urlInfos = findImportsInStyleSheet(styleSheet, _packageRoot,
584 _inputUrl, _messages); 501 _inputUrl, _messages);
585 imports.addAll(urlInfos); 502 imports.addAll(urlInfos);
586 } 503 }
587 } 504 }
588 super.visitElement(node); 505 super.visitElement(node);
589 } 506 }
590 } 507 }
OLDNEW
« no previous file with comments | « lib/src/compiler_options.dart ('k') | lib/src/css_emitters.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698