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 #5006 Closes #4993
This commit is contained in:
@ -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';
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 (<any>el).href; }
|
||||
getEventKey(event): string {
|
||||
var key = event.key;
|
||||
|
@ -111,17 +111,11 @@ export abstract class DomAdapter {
|
||||
abstract isShadowRoot(node): boolean;
|
||||
abstract importIntoDoc /*<T extends Node>*/ (node: Node /*T*/): Node /*T*/;
|
||||
abstract adoptNode /*<T extends Node>*/ (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;
|
||||
|
@ -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<EmulatedCssRule> parseAndEmulateCssRules(String css) {
|
||||
var stylesheet = cssp.parse(css);
|
||||
return emulateRules(stylesheet.topLevels);
|
||||
}
|
||||
|
||||
/// Converts `csslib` [rules] to their emulated counterparts.
|
||||
List<EmulatedCssRule> emulateRules(Iterable<cssv.TreeNode> 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<EmulatedCssStyleRule> 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};';
|
||||
}
|
||||
}
|
@ -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 = (<any>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((<any>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 : "";
|
||||
}
|
||||
|
@ -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'; }
|
||||
|
Reference in New Issue
Block a user