AngleSharp by Florian Rappl

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

 CssParser

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