CssParser
The CSS parser.
See http://dev.w3.org/csswg/css-syntax/#parsing for more details.
using AngleSharp.DOM;
using AngleSharp.DOM.Collections;
using AngleSharp.DOM.Css;
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 : IParser
{
private delegate CSSRule Creator (CssParser parser, IEnumerator<CssToken> tokens);
internal static readonly string Important;
internal static readonly string Inherit;
private readonly CssSelectorConstructor selector;
private readonly CssValueBuilder value;
private readonly CssTokenizer tokenizer;
private readonly object sync;
private readonly CSSStyleSheet sheet;
private bool started;
private bool quirks;
private Task task;
private static readonly Dictionary<string, Creator> creators;
public bool IsAsync => task != null;
public ICssStyleSheet Result {
get {
Parse();
return sheet;
}
}
public bool IsQuirksMode => quirks;
public event EventHandler<ParseErrorEventArgs> ParseError;
static CssParser()
{
Important = "important";
Inherit = "inherit";
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 {
Options = configuration
}, new TextSource(source, configuration.DefaultEncoding()))
{
}
public CssParser(Stream stream, IConfiguration configuration = null)
: this(new CSSStyleSheet {
Options = configuration
}, new TextSource(stream, configuration.DefaultEncoding()))
{
}
internal CssParser(CSSStyleSheet stylesheet, string source)
: this(stylesheet, new TextSource(source, stylesheet.Options.DefaultEncoding()))
{
}
internal CssParser(CSSStyleSheet stylesheet, Stream stream)
: this(stylesheet, new TextSource(stream, stylesheet.Options.DefaultEncoding()))
{
}
internal CssParser(CSSStyleSheet stylesheet, TextSource source)
{
selector = new CssSelectorConstructor();
value = new CssValueBuilder();
sync = new object();
tokenizer = new CssTokenizer(source);
tokenizer.IgnoreComments = true;
tokenizer.IgnoreWhitespace = true;
CssTokenizer cssTokenizer = tokenizer;
EventHandler<ParseErrorEventArgs> eventHandler = delegate(object s, ParseErrorEventArgs ev) {
if (this.ParseError != null)
this.ParseError(this, ev);
};
cssTokenizer.ErrorOccurred += eventHandler;
quirks = stylesheet.Options.UseQuirksMode;
started = false;
sheet = stylesheet;
}
public Task ParseAsync()
{
return ParseAsync(CancellationToken.None);
}
public Task ParseAsync(CancellationToken cancelToken)
{
lock (sync) {
if (!started) {
started = true;
task = KernelAsync(cancelToken);
}
}
return task;
}
public void Parse()
{
if (!started) {
started = true;
Kernel();
}
}
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)
{
CSSStyleDeclaration style = new CSSStyleDeclaration();
CSSPageRule cSSPageRule = new CSSPageRule(style);
if (tokens.MoveNext())
cSSPageRule.Selector = parser.InSelector(tokens);
if (tokens.Current.Type == CssTokenType.CurlyBracketOpen)
parser.FillDeclarations(style, tokens);
return cSSPageRule;
}
private static CSSFontFaceRule CreateFontFaceRule(CssParser parser, IEnumerator<CssToken> tokens)
{
CSSStyleDeclaration style = new CSSStyleDeclaration();
if (tokens.Current.Type == CssTokenType.CurlyBracketOpen)
parser.FillDeclarations(style, tokens);
return new CSSFontFaceRule(style);
}
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 = ((CssKeywordToken)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;
if (current.Type == CssTokenType.AtKeyword) {
if (creators.TryGetValue(((CssKeywordToken)current).Data, out Creator creator))
return creator(this, tokens);
SkipUnknownRule(tokens);
return null;
}
if (current.Type == CssTokenType.CurlyBracketClose || current.Type == CssTokenType.RoundBracketClose || current.Type == CssTokenType.SquareBracketClose) {
while (tokens.MoveNext()) {
}
return null;
}
ISelector selector = InSelector(tokens);
if (selector == null) {
SkipUnknownRule(tokens);
return null;
}
CSSStyleDeclaration style = new CSSStyleDeclaration();
FillDeclarations(style, tokens);
CSSStyleRule cSSStyleRule = new CSSStyleRule(style);
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());
return selector.Result;
}
private CSSProperty Declaration(CSSStyleDeclaration style, IEnumerator<CssToken> tokens)
{
CssToken current = tokens.Current;
if (current.Type == CssTokenType.Ident) {
string data = ((CssKeywordToken)current).Data;
if (!tokens.MoveNext())
return null;
current = tokens.Current;
if (current.Type != CssTokenType.Colon) {
JumpToEndOfDeclaration(tokens);
return null;
}
if (tokens.MoveNext()) {
CSSProperty cSSProperty = CssPropertyFactory.Create(data, style);
if (cSSProperty != null) {
if (cSSProperty.TrySetValue(InValue(tokens)))
cSSProperty.IsImportant = IsImportant(tokens);
else if (style != null) {
cSSProperty = null;
}
}
JumpToEndOfDeclaration(tokens);
return cSSProperty;
}
}
return null;
}
private bool IsImportant(IEnumerator<CssToken> tokens)
{
CssToken current = tokens.Current;
if (current.Type == CssTokenType.Ident)
return ((CssKeywordToken)current).Data == 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(((CssKeywordToken)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 ((CssKeywordToken)current).Data;
}
return string.Empty;
}
private CSSKeyframeRule CreateKeyframeRule(IEnumerator<CssToken> tokens)
{
CSSStyleDeclaration style = new CSSStyleDeclaration();
CSSKeyframeRule cSSKeyframeRule = new CSSKeyframeRule(style);
cSSKeyframeRule.KeyText = InKeyframeText(tokens);
FillDeclarations(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();
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 = ((CssKeywordToken)current).Data;
if (!tokens.MoveNext())
return medium;
current = tokens.Current;
if (current.Type != CssTokenType.Ident || string.Compare(((CssKeywordToken)current).Data, "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(((CssKeywordToken)current).Data, "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 = ((CssKeywordToken)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 = ((CssKeywordToken)current).Data;
if (string.Compare(data, "not", StringComparison.OrdinalIgnoreCase) == 0) {
tokens.MoveNext();
cSSMedium.IsInverse = true;
} else if (string.Compare(data, "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(GetColorFromHexValue(((CssKeywordToken)current).Data), tokens);
case CssTokenType.Delim:
return GetValueFromDelim(((CssDelimToken)current).Data, tokens);
case CssTokenType.Ident:
value.AddValue(ToIdentifier(((CssKeywordToken)current).Data));
return tokens.MoveNext();
case CssTokenType.String:
value.AddValue(new CSSStringValue(((CssStringToken)current).Data));
return tokens.MoveNext();
case CssTokenType.Url:
value.AddValue(new CSSPrimitiveValue<Url>(new Url(((CssStringToken)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(CSSValue 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());
CSSPrimitiveValue<Color> colorFromHexValue = GetColorFromHexValue(stringBuilder.ToPool());
if (colorFromHexValue != null)
value.AddValue(colorFromHexValue);
return result;
}
private static CSSPrimitiveValue<Color> GetColorFromHexValue(string hexColor)
{
if (Color.TryFromHex(hexColor, out Color color))
return new CSSPrimitiveValue<Color>(color);
return null;
}
private void Kernel()
{
IEnumerator<CssToken> enumerator = tokenizer.Tokens.GetEnumerator();
while (enumerator.MoveNext()) {
CSSRule cSSRule = CreateRule(enumerator);
if (cSSRule != null) {
cSSRule.Owner = sheet;
sheet.AddRule(cSSRule);
}
}
}
private Task KernelAsync(CancellationToken cancelToken)
{
return Task.Factory.StartNew(delegate {
IEnumerator<CssToken> enumerator = tokenizer.Tokens.GetEnumerator();
while (enumerator.MoveNext()) {
CSSRule cSSRule = CreateRule(enumerator);
if (cSSRule != null) {
cSSRule.Owner = sheet;
sheet.AddRule(cSSRule);
}
}
});
}
private void FillRules(CSSGroupingRule parentRule, IEnumerator<CssToken> tokens)
{
while (tokens.MoveNext() && tokens.Current.Type != CssTokenType.CurlyBracketClose) {
CSSRule cSSRule = CreateRule(tokens);
if (cSSRule != null) {
cSSRule.Owner = sheet;
cSSRule.Parent = parentRule;
parentRule.AddRule(cSSRule);
}
}
}
private void FillRules(CSSKeyframesRule parentRule, IEnumerator<CssToken> tokens)
{
while (tokens.MoveNext() && tokens.Current.Type != CssTokenType.CurlyBracketClose) {
CSSKeyframeRule cSSKeyframeRule = CreateKeyframeRule(tokens);
if (cSSKeyframeRule != null) {
cSSKeyframeRule.Owner = sheet;
cSSKeyframeRule.Parent = parentRule;
parentRule.AddRule(cSSKeyframeRule);
}
}
}
private void FillDeclarations(CSSStyleDeclaration style, IEnumerator<CssToken> tokens)
{
while (tokens.MoveNext() && tokens.Current.Type != CssTokenType.CurlyBracketClose) {
CSSProperty cSSProperty = Declaration(style, tokens);
if (cSSProperty != null)
style.Set(cSSProperty);
}
}
private static void JumpToEndOfDeclaration(IEnumerator<CssToken> tokens)
{
int num = 0;
int num2 = 0;
int num3 = 0;
do {
switch (tokens.Current.Type) {
case CssTokenType.CurlyBracketClose:
num2--;
goto case CssTokenType.Semicolon;
case CssTokenType.CurlyBracketOpen:
num2++;
break;
case CssTokenType.RoundBracketClose:
num--;
break;
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.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.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 CSSValue ToUnit(CssUnitToken token)
{
if (token.Type != CssTokenType.Percentage) {
switch (token.Unit.ToLower()) {
case "em":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Em));
case "cm":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Cm));
case "ex":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Ex));
case "in":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.In));
case "mm":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Mm));
case "pc":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Pc));
case "pt":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Pt));
case "px":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Px));
case "rem":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Rem));
case "ch":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Ch));
case "vw":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Vw));
case "vh":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Vh));
case "vmin":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Vmin));
case "vmax":
return new CSSPrimitiveValue<Length>(new Length(token.Data, Length.Unit.Vmax));
case "ms":
return new CSSPrimitiveValue<Time>(new Time(token.Data, Time.Unit.Ms));
case "s":
return new CSSPrimitiveValue<Time>(new Time(token.Data, Time.Unit.S));
case "dpi":
return new CSSPrimitiveValue<Resolution>(new Resolution(token.Data, Resolution.Unit.Dpi));
case "dpcm":
return new CSSPrimitiveValue<Resolution>(new Resolution(token.Data, Resolution.Unit.Dpcm));
case "dppx":
return new CSSPrimitiveValue<Resolution>(new Resolution(token.Data, Resolution.Unit.Dppx));
case "deg":
return new CSSPrimitiveValue<Angle>(new Angle(token.Data, Angle.Unit.Deg));
case "grad":
return new CSSPrimitiveValue<Angle>(new Angle(token.Data, Angle.Unit.Grad));
case "rad":
return new CSSPrimitiveValue<Angle>(new Angle(token.Data, Angle.Unit.Rad));
case "turn":
return new CSSPrimitiveValue<Angle>(new Angle(token.Data, Angle.Unit.Turn));
case "hz":
return new CSSPrimitiveValue<Frequency>(new Frequency(token.Data, Frequency.Unit.Hz));
case "khz":
return new CSSPrimitiveValue<Frequency>(new Frequency(token.Data, Frequency.Unit.Khz));
default:
return null;
}
}
return new CSSPrimitiveValue<Percent>(new Percent(token.Data));
}
private static CSSValue ToIdentifier(string identifier)
{
if (identifier == Inherit)
return CSSValue.Inherit;
return new CSSIdentifierValue(identifier);
}
private static CSSValue ToNumber(CssNumberToken token)
{
if (token.Data == 0)
return new CSSPrimitiveValue<Number>(Number.Zero);
return new CSSPrimitiveValue<Number>(new Number(token.Data));
}
public static ISelector ParseSelector(string selector, IConfiguration configuration = null)
{
TextSource source = new TextSource(selector, configuration.DefaultEncoding());
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))
throw new DomException(ErrorCode.Syntax);
}
return cssSelectorConstructor.ToPool();
}
public static ICssStyleSheet ParseStyleSheet(string stylesheet, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(stylesheet, configuration ?? Configuration.Default);
return cssParser.Result;
}
public static ICssRule 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();
AppendDeclarations(cSSStyleDeclaration, declarations, configuration);
return cSSStyleDeclaration;
}
public 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(null, enumerator);
}
public 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);
}
public 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)
{
if (this.ParseError != null) {
ParseErrorEventArgs parseErrorEventArgs = new ParseErrorEventArgs((int)code, code.GetErrorMessage());
parseErrorEventArgs.Line = tokenizer.Line;
parseErrorEventArgs.Column = tokenizer.Column;
this.ParseError(this, parseErrorEventArgs);
}
}
}
}