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.Tasks;
namespace AngleSharp.Parser.Css
{
[DebuggerStepThrough]
public sealed class CssParser : IParser
{
private enum CssState
{
Data,
InSelector,
InDeclaration,
AfterProperty,
BeforeValue,
InValuePool,
InValueList,
InSingleValue,
InMediaList,
InMediaValue,
BeforeImport,
BeforeCharset,
BeforeNamespacePrefix,
AfterNamespacePrefix,
AfterInstruction,
InCondition,
BeforeKeyframesName,
BeforeKeyframesData,
KeyframesData,
InKeyframeText,
BeforeDocumentFunction,
InDocumentFunction,
AfterDocumentFunction,
BetweenDocumentFunctions,
InUnknown,
ValueImportant,
AfterValue,
InHexValue,
InFunction
}
private CssSelectorConstructor selector;
private CssValueBuilder value;
private bool skipExceptions;
private CssTokenizer tokenizer;
private CSSProperty property;
private bool started;
private bool quirks;
private CSSStyleSheet sheet;
private Stack<CSSRule> open;
private StringBuilder buffer;
private CssState state;
private object sync;
private Task task;
public bool IsAsync => task != null;
public CSSStyleSheet Result {
get {
Parse();
return sheet;
}
}
public bool IsQuirksMode => quirks;
internal CSSRule CurrentRule {
get {
if (open.Count <= 0)
return null;
return open.Peek();
}
}
public event EventHandler<ParseErrorEventArgs> ParseError;
public CssParser(string source, IConfiguration configuration = null)
: this(new CSSStyleSheet(), new SourceManager(source, configuration))
{
}
public CssParser(Stream stream, IConfiguration configuration = null)
: this(new CSSStyleSheet(), new SourceManager(stream, configuration))
{
}
public CssParser(CSSStyleSheet stylesheet, string source)
: this(stylesheet, new SourceManager(source, stylesheet.Options))
{
}
public CssParser(CSSStyleSheet stylesheet, Stream stream)
: this(stylesheet, new SourceManager(stream, stylesheet.Options))
{
}
internal CssParser(CSSStyleSheet stylesheet, SourceManager source)
{
selector = Pool.NewSelectorConstructor();
value = new CssValueBuilder();
sync = new object();
skipExceptions = true;
tokenizer = new CssTokenizer(source);
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;
open = new Stack<CSSRule>();
SwitchTo(CssState.Data);
}
public Task ParseAsync()
{
lock (sync) {
if (!started) {
started = true;
task = Task.Factory.StartNew(delegate {
Kernel();
});
} else if (task == null) {
throw new InvalidOperationException("The parser has already run synchronously.");
}
return task;
}
}
public void Parse()
{
bool flag = false;
lock (sync) {
if (!started) {
started = true;
flag = true;
}
}
if (flag)
Kernel();
}
private bool Data(CssToken token)
{
if (token.Type == CssTokenType.AtKeyword) {
switch (((CssKeywordToken)token).Data) {
case "media":
AddRule(new CSSMediaRule());
SwitchTo(CssState.InMediaList);
break;
case "page":
AddRule(new CSSPageRule());
SwitchTo(CssState.InSelector);
break;
case "import":
AddRule(new CSSImportRule());
SwitchTo(CssState.BeforeImport);
break;
case "font-face":
AddRule(new CSSFontFaceRule());
SwitchTo(CssState.InDeclaration);
break;
case "charset":
AddRule(new CSSCharsetRule());
SwitchTo(CssState.BeforeCharset);
break;
case "namespace":
AddRule(new CSSNamespaceRule());
SwitchTo(CssState.BeforeNamespacePrefix);
break;
case "supports":
buffer = Pool.NewStringBuilder();
AddRule(new CSSSupportsRule());
SwitchTo(CssState.InCondition);
break;
case "keyframes":
AddRule(new CSSKeyframesRule());
SwitchTo(CssState.BeforeKeyframesName);
break;
case "document":
AddRule(new CSSDocumentRule());
SwitchTo(CssState.BeforeDocumentFunction);
break;
default:
buffer = Pool.NewStringBuilder();
AddRule(new CSSUnknownRule());
SwitchTo(CssState.InUnknown);
InUnknown(token);
break;
}
return true;
}
if (token.Type == CssTokenType.CurlyBracketClose)
return CloseRule();
AddRule(new CSSStyleRule());
SwitchTo(CssState.InSelector);
InSelector(token);
return true;
}
private bool InUnknown(CssToken token)
{
switch (token.Type) {
case CssTokenType.Semicolon:
CurrentRuleAs<CSSUnknownRule>().SetInstruction(buffer.ToPool());
SwitchTo(CssState.Data);
return CloseRule();
case CssTokenType.CurlyBracketOpen:
CurrentRuleAs<CSSUnknownRule>().SetCondition(buffer.ToPool());
SwitchTo(CssState.Data);
break;
default:
buffer.Append(token.ToValue());
break;
}
return true;
}
private bool InSelector(CssToken token)
{
if (token.Type == CssTokenType.CurlyBracketOpen) {
ICssSelector cssSelector = CurrentRule as ICssSelector;
if (cssSelector != null)
cssSelector.Selector = selector.Result;
SwitchTo((CurrentRule is CSSStyleRule) ? CssState.InDeclaration : CssState.Data);
} else {
if (token.Type == CssTokenType.CurlyBracketClose)
return false;
selector.Apply(token);
}
return true;
}
private bool InDeclaration(CssToken token)
{
if (token.Type == CssTokenType.CurlyBracketClose) {
CloseProperty();
SwitchTo((CurrentRule is CSSKeyframeRule) ? CssState.KeyframesData : CssState.Data);
return CloseRule();
}
if (token.Type == CssTokenType.Ident) {
AddDeclaration(((CssKeywordToken)token).Data);
SwitchTo(CssState.AfterProperty);
return true;
}
return false;
}
private bool AfterInstruction(CssToken token)
{
if (token.Type == CssTokenType.Semicolon) {
SwitchTo(CssState.Data);
return CloseRule();
}
return false;
}
private bool InCondition(CssToken token)
{
CssTokenType type = token.Type;
if (type == CssTokenType.CurlyBracketOpen) {
CurrentRuleAs<CSSSupportsRule>().ConditionText = buffer.ToPool();
SwitchTo(CssState.Data);
} else
buffer.Append(token.ToValue());
return true;
}
private bool BeforePrefix(CssToken token)
{
if (token.Type == CssTokenType.Ident) {
CurrentRuleAs<CSSNamespaceRule>().Prefix = ((CssKeywordToken)token).Data;
SwitchTo(CssState.AfterNamespacePrefix);
return true;
}
SwitchTo(CssState.AfterInstruction);
return AfterInstruction(token);
}
private bool BeforeNamespace(CssToken token)
{
SwitchTo(CssState.AfterInstruction);
if (token.Type == CssTokenType.String) {
CurrentRuleAs<CSSNamespaceRule>().NamespaceURI = ((CssStringToken)token).Data;
return true;
}
return AfterInstruction(token);
}
private bool BeforeCharset(CssToken token)
{
SwitchTo(CssState.AfterInstruction);
if (token.Type == CssTokenType.String) {
CurrentRuleAs<CSSCharsetRule>().Encoding = ((CssStringToken)token).Data;
return true;
}
return AfterInstruction(token);
}
private bool BeforeImport(CssToken token)
{
if (token.Type == CssTokenType.String || token.Type == CssTokenType.Url) {
CurrentRuleAs<CSSImportRule>().Href = ((CssStringToken)token).Data;
SwitchTo(CssState.InMediaList);
return true;
}
SwitchTo(CssState.AfterInstruction);
return false;
}
private bool AfterProperty(CssToken token)
{
if (token.Type == CssTokenType.Colon) {
value.Reset();
SwitchTo(CssState.BeforeValue);
return true;
}
if (token.Type == CssTokenType.Semicolon || token.Type == CssTokenType.CurlyBracketClose)
AfterValue(token);
return false;
}
private bool BeforeValue(CssToken token)
{
if (token.Type == CssTokenType.Semicolon)
SwitchTo(CssState.InDeclaration);
else {
if (token.Type != CssTokenType.CurlyBracketClose) {
SwitchTo(CssState.InSingleValue);
return InSingleValue(token);
}
InDeclaration(token);
}
return false;
}
private bool InSingleValue(CssToken token)
{
switch (token.Type) {
case CssTokenType.Percentage:
case CssTokenType.Dimension:
value.AddValue(ToUnit((CssUnitToken)token));
return true;
case CssTokenType.Hash:
return InSingleValueHexColor(((CssKeywordToken)token).Data);
case CssTokenType.Delim:
return InSingleValueDelim((CssDelimToken)token);
case CssTokenType.Ident:
return InSingleValueIdent((CssKeywordToken)token);
case CssTokenType.String:
value.AddValue(new CSSStringValue(((CssStringToken)token).Data));
return true;
case CssTokenType.Url:
value.AddValue(new CSSPrimitiveValue<Location>(new Location(((CssStringToken)token).Data)));
return true;
case CssTokenType.Number:
value.AddValue(ToNumber((CssNumberToken)token));
return true;
case CssTokenType.Whitespace:
SwitchTo(CssState.InValueList);
return true;
case CssTokenType.Function:
value.AddFunction(((CssKeywordToken)token).Data);
SwitchTo(CssState.InFunction);
return true;
case CssTokenType.Comma:
SwitchTo(CssState.InValuePool);
return true;
case CssTokenType.CurlyBracketClose:
case CssTokenType.Semicolon:
return AfterValue(token);
default:
return false;
}
}
private bool InValueFunction(CssToken token)
{
switch (token.Type) {
case CssTokenType.RoundBracketClose:
if (value.CloseFunction())
SwitchTo(CssState.InSingleValue);
return true;
case CssTokenType.Comma:
value.NextArgument();
return true;
default:
return InSingleValue(token);
}
}
private bool InValueList(CssToken token)
{
if (token.Type == CssTokenType.Semicolon || token.Type == CssTokenType.CurlyBracketClose)
AfterValue(token);
else {
if (token.Type != CssTokenType.Comma) {
SwitchTo(CssState.InSingleValue);
return InSingleValue(token);
}
SwitchTo(CssState.InValuePool);
}
return true;
}
private bool InValuePool(CssToken token)
{
if (token.Type != CssTokenType.Semicolon && token.Type != CssTokenType.CurlyBracketClose) {
value.NextArgument();
SwitchTo(CssState.InSingleValue);
return InSingleValue(token);
}
AfterValue(token);
return false;
}
private bool InHexValue(CssToken token)
{
switch (token.Type) {
case CssTokenType.Ident:
case CssTokenType.Number:
case CssTokenType.Dimension: {
string text = token.ToValue();
if (buffer.Length + text.Length <= 6) {
buffer.Append(text);
return true;
}
break;
}
}
buffer.ToPool();
InSingleValueHexColor(buffer.ToString());
SwitchTo(CssState.InSingleValue);
return InSingleValue(token);
}
private bool AfterValue(CssToken token)
{
if (token.Type == CssTokenType.Semicolon) {
CloseProperty();
SwitchTo(CssState.InDeclaration);
return true;
}
if (token.Type == CssTokenType.CurlyBracketClose)
return InDeclaration(token);
return false;
}
private bool ValueImportant(CssToken token)
{
if (token.Type == CssTokenType.Ident && ((CssKeywordToken)token).Data == "important") {
SwitchTo(CssState.AfterValue);
property.Important = true;
return true;
}
return AfterValue(token);
}
private bool BeforeKeyframesName(CssToken token)
{
SwitchTo(CssState.BeforeKeyframesData);
if (token.Type == CssTokenType.Ident) {
CurrentRuleAs<CSSKeyframesRule>().Name = ((CssKeywordToken)token).Data;
return true;
}
if (token.Type == CssTokenType.CurlyBracketOpen)
SwitchTo(CssState.KeyframesData);
return false;
}
private bool BeforeKeyframesData(CssToken token)
{
if (token.Type == CssTokenType.CurlyBracketOpen) {
SwitchTo(CssState.BeforeKeyframesData);
return true;
}
return false;
}
private bool KeyframesData(CssToken token)
{
if (token.Type == CssTokenType.CurlyBracketClose) {
SwitchTo(CssState.Data);
return CloseRule();
}
buffer = Pool.NewStringBuilder();
return InKeyframeText(token);
}
private bool InKeyframeText(CssToken token)
{
if (token.Type == CssTokenType.CurlyBracketOpen) {
CSSKeyframeRule cSSKeyframeRule = new CSSKeyframeRule();
cSSKeyframeRule.KeyText = buffer.ToPool();
AddRule(cSSKeyframeRule);
SwitchTo(CssState.InDeclaration);
return true;
}
if (token.Type == CssTokenType.CurlyBracketClose) {
buffer.ToPool();
KeyframesData(token);
return false;
}
buffer.Append(token.ToValue());
return true;
}
private bool BeforeDocumentFunction(CssToken token)
{
switch (token.Type) {
case CssTokenType.Url:
CurrentRuleAs<CSSDocumentRule>().Conditions.Add(Tuple.Create(CSSDocumentRule.DocumentFunction.Url, ((CssStringToken)token).Data));
break;
case CssTokenType.UrlPrefix:
CurrentRuleAs<CSSDocumentRule>().Conditions.Add(Tuple.Create(CSSDocumentRule.DocumentFunction.UrlPrefix, ((CssStringToken)token).Data));
break;
case CssTokenType.Domain:
CurrentRuleAs<CSSDocumentRule>().Conditions.Add(Tuple.Create(CSSDocumentRule.DocumentFunction.Domain, ((CssStringToken)token).Data));
break;
case CssTokenType.Function:
if (string.Compare(((CssKeywordToken)token).Data, "regexp", StringComparison.OrdinalIgnoreCase) == 0) {
SwitchTo(CssState.InDocumentFunction);
return true;
}
SwitchTo(CssState.AfterDocumentFunction);
return false;
default:
SwitchTo(CssState.Data);
return false;
}
SwitchTo(CssState.BetweenDocumentFunctions);
return true;
}
private bool InDocumentFunction(CssToken token)
{
SwitchTo(CssState.AfterDocumentFunction);
if (token.Type == CssTokenType.String) {
CurrentRuleAs<CSSDocumentRule>().Conditions.Add(Tuple.Create(CSSDocumentRule.DocumentFunction.RegExp, ((CssStringToken)token).Data));
return true;
}
return false;
}
private bool AfterDocumentFunction(CssToken token)
{
SwitchTo(CssState.BetweenDocumentFunctions);
return token.Type == CssTokenType.RoundBracketClose;
}
private bool BetweenDocumentFunctions(CssToken token)
{
if (token.Type == CssTokenType.Comma) {
SwitchTo(CssState.BeforeDocumentFunction);
return true;
}
if (token.Type == CssTokenType.CurlyBracketOpen) {
SwitchTo(CssState.Data);
return true;
}
SwitchTo(CssState.Data);
return false;
}
private bool InMediaList(CssToken token)
{
if (token.Type == CssTokenType.Semicolon) {
CloseRule();
SwitchTo(CssState.Data);
return true;
}
buffer = Pool.NewStringBuilder();
SwitchTo(CssState.InMediaValue);
return InMediaValue(token);
}
private bool InMediaValue(CssToken token)
{
switch (token.Type) {
case CssTokenType.CurlyBracketOpen:
case CssTokenType.Semicolon: {
ICssMedia cssMedia = CurrentRule as ICssMedia;
string newMedium = buffer.ToPool();
cssMedia?.Media.AppendMedium(newMedium);
if (CurrentRule is CSSImportRule)
return AfterInstruction(token);
SwitchTo(CssState.Data);
return token.Type == CssTokenType.CurlyBracketClose;
}
case CssTokenType.Comma:
(CurrentRule as ICssMedia)?.Media.AppendMedium(buffer.ToString());
buffer.Clear();
return true;
case CssTokenType.Whitespace:
buffer.Append(' ');
return true;
default:
buffer.Append(token.ToValue());
return true;
}
}
private bool InSingleValueDelim(CssDelimToken token)
{
switch (token.Data) {
case '!':
SwitchTo(CssState.ValueImportant);
return true;
case '#':
buffer = Pool.NewStringBuilder();
SwitchTo(CssState.InHexValue);
return true;
case '/':
value.InsertDelimiter();
return true;
default:
return false;
}
}
private bool InSingleValueIdent(CssKeywordToken token)
{
if (token.Data == "inherit") {
property.Value = CSSValue.Inherit;
SwitchTo(CssState.AfterValue);
return true;
}
value.AddValue(new CSSIdentifierValue(token.Data));
return true;
}
private bool InSingleValueHexColor(string color)
{
if (Color.TryFromHex(color, out Color color2)) {
value.AddValue(new CSSPrimitiveValue<Color>(color2));
return true;
}
return false;
}
private void CloseProperty()
{
if (property != null) {
property.Value = value.ToValue();
property = null;
}
}
private bool CloseRule()
{
if (open.Count > 0) {
open.Pop();
return true;
}
return false;
}
private void AddRule(CSSRule rule)
{
rule.ParentStyleSheet = sheet;
if (open.Count > 0) {
ICssRules cssRules = open.Peek() as ICssRules;
if (cssRules != null) {
cssRules.CssRules.List.Add(rule);
rule.ParentRule = open.Peek();
}
} else
sheet.CssRules.List.Add(rule);
open.Push(rule);
}
private void AddDeclaration(string propertyName)
{
IStyleDeclaration styleDeclaration = CurrentRule as IStyleDeclaration;
if (styleDeclaration != null) {
property = CSSFactory.Create(propertyName, styleDeclaration.Style);
styleDeclaration.Style.Set(property);
}
}
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 new CSSPrimitiveValue<Number>(Number.Zero);
}
}
return new CSSPrimitiveValue<Percent>(new Percent(token.Data));
}
private static CSSValue ToNumber(CssNumberToken token)
{
if (token.Data == 0)
return new CSSPrimitiveValue<Number>(Number.Zero);
return new CSSPrimitiveValue<Number>(new Number(token.Data));
}
private T CurrentRuleAs<T>() where T : CSSRule
{
if (open.Count > 0)
return open.Peek() as T;
return null;
}
private void SwitchTo(CssState newState)
{
switch (newState) {
case CssState.InSelector:
tokenizer.IgnoreComments = true;
tokenizer.IgnoreWhitespace = false;
selector.Reset();
selector.IgnoreErrors = skipExceptions;
break;
case CssState.InSingleValue:
case CssState.InMediaValue:
case CssState.InCondition:
case CssState.InUnknown:
case CssState.InHexValue:
tokenizer.IgnoreComments = true;
tokenizer.IgnoreWhitespace = false;
break;
default:
tokenizer.IgnoreComments = true;
tokenizer.IgnoreWhitespace = true;
break;
}
state = newState;
}
private void Kernel()
{
IEnumerable<CssToken> tokens = tokenizer.Tokens;
foreach (CssToken item in tokens) {
if (!General(item))
RaiseErrorOccurred(ErrorCode.InputUnexpected);
}
if (property != null)
General(CssSpecialCharacter.Semicolon);
selector.ToPool();
}
private bool General(CssToken token)
{
switch (state) {
case CssState.Data:
return Data(token);
case CssState.InSelector:
return InSelector(token);
case CssState.InDeclaration:
return InDeclaration(token);
case CssState.AfterProperty:
return AfterProperty(token);
case CssState.BeforeValue:
return BeforeValue(token);
case CssState.InValuePool:
return InValuePool(token);
case CssState.InValueList:
return InValueList(token);
case CssState.InSingleValue:
return InSingleValue(token);
case CssState.ValueImportant:
return ValueImportant(token);
case CssState.AfterValue:
return AfterValue(token);
case CssState.InMediaList:
return InMediaList(token);
case CssState.InMediaValue:
return InMediaValue(token);
case CssState.BeforeImport:
return BeforeImport(token);
case CssState.AfterInstruction:
return AfterInstruction(token);
case CssState.BeforeCharset:
return BeforeCharset(token);
case CssState.BeforeNamespacePrefix:
return BeforePrefix(token);
case CssState.AfterNamespacePrefix:
return BeforeNamespace(token);
case CssState.InCondition:
return InCondition(token);
case CssState.InUnknown:
return InUnknown(token);
case CssState.InKeyframeText:
return InKeyframeText(token);
case CssState.BeforeDocumentFunction:
return BeforeDocumentFunction(token);
case CssState.InDocumentFunction:
return InDocumentFunction(token);
case CssState.AfterDocumentFunction:
return AfterDocumentFunction(token);
case CssState.BetweenDocumentFunctions:
return BetweenDocumentFunctions(token);
case CssState.BeforeKeyframesName:
return BeforeKeyframesName(token);
case CssState.BeforeKeyframesData:
return BeforeKeyframesData(token);
case CssState.KeyframesData:
return KeyframesData(token);
case CssState.InHexValue:
return InHexValue(token);
case CssState.InFunction:
return InValueFunction(token);
default:
return false;
}
}
public static Selector ParseSelector(string selector, IConfiguration configuration = null)
{
CssTokenizer cssTokenizer = new CssTokenizer(new SourceManager(selector, configuration ?? Configuration.Default));
IEnumerable<CssToken> tokens = cssTokenizer.Tokens;
CssSelectorConstructor cssSelectorConstructor = Pool.NewSelectorConstructor();
foreach (CssToken item in tokens) {
cssSelectorConstructor.Apply(item);
}
Selector result = cssSelectorConstructor.Result;
cssSelectorConstructor.ToPool();
return result;
}
public static CSSStyleSheet ParseStyleSheet(string stylesheet, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(stylesheet, configuration ?? Configuration.Default);
return cssParser.Result;
}
public static CSSRule ParseRule(string rule, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(rule, configuration ?? Configuration.Default);
cssParser.skipExceptions = false;
cssParser.Parse();
if (cssParser.sheet.CssRules.Length > 0)
return cssParser.sheet.CssRules[0];
return null;
}
public static CSSStyleDeclaration ParseDeclarations(string declarations, IConfiguration configuration = null)
{
CSSStyleDeclaration cSSStyleDeclaration = new CSSStyleDeclaration();
AppendDeclarations(cSSStyleDeclaration, declarations, configuration);
return cSSStyleDeclaration;
}
public static CSSProperty ParseDeclaration(string declarations, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(declarations, configuration ?? Configuration.Default);
CSSStyleRule cSSStyleRule = new CSSStyleRule();
cssParser.AddRule(cSSStyleRule);
cssParser.state = CssState.InDeclaration;
cssParser.skipExceptions = false;
cssParser.Parse();
if (cSSStyleRule.Style.Length == 0)
return null;
return cSSStyleRule.Style.Get(0);
}
public static CSSValue ParseValue(string source, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(source, configuration ?? Configuration.Default);
CSSProperty cSSProperty = cssParser.property = new CSSProperty(string.Empty);
cssParser.skipExceptions = false;
cssParser.state = CssState.BeforeValue;
cssParser.Parse();
return cSSProperty.Value;
}
internal static CSSValueList ParseValueList(string source, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(source, configuration);
CSSProperty cSSProperty = cssParser.property = new CSSProperty(string.Empty);
cssParser.skipExceptions = false;
cssParser.state = CssState.InValueList;
cssParser.Parse();
if (!cSSProperty.HasValue)
return new CSSValueList();
CSSValueList cSSValueList = (cSSProperty.Value as CSSValueList) ?? new CSSValueList(cSSProperty.Value);
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 List<CSSValueList> ParseMultipleValues(string source, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(source, configuration);
CSSProperty cSSProperty = new CSSProperty(string.Empty);
List<CSSValueList> list = new List<CSSValueList>();
cssParser.property = cSSProperty;
cssParser.skipExceptions = false;
cssParser.state = CssState.InValuePool;
cssParser.Parse();
if (cSSProperty.HasValue) {
CSSValueList cSSValueList = (cSSProperty.Value as CSSValueList) ?? new CSSValueList(cSSProperty.Value);
CSSValueList cSSValueList2 = new CSSValueList();
foreach (CSSValue item in cSSValueList) {
if (item == CSSValue.Separator) {
if (cSSValueList2.Length > 0)
list.Add(cSSValueList2);
cSSValueList2 = new CSSValueList();
} else
cSSValueList2.Add(item);
}
if (cSSValueList2.Length > 0)
list.Add(cSSValueList2);
cSSValueList2 = null;
}
return list;
}
internal static CSSKeyframeRule ParseKeyframeRule(string rule, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(rule, configuration);
CSSKeyframeRule cSSKeyframeRule = new CSSKeyframeRule();
cssParser.AddRule(cSSKeyframeRule);
cssParser.skipExceptions = false;
cssParser.state = CssState.InKeyframeText;
cssParser.Parse();
return cSSKeyframeRule;
}
internal static void AppendDeclarations(CSSStyleDeclaration list, string declarations, IConfiguration configuration = null)
{
CssParser cssParser = new CssParser(declarations, configuration ?? Configuration.Default);
cssParser.skipExceptions = false;
if (list.ParentRule != null)
cssParser.AddRule(list.ParentRule);
else
cssParser.AddRule(new CSSStyleRule(list));
cssParser.state = CssState.InDeclaration;
cssParser.Parse();
}
private void RaiseErrorOccurred(ErrorCode code)
{
value.IsFaulted = true;
if (this.ParseError != null) {
ParseErrorEventArgs parseErrorEventArgs = new ParseErrorEventArgs((int)code, Errors.GetError(code));
parseErrorEventArgs.Line = tokenizer.Stream.Line;
parseErrorEventArgs.Column = tokenizer.Stream.Column;
this.ParseError(this, parseErrorEventArgs);
}
}
}
}