processDirective method
Directive grammar:
import: '@import' [string | URI] media_list?
media: '@media' media_query_list '{' ruleset '}'
page: '@page' [':' IDENT]? '{' declarations '}'
stylet: '@stylet' IDENT '{' ruleset '}'
media_query_list: IDENT [',' IDENT]
keyframes: '@-webkit-keyframes ...' (see grammar below).
font_face: '@font-face' '{' declarations '}'
namespace: '@namespace name url("xmlns")
host: '@host '{' ruleset '}'
mixin: '@mixin name [(args,...)] '{' declarations/ruleset '}'
include: '@include name [(@arg,@arg1)]
'@include name [(@arg...)]
content: '@content'
-moz-document: '@-moz-document' [ <url> | url-prefix(<string>) |
domain(<string>) | regexp(<string) ]# '{'
declarations
'}'
supports: '@supports' supports_condition group_rule_body
Implementation
CSSRule? processDirective() {
var tokenId = _peek();
switch (tokenId) {
case TokenKind.DIRECTIVE_IMPORT:
_next();
// Parse @import url or string and optional media list
String importHref = '';
String? mediaText;
// @import "file.css" ...;
if (_peek() == TokenKind.SINGLE_QUOTE || _peek() == TokenKind.DOUBLE_QUOTE) {
importHref = processQuotedString(false);
} else if (_peekIdentifier() && _peekToken.text.toLowerCase() == 'url') {
// @import url(file.css) ...;
// Consume 'url' and parse inside parentheses.
_next();
// processQuotedString(true) will optionally consume '('
final urlParam = processQuotedString(true);
// If processQuotedString(true) didn't consume a trailing ')', do it here.
if (_peek() == TokenKind.RPAREN) {
_next();
}
importHref = urlParam.trim();
}
// Parse optional media list until ';'
if (!_peekKind(TokenKind.SEMICOLON)) {
final buf = StringBuffer();
while (!_peekKind(TokenKind.SEMICOLON) && !_peekKind(TokenKind.END_OF_FILE) && !_peekKind(TokenKind.LBRACE)) {
buf.write(_next().text);
}
final text = buf.toString().trim();
if (text.isNotEmpty) mediaText = text;
}
_maybeEat(TokenKind.SEMICOLON);
return CSSImportRule(importHref, media: mediaText);
case TokenKind.DIRECTIVE_MEDIA:
_next();
// print('processDirective CSSMediaDirective start ----- TokenKind.DIRECTIVE_MEDIA');
CSSMediaQuery? cssMediaQuery = processMediaQuery();
if (cssMediaQuery != null) {
_next();
}
List<CSSRule>? rules = [];
do {
List<CSSRule>? rule = processRule();
if (rule != null) {
rules.addAll(rule);
}
else {
break;
}
} while (!_maybeEat(TokenKind.RBRACE));
// rules.forEach((rule) {
// if (rule is CSSStyleRule) {
// print(' ----> processDirective CSSMediaDirective forEach ${rule.selectorGroup.selectorText}, color ${rule.declaration.getPropertyValue('color')}');
// } else {
// print(' ----> processDirective CSSMediaDirective forEach ${rule.runtimeType}');
// }
// });
// print('processDirective CSSMediaDirective end ----- rules ${rules.length} TokenKind.DIRECTIVE_MEDIA');
return CSSMediaDirective(cssMediaQuery, rules);
case TokenKind.DIRECTIVE_HOST:
_next();
return null;
case TokenKind.DIRECTIVE_PAGE:
// @page S* IDENT? pseudo_page?
// S* '{' S*
// [ declaration | margin ]?
// [ ';' S* [ declaration | margin ]? ]* '}' S*
//
// pseudo_page :
// ':' [ "left" | "right" | "first" ]
//
// margin :
// margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
//
// margin_sym : @top-left-corner, @top-left, @bottom-left, etc.
//
// See http://www.w3.org/TR/css3-page/#CSS21
_next();
return null;
case TokenKind.DIRECTIVE_CHARSET:
// @charset S* STRING S* ';'
_next();
processQuotedString(false);
return null;
// TODO(terry): Workaround Dart2js bug continue not implemented in switch
// see https://code.google.com/p/dart/issues/detail?id=8270
/*
case TokenKind.DIRECTIVE_MS_KEYFRAMES:
// TODO(terry): For now only IE 10 (are base level) supports @keyframes,
// -moz- has only been optional since Oct 2012 release of Firefox, not
// all versions of webkit support @keyframes and opera doesn't yet
// support w/o -o- prefix. Add more warnings for other prefixes when
// they become optional.
if (isChecked) {
_warning('@-ms-keyframes should be @keyframes');
}
continue keyframeDirective;
keyframeDirective:
*/
case TokenKind.DIRECTIVE_KEYFRAMES:
case TokenKind.DIRECTIVE_WEB_KIT_KEYFRAMES:
case TokenKind.DIRECTIVE_MOZ_KEYFRAMES:
case TokenKind.DIRECTIVE_O_KEYFRAMES:
// TODO(terry): Remove workaround when bug 8270 is fixed.
case TokenKind.DIRECTIVE_MS_KEYFRAMES:
if (tokenId == TokenKind.DIRECTIVE_MS_KEYFRAMES && isChecked) {
_warning('@-ms-keyframes should be @keyframes');
}
// TODO(terry): End of workaround.
// Key frames grammar:
//
// @[browser]? keyframes [IDENT|STRING] '{' keyframes-blocks '}';
//
// browser: [-webkit-, -moz-, -ms-, -o-]
//
// keyframes-blocks:
// [keyframe-selectors '{' declarations '}']* ;
//
// keyframe-selectors:
// ['from'|'to'|PERCENTAGE] [',' ['from'|'to'|PERCENTAGE] ]* ;
_next();
String name = '';
if (_peekIdentifier()) {
name = identifier().name;
}
assert(name.isNotEmpty, 'keyframes rule name must not be null');
_eat(TokenKind.LBRACE);
var keyframe = CSSKeyframesRule(tokenId, name);
do {
List<String> selectors = [];
do {
var selector = _next().text;
final text = _peekToken.text;
// ignore unit type
if (TokenKind.matchUnits(text, 0, text.length) != -1) {
if (_peekToken.kind == TokenKind.PERCENT) {
selector += text; // join selector & unit
}
_next();
}
selectors.add(selector);
} while (_maybeEat(TokenKind.COMMA));
final declarations = processDeclarations();
if (declarations.last is CSSStyleDeclaration) {
keyframe.add(KeyFrameBlock(selectors, declarations.last));
}
} while (!_maybeEat(TokenKind.RBRACE) && !isPrematureEndOfFile());
return keyframe;
case TokenKind.DIRECTIVE_FONTFACE:
_next();
_eat(TokenKind.LBRACE);
List data = processDeclarations();
assert(data.isNotEmpty);
return CSSFontFaceRule(data[0]);
case TokenKind.DIRECTIVE_STYLET:
// Stylet grammar:
//
// @stylet IDENT '{'
// ruleset
// '}'
_next();
return null;
case TokenKind.DIRECTIVE_NAMESPACE:
// Namespace grammar:
//
// @namespace S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
// namespace_prefix : IDENT
_next();
return null;
case TokenKind.DIRECTIVE_MIXIN:
return null;
case TokenKind.DIRECTIVE_INCLUDE:
return null;
case TokenKind.DIRECTIVE_CONTENT:
// TODO(terry): TBD
_warning('@content not implemented.');
return null;
case TokenKind.DIRECTIVE_MOZ_DOCUMENT:
return null;
case TokenKind.DIRECTIVE_SUPPORTS:
return null;
case TokenKind.DIRECTIVE_VIEWPORT:
case TokenKind.DIRECTIVE_MS_VIEWPORT:
return null;
}
return null;
}