2001-09-25 01:32:19 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-21 11:12:37 +00:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2011-10-11 05:50:08 +00:00
|
|
|
|
|
|
|
|
2006-03-25 05:47:31 +00:00
|
|
|
/* tokenization of CSS style sheets */
|
|
|
|
|
2011-05-19 22:44:14 +00:00
|
|
|
#include <math.h> // must be first due to symbol conflicts
|
|
|
|
|
1998-04-13 20:24:54 +00:00
|
|
|
#include "nsCSSScanner.h"
|
2012-11-17 02:53:38 +00:00
|
|
|
#include "nsStyleUtil.h"
|
2012-11-15 16:36:15 +00:00
|
|
|
#include "mozilla/css/ErrorReporter.h"
|
2012-10-26 13:32:10 +00:00
|
|
|
#include "mozilla/Likely.h"
|
2012-11-15 16:36:15 +00:00
|
|
|
#include "mozilla/Util.h"
|
2013-01-15 12:22:03 +00:00
|
|
|
#include <algorithm>
|
2011-05-25 06:31:59 +00:00
|
|
|
|
2012-11-15 16:36:15 +00:00
|
|
|
using mozilla::ArrayLength;
|
2004-09-30 05:18:05 +00:00
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
static const uint8_t IS_HEX_DIGIT = 0x01;
|
2013-02-16 23:27:53 +00:00
|
|
|
static const uint8_t IS_IDSTART = 0x02;
|
|
|
|
static const uint8_t IS_IDCHAR = 0x04;
|
|
|
|
static const uint8_t IS_URL_CHAR = 0x08;
|
|
|
|
static const uint8_t IS_HSPACE = 0x10;
|
|
|
|
static const uint8_t IS_VSPACE = 0x20;
|
|
|
|
static const uint8_t IS_SPACE = IS_HSPACE|IS_VSPACE;
|
|
|
|
|
|
|
|
#define H IS_HSPACE
|
|
|
|
#define V IS_VSPACE
|
|
|
|
#define I IS_IDCHAR
|
2012-05-14 23:01:05 +00:00
|
|
|
#define U IS_URL_CHAR
|
2013-02-16 23:27:53 +00:00
|
|
|
#define S IS_IDSTART
|
|
|
|
#define UI IS_IDCHAR |IS_URL_CHAR
|
|
|
|
#define USI IS_IDCHAR|IS_IDSTART |IS_URL_CHAR
|
|
|
|
#define UXI IS_IDCHAR |IS_HEX_DIGIT|IS_URL_CHAR
|
|
|
|
#define UXSI IS_IDCHAR|IS_IDSTART|IS_HEX_DIGIT|IS_URL_CHAR
|
2009-10-15 20:18:21 +00:00
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
static const uint8_t gLexTable[] = {
|
2009-10-15 20:18:21 +00:00
|
|
|
// TAB LF FF CR
|
2013-02-16 23:27:53 +00:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, H, V, 0, V, V, 0, 0,
|
2009-10-15 20:18:21 +00:00
|
|
|
//
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
// SPC ! " # $ % & ' ( ) * + , - . /
|
2013-02-16 23:27:53 +00:00
|
|
|
H, U, 0, U, U, U, U, 0, 0, 0, U, U, U, UI, U, U,
|
2009-10-15 20:18:21 +00:00
|
|
|
// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
|
2012-05-14 23:01:05 +00:00
|
|
|
UXI,UXI,UXI,UXI,UXI,UXI,UXI,UXI,UXI,UXI,U, U, U, U, U, U,
|
|
|
|
// @ A B C D E F G H I J K L M N O
|
|
|
|
U,UXSI,UXSI,UXSI,UXSI,UXSI,UXSI,USI,USI,USI,USI,USI,USI,USI,USI,USI,
|
2009-10-15 20:18:21 +00:00
|
|
|
// P Q R S T U V W X Y Z [ \ ] ^ _
|
2012-05-14 23:01:05 +00:00
|
|
|
USI,USI,USI,USI,USI,USI,USI,USI,USI,USI,USI,U, S, U, U, USI,
|
|
|
|
// ` a b c d e f g h i j k l m n o
|
|
|
|
U,UXSI,UXSI,UXSI,UXSI,UXSI,UXSI,USI,USI,USI,USI,USI,USI,USI,USI,USI,
|
2009-10-15 20:18:21 +00:00
|
|
|
// p q r s t u v w x y z { | } ~
|
2013-02-01 04:09:47 +00:00
|
|
|
USI,USI,USI,USI,USI,USI,USI,USI,USI,USI,USI,U, U, U, U, 0
|
2009-10-15 20:18:21 +00:00
|
|
|
};
|
|
|
|
|
2013-02-16 23:27:53 +00:00
|
|
|
MOZ_STATIC_ASSERT(MOZ_ARRAY_LENGTH(gLexTable) == 128,
|
2013-02-01 04:09:47 +00:00
|
|
|
"gLexTable expected to cover all 128 ASCII characters");
|
2011-04-17 21:13:15 +00:00
|
|
|
|
2013-02-16 23:27:53 +00:00
|
|
|
#undef H
|
|
|
|
#undef V
|
2009-10-15 20:18:21 +00:00
|
|
|
#undef S
|
|
|
|
#undef I
|
2012-05-14 23:01:05 +00:00
|
|
|
#undef U
|
|
|
|
#undef UI
|
|
|
|
#undef USI
|
|
|
|
#undef UXI
|
|
|
|
#undef UXSI
|
1998-04-13 20:24:54 +00:00
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
static inline bool
|
2013-02-16 23:27:53 +00:00
|
|
|
IsHorzSpace(int32_t ch) {
|
|
|
|
return uint32_t(ch) < 128 && (gLexTable[ch] & IS_HSPACE) != 0;
|
2008-09-10 04:38:29 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
static inline bool
|
2013-02-16 23:27:53 +00:00
|
|
|
IsVertSpace(int32_t ch) {
|
|
|
|
return uint32_t(ch) < 128 && (gLexTable[ch] & IS_VSPACE) != 0;
|
2008-09-10 04:38:29 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
static inline bool
|
2012-08-22 15:56:38 +00:00
|
|
|
IsWhitespace(int32_t ch) {
|
2013-02-16 23:27:53 +00:00
|
|
|
return uint32_t(ch) < 128 && (gLexTable[ch] & IS_SPACE) != 0;
|
2008-09-10 04:38:29 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
static inline bool
|
2013-02-16 23:27:53 +00:00
|
|
|
IsIdentChar(int32_t ch) {
|
|
|
|
return ch >= 0 && (ch >= 128 || (gLexTable[ch] & IS_IDCHAR) != 0);
|
2008-09-10 04:38:29 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
static inline bool
|
2013-02-16 23:27:53 +00:00
|
|
|
IsIdentStart(int32_t ch) {
|
|
|
|
return ch >= 0 && (ch >= 128 || (gLexTable[ch] & IS_IDSTART) != 0);
|
2008-09-10 04:38:29 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
static inline bool
|
2013-02-16 23:27:53 +00:00
|
|
|
StartsIdent(int32_t aFirstChar, int32_t aSecondChar)
|
|
|
|
{
|
|
|
|
return IsIdentStart(aFirstChar) ||
|
|
|
|
(aFirstChar == '-' && IsIdentStart(aSecondChar));
|
2008-09-10 04:38:29 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 23:01:05 +00:00
|
|
|
static inline bool
|
2012-08-22 15:56:38 +00:00
|
|
|
IsURLChar(int32_t ch) {
|
2013-02-01 04:09:47 +00:00
|
|
|
return ch >= 0 && (ch >= 128 || (gLexTable[ch] & IS_URL_CHAR) != 0);
|
2012-05-14 23:01:05 +00:00
|
|
|
}
|
|
|
|
|
2013-02-16 23:27:53 +00:00
|
|
|
static inline bool
|
|
|
|
IsDigit(int32_t ch) {
|
|
|
|
return (ch >= '0') && (ch <= '9');
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
IsHexDigit(int32_t ch) {
|
|
|
|
return uint32_t(ch) < 128 && (gLexTable[ch] & IS_HEX_DIGIT) != 0;
|
|
|
|
}
|
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
static inline uint32_t
|
|
|
|
DecimalDigitValue(int32_t ch)
|
2009-08-20 21:52:47 +00:00
|
|
|
{
|
|
|
|
return ch - '0';
|
|
|
|
}
|
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
static inline uint32_t
|
|
|
|
HexDigitValue(int32_t ch)
|
2009-08-20 21:52:47 +00:00
|
|
|
{
|
|
|
|
if (IsDigit(ch)) {
|
|
|
|
return DecimalDigitValue(ch);
|
|
|
|
} else {
|
|
|
|
// Note: c&7 just keeps the low three bits which causes
|
|
|
|
// upper and lower case alphabetics to both yield their
|
|
|
|
// "relative to 10" value for computing the hex value.
|
|
|
|
return (ch & 0x7) + 9;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-11-15 16:36:15 +00:00
|
|
|
nsCSSToken::AppendToString(nsString& aBuffer) const
|
1999-02-07 21:47:48 +00:00
|
|
|
{
|
|
|
|
switch (mType) {
|
2012-11-16 21:59:38 +00:00
|
|
|
case eCSSToken_Ident:
|
2012-11-17 02:53:38 +00:00
|
|
|
nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eCSSToken_AtKeyword:
|
|
|
|
aBuffer.Append('@');
|
|
|
|
nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eCSSToken_ID:
|
2013-02-16 23:27:53 +00:00
|
|
|
case eCSSToken_Hash:
|
2012-11-17 02:53:38 +00:00
|
|
|
aBuffer.Append('#');
|
|
|
|
nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer);
|
|
|
|
break;
|
|
|
|
|
1999-02-07 21:47:48 +00:00
|
|
|
case eCSSToken_Function:
|
2012-11-17 02:53:38 +00:00
|
|
|
nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer);
|
|
|
|
aBuffer.Append('(');
|
1999-02-07 21:47:48 +00:00
|
|
|
break;
|
2012-11-17 02:53:38 +00:00
|
|
|
|
2011-03-11 17:29:45 +00:00
|
|
|
case eCSSToken_URL:
|
|
|
|
case eCSSToken_Bad_URL:
|
2011-03-11 17:29:45 +00:00
|
|
|
aBuffer.AppendLiteral("url(");
|
2011-03-11 17:29:45 +00:00
|
|
|
if (mSymbol != PRUnichar(0)) {
|
2012-11-17 02:53:38 +00:00
|
|
|
nsStyleUtil::AppendEscapedCSSString(mIdent, aBuffer, mSymbol);
|
|
|
|
} else {
|
|
|
|
aBuffer.Append(mIdent);
|
2011-03-11 17:29:45 +00:00
|
|
|
}
|
|
|
|
if (mType == eCSSToken_URL) {
|
|
|
|
aBuffer.Append(PRUnichar(')'));
|
|
|
|
}
|
|
|
|
break;
|
2012-11-17 02:53:38 +00:00
|
|
|
|
1999-02-07 21:47:48 +00:00
|
|
|
case eCSSToken_Number:
|
|
|
|
if (mIntegerValid) {
|
2000-04-15 20:15:37 +00:00
|
|
|
aBuffer.AppendInt(mInteger, 10);
|
2012-11-17 02:53:38 +00:00
|
|
|
} else {
|
2000-04-15 20:15:37 +00:00
|
|
|
aBuffer.AppendFloat(mNumber);
|
1999-02-07 21:47:48 +00:00
|
|
|
}
|
|
|
|
break;
|
2012-11-17 02:53:38 +00:00
|
|
|
|
1999-02-07 21:47:48 +00:00
|
|
|
case eCSSToken_Percentage:
|
2003-03-18 05:43:12 +00:00
|
|
|
NS_ASSERTION(!mIntegerValid, "How did a percentage token get this set?");
|
|
|
|
aBuffer.AppendFloat(mNumber * 100.0f);
|
2011-05-19 22:44:14 +00:00
|
|
|
aBuffer.Append(PRUnichar('%'));
|
1999-02-07 21:47:48 +00:00
|
|
|
break;
|
2012-11-17 02:53:38 +00:00
|
|
|
|
1999-02-07 21:47:48 +00:00
|
|
|
case eCSSToken_Dimension:
|
|
|
|
if (mIntegerValid) {
|
2000-04-15 20:15:37 +00:00
|
|
|
aBuffer.AppendInt(mInteger, 10);
|
2012-11-17 02:53:38 +00:00
|
|
|
} else {
|
2000-04-15 20:15:37 +00:00
|
|
|
aBuffer.AppendFloat(mNumber);
|
1999-02-07 21:47:48 +00:00
|
|
|
}
|
2012-11-17 02:53:38 +00:00
|
|
|
nsStyleUtil::AppendEscapedCSSIdent(mIdent, aBuffer);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eCSSToken_Bad_String:
|
|
|
|
nsStyleUtil::AppendEscapedCSSString(mIdent, aBuffer, mSymbol);
|
|
|
|
// remove the trailing quote character
|
|
|
|
aBuffer.Truncate(aBuffer.Length() - 1);
|
1999-02-07 21:47:48 +00:00
|
|
|
break;
|
2012-11-17 02:53:38 +00:00
|
|
|
|
1999-02-07 21:47:48 +00:00
|
|
|
case eCSSToken_String:
|
2012-11-17 02:53:38 +00:00
|
|
|
nsStyleUtil::AppendEscapedCSSString(mIdent, aBuffer, mSymbol);
|
|
|
|
break;
|
|
|
|
|
1999-02-07 21:47:48 +00:00
|
|
|
case eCSSToken_Symbol:
|
|
|
|
aBuffer.Append(mSymbol);
|
|
|
|
break;
|
2012-11-17 02:53:38 +00:00
|
|
|
|
2013-02-16 23:27:53 +00:00
|
|
|
case eCSSToken_Whitespace:
|
2012-11-17 02:53:38 +00:00
|
|
|
aBuffer.Append(' ');
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eCSSToken_HTMLComment:
|
|
|
|
case eCSSToken_URange:
|
1999-02-07 21:47:48 +00:00
|
|
|
aBuffer.Append(mIdent);
|
|
|
|
break;
|
2012-11-17 02:53:38 +00:00
|
|
|
|
2000-04-27 00:12:25 +00:00
|
|
|
case eCSSToken_Includes:
|
2004-06-17 00:13:25 +00:00
|
|
|
aBuffer.AppendLiteral("~=");
|
2000-04-27 00:12:25 +00:00
|
|
|
break;
|
|
|
|
case eCSSToken_Dashmatch:
|
2004-06-17 00:13:25 +00:00
|
|
|
aBuffer.AppendLiteral("|=");
|
2000-04-27 00:12:25 +00:00
|
|
|
break;
|
2008-07-11 22:49:46 +00:00
|
|
|
case eCSSToken_Beginsmatch:
|
|
|
|
aBuffer.AppendLiteral("^=");
|
|
|
|
break;
|
|
|
|
case eCSSToken_Endsmatch:
|
|
|
|
aBuffer.AppendLiteral("$=");
|
|
|
|
break;
|
|
|
|
case eCSSToken_Containsmatch:
|
|
|
|
aBuffer.AppendLiteral("*=");
|
|
|
|
break;
|
2012-11-17 02:53:38 +00:00
|
|
|
|
1999-02-07 21:47:48 +00:00
|
|
|
default:
|
|
|
|
NS_ERROR("invalid token type");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-15 16:36:15 +00:00
|
|
|
nsCSSScanner::nsCSSScanner(const nsAString& aBuffer, uint32_t aLineNumber)
|
2013-02-16 23:27:53 +00:00
|
|
|
: mBuffer(aBuffer.BeginReading())
|
2012-11-15 16:36:15 +00:00
|
|
|
, mOffset(0)
|
|
|
|
, mCount(aBuffer.Length())
|
|
|
|
, mLineNumber(aLineNumber)
|
|
|
|
, mLineOffset(0)
|
2013-01-11 17:27:43 +00:00
|
|
|
, mTokenLineNumber(aLineNumber)
|
|
|
|
, mTokenLineOffset(0)
|
|
|
|
, mTokenOffset(0)
|
2012-11-15 16:36:15 +00:00
|
|
|
, mRecordStartOffset(0)
|
|
|
|
, mReporter(nullptr)
|
2011-10-17 14:59:28 +00:00
|
|
|
, mSVGMode(false)
|
2012-11-15 16:36:15 +00:00
|
|
|
, mRecording(false)
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
1999-10-08 20:41:19 +00:00
|
|
|
MOZ_COUNT_CTOR(nsCSSScanner);
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsCSSScanner::~nsCSSScanner()
|
|
|
|
{
|
1999-10-08 20:41:19 +00:00
|
|
|
MOZ_COUNT_DTOR(nsCSSScanner);
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
2013-02-16 23:27:53 +00:00
|
|
|
void
|
|
|
|
nsCSSScanner::StartRecording()
|
|
|
|
{
|
2013-02-16 23:27:53 +00:00
|
|
|
MOZ_ASSERT(!mRecording, "already started recording");
|
2013-02-16 23:27:53 +00:00
|
|
|
mRecording = true;
|
2013-02-16 23:27:53 +00:00
|
|
|
mRecordStartOffset = mOffset;
|
2013-02-16 23:27:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsCSSScanner::StopRecording()
|
|
|
|
{
|
2013-02-16 23:27:53 +00:00
|
|
|
MOZ_ASSERT(mRecording, "haven't started recording");
|
2013-02-16 23:27:53 +00:00
|
|
|
mRecording = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsCSSScanner::StopRecording(nsString& aBuffer)
|
|
|
|
{
|
2013-02-16 23:27:53 +00:00
|
|
|
MOZ_ASSERT(mRecording, "haven't started recording");
|
2013-02-16 23:27:53 +00:00
|
|
|
mRecording = false;
|
|
|
|
aBuffer.Append(mBuffer + mRecordStartOffset,
|
2013-02-16 23:27:53 +00:00
|
|
|
mOffset - mRecordStartOffset);
|
2013-02-16 23:27:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsDependentSubstring
|
|
|
|
nsCSSScanner::GetCurrentLine() const
|
|
|
|
{
|
|
|
|
uint32_t end = mTokenOffset;
|
2013-02-16 23:27:53 +00:00
|
|
|
while (end < mCount && !IsVertSpace(mBuffer[end])) {
|
2013-02-16 23:27:53 +00:00
|
|
|
end++;
|
|
|
|
}
|
|
|
|
return nsDependentSubstring(mBuffer + mTokenLineOffset,
|
|
|
|
mBuffer + end);
|
|
|
|
}
|
|
|
|
|
2013-02-16 23:27:53 +00:00
|
|
|
/**
|
|
|
|
* Return the raw UTF-16 code unit at position |mOffset + n| within
|
|
|
|
* the read buffer. If that is beyond the end of the buffer, returns
|
|
|
|
* -1 to indicate end of input.
|
|
|
|
*/
|
|
|
|
inline int32_t
|
|
|
|
nsCSSScanner::Peek(uint32_t n)
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
2013-02-16 23:27:53 +00:00
|
|
|
if (mOffset + n >= mCount) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return mBuffer[mOffset + n];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Advance |mOffset| over |n| code units. Advance(0) is a no-op.
|
|
|
|
* If |n| is greater than the distance to end of input, will silently
|
|
|
|
* stop at the end. May not be used to advance over a line boundary;
|
|
|
|
* AdvanceLine() must be used instead.
|
|
|
|
*/
|
|
|
|
inline void
|
|
|
|
nsCSSScanner::Advance(uint32_t n)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
while (mOffset < mCount && n > 0) {
|
|
|
|
MOZ_ASSERT(!IsVertSpace(mBuffer[mOffset]),
|
|
|
|
"may not Advance() over a line boundary");
|
|
|
|
mOffset++;
|
|
|
|
n--;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (mOffset + n >= mCount || mOffset + n < mOffset)
|
|
|
|
mOffset = mCount;
|
|
|
|
else
|
|
|
|
mOffset += n;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Advance |mOffset| over a line boundary.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
nsCSSScanner::AdvanceLine()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(IsVertSpace(mBuffer[mOffset]),
|
|
|
|
"may not AdvanceLine() over a horizontal character");
|
|
|
|
// Advance over \r\n as a unit.
|
|
|
|
if (mBuffer[mOffset] == '\r' && mOffset + 1 < mCount &&
|
|
|
|
mBuffer[mOffset+1] == '\n')
|
|
|
|
mOffset += 2;
|
|
|
|
else
|
|
|
|
mOffset += 1;
|
|
|
|
// 0 is a magical line number meaning that we don't know (i.e., script)
|
|
|
|
if (mLineNumber != 0)
|
|
|
|
mLineNumber++;
|
|
|
|
mLineOffset = mOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Back up |mOffset| over |n| code units. Backup(0) is a no-op.
|
|
|
|
* If |n| is greater than the distance to beginning of input, will
|
|
|
|
* silently stop at the beginning. May not be used to back up over a
|
|
|
|
* line boundary.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
nsCSSScanner::Backup(uint32_t n)
|
|
|
|
{
|
|
|
|
#if 1
|
|
|
|
// Right now, code below does occasionally need to back up over a
|
|
|
|
// line boundary. This will cease to be required later in this
|
|
|
|
// patch series. Note that we do not attempt to correct mLineOffset.
|
|
|
|
while (mOffset > 0 && n > 0) {
|
|
|
|
if (IsVertSpace(mBuffer[mOffset-1])) {
|
|
|
|
if (mBuffer[mOffset-1] == '\n' && mOffset > 1 &&
|
|
|
|
mBuffer[mOffset-2] == '\r') {
|
|
|
|
mOffset -= 2;
|
|
|
|
} else {
|
|
|
|
mOffset -= 1;
|
2007-08-23 23:01:52 +00:00
|
|
|
}
|
2013-02-16 23:27:53 +00:00
|
|
|
n--;
|
|
|
|
mLineNumber--;
|
|
|
|
} else {
|
|
|
|
mOffset--;
|
|
|
|
n--;
|
2011-05-19 22:44:14 +00:00
|
|
|
}
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
2013-02-16 23:27:53 +00:00
|
|
|
#else
|
|
|
|
#ifdef DEBUG
|
|
|
|
while (mOffset > 0 && n > 0) {
|
|
|
|
MOZ_ASSERT(!IsVertSpace(mBuffer[mOffset-1]),
|
|
|
|
"may not Backup() over a line boundary");
|
|
|
|
mOffset--;
|
|
|
|
n--;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (mOffset < n)
|
|
|
|
mOffset = 0;
|
|
|
|
else
|
|
|
|
mOffset -= n;
|
|
|
|
#endif
|
|
|
|
#endif
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
2013-02-16 23:27:53 +00:00
|
|
|
// Returns -1 on error or eof
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t
|
2013-02-16 23:27:53 +00:00
|
|
|
nsCSSScanner::Read()
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
2013-02-16 23:27:53 +00:00
|
|
|
int32_t rv = Peek();
|
|
|
|
|
|
|
|
// There are four types of newlines in CSS: "\r", "\n", "\r\n", and "\f".
|
|
|
|
// To simplify dealing with newlines, they are all normalized to "\n" here.
|
|
|
|
if (IsVertSpace(rv)) {
|
|
|
|
AdvanceLine();
|
|
|
|
rv = '\n';
|
|
|
|
} else if (rv >= 0) {
|
|
|
|
Advance();
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
2013-02-16 23:27:53 +00:00
|
|
|
return rv;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
2008-09-10 04:38:14 +00:00
|
|
|
void
|
|
|
|
nsCSSScanner::Pushback(PRUnichar aChar)
|
1998-09-25 01:50:51 +00:00
|
|
|
{
|
2013-02-16 23:27:53 +00:00
|
|
|
MOZ_ASSERT(mOffset > 0 && aChar == mBuffer[mOffset-1],
|
|
|
|
"may only push back exactly what was read");
|
|
|
|
Backup(1);
|
1998-09-25 01:50:51 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool
|
2008-09-10 04:38:14 +00:00
|
|
|
nsCSSScanner::LookAhead(PRUnichar aChar)
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
2013-02-16 23:27:53 +00:00
|
|
|
if (Peek() == aChar) {
|
|
|
|
if (IsVertSpace(aChar)) {
|
|
|
|
AdvanceLine();
|
|
|
|
} else {
|
|
|
|
Advance();
|
|
|
|
}
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
2011-10-17 14:59:28 +00:00
|
|
|
return false;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool
|
2011-03-11 17:29:45 +00:00
|
|
|
nsCSSScanner::LookAheadOrEOF(PRUnichar aChar)
|
|
|
|
{
|
2013-02-16 23:27:53 +00:00
|
|
|
int32_t ch = Peek();
|
|
|
|
if (ch == -1) {
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
2011-03-11 17:29:45 +00:00
|
|
|
}
|
|
|
|
if (ch == aChar) {
|
2013-02-16 23:27:53 +00:00
|
|
|
if (IsVertSpace(aChar)) {
|
|
|
|
AdvanceLine();
|
|
|
|
} else {
|
|
|
|
Advance();
|
|
|
|
}
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
2011-03-11 17:29:45 +00:00
|
|
|
}
|
2011-10-17 14:59:28 +00:00
|
|
|
return false;
|
2011-03-11 17:29:45 +00:00
|
|
|
}
|
|
|
|
|
2009-04-09 06:46:26 +00:00
|
|
|
void
|
2013-02-16 23:27:53 +00:00
|
|
|
nsCSSScanner::SkipWhitespace()
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
|
|
|
for (;;) {
|
2013-02-16 23:27:53 +00:00
|
|
|
int32_t ch = Peek();
|
|
|
|
if (!IsWhitespace(ch)) { // EOF counts as non-whitespace
|
1998-04-13 20:24:54 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-02-16 23:27:53 +00:00
|
|
|
if (IsVertSpace(ch)) {
|
|
|
|
AdvanceLine();
|
|
|
|
} else {
|
|
|
|
Advance();
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-16 23:27:53 +00:00
|
|
|
void
|
|
|
|
nsCSSScanner::SkipComment()
|
1998-10-26 23:22:40 +00:00
|
|
|
{
|
2009-08-06 00:45:49 +00:00
|
|
|
for (;;) {
|
2013-02-16 23:27:53 +00:00
|
|
|
int32_t ch = Peek();
|
|
|
|
if (ch < 0) {
|
|
|
|
mReporter->ReportUnexpectedEOF("PECommentEOF");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (ch == '*' && Peek(1) == '/') {
|
|
|
|
Advance(2);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (IsVertSpace(ch)) {
|
|
|
|
AdvanceLine();
|
|
|
|
} else {
|
|
|
|
Advance();
|
2007-08-20 03:39:22 +00:00
|
|
|
}
|
2009-08-06 00:45:49 +00:00
|
|
|
}
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
2011-05-03 20:19:19 +00:00
|
|
|
/**
|
|
|
|
* Returns whether an escape was succesfully parsed; if it was not,
|
|
|
|
* the backslash needs to be its own symbol token.
|
|
|
|
*/
|
2011-09-29 06:19:26 +00:00
|
|
|
bool
|
2013-02-16 23:27:53 +00:00
|
|
|
nsCSSScanner::GatherEscape(nsString& aOutput, bool aInString)
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t ch = Read();
|
1998-04-13 20:24:54 +00:00
|
|
|
if (ch < 0) {
|
2011-10-17 14:59:28 +00:00
|
|
|
return false;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
2007-08-26 02:20:27 +00:00
|
|
|
if (IsHexDigit(ch)) {
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t rv = 0;
|
1999-05-19 00:18:30 +00:00
|
|
|
int i;
|
2011-05-03 20:19:19 +00:00
|
|
|
Pushback(ch);
|
1999-05-19 00:18:30 +00:00
|
|
|
for (i = 0; i < 6; i++) { // up to six digits
|
2008-09-10 04:38:14 +00:00
|
|
|
ch = Read();
|
1998-04-13 20:24:54 +00:00
|
|
|
if (ch < 0) {
|
|
|
|
// Whoops: error or premature eof
|
|
|
|
break;
|
|
|
|
}
|
2007-08-26 02:20:27 +00:00
|
|
|
if (!IsHexDigit(ch) && !IsWhitespace(ch)) {
|
2007-08-21 18:29:50 +00:00
|
|
|
Pushback(ch);
|
2004-02-08 19:22:47 +00:00
|
|
|
break;
|
2007-08-26 02:20:27 +00:00
|
|
|
} else if (IsHexDigit(ch)) {
|
2009-08-20 21:52:47 +00:00
|
|
|
rv = rv * 16 + HexDigitValue(ch);
|
2004-02-08 19:22:47 +00:00
|
|
|
} else {
|
2007-08-26 02:20:27 +00:00
|
|
|
NS_ASSERTION(IsWhitespace(ch), "bad control flow");
|
2004-02-08 19:22:47 +00:00
|
|
|
// single space ends escape
|
1999-05-18 23:13:27 +00:00
|
|
|
break;
|
|
|
|
}
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
1999-05-18 23:13:27 +00:00
|
|
|
if (6 == i) { // look for trailing whitespace and eat it
|
2008-09-10 04:38:14 +00:00
|
|
|
ch = Peek();
|
2007-08-26 02:20:27 +00:00
|
|
|
if (IsWhitespace(ch)) {
|
2008-10-23 16:29:24 +00:00
|
|
|
(void) Read();
|
1999-05-18 23:13:27 +00:00
|
|
|
}
|
|
|
|
}
|
2005-11-17 15:17:00 +00:00
|
|
|
NS_ASSERTION(rv >= 0, "How did rv become negative?");
|
2008-10-23 16:29:24 +00:00
|
|
|
// "[at most six hexadecimal digits following a backslash] stand
|
|
|
|
// for the ISO 10646 character with that number, which must not be
|
|
|
|
// zero. (It is undefined in CSS 2.1 what happens if a style sheet
|
|
|
|
// does contain a character with Unicode codepoint zero.)"
|
|
|
|
// -- CSS2.1 section 4.1.3
|
|
|
|
//
|
|
|
|
// Silently deleting \0 opens a content-filtration loophole (see
|
|
|
|
// bug 228856), so what we do instead is pretend the "cancels the
|
|
|
|
// meaning of special characters" rule applied.
|
2005-11-17 15:17:00 +00:00
|
|
|
if (rv > 0) {
|
|
|
|
AppendUCS4ToUTF16(ENSURE_VALID_CHAR(rv), aOutput);
|
2008-10-23 16:29:24 +00:00
|
|
|
} else {
|
|
|
|
while (i--)
|
|
|
|
aOutput.Append('0');
|
|
|
|
if (IsWhitespace(ch))
|
|
|
|
Pushback(ch);
|
2005-11-17 15:17:00 +00:00
|
|
|
}
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
2012-11-15 16:36:15 +00:00
|
|
|
}
|
|
|
|
// "Any character except a hexadecimal digit can be escaped to
|
2009-04-09 06:46:26 +00:00
|
|
|
// remove its special meaning by putting a backslash in front"
|
|
|
|
// -- CSS1 spec section 7.1
|
2011-05-03 20:19:19 +00:00
|
|
|
if (ch == '\n') {
|
|
|
|
if (!aInString) {
|
|
|
|
// Outside of strings (which includes url() that contains a
|
|
|
|
// string), escaped newlines aren't special, and just tokenize as
|
|
|
|
// eCSSToken_Symbol (DELIM).
|
|
|
|
Pushback(ch);
|
2011-10-17 14:59:28 +00:00
|
|
|
return false;
|
2011-05-03 20:19:19 +00:00
|
|
|
}
|
|
|
|
// In strings (and in url() containing a string), escaped newlines
|
|
|
|
// are just dropped to allow splitting over multiple lines.
|
|
|
|
} else {
|
2009-04-09 06:46:26 +00:00
|
|
|
aOutput.Append(ch);
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
2011-05-03 20:19:19 +00:00
|
|
|
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gather up the characters in an identifier. The identfier was
|
|
|
|
* started by "aChar" which will be appended to aIdent. The result
|
|
|
|
* will be aIdent with all of the identifier characters appended
|
|
|
|
* until the first non-identifier character is seen. The termination
|
|
|
|
* character is unread for the future re-reading.
|
2011-05-03 20:19:19 +00:00
|
|
|
*
|
|
|
|
* Returns failure when the character sequence does not form an ident at
|
|
|
|
* all, in which case the caller is responsible for pushing back or
|
|
|
|
* otherwise handling aChar. (This occurs only when aChar is '\'.)
|
1998-04-13 20:24:54 +00:00
|
|
|
*/
|
2011-09-29 06:19:26 +00:00
|
|
|
bool
|
2012-08-22 15:56:38 +00:00
|
|
|
nsCSSScanner::GatherIdent(int32_t aChar, nsString& aIdent)
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
2011-05-19 22:44:14 +00:00
|
|
|
if (aChar == '\\') {
|
2013-02-16 23:27:53 +00:00
|
|
|
if (!GatherEscape(aIdent, false)) {
|
2011-10-17 14:59:28 +00:00
|
|
|
return false;
|
2011-05-03 20:19:19 +00:00
|
|
|
}
|
2012-09-11 22:20:52 +00:00
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(aChar > 0);
|
2005-11-17 15:17:00 +00:00
|
|
|
aIdent.Append(aChar);
|
1999-05-18 23:13:27 +00:00
|
|
|
}
|
1998-04-13 20:24:54 +00:00
|
|
|
for (;;) {
|
2013-02-16 23:27:53 +00:00
|
|
|
if (mOffset < mCount) {
|
2008-02-22 01:37:04 +00:00
|
|
|
// See how much we can consume and append in one go
|
2012-08-22 15:56:38 +00:00
|
|
|
uint32_t n = mOffset;
|
2008-02-22 01:37:04 +00:00
|
|
|
// Count number of Ident characters that can be processed
|
2013-02-16 23:27:53 +00:00
|
|
|
while (n < mCount && IsIdentChar(mBuffer[n])) {
|
2008-02-22 01:37:04 +00:00
|
|
|
++n;
|
|
|
|
}
|
|
|
|
// Add to the token what we have so far
|
|
|
|
if (n > mOffset) {
|
2013-02-16 23:27:53 +00:00
|
|
|
aIdent.Append(&mBuffer[mOffset], n - mOffset);
|
2008-02-22 01:37:04 +00:00
|
|
|
mOffset = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-10 04:38:14 +00:00
|
|
|
aChar = Read();
|
2013-02-16 23:27:53 +00:00
|
|
|
if (aChar < 0) break;
|
|
|
|
if (aChar == '\\') {
|
|
|
|
if (!GatherEscape(aIdent, false)) {
|
|
|
|
Pushback(aChar);
|
|
|
|
break;
|
|
|
|
}
|
2013-02-16 23:27:53 +00:00
|
|
|
} else if (IsIdentChar(aChar)) {
|
2013-02-16 23:27:53 +00:00
|
|
|
aIdent.Append(PRUnichar(aChar));
|
|
|
|
} else {
|
|
|
|
Pushback(aChar);
|
|
|
|
break;
|
2005-07-19 20:49:34 +00:00
|
|
|
}
|
|
|
|
}
|
2013-02-16 23:27:53 +00:00
|
|
|
MOZ_ASSERT(aIdent.Length() > 0);
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool
|
2013-02-16 23:27:53 +00:00
|
|
|
nsCSSScanner::ScanIdent(int32_t aChar, nsCSSToken& aToken)
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
1998-10-26 23:22:40 +00:00
|
|
|
nsString& ident = aToken.mIdent;
|
1998-04-13 20:24:54 +00:00
|
|
|
ident.SetLength(0);
|
2008-09-10 04:38:14 +00:00
|
|
|
if (!GatherIdent(aChar, ident)) {
|
2011-05-03 20:19:19 +00:00
|
|
|
aToken.mType = eCSSToken_Symbol;
|
|
|
|
aToken.mSymbol = aChar;
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsCSSTokenType tokenType = eCSSToken_Ident;
|
1998-10-26 23:22:40 +00:00
|
|
|
// look for functions (ie: "ident(")
|
2010-01-28 00:20:04 +00:00
|
|
|
if (Peek() == PRUnichar('(')) {
|
|
|
|
Read();
|
1998-10-26 23:22:40 +00:00
|
|
|
tokenType = eCSSToken_Function;
|
2011-03-11 17:29:45 +00:00
|
|
|
|
|
|
|
if (ident.LowerCaseEqualsLiteral("url")) {
|
|
|
|
NextURL(aToken); // ignore return value, since *we* read something
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
2011-03-11 17:29:45 +00:00
|
|
|
}
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
1998-10-26 23:22:40 +00:00
|
|
|
|
|
|
|
aToken.mType = tokenType;
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool
|
2013-02-16 23:27:53 +00:00
|
|
|
nsCSSScanner::ScanAtKeyword(nsCSSToken& aToken)
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
2012-09-11 22:20:52 +00:00
|
|
|
int32_t ch = Read();
|
|
|
|
if (StartsIdent(ch, Peek())) {
|
|
|
|
aToken.mIdent.SetLength(0);
|
|
|
|
aToken.mType = eCSSToken_AtKeyword;
|
|
|
|
if (GatherIdent(ch, aToken.mIdent)) {
|
|
|
|
return true;
|
|
|
|
}
|
2011-05-03 20:19:19 +00:00
|
|
|
}
|
2012-09-11 22:20:52 +00:00
|
|
|
if (ch >= 0) {
|
|
|
|
Pushback(ch);
|
|
|
|
}
|
|
|
|
aToken.mType = eCSSToken_Symbol;
|
|
|
|
aToken.mSymbol = PRUnichar('@');
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool
|
2013-02-16 23:27:53 +00:00
|
|
|
nsCSSScanner::ScanHash(int32_t aChar, nsCSSToken& aToken)
|
|
|
|
{
|
|
|
|
// Fall back for when we don't have name characters following:
|
|
|
|
aToken.mType = eCSSToken_Symbol;
|
|
|
|
aToken.mSymbol = aChar;
|
|
|
|
|
|
|
|
int32_t ch = Read();
|
|
|
|
if (ch < 0) {
|
|
|
|
return true;
|
|
|
|
}
|
2013-02-16 23:27:53 +00:00
|
|
|
if (IsIdentChar(ch) || ch == '\\') {
|
2013-02-16 23:27:53 +00:00
|
|
|
// First char after the '#' is a valid ident char (or an escape),
|
|
|
|
// so it makes sense to keep going
|
|
|
|
nsCSSTokenType type =
|
|
|
|
StartsIdent(ch, Peek()) ? eCSSToken_ID : eCSSToken_Hash;
|
|
|
|
aToken.mIdent.SetLength(0);
|
|
|
|
if (GatherIdent(ch, aToken.mIdent)) {
|
|
|
|
aToken.mType = type;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No ident chars after the '#'. Just unread |ch| and get out of here.
|
|
|
|
Pushback(ch);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
nsCSSScanner::ScanNumber(int32_t c, nsCSSToken& aToken)
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
2009-07-10 01:44:20 +00:00
|
|
|
NS_PRECONDITION(c == '.' || c == '+' || c == '-' || IsDigit(c),
|
|
|
|
"Why did we get called?");
|
2008-06-03 03:17:35 +00:00
|
|
|
aToken.mHasSign = (c == '+' || c == '-');
|
2009-07-10 01:44:20 +00:00
|
|
|
|
|
|
|
// Our sign.
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t sign = c == '-' ? -1 : 1;
|
2009-07-10 01:44:20 +00:00
|
|
|
// 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
|
2012-08-22 15:56:38 +00:00
|
|
|
// floating-point value while still being able to express the full int32_t
|
2009-07-10 01:44:20 +00:00
|
|
|
// 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 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...
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t exponent = 0;
|
2009-07-10 01:44:20 +00:00
|
|
|
// Sign of the exponent.
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t expSign = 1;
|
2009-07-23 01:35:07 +00:00
|
|
|
|
|
|
|
if (aToken.mHasSign) {
|
|
|
|
NS_ASSERTION(c != '.', "How did that happen?");
|
|
|
|
c = Read();
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool gotDot = (c == '.');
|
2009-07-23 01:35:07 +00:00
|
|
|
|
|
|
|
if (!gotDot) {
|
2013-02-16 23:27:53 +00:00
|
|
|
// Scan the integer part of the mantisssa
|
2009-07-23 01:35:07 +00:00
|
|
|
NS_ASSERTION(IsDigit(c), "Why did we get called?");
|
|
|
|
do {
|
2009-08-20 21:52:47 +00:00
|
|
|
intPart = 10*intPart + DecimalDigitValue(c);
|
2009-07-23 01:35:07 +00:00
|
|
|
c = Read();
|
|
|
|
// The IsDigit check will do the right thing even if Read() returns < 0
|
|
|
|
} while (IsDigit(c));
|
|
|
|
|
|
|
|
gotDot = (c == '.') && IsDigit(Peek());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gotDot) {
|
2013-02-16 23:27:53 +00:00
|
|
|
// Scan the fractional part of the mantissa.
|
2008-09-10 04:38:14 +00:00
|
|
|
c = Read();
|
2009-07-23 01:35:07 +00:00
|
|
|
NS_ASSERTION(IsDigit(c), "How did we get here?");
|
|
|
|
// Power of ten by which we need to divide our next digit
|
|
|
|
float divisor = 10;
|
|
|
|
do {
|
2009-08-20 21:52:47 +00:00
|
|
|
fracPart += DecimalDigitValue(c) / divisor;
|
2009-07-23 01:35:07 +00:00
|
|
|
divisor *= 10;
|
|
|
|
c = Read();
|
|
|
|
// The IsDigit check will do the right thing even if Read() returns < 0
|
|
|
|
} while (IsDigit(c));
|
|
|
|
}
|
2009-07-10 03:36:57 +00:00
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool gotE = false;
|
2009-07-23 01:35:07 +00:00
|
|
|
if (IsSVGMode() && (c == 'e' || c == 'E')) {
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t nextChar = Peek();
|
|
|
|
int32_t expSignChar = 0;
|
2009-07-23 01:35:07 +00:00
|
|
|
if (nextChar == '-' || nextChar == '+') {
|
|
|
|
expSignChar = Read();
|
|
|
|
nextChar = Peek();
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
2009-07-23 01:35:07 +00:00
|
|
|
if (IsDigit(nextChar)) {
|
2011-10-17 14:59:28 +00:00
|
|
|
gotE = true;
|
2009-07-23 01:35:07 +00:00
|
|
|
if (expSignChar == '-') {
|
|
|
|
expSign = -1;
|
2009-07-10 03:36:57 +00:00
|
|
|
}
|
2009-07-23 01:35:07 +00:00
|
|
|
|
|
|
|
c = Read();
|
|
|
|
NS_ASSERTION(IsDigit(c), "Peek() must have lied");
|
|
|
|
do {
|
2009-08-20 21:52:47 +00:00
|
|
|
exponent = 10*exponent + DecimalDigitValue(c);
|
2009-07-23 01:35:07 +00:00
|
|
|
c = Read();
|
|
|
|
// The IsDigit check will do the right thing even if Read() returns < 0
|
|
|
|
} while (IsDigit(c));
|
2009-07-10 01:44:20 +00:00
|
|
|
} else {
|
2009-07-23 01:35:07 +00:00
|
|
|
if (expSignChar) {
|
|
|
|
Pushback(expSignChar);
|
|
|
|
}
|
2009-07-10 01:44:20 +00:00
|
|
|
}
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsCSSTokenType type = eCSSToken_Number;
|
|
|
|
|
2008-06-03 03:17:35 +00:00
|
|
|
// Set mIntegerValid for all cases (except %, below) because we need
|
|
|
|
// it for the "2n" in :nth-child(2n).
|
2011-10-17 14:59:28 +00:00
|
|
|
aToken.mIntegerValid = false;
|
2009-07-10 01:44:20 +00:00
|
|
|
|
|
|
|
// Time to reassemble our number.
|
|
|
|
float value = float(sign * (intPart + fracPart));
|
|
|
|
if (gotE) {
|
|
|
|
// pow(), not powf(), because at least wince doesn't have the latter.
|
|
|
|
// And explicitly cast everything to doubles to avoid issues with
|
|
|
|
// overloaded pow() on Windows.
|
|
|
|
value *= pow(10.0, double(expSign * exponent));
|
|
|
|
} else if (!gotDot) {
|
2010-10-18 02:36:26 +00:00
|
|
|
// Clamp values outside of integer range.
|
|
|
|
if (sign > 0) {
|
2013-01-15 12:22:03 +00:00
|
|
|
aToken.mInteger = int32_t(std::min(intPart, double(INT32_MAX)));
|
2010-10-18 02:36:26 +00:00
|
|
|
} else {
|
2013-01-15 12:22:03 +00:00
|
|
|
aToken.mInteger = int32_t(std::max(-intPart, double(INT32_MIN)));
|
2009-07-10 01:44:20 +00:00
|
|
|
}
|
2011-10-17 14:59:28 +00:00
|
|
|
aToken.mIntegerValid = true;
|
2008-06-03 03:17:35 +00:00
|
|
|
}
|
2009-07-10 01:44:20 +00:00
|
|
|
|
|
|
|
nsString& ident = aToken.mIdent;
|
|
|
|
ident.Truncate();
|
2008-06-03 03:17:35 +00:00
|
|
|
|
|
|
|
// Look at character that terminated the number
|
1998-04-13 20:24:54 +00:00
|
|
|
if (c >= 0) {
|
2008-09-10 04:38:14 +00:00
|
|
|
if (StartsIdent(c, Peek())) {
|
2011-05-03 20:19:19 +00:00
|
|
|
if (GatherIdent(c, ident)) {
|
|
|
|
type = eCSSToken_Dimension;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
} else if ('%' == c) {
|
|
|
|
type = eCSSToken_Percentage;
|
|
|
|
value = value / 100.0f;
|
2011-10-17 14:59:28 +00:00
|
|
|
aToken.mIntegerValid = false;
|
1998-04-13 20:24:54 +00:00
|
|
|
} else {
|
|
|
|
// Put back character that stopped numeric scan
|
2007-08-21 18:29:50 +00:00
|
|
|
Pushback(c);
|
1999-02-11 06:42:02 +00:00
|
|
|
}
|
|
|
|
}
|
1998-10-26 23:22:40 +00:00
|
|
|
aToken.mNumber = value;
|
|
|
|
aToken.mType = type;
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool
|
2013-02-16 23:27:53 +00:00
|
|
|
nsCSSScanner::ScanString(int32_t aStop, nsCSSToken& aToken)
|
1998-04-13 20:24:54 +00:00
|
|
|
{
|
2005-03-18 06:56:56 +00:00
|
|
|
aToken.mIdent.SetLength(0);
|
|
|
|
aToken.mType = eCSSToken_String;
|
|
|
|
aToken.mSymbol = PRUnichar(aStop); // remember how it's quoted
|
1998-04-13 20:24:54 +00:00
|
|
|
for (;;) {
|
2013-02-16 23:27:53 +00:00
|
|
|
if (mOffset < mCount) {
|
2008-02-22 01:37:04 +00:00
|
|
|
// See how much we can consume and append in one go
|
2012-08-22 15:56:38 +00:00
|
|
|
uint32_t n = mOffset;
|
2008-02-22 01:37:04 +00:00
|
|
|
// Count number of characters that can be processed
|
|
|
|
for (;n < mCount; ++n) {
|
2013-02-16 23:27:53 +00:00
|
|
|
PRUnichar nextChar = mBuffer[n];
|
2011-05-19 22:44:14 +00:00
|
|
|
if ((nextChar == aStop) || (nextChar == '\\') ||
|
2008-02-22 01:37:04 +00:00
|
|
|
(nextChar == '\n') || (nextChar == '\r') || (nextChar == '\f')) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Add to the token what we have so far
|
|
|
|
if (n > mOffset) {
|
2013-02-16 23:27:53 +00:00
|
|
|
aToken.mIdent.Append(&mBuffer[mOffset], n - mOffset);
|
2008-02-22 01:37:04 +00:00
|
|
|
mOffset = n;
|
|
|
|
}
|
1999-02-07 21:47:48 +00:00
|
|
|
}
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t ch = Read();
|
2008-02-22 01:37:04 +00:00
|
|
|
if (ch < 0 || ch == aStop) {
|
|
|
|
break;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
2008-02-22 01:37:04 +00:00
|
|
|
if (ch == '\n') {
|
2011-03-11 17:29:44 +00:00
|
|
|
aToken.mType = eCSSToken_Bad_String;
|
2012-11-15 16:36:15 +00:00
|
|
|
mReporter->ReportUnexpected("SEUnterminatedString", aToken);
|
1998-04-13 20:24:54 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-05-19 22:44:14 +00:00
|
|
|
if (ch == '\\') {
|
2013-02-16 23:27:53 +00:00
|
|
|
if (!GatherEscape(aToken.mIdent, true)) {
|
2011-05-03 20:19:19 +00:00
|
|
|
aToken.mType = eCSSToken_Bad_String;
|
|
|
|
Pushback(ch);
|
2013-02-16 23:27:53 +00:00
|
|
|
// For strings, the only case where GatherEscape will
|
2011-05-03 20:19:19 +00:00
|
|
|
// return false is when there's a backslash to start an escape
|
|
|
|
// immediately followed by end-of-stream. In that case, the
|
|
|
|
// correct tokenization is badstring *followed* by a DELIM for
|
|
|
|
// the backslash, but as far as the author is concerned, it
|
|
|
|
// works pretty much the same as an unterminated string, so we
|
|
|
|
// use the same error message.
|
2012-11-15 16:36:15 +00:00
|
|
|
mReporter->ReportUnexpected("SEUnterminatedString", aToken);
|
2011-05-03 20:19:19 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-02-22 01:37:04 +00:00
|
|
|
} else {
|
2005-11-17 15:17:00 +00:00
|
|
|
aToken.mIdent.Append(ch);
|
1999-02-07 21:47:48 +00:00
|
|
|
}
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
1998-04-13 20:24:54 +00:00
|
|
|
}
|
2009-08-20 21:52:47 +00:00
|
|
|
|
|
|
|
// UNICODE-RANGE tokens match the regular expression
|
|
|
|
//
|
|
|
|
// u\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?
|
|
|
|
//
|
|
|
|
// However, some such tokens are "invalid". There are three valid forms:
|
|
|
|
//
|
|
|
|
// u+[0-9a-f]{x} 1 <= x <= 6
|
|
|
|
// u+[0-9a-f]{x}\?{y} 1 <= x+y <= 6
|
|
|
|
// u+[0-9a-f]{x}-[0-9a-f]{y} 1 <= x <= 6, 1 <= y <= 6
|
|
|
|
//
|
|
|
|
// All unicode-range tokens have their text recorded in mIdent; valid ones
|
|
|
|
// are also decoded into mInteger and mInteger2, and mIntegerValid is set.
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool
|
2013-02-16 23:27:53 +00:00
|
|
|
nsCSSScanner::ScanURange(int32_t aChar, nsCSSToken& aResult)
|
2009-08-20 21:52:47 +00:00
|
|
|
{
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t intro2 = Read();
|
|
|
|
int32_t ch = Peek();
|
2009-08-20 21:52:47 +00:00
|
|
|
|
|
|
|
// We should only ever be called if these things are true.
|
|
|
|
NS_ASSERTION(aChar == 'u' || aChar == 'U',
|
|
|
|
"unicode-range called with improper introducer (U)");
|
|
|
|
NS_ASSERTION(intro2 == '+',
|
|
|
|
"unicode-range called with improper introducer (+)");
|
|
|
|
|
|
|
|
// If the character immediately after the '+' is not a hex digit or
|
|
|
|
// '?', this is not really a unicode-range token; push everything
|
|
|
|
// back and scan the U as an ident.
|
|
|
|
if (!IsHexDigit(ch) && ch != '?') {
|
|
|
|
Pushback(intro2);
|
|
|
|
Pushback(aChar);
|
2013-02-16 23:27:53 +00:00
|
|
|
return ScanIdent(aChar, aResult);
|
2009-08-20 21:52:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
aResult.mIdent.Truncate();
|
|
|
|
aResult.mIdent.Append(aChar);
|
|
|
|
aResult.mIdent.Append(intro2);
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
bool valid = true;
|
|
|
|
bool haveQues = false;
|
2012-08-22 15:56:38 +00:00
|
|
|
uint32_t low = 0;
|
|
|
|
uint32_t high = 0;
|
2009-08-20 21:52:47 +00:00
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
ch = Read();
|
|
|
|
i++;
|
|
|
|
if (i == 7 || !(IsHexDigit(ch) || ch == '?')) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
aResult.mIdent.Append(ch);
|
|
|
|
if (IsHexDigit(ch)) {
|
|
|
|
if (haveQues) {
|
2011-10-17 14:59:28 +00:00
|
|
|
valid = false; // all question marks should be at the end
|
2009-08-20 21:52:47 +00:00
|
|
|
}
|
|
|
|
low = low*16 + HexDigitValue(ch);
|
|
|
|
high = high*16 + HexDigitValue(ch);
|
|
|
|
} else {
|
2011-10-17 14:59:28 +00:00
|
|
|
haveQues = true;
|
2009-08-20 21:52:47 +00:00
|
|
|
low = low*16 + 0x0;
|
|
|
|
high = high*16 + 0xF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ch == '-' && IsHexDigit(Peek())) {
|
|
|
|
if (haveQues) {
|
2011-10-17 14:59:28 +00:00
|
|
|
valid = false;
|
2009-08-20 21:52:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
aResult.mIdent.Append(ch);
|
|
|
|
high = 0;
|
|
|
|
i = 0;
|
|
|
|
for (;;) {
|
|
|
|
ch = Read();
|
|
|
|
i++;
|
|
|
|
if (i == 7 || !IsHexDigit(ch)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
aResult.mIdent.Append(ch);
|
|
|
|
high = high*16 + HexDigitValue(ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Pushback(ch);
|
|
|
|
|
|
|
|
aResult.mInteger = low;
|
|
|
|
aResult.mInteger2 = high;
|
|
|
|
aResult.mIntegerValid = valid;
|
|
|
|
aResult.mType = eCSSToken_URange;
|
2011-10-17 14:59:28 +00:00
|
|
|
return true;
|
2009-08-20 21:52:47 +00:00
|
|
|
}
|
2013-02-16 23:27:53 +00:00
|
|
|
|
|
|
|
bool
|
|
|
|
nsCSSScanner::NextURL(nsCSSToken& aToken)
|
|
|
|
{
|
|
|
|
SkipWhitespace();
|
|
|
|
|
|
|
|
int32_t ch = Read();
|
|
|
|
if (ch < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// STRING
|
|
|
|
if ((ch == '"') || (ch == '\'')) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
bool ok =
|
|
|
|
#endif
|
|
|
|
ScanString(ch, aToken);
|
|
|
|
NS_ABORT_IF_FALSE(ok, "ScanString should never fail, "
|
|
|
|
"since there's always something read");
|
|
|
|
|
|
|
|
NS_ABORT_IF_FALSE(aToken.mType == eCSSToken_String ||
|
|
|
|
aToken.mType == eCSSToken_Bad_String,
|
|
|
|
"unexpected token type");
|
|
|
|
if (MOZ_LIKELY(aToken.mType == eCSSToken_String)) {
|
|
|
|
SkipWhitespace();
|
|
|
|
if (LookAheadOrEOF(')')) {
|
|
|
|
aToken.mType = eCSSToken_URL;
|
|
|
|
} else {
|
|
|
|
aToken.mType = eCSSToken_Bad_URL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
aToken.mType = eCSSToken_Bad_URL;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process a url lexical token. A CSS1 url token can contain
|
|
|
|
// characters beyond identifier characters (e.g. '/', ':', etc.)
|
|
|
|
// Because of this the normal rules for tokenizing the input don't
|
|
|
|
// apply very well. To simplify the parser and relax some of the
|
|
|
|
// requirements on the scanner we parse url's here. If we find a
|
|
|
|
// malformed URL then we emit a token of type "Bad_URL" so that
|
|
|
|
// the CSS1 parser can ignore the invalid input. The parser must
|
|
|
|
// treat a Bad_URL token like a Function token, and process
|
|
|
|
// tokens until a matching parenthesis.
|
|
|
|
|
|
|
|
aToken.mType = eCSSToken_Bad_URL;
|
|
|
|
aToken.mSymbol = PRUnichar(0);
|
|
|
|
nsString& ident = aToken.mIdent;
|
|
|
|
ident.SetLength(0);
|
|
|
|
|
|
|
|
// start of a non-quoted url (which may be empty)
|
|
|
|
bool ok = true;
|
|
|
|
for (;;) {
|
|
|
|
if (IsURLChar(ch)) {
|
|
|
|
// A regular url character.
|
|
|
|
ident.Append(PRUnichar(ch));
|
|
|
|
} else if (ch == ')') {
|
|
|
|
// All done
|
|
|
|
break;
|
|
|
|
} else if (IsWhitespace(ch)) {
|
|
|
|
// Whitespace is allowed at the end of the URL
|
|
|
|
SkipWhitespace();
|
|
|
|
// Consume the close paren if we have it; if not we're an invalid URL.
|
|
|
|
ok = LookAheadOrEOF(')');
|
|
|
|
break;
|
|
|
|
} else if (ch == '\\') {
|
|
|
|
if (!GatherEscape(ident, false)) {
|
|
|
|
ok = false;
|
|
|
|
Pushback(ch);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// This is an invalid URL spec
|
|
|
|
ok = false;
|
|
|
|
Pushback(ch); // push it back so the parser can match tokens and
|
|
|
|
// then closing parenthesis
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ch = Read();
|
|
|
|
if (ch < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the result of the above scanning is ok then change the token
|
|
|
|
// type to a useful one.
|
|
|
|
if (ok) {
|
|
|
|
aToken.mType = eCSSToken_URL;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
nsCSSScanner::Next(nsCSSToken& aToken, bool aSkipWS)
|
|
|
|
{
|
|
|
|
for (;;) { // Infinite loop so we can restart after comments.
|
|
|
|
mTokenOffset = mOffset;
|
|
|
|
mTokenLineOffset = mLineOffset;
|
|
|
|
mTokenLineNumber = mLineNumber;
|
|
|
|
|
|
|
|
int32_t ch = Read();
|
|
|
|
if (ch < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// UNICODE-RANGE
|
|
|
|
if ((ch == 'u' || ch == 'U') && Peek() == '+')
|
|
|
|
return ScanURange(ch, aToken);
|
|
|
|
|
|
|
|
// IDENT
|
|
|
|
if (StartsIdent(ch, Peek()))
|
|
|
|
return ScanIdent(ch, aToken);
|
|
|
|
|
|
|
|
// AT_KEYWORD
|
|
|
|
if (ch == '@') {
|
|
|
|
return ScanAtKeyword(aToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
// NUMBER or DIM
|
|
|
|
if ((ch == '.') || (ch == '+') || (ch == '-')) {
|
|
|
|
int32_t nextChar = Peek();
|
|
|
|
if (IsDigit(nextChar)) {
|
|
|
|
return ScanNumber(ch, aToken);
|
|
|
|
}
|
|
|
|
else if (('.' == nextChar) && ('.' != ch)) {
|
|
|
|
nextChar = Read();
|
|
|
|
int32_t followingChar = Peek();
|
|
|
|
Pushback(nextChar);
|
|
|
|
if (IsDigit(followingChar))
|
|
|
|
return ScanNumber(ch, aToken);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (IsDigit(ch)) {
|
|
|
|
return ScanNumber(ch, aToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ID
|
|
|
|
if (ch == '#') {
|
|
|
|
return ScanHash(ch, aToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
// STRING
|
|
|
|
if ((ch == '"') || (ch == '\'')) {
|
|
|
|
return ScanString(ch, aToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
// WS
|
|
|
|
if (IsWhitespace(ch)) {
|
|
|
|
SkipWhitespace();
|
|
|
|
if (!aSkipWS) {
|
|
|
|
aToken.mType = eCSSToken_Whitespace;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
continue; // start again at the beginning
|
|
|
|
}
|
|
|
|
if (ch == '/' && !IsSVGMode()) {
|
|
|
|
int32_t nextChar = Peek();
|
|
|
|
if (nextChar == '*') {
|
|
|
|
Read();
|
|
|
|
// FIXME: Editor wants comments to be preserved (bug 60290).
|
|
|
|
SkipComment();
|
|
|
|
continue; // start again at the beginning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ch == '<') { // consume HTML comment tags
|
|
|
|
if (LookAhead('!')) {
|
|
|
|
if (LookAhead('-')) {
|
|
|
|
if (LookAhead('-')) {
|
|
|
|
aToken.mType = eCSSToken_HTMLComment;
|
|
|
|
aToken.mIdent.AssignLiteral("<!--");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Pushback('-');
|
|
|
|
}
|
|
|
|
Pushback('!');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ch == '-') { // check for HTML comment end
|
|
|
|
if (LookAhead('-')) {
|
|
|
|
if (LookAhead('>')) {
|
|
|
|
aToken.mType = eCSSToken_HTMLComment;
|
|
|
|
aToken.mIdent.AssignLiteral("-->");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Pushback('-');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// INCLUDES ("~=") and DASHMATCH ("|=")
|
|
|
|
if (( ch == '|' ) || ( ch == '~' ) || ( ch == '^' ) ||
|
|
|
|
( ch == '$' ) || ( ch == '*' )) {
|
|
|
|
int32_t nextChar = Read();
|
|
|
|
if ( nextChar == '=' ) {
|
|
|
|
if (ch == '~') {
|
|
|
|
aToken.mType = eCSSToken_Includes;
|
|
|
|
}
|
|
|
|
else if (ch == '|') {
|
|
|
|
aToken.mType = eCSSToken_Dashmatch;
|
|
|
|
}
|
|
|
|
else if (ch == '^') {
|
|
|
|
aToken.mType = eCSSToken_Beginsmatch;
|
|
|
|
}
|
|
|
|
else if (ch == '$') {
|
|
|
|
aToken.mType = eCSSToken_Endsmatch;
|
|
|
|
}
|
|
|
|
else if (ch == '*') {
|
|
|
|
aToken.mType = eCSSToken_Containsmatch;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} else if (nextChar >= 0) {
|
|
|
|
Pushback(nextChar);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
aToken.mType = eCSSToken_Symbol;
|
|
|
|
aToken.mSymbol = ch;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|