| Index: packages/polymer/lib/src/build/html_finalizer.dart
|
| diff --git a/packages/polymer/lib/src/build/html_finalizer.dart b/packages/polymer/lib/src/build/html_finalizer.dart
|
| deleted file mode 100644
|
| index d476894b07cc78c583a89397804bb2d7e5b67b18..0000000000000000000000000000000000000000
|
| --- a/packages/polymer/lib/src/build/html_finalizer.dart
|
| +++ /dev/null
|
| @@ -1,333 +0,0 @@
|
| -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -/// Transfomer that finalizes an html file for deployment:
|
| -/// - Extracts inline js scripts in csp mode.
|
| -/// - Inlines css files into the document.
|
| -/// - Validates polymer-element templates.
|
| -library polymer.src.build.html_finalizer;
|
| -
|
| -import 'dart:async';
|
| -import 'dart:collection' show LinkedHashSet;
|
| -
|
| -import 'package:barback/barback.dart';
|
| -import 'package:code_transformers/assets.dart';
|
| -import 'package:code_transformers/messages/build_logger.dart';
|
| -import 'package:path/path.dart' as path;
|
| -import 'package:html/dom.dart'
|
| - show Document, DocumentFragment, Element, Node;
|
| -import 'package:html/dom_parsing.dart' show TreeVisitor;
|
| -import 'package:source_span/source_span.dart';
|
| -
|
| -import 'common.dart';
|
| -import 'messages.dart';
|
| -
|
| -/// Inlines css files and extracts inline js scripts into files if in csp mode.
|
| -// TODO(jakemac): Move to a different package. Will need to break out the
|
| -// binding-specific logic when this happens (add it to the linter?).
|
| -class _HtmlFinalizer extends PolymerTransformer {
|
| - final TransformOptions options;
|
| - final Transform transform;
|
| - final BuildLogger logger;
|
| - final AssetId docId;
|
| - final seen = new Set<AssetId>();
|
| - final scriptIds = new LinkedHashSet<AssetId>();
|
| - final inlinedStylesheetIds = new Set<AssetId>();
|
| - final extractedFiles = new Set<AssetId>();
|
| -
|
| - /// The number of extracted inline Dart scripts. Used as a counter to give
|
| - /// unique-ish filenames.
|
| - int inlineScriptCounter = 0;
|
| -
|
| - _HtmlFinalizer(TransformOptions options, Transform transform)
|
| - : options = options,
|
| - transform = transform,
|
| - logger = new BuildLogger(transform,
|
| - convertErrorsToWarnings: !options.releaseMode,
|
| - detailsUri: 'http://goo.gl/5HPeuP'),
|
| - docId = transform.primaryInput.id;
|
| -
|
| - Future apply() {
|
| - seen.add(docId);
|
| -
|
| - Document document;
|
| - bool changed = false;
|
| -
|
| - return readPrimaryAsHtml(transform, logger).then((doc) {
|
| - document = doc;
|
| - new _UrlAttributeValidator(docId, logger).visit(document);
|
| -
|
| - changed = _extractScripts(document) || changed;
|
| -
|
| - return _inlineCss(document);
|
| - }).then((cssInlined) {
|
| - changed = changed || cssInlined;
|
| -
|
| - var output = transform.primaryInput;
|
| - if (changed) output = new Asset.fromString(docId, document.outerHtml);
|
| - transform.addOutput(output);
|
| -
|
| - // Write out the logs collected by our [BuildLogger].
|
| - if (options.injectBuildLogsInOutput) {
|
| - return logger.writeOutput();
|
| - }
|
| - });
|
| - }
|
| -
|
| - /// Inlines any css files found into document. Returns a [bool] indicating
|
| - /// whether or not the document was modified.
|
| - Future<bool> _inlineCss(Document document) {
|
| - bool changed = false;
|
| -
|
| - // Note: we need to preserve the import order in the generated output.
|
| - var tags = document.querySelectorAll('link[rel="stylesheet"]');
|
| - return Future.forEach(tags, (Element tag) {
|
| - var href = tag.attributes['href'];
|
| - var id = uriToAssetId(docId, href, logger, tag.sourceSpan,
|
| - errorOnAbsolute: false);
|
| - if (id == null) return null;
|
| - if (!options.shouldInlineStylesheet(id)) return null;
|
| -
|
| - changed = true;
|
| - if (inlinedStylesheetIds.contains(id) &&
|
| - !options.stylesheetInliningIsOverridden(id)) {
|
| - logger.warning(CSS_FILE_INLINED_MULTIPLE_TIMES.create({'url': id.path}),
|
| - span: tag.sourceSpan);
|
| - }
|
| - inlinedStylesheetIds.add(id);
|
| - return _inlineStylesheet(id, tag);
|
| - }).then((_) => changed);
|
| - }
|
| -
|
| - /// Inlines a single css file by replacing [link] with an inline style tag.
|
| - Future _inlineStylesheet(AssetId id, Element link) {
|
| - return transform.readInputAsString(id).catchError((error) {
|
| - // TODO(jakemac): Move this warning to the linter once we can make it run
|
| - // always (see http://dartbug.com/17199). Then hide this error and replace
|
| - // with a comment pointing to the linter error (so we don't double warn).
|
| - logger.warning(INLINE_STYLE_FAIL.create({'error': error}),
|
| - span: link.sourceSpan);
|
| - }).then((css) {
|
| - if (css == null) return null;
|
| - css = new _UrlNormalizer(transform, id, logger).visitCss(css);
|
| - var styleElement = new Element.tag('style')..text = css;
|
| - // Copy over the extra attributes from the link tag to the style tag.
|
| - // This adds support for no-shim, shim-shadowdom, etc.
|
| - link.attributes.forEach((key, value) {
|
| - if (!IGNORED_LINKED_STYLE_ATTRS.contains(key)) {
|
| - styleElement.attributes[key] = value;
|
| - }
|
| - });
|
| - link.replaceWith(styleElement);
|
| - });
|
| - }
|
| -
|
| - /// Splits inline js scripts into their own files in csp mode.
|
| - bool _extractScripts(Document doc) {
|
| - if (!options.contentSecurityPolicy) return false;
|
| -
|
| - bool changed = false;
|
| - for (var script in doc.querySelectorAll('script')) {
|
| - var src = script.attributes['src'];
|
| - if (src != null) continue;
|
| -
|
| - var type = script.attributes['type'];
|
| - if (type == TYPE_DART) continue;
|
| -
|
| - var extension = 'js';
|
| - final filename = path.url.basename(docId.path);
|
| - final count = inlineScriptCounter++;
|
| - var code = script.text;
|
| - // TODO(sigmund): ensure this path is unique (dartbug.com/12618).
|
| - script.attributes['src'] = src = '$filename.$count.$extension';
|
| - script.text = '';
|
| - changed = true;
|
| -
|
| - var newId = docId.addExtension('.$count.$extension');
|
| - extractedFiles.add(newId);
|
| - transform.addOutput(new Asset.fromString(newId, code));
|
| - }
|
| - return changed;
|
| - }
|
| -}
|
| -
|
| -/// Finalizes a single html document for deployment.
|
| -class HtmlFinalizer extends Transformer {
|
| - final TransformOptions options;
|
| -
|
| - HtmlFinalizer(this.options);
|
| -
|
| - /// Only run on entry point .html files.
|
| - bool isPrimary(AssetId id) => options.isHtmlEntryPoint(id);
|
| -
|
| - Future apply(Transform transform) =>
|
| - new _HtmlFinalizer(options, transform).apply();
|
| -}
|
| -
|
| -const TYPE_DART = 'application/dart';
|
| -const TYPE_JS = 'text/javascript';
|
| -
|
| -/// Internally adjusts urls in the html that we are about to inline.
|
| -class _UrlNormalizer {
|
| - final Transform transform;
|
| -
|
| - /// Asset where the original content (and original url) was found.
|
| - final AssetId sourceId;
|
| -
|
| - /// Path to the top level folder relative to the transform primaryInput.
|
| - /// This should just be some arbitrary # of ../'s.
|
| - final String topLevelPath;
|
| -
|
| - /// Whether or not the normalizer has changed something in the tree.
|
| - bool changed = false;
|
| -
|
| - final BuildLogger logger;
|
| -
|
| - _UrlNormalizer(transform, this.sourceId, this.logger)
|
| - : transform = transform,
|
| - topLevelPath = '../' *
|
| - (path.url.split(transform.primaryInput.id.path).length - 2);
|
| -
|
| - static final _URL = new RegExp(r'url\(([^)]*)\)', multiLine: true);
|
| - static final _QUOTE = new RegExp('["\']', multiLine: true);
|
| -
|
| - /// Visit the CSS text and replace any relative URLs so we can inline it.
|
| - // Ported from:
|
| - // https://github.com/Polymer/vulcanize/blob/c14f63696797cda18dc3d372b78aa3378acc691f/lib/vulcan.js#L149
|
| - // TODO(jmesserly): use csslib here instead? Parsing with RegEx is sadness.
|
| - // Maybe it's reliable enough for finding URLs in CSS? I'm not sure.
|
| - String visitCss(String cssText) {
|
| - var url = spanUrlFor(sourceId, transform, logger);
|
| - var src = new SourceFile(cssText, url: url);
|
| - return cssText.replaceAllMapped(_URL, (match) {
|
| - // Extract the URL, without any surrounding quotes.
|
| - var span = src.span(match.start, match.end);
|
| - var href = match[1].replaceAll(_QUOTE, '');
|
| - href = _newUrl(href, span);
|
| - return 'url($href)';
|
| - });
|
| - }
|
| -
|
| - String _newUrl(String href, SourceSpan span) {
|
| - var uri = Uri.parse(href);
|
| - if (uri.isAbsolute) return href;
|
| - if (!uri.scheme.isEmpty) return href;
|
| - if (!uri.host.isEmpty) return href;
|
| - if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI.
|
| - if (path.isAbsolute(href)) return href;
|
| -
|
| - var id = uriToAssetId(sourceId, href, logger, span);
|
| - if (id == null) return href;
|
| - var primaryId = transform.primaryInput.id;
|
| -
|
| - if (id.path.startsWith('lib/')) {
|
| - return '${topLevelPath}packages/${id.package}/${id.path.substring(4)}';
|
| - }
|
| -
|
| - if (id.path.startsWith('asset/')) {
|
| - return '${topLevelPath}assets/${id.package}/${id.path.substring(6)}';
|
| - }
|
| -
|
| - if (primaryId.package != id.package) {
|
| - // Technically we shouldn't get there
|
| - logger.error(INTERNAL_ERROR_DONT_KNOW_HOW_TO_IMPORT
|
| - .create({'target': id, 'source': primaryId, 'extra': ''}),
|
| - span: span);
|
| - return href;
|
| - }
|
| -
|
| - var builder = path.url;
|
| - return builder.relative(builder.join('/', id.path),
|
| - from: builder.join('/', builder.dirname(primaryId.path)));
|
| - }
|
| -}
|
| -
|
| -/// Validates url-like attributes and throws warnings as appropriate.
|
| -/// TODO(jakemac): Move to the linter.
|
| -class _UrlAttributeValidator extends TreeVisitor {
|
| - /// Asset where the original content (and original url) was found.
|
| - final AssetId sourceId;
|
| -
|
| - final BuildLogger logger;
|
| -
|
| - _UrlAttributeValidator(this.sourceId, this.logger);
|
| -
|
| - visit(Node node) {
|
| - return super.visit(node);
|
| - }
|
| -
|
| - visitElement(Element node) {
|
| - // TODO(jakemac): Support custom elements that extend html elements which
|
| - // have url-like attributes. This probably means keeping a list of which
|
| - // html elements support each url-like attribute.
|
| - if (!isCustomTagName(node.localName)) {
|
| - node.attributes.forEach((name, value) {
|
| - if (_urlAttributes.contains(name)) {
|
| - if (!name.startsWith('_') && value.contains(_BINDING_REGEX)) {
|
| - logger.warning(USE_UNDERSCORE_PREFIX.create({'name': name}),
|
| - span: node.sourceSpan, asset: sourceId);
|
| - } else if (name.startsWith('_') && !value.contains(_BINDING_REGEX)) {
|
| - logger.warning(
|
| - DONT_USE_UNDERSCORE_PREFIX.create({'name': name.substring(1)}),
|
| - span: node.sourceSpan, asset: sourceId);
|
| - }
|
| - }
|
| - });
|
| - }
|
| - return super.visitElement(node);
|
| - }
|
| -}
|
| -
|
| -/// HTML attributes that expect a URL value.
|
| -/// <http://dev.w3.org/html5/spec/section-index.html#attributes-1>
|
| -///
|
| -/// Every one of these attributes is a URL in every context where it is used in
|
| -/// the DOM. The comments show every DOM element where an attribute can be used.
|
| -///
|
| -/// The _* version of each attribute is also supported, see http://goo.gl/5av8cU
|
| -const _urlAttributes = const [
|
| - // in form
|
| - 'action',
|
| - '_action',
|
| - // in body
|
| - 'background',
|
| - '_background',
|
| - // in blockquote, del, ins, q
|
| - 'cite',
|
| - '_cite',
|
| - // in object
|
| - 'data',
|
| - '_data',
|
| - // in button, input
|
| - 'formaction',
|
| - '_formaction',
|
| - // in a, area, link, base, command
|
| - 'href',
|
| - '_href',
|
| - // in command
|
| - 'icon',
|
| - '_icon',
|
| - // in html
|
| - 'manifest',
|
| - '_manifest',
|
| - // in video
|
| - 'poster',
|
| - '_poster',
|
| - // in audio, embed, iframe, img, input, script, source, track, video
|
| - 'src',
|
| - '_src',
|
| -];
|
| -
|
| -/// When inlining <link rel="stylesheet"> tags copy over all attributes to the
|
| -/// style tag except these ones.
|
| -const IGNORED_LINKED_STYLE_ATTRS = const [
|
| - 'charset',
|
| - 'href',
|
| - 'href-lang',
|
| - 'rel',
|
| - 'rev'
|
| -];
|
| -
|
| -/// Global RegExp objects.
|
| -final _BINDING_REGEX = new RegExp(r'(({{.*}})|(\[\[.*\]\]))');
|
|
|