This source file includes following definitions.
- isNameStart
- isNameChar
- twoCharsAreValidEscape
- reconsume
- consume
- consume
- whiteSpace
- leftParenthesis
- rightParenthesis
- leftBracket
- rightBracket
- leftBrace
- rightBrace
- plusOrFullStop
- comma
- hyphenMinus
- solidus
- colon
- semiColon
- reverseSolidus
- asciiDigit
- nameStart
- stringStart
- endOfFile
- tokenize
- nextToken
- getSign
- getInteger
- getFraction
- getExponent
- consumeNumber
- consumeNumericToken
- consumeIdentLikeToken
- isNewLine
- consumeStringTokenUntil
- consumeUntilNonWhitespace
- consumeUntilCommentEndFound
- consumeIfNext
- consumeName
- consumeEscape
- nextTwoCharsAreValidEscape
- nextCharsAreNumber
- nextCharsAreIdentifier
#include "config.h"
#include "core/css/parser/MediaQueryTokenizer.h"
namespace WebCore {
#include "MediaQueryTokenizerCodepoints.cpp"
}
#include "core/css/parser/MediaQueryInputStream.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "wtf/unicode/CharacterNames.h"
namespace WebCore {
static bool isNameStart(UChar c)
{
if (isASCIIAlpha(c))
return true;
if (c == '_')
return true;
return !isASCII(c);
}
static bool isNameChar(UChar c)
{
return isNameStart(c) || isASCIIDigit(c) || c == '-';
}
static bool twoCharsAreValidEscape(UChar first, UChar second)
{
return ((first == '\\') && (second != '\n') && (second != kEndOfFileMarker));
}
MediaQueryTokenizer::MediaQueryTokenizer(MediaQueryInputStream& inputStream)
: m_input(inputStream)
{
}
void MediaQueryTokenizer::reconsume(UChar c)
{
m_input.pushBack(c);
}
UChar MediaQueryTokenizer::consume()
{
UChar current = m_input.currentInputChar();
m_input.advance();
return current;
}
void MediaQueryTokenizer::consume(unsigned offset)
{
m_input.advance(offset);
}
MediaQueryToken MediaQueryTokenizer::whiteSpace(UChar cc)
{
consumeUntilNonWhitespace();
return MediaQueryToken(WhitespaceToken);
}
MediaQueryToken MediaQueryTokenizer::leftParenthesis(UChar cc)
{
return MediaQueryToken(LeftParenthesisToken);
}
MediaQueryToken MediaQueryTokenizer::rightParenthesis(UChar cc)
{
return MediaQueryToken(RightParenthesisToken);
}
MediaQueryToken MediaQueryTokenizer::leftBracket(UChar cc)
{
return MediaQueryToken(LeftBracketToken);
}
MediaQueryToken MediaQueryTokenizer::rightBracket(UChar cc)
{
return MediaQueryToken(RightBracketToken);
}
MediaQueryToken MediaQueryTokenizer::leftBrace(UChar cc)
{
return MediaQueryToken(LeftBraceToken);
}
MediaQueryToken MediaQueryTokenizer::rightBrace(UChar cc)
{
return MediaQueryToken(RightBraceToken);
}
MediaQueryToken MediaQueryTokenizer::plusOrFullStop(UChar cc)
{
if (nextCharsAreNumber()) {
reconsume(cc);
return consumeNumericToken();
}
return MediaQueryToken(DelimiterToken, cc);
}
MediaQueryToken MediaQueryTokenizer::comma(UChar cc)
{
return MediaQueryToken(CommaToken);
}
MediaQueryToken MediaQueryTokenizer::hyphenMinus(UChar cc)
{
if (nextCharsAreNumber()) {
reconsume(cc);
return consumeNumericToken();
}
if (nextCharsAreIdentifier()) {
reconsume(cc);
return consumeIdentLikeToken();
}
return MediaQueryToken(DelimiterToken, cc);
}
MediaQueryToken MediaQueryTokenizer::solidus(UChar cc)
{
if (consumeIfNext('*')) {
return consumeUntilCommentEndFound()? MediaQueryToken(CommentToken): MediaQueryToken(EOFToken);
}
return MediaQueryToken(DelimiterToken, cc);
}
MediaQueryToken MediaQueryTokenizer::colon(UChar cc)
{
return MediaQueryToken(ColonToken);
}
MediaQueryToken MediaQueryTokenizer::semiColon(UChar cc)
{
return MediaQueryToken(SemicolonToken);
}
MediaQueryToken MediaQueryTokenizer::reverseSolidus(UChar cc)
{
if (twoCharsAreValidEscape(cc, m_input.currentInputChar())) {
reconsume(cc);
return consumeIdentLikeToken();
}
return MediaQueryToken(DelimiterToken, cc);
}
MediaQueryToken MediaQueryTokenizer::asciiDigit(UChar cc)
{
reconsume(cc);
return consumeNumericToken();
}
MediaQueryToken MediaQueryTokenizer::nameStart(UChar cc)
{
reconsume(cc);
return consumeIdentLikeToken();
}
MediaQueryToken MediaQueryTokenizer::stringStart(UChar cc)
{
return consumeStringTokenUntil(cc);
}
MediaQueryToken MediaQueryTokenizer::endOfFile(UChar cc)
{
return MediaQueryToken(EOFToken);
}
void MediaQueryTokenizer::tokenize(String string, Vector<MediaQueryToken>& outTokens)
{
MediaQueryInputStream input(string);
MediaQueryTokenizer tokenizer(input);
while (true) {
MediaQueryToken token = tokenizer.nextToken();
outTokens.append(token);
if (token.type() == EOFToken)
return;
}
}
MediaQueryToken MediaQueryTokenizer::nextToken()
{
UChar cc = consume();
CodePoint codePointFunc = 0;
if (isASCII(cc)) {
ASSERT_WITH_SECURITY_IMPLICATION(cc < codePointsNumber);
codePointFunc = codePoints[cc];
} else {
codePointFunc = &MediaQueryTokenizer::nameStart;
}
if (codePointFunc)
return ((this)->*(codePointFunc))(cc);
return MediaQueryToken(DelimiterToken, cc);
}
static int getSign(MediaQueryInputStream& input, unsigned& offset)
{
int sign = 1;
if (input.currentInputChar() == '+') {
++offset;
} else if (input.peek(offset) == '-') {
sign = -1;
++offset;
}
return sign;
}
static unsigned long long getInteger(MediaQueryInputStream& input, unsigned& offset)
{
unsigned intStartPos = offset;
offset = input.skipWhilePredicate<isASCIIDigit>(offset);
unsigned intEndPos = offset;
return input.getUInt(intStartPos, intEndPos);
}
static double getFraction(MediaQueryInputStream& input, unsigned& offset, unsigned& digitsNumber)
{
unsigned fractionStartPos = 0;
unsigned fractionEndPos = 0;
if (input.peek(offset) == '.' && isASCIIDigit(input.peek(++offset))) {
fractionStartPos = offset - 1;
offset = input.skipWhilePredicate<isASCIIDigit>(offset);
fractionEndPos = offset;
}
digitsNumber = fractionEndPos- fractionStartPos;
return input.getDouble(fractionStartPos, fractionEndPos);
}
static unsigned long long getExponent(MediaQueryInputStream& input, unsigned& offset, int sign)
{
unsigned exponentStartPos = 0;
unsigned exponentEndPos = 0;
if ((input.peek(offset) == 'E' || input.peek(offset) == 'e')) {
int offsetBeforeExponent = offset;
++offset;
if (input.peek(offset) == '+') {
++offset;
} else if (input.peek(offset) =='-') {
sign = -1;
++offset;
}
exponentStartPos = offset;
offset = input.skipWhilePredicate<isASCIIDigit>(offset);
exponentEndPos = offset;
if (exponentEndPos == exponentStartPos)
offset = offsetBeforeExponent;
}
return input.getUInt(exponentStartPos, exponentEndPos);
}
MediaQueryToken MediaQueryTokenizer::consumeNumber()
{
ASSERT(nextCharsAreNumber());
NumericValueType type = IntegerValueType;
double value = 0;
unsigned offset = 0;
int exponentSign = 1;
unsigned fractionDigits;
int sign = getSign(m_input, offset);
unsigned long long integerPart = getInteger(m_input, offset);
double fractionPart = getFraction(m_input, offset, fractionDigits);
unsigned long long exponentPart = getExponent(m_input, offset, exponentSign);
double exponent = pow(10, (float)exponentSign * (double)exponentPart);
value = (double)sign * ((double)integerPart + fractionPart) * exponent;
m_input.advance(offset);
if (fractionDigits > 0)
type = NumberValueType;
return MediaQueryToken(NumberToken, value, type);
}
MediaQueryToken MediaQueryTokenizer::consumeNumericToken()
{
MediaQueryToken token = consumeNumber();
if (nextCharsAreIdentifier())
token.convertToDimensionWithUnit(consumeName());
else if (consumeIfNext('%'))
token.convertToPercentage();
return token;
}
MediaQueryToken MediaQueryTokenizer::consumeIdentLikeToken()
{
String name = consumeName();
if (consumeIfNext('('))
return MediaQueryToken(FunctionToken, name);
return MediaQueryToken(IdentToken, name);
}
static bool isNewLine(UChar cc)
{
return (cc == '\r' || cc == '\n' || cc == '\f');
}
MediaQueryToken MediaQueryTokenizer::consumeStringTokenUntil(UChar endingCodePoint)
{
StringBuilder output;
while (true) {
UChar cc = consume();
if (cc == endingCodePoint || cc == kEndOfFileMarker) {
if (cc == kEndOfFileMarker)
reconsume(cc);
return MediaQueryToken(StringToken, output.toString());
}
if (isNewLine(cc)) {
reconsume(cc);
return MediaQueryToken(BadStringToken);
}
if (cc == '\\') {
if (m_input.currentInputChar() == kEndOfFileMarker)
continue;
if (isNewLine(m_input.currentInputChar()))
consume();
else
output.append(consumeEscape());
} else {
output.append(cc);
}
}
}
void MediaQueryTokenizer::consumeUntilNonWhitespace()
{
while (isHTMLSpace<UChar>(m_input.currentInputChar()))
consume();
}
bool MediaQueryTokenizer::consumeUntilCommentEndFound()
{
UChar c = consume();
while (true) {
if (c == kEndOfFileMarker)
return false;
if (c != '*') {
c = consume();
continue;
}
c = consume();
if (c == '/')
break;
}
return true;
}
bool MediaQueryTokenizer::consumeIfNext(UChar character)
{
if (m_input.currentInputChar() == character) {
consume();
return true;
}
return false;
}
String MediaQueryTokenizer::consumeName()
{
StringBuilder result;
while (true) {
UChar cc = consume();
if (isNameChar(cc)) {
result.append(cc);
continue;
}
if (twoCharsAreValidEscape(cc, m_input.currentInputChar())) {
result.append(consumeEscape());
continue;
}
reconsume(cc);
return result.toString();
}
}
UChar MediaQueryTokenizer::consumeEscape()
{
UChar cc = consume();
ASSERT(cc != '\n');
if (isASCIIHexDigit(cc)) {
unsigned consumedHexDigits = 1;
StringBuilder hexChars;
hexChars.append(cc);
while (consumedHexDigits < 6 && isASCIIHexDigit(m_input.currentInputChar())) {
cc = consume();
hexChars.append(cc);
consumedHexDigits++;
};
bool ok = false;
UChar codePoint = hexChars.toString().toUIntStrict(&ok, 16);
if (!ok)
return WTF::Unicode::replacementCharacter;
return codePoint;
}
if (cc == kEndOfFileMarker)
return WTF::Unicode::replacementCharacter;
return cc;
}
bool MediaQueryTokenizer::nextTwoCharsAreValidEscape(unsigned offset)
{
if (m_input.leftChars() < offset + 1)
return false;
return twoCharsAreValidEscape(m_input.peek(offset), m_input.peek(offset + 1));
}
bool MediaQueryTokenizer::nextCharsAreNumber()
{
UChar first = m_input.currentInputChar();
UChar second = m_input.peek(1);
if (isASCIIDigit(first))
return true;
if (first == '+' || first == '-')
return ((isASCIIDigit(second)) || (second == '.' && isASCIIDigit(m_input.peek(2))));
if (first =='.')
return (isASCIIDigit(second));
return false;
}
bool MediaQueryTokenizer::nextCharsAreIdentifier()
{
UChar firstChar = m_input.currentInputChar();
if (isNameStart(firstChar) || nextTwoCharsAreValidEscape(0))
return true;
if (firstChar == '-') {
if (isNameStart(m_input.peek(1)))
return true;
return nextTwoCharsAreValidEscape(1);
}
return false;
}
}