AngleSharp by Florian Rappl

<PackageReference Include="AngleSharp" Version="0.6.1" />

 CssParser

public sealed class CssParser : IParser
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); } } } }