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 /** Transfomer that inlines polymer-element definitions from html imports. */ | 5 /** Transfomer that inlines polymer-element definitions from html imports. */ |
| 6 library polymer.src.build.import_inliner; | 6 library polymer.src.build.import_inliner; |
| 7 | 7 |
| 8 import 'dart:async'; | 8 import 'dart:async'; |
| 9 import 'dart:convert'; | 9 import 'dart:convert'; |
| 10 | 10 |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 31 final TransformOptions options; | 31 final TransformOptions options; |
| 32 | 32 |
| 33 ImportInliner(this.options); | 33 ImportInliner(this.options); |
| 34 | 34 |
| 35 /** Only run on entry point .html files. */ | 35 /** Only run on entry point .html files. */ |
| 36 Future<bool> isPrimary(Asset input) => | 36 Future<bool> isPrimary(Asset input) => |
| 37 new Future.value(options.isHtmlEntryPoint(input.id)); | 37 new Future.value(options.isHtmlEntryPoint(input.id)); |
| 38 | 38 |
| 39 Future apply(Transform transform) { | 39 Future apply(Transform transform) { |
| 40 var logger = transform.logger; | 40 var logger = transform.logger; |
| 41 var seen = new Set<AssetId>(); | |
| 42 var documents = []; | |
| 43 var id = transform.primaryInput.id; | 41 var id = transform.primaryInput.id; |
| 44 seen.add(id); | 42 var seen = new Set<AssetId>()..add(id); |
| 45 return readPrimaryAsHtml(transform).then((document) { | 43 var scriptIds = []; |
| 46 var future = _visitImports(document, id, transform, seen, documents); | 44 var imported = new DocumentFragment(); |
| 47 return future.then((importsFound) { | 45 Document document; |
| 48 // We produce a secondary asset with extra information for later phases. | |
| 49 var secondaryId = id.addExtension('.scriptUrls'); | |
| 50 if (!importsFound) { | |
| 51 transform.addOutput(transform.primaryInput); | |
| 52 transform.addOutput(new Asset.fromString(secondaryId, '[]')); | |
| 53 return; | |
| 54 } | |
| 55 | 46 |
| 56 // Split Dart script tags from all the other elements. Now that Dartium | 47 return readPrimaryAsHtml(transform).then((doc) { |
| 57 // only allows a single script tag per page, we can't inline script | 48 document = doc; |
| 58 // tags. Instead, we collect the urls of each script tag so we import | 49 return _visitImports(doc, id, transform, seen, imported, scriptIds); |
|
Jennifer Messerly
2014/02/12 00:48:47
visit imports now produces the two primary outputs
| |
| 59 // them directly from the Dart bootstrap code. | 50 }).then((importsFound) { |
|
Jennifer Messerly
2014/02/12 00:48:47
switched to this style to reduce nesting caused by
| |
| 60 var scripts = []; | |
| 61 | 51 |
| 62 var fragment = new DocumentFragment(); | 52 if (importsFound) { |
| 63 for (var importedDoc in documents) { | 53 document.body.insertBefore(imported, document.body.firstChild); |
| 64 bool first = true; | 54 transform.addOutput(new Asset.fromString(id, document.outerHtml)); |
| 65 for (var e in importedDoc.queryAll('script')) { | 55 } else { |
| 66 if (e.attributes['type'] == 'application/dart') { | 56 transform.addOutput(transform.primaryInput); |
| 67 e.remove(); | 57 } |
| 68 | 58 |
| 69 // only one Dart script per document is supported in Dartium. | 59 // We produce a secondary asset with extra information for later phases. |
| 70 if (first) { | 60 transform.addOutput(new Asset.fromString(id.addExtension('.scriptUrls'), |
| 71 first = false; | 61 JSON.encode(scriptIds, toEncodable: (id) => id.serialize()))); |
|
Jennifer Messerly
2014/02/12 00:48:47
an extra JSON.encode in the !importsFound case is
| |
| 72 scripts.add(e); | 62 }); |
| 73 } else { | 63 } |
| 74 // TODO(jmesserly): remove this when we are running linter. | |
| 75 logger.warning('more than one Dart script per HTML document is ' | |
| 76 'not supported. Script will be ignored.', | |
| 77 span: e.sourceSpan); | |
| 78 } | |
| 79 } | |
| 80 } | |
| 81 | 64 |
| 82 // TODO(jmesserly): should we merge the head too? | 65 /** |
| 83 fragment.nodes.addAll(importedDoc.body.nodes); | 66 * Visits imports in [document] and add the imported documents to [documents]. |
| 84 } | 67 * Documents are added in the order they appear, transitive imports are added |
| 68 * first. | |
| 69 */ | |
| 70 Future<bool> _visitImports(Document document, AssetId sourceId, | |
| 71 Transform transform, Set<AssetId> seen, DocumentFragment imported, | |
| 72 List<AssetId> scripts) { | |
| 85 | 73 |
| 86 document.body.insertBefore(fragment, document.body.firstChild); | 74 bool hasImports = false; |
| 87 | 75 |
| 88 for (var tag in document.queryAll('link')) { | 76 // Note: we need to preserve the import order in the generated output. |
| 89 if (tag.attributes['rel'] == 'import') tag.remove(); | 77 return Future.forEach(document.queryAll('link'), (tag) { |
|
Jennifer Messerly
2014/02/12 00:48:47
instead of two back to back for loops, it now just
| |
| 90 } | 78 if (tag.attributes['rel'] != 'import') return null; |
| 79 var href = tag.attributes['href']; | |
| 80 var id = resolve(sourceId, href, transform.logger, tag.sourceSpan); | |
| 81 hasImports = true; | |
| 91 | 82 |
| 92 transform.addOutput(new Asset.fromString(id, document.outerHtml)); | 83 tag.remove(); |
|
Jennifer Messerly
2014/02/12 00:48:47
remove the tag here, rather than another pass late
| |
| 84 if (id == null || !seen.add(id) || | |
|
Jennifer Messerly
2014/02/12 00:48:47
Set.add now returns a boolean (true if it was adde
| |
| 85 (id.package == 'polymer' && id.path == 'lib/init.html')) return null; | |
| 93 | 86 |
| 94 var scriptIds = []; | 87 // Loads an asset identified by [id], visits its imports and collects its |
| 95 for (var script in scripts) { | 88 // html imports. Then inlines it into the main document. |
| 89 Document importedDoc; | |
| 90 return readAsHtml(id, transform).then((doc) { | |
| 91 // Visit transitive imports first. | |
| 92 importedDoc = doc; | |
| 93 return _visitImports(doc, id, transform, seen, imported, scripts); | |
| 94 }).then((_) { | |
| 95 new _UrlNormalizer(transform, id).visit(importedDoc); | |
| 96 | |
| 97 _extractScripts(id, transform.logger, importedDoc, scripts); | |
|
Jennifer Messerly
2014/02/12 00:48:47
FYI -- I'm changing this back to sourceId, not imp
| |
| 98 | |
| 99 // TODO(jmesserly): figure out how this is working in vulcanizer. | |
| 100 // Do they produce a <body> tag with a <head> and <body> inside? | |
| 101 imported.nodes | |
| 102 ..addAll(importedDoc.head.nodes) | |
| 103 ..addAll(importedDoc.body.nodes); | |
| 104 }); | |
| 105 }).then((_) => hasImports); | |
| 106 } | |
| 107 | |
| 108 /** | |
| 109 * Split Dart script tags from all the other elements. Now that Dartium | |
| 110 * only allows a single script tag per page, we can't inline script | |
| 111 * tags. Instead, we collect the urls of each script tag so we import | |
| 112 * them directly from the Dart bootstrap code. | |
| 113 */ | |
| 114 static void _extractScripts(id, logger, document, List scriptIds) { | |
|
Jennifer Messerly
2014/02/12 00:48:47
this function seemed pretty self contained
| |
| 115 bool first = true; | |
| 116 for (var script in document.queryAll('script')) { | |
| 117 if (script.attributes['type'] == 'application/dart') { | |
| 118 script.remove(); | |
| 119 | |
| 120 // only one Dart script per document is supported in Dartium. | |
| 121 if (first) { | |
| 122 first = false; | |
| 123 | |
| 96 var src = script.attributes['src']; | 124 var src = script.attributes['src']; |
| 97 if (src == null) { | 125 if (src == null) { |
| 98 logger.warning('unexpected script without a src url. The ' | 126 logger.warning('unexpected script without a src url. The ' |
| 99 'ImportInliner transformer should run after running the ' | 127 'ImportInliner transformer should run after running the ' |
| 100 'InlineCodeExtractor', span: script.sourceSpan); | 128 'InlineCodeExtractor', span: script.sourceSpan); |
| 101 continue; | 129 continue; |
| 102 } | 130 } |
| 103 scriptIds.add(resolve(id, src, logger, script.sourceSpan)); | 131 scriptIds.add(resolve(id, src, logger, script.sourceSpan)); |
| 132 | |
| 133 } else { | |
| 134 // TODO(jmesserly): remove this when we are running linter. | |
| 135 logger.warning('more than one Dart script per HTML ' | |
| 136 'document is not supported. Script will be ignored.', | |
| 137 span: script.sourceSpan); | |
| 104 } | 138 } |
| 105 transform.addOutput(new Asset.fromString(secondaryId, | 139 } |
| 106 JSON.encode(scriptIds, toEncodable: (id) => id.serialize()))); | |
| 107 }); | |
| 108 }); | |
| 109 } | |
| 110 | |
| 111 /** | |
| 112 * Visits imports in [document] and add their polymer-element and script tags | |
| 113 * to [elements], unless they have already been [seen]. Elements are added in | |
| 114 * the order they appear, transitive imports are added first. | |
| 115 */ | |
| 116 Future<bool> _visitImports(Document document, AssetId sourceId, | |
| 117 Transform transform, Set<AssetId> seen, List<Document> documents) { | |
| 118 var importIds = []; | |
| 119 bool hasImports = false; | |
| 120 for (var tag in document.queryAll('link')) { | |
| 121 if (tag.attributes['rel'] != 'import') continue; | |
| 122 var href = tag.attributes['href']; | |
| 123 var id = resolve(sourceId, href, transform.logger, tag.sourceSpan); | |
| 124 hasImports = true; | |
| 125 if (id == null || seen.contains(id) || | |
| 126 (id.package == 'polymer' && id.path == 'lib/init.html')) continue; | |
| 127 importIds.add(id); | |
| 128 } | 140 } |
| 129 | |
| 130 if (importIds.isEmpty) return new Future.value(hasImports); | |
| 131 | |
| 132 // Note: we need to preserve the import order in the generated output. | |
| 133 return Future.forEach(importIds, (id) { | |
| 134 if (seen.contains(id)) return new Future.value(null); | |
| 135 seen.add(id); | |
| 136 return _collectImportedDocuments(id, transform, seen, documents); | |
| 137 }).then((_) => true); | |
| 138 } | |
| 139 | |
| 140 /** | |
| 141 * Loads an asset identified by [id], visits its imports and collects it's | |
| 142 * polymer-element definitions and script tags. | |
| 143 */ | |
| 144 Future _collectImportedDocuments(AssetId id, Transform transform, | |
| 145 Set<AssetId> seen, List documents) { | |
| 146 return readAsHtml(id, transform).then((document) { | |
| 147 return _visitImports(document, id, transform, seen, documents).then((_) { | |
| 148 new _UrlNormalizer(transform, id).visit(document); | |
| 149 documents.add(document); | |
| 150 }); | |
| 151 }); | |
| 152 } | 141 } |
| 153 } | 142 } |
| 154 | 143 |
| 144 | |
| 155 /** Internally adjusts urls in the html that we are about to inline. */ | 145 /** Internally adjusts urls in the html that we are about to inline. */ |
| 156 class _UrlNormalizer extends TreeVisitor { | 146 class _UrlNormalizer extends TreeVisitor { |
| 157 final Transform transform; | 147 final Transform transform; |
| 158 | 148 |
| 159 /** Asset where the original content (and original url) was found. */ | 149 /** Asset where the original content (and original url) was found. */ |
| 160 final AssetId sourceId; | 150 final AssetId sourceId; |
| 161 | 151 |
| 162 _UrlNormalizer(this.transform, this.sourceId); | 152 _UrlNormalizer(this.transform, this.sourceId); |
| 163 | 153 |
| 164 visitElement(Element node) { | 154 visitElement(Element node) { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 219 'cite', // in blockquote, del, ins, q | 209 'cite', // in blockquote, del, ins, q |
| 220 'data', // in object | 210 'data', // in object |
| 221 'formaction', // in button, input | 211 'formaction', // in button, input |
| 222 'href', // in a, area, link, base, command | 212 'href', // in a, area, link, base, command |
| 223 'icon', // in command | 213 'icon', // in command |
| 224 'manifest', // in html | 214 'manifest', // in html |
| 225 'poster', // in video | 215 'poster', // in video |
| 226 'src', // in audio, embed, iframe, img, input, script, source, track, | 216 'src', // in audio, embed, iframe, img, input, script, source, track, |
| 227 // video | 217 // video |
| 228 ]; | 218 ]; |
| OLD | NEW |