/// <summary>
        /// Parse CSS from a text provider
        /// </summary>
        /// <param name="text">Text provider that supplies text</param>
        /// <param name="insertComments">True stylesheet should include comment items</param>
        /// <returns>Stylesheet object</returns>
        public StyleSheet Parse(ITextProvider text, bool insertComments)
        {
            DateTime startTime = DateTime.UtcNow;

            ICssTokenizer tokenizer    = CreateTokenizer();
            ITextProvider textProvider = text ?? new StringTextProvider(string.Empty);
            TokenList     tokens       = tokenizer.Tokenize(textProvider, 0, textProvider.Length, keepWhiteSpace: false);

            LastTokenizeMilliseconds = (int)(DateTime.UtcNow - startTime).TotalMilliseconds;

            return(Parse(text, tokens, insertComments));
        }
        /// <summary>
        /// Internal function to parse a new StyleSheet object
        /// </summary>
        private void ParseNewStyleSheet(ITextProvider textProvider, TokenList tokens)
        {
            ICssParser parser = _parserFactory.CreateParser();

            if (tokens == null)
            {
                ICssTokenizer tokenizer = parser.TokenizerFactory.CreateTokenizer();
                tokens = tokenizer.Tokenize(textProvider, 0, textProvider.Length, keepWhiteSpace: false);
            }

            using (CreateWriteLock())
            {
                Tokens     = tokens;
                StyleSheet = parser.Parse(textProvider, tokens, insertComments: true);
            }

            TreeUpdated?.Invoke(this, new CssTreeUpdateEventArgs(this));
        }
        internal InlineStyle ParseInlineStyle(ITextProvider text, bool insertComments)
        {
            InlineStyle inlineStyle = new InlineStyle();

            ICssTokenizer tokenizer    = CreateTokenizer();
            ITextProvider textProvider = text ?? new StringTextProvider(string.Empty);
            TokenList     tokens       = tokenizer.Tokenize(textProvider, 0, textProvider.Length, keepWhiteSpace: false);

            IList <Comment> comments    = ExtractComments(text, tokens, 0, tokens.Count);
            TokenStream     tokenStream = new TokenStream(tokens)
            {
                SkipComments = true
            };

            ItemFactory itemFactory = new ItemFactory(ExternalItemFactory, textProvider, tokenStream);

            if (!inlineStyle.Parse(itemFactory, textProvider, tokenStream))
            {
                inlineStyle = null;
            }
            else
            {
                if (insertComments)
                {
                    foreach (ParseItem comment in comments)
                    {
                        inlineStyle.InsertChildIntoSubtree(comment);
                    }
                }

                // There must be a StyleSheet root object, so create one
                StyleSheet styleSheet = new StyleSheet
                {
                    TextProvider = textProvider
                };

                styleSheet.Children.Add(inlineStyle);
            }

            return(inlineStyle);
        }
        private void VerifyTokensAfterIncrementalChange(ICssTokenizerFactory tokenizerFactory, ITextProvider newText, TokenList newTokens)
        {
            ICssTokenizer tokenizer   = tokenizerFactory.CreateTokenizer();
            TokenList     validTokens = tokenizer.Tokenize(newText, 0, newText.Length, keepWhiteSpace: false);

            if (validTokens.Count == newTokens.Count)
            {
                for (int i = 0; i < validTokens.Count && i < newTokens.Count; i++)
                {
                    if (!CssToken.CompareTokens(validTokens[i], newTokens[i], newText, newText))
                    {
                        Debug.Fail("The CssTree.Tokens list is bad");
                        break;
                    }
                }
            }
            else
            {
                Debug.Fail("The CssTree.Tokens list is bad, wrong number of tokens");
            }
        }