Пример #1
0
 private static void AssertNextToken(nsCSSScanner lex, nsCSSTokenType type, Func<nsCSSToken, bool> condition)
 {
     var token = new nsCSSToken();
     Assert.IsTrue(lex.Next(token, true), "Unexpected EOF");
     Assert.AreEqual(type, token.mType);
     Assert.IsTrue(condition(token), "Condition for token {0} failed".Fmt(token.mType));
 }
Пример #2
0
        /**
         * Scan a Hash token.  Handles the distinction between nsCSSTokenType.ID
         * and nsCSSTokenType.Hash, and handles production of Symbol when a '#'
         * is not followed by identifier characters.
         */
        internal bool ScanHash(nsCSSToken aToken)
        {
            Debug.Assert(Peek() == '#', "should not have been called");

            // Fall back for when '#' isn't followed by identifier characters.
            aToken.mSymbol = '#';
            Advance();

            int32_t ch = Peek();

            if (IsIdentChar(ch) || ch == '\\')
            {
                nsCSSTokenType type =
                    StartsIdent(ch, Peek(1)) ? nsCSSTokenType.ID : nsCSSTokenType.Hash;
                aToken.mIdent.SetLength(0);
                if (GatherText(IS_IDCHAR, aToken.mIdent))
                {
                    aToken.mType = type;
                }
            }

            return(true);
        }
Пример #3
0
        internal bool IsLegacyGradientLine(nsCSSTokenType aType,
                                            string aId)
        {
            // N.B. ParseBoxPositionValues is not guaranteed to put back
              // everything it scanned if it fails, so we must only call it
              // if there is no alternative to consuming a <box-position>.
              // ParseVariant, as used here, will either succeed and consume
              // a single token, or fail and consume none, so we can be more
              // cavalier about calling it.

              bool haveGradientLine = false;
              switch (aType) {
              case nsCSSTokenType.Percentage:
              case nsCSSTokenType.Number:
              case nsCSSTokenType.Dimension:
            haveGradientLine = true;
            break;

              case nsCSSTokenType.Function:
            if (aId.LowerCaseEqualsLiteral("calc") ||
                aId.LowerCaseEqualsLiteral("-moz-calc")) {
              haveGradientLine = true;
              break;
            }
            // fall through
            goto case nsCSSTokenType.ID;
              case nsCSSTokenType.ID:
              case nsCSSTokenType.Hash:
            // this is a color
            break;

              case nsCSSTokenType.Ident: {
            // This is only a gradient line if it's a box position keyword.
            nsCSSKeyword kw = nsCSSKeywords.LookupKeyword(aId);
            int32_t junk = 0;
            if (kw != nsCSSKeyword.UNKNOWN &&
                nsCSSProps.FindKeyword(kw, nsCSSProps.kBackgroundPositionKTable,
                                        ref junk)) {
              haveGradientLine = true;
            }
            break;
              }

            goto default;
              default:
            // error
            break;
              }

              return haveGradientLine;
        }
Пример #4
0
        /**
         * Scan a Number, Percentage, or Dimension token (all of which begin
         * like a Number).  Can produce a Symbol when a '.' is not followed by
         * digits, or when '+' or '-' are not followed by either a digit or a
         * '.' and then a digit.  Can also produce a HTMLComment when it
         * encounters '-.'.
         */
        internal bool ScanNumber(nsCSSToken aToken)
        {
            int32_t c = Peek();

        #if DEBUG
            {
                int32_t c2 = Peek(1);
                int32_t c3 = Peek(2);
                Debug.Assert(IsDigit(c) ||
                             (IsDigit(c2) && (c == '.' || c == '+' || c == '-')) ||
                             (IsDigit(c3) && (c == '+' || c == '-') && c2 == '.'),
                             "should not have been called");
            }
        #endif

            // Sign of the mantissa (-1 or 1).
            int32_t sign = c == '-' ? -1 : 1;
            // Absolute value of the integer part of the mantissa.  This is a double so
            // we don't run into overflow issues for consumers that only care about our
            // floating-point value while still being able to express the full int32_t
            // range for consumers who want integers.
            double intPart = 0;
            // Fractional part of the mantissa.  This is a double so that when we convert
            // to float at the end we'll end up rounding to nearest float instead of
            // truncating down (as we would if fracPart were a float and we just
            // effectively lost the last several digits).
            double fracPart = 0;
            // Absolute value of the Math.Power of 10 that we should multiply by (only
            // relevant for numbers in scientific notation).  Has to be a signed integer,
            // because multiplication of signed by unsigned converts the unsigned to
            // signed, so if we plan to actually multiply by expSign...
            int32_t exponent = 0;
            // Sign of the exponent.
            int32_t expSign = 1;

            aToken.mHasSign = (c == '+' || c == '-');
            if (aToken.mHasSign)
            {
                Advance();
                c = Peek();
            }

            bool gotDot = (c == '.');

            if (!gotDot)
            {
                // Scan the integer part of the mantissa.
                Debug.Assert(IsDigit(c), "should have been excluded by logic above");
                do
                {
                    intPart = 10 * intPart + DecimalDigitValue(c);
                    Advance();
                    c = Peek();
                } while (IsDigit(c));

                gotDot = (c == '.') && IsDigit(Peek(1));
            }

            if (gotDot)
            {
                // Scan the fractional part of the mantissa.
                Advance();
                c = Peek();
                Debug.Assert(IsDigit(c), "should have been excluded by logic above");
                // Power of ten by which we need to divide our next digit
                double divisor = 10;
                do
                {
                    fracPart += DecimalDigitValue(c) / divisor;
                    divisor  *= 10;
                    Advance();
                    c = Peek();
                } while (IsDigit(c));
            }

            bool gotE = false;
            if (IsSVGMode() && (c == 'e' || c == 'E'))
            {
                int32_t expSignChar = Peek(1);
                int32_t nextChar    = Peek(2);
                if (IsDigit(expSignChar) ||
                    ((expSignChar == '-' || expSignChar == '+') && IsDigit(nextChar)))
                {
                    gotE = true;
                    if (expSignChar == '-')
                    {
                        expSign = -1;
                    }
                    Advance(); // consumes the E
                    if (expSignChar == '-' || expSignChar == '+')
                    {
                        Advance();
                        c = nextChar;
                    }
                    else
                    {
                        c = expSignChar;
                    }
                    Debug.Assert(IsDigit(c), "should have been excluded by logic above");
                    do
                    {
                        exponent = 10 * exponent + DecimalDigitValue(c);
                        Advance();
                        c = Peek();
                    } while (IsDigit(c));
                }
            }

            nsCSSTokenType type = nsCSSTokenType.Number;

            // Set mIntegerValid for all cases (except %, below) because we need
            // it for the "2n" in :nth-child(2n).
            aToken.mIntegerValid = false;

            // Time to reassemble our number.
            // Do all the math in double precision so it's truncated only once.
            double value = sign * (intPart + fracPart);
            if (gotE)
            {
                // Explicitly cast expSign*exponent to double to avoid issues with
                // overloaded Math.Pow() on Windows.
                value *= Math.Pow(10.0, ((double)(expSign * exponent)));
            }
            else if (!gotDot)
            {
                // Clamp values outside of integer range.
                if (sign > 0)
                {
                    aToken.mInteger = ((int32_t)(Math.Min(intPart, ((double)(Int32.MaxValue)))));
                }
                else
                {
                    aToken.mInteger = ((int32_t)(Math.Max(-intPart, ((double)(Int32.MinValue)))));
                }
                aToken.mIntegerValid = true;
            }

            StringBuilder ident = aToken.mIdent;

            // Check for Dimension and Percentage tokens.
            if (c >= 0)
            {
                if (StartsIdent(c, Peek(1)))
                {
                    if (GatherText(IS_IDCHAR, ident))
                    {
                        type = nsCSSTokenType.Dimension;
                    }
                }
                else if (c == '%')
                {
                    Advance();
                    type  = nsCSSTokenType.Percentage;
                    value = value / 100.0f;
                    aToken.mIntegerValid = false;
                }
            }
            aToken.mNumber = (float)value;
            aToken.mType   = type;
            return(true);
        }
Пример #5
0
        /**
         * Primary scanner entry point.  Consume one token and fill in
         * |aToken| accordingly.  Will skip over any number of comments first,
         * and will also skip over rather than return whitespace tokens if
         * |aSkipWS| is true.
         *
         * Returns true if it successfully consumed a token, false if EOF has
         * been reached.  Will always advance the current read position by at
         * least one character unless called when already at EOF.
         */
        internal bool Next(nsCSSToken aToken, bool aSkipWS)
        {
            int32_t ch;

            // do this here so we don't have to do it in dozens of other places
            aToken.mIdent.Truncate();
            aToken.mType = nsCSSTokenType.Symbol;

            for (;;)
            {
                // Consume any number of comments, and possibly also whitespace tokens,
                // in between other tokens.
                mTokenOffset     = mOffset;
                mTokenLineOffset = mLineOffset;
                mTokenLineNumber = mLineNumber;

                ch = Peek();
                if (IsWhitespace(ch))
                {
                    SkipWhitespace();
                    if (!aSkipWS)
                    {
                        aToken.mType = nsCSSTokenType.Whitespace;
                        return(true);
                    }
                    continue; // start again at the beginning
                }
                if (ch == '/' && !IsSVGMode() && Peek(1) == '*')
                {
                    // FIXME: Editor wants comments to be preserved (bug 60290).
                    SkipComment();
                    continue; // start again at the beginning
                }
                break;
            }

            // EOF
            if (ch < 0)
            {
                return(false);
            }

            // 'u' could be UNICODE-RANGE or an identifier-family token
            if (ch == 'u' || ch == 'U')
            {
                int32_t c2 = Peek(1);
                int32_t c3 = Peek(2);
                if (c2 == '+' && (IsHexDigit(c3) || c3 == '?'))
                {
                    return(ScanURange(aToken));
                }
                return(ScanIdent(aToken));
            }

            // identifier family
            if (IsIdentStart(ch))
            {
                return(ScanIdent(aToken));
            }

            // number family
            if (IsDigit(ch))
            {
                return(ScanNumber(aToken));
            }

            if (ch == '.' && IsDigit(Peek(1)))
            {
                return(ScanNumber(aToken));
            }

            if (ch == '+')
            {
                int32_t c2 = Peek(1);
                if (IsDigit(c2) || (c2 == '.' && IsDigit(Peek(2))))
                {
                    return(ScanNumber(aToken));
                }
            }

            // '-' can start an identifier-family token, a number-family token,
            // or an HTML-comment
            if (ch == '-')
            {
                int32_t c2 = Peek(1);
                int32_t c3 = Peek(2);
                if (IsIdentStart(c2))
                {
                    return(ScanIdent(aToken));
                }
                if (IsDigit(c2) || (c2 == '.' && IsDigit(c3)))
                {
                    return(ScanNumber(aToken));
                }
                if (c2 == '-' && c3 == '>')
                {
                    Advance(3);
                    aToken.mType = nsCSSTokenType.HTMLComment;
                    aToken.mIdent.AssignLiteral("-.");
                    return(true);
                }
            }

            // the other HTML-comment token
            if (ch == '<' && Peek(1) == '!' && Peek(2) == '-' && Peek(3) == '-')
            {
                Advance(4);
                aToken.mType = nsCSSTokenType.HTMLComment;
                aToken.mIdent.AssignLiteral("<!--");
                return(true);
            }

            // AT_KEYWORD
            if (ch == '@')
            {
                return(ScanAtKeyword(aToken));
            }

            // HASH
            if (ch == '#')
            {
                return(ScanHash(aToken));
            }

            // STRING
            if (ch == '"' || ch == '\'')
            {
                return(ScanString(aToken));
            }

            // Match operators: ~= |= ^= $= *=
            nsCSSTokenType opType = MatchOperatorType(ch);

            if (opType != nsCSSTokenType.Symbol && Peek(1) == '=')
            {
                aToken.mType = opType;
                Advance(2);
                return(true);
            }

            // Otherwise, a symbol (DELIM).
            aToken.mSymbol = (char)ch;
            Advance();
            return(true);
        }