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 |