From 993b3d62de717887bd2e137a9429a1a16255e47b Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Thu, 29 Oct 2015 10:57:54 -0700 Subject: [PATCH] =?UTF-8?q?refactor(compiler):=20don=E2=80=99t=20rely=20on?= =?UTF-8?q?=20external=20css=20parser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We used to use different external css parsers, depending on the `DomAdapter`. This lead to inconsistent behavior and environment specific errors. Closes #5006 Closes #4993 --- modules/angular2/pubspec.yaml | 1 - .../angular2/src/core/compiler/shadow_css.ts | 143 ++++++++---------- .../src/core/dom/abstract_html_adapter.dart | 20 --- .../src/core/dom/browser_adapter.dart | 4 - .../angular2/src/core/dom/browser_adapter.ts | 4 - modules/angular2/src/core/dom/dom_adapter.ts | 6 - .../angular2/src/core/dom/emulated_css.dart | 110 -------------- .../src/core/dom/generic_browser_adapter.ts | 27 ---- .../angular2/src/core/dom/parse5_adapter.ts | 29 ---- .../shadow_css_html5lib.server.spec.dart | 11 -- .../test/core/compiler/shadow_css_spec.ts | 89 +++++------ .../test/core/compiler/style_compiler_spec.ts | 13 +- .../debug_tools/bootstrap.server.spec.dart | 4 + npm-shrinkwrap.clean.json | 34 ----- npm-shrinkwrap.json | 51 ------- package.json | 1 - 16 files changed, 115 insertions(+), 432 deletions(-) delete mode 100644 modules/angular2/src/core/dom/emulated_css.dart delete mode 100644 modules/angular2/test/core/compiler/shadow_css_html5lib.server.spec.dart diff --git a/modules/angular2/pubspec.yaml b/modules/angular2/pubspec.yaml index eca0b94371..e60ed1da39 100644 --- a/modules/angular2/pubspec.yaml +++ b/modules/angular2/pubspec.yaml @@ -11,7 +11,6 @@ environment: dependencies: analyzer: '>=0.24.4 <0.27.0' barback: '^0.15.2+2' - csslib: '>=0.12.0 <1.0.0' code_transformers: '^0.2.8' dart_style: '>=0.1.8 <0.3.0' glob: '^1.0.0' diff --git a/modules/angular2/src/core/compiler/shadow_css.ts b/modules/angular2/src/core/compiler/shadow_css.ts index 05b5d75a22..70d078cdba 100644 --- a/modules/angular2/src/core/compiler/shadow_css.ts +++ b/modules/angular2/src/core/compiler/shadow_css.ts @@ -1,4 +1,3 @@ -import {DOM} from 'angular2/src/core/dom/dom_adapter'; import {ListWrapper} from 'angular2/src/core/facade/collection'; import { StringWrapper, @@ -144,8 +143,7 @@ export class ShadowCss { * Shim a style element with the given selector. Returns cssText that can * be included in the document via WebComponents.ShadowCSS.addCssToDocument(css). */ - shimStyle(style: string, selector: string, hostSelector: string = ''): string { - var cssText = DOM.getText(style); + shimStyle(cssText: string, selector: string, hostSelector: string = ''): string { return this.shimCssText(cssText, selector, hostSelector); } @@ -231,8 +229,7 @@ export class ShadowCss { cssText = this._convertColonHostContext(cssText); cssText = this._convertShadowDOMSelectors(cssText); if (isPresent(scopeSelector)) { - _withCssRules(cssText, - (rules) => { cssText = this._scopeRules(rules, scopeSelector, hostSelector); }); + cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector); } cssText = cssText + '\n' + unscoped; return cssText.trim(); @@ -347,50 +344,37 @@ export class ShadowCss { // change a selector like 'div' to 'name div' /** @internal */ - _scopeRules(cssRules, scopeSelector: string, hostSelector: string): string { - var cssText = ''; - if (isPresent(cssRules)) { - for (var i = 0; i < cssRules.length; i++) { - var rule = cssRules[i]; - if (DOM.isStyleRule(rule) || DOM.isPageRule(rule)) { - cssText += this._scopeSelector(rule.selectorText, scopeSelector, hostSelector, - this.strictStyling) + - ' {\n'; - cssText += this._propertiesFromRule(rule) + '\n}\n\n'; - } else if (DOM.isMediaRule(rule)) { - cssText += '@media ' + rule.media.mediaText + ' {\n'; - cssText += this._scopeRules(rule.cssRules, scopeSelector, hostSelector); - cssText += '\n}\n\n'; - } else { - // KEYFRAMES_RULE in IE throws when we query cssText - // when it contains a -webkit- property. - // if this happens, we fallback to constructing the rule - // from the CSSRuleSet - // https://connect.microsoft.com/IE/feedbackdetail/view/955703/accessing-csstext-of-a-keyframe-rule-that-contains-a-webkit-property-via-cssom-generates-exception - try { - if (isPresent(rule.cssText)) { - cssText += rule.cssText + '\n\n'; - } - } catch (x) { - if (DOM.isKeyframesRule(rule) && isPresent(rule.cssRules)) { - cssText += this._ieSafeCssTextFromKeyFrameRule(rule); - } - } + _scopeSelectors(cssText: string, scopeSelector: string, hostSelector: string): string { + var parts = splitCurlyBlocks(cssText); + var result = []; + for (var i = 0; i < parts.length; i += 2) { + var selectorTextWithCommands = parts[i]; + var selectorStart = selectorTextWithCommands.lastIndexOf(';') + 1; + var selectorText = + selectorTextWithCommands.substring(selectorStart, selectorTextWithCommands.length); + var ruleContent = parts[i + 1]; + var selectorMatch = RegExpWrapper.firstMatch(_singleSelectorRe, selectorText); + if (isPresent(selectorMatch) && ruleContent.length > 0) { + var selPrefix = selectorMatch[1]; + var selAt = isPresent(selectorMatch[2]) ? selectorMatch[2] : ''; + var selector = selectorMatch[3]; + var selSuffix = selectorMatch[4]; + if (selAt.length === 0 || selAt == '@page') { + var scopedSelector = + this._scopeSelector(selector, scopeSelector, hostSelector, this.strictStyling); + selectorText = `${selPrefix}${selAt}${scopedSelector}${selSuffix}`; + } else if (selAt == '@media' && ruleContent[0] == OPEN_CURLY && + ruleContent[ruleContent.length - 1] == CLOSE_CURLY) { + var scopedContent = this._scopeSelectors(ruleContent.substring(1, ruleContent.length - 1), + scopeSelector, hostSelector); + ruleContent = `${OPEN_CURLY}${scopedContent}${CLOSE_CURLY}`; } } + result.push(selectorTextWithCommands.substring(0, selectorStart)); + result.push(selectorText); + result.push(ruleContent); } - return cssText; - } - - /** @internal */ - _ieSafeCssTextFromKeyFrameRule(rule): string { - var cssText = '@keyframes ' + rule.name + ' {'; - for (var i = 0; i < rule.cssRules.length; i++) { - var r = rule.cssRules[i]; - cssText += ' ' + r.keyText + ' {' + r.style.cssText + '}'; - } - cssText += ' }'; - return cssText; + return result.join(''); } /** @internal */ @@ -477,36 +461,6 @@ export class ShadowCss { selector = StringWrapper.replaceAll(selector, _colonHostRe, _polyfillHost); return selector; } - - /** @internal */ - _propertiesFromRule(rule): string { - var cssText = rule.style.cssText; - // TODO(sorvell): Safari cssom incorrectly removes quotes from the content - // property. (https://bugs.webkit.org/show_bug.cgi?id=118045) - // don't replace attr rules - var attrRe = /['"]+|attr/g; - if (rule.style.content.length > 0 && - !isPresent(RegExpWrapper.firstMatch(attrRe, rule.style.content))) { - var contentRe = /content:[^;]*;/g; - cssText = - StringWrapper.replaceAll(cssText, contentRe, 'content: \'' + rule.style.content + '\';'); - } - // TODO(sorvell): we can workaround this issue here, but we need a list - // of troublesome properties to fix https://github.com/Polymer/platform/issues/53 - // - // inherit rules can be omitted from cssText - // TODO(sorvell): remove when Blink bug is fixed: - // https://code.google.com/p/chromium/issues/detail?id=358273 - // var style = rule.style; - // for (var i = 0; i < style.length; i++) { - // var name = style.item(i); - // var value = style.getPropertyValue(name); - // if (value == 'initial') { - // cssText += name + ': initial; '; - // } - //} - return cssText; - } } var _cssContentNextSelectorRe = /polyfill-next-selector[^}]*content:[\s]*?['"](.*?)['"][;\s]*}([^{]*?){/gim; @@ -539,13 +493,34 @@ var _polyfillHostRe = RegExpWrapper.create(_polyfillHost, 'im'); var _colonHostRe = /:host/gim; var _colonHostContextRe = /:host-context/gim; -function _cssToRules(cssText: string) { - return DOM.cssToRules(cssText); +var _singleSelectorRe = /^(\s*)(@\S+)?(.*?)(\s*)$/g; + +var _curlyRe = /([{}])/g; +var OPEN_CURLY = '{'; +var CLOSE_CURLY = '}'; + +export function splitCurlyBlocks(cssText:string):string[] { + var parts = StringWrapper.split(cssText, _curlyRe); + var result = []; + var bracketCount = 0; + var currentCurlyParts = []; + for (var partIndex = 0; partIndex= 2 && result[result.length-1] == '' && result[result.length-2] == '') { + result = result.slice(0, result.length-2); + } + return result; } -function _withCssRules(cssText: string, callback: Function) { - // Difference from webcomponentjs: remove the workaround for an old bug in Chrome - if (isBlank(callback)) return; - var rules = _cssToRules(cssText); - callback(rules); -} diff --git a/modules/angular2/src/core/dom/abstract_html_adapter.dart b/modules/angular2/src/core/dom/abstract_html_adapter.dart index 05b2cd2f1e..feea49fc33 100644 --- a/modules/angular2/src/core/dom/abstract_html_adapter.dart +++ b/modules/angular2/src/core/dom/abstract_html_adapter.dart @@ -4,7 +4,6 @@ import 'package:html/parser.dart' as parser; import 'package:html/dom.dart'; import 'dom_adapter.dart'; -import 'emulated_css.dart'; import '../compiler/xhr.dart'; const _attrToPropMap = const { @@ -348,14 +347,6 @@ abstract class AbstractHtml5LibAdapter implements DomAdapter { throw 'not implemented'; } - bool isPageRule(rule) => (rule.type == 6); - - bool isStyleRule(rule) => (rule.type == 1); - - bool isMediaRule(rule) => (rule.type == 4); - - bool isKeyframesRule(rule) => (rule.type == 7); - String getHref(element) { throw 'not implemented'; } @@ -364,10 +355,6 @@ abstract class AbstractHtml5LibAdapter implements DomAdapter { throw 'not implemented'; } - List cssToRules(String css) { - return parseAndEmulateCssRules(css); - } - List getDistributedNodes(Node) { throw 'not implemented'; } @@ -380,13 +367,6 @@ abstract class AbstractHtml5LibAdapter implements DomAdapter { return false; } - bool supportsUnprefixedCssAnimation() { - // Currently during code transformation we do not know what - // browsers we are targetting. To play it safe, we assume - // unprefixed animations are not supported. - return false; - } - getHistory() { throw 'not implemented'; } diff --git a/modules/angular2/src/core/dom/browser_adapter.dart b/modules/angular2/src/core/dom/browser_adapter.dart index 1a7a46ddce..94db4fcaa2 100644 --- a/modules/angular2/src/core/dom/browser_adapter.dart +++ b/modules/angular2/src/core/dom/browser_adapter.dart @@ -410,10 +410,6 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter { return document.adoptNode(node); } - bool isPageRule(CssRule rule) => rule is CssPageRule; - bool isStyleRule(CssRule rule) => rule is CssStyleRule; - bool isMediaRule(CssRule rule) => rule is CssMediaRule; - bool isKeyframesRule(CssRule rule) => rule is CssKeyframesRule; String getHref(AnchorElement element) { return element.href; } diff --git a/modules/angular2/src/core/dom/browser_adapter.ts b/modules/angular2/src/core/dom/browser_adapter.ts index 35539f02e7..52cc6b7384 100644 --- a/modules/angular2/src/core/dom/browser_adapter.ts +++ b/modules/angular2/src/core/dom/browser_adapter.ts @@ -273,10 +273,6 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter { return document.importNode(toImport, true); } adoptNode(node: Node): any { return document.adoptNode(node); } - isPageRule(rule): boolean { return rule.type === CSSRule.PAGE_RULE; } - isStyleRule(rule): boolean { return rule.type === CSSRule.STYLE_RULE; } - isMediaRule(rule): boolean { return rule.type === CSSRule.MEDIA_RULE; } - isKeyframesRule(rule): boolean { return rule.type === CSSRule.KEYFRAMES_RULE; } getHref(el: Element): string { return (el).href; } getEventKey(event): string { var key = event.key; diff --git a/modules/angular2/src/core/dom/dom_adapter.ts b/modules/angular2/src/core/dom/dom_adapter.ts index 2ac78dfaf1..6c5e39e74a 100644 --- a/modules/angular2/src/core/dom/dom_adapter.ts +++ b/modules/angular2/src/core/dom/dom_adapter.ts @@ -111,17 +111,11 @@ export abstract class DomAdapter { abstract isShadowRoot(node): boolean; abstract importIntoDoc /**/ (node: Node /*T*/): Node /*T*/; abstract adoptNode /**/ (node: Node /*T*/): Node /*T*/; - abstract isPageRule(rule): boolean; - abstract isStyleRule(rule): boolean; - abstract isMediaRule(rule): boolean; - abstract isKeyframesRule(rule): boolean; abstract getHref(element): string; abstract getEventKey(event): string; abstract resolveAndSetHref(element, baseUrl: string, href: string); - abstract cssToRules(css: string): any[]; abstract supportsDOMEvents(): boolean; abstract supportsNativeShadowDOM(): boolean; - abstract supportsUnprefixedCssAnimation(): boolean; abstract getGlobalEventTarget(target: string): any; abstract getHistory(): History; abstract getLocation(): Location; diff --git a/modules/angular2/src/core/dom/emulated_css.dart b/modules/angular2/src/core/dom/emulated_css.dart deleted file mode 100644 index 4e263692e1..0000000000 --- a/modules/angular2/src/core/dom/emulated_css.dart +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Emulates browser CSS API. - * - * WARNING: this is a very incomplete emulation; it only has enough to support - * Angular's CSS scoping (a.k.a. shimming). - */ -library angular2.dom.emulated_css; - -import 'package:csslib/parser.dart' as cssp; -import 'package:csslib/visitor.dart' as cssv; - -/// Parses [css] string and emits the list of top-level CSS rules in it via -/// data structures that mimick browser CSS APIs. -List parseAndEmulateCssRules(String css) { - var stylesheet = cssp.parse(css); - return emulateRules(stylesheet.topLevels); -} - -/// Converts `csslib` [rules] to their emulated counterparts. -List emulateRules(Iterable rules) { - return rules.map((cssv.TreeNode node) { - if (node is cssv.RuleSet) { - if (node.declarationGroup.span.text.isEmpty) { - // Skip CSS matchers with no bodies - return null; - } - return new EmulatedCssStyleRule(node); - } else if (node is cssv.MediaDirective) { - return new EmulatedCssMedialRule(node); - } else if (node is cssv.ImportDirective) { - return new EmulatedCssImportRule(node); - } - }).where((r) => r != null).toList(); -} - -/// Emulates [CSSRule](https://developer.mozilla.org/en-US/docs/Web/API/CSSRule) -abstract class EmulatedCssRule { - int type; - String cssText; -} - -/// Emulates [CSSStyleRule](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleRule) -class EmulatedCssStyleRule extends EmulatedCssRule { - String selectorText; - EmulatedCssStyleDeclaration style; - - EmulatedCssStyleRule(cssv.RuleSet ruleSet) { - final declarationText = new StringBuffer(); - ruleSet.declarationGroup.declarations.forEach((d) { - if (d is! cssv.Declaration) { - // Nested selectors not supported - return; - } - // TODO: expression spans are currently broken in csslib; see: - // https://github.com/dart-lang/csslib/pull/14 - var declarationSpan = d.span.text; - var colonIdx = declarationSpan.indexOf(':'); - var expression = declarationSpan.substring(colonIdx + 1); - declarationText.write('${d.property}: ${expression};'); - }); - - final style = new EmulatedCssStyleDeclaration() - ..cssText = declarationText.toString(); - - this - ..type = 1 - ..cssText = ruleSet.span.text - ..selectorText = ruleSet.selectorGroup.span.text - ..style = style; - } -} - -/// Emulates [CSSStyleDeclaration](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration) -class EmulatedCssStyleDeclaration { - final String content = ''; - String cssText; -} - -/// Emulates [CSSMediaRule](https://developer.mozilla.org/en-US/docs/Web/API/CSSMediaRule) -class EmulatedCssMedialRule extends EmulatedCssRule { - List cssRules; - EmulatedMediaList media; - - EmulatedCssMedialRule(cssv.MediaDirective directive) { - this - ..type = 4 - ..media = new EmulatedMediaList(directive) - ..cssText = directive.span.text - ..cssRules = emulateRules(directive.rulesets); - } -} - -/// Emulates [MediaList](https://developer.mozilla.org/en-US/docs/Web/API/MediaList) -class EmulatedMediaList { - String mediaText; - - EmulatedMediaList(cssv.MediaDirective directive) { - this.mediaText = - directive.mediaQueries.map((q) => q.span.text).join(' and '); - } -} - -/// Emulates [CSSImportRule](https://developer.mozilla.org/en-US/docs/Web/API/CSSImportRule) -class EmulatedCssImportRule extends EmulatedCssRule { - EmulatedCssImportRule(cssv.ImportDirective directive) { - this - ..type = 3 - ..cssText = '@${directive.span.text};'; - } -} diff --git a/modules/angular2/src/core/dom/generic_browser_adapter.ts b/modules/angular2/src/core/dom/generic_browser_adapter.ts index 30e0561be3..7336385341 100644 --- a/modules/angular2/src/core/dom/generic_browser_adapter.ts +++ b/modules/angular2/src/core/dom/generic_browser_adapter.ts @@ -47,37 +47,10 @@ export abstract class GenericBrowserDomAdapter extends DomAdapter { resolveAndSetHref(el: HTMLAnchorElement, baseUrl: string, href: string) { el.href = href == null ? baseUrl : baseUrl + '/../' + href; } - cssToRules(css: string): any[] { - var style = this.createStyleElement(css); - this.appendChild(this.defaultDoc().head, style); - var rules = []; - if (isPresent(style.sheet)) { - // TODO(sorvell): Firefox throws when accessing the rules of a stylesheet - // with an @import - // https://bugzilla.mozilla.org/show_bug.cgi?id=625013 - try { - var rawRules = (style.sheet).cssRules; - rules = ListWrapper.createFixedSize(rawRules.length); - for (var i = 0; i < rawRules.length; i++) { - rules[i] = rawRules[i]; - } - } catch (e) { - // - } - } else { - // console.warn('sheet not found', style); - } - this.remove(style); - return rules; - } supportsDOMEvents(): boolean { return true; } supportsNativeShadowDOM(): boolean { return isFunction((this.defaultDoc().body).createShadowRoot); } - supportsUnprefixedCssAnimation(): boolean { - return isPresent(this.defaultDoc().body.style) && - isPresent(this.defaultDoc().body.style.animationName); - } getAnimationPrefix(): string { return isPresent(this._animationPrefix) ? this._animationPrefix : ""; } diff --git a/modules/angular2/src/core/dom/parse5_adapter.ts b/modules/angular2/src/core/dom/parse5_adapter.ts index d05cf7ce00..2f0e524313 100644 --- a/modules/angular2/src/core/dom/parse5_adapter.ts +++ b/modules/angular2/src/core/dom/parse5_adapter.ts @@ -3,8 +3,6 @@ var parser = new parse5.Parser(parse5.TreeAdapters.htmlparser2); var serializer = new parse5.Serializer(parse5.TreeAdapters.htmlparser2); var treeAdapter = parser.treeAdapter; -var cssParse = require('css/lib/parse/index'); - import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection'; import {DomAdapter, setRootDomAdapter} from './dom_adapter'; import { @@ -474,18 +472,6 @@ export class Parse5DomAdapter extends DomAdapter { isShadowRoot(node): boolean { return this.getShadowRoot(node) == node; } importIntoDoc(node): any { return this.clone(node); } adoptNode(node): any { return node; } - isPageRule(rule): boolean { - return rule.type === 6; // CSSRule.PAGE_RULE - } - isStyleRule(rule): boolean { - return rule.type === 1; // CSSRule.MEDIA_RULE - } - isMediaRule(rule): boolean { - return rule.type === 4; // CSSRule.MEDIA_RULE - } - isKeyframesRule(rule): boolean { - return rule.type === 7; // CSSRule.KEYFRAMES_RULE - } getHref(el): string { return el.href; } resolveAndSetHref(el, baseUrl: string, href: string) { if (href == null) { @@ -531,15 +517,6 @@ export class Parse5DomAdapter extends DomAdapter { } return rules; } - cssToRules(css: string): any[] { - css = css.replace(/url\(\'(.+)\'\)/g, 'url($1)'); - var rules = []; - var parsedCSS = cssParse(css, {silent: true}); - if (parsedCSS.stylesheet && parsedCSS.stylesheet.rules) { - rules = this._buildRules(parsedCSS.stylesheet.rules, css); - } - return rules; - } supportsDOMEvents(): boolean { return false; } supportsNativeShadowDOM(): boolean { return false; } getGlobalEventTarget(target: string): any { @@ -551,12 +528,6 @@ export class Parse5DomAdapter extends DomAdapter { return this.defaultDoc().body; } } - supportsUnprefixedCssAnimation(): boolean { - // Currently during offline code transformation we do not know - // what browsers we are targetting. To play it safe, we assume - // unprefixed animations are not supported. - return false; - } getBaseHref(): string { throw 'not implemented'; } resetBaseElement(): void { throw 'not implemented'; } getHistory(): History { throw 'not implemented'; } diff --git a/modules/angular2/test/core/compiler/shadow_css_html5lib.server.spec.dart b/modules/angular2/test/core/compiler/shadow_css_html5lib.server.spec.dart deleted file mode 100644 index 05206f2e75..0000000000 --- a/modules/angular2/test/core/compiler/shadow_css_html5lib.server.spec.dart +++ /dev/null @@ -1,11 +0,0 @@ -library angular2.compiler.shadow_css_html5lib.test; - -import 'package:angular2/src/core/dom/html_adapter.dart'; -import 'package:angular2/src/testing/testing_internal.dart' show testSetup; -import 'shadow_css_spec.dart' as shadow_css_spec_test; - -void main() { - Html5LibDomAdapter.makeCurrent(); - testSetup(); - shadow_css_spec_test.main(); -} diff --git a/modules/angular2/test/core/compiler/shadow_css_spec.ts b/modules/angular2/test/core/compiler/shadow_css_spec.ts index 4a25d8253c..568bad7c2c 100644 --- a/modules/angular2/test/core/compiler/shadow_css_spec.ts +++ b/modules/angular2/test/core/compiler/shadow_css_spec.ts @@ -7,13 +7,11 @@ import { iit, SpyObject, el, - normalizeCSS, - browserDetection + normalizeCSS } from 'angular2/testing_internal'; -import {ShadowCss} from 'angular2/src/core/compiler/shadow_css'; +import {ShadowCss, splitCurlyBlocks} from 'angular2/src/core/compiler/shadow_css'; import {RegExpWrapper, StringWrapper, isPresent} from 'angular2/src/core/facade/lang'; -import {DOM} from 'angular2/src/core/dom/dom_adapter'; export function main() { describe('ShadowCss', function() { @@ -35,7 +33,7 @@ export function main() { it('should handle invalid css', () => { var css = 'one {color: red;}garbage'; - var expected = 'one[a] {color:red;}'; + var expected = 'one[a] {color:red;}garbage'; expect(s(css, 'a')).toEqual(expected); }); @@ -53,28 +51,20 @@ export function main() { it('should handle media rules with simple rules', () => { var css = '@media screen and (max-width: 800px) {div {font-size: 50px;}} div {}'; - var expected = '@media screen and (max-width:800px) {div[a] {font-size:50px;}}div[a] {}'; + var expected = '@media screen and (max-width:800px) {div[a] {font-size:50px;}} div[a] {}'; expect(s(css, 'a')).toEqual(expected); }); // Check that the browser supports unprefixed CSS animation - if (DOM.supportsUnprefixedCssAnimation()) { - it('should handle keyframes rules', () => { - var css = '@keyframes foo {0% {transform: translate(-50%) scaleX(0);}}'; - var passRe = - /@(-webkit-)*keyframes foo {\s*0% {\s*transform:translate\(-50%\) scaleX\(0\);\s*}\s*}/g; - expect(RegExpWrapper.test(passRe, s(css, 'a'))).toEqual(true); - }); - } + it('should handle keyframes rules', () => { + var css = '@keyframes foo {0% {transform:translate(-50%) scaleX(0);}}'; + expect(s(css, 'a')).toEqual(css); + }); - if (browserDetection.isWebkit) { - it('should handle -webkit-keyframes rules', () => { - var css = '@-webkit-keyframes foo {0% {-webkit-transform: translate(-50%) scaleX(0);}}'; - var passRe = - /@-webkit-keyframes foo {\s*0% {\s*(-webkit-)*transform:translate\(-50%\) scaleX\(0\);\s*}}/g; - expect(RegExpWrapper.test(passRe, s(css, 'a'))).toEqual(true); - }); - } + it('should handle -webkit-keyframes rules', () => { + var css = '@-webkit-keyframes foo {0% {-webkit-transform:translate(-50%) scaleX(0);}}'; + expect(s(css, 'a')).toEqual(css); + }); it('should handle complicated selectors', () => { expect(s('one::before {}', 'a')).toEqual('one[a]::before {}'); @@ -110,10 +100,10 @@ export function main() { it('should support polyfill-next-selector', () => { var css = s("polyfill-next-selector {content: 'x > y'} z {}", 'a'); - expect(css).toEqual('x[a] > y[a] {}'); + expect(css).toEqual('x[a] > y[a]{}'); css = s('polyfill-next-selector {content: "x > y"} z {}', 'a'); - expect(css).toEqual('x[a] > y[a] {}'); + expect(css).toEqual('x[a] > y[a]{}'); }); it('should support polyfill-unscoped-rule', () => { @@ -134,10 +124,10 @@ export function main() { it('should support polyfill-rule', () => { var css = s("polyfill-rule {content: ':host.foo .bar';color: blue;}", 'a', 'a-host'); - expect(css).toEqual('[a-host].foo .bar {color:blue;}'); + expect(css).toEqual('[a-host].foo .bar {;color:blue;}'); css = s('polyfill-rule {content: ":host.foo .bar";color:blue;}', 'a', 'a-host'); - expect(css).toEqual('[a-host].foo .bar {color:blue;}'); + expect(css).toEqual('[a-host].foo .bar {;color:blue;}'); }); it('should handle ::shadow', () => { @@ -155,23 +145,36 @@ export function main() { expect(css).toEqual('x[a] y[a] {}'); }); - // TODO: can't work in Firefox, see https://bugzilla.mozilla.org/show_bug.cgi?id=625013 - // Issue opened to track that: https://github.com/angular/angular/issues/4628 - if (!browserDetection.isFirefox) { - it('should pass through @import directives', () => { - var styleStr = '@import url("https://fonts.googleapis.com/css?family=Roboto");'; - var css = s(styleStr, 'a'); - expect(css).toEqual(styleStr); - }); - } + it('should pass through @import directives', () => { + var styleStr = '@import url("https://fonts.googleapis.com/css?family=Roboto");'; + var css = s(styleStr, 'a'); + expect(css).toEqual(styleStr); + }); - // Android browser does not support calc(), see http://caniuse.com/#feat=calc - if (!browserDetection.isAndroid) { - it('should leave calc() unchanged', () => { - var styleStr = 'a {height:calc(100% - 55px);}'; - var css = s(styleStr, 'a'); - expect(css).toEqual(styleStr); - }); - } + it('should shim rules after @import', () => { + var styleStr = '@import url("a"); div {}'; + var css = s(styleStr, 'a'); + expect(css).toEqual('@import url("a"); div[a] {}'); + }); + + it('should leave calc() unchanged', () => { + var styleStr = 'div {height:calc(100% - 55px);}'; + var css = s(styleStr, 'a'); + expect(css).toEqual('div[a] {height:calc(100% - 55px);}'); + }); + }); + + describe('splitCurlyBlocks', () => { + it('should split empty css', () => { expect(splitCurlyBlocks('')).toEqual([]); }); + + it('should split css rules without body', + () => { expect(splitCurlyBlocks('a')).toEqual(['a', '']); }); + + it('should split css rules with body', + () => { expect(splitCurlyBlocks('a {b}')).toEqual(['a ', '{b}']); }); + + it('should split css rules with nested rules', () => { + expect(splitCurlyBlocks('a {b {c}} d {e}')).toEqual(['a ', '{b {c}}', ' d ', '{e}']); + }); }); } diff --git a/modules/angular2/test/core/compiler/style_compiler_spec.ts b/modules/angular2/test/core/compiler/style_compiler_spec.ts index f37170c5a9..191d311ca9 100644 --- a/modules/angular2/test/core/compiler/style_compiler_spec.ts +++ b/modules/angular2/test/core/compiler/style_compiler_spec.ts @@ -140,7 +140,7 @@ export function main() { .then(styles => { compareStyles(styles, [ 'div[_ngcontent-app1-23] {\ncolor: red;\n}', - 'span[_ngcontent-app1-23] {\ncolor: blue;\n}' + 'span[_ngcontent-app1-23] {color: blue}' ]); async.done(); }); @@ -152,8 +152,8 @@ export function main() { .then(styles => { compareStyles(styles, [ 'div[_ngcontent-app1-23] {\ncolor: red;\n}', - 'a[_ngcontent-app1-23] {\ncolor: green;\n}', - 'span[_ngcontent-app1-23] {\ncolor: blue;\n}' + 'a[_ngcontent-app1-23] {color: green}', + 'span[_ngcontent-app1-23] {color: blue}' ]); async.done(); }); @@ -260,7 +260,7 @@ export function main() { compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation) .then(styles => { compareStyles(styles, [ - 'div[_ngcontent-app1-23] {\ncolor: red;\n}', + 'div[_ngcontent-app1-23] {color: red}', 'span[_ngcontent-app1-23] {\ncolor: blue;\n}' ]); async.done(); @@ -281,8 +281,7 @@ export function main() { it('should compile plain css rules', inject([AsyncTestCompleter], (async) => { compile('div {color: red;}') .then(stylesAndShimStyles => { - var expected = - [['div {color: red;}'], ['div[_ngcontent-%COMP%] {\ncolor: red;\n}']]; + var expected = [['div {color: red;}'], ['div[_ngcontent-%COMP%] {color: red;}']]; compareStyles(stylesAndShimStyles[0], expected[0]); compareStyles(stylesAndShimStyles[1], expected[1]); async.done(); @@ -296,7 +295,7 @@ export function main() { var expected = [ ['div {color: red}', 'span {color: blue}'], [ - 'div[_ngcontent-%COMP%] {\ncolor: red;\n}', + 'div[_ngcontent-%COMP%] {color: red}', 'span[_ngcontent-%COMP%] {\ncolor: blue;\n}' ] ]; diff --git a/modules/angular2/test/web_workers/debug_tools/bootstrap.server.spec.dart b/modules/angular2/test/web_workers/debug_tools/bootstrap.server.spec.dart index a217505310..e6e479f3b4 100644 --- a/modules/angular2/test/web_workers/debug_tools/bootstrap.server.spec.dart +++ b/modules/angular2/test/web_workers/debug_tools/bootstrap.server.spec.dart @@ -1,5 +1,6 @@ library angular2.test.web_workers.debug_tools.bootstrap; +import 'package:angular2/src/core/dom/html_adapter.dart'; import "package:angular2/testing_internal.dart"; import "package:angular2/src/core/reflection/reflection_capabilities.dart"; import "package:angular2/src/core/reflection/reflection.dart"; @@ -9,6 +10,9 @@ import "../shared/web_worker_test_util.dart"; import "dart:convert"; main() { + Html5LibDomAdapter.makeCurrent(); + testSetup(); + describe("bootstrapWebWorkerCommon", () { it("should bootstrap on a Dart VM", () { reflector.reflectionCapabilities = new ReflectionCapabilities(); diff --git a/npm-shrinkwrap.clean.json b/npm-shrinkwrap.clean.json index cd47b13bf9..56214df438 100644 --- a/npm-shrinkwrap.clean.json +++ b/npm-shrinkwrap.clean.json @@ -2974,40 +2974,6 @@ } } }, - "css": { - "version": "2.2.0", - "resolved": "git://github.com/mlaval/css.git#9de8bc988d13c27d143486cf60ddf5f9deec0472", - "dependencies": { - "source-map": { - "version": "0.1.43", - "dependencies": { - "amdefine": { - "version": "0.1.0" - } - } - }, - "source-map-resolve": { - "version": "0.3.1", - "dependencies": { - "source-map-url": { - "version": "0.3.0" - }, - "atob": { - "version": "1.1.2" - }, - "resolve-url": { - "version": "0.2.1" - } - } - }, - "urix": { - "version": "0.1.0" - }, - "inherits": { - "version": "2.0.1" - } - } - }, "del": { "version": "1.1.1", "dependencies": { diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index c2ae858493..7d5f8604f1 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -4589,57 +4589,6 @@ } } }, - "css": { - "version": "2.2.0", - "from": "git://github.com/mlaval/css.git#9de8bc988d13c27d143486cf60ddf5f9deec0472", - "resolved": "git://github.com/mlaval/css.git#9de8bc988d13c27d143486cf60ddf5f9deec0472", - "dependencies": { - "source-map": { - "version": "0.1.43", - "from": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "dependencies": { - "amdefine": { - "version": "0.1.0", - "from": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz" - } - } - }, - "source-map-resolve": { - "version": "0.3.1", - "from": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.3.1.tgz", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.3.1.tgz", - "dependencies": { - "source-map-url": { - "version": "0.3.0", - "from": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.3.0.tgz", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.3.0.tgz" - }, - "atob": { - "version": "1.1.2", - "from": "https://registry.npmjs.org/atob/-/atob-1.1.2.tgz", - "resolved": "https://registry.npmjs.org/atob/-/atob-1.1.2.tgz" - }, - "resolve-url": { - "version": "0.2.1", - "from": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" - } - } - }, - "urix": { - "version": "0.1.0", - "from": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz" - }, - "inherits": { - "version": "2.0.1", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - } - } - }, "del": { "version": "1.1.1", "from": "https://registry.npmjs.org/del/-/del-1.1.1.tgz", diff --git a/package.json b/package.json index 91fbacdf0f..5848a7a2a9 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,6 @@ "chokidar": "^1.1.0", "clang-format": "^1.0.32", "conventional-changelog": "^0.2.1", - "css": "mlaval/css#issue65", "del": "~1", "es6-shim": "^0.33.3", "event-stream": "^3.1.5",