コード例 #1
0
        private bool HandleUnits()
        {
            // Makes a token for the units identifier after a number

            int start = CS.Position;

            if (CS.CurrentChar == '%')
            {
                AddToken(CssTokenType.Units, start, 1);
                CS.Advance(1);
                return(true);
            }

            if (SkipIdentifier())
            {
                UnitType unitType = UnitHelpers.GetUnitType(CS.TextProvider, start, CS.Position - start);

                if (unitType != UnitType.Unknown)
                {
                    // only known units after whitespace (CSS2)
                    AddToken(CssTokenType.Units, start, CS.Position - start);
                    return(true);
                }
            }

            // Not a valid unit identifier, go back
            CS.Position = start;

            return(false);
        }
コード例 #2
0
        protected bool SkipName()
        {
            int start = CS.Position;

            while (AtName())
            {
                if (AtEscape())
                {
                    if (AtSlashNineHack())
                    {
                        // Stop the name token at the start of the \9, which allows that CSS workaround to work:
                        //    color: red\9;
                        //    border-width: 50px\9;
                        break;
                    }
                    else
                    {
                        SkipEscape();
                    }
                }
                else
                {
                    CS.Advance(1);
                }
            }

            // In case the last escaped char had a space after it:
            SkipWhitespaceReverse();

            return(start != CS.Position);
        }
コード例 #3
0
        /// <summary>
        /// If the user puts \9 after an identifier, then we're going to treat that
        /// \9 as a comment. That workaround is used to make different values work in different browsers.
        /// Old IE browsers probably ignore the \9 even though it really should be part of the identifier.
        /// </summary>
        private bool HandleSlashNineHack()
        {
            if (AtSlashNineHack())
            {
                int start = CS.Position;

                CS.Advance(2);
                AddToken(CssTokenType.CommentText, start, CS.Position - start);
                return(true);
            }

            return(false);
        }
コード例 #4
0
        protected bool SkipUri()
        {
            // The CSS spec uses this notation for URIs:
            //    ([!#$%&*-~]|{nonascii}|{escape})*
            // http://www.w3.org/TR/CSS21/syndata.html

            int start = CS.Position;

            while (!CS.IsAtEnd)
            {
                char ch = CS.CurrentChar;

                if (AtComment() || AtScopeBlocker())
                {
                    // Found the end
                    break;
                }
                else if (AtEscape())
                {
                    SkipEscape();
                }
                else if ((ch >= '*' && ch <= '~') ||
                         ch == '!' ||
                         ch == '#' ||
                         ch == '$' ||
                         ch == '%' ||
                         ch == '&' ||
                         IsNonAscii(ch))
                {
                    CS.Advance(1);
                }
                else
                {
                    // Found the end
                    break;
                }
            }

            if (CS.Position > start)
            {
                // In case the last escaped char had a space after it:
                SkipWhitespaceReverse();
            }

            return(start != CS.Position);
        }
コード例 #5
0
        protected bool SkipUnicodeRange()
        {
            // Skip U+012???-012FFF

            // The rules for a Unicode range in the lexer are more relaxed than in the parser.
            // The lexer allows question marks anywhere within the first part of the range,
            // while the parser would consider "U+??99" to be invalid.
            //
            // Lexer: u\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?
            // Parser: http://www.w3.org/TR/css3-webfonts/#character-range-the-unicode-range-descri

            int start = CS.Position;

            if (AtUnicodeRange())
            {
                CS.Advance(3); // Skip "U+X"

                for (int count = 1; count < 6; count++, CS.Advance(1))
                {
                    if (!IsHexDigit(CS.CurrentChar) && CS.CurrentChar != '?')
                    {
                        break;
                    }
                }

                if (CS.CurrentChar == '-' && IsHexDigit(CS.Peek(1)))
                {
                    CS.Advance(2); // Skip "-X"

                    for (int count = 1; count < 6; count++, CS.Advance(1))
                    {
                        if (!IsHexDigit(CS.CurrentChar))
                        {
                            break;
                        }
                    }
                }
            }

            return(start != CS.Position);
        }
コード例 #6
0
        protected bool SkipIdentifier()
        {
            int start = CS.Position;

            // Identifiers can start with one dash or two
            if (CS.CurrentChar == '-')
            {
                CS.Advance(1);

                if (CS.CurrentChar == '-')
                {
                    CS.Advance(1);
                }
            }

            if (!AtNameStart())
            {
                // Not a valid name
                CS.Position = start;

                return(false);
            }
            else
            {
                if (AtEscape())
                {
                    SkipEscape();
                }
                else
                {
                    CS.Advance(1);
                }

                // The rest is just name chars
                SkipName();

                return(true);
            }
        }
コード例 #7
0
        private bool HandleHash()
        {
            // Makes a token for: #foo

            int start = CS.Position;

            if (CS.CurrentChar == '#')
            {
                CS.Advance(1);

                if (SkipName())
                {
                    AddToken(CssTokenType.HashName, start, CS.Position - start);
                }
                else
                {
                    // There is no name after the '#'
                    CS.Position = start;
                }
            }

            return(start != CS.Position);
        }
コード例 #8
0
        /// <summary>
        /// Skips: "progid:Foo.Bar(foo=bar)"
        /// </summary>
        protected bool SkipProgramIdFunction()
        {
            int start = CS.Position;

            if ((CS.CurrentChar == 'p' || CS.CurrentChar == 'P') &&
                TextHelpers.CompareCurrentDecodedString(CS, "progid:", /* ignoreCase = */ true, out _))
            {
                CS.Advance(7);

                while (true)
                {
                    if (SkipIdentifier())
                    {
                        // part of the function name
                    }
                    else if (CS.CurrentChar == '.')
                    {
                        // still part of the function name
                        CS.Advance(1);
                    }
                    else if (CS.CurrentChar == '(')
                    {
                        // found the end of the function name
                        CS.Advance(1);
                        return(true);
                    }
                    else
                    {
                        // bad function name
                        CS.Position = start;
                        break;
                    }
                }
            }

            return(start != CS.Position);
        }
コード例 #9
0
        protected bool SkipNumber()
        {
            // Skips: 123.456

            int  start     = CS.Position;
            bool seenDot   = false;
            bool seenDigit = false;

            while (true)
            {
                if (!char.IsDigit(CS.CurrentChar))
                {
                    if (!seenDot && CS.CurrentChar == '.' && char.IsDigit(CS.Peek(1)))
                    {
                        seenDot = true;
                    }
                    else
                    {
                        break;
                    }
                }
                else
                {
                    seenDigit = true;
                }

                CS.Advance(1);
            }

            if (!seenDigit)
            {
                CS.Position = start;
            }

            return(start != CS.Position);
        }
コード例 #10
0
        private bool HandleDimension()
        {
            // Makes tokens for: 100, 100px, +0.7em, -20vw

            int start = CS.Position;

            if (CS.CurrentChar == '+' || CS.CurrentChar == '-')
            {
                CS.Advance(1);
            }

            if (!SkipNumber())
            {
                CS.Position = start;
                return(false);
            }

            AddToken(CssTokenType.Number, start, CS.Position - start);

            // Deal with the units after the number (don't care if it fails)
            HandleUnits();

            return(true);
        }
コード例 #11
0
        private bool HandleComment()
        {
            const CssTokenType startTokenType = CssTokenType.OpenCComment;
            const CssTokenType endTokenType   = CssTokenType.CloseCComment;
            const string       commentStart   = "/*";
            const string       commentEnd     = "*/";

            if (AtScopeBlocker())
            {
                AddToken(CssTokenType.ScopeBlocker, CS.Position, ScopeBlockerText.Length);
                CS.Advance(ScopeBlockerText.Length);

                return(true);
            }

            // Comment: /* foo */
            // Makes three tokens for a comment (start, text, and end)

            if (!CS.TextProvider.CompareTo(CS.Position, commentStart, ignoreCase: false))
            {
                return(false);
            }

            // Skip the start of the comment

            AddToken(startTokenType, CS.Position, commentStart.Length);
            CS.Advance(commentStart.Length);

            // Skip the inner text of the comment

            bool endFound = false;

            if (!CS.IsAtEnd)
            {
                SkipWhitespace();

                int start = CS.Position;

                for (; !CS.IsAtEnd; CS.Advance(1))
                {
                    if (CS.TextProvider.CompareTo(CS.Position, commentEnd, ignoreCase: false))
                    {
                        endFound = true;
                        break;
                    }

                    if (AtScopeBlocker())
                    {
                        // Catch: /* Foo /* END EXTERNAL SOURCE */
                        break;
                    }
                }

                if (CS.Position >= start)
                {
                    // Create a token for the comment text, don't include trailing whitespace
                    SkipWhitespaceReverse();

                    if (CS.Position > start)
                    {
                        AddToken(CssTokenType.CommentText, start, CS.Position - start);
                    }

                    SkipWhitespace();
                }
            }

            // Skip the end of the comment

            if (endFound)
            {
                AddToken(endTokenType, CS.Position, commentEnd.Length);
                CS.Advance(commentEnd.Length);
            }

            return(true);
        }
コード例 #12
0
        protected virtual bool HandleToken()
        {
            CssTokenType tokenType  = CssTokenType.Unknown;
            int          tokenStart = CS.Position;

            switch (CS.CurrentChar)
            {
            case '*':
                if (CS.Peek(1) == '=')
                {
                    tokenType = CssTokenType.ContainsString;
                    CS.Advance(1);
                }
                else
                {
                    tokenType = CssTokenType.Asterisk;
                }
                break;

            case '&':
                tokenType = CssTokenType.Ampersand;
                break;

            case '.':
                tokenType = CssTokenType.Dot;
                break;

            case '!':
                tokenType = CssTokenType.Bang;
                break;

            case ',':
                tokenType = CssTokenType.Comma;
                break;

            case '^':
                if (CS.Peek(1) == '=')
                {
                    tokenType = CssTokenType.BeginsWith;
                    CS.Advance(1);
                }
                else
                {
                    tokenType = CssTokenType.Caret;
                }
                break;

            case ':':
                if (CS.Peek(1) == ':')
                {
                    tokenType = CssTokenType.DoubleColon;
                    CS.Advance(1);
                }
                else
                {
                    tokenType = CssTokenType.Colon;
                }
                break;

            case '$':
                if (CS.Peek(1) == '=')
                {
                    tokenType = CssTokenType.EndsWith;
                    CS.Advance(1);
                }
                else
                {
                    tokenType = CssTokenType.Dollar;
                }
                break;

            case '=':
                tokenType = CssTokenType.Equals;
                break;

            case '>':
                tokenType = CssTokenType.Greater;
                break;

            case '|':
                if (CS.Peek(1) == '=')
                {
                    tokenType = CssTokenType.ListBeginsWith;
                    CS.Advance(1);
                }
                else if (CS.Peek(1) == '|')
                {
                    tokenType = CssTokenType.DoublePipe;
                    CS.Advance(1);
                }
                else
                {
                    tokenType = CssTokenType.Or;
                }
                break;

            case '%':
                tokenType = CssTokenType.Percent;
                break;

            case ';':
                tokenType = CssTokenType.Semicolon;
                break;

            case '/':
                if (HandleComment())
                {
                    return(true);
                }
                else
                {
                    tokenType = CssTokenType.Slash;
                }
                break;

            case '\\':
                if (HandleSlashNineHack())
                {
                    return(true);
                }
                break;

            case '<':
                if (CS.TextProvider.CompareTo(CS.Position, "<!--", ignoreCase: false))
                {
                    CS.Advance(4);
                    AddToken(CssTokenType.OpenHtmlComment, tokenStart, CS.Position - tokenStart);
                    return(true);
                }
                break;

            case '~':
                if (CS.Peek(1) == '=')
                {
                    tokenType = CssTokenType.OneOf;
                    CS.Advance(1);
                }
                else
                {
                    tokenType = CssTokenType.Tilde;
                }
                break;

            case '(':
                tokenType = CssTokenType.OpenFunctionBrace;
                break;

            case ')':
                tokenType = CssTokenType.CloseFunctionBrace;
                break;

            case '[':
                tokenType = CssTokenType.OpenSquareBracket;
                break;

            case ']':
                tokenType = CssTokenType.CloseSquareBracket;
                break;

            case '{':
                tokenType = CssTokenType.OpenCurlyBrace;
                break;

            case '}':
                tokenType = CssTokenType.CloseCurlyBrace;
                break;

            case '@':
                tokenType = CssTokenType.At;
                break;

            case '\'':
            case '\"':
                if (HandleString() != CssTokenType.Unknown)
                {
                    return(true);
                }
                break;

            case '#':
                if (HandleHash())
                {
                    return(true);
                }
                else
                {
                    tokenType = CssTokenType.Hash;
                }
                break;

            case '+':
                tokenType = CssTokenType.Plus;
                break;

            case '-':
                if (HandleIdentifier())
                {
                    return(true);
                }
                else if (CS.TextProvider.CompareTo(CS.Position, "-->", ignoreCase: false))
                {
                    CS.Advance(3);
                    AddToken(CssTokenType.CloseHtmlComment, tokenStart, CS.Position - tokenStart);
                    return(true);
                }
                else
                {
                    tokenType = CssTokenType.Minus;
                }
                break;

            case 'u':
            case 'U':
                if (HandleUnicodeRange())
                {
                    return(true);
                }
                break;
            }

            if (tokenType == CssTokenType.Unknown &&
                HandleIdentifier())
            {
                return(true);
            }

            if (!HandleUnknown())
            {
                // Some kind of junk in the CSS, just deal with it by making an unknown token
                CS.Advance(1);
                AddToken(tokenType, tokenStart, CS.Position - tokenStart);
            }

            return(true);
        }
コード例 #13
0
        protected CssTokenType SkipString()
        {
            // This can detect single and double quoted strings, spanning a single or multiple lines.
            // Also, unterminated strings are detected. The return value tells you the type:
            //
            // CssTokenType.Unknown (not a string at all)
            // CssTokenType.MultilineString
            // CssTokenType.String
            // CssTokenType.InvalidString (unterminated)

            CssTokenType tokenType = CssTokenType.Unknown;

            if (TextHelper.IsQuote(CS.CurrentChar))
            {
                // Guilty until proven valid
                tokenType = CssTokenType.InvalidString;

                char quote     = CS.CurrentChar;
                bool multiLine = false;

                CS.Advance(1);

                while (!CS.IsAtEnd)
                {
                    if (AtScopeBlocker())
                    {
                        break;
                    }
                    else if (CS.CurrentChar == quote)
                    {
                        // Found matching end quote
                        tokenType = multiLine ? CssTokenType.MultilineString : CssTokenType.String;

                        CS.Advance(1);
                        break;
                    }
                    else if (TextHelper.IsNewLine(CS.CurrentChar))
                    {
                        break;
                    }
                    else if (AtEscape())
                    {
                        SkipEscape();
                    }
                    else if (CS.CurrentChar == '\\')
                    {
                        // must be an escaped line break
                        CS.Advance(1);

                        Debug.Assert(TextHelper.IsNewLine(CS.CurrentChar));
                        SkipNewLine();

                        multiLine = true;
                    }
                    else
                    {
                        CS.Advance(1);
                    }
                }

                if (tokenType == CssTokenType.InvalidString)
                {
                    SkipWhitespaceReverse();
                }
            }

            return(tokenType);
        }