CssParser
The CSS parser.
See http://dev.w3.org/csswg/css-syntax/#parsing for more details.
using AngleSharp.Css;
using AngleSharp.DOM;
using AngleSharp.DOM.Collections;
using AngleSharp.DOM.Css;
using AngleSharp.Extensions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AngleSharp.Parser.Css
{
[DebuggerStepThrough]
public sealed class CssParser
{
private delegate CSSRule Creator (CssParser parser, IEnumerator<CssToken> tokens);
private readonly CssSelectorConstructor selector;
private readonly CssValueBuilder value;
private readonly CssTokenizer tokenizer;
private readonly object sync;
private readonly CSSStyleSheet sheet;
private bool started;
private Task<ICssStyleSheet> task;
private static readonly Dictionary<string, Creator> creators;
public bool IsAsync => task != null;
public ICssStyleSheet Result {
get {
Parse();
return sheet;
}
}
public event EventHandler<ParseErrorEventArgs> ParseError {
add {
tokenizer.ErrorOccurred += value;
}
remove {
tokenizer.ErrorOccurred -= value;
}
}
static CssParser()
{
creators = new Dictionary<string, Creator>();
creators.Add(RuleNames.Charset, CreateCharsetRule);
creators.Add(RuleNames.Page, CreatePageRule);
creators.Add(RuleNames.Import, CreateImportRule);
creators.Add(RuleNames.FontFace, CreateFontFaceRule);
creators.Add(RuleNames.Media, CreateMediaRule);
creators.Add(RuleNames.Namespace, CreateNamespaceRule);
creators.Add(RuleNames.Supports, CreateSupportsRule);
creators.Add(RuleNames.Keyframes, CreateKeyframesRule);
creators.Add(RuleNames.Document, CreateDocumentRule);
}
public CssParser(string source, IConfiguration configuration = null)
: this(new CSSStyleSheet(new TextSource(source)) {
Options = configuration
})
{
}
public CssParser(Stream stream, IConfiguration configuration = null)
: this(new CSSStyleSheet(new TextSource(stream, configuration.DefaultEncoding())) {
Options = configuration
})
{
}
internal CssParser(CSSStyleSheet stylesheet)
{
IElement ownerNode = stylesheet.OwnerNode;
selector = new CssSelectorConstructor();
value = new CssValueBuilder();
sync = new object();
tokenizer = new CssTokenizer(stylesheet.Source) {
IgnoreComments = true,
IgnoreWhitespace = true
};
started = false;
sheet = stylesheet;
}
public Task<ICssStyleSheet> ParseAsync()
{
return ParseAsync(CancellationToken.None);
}
public Task<ICssStyleSheet> ParseAsync(CancellationToken cancelToken)
{
lock (sync) {
if (!started) {
started = true;
task = KernelAsync(cancelToken);
}
}
return task;
}
public ICssStyleSheet Parse()
{
if (!started) {
started = true;
Kernel();
}
return sheet;
}
private static CSSMediaRule CreateMediaRule(CssParser parser, IEnumerator<CssToken> tokens)
{
MediaList media = tokens.MoveNext() ? parser.InMediaList(tokens) : new MediaList();
CSSMediaRule cSSMediaRule = new CSSMediaRule(media);
if (tokens.Current.Type != CssTokenType.CurlyBracketOpen)
return null;
parser.FillRules(cSSMediaRule, tokens);
return cSSMediaRule;
}
private static CSSPageRule CreatePageRule(CssParser parser, IEnumerator<CssToken> tokens)
{
CSSPageRule cSSPageRule = new CSSPageRule();
if (tokens.MoveNext())
cSSPageRule.Selector = parser.InSelector(tokens);
if (tokens.Current.Type == CssTokenType.CurlyBracketOpen)
parser.FillDeclarations(cSSPageRule.Style, tokens);
return cSSPageRule;
}
private static CSSFontFaceRule CreateFontFaceRule(CssParser parser, IEnumerator<CssToken> tokens)
{
CSSFontFaceRule cSSFontFaceRule = new CSSFontFaceRule();
if (tokens.MoveNext() && tokens.Current.Type == CssTokenType.CurlyBracketOpen)
parser.FillDeclarations(cSSFontFaceRule.Style, tokens);
return cSSFontFaceRule;
}
private static CSSSupportsRule CreateSupportsRule(CssParser parser, IEnumerator<CssToken> tokens)
{
CSSSupportsRule cSSSupportsRule = new CSSSupportsRule();
if (tokens.MoveNext())
cSSSupportsRule.ConditionText = parser.Condition(tokens);
if (tokens.Current.Type == CssTokenType.CurlyBracketOpen)
parser.FillRules(cSSSupportsRule, tokens);
return cSSSupportsRule;
}
private static CSSDocumentRule CreateDocumentRule(CssParser parser, IEnumerator<CssToken> tokens)
{
CSSDocumentRule cSSDocumentRule = new CSSDocumentRule();
if (tokens.MoveNext())
cSSDocumentRule.Conditions.AddRange(parser.InDocumentFunctions(tokens));
if (tokens.Current.Type == CssTokenType.CurlyBracketOpen)
parser.FillRules(cSSDocumentRule, tokens);
return cSSDocumentRule;
}
private static CSSKeyframesRule CreateKeyframesRule(CssParser parser, IEnumerator<CssToken> tokens)
{
CSSKeyframesRule cSSKeyframesRule = new CSSKeyframesRule();
if (tokens.MoveNext())
cSSKeyframesRule.Name = parser.InKeyframesName(tokens);
if (tokens.Current.Type == CssTokenType.CurlyBracketOpen)
parser.FillRules(cSSKeyframesRule, tokens);
return cSSKeyframesRule;
}
private static CSSNamespaceRule CreateNamespaceRule(CssParser parser, IEnumerator<CssToken> tokens)
{
CSSNamespaceRule cSSNamespaceRule = new CSSNamespaceRule();
if (tokens.MoveNext()) {
CssToken current = tokens.Current;
if (current.Type == CssTokenType.Ident) {
cSSNamespaceRule.Prefix = current.Data;
if (tokens.MoveNext())
current = tokens.Current;
if (current.Type == CssTokenType.String)
cSSNamespaceRule.NamespaceUri = ((CssStringToken)current).Data;
}
JumpToNextSemicolon(tokens);
}
return cSSNamespaceRule;
}
private static CSSCharsetRule CreateCharsetRule(CssParser parser, IEnumerator<CssToken> tokens)
{
CSSCharsetRule cSSCharsetRule = new CSSCharsetRule();
if (tokens.MoveNext()) {
CssToken current = tokens.Current;
if (current.Type == CssTokenType.String) {
cSSCharsetRule.CharacterSet = ((CssStringToken)current).Data;
tokens.MoveNext();
}
JumpToNextSemicolon(tokens);
}
return cSSCharsetRule;
}
private static CSSImportRule CreateImportRule(CssParser parser, IEnumerator<CssToken> tokens)
{
CSSImportRule cSSImportRule = new CSSImportRule();
if (tokens.MoveNext()) {
CssToken current = tokens.Current;
if (current.Type == CssTokenType.String || current.Type == CssTokenType.Url) {
cSSImportRule.Href = ((CssStringToken)current).Data;
if (tokens.MoveNext())
cSSImportRule.Media = parser.InMediaList(tokens);
}
JumpToNextSemicolon(tokens);
}
return cSSImportRule;
}
private CSSRule CreateRule(IEnumerator<CssToken> tokens)
{
CssToken current = tokens.Current;
switch (current.Type) {
case CssTokenType.AtKeyword:
if (creators.TryGetValue(current.Data, out Creator creator))
return creator(this, tokens);
SkipUnknownRule(tokens);
return null;
case CssTokenType.RoundBracketClose:
case CssTokenType.CurlyBracketClose:
case CssTokenType.SquareBracketClose:
while (tokens.MoveNext()) {
}
return null;
default: {
ISelector selector = InSelector(tokens);
if (selector == null) {
SkipUnknownRule(tokens);
return null;
}
CSSStyleRule cSSStyleRule = new CSSStyleRule();
FillDeclarations(cSSStyleRule.Style, tokens);
cSSStyleRule.Selector = selector;
return cSSStyleRule;
}
}
}
private string Condition(IEnumerator<CssToken> tokens)
{
StringBuilder stringBuilder = Pool.NewStringBuilder();
tokenizer.IgnoreWhitespace = false;
do {
CssToken current = tokens.Current;
if (current.Type == CssTokenType.CurlyBracketOpen)
break;
stringBuilder.Append(current.ToValue());
} while (tokens.MoveNext());
tokenizer.IgnoreWhitespace = true;
return stringBuilder.ToPool();
}
private ISelector InSelector(IEnumerator<CssToken> tokens)
{
tokenizer.IgnoreWhitespace = false;
selector.Reset();
do {
CssToken current = tokens.Current;
if (current.Type == CssTokenType.CurlyBracketOpen || current.Type == CssTokenType.CurlyBracketClose)
break;
if (!selector.Apply(current)) {
tokenizer.IgnoreWhitespace = true;
return null;
}
} while (tokens.MoveNext());
tokenizer.IgnoreWhitespace = true;
return selector.Result;
}
private CSSProperty Declaration(IEnumerator<CssToken> tokens, CSSStyleDeclaration style)
{
CssToken current = tokens.Current;
if (current.Type == CssTokenType.Ident) {
string data = current.Data;
if (!tokens.MoveNext())
return null;
if (tokens.Current.Type != CssTokenType.Colon)
JumpToEndOfDeclaration(tokens);
else if (tokens.MoveNext()) {
CSSProperty cSSProperty = style.CreateProperty(data);
if (cSSProperty != null) {
CSSValue cSSValue = InValue(tokens);
if (cSSValue != null && cSSProperty.TrySetValue(cSSValue))
style.SetProperty(cSSProperty);
if (IsImportant(tokens))
cSSProperty.IsImportant = true;
}
JumpToEndOfDeclaration(tokens);
return cSSProperty;
}
}
return null;
}
private bool IsImportant(IEnumerator<CssToken> tokens)
{
CssToken current = tokens.Current;
if (current.Type == CssTokenType.Ident)
return current.Data == Keywords.Important;
return false;
}
private IEnumerable<Tuple<CSSDocumentRule.DocumentFunction, string>> InDocumentFunctions(IEnumerator<CssToken> tokens)
{
do {
Tuple<CSSDocumentRule.DocumentFunction, string> function = InDocumentFunction(tokens);
if (function != null)
yield return function;
} while (tokens.MoveNext() && tokens.Current.Type == CssTokenType.Comma);
}
private Tuple<CSSDocumentRule.DocumentFunction, string> InDocumentFunction(IEnumerator<CssToken> tokens)
{
CssToken current = tokens.Current;
switch (current.Type) {
case CssTokenType.Url:
return Tuple.Create(CSSDocumentRule.DocumentFunction.Url, ((CssStringToken)current).Data);
case CssTokenType.UrlPrefix:
return Tuple.Create(CSSDocumentRule.DocumentFunction.UrlPrefix, ((CssStringToken)current).Data);
case CssTokenType.Domain:
return Tuple.Create(CSSDocumentRule.DocumentFunction.Domain, ((CssStringToken)current).Data);
case CssTokenType.Function:
if (string.Compare(current.Data, FunctionNames.Regexp, StringComparison.OrdinalIgnoreCase) == 0 && tokens.MoveNext()) {
current = tokens.Current;
if (current.Type == CssTokenType.String)
return Tuple.Create(CSSDocumentRule.DocumentFunction.RegExp, ((CssStringToken)current).Data);
JumpToClosedArguments(tokens);
}
break;
}
return null;
}
private string InKeyframesName(IEnumerator<CssToken> tokens)
{
CssToken current = tokens.Current;
if (current.Type == CssTokenType.Ident) {
tokens.MoveNext();
return current.Data;
}
return string.Empty;
}
private CSSKeyframeRule CreateKeyframeRule(IEnumerator<CssToken> tokens)
{
CSSKeyframeRule cSSKeyframeRule = new CSSKeyframeRule();
cSSKeyframeRule.KeyText = InKeyframeText(tokens);
FillDeclarations(cSSKeyframeRule.Style, tokens);
return cSSKeyframeRule;
}
private string InKeyframeText(IEnumerator<CssToken> tokens)
{
StringBuilder stringBuilder = Pool.NewStringBuilder();
do {
CssToken current = tokens.Current;
if (current.Type == CssTokenType.CurlyBracketOpen)
break;
stringBuilder.Append(current.ToValue());
} while (tokens.MoveNext());
return stringBuilder.ToPool();
}
private MediaList InMediaList(IEnumerator<CssToken> tokens)
{
MediaList mediaList = new MediaList();
do {
CSSMedium cSSMedium = InMediaValue(tokens);
if (cSSMedium == null)
break;
mediaList.Add(cSSMedium);
} while (tokens.Current.Type == CssTokenType.Comma && tokens.MoveNext());
if (tokens.Current.Type != CssTokenType.CurlyBracketOpen) {
if (tokens.Current.Type == CssTokenType.RoundBracketClose)
tokens.MoveNext();
if (tokens.Current.Type == CssTokenType.CurlyBracketOpen)
tokens.MoveNext();
JumpToEndOfDeclaration(tokens);
} else if (mediaList.Length == 0 && tokens.MoveNext()) {
JumpToEndOfDeclaration(tokens);
}
return mediaList;
}
private CSSMedium InMediaValue(IEnumerator<CssToken> tokens)
{
CSSMedium medium = GetMedium(tokens);
CssToken current = tokens.Current;
if (current.Type == CssTokenType.Ident) {
medium.Type = current.Data;
if (!tokens.MoveNext())
return medium;
current = tokens.Current;
if (current.Type != CssTokenType.Ident || string.Compare(current.Data, Keywords.And, StringComparison.OrdinalIgnoreCase) != 0 || !tokens.MoveNext())
return medium;
}
do {
if (tokens.Current.Type != CssTokenType.RoundBracketOpen)
return null;
if (!tokens.MoveNext())
return medium;
Tuple<string, CSSValue> constraint = GetConstraint(tokens);
if (constraint == null || tokens.Current.Type != CssTokenType.RoundBracketClose || !medium.AddConstraint(constraint.Item1, constraint.Item2))
return null;
if (!tokens.MoveNext())
return medium;
current = tokens.Current;
} while (current.Type == CssTokenType.Ident && string.Compare(current.Data, Keywords.And, StringComparison.OrdinalIgnoreCase) == 0 && tokens.MoveNext());
return medium;
}
private Tuple<string, CSSValue> GetConstraint(IEnumerator<CssToken> tokens)
{
CssToken current = tokens.Current;
if (current.Type != CssTokenType.Ident) {
JumpToClosedArguments(tokens);
return null;
}
value.Reset();
string data = current.Data;
tokens.MoveNext();
current = tokens.Current;
if (current.Type == CssTokenType.Colon) {
tokenizer.IgnoreWhitespace = false;
tokens.MoveNext();
while (GetSingleValue(tokens) && tokens.Current.Type != CssTokenType.RoundBracketClose) {
}
tokenizer.IgnoreWhitespace = true;
}
return Tuple.Create(data, value.ToValue());
}
private static CSSMedium GetMedium(IEnumerator<CssToken> tokens)
{
CssToken current = tokens.Current;
CSSMedium cSSMedium = new CSSMedium();
if (current.Type == CssTokenType.Ident) {
string data = current.Data;
if (string.Compare(data, Keywords.Not, StringComparison.OrdinalIgnoreCase) == 0) {
tokens.MoveNext();
cSSMedium.IsInverse = true;
} else if (string.Compare(data, Keywords.Only, StringComparison.OrdinalIgnoreCase) == 0) {
tokens.MoveNext();
cSSMedium.IsExclusive = true;
}
}
return cSSMedium;
}
private CSSValue InValue(IEnumerator<CssToken> tokens)
{
tokenizer.IgnoreWhitespace = false;
value.Reset();
while (GetSingleValue(tokens)) {
}
tokenizer.IgnoreWhitespace = true;
return value.ToValue();
}
private bool GetSingleValue(IEnumerator<CssToken> tokens)
{
CssToken current = tokens.Current;
switch (current.Type) {
case CssTokenType.Percentage:
case CssTokenType.Dimension:
return TakeValue(ToUnit((CssUnitToken)current), tokens);
case CssTokenType.Hash:
return TakeValue((ICssObject)(object)GetColorFromHexValue(current.Data), tokens);
case CssTokenType.Delim:
return GetValueFromDelim(current.Data[0], tokens);
case CssTokenType.Ident:
value.AddValue(ToIdentifier(current.Data));
return tokens.MoveNext();
case CssTokenType.String:
value.AddValue((CssString)current.Data);
return tokens.MoveNext();
case CssTokenType.Url:
value.AddValue((CssUrl)current.Data);
return tokens.MoveNext();
case CssTokenType.Number:
value.AddValue(ToNumber((CssNumberToken)current));
return tokens.MoveNext();
case CssTokenType.Function:
return GetValueFunction(tokens);
case CssTokenType.Comma:
value.NextArgument();
return tokens.MoveNext();
case CssTokenType.Whitespace:
return tokens.MoveNext();
case CssTokenType.CurlyBracketClose:
case CssTokenType.Semicolon:
return false;
default:
value.IsFaulted = true;
return false;
}
}
private bool TakeValue(ICssObject val, IEnumerator<CssToken> tokens)
{
bool result = tokens.MoveNext();
if (val == null) {
value.IsFaulted = true;
return false;
}
value.AddValue(val);
return result;
}
private bool GetValueFromDelim(char delimiter, IEnumerator<CssToken> tokens)
{
if (delimiter == '#' && tokens.MoveNext())
return GetColorFromHexValue(tokens);
switch (delimiter) {
case '/':
value.AddValue(CSSValue.Delimiter);
return tokens.MoveNext();
case '!':
if (tokens.MoveNext() && IsImportant(tokens))
break;
goto default;
default:
value.IsFaulted = true;
break;
}
return false;
}
private bool GetValueFunction(IEnumerator<CssToken> tokens)
{
string data = ((CssKeywordToken)tokens.Current).Data;
value.AddFunction(data);
if (!tokens.MoveNext())
return false;
while (GetSingleValue(tokens) && tokens.Current.Type != CssTokenType.RoundBracketClose) {
}
value.CloseFunction();
return tokens.MoveNext();
}
private bool GetColorFromHexValue(IEnumerator<CssToken> tokens)
{
StringBuilder stringBuilder = Pool.NewStringBuilder();
bool result = true;
do {
CssToken current = tokens.Current;
if (current.Type != CssTokenType.Number && current.Type != CssTokenType.Dimension && current.Type != CssTokenType.Ident)
break;
string text = current.ToValue();
if (stringBuilder.Length + text.Length > 6)
break;
stringBuilder.Append(text);
} while (result = tokens.MoveNext());
Color? colorFromHexValue = GetColorFromHexValue(stringBuilder.ToPool());
if (colorFromHexValue.HasValue)
value.AddValue(colorFromHexValue.Value);
return result;
}
private static Color? GetColorFromHexValue(string hexColor)
{
if (Color.TryFromHex(hexColor, out Color color))
return color;
return null;
}
private void Kernel()
{
IEnumerator<CssToken> enumerator = tokenizer.Tokens.GetEnumerator();
while (enumerator.MoveNext()) {
CSSRule cSSRule = CreateRule(enumerator);
if (cSSRule != null)
sheet.Rules.Add(cSSRule, sheet, null);
}
}
private async Task<ICssStyleSheet> KernelAsync(CancellationToken cancelToken)
{
ITextSource source = sheet.Source;
IEnumerator<CssToken> tokens = tokenizer.Tokens.GetEnumerator();
while (true) {
if (source.Length - source.Index < 1024)
await source.Prefetch(8192, cancelToken).ConfigureAwait(false);
if (!tokens.MoveNext())
break;
CSSRule rule = CreateRule(tokens);
if (rule != null)
sheet.Rules.Add(rule, sheet, null);
}
return sheet;
}
private void FillRules(CSSGroupingRule parentRule, IEnumerator<CssToken> tokens)
{
while (tokens.MoveNext() && tokens.Current.Type != CssTokenType.CurlyBracketClose) {
CSSRule cSSRule = CreateRule(tokens);
if (cSSRule != null)
parentRule.Rules.Add(cSSRule, sheet, parentRule);
}
}
private void FillRules(CSSKeyframesRule parentRule, IEnumerator<CssToken> tokens)
{
while (tokens.MoveNext() && tokens.Current.Type != CssTokenType.CurlyBracketClose) {
CSSKeyframeRule cSSKeyframeRule = CreateKeyframeRule(tokens);
if (cSSKeyframeRule != null)
parentRule.Rules.Add(cSSKeyframeRule, sheet, parentRule);
}
}
private void FillDeclarations(CSSStyleDeclaration style, IEnumerator<CssToken> tokens)
{
while (tokens.MoveNext()) {
if (tokens.Current.Type == CssTokenType.CurlyBracketClose)
break;
Declaration(tokens, style);
if (tokens.Current.Type == CssTokenType.CurlyBracketClose)
break;
}
}
private static void JumpToEndOfDeclaration(IEnumerator<CssToken> tokens)
{
int num = 0;
int num2 = 0;
int num3 = 0;
do {
switch (tokens.Current.Type) {
case CssTokenType.CurlyBracketClose:
if (num <= 0 && num2 <= 0 && num3 <= 0)
return;
num2--;
break;
case CssTokenType.CurlyBracketOpen:
num2++;
break;
case CssTokenType.RoundBracketClose:
num--;
break;
case CssTokenType.Function:
case CssTokenType.RoundBracketOpen:
num++;
break;
case CssTokenType.SquareBracketClose:
num3--;
break;
case CssTokenType.SquareBracketOpen:
num3++;
break;
case CssTokenType.Semicolon:
if (num <= 0 && num2 <= 0 && num3 <= 0)
return;
break;
}
} while (tokens.MoveNext());
}
private static void JumpToNextSemicolon(IEnumerator<CssToken> tokens)
{
int num = 0;
int num2 = 0;
int num3 = 0;
do {
switch (tokens.Current.Type) {
case CssTokenType.CurlyBracketClose:
num2--;
break;
case CssTokenType.CurlyBracketOpen:
num2++;
break;
case CssTokenType.RoundBracketClose:
num--;
break;
case CssTokenType.Function:
case CssTokenType.RoundBracketOpen:
num++;
break;
case CssTokenType.SquareBracketClose:
num3--;
break;
case CssTokenType.SquareBracketOpen:
num3++;
break;
case CssTokenType.Semicolon:
if (num <= 0 && num2 <= 0 && num3 <= 0)
return;
break;
}
} while (tokens.MoveNext());
}
private static void JumpToClosedArguments(IEnumerator<CssToken> tokens)
{
int num = 0;
int num2 = 0;
int num3 = 0;
do {
switch (tokens.Current.Type) {
case CssTokenType.CurlyBracketClose:
num2--;
break;
case CssTokenType.CurlyBracketOpen:
num2++;
break;
case CssTokenType.RoundBracketClose:
if (num <= 0 && num2 <= 0 && num3 <= 0)
return;
num--;
break;
case CssTokenType.Function:
case CssTokenType.RoundBracketOpen:
num++;
break;
case CssTokenType.SquareBracketClose:
num3--;
break;
case CssTokenType.SquareBracketOpen:
num3++;
break;
}
} while (tokens.MoveNext());
}
private static void SkipUnknownRule(IEnumerator<CssToken> tokens)
{
int num = 0;
int num2 = 0;
int num3 = 0;
bool flag = true;
do {
CssToken current = tokens.Current;
switch (current.Type) {
case CssTokenType.Semicolon:
flag = (num != 0 || num2 != 0 || num3 != 0);
break;
case CssTokenType.CurlyBracketClose:
num--;
flag = (num != 0 || num2 != 0 || num3 != 0);
break;
case CssTokenType.RoundBracketOpen:
num2++;
break;
case CssTokenType.RoundBracketClose:
num2--;
break;
case CssTokenType.SquareBracketClose:
num3--;
break;
case CssTokenType.SquareBracketOpen:
num3++;
break;
case CssTokenType.CurlyBracketOpen:
num++;
break;
}
} while (flag && tokens.MoveNext());
}
private static ICssObject ToUnit(CssUnitToken token)
{
if (token.Type == CssTokenType.Percentage)
return new Percent(token.Value);
return CssUnitFactory.Create(token.Unit.ToLowerInvariant(), token.Value);
}
private static ICssObject ToIdentifier(string identifier)
{
if (identifier.Equals(Keywords.Inherit, StringComparison.OrdinalIgnoreCase))
return CSSValue.Inherit;
if (identifier.Equals(Keywords.Initial, StringComparison.OrdinalIgnoreCase))
return CSSValue.Initial;
return new CssIdentifier(identifier);
}
private static Number ToNumber(CssNumberToken token)
{
if (token.Value == 0)
return Number.Zero;
return new Number(token.Value);
}
public static ISelector ParseSelector(string selector, IConfiguration configuration = null)
{
TextSource source = new TextSource(selector);
CssTokenizer cssTokenizer = new CssTokenizer(source);
cssTokenizer.IgnoreComments = true;
IEnumerable<CssToken> tokens = cssTokenizer.Tokens;
CssSelectorConstructor cssSelectorConstructor = Pool.NewSelectorConstructor();
foreach (CssToken item in tokens) {
if (!cssSelectorConstructor.Apply(item))
return null;
}
return cssSelectorConstructor.ToPool();
}
public static ICssStyleSheet ParseStyleSheet(string stylesheet, IConfiguration configuration = null)
{
return new CssParser(stylesheet, configuration ?? Configuration.Default).Parse();
}
internal static CSSRule ParseRule(string rule, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(rule, configuration ?? Configuration.Default);
cssParser.Parse();
if (cssParser.sheet.Rules.Length == 0)
return null;
return cssParser.sheet.Rules[0];
}
internal static CSSStyleDeclaration ParseDeclarations(string declarations, IConfiguration configuration = null)
{
CSSStyleDeclaration cSSStyleDeclaration = new CSSStyleDeclaration((string)null);
AppendDeclarations(cSSStyleDeclaration, declarations, configuration);
return cSSStyleDeclaration;
}
internal static CSSProperty ParseDeclaration(string declaration, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(declaration, configuration ?? Configuration.Default);
IEnumerator<CssToken> enumerator = cssParser.tokenizer.Tokens.GetEnumerator();
if (!enumerator.MoveNext())
return null;
return cssParser.Declaration(enumerator, new CSSStyleDeclaration((string)null));
}
internal static CSSValue ParseValue(string source, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(source, configuration ?? Configuration.Default);
IEnumerator<CssToken> enumerator = cssParser.tokenizer.Tokens.GetEnumerator();
if (!enumerator.MoveNext())
return null;
return cssParser.InValue(enumerator);
}
internal static IEnumerable<CSSMedium> ParseMediaList(string source, IConfiguration configuration = null)
{
CssParser parser = new CssParser(source, configuration);
IEnumerator<CssToken> tokens = parser.tokenizer.Tokens.GetEnumerator();
if (tokens.MoveNext()) {
do {
CSSMedium medium = parser.InMediaValue(tokens);
if (medium == null)
break;
yield return medium;
} while (tokens.MoveNext());
}
if (tokens.MoveNext())
throw new DomException(ErrorCode.Syntax);
}
internal static CSSValueList ParseValueList(string source, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(source, configuration);
IEnumerator<CssToken> enumerator = cssParser.tokenizer.Tokens.GetEnumerator();
CSSValue cSSValue = enumerator.MoveNext() ? cssParser.InValue(enumerator) : null;
CSSValueList cSSValueList = cSSValue as CSSValueList;
if (cSSValueList == null) {
cSSValueList = new CSSValueList();
if (cSSValue != null)
cSSValueList.Add(cSSValue);
}
for (int i = 0; i < cSSValueList.Length; i++) {
if (cSSValueList[i] == CSSValue.Separator) {
for (int num = cSSValueList.Length - 1; num >= i; num--) {
cSSValueList.Remove(cSSValueList[num]);
}
break;
}
}
return cSSValueList;
}
internal static CSSMedium ParseMedium(string source, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(source, configuration);
IEnumerator<CssToken> enumerator = cssParser.tokenizer.Tokens.GetEnumerator();
if (enumerator.MoveNext()) {
CSSMedium result = cssParser.InMediaValue(enumerator);
if (enumerator.MoveNext())
throw new DomException(ErrorCode.Syntax);
return result;
}
return null;
}
internal static List<CSSValueList> ParseMultipleValues(string source, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(source, configuration);
IEnumerator<CssToken> enumerator = cssParser.tokenizer.Tokens.GetEnumerator();
CSSValue cSSValue = enumerator.MoveNext() ? cssParser.InValue(enumerator) : new CSSValueList();
CSSValueList cSSValueList = cSSValue as CSSValueList;
if (cSSValueList == null) {
cSSValueList = new CSSValueList();
if (cSSValue != null)
cSSValueList.Add(cSSValue);
}
return cSSValueList.ToList();
}
internal static CSSKeyframeRule ParseKeyframeRule(string rule, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(rule, configuration);
IEnumerator<CssToken> enumerator = cssParser.tokenizer.Tokens.GetEnumerator();
if (!enumerator.MoveNext())
return new CSSKeyframeRule();
return cssParser.CreateKeyframeRule(enumerator);
}
internal static void AppendDeclarations(CSSStyleDeclaration list, string declarations, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(declarations, configuration ?? Configuration.Default);
IEnumerator<CssToken> enumerator = cssParser.tokenizer.Tokens.GetEnumerator();
cssParser.FillDeclarations(list, enumerator);
}
private void RaiseErrorOccurred(ErrorCode code)
{
tokenizer.RaiseErrorOccurred(code);
}
}
}