Index: Source/core/css/CSSParser.cpp |
diff --git a/Source/core/css/CSSParser.cpp b/Source/core/css/CSSParser.cpp |
index 6e82eb085957a8461fbd244cdca13925f97cec6c..e93cfed27a075408e0069529a44bba2a8c48dcdf 100644 |
--- a/Source/core/css/CSSParser.cpp |
+++ b/Source/core/css/CSSParser.cpp |
@@ -7,6 +7,7 @@ |
* Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) |
* Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. |
* Copyright (C) 2012 Intel Corporation. All rights reserved. |
+ * Copyright (C) 2013 Opera Software ASA. All rights reserved. |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Library General Public |
@@ -319,6 +320,8 @@ CSSParser::CSSParser(const CSSParserContext& context) |
#if ENABLE(CSS_DEVICE_ADAPTATION) |
, m_inViewport(false) |
#endif |
+ , m_reEmissionState(DontEmit) |
+ , m_storedBlockLevel(0) |
{ |
#if YYDEBUG > 0 |
cssyydebug = 1; |
@@ -1437,9 +1440,10 @@ PassOwnPtr<MediaQuery> CSSParser::parseMediaQuery(const String& string) |
ASSERT(!m_mediaQuery); |
- // can't use { because tokenizer state switches from mediaquery to initial state when it sees { token. |
- // instead insert one " " (which is WHITESPACE in CSSGrammar.y) |
- setupParser("@-webkit-mediaquery ", string, "} "); |
+ // Can't use { because tokenizer state switches from mediaquery to initial |
+ // state when it sees { token. instead insert one " " (which is WHITESPACE |
+ // in CSSGrammar.y). Also, can't use } because of error handling. |
+ setupParser("@-webkit-mediaquery ", string, ""); |
cssyyparse(this); |
return m_mediaQuery.release(); |
@@ -9242,6 +9246,8 @@ enum CharacterType { |
CharacterXor, |
CharacterVerticalBar, |
CharacterTilde, |
+ CharacterOpeningBracket, |
+ CharacterClosingBracket |
}; |
// 128 ASCII codes |
@@ -9286,7 +9292,7 @@ static const CharacterType typesOfASCIICharacters[128] = { |
/* 37 - % */ CharacterOther, |
/* 38 - & */ CharacterOther, |
/* 39 - ' */ CharacterQuote, |
-/* 40 - ( */ CharacterOther, |
+/* 40 - ( */ CharacterOpeningBracket, |
/* 41 - ) */ CharacterEndNthChild, |
/* 42 - * */ CharacterAsterisk, |
/* 43 - + */ CharacterPlus, |
@@ -9337,9 +9343,9 @@ static const CharacterType typesOfASCIICharacters[128] = { |
/* 88 - X */ CharacterIdentifierStart, |
/* 89 - Y */ CharacterIdentifierStart, |
/* 90 - Z */ CharacterIdentifierStart, |
-/* 91 - [ */ CharacterOther, |
+/* 91 - [ */ CharacterOpeningBracket, |
/* 92 - \ */ CharacterBackSlash, |
-/* 93 - ] */ CharacterOther, |
+/* 93 - ] */ CharacterClosingBracket, |
/* 94 - ^ */ CharacterXor, |
/* 95 - _ */ CharacterIdentifierStart, |
/* 96 - ` */ CharacterOther, |
@@ -9371,7 +9377,7 @@ static const CharacterType typesOfASCIICharacters[128] = { |
/* 122 - z */ CharacterIdentifierStart, |
/* 123 - { */ CharacterEndMediaQuery, |
/* 124 - | */ CharacterVerticalBar, |
-/* 125 - } */ CharacterOther, |
+/* 125 - } */ CharacterClosingBracket, |
/* 126 - ~ */ CharacterTilde, |
/* 127 - Delete */ CharacterOther, |
}; |
@@ -10331,6 +10337,14 @@ inline void CSSParser::detectSupportsToken(int length) |
template <typename SrcCharacterType> |
int CSSParser::realLex(void* yylvalWithoutType) |
{ |
+ if (m_reEmissionState == ReEmit) { |
+ m_reEmissionState = DidReEmit; |
+ return token(); |
+ } |
+ |
+ if (m_reEmissionState == DidReEmit) |
+ m_reEmissionState = DontEmit; |
+ |
YYSTYPE* yylval = static_cast<YYSTYPE*>(yylvalWithoutType); |
// Write pointer for the next character. |
SrcCharacterType* result; |
@@ -10374,22 +10388,12 @@ restartAfterComment: |
} |
m_token = FUNCTION; |
- bool shouldSkipParenthesis = true; |
- if (!hasEscape) { |
- bool detected = detectFunctionTypeToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); |
- if (!detected && m_parsingMode == MediaQueryMode) { |
- // ... and(max-width: 480px) ... looks like a function, but in fact it is not, |
- // so run more detection code in the MediaQueryMode. |
- detectMediaQueryToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); |
- shouldSkipParenthesis = false; |
- } |
- } |
- if (LIKELY(shouldSkipParenthesis)) { |
- ++currentCharacter<SrcCharacterType>(); |
- ++result; |
- ++yylval->string.m_length; |
- } |
+ if (!hasEscape) |
+ detectFunctionTypeToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); |
+ ++currentCharacter<SrcCharacterType>(); |
+ ++result; |
+ ++yylval->string.m_length; |
if (token() == URI) { |
m_token = FUNCTION; |
@@ -10399,6 +10403,12 @@ restartAfterComment: |
else |
parseURI<UChar>(yylval->string); |
} |
+ |
+ // URI consumes the ')'. For other function tokens, push the |
+ // opening parenthesis to match the closing one when it comes. |
+ if (token() != URI) |
+ m_brackets.push('('); |
+ |
} else if (UNLIKELY(m_parsingMode != NormalMode) && !hasEscape) { |
if (m_parsingMode == MediaQueryMode) |
detectMediaQueryToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); |
@@ -10516,6 +10526,7 @@ restartAfterComment: |
m_token = VAR_DEFINITION; |
else if (*currentCharacter<SrcCharacterType>() == '(') { |
m_token = FUNCTION; |
+ m_brackets.push('('); |
if (!hasEscape) |
detectDashToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); |
++currentCharacter<SrcCharacterType>(); |
@@ -10561,6 +10572,8 @@ restartAfterComment: |
break; |
case CharacterNull: |
+ if (m_brackets.level() > 0) |
+ m_token = m_brackets.pop(); |
// Do not advance pointer at the end of input. |
--currentCharacter<SrcCharacterType>(); |
break; |
@@ -10579,11 +10592,14 @@ restartAfterComment: |
case CharacterEndMediaQuery: |
if (m_parsingMode == MediaQueryMode) |
m_parsingMode = NormalMode; |
+ if (m_token == '{') |
+ m_brackets.push('{'); |
break; |
case CharacterEndNthChild: |
if (m_parsingMode == NthChildMode) |
m_parsingMode = NormalMode; |
+ m_brackets.pop(')'); |
break; |
case CharacterQuote: |
@@ -10731,6 +10747,14 @@ restartAfterComment: |
} |
break; |
+ case CharacterOpeningBracket: |
+ m_brackets.push(m_token); |
+ break; |
+ |
+ case CharacterClosingBracket: |
+ m_brackets.pop(m_token); |
+ break; |
+ |
default: |
ASSERT_NOT_REACHED(); |
break; |
@@ -11479,6 +11503,50 @@ bool CSSParser::parseViewportShorthand(CSSPropertyID propId, CSSPropertyID first |
} |
#endif |
+void CSSParser::markMediaListStart() |
+{ |
+ m_storedBlockLevel = m_brackets.level(); |
+ if (m_storedBlockLevel > 0 && token() == m_brackets.top()) { |
+ // Don't take the currently pushed lookahead token into account. |
+ // This could happen for "@media{}", "@media()", etc. |
+ --m_storedBlockLevel; |
+ } |
+} |
+ |
+MediaQuery* CSSParser::recoverMediaQuery() |
+{ |
+ size_t blockLevel = m_brackets.level(); |
+ if (blockLevel >= m_storedBlockLevel) { |
+ YYSTYPE yyval; |
+ |
+ while (token() != TOKEN_EOF) { |
+ // End media query at ',', ';', or '{' at the same block level as the |
+ // media list started. |
+ if (blockLevel == m_storedBlockLevel && (token() == ',' || token() == ';')) |
+ break; |
+ // The '{' token will have increased the block level by 1 already. |
+ if (blockLevel == m_storedBlockLevel + 1 && token() == '{') |
+ break; |
+ |
+ lex(&yyval); |
+ blockLevel = m_brackets.level(); |
+ } |
+ } else { |
+ // Recovering from error token when we are at a block level above the |
+ // block level where the media list started. That should only happen |
+ // when synchronizing on a closing '}' just one level above the stored |
+ // level. Typically happens when an @media rule is terminated |
+ // prematurely: |
+ // |
+ // "@supports (color: red) { @media all and }" |
+ ASSERT(token() == '}' && blockLevel == m_storedBlockLevel - 1); |
+ } |
+ |
+ markCurrentTokenForReEmission(); |
+ |
+ return createFloatingMediaQuery(MediaQuery::Not, "all", sinkFloatingMediaQueryExpList(createFloatingMediaQueryExpList())); |
+} |
+ |
template <typename CharacterType> |
static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length) |
{ |