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

Side by Side Diff: lib/src/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/shadowdom.min.js ('k') | lib/src/code_printer.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 /** 5 /**
6 * Part of the template compilation that concerns with extracting information 6 * Part of the template compilation that concerns with extracting information
7 * from the HTML parse tree. 7 * from the HTML parse tree.
8 */ 8 */
9 library analyzer; 9 library analyzer;
10 10
11 import 'package:csslib/parser.dart' as css;
12 import 'package:csslib/visitor.dart' show StyleSheet, treeToDebugString, Visitor , Expressions, VarDefinition;
13 import 'package:html5lib/dom.dart'; 11 import 'package:html5lib/dom.dart';
14 import 'package:html5lib/dom_parsing.dart'; 12 import 'package:html5lib/dom_parsing.dart';
15 import 'package:source_maps/span.dart' hide SourceFile; 13 import 'package:source_maps/span.dart' hide SourceFile;
16 14
17 import 'custom_tag_name.dart'; 15 import 'custom_tag_name.dart';
18 import 'dart_parser.dart'; 16 import 'dart_parser.dart' show parseDartCode;
19 import 'files.dart'; 17 import 'files.dart';
20 import 'html_css_fixup.dart';
21 import 'info.dart'; 18 import 'info.dart';
22 import 'messages.dart'; 19 import 'messages.dart';
23 import 'summary.dart'; 20 import 'summary.dart';
24 import 'utils.dart';
25 21
26 /** 22 /**
27 * Finds custom elements in this file and the list of referenced files with 23 * Finds custom elements in this file and the list of referenced files with
28 * component declarations. This is the first pass of analysis on a file. 24 * component declarations. This is the first pass of analysis on a file.
29 * 25 *
30 * Adds emitted error/warning messages to [messages], if [messages] is 26 * Adds emitted error/warning messages to [messages], if [messages] is
31 * supplied. 27 * supplied.
32 */ 28 */
33 FileInfo analyzeDefinitions(GlobalInfo global, UrlInfo inputUrl, 29 FileInfo analyzeDefinitions(GlobalInfo global, UrlInfo inputUrl,
34 Document document, String packageRoot, 30 Document document, String packageRoot,
35 Messages messages, {bool isEntryPoint: false}) { 31 Messages messages, {bool isEntryPoint: false}) {
36 var result = new FileInfo(inputUrl, isEntryPoint); 32 var result = new FileInfo(inputUrl, isEntryPoint);
37 var loader = new _ElementLoader(global, result, packageRoot, messages); 33 var loader = new _ElementLoader(global, result, packageRoot, messages);
38 loader.visit(document); 34 loader.visit(document);
39 return result; 35 return result;
40 } 36 }
41 37
42 /** 38 /**
43 * Extract relevant information from [source] and it's children.
44 * Used for testing.
45 *
46 * Adds emitted error/warning messages to [messages], if [messages] is
47 * supplied.
48 */
49 FileInfo analyzeNodeForTesting(Node source, Messages messages,
50 {String filepath: 'mock_testing_file.html'}) {
51 var result = new FileInfo(new UrlInfo(filepath, filepath, null));
52 new _Analyzer(result, new IntIterator(), new GlobalInfo(), messages)
53 .visit(source);
54 return result;
55 }
56
57 /**
58 * Extract relevant information from all files found from the root document. 39 * Extract relevant information from all files found from the root document.
59 * 40 *
60 * Adds emitted error/warning messages to [messages], if [messages] is 41 * Adds emitted error/warning messages to [messages], if [messages] is
61 * supplied. 42 * supplied.
62 */ 43 */
63 void analyzeFile(SourceFile file, Map<String, FileInfo> info, 44 void analyzeFile(SourceFile file, Map<String, FileInfo> info,
64 Iterator<int> uniqueIds, GlobalInfo global, 45 Iterator<int> uniqueIds, GlobalInfo global,
65 Messages messages) { 46 Messages messages, emulateScopedCss) {
66 var fileInfo = info[file.path]; 47 var fileInfo = info[file.path];
67 var analyzer = new _Analyzer(fileInfo, uniqueIds, global, messages); 48 var analyzer = new _Analyzer(fileInfo, uniqueIds, global, messages,
49 emulateScopedCss);
68 analyzer._normalize(fileInfo, info); 50 analyzer._normalize(fileInfo, info);
69 analyzer.visit(file.document); 51 analyzer.visit(file.document);
70 } 52 }
71 53
72 54
73 /** A visitor that walks the HTML to extract all the relevant information. */ 55 /** A visitor that walks the HTML to extract all the relevant information. */
74 class _Analyzer extends TreeVisitor { 56 class _Analyzer extends TreeVisitor {
75 final FileInfo _fileInfo; 57 final FileInfo _fileInfo;
76 LibraryInfo _currentInfo; 58 LibraryInfo _currentInfo;
77 Iterator<int> _uniqueIds; 59 Iterator<int> _uniqueIds;
78 GlobalInfo _global; 60 GlobalInfo _global;
79 Messages _messages; 61 Messages _messages;
80 62
81 int _generatedClassNumber = 0; 63 int _generatedClassNumber = 0;
82 64
83 /** 65 /**
84 * Whether to keep indentation spaces. Break lines and indentation spaces 66 * Whether to keep indentation spaces. Break lines and indentation spaces
85 * within templates are preserved in HTML. When users specify the attribute 67 * within templates are preserved in HTML. When users specify the attribute
86 * 'indentation="remove"' on a template tag, we'll trim those indentation 68 * 'indentation="remove"' on a template tag, we'll trim those indentation
87 * spaces that occur within that tag and its decendants. If any decendant 69 * spaces that occur within that tag and its decendants. If any decendant
88 * specifies 'indentation="preserve"', then we'll switch back to the normal 70 * specifies 'indentation="preserve"', then we'll switch back to the normal
89 * behavior. 71 * behavior.
90 */ 72 */
91 bool _keepIndentationSpaces = true; 73 bool _keepIndentationSpaces = true;
92 74
93 _Analyzer(this._fileInfo, this._uniqueIds, this._global, this._messages) { 75 final bool _emulateScopedCss;
76
77 _Analyzer(this._fileInfo, this._uniqueIds, this._global, this._messages,
78 this._emulateScopedCss) {
94 _currentInfo = _fileInfo; 79 _currentInfo = _fileInfo;
95 } 80 }
96 81
97 void visitElement(Element node) { 82 void visitElement(Element node) {
98 if (node.tagName == 'script') { 83 if (node.tagName == 'script') {
99 // We already extracted script tags in previous phase. 84 // We already extracted script tags in previous phase.
100 return; 85 return;
101 } 86 }
102 87
103 if (node.tagName == 'style') { 88 if (node.tagName == 'style') {
104 // We've already parsed the CSS. 89 // We've already parsed the CSS.
105 // If this is a component remove the style node. 90 // If this is a component remove the style node.
106 if (_currentInfo is ComponentInfo) node.remove(); 91 if (_currentInfo is ComponentInfo && _emulateScopedCss) node.remove();
107 return; 92 return;
108 } 93 }
109 94
110 node = _bindAndReplaceElement(node); 95 node = _bindAndReplaceElement(node);
111 96
112 var lastInfo = _currentInfo; 97 var lastInfo = _currentInfo;
113 if (node.tagName == 'polymer-element') { 98 if (node.tagName == 'polymer-element') {
114 // If element is invalid _ElementLoader already reported an error, but 99 // If element is invalid _ElementLoader already reported an error, but
115 // we skip the body of the element here. 100 // we skip the body of the element here.
116 var name = node.attributes['name']; 101 var name = node.attributes['name'];
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after
431 // inside a Shadow DOM should be scoped to that <template> tag, and not 416 // inside a Shadow DOM should be scoped to that <template> tag, and not
432 // visible from the outside. 417 // visible from the outside.
433 if (_currentInfo is ComponentInfo) { 418 if (_currentInfo is ComponentInfo) {
434 _messages.error('Nested component definitions are not yet supported.', 419 _messages.error('Nested component definitions are not yet supported.',
435 node.sourceSpan); 420 node.sourceSpan);
436 return; 421 return;
437 } 422 }
438 423
439 var tagName = node.attributes['name']; 424 var tagName = node.attributes['name'];
440 var extendsTag = node.attributes['extends']; 425 var extendsTag = node.attributes['extends'];
441 var templateNodes = node.nodes.where((n) => n.tagName == 'template');
442 426
443 if (tagName == null) { 427 if (tagName == null) {
444 _messages.error('Missing tag name of the component. Please include an ' 428 _messages.error('Missing tag name of the component. Please include an '
445 'attribute like \'name="your-tag-name"\'.', 429 'attribute like \'name="your-tag-name"\'.',
446 node.sourceSpan); 430 node.sourceSpan);
447 return; 431 return;
448 } 432 }
449 433
450 if (extendsTag == null) { 434 if (extendsTag == null) {
451 // From the spec: 435 // From the spec:
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
556 Text text = node.nodes[0]; 540 Text text = node.nodes[0];
557 541
558 if (_currentInfo.codeAttached) { 542 if (_currentInfo.codeAttached) {
559 _tooManyScriptsError(node); 543 _tooManyScriptsError(node);
560 } else if (_currentInfo == _fileInfo && !_fileInfo.isEntryPoint) { 544 } else if (_currentInfo == _fileInfo && !_fileInfo.isEntryPoint) {
561 _messages.warning('top-level dart code is ignored on ' 545 _messages.warning('top-level dart code is ignored on '
562 ' HTML pages that define components, but are not the entry HTML ' 546 ' HTML pages that define components, but are not the entry HTML '
563 'file.', node.sourceSpan); 547 'file.', node.sourceSpan);
564 } else { 548 } else {
565 _currentInfo.inlinedCode = parseDartCode( 549 _currentInfo.inlinedCode = parseDartCode(
566 _currentInfo.dartCodeUrl.resolvedPath, text.value, _messages, 550 _currentInfo.dartCodeUrl.resolvedPath, text.value,
567 text.sourceSpan.start); 551 text.sourceSpan.start);
568 if (_currentInfo.userCode.partOf != null) { 552 if (_currentInfo.userCode.partOf != null) {
569 _messages.error('expected a library, not a part.', 553 _messages.error('expected a library, not a part.',
570 node.sourceSpan); 554 node.sourceSpan);
571 } 555 }
572 } 556 }
573 } 557 }
574 558
575 void _tooManyScriptsError(Node node) { 559 void _tooManyScriptsError(Node node) {
576 var location = _currentInfo is ComponentInfo ? 560 var location = _currentInfo is ComponentInfo ?
577 'a custom element declaration' : 'the top-level HTML page'; 561 'a custom element declaration' : 'the top-level HTML page';
578 562
579 _messages.error('there should be only one dart script tag in $location.', 563 _messages.error('there should be only one dart script tag in $location.',
580 node.sourceSpan); 564 node.sourceSpan);
581 } 565 }
582 } 566 }
583
584
585 void analyzeCss(String packageRoot, List<SourceFile> files,
586 Map<String, FileInfo> info, Map<String, String> pseudoElements,
587 Messages messages, {warningsAsErrors: false}) {
588 var analyzer = new _AnalyzerCss(packageRoot, info, pseudoElements, messages,
589 warningsAsErrors);
590 for (var file in files) analyzer.process(file);
591 analyzer.normalize();
592 }
593
594 class _AnalyzerCss {
595 final String packageRoot;
596 final Map<String, FileInfo> info;
597 final Map<String, String> _pseudoElements;
598 final Messages _messages;
599 final bool _warningsAsErrors;
600
601 Set<StyleSheet> allStyleSheets = new Set<StyleSheet>();
602
603 /**
604 * [_pseudoElements] list of known pseudo attributes found in HTML, any
605 * CSS pseudo-elements 'name::custom-element' is mapped to the manged name
606 * associated with the pseudo-element key.
607 */
608 _AnalyzerCss(this.packageRoot, this.info, this._pseudoElements,
609 this._messages, this._warningsAsErrors);
610
611 /**
612 * Run the analyzer on every file that is a style sheet or any component that
613 * has a style tag.
614 */
615 void process(SourceFile file) {
616 var fileInfo = info[file.path];
617 if (file.isStyleSheet || fileInfo.styleSheets.length > 0) {
618 var styleSheets = processVars(fileInfo);
619
620 // Add to list of all style sheets analyzed.
621 allStyleSheets.addAll(styleSheets);
622 }
623
624 // Process any components.
625 for (var component in fileInfo.declaredComponents) {
626 var all = processVars(component);
627
628 // Add to list of all style sheets analyzed.
629 allStyleSheets.addAll(all);
630 }
631
632 processCustomPseudoElements();
633 }
634
635 void normalize() {
636 // Remove all var definitions for all style sheets analyzed.
637 for (var tree in allStyleSheets) new RemoveVarDefinitions().visitTree(tree);
638 }
639
640 List<StyleSheet> processVars(var libraryInfo) {
641 // Get list of all stylesheet(s) dependencies referenced from this file.
642 var styleSheets = _dependencies(libraryInfo).toList();
643
644 var errors = [];
645 css.analyze(styleSheets, errors: errors, options:
646 [_warningsAsErrors ? '--warnings_as_errors' : '', 'memory']);
647
648 // Print errors as warnings.
649 for (var e in errors) {
650 _messages.warning(e.message, e.span);
651 }
652
653 // Build list of all var definitions.
654 Map varDefs = new Map();
655 for (var tree in styleSheets) {
656 var allDefs = (new VarDefinitions()..visitTree(tree)).found;
657 allDefs.forEach((key, value) {
658 varDefs[key] = value;
659 });
660 }
661
662 // Resolve all definitions to a non-VarUsage (terminal expression).
663 varDefs.forEach((key, value) {
664 for (var expr in (value.expression as Expressions).expressions) {
665 var def = findTerminalVarDefinition(varDefs, value);
666 varDefs[key] = def;
667 }
668 });
669
670 // Resolve all var usages.
671 for (var tree in styleSheets) new ResolveVarUsages(varDefs).visitTree(tree);
672
673 return styleSheets;
674 }
675
676 processCustomPseudoElements() {
677 var polyFiller = new PseudoElementExpander(_pseudoElements);
678 for (var tree in allStyleSheets) {
679 polyFiller.visitTree(tree);
680 }
681 }
682
683 /**
684 * Given a component or file check if any stylesheets referenced. If so then
685 * return a list of all referenced stylesheet dependencies (@imports or <link
686 * rel="stylesheet" ..>).
687 */
688 Set<StyleSheet> _dependencies(var libraryInfo, {Set<StyleSheet> seen}) {
689 if (seen == null) seen = new Set();
690
691 // Used to resolve all pathing information.
692 var inputUrl = libraryInfo is FileInfo
693 ? libraryInfo.inputUrl
694 : (libraryInfo as ComponentInfo).declaringFile.inputUrl;
695
696 for (var styleSheet in libraryInfo.styleSheets) {
697 if (!seen.contains(styleSheet)) {
698 // TODO(terry): VM uses expandos to implement hashes. Currently, it's a
699 // linear (not constant) time cost (see dartbug.com/5746).
700 // If this bug isn't fixed and performance show's this a
701 // a problem we'll need to implement our own hashCode or
702 // use a different key for better perf.
703 // Add the stylesheet.
704 seen.add(styleSheet);
705
706 // Any other imports in this stylesheet?
707 var urlInfos = findImportsInStyleSheet(styleSheet, packageRoot,
708 inputUrl, _messages);
709
710 // Process other imports in this stylesheets.
711 for (var importSS in urlInfos) {
712 var importInfo = info[importSS.resolvedPath];
713 if (importInfo != null) {
714 // Add all known stylesheets processed.
715 seen.addAll(importInfo.styleSheets);
716 // Find dependencies for stylesheet referenced with a
717 // @import
718 for (var ss in importInfo.styleSheets) {
719 var urls = findImportsInStyleSheet(ss, packageRoot, inputUrl,
720 _messages);
721 for (var url in urls) {
722 _dependencies(info[url.resolvedPath], seen: seen);
723 }
724 }
725 }
726 }
727 }
728 }
729
730 return seen;
731 }
732 }
OLDNEW
« no previous file with comments | « lib/shadowdom.min.js ('k') | lib/src/code_printer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698