From 7dde18b181a610459cd6970aa3349e99cb63af47 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Wed, 14 Oct 2015 09:39:40 -0700 Subject: [PATCH] =?UTF-8?q?fix(style=5Fcompiler):=20don=E2=80=99t=20touch?= =?UTF-8?q?=20urls=20in=20stylesheets=20and=20keep=20stylesheets=20with=20?= =?UTF-8?q?absolute=20urls=20in=20templates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can’t resolve relative urls (e.g. for images) in the compiler as these urls are meant to be loaded in the browser (unless we would inline images as base64…). Also, keep `` in templates that reference absolute urls with e.g. `http://`. This behavior was already present for `@import` rules within stylesheets. Closes #4740 --- .../src/core/compiler/style_compiler.ts | 6 +- .../src/core/compiler/style_url_resolver.ts | 63 +++++++--------- .../src/core/compiler/template_normalizer.ts | 7 +- .../src/core/compiler/template_parser.ts | 14 +++- .../core/compiler/style_url_resolver_spec.ts | 72 +++++++++---------- .../core/compiler/template_normalizer_spec.ts | 15 +++- .../core/compiler/template_parser_spec.ts | 42 ++++++++++- 7 files changed, 126 insertions(+), 93 deletions(-) diff --git a/modules/angular2/src/core/compiler/style_compiler.ts b/modules/angular2/src/core/compiler/style_compiler.ts index 139ad9bef2..3b89379013 100644 --- a/modules/angular2/src/core/compiler/style_compiler.ts +++ b/modules/angular2/src/core/compiler/style_compiler.ts @@ -6,7 +6,7 @@ import {StringWrapper, isBlank} from 'angular2/src/core/facade/lang'; import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async'; import {ShadowCss} from 'angular2/src/core/compiler/shadow_css'; import {UrlResolver} from 'angular2/src/core/compiler/url_resolver'; -import {resolveStyleUrls} from './style_url_resolver'; +import {extractStyleUrls} from './style_url_resolver'; import { escapeSingleQuoteString, IS_DART, @@ -58,7 +58,7 @@ export class StyleCompiler { } compileStylesheetCodeGen(stylesheetUrl: string, cssText: string): SourceModule[] { - var styleWithImports = resolveStyleUrls(this._urlResolver, stylesheetUrl, cssText); + var styleWithImports = extractStyleUrls(this._urlResolver, stylesheetUrl, cssText); return [ this._styleModule( stylesheetUrl, false, @@ -78,7 +78,7 @@ export class StyleCompiler { var result = this._styleCache.get(cacheKey); if (isBlank(result)) { result = this._xhr.get(absUrl).then((style) => { - var styleWithImports = resolveStyleUrls(this._urlResolver, absUrl, style); + var styleWithImports = extractStyleUrls(this._urlResolver, absUrl, style); return this._loadStyles([styleWithImports.style], styleWithImports.styleUrls, encapsulate); }); diff --git a/modules/angular2/src/core/compiler/style_url_resolver.ts b/modules/angular2/src/core/compiler/style_url_resolver.ts index b1acc89cbe..a95352392f 100644 --- a/modules/angular2/src/core/compiler/style_url_resolver.ts +++ b/modules/angular2/src/core/compiler/style_url_resolver.ts @@ -1,60 +1,45 @@ // Some of the code comes from WebComponents.JS // https://github.com/webcomponents/webcomponentsjs/blob/master/src/HTMLImports/path.js -import {RegExp, RegExpWrapper, StringWrapper, isPresent} from 'angular2/src/core/facade/lang'; +import { + RegExp, + RegExpWrapper, + StringWrapper, + isPresent, + isBlank +} from 'angular2/src/core/facade/lang'; import {UrlResolver} from 'angular2/src/core/compiler/url_resolver'; -/** - * Rewrites URLs by resolving '@import' and 'url()' URLs from the given base URL, - * removes and returns the @import urls - */ -export function resolveStyleUrls(resolver: UrlResolver, baseUrl: string, cssText: string): - StyleWithImports { - var foundUrls = []; - cssText = extractUrls(resolver, baseUrl, cssText, foundUrls); - cssText = replaceUrls(resolver, baseUrl, cssText); - return new StyleWithImports(cssText, foundUrls); -} - export class StyleWithImports { constructor(public style: string, public styleUrls: string[]) {} } -function extractUrls(resolver: UrlResolver, baseUrl: string, cssText: string, foundUrls: string[]): - string { - return StringWrapper.replaceAllMapped(cssText, _cssImportRe, (m) => { +export function isStyleUrlResolvable(url: string): boolean { + if (isBlank(url) || url.length === 0) return false; + var schemeMatch = RegExpWrapper.firstMatch(_urlWithSchemaRe, url); + return isBlank(schemeMatch) || schemeMatch[1] == 'package'; +} + +/** + * Rewrites stylesheets by resolving and removing the @import urls that + * are either relative or don't have a `package:` scheme + */ +export function extractStyleUrls(resolver: UrlResolver, baseUrl: string, cssText: string): + StyleWithImports { + var foundUrls = []; + var modifiedCssText = StringWrapper.replaceAllMapped(cssText, _cssImportRe, (m) => { var url = isPresent(m[1]) ? m[1] : m[2]; - var schemeMatch = RegExpWrapper.firstMatch(_urlWithSchemaRe, url); - if (isPresent(schemeMatch) && schemeMatch[1] != 'package') { + if (!isStyleUrlResolvable(url)) { // Do not attempt to resolve non-package absolute URLs with URI scheme return m[0]; } foundUrls.push(resolver.resolve(baseUrl, url)); return ''; }); + return new StyleWithImports(modifiedCssText, foundUrls); } -function replaceUrls(resolver: UrlResolver, baseUrl: string, cssText: string): string { - return StringWrapper.replaceAllMapped(cssText, _cssUrlRe, (m) => { - var pre = m[1]; - var originalUrl = m[2]; - if (RegExpWrapper.test(_dataUrlRe, originalUrl)) { - // Do not attempt to resolve data: URLs - return m[0]; - } - var url = StringWrapper.replaceAll(originalUrl, _quoteRe, ''); - var post = m[3]; - - var resolvedUrl = resolver.resolve(baseUrl, url); - - return pre + "'" + resolvedUrl + "'" + post; - }); -} - -var _cssUrlRe = /(url\()([^)]*)(\))/g; var _cssImportRe = /@import\s+(?:url\()?\s*(?:(?:['"]([^'"]*))|([^;\)\s]*))[^;]*;?/g; -var _quoteRe = /['"]/g; -var _dataUrlRe = /^['"]?data:/g; // TODO: can't use /^[^:/?#.]+:/g due to clang-format bug: // https://github.com/angular/angular/issues/4596 -var _urlWithSchemaRe = /^['"]?([a-zA-Z\-\+\.]+):/g; +var _urlWithSchemaRe = /^['"]?([a-zA-Z\-\+\.]+):/g; \ No newline at end of file diff --git a/modules/angular2/src/core/compiler/template_normalizer.ts b/modules/angular2/src/core/compiler/template_normalizer.ts index a1114cae85..05970cf1b4 100644 --- a/modules/angular2/src/core/compiler/template_normalizer.ts +++ b/modules/angular2/src/core/compiler/template_normalizer.ts @@ -4,12 +4,13 @@ import { CompileTemplateMetadata } from './directive_metadata'; import {isPresent, isBlank} from 'angular2/src/core/facade/lang'; +import {ListWrapper} from 'angular2/src/core/facade/collection'; import {BaseException} from 'angular2/src/core/facade/exceptions'; import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async'; import {XHR} from 'angular2/src/core/compiler/xhr'; import {UrlResolver} from 'angular2/src/core/compiler/url_resolver'; -import {resolveStyleUrls} from './style_url_resolver'; +import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver'; import {Injectable} from 'angular2/src/core/di'; import {ViewEncapsulation} from 'angular2/src/core/metadata/view'; @@ -57,9 +58,9 @@ export class TemplateNormalizer { visitor.styleUrls.map(url => this._urlResolver.resolve(templateAbsUrl, url)) .concat(templateMeta.styleUrls.map( url => this._urlResolver.resolve(directiveType.moduleUrl, url))); - + allStyleAbsUrls = ListWrapper.filter(allStyleAbsUrls, isStyleUrlResolvable); var allResolvedStyles = allStyles.map(style => { - var styleWithImports = resolveStyleUrls(this._urlResolver, templateAbsUrl, style); + var styleWithImports = extractStyleUrls(this._urlResolver, templateAbsUrl, style); styleWithImports.styleUrls.forEach(styleUrl => allStyleAbsUrls.push(styleUrl)); return styleWithImports.style; }); diff --git a/modules/angular2/src/core/compiler/template_parser.ts b/modules/angular2/src/core/compiler/template_parser.ts index c68991f962..452e45a3ef 100644 --- a/modules/angular2/src/core/compiler/template_parser.ts +++ b/modules/angular2/src/core/compiler/template_parser.ts @@ -40,6 +40,8 @@ import {CssSelector, SelectorMatcher} from 'angular2/src/core/compiler/selector' import {ElementSchemaRegistry} from 'angular2/src/core/compiler/schema/element_schema_registry'; import {preparseElement, PreparsedElement, PreparsedElementType} from './template_preparser'; +import {isStyleUrlResolvable} from './style_url_resolver'; + import { HtmlAstVisitor, HtmlAst, @@ -165,10 +167,16 @@ class TemplateParseVisitor implements HtmlAstVisitor { var nodeName = element.name; var preparsedElement = preparseElement(element); if (preparsedElement.type === PreparsedElementType.SCRIPT || - preparsedElement.type === PreparsedElementType.STYLE || - preparsedElement.type === PreparsedElementType.STYLESHEET) { + preparsedElement.type === PreparsedElementType.STYLE) { // Skipping