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.transform.import_inliner; | 6 library polymer.src.transform.import_inliner; |
| 7 | 7 |
| 8 import 'dart:async'; | 8 import 'dart:async'; |
| 9 | 9 |
| 10 import 'package:barback/barback.dart'; | 10 import 'package:barback/barback.dart'; |
| 11 import 'package:path/path.dart' as path; | |
| 11 import 'package:html5lib/dom.dart' show Document, Node, DocumentFragment; | 12 import 'package:html5lib/dom.dart' show Document, Node, DocumentFragment; |
| 13 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; | |
| 12 import 'common.dart'; | 14 import 'common.dart'; |
| 13 | 15 |
| 14 /** Recursively inlines polymer-element definitions from html imports. */ | 16 /** Recursively inlines polymer-element definitions from html imports. */ |
| 15 // TODO(sigmund): make sure we match semantics of html-imports for tags other | 17 // TODO(sigmund): make sure we match semantics of html-imports for tags other |
| 16 // than polymer-element (see dartbug.com/12613). | 18 // than polymer-element (see dartbug.com/12613). |
| 17 class ImportedElementInliner extends Transformer { | 19 class ImportedElementInliner extends Transformer { |
| 18 Future<bool> isPrimary(Asset input) => | 20 /** Only run this transformer on .html files. */ |
| 19 new Future.value(input.id.extension == ".html"); | 21 final String allowedExtensions = ".html"; |
| 20 | 22 |
| 21 Future apply(Transform transform) { | 23 Future apply(Transform transform) { |
| 22 var seen = new Set<AssetId>(); | 24 var seen = new Set<AssetId>(); |
| 23 var elements = []; | 25 var elements = []; |
| 24 var id = transform.primaryId; | 26 var id = transform.primaryId; |
| 25 seen.add(id); | 27 seen.add(id); |
| 26 return getPrimaryContent(transform).then((content) { | 28 return getPrimaryContent(transform).then((content) { |
| 27 var document = parseHtml(content, id.path, transform.logger); | 29 var document = parseHtml(content, id.path, transform.logger); |
| 28 var future = _visitImports(document, id, transform, seen, elements); | 30 var future = _visitImports(document, id, transform, seen, elements); |
| 29 return future.then((importsFound) { | 31 return future.then((importsFound) { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 71 if (seen.contains(id)) return new Future.value(null); | 73 if (seen.contains(id)) return new Future.value(null); |
| 72 seen.add(id); | 74 seen.add(id); |
| 73 return _collectPolymerElements(id, transform, seen, elements); | 75 return _collectPolymerElements(id, transform, seen, elements); |
| 74 }).then((_) => true); | 76 }).then((_) => true); |
| 75 } | 77 } |
| 76 | 78 |
| 77 /** | 79 /** |
| 78 * Loads an asset identified by [id], visits its imports and collects it's | 80 * Loads an asset identified by [id], visits its imports and collects it's |
| 79 * polymer-element definitions. | 81 * polymer-element definitions. |
| 80 */ | 82 */ |
| 81 Future _collectPolymerElements( | 83 Future _collectPolymerElements(AssetId id, Transform transform, |
| 82 AssetId id, Transform transform, Set<AssetId> seen, List elements) { | 84 Set<AssetId> seen, List elements) { |
| 83 return getContent(transform, id) | 85 return getContent(transform, id).then((content) { |
| 84 .then((content) => parseHtml( | 86 var document = parseHtml( |
| 85 content, id.path, transform.logger, checkDocType: false)) | 87 content, id.path, transform.logger, checkDocType: false); |
| 86 .then((document) { | 88 return _visitImports(document, id, transform, seen, elements).then((_) { |
| 87 return _visitImports(document, id, transform, seen, elements) | 89 var normalizer = new _UrlNormalizer(transform, id); |
| 88 .then((_) => elements.addAll(document.queryAll('polymer-element'))); | 90 for (var element in document.queryAll('polymer-element')) { |
| 89 }); | 91 normalizer.visit(document); |
| 92 elements.add(element); | |
| 93 } | |
| 94 }); | |
| 95 }); | |
| 90 } | 96 } |
| 91 } | 97 } |
| 98 | |
| 99 /** Internally adjusts urls in the html that we are about to inline. */ | |
| 100 class _UrlNormalizer extends TreeVisitor { | |
| 101 final Transform transform; | |
| 102 | |
| 103 /** Asset where the original content (and original url) was found. */ | |
| 104 final AssetId sourceId; | |
| 105 | |
| 106 _UrlNormalizer(this.transform, this.sourceId); | |
| 107 | |
| 108 visitElement(Element node) { | |
| 109 for (var key in node.attributes.keys) { | |
| 110 if (_urlAttributes.contains(key)) { | |
| 111 var url = node.attributes[key]; | |
| 112 if (url != null && url != '') { | |
| 113 node.attributes[key] = _newUrl(url, node.sourceSpan); | |
| 114 } | |
| 115 } | |
| 116 } | |
| 117 super.visitElement(node); | |
| 118 } | |
| 119 | |
| 120 _newUrl(String href, Span span) { | |
| 121 var uri = Uri.parse(href); | |
| 122 if (uri.isAbsolute) return href; | |
| 123 if (!uri.scheme.isEmpty) return href; | |
| 124 if (!uri.host.isEmpty) return href; | |
| 125 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI. | |
| 126 if (path.isAbsolute(href)) return href; | |
| 127 | |
| 128 var id = resolve(sourceId, href, transform.logger, span); | |
| 129 var primaryId = transform.primaryId; | |
| 130 | |
| 131 if (id.path.startsWith('lib/')) { | |
| 132 return 'packages/${id.package}/${id.path.substring(4)}'; | |
| 133 } | |
| 134 | |
| 135 if (id.path.startsWith('asset/')) { | |
| 136 return 'assets/${id.package}/${id.path.substring(6)}'; | |
| 137 } | |
| 138 | |
| 139 if (primaryId.package != id.package) { | |
| 140 // Techincally we shouldn't get there | |
| 141 logger.error("don't know how to include $id from $primaryId", span); | |
| 142 return null; | |
| 143 } | |
| 144 | |
| 145 var builder = path.url; | |
| 146 return builder.relative(builder.join('/', id.path), | |
| 147 from: builder.join('/', builder.dirname(primaryId.path))); | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 /** | |
| 152 * HTML attributes that expect a URL value. | |
| 153 * <http://dev.w3.org/html5/spec/section-index.html#attributes-1> | |
| 154 * | |
| 155 * Every one of these attributes is a URL in every context where it is used in | |
| 156 * the DOM. The comments show every DOM element where an attribute can be used. | |
| 157 */ | |
| 158 const _urlAttributes = const [ | |
|
Jennifer Messerly
2013/08/28 22:25:45
interesting. FWIW, this is a much bigger list than
Siggi Cherem (dart-lang)
2013/08/28 23:17:06
good point, totally.
Siggi Cherem (dart-lang)
2013/08/28 23:36:34
filed here: https://github.com/Polymer/HTMLImports
| |
| 159 'action', // in form | |
| 160 'background', // in body | |
| 161 'cite', // in blockquote, del, ins, q | |
| 162 'data', // in object | |
| 163 'formaction', // in button, input | |
| 164 'href', // in a, area, link, base, command | |
| 165 'icon', // in command | |
| 166 'manifest', // in html | |
| 167 'poster', // in video | |
| 168 'src', // in audio, embed, iframe, img, input, script, source, track, | |
| 169 // video | |
| 170 ]; | |
| OLD | NEW |