Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 library html_css_fixup; |
| 6 | 6 |
| 7 import 'dart:json' as json; | 7 import 'dart:json' as json; |
| 8 | 8 |
| 9 import 'package:csslib/parser.dart' as css; | 9 import 'package:csslib/parser.dart' as css; |
| 10 import 'package:csslib/visitor.dart'; | 10 import 'package:csslib/visitor.dart'; |
| 11 import 'package:html5lib/dom.dart'; | 11 import 'package:html5lib/dom.dart'; |
| 12 import 'package:html5lib/dom_parsing.dart'; | 12 import 'package:html5lib/dom_parsing.dart'; |
| 13 | 13 |
| 14 import 'compiler.dart'; | 14 import 'compiler.dart'; |
| 15 import 'emitters.dart'; | 15 import 'emitters.dart'; |
| 16 import 'info.dart'; | 16 import 'info.dart'; |
| 17 import 'messages.dart'; | 17 import 'messages.dart'; |
| 18 import 'options.dart'; | 18 import 'options.dart'; |
| 19 import 'paths.dart'; | 19 import 'paths.dart'; |
| 20 import 'utils.dart'; | 20 import 'utils.dart'; |
| 21 | 21 |
| 22 /** | 22 // TODO(terry): Replace with enum when supported. |
|
Jennifer Messerly
2013/07/18 23:56:04
remove TODO?
Siggi Cherem (dart-lang)
2013/07/19 22:52:52
Done.
| |
| 23 * Helper function returns [true] if CSS polyfill is on and component has a | 23 /** Enum for type of polyfills supported. */ |
| 24 * scoped style tag. | 24 class CssPolyfillKind { |
| 25 */ | 25 final index; |
| 26 bool useCssPolyfill(CompilerOptions opts, ComponentInfo component) => | 26 const CssPolyfillKind(this.index); |
| 27 opts.processCss && component.scoped; | 27 |
| 28 /** Emit CSS selectors as seen (no polyfill). */ | |
| 29 static const NO_POLYFILL = const CssPolyfillKind(0); | |
| 30 | |
| 31 /** Emit CSS selectors scoped to the "is" attribute of the component. */ | |
| 32 static const SCOPED_POLYFILL = const CssPolyfillKind(1); | |
| 33 | |
| 34 /** Emit CSS selectors mangled. */ | |
| 35 static const MANGLED_POLYFILL = const CssPolyfillKind(2); | |
| 36 | |
| 37 static CssPolyfillKind of(CompilerOptions options, ComponentInfo component) { | |
| 38 if (!options.processCss || !component.scoped) return NO_POLYFILL; | |
| 39 if (options.mangleCss) return MANGLED_POLYFILL; | |
| 40 if (!component.hasAuthorStyles && !hasCssReset) return MANGLED_POLYFILL; | |
| 41 return SCOPED_POLYFILL; | |
| 42 } | |
| 43 } | |
| 44 | |
| 28 | 45 |
| 29 /** | 46 /** |
| 30 * If processCss is enabled, prefix any component's HTML attributes for id or | 47 * If processCss is enabled, prefix any component's HTML attributes for id or |
| 31 * class to reference the mangled CSS class name or id. | 48 * class to reference the mangled CSS class name or id. |
| 32 */ | 49 */ |
| 33 void fixupHtmlCss(FileInfo fileInfo, CompilerOptions options, | 50 void fixupHtmlCss(FileInfo fileInfo, CompilerOptions options) { |
| 34 CssPolyfillKind polyfillKind(ComponentInfo component)) { | |
| 35 // Walk the HTML tree looking for class names or id that are in our parsed | 51 // Walk the HTML tree looking for class names or id that are in our parsed |
| 36 // stylesheet selectors and making those CSS classes and ids unique to that | 52 // stylesheet selectors and making those CSS classes and ids unique to that |
| 37 // component. | 53 // component. |
| 38 if (options.verbose) { | 54 if (options.verbose) { |
| 39 print(" CSS fixup ${path.basename(fileInfo.inputUrl.resolvedPath)}"); | 55 print(" CSS fixup ${path.basename(fileInfo.inputUrl.resolvedPath)}"); |
| 40 } | 56 } |
| 41 for (var component in fileInfo.declaredComponents) { | 57 for (var component in fileInfo.declaredComponents) { |
| 42 // Mangle class names and element ids in the HTML to match the stylesheet. | 58 // Mangle class names and element ids in the HTML to match the stylesheet. |
| 43 // TODO(terry): Allow more than one style sheet per component. | 59 // TODO(terry): Allow more than one style sheet per component. |
| 44 if (component.styleSheets.length == 1) { | 60 if (component.styleSheets.length == 1) { |
| 45 // For components only 1 stylesheet allowed. | 61 // For components only 1 stylesheet allowed. |
| 46 var styleSheet = component.styleSheets[0]; | 62 var styleSheet = component.styleSheets[0]; |
| 47 var prefix = polyfillKind(component) == CssPolyfillKind.MANGLED_POLYFILL ? | 63 var prefix = CssPolyfillKind.of(options, component) == |
| 48 component.tagName : null; | 64 CssPolyfillKind.MANGLED_POLYFILL ? component.tagName : null; |
| 49 | 65 |
| 50 // List of referenced #id and .class in CSS. | 66 // List of referenced #id and .class in CSS. |
| 51 var knownCss = new IdClassVisitor()..visitTree(styleSheet); | 67 var knownCss = new IdClassVisitor()..visitTree(styleSheet); |
| 52 // Prefix all id and class refs in CSS selectors and HTML attributes. | 68 // Prefix all id and class refs in CSS selectors and HTML attributes. |
| 53 new _ScopedStyleRenamer(knownCss, prefix, options.debugCss) | 69 new _ScopedStyleRenamer(knownCss, prefix, options.debugCss) |
| 54 .visit(component.elementNode); | 70 .visit(component.element); |
| 55 } | 71 } |
| 56 } | 72 } |
| 57 } | 73 } |
| 58 | 74 |
| 59 /** Build list of every CSS class name and id selector in a stylesheet. */ | 75 /** Build list of every CSS class name and id selector in a stylesheet. */ |
| 60 class IdClassVisitor extends Visitor { | 76 class IdClassVisitor extends Visitor { |
| 61 final Set<String> classes = new Set(); | 77 final Set<String> classes = new Set(); |
| 62 final Set<String> ids = new Set(); | 78 final Set<String> ids = new Set(); |
| 63 | 79 |
| 64 void visitClassSelector(ClassSelector node) { | 80 void visitClassSelector(ClassSelector node) { |
| 65 classes.add(node.name); | 81 classes.add(node.name); |
| 66 } | 82 } |
| 67 | 83 |
| 68 void visitIdSelector(IdSelector node) { | 84 void visitIdSelector(IdSelector node) { |
| 69 ids.add(node.name); | 85 ids.add(node.name); |
| 70 } | 86 } |
| 71 } | 87 } |
| 72 | 88 |
| 73 /** Build the Dart map of managled class/id names and component tag name. */ | 89 /** Build the Dart map of managled class/id names and component tag name. */ |
| 74 Map _createCssSimpleSelectors(IdClassVisitor visitedCss, ComponentInfo info, | 90 Map _createCssSimpleSelectors(IdClassVisitor visitedCss, ComponentInfo info, |
| 75 bool mangleNames) { | 91 CssPolyfillKind kind) { |
| 92 bool mangleNames = kind == CssPolyfillKind.MANGLED_POLYFILL; | |
| 76 Map selectors = {}; | 93 Map selectors = {}; |
| 77 if (visitedCss != null) { | 94 if (visitedCss != null) { |
| 78 for (var cssClass in visitedCss.classes) { | 95 for (var cssClass in visitedCss.classes) { |
| 79 selectors['.$cssClass'] = | 96 selectors['.$cssClass'] = |
| 80 mangleNames ? '${info.tagName}_$cssClass' : cssClass; | 97 mangleNames ? '${info.tagName}_$cssClass' : cssClass; |
| 81 } | 98 } |
| 82 for (var id in visitedCss.ids) { | 99 for (var id in visitedCss.ids) { |
| 83 selectors['#$id'] = mangleNames ? '${info.tagName}_$id' : id; | 100 selectors['#$id'] = mangleNames ? '${info.tagName}_$id' : id; |
| 84 } | 101 } |
| 85 } | 102 } |
| 86 | 103 |
| 87 // Add tag name selector x-comp == [is="x-comp"]. | 104 // Add tag name selector x-comp == [is="x-comp"]. |
| 88 var componentName = info.tagName; | 105 var componentName = info.tagName; |
| 89 selectors['$componentName'] = '[is="$componentName"]'; | 106 selectors['$componentName'] = '[is="$componentName"]'; |
| 90 | 107 |
| 91 return selectors; | 108 return selectors; |
| 92 } | 109 } |
| 93 | 110 |
| 94 /** | 111 /** |
| 95 * Return a map of simple CSS selectors (class and id selectors) as a Dart map | 112 * Return a map of simple CSS selectors (class and id selectors) as a Dart map |
| 96 * definition. | 113 * definition. |
| 97 */ | 114 */ |
| 98 String createCssSelectorsExpression(ComponentInfo info, bool mangled) { | 115 String createCssSelectorsExpression(ComponentInfo info, CssPolyfillKind kind) { |
| 99 var cssVisited = new IdClassVisitor(); | 116 var cssVisited = new IdClassVisitor(); |
| 100 | 117 |
| 101 // For components only 1 stylesheet allowed. | 118 // For components only 1 stylesheet allowed. |
| 102 if (!info.styleSheets.isEmpty && info.styleSheets.length == 1) { | 119 if (!info.styleSheets.isEmpty && info.styleSheets.length == 1) { |
| 103 var styleSheet = info.styleSheets[0]; | 120 var styleSheet = info.styleSheets[0]; |
| 104 cssVisited..visitTree(styleSheet); | 121 cssVisited..visitTree(styleSheet); |
| 105 } | 122 } |
| 106 | 123 |
| 107 return json.stringify(_createCssSimpleSelectors(cssVisited, info, mangled)); | 124 return json.stringify(_createCssSimpleSelectors(cssVisited, info, kind)); |
| 108 } | 125 } |
| 109 | 126 |
| 110 // TODO(terry): Need to handle other selectors than IDs/classes like tag name | 127 // TODO(terry): Need to handle other selectors than IDs/classes like tag name |
| 111 // e.g., DIV { color: red; } | 128 // e.g., DIV { color: red; } |
| 112 // TODO(terry): Would be nice if we didn't need to mangle names; requires users | 129 // TODO(terry): Would be nice if we didn't need to mangle names; requires users |
| 113 // to be careful in their code and makes it more than a "polyfill". | 130 // to be careful in their code and makes it more than a "polyfill". |
| 114 // Maybe mechanism that generates CSS class name for scoping. This | 131 // Maybe mechanism that generates CSS class name for scoping. This |
| 115 // would solve tag name selectors (see above TODO). | 132 // would solve tag name selectors (see above TODO). |
| 116 /** | 133 /** |
| 117 * Fix a component's HTML to implement scoped stylesheets. | 134 * Fix a component's HTML to implement scoped stylesheets. |
| (...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 538 /** List of @imports found. */ | 555 /** List of @imports found. */ |
| 539 List<UrlInfo> imports = []; | 556 List<UrlInfo> imports = []; |
| 540 | 557 |
| 541 CssStyleTag(this._packageRoot, this._info, this._inputUrl, this._messages, | 558 CssStyleTag(this._packageRoot, this._info, this._inputUrl, this._messages, |
| 542 this._options); | 559 this._options); |
| 543 | 560 |
| 544 void visitElement(Element node) { | 561 void visitElement(Element node) { |
| 545 // Don't process any style tags inside of element if we're processing a | 562 // Don't process any style tags inside of element if we're processing a |
| 546 // FileInfo. The style tags inside of a component defintion will be | 563 // FileInfo. The style tags inside of a component defintion will be |
| 547 // processed when _info is a ComponentInfo. | 564 // processed when _info is a ComponentInfo. |
| 548 if (node.tagName == 'element' && _info is FileInfo) return; | 565 if (node.tagName == 'polymer-element' && _info is FileInfo) return; |
| 549 if (node.tagName == 'style') { | 566 if (node.tagName == 'style') { |
| 550 // Parse the contents of the scoped style tag. | 567 // Parse the contents of the scoped style tag. |
| 551 var styleSheet = parseCss(node.nodes.single.value, _messages, _options); | 568 var styleSheet = parseCss(node.nodes.single.value, _messages, _options); |
| 552 if (styleSheet != null) { | 569 if (styleSheet != null) { |
| 553 _info.styleSheets.add(styleSheet); | 570 _info.styleSheets.add(styleSheet); |
| 554 | 571 |
| 555 // TODO(terry): Check on scoped attribute there's a rumor that styles | 572 // TODO(terry): Check on scoped attribute there's a rumor that styles |
| 556 // might always be scoped in a component. | 573 // might always be scoped in a component. |
| 557 // TODO(terry): May need to handle multiple style tags some with scoped | 574 // TODO(terry): May need to handle multiple style tags some with scoped |
| 558 // and some without for now first style tag determines how | 575 // and some without for now first style tag determines how |
| 559 // CSS is emitted. | 576 // CSS is emitted. |
| 560 if (node.attributes.containsKey('scoped') && _info is ComponentInfo) { | 577 if (node.attributes.containsKey('scoped') && _info is ComponentInfo) { |
| 561 (_info as ComponentInfo).scoped = true; | 578 (_info as ComponentInfo).scoped = true; |
| 562 } | 579 } |
| 563 | 580 |
| 564 // Find all imports return list of @imports in this style tag. | 581 // Find all imports return list of @imports in this style tag. |
| 565 var urlInfos = findImportsInStyleSheet(styleSheet, _packageRoot, | 582 var urlInfos = findImportsInStyleSheet(styleSheet, _packageRoot, |
| 566 _inputUrl, _messages); | 583 _inputUrl, _messages); |
| 567 imports.addAll(urlInfos); | 584 imports.addAll(urlInfos); |
| 568 } | 585 } |
| 569 } | 586 } |
| 570 super.visitElement(node); | 587 super.visitElement(node); |
| 571 } | 588 } |
| 572 } | 589 } |
| OLD | NEW |