Skip to content

Commit 993b3d6

Browse files
committed
refactor(compiler): don’t rely on external css parser
We used to use different external css parsers, depending on the `DomAdapter`. This lead to inconsistent behavior and environment specific errors. Closes angular#5006 Closes angular#4993
1 parent 5f2eb3e commit 993b3d6

16 files changed

Lines changed: 115 additions & 432 deletions

modules/angular2/pubspec.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ environment:
1111
dependencies:
1212
analyzer: '>=0.24.4 <0.27.0'
1313
barback: '^0.15.2+2'
14-
csslib: '>=0.12.0 <1.0.0'
1514
code_transformers: '^0.2.8'
1615
dart_style: '>=0.1.8 <0.3.0'
1716
glob: '^1.0.0'

modules/angular2/src/core/compiler/shadow_css.ts

Lines changed: 59 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {DOM} from 'angular2/src/core/dom/dom_adapter';
21
import {ListWrapper} from 'angular2/src/core/facade/collection';
32
import {
43
StringWrapper,
@@ -144,8 +143,7 @@ export class ShadowCss {
144143
* Shim a style element with the given selector. Returns cssText that can
145144
* be included in the document via WebComponents.ShadowCSS.addCssToDocument(css).
146145
*/
147-
shimStyle(style: string, selector: string, hostSelector: string = ''): string {
148-
var cssText = DOM.getText(style);
146+
shimStyle(cssText: string, selector: string, hostSelector: string = ''): string {
149147
return this.shimCssText(cssText, selector, hostSelector);
150148
}
151149

@@ -231,8 +229,7 @@ export class ShadowCss {
231229
cssText = this._convertColonHostContext(cssText);
232230
cssText = this._convertShadowDOMSelectors(cssText);
233231
if (isPresent(scopeSelector)) {
234-
_withCssRules(cssText,
235-
(rules) => { cssText = this._scopeRules(rules, scopeSelector, hostSelector); });
232+
cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector);
236233
}
237234
cssText = cssText + '\n' + unscoped;
238235
return cssText.trim();
@@ -347,50 +344,37 @@ export class ShadowCss {
347344

348345
// change a selector like 'div' to 'name div'
349346
/** @internal */
350-
_scopeRules(cssRules, scopeSelector: string, hostSelector: string): string {
351-
var cssText = '';
352-
if (isPresent(cssRules)) {
353-
for (var i = 0; i < cssRules.length; i++) {
354-
var rule = cssRules[i];
355-
if (DOM.isStyleRule(rule) || DOM.isPageRule(rule)) {
356-
cssText += this._scopeSelector(rule.selectorText, scopeSelector, hostSelector,
357-
this.strictStyling) +
358-
' {\n';
359-
cssText += this._propertiesFromRule(rule) + '\n}\n\n';
360-
} else if (DOM.isMediaRule(rule)) {
361-
cssText += '@media ' + rule.media.mediaText + ' {\n';
362-
cssText += this._scopeRules(rule.cssRules, scopeSelector, hostSelector);
363-
cssText += '\n}\n\n';
364-
} else {
365-
// KEYFRAMES_RULE in IE throws when we query cssText
366-
// when it contains a -webkit- property.
367-
// if this happens, we fallback to constructing the rule
368-
// from the CSSRuleSet
369-
// https://connect.microsoft.com/IE/feedbackdetail/view/955703/accessing-csstext-of-a-keyframe-rule-that-contains-a-webkit-property-via-cssom-generates-exception
370-
try {
371-
if (isPresent(rule.cssText)) {
372-
cssText += rule.cssText + '\n\n';
373-
}
374-
} catch (x) {
375-
if (DOM.isKeyframesRule(rule) && isPresent(rule.cssRules)) {
376-
cssText += this._ieSafeCssTextFromKeyFrameRule(rule);
377-
}
378-
}
347+
_scopeSelectors(cssText: string, scopeSelector: string, hostSelector: string): string {
348+
var parts = splitCurlyBlocks(cssText);
349+
var result = [];
350+
for (var i = 0; i < parts.length; i += 2) {
351+
var selectorTextWithCommands = parts[i];
352+
var selectorStart = selectorTextWithCommands.lastIndexOf(';') + 1;
353+
var selectorText =
354+
selectorTextWithCommands.substring(selectorStart, selectorTextWithCommands.length);
355+
var ruleContent = parts[i + 1];
356+
var selectorMatch = RegExpWrapper.firstMatch(_singleSelectorRe, selectorText);
357+
if (isPresent(selectorMatch) && ruleContent.length > 0) {
358+
var selPrefix = selectorMatch[1];
359+
var selAt = isPresent(selectorMatch[2]) ? selectorMatch[2] : '';
360+
var selector = selectorMatch[3];
361+
var selSuffix = selectorMatch[4];
362+
if (selAt.length === 0 || selAt == '@page') {
363+
var scopedSelector =
364+
this._scopeSelector(selector, scopeSelector, hostSelector, this.strictStyling);
365+
selectorText = `${selPrefix}${selAt}${scopedSelector}${selSuffix}`;
366+
} else if (selAt == '@media' && ruleContent[0] == OPEN_CURLY &&
367+
ruleContent[ruleContent.length - 1] == CLOSE_CURLY) {
368+
var scopedContent = this._scopeSelectors(ruleContent.substring(1, ruleContent.length - 1),
369+
scopeSelector, hostSelector);
370+
ruleContent = `${OPEN_CURLY}${scopedContent}${CLOSE_CURLY}`;
379371
}
380372
}
373+
result.push(selectorTextWithCommands.substring(0, selectorStart));
374+
result.push(selectorText);
375+
result.push(ruleContent);
381376
}
382-
return cssText;
383-
}
384-
385-
/** @internal */
386-
_ieSafeCssTextFromKeyFrameRule(rule): string {
387-
var cssText = '@keyframes ' + rule.name + ' {';
388-
for (var i = 0; i < rule.cssRules.length; i++) {
389-
var r = rule.cssRules[i];
390-
cssText += ' ' + r.keyText + ' {' + r.style.cssText + '}';
391-
}
392-
cssText += ' }';
393-
return cssText;
377+
return result.join('');
394378
}
395379

396380
/** @internal */
@@ -477,36 +461,6 @@ export class ShadowCss {
477461
selector = StringWrapper.replaceAll(selector, _colonHostRe, _polyfillHost);
478462
return selector;
479463
}
480-
481-
/** @internal */
482-
_propertiesFromRule(rule): string {
483-
var cssText = rule.style.cssText;
484-
// TODO(sorvell): Safari cssom incorrectly removes quotes from the content
485-
// property. (https://bugs.webkit.org/show_bug.cgi?id=118045)
486-
// don't replace attr rules
487-
var attrRe = /['"]+|attr/g;
488-
if (rule.style.content.length > 0 &&
489-
!isPresent(RegExpWrapper.firstMatch(attrRe, rule.style.content))) {
490-
var contentRe = /content:[^;]*;/g;
491-
cssText =
492-
StringWrapper.replaceAll(cssText, contentRe, 'content: \'' + rule.style.content + '\';');
493-
}
494-
// TODO(sorvell): we can workaround this issue here, but we need a list
495-
// of troublesome properties to fix https://github.com/Polymer/platform/issues/53
496-
//
497-
// inherit rules can be omitted from cssText
498-
// TODO(sorvell): remove when Blink bug is fixed:
499-
// https://code.google.com/p/chromium/issues/detail?id=358273
500-
// var style = rule.style;
501-
// for (var i = 0; i < style.length; i++) {
502-
// var name = style.item(i);
503-
// var value = style.getPropertyValue(name);
504-
// if (value == 'initial') {
505-
// cssText += name + ': initial; ';
506-
// }
507-
//}
508-
return cssText;
509-
}
510464
}
511465
var _cssContentNextSelectorRe =
512466
/polyfill-next-selector[^}]*content:[\s]*?['"](.*?)['"][;\s]*}([^{]*?){/gim;
@@ -539,13 +493,34 @@ var _polyfillHostRe = RegExpWrapper.create(_polyfillHost, 'im');
539493
var _colonHostRe = /:host/gim;
540494
var _colonHostContextRe = /:host-context/gim;
541495

542-
function _cssToRules(cssText: string) {
543-
return DOM.cssToRules(cssText);
496+
var _singleSelectorRe = /^(\s*)(@\S+)?(.*?)(\s*)$/g;
497+
498+
var _curlyRe = /([{}])/g;
499+
var OPEN_CURLY = '{';
500+
var CLOSE_CURLY = '}';
501+
502+
export function splitCurlyBlocks(cssText:string):string[] {
503+
var parts = StringWrapper.split(cssText, _curlyRe);
504+
var result = [];
505+
var bracketCount = 0;
506+
var currentCurlyParts = [];
507+
for (var partIndex = 0; partIndex<parts.length; partIndex++) {
508+
var part = parts[partIndex];
509+
currentCurlyParts.push(part);
510+
if (part == OPEN_CURLY) {
511+
bracketCount++;
512+
} else if (part == CLOSE_CURLY) {
513+
bracketCount--;
514+
}
515+
if (bracketCount === 0) {
516+
result.push(currentCurlyParts.join(''));
517+
currentCurlyParts = [];
518+
}
519+
}
520+
result.push(currentCurlyParts.join(''));
521+
if (result.length >= 2 && result[result.length-1] == '' && result[result.length-2] == '') {
522+
result = result.slice(0, result.length-2);
523+
}
524+
return result;
544525
}
545526

546-
function _withCssRules(cssText: string, callback: Function) {
547-
// Difference from webcomponentjs: remove the workaround for an old bug in Chrome
548-
if (isBlank(callback)) return;
549-
var rules = _cssToRules(cssText);
550-
callback(rules);
551-
}

modules/angular2/src/core/dom/abstract_html_adapter.dart

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import 'package:html/parser.dart' as parser;
44
import 'package:html/dom.dart';
55

66
import 'dom_adapter.dart';
7-
import 'emulated_css.dart';
87
import '../compiler/xhr.dart';
98

109
const _attrToPropMap = const {
@@ -348,14 +347,6 @@ abstract class AbstractHtml5LibAdapter implements DomAdapter {
348347
throw 'not implemented';
349348
}
350349

351-
bool isPageRule(rule) => (rule.type == 6);
352-
353-
bool isStyleRule(rule) => (rule.type == 1);
354-
355-
bool isMediaRule(rule) => (rule.type == 4);
356-
357-
bool isKeyframesRule(rule) => (rule.type == 7);
358-
359350
String getHref(element) {
360351
throw 'not implemented';
361352
}
@@ -364,10 +355,6 @@ abstract class AbstractHtml5LibAdapter implements DomAdapter {
364355
throw 'not implemented';
365356
}
366357

367-
List cssToRules(String css) {
368-
return parseAndEmulateCssRules(css);
369-
}
370-
371358
List getDistributedNodes(Node) {
372359
throw 'not implemented';
373360
}
@@ -380,13 +367,6 @@ abstract class AbstractHtml5LibAdapter implements DomAdapter {
380367
return false;
381368
}
382369

383-
bool supportsUnprefixedCssAnimation() {
384-
// Currently during code transformation we do not know what
385-
// browsers we are targetting. To play it safe, we assume
386-
// unprefixed animations are not supported.
387-
return false;
388-
}
389-
390370
getHistory() {
391371
throw 'not implemented';
392372
}

modules/angular2/src/core/dom/browser_adapter.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -410,10 +410,6 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter {
410410
return document.adoptNode(node);
411411
}
412412

413-
bool isPageRule(CssRule rule) => rule is CssPageRule;
414-
bool isStyleRule(CssRule rule) => rule is CssStyleRule;
415-
bool isMediaRule(CssRule rule) => rule is CssMediaRule;
416-
bool isKeyframesRule(CssRule rule) => rule is CssKeyframesRule;
417413
String getHref(AnchorElement element) {
418414
return element.href;
419415
}

modules/angular2/src/core/dom/browser_adapter.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,6 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
273273
return document.importNode(toImport, true);
274274
}
275275
adoptNode(node: Node): any { return document.adoptNode(node); }
276-
isPageRule(rule): boolean { return rule.type === CSSRule.PAGE_RULE; }
277-
isStyleRule(rule): boolean { return rule.type === CSSRule.STYLE_RULE; }
278-
isMediaRule(rule): boolean { return rule.type === CSSRule.MEDIA_RULE; }
279-
isKeyframesRule(rule): boolean { return rule.type === CSSRule.KEYFRAMES_RULE; }
280276
getHref(el: Element): string { return (<any>el).href; }
281277
getEventKey(event): string {
282278
var key = event.key;

modules/angular2/src/core/dom/dom_adapter.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,11 @@ export abstract class DomAdapter {
111111
abstract isShadowRoot(node): boolean;
112112
abstract importIntoDoc /*<T extends Node>*/ (node: Node /*T*/): Node /*T*/;
113113
abstract adoptNode /*<T extends Node>*/ (node: Node /*T*/): Node /*T*/;
114-
abstract isPageRule(rule): boolean;
115-
abstract isStyleRule(rule): boolean;
116-
abstract isMediaRule(rule): boolean;
117-
abstract isKeyframesRule(rule): boolean;
118114
abstract getHref(element): string;
119115
abstract getEventKey(event): string;
120116
abstract resolveAndSetHref(element, baseUrl: string, href: string);
121-
abstract cssToRules(css: string): any[];
122117
abstract supportsDOMEvents(): boolean;
123118
abstract supportsNativeShadowDOM(): boolean;
124-
abstract supportsUnprefixedCssAnimation(): boolean;
125119
abstract getGlobalEventTarget(target: string): any;
126120
abstract getHistory(): History;
127121
abstract getLocation(): Location;

modules/angular2/src/core/dom/emulated_css.dart

Lines changed: 0 additions & 110 deletions
This file was deleted.

0 commit comments

Comments
 (0)