Initial landing of CSS Parser Tokenization (and files to support that). Not used...
authorhyatt@apple.com <hyatt@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 28 Aug 2016 15:46:11 +0000 (15:46 +0000)
committerhyatt@apple.com <hyatt@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 28 Aug 2016 15:46:11 +0000 (15:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=161174

This code is imported from Blink and is their CSS parser (modified to work in WebKit).
It is from commit e4bb90df7f2ed8a63975b5ed27a1b488cb9b146f, Mon Aug 22 2016.

Reviewed by Simon Fraser.

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:
* css/CSSMarkup.cpp: Added.
(WebCore::isCSSTokenizerIdentifier):
(WebCore::serializeCharacter):
(WebCore::serializeCharacterAsCodePoint):
(WebCore::serializeIdentifier):
(WebCore::serializeString):
(WebCore::serializeURI):
(WebCore::serializeFontFamily):
* css/CSSMarkup.h: Added.
* css/CSSOMUtils.cpp: Removed.
* css/CSSOMUtils.h: Removed.
* css/CSSSelector.cpp:
* css/parser/CSSParserFastPaths.cpp: Added.
* css/parser/CSSParserFastPaths.h: Added.
* css/parser/CSSParserIdioms.h: Added.
(WebCore::isCSSSpace):
(WebCore::isNameStartCodePoint):
(WebCore::isNameCodePoint):
* css/parser/CSSParserObserver.h: Added.
(WebCore::CSSParserObserver::~CSSParserObserver):
* css/parser/CSSParserObserverWrapper.cpp: Added.
(WebCore::CSSParserObserverWrapper::startOffset):
(WebCore::CSSParserObserverWrapper::previousTokenStartOffset):
(WebCore::CSSParserObserverWrapper::endOffset):
(WebCore::CSSParserObserverWrapper::skipCommentsBefore):
(WebCore::CSSParserObserverWrapper::yieldCommentsBefore):
* css/parser/CSSParserObserverWrapper.h: Added.
(WebCore::CSSParserObserverWrapper::CSSParserObserverWrapper):
(WebCore::CSSParserObserverWrapper::observer):
(WebCore::CSSParserObserverWrapper::addComment):
(WebCore::CSSParserObserverWrapper::addToken):
(WebCore::CSSParserObserverWrapper::finalizeConstruction):
* css/parser/CSSParserToken.cpp: Added.
(WebCore::cssPrimitiveValueUnitFromTrie):
(WebCore::stringToUnitType):
(WebCore::CSSParserToken::CSSParserToken):
(WebCore::CSSParserToken::convertToDimensionWithUnit):
(WebCore::CSSParserToken::convertToPercentage):
(WebCore::CSSParserToken::delimiter):
(WebCore::CSSParserToken::numericSign):
(WebCore::CSSParserToken::numericValueType):
(WebCore::CSSParserToken::numericValue):
(WebCore::CSSParserToken::parseAsUnresolvedCSSPropertyID):
(WebCore::CSSParserToken::id):
(WebCore::CSSParserToken::functionId):
(WebCore::CSSParserToken::hasStringBacking):
(WebCore::CSSParserToken::copyWithUpdatedString):
(WebCore::CSSParserToken::valueDataCharRawEqual):
(WebCore::CSSParserToken::operator==):
(WebCore::CSSParserToken::serialize):
* css/parser/CSSParserToken.h: Added.
(WebCore::CSSParserToken::operator!=):
(WebCore::CSSParserToken::type):
(WebCore::CSSParserToken::value):
(WebCore::CSSParserToken::getHashTokenType):
(WebCore::CSSParserToken::getBlockType):
(WebCore::CSSParserToken::unitType):
(WebCore::CSSParserToken::unicodeRangeStart):
(WebCore::CSSParserToken::unicodeRangeEnd):
(WebCore::CSSParserToken::initValueFromStringView):
* css/parser/CSSParserTokenRange.cpp: Added.
(WebCore::CSSParserTokenRange::eofToken):
(WebCore::CSSParserTokenRange::makeSubRange):
(WebCore::CSSParserTokenRange::consumeBlock):
(WebCore::CSSParserTokenRange::consumeComponentValue):
(WebCore::CSSParserTokenRange::serialize):
* css/parser/CSSParserTokenRange.h: Added.
(WebCore::CSSParserTokenRange::CSSParserTokenRange):
(WebCore::CSSParserTokenRange::atEnd):
(WebCore::CSSParserTokenRange::end):
(WebCore::CSSParserTokenRange::peek):
(WebCore::CSSParserTokenRange::consume):
(WebCore::CSSParserTokenRange::consumeIncludingWhitespace):
(WebCore::CSSParserTokenRange::consumeWhitespace):
(WebCore::CSSParserTokenRange::begin):
* css/parser/CSSPropertyParser.cpp: Added.
(WebCore::hasPrefix):
(WebCore::cssPropertyID):
(WebCore::cssPropertyNameIOSAliasing):
(WebCore::isAppleLegacyCssValueKeyword):
(WebCore::cssValueKeywordID):
(WebCore::unresolvedCSSPropertyID):
* css/parser/CSSPropertyParser.h: Added.
(WebCore::CSSPropertyParser::inQuirksMode):
* css/parser/CSSPropertyParserHelpers.cpp: Added.
* css/parser/CSSPropertyParserHelpers.h: Added.
(WebCore::CSSPropertyParserHelpers::identMatches):
(WebCore::CSSPropertyParserHelpers::consumeIdent):
(WebCore::CSSPropertyParserHelpers::isCSSWideKeyword):
* css/parser/CSSTokenizer.cpp: Added.
(WebCore::CSSTokenizer::Scope::Scope):
(WebCore::CSSTokenizer::Scope::tokenRange):
(WebCore::CSSTokenizer::Scope::tokenCount):
(WebCore::isNewLine):
(WebCore::twoCharsAreValidEscape):
(WebCore::CSSTokenizer::CSSTokenizer):
(WebCore::CSSTokenizer::reconsume):
(WebCore::CSSTokenizer::consume):
(WebCore::CSSTokenizer::whiteSpace):
(WebCore::CSSTokenizer::blockStart):
(WebCore::CSSTokenizer::blockEnd):
(WebCore::CSSTokenizer::leftParenthesis):
(WebCore::CSSTokenizer::rightParenthesis):
(WebCore::CSSTokenizer::leftBracket):
(WebCore::CSSTokenizer::rightBracket):
(WebCore::CSSTokenizer::leftBrace):
(WebCore::CSSTokenizer::rightBrace):
(WebCore::CSSTokenizer::plusOrFullStop):
(WebCore::CSSTokenizer::asterisk):
(WebCore::CSSTokenizer::lessThan):
(WebCore::CSSTokenizer::comma):
(WebCore::CSSTokenizer::hyphenMinus):
(WebCore::CSSTokenizer::solidus):
(WebCore::CSSTokenizer::colon):
(WebCore::CSSTokenizer::semiColon):
(WebCore::CSSTokenizer::hash):
(WebCore::CSSTokenizer::circumflexAccent):
(WebCore::CSSTokenizer::dollarSign):
(WebCore::CSSTokenizer::verticalLine):
(WebCore::CSSTokenizer::tilde):
(WebCore::CSSTokenizer::commercialAt):
(WebCore::CSSTokenizer::reverseSolidus):
(WebCore::CSSTokenizer::asciiDigit):
(WebCore::CSSTokenizer::letterU):
(WebCore::CSSTokenizer::nameStart):
(WebCore::CSSTokenizer::stringStart):
(WebCore::CSSTokenizer::endOfFile):
(WebCore::CSSTokenizer::nextToken):
(WebCore::CSSTokenizer::consumeNumber):
(WebCore::CSSTokenizer::consumeNumericToken):
(WebCore::CSSTokenizer::consumeIdentLikeToken):
(WebCore::CSSTokenizer::consumeStringTokenUntil):
(WebCore::CSSTokenizer::consumeUnicodeRange):
(WebCore::isNonPrintableCodePoint):
(WebCore::CSSTokenizer::consumeUrlToken):
(WebCore::CSSTokenizer::consumeBadUrlRemnants):
(WebCore::CSSTokenizer::consumeSingleWhitespaceIfNext):
(WebCore::CSSTokenizer::consumeUntilCommentEndFound):
(WebCore::CSSTokenizer::consumeIfNext):
(WebCore::CSSTokenizer::consumeName):
(WebCore::CSSTokenizer::consumeEscape):
(WebCore::CSSTokenizer::nextTwoCharsAreValidEscape):
(WebCore::CSSTokenizer::nextCharsAreNumber):
(WebCore::CSSTokenizer::nextCharsAreIdentifier):
(WebCore::CSSTokenizer::registerString):
* css/parser/CSSTokenizer.h: Added.
(WebCore::CSSTokenizer::Scope::storeString):
* css/parser/CSSTokenizerInputStream.cpp: Added.
(WebCore::CSSTokenizerInputStream::CSSTokenizerInputStream):
(WebCore::CSSTokenizerInputStream::advanceUntilNonWhitespace):
(WebCore::CSSTokenizerInputStream::getDouble):
* css/parser/CSSTokenizerInputStream.h: Added.
(WebCore::CSSTokenizerInputStream::nextInputChar):
(WebCore::CSSTokenizerInputStream::peekWithoutReplacement):
(WebCore::CSSTokenizerInputStream::advance):
(WebCore::CSSTokenizerInputStream::pushBack):
(WebCore::CSSTokenizerInputStream::skipWhilePredicate):
(WebCore::CSSTokenizerInputStream::length):
(WebCore::CSSTokenizerInputStream::offset):
(WebCore::CSSTokenizerInputStream::rangeAt):
* platform/Length.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@205103 268f45cc-cd09-0410-ab3c-d52691b4dbfc

29 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/css/CSSAllInOne.cpp
Source/WebCore/css/CSSMarkup.cpp [new file with mode: 0644]
Source/WebCore/css/CSSMarkup.h [new file with mode: 0644]
Source/WebCore/css/CSSOMUtils.cpp [deleted file]
Source/WebCore/css/CSSSelector.cpp
Source/WebCore/css/DOMCSSNamespace.cpp
Source/WebCore/css/parser/CSSParser.cpp
Source/WebCore/css/parser/CSSParserFastPaths.cpp [new file with mode: 0644]
Source/WebCore/css/parser/CSSParserFastPaths.h [new file with mode: 0644]
Source/WebCore/css/parser/CSSParserIdioms.h [moved from Source/WebCore/css/CSSOMUtils.h with 66% similarity]
Source/WebCore/css/parser/CSSParserObserver.h [new file with mode: 0644]
Source/WebCore/css/parser/CSSParserObserverWrapper.cpp [new file with mode: 0644]
Source/WebCore/css/parser/CSSParserObserverWrapper.h [new file with mode: 0644]
Source/WebCore/css/parser/CSSParserToken.cpp [new file with mode: 0644]
Source/WebCore/css/parser/CSSParserToken.h [new file with mode: 0644]
Source/WebCore/css/parser/CSSParserTokenRange.cpp [new file with mode: 0644]
Source/WebCore/css/parser/CSSParserTokenRange.h [new file with mode: 0644]
Source/WebCore/css/parser/CSSPropertyParser.cpp [new file with mode: 0644]
Source/WebCore/css/parser/CSSPropertyParser.h [new file with mode: 0644]
Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp [new file with mode: 0644]
Source/WebCore/css/parser/CSSPropertyParserHelpers.h [new file with mode: 0644]
Source/WebCore/css/parser/CSSTokenizer.cpp [new file with mode: 0644]
Source/WebCore/css/parser/CSSTokenizer.h [new file with mode: 0644]
Source/WebCore/css/parser/CSSTokenizerInputStream.cpp [new file with mode: 0644]
Source/WebCore/css/parser/CSSTokenizerInputStream.h [new file with mode: 0644]
Source/WebCore/platform/Length.h

index ed1f7c8..d06dc7a 100644 (file)
@@ -1337,9 +1337,9 @@ set(WebCore_SOURCES
     css/CSSKeyframeRule.cpp
     css/CSSKeyframesRule.cpp
     css/CSSLineBoxContainValue.cpp
+    css/CSSMarkup.cpp
     css/CSSMediaRule.cpp
     css/CSSNamedImageValue.cpp
-    css/CSSOMUtils.cpp
     css/CSSPageRule.cpp
     css/CSSPrimitiveValue.cpp
     css/CSSProperty.cpp
@@ -1406,7 +1406,15 @@ set(WebCore_SOURCES
     css/WebKitCSSViewportRule.cpp
 
     css/parser/CSSParser.cpp
+    css/parser/CSSParserFastPaths.cpp
+    css/parser/CSSParserObserverWrapper.cpp
+    css/parser/CSSParserToken.cpp
+    css/parser/CSSParserTokenRange.cpp
     css/parser/CSSParserValues.cpp
+    css/parser/CSSPropertyParser.cpp
+    css/parser/CSSPropertyParserHelpers.cpp
+    css/parser/CSSTokenizer.cpp
+    css/parser/CSSTokenizerInputStream.cpp
     css/parser/SVGCSSParser.cpp
 
     cssjit/SelectorCompiler.cpp
index 6a9ed54..2abf647 100644 (file)
@@ -1,3 +1,177 @@
+2016-08-24  Dave Hyatt  <hyatt@apple.com>
+
+        Initial landing of CSS Parser Tokenization (and files to support that). Not used yet.
+        https://bugs.webkit.org/show_bug.cgi?id=161174
+
+        This code is imported from Blink and is their CSS parser (modified to work in WebKit).
+        It is from commit e4bb90df7f2ed8a63975b5ed27a1b488cb9b146f, Mon Aug 22 2016.
+
+        Reviewed by Simon Fraser.
+
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * css/CSSMarkup.cpp: Added.
+        (WebCore::isCSSTokenizerIdentifier):
+        (WebCore::serializeCharacter):
+        (WebCore::serializeCharacterAsCodePoint):
+        (WebCore::serializeIdentifier):
+        (WebCore::serializeString):
+        (WebCore::serializeURI):
+        (WebCore::serializeFontFamily):
+        * css/CSSMarkup.h: Added.
+        * css/CSSOMUtils.cpp: Removed.
+        * css/CSSOMUtils.h: Removed.
+        * css/CSSSelector.cpp:
+        * css/parser/CSSParserFastPaths.cpp: Added.
+        * css/parser/CSSParserFastPaths.h: Added.
+        * css/parser/CSSParserIdioms.h: Added.
+        (WebCore::isCSSSpace):
+        (WebCore::isNameStartCodePoint):
+        (WebCore::isNameCodePoint):
+        * css/parser/CSSParserObserver.h: Added.
+        (WebCore::CSSParserObserver::~CSSParserObserver):
+        * css/parser/CSSParserObserverWrapper.cpp: Added.
+        (WebCore::CSSParserObserverWrapper::startOffset):
+        (WebCore::CSSParserObserverWrapper::previousTokenStartOffset):
+        (WebCore::CSSParserObserverWrapper::endOffset):
+        (WebCore::CSSParserObserverWrapper::skipCommentsBefore):
+        (WebCore::CSSParserObserverWrapper::yieldCommentsBefore):
+        * css/parser/CSSParserObserverWrapper.h: Added.
+        (WebCore::CSSParserObserverWrapper::CSSParserObserverWrapper):
+        (WebCore::CSSParserObserverWrapper::observer):
+        (WebCore::CSSParserObserverWrapper::addComment):
+        (WebCore::CSSParserObserverWrapper::addToken):
+        (WebCore::CSSParserObserverWrapper::finalizeConstruction):
+        * css/parser/CSSParserToken.cpp: Added.
+        (WebCore::cssPrimitiveValueUnitFromTrie):
+        (WebCore::stringToUnitType):
+        (WebCore::CSSParserToken::CSSParserToken):
+        (WebCore::CSSParserToken::convertToDimensionWithUnit):
+        (WebCore::CSSParserToken::convertToPercentage):
+        (WebCore::CSSParserToken::delimiter):
+        (WebCore::CSSParserToken::numericSign):
+        (WebCore::CSSParserToken::numericValueType):
+        (WebCore::CSSParserToken::numericValue):
+        (WebCore::CSSParserToken::parseAsUnresolvedCSSPropertyID):
+        (WebCore::CSSParserToken::id):
+        (WebCore::CSSParserToken::functionId):
+        (WebCore::CSSParserToken::hasStringBacking):
+        (WebCore::CSSParserToken::copyWithUpdatedString):
+        (WebCore::CSSParserToken::valueDataCharRawEqual):
+        (WebCore::CSSParserToken::operator==):
+        (WebCore::CSSParserToken::serialize):
+        * css/parser/CSSParserToken.h: Added.
+        (WebCore::CSSParserToken::operator!=):
+        (WebCore::CSSParserToken::type):
+        (WebCore::CSSParserToken::value):
+        (WebCore::CSSParserToken::getHashTokenType):
+        (WebCore::CSSParserToken::getBlockType):
+        (WebCore::CSSParserToken::unitType):
+        (WebCore::CSSParserToken::unicodeRangeStart):
+        (WebCore::CSSParserToken::unicodeRangeEnd):
+        (WebCore::CSSParserToken::initValueFromStringView):
+        * css/parser/CSSParserTokenRange.cpp: Added.
+        (WebCore::CSSParserTokenRange::eofToken):
+        (WebCore::CSSParserTokenRange::makeSubRange):
+        (WebCore::CSSParserTokenRange::consumeBlock):
+        (WebCore::CSSParserTokenRange::consumeComponentValue):
+        (WebCore::CSSParserTokenRange::serialize):
+        * css/parser/CSSParserTokenRange.h: Added.
+        (WebCore::CSSParserTokenRange::CSSParserTokenRange):
+        (WebCore::CSSParserTokenRange::atEnd):
+        (WebCore::CSSParserTokenRange::end):
+        (WebCore::CSSParserTokenRange::peek):
+        (WebCore::CSSParserTokenRange::consume):
+        (WebCore::CSSParserTokenRange::consumeIncludingWhitespace):
+        (WebCore::CSSParserTokenRange::consumeWhitespace):
+        (WebCore::CSSParserTokenRange::begin):
+        * css/parser/CSSPropertyParser.cpp: Added.
+        (WebCore::hasPrefix):
+        (WebCore::cssPropertyID):
+        (WebCore::cssPropertyNameIOSAliasing):
+        (WebCore::isAppleLegacyCssValueKeyword):
+        (WebCore::cssValueKeywordID):
+        (WebCore::unresolvedCSSPropertyID):
+        * css/parser/CSSPropertyParser.h: Added.
+        (WebCore::CSSPropertyParser::inQuirksMode):
+        * css/parser/CSSPropertyParserHelpers.cpp: Added.
+        * css/parser/CSSPropertyParserHelpers.h: Added.
+        (WebCore::CSSPropertyParserHelpers::identMatches):
+        (WebCore::CSSPropertyParserHelpers::consumeIdent):
+        (WebCore::CSSPropertyParserHelpers::isCSSWideKeyword):
+        * css/parser/CSSTokenizer.cpp: Added.
+        (WebCore::CSSTokenizer::Scope::Scope):
+        (WebCore::CSSTokenizer::Scope::tokenRange):
+        (WebCore::CSSTokenizer::Scope::tokenCount):
+        (WebCore::isNewLine):
+        (WebCore::twoCharsAreValidEscape):
+        (WebCore::CSSTokenizer::CSSTokenizer):
+        (WebCore::CSSTokenizer::reconsume):
+        (WebCore::CSSTokenizer::consume):
+        (WebCore::CSSTokenizer::whiteSpace):
+        (WebCore::CSSTokenizer::blockStart):
+        (WebCore::CSSTokenizer::blockEnd):
+        (WebCore::CSSTokenizer::leftParenthesis):
+        (WebCore::CSSTokenizer::rightParenthesis):
+        (WebCore::CSSTokenizer::leftBracket):
+        (WebCore::CSSTokenizer::rightBracket):
+        (WebCore::CSSTokenizer::leftBrace):
+        (WebCore::CSSTokenizer::rightBrace):
+        (WebCore::CSSTokenizer::plusOrFullStop):
+        (WebCore::CSSTokenizer::asterisk):
+        (WebCore::CSSTokenizer::lessThan):
+        (WebCore::CSSTokenizer::comma):
+        (WebCore::CSSTokenizer::hyphenMinus):
+        (WebCore::CSSTokenizer::solidus):
+        (WebCore::CSSTokenizer::colon):
+        (WebCore::CSSTokenizer::semiColon):
+        (WebCore::CSSTokenizer::hash):
+        (WebCore::CSSTokenizer::circumflexAccent):
+        (WebCore::CSSTokenizer::dollarSign):
+        (WebCore::CSSTokenizer::verticalLine):
+        (WebCore::CSSTokenizer::tilde):
+        (WebCore::CSSTokenizer::commercialAt):
+        (WebCore::CSSTokenizer::reverseSolidus):
+        (WebCore::CSSTokenizer::asciiDigit):
+        (WebCore::CSSTokenizer::letterU):
+        (WebCore::CSSTokenizer::nameStart):
+        (WebCore::CSSTokenizer::stringStart):
+        (WebCore::CSSTokenizer::endOfFile):
+        (WebCore::CSSTokenizer::nextToken):
+        (WebCore::CSSTokenizer::consumeNumber):
+        (WebCore::CSSTokenizer::consumeNumericToken):
+        (WebCore::CSSTokenizer::consumeIdentLikeToken):
+        (WebCore::CSSTokenizer::consumeStringTokenUntil):
+        (WebCore::CSSTokenizer::consumeUnicodeRange):
+        (WebCore::isNonPrintableCodePoint):
+        (WebCore::CSSTokenizer::consumeUrlToken):
+        (WebCore::CSSTokenizer::consumeBadUrlRemnants):
+        (WebCore::CSSTokenizer::consumeSingleWhitespaceIfNext):
+        (WebCore::CSSTokenizer::consumeUntilCommentEndFound):
+        (WebCore::CSSTokenizer::consumeIfNext):
+        (WebCore::CSSTokenizer::consumeName):
+        (WebCore::CSSTokenizer::consumeEscape):
+        (WebCore::CSSTokenizer::nextTwoCharsAreValidEscape):
+        (WebCore::CSSTokenizer::nextCharsAreNumber):
+        (WebCore::CSSTokenizer::nextCharsAreIdentifier):
+        (WebCore::CSSTokenizer::registerString):
+        * css/parser/CSSTokenizer.h: Added.
+        (WebCore::CSSTokenizer::Scope::storeString):
+        * css/parser/CSSTokenizerInputStream.cpp: Added.
+        (WebCore::CSSTokenizerInputStream::CSSTokenizerInputStream):
+        (WebCore::CSSTokenizerInputStream::advanceUntilNonWhitespace):
+        (WebCore::CSSTokenizerInputStream::getDouble):
+        * css/parser/CSSTokenizerInputStream.h: Added.
+        (WebCore::CSSTokenizerInputStream::nextInputChar):
+        (WebCore::CSSTokenizerInputStream::peekWithoutReplacement):
+        (WebCore::CSSTokenizerInputStream::advance):
+        (WebCore::CSSTokenizerInputStream::pushBack):
+        (WebCore::CSSTokenizerInputStream::skipWhilePredicate):
+        (WebCore::CSSTokenizerInputStream::length):
+        (WebCore::CSSTokenizerInputStream::offset):
+        (WebCore::CSSTokenizerInputStream::rangeAt):
+        * platform/Length.h:
+
 2016-08-28  Javier Fernandez  <jfernandez@igalia.com>
 
         Should never be reached failure in WebCore::RenderFlexibleBox::alignChildren
index 16ad618..cd14b82 100644 (file)
                946D37301D6CB2940077084F /* CSSParserValues.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 946D372B1D6CB28B0077084F /* CSSParserValues.cpp */; };
                946D37311D6CB2940077084F /* CSSParserValues.h in Headers */ = {isa = PBXBuildFile; fileRef = 946D372C1D6CB28B0077084F /* CSSParserValues.h */; settings = {ATTRIBUTES = (Private, ); }; };
                946D37331D6CC42B0077084F /* SVGCSSParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 946D37321D6CC3720077084F /* SVGCSSParser.cpp */; };
+               946D37391D6CDFC00077084F /* CSSTokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 946D37341D6CDF980077084F /* CSSTokenizer.cpp */; };
+               946D373A1D6CDFC00077084F /* CSSTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 946D37371D6CDF980077084F /* CSSTokenizer.h */; };
+               946D373B1D6CDFC00077084F /* CSSTokenizerInputStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 946D37351D6CDF980077084F /* CSSTokenizerInputStream.cpp */; };
+               946D373C1D6CDFC00077084F /* CSSTokenizerInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 946D37381D6CDF980077084F /* CSSTokenizerInputStream.h */; };
+               946D373F1D6CE3C20077084F /* CSSParserToken.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 946D373E1D6CE31A0077084F /* CSSParserToken.cpp */; };
+               946D37401D6CE3C20077084F /* CSSParserToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 946D373D1D6CE31A0077084F /* CSSParserToken.h */; };
+               946D37441D6CF7B20077084F /* CSSParserIdioms.h in Headers */ = {isa = PBXBuildFile; fileRef = 946D37431D6CF7880077084F /* CSSParserIdioms.h */; };
+               946D37451D6D01D40077084F /* CSSPropertyParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 946D37411D6CF6320077084F /* CSSPropertyParser.cpp */; };
+               946D37461D6D01D40077084F /* CSSPropertyParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 946D37421D6CF6320077084F /* CSSPropertyParser.h */; };
+               946D37491D6D06280077084F /* CSSMarkup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 946D37471D6D060C0077084F /* CSSMarkup.cpp */; };
+               946D374A1D6D06280077084F /* CSSMarkup.h in Headers */ = {isa = PBXBuildFile; fileRef = 946D37481D6D060C0077084F /* CSSMarkup.h */; };
+               946D374D1D6D08A60077084F /* CSSParserTokenRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 946D374B1D6D07F50077084F /* CSSParserTokenRange.cpp */; };
+               946D374E1D6D08AA0077084F /* CSSParserTokenRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 946D374C1D6D07F50077084F /* CSSParserTokenRange.h */; };
+               949C77001D6E1D9800C0DE4F /* CSSParserFastPaths.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949C76FE1D6E1D8C00C0DE4F /* CSSParserFastPaths.cpp */; };
+               949C77011D6E1D9800C0DE4F /* CSSParserFastPaths.h in Headers */ = {isa = PBXBuildFile; fileRef = 949C76FF1D6E1D8C00C0DE4F /* CSSParserFastPaths.h */; };
+               949C77041D6E39EA00C0DE4F /* CSSPropertyParserHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949C77021D6E393500C0DE4F /* CSSPropertyParserHelpers.cpp */; };
+               949C77051D6E39EA00C0DE4F /* CSSPropertyParserHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 949C77031D6E393500C0DE4F /* CSSPropertyParserHelpers.h */; };
+               949C77081D6E498700C0DE4F /* CSSParserObserverWrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949C77061D6E48ED00C0DE4F /* CSSParserObserverWrapper.cpp */; };
+               949C77091D6E498700C0DE4F /* CSSParserObserverWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 949C77071D6E48ED00C0DE4F /* CSSParserObserverWrapper.h */; };
+               949C770B1D6E49ED00C0DE4F /* CSSParserObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 949C770A1D6E49C300C0DE4F /* CSSParserObserver.h */; };
                96ABA42314BCB80E00D56204 /* GraphicsContext3DOpenGLCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 96ABA42214BCB80E00D56204 /* GraphicsContext3DOpenGLCommon.cpp */; };
                9703E1BF15DC4E37001F24C8 /* JSVoidCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 97E9EC8B15DC492F004F2E71 /* JSVoidCallback.cpp */; };
                97059977107D975200A50A7C /* PolicyCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 97059973107D975200A50A7C /* PolicyCallback.cpp */; };
                F5E0C65C1643C42C00D6CB69 /* BaseChooserOnlyDateAndTimeInputType.h in Headers */ = {isa = PBXBuildFile; fileRef = F5E0C65A1643C42C00D6CB69 /* BaseChooserOnlyDateAndTimeInputType.h */; };
                F916C48D0DB510F80076CD83 /* JSXMLHttpRequestProgressEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F916C48B0DB510F80076CD83 /* JSXMLHttpRequestProgressEvent.cpp */; };
                F916C48E0DB510F80076CD83 /* JSXMLHttpRequestProgressEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = F916C48C0DB510F80076CD83 /* JSXMLHttpRequestProgressEvent.h */; };
-               F98FFF4411A2676200F548E8 /* CSSOMUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98FFF4211A2676200F548E8 /* CSSOMUtils.cpp */; };
-               F98FFF4511A2676200F548E8 /* CSSOMUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = F98FFF4311A2676200F548E8 /* CSSOMUtils.h */; };
                F9F0ED7A0DB50CA200D16DB9 /* XMLHttpRequestProgressEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = F9F0ED770DB50CA200D16DB9 /* XMLHttpRequestProgressEvent.h */; };
                FA654A6B1108ABED002615E0 /* MathMLTokenElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA654A691108ABED002615E0 /* MathMLTokenElement.cpp */; };
                FA654A6B1108ABED002616F1 /* MathMLOperatorElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA654A691108ABED002616F1 /* MathMLOperatorElement.cpp */; };
                946D372B1D6CB28B0077084F /* CSSParserValues.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSSParserValues.cpp; path = parser/CSSParserValues.cpp; sourceTree = "<group>"; };
                946D372C1D6CB28B0077084F /* CSSParserValues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSParserValues.h; path = parser/CSSParserValues.h; sourceTree = "<group>"; };
                946D37321D6CC3720077084F /* SVGCSSParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SVGCSSParser.cpp; path = parser/SVGCSSParser.cpp; sourceTree = "<group>"; };
+               946D37341D6CDF980077084F /* CSSTokenizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSSTokenizer.cpp; path = parser/CSSTokenizer.cpp; sourceTree = "<group>"; };
+               946D37351D6CDF980077084F /* CSSTokenizerInputStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSSTokenizerInputStream.cpp; path = parser/CSSTokenizerInputStream.cpp; sourceTree = "<group>"; };
+               946D37371D6CDF980077084F /* CSSTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSTokenizer.h; path = parser/CSSTokenizer.h; sourceTree = "<group>"; };
+               946D37381D6CDF980077084F /* CSSTokenizerInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSTokenizerInputStream.h; path = parser/CSSTokenizerInputStream.h; sourceTree = "<group>"; };
+               946D373D1D6CE31A0077084F /* CSSParserToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSParserToken.h; path = parser/CSSParserToken.h; sourceTree = "<group>"; };
+               946D373E1D6CE31A0077084F /* CSSParserToken.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSSParserToken.cpp; path = parser/CSSParserToken.cpp; sourceTree = "<group>"; };
+               946D37411D6CF6320077084F /* CSSPropertyParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSSPropertyParser.cpp; path = parser/CSSPropertyParser.cpp; sourceTree = "<group>"; };
+               946D37421D6CF6320077084F /* CSSPropertyParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSPropertyParser.h; path = parser/CSSPropertyParser.h; sourceTree = "<group>"; };
+               946D37431D6CF7880077084F /* CSSParserIdioms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSParserIdioms.h; path = parser/CSSParserIdioms.h; sourceTree = "<group>"; };
+               946D37471D6D060C0077084F /* CSSMarkup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSSMarkup.cpp; sourceTree = "<group>"; };
+               946D37481D6D060C0077084F /* CSSMarkup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSMarkup.h; sourceTree = "<group>"; };
+               946D374B1D6D07F50077084F /* CSSParserTokenRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSSParserTokenRange.cpp; path = parser/CSSParserTokenRange.cpp; sourceTree = "<group>"; };
+               946D374C1D6D07F50077084F /* CSSParserTokenRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSParserTokenRange.h; path = parser/CSSParserTokenRange.h; sourceTree = "<group>"; };
+               949C76FE1D6E1D8C00C0DE4F /* CSSParserFastPaths.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSSParserFastPaths.cpp; path = parser/CSSParserFastPaths.cpp; sourceTree = "<group>"; };
+               949C76FF1D6E1D8C00C0DE4F /* CSSParserFastPaths.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSParserFastPaths.h; path = parser/CSSParserFastPaths.h; sourceTree = "<group>"; };
+               949C77021D6E393500C0DE4F /* CSSPropertyParserHelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSSPropertyParserHelpers.cpp; path = parser/CSSPropertyParserHelpers.cpp; sourceTree = "<group>"; };
+               949C77031D6E393500C0DE4F /* CSSPropertyParserHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSPropertyParserHelpers.h; path = parser/CSSPropertyParserHelpers.h; sourceTree = "<group>"; };
+               949C77061D6E48ED00C0DE4F /* CSSParserObserverWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSSParserObserverWrapper.cpp; path = parser/CSSParserObserverWrapper.cpp; sourceTree = "<group>"; };
+               949C77071D6E48ED00C0DE4F /* CSSParserObserverWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSParserObserverWrapper.h; path = parser/CSSParserObserverWrapper.h; sourceTree = "<group>"; };
+               949C770A1D6E49C300C0DE4F /* CSSParserObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSSParserObserver.h; path = parser/CSSParserObserver.h; sourceTree = "<group>"; };
                950C4C02BED8936F818E2F99 /* JSSVGGraphicsElement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSSVGGraphicsElement.h; sourceTree = "<group>"; };
                96ABA42214BCB80E00D56204 /* GraphicsContext3DOpenGLCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GraphicsContext3DOpenGLCommon.cpp; sourceTree = "<group>"; };
                97059973107D975200A50A7C /* PolicyCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PolicyCallback.cpp; sourceTree = "<group>"; };
                F8216299029F4FB501000131 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = JavaScriptCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
                F916C48B0DB510F80076CD83 /* JSXMLHttpRequestProgressEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSXMLHttpRequestProgressEvent.cpp; sourceTree = "<group>"; };
                F916C48C0DB510F80076CD83 /* JSXMLHttpRequestProgressEvent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSXMLHttpRequestProgressEvent.h; sourceTree = "<group>"; };
-               F98FFF4211A2676200F548E8 /* CSSOMUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSSOMUtils.cpp; sourceTree = "<group>"; };
-               F98FFF4311A2676200F548E8 /* CSSOMUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSOMUtils.h; sourceTree = "<group>"; };
                F9F0ED770DB50CA200D16DB9 /* XMLHttpRequestProgressEvent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = XMLHttpRequestProgressEvent.h; sourceTree = "<group>"; };
                F9F0ED780DB50CA200D16DB9 /* XMLHttpRequestProgressEvent.idl */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = XMLHttpRequestProgressEvent.idl; sourceTree = "<group>"; };
                FA654A631108ABB7002615E0 /* mathml.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = mathml.css; sourceTree = "<group>"; };
                946D37271D6CB2250077084F /* parser */ = {
                        isa = PBXGroup;
                        children = (
-                               946D37321D6CC3720077084F /* SVGCSSParser.cpp */,
                                946D37281D6CB28B0077084F /* CSSParser.cpp */,
                                946D37291D6CB28B0077084F /* CSSParser.h */,
+                               949C76FE1D6E1D8C00C0DE4F /* CSSParserFastPaths.cpp */,
+                               949C76FF1D6E1D8C00C0DE4F /* CSSParserFastPaths.h */,
+                               946D37431D6CF7880077084F /* CSSParserIdioms.h */,
                                946D372A1D6CB28B0077084F /* CSSParserMode.h */,
+                               949C770A1D6E49C300C0DE4F /* CSSParserObserver.h */,
+                               949C77061D6E48ED00C0DE4F /* CSSParserObserverWrapper.cpp */,
+                               949C77071D6E48ED00C0DE4F /* CSSParserObserverWrapper.h */,
+                               946D373E1D6CE31A0077084F /* CSSParserToken.cpp */,
+                               946D373D1D6CE31A0077084F /* CSSParserToken.h */,
+                               946D374B1D6D07F50077084F /* CSSParserTokenRange.cpp */,
+                               946D374C1D6D07F50077084F /* CSSParserTokenRange.h */,
                                946D372B1D6CB28B0077084F /* CSSParserValues.cpp */,
                                946D372C1D6CB28B0077084F /* CSSParserValues.h */,
+                               946D37411D6CF6320077084F /* CSSPropertyParser.cpp */,
+                               946D37421D6CF6320077084F /* CSSPropertyParser.h */,
+                               949C77021D6E393500C0DE4F /* CSSPropertyParserHelpers.cpp */,
+                               949C77031D6E393500C0DE4F /* CSSPropertyParserHelpers.h */,
+                               946D37341D6CDF980077084F /* CSSTokenizer.cpp */,
+                               946D37371D6CDF980077084F /* CSSTokenizer.h */,
+                               946D37351D6CDF980077084F /* CSSTokenizerInputStream.cpp */,
+                               946D37381D6CDF980077084F /* CSSTokenizerInputStream.h */,
+                               946D37321D6CC3720077084F /* SVGCSSParser.cpp */,
                        );
                        name = parser;
                        sourceTree = "<group>";
                                BE6DF70E171CA2DA00DD52B8 /* JSAudioTrackCustom.cpp */,
                                BE6DF710171CA2DA00DD52B8 /* JSAudioTrackListCustom.cpp */,
                                8931DE5A14C44C44000DC9D2 /* JSBlobCustom.cpp */,
-                               49EED14B1051971900099FAB /* JSCanvasRenderingContext2DCustom.cpp */,
                                4659D2701D6B909F0096FD86 /* JSCanvasRenderingContext.h */,
+                               49EED14B1051971900099FAB /* JSCanvasRenderingContext2DCustom.cpp */,
                                7C33F3581B4A044800502CAF /* JSCharacterDataCustom.cpp */,
                                46A58AC41D46B3FA00432036 /* JSClientRectCustom.cpp */,
                                A584FE371864DAC100843B10 /* JSCommandLineAPIHostCustom.cpp */,
                                316FE0920E6CCD7F00BF6088 /* CSSKeyframesRule.idl */,
                                BC772E15133162C2001EC9CE /* CSSLineBoxContainValue.cpp */,
                                BC772E121331620C001EC9CE /* CSSLineBoxContainValue.h */,
+                               946D37471D6D060C0077084F /* CSSMarkup.cpp */,
+                               946D37481D6D060C0077084F /* CSSMarkup.h */,
                                A80E6CD20A1989CA007FB8C5 /* CSSMediaRule.cpp */,
                                A80E6CD90A1989CA007FB8C5 /* CSSMediaRule.h */,
                                85C56CA20AA89C1000D95755 /* CSSMediaRule.idl */,
                                314BE3A21B30F6D100141982 /* CSSNamedImageValue.cpp */,
                                314BE3A01B30F6B700141982 /* CSSNamedImageValue.h */,
-                               F98FFF4211A2676200F548E8 /* CSSOMUtils.cpp */,
-                               F98FFF4311A2676200F548E8 /* CSSOMUtils.h */,
                                A80E6CCB0A1989CA007FB8C5 /* CSSPageRule.cpp */,
                                A80E6CD60A1989CA007FB8C5 /* CSSPageRule.h */,
                                85C56CA60AA89D5F00D95755 /* CSSPageRule.idl */,
                                FABE72EE1059C1EB00D999DD /* MathMLElement.h */,
                                0BCF83EF1059C1EB00D999DD /* MathMLFractionElement.cpp */,
                                0BCF83F01059C1EB00D999DD /* MathMLFractionElement.h */,
-                               FABE72EF1059C1EB00D999DD /* MathMLPresentationElement.cpp */,
-                               FABE72F01059C1EB00D999DD /* MathMLPresentationElement.h */,
                                FABE72F11059C1EB00D999DD /* MathMLMathElement.cpp */,
                                FABE72F21059C1EB00D999DD /* MathMLMathElement.h */,
                                C3E61C653A64807A83E76FB8 /* MathMLMencloseElement.cpp */,
                                FA654A6A1108ABED002616F1 /* MathMLOperatorElement.h */,
                                B59CA59AF170D8FAA5B8CABE /* MathMLPaddedElement.cpp */,
                                B59CA849D41E6F65D81198BC /* MathMLPaddedElement.h */,
+                               FABE72EF1059C1EB00D999DD /* MathMLPresentationElement.cpp */,
+                               FABE72F01059C1EB00D999DD /* MathMLPresentationElement.h */,
+                               FA765A691108ABED002615E0 /* MathMLRowElement.cpp */,
+                               FA765A6A1108ABED002615E0 /* MathMLRowElement.h */,
                                B59CA59AF170D8FAA5B8C9AD /* MathMLScriptsElement.cpp */,
                                B59CA849D41E6F65D81197AB /* MathMLScriptsElement.h */,
                                F75A059AF170D8FAA5B8CABE /* MathMLSelectElement.cpp */,
                                4FA65A6A1108ABED002615E0 /* MathMLSpaceElement.h */,
                                FA654A691108ABED002615E0 /* MathMLTokenElement.cpp */,
                                FA654A6A1108ABED002615E0 /* MathMLTokenElement.h */,
-                               FA765A691108ABED002615E0 /* MathMLRowElement.cpp */,
-                               FA765A6A1108ABED002615E0 /* MathMLRowElement.h */,
                                FA654A691108ABED002626F1 /* MathMLUnderOverElement.cpp */,
                                FA654A6A1108ABED002626F1 /* MathMLUnderOverElement.h */,
                                FABE72F31059C1EB00D999DD /* mathtags.in */,
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               51058ADE1D6792C1009A538C /* MockGamepadProvider.h in Headers */,
-                               51058ADC1D6792C1009A538C /* MockGamepad.h in Headers */,
                                51714EAD1CF65951004723C4 /* GCObservation.h in Headers */,
                                417DA6DA13734E6E007C57FB /* Internals.h in Headers */,
                                A7BF7EE014C9175A0014489D /* InternalSettings.h in Headers */,
                                CDC26B41160A8CCE0026757B /* MockCDM.h in Headers */,
                                A1BF6B831AA96C7D00AF4A8A /* MockContentFilter.h in Headers */,
                                A1B5B29F1AAA846F008B6042 /* MockContentFilterSettings.h in Headers */,
+                               51058ADC1D6792C1009A538C /* MockGamepad.h in Headers */,
+                               51058ADE1D6792C1009A538C /* MockGamepadProvider.h in Headers */,
                                2D6F3E911C1ECB2F0061DBD4 /* MockPageOverlay.h in Headers */,
                                2D97F04819DD4140001EE9C3 /* MockPageOverlayClient.h in Headers */,
                                AA5F3B8D16CC33D100455EB0 /* PlatformSpeechSynthesizerMock.h in Headers */,
                                BEF29EEB1715DD0900C4B4C9 /* AudioTrackPrivate.h in Headers */,
                                CDE3A85417F5FCE600C5BE20 /* AudioTrackPrivateAVF.h in Headers */,
                                CDE3A85817F6020400C5BE20 /* AudioTrackPrivateAVFObjC.h in Headers */,
-                               51058AE21D67C229009A538C /* MockGamepadProvider.h in Headers */,
                                CD54A763180F9F7000B076C9 /* AudioTrackPrivateMediaSourceAVFObjC.h in Headers */,
                                07D6A4F81BF2307D00174146 /* AudioTrackPrivateMediaStream.h in Headers */,
                                FD31608B12B026F700C1A359 /* AudioUtilities.h in Headers */,
                                31BC742E1AAFF45C006B4340 /* CSSAnimationTriggerScrollValue.h in Headers */,
                                CAE9F910146441F000C245B0 /* CSSAspectRatioValue.h in Headers */,
                                FBD6AF8815EF25C9008B7110 /* CSSBasicShapes.h in Headers */,
-                               51058AE01D67C229009A538C /* MockGamepad.h in Headers */,
                                E16A84FA14C85CCC002977DF /* CSSBorderImage.h in Headers */,
                                BC274B2F140EBEB200EADFA6 /* CSSBorderImageSliceValue.h in Headers */,
                                49AE2D8F134EE50C0072920A /* CSSCalculationValue.h in Headers */,
                                31288E730E3005D6003619AE /* CSSKeyframeRule.h in Headers */,
                                31288E750E3005D6003619AE /* CSSKeyframesRule.h in Headers */,
                                BC772E131331620C001EC9CE /* CSSLineBoxContainValue.h in Headers */,
+                               946D374A1D6D06280077084F /* CSSMarkup.h in Headers */,
                                A80E6D030A1989CA007FB8C5 /* CSSMediaRule.h in Headers */,
                                314BE3A11B30F6B700141982 /* CSSNamedImageValue.h in Headers */,
-                               F98FFF4511A2676200F548E8 /* CSSOMUtils.h in Headers */,
                                A80E6D000A1989CA007FB8C5 /* CSSPageRule.h in Headers */,
+                               946D372E1D6CB2940077084F /* CSSParser.h in Headers */,
+                               949C77011D6E1D9800C0DE4F /* CSSParserFastPaths.h in Headers */,
+                               946D37441D6CF7B20077084F /* CSSParserIdioms.h in Headers */,
+                               946D372F1D6CB2940077084F /* CSSParserMode.h in Headers */,
+                               949C770B1D6E49ED00C0DE4F /* CSSParserObserver.h in Headers */,
+                               949C77091D6E498700C0DE4F /* CSSParserObserverWrapper.h in Headers */,
+                               946D37401D6CE3C20077084F /* CSSParserToken.h in Headers */,
+                               946D374E1D6D08AA0077084F /* CSSParserTokenRange.h in Headers */,
+                               946D37311D6CB2940077084F /* CSSParserValues.h in Headers */,
                                977B3863122883E900B81FF8 /* CSSPreloadScanner.h in Headers */,
                                A80E6CE60A1989CA007FB8C5 /* CSSPrimitiveValue.h in Headers */,
                                E1ED8AC30CC49BE000BFC557 /* CSSPrimitiveValueMappings.h in Headers */,
                                A80E6CFF0A1989CA007FB8C5 /* CSSProperty.h in Headers */,
                                78D02BC6154A18DF00B62D05 /* CSSPropertyAnimation.h in Headers */,
                                656580F409D12B20000E61D7 /* CSSPropertyNames.h in Headers */,
+                               946D37461D6D01D40077084F /* CSSPropertyParser.h in Headers */,
+                               949C77051D6E39EA00C0DE4F /* CSSPropertyParserHelpers.h in Headers */,
                                82E3D8DF122EA0D1003AE5BC /* CSSPropertySourceData.h in Headers */,
                                9362640B0DE1137D009D5A00 /* CSSReflectionDirection.h in Headers */,
                                BC5A12E00DC0414800C9AFAD /* CSSReflectValue.h in Headers */,
                                A8EA80070A19516E00A8EF5F /* CSSStyleSheet.h in Headers */,
                                FC54D05716A7673100575E4D /* CSSSupportsRule.h in Headers */,
                                BC80C9880CD294EE00A0B7B3 /* CSSTimingFunctionValue.h in Headers */,
+                               946D373A1D6CDFC00077084F /* CSSTokenizer.h in Headers */,
+                               946D373C1D6CDFC00077084F /* CSSTokenizerInputStream.h in Headers */,
                                9AB1F38018E2489A00534743 /* CSSToLengthConversionData.h in Headers */,
                                A882DA231593848D000115ED /* CSSToStyleMap.h in Headers */,
                                371F53E90D2704F900ECE0D5 /* CSSUnicodeRangeValue.h in Headers */,
                                65DF323C09D1DE65000BE325 /* JSCanvasPattern.h in Headers */,
                                65DF323C09D1DE65001BE325 /* JSCanvasProxy.h in Headers */,
                                49EED1431051969400099FAB /* JSCanvasRenderingContext.h in Headers */,
+                               4659D2711D6B90A50096FD86 /* JSCanvasRenderingContext.h in Headers */,
                                49EED1451051969400099FAB /* JSCanvasRenderingContext2D.h in Headers */,
                                93F9B7A10BA6032600854064 /* JSCDATASection.h in Headers */,
                                FDA15EA212B03EE1003A583A /* JSChannelMergerNode.h in Headers */,
                                FD23A12613F5FA5900F67001 /* JSMediaElementAudioSourceNode.h in Headers */,
                                E44614190CD6826900FADA75 /* JSMediaError.h in Headers */,
                                BC3C39B70C0D3D8D005F4D7A /* JSMediaList.h in Headers */,
-                               4659D2711D6B90A50096FD86 /* JSCanvasRenderingContext.h in Headers */,
                                93D437A31D57B7E200AB85EA /* JSMediaListCustom.h in Headers */,
                                D3A94A47122DC40F00A37BBC /* JSMediaQueryList.h in Headers */,
                                7C5343FD17B74B63004232F0 /* JSMediaQueryListListener.h in Headers */,
                                B2FA3DB50AB75A6F000E5AC4 /* JSSVGPathElement.h in Headers */,
                                B2FA3DB70AB75A6F000E5AC4 /* JSSVGPathSeg.h in Headers */,
                                B2FA3DB90AB75A6F000E5AC4 /* JSSVGPathSegArcAbs.h in Headers */,
-                               946D372E1D6CB2940077084F /* CSSParser.h in Headers */,
                                B2FA3DBB0AB75A6F000E5AC4 /* JSSVGPathSegArcRel.h in Headers */,
                                B2FA3DBD0AB75A6F000E5AC4 /* JSSVGPathSegClosePath.h in Headers */,
                                B2FA3DBF0AB75A6F000E5AC4 /* JSSVGPathSegCurvetoCubicAbs.h in Headers */,
                                E4E39AFB1330EFA8003AB274 /* LegacyTileLayerPool.h in Headers */,
                                512DD8F50D91E6AF000F89EE /* LegacyWebArchive.h in Headers */,
                                BCE65BEB0EACDF16007E4533 /* Length.h in Headers */,
-                               946D372F1D6CB2940077084F /* CSSParserMode.h in Headers */,
                                BCFF64910EAD15C200C1D6F7 /* LengthBox.h in Headers */,
                                E5BA7D63151437CA00FE1E3F /* LengthFunctions.h in Headers */,
                                0F8716701C869D83004FF0DE /* LengthPoint.h in Headers */,
                                FABE72F51059C1EB00D999DD /* MathMLElement.h in Headers */,
                                44A28AAC12DFB8AC00AE923B /* MathMLElementFactory.h in Headers */,
                                0BCF83F71059C1EB00D999DD /* MathMLFractionElement.h in Headers */,
-                               FABE72F71059C1EB00D999DD /* MathMLPresentationElement.h in Headers */,
                                FABE72F91059C1EB00D999DD /* MathMLMathElement.h in Headers */,
                                44A28AAF12DFB8BF00AE923B /* MathMLNames.h in Headers */,
                                FA654A6C1108ABED002616F1 /* MathMLOperatorElement.h in Headers */,
+                               FABE72F71059C1EB00D999DD /* MathMLPresentationElement.h in Headers */,
+                               FA765A6C1108ABED002615E0 /* MathMLRowElement.h in Headers */,
                                4FA65A6C1108ABED002615E0 /* MathMLSpaceElement.h in Headers */,
                                439176E012DA25E17BAF80A2 /* MathMLStyle.h in Headers */,
                                FA654A6C1108ABED002615E0 /* MathMLTokenElement.h in Headers */,
-                               FA765A6C1108ABED002615E0 /* MathMLRowElement.h in Headers */,
                                FA654A6C1108ABED002626F1 /* MathMLUnderOverElement.h in Headers */,
                                439046EA12DA25E812AF80AC /* MathOperator.h in Headers */,
                                49D5DC2C0F423A73008F20FD /* Matrix3DTransformOperation.h in Headers */,
                                52F10866162B6DA8009AC81E /* MixedContentChecker.h in Headers */,
                                CE1252491A16C3BC00864480 /* MobileGestaltSPI.h in Headers */,
                                CDF2B0111820540600F2B424 /* MockBox.h in Headers */,
+                               51058AE01D67C229009A538C /* MockGamepad.h in Headers */,
+                               51058AE21D67C229009A538C /* MockGamepadProvider.h in Headers */,
                                5EA3D6DF1C859D7F00300BBB /* MockMediaEndpoint.h in Headers */,
                                CDF2B0131820540600F2B424 /* MockMediaPlayerMediaSource.h in Headers */,
                                CDF2B0151820540600F2B424 /* MockMediaSourcePrivate.h in Headers */,
                                0813A4EA1284132600992511 /* SVGStaticPropertyTearOff.h in Headers */,
                                B2227AA90D00BF220071B782 /* SVGStopElement.h in Headers */,
                                B2227AAC0D00BF220071B782 /* SVGStringList.h in Headers */,
-                               946D37311D6CB2940077084F /* CSSParserValues.h in Headers */,
                                B2227AB80D00BF220071B782 /* SVGStyleElement.h in Headers */,
                                B2227ABB0D00BF220071B782 /* SVGSVGElement.h in Headers */,
                                B2227ABE0D00BF220071B782 /* SVGSwitchElement.h in Headers */,
                                417DA6D913734E6E007C57FB /* Internals.cpp in Sources */,
                                E179F0DA1B9774FE00ED0A27 /* Internals.mm in Sources */,
                                A7BF7EDF14C9175A0014489D /* InternalSettings.cpp in Sources */,
-                               51058ADD1D6792C1009A538C /* MockGamepadProvider.cpp in Sources */,
                                53E29E5E167A8A1900586D3D /* InternalSettingsGenerated.cpp in Sources */,
                                51714EB01CF665CE004723C4 /* JSGCObservation.cpp in Sources */,
                                417DA71D13735DFA007C57FB /* JSInternals.cpp in Sources */,
                                CD5393D3175E018600C07123 /* JSMemoryInfo.cpp in Sources */,
                                A19AEA221AAA808A00B52B25 /* JSMockContentFilterSettings.cpp in Sources */,
                                A1E5B31F1AAD1DA4006EBEFB /* JSMockContentFilterSettingsCustom.cpp in Sources */,
-                               51058ADB1D6792C1009A538C /* MockGamepad.cpp in Sources */,
                                2D4150DE1C1F868C000A3BA2 /* JSMockPageOverlay.cpp in Sources */,
                                EBF5121C1696496C0056BD25 /* JSTypeConversions.cpp in Sources */,
                                CDC26B40160A8CC60026757B /* MockCDM.cpp in Sources */,
                                A1BF6B821AA96C7D00AF4A8A /* MockContentFilter.cpp in Sources */,
                                A1B5B29E1AAA846E008B6042 /* MockContentFilterSettings.cpp in Sources */,
+                               51058ADB1D6792C1009A538C /* MockGamepad.cpp in Sources */,
+                               51058ADD1D6792C1009A538C /* MockGamepadProvider.cpp in Sources */,
                                2D6F3E901C1ECB270061DBD4 /* MockPageOverlay.cpp in Sources */,
                                2D97F04719DD413C001EE9C3 /* MockPageOverlayClient.cpp in Sources */,
                                AA5F3B8F16CC4B3900455EB0 /* PlatformSpeechSynthesizerMock.cpp in Sources */,
                                31288E720E3005D6003619AE /* CSSKeyframeRule.cpp in Sources */,
                                31288E740E3005D6003619AE /* CSSKeyframesRule.cpp in Sources */,
                                BC772E16133162C2001EC9CE /* CSSLineBoxContainValue.cpp in Sources */,
+                               946D37491D6D06280077084F /* CSSMarkup.cpp in Sources */,
                                A80E6CFC0A1989CA007FB8C5 /* CSSMediaRule.cpp in Sources */,
                                314BE3A31B30F6D100141982 /* CSSNamedImageValue.cpp in Sources */,
-                               F98FFF4411A2676200F548E8 /* CSSOMUtils.cpp in Sources */,
                                A80E6CF50A1989CA007FB8C5 /* CSSPageRule.cpp in Sources */,
+                               946D372D1D6CB2940077084F /* CSSParser.cpp in Sources */,
+                               949C77001D6E1D9800C0DE4F /* CSSParserFastPaths.cpp in Sources */,
+                               949C77081D6E498700C0DE4F /* CSSParserObserverWrapper.cpp in Sources */,
+                               946D373F1D6CE3C20077084F /* CSSParserToken.cpp in Sources */,
+                               946D374D1D6D08A60077084F /* CSSParserTokenRange.cpp in Sources */,
+                               946D37301D6CB2940077084F /* CSSParserValues.cpp in Sources */,
                                977B3862122883E900B81FF8 /* CSSPreloadScanner.cpp in Sources */,
                                A80E6D050A1989CA007FB8C5 /* CSSPrimitiveValue.cpp in Sources */,
                                A80E6CF70A1989CA007FB8C5 /* CSSProperty.cpp in Sources */,
                                78D02BC5154A18DF00B62D05 /* CSSPropertyAnimation.cpp in Sources */,
                                1ABA76CA11D20E50004C201C /* CSSPropertyNames.cpp in Sources */,
+                               946D37451D6D01D40077084F /* CSSPropertyParser.cpp in Sources */,
+                               949C77041D6E39EA00C0DE4F /* CSSPropertyParserHelpers.cpp in Sources */,
                                82E3D8DE122EA0D1003AE5BC /* CSSPropertySourceData.cpp in Sources */,
                                BC5A12DF0DC0414800C9AFAD /* CSSReflectValue.cpp in Sources */,
                                BC7D8FF31BD1A47900FFE540 /* CSSRevertValue.cpp in Sources */,
                                A8EA80080A19516E00A8EF5F /* CSSStyleSheet.cpp in Sources */,
                                FD677739195CAFBA0072E0D3 /* CSSSupportsRule.cpp in Sources */,
                                BC80C9870CD294EE00A0B7B3 /* CSSTimingFunctionValue.cpp in Sources */,
+                               946D37391D6CDFC00077084F /* CSSTokenizer.cpp in Sources */,
+                               946D373B1D6CDFC00077084F /* CSSTokenizerInputStream.cpp in Sources */,
                                9AB1F38118E2489A00534743 /* CSSToLengthConversionData.cpp in Sources */,
                                A882DA201593846A000115ED /* CSSToStyleMap.cpp in Sources */,
                                371F53EA0D2704F900ECE0D5 /* CSSUnicodeRangeValue.cpp in Sources */,
                                975CA2A11303679D00E99AD9 /* JSCrypto.cpp in Sources */,
                                E157A8F018185425009F821D /* JSCryptoAlgorithmBuilder.cpp in Sources */,
                                E1C657121815F9DD00256CDD /* JSCryptoAlgorithmDictionary.cpp in Sources */,
+                               7CEF26191D6A931700BE905D /* JSCryptoCustom.cpp in Sources */,
                                E157A8E01817331C009F821D /* JSCryptoKey.cpp in Sources */,
                                E157A8E818184C67009F821D /* JSCryptoKeyCustom.cpp in Sources */,
                                E1F80B8D183172B5007885C3 /* JSCryptoKeyPair.cpp in Sources */,
                                1A750D8D0A90E521000FF215 /* JSNodeIterator.cpp in Sources */,
                                1A750DD40A90E729000FF215 /* JSNodeIteratorCustom.cpp in Sources */,
                                BCD9C2C20C17B69E005C90A2 /* JSNodeList.cpp in Sources */,
-                               946D37301D6CB2940077084F /* CSSParserValues.cpp in Sources */,
                                BCD9C2650C17AA67005C90A2 /* JSNodeListCustom.cpp in Sources */,
                                7C91A38F1B498ABE003F9EFA /* JSNodeOrString.cpp in Sources */,
                                33503CA310179AD7003B47E1 /* JSNotification.cpp in Sources */,
                                FABE72F41059C1EB00D999DD /* MathMLElement.cpp in Sources */,
                                FABE72FD1059C21100D999DD /* MathMLElementFactory.cpp in Sources */,
                                0BCF83F61059C1EB00D999DD /* MathMLFractionElement.cpp in Sources */,
-                               FABE72F61059C1EB00D999DD /* MathMLPresentationElement.cpp in Sources */,
                                FABE72F81059C1EB00D999DD /* MathMLMathElement.cpp in Sources */,
                                05D913CEEAB2A60534218ACF /* MathMLMencloseElement.cpp in Sources */,
                                FABE72FE1059C21100D999DD /* MathMLNames.cpp in Sources */,
                                16EA24CEEAB2A60534218ACF /* MathMLOperatorDictionary.cpp in Sources */,
                                FA654A6B1108ABED002616F1 /* MathMLOperatorElement.cpp in Sources */,
                                B59CA390CED66C3255F72C59 /* MathMLPaddedElement.cpp in Sources */,
+                               FABE72F61059C1EB00D999DD /* MathMLPresentationElement.cpp in Sources */,
+                               FA765A6B1108ABED002615E0 /* MathMLRowElement.cpp in Sources */,
                                B59CA390CED66C3255F72B48 /* MathMLScriptsElement.cpp in Sources */,
                                FED48390CED66C3255F72C59 /* MathMLSelectElement.cpp in Sources */,
                                4FA65A6B1108ABED002615E0 /* MathMLSpaceElement.cpp in Sources */,
                                439176DF12DA25E17BAF80A2 /* MathMLStyle.cpp in Sources */,
                                FA654A6B1108ABED002615E0 /* MathMLTokenElement.cpp in Sources */,
-                               FA765A6B1108ABED002615E0 /* MathMLRowElement.cpp in Sources */,
                                FA654A6B1108ABED002626F1 /* MathMLUnderOverElement.cpp in Sources */,
                                439046E912DA25E812AF80AC /* MathOperator.cpp in Sources */,
                                49D5DC2B0F423A73008F20FD /* Matrix3DTransformOperation.cpp in Sources */,
                                C53D39341C978A45007F3AE9 /* MIMETypeRegistryCocoa.mm in Sources */,
                                52F10865162B6DA4009AC81E /* MixedContentChecker.cpp in Sources */,
                                CDF2B0101820540600F2B424 /* MockBox.cpp in Sources */,
+                               51058ADF1D67C229009A538C /* MockGamepad.cpp in Sources */,
+                               51058AE11D67C229009A538C /* MockGamepadProvider.cpp in Sources */,
                                5EA3D6E01C859D8400300BBB /* MockMediaEndpoint.cpp in Sources */,
                                CDF2B0121820540600F2B424 /* MockMediaPlayerMediaSource.cpp in Sources */,
                                CDF2B0141820540600F2B424 /* MockMediaSourcePrivate.cpp in Sources */,
                                443F04270E75C8FB007E5407 /* NetworkStateNotifierIOS.mm in Sources */,
                                1A7FA6490DDA3CBA0028F8A5 /* NetworkStateNotifierMac.cpp in Sources */,
                                5C3C856D1D5A7ADE0088B9EC /* NetworkStorageSession.cpp in Sources */,
-                               51058ADF1D67C229009A538C /* MockGamepad.cpp in Sources */,
                                E13EF34916850C470034C83F /* NetworkStorageSessionCFNet.cpp in Sources */,
                                269397261A4A5FBD00E8349D /* NFA.cpp in Sources */,
                                267726001A5B3AD9003C24DD /* NFAToDFA.cpp in Sources */,
                                FDA9325D16703B2A008982DC /* OfflineAudioContext.cpp in Sources */,
                                FDA3E95B134A49EF008D4B5A /* OfflineAudioDestinationNode.cpp in Sources */,
                                B2D3DA640D006CD600EF6F3A /* OpenTypeCG.cpp in Sources */,
-                               51058AE11D67C229009A538C /* MockGamepadProvider.cpp in Sources */,
                                B2D3DA640D006CD600EF6F27 /* OpenTypeMathData.cpp in Sources */,
                                CDE7FC44181904B1002BBB77 /* OrderIterator.cpp in Sources */,
                                0014628A103CD1DE000B20DB /* OriginAccessEntry.cpp in Sources */,
                                0F43C85D189E10CF00019AE2 /* PerformanceTiming.cpp in Sources */,
                                FD581FB41520F93B003A7A75 /* PeriodicWave.cpp in Sources */,
                                49D5DC2D0F423A73008F20FD /* PerspectiveTransformOperation.cpp in Sources */,
-                               946D37331D6CC42B0077084F /* SVGCSSParser.cpp in Sources */,
                                D0FF2A5D11F8C45A007E74E0 /* PingLoader.cpp in Sources */,
                                CD7D33431C7A123F00041293 /* PixelBufferConformerCV.cpp in Sources */,
                                0FDF45A71BD1C6FD00E4FA8C /* PlatformCAAnimation.cpp in Sources */,
                                93309E15099E64920056E581 /* SplitTextNodeContainingElementCommand.cpp in Sources */,
                                A1E1154813015C5D0054AC8C /* SpotLightSource.cpp in Sources */,
                                97BC6A3E1505F081001B74AC /* SQLException.cpp in Sources */,
-                               7CEF26191D6A931700BE905D /* JSCryptoCustom.cpp in Sources */,
                                1A2E6E7A0CC556D5004A2062 /* SQLiteAuthorizer.cpp in Sources */,
                                1A2246490CC98DDB00C05240 /* SQLiteDatabase.cpp in Sources */,
                                7E474E2012494DC900235364 /* SQLiteDatabaseTracker.cpp in Sources */,
                                B22279A10D00BF220071B782 /* SVGColor.cpp in Sources */,
                                B22279A40D00BF220071B782 /* SVGComponentTransferFunctionElement.cpp in Sources */,
                                B2227B050D00BFF10071B782 /* SVGCSSComputedStyleDeclaration.cpp in Sources */,
+                               946D37331D6CC42B0077084F /* SVGCSSParser.cpp in Sources */,
                                B22279A70D00BF220071B782 /* SVGCursorElement.cpp in Sources */,
                                B22279AD0D00BF220071B782 /* SVGDefsElement.cpp in Sources */,
                                B22279B00D00BF220071B782 /* SVGDescElement.cpp in Sources */,
                                7AA3A699194A64E7001CBD24 /* TileController.cpp in Sources */,
                                1F72BF0A187FD4490009BCB3 /* TileControllerMemoryHandlerIOS.cpp in Sources */,
                                7AA3A6A3194B5C22001CBD24 /* TileCoverageMap.cpp in Sources */,
-                               946D372D1D6CB2940077084F /* CSSParser.cpp in Sources */,
                                7AA3A69B194A64E7001CBD24 /* TileGrid.cpp in Sources */,
                                498770F21242C535002226BA /* TilingData.cpp in Sources */,
                                F55B3DDB1251F12D003EF269 /* TimeInputType.cpp in Sources */,
index 2f614f2..b4f99e8 100644 (file)
@@ -62,9 +62,9 @@
 #include "CSSKeyframeRule.cpp"
 #include "CSSKeyframesRule.cpp"
 #include "CSSLineBoxContainValue.cpp"
+#include "CSSMarkup.cpp"
 #include "CSSMediaRule.cpp"
 #include "CSSNamedImageValue.cpp"
-#include "CSSOMUtils.cpp"
 #include "CSSPageRule.cpp"
 #include "CSSParser.cpp"
 #include "CSSParserValues.cpp"
diff --git a/Source/WebCore/css/CSSMarkup.cpp b/Source/WebCore/css/CSSMarkup.cpp
new file mode 100644 (file)
index 0000000..2c578d9
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2003 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ * Copyright (C) 2004-2012, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+ * 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.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "CSSMarkup.h"
+
+#include "CSSParserIdioms.h"
+#include <wtf/HexNumber.h>
+#include <wtf/text/StringBuffer.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+template <typename CharacterType>
+static inline bool isCSSTokenizerIdentifier(const CharacterType* characters, unsigned length)
+{
+    const CharacterType* end = characters + length;
+
+    // -?
+    if (characters != end && characters[0] == '-')
+        ++characters;
+
+    // {nmstart}
+    if (characters == end || !isNameStartCodePoint(characters[0]))
+        return false;
+    ++characters;
+
+    // {nmchar}*
+    for (; characters != end; ++characters) {
+        if (!isNameCodePoint(characters[0]))
+            return false;
+    }
+
+    return true;
+}
+
+// "ident" from the CSS tokenizer, minus backslash-escape sequences
+static bool isCSSTokenizerIdentifier(const String& string)
+{
+    unsigned length = string.length();
+
+    if (!length)
+        return false;
+
+    if (string.is8Bit())
+        return isCSSTokenizerIdentifier(string.characters8(), length);
+    return isCSSTokenizerIdentifier(string.characters16(), length);
+}
+
+static void serializeCharacter(UChar32 c, StringBuilder& appendTo)
+{
+    appendTo.append('\\');
+    appendTo.append(c);
+}
+
+static void serializeCharacterAsCodePoint(UChar32 c, StringBuilder& appendTo)
+{
+    appendTo.append('\\');
+    appendUnsignedAsHex(c, appendTo, Lowercase);
+    appendTo.append(' ');
+}
+
+void serializeIdentifier(const String& identifier, StringBuilder& appendTo, bool skipStartChecks)
+{
+    bool isFirst = !skipStartChecks;
+    bool isSecond = false;
+    bool isFirstCharHyphen = false;
+    unsigned index = 0;
+    while (index < identifier.length()) {
+        UChar32 c = identifier.characterStartingAt(index);
+        if (!c) {
+            // Check for lone surrogate which characterStartingAt does not return.
+            c = identifier[index];
+        }
+
+        index += U16_LENGTH(c);
+
+        if (!c)
+            appendTo.append(0xfffd);
+        else if (c <= 0x1f || c == 0x7f || (0x30 <= c && c <= 0x39 && (isFirst || (isSecond && isFirstCharHyphen))))
+            serializeCharacterAsCodePoint(c, appendTo);
+        else if (c == 0x2d && isFirst && index == identifier.length())
+            serializeCharacter(c, appendTo);
+        else if (0x80 <= c || c == 0x2d || c == 0x5f || (0x30 <= c && c <= 0x39) || (0x41 <= c && c <= 0x5a) || (0x61 <= c && c <= 0x7a))
+            appendTo.append(c);
+        else
+            serializeCharacter(c, appendTo);
+
+        if (isFirst) {
+            isFirst = false;
+            isSecond = true;
+            isFirstCharHyphen = (c == 0x2d);
+        } else if (isSecond)
+            isSecond = false;
+    }
+}
+
+void serializeString(const String& string, StringBuilder& appendTo)
+{
+    appendTo.append('\"');
+
+    unsigned index = 0;
+    while (index < string.length()) {
+        UChar32 c = string.characterStartingAt(index);
+        index += U16_LENGTH(c);
+
+        if (c <= 0x1f || c == 0x7f)
+            serializeCharacterAsCodePoint(c, appendTo);
+        else if (c == 0x22 || c == 0x5c)
+            serializeCharacter(c, appendTo);
+        else
+            appendTo.append(c);
+    }
+
+    appendTo.append('\"');
+}
+
+String serializeString(const String& string)
+{
+    StringBuilder builder;
+    serializeString(string, builder);
+    return builder.toString();
+}
+
+String serializeURI(const String& string)
+{
+    return "url(" + serializeString(string) + ")";
+}
+
+String serializeFontFamily(const String& string)
+{
+    return isCSSTokenizerIdentifier(string) ? string : serializeString(string);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/css/CSSMarkup.h b/Source/WebCore/css/CSSMarkup.h
new file mode 100644 (file)
index 0000000..a1ae16e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2004-2008, 2009-2010, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 - 2010  Torch Mobile (Beijing) Co. Ltd. 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef CSSMarkup_h
+#define CSSMarkup_h
+
+#include <wtf/text/WTFString.h>
+
+// Helper functions for converting from CSSValues to text.
+
+namespace WebCore {
+
+// Common serializing methods. See: http://dev.w3.org/csswg/cssom/#common-serializing-idioms
+void serializeIdentifier(const String& identifier, StringBuilder& appendTo, bool skipStartChecks = false);
+void serializeString(const String&, StringBuilder& appendTo);
+String serializeString(const String&);
+String serializeURI(const String&);
+String serializeFontFamily(const String&);
+
+} // namespace WebCore
+
+#endif // CSSMarkup_h
diff --git a/Source/WebCore/css/CSSOMUtils.cpp b/Source/WebCore/css/CSSOMUtils.cpp
deleted file mode 100644 (file)
index 6913cd9..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2010 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "CSSOMUtils.h"
-
-#include <wtf/HexNumber.h>
-#include <wtf/text/StringBuilder.h>
-
-namespace WebCore {
-
-void serializeCharacter(UChar32 c, StringBuilder& appendTo)
-{
-    appendTo.append('\\');
-    appendTo.append(c);
-}
-
-void serializeCharacterAsCodePoint(UChar32 c, StringBuilder& appendTo)
-{
-    appendTo.append('\\');
-    appendUnsignedAsHex(c, appendTo, Lowercase);
-    appendTo.append(' ');
-}
-
-void serializeIdentifier(const String& identifier, StringBuilder& appendTo)
-{
-    bool isFirst = true;
-    bool isSecond = false;
-    bool isFirstCharHyphen = false;
-    unsigned index = 0;
-    while (index < identifier.length()) {
-        UChar32 c = identifier.characterStartingAt(index);
-        if (!c) {
-            // Check for lone surrogate which characterStartingAt does not return.
-            c = identifier[index];
-        }
-
-        index += U16_LENGTH(c);
-
-        if (!c)
-            appendTo.append(0xfffd);
-        else if (c <= 0x1f || c == 0x7f || (0x30 <= c && c <= 0x39 && (isFirst || (isSecond && isFirstCharHyphen))))
-            serializeCharacterAsCodePoint(c, appendTo);
-        else if (c == 0x2d && isFirst && index == identifier.length())
-            serializeCharacter(c, appendTo);
-        else if (0x80 <= c || c == 0x2d || c == 0x5f || (0x30 <= c && c <= 0x39) || (0x41 <= c && c <= 0x5a) || (0x61 <= c && c <= 0x7a))
-            appendTo.append(c);
-        else
-            serializeCharacter(c, appendTo);
-
-        if (isFirst) {
-            isFirst = false;
-            isSecond = true;
-            isFirstCharHyphen = (c == 0x2d);
-        } else if (isSecond)
-            isSecond = false;
-    }
-}
-
-void serializeString(const String& string, StringBuilder& appendTo)
-{
-    appendTo.append('\"');
-
-    unsigned index = 0;
-    while (index < string.length()) {
-        UChar32 c = string.characterStartingAt(index);
-        index += U16_LENGTH(c);
-        if (c <= 0x1f)
-            serializeCharacterAsCodePoint(c, appendTo);
-        else if (c == 0x22 || c == 0x5c)
-            serializeCharacter(c, appendTo);
-        else
-            appendTo.append(c);
-    }
-
-    appendTo.append('\"');
-}
-
-} // namespace WebCore
index 2a2281f..701a901 100644 (file)
@@ -26,7 +26,7 @@
 #include "config.h"
 #include "CSSSelector.h"
 
-#include "CSSOMUtils.h"
+#include "CSSMarkup.h"
 #include "CSSSelectorList.h"
 #include "HTMLNames.h"
 #include "SelectorPseudoTypeMap.h"
index d963c82..7f878b2 100644 (file)
@@ -30,7 +30,7 @@
 #include "config.h"
 #include "DOMCSSNamespace.h"
 
-#include "CSSOMUtils.h"
+#include "CSSMarkup.h"
 #include "CSSParser.h"
 #include "StyleProperties.h"
 #include <wtf/text/StringBuilder.h>
index e078695..1c4490c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2003 Lars Knoll (knoll@kde.org)
  * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
- * Copyright (C) 2004-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2016 Apple Inc. All rights reserved.
  * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
  * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
@@ -13707,7 +13707,7 @@ CSSValueID cssValueKeywordID(const CSSParserString& string)
 }
 
 template <typename CharacterType>
-static inline bool isCSSTokenizerIdentifier(const CharacterType* characters, unsigned length)
+static inline bool isCSSTokenizerIdent(const CharacterType* characters, unsigned length)
 {
     const CharacterType* end = characters + length;
 
@@ -13730,7 +13730,7 @@ static inline bool isCSSTokenizerIdentifier(const CharacterType* characters, uns
 }
 
 // "ident" from the CSS tokenizer, minus backslash-escape sequences
-static bool isCSSTokenizerIdentifier(const String& string)
+static bool isCSSTokenizerIdent(const String& string)
 {
     unsigned length = string.length();
 
@@ -13738,8 +13738,8 @@ static bool isCSSTokenizerIdentifier(const String& string)
         return false;
 
     if (string.is8Bit())
-        return isCSSTokenizerIdentifier(string.characters8(), length);
-    return isCSSTokenizerIdentifier(string.characters16(), length);
+        return isCSSTokenizerIdent(string.characters8(), length);
+    return isCSSTokenizerIdent(string.characters16(), length);
 }
 
 template <typename CharacterType>
@@ -13853,7 +13853,7 @@ String quoteCSSString(const String& string)
 
 String quoteCSSStringIfNeeded(const String& string)
 {
-    return isCSSTokenizerIdentifier(string) ? string : quoteCSSString(string);
+    return isCSSTokenizerIdent(string) ? string : quoteCSSString(string);
 }
 
 String quoteCSSURLIfNeeded(const String& string)
diff --git a/Source/WebCore/css/parser/CSSParserFastPaths.cpp b/Source/WebCore/css/parser/CSSParserFastPaths.cpp
new file mode 100644 (file)
index 0000000..abcf29b
--- /dev/null
@@ -0,0 +1,1118 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "CSSParserFastPaths.h"
+
+// FIXME-NEWPARSER: #include "CSSColorValue.h"
+#include "CSSFunctionValue.h"
+#include "CSSInheritedValue.h"
+#include "CSSInitialValue.h"
+#include "CSSParserIdioms.h"
+#include "CSSPrimitiveValue.h"
+#include "CSSPropertyParser.h"
+#include "HTMLParserIdioms.h"
+#include "RuntimeEnabledFeatures.h"
+// FIXME-NEWPARSER: #include "StyleColor.h"
+#include "StylePropertyShorthand.h"
+
+namespace WebCore {
+
+/* FIXME-NEWPARSER: Turn off for now.
+static inline bool isSimpleLengthPropertyID(CSSPropertyID propertyId, bool& acceptsNegativeNumbers)
+{
+    switch (propertyId) {
+    case CSSPropertyFontSize:
+    case CSSPropertyGridColumnGap:
+    case CSSPropertyGridRowGap:
+    case CSSPropertyHeight:
+    case CSSPropertyWidth:
+    case CSSPropertyMinHeight:
+    case CSSPropertyMinWidth:
+    case CSSPropertyPaddingBottom:
+    case CSSPropertyPaddingLeft:
+    case CSSPropertyPaddingRight:
+    case CSSPropertyPaddingTop:
+    case CSSPropertyWebkitLogicalWidth:
+    case CSSPropertyWebkitLogicalHeight:
+    case CSSPropertyWebkitMinLogicalWidth:
+    case CSSPropertyWebkitMinLogicalHeight:
+    case CSSPropertyWebkitPaddingAfter:
+    case CSSPropertyWebkitPaddingBefore:
+    case CSSPropertyWebkitPaddingEnd:
+    case CSSPropertyWebkitPaddingStart:
+  //  case CSSPropertyShapeMargin:
+    case CSSPropertyR:
+    case CSSPropertyRx:
+    case CSSPropertyRy:
+        acceptsNegativeNumbers = false;
+        return true;
+    case CSSPropertyBottom:
+    case CSSPropertyCx:
+    case CSSPropertyCy:
+    case CSSPropertyLeft:
+    case CSSPropertyMarginBottom:
+    case CSSPropertyMarginLeft:
+    case CSSPropertyMarginRight:
+    case CSSPropertyMarginTop:
+ //   case CSSPropertyMotionOffset:
+    case CSSPropertyRight:
+    case CSSPropertyTop:
+    case CSSPropertyWebkitMarginAfter:
+    case CSSPropertyWebkitMarginBefore:
+    case CSSPropertyWebkitMarginEnd:
+    case CSSPropertyWebkitMarginStart:
+    case CSSPropertyX:
+    case CSSPropertyY:
+        acceptsNegativeNumbers = true;
+        return true;
+    default:
+        return false;
+    }
+}
+
+template <typename CharacterType>
+static inline bool parseSimpleLength(const CharacterType* characters, unsigned length, CSSPrimitiveValue::UnitTypes& unit, double& number)
+{
+    if (length > 2 && (characters[length - 2] | 0x20) == 'p' && (characters[length - 1] | 0x20) == 'x') {
+        length -= 2;
+        unit = CSSPrimitiveValue::UnitTypes::CSS_PX;
+    } else if (length > 1 && characters[length - 1] == '%') {
+        length -= 1;
+        unit = CSSPrimitiveValue::UnitTypes::CSS_PERCENTAGE;
+    }
+
+    // We rely on charactersToDouble for validation as well. The function
+    // will set "ok" to "false" if the entire passed-in character range does
+    // not represent a double.
+    bool ok;
+    number = charactersToDouble(characters, length, &ok);
+    if (!ok)
+        return false;
+    number = clampTo<double>(number, -std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
+    return true;
+}
+
+static CSSValue* parseSimpleLengthValue(CSSPropertyID propertyId, const String& string, CSSParserMode cssParserMode)
+{
+    ASSERT(!string.isEmpty());
+    bool acceptsNegativeNumbers = false;
+
+    // In @viewport, width and height are shorthands, not simple length values.
+    if (isCSSViewportParsingEnabledForMode(cssParserMode) || !isSimpleLengthPropertyID(propertyId, acceptsNegativeNumbers))
+        return nullptr;
+
+    unsigned length = string.length();
+    double number;
+    CSSPrimitiveValue::UnitTypes unit = CSSPrimitiveValue::UnitTypes::Number;
+
+    if (string.is8Bit()) {
+        if (!parseSimpleLength(string.characters8(), length, unit, number))
+            return nullptr;
+    } else {
+        if (!parseSimpleLength(string.characters16(), length, unit, number))
+            return nullptr;
+    }
+
+    if (unit == CSSPrimitiveValue::UnitTypes::Number) {
+        if (cssParserMode == SVGAttributeMode)
+            unit = CSSPrimitiveValue::UnitTypes::UserUnits;
+        else if (!number)
+            unit = CSSPrimitiveValue::UnitTypes::Pixels;
+        else
+            return nullptr;
+    }
+
+    if (number < 0 && !acceptsNegativeNumbers)
+        return nullptr;
+
+    return CSSPrimitiveValue::create(number, unit);
+}
+
+static inline bool isColorPropertyID(CSSPropertyID propertyId)
+{
+    switch (propertyId) {
+    case CSSPropertyColor:
+    case CSSPropertyBackgroundColor:
+    case CSSPropertyBorderBottomColor:
+    case CSSPropertyBorderLeftColor:
+    case CSSPropertyBorderRightColor:
+    case CSSPropertyBorderTopColor:
+    case CSSPropertyFill:
+    case CSSPropertyFloodColor:
+    case CSSPropertyLightingColor:
+    case CSSPropertyOutlineColor:
+    case CSSPropertyStopColor:
+    case CSSPropertyStroke:
+    case CSSPropertyWebkitBorderAfterColor:
+    case CSSPropertyWebkitBorderBeforeColor:
+    case CSSPropertyWebkitBorderEndColor:
+    case CSSPropertyWebkitBorderStartColor:
+    case CSSPropertyColumnRuleColor:
+    case CSSPropertyWebkitTextEmphasisColor:
+    case CSSPropertyWebkitTextFillColor:
+    case CSSPropertyWebkitTextStrokeColor:
+    case CSSPropertyTextDecorationColor:
+        return true;
+    default:
+        return false;
+    }
+}
+
+// Returns the number of characters which form a valid double
+// and are terminated by the given terminator character
+template <typename CharacterType>
+static int checkForValidDouble(const CharacterType* string, const CharacterType* end, const char terminator)
+{
+    int length = end - string;
+    if (length < 1)
+        return 0;
+
+    bool decimalMarkSeen = false;
+    int processedLength = 0;
+
+    for (int i = 0; i < length; ++i) {
+        if (string[i] == terminator) {
+            processedLength = i;
+            break;
+        }
+        if (!isASCIIDigit(string[i])) {
+            if (!decimalMarkSeen && string[i] == '.')
+                decimalMarkSeen = true;
+            else
+                return 0;
+        }
+    }
+
+    if (decimalMarkSeen && processedLength == 1)
+        return 0;
+
+    return processedLength;
+}
+
+// Returns the number of characters consumed for parsing a valid double
+// terminated by the given terminator character
+template <typename CharacterType>
+static int parseDouble(const CharacterType* string, const CharacterType* end, const char terminator, double& value)
+{
+    int length = checkForValidDouble(string, end, terminator);
+    if (!length)
+        return 0;
+
+    int position = 0;
+    double localValue = 0;
+
+    // The consumed characters here are guaranteed to be
+    // ASCII digits with or without a decimal mark
+    for (; position < length; ++position) {
+        if (string[position] == '.')
+            break;
+        localValue = localValue * 10 + string[position] - '0';
+    }
+
+    if (++position == length) {
+        value = localValue;
+        return length;
+    }
+
+    double fraction = 0;
+    double scale = 1;
+
+    const double maxScale = 1000000;
+    while (position < length && scale < maxScale) {
+        fraction = fraction * 10 + string[position++] - '0';
+        scale *= 10;
+    }
+
+    value = localValue + fraction / scale;
+    return length;
+}
+
+template <typename CharacterType>
+static bool parseColorIntOrPercentage(const CharacterType*& string, const CharacterType* end, const char terminator, CSSPrimitiveValue::UnitTypes& expect, int& value)
+{
+    const CharacterType* current = string;
+    double localValue = 0;
+    bool negative = false;
+    while (current != end && isHTMLSpace<CharacterType>(*current))
+        current++;
+    if (current != end && *current == '-') {
+        negative = true;
+        current++;
+    }
+    if (current == end || !isASCIIDigit(*current))
+        return false;
+    while (current != end && isASCIIDigit(*current)) {
+        double newValue = localValue * 10 + *current++ - '0';
+        if (newValue >= 255) {
+            // Clamp values at 255.
+            localValue = 255;
+            while (current != end && isASCIIDigit(*current))
+                ++current;
+            break;
+        }
+        localValue = newValue;
+    }
+
+    if (current == end)
+        return false;
+
+    if (expect == CSSPrimitiveValue::UnitTypes::Number && (*current == '.' || *current == '%'))
+        return false;
+
+    if (*current == '.') {
+        // We already parsed the integral part, try to parse
+        // the fraction part of the percentage value.
+        double percentage = 0;
+        int numCharactersParsed = parseDouble(current, end, '%', percentage);
+        if (!numCharactersParsed)
+            return false;
+        current += numCharactersParsed;
+        if (*current != '%')
+            return false;
+        localValue += percentage;
+    }
+
+    if (expect == CSSPrimitiveValue::UnitTypes::Percentage && *current != '%')
+        return false;
+
+    if (*current == '%') {
+        expect = CSSPrimitiveValue::UnitTypes::Percentage;
+        localValue = localValue / 100.0 * 256.0;
+        // Clamp values at 255 for percentages over 100%
+        if (localValue > 255)
+            localValue = 255;
+        current++;
+    } else {
+        expect = CSSPrimitiveValue::UnitTypes::Number;
+    }
+
+    while (current != end && isHTMLSpace<CharacterType>(*current))
+        current++;
+    if (current == end || *current++ != terminator)
+        return false;
+    // Clamp negative values at zero.
+    value = negative ? 0 : static_cast<int>(localValue);
+    string = current;
+    return true;
+}
+
+template <typename CharacterType>
+static inline bool isTenthAlpha(const CharacterType* string, const int length)
+{
+    // "0.X"
+    if (length == 3 && string[0] == '0' && string[1] == '.' && isASCIIDigit(string[2]))
+        return true;
+
+    // ".X"
+    if (length == 2 && string[0] == '.' && isASCIIDigit(string[1]))
+        return true;
+
+    return false;
+}
+
+template <typename CharacterType>
+static inline bool parseAlphaValue(const CharacterType*& string, const CharacterType* end, const char terminator, int& value)
+{
+    while (string != end && isHTMLSpace<CharacterType>(*string))
+        string++;
+
+    bool negative = false;
+
+    if (string != end && *string == '-') {
+        negative = true;
+        string++;
+    }
+
+    value = 0;
+
+    int length = end - string;
+    if (length < 2)
+        return false;
+
+    if (string[length - 1] != terminator || !isASCIIDigit(string[length - 2]))
+        return false;
+
+    if (string[0] != '0' && string[0] != '1' && string[0] != '.') {
+        if (checkForValidDouble(string, end, terminator)) {
+            value = negative ? 0 : 255;
+            string = end;
+            return true;
+        }
+        return false;
+    }
+
+    if (length == 2 && string[0] != '.') {
+        value = !negative && string[0] == '1' ? 255 : 0;
+        string = end;
+        return true;
+    }
+
+    if (isTenthAlpha(string, length - 1)) {
+        static const int tenthAlphaValues[] = { 0, 25, 51, 76, 102, 127, 153, 179, 204, 230 };
+        value = negative ? 0 : tenthAlphaValues[string[length - 2] - '0'];
+        string = end;
+        return true;
+    }
+
+    double alpha = 0;
+    if (!parseDouble(string, end, terminator, alpha))
+        return false;
+    value = negative ? 0 : static_cast<int>(alpha * nextafter(256.0, 0.0));
+    string = end;
+    return true;
+}
+
+template <typename CharacterType>
+static inline bool mightBeRGBA(const CharacterType* characters, unsigned length)
+{
+    if (length < 5)
+        return false;
+    return characters[4] == '('
+        && isASCIIAlphaCaselessEqual(characters[0], 'r')
+        && isASCIIAlphaCaselessEqual(characters[1], 'g')
+        && isASCIIAlphaCaselessEqual(characters[2], 'b')
+        && isASCIIAlphaCaselessEqual(characters[3], 'a');
+}
+
+template <typename CharacterType>
+static inline bool mightBeRGB(const CharacterType* characters, unsigned length)
+{
+    if (length < 4)
+        return false;
+    return characters[3] == '('
+        && isASCIIAlphaCaselessEqual(characters[0], 'r')
+        && isASCIIAlphaCaselessEqual(characters[1], 'g')
+        && isASCIIAlphaCaselessEqual(characters[2], 'b');
+}
+
+template <typename CharacterType>
+static bool fastParseColorInternal(RGBA32& rgb, const CharacterType* characters, unsigned length, bool quirksMode)
+{
+    CSSPrimitiveValue::UnitTypes expect = CSSPrimitiveValue::UnitTypes::Unknown;
+
+    if (length >= 4 && characters[0] == '#')
+        return Color::parseHexColor(characters + 1, length - 1, rgb);
+
+    if (quirksMode && (length == 3 || length == 6)) {
+        if (Color::parseHexColor(characters, length, rgb))
+            return true;
+    }
+
+    // Try rgba() syntax.
+    if (mightBeRGBA(characters, length)) {
+        const CharacterType* current = characters + 5;
+        const CharacterType* end = characters + length;
+        int red;
+        int green;
+        int blue;
+        int alpha;
+
+        if (!parseColorIntOrPercentage(current, end, ',', expect, red))
+            return false;
+        if (!parseColorIntOrPercentage(current, end, ',', expect, green))
+            return false;
+        if (!parseColorIntOrPercentage(current, end, ',', expect, blue))
+            return false;
+        if (!parseAlphaValue(current, end, ')', alpha))
+            return false;
+        if (current != end)
+            return false;
+        rgb = makeRGBA(red, green, blue, alpha);
+        return true;
+    }
+
+    // Try rgb() syntax.
+    if (mightBeRGB(characters, length)) {
+        const CharacterType* current = characters + 4;
+        const CharacterType* end = characters + length;
+        int red;
+        int green;
+        int blue;
+        if (!parseColorIntOrPercentage(current, end, ',', expect, red))
+            return false;
+        if (!parseColorIntOrPercentage(current, end, ',', expect, green))
+            return false;
+        if (!parseColorIntOrPercentage(current, end, ')', expect, blue))
+            return false;
+        if (current != end)
+            return false;
+        rgb = makeRGB(red, green, blue);
+        return true;
+    }
+
+    return false;
+}
+
+CSSValue* CSSParserFastPaths::parseColor(const String& string, CSSParserMode parserMode)
+{
+    ASSERT(!string.isEmpty());
+    CSSValueID valueID = cssValueKeywordID(string);
+    if (StyleColor::isColorKeyword(valueID)) {
+        if (!isValueAllowedInMode(valueID, parserMode))
+            return nullptr;
+        return CSSPrimitiveValue::createIdentifier(valueID);
+    }
+
+    RGBA32 color;
+    bool quirksMode = isQuirksModeBehavior(parserMode);
+
+    // Fast path for hex colors and rgb()/rgba() colors
+    bool parseResult;
+    if (string.is8Bit())
+        parseResult = fastParseColorInternal(color, string.characters8(), string.length(), quirksMode);
+    else
+        parseResult = fastParseColorInternal(color, string.characters16(), string.length(), quirksMode);
+    if (!parseResult)
+        return nullptr;
+    return CSSColorValue::create(color);
+}
+
+bool CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyID propertyId, CSSValueID valueID, CSSParserMode parserMode)
+{
+    if (valueID == CSSValueInvalid || !isValueAllowedInMode(valueID, parserMode))
+        return false;
+
+    switch (propertyId) {
+    case CSSPropertyAlignmentBaseline:
+        // auto | baseline | before-edge | text-before-edge | middle |
+        // central | after-edge | text-after-edge | ideographic | alphabetic |
+        // hanging | mathematical
+        return valueID == CSSValueAuto || valueID == CSSValueAlphabetic || valueID == CSSValueBaseline
+            || valueID == CSSValueMiddle || (valueID >= CSSValueBeforeEdge && valueID <= CSSValueMathematical);
+    case CSSPropertyAll:
+        return false; // Only accepts css-wide keywords
+    case CSSPropertyBackgroundRepeatX: // repeat | no-repeat
+    case CSSPropertyBackgroundRepeatY: // repeat | no-repeat
+        return valueID == CSSValueRepeat || valueID == CSSValueNoRepeat;
+    case CSSPropertyBorderCollapse: // collapse | separate
+        return valueID == CSSValueCollapse || valueID == CSSValueSeparate;
+    case CSSPropertyBorderTopStyle: // <border-style>
+    case CSSPropertyBorderRightStyle: // Defined as: none | hidden | dotted | dashed |
+    case CSSPropertyBorderBottomStyle: // solid | double | groove | ridge | inset | outset
+    case CSSPropertyBorderLeftStyle:
+    case CSSPropertyWebkitBorderAfterStyle:
+    case CSSPropertyWebkitBorderBeforeStyle:
+    case CSSPropertyWebkitBorderEndStyle:
+    case CSSPropertyWebkitBorderStartStyle:
+    case CSSPropertyColumnRuleStyle:
+        return valueID >= CSSValueNone && valueID <= CSSValueDouble;
+    case CSSPropertyBoxSizing:
+        return valueID == CSSValueBorderBox || valueID == CSSValueContentBox;
+    case CSSPropertyBufferedRendering:
+        return valueID == CSSValueAuto || valueID == CSSValueDynamic || valueID == CSSValueStatic;
+    case CSSPropertyCaptionSide: // top | bottom | left | right
+        return valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueTop || valueID == CSSValueBottom;
+    case CSSPropertyClear: // none | left | right | both
+        return valueID == CSSValueNone || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueBoth;
+    case CSSPropertyClipRule:
+    case CSSPropertyFillRule:
+        return valueID == CSSValueNonzero || valueID == CSSValueEvenodd;
+    case CSSPropertyColorInterpolation:
+    case CSSPropertyColorInterpolationFilters:
+        return valueID == CSSValueAuto || valueID == CSSValueSRGB || valueID == CSSValueLinearRGB;
+    case CSSPropertyColorRendering:
+        return valueID == CSSValueAuto || valueID == CSSValueOptimizeSpeed || valueID == CSSValueOptimizeQuality;
+    case CSSPropertyDirection: // ltr | rtl
+        return valueID == CSSValueLtr || valueID == CSSValueRtl;
+    case CSSPropertyDisplay:
+        // inline | block | list-item | inline-block | table |
+        // inline-table | table-row-group | table-header-group | table-footer-group | table-row |
+        // table-column-group | table-column | table-cell | table-caption | -webkit-box | -webkit-inline-box | none
+        // flex | inline-flex | -webkit-flex | -webkit-inline-flex | grid | inline-grid
+        return (valueID >= CSSValueInline && valueID <= CSSValueInlineFlex) || valueID == CSSValueWebkitFlex || valueID == CSSValueWebkitInlineFlex || valueID == CSSValueNone
+            || (RuntimeEnabledFeatures::cssGridLayoutEnabled() && (valueID == CSSValueGrid || valueID == CSSValueInlineGrid));
+    case CSSPropertyDominantBaseline:
+        // auto | use-script | no-change | reset-size | ideographic |
+        // alphabetic | hanging | mathematical | central | middle |
+        // text-after-edge | text-before-edge
+        return valueID == CSSValueAuto || valueID == CSSValueAlphabetic || valueID == CSSValueMiddle
+            || (valueID >= CSSValueUseScript && valueID <= CSSValueResetSize)
+            || (valueID >= CSSValueCentral && valueID <= CSSValueMathematical);
+    case CSSPropertyEmptyCells: // show | hide
+        return valueID == CSSValueShow || valueID == CSSValueHide;
+    case CSSPropertyFloat: // left | right | none
+        return valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueNone;
+    case CSSPropertyFontStyle: // normal | italic | oblique
+        return valueID == CSSValueNormal || valueID == CSSValueItalic || valueID == CSSValueOblique;
+    case CSSPropertyFontStretch: // normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded
+        return valueID == CSSValueNormal || (valueID >= CSSValueUltraCondensed && valueID <= CSSValueUltraExpanded);
+    case CSSPropertyImageRendering: // auto | optimizeContrast | pixelated
+        return valueID == CSSValueAuto || valueID == CSSValueWebkitOptimizeContrast || valueID == CSSValuePixelated;
+    case CSSPropertyIsolation: // auto | isolate
+        return valueID == CSSValueAuto || valueID == CSSValueIsolate;
+    case CSSPropertyListStylePosition: // inside | outside
+        return valueID == CSSValueInside || valueID == CSSValueOutside;
+    case CSSPropertyListStyleType:
+        // See section CSS_PROP_LIST_STYLE_TYPE of file CSSValueKeywords.in
+        // for the list of supported list-style-types.
+        return (valueID >= CSSValueDisc && valueID <= CSSValueKatakanaIroha) || valueID == CSSValueNone;
+    case CSSPropertyMaskType:
+        return valueID == CSSValueLuminance || valueID == CSSValueAlpha;
+    case CSSPropertyObjectFit:
+        return valueID == CSSValueFill || valueID == CSSValueContain || valueID == CSSValueCover || valueID == CSSValueNone || valueID == CSSValueScaleDown;
+    case CSSPropertyOutlineStyle: // (<border-style> except hidden) | auto
+        return valueID == CSSValueAuto || valueID == CSSValueNone || (valueID >= CSSValueInset && valueID <= CSSValueDouble);
+    case CSSPropertyOverflowAnchor:
+        return valueID == CSSValueVisible || valueID == CSSValueNone || valueID == CSSValueAuto;
+    case CSSPropertyOverflowWrap: // normal | break-word
+    case CSSPropertyWordWrap:
+        return valueID == CSSValueNormal || valueID == CSSValueBreakWord;
+    case CSSPropertyOverflowX: // visible | hidden | scroll | auto | overlay
+        return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay;
+    case CSSPropertyOverflowY: // visible | hidden | scroll | auto | overlay | -webkit-paged-x | -webkit-paged-y
+        return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay || valueID == CSSValueWebkitPagedX || valueID == CSSValueWebkitPagedY;
+    case CSSPropertyBreakAfter:
+    case CSSPropertyBreakBefore:
+        return valueID == CSSValueAuto || valueID == CSSValueAvoid || valueID == CSSValueAvoidPage || valueID == CSSValuePage || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueRecto || valueID == CSSValueVerso || valueID == CSSValueAvoidColumn || valueID == CSSValueColumn;
+    case CSSPropertyBreakInside:
+        return valueID == CSSValueAuto || valueID == CSSValueAvoid || valueID == CSSValueAvoidPage || valueID == CSSValueAvoidColumn;
+    case CSSPropertyPointerEvents:
+        // none | visiblePainted | visibleFill | visibleStroke | visible |
+        // painted | fill | stroke | auto | all | bounding-box
+        return valueID == CSSValueVisible || valueID == CSSValueNone || valueID == CSSValueAll || valueID == CSSValueAuto || (valueID >= CSSValueVisiblePainted && valueID <= CSSValueBoundingBox);
+    case CSSPropertyPosition: // static | relative | absolute | fixed | sticky
+        return valueID == CSSValueStatic || valueID == CSSValueRelative || valueID == CSSValueAbsolute || valueID == CSSValueFixed || (RuntimeEnabledFeatures::cssStickyPositionEnabled() && valueID == CSSValueSticky);
+    case CSSPropertyResize: // none | both | horizontal | vertical | auto
+        return valueID == CSSValueNone || valueID == CSSValueBoth || valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueAuto;
+    case CSSPropertyScrollBehavior: // auto | smooth
+        ASSERT(RuntimeEnabledFeatures::cssomSmoothScrollEnabled());
+        return valueID == CSSValueAuto || valueID == CSSValueSmooth;
+    case CSSPropertyShapeRendering:
+        return valueID == CSSValueAuto || valueID == CSSValueOptimizeSpeed || valueID == CSSValueCrispEdges || valueID == CSSValueGeometricPrecision;
+    case CSSPropertySpeak: // none | normal | spell-out | digits | literal-punctuation | no-punctuation
+        return valueID == CSSValueNone || valueID == CSSValueNormal || valueID == CSSValueSpellOut || valueID == CSSValueDigits || valueID == CSSValueLiteralPunctuation || valueID == CSSValueNoPunctuation;
+    case CSSPropertyStrokeLinejoin:
+        return valueID == CSSValueMiter || valueID == CSSValueRound || valueID == CSSValueBevel;
+    case CSSPropertyStrokeLinecap:
+        return valueID == CSSValueButt || valueID == CSSValueRound || valueID == CSSValueSquare;
+    case CSSPropertyTableLayout: // auto | fixed
+        return valueID == CSSValueAuto || valueID == CSSValueFixed;
+    case CSSPropertyTextAlign:
+        return (valueID >= CSSValueWebkitAuto && valueID <= CSSValueInternalCenter) || valueID == CSSValueStart || valueID == CSSValueEnd;
+    case CSSPropertyTextAlignLast:
+        // auto | start | end | left | right | center | justify
+        return (valueID >= CSSValueLeft && valueID <= CSSValueJustify) || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueAuto;
+    case CSSPropertyTextAnchor:
+        return valueID == CSSValueStart || valueID == CSSValueMiddle || valueID == CSSValueEnd;
+    case CSSPropertyTextCombineUpright:
+        return valueID == CSSValueNone || valueID == CSSValueAll;
+    case CSSPropertyTextDecorationStyle:
+        // solid | double | dotted | dashed | wavy
+        ASSERT(RuntimeEnabledFeatures::css3TextDecorationsEnabled());
+        return valueID == CSSValueSolid || valueID == CSSValueDouble || valueID == CSSValueDotted || valueID == CSSValueDashed || valueID == CSSValueWavy;
+    case CSSPropertyTextJustify:
+        // auto | none | inter-word | distribute
+        ASSERT(RuntimeEnabledFeatures::css3TextEnabled());
+        return valueID == CSSValueInterWord || valueID == CSSValueDistribute || valueID == CSSValueAuto || valueID == CSSValueNone;
+    case CSSPropertyTextOrientation: // mixed | upright | sideways | sideways-right
+        return valueID == CSSValueMixed || valueID == CSSValueUpright || valueID == CSSValueSideways || valueID == CSSValueSidewaysRight;
+    case CSSPropertyWebkitTextOrientation:
+        return valueID == CSSValueSideways || valueID == CSSValueSidewaysRight || valueID == CSSValueVerticalRight || valueID == CSSValueUpright;
+    case CSSPropertyTextOverflow: // clip | ellipsis
+        return valueID == CSSValueClip || valueID == CSSValueEllipsis;
+    case CSSPropertyTextRendering: // auto | optimizeSpeed | optimizeLegibility | geometricPrecision
+        return valueID == CSSValueAuto || valueID == CSSValueOptimizeSpeed || valueID == CSSValueOptimizeLegibility || valueID == CSSValueGeometricPrecision;
+    case CSSPropertyTextTransform: // capitalize | uppercase | lowercase | none
+        return (valueID >= CSSValueCapitalize && valueID <= CSSValueLowercase) || valueID == CSSValueNone;
+    case CSSPropertyUnicodeBidi:
+        return valueID == CSSValueNormal || valueID == CSSValueEmbed
+            || valueID == CSSValueBidiOverride || valueID == CSSValueWebkitIsolate
+            || valueID == CSSValueWebkitIsolateOverride || valueID == CSSValueWebkitPlaintext
+            || valueID == CSSValueIsolate || valueID == CSSValueIsolateOverride || valueID == CSSValuePlaintext;
+    case CSSPropertyVectorEffect:
+        return valueID == CSSValueNone || valueID == CSSValueNonScalingStroke;
+    case CSSPropertyVisibility: // visible | hidden | collapse
+        return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueCollapse;
+    case CSSPropertyWebkitAppRegion:
+        return valueID >= CSSValueDrag && valueID <= CSSValueNoDrag;
+    case CSSPropertyWebkitAppearance:
+        return (valueID >= CSSValueCheckbox && valueID <= CSSValueTextarea) || valueID == CSSValueNone;
+    case CSSPropertyBackfaceVisibility:
+        return valueID == CSSValueVisible || valueID == CSSValueHidden;
+    case CSSPropertyMixBlendMode:
+        return valueID == CSSValueNormal || valueID == CSSValueMultiply || valueID == CSSValueScreen || valueID == CSSValueOverlay
+            || valueID == CSSValueDarken || valueID == CSSValueLighten || valueID == CSSValueColorDodge || valueID == CSSValueColorBurn
+            || valueID == CSSValueHardLight || valueID == CSSValueSoftLight || valueID == CSSValueDifference || valueID == CSSValueExclusion
+            || valueID == CSSValueHue || valueID == CSSValueSaturation || valueID == CSSValueColor || valueID == CSSValueLuminosity;
+    case CSSPropertyWebkitBoxAlign:
+        return valueID == CSSValueStretch || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline;
+    case CSSPropertyWebkitBoxDecorationBreak:
+        return valueID == CSSValueClone || valueID == CSSValueSlice;
+    case CSSPropertyWebkitBoxDirection:
+        return valueID == CSSValueNormal || valueID == CSSValueReverse;
+    case CSSPropertyWebkitBoxLines:
+        return valueID == CSSValueSingle || valueID == CSSValueMultiple;
+    case CSSPropertyWebkitBoxOrient:
+        return valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueInlineAxis || valueID == CSSValueBlockAxis;
+    case CSSPropertyWebkitBoxPack:
+        return valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueJustify;
+    case CSSPropertyColumnFill:
+        return valueID == CSSValueAuto || valueID == CSSValueBalance;
+    case CSSPropertyAlignContent:
+        // FIXME: Per CSS alignment, this property should accept an optional <overflow-position>. We should share this parsing code with 'justify-self'.
+        return valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueSpaceBetween || valueID == CSSValueSpaceAround || valueID == CSSValueStretch;
+    case CSSPropertyAlignItems:
+        // FIXME: Per CSS alignment, this property should accept the same arguments as 'justify-self' so we should share its parsing code.
+        return valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch;
+    case CSSPropertyAlignSelf:
+        // FIXME: Per CSS alignment, this property should accept the same arguments as 'justify-self' so we should share its parsing code.
+        return valueID == CSSValueAuto || valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch;
+    case CSSPropertyFlexDirection:
+        return valueID == CSSValueRow || valueID == CSSValueRowReverse || valueID == CSSValueColumn || valueID == CSSValueColumnReverse;
+    case CSSPropertyFlexWrap:
+        return valueID == CSSValueNowrap || valueID == CSSValueWrap || valueID == CSSValueWrapReverse;
+    case CSSPropertyHyphens:
+        return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueManual;
+    case CSSPropertyJustifyContent:
+        // FIXME: Per CSS alignment, this property should accept an optional <overflow-position>. We should share this parsing code with 'justify-self'.
+        return valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueSpaceBetween || valueID == CSSValueSpaceAround;
+    case CSSPropertyFontKerning:
+        return valueID == CSSValueAuto || valueID == CSSValueNormal || valueID == CSSValueNone;
+    case CSSPropertyWebkitFontSmoothing:
+        return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueAntialiased || valueID == CSSValueSubpixelAntialiased;
+    case CSSPropertyWebkitLineBreak: // auto | loose | normal | strict | after-white-space
+        return valueID == CSSValueAuto || valueID == CSSValueLoose || valueID == CSSValueNormal || valueID == CSSValueStrict || valueID == CSSValueAfterWhiteSpace;
+    case CSSPropertyWebkitMarginAfterCollapse:
+    case CSSPropertyWebkitMarginBeforeCollapse:
+    case CSSPropertyWebkitMarginBottomCollapse:
+    case CSSPropertyWebkitMarginTopCollapse:
+        return valueID == CSSValueCollapse || valueID == CSSValueSeparate || valueID == CSSValueDiscard;
+    case CSSPropertyWebkitPrintColorAdjust:
+        return valueID == CSSValueExact || valueID == CSSValueEconomy;
+    case CSSPropertyWebkitRtlOrdering:
+        return valueID == CSSValueLogical || valueID == CSSValueVisual;
+    case CSSPropertyWebkitRubyPosition:
+        return valueID == CSSValueBefore || valueID == CSSValueAfter;
+    case CSSPropertyWebkitTextCombine:
+        return valueID == CSSValueNone || valueID == CSSValueHorizontal;
+    case CSSPropertyWebkitTextEmphasisPosition:
+        return valueID == CSSValueOver || valueID == CSSValueUnder;
+    case CSSPropertyWebkitTextSecurity: // disc | circle | square | none
+        return valueID == CSSValueDisc || valueID == CSSValueCircle || valueID == CSSValueSquare || valueID == CSSValueNone;
+    case CSSPropertyTransformStyle:
+        return valueID == CSSValueFlat || valueID == CSSValuePreserve3d;
+    case CSSPropertyWebkitUserDrag: // auto | none | element
+        return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueElement;
+    case CSSPropertyWebkitUserModify: // read-only | read-write
+        return valueID == CSSValueReadOnly || valueID == CSSValueReadWrite || valueID == CSSValueReadWritePlaintextOnly;
+    case CSSPropertyUserSelect: // auto | none | text | all
+        return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueText || valueID == CSSValueAll;
+    case CSSPropertyWebkitWritingMode:
+        return valueID >= CSSValueHorizontalTb && valueID <= CSSValueVerticalLr;
+    case CSSPropertyWritingMode:
+        return valueID == CSSValueHorizontalTb
+            || valueID == CSSValueVerticalRl || valueID == CSSValueVerticalLr
+            || valueID == CSSValueLrTb || valueID == CSSValueRlTb || valueID == CSSValueTbRl
+            || valueID == CSSValueLr || valueID == CSSValueRl || valueID == CSSValueTb;
+    case CSSPropertyWhiteSpace: // normal | pre | nowrap
+        return valueID == CSSValueNormal || valueID == CSSValuePre || valueID == CSSValuePreWrap || valueID == CSSValuePreLine || valueID == CSSValueNowrap;
+    case CSSPropertyWordBreak: // normal | break-all | keep-all | break-word (this is a custom extension)
+        return valueID == CSSValueNormal || valueID == CSSValueBreakAll || valueID == CSSValueKeepAll || valueID == CSSValueBreakWord;
+    case CSSPropertyScrollSnapType: // none | mandatory | proximity
+        ASSERT(RuntimeEnabledFeatures::cssScrollSnapPointsEnabled());
+        return valueID == CSSValueNone || valueID == CSSValueMandatory || valueID == CSSValueProximity;
+    default:
+        ASSERT_NOT_REACHED();
+        return false;
+    }
+}
+
+bool CSSParserFastPaths::isKeywordPropertyID(CSSPropertyID propertyId)
+{
+    switch (propertyId) {
+    case CSSPropertyAlignmentBaseline:
+    case CSSPropertyAll:
+    case CSSPropertyMixBlendMode:
+    case CSSPropertyIsolation:
+    case CSSPropertyBackgroundRepeatX:
+    case CSSPropertyBackgroundRepeatY:
+    case CSSPropertyBorderBottomStyle:
+    case CSSPropertyBorderCollapse:
+    case CSSPropertyBorderLeftStyle:
+    case CSSPropertyBorderRightStyle:
+    case CSSPropertyBorderTopStyle:
+    case CSSPropertyBoxSizing:
+    case CSSPropertyBufferedRendering:
+    case CSSPropertyCaptionSide:
+    case CSSPropertyClear:
+    case CSSPropertyClipRule:
+    case CSSPropertyColorInterpolation:
+    case CSSPropertyColorInterpolationFilters:
+    case CSSPropertyColorRendering:
+    case CSSPropertyDirection:
+    case CSSPropertyDisplay:
+    case CSSPropertyDominantBaseline:
+    case CSSPropertyEmptyCells:
+    case CSSPropertyFillRule:
+    case CSSPropertyFloat:
+    case CSSPropertyFontStyle:
+    case CSSPropertyFontStretch:
+    case CSSPropertyHyphens:
+    case CSSPropertyImageRendering:
+    case CSSPropertyListStylePosition:
+    case CSSPropertyListStyleType:
+    case CSSPropertyMaskType:
+    case CSSPropertyObjectFit:
+    case CSSPropertyOutlineStyle:
+    case CSSPropertyOverflowAnchor:
+    case CSSPropertyOverflowWrap:
+    case CSSPropertyOverflowX:
+    case CSSPropertyOverflowY:
+    case CSSPropertyBreakAfter:
+    case CSSPropertyBreakBefore:
+    case CSSPropertyBreakInside:
+    case CSSPropertyPointerEvents:
+    case CSSPropertyPosition:
+    case CSSPropertyResize:
+    case CSSPropertyScrollBehavior:
+    case CSSPropertyShapeRendering:
+    case CSSPropertySpeak:
+    case CSSPropertyStrokeLinecap:
+    case CSSPropertyStrokeLinejoin:
+    case CSSPropertyTableLayout:
+    case CSSPropertyTextAlign:
+    case CSSPropertyTextAlignLast:
+    case CSSPropertyTextAnchor:
+    case CSSPropertyTextCombineUpright:
+    case CSSPropertyTextDecorationStyle:
+    case CSSPropertyTextJustify:
+    case CSSPropertyTextOrientation:
+    case CSSPropertyWebkitTextOrientation:
+    case CSSPropertyTextOverflow:
+    case CSSPropertyTextRendering:
+    case CSSPropertyTextTransform:
+    case CSSPropertyUnicodeBidi:
+    case CSSPropertyVectorEffect:
+    case CSSPropertyVisibility:
+    case CSSPropertyWebkitAppRegion:
+    case CSSPropertyWebkitAppearance:
+    case CSSPropertyBackfaceVisibility:
+    case CSSPropertyWebkitBorderAfterStyle:
+    case CSSPropertyWebkitBorderBeforeStyle:
+    case CSSPropertyWebkitBorderEndStyle:
+    case CSSPropertyWebkitBorderStartStyle:
+    case CSSPropertyWebkitBoxAlign:
+    case CSSPropertyWebkitBoxDecorationBreak:
+    case CSSPropertyWebkitBoxDirection:
+    case CSSPropertyWebkitBoxLines:
+    case CSSPropertyWebkitBoxOrient:
+    case CSSPropertyWebkitBoxPack:
+    case CSSPropertyColumnFill:
+    case CSSPropertyColumnRuleStyle:
+    case CSSPropertyFlexDirection:
+    case CSSPropertyFlexWrap:
+    case CSSPropertyFontKerning:
+    case CSSPropertyWebkitFontSmoothing:
+    case CSSPropertyWebkitLineBreak:
+    case CSSPropertyWebkitMarginAfterCollapse:
+    case CSSPropertyWebkitMarginBeforeCollapse:
+    case CSSPropertyWebkitMarginBottomCollapse:
+    case CSSPropertyWebkitMarginTopCollapse:
+    case CSSPropertyWebkitPrintColorAdjust:
+    case CSSPropertyWebkitRtlOrdering:
+    case CSSPropertyWebkitRubyPosition:
+    case CSSPropertyWebkitTextCombine:
+    case CSSPropertyWebkitTextEmphasisPosition:
+    case CSSPropertyWebkitTextSecurity:
+    case CSSPropertyTransformStyle:
+    case CSSPropertyWebkitUserDrag:
+    case CSSPropertyWebkitUserModify:
+    case CSSPropertyUserSelect:
+    case CSSPropertyWebkitWritingMode:
+    case CSSPropertyWhiteSpace:
+    case CSSPropertyWordBreak:
+    case CSSPropertyWordWrap:
+    case CSSPropertyWritingMode:
+    case CSSPropertyScrollSnapType:
+        return true;
+    case CSSPropertyJustifyContent:
+    case CSSPropertyAlignContent:
+    case CSSPropertyAlignItems:
+    case CSSPropertyAlignSelf:
+        return !RuntimeEnabledFeatures::cssGridLayoutEnabled();
+    default:
+        return false;
+    }
+}
+
+static CSSValue* parseKeywordValue(CSSPropertyID propertyId, const String& string, CSSParserMode parserMode)
+{
+    ASSERT(!string.isEmpty());
+
+    if (!CSSParserFastPaths::isKeywordPropertyID(propertyId)) {
+        // All properties accept the values of "initial" and "inherit".
+        if (!equalIgnoringASCIICase(string, "initial") && !equalIgnoringASCIICase(string, "inherit"))
+            return nullptr;
+
+        // Parse initial/inherit shorthands using the CSSPropertyParser.
+        if (shorthandForProperty(propertyId).length())
+            return nullptr;
+
+        // Descriptors do not support css wide keywords.
+        if (CSSPropertyMetadata::isDescriptorOnly(propertyId))
+            return nullptr;
+    }
+
+    CSSValueID valueID = cssValueKeywordID(string);
+
+    if (!valueID)
+        return nullptr;
+
+    if (valueID == CSSValueInherit)
+        return CSSInheritedValue::create();
+    if (valueID == CSSValueInitial)
+        return CSSInitialValue::create();
+    if (CSSParserFastPaths::isValidKeywordPropertyAndValue(propertyId, valueID, parserMode))
+        return CSSPrimitiveValue::createIdentifier(valueID);
+    return nullptr;
+}
+
+template <typename CharType>
+static bool parseTransformTranslateArguments(CharType*& pos, CharType* end, unsigned expectedCount, CSSFunctionValue* transformValue)
+{
+    while (expectedCount) {
+        size_t delimiter = WTF::find(pos, end - pos, expectedCount == 1 ? ')' : ',');
+        if (delimiter == kNotFound)
+            return false;
+        unsigned argumentLength = static_cast<unsigned>(delimiter);
+        CSSPrimitiveValue::UnitTypes unit = CSSPrimitiveValue::UnitTypes::Number;
+        double number;
+        if (!parseSimpleLength(pos, argumentLength, unit, number))
+            return false;
+        if (unit != CSSPrimitiveValue::UnitTypes::Pixels && (number || unit != CSSPrimitiveValue::UnitTypes::Number))
+            return false;
+        transformValue->append(*CSSPrimitiveValue::create(number, CSSPrimitiveValue::UnitTypes::Pixels));
+        pos += argumentLength + 1;
+        --expectedCount;
+    }
+    return true;
+}
+
+template <typename CharType>
+static bool parseTransformNumberArguments(CharType*& pos, CharType* end, unsigned expectedCount, CSSFunctionValue* transformValue)
+{
+    while (expectedCount) {
+        size_t delimiter = WTF::find(pos, end - pos, expectedCount == 1 ? ')' : ',');
+        if (delimiter == kNotFound)
+            return false;
+        unsigned argumentLength = static_cast<unsigned>(delimiter);
+        bool ok;
+        double number = charactersToDouble(pos, argumentLength, &ok);
+        if (!ok)
+            return false;
+        transformValue->append(*CSSPrimitiveValue::create(number, CSSPrimitiveValue::UnitTypes::Number));
+        pos += argumentLength + 1;
+        --expectedCount;
+    }
+    return true;
+}
+
+static const int kShortestValidTransformStringLength = 12;
+
+template <typename CharType>
+static CSSFunctionValue* parseSimpleTransformValue(CharType*& pos, CharType* end)
+{
+    if (end - pos < kShortestValidTransformStringLength)
+        return nullptr;
+
+    const bool isTranslate = toASCIILower(pos[0]) == 't'
+        && toASCIILower(pos[1]) == 'r'
+        && toASCIILower(pos[2]) == 'a'
+        && toASCIILower(pos[3]) == 'n'
+        && toASCIILower(pos[4]) == 's'
+        && toASCIILower(pos[5]) == 'l'
+        && toASCIILower(pos[6]) == 'a'
+        && toASCIILower(pos[7]) == 't'
+        && toASCIILower(pos[8]) == 'e';
+
+    if (isTranslate) {
+        CSSValueID transformType;
+        unsigned expectedArgumentCount = 1;
+        unsigned argumentStart = 11;
+        CharType c9 = toASCIILower(pos[9]);
+        if (c9 == 'x' && pos[10] == '(') {
+            transformType = CSSValueTranslateX;
+        } else if (c9 == 'y' && pos[10] == '(') {
+            transformType = CSSValueTranslateY;
+        } else if (c9 == 'z' && pos[10] == '(') {
+            transformType = CSSValueTranslateZ;
+        } else if (c9 == '(') {
+            transformType = CSSValueTranslate;
+            expectedArgumentCount = 2;
+            argumentStart = 10;
+        } else if (c9 == '3' && toASCIILower(pos[10]) == 'd' && pos[11] == '(') {
+            transformType = CSSValueTranslate3d;
+            expectedArgumentCount = 3;
+            argumentStart = 12;
+        } else {
+            return nullptr;
+        }
+        pos += argumentStart;
+        CSSFunctionValue* transformValue = CSSFunctionValue::create(transformType);
+        if (!parseTransformTranslateArguments(pos, end, expectedArgumentCount, transformValue))
+            return nullptr;
+        return transformValue;
+    }
+
+    const bool isMatrix3d = toASCIILower(pos[0]) == 'm'
+        && toASCIILower(pos[1]) == 'a'
+        && toASCIILower(pos[2]) == 't'
+        && toASCIILower(pos[3]) == 'r'
+        && toASCIILower(pos[4]) == 'i'
+        && toASCIILower(pos[5]) == 'x'
+        && pos[6] == '3'
+        && toASCIILower(pos[7]) == 'd'
+        && pos[8] == '(';
+
+    if (isMatrix3d) {
+        pos += 9;
+        CSSFunctionValue* transformValue = CSSFunctionValue::create(CSSValueMatrix3d);
+        if (!parseTransformNumberArguments(pos, end, 16, transformValue))
+            return nullptr;
+        return transformValue;
+    }
+
+    const bool isScale3d = toASCIILower(pos[0]) == 's'
+        && toASCIILower(pos[1]) == 'c'
+        && toASCIILower(pos[2]) == 'a'
+        && toASCIILower(pos[3]) == 'l'
+        && toASCIILower(pos[4]) == 'e'
+        && pos[5] == '3'
+        && toASCIILower(pos[6]) == 'd'
+        && pos[7] == '(';
+
+    if (isScale3d) {
+        pos += 8;
+        CSSFunctionValue* transformValue = CSSFunctionValue::create(CSSValueScale3d);
+        if (!parseTransformNumberArguments(pos, end, 3, transformValue))
+            return nullptr;
+        return transformValue;
+    }
+
+    return nullptr;
+}
+
+template <typename CharType>
+static bool transformCanLikelyUseFastPath(const CharType* chars, unsigned length)
+{
+    // Very fast scan that attempts to reject most transforms that couldn't
+    // take the fast path. This avoids doing the malloc and string->double
+    // conversions in parseSimpleTransformValue only to discard them when we
+    // run into a transform component we don't understand.
+    unsigned i = 0;
+    while (i < length) {
+        if (isCSSSpace(chars[i])) {
+            ++i;
+            continue;
+        }
+        if (length - i < kShortestValidTransformStringLength)
+            return false;
+        switch (toASCIILower(chars[i])) {
+        case 't':
+            // translate, translateX, translateY, translateZ, translate3d.
+            if (toASCIILower(chars[i + 8]) != 'e')
+                return false;
+            i += 9;
+            break;
+        case 'm':
+            // matrix3d.
+            if (toASCIILower(chars[i + 7]) != 'd')
+                return false;
+            i += 8;
+            break;
+        case 's':
+            // scale3d.
+            if (toASCIILower(chars[i + 6]) != 'd')
+                return false;
+            i += 7;
+            break;
+        default:
+            // All other things, ex. rotate.
+            return false;
+        }
+        size_t argumentsEnd = WTF::find(chars, length, ')', i);
+        if (argumentsEnd == kNotFound)
+            return false;
+        // Advance to the end of the arguments.
+        i = argumentsEnd + 1;
+    }
+    return i == length;
+}
+
+template <typename CharType>
+static CSSValueList* parseSimpleTransformList(const CharType* chars, unsigned length)
+{
+    if (!transformCanLikelyUseFastPath(chars, length))
+        return nullptr;
+    const CharType*& pos = chars;
+    const CharType* end = chars + length;
+    CSSValueList* transformList = nullptr;
+    while (pos < end) {
+        while (pos < end && isCSSSpace(*pos))
+            ++pos;
+        if (pos >= end)
+            break;
+        CSSFunctionValue* transformValue = parseSimpleTransformValue(pos, end);
+        if (!transformValue)
+            return nullptr;
+        if (!transformList)
+            transformList = CSSValueList::createSpaceSeparated();
+        transformList->append(*transformValue);
+    }
+    return transformList;
+}
+
+static CSSValue* parseSimpleTransform(CSSPropertyID propertyID, const String& string)
+{
+    ASSERT(!string.isEmpty());
+
+    if (propertyID != CSSPropertyTransform)
+        return nullptr;
+    if (string.is8Bit())
+        return parseSimpleTransformList(string.characters8(), string.length());
+    return parseSimpleTransformList(string.characters16(), string.length());
+}
+
+CSSValue* CSSParserFastPaths::maybeParseValue(CSSPropertyID propertyID, const String& string, CSSParserMode parserMode)
+{
+    if (CSSValue* length = parseSimpleLengthValue(propertyID, string, parserMode))
+        return length;
+    if (isColorPropertyID(propertyID))
+        return parseColor(string, parserMode);
+    if (CSSValue* keyword = parseKeywordValue(propertyID, string, parserMode))
+        return keyword;
+    if (CSSValue* transform = parseSimpleTransform(propertyID, string))
+        return transform;
+    return nullptr;
+}
+
+     */
+} // namespace WebCore
diff --git a/Source/WebCore/css/parser/CSSParserFastPaths.h b/Source/WebCore/css/parser/CSSParserFastPaths.h
new file mode 100644 (file)
index 0000000..9c18cb8
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CSSParserFastPaths_h
+#define CSSParserFastPaths_h
+
+#include "CSSParserMode.h"
+#include "CSSPropertyNames.h"
+#include "CSSValueKeywords.h"
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CSSValue;
+
+class CSSParserFastPaths {
+public:
+    // Parses simple values like '10px' or 'green', but makes no guarantees
+    // about handling any property completely.
+    static CSSValue* maybeParseValue(CSSPropertyID, const String&, CSSParserMode);
+
+    // Properties handled here shouldn't be explicitly handled in CSSPropertyParser
+    static bool isKeywordPropertyID(CSSPropertyID);
+    static bool isValidKeywordPropertyAndValue(CSSPropertyID, CSSValueID, CSSParserMode);
+
+    static CSSValue* parseColor(const String&, CSSParserMode);
+};
+
+} // namespace WebCore
+
+#endif // CSSParserFastPaths_h
similarity index 66%
rename from Source/WebCore/css/CSSOMUtils.h
rename to Source/WebCore/css/parser/CSSParserIdioms.h
index 4bd07cc..5402cab 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#pragma once
+#ifndef CSSParserIdioms_h
+#define CSSParserIdioms_h
 
-#include <unicode/umachine.h>
-#include <wtf/Forward.h>
-
-// Utilities for CSSOM http://dev.w3.org/csswg/cssom/
+#include <wtf/ASCIICType.h>
 
 namespace WebCore {
 
-// Common serializing methods. See: http://dev.w3.org/csswg/cssom/#common-serializing-idioms
-void serializeCharacter(UChar32, StringBuilder&);
-void serializeCharacterAsCodePoint(UChar32, StringBuilder&);
-void serializeIdentifier(const String& identifier, StringBuilder&);
-void serializeString(const String&, StringBuilder&);
+// Space characters as defined by the CSS specification.
+// http://www.w3.org/TR/css3-syntax/#whitespace
+inline bool isCSSSpace(UChar c)
+{
+    return c == ' ' || c == '\t' || c == '\n';
+}
+
+// http://dev.w3.org/csswg/css-syntax/#name-start-code-point
+template <typename CharacterType>
+bool isNameStartCodePoint(CharacterType c)
+{
+    return isASCIIAlpha(c) || c == '_' || !isASCII(c);
+}
+
+// http://dev.w3.org/csswg/css-syntax/#name-code-point
+template <typename CharacterType>
+bool isNameCodePoint(CharacterType c)
+{
+    return isNameStartCodePoint(c) || isASCIIDigit(c) || c == '-';
+}
+
+}
 
-} // namespace WebCore
+#endif
diff --git a/Source/WebCore/css/parser/CSSParserObserver.h b/Source/WebCore/css/parser/CSSParserObserver.h
new file mode 100644 (file)
index 0000000..1f139eb
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2003 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 - 2010  Torch Mobile (Beijing) Co. Ltd. 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef CSSParserObserver_h
+#define CSSParserObserver_h
+
+#include "CSSPropertySourceData.h"
+#include "StyleRule.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CSSParserToken;
+class CSSParserTokenRange;
+
+// This is only for the inspector and shouldn't be used elsewhere.
+class CSSParserObserver {
+public:
+    virtual ~CSSParserObserver() { };
+    virtual void startRuleHeader(StyleRule::Type, unsigned offset) = 0;
+    virtual void endRuleHeader(unsigned offset) = 0;
+    virtual void observeSelector(unsigned startOffset, unsigned endOffset) = 0;
+    virtual void startRuleBody(unsigned offset) = 0;
+    virtual void endRuleBody(unsigned offset) = 0;
+    virtual void observeProperty(unsigned startOffset, unsigned endOffset, bool isImportant, bool isParsed) = 0;
+    virtual void observeComment(unsigned startOffset, unsigned endOffset) = 0;
+    // FIXME: Unused, should be removed
+    virtual void startMediaQueryExp(unsigned offset) = 0;
+    virtual void endMediaQueryExp(unsigned offset) = 0;
+    virtual void startMediaQuery() = 0;
+    virtual void endMediaQuery() = 0;
+};
+
+} // namespace WebCore
+
+#endif // CSSParserObserver_h
diff --git a/Source/WebCore/css/parser/CSSParserObserverWrapper.cpp b/Source/WebCore/css/parser/CSSParserObserverWrapper.cpp
new file mode 100644 (file)
index 0000000..65b2248
--- /dev/null
@@ -0,0 +1,72 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "CSSParserObserverWrapper.h"
+
+#include "CSSParserTokenRange.h"
+
+namespace WebCore {
+
+unsigned CSSParserObserverWrapper::startOffset(const CSSParserTokenRange& range)
+{
+    return m_tokenOffsets[range.begin() - m_firstParserToken];
+}
+
+unsigned CSSParserObserverWrapper::previousTokenStartOffset(const CSSParserTokenRange& range)
+{
+    if (range.begin() == m_firstParserToken)
+        return 0;
+    return m_tokenOffsets[range.begin() - m_firstParserToken - 1];
+}
+
+unsigned CSSParserObserverWrapper::endOffset(const CSSParserTokenRange& range)
+{
+    return m_tokenOffsets[range.end() - m_firstParserToken];
+}
+
+void CSSParserObserverWrapper::skipCommentsBefore(const CSSParserTokenRange& range, bool leaveDirectlyBefore)
+{
+    unsigned startIndex = range.begin() - m_firstParserToken;
+    if (!leaveDirectlyBefore)
+        startIndex++;
+    while (m_commentIterator < m_commentOffsets.end() && m_commentIterator->tokensBefore < startIndex)
+        m_commentIterator++;
+}
+
+void CSSParserObserverWrapper::yieldCommentsBefore(const CSSParserTokenRange& range)
+{
+    unsigned startIndex = range.begin() - m_firstParserToken;
+    while (m_commentIterator < m_commentOffsets.end() && m_commentIterator->tokensBefore <= startIndex) {
+        m_observer.observeComment(m_commentIterator->startOffset, m_commentIterator->endOffset);
+        m_commentIterator++;
+    }
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/css/parser/CSSParserObserverWrapper.h b/Source/WebCore/css/parser/CSSParserObserverWrapper.h
new file mode 100644 (file)
index 0000000..7489d48
--- /dev/null
@@ -0,0 +1,80 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CSSParserObserverWrapper_h
+#define CSSParserObserverWrapper_h
+
+#include "CSSParserObserver.h"
+
+namespace WebCore {
+
+class CSSParserObserverWrapper {
+public:
+    explicit CSSParserObserverWrapper(CSSParserObserver& observer)
+        : m_observer(observer)
+    { }
+
+    unsigned startOffset(const CSSParserTokenRange&);
+    unsigned previousTokenStartOffset(const CSSParserTokenRange&);
+    unsigned endOffset(const CSSParserTokenRange&); // Includes trailing comments
+
+    void skipCommentsBefore(const CSSParserTokenRange&, bool leaveDirectlyBefore);
+    void yieldCommentsBefore(const CSSParserTokenRange&);
+
+    CSSParserObserver& observer() { return m_observer; }
+    void addComment(unsigned startOffset, unsigned endOffset, unsigned tokensBefore)
+    {
+        CommentPosition position = {startOffset, endOffset, tokensBefore};
+        m_commentOffsets.append(position);
+    }
+    void addToken(unsigned startOffset) { m_tokenOffsets.append(startOffset); }
+    void finalizeConstruction(CSSParserToken* firstParserToken)
+    {
+        m_firstParserToken = firstParserToken;
+        m_commentIterator = m_commentOffsets.begin();
+    }
+
+private:
+    CSSParserObserver& m_observer;
+    Vector<unsigned> m_tokenOffsets;
+    CSSParserToken* m_firstParserToken;
+
+    struct CommentPosition {
+        unsigned startOffset;
+        unsigned endOffset;
+        unsigned tokensBefore;
+    };
+
+    Vector<CommentPosition> m_commentOffsets;
+    Vector<CommentPosition>::iterator m_commentIterator;
+};
+
+} // namespace WebCore
+
+#endif // CSSParserObserverWrapper_h
diff --git a/Source/WebCore/css/parser/CSSParserToken.cpp b/Source/WebCore/css/parser/CSSParserToken.cpp
new file mode 100644 (file)
index 0000000..07ea3ef
--- /dev/null
@@ -0,0 +1,477 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "CSSParserToken.h"
+
+#include "CSSMarkup.h"
+#include "CSSPrimitiveValue.h"
+#include "CSSPropertyParser.h"
+#include <limits.h>
+#include <wtf/HashMap.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+template<typename CharacterType>
+CSSPrimitiveValue::UnitTypes cssPrimitiveValueUnitFromTrie(const CharacterType* data, unsigned length)
+{
+    ASSERT(data);
+    ASSERT(length);
+    switch (length) {
+    case 1:
+        switch (toASCIILower(data[0])) {
+        case 's':
+            return CSSPrimitiveValue::UnitTypes::CSS_S;
+        }
+        break;
+    case 2:
+        switch (toASCIILower(data[0])) {
+        case 'c':
+            switch (toASCIILower(data[1])) {
+            case 'h':
+                return CSSPrimitiveValue::UnitTypes::CSS_CHS;
+            case 'm':
+                return CSSPrimitiveValue::UnitTypes::CSS_CM;
+            }
+            break;
+        case 'e':
+            switch (toASCIILower(data[1])) {
+            case 'm':
+                return CSSPrimitiveValue::UnitTypes::CSS_EMS;
+            case 'x':
+                return CSSPrimitiveValue::UnitTypes::CSS_EXS;
+            }
+            break;
+        case 'f':
+            if (toASCIILower(data[1]) == 'r')
+                return CSSPrimitiveValue::UnitTypes::CSS_FR;
+            break;
+        case 'h':
+            if (toASCIILower(data[1]) == 'z')
+                return CSSPrimitiveValue::UnitTypes::CSS_HZ;
+            break;
+        case 'i':
+            if (toASCIILower(data[1]) == 'n')
+                return CSSPrimitiveValue::UnitTypes::CSS_IN;
+            break;
+        case 'm':
+            switch (toASCIILower(data[1])) {
+            case 'm':
+                return CSSPrimitiveValue::UnitTypes::CSS_MM;
+            case 's':
+                return CSSPrimitiveValue::UnitTypes::CSS_MS;
+            }
+            break;
+        case 'p':
+            switch (toASCIILower(data[1])) {
+            case 'c':
+                return CSSPrimitiveValue::UnitTypes::CSS_PC;
+            case 't':
+                return CSSPrimitiveValue::UnitTypes::CSS_PT;
+            case 'x':
+                return CSSPrimitiveValue::UnitTypes::CSS_PX;
+            }
+            break;
+        case 'v':
+            switch (toASCIILower(data[1])) {
+            case 'h':
+                return CSSPrimitiveValue::UnitTypes::CSS_VH;
+            case 'w':
+                return CSSPrimitiveValue::UnitTypes::CSS_VW;
+            }
+            break;
+        }
+        break;
+    case 3:
+        switch (toASCIILower(data[0])) {
+        case 'd':
+            switch (toASCIILower(data[1])) {
+            case 'e':
+                if (toASCIILower(data[2]) == 'g')
+                    return CSSPrimitiveValue::UnitTypes::CSS_DEG;
+                break;
+            case 'p':
+                if (toASCIILower(data[2]) == 'i')
+                    return CSSPrimitiveValue::UnitTypes::CSS_DPI;
+                break;
+            }
+        break;
+        case 'k':
+            if (toASCIILower(data[1]) == 'h' && toASCIILower(data[2]) == 'z')
+                return CSSPrimitiveValue::UnitTypes::CSS_KHZ;
+            break;
+        case 'r':
+            switch (toASCIILower(data[1])) {
+            case 'a':
+                if (toASCIILower(data[2]) == 'd')
+                    return CSSPrimitiveValue::UnitTypes::CSS_RAD;
+                break;
+            case 'e':
+                if (toASCIILower(data[2]) == 'm')
+                    return CSSPrimitiveValue::UnitTypes::CSS_REMS;
+                break;
+            }
+        break;
+    }
+    break;
+    case 4:
+        switch (toASCIILower(data[0])) {
+        case 'd':
+            switch (toASCIILower(data[1])) {
+            case 'p':
+                switch (toASCIILower(data[2])) {
+                case 'c':
+                    if (toASCIILower(data[3]) == 'm')
+                        return CSSPrimitiveValue::UnitTypes::CSS_DPCM;
+                    break;
+                case 'p':
+                    if (toASCIILower(data[3]) == 'x')
+                        return CSSPrimitiveValue::UnitTypes::CSS_DPPX;
+                    break;
+                }
+            break;
+        }
+        break;
+        case 'g':
+            if (toASCIILower(data[1]) == 'r' && toASCIILower(data[2]) == 'a' && toASCIILower(data[3]) == 'd')
+                return CSSPrimitiveValue::UnitTypes::CSS_GRAD;
+            break;
+        case 't':
+            if (toASCIILower(data[1]) == 'u' && toASCIILower(data[2]) == 'r' && toASCIILower(data[3]) == 'n')
+                return CSSPrimitiveValue::UnitTypes::CSS_TURN;
+            break;
+        case 'v':
+            switch (toASCIILower(data[1])) {
+            case 'm':
+                switch (toASCIILower(data[2])) {
+                case 'a':
+                    if (toASCIILower(data[3]) == 'x')
+                        return CSSPrimitiveValue::UnitTypes::CSS_VMAX;
+                    break;
+                case 'i':
+                    if (toASCIILower(data[3]) == 'n')
+                        return CSSPrimitiveValue::UnitTypes::CSS_VMIN;
+                    break;
+                }
+                break;
+            }
+            break;
+        }
+        break;
+    case 5:
+        switch (toASCIILower(data[0])) {
+        case '_':
+            if (toASCIILower(data[1]) == '_' && toASCIILower(data[2]) == 'q' && toASCIILower(data[3]) == 'e' && toASCIILower(data[4]) == 'm')
+                return CSSPrimitiveValue::UnitTypes::CSS_EMS; // FIXME-NEWPARSER: Need quirky ems.
+            break;
+        }
+        break;
+    }
+    return CSSPrimitiveValue::UnitTypes::CSS_UNKNOWN;
+}
+
+static CSSPrimitiveValue::UnitTypes stringToUnitType(StringView stringView)
+{
+    if (stringView.is8Bit())
+        return cssPrimitiveValueUnitFromTrie(stringView.characters8(), stringView.length());
+    return cssPrimitiveValueUnitFromTrie(stringView.characters16(), stringView.length());
+}
+
+CSSParserToken::CSSParserToken(CSSParserTokenType type, BlockType blockType)
+    : m_type(type)
+    , m_blockType(blockType)
+{
+}
+
+// Just a helper used for Delimiter tokens.
+CSSParserToken::CSSParserToken(CSSParserTokenType type, UChar c)
+    : m_type(type)
+    , m_blockType(NotBlock)
+    , m_delimiter(c)
+{
+    ASSERT(m_type == DelimiterToken);
+}
+
+CSSParserToken::CSSParserToken(CSSParserTokenType type, StringView value, BlockType blockType)
+    : m_type(type)
+    , m_blockType(blockType)
+{
+    initValueFromStringView(value);
+    m_id = -1;
+}
+
+CSSParserToken::CSSParserToken(CSSParserTokenType type, double numericValue, NumericValueType numericValueType, NumericSign sign)
+    : m_type(type)
+    , m_blockType(NotBlock)
+    , m_numericValueType(numericValueType)
+    , m_numericSign(sign)
+    , m_unit(static_cast<unsigned>(CSSPrimitiveValue::UnitTypes::CSS_NUMBER))
+{
+    ASSERT(type == NumberToken);
+    m_numericValue = clampTo<double>(numericValue, -std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
+}
+
+CSSParserToken::CSSParserToken(CSSParserTokenType type, UChar32 start, UChar32 end)
+    : m_type(UnicodeRangeToken)
+    , m_blockType(NotBlock)
+{
+    ASSERT_UNUSED(type, type == UnicodeRangeToken);
+    m_unicodeRange.start = start;
+    m_unicodeRange.end = end;
+}
+
+CSSParserToken::CSSParserToken(HashTokenType type, StringView value)
+    : m_type(HashToken)
+    , m_blockType(NotBlock)
+    , m_hashTokenType(type)
+{
+    initValueFromStringView(value);
+}
+
+void CSSParserToken::convertToDimensionWithUnit(StringView unit)
+{
+    ASSERT(m_type == NumberToken);
+    m_type = DimensionToken;
+    initValueFromStringView(unit);
+    m_unit = static_cast<unsigned>(stringToUnitType(unit));
+}
+
+void CSSParserToken::convertToPercentage()
+{
+    ASSERT(m_type == NumberToken);
+    m_type = PercentageToken;
+    m_unit = static_cast<unsigned>(CSSPrimitiveValue::UnitTypes::CSS_PERCENTAGE);
+}
+
+UChar CSSParserToken::delimiter() const
+{
+    ASSERT(m_type == DelimiterToken);
+    return m_delimiter;
+}
+
+NumericSign CSSParserToken::numericSign() const
+{
+    // This is valid for DimensionToken and PercentageToken, but only used
+    // in <an+b> parsing on NumberTokens.
+    ASSERT(m_type == NumberToken);
+    return static_cast<NumericSign>(m_numericSign);
+}
+
+NumericValueType CSSParserToken::numericValueType() const
+{
+    ASSERT(m_type == NumberToken || m_type == PercentageToken || m_type == DimensionToken);
+    return static_cast<NumericValueType>(m_numericValueType);
+}
+
+double CSSParserToken::numericValue() const
+{
+    ASSERT(m_type == NumberToken || m_type == PercentageToken || m_type == DimensionToken);
+    return m_numericValue;
+}
+
+CSSPropertyID CSSParserToken::parseAsUnresolvedCSSPropertyID() const
+{
+    ASSERT(m_type == IdentToken);
+    return unresolvedCSSPropertyID(value());
+}
+
+CSSValueID CSSParserToken::id() const
+{
+    if (m_type != IdentToken)
+        return CSSValueInvalid;
+    if (m_id < 0)
+        m_id = cssValueKeywordID(value());
+    return static_cast<CSSValueID>(m_id);
+}
+
+CSSValueID CSSParserToken::functionId() const
+{
+    if (m_type != FunctionToken)
+        return CSSValueInvalid;
+    if (m_id < 0)
+        m_id = cssValueKeywordID(value());
+    return static_cast<CSSValueID>(m_id);
+}
+
+bool CSSParserToken::hasStringBacking() const
+{
+    CSSParserTokenType tokenType = type();
+    return tokenType == IdentToken
+        || tokenType == FunctionToken
+        || tokenType == AtKeywordToken
+        || tokenType == HashToken
+        || tokenType == UrlToken
+        || tokenType == DimensionToken
+        || tokenType == StringToken;
+}
+
+CSSParserToken CSSParserToken::copyWithUpdatedString(const StringView& string) const
+{
+    CSSParserToken copy(*this);
+    copy.initValueFromStringView(string);
+    return copy;
+}
+
+bool CSSParserToken::valueDataCharRawEqual(const CSSParserToken& other) const
+{
+    if (m_valueLength != other.m_valueLength)
+        return false;
+
+    if (m_valueDataCharRaw == other.m_valueDataCharRaw && m_valueIs8Bit == other.m_valueIs8Bit)
+        return true;
+
+    if (m_valueIs8Bit)
+        return other.m_valueIs8Bit ? equal(static_cast<const LChar*>(m_valueDataCharRaw), static_cast<const LChar*>(other.m_valueDataCharRaw), m_valueLength) : equal(static_cast<const LChar*>(m_valueDataCharRaw), static_cast<const UChar*>(other.m_valueDataCharRaw), m_valueLength);
+    
+    return other.m_valueIs8Bit ? equal(static_cast<const UChar*>(m_valueDataCharRaw), static_cast<const LChar*>(other.m_valueDataCharRaw), m_valueLength) : equal(static_cast<const UChar*>(m_valueDataCharRaw), static_cast<const UChar*>(other.m_valueDataCharRaw), m_valueLength);
+}
+
+bool CSSParserToken::operator==(const CSSParserToken& other) const
+{
+    if (m_type != other.m_type)
+        return false;
+    switch (m_type) {
+    case DelimiterToken:
+        return delimiter() == other.delimiter();
+    case HashToken:
+        if (m_hashTokenType != other.m_hashTokenType)
+            return false;
+        FALLTHROUGH;
+    case IdentToken:
+    case FunctionToken:
+    case StringToken:
+    case UrlToken:
+        return valueDataCharRawEqual(other);
+    case DimensionToken:
+        if (!valueDataCharRawEqual(other))
+            return false;
+        FALLTHROUGH;
+    case NumberToken:
+    case PercentageToken:
+        return m_numericSign == other.m_numericSign && m_numericValue == other.m_numericValue && m_numericValueType == other.m_numericValueType;
+    case UnicodeRangeToken:
+        return m_unicodeRange.start == other.m_unicodeRange.start && m_unicodeRange.end == other.m_unicodeRange.end;
+    default:
+        return true;
+    }
+}
+
+void CSSParserToken::serialize(StringBuilder& builder) const
+{
+    // This is currently only used for @supports CSSOM. To keep our implementation
+    // simple we handle some of the edge cases incorrectly (see comments below).
+    switch (type()) {
+    case IdentToken:
+        serializeIdentifier(value().toString(), builder);
+        break;
+    case FunctionToken:
+        serializeIdentifier(value().toString(), builder);
+        return builder.append('(');
+    case AtKeywordToken:
+        builder.append('@');
+        serializeIdentifier(value().toString(), builder);
+        break;
+    case HashToken:
+        builder.append('#');
+        serializeIdentifier(value().toString(), builder, (getHashTokenType() == HashTokenUnrestricted));
+        break;
+    case UrlToken:
+        builder.append("url(");
+        serializeIdentifier(value().toString(), builder);
+        return builder.append(')');
+    case DelimiterToken:
+        if (delimiter() == '\\')
+            return builder.append("\\\n");
+        return builder.append(delimiter());
+    case NumberToken:
+        // These won't properly preserve the NumericValueType flag
+        return builder.appendNumber(numericValue());
+    case PercentageToken:
+        builder.appendNumber(numericValue());
+        return builder.append('%');
+    case DimensionToken:
+        // This will incorrectly serialize e.g. 4e3e2 as 4000e2
+        builder.appendNumber(numericValue());
+        serializeIdentifier(value().toString(), builder);
+        break;
+    case UnicodeRangeToken:
+        return builder.append(String::format("U+%X-%X", unicodeRangeStart(), unicodeRangeEnd()));
+    case StringToken:
+        return serializeString(value().toString(), builder);
+
+    case IncludeMatchToken:
+        return builder.append("~=");
+    case DashMatchToken:
+        return builder.append("|=");
+    case PrefixMatchToken:
+        return builder.append("^=");
+    case SuffixMatchToken:
+        return builder.append("$=");
+    case SubstringMatchToken:
+        return builder.append("*=");
+    case ColumnToken:
+        return builder.append("||");
+    case CDOToken:
+        return builder.append("<!--");
+    case CDCToken:
+        return builder.append("-->");
+    case BadStringToken:
+        return builder.append("'\n");
+    case BadUrlToken:
+        return builder.append("url(()");
+    case WhitespaceToken:
+        return builder.append(' ');
+    case ColonToken:
+        return builder.append(':');
+    case SemicolonToken:
+        return builder.append(';');
+    case CommaToken:
+        return builder.append(',');
+    case LeftParenthesisToken:
+        return builder.append('(');
+    case RightParenthesisToken:
+        return builder.append(')');
+    case LeftBracketToken:
+        return builder.append('[');
+    case RightBracketToken:
+        return builder.append(']');
+    case LeftBraceToken:
+        return builder.append('{');
+    case RightBraceToken:
+        return builder.append('}');
+
+    case EOFToken:
+    case CommentToken:
+        ASSERT_NOT_REACHED();
+        return;
+    }
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/css/parser/CSSParserToken.h b/Source/WebCore/css/parser/CSSParserToken.h
new file mode 100644 (file)
index 0000000..c073e8c
--- /dev/null
@@ -0,0 +1,181 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CSSParserToken_h
+#define CSSParserToken_h
+
+#include "CSSPrimitiveValue.h"
+#include <wtf/text/StringView.h>
+
+namespace WebCore {
+
+enum CSSParserTokenType {
+    IdentToken = 0,
+    FunctionToken,
+    AtKeywordToken,
+    HashToken,
+    UrlToken,
+    BadUrlToken,
+    DelimiterToken,
+    NumberToken,
+    PercentageToken,
+    DimensionToken,
+    IncludeMatchToken,
+    DashMatchToken,
+    PrefixMatchToken,
+    SuffixMatchToken,
+    SubstringMatchToken,
+    ColumnToken,
+    UnicodeRangeToken,
+    WhitespaceToken,
+    CDOToken,
+    CDCToken,
+    ColonToken,
+    SemicolonToken,
+    CommaToken,
+    LeftParenthesisToken,
+    RightParenthesisToken,
+    LeftBracketToken,
+    RightBracketToken,
+    LeftBraceToken,
+    RightBraceToken,
+    StringToken,
+    BadStringToken,
+    EOFToken,
+    CommentToken,
+};
+
+enum NumericSign {
+    NoSign,
+    PlusSign,
+    MinusSign,
+};
+
+enum NumericValueType {
+    IntegerValueType,
+    NumberValueType,
+};
+
+enum HashTokenType {
+    HashTokenId,
+    HashTokenUnrestricted,
+};
+
+class CSSParserToken {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    enum BlockType {
+        NotBlock,
+        BlockStart,
+        BlockEnd,
+    };
+
+    CSSParserToken(CSSParserTokenType, BlockType = NotBlock);
+    CSSParserToken(CSSParserTokenType, StringView, BlockType = NotBlock);
+
+    CSSParserToken(CSSParserTokenType, UChar); // for DelimiterToken
+    CSSParserToken(CSSParserTokenType, double, NumericValueType, NumericSign); // for NumberToken
+    CSSParserToken(CSSParserTokenType, UChar32, UChar32); // for UnicodeRangeToken
+
+    CSSParserToken(HashTokenType, StringView);
+
+    bool operator==(const CSSParserToken& other) const;
+    bool operator!=(const CSSParserToken& other) const { return !(*this == other); }
+
+    // Converts NumberToken to DimensionToken.
+    void convertToDimensionWithUnit(StringView);
+
+    // Converts NumberToken to PercentageToken.
+    void convertToPercentage();
+
+    CSSParserTokenType type() const { return static_cast<CSSParserTokenType>(m_type); }
+    StringView value() const
+    {
+        if (m_valueIs8Bit)
+            return StringView(reinterpret_cast<const LChar*>(m_valueDataCharRaw), m_valueLength);
+        return StringView(reinterpret_cast<const UChar*>(m_valueDataCharRaw), m_valueLength);
+    }
+
+    UChar delimiter() const;
+    NumericSign numericSign() const;
+    NumericValueType numericValueType() const;
+    double numericValue() const;
+    HashTokenType getHashTokenType() const { ASSERT(m_type == HashToken); return m_hashTokenType; }
+    BlockType getBlockType() const { return static_cast<BlockType>(m_blockType); }
+    CSSPrimitiveValue::UnitTypes unitType() const { return static_cast<CSSPrimitiveValue::UnitTypes>(m_unit); }
+    UChar32 unicodeRangeStart() const { ASSERT(m_type == UnicodeRangeToken); return m_unicodeRange.start; }
+    UChar32 unicodeRangeEnd() const { ASSERT(m_type == UnicodeRangeToken); return m_unicodeRange.end; }
+    CSSValueID id() const;
+    CSSValueID functionId() const;
+
+    bool hasStringBacking() const;
+
+    CSSPropertyID parseAsUnresolvedCSSPropertyID() const;
+
+    void serialize(StringBuilder&) const;
+
+    CSSParserToken copyWithUpdatedString(const StringView&) const;
+
+private:
+    void initValueFromStringView(StringView string)
+    {
+        m_valueLength = string.length();
+        m_valueIs8Bit = string.is8Bit();
+        m_valueDataCharRaw = m_valueIs8Bit ? static_cast<const void*>(string.characters8()) : static_cast<const void*>(string.characters16());
+    }
+    unsigned m_type : 6; // CSSParserTokenType
+    unsigned m_blockType : 2; // BlockType
+    unsigned m_numericValueType : 1; // NumericValueType
+    unsigned m_numericSign : 2; // NumericSign
+    unsigned m_unit : 7; // CSSPrimitiveValue::UnitTypes
+
+    bool valueDataCharRawEqual(const CSSParserToken& other) const;
+
+    // m_value... is an unpacked StringView so that we can pack it
+    // tightly with the rest of this object for a smaller object size.
+    bool m_valueIs8Bit : 1;
+    unsigned m_valueLength;
+    const void* m_valueDataCharRaw; // Either LChar* or UChar*.
+
+    union {
+        UChar m_delimiter;
+        HashTokenType m_hashTokenType;
+        double m_numericValue;
+        mutable int m_id;
+
+        struct {
+            UChar32 start;
+            UChar32 end;
+        } m_unicodeRange;
+    };
+};
+
+} // namespace WebCore
+
+#endif // CSSSParserToken_h
diff --git a/Source/WebCore/css/parser/CSSParserTokenRange.cpp b/Source/WebCore/css/parser/CSSParserTokenRange.cpp
new file mode 100644 (file)
index 0000000..58e5c48
--- /dev/null
@@ -0,0 +1,97 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "CSSParserTokenRange.h"
+
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+CSSParserToken& CSSParserTokenRange::eofToken()
+{
+    static NeverDestroyed<CSSParserToken> eofToken(EOFToken);
+    return eofToken.get();
+}
+
+CSSParserTokenRange CSSParserTokenRange::makeSubRange(const CSSParserToken* first, const CSSParserToken* last) const
+{
+    if (first == &eofToken())
+        first = m_last;
+    if (last == &eofToken())
+        last = m_last;
+    ASSERT(first <= last);
+    return CSSParserTokenRange(first, last);
+}
+
+CSSParserTokenRange CSSParserTokenRange::consumeBlock()
+{
+    ASSERT(peek().getBlockType() == CSSParserToken::BlockStart);
+    const CSSParserToken* start = &peek() + 1;
+    unsigned nestingLevel = 0;
+    do {
+        const CSSParserToken& token = consume();
+        if (token.getBlockType() == CSSParserToken::BlockStart)
+            nestingLevel++;
+        else if (token.getBlockType() == CSSParserToken::BlockEnd)
+            nestingLevel--;
+    } while (nestingLevel && m_first < m_last);
+
+    if (nestingLevel)
+        return makeSubRange(start, m_first); // Ended at EOF
+    return makeSubRange(start, m_first - 1);
+}
+
+void CSSParserTokenRange::consumeComponentValue()
+{
+    // FIXME: This is going to do multiple passes over large sections of a stylesheet.
+    // We should consider optimising this by precomputing where each block ends.
+    unsigned nestingLevel = 0;
+    do {
+        const CSSParserToken& token = consume();
+        if (token.getBlockType() == CSSParserToken::BlockStart)
+            nestingLevel++;
+        else if (token.getBlockType() == CSSParserToken::BlockEnd)
+            nestingLevel--;
+    } while (nestingLevel && m_first < m_last);
+}
+
+String CSSParserTokenRange::serialize() const
+{
+    // We're supposed to insert comments between certain pairs of token types
+    // as per spec, but since this is currently only used for @supports CSSOM
+    // we just get these cases wrong and avoid the additional complexity.
+    StringBuilder builder;
+    for (const CSSParserToken* it = m_first; it < m_last; ++it)
+        it->serialize(builder);
+    return builder.toString();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/css/parser/CSSParserTokenRange.h b/Source/WebCore/css/parser/CSSParserTokenRange.h
new file mode 100644 (file)
index 0000000..ea94619
--- /dev/null
@@ -0,0 +1,106 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CSSParserTokenRange_h
+#define CSSParserTokenRange_h
+
+#include "CSSParserToken.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+// A CSSParserTokenRange is an iterator over a subrange of a vector of CSSParserTokens.
+// Accessing outside of the range will return an endless stream of EOF tokens.
+// This class refers to half-open intervals [first, last).
+class CSSParserTokenRange {
+public:
+    template<size_t inlineBuffer>
+    CSSParserTokenRange(const Vector<CSSParserToken, inlineBuffer>& vector)
+        : m_first(vector.begin())
+        , m_last(vector.end())
+    {
+    }
+
+    // This should be called on a range with tokens returned by that range.
+    CSSParserTokenRange makeSubRange(const CSSParserToken* first, const CSSParserToken* last) const;
+
+    bool atEnd() const { return m_first == m_last; }
+    const CSSParserToken* end() const { return m_last; }
+
+    const CSSParserToken& peek(unsigned offset = 0) const
+    {
+        if (m_first + offset >= m_last)
+            return eofToken();
+        return *(m_first + offset);
+    }
+
+    const CSSParserToken& consume()
+    {
+        if (m_first == m_last)
+            return eofToken();
+        return *m_first++;
+    }
+
+    const CSSParserToken& consumeIncludingWhitespace()
+    {
+        const CSSParserToken& result = consume();
+        consumeWhitespace();
+        return result;
+    }
+
+    // The returned range doesn't include the brackets
+    CSSParserTokenRange consumeBlock();
+
+    void consumeComponentValue();
+
+    void consumeWhitespace()
+    {
+        while (peek().type() == WhitespaceToken)
+            ++m_first;
+    }
+
+    String serialize() const;
+
+    const CSSParserToken* begin() const { return m_first; }
+
+    static CSSParserToken& eofToken();
+
+private:
+    CSSParserTokenRange(const CSSParserToken* first, const CSSParserToken* last)
+        : m_first(first)
+        , m_last(last)
+    { }
+
+    const CSSParserToken* m_first;
+    const CSSParserToken* m_last;
+};
+
+} // namespace WebCore
+
+#endif // CSSParserTokenRange_h
diff --git a/Source/WebCore/css/parser/CSSPropertyParser.cpp b/Source/WebCore/css/parser/CSSPropertyParser.cpp
new file mode 100644 (file)
index 0000000..0b7bf70
--- /dev/null
@@ -0,0 +1,4784 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "CSSPropertyParser.h"
+
+#include "CSSBasicShapes.h"
+#include "CSSBorderImage.h"
+#include "CSSContentDistributionValue.h"
+#include "CSSCursorImageValue.h"
+// FIXME-NEWPARSER #include "CSSCustomIdentValue.h"
+#include "CSSFontFaceSrcValue.h"
+// FIXME-NEWPARSER #include "CSSFontFamilyValue.h"
+#include "CSSFontFeatureValue.h"
+#include "CSSFunctionValue.h"
+#include "CSSGridAutoRepeatValue.h"
+#include "CSSGridLineNamesValue.h"
+#include "CSSGridTemplateAreasValue.h"
+#include "CSSInheritedValue.h"
+#include "CSSInitialValue.h"
+#include "CSSParserFastPaths.h"
+#include "CSSParserIdioms.h"
+// FIXME-NEWPARSER #include "CSSPathValue.h"
+// FIXME-NEWPARSER #include "CSSPendingSubstitutionValue.h"
+#include "CSSPrimitiveValueMappings.h"
+#include "CSSPropertyParserHelpers.h"
+// FIXME-NEWPARSER #include "CSSQuadValue.h"
+#include "CSSReflectValue.h"
+#include "CSSShadowValue.h"
+// FIXME-NEWPARSER #include "CSSStringValue.h"
+#include "CSSTimingFunctionValue.h"
+// FIXME-NEWPARSER #include "CSSURIValue.h"
+#include "CSSUnicodeRangeValue.h"
+#include "CSSUnsetValue.h"
+// FIXME-NEWPARSER #include "CSSValuePair.h"
+// FIXME-NEWPARSER #include "CSSVariableReferenceValue.h"
+// FIXME-NEWPARSER #include "CSSVariableParser.h"
+#include "Counter.h"
+#include "FontFace.h"
+#include "HashTools.h"
+#include "RenderTheme.h"
+#include "RuntimeEnabledFeatures.h"
+#include "SVGPathUtilities.h"
+#include "StylePropertyShorthand.h"
+#include <memory>
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+static bool hasPrefix(const char* string, unsigned length, const char* prefix)
+{
+    for (unsigned i = 0; i < length; ++i) {
+        if (!prefix[i])
+            return true;
+        if (string[i] != prefix[i])
+            return false;
+    }
+    return false;
+}
+
+#if PLATFORM(IOS)
+static void cssPropertyNameIOSAliasing(const char* propertyName, const char*& propertyNameAlias, unsigned& newLength)
+{
+    if (!strcmp(propertyName, "-webkit-hyphenate-locale")) {
+        // Worked in iOS 4.2.
+        static const char webkitLocale[] = "-webkit-locale";
+        propertyNameAlias = webkitLocale;
+        newLength = strlen(webkitLocale);
+    }
+}
+#endif
+
+template <typename CharacterType>
+static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length)
+{
+    char buffer[maxCSSPropertyNameLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
+    
+    for (unsigned i = 0; i != length; ++i) {
+        CharacterType c = propertyName[i];
+        if (!c || c >= 0x7F)
+            return CSSPropertyInvalid; // illegal character
+        buffer[i] = toASCIILower(c);
+    }
+    buffer[length] = '\0';
+    
+    const char* name = buffer;
+    if (buffer[0] == '-') {
+#if ENABLE(LEGACY_CSS_VENDOR_PREFIXES)
+        // If the prefix is -apple- or -khtml-, change it to -webkit-.
+        // This makes the string one character longer.
+        if (RuntimeEnabledFeatures::sharedFeatures().legacyCSSVendorPrefixesEnabled()
+            && (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-"))) {
+            memmove(buffer + 7, buffer + 6, length + 1 - 6);
+            memcpy(buffer, "-webkit", 7);
+            ++length;
+        }
+#endif
+#if PLATFORM(IOS)
+        cssPropertyNameIOSAliasing(buffer, name, length);
+#endif
+    }
+    
+    const Property* hashTableEntry = findProperty(name, length);
+    return hashTableEntry ? static_cast<CSSPropertyID>(hashTableEntry->id) : CSSPropertyInvalid;
+}
+
+static bool isAppleLegacyCssValueKeyword(const char* valueKeyword, unsigned length)
+{
+    static const char applePrefix[] = "-apple-";
+    static const char appleSystemPrefix[] = "-apple-system";
+    static const char* appleWirelessPlaybackTargetActive = getValueName(CSSValueAppleWirelessPlaybackTargetActive);
+    
+    return hasPrefix(valueKeyword, length, applePrefix)
+    && !hasPrefix(valueKeyword, length, appleSystemPrefix)
+    && !WTF::equal(reinterpret_cast<const LChar*>(valueKeyword), reinterpret_cast<const LChar*>(appleWirelessPlaybackTargetActive), length);
+}
+
+template <typename CharacterType>
+static CSSValueID cssValueKeywordID(const CharacterType* valueKeyword, unsigned length)
+{
+    char buffer[maxCSSValueKeywordLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
+    
+    for (unsigned i = 0; i != length; ++i) {
+        CharacterType c = valueKeyword[i];
+        if (!c || c >= 0x7F)
+            return CSSValueInvalid; // illegal keyword.
+        buffer[i] = WTF::toASCIILower(c);
+    }
+    buffer[length] = '\0';
+    
+    if (buffer[0] == '-') {
+        // If the prefix is -apple- or -khtml-, change it to -webkit-.
+        // This makes the string one character longer.
+        // On iOS we don't want to change values starting with -apple-system to -webkit-system.
+        // FIXME: Remove this mangling without breaking the web.
+        if (isAppleLegacyCssValueKeyword(buffer, length) || hasPrefix(buffer, length, "-khtml-")) {
+            memmove(buffer + 7, buffer + 6, length + 1 - 6);
+            memcpy(buffer, "-webkit", 7);
+            ++length;
+        }
+    }
+    
+    const Value* hashTableEntry = findValue(buffer, length);
+    return hashTableEntry ? static_cast<CSSValueID>(hashTableEntry->id) : CSSValueInvalid;
+}
+
+CSSValueID cssValueKeywordID(StringView string)
+{
+    unsigned length = string.length();
+    if (!length)
+        return CSSValueInvalid;
+    if (length > maxCSSValueKeywordLength)
+        return CSSValueInvalid;
+    
+    return string.is8Bit() ? cssValueKeywordID(string.characters8(), length) : cssValueKeywordID(string.characters16(), length);
+}
+
+CSSPropertyID unresolvedCSSPropertyID(StringView string)
+{
+    unsigned length = string.length();
+    
+    if (!length)
+        return CSSPropertyInvalid;
+    if (length > maxCSSPropertyNameLength)
+        return CSSPropertyInvalid;
+    
+    return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length);
+}
+    
+// FIXME-NEWPARSER
+// Comment out property parsing for now.
+/*
+using namespace CSSPropertyParserHelpers;
+
+
+CSSPropertyParser::CSSPropertyParser(const CSSParserTokenRange& range,
+    const CSSParserContext& context, Vector<CSSProperty, 256>* parsedProperties)
+    : m_range(range)
+    , m_context(context)
+    , m_parsedProperties(parsedProperties)
+{
+    m_range.consumeWhitespace();
+}
+
+void CSSPropertyParser::addProperty(CSSPropertyID property, CSSPropertyID currentShorthand, const CSSValue& value, bool important, bool implicit)
+{
+    ASSERT(!isPropertyAlias(property));
+
+    int shorthandIndex = 0;
+    bool setFromShorthand = false;
+
+    if (currentShorthand) {
+        Vector<StylePropertyShorthand, 4> shorthands;
+        getMatchingShorthandsForLonghand(property, &shorthands);
+        setFromShorthand = true;
+        if (shorthands.size() > 1)
+            shorthandIndex = indexOfShorthandForLonghand(currentShorthand, shorthands);
+    }
+
+    m_parsedProperties->append(CSSProperty(property, value, important, setFromShorthand, shorthandIndex, implicit));
+}
+
+void CSSPropertyParser::addExpandedPropertyForValue(CSSPropertyID property, const CSSValue& value, bool important)
+{
+    const StylePropertyShorthand& shorthand = shorthandForProperty(property);
+    unsigned shorthandLength = shorthand.length();
+    ASSERT(shorthandLength);
+    const CSSPropertyID* longhands = shorthand.properties();
+    for (unsigned i = 0; i < shorthandLength; ++i)
+        addProperty(longhands[i], property, value, important);
+}
+
+bool CSSPropertyParser::parseValue(CSSPropertyID unresolvedProperty, bool important,
+    const CSSParserTokenRange& range, const CSSParserContext& context,
+    Vector<CSSProperty, 256>& parsedProperties, StyleRule::Type ruleType)
+{
+    int parsedPropertiesSize = parsedProperties.size();
+
+    CSSPropertyParser parser(range, context, &parsedProperties);
+    CSSPropertyID resolvedProperty = resolveCSSPropertyID(unresolvedProperty);
+    bool parseSuccess;
+
+    if (ruleType == StyleRule::Viewport) {
+        parseSuccess = (RuntimeEnabledFeatures::cssViewportEnabled() || isUASheetBehavior(context.mode()))
+            && parser.parseViewportDescriptor(resolvedProperty, important);
+    } else if (ruleType == StyleRule::FontFace) {
+        parseSuccess = parser.parseFontFaceDescriptor(resolvedProperty);
+    } else {
+        parseSuccess = parser.parseValueStart(unresolvedProperty, important);
+    }
+
+    // This doesn't count UA style sheets
+    if (parseSuccess && context.useCounter())
+        context.useCounter()->count(context.mode(), unresolvedProperty);
+
+    if (!parseSuccess)
+        parsedProperties.shrink(parsedPropertiesSize);
+
+    return parseSuccess;
+}
+
+const CSSValue* CSSPropertyParser::parseSingleValue(
+    CSSPropertyID property, const CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    CSSPropertyParser parser(range, context, nullptr);
+    const CSSValue* value = parser.parseSingleValue(property);
+    if (!value || !parser.m_range.atEnd())
+        return nullptr;
+    return value;
+}
+
+bool CSSPropertyParser::parseValueStart(CSSPropertyID unresolvedProperty, bool important)
+{
+    if (consumeCSSWideKeyword(unresolvedProperty, important))
+        return true;
+
+    CSSParserTokenRange originalRange = m_range;
+    CSSPropertyID propertyId = resolveCSSPropertyID(unresolvedProperty);
+    bool isShorthand = isShorthandProperty(propertyId);
+
+    if (isShorthand) {
+        // Variable references will fail to parse here and will fall out to the variable ref parser below.
+        if (parseShorthand(unresolvedProperty, important))
+            return true;
+    } else {
+        if (const CSSValue* parsedValue = parseSingleValue(unresolvedProperty)) {
+            if (m_range.atEnd()) {
+                addProperty(propertyId, CSSPropertyInvalid, *parsedValue, important);
+                return true;
+            }
+        }
+    }
+
+    if (RuntimeEnabledFeatures::cssVariablesEnabled() && CSSVariableParser::containsValidVariableReferences(originalRange)) {
+        CSSVariableReferenceValue* variable = CSSVariableReferenceValue::create(CSSVariableData::create(originalRange));
+
+        if (isShorthand) {
+            const CSSPendingSubstitutionValue& pendingValue = *CSSPendingSubstitutionValue::create(propertyId, variable);
+            addExpandedPropertyForValue(propertyId, pendingValue, important);
+        } else {
+            addProperty(propertyId, CSSPropertyInvalid, *variable, important);
+        }
+        return true;
+    }
+
+    return false;
+}
+bool CSSPropertyParser::consumeCSSWideKeyword(CSSPropertyID unresolvedProperty, bool important)
+{
+    CSSParserTokenRange rangeCopy = m_range;
+    CSSValueID id = rangeCopy.consumeIncludingWhitespace().id();
+    if (!rangeCopy.atEnd())
+        return false;
+
+    CSSValue* value = nullptr;
+    if (id == CSSValueInitial)
+        value = CSSInitialValue::create();
+    else if (id == CSSValueInherit)
+        value = CSSInheritedValue::create();
+    else if (id == CSSValueUnset)
+        value = CSSUnsetValue::create();
+    else
+        return false;
+
+    CSSPropertyID property = resolveCSSPropertyID(unresolvedProperty);
+    const StylePropertyShorthand& shorthand = shorthandForProperty(property);
+    if (!shorthand.length()) {
+        if (CSSPropertyMetadata::isDescriptorOnly(unresolvedProperty))
+            return false;
+        addProperty(property, CSSPropertyInvalid, *value, important);
+    } else {
+        addExpandedPropertyForValue(property, *value, important);
+    }
+    m_range = rangeCopy;
+    return true;
+}
+
+static CSSValueList* consumeTransformOrigin(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
+{
+    CSSValue* resultX = nullptr;
+    CSSValue* resultY = nullptr;
+    if (consumeOneOrTwoValuedPosition(range, cssParserMode, unitless, resultX, resultY)) {
+        CSSValueList* list = CSSValueList::createSpaceSeparated();
+        list->append(*resultX);
+        list->append(*resultY);
+        CSSValue* resultZ = consumeLength(range, cssParserMode, ValueRangeAll);
+        if (!resultZ)
+            resultZ = CSSPrimitiveValue::create(0, CSSPrimitiveValue::UnitType::Pixels);
+        list->append(*resultZ);
+        return list;
+    }
+    return nullptr;
+}
+
+// Methods for consuming non-shorthand properties starts here.
+static CSSValue* consumeWillChange(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+
+    CSSValueList* values = CSSValueList::createCommaSeparated();
+    // Every comma-separated list of identifiers is a valid will-change value,
+    // unless the list includes an explicitly disallowed identifier.
+    while (true) {
+        if (range.peek().type() != IdentToken)
+            return nullptr;
+        CSSPropertyID unresolvedProperty = unresolvedCSSPropertyID(range.peek().value());
+        if (unresolvedProperty) {
+            ASSERT(CSSPropertyMetadata::isEnabledProperty(unresolvedProperty));
+            // Now "all" is used by both CSSValue and CSSPropertyValue.
+            // Need to return nullptr when currentValue is CSSPropertyAll.
+            if (unresolvedProperty == CSSPropertyWillChange || unresolvedProperty == CSSPropertyAll)
+                return nullptr;
+            values->append(*CSSCustomIdentValue::create(unresolvedProperty));
+            range.consumeIncludingWhitespace();
+        } else {
+            switch (range.peek().id()) {
+            case CSSValueNone:
+            case CSSValueAll:
+            case CSSValueAuto:
+            case CSSValueDefault:
+            case CSSValueInitial:
+            case CSSValueInherit:
+                return nullptr;
+            case CSSValueContents:
+            case CSSValueScrollPosition:
+                values->append(*consumeIdent(range));
+                break;
+            default:
+                range.consumeIncludingWhitespace();
+                break;
+            }
+        }
+
+        if (range.atEnd())
+            break;
+        if (!consumeCommaIncludingWhitespace(range))
+            return nullptr;
+    }
+
+    return values;
+}
+
+static CSSFontFeatureValue* consumeFontFeatureTag(CSSParserTokenRange& range)
+{
+    // Feature tag name consists of 4-letter characters.
+    static const unsigned tagNameLength = 4;
+
+    const CSSParserToken& token = range.consumeIncludingWhitespace();
+    // Feature tag name comes first
+    if (token.type() != StringToken)
+        return nullptr;
+    if (token.value().length() != tagNameLength)
+        return nullptr;
+    AtomicString tag = token.value().toAtomicString();
+    for (unsigned i = 0; i < tagNameLength; ++i) {
+        // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
+        UChar character = tag[i];
+        if (character < 0x20 || character > 0x7E)
+            return nullptr;
+    }
+
+    int tagValue = 1;
+    // Feature tag values could follow: <integer> | on | off
+    if (range.peek().type() == NumberToken && range.peek().numericValueType() == IntegerValueType && range.peek().numericValue() >= 0) {
+        tagValue = clampTo<int>(range.consumeIncludingWhitespace().numericValue());
+        if (tagValue < 0)
+            return nullptr;
+    } else if (range.peek().id() == CSSValueOn || range.peek().id() == CSSValueOff) {
+        tagValue = range.consumeIncludingWhitespace().id() == CSSValueOn;
+    }
+    return CSSFontFeatureValue::create(tag, tagValue);
+}
+
+static CSSValue* consumeFontFeatureSettings(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueNormal)
+        return consumeIdent(range);
+    CSSValueList* settings = CSSValueList::createCommaSeparated();
+    do {
+        CSSFontFeatureValue* fontFeatureValue = consumeFontFeatureTag(range);
+        if (!fontFeatureValue)
+            return nullptr;
+        settings->append(*fontFeatureValue);
+    } while (consumeCommaIncludingWhitespace(range));
+    return settings;
+}
+
+static CSSValue* consumePage(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+    return consumeCustomIdent(range);
+}
+
+static CSSValue* consumeQuotes(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    CSSValueList* values = CSSValueList::createSpaceSeparated();
+    while (!range.atEnd()) {
+        CSSStringValue* parsedValue = consumeString(range);
+        if (!parsedValue)
+            return nullptr;
+        values->append(*parsedValue);
+    }
+    if (values->length() && values->length() % 2 == 0)
+        return values;
+    return nullptr;
+}
+
+static CSSValue* consumeWebkitHighlight(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    return consumeString(range);
+}
+
+class FontVariantLigaturesParser {
+    STACK_ALLOCATED();
+
+public:
+    FontVariantLigaturesParser()
+        : m_sawCommonLigaturesValue(false)
+        , m_sawDiscretionaryLigaturesValue(false)
+        , m_sawHistoricalLigaturesValue(false)
+        , m_sawContextualLigaturesValue(false)
+        , m_result(CSSValueList::createSpaceSeparated())
+    {
+    }
+
+    enum class ParseResult {
+        ConsumedValue,
+        DisallowedValue,
+        UnknownValue
+    };
+
+    ParseResult consumeLigature(CSSParserTokenRange& range)
+    {
+        CSSValueID valueID = range.peek().id();
+        switch (valueID) {
+        case CSSValueNoCommonLigatures:
+        case CSSValueCommonLigatures:
+            if (m_sawCommonLigaturesValue)
+                return ParseResult::DisallowedValue;
+            m_sawCommonLigaturesValue = true;
+            break;
+        case CSSValueNoDiscretionaryLigatures:
+        case CSSValueDiscretionaryLigatures:
+            if (m_sawDiscretionaryLigaturesValue)
+                return ParseResult::DisallowedValue;
+            m_sawDiscretionaryLigaturesValue = true;
+            break;
+        case CSSValueNoHistoricalLigatures:
+        case CSSValueHistoricalLigatures:
+            if (m_sawHistoricalLigaturesValue)
+                return ParseResult::DisallowedValue;
+            m_sawHistoricalLigaturesValue = true;
+            break;
+        case CSSValueNoContextual:
+        case CSSValueContextual:
+            if (m_sawContextualLigaturesValue)
+                return ParseResult::DisallowedValue;
+            m_sawContextualLigaturesValue = true;
+            break;
+        default:
+            return ParseResult::UnknownValue;
+        }
+        m_result->append(*consumeIdent(range));
+        return ParseResult::ConsumedValue;
+    }
+
+    CSSValue* finalizeValue()
+    {
+        if (!m_result->length())
+            return CSSPrimitiveValue::createIdentifier(CSSValueNormal);
+        return m_result.release();
+    }
+
+private:
+    bool m_sawCommonLigaturesValue;
+    bool m_sawDiscretionaryLigaturesValue;
+    bool m_sawHistoricalLigaturesValue;
+    bool m_sawContextualLigaturesValue;
+    Member<CSSValueList> m_result;
+};
+
+static CSSValue* consumeFontVariantLigatures(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueNormal || range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+
+    FontVariantLigaturesParser ligaturesParser;
+    do {
+        if (ligaturesParser.consumeLigature(range) !=
+            FontVariantLigaturesParser::ParseResult::ConsumedValue)
+            return nullptr;
+    } while (!range.atEnd());
+
+    return ligaturesParser.finalizeValue();
+}
+
+static CSSPrimitiveValue* consumeFontVariantCaps(CSSParserTokenRange& range)
+{
+    return consumeIdent<CSSValueNormal, CSSValueSmallCaps, CSSValueAllSmallCaps,
+        CSSValuePetiteCaps, CSSValueAllPetiteCaps,
+        CSSValueUnicase, CSSValueTitlingCaps>(range);
+}
+
+class FontVariantNumericParser {
+    STACK_ALLOCATED();
+
+public:
+    FontVariantNumericParser()
+        : m_sawNumericFigureValue(false)
+        , m_sawNumericSpacingValue(false)
+        , m_sawNumericFractionValue(false)
+        , m_sawOrdinalValue(false)
+        , m_sawSlashedZeroValue(false)
+        , m_result(CSSValueList::createSpaceSeparated())
+    {
+    }
+
+    enum class ParseResult {
+        ConsumedValue,
+        DisallowedValue,
+        UnknownValue
+    };
+
+    ParseResult consumeNumeric(CSSParserTokenRange& range)
+    {
+        CSSValueID valueID = range.peek().id();
+        switch (valueID) {
+        case CSSValueLiningNums:
+        case CSSValueOldstyleNums:
+            if (m_sawNumericFigureValue)
+                return ParseResult::DisallowedValue;
+            m_sawNumericFigureValue = true;
+            break;
+        case CSSValueProportionalNums:
+        case CSSValueTabularNums:
+            if (m_sawNumericSpacingValue)
+                return ParseResult::DisallowedValue;
+            m_sawNumericSpacingValue = true;
+            break;
+        case CSSValueDiagonalFractions:
+        case CSSValueStackedFractions:
+            if (m_sawNumericFractionValue)
+                return ParseResult::DisallowedValue;
+            m_sawNumericFractionValue = true;
+            break;
+        case CSSValueOrdinal:
+            if (m_sawOrdinalValue)
+                return ParseResult::DisallowedValue;
+            m_sawOrdinalValue = true;
+            break;
+        case CSSValueSlashedZero:
+            if (m_sawSlashedZeroValue)
+                return ParseResult::DisallowedValue;
+            m_sawSlashedZeroValue = true;
+            break;
+        default:
+            return ParseResult::UnknownValue;
+        }
+        m_result->append(*consumeIdent(range));
+        return ParseResult::ConsumedValue;
+    }
+
+    CSSValue* finalizeValue()
+    {
+        if (!m_result->length())
+            return CSSPrimitiveValue::createIdentifier(CSSValueNormal);
+        return m_result.release();
+    }
+
+
+private:
+    bool m_sawNumericFigureValue;
+    bool m_sawNumericSpacingValue;
+    bool m_sawNumericFractionValue;
+    bool m_sawOrdinalValue;
+    bool m_sawSlashedZeroValue;
+    Member<CSSValueList> m_result;
+};
+
+static CSSValue* consumeFontVariantNumeric(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueNormal)
+        return consumeIdent(range);
+
+    FontVariantNumericParser numericParser;
+    do {
+        if (numericParser.consumeNumeric(range) !=
+            FontVariantNumericParser::ParseResult::ConsumedValue)
+            return nullptr;
+    } while (!range.atEnd());
+
+    return numericParser.finalizeValue();
+}
+
+static CSSPrimitiveValue* consumeFontVariantCSS21(CSSParserTokenRange& range)
+{
+    return consumeIdent<CSSValueNormal, CSSValueSmallCaps>(range);
+}
+
+static CSSValue* consumeFontVariantList(CSSParserTokenRange& range)
+{
+    CSSValueList* values = CSSValueList::createCommaSeparated();
+    do {
+        if (range.peek().id() == CSSValueAll) {
+            // FIXME: CSSPropertyParser::parseFontVariant() implements
+            // the old css3 draft:
+            // http://www.w3.org/TR/2002/WD-css3-webfonts-20020802/#font-variant
+            // 'all' is only allowed in @font-face and with no other values.
+            if (values->length())
+                return nullptr;
+            return consumeIdent(range);
+        }
+        CSSPrimitiveValue* fontVariant = consumeFontVariantCSS21(range);
+        if (fontVariant)
+            values->append(*fontVariant);
+    } while (consumeCommaIncludingWhitespace(range));
+
+    if (values->length())
+        return values;
+
+    return nullptr;
+}
+
+static CSSPrimitiveValue* consumeFontWeight(CSSParserTokenRange& range)
+{
+    const CSSParserToken& token = range.peek();
+    if (token.id() >= CSSValueNormal && token.id() <= CSSValueLighter)
+        return consumeIdent(range);
+    if (token.type() != NumberToken || token.numericValueType() != IntegerValueType)
+        return nullptr;
+    int weight = static_cast<int>(token.numericValue());
+    if ((weight % 100) || weight < 100 || weight > 900)
+        return nullptr;
+    range.consumeIncludingWhitespace();
+    return CSSPrimitiveValue::createIdentifier(static_cast<CSSValueID>(CSSValue100 + weight / 100 - 1));
+}
+
+static String concatenateFamilyName(CSSParserTokenRange& range)
+{
+    StringBuilder builder;
+    bool addedSpace = false;
+    const CSSParserToken& firstToken = range.peek();
+    while (range.peek().type() == IdentToken) {
+        if (!builder.isEmpty()) {
+            builder.append(' ');
+            addedSpace = true;
+        }
+        builder.append(range.consumeIncludingWhitespace().value());
+    }
+    if (!addedSpace && isCSSWideKeyword(firstToken.id()))
+        return String();
+    return builder.toString();
+}
+
+static CSSValue* consumeFamilyName(CSSParserTokenRange& range)
+{
+    if (range.peek().type() == StringToken)
+        return CSSFontFamilyValue::create(range.consumeIncludingWhitespace().value().toString());
+    if (range.peek().type() != IdentToken)
+        return nullptr;
+    String familyName = concatenateFamilyName(range);
+    if (familyName.isNull())
+        return nullptr;
+    return CSSFontFamilyValue::create(familyName);
+}
+
+static CSSValue* consumeGenericFamily(CSSParserTokenRange& range)
+{
+    return consumeIdentRange(range, CSSValueSerif, CSSValueWebkitBody);
+}
+
+static CSSValueList* consumeFontFamily(CSSParserTokenRange& range)
+{
+    CSSValueList* list = CSSValueList::createCommaSeparated();
+    do {
+        CSSValue* parsedValue = consumeGenericFamily(range);
+        if (parsedValue) {
+            list->append(*parsedValue);
+        } else {
+            parsedValue = consumeFamilyName(range);
+            if (parsedValue) {
+                list->append(*parsedValue);
+            } else {
+                return nullptr;
+            }
+        }
+    } while (consumeCommaIncludingWhitespace(range));
+    return list;
+}
+
+static CSSValue* consumeSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().id() == CSSValueNormal)
+        return consumeIdent(range);
+    // FIXME: allow <percentage>s in word-spacing.
+    return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
+}
+
+static CSSValue* consumeTabSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    CSSPrimitiveValue* parsedValue = consumeInteger(range, 0);
+    if (parsedValue)
+        return parsedValue;
+    return consumeLength(range, cssParserMode, ValueRangeNonNegative);
+}
+
+static CSSValue* consumeTextSizeAdjust(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    return consumePercent(range, ValueRangeNonNegative);
+}
+
+static CSSValue* consumeFontSize(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
+{
+    if (range.peek().id() >= CSSValueXxSmall && range.peek().id() <= CSSValueLarger)
+        return consumeIdent(range);
+    return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, unitless);
+}
+
+static CSSPrimitiveValue* consumeLineHeight(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().id() == CSSValueNormal)
+        return consumeIdent(range);
+
+    CSSPrimitiveValue* lineHeight = consumeNumber(range, ValueRangeNonNegative);
+    if (lineHeight)
+        return lineHeight;
+    return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
+}
+
+static CSSValueList* consumeRotation(CSSParserTokenRange& range)
+{
+    ASSERT(RuntimeEnabledFeatures::cssIndependentTransformPropertiesEnabled());
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+
+    CSSValue* rotation = consumeAngle(range);
+    if (!rotation)
+        return nullptr;
+    list->append(*rotation);
+
+    if (range.atEnd())
+        return list;
+
+    for (unsigned i = 0; i < 3; i++) { // 3 dimensions of rotation
+        CSSValue* dimension = consumeNumber(range, ValueRangeAll);
+        if (!dimension)
+            return nullptr;
+        list->append(*dimension);
+    }
+
+    return list;
+}
+
+static CSSValueList* consumeScale(CSSParserTokenRange& range)
+{
+    ASSERT(RuntimeEnabledFeatures::cssIndependentTransformPropertiesEnabled());
+
+    CSSValue* scale = consumeNumber(range, ValueRangeAll);
+    if (!scale)
+        return nullptr;
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    list->append(*scale);
+    scale = consumeNumber(range, ValueRangeAll);
+    if (scale) {
+        list->append(*scale);
+        scale = consumeNumber(range, ValueRangeAll);
+        if (scale)
+            list->append(*scale);
+    }
+
+    return list;
+}
+
+static CSSValueList* consumeTranslate(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    ASSERT(RuntimeEnabledFeatures::cssIndependentTransformPropertiesEnabled());
+    CSSValue* translate = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
+    if (!translate)
+        return nullptr;
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    list->append(*translate);
+    translate = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
+    if (translate) {
+        list->append(*translate);
+        translate = consumeLength(range, cssParserMode, ValueRangeAll);
+        if (translate)
+            list->append(*translate);
+    }
+
+    return list;
+}
+
+static CSSValue* consumeCounter(CSSParserTokenRange& range, int defaultValue)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    do {
+        CSSCustomIdentValue* counterName = consumeCustomIdent(range);
+        if (!counterName)
+            return nullptr;
+        int i = defaultValue;
+        if (CSSPrimitiveValue* counterValue = consumeInteger(range))
+            i = clampTo<int>(counterValue->getDoubleValue());
+        list->append(*CSSValuePair::create(counterName,
+            CSSPrimitiveValue::create(i, CSSPrimitiveValue::UnitType::Integer),
+            CSSValuePair::DropIdenticalValues));
+    } while (!range.atEnd());
+    return list;
+}
+
+static CSSValue* consumePageSize(CSSParserTokenRange& range)
+{
+    return consumeIdent<CSSValueA3, CSSValueA4, CSSValueA5, CSSValueB4, CSSValueB5, CSSValueLedger, CSSValueLegal, CSSValueLetter>(range);
+}
+
+static CSSValueList* consumeSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    CSSValueList* result = CSSValueList::createSpaceSeparated();
+
+    if (range.peek().id() == CSSValueAuto) {
+        result->append(*consumeIdent(range));
+        return result;
+    }
+
+    if (CSSValue* width = consumeLength(range, cssParserMode, ValueRangeNonNegative)) {
+        CSSValue* height = consumeLength(range, cssParserMode, ValueRangeNonNegative);
+        result->append(*width);
+        if (height)
+            result->append(*height);
+        return result;
+    }
+
+    CSSValue* pageSize = consumePageSize(range);
+    CSSValue* orientation = consumeIdent<CSSValuePortrait, CSSValueLandscape>(range);
+    if (!pageSize)
+        pageSize = consumePageSize(range);
+
+    if (!orientation && !pageSize)
+        return nullptr;
+    if (pageSize)
+        result->append(*pageSize);
+    if (orientation)
+        result->append(*orientation);
+    return result;
+}
+
+static CSSValue* consumeSnapHeight(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    CSSPrimitiveValue* unit = consumeLength(range, cssParserMode, ValueRangeNonNegative);
+    if (!unit)
+        return nullptr;
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    list->append(*unit);
+
+    if (CSSPrimitiveValue* position = consumePositiveInteger(range)) {
+        if (position->getIntValue() > 100)
+            return nullptr;
+        list->append(*position);
+    }
+
+    return list;
+}
+
+static CSSValue* consumeTextIndent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    // [ <length> | <percentage> ] && hanging? && each-line?
+    // Keywords only allowed when css3Text is enabled.
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+
+    bool hasLengthOrPercentage = false;
+    bool hasEachLine = false;
+    bool hasHanging = false;
+
+    do {
+        if (!hasLengthOrPercentage) {
+            if (CSSValue* textIndent = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow)) {
+                list->append(*textIndent);
+                hasLengthOrPercentage = true;
+                continue;
+            }
+        }
+
+        if (RuntimeEnabledFeatures::css3TextEnabled()) {
+            CSSValueID id = range.peek().id();
+            if (!hasEachLine && id == CSSValueEachLine) {
+                list->append(*consumeIdent(range));
+                hasEachLine = true;
+                continue;
+            }
+            if (!hasHanging && id == CSSValueHanging) {
+                list->append(*consumeIdent(range));
+                hasHanging = true;
+                continue;
+            }
+        }
+        return nullptr;
+    } while (!range.atEnd());
+
+    if (!hasLengthOrPercentage)
+        return nullptr;
+
+    return list;
+}
+
+static bool validWidthOrHeightKeyword(CSSValueID id, const CSSParserContext& context)
+{
+    if (id == CSSValueWebkitMinContent || id == CSSValueWebkitMaxContent || id == CSSValueWebkitFillAvailable || id == CSSValueWebkitFitContent
+        || id == CSSValueMinContent || id == CSSValueMaxContent || id == CSSValueFitContent) {
+        if (context.useCounter()) {
+            switch (id) {
+            case CSSValueWebkitMinContent:
+                context.useCounter()->count(UseCounter::CSSValuePrefixedMinContent);
+                break;
+            case CSSValueWebkitMaxContent:
+                context.useCounter()->count(UseCounter::CSSValuePrefixedMaxContent);
+                break;
+            case CSSValueWebkitFillAvailable:
+                context.useCounter()->count(UseCounter::CSSValuePrefixedFillAvailable);
+                break;
+            case CSSValueWebkitFitContent:
+                context.useCounter()->count(UseCounter::CSSValuePrefixedFitContent);
+                break;
+            default:
+                break;
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+static CSSValue* consumeMaxWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
+{
+    if (range.peek().id() == CSSValueNone || validWidthOrHeightKeyword(range.peek().id(), context))
+        return consumeIdent(range);
+    return consumeLengthOrPercent(range, context.mode(), ValueRangeNonNegative, unitless);
+}
+
+static CSSValue* consumeWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
+{
+    if (range.peek().id() == CSSValueAuto || validWidthOrHeightKeyword(range.peek().id(), context))
+        return consumeIdent(range);
+    return consumeLengthOrPercent(range, context.mode(), ValueRangeNonNegative, unitless);
+}
+
+static CSSValue* consumeMarginOrOffset(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+    return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, unitless);
+}
+
+static CSSPrimitiveValue* consumeClipComponent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+    return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
+}
+
+static CSSValue* consumeClip(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+
+    if (range.peek().functionId() != CSSValueRect)
+        return nullptr;
+
+    CSSParserTokenRange args = consumeFunction(range);
+    // rect(t, r, b, l) || rect(t r b l)
+    CSSPrimitiveValue* top = consumeClipComponent(args, cssParserMode);
+    if (!top)
+        return nullptr;
+    bool needsComma = consumeCommaIncludingWhitespace(args);
+    CSSPrimitiveValue* right = consumeClipComponent(args, cssParserMode);
+    if (!right || (needsComma && !consumeCommaIncludingWhitespace(args)))
+        return nullptr;
+    CSSPrimitiveValue* bottom = consumeClipComponent(args, cssParserMode);
+    if (!bottom || (needsComma && !consumeCommaIncludingWhitespace(args)))
+        return nullptr;
+    CSSPrimitiveValue* left = consumeClipComponent(args, cssParserMode);
+    if (!left || !args.atEnd())
+        return nullptr;
+    return CSSQuadValue::create(top, right, bottom, left, CSSQuadValue::SerializeAsRect);
+}
+
+static bool consumePan(CSSParserTokenRange& range, CSSValue*& panX, CSSValue*& panY)
+{
+    CSSValueID id = range.peek().id();
+    if ((id == CSSValuePanX || id == CSSValuePanRight || id == CSSValuePanLeft) && !panX) {
+        if (id != CSSValuePanX && !RuntimeEnabledFeatures::cssTouchActionPanDirectionsEnabled())
+            return false;
+        panX = consumeIdent(range);
+    } else if ((id == CSSValuePanY || id == CSSValuePanDown || id == CSSValuePanUp) && !panY) {
+        if (id != CSSValuePanY && !RuntimeEnabledFeatures::cssTouchActionPanDirectionsEnabled())
+            return false;
+        panY = consumeIdent(range);
+    } else {
+        return false;
+    }
+    return true;
+}
+
+static CSSValue* consumeTouchAction(CSSParserTokenRange& range)
+{
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueAuto || id == CSSValueNone || id == CSSValueManipulation) {
+        list->append(*consumeIdent(range));
+        return list;
+    }
+
+    CSSValue* panX = nullptr;
+    CSSValue* panY = nullptr;
+    if (!consumePan(range, panX, panY))
+        return nullptr;
+    if (!range.atEnd() && !consumePan(range, panX, panY))
+        return nullptr;
+
+    if (panX)
+        list->append(*panX);
+    if (panY)
+        list->append(*panY);
+    return list;
+}
+
+static CSSPrimitiveValue* consumeLineClamp(CSSParserTokenRange& range)
+{
+    if (range.peek().type() != PercentageToken && range.peek().type() != NumberToken)
+        return nullptr;
+    CSSPrimitiveValue* clampValue = consumePercent(range, ValueRangeNonNegative);
+    if (clampValue)
+        return clampValue;
+    // When specifying number of lines, don't allow 0 as a valid value.
+    return consumePositiveInteger(range);
+}
+
+static CSSValue* consumeLocale(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+    return consumeString(range);
+}
+
+static CSSValue* consumeColumnWidth(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+    // Always parse lengths in strict mode here, since it would be ambiguous otherwise when used in
+    // the 'columns' shorthand property.
+    CSSPrimitiveValue* columnWidth = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
+    if (!columnWidth || (!columnWidth->isCalculated() && columnWidth->getDoubleValue() == 0))
+        return nullptr;
+    return columnWidth;
+}
+
+static CSSValue* consumeColumnCount(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+    return consumePositiveInteger(range);
+}
+
+static CSSValue* consumeColumnGap(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().id() == CSSValueNormal)
+        return consumeIdent(range);
+    return consumeLength(range, cssParserMode, ValueRangeNonNegative);
+}
+
+static CSSValue* consumeColumnSpan(CSSParserTokenRange& range)
+{
+    return consumeIdent<CSSValueAll, CSSValueNone>(range);
+}
+
+static CSSValue* consumeZoom(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    const CSSParserToken& token = range.peek();
+    CSSPrimitiveValue* zoom = nullptr;
+    if (token.type() == IdentToken) {
+        zoom = consumeIdent<CSSValueNormal, CSSValueReset, CSSValueDocument>(range);
+    } else {
+        zoom = consumePercent(range, ValueRangeNonNegative);
+        if (!zoom)
+            zoom = consumeNumber(range, ValueRangeNonNegative);
+    }
+    if (zoom && context.useCounter()
+        && !(token.id() == CSSValueNormal
+            || (token.type() == NumberToken && zoom->getDoubleValue() == 1)
+            || (token.type() == PercentageToken && zoom->getDoubleValue() == 100)))
+        context.useCounter()->count(UseCounter::CSSZoomNotEqualToOne);
+    return zoom;
+}
+
+static CSSValue* consumeAnimationIterationCount(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueInfinite)
+        return consumeIdent(range);
+    return consumeNumber(range, ValueRangeNonNegative);
+}
+
+static CSSValue* consumeAnimationName(CSSParserTokenRange& range, const CSSParserContext& context, bool allowQuotedName)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+
+    if (allowQuotedName && range.peek().type() == StringToken) {
+        // Legacy support for strings in prefixed animations.
+        if (context.useCounter())
+            context.useCounter()->count(UseCounter::QuotedAnimationName);
+
+        const CSSParserToken& token = range.consumeIncludingWhitespace();
+        if (equalIgnoringASCIICase(token.value(), "none"))
+            return CSSPrimitiveValue::createIdentifier(CSSValueNone);
+        return CSSCustomIdentValue::create(token.value().toString());
+    }
+
+    return consumeCustomIdent(range);
+}
+
+static CSSValue* consumeTransitionProperty(CSSParserTokenRange& range)
+{
+    const CSSParserToken& token = range.peek();
+    if (token.type() != IdentToken)
+        return nullptr;
+    if (token.id() == CSSValueNone)
+        return consumeIdent(range);
+
+    if (CSSPropertyID property = token.parseAsUnresolvedCSSPropertyID()) {
+        ASSERT(CSSPropertyMetadata::isEnabledProperty(property));
+        range.consumeIncludingWhitespace();
+        return CSSCustomIdentValue::create(property);
+    }
+    return consumeCustomIdent(range);
+}
+
+static CSSValue* consumeSteps(CSSParserTokenRange& range)
+{
+    ASSERT(range.peek().functionId() == CSSValueSteps);
+    CSSParserTokenRange rangeCopy = range;
+    CSSParserTokenRange args = consumeFunction(rangeCopy);
+
+    CSSPrimitiveValue* steps = consumePositiveInteger(args);
+    if (!steps)
+        return nullptr;
+
+    StepsTimingFunction::StepPosition position = StepsTimingFunction::StepPosition::END;
+    if (consumeCommaIncludingWhitespace(args)) {
+        switch (args.consumeIncludingWhitespace().id()) {
+        case CSSValueMiddle:
+            if (!RuntimeEnabledFeatures::webAnimationsAPIEnabled())
+                return nullptr;
+            position = StepsTimingFunction::StepPosition::MIDDLE;
+            break;
+        case CSSValueStart:
+            position = StepsTimingFunction::StepPosition::START;
+            break;
+        case CSSValueEnd:
+            position = StepsTimingFunction::StepPosition::END;
+            break;
+        default:
+            return nullptr;
+        }
+    }
+
+    if (!args.atEnd())
+        return nullptr;
+
+    range = rangeCopy;
+    return CSSStepsTimingFunctionValue::create(steps->getIntValue(), position);
+}
+
+static CSSValue* consumeCubicBezier(CSSParserTokenRange& range)
+{
+    ASSERT(range.peek().functionId() == CSSValueCubicBezier);
+    CSSParserTokenRange rangeCopy = range;
+    CSSParserTokenRange args = consumeFunction(rangeCopy);
+
+    double x1, y1, x2, y2;
+    if (consumeNumberRaw(args, x1)
+        && x1 >= 0 && x1 <= 1
+        && consumeCommaIncludingWhitespace(args)
+        && consumeNumberRaw(args, y1)
+        && consumeCommaIncludingWhitespace(args)
+        && consumeNumberRaw(args, x2)
+        && x2 >= 0 && x2 <= 1
+        && consumeCommaIncludingWhitespace(args)
+        && consumeNumberRaw(args, y2)
+        && args.atEnd()) {
+        range = rangeCopy;
+        return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2);
+    }
+
+    return nullptr;
+}
+
+static CSSValue* consumeAnimationTimingFunction(CSSParserTokenRange& range)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueEase || id == CSSValueLinear || id == CSSValueEaseIn
+        || id == CSSValueEaseOut || id == CSSValueEaseInOut || id == CSSValueStepStart
+        || id == CSSValueStepEnd || id == CSSValueStepMiddle)
+        return consumeIdent(range);
+
+    CSSValueID function = range.peek().functionId();
+    if (function == CSSValueSteps)
+        return consumeSteps(range);
+    if (function == CSSValueCubicBezier)
+        return consumeCubicBezier(range);
+    return nullptr;
+}
+
+static CSSValue* consumeAnimationValue(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context, bool useLegacyParsing)
+{
+    switch (property) {
+    case CSSPropertyAnimationDelay:
+    case CSSPropertyTransitionDelay:
+        return consumeTime(range, ValueRangeAll);
+    case CSSPropertyAnimationDirection:
+        return consumeIdent<CSSValueNormal, CSSValueAlternate, CSSValueReverse, CSSValueAlternateReverse>(range);
+    case CSSPropertyAnimationDuration:
+    case CSSPropertyTransitionDuration:
+        return consumeTime(range, ValueRangeNonNegative);
+    case CSSPropertyAnimationFillMode:
+        return consumeIdent<CSSValueNone, CSSValueForwards, CSSValueBackwards, CSSValueBoth>(range);
+    case CSSPropertyAnimationIterationCount:
+        return consumeAnimationIterationCount(range);
+    case CSSPropertyAnimationName:
+        return consumeAnimationName(range, context, useLegacyParsing);
+    case CSSPropertyAnimationPlayState:
+        return consumeIdent<CSSValueRunning, CSSValuePaused>(range);
+    case CSSPropertyTransitionProperty:
+        return consumeTransitionProperty(range);
+    case CSSPropertyAnimationTimingFunction:
+    case CSSPropertyTransitionTimingFunction:
+        return consumeAnimationTimingFunction(range);
+    default:
+        ASSERT_NOT_REACHED();
+        return nullptr;
+    }
+}
+
+static bool isValidAnimationPropertyList(CSSPropertyID property, const CSSValueList& valueList)
+{
+    if (property != CSSPropertyTransitionProperty || valueList.length() < 2)
+        return true;
+    for (auto& value : valueList) {
+        if (value->isPrimitiveValue() && toCSSPrimitiveValue(*value).isValueID()
+            && toCSSPrimitiveValue(*value).getValueID() == CSSValueNone)
+            return false;
+    }
+    return true;
+}
+
+static CSSValueList* consumeAnimationPropertyList(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context, bool useLegacyParsing)
+{
+    CSSValueList* list = CSSValueList::createCommaSeparated();
+    do {
+        CSSValue* value = consumeAnimationValue(property, range, context, useLegacyParsing);
+        if (!value)
+            return nullptr;
+        list->append(*value);
+    } while (consumeCommaIncludingWhitespace(range));
+    if (!isValidAnimationPropertyList(property, *list))
+        return nullptr;
+    ASSERT(list->length());
+    return list;
+}
+
+bool CSSPropertyParser::consumeAnimationShorthand(const StylePropertyShorthand& shorthand, bool useLegacyParsing, bool important)
+{
+    const unsigned longhandCount = shorthand.length();
+    CSSValueList* longhands[8];
+    ASSERT(longhandCount <= 8);
+    for (size_t i = 0; i < longhandCount; ++i)
+        longhands[i] = CSSValueList::createCommaSeparated();
+
+    do {
+        bool parsedLonghand[8] = { false };
+        do {
+            bool foundProperty = false;
+            for (size_t i = 0; i < longhandCount; ++i) {
+                if (parsedLonghand[i])
+                    continue;
+
+                if (CSSValue* value = consumeAnimationValue(shorthand.properties()[i], m_range, m_context, useLegacyParsing)) {
+                    parsedLonghand[i] = true;
+                    foundProperty = true;
+                    longhands[i]->append(*value);
+                    break;
+                }
+            }
+            if (!foundProperty)
+                return false;
+        } while (!m_range.atEnd() && m_range.peek().type() != CommaToken);
+
+        // FIXME: This will make invalid longhands, see crbug.com/386459
+        for (size_t i = 0; i < longhandCount; ++i) {
+            if (!parsedLonghand[i])
+                longhands[i]->append(*CSSInitialValue::createLegacyImplicit());
+            parsedLonghand[i] = false;
+        }
+    } while (consumeCommaIncludingWhitespace(m_range));
+
+    for (size_t i = 0; i < longhandCount; ++i) {
+        if (!isValidAnimationPropertyList(shorthand.properties()[i], *longhands[i]))
+            return false;
+    }
+
+    for (size_t i = 0; i < longhandCount; ++i)
+        addProperty(shorthand.properties()[i], shorthand.id(), *longhands[i], important);
+
+    return m_range.atEnd();
+}
+
+static CSSValue* consumeZIndex(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+    return consumeInteger(range);
+}
+
+static CSSShadowValue* parseSingleShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool allowInset, bool allowSpread)
+{
+    CSSPrimitiveValue* style = nullptr;
+    CSSValue* color = nullptr;
+
+    if (range.atEnd())
+        return nullptr;
+    if (range.peek().id() == CSSValueInset) {
+        if (!allowInset)
+            return nullptr;
+        style = consumeIdent(range);
+    }
+    color = consumeColor(range, cssParserMode);
+
+    CSSPrimitiveValue* horizontalOffset = consumeLength(range, cssParserMode, ValueRangeAll);
+    if (!horizontalOffset)
+        return nullptr;
+
+    CSSPrimitiveValue* verticalOffset = consumeLength(range, cssParserMode, ValueRangeAll);
+    if (!verticalOffset)
+        return nullptr;
+
+    CSSPrimitiveValue* blurRadius = consumeLength(range, cssParserMode, ValueRangeAll);
+    CSSPrimitiveValue* spreadDistance = nullptr;
+    if (blurRadius) {
+        // Blur radius must be non-negative.
+        if (blurRadius->getDoubleValue() < 0)
+            return nullptr;
+        if (allowSpread)
+            spreadDistance = consumeLength(range, cssParserMode, ValueRangeAll);
+    }
+
+    if (!range.atEnd()) {
+        if (!color)
+            color = consumeColor(range, cssParserMode);
+        if (range.peek().id() == CSSValueInset) {
+            if (!allowInset || style)
+                return nullptr;
+            style = consumeIdent(range);
+        }
+    }
+    return CSSShadowValue::create(horizontalOffset, verticalOffset, blurRadius,
+        spreadDistance, style, color);
+}
+
+static CSSValue* consumeShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool isBoxShadowProperty)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+
+    CSSValueList* shadowValueList = CSSValueList::createCommaSeparated();
+    do {
+        if (CSSShadowValue* shadowValue = parseSingleShadow(range, cssParserMode, isBoxShadowProperty, isBoxShadowProperty))
+            shadowValueList->append(*shadowValue);
+        else
+            return nullptr;
+    } while (consumeCommaIncludingWhitespace(range));
+    return shadowValueList;
+}
+
+static CSSFunctionValue* consumeFilterFunction(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    CSSValueID filterType = range.peek().functionId();
+    if (filterType < CSSValueInvert || filterType > CSSValueDropShadow)
+        return nullptr;
+    CSSParserTokenRange args = consumeFunction(range);
+    CSSFunctionValue* filterValue = CSSFunctionValue::create(filterType);
+    CSSValue* parsedValue = nullptr;
+
+    if (filterType == CSSValueDropShadow) {
+        parsedValue = parseSingleShadow(args, context.mode(), false, false);
+    } else {
+        if (args.atEnd()) {
+            if (context.useCounter())
+                context.useCounter()->count(UseCounter::CSSFilterFunctionNoArguments);
+            return filterValue;
+        }
+        if (filterType == CSSValueBrightness) {
+            // FIXME (crbug.com/397061): Support calc expressions like calc(10% + 0.5)
+            parsedValue = consumePercent(args, ValueRangeAll);
+            if (!parsedValue)
+                parsedValue = consumeNumber(args, ValueRangeAll);
+        } else if (filterType == CSSValueHueRotate) {
+            parsedValue = consumeAngle(args);
+        } else if (filterType == CSSValueBlur) {
+            parsedValue = consumeLength(args, HTMLStandardMode, ValueRangeNonNegative);
+        } else {
+            // FIXME (crbug.com/397061): Support calc expressions like calc(10% + 0.5)
+            parsedValue = consumePercent(args, ValueRangeNonNegative);
+            if (!parsedValue)
+                parsedValue = consumeNumber(args, ValueRangeNonNegative);
+            if (parsedValue && filterType != CSSValueSaturate && filterType != CSSValueContrast) {
+                bool isPercentage = toCSSPrimitiveValue(parsedValue)->isPercentage();
+                double maxAllowed = isPercentage ? 100.0 : 1.0;
+                if (toCSSPrimitiveValue(parsedValue)->getDoubleValue() > maxAllowed) {
+                    parsedValue = CSSPrimitiveValue::create(
+                        maxAllowed,
+                        isPercentage ? CSSPrimitiveValue::UnitType::Percentage : CSSPrimitiveValue::UnitType::Number);
+                }
+            }
+        }
+    }
+    if (!parsedValue || !args.atEnd())
+        return nullptr;
+    filterValue->append(*parsedValue);
+    return filterValue;
+}
+
+static CSSValue* consumeFilter(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    do {
+        CSSValue* filterValue = consumeUrl(range);
+        if (!filterValue) {
+            filterValue = consumeFilterFunction(range, context);
+            if (!filterValue)
+                return nullptr;
+        }
+        list->append(*filterValue);
+    } while (!range.atEnd());
+    return list;
+}
+
+static CSSValue* consumeTextDecorationLine(CSSParserTokenRange& range)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueNone)
+        return consumeIdent(range);
+
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    while (true) {
+        CSSPrimitiveValue* ident = consumeIdent<CSSValueBlink, CSSValueUnderline, CSSValueOverline, CSSValueLineThrough>(range);
+        if (!ident)
+            break;
+        if (list->hasValue(*ident))
+            return nullptr;
+        list->append(*ident);
+    }
+
+    if (!list->length())
+        return nullptr;
+    return list;
+}
+
+// none | strict | content | [ layout || style || paint || size ]
+static CSSValue* consumeContain(CSSParserTokenRange& range)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueNone)
+        return consumeIdent(range);
+
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    if (id == CSSValueStrict || id == CSSValueContent) {
+        list->append(*consumeIdent(range));
+        return list;
+    }
+    while (true) {
+        CSSPrimitiveValue* ident = consumeIdent<CSSValuePaint, CSSValueLayout, CSSValueStyle, CSSValueSize>(range);
+        if (!ident)
+            break;
+        if (list->hasValue(*ident))
+            return nullptr;
+        list->append(*ident);
+    }
+
+    if (!list->length())
+        return nullptr;
+    return list;
+}
+
+static CSSValue* consumePath(CSSParserTokenRange& range)
+{
+    // FIXME: Add support for <url>, <basic-shape>, <geometry-box>.
+    if (range.peek().functionId() != CSSValuePath)
+        return nullptr;
+
+    CSSParserTokenRange functionRange = range;
+    CSSParserTokenRange functionArgs = consumeFunction(functionRange);
+
+    if (functionArgs.peek().type() != StringToken)
+        return nullptr;
+    String pathString = functionArgs.consumeIncludingWhitespace().value().toString();
+
+    std::unique_ptr<SVGPathByteStream> byteStream = SVGPathByteStream::create();
+    if (buildByteStreamFromString(pathString, *byteStream) != SVGParseStatus::NoError
+        || !functionArgs.atEnd())
+        return nullptr;
+
+    range = functionRange;
+    if (byteStream->isEmpty())
+        return CSSPrimitiveValue::createIdentifier(CSSValueNone);
+    return CSSPathValue::create(std::move(byteStream));
+}
+
+static CSSValue* consumePathOrNone(CSSParserTokenRange& range)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueNone)
+        return consumeIdent(range);
+
+    return consumePath(range);
+}
+
+static CSSValue* consumeMotionRotation(CSSParserTokenRange& range)
+{
+    CSSValue* angle = consumeAngle(range);
+    CSSValue* keyword = consumeIdent<CSSValueAuto, CSSValueReverse>(range);
+    if (!angle && !keyword)
+        return nullptr;
+
+    if (!angle)
+        angle = consumeAngle(range);
+
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    if (keyword)
+        list->append(*keyword);
+    if (angle)
+        list->append(*angle);
+    return list;
+}
+
+static CSSValue* consumeTextEmphasisStyle(CSSParserTokenRange& range)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueNone)
+        return consumeIdent(range);
+
+    if (CSSValue* textEmphasisStyle = consumeString(range))
+        return textEmphasisStyle;
+
+    CSSPrimitiveValue* fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
+    CSSPrimitiveValue* shape = consumeIdent<CSSValueDot, CSSValueCircle, CSSValueDoubleCircle, CSSValueTriangle, CSSValueSesame>(range);
+    if (!fill)
+        fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
+    if (fill && shape) {
+        CSSValueList* parsedValues = CSSValueList::createSpaceSeparated();
+        parsedValues->append(*fill);
+        parsedValues->append(*shape);
+        return parsedValues;
+    }
+    if (fill)
+        return fill;
+    if (shape)
+        return shape;
+    return nullptr;
+}
+
+static CSSValue* consumeOutlineColor(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    // Allow the special focus color even in HTML Standard parsing mode.
+    if (range.peek().id() == CSSValueWebkitFocusRingColor)
+        return consumeIdent(range);
+    return consumeColor(range, cssParserMode);
+}
+
+static CSSPrimitiveValue* consumeLineWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick)
+        return consumeIdent(range);
+    return consumeLength(range, cssParserMode, ValueRangeNonNegative, unitless);
+}
+
+static CSSPrimitiveValue* consumeBorderWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
+{
+    return consumeLineWidth(range, cssParserMode, unitless);
+}
+
+static CSSPrimitiveValue* consumeTextStrokeWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
+}
+
+static CSSPrimitiveValue* consumeColumnRuleWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
+}
+
+static bool consumeTranslate3d(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSFunctionValue*& transformValue)
+{
+    unsigned numberOfArguments = 2;
+    CSSValue* parsedValue = nullptr;
+    do {
+        parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
+        if (!parsedValue)
+            return false;
+        transformValue->append(*parsedValue);
+        if (!consumeCommaIncludingWhitespace(args))
+            return false;
+    } while (--numberOfArguments);
+    parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
+    if (!parsedValue)
+        return false;
+    transformValue->append(*parsedValue);
+    return true;
+}
+
+static bool consumeNumbers(CSSParserTokenRange& args, CSSFunctionValue*& transformValue, unsigned numberOfArguments)
+{
+    do {
+        CSSValue* parsedValue = consumeNumber(args, ValueRangeAll);
+        if (!parsedValue)
+            return false;
+        transformValue->append(*parsedValue);
+        if (--numberOfArguments && !consumeCommaIncludingWhitespace(args))
+            return false;
+    } while (numberOfArguments);
+    return true;
+}
+
+static bool consumePerspective(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSFunctionValue*& transformValue, bool useLegacyParsing)
+{
+    CSSPrimitiveValue* parsedValue = consumeLength(args, cssParserMode, ValueRangeNonNegative);
+    if (!parsedValue && useLegacyParsing) {
+        double perspective;
+        if (!consumeNumberRaw(args, perspective) || perspective < 0)
+            return false;
+        parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitType::Pixels);
+    }
+    if (!parsedValue)
+        return false;
+    transformValue->append(*parsedValue);
+    return true;
+}
+
+static CSSValue* consumeTransformValue(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool useLegacyParsing)
+{
+    CSSValueID functionId = range.peek().functionId();
+    if (functionId == CSSValueInvalid)
+        return nullptr;
+    CSSParserTokenRange args = consumeFunction(range);
+    if (args.atEnd())
+        return nullptr;
+    CSSFunctionValue* transformValue = CSSFunctionValue::create(functionId);
+    CSSValue* parsedValue = nullptr;
+    switch (functionId) {
+    case CSSValueRotate:
+    case CSSValueRotateX:
+    case CSSValueRotateY:
+    case CSSValueRotateZ:
+    case CSSValueSkewX:
+    case CSSValueSkewY:
+    case CSSValueSkew:
+        parsedValue = consumeAngle(args);
+        if (!parsedValue)
+            return nullptr;
+        if (functionId == CSSValueSkew && consumeCommaIncludingWhitespace(args)) {
+            transformValue->append(*parsedValue);
+            parsedValue = consumeAngle(args);
+            if (!parsedValue)
+                return nullptr;
+        }
+        break;
+    case CSSValueScaleX:
+    case CSSValueScaleY:
+    case CSSValueScaleZ:
+    case CSSValueScale:
+        parsedValue = consumeNumber(args, ValueRangeAll);
+        if (!parsedValue)
+            return nullptr;
+        if (functionId == CSSValueScale && consumeCommaIncludingWhitespace(args)) {
+            transformValue->append(*parsedValue);
+            parsedValue = consumeNumber(args, ValueRangeAll);
+            if (!parsedValue)
+                return nullptr;
+        }
+        break;
+    case CSSValuePerspective:
+        if (!consumePerspective(args, cssParserMode, transformValue, useLegacyParsing))
+            return nullptr;
+        break;
+    case CSSValueTranslateX:
+    case CSSValueTranslateY:
+    case CSSValueTranslate:
+        parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
+        if (!parsedValue)
+            return nullptr;
+        if (functionId == CSSValueTranslate && consumeCommaIncludingWhitespace(args)) {
+            transformValue->append(*parsedValue);
+            parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
+            if (!parsedValue)
+                return nullptr;
+        }
+        break;
+    case CSSValueTranslateZ:
+        parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
+        break;
+    case CSSValueMatrix:
+    case CSSValueMatrix3d:
+        if (!consumeNumbers(args, transformValue, (functionId == CSSValueMatrix3d) ? 16 : 6))
+            return nullptr;
+        break;
+    case CSSValueScale3d:
+        if (!consumeNumbers(args, transformValue, 3))
+            return nullptr;
+        break;
+    case CSSValueRotate3d:
+        if (!consumeNumbers(args, transformValue, 3) || !consumeCommaIncludingWhitespace(args))
+            return nullptr;
+        parsedValue = consumeAngle(args);
+        if (!parsedValue)
+            return nullptr;
+        break;
+    case CSSValueTranslate3d:
+        if (!consumeTranslate3d(args, cssParserMode, transformValue))
+            return nullptr;
+        break;
+    default:
+        return nullptr;
+    }
+    if (parsedValue)
+        transformValue->append(*parsedValue);
+    if (!args.atEnd())
+        return nullptr;
+    return transformValue;
+}
+
+static CSSValue* consumeTransform(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool useLegacyParsing)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    do {
+        CSSValue* parsedTransformValue = consumeTransformValue(range, cssParserMode, useLegacyParsing);
+        if (!parsedTransformValue)
+            return nullptr;
+        list->append(*parsedTransformValue);
+    } while (!range.atEnd());
+
+    return list;
+}
+
+template <CSSValueID start, CSSValueID end>
+static CSSValue* consumePositionLonghand(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().type() == IdentToken) {
+        CSSValueID id = range.peek().id();
+        int percent;
+        if (id == start)
+            percent = 0;
+        else if (id == CSSValueCenter)
+            percent = 50;
+        else if (id == end)
+            percent = 100;
+        else
+            return nullptr;
+        range.consumeIncludingWhitespace();
+        return CSSPrimitiveValue::create(percent, CSSPrimitiveValue::UnitType::Percentage);
+    }
+    return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
+}
+
+static CSSValue* consumePositionX(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    return consumePositionLonghand<CSSValueLeft, CSSValueRight>(range, cssParserMode);
+}
+
+static CSSValue* consumePositionY(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    return consumePositionLonghand<CSSValueTop, CSSValueBottom>(range, cssParserMode);
+}
+
+static CSSValue* consumePaintStroke(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    CSSURIValue* url = consumeUrl(range);
+    if (url) {
+        CSSValue* parsedValue = nullptr;
+        if (range.peek().id() == CSSValueNone)
+            parsedValue = consumeIdent(range);
+        else
+            parsedValue = consumeColor(range, cssParserMode);
+        if (parsedValue) {
+            CSSValueList* values = CSSValueList::createSpaceSeparated();
+            values->append(*url);
+            values->append(*parsedValue);
+            return values;
+        }
+        return url;
+    }
+    return consumeColor(range, cssParserMode);
+}
+
+static CSSValue* consumePaintOrder(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueNormal)
+        return consumeIdent(range);
+
+    Vector<CSSValueID, 3> paintTypeList;
+    CSSPrimitiveValue* fill = nullptr;
+    CSSPrimitiveValue* stroke = nullptr;
+    CSSPrimitiveValue* markers = nullptr;
+    do {
+        CSSValueID id = range.peek().id();
+        if (id == CSSValueFill && !fill)
+            fill = consumeIdent(range);
+        else if (id == CSSValueStroke && !stroke)
+            stroke = consumeIdent(range);
+        else if (id == CSSValueMarkers && !markers)
+            markers = consumeIdent(range);
+        else
+            return nullptr;
+        paintTypeList.append(id);
+    } while (!range.atEnd());
+
+    // After parsing we serialize the paint-order list. Since it is not possible to
+    // pop a last list items from CSSValueList without bigger cost, we create the
+    // list after parsing.
+    CSSValueID firstPaintOrderType = paintTypeList.at(0);
+    CSSValueList* paintOrderList = CSSValueList::createSpaceSeparated();
+    switch (firstPaintOrderType) {
+    case CSSValueFill:
+    case CSSValueStroke:
+        paintOrderList->append(firstPaintOrderType == CSSValueFill ? *fill : *stroke);
+        if (paintTypeList.size() > 1) {
+            if (paintTypeList.at(1) == CSSValueMarkers)
+                paintOrderList->append(*markers);
+        }
+        break;
+    case CSSValueMarkers:
+        paintOrderList->append(*markers);
+        if (paintTypeList.size() > 1) {
+            if (paintTypeList.at(1) == CSSValueStroke)
+                paintOrderList->append(*stroke);
+        }
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+    }
+
+    return paintOrderList;
+}
+
+static CSSValue* consumeNoneOrURI(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    return consumeUrl(range);
+}
+
+static CSSValue* consumeFlexBasis(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    // FIXME: Support intrinsic dimensions too.
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+    return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
+}
+
+static CSSValue* consumeStrokeDasharray(CSSParserTokenRange& range)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueNone)
+        return consumeIdent(range);
+
+    CSSValueList* dashes = CSSValueList::createCommaSeparated();
+    do {
+        CSSPrimitiveValue* dash = consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeNonNegative);
+        if (!dash || (consumeCommaIncludingWhitespace(range) && range.atEnd()))
+            return nullptr;
+        dashes->append(*dash);
+    } while (!range.atEnd());
+    return dashes;
+}
+
+static CSSPrimitiveValue* consumeBaselineShift(CSSParserTokenRange& range)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueBaseline || id == CSSValueSub || id == CSSValueSuper)
+        return consumeIdent(range);
+    return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll);
+}
+
+static CSSPrimitiveValue* consumeRxOrRy(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+    return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
+}
+
+static CSSValue* consumeCursor(CSSParserTokenRange& range, const CSSParserContext& context, bool inQuirksMode)
+{
+    CSSValueList* list = nullptr;
+    while (CSSValue* image = consumeImage(range, context, ConsumeGeneratedImage::Forbid)) {
+        double num;
+        IntPoint hotSpot(-1, -1);
+        bool hotSpotSpecified = false;
+        if (consumeNumberRaw(range, num)) {
+            hotSpot.setX(int(num));
+            if (!consumeNumberRaw(range, num))
+                return nullptr;
+            hotSpot.setY(int(num));
+            hotSpotSpecified = true;
+        }
+
+        if (!list)
+            list = CSSValueList::createCommaSeparated();
+
+        list->append(*CSSCursorImageValue::create(image, hotSpotSpecified, hotSpot));
+        if (!consumeCommaIncludingWhitespace(range))
+            return nullptr;
+    }
+
+    CSSValueID id = range.peek().id();
+    if (!range.atEnd() && context.useCounter()) {
+        if (id == CSSValueWebkitZoomIn)
+            context.useCounter()->count(UseCounter::PrefixedCursorZoomIn);
+        else if (id == CSSValueWebkitZoomOut)
+            context.useCounter()->count(UseCounter::PrefixedCursorZoomOut);
+    }
+    CSSValue* cursorType = nullptr;
+    if (id == CSSValueHand) {
+        if (!inQuirksMode) // Non-standard behavior
+            return nullptr;
+        cursorType = CSSPrimitiveValue::createIdentifier(CSSValuePointer);
+        range.consumeIncludingWhitespace();
+    } else if ((id >= CSSValueAuto && id <= CSSValueWebkitZoomOut) || id == CSSValueCopy || id == CSSValueNone) {
+        cursorType = consumeIdent(range);
+    } else {
+        return nullptr;
+    }
+
+    if (!list)
+        return cursorType;
+    list->append(*cursorType);
+    return list;
+}
+
+static CSSValue* consumeAttr(CSSParserTokenRange args, CSSParserContext context)
+{
+    if (args.peek().type() != IdentToken)
+        return nullptr;
+
+    String attrName = args.consumeIncludingWhitespace().value().toString();
+    if (!args.atEnd())
+        return nullptr;
+
+    if (context.isHTMLDocument())
+        attrName = attrName.lower();
+
+    CSSFunctionValue* attrValue = CSSFunctionValue::create(CSSValueAttr);
+    attrValue->append(*CSSCustomIdentValue::create(attrName));
+    return attrValue;
+}
+
+static CSSValue* consumeCounterContent(CSSParserTokenRange args, bool counters)
+{
+    CSSCustomIdentValue* identifier = consumeCustomIdent(args);
+    if (!identifier)
+        return nullptr;
+
+    CSSStringValue* separator = nullptr;
+    if (!counters) {
+        separator = CSSStringValue::create(String());
+    } else {
+        if (!consumeCommaIncludingWhitespace(args) || args.peek().type() != StringToken)
+            return nullptr;
+        separator = CSSStringValue::create(args.consumeIncludingWhitespace().value().toString());
+    }
+
+    CSSPrimitiveValue* listStyle = nullptr;
+    if (consumeCommaIncludingWhitespace(args)) {
+        CSSValueID id = args.peek().id();
+        if ((id != CSSValueNone && (id < CSSValueDisc || id > CSSValueKatakanaIroha)))
+            return nullptr;
+        listStyle = consumeIdent(args);
+    } else {
+        listStyle = CSSPrimitiveValue::createIdentifier(CSSValueDecimal);
+    }
+
+    if (!args.atEnd())
+        return nullptr;
+    return CSSCounterValue::create(identifier, listStyle, separator);
+}
+
+static CSSValue* consumeContent(CSSParserTokenRange& range, CSSParserContext context)
+{
+    if (identMatches<CSSValueNone, CSSValueNormal>(range.peek().id()))
+        return consumeIdent(range);
+
+    CSSValueList* values = CSSValueList::createSpaceSeparated();
+
+    do {
+        CSSValue* parsedValue = consumeImage(range, context);
+        if (!parsedValue)
+            parsedValue = consumeIdent<CSSValueOpenQuote, CSSValueCloseQuote, CSSValueNoOpenQuote, CSSValueNoCloseQuote>(range);
+        if (!parsedValue)
+            parsedValue = consumeString(range);
+        if (!parsedValue) {
+            if (range.peek().functionId() == CSSValueAttr)
+                parsedValue = consumeAttr(consumeFunction(range), context);
+            else if (range.peek().functionId() == CSSValueCounter)
+                parsedValue = consumeCounterContent(consumeFunction(range), false);
+            else if (range.peek().functionId() == CSSValueCounters)
+                parsedValue = consumeCounterContent(consumeFunction(range), true);
+            if (!parsedValue)
+                return nullptr;
+        }
+        values->append(*parsedValue);
+    } while (!range.atEnd());
+
+    return values;
+}
+
+static CSSPrimitiveValue* consumePerspective(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSPropertyID unresolvedProperty)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    CSSPrimitiveValue* parsedValue = consumeLength(range, cssParserMode, ValueRangeAll);
+    if (!parsedValue && (unresolvedProperty == CSSPropertyAliasWebkitPerspective)) {
+        double perspective;
+        if (!consumeNumberRaw(range, perspective))
+            return nullptr;
+        parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitType::Pixels);
+    }
+    if (parsedValue && (parsedValue->isCalculated() || parsedValue->getDoubleValue() > 0))
+        return parsedValue;
+    return nullptr;
+}
+
+static CSSValueList* consumePositionList(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    CSSValueList* positions = CSSValueList::createCommaSeparated();
+    do {
+        CSSValue* position = consumePosition(range, cssParserMode, UnitlessQuirk::Forbid);
+        if (!position)
+            return nullptr;
+        positions->append(*position);
+    } while (consumeCommaIncludingWhitespace(range));
+    return positions;
+}
+
+static CSSValue* consumeScrollSnapCoordinate(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    return consumePositionList(range, cssParserMode);
+}
+
+static CSSValue* consumeScrollSnapPoints(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    if (range.peek().functionId() == CSSValueRepeat) {
+        CSSParserTokenRange args = consumeFunction(range);
+        CSSPrimitiveValue* parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
+        if (args.atEnd() && parsedValue && (parsedValue->isCalculated() || parsedValue->getDoubleValue() > 0)) {
+            CSSFunctionValue* result = CSSFunctionValue::create(CSSValueRepeat);
+            result->append(*parsedValue);
+            return result;
+        }
+    }
+    return nullptr;
+}
+
+static CSSValue* consumeBorderRadiusCorner(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    CSSValue* parsedValue1 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
+    if (!parsedValue1)
+        return nullptr;
+    CSSValue* parsedValue2 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
+    if (!parsedValue2)
+        parsedValue2 = parsedValue1;
+    return CSSValuePair::create(parsedValue1, parsedValue2, CSSValuePair::DropIdenticalValues);
+}
+
+static CSSPrimitiveValue* consumeVerticalAlign(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    CSSPrimitiveValue* parsedValue = consumeIdentRange(range, CSSValueBaseline, CSSValueWebkitBaselineMiddle);
+    if (!parsedValue)
+        parsedValue = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
+    return parsedValue;
+}
+
+static CSSPrimitiveValue* consumeShapeRadius(CSSParserTokenRange& args, CSSParserMode cssParserMode)
+{
+    if (identMatches<CSSValueClosestSide, CSSValueFarthestSide>(args.peek().id()))
+        return consumeIdent(args);
+    return consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
+}
+
+static CSSBasicShapeCircleValue* consumeBasicShapeCircle(CSSParserTokenRange& args, const CSSParserContext& context)
+{
+    // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
+    // circle( [<shape-radius>]? [at <position>]? )
+    CSSBasicShapeCircleValue* shape = CSSBasicShapeCircleValue::create();
+    if (CSSPrimitiveValue* radius = consumeShapeRadius(args, context.mode()))
+        shape->setRadius(radius);
+    if (consumeIdent<CSSValueAt>(args)) {
+        CSSValue* centerX = nullptr;
+        CSSValue* centerY = nullptr;
+        if (!consumePosition(args, context.mode(), UnitlessQuirk::Forbid, centerX, centerY))
+            return nullptr;
+        shape->setCenterX(centerX);
+        shape->setCenterY(centerY);
+    }
+    return shape;
+}
+
+static CSSBasicShapeEllipseValue* consumeBasicShapeEllipse(CSSParserTokenRange& args, const CSSParserContext& context)
+{
+    // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
+    // ellipse( [<shape-radius>{2}]? [at <position>]? )
+    CSSBasicShapeEllipseValue* shape = CSSBasicShapeEllipseValue::create();
+    if (CSSPrimitiveValue* radiusX = consumeShapeRadius(args, context.mode())) {
+        shape->setRadiusX(radiusX);
+        if (CSSPrimitiveValue* radiusY = consumeShapeRadius(args, context.mode()))
+            shape->setRadiusY(radiusY);
+    }
+    if (consumeIdent<CSSValueAt>(args)) {
+        CSSValue* centerX = nullptr;
+        CSSValue* centerY = nullptr;
+        if (!consumePosition(args, context.mode(), UnitlessQuirk::Forbid, centerX, centerY))
+            return nullptr;
+        shape->setCenterX(centerX);
+        shape->setCenterY(centerY);
+    }
+    return shape;
+}
+
+static CSSBasicShapePolygonValue* consumeBasicShapePolygon(CSSParserTokenRange& args, const CSSParserContext& context)
+{
+    CSSBasicShapePolygonValue* shape = CSSBasicShapePolygonValue::create();
+    if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
+        shape->setWindRule(args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? RULE_EVENODD : RULE_NONZERO);
+        if (!consumeCommaIncludingWhitespace(args))
+            return nullptr;
+    }
+
+    do {
+        CSSPrimitiveValue* xLength = consumeLengthOrPercent(args, context.mode(), ValueRangeAll);
+        if (!xLength)
+            return nullptr;
+        CSSPrimitiveValue* yLength = consumeLengthOrPercent(args, context.mode(), ValueRangeAll);
+        if (!yLength)
+            return nullptr;
+        shape->appendPoint(xLength, yLength);
+    } while (consumeCommaIncludingWhitespace(args));
+    return shape;
+}
+
+static void complete4Sides(CSSPrimitiveValue* side[4])
+{
+    if (side[3])
+        return;
+    if (!side[2]) {
+        if (!side[1])
+            side[1] = side[0];
+        side[2] = side[0];
+    }
+    side[3] = side[1];
+}
+
+static bool consumeRadii(CSSPrimitiveValue* horizontalRadii[4], CSSPrimitiveValue* verticalRadii[4], CSSParserTokenRange& range, CSSParserMode cssParserMode, bool useLegacyParsing)
+{
+    unsigned i = 0;
+    for (; i < 4 && !range.atEnd() && range.peek().type() != DelimiterToken; ++i) {
+        horizontalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
+        if (!horizontalRadii[i])
+            return false;
+    }
+    if (!horizontalRadii[0])
+        return false;
+    if (range.atEnd()) {
+        // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2;
+        if (useLegacyParsing && i == 2) {
+            verticalRadii[0] = horizontalRadii[1];
+            horizontalRadii[1] = nullptr;
+        } else {
+            complete4Sides(horizontalRadii);
+            for (unsigned i = 0; i < 4; ++i)
+                verticalRadii[i] = horizontalRadii[i];
+            return true;
+        }
+    } else {
+        if (!consumeSlashIncludingWhitespace(range))
+            return false;
+        for (i = 0; i < 4 && !range.atEnd(); ++i) {
+            verticalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
+            if (!verticalRadii[i])
+                return false;
+        }
+        if (!verticalRadii[0] || !range.atEnd())
+            return false;
+    }
+    complete4Sides(horizontalRadii);
+    complete4Sides(verticalRadii);
+    return true;
+}
+
+static CSSBasicShapeInsetValue* consumeBasicShapeInset(CSSParserTokenRange& args, const CSSParserContext& context)
+{
+    CSSBasicShapeInsetValue* shape = CSSBasicShapeInsetValue::create();
+    CSSPrimitiveValue* top = consumeLengthOrPercent(args, context.mode(), ValueRangeAll);
+    if (!top)
+        return nullptr;
+    CSSPrimitiveValue* right = consumeLengthOrPercent(args, context.mode(), ValueRangeAll);
+    CSSPrimitiveValue* bottom = nullptr;
+    CSSPrimitiveValue* left = nullptr;
+    if (right) {
+        bottom = consumeLengthOrPercent(args, context.mode(), ValueRangeAll);
+        if (bottom)
+            left = consumeLengthOrPercent(args, context.mode(), ValueRangeAll);
+    }
+    if (left)
+        shape->updateShapeSize4Values(top, right, bottom, left);
+    else if (bottom)
+        shape->updateShapeSize3Values(top, right, bottom);
+    else if (right)
+        shape->updateShapeSize2Values(top, right);
+    else
+        shape->updateShapeSize1Value(top);
+
+    if (consumeIdent<CSSValueRound>(args)) {
+        CSSPrimitiveValue* horizontalRadii[4] = { 0 };
+        CSSPrimitiveValue* verticalRadii[4] = { 0 };
+        if (!consumeRadii(horizontalRadii, verticalRadii, args, context.mode(), false))
+            return nullptr;
+        shape->setTopLeftRadius(CSSValuePair::create(horizontalRadii[0], verticalRadii[0], CSSValuePair::DropIdenticalValues));
+        shape->setTopRightRadius(CSSValuePair::create(horizontalRadii[1], verticalRadii[1], CSSValuePair::DropIdenticalValues));
+        shape->setBottomRightRadius(CSSValuePair::create(horizontalRadii[2], verticalRadii[2], CSSValuePair::DropIdenticalValues));
+        shape->setBottomLeftRadius(CSSValuePair::create(horizontalRadii[3], verticalRadii[3], CSSValuePair::DropIdenticalValues));
+    }
+    return shape;
+}
+
+static CSSValue* consumeBasicShape(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    CSSValue* shape = nullptr;
+    if (range.peek().type() != FunctionToken)
+        return nullptr;
+    CSSValueID id = range.peek().functionId();
+    CSSParserTokenRange rangeCopy = range;
+    CSSParserTokenRange args = consumeFunction(rangeCopy);
+    if (id == CSSValueCircle)
+        shape = consumeBasicShapeCircle(args, context);
+    else if (id == CSSValueEllipse)
+        shape = consumeBasicShapeEllipse(args, context);
+    else if (id == CSSValuePolygon)
+        shape = consumeBasicShapePolygon(args, context);
+    else if (id == CSSValueInset)
+        shape = consumeBasicShapeInset(args, context);
+    if (!shape || !args.atEnd())
+        return nullptr;
+    range = rangeCopy;
+    return shape;
+}
+
+static CSSValue* consumeWebkitClipPath(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    if (CSSURIValue* url = consumeUrl(range))
+        return url;
+    return consumeBasicShape(range, context);
+}
+
+static CSSValue* consumeShapeOutside(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    if (CSSValue* imageValue = consumeImageOrNone(range, context))
+        return imageValue;
+    CSSValueList* list = CSSValueList::createSpaceSeparated();
+    if (CSSValue* boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
+        list->append(*boxValue);
+    if (CSSValue* shapeValue = consumeBasicShape(range, context)) {
+        list->append(*shapeValue);
+        if (list->length() < 2) {
+            if (CSSValue* boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
+                list->append(*boxValue);
+        }
+    }
+    if (!list->length())
+        return nullptr;
+    return list;
+}
+
+static CSSValue* consumeContentDistributionOverflowPosition(CSSParserTokenRange& range)
+{
+    if (identMatches<CSSValueNormal, CSSValueBaseline, CSSValueLastBaseline>(range.peek().id()))
+        return CSSContentDistributionValue::create(CSSValueInvalid, range.consumeIncludingWhitespace().id(), CSSValueInvalid);
+
+    CSSValueID distribution = CSSValueInvalid;
+    CSSValueID position = CSSValueInvalid;
+    CSSValueID overflow = CSSValueInvalid;
+    do {
+        CSSValueID id = range.peek().id();
+        if (identMatches<CSSValueSpaceBetween, CSSValueSpaceAround, CSSValueSpaceEvenly, CSSValueStretch>(id)) {
+            if (distribution != CSSValueInvalid)
+                return nullptr;
+            distribution = id;
+        } else if (identMatches<CSSValueStart, CSSValueEnd, CSSValueCenter, CSSValueFlexStart, CSSValueFlexEnd, CSSValueLeft, CSSValueRight>(id)) {
+            if (position != CSSValueInvalid)
+                return nullptr;
+            position = id;
+        } else if (identMatches<CSSValueUnsafe, CSSValueSafe>(id)) {
+            if (overflow != CSSValueInvalid)
+                return nullptr;
+            overflow = id;
+        } else {
+            return nullptr;
+        }
+        range.consumeIncludingWhitespace();
+    } while (!range.atEnd());
+
+    // The grammar states that we should have at least <content-distribution> or <content-position>.
+    if (position == CSSValueInvalid && distribution == CSSValueInvalid)
+        return nullptr;
+
+    // The grammar states that <overflow-position> must be associated to <content-position>.
+    if (overflow != CSSValueInvalid && position == CSSValueInvalid)
+        return nullptr;
+
+    return CSSContentDistributionValue::create(distribution, position, overflow);
+}
+
+static CSSPrimitiveValue* consumeBorderImageRepeatKeyword(CSSParserTokenRange& range)
+{
+    return consumeIdent<CSSValueStretch, CSSValueRepeat, CSSValueSpace, CSSValueRound>(range);
+}
+
+static CSSValue* consumeBorderImageRepeat(CSSParserTokenRange& range)
+{
+    CSSPrimitiveValue* horizontal = consumeBorderImageRepeatKeyword(range);
+    if (!horizontal)
+        return nullptr;
+    CSSPrimitiveValue* vertical = consumeBorderImageRepeatKeyword(range);
+    if (!vertical)
+        vertical = horizontal;
+    return CSSValuePair::create(horizontal, vertical, CSSValuePair::DropIdenticalValues);
+}
+
+static CSSValue* consumeBorderImageSlice(CSSPropertyID property, CSSParserTokenRange& range)
+{
+    bool fill = consumeIdent<CSSValueFill>(range);
+    CSSPrimitiveValue* slices[4] = { 0 };
+
+    for (size_t index = 0; index < 4; ++index) {
+        CSSPrimitiveValue* value = consumePercent(range, ValueRangeNonNegative);
+        if (!value)
+            value = consumeNumber(range, ValueRangeNonNegative);
+        if (!value)
+            break;
+        slices[index] = value;
+    }
+    if (!slices[0])
+        return nullptr;
+    if (consumeIdent<CSSValueFill>(range)) {
+        if (fill)
+            return nullptr;
+        fill = true;
+    }
+    complete4Sides(slices);
+    // FIXME: For backwards compatibility, -webkit-border-image, -webkit-mask-box-image and -webkit-box-reflect have to do a fill by default.
+    // FIXME: What do we do with -webkit-box-reflect and -webkit-mask-box-image? Probably just have to leave them filling...
+    if (property == CSSPropertyWebkitBorderImage || property == CSSPropertyWebkitMaskBoxImage || property == CSSPropertyWebkitBoxReflect)
+        fill = true;
+    return CSSBorderImageSliceValue::create(CSSQuadValue::create(slices[0], slices[1], slices[2], slices[3], CSSQuadValue::SerializeAsQuad), fill);
+}
+
+static CSSValue* consumeBorderImageOutset(CSSParserTokenRange& range)
+{
+    CSSPrimitiveValue* outsets[4] = { 0 };
+
+    CSSPrimitiveValue* value = nullptr;
+    for (size_t index = 0; index < 4; ++index) {
+        value = consumeNumber(range, ValueRangeNonNegative);
+        if (!value)
+            value = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
+        if (!value)
+            break;
+        outsets[index] = value;
+    }
+    if (!outsets[0])
+        return nullptr;
+    complete4Sides(outsets);
+    return CSSQuadValue::create(outsets[0], outsets[1], outsets[2], outsets[3], CSSQuadValue::SerializeAsQuad);
+}
+
+static CSSValue* consumeBorderImageWidth(CSSParserTokenRange& range)
+{
+    CSSPrimitiveValue* widths[4] = { 0 };
+
+    CSSPrimitiveValue* value = nullptr;
+    for (size_t index = 0; index < 4; ++index) {
+        value = consumeNumber(range, ValueRangeNonNegative);
+        if (!value)
+            value = consumeLengthOrPercent(range, HTMLStandardMode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
+        if (!value)
+            value = consumeIdent<CSSValueAuto>(range);
+        if (!value)
+            break;
+        widths[index] = value;
+    }
+    if (!widths[0])
+        return nullptr;
+    complete4Sides(widths);
+    return CSSQuadValue::create(widths[0], widths[1], widths[2], widths[3], CSSQuadValue::SerializeAsQuad);
+}
+
+static bool consumeBorderImageComponents(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context, CSSValue*& source,
+    CSSValue*& slice, CSSValue*& width, CSSValue*& outset, CSSValue*& repeat)
+{
+    do {
+        if (!source) {
+            source = consumeImageOrNone(range, context);
+            if (source)
+                continue;
+        }
+        if (!repeat) {
+            repeat = consumeBorderImageRepeat(range);
+            if (repeat)
+                continue;
+        }
+        if (!slice) {
+            slice = consumeBorderImageSlice(property, range);
+            if (slice) {
+                ASSERT(!width && !outset);
+                if (consumeSlashIncludingWhitespace(range)) {
+                    width = consumeBorderImageWidth(range);
+                    if (consumeSlashIncludingWhitespace(range)) {
+                        outset = consumeBorderImageOutset(range);
+                        if (!outset)
+                            return false;
+                    } else if (!width) {
+                        return false;
+                    }
+                }
+            } else {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    } while (!range.atEnd());
+    return true;
+}
+
+static CSSValue* consumeWebkitBorderImage(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    CSSValue* source = nullptr;
+    CSSValue* slice = nullptr;
+    CSSValue* width = nullptr;
+    CSSValue* outset = nullptr;
+    CSSValue* repeat = nullptr;
+    if (consumeBorderImageComponents(property, range, context, source, slice, width, outset, repeat))
+        return createBorderImageValue(source, slice, width, outset, repeat);
+    return nullptr;
+}
+
+static CSSValue* consumeReflect(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    CSSPrimitiveValue* direction = consumeIdent<CSSValueAbove, CSSValueBelow, CSSValueLeft, CSSValueRight>(range);
+    if (!direction)
+        return nullptr;
+
+    CSSPrimitiveValue* offset = nullptr;
+    if (range.atEnd()) {
+        offset = CSSPrimitiveValue::create(0, CSSPrimitiveValue::UnitType::Pixels);
+    } else {
+        offset = consumeLengthOrPercent(range, context.mode(), ValueRangeAll, UnitlessQuirk::Forbid);
+        if (!offset)
+            return nullptr;
+    }
+
+    CSSValue* mask = nullptr;
+    if (!range.atEnd()) {
+        mask = consumeWebkitBorderImage(CSSPropertyWebkitBoxReflect, range, context);
+        if (!mask)
+            return nullptr;
+    }
+    return CSSReflectValue::create(direction, offset, mask);
+}
+
+static CSSValue* consumeFontSizeAdjust(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    return consumeNumber(range, ValueRangeNonNegative);
+}
+
+static CSSValue* consumeImageOrientation(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueFromImage)
+        return consumeIdent(range);
+    if (range.peek().type() != NumberToken) {
+        CSSPrimitiveValue* angle = consumeAngle(range);
+        if (angle && angle->getDoubleValue() == 0)
+            return angle;
+    }
+    return nullptr;
+}
+
+static CSSValue* consumeBackgroundBlendMode(CSSParserTokenRange& range)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueNormal || id == CSSValueOverlay || (id >= CSSValueMultiply && id <= CSSValueLuminosity))
+        return consumeIdent(range);
+    return nullptr;
+}
+
+static CSSValue* consumeBackgroundAttachment(CSSParserTokenRange& range)
+{
+    return consumeIdent<CSSValueScroll, CSSValueFixed, CSSValueLocal>(range);
+}
+
+static CSSValue* consumeBackgroundBox(CSSParserTokenRange& range)
+{
+    return consumeIdent<CSSValueBorderBox, CSSValuePaddingBox, CSSValueContentBox>(range);
+}
+
+static CSSValue* consumeBackgroundComposite(CSSParserTokenRange& range)
+{
+    return consumeIdentRange(range, CSSValueClear, CSSValuePlusLighter);
+}
+
+static CSSValue* consumeMaskSourceType(CSSParserTokenRange& range)
+{
+    ASSERT(RuntimeEnabledFeatures::cssMaskSourceTypeEnabled());
+    return consumeIdent<CSSValueAuto, CSSValueAlpha, CSSValueLuminance>(range);
+}
+
+static CSSValue* consumePrefixedBackgroundBox(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    // The values 'border', 'padding' and 'content' are deprecated and do not apply to the version of the property that has the -webkit- prefix removed.
+    if (CSSValue* value = consumeIdentRange(range, CSSValueBorder, CSSValuePaddingBox))
+        return value;
+    if ((property == CSSPropertyWebkitBackgroundClip || property == CSSPropertyWebkitMaskClip) && range.peek().id() == CSSValueText)
+        return consumeIdent(range);
+    return nullptr;
+}
+
+static CSSValue* consumeBackgroundSize(CSSPropertyID unresolvedProperty, CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (identMatches<CSSValueContain, CSSValueCover>(range.peek().id()))
+        return consumeIdent(range);
+
+    CSSPrimitiveValue* horizontal = consumeIdent<CSSValueAuto>(range);
+    if (!horizontal)
+        horizontal = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid);
+
+    CSSPrimitiveValue* vertical = nullptr;
+    if (!range.atEnd()) {
+        if (range.peek().id() == CSSValueAuto) // `auto' is the default
+            range.consumeIncludingWhitespace();
+        else
+            vertical = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid);
+    } else if (unresolvedProperty == CSSPropertyAliasWebkitBackgroundSize) {
+        // Legacy syntax: "-webkit-background-size: 10px" is equivalent to "background-size: 10px 10px".
+        vertical = horizontal;
+    }
+    if (!vertical)
+        return horizontal;
+    return CSSValuePair::create(horizontal, vertical, CSSValuePair::KeepIdenticalValues);
+}
+
+static CSSValueList* consumeGridAutoFlow(CSSParserTokenRange& range)
+{
+    CSSPrimitiveValue* rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
+    CSSPrimitiveValue* denseAlgorithm = consumeIdent<CSSValueDense>(range);
+    if (!rowOrColumnValue) {
+        rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
+        if (!rowOrColumnValue && !denseAlgorithm)
+            return nullptr;
+    }
+    CSSValueList* parsedValues = CSSValueList::createSpaceSeparated();
+    if (rowOrColumnValue)
+        parsedValues->append(*rowOrColumnValue);
+    if (denseAlgorithm)
+        parsedValues->append(*denseAlgorithm);
+    return parsedValues;
+}
+
+static CSSValue* consumeBackgroundComponent(CSSPropertyID unresolvedProperty, CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    switch (unresolvedProperty) {
+    case CSSPropertyBackgroundClip:
+        return consumeBackgroundBox(range);
+    case CSSPropertyBackgroundBlendMode:
+        return consumeBackgroundBlendMode(range);
+    case CSSPropertyBackgroundAttachment:
+        return consumeBackgroundAttachment(range);
+    case CSSPropertyBackgroundOrigin:
+        return consumeBackgroundBox(range);
+    case CSSPropertyWebkitMaskComposite:
+        return consumeBackgroundComposite(range);
+    case CSSPropertyMaskSourceType:
+        return consumeMaskSourceType(range);
+    case CSSPropertyWebkitBackgroundClip:
+    case CSSPropertyWebkitBackgroundOrigin:
+    case CSSPropertyWebkitMaskClip:
+    case CSSPropertyWebkitMaskOrigin:
+        return consumePrefixedBackgroundBox(unresolvedProperty, range, context);
+    case CSSPropertyBackgroundImage:
+    case CSSPropertyWebkitMaskImage:
+        return consumeImageOrNone(range, context);
+    case CSSPropertyBackgroundPositionX:
+    case CSSPropertyWebkitMaskPositionX:
+        return consumePositionX(range, context.mode());
+    case CSSPropertyBackgroundPositionY:
+    case CSSPropertyWebkitMaskPositionY:
+        return consumePositionY(range, context.mode());
+    case CSSPropertyBackgroundSize:
+    case CSSPropertyAliasWebkitBackgroundSize:
+    case CSSPropertyWebkitMaskSize:
+        return consumeBackgroundSize(unresolvedProperty, range, context.mode());
+    case CSSPropertyBackgroundColor:
+        return consumeColor(range, context.mode());
+    default:
+        break;
+    };
+    return nullptr;
+}
+
+static void addBackgroundValue(CSSValue*& list, CSSValue* value)
+{
+    if (list) {
+        if (!list->isBaseValueList()) {
+            CSSValue* firstValue = list;
+            list = CSSValueList::createCommaSeparated();
+            toCSSValueList(list)->append(*firstValue);
+        }
+        toCSSValueList(list)->append(*value);
+    } else {
+        // To conserve memory we don't actually wrap a single value in a list.
+        list = value;
+    }
+}
+
+static CSSValue* consumeCommaSeparatedBackgroundComponent(CSSPropertyID unresolvedProperty, CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    CSSValue* result = nullptr;
+    do {
+        CSSValue* value = consumeBackgroundComponent(unresolvedProperty, range, context);
+        if (!value)
+            return nullptr;
+        addBackgroundValue(result, value);
+    } while (consumeCommaIncludingWhitespace(range));
+    return result;
+}
+
+static CSSPrimitiveValue* consumeSelfPositionKeyword(CSSParserTokenRange& range)
+{
+    CSSValueID id = range.peek().id();
+    if (id == CSSValueStart || id == CSSValueEnd || id == CSSValueCenter
+        || id == CSSValueSelfStart || id == CSSValueSelfEnd || id == CSSValueFlexStart
+        || id == CSSValueFlexEnd || id == CSSValueLeft || id == CSSValueRight)
+        return consumeIdent(range);
+    return nullptr;
+}
+
+static CSSValue* consumeSelfPositionOverflowPosition(CSSParserTokenRange& range)
+{
+    if (identMatches<CSSValueAuto, CSSValueNormal, CSSValueStretch, CSSValueBaseline, CSSValueLastBaseline>(range.peek().id()))
+        return consumeIdent(range);
+
+    CSSPrimitiveValue* overflowPosition = consumeIdent<CSSValueUnsafe, CSSValueSafe>(range);
+    CSSPrimitiveValue* selfPosition = consumeSelfPositionKeyword(range);
+    if (!selfPosition)
+        return nullptr;
+    if (!overflowPosition)
+        overflowPosition = consumeIdent<CSSValueUnsafe, CSSValueSafe>(range);
+    if (overflowPosition)
+        return CSSValuePair::create(selfPosition, overflowPosition, CSSValuePair::DropIdenticalValues);
+    return selfPosition;
+}
+
+static CSSValue* consumeAlignItems(CSSParserTokenRange& range)
+{
+    // align-items property does not allow the 'auto' value.
+    if (identMatches<CSSValueAuto>(range.peek().id()))
+        return nullptr;
+    return consumeSelfPositionOverflowPosition(range);
+}
+
+static CSSValue* consumeJustifyItems(CSSParserTokenRange& range)
+{
+    CSSParserTokenRange rangeCopy = range;
+    CSSPrimitiveValue* legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
+    CSSPrimitiveValue* positionKeyword = consumeIdent<CSSValueCenter, CSSValueLeft, CSSValueRight>(rangeCopy);
+    if (!legacy)
+        legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
+    if (legacy && positionKeyword) {
+        range = rangeCopy;
+        return CSSValuePair::create(legacy, positionKeyword, CSSValuePair::DropIdenticalValues);
+    }
+    return consumeSelfPositionOverflowPosition(range);
+}
+
+static CSSCustomIdentValue* consumeCustomIdentForGridLine(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueAuto || range.peek().id() == CSSValueSpan)
+        return nullptr;
+    return consumeCustomIdent(range);
+}
+
+static CSSValue* consumeGridLine(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueAuto)
+        return consumeIdent(range);
+
+    CSSPrimitiveValue* spanValue = nullptr;
+    CSSCustomIdentValue* gridLineName = nullptr;
+    CSSPrimitiveValue* numericValue = consumeInteger(range);
+    if (numericValue) {
+        gridLineName = consumeCustomIdentForGridLine(range);
+        spanValue = consumeIdent<CSSValueSpan>(range);
+    } else {
+        spanValue = consumeIdent<CSSValueSpan>(range);
+        if (spanValue) {
+            numericValue = consumeInteger(range);
+            gridLineName = consumeCustomIdentForGridLine(range);
+            if (!numericValue)
+                numericValue = consumeInteger(range);
+        } else {
+            gridLineName = consumeCustomIdentForGridLine(range);
+            if (gridLineName) {
+                numericValue = consumeInteger(range);
+                spanValue = consumeIdent<CSSValueSpan>(range);
+                if (!spanValue && !numericValue)
+                    return gridLineName;
+            } else {
+                return nullptr;
+            }
+        }
+    }
+
+    if (spanValue && !numericValue && !gridLineName)
+        return nullptr; // "span" keyword alone is invalid.
+    if (spanValue && numericValue && numericValue->getIntValue() < 0)
+        return nullptr; // Negative numbers are not allowed for span.
+    if (numericValue && numericValue->getIntValue() == 0)
+        return nullptr; // An <integer> value of zero makes the declaration invalid.
+
+    CSSValueList* values = CSSValueList::createSpaceSeparated();
+    if (spanValue)
+        values->append(*spanValue);
+    if (numericValue)
+        values->append(*numericValue);
+    if (gridLineName)
+        values->append(*gridLineName);
+    ASSERT(values->length());
+    return values;
+}
+
+static bool isGridTrackFixedSized(const CSSPrimitiveValue& primitiveValue)
+{
+    CSSValueID valueID = primitiveValue.getValueID();
+    if (valueID == CSSValueMinContent || valueID == CSSValueMaxContent || valueID == CSSValueAuto || primitiveValue.isFlex())
+        return false;
+
+    return true;
+}
+
+static bool isGridTrackFixedSized(const CSSValue& value)
+{
+    if (value.isPrimitiveValue())
+        return isGridTrackFixedSized(toCSSPrimitiveValue(value));
+
+    const CSSPrimitiveValue& minPrimitiveValue = toCSSPrimitiveValue(toCSSFunctionValue(value).item(0));
+    const CSSPrimitiveValue& maxPrimitiveValue = toCSSPrimitiveValue(toCSSFunctionValue(value).item(1));
+    return isGridTrackFixedSized(minPrimitiveValue) || isGridTrackFixedSized(maxPrimitiveValue);
+}
+
+static Vector<String> parseGridTemplateAreasColumnNames(const String& gridRowNames)
+{
+    ASSERT(!gridRowNames.isEmpty());
+    Vector<String> columnNames;
+    // Using StringImpl to avoid checks and indirection in every call to String::operator[].
+    StringImpl& text = *gridRowNames.impl();
+
+    StringBuilder areaName;
+    for (unsigned i = 0; i < text.length(); ++i) {
+        if (isCSSSpace(text[i])) {
+            if (!areaName.isEmpty()) {
+                columnNames.append(areaName.toString());
+                areaName.clear();
+            }
+            continue;
+        }
+        if (text[i] == '.') {
+            if (areaName == ".")
+                continue;
+            if (!areaName.isEmpty()) {
+                columnNames.append(areaName.toString());
+                areaName.clear();
+            }
+        } else {
+            if (!isNameCodePoint(text[i]))
+                return Vector<String>();
+            if (areaName == ".") {
+                columnNames.append(areaName.toString());
+                areaName.clear();
+            }
+        }
+
+        areaName.append(text[i]);
+    }
+
+    if (!areaName.isEmpty())
+        columnNames.append(areaName.toString());
+
+    return columnNames;
+}
+
+static bool parseGridTemplateAreasRow(const String& gridRowNames, NamedGridAreaMap& gridAreaMap, const size_t rowCount, size_t& columnCount)
+{
+    if (gridRowNames.isEmpty() || gridRowNames.containsOnlyWhitespace())
+        return false;
+
+    Vector<String> columnNames = parseGridTemplateAreasColumnNames(gridRowNames);
+    if (rowCount == 0) {
+        columnCount = columnNames.size();
+        if (columnCount == 0)
+            return false;
+    } else if (columnCount != columnNames.size()) {
+        // The declaration is invalid if all the rows don't have the number of columns.
+        return false;
+    }
+
+    for (size_t currentColumn = 0; currentColumn < columnCount; ++currentColumn) {
+        const String& gridAreaName = columnNames[currentColumn];
+
+        // Unamed areas are always valid (we consider them to be 1x1).
+        if (gridAreaName == ".")
+            continue;
+
+        size_t lookAheadColumn = currentColumn + 1;
+        while (lookAheadColumn < columnCount && columnNames[lookAheadColumn] == gridAreaName)
+            lookAheadColumn++;
+
+        NamedGridAreaMap::iterator gridAreaIt = gridAreaMap.find(gridAreaName);
+        if (gridAreaIt == gridAreaMap.end()) {
+            gridAreaMap.add(gridAreaName, GridArea(GridSpan::translatedDefiniteGridSpan(rowCount, rowCount + 1), GridSpan::translatedDefiniteGridSpan(currentColumn, lookAheadColumn)));
+        } else {
+            GridArea& gridArea = gridAreaIt->value;
+
+            // The following checks test that the grid area is a single filled-in rectangle.
+            // 1. The new row is adjacent to the previously parsed row.
+            if (rowCount != gridArea.rows.endLine())
+                return false;
+
+            // 2. The new area starts at the same position as the previously parsed area.
+            if (currentColumn != gridArea.columns.startLine())
+                return false;
+
+            // 3. The new area ends at the same position as the previously parsed area.
+            if (lookAheadColumn != gridArea.columns.endLine())
+                return false;
+
+            gridArea.rows = GridSpan::translatedDefiniteGridSpan(gridArea.rows.startLine(), gridArea.rows.endLine() + 1);
+        }
+        currentColumn = lookAheadColumn - 1;
+    }
+
+    return true;
+}
+
+static CSSPrimitiveValue* consumeGridBreadth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    const CSSParserToken& token = range.peek();
+    if (identMatches<CSSValueMinContent, CSSValueMaxContent, CSSValueAuto>(token.id()))
+        return consumeIdent(range);
+    if (token.type() == DimensionToken && token.unitType() == CSSPrimitiveValue::UnitType::Fraction) {
+        if (range.peek().numericValue() < 0)
+            return nullptr;
+        return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::Fraction);
+    }
+    return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
+}
+
+static CSSValue* consumeGridTrackSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    const CSSParserToken& token = range.peek();
+    if (identMatches<CSSValueAuto>(token.id()))
+        return consumeIdent(range);
+
+    if (token.functionId() == CSSValueMinmax) {
+        CSSParserTokenRange rangeCopy = range;
+        CSSParserTokenRange args = consumeFunction(rangeCopy);
+        CSSPrimitiveValue* minTrackBreadth = consumeGridBreadth(args, cssParserMode);
+        if (!minTrackBreadth || minTrackBreadth->isFlex() || !consumeCommaIncludingWhitespace(args))
+            return nullptr;
+        CSSPrimitiveValue* maxTrackBreadth = consumeGridBreadth(args, cssParserMode);
+        if (!maxTrackBreadth || !args.atEnd())
+            return nullptr;
+        range = rangeCopy;
+        CSSFunctionValue* result = CSSFunctionValue::create(CSSValueMinmax);
+        result->append(*minTrackBreadth);
+        result->append(*maxTrackBreadth);
+        return result;
+    }
+    return consumeGridBreadth(range, cssParserMode);
+}
+
+// Appends to the passed in CSSGridLineNamesValue if any, otherwise creates a new one.
+static CSSGridLineNamesValue* consumeGridLineNames(CSSParserTokenRange& range, CSSGridLineNamesValue* lineNames = nullptr)
+{
+    CSSParserTokenRange rangeCopy = range;
+    if (rangeCopy.consumeIncludingWhitespace().type() != LeftBracketToken)
+        return nullptr;
+    if (!lineNames)
+        lineNames = CSSGridLineNamesValue::create();
+    while (CSSCustomIdentValue* lineName = consumeCustomIdentForGridLine(rangeCopy))
+        lineNames->append(*lineName);
+    if (rangeCopy.consumeIncludingWhitespace().type() != RightBracketToken)
+        return nullptr;
+    range = rangeCopy;
+    return lineNames;
+}
+
+static bool consumeGridTrackRepeatFunction(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSValueList& list, bool& isAutoRepeat, bool& allTracksAreFixedSized)
+{
+    CSSParserTokenRange args = consumeFunction(range);
+    // The number of repetitions for <auto-repeat> is not important at parsing level
+    // because it will be computed later, let's set it to 1.
+    size_t repetitions = 1;
+    isAutoRepeat = identMatches<CSSValueAutoFill, CSSValueAutoFit>(args.peek().id());
+    CSSValueList* repeatedValues;
+    if (isAutoRepeat) {
+        repeatedValues = CSSGridAutoRepeatValue::create(args.consumeIncludingWhitespace().id());
+    } else {
+        // FIXME: a consumeIntegerRaw would be more efficient here.
+        CSSPrimitiveValue* repetition = consumePositiveInteger(args);
+        if (!repetition)
+            return false;
+        repetitions = clampTo<size_t>(repetition->getDoubleValue(), 0, kGridMaxTracks);
+        repeatedValues = CSSValueList::createSpaceSeparated();
+    }
+    if (!consumeCommaIncludingWhitespace(args))
+        return false;
+    CSSGridLineNamesValue* lineNames = consumeGridLineNames(args);
+    if (lineNames)
+        repeatedValues->append(*lineNames);
+
+    size_t numberOfTracks = 0;
+    while (!args.atEnd()) {
+        CSSValue* trackSize = consumeGridTrackSize(args, cssParserMode);
+        if (!trackSize)
+            return false;
+        if (allTracksAreFixedSized)
+            allTracksAreFixedSized = isGridTrackFixedSized(*trackSize);
+        repeatedValues->append(*trackSize);
+        ++numberOfTracks;
+        lineNames = consumeGridLineNames(args);
+        if (lineNames)
+            repeatedValues->append(*lineNames);
+    }
+    // We should have found at least one <track-size> or else it is not a valid <track-list>.
+    if (!numberOfTracks)
+        return false;
+
+    if (isAutoRepeat) {
+        list.append(*repeatedValues);
+    } else {
+        // We clamp the repetitions to a multiple of the repeat() track list's size, while staying below the max grid size.
+        repetitions = std::min(repetitions, kGridMaxTracks / numberOfTracks);
+        for (size_t i = 0; i < repetitions; ++i) {
+            for (size_t j = 0; j < repeatedValues->length(); ++j)
+                list.append(repeatedValues->item(j));
+        }
+    }
+    return true;
+}
+
+enum TrackListType { GridTemplate, GridTemplateNoRepeat, GridAuto };
+
+static CSSValue* consumeGridTrackList(CSSParserTokenRange& range, CSSParserMode cssParserMode, TrackListType trackListType)
+{
+    bool allowGridLineNames = trackListType != GridAuto;
+    CSSValueList* values = CSSValueList::createSpaceSeparated();
+    CSSGridLineNamesValue* lineNames = consumeGridLineNames(range);
+    if (lineNames) {
+        if (!allowGridLineNames)
+            return nullptr;
+        values->append(*lineNames);
+    }
+
+    bool allowRepeat = trackListType == GridTemplate;
+    bool seenAutoRepeat = false;
+    bool allTracksAreFixedSized = true;
+    do {
+        bool isAutoRepeat;
+        if (range.peek().functionId() == CSSValueRepeat) {
+            if (!allowRepeat)
+                return nullptr;
+            if (!consumeGridTrackRepeatFunction(range, cssParserMode, *values, isAutoRepeat, allTracksAreFixedSized))
+                return nullptr;
+            if (isAutoRepeat && seenAutoRepeat)
+                return nullptr;
+            seenAutoRepeat = seenAutoRepeat || isAutoRepeat;
+        } else if (CSSValue* value = consumeGridTrackSize(range, cssParserMode)) {
+            if (allTracksAreFixedSized)
+                allTracksAreFixedSized = isGridTrackFixedSized(*value);
+            values->append(*value);
+        } else {
+            return nullptr;
+        }
+        if (seenAutoRepeat && !allTracksAreFixedSized)
+            return nullptr;
+        lineNames = consumeGridLineNames(range);
+        if (lineNames) {
+            if (!allowGridLineNames)
+                return nullptr;
+            values->append(*lineNames);
+        }
+    } while (!range.atEnd() && range.peek().type() != DelimiterToken);
+    return values;
+}
+
+static CSSValue* consumeGridTemplatesRowsOrColumns(CSSParserTokenRange& range, CSSParserMode cssParserMode)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+    return consumeGridTrackList(range, cssParserMode, GridTemplate);
+}
+
+static CSSValue* consumeGridTemplateAreas(CSSParserTokenRange& range)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+
+    NamedGridAreaMap gridAreaMap;
+    size_t rowCount = 0;
+    size_t columnCount = 0;
+
+    while (range.peek().type() == StringToken) {
+        if (!parseGridTemplateAreasRow(range.consumeIncludingWhitespace().value().toString(), gridAreaMap, rowCount, columnCount))
+            return nullptr;
+        ++rowCount;
+    }
+
+    if (rowCount == 0)
+        return nullptr;
+    ASSERT(columnCount);
+    return CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount);
+}
+
+static void countKeywordOnlyPropertyUsage(CSSPropertyID property, UseCounter* counter, CSSValueID valueID)
+{
+    if (!counter)
+        return;
+    switch (property) {
+    case CSSPropertyWebkitAppearance:
+        if (valueID == CSSValueNone) {
+            counter->count(UseCounter::CSSValueAppearanceNone);
+        } else {
+            counter->count(UseCounter::CSSValueAppearanceNotNone);
+            if (valueID == CSSValueButton)
+                counter->count(UseCounter::CSSValueAppearanceButton);
+            else if (valueID == CSSValueCaret)
+                counter->count(UseCounter::CSSValueAppearanceCaret);
+            else if (valueID == CSSValueCheckbox)
+                counter->count(UseCounter::CSSValueAppearanceCheckbox);
+            else if (valueID == CSSValueMenulist)
+                counter->count(UseCounter::CSSValueAppearanceMenulist);
+            else if (valueID == CSSValueMenulistButton)
+                counter->count(UseCounter::CSSValueAppearanceMenulistButton);
+            else if (valueID == CSSValueListbox)
+                counter->count(UseCounter::CSSValueAppearanceListbox);
+            else if (valueID == CSSValueRadio)
+                counter->count(UseCounter::CSSValueAppearanceRadio);
+            else if (valueID == CSSValueSearchfield)
+                counter->count(UseCounter::CSSValueAppearanceSearchField);
+            else if (valueID == CSSValueTextfield)
+                counter->count(UseCounter::CSSValueAppearanceTextField);
+            else
+                counter->count(UseCounter::CSSValueAppearanceOthers);
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+const CSSValue* CSSPropertyParser::parseSingleValue(CSSPropertyID unresolvedProperty, CSSPropertyID currentShorthand)
+{
+    CSSPropertyID property = resolveCSSPropertyID(unresolvedProperty);
+    if (CSSParserFastPaths::isKeywordPropertyID(property)) {
+        if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(property, m_range.peek().id(), m_context.mode()))
+            return nullptr;
+        countKeywordOnlyPropertyUsage(property, m_context.useCounter(), m_range.peek().id());
+        return consumeIdent(m_range);
+    }
+    switch (property) {
+    case CSSPropertyWillChange:
+        return consumeWillChange(m_range);
+    case CSSPropertyPage:
+        return consumePage(m_range);
+    case CSSPropertyQuotes:
+        return consumeQuotes(m_range);
+    case CSSPropertyWebkitHighlight:
+        return consumeWebkitHighlight(m_range);
+    case CSSPropertyFontVariantCaps:
+        return consumeFontVariantCaps(m_range);
+    case CSSPropertyFontVariantLigatures:
+        return consumeFontVariantLigatures(m_range);
+    case CSSPropertyFontVariantNumeric:
+        return consumeFontVariantNumeric(m_range);
+    case CSSPropertyFontFeatureSettings:
+        return consumeFontFeatureSettings(m_range);
+    case CSSPropertyFontFamily:
+        return consumeFontFamily(m_range);
+    case CSSPropertyFontWeight:
+        return consumeFontWeight(m_range);
+    case CSSPropertyLetterSpacing:
+    case CSSPropertyWordSpacing:
+        return consumeSpacing(m_range, m_context.mode());
+    case CSSPropertyTabSize:
+        return consumeTabSize(m_range, m_context.mode());
+    case CSSPropertyTextSizeAdjust:
+        return consumeTextSizeAdjust(m_range, m_context.mode());
+    case CSSPropertyFontSize:
+        return consumeFontSize(m_range, m_context.mode(), UnitlessQuirk::Allow);
+    case CSSPropertyLineHeight:
+        return consumeLineHeight(m_range, m_context.mode());
+    case CSSPropertyRotate:
+        return consumeRotation(m_range);
+    case CSSPropertyScale:
+        return consumeScale(m_range);
+    case CSSPropertyTranslate:
+        return consumeTranslate(m_range, m_context.mode());
+    case CSSPropertyWebkitBorderHorizontalSpacing:
+    case CSSPropertyWebkitBorderVerticalSpacing:
+        return consumeLength(m_range, m_context.mode(), ValueRangeNonNegative);
+    case CSSPropertyCounterIncrement:
+    case CSSPropertyCounterReset:
+        return consumeCounter(m_range, property == CSSPropertyCounterIncrement ? 1 : 0);
+    case CSSPropertySize:
+        return consumeSize(m_range, m_context.mode());
+    case CSSPropertySnapHeight:
+        return consumeSnapHeight(m_range, m_context.mode());
+    case CSSPropertyTextIndent:
+        return consumeTextIndent(m_range, m_context.mode());
+    case CSSPropertyMaxWidth:
+    case CSSPropertyMaxHeight:
+        return consumeMaxWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
+    case CSSPropertyWebkitMaxLogicalWidth:
+    case CSSPropertyWebkitMaxLogicalHeight:
+        return consumeMaxWidthOrHeight(m_range, m_context);
+    case CSSPropertyMinWidth:
+    case CSSPropertyMinHeight:
+    case CSSPropertyWidth:
+    case CSSPropertyHeight:
+        return consumeWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
+    case CSSPropertyWebkitMinLogicalWidth:
+    case CSSPropertyWebkitMinLogicalHeight:
+    case CSSPropertyWebkitLogicalWidth:
+    case CSSPropertyWebkitLogicalHeight:
+        return consumeWidthOrHeight(m_range, m_context);
+    case CSSPropertyMarginTop:
+    case CSSPropertyMarginRight:
+    case CSSPropertyMarginBottom:
+    case CSSPropertyMarginLeft:
+    case CSSPropertyBottom:
+    case CSSPropertyLeft:
+    case CSSPropertyRight:
+    case CSSPropertyTop:
+        return consumeMarginOrOffset(m_range, m_context.mode(), UnitlessQuirk::Allow);
+    case CSSPropertyWebkitMarginStart:
+    case CSSPropertyWebkitMarginEnd:
+    case CSSPropertyWebkitMarginBefore:
+    case CSSPropertyWebkitMarginAfter:
+        return consumeMarginOrOffset(m_range, m_context.mode(), UnitlessQuirk::Forbid);
+    case CSSPropertyPaddingTop:
+    case CSSPropertyPaddingRight:
+    case CSSPropertyPaddingBottom:
+    case CSSPropertyPaddingLeft:
+        return consumeLengthOrPercent(m_range, m_context.mode(), ValueRangeNonNegative, UnitlessQuirk::Allow);
+    case CSSPropertyWebkitPaddingStart:
+    case CSSPropertyWebkitPaddingEnd:
+    case CSSPropertyWebkitPaddingBefore:
+    case CSSPropertyWebkitPaddingAfter:
+        return consumeLengthOrPercent(m_range, m_context.mode(), ValueRangeNonNegative, UnitlessQuirk::Forbid);
+    case CSSPropertyClip:
+        return consumeClip(m_range, m_context.mode());
+    case CSSPropertyTouchAction:
+        return consumeTouchAction(m_range);
+    case CSSPropertyScrollSnapDestination:
+    case CSSPropertyObjectPosition:
+    case CSSPropertyPerspectiveOrigin:
+        return consumePosition(m_range, m_context.mode(), UnitlessQuirk::Forbid);
+    case CSSPropertyWebkitLineClamp:
+        return consumeLineClamp(m_range);
+    case CSSPropertyWebkitFontSizeDelta:
+        return consumeLength(m_range, m_context.mode(), ValueRangeAll, UnitlessQuirk::Allow);
+    case CSSPropertyWebkitHyphenateCharacter:
+    case CSSPropertyWebkitLocale:
+        return consumeLocale(m_range);
+    case CSSPropertyColumnWidth:
+        return consumeColumnWidth(m_range);
+    case CSSPropertyColumnCount:
+        return consumeColumnCount(m_range);
+    case CSSPropertyColumnGap:
+        return consumeColumnGap(m_range, m_context.mode());
+    case CSSPropertyColumnSpan:
+        return consumeColumnSpan(m_range);
+    case CSSPropertyZoom:
+        return consumeZoom(m_range, m_context);
+    case CSSPropertyAnimationDelay:
+    case CSSPropertyTransitionDelay:
+    case CSSPropertyAnimationDirection:
+    case CSSPropertyAnimationDuration:
+    case CSSPropertyTransitionDuration:
+    case CSSPropertyAnimationFillMode:
+    case CSSPropertyAnimationIterationCount:
+    case CSSPropertyAnimationName:
+    case CSSPropertyAnimationPlayState:
+    case CSSPropertyTransitionProperty:
+    case CSSPropertyAnimationTimingFunction:
+    case CSSPropertyTransitionTimingFunction:
+        return consumeAnimationPropertyList(property, m_range, m_context, unresolvedProperty == CSSPropertyAliasWebkitAnimationName);
+    case CSSPropertyGridColumnGap:
+    case CSSPropertyGridRowGap:
+        return consumeLength(m_range, m_context.mode(), ValueRangeNonNegative);
+    case CSSPropertyShapeMargin:
+        return consumeLengthOrPercent(m_range, m_context.mode(), ValueRangeNonNegative);
+    case CSSPropertyShapeImageThreshold:
+        return consumeNumber(m_range, ValueRangeAll);
+    case CSSPropertyWebkitBoxOrdinalGroup:
+    case CSSPropertyOrphans:
+    case CSSPropertyWidows:
+        return consumePositiveInteger(m_range);
+    case CSSPropertyTextDecorationColor:
+        ASSERT(RuntimeEnabledFeatures::css3TextDecorationsEnabled());
+        return consumeColor(m_range, m_context.mode());
+    case CSSPropertyWebkitTextStrokeWidth:
+        return consumeTextStrokeWidth(m_range, m_context.mode());
+    case CSSPropertyWebkitTextFillColor:
+    case CSSPropertyWebkitTapHighlightColor:
+    case CSSPropertyWebkitTextEmphasisColor:
+    case CSSPropertyWebkitBorderStartColor:
+    case CSSPropertyWebkitBorderEndColor:
+    case CSSPropertyWebkitBorderBeforeColor:
+    case CSSPropertyWebkitBorderAfterColor:
+    case CSSPropertyWebkitTextStrokeColor:
+    case CSSPropertyStopColor:
+    case CSSPropertyFloodColor:
+    case CSSPropertyLightingColor:
+    case CSSPropertyColumnRuleColor:
+        return consumeColor(m_range, m_context.mode());
+    case CSSPropertyColor:
+    case CSSPropertyBackgroundColor:
+        return consumeColor(m_range, m_context.mode(), inQuirksMode());
+    case CSSPropertyWebkitBorderStartWidth:
+    case CSSPropertyWebkitBorderEndWidth:
+    case CSSPropertyWebkitBorderBeforeWidth:
+    case CSSPropertyWebkitBorderAfterWidth:
+        return consumeBorderWidth(m_range, m_context.mode(), UnitlessQuirk::Forbid);
+    case CSSPropertyBorderBottomColor:
+    case CSSPropertyBorderLeftColor:
+    case CSSPropertyBorderRightColor:
+    case CSSPropertyBorderTopColor: {
+        bool allowQuirkyColors = inQuirksMode()
+            && (currentShorthand == CSSPropertyInvalid || currentShorthand == CSSPropertyBorderColor);
+        return consumeColor(m_range, m_context.mode(), allowQuirkyColors);
+    }
+    case CSSPropertyBorderBottomWidth:
+    case CSSPropertyBorderLeftWidth:
+    case CSSPropertyBorderRightWidth:
+    case CSSPropertyBorderTopWidth: {
+        bool allowQuirkyLengths = inQuirksMode()
+            && (currentShorthand == CSSPropertyInvalid || currentShorthand == CSSPropertyBorderWidth);
+        UnitlessQuirk unitless = allowQuirkyLengths ? UnitlessQuirk::Allow : UnitlessQuirk::Forbid;
+        return consumeBorderWidth(m_range, m_context.mode(), unitless);
+    }
+    case CSSPropertyZIndex:
+        return consumeZIndex(m_range);
+    case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3
+    case CSSPropertyBoxShadow:
+        return consumeShadow(m_range, m_context.mode(), property == CSSPropertyBoxShadow);
+    case CSSPropertyFilter:
+    case CSSPropertyBackdropFilter:
+        return consumeFilter(m_range, m_context);
+    case CSSPropertyTextDecoration:
+        ASSERT(!RuntimeEnabledFeatures::css3TextDecorationsEnabled());
+        // fallthrough
+    case CSSPropertyWebkitTextDecorationsInEffect:
+    case CSSPropertyTextDecorationLine:
+        return consumeTextDecorationLine(m_range);
+    case CSSPropertyD:
+    case CSSPropertyMotionPath:
+        return consumePathOrNone(m_range);
+    case CSSPropertyMotionOffset:
+        return consumeLengthOrPercent(m_range, m_context.mode(), ValueRangeAll);
+    case CSSPropertyMotionRotation:
+        return consumeMotionRotation(m_range);
+    case CSSPropertyWebkitTextEmphasisStyle:
+        return consumeTextEmphasisStyle(m_range);
+    case CSSPropertyOutlineColor:
+        return consumeOutlineColor(m_range, m_context.mode());
+    case CSSPropertyOutlineOffset:
+        return consumeLength(m_range, m_context.mode(), ValueRangeAll);
+    case CSSPropertyOutlineWidth:
+        return consumeLineWidth(m_range, m_context.mode(), UnitlessQuirk::Forbid);
+    case CSSPropertyTransform:
+        return consumeTransform(m_range, m_context.mode(), unresolvedProperty == CSSPropertyAliasWebkitTransform);
+    case CSSPropertyWebkitTransformOriginX:
+    case CSSPropertyWebkitPerspectiveOriginX:
+        return consumePositionX(m_range, m_context.mode());
+    case CSSPropertyWebkitTransformOriginY:
+    case CSSPropertyWebkitPerspectiveOriginY:
+        return consumePositionY(m_range, m_context.mode());
+    case CSSPropertyWebkitTransformOriginZ:
+        return consumeLength(m_range, m_context.mode(), ValueRangeAll);
+    case CSSPropertyFill:
+    case CSSPropertyStroke:
+        return consumePaintStroke(m_range, m_context.mode());
+    case CSSPropertyPaintOrder:
+        return consumePaintOrder(m_range);
+    case CSSPropertyMarkerStart:
+    case CSSPropertyMarkerMid:
+    case CSSPropertyMarkerEnd:
+    case CSSPropertyClipPath:
+    case CSSPropertyMask:
+        return consumeNoneOrURI(m_range);
+    case CSSPropertyFlexBasis:
+        return consumeFlexBasis(m_range, m_context.mode());
+    case CSSPropertyFlexGrow:
+    case CSSPropertyFlexShrink:
+        return consumeNumber(m_range, ValueRangeNonNegative);
+    case CSSPropertyStrokeDasharray:
+        return consumeStrokeDasharray(m_range);
+    case CSSPropertyColumnRuleWidth:
+        return consumeColumnRuleWidth(m_range, m_context.mode());
+    case CSSPropertyStrokeOpacity:
+    case CSSPropertyFillOpacity:
+    case CSSPropertyStopOpacity:
+    case CSSPropertyFloodOpacity:
+    case CSSPropertyOpacity:
+    case CSSPropertyWebkitBoxFlex:
+        return consumeNumber(m_range, ValueRangeAll);
+    case CSSPropertyBaselineShift:
+        return consumeBaselineShift(m_range);
+    case CSSPropertyStrokeMiterlimit:
+        return consumeNumber(m_range, ValueRangeNonNegative);
+    case CSSPropertyStrokeWidth:
+    case CSSPropertyStrokeDashoffset:
+    case CSSPropertyCx:
+    case CSSPropertyCy:
+    case CSSPropertyX:
+    case CSSPropertyY:
+    case CSSPropertyR:
+        return consumeLengthOrPercent(m_range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
+    case CSSPropertyRx:
+    case CSSPropertyRy:
+        return consumeRxOrRy(m_range);
+    case CSSPropertyCursor:
+        return consumeCursor(m_range, m_context, inQuirksMode());
+    case CSSPropertyContain:
+        return consumeContain(m_range);
+    case CSSPropertyTransformOrigin:
+        return consumeTransformOrigin(m_range, m_context.mode(), UnitlessQuirk::Forbid);
+    case CSSPropertyContent:
+        return consumeContent(m_range, m_context);
+    case CSSPropertyListStyleImage:
+    case CSSPropertyBorderImageSource:
+    case CSSPropertyWebkitMaskBoxImageSource:
+        return consumeImageOrNone(m_range, m_context);
+    case CSSPropertyPerspective:
+        return consumePerspective(m_range, m_context.mode(), unresolvedProperty);
+    case CSSPropertyScrollSnapCoordinate:
+        return consumeScrollSnapCoordinate(m_range, m_context.mode());
+    case CSSPropertyScrollSnapPointsX:
+    case CSSPropertyScrollSnapPointsY:
+        return consumeScrollSnapPoints(m_range, m_context.mode());
+    case CSSPropertyBorderTopRightRadius:
+    case CSSPropertyBorderTopLeftRadius:
+    case CSSPropertyBorderBottomLeftRadius:
+    case CSSPropertyBorderBottomRightRadius:
+        return consumeBorderRadiusCorner(m_range, m_context.mode());
+    case CSSPropertyWebkitBoxFlexGroup:
+        return consumeInteger(m_range, 0);
+    case CSSPropertyOrder:
+        return consumeInteger(m_range);
+    case CSSPropertyTextUnderlinePosition:
+        // auto | [ under || [ left | right ] ], but we only support auto | under for now
+        ASSERT(RuntimeEnabledFeatures::css3TextDecorationsEnabled());
+        return consumeIdent<CSSValueAuto, CSSValueUnder>(m_range);
+    case CSSPropertyVerticalAlign:
+        return consumeVerticalAlign(m_range, m_context.mode());
+    case CSSPropertyShapeOutside:
+        return consumeShapeOutside(m_range, m_context);
+    case CSSPropertyWebkitClipPath:
+        return consumeWebkitClipPath(m_range, m_context);
+    case CSSPropertyJustifyContent:
+    case CSSPropertyAlignContent:
+        ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+        return consumeContentDistributionOverflowPosition(m_range);
+    case CSSPropertyBorderImageRepeat:
+    case CSSPropertyWebkitMaskBoxImageRepeat:
+        return consumeBorderImageRepeat(m_range);
+    case CSSPropertyBorderImageSlice:
+    case CSSPropertyWebkitMaskBoxImageSlice:
+        return consumeBorderImageSlice(property, m_range);
+    case CSSPropertyBorderImageOutset:
+    case CSSPropertyWebkitMaskBoxImageOutset:
+        return consumeBorderImageOutset(m_range);
+    case CSSPropertyBorderImageWidth:
+    case CSSPropertyWebkitMaskBoxImageWidth:
+        return consumeBorderImageWidth(m_range);
+    case CSSPropertyWebkitBorderImage:
+        return consumeWebkitBorderImage(property, m_range, m_context);
+    case CSSPropertyWebkitBoxReflect:
+        return consumeReflect(m_range, m_context);
+    case CSSPropertyFontSizeAdjust:
+        ASSERT(RuntimeEnabledFeatures::cssFontSizeAdjustEnabled());
+        return consumeFontSizeAdjust(m_range);
+    case CSSPropertyImageOrientation:
+        ASSERT(RuntimeEnabledFeatures::imageOrientationEnabled());
+        return consumeImageOrientation(m_range);
+    case CSSPropertyBackgroundAttachment:
+    case CSSPropertyBackgroundBlendMode:
+    case CSSPropertyBackgroundClip:
+    case CSSPropertyBackgroundImage:
+    case CSSPropertyBackgroundOrigin:
+    case CSSPropertyBackgroundPositionX:
+    case CSSPropertyBackgroundPositionY:
+    case CSSPropertyBackgroundSize:
+    case CSSPropertyMaskSourceType:
+    case CSSPropertyWebkitBackgroundClip:
+    case CSSPropertyWebkitBackgroundOrigin:
+    case CSSPropertyWebkitMaskClip:
+    case CSSPropertyWebkitMaskComposite:
+    case CSSPropertyWebkitMaskImage:
+    case CSSPropertyWebkitMaskOrigin:
+    case CSSPropertyWebkitMaskPositionX:
+    case CSSPropertyWebkitMaskPositionY:
+    case CSSPropertyWebkitMaskSize:
+        return consumeCommaSeparatedBackgroundComponent(unresolvedProperty, m_range, m_context);
+    case CSSPropertyWebkitMaskRepeatX:
+    case CSSPropertyWebkitMaskRepeatY:
+        return nullptr;
+    case CSSPropertyAlignItems:
+        DCHECK(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+        return consumeAlignItems(m_range);
+    case CSSPropertyJustifySelf:
+    case CSSPropertyAlignSelf:
+        ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+        return consumeSelfPositionOverflowPosition(m_range);
+    case CSSPropertyJustifyItems:
+        ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+        return consumeJustifyItems(m_range);
+    case CSSPropertyGridColumnEnd:
+    case CSSPropertyGridColumnStart:
+    case CSSPropertyGridRowEnd:
+    case CSSPropertyGridRowStart:
+        ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+        return consumeGridLine(m_range);
+    case CSSPropertyGridAutoColumns:
+    case CSSPropertyGridAutoRows:
+        ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+        return consumeGridTrackList(m_range, m_context.mode(), GridAuto);
+    case CSSPropertyGridTemplateColumns:
+    case CSSPropertyGridTemplateRows:
+        ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+        return consumeGridTemplatesRowsOrColumns(m_range, m_context.mode());
+    case CSSPropertyGridTemplateAreas:
+        ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+        return consumeGridTemplateAreas(m_range);
+    case CSSPropertyGridAutoFlow:
+        ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+        return consumeGridAutoFlow(m_range);
+    default:
+        return nullptr;
+    }
+}
+
+static CSSPrimitiveValue* consumeFontDisplay(CSSParserTokenRange& range)
+{
+    return consumeIdent<CSSValueAuto, CSSValueBlock, CSSValueSwap, CSSValueFallback, CSSValueOptional>(range);
+}
+
+static CSSValueList* consumeFontFaceUnicodeRange(CSSParserTokenRange& range)
+{
+    CSSValueList* values = CSSValueList::createCommaSeparated();
+
+    do {
+        const CSSParserToken& token = range.consumeIncludingWhitespace();
+        if (token.type() != UnicodeRangeToken)
+            return nullptr;
+
+        UChar32 start = token.unicodeRangeStart();
+        UChar32 end = token.unicodeRangeEnd();
+        if (start > end)
+            return nullptr;
+        values->append(*CSSUnicodeRangeValue::create(start, end));
+    } while (consumeCommaIncludingWhitespace(range));
+
+    return values;
+}
+
+static CSSValue* consumeFontFaceSrcURI(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    String url = consumeUrlAsStringView(range).toString();
+    if (url.isNull())
+        return nullptr;
+    CSSFontFaceSrcValue* uriValue(CSSFontFaceSrcValue::create(url, context.completeURL(url), context.shouldCheckContentSecurityPolicy()));
+    uriValue->setReferrer(context.referrer());
+
+    if (range.peek().functionId() != CSSValueFormat)
+        return uriValue;
+
+    // FIXME: https://drafts.csswg.org/css-fonts says that format() contains a comma-separated list of strings,
+    // but CSSFontFaceSrcValue stores only one format. Allowing one format for now.
+    CSSParserTokenRange args = consumeFunction(range);
+    const CSSParserToken& arg = args.consumeIncludingWhitespace();
+    if ((arg.type() != StringToken) || !args.atEnd())
+        return nullptr;
+    uriValue->setFormat(arg.value().toString());
+    return uriValue;
+}
+
+static CSSValue* consumeFontFaceSrcLocal(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    CSSParserTokenRange args = consumeFunction(range);
+    ContentSecurityPolicyDisposition shouldCheckContentSecurityPolicy = context.shouldCheckContentSecurityPolicy();
+    if (args.peek().type() == StringToken) {
+        const CSSParserToken& arg = args.consumeIncludingWhitespace();
+        if (!args.atEnd())
+            return nullptr;
+        return CSSFontFaceSrcValue::createLocal(arg.value().toString(), shouldCheckContentSecurityPolicy);
+    }
+    if (args.peek().type() == IdentToken) {
+        String familyName = concatenateFamilyName(args);
+        if (!args.atEnd())
+            return nullptr;
+        return CSSFontFaceSrcValue::createLocal(familyName, shouldCheckContentSecurityPolicy);
+    }
+    return nullptr;
+}
+
+static CSSValueList* consumeFontFaceSrc(CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    CSSValueList* values = CSSValueList::createCommaSeparated();
+
+    do {
+        const CSSParserToken& token = range.peek();
+        CSSValue* parsedValue = nullptr;
+        if (token.functionId() == CSSValueLocal)
+            parsedValue = consumeFontFaceSrcLocal(range, context);
+        else
+            parsedValue = consumeFontFaceSrcURI(range, context);
+        if (!parsedValue)
+            return nullptr;
+        values->append(*parsedValue);
+    } while (consumeCommaIncludingWhitespace(range));
+    return values;
+}
+
+bool CSSPropertyParser::parseFontFaceDescriptor(CSSPropertyID propId)
+{
+    CSSValue* parsedValue = nullptr;
+    switch (propId) {
+    case CSSPropertyFontFamily:
+        if (consumeGenericFamily(m_range))
+            return false;
+        parsedValue = consumeFamilyName(m_range);
+        break;
+    case CSSPropertySrc: // This is a list of urls or local references.
+        parsedValue = consumeFontFaceSrc(m_range, m_context);
+        break;
+    case CSSPropertyUnicodeRange:
+        parsedValue = consumeFontFaceUnicodeRange(m_range);
+        break;
+    case CSSPropertyFontDisplay:
+        parsedValue = consumeFontDisplay(m_range);
+        break;
+    case CSSPropertyFontStretch:
+    case CSSPropertyFontStyle: {
+        CSSValueID id = m_range.consumeIncludingWhitespace().id();
+        if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(propId, id, m_context.mode()))
+            return false;
+        parsedValue = CSSPrimitiveValue::createIdentifier(id);
+        break;
+    }
+    case CSSPropertyFontVariant:
+        parsedValue = consumeFontVariantList(m_range);
+        break;
+    case CSSPropertyFontWeight:
+        parsedValue = consumeFontWeight(m_range);
+        break;
+    case CSSPropertyFontFeatureSettings:
+        parsedValue = consumeFontFeatureSettings(m_range);
+        break;
+    default:
+        break;
+    }
+
+    if (!parsedValue || !m_range.atEnd())
+        return false;
+
+    addProperty(propId, CSSPropertyInvalid, *parsedValue, false);
+    return true;
+}
+
+bool CSSPropertyParser::consumeSystemFont(bool important)
+{
+    CSSValueID systemFontID = m_range.consumeIncludingWhitespace().id();
+    ASSERT(systemFontID >= CSSValueCaption && systemFontID <= CSSValueStatusBar);
+    if (!m_range.atEnd())
+        return false;
+
+    FontStyle fontStyle = FontStyleNormal;
+    FontWeight fontWeight = FontWeightNormal;
+    float fontSize = 0;
+    AtomicString fontFamily;
+    LayoutTheme::theme().systemFont(systemFontID, fontStyle, fontWeight, fontSize, fontFamily);
+
+    addProperty(CSSPropertyFontStyle, CSSPropertyFont, *CSSPrimitiveValue::createIdentifier(fontStyle == FontStyleItalic ? CSSValueItalic : CSSValueNormal), important);
+    addProperty(CSSPropertyFontWeight, CSSPropertyFont, *CSSPrimitiveValue::create(fontWeight), important);
+    addProperty(CSSPropertyFontSize, CSSPropertyFont, *CSSPrimitiveValue::create(fontSize, CSSPrimitiveValue::UnitType::Pixels), important);
+    CSSValueList* fontFamilyList = CSSValueList::createCommaSeparated();
+    fontFamilyList->append(*CSSFontFamilyValue::create(fontFamily));
+    addProperty(CSSPropertyFontFamily, CSSPropertyFont, *fontFamilyList, important);
+
+    addProperty(CSSPropertyFontStretch, CSSPropertyFont, *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    addProperty(CSSPropertyFontVariantCaps, CSSPropertyFont, *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    addProperty(CSSPropertyFontVariantLigatures, CSSPropertyFont, *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    addProperty(CSSPropertyFontVariantNumeric, CSSPropertyFont, *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    addProperty(CSSPropertyLineHeight, CSSPropertyFont, *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    return true;
+}
+
+bool CSSPropertyParser::consumeFont(bool important)
+{
+    // Let's check if there is an inherit or initial somewhere in the shorthand.
+    CSSParserTokenRange range = m_range;
+    while (!range.atEnd()) {
+        CSSValueID id = range.consumeIncludingWhitespace().id();
+        if (id == CSSValueInherit || id == CSSValueInitial)
+            return false;
+    }
+    // Optional font-style, font-variant, font-stretch and font-weight.
+    CSSPrimitiveValue* fontStyle = nullptr;
+    CSSPrimitiveValue* fontVariantCaps = nullptr;
+    CSSPrimitiveValue* fontWeight = nullptr;
+    CSSPrimitiveValue* fontStretch = nullptr;
+    while (!m_range.atEnd()) {
+        CSSValueID id = m_range.peek().id();
+        if (!fontStyle && CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyFontStyle, id, m_context.mode())) {
+            fontStyle = consumeIdent(m_range);
+            continue;
+        }
+        if (!fontVariantCaps && (id == CSSValueNormal || id == CSSValueSmallCaps)) {
+            // Font variant in the shorthand is particular, it only accepts normal or small-caps.
+            // See https://drafts.csswg.org/css-fonts/#propdef-font
+            fontVariantCaps = consumeFontVariantCSS21(m_range);
+            if (fontVariantCaps)
+                continue;
+        }
+        if (!fontWeight) {
+            fontWeight = consumeFontWeight(m_range);
+            if (fontWeight)
+                continue;
+        }
+        if (!fontStretch && CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyFontStretch, id, m_context.mode()))
+            fontStretch = consumeIdent(m_range);
+        else
+            break;
+    }
+
+    if (m_range.atEnd())
+        return false;
+
+    addProperty(CSSPropertyFontStyle, CSSPropertyFont, fontStyle ? *fontStyle : *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    addProperty(CSSPropertyFontVariantCaps, CSSPropertyFont, fontVariantCaps ? *fontVariantCaps : *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    addProperty(CSSPropertyFontVariantLigatures, CSSPropertyFont, *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    addProperty(CSSPropertyFontVariantNumeric, CSSPropertyFont, *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+
+    addProperty(CSSPropertyFontWeight, CSSPropertyFont, fontWeight ? *fontWeight : *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    addProperty(CSSPropertyFontStretch, CSSPropertyFont, fontStretch ? *fontStretch : *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+
+    // Now a font size _must_ come.
+    CSSValue* fontSize = consumeFontSize(m_range, m_context.mode());
+    if (!fontSize || m_range.atEnd())
+        return false;
+
+    addProperty(CSSPropertyFontSize, CSSPropertyFont, *fontSize, important);
+
+    if (consumeSlashIncludingWhitespace(m_range)) {
+        CSSPrimitiveValue* lineHeight = consumeLineHeight(m_range, m_context.mode());
+        if (!lineHeight)
+            return false;
+        addProperty(CSSPropertyLineHeight, CSSPropertyFont, *lineHeight, important);
+    } else {
+        addProperty(CSSPropertyLineHeight, CSSPropertyFont, *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    }
+
+    // Font family must come now.
+    CSSValue* parsedFamilyValue = consumeFontFamily(m_range);
+    if (!parsedFamilyValue)
+        return false;
+
+    addProperty(CSSPropertyFontFamily, CSSPropertyFont, *parsedFamilyValue, important);
+
+    // FIXME: http://www.w3.org/TR/2011/WD-css3-fonts-20110324/#font-prop requires that
+    // "font-stretch", "font-size-adjust", and "font-kerning" be reset to their initial values
+    // but we don't seem to support them at the moment. They should also be added here once implemented.
+    return m_range.atEnd();
+}
+
+bool CSSPropertyParser::consumeFontVariantShorthand(bool important)
+{
+    if (identMatches<CSSValueNormal, CSSValueNone>(m_range.peek().id())) {
+        addProperty(CSSPropertyFontVariantLigatures, CSSPropertyFontVariant, *consumeIdent(m_range), important);
+        addProperty(CSSPropertyFontVariantCaps, CSSPropertyFontVariant, *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+        return m_range.atEnd();
+    }
+
+    CSSPrimitiveValue* capsValue = nullptr;
+    FontVariantLigaturesParser ligaturesParser;
+    FontVariantNumericParser numericParser;
+    do {
+        FontVariantLigaturesParser::ParseResult ligaturesParseResult = ligaturesParser.consumeLigature(m_range);
+        FontVariantNumericParser::ParseResult numericParseResult = numericParser.consumeNumeric(m_range);
+        if (ligaturesParseResult == FontVariantLigaturesParser::ParseResult::ConsumedValue
+            || numericParseResult == FontVariantNumericParser::ParseResult::ConsumedValue)
+            continue;
+
+        if (ligaturesParseResult == FontVariantLigaturesParser::ParseResult::DisallowedValue
+            || numericParseResult == FontVariantNumericParser::ParseResult::DisallowedValue)
+            return false;
+
+        CSSValueID id = m_range.peek().id();
+        switch (id) {
+        case CSSValueSmallCaps:
+        case CSSValueAllSmallCaps:
+        case CSSValuePetiteCaps:
+        case CSSValueAllPetiteCaps:
+        case CSSValueUnicase:
+        case CSSValueTitlingCaps:
+            // Only one caps value permitted in font-variant grammar.
+            if (capsValue)
+                return false;
+            capsValue = consumeIdent(m_range);
+            break;
+        default:
+            return false;
+        }
+    } while (!m_range.atEnd());
+
+    addProperty(CSSPropertyFontVariantLigatures, CSSPropertyFontVariant, *ligaturesParser.finalizeValue(), important);
+    addProperty(CSSPropertyFontVariantNumeric, CSSPropertyFontVariant, *numericParser.finalizeValue(), important);
+    addProperty(CSSPropertyFontVariantCaps, CSSPropertyFontVariant, capsValue ? *capsValue : *CSSPrimitiveValue::createIdentifier(CSSValueNormal), important);
+    return true;
+}
+
+bool CSSPropertyParser::consumeBorderSpacing(bool important)
+{
+    CSSValue* horizontalSpacing = consumeLength(m_range, m_context.mode(), ValueRangeNonNegative, UnitlessQuirk::Allow);
+    if (!horizontalSpacing)
+        return false;
+    CSSValue* verticalSpacing = horizontalSpacing;
+    if (!m_range.atEnd())
+        verticalSpacing = consumeLength(m_range, m_context.mode(), ValueRangeNonNegative, UnitlessQuirk::Allow);
+    if (!verticalSpacing || !m_range.atEnd())
+        return false;
+    addProperty(CSSPropertyWebkitBorderHorizontalSpacing, CSSPropertyBorderSpacing, *horizontalSpacing, important);
+    addProperty(CSSPropertyWebkitBorderVerticalSpacing, CSSPropertyBorderSpacing, *verticalSpacing, important);
+    return true;
+}
+
+static CSSValue* consumeSingleViewportDescriptor(CSSParserTokenRange& range, CSSPropertyID propId, CSSParserMode cssParserMode)
+{
+    CSSValueID id = range.peek().id();
+    switch (propId) {
+    case CSSPropertyMinWidth:
+    case CSSPropertyMaxWidth:
+    case CSSPropertyMinHeight:
+    case CSSPropertyMaxHeight:
+        if (id == CSSValueAuto || id == CSSValueInternalExtendToZoom)
+            return consumeIdent(range);
+        return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
+    case CSSPropertyMinZoom:
+    case CSSPropertyMaxZoom:
+    case CSSPropertyZoom: {
+        if (id == CSSValueAuto)
+            return consumeIdent(range);
+        CSSValue* parsedValue = consumeNumber(range, ValueRangeNonNegative);
+        if (parsedValue)
+            return parsedValue;
+        return consumePercent(range, ValueRangeNonNegative);
+    }
+    case CSSPropertyUserZoom:
+        return consumeIdent<CSSValueZoom, CSSValueFixed>(range);
+    case CSSPropertyOrientation:
+        return consumeIdent<CSSValueAuto, CSSValuePortrait, CSSValueLandscape>(range);
+    default:
+        ASSERT_NOT_REACHED();
+        break;
+    }
+
+    ASSERT_NOT_REACHED();
+    return nullptr;
+}
+
+bool CSSPropertyParser::parseViewportDescriptor(CSSPropertyID propId, bool important)
+{
+    ASSERT(RuntimeEnabledFeatures::cssViewportEnabled() || isUASheetBehavior(m_context.mode()));
+
+    switch (propId) {
+    case CSSPropertyWidth: {
+        CSSValue* minWidth = consumeSingleViewportDescriptor(m_range, CSSPropertyMinWidth, m_context.mode());
+        if (!minWidth)
+            return false;
+        CSSValue* maxWidth = minWidth;
+        if (!m_range.atEnd())
+            maxWidth = consumeSingleViewportDescriptor(m_range, CSSPropertyMaxWidth, m_context.mode());
+        if (!maxWidth || !m_range.atEnd())
+            return false;
+        addProperty(CSSPropertyMinWidth, CSSPropertyInvalid, *minWidth, important);
+        addProperty(CSSPropertyMaxWidth, CSSPropertyInvalid, *maxWidth, important);
+        return true;
+    }
+    case CSSPropertyHeight: {
+        CSSValue* minHeight = consumeSingleViewportDescriptor(m_range, CSSPropertyMinHeight, m_context.mode());
+        if (!minHeight)
+            return false;
+        CSSValue* maxHeight = minHeight;
+        if (!m_range.atEnd())
+            maxHeight = consumeSingleViewportDescriptor(m_range, CSSPropertyMaxHeight, m_context.mode());
+        if (!maxHeight || !m_range.atEnd())
+            return false;
+        addProperty(CSSPropertyMinHeight, CSSPropertyInvalid, *minHeight, important);
+        addProperty(CSSPropertyMaxHeight, CSSPropertyInvalid, *maxHeight, important);
+        return true;
+    }
+    case CSSPropertyMinWidth:
+    case CSSPropertyMaxWidth:
+    case CSSPropertyMinHeight:
+    case CSSPropertyMaxHeight:
+    case CSSPropertyMinZoom:
+    case CSSPropertyMaxZoom:
+    case CSSPropertyZoom:
+    case CSSPropertyUserZoom:
+    case CSSPropertyOrientation: {
+        CSSValue* parsedValue = consumeSingleViewportDescriptor(m_range, propId, m_context.mode());
+        if (!parsedValue || !m_range.atEnd())
+            return false;
+        addProperty(propId, CSSPropertyInvalid, *parsedValue, important);
+        return true;
+    }
+    default:
+        return false;
+    }
+}
+
+static bool consumeColumnWidthOrCount(CSSParserTokenRange& range, CSSValue*& columnWidth, CSSValue*& columnCount)
+{
+    if (range.peek().id() == CSSValueAuto) {
+        consumeIdent(range);
+        return true;
+    }
+    if (!columnWidth) {
+        columnWidth = consumeColumnWidth(range);
+        if (columnWidth)
+            return true;
+    }
+    if (!columnCount)
+        columnCount = consumeColumnCount(range);
+    return columnCount;
+}
+
+bool CSSPropertyParser::consumeColumns(bool important)
+{
+    CSSValue* columnWidth = nullptr;
+    CSSValue* columnCount = nullptr;
+    if (!consumeColumnWidthOrCount(m_range, columnWidth, columnCount))
+        return false;
+    consumeColumnWidthOrCount(m_range, columnWidth, columnCount);
+    if (!m_range.atEnd())
+        return false;
+    if (!columnWidth)
+        columnWidth = CSSPrimitiveValue::createIdentifier(CSSValueAuto);
+    if (!columnCount)
+        columnCount = CSSPrimitiveValue::createIdentifier(CSSValueAuto);
+    addProperty(CSSPropertyColumnWidth, CSSPropertyInvalid, *columnWidth, important);
+    addProperty(CSSPropertyColumnCount, CSSPropertyInvalid, *columnCount, important);
+    return true;
+}
+
+bool CSSPropertyParser::consumeShorthandGreedily(const StylePropertyShorthand& shorthand, bool important)
+{
+    ASSERT(shorthand.length() <= 6); // Existing shorthands have at most 6 longhands.
+    const CSSValue* longhands[6] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
+    const CSSPropertyID* shorthandProperties = shorthand.properties();
+    do {
+        bool foundLonghand = false;
+        for (size_t i = 0; !foundLonghand && i < shorthand.length(); ++i) {
+            if (longhands[i])
+                continue;
+            longhands[i] = parseSingleValue(shorthandProperties[i], shorthand.id());
+            if (longhands[i])
+                foundLonghand = true;
+        }
+        if (!foundLonghand)
+            return false;
+    } while (!m_range.atEnd());
+
+    for (size_t i = 0; i < shorthand.length(); ++i) {
+        if (longhands[i])
+            addProperty(shorthandProperties[i], shorthand.id(), *longhands[i], important);
+        else
+            addProperty(shorthandProperties[i], shorthand.id(), *CSSInitialValue::createLegacyImplicit(), important);
+    }
+    return true;
+}
+
+bool CSSPropertyParser::consumeFlex(bool important)
+{
+    static const double unsetValue = -1;
+    double flexGrow = unsetValue;
+    double flexShrink = unsetValue;
+    CSSPrimitiveValue* flexBasis = nullptr;
+
+    if (m_range.peek().id() == CSSValueNone) {
+        flexGrow = 0;
+        flexShrink = 0;
+        flexBasis = CSSPrimitiveValue::createIdentifier(CSSValueAuto);
+        m_range.consumeIncludingWhitespace();
+    } else {
+        unsigned index = 0;
+        while (!m_range.atEnd() && index++ < 3) {
+            double num;
+            if (consumeNumberRaw(m_range, num)) {
+                if (num < 0)
+                    return false;
+                if (flexGrow == unsetValue)
+                    flexGrow = num;
+                else if (flexShrink == unsetValue)
+                    flexShrink = num;
+                else if (!num) // flex only allows a basis of 0 (sans units) if flex-grow and flex-shrink values have already been set.
+                    flexBasis = CSSPrimitiveValue::create(0, CSSPrimitiveValue::UnitType::Pixels);
+                else
+                    return false;
+            } else if (!flexBasis) {
+                if (m_range.peek().id() == CSSValueAuto)
+                    flexBasis = consumeIdent(m_range);
+                if (!flexBasis)
+                    flexBasis = consumeLengthOrPercent(m_range, m_context.mode(), ValueRangeNonNegative);
+                if (index == 2 && !m_range.atEnd())
+                    return false;
+            }
+        }
+        if (index == 0)
+            return false;
+        if (flexGrow == unsetValue)
+            flexGrow = 1;
+        if (flexShrink == unsetValue)
+            flexShrink = 1;
+        if (!flexBasis)
+            flexBasis = CSSPrimitiveValue::create(0, CSSPrimitiveValue::UnitType::Percentage);
+    }
+
+    if (!m_range.atEnd())
+        return false;
+    addProperty(CSSPropertyFlexGrow, CSSPropertyFlex, *CSSPrimitiveValue::create(clampTo<float>(flexGrow), CSSPrimitiveValue::UnitType::Number), important);
+    addProperty(CSSPropertyFlexShrink, CSSPropertyFlex, *CSSPrimitiveValue::create(clampTo<float>(flexShrink), CSSPrimitiveValue::UnitType::Number), important);
+    addProperty(CSSPropertyFlexBasis, CSSPropertyFlex, *flexBasis, important);
+    return true;
+}
+
+bool CSSPropertyParser::consumeBorder(bool important)
+{
+    CSSValue* width = nullptr;
+    const CSSValue* style = nullptr;
+    CSSValue* color = nullptr;
+
+    while (!width || !style || !color) {
+        if (!width) {
+            width = consumeLineWidth(m_range, m_context.mode(), UnitlessQuirk::Forbid);
+            if (width)
+                continue;
+        }
+        if (!style) {
+            style = parseSingleValue(CSSPropertyBorderLeftStyle, CSSPropertyBorder);
+            if (style)
+                continue;
+        }
+        if (!color) {
+            color = consumeColor(m_range, m_context.mode());
+            if (color)
+                continue;
+        }
+        break;
+    }
+
+    if (!width && !style && !color)
+        return false;
+
+    if (!width)
+        width = CSSInitialValue::createLegacyImplicit();
+    if (!style)
+        style = CSSInitialValue::createLegacyImplicit();
+    if (!color)
+        color = CSSInitialValue::createLegacyImplicit();
+
+    addExpandedPropertyForValue(CSSPropertyBorderWidth, *width, important);
+    addExpandedPropertyForValue(CSSPropertyBorderStyle, *style, important);
+    addExpandedPropertyForValue(CSSPropertyBorderColor, *color, important);
+    addExpandedPropertyForValue(CSSPropertyBorderImage, *CSSInitialValue::createLegacyImplicit(), important);
+
+    return m_range.atEnd();
+}
+
+bool CSSPropertyParser::consume4Values(const StylePropertyShorthand& shorthand, bool important)
+{
+    ASSERT(shorthand.length() == 4);
+    const CSSPropertyID* longhands = shorthand.properties();
+    const CSSValue* top = parseSingleValue(longhands[0], shorthand.id());
+    if (!top)
+        return false;
+
+    const CSSValue* right = parseSingleValue(longhands[1], shorthand.id());
+    const CSSValue* bottom = nullptr;
+    const CSSValue* left = nullptr;
+    if (right) {
+        bottom = parseSingleValue(longhands[2], shorthand.id());
+        if (bottom)
+            left = parseSingleValue(longhands[3], shorthand.id());
+    }
+
+    if (!right)
+        right = top;
+    if (!bottom)
+        bottom = top;
+    if (!left)
+        left = right;
+
+    addProperty(longhands[0], shorthand.id(), *top, important);
+    addProperty(longhands[1], shorthand.id(), *right, important);
+    addProperty(longhands[2], shorthand.id(), *bottom, important);
+    addProperty(longhands[3], shorthand.id(), *left, important);
+
+    return m_range.atEnd();
+}
+
+bool CSSPropertyParser::consumeBorderImage(CSSPropertyID property, bool important)
+{
+    CSSValue* source = nullptr;
+    CSSValue* slice = nullptr;
+    CSSValue* width = nullptr;
+    CSSValue* outset = nullptr;
+    CSSValue* repeat = nullptr;
+    if (consumeBorderImageComponents(property, m_range, m_context, source, slice, width, outset, repeat)) {
+        switch (property) {
+        case CSSPropertyWebkitMaskBoxImage:
+            addProperty(CSSPropertyWebkitMaskBoxImageSource, CSSPropertyWebkitMaskBoxImage, source ? *source : *CSSInitialValue::createLegacyImplicit(), important);
+            addProperty(CSSPropertyWebkitMaskBoxImageSlice, CSSPropertyWebkitMaskBoxImage, slice ? *slice : *CSSInitialValue::createLegacyImplicit(), important);
+            addProperty(CSSPropertyWebkitMaskBoxImageWidth, CSSPropertyWebkitMaskBoxImage, width ? *width : *CSSInitialValue::createLegacyImplicit(), important);
+            addProperty(CSSPropertyWebkitMaskBoxImageOutset, CSSPropertyWebkitMaskBoxImage, outset ? *outset : *CSSInitialValue::createLegacyImplicit(), important);
+            addProperty(CSSPropertyWebkitMaskBoxImageRepeat, CSSPropertyWebkitMaskBoxImage, repeat ? *repeat : *CSSInitialValue::createLegacyImplicit(), important);
+            return true;
+        case CSSPropertyBorderImage:
+            addProperty(CSSPropertyBorderImageSource, CSSPropertyBorderImage, source ? *source : *CSSInitialValue::createLegacyImplicit(), important);
+            addProperty(CSSPropertyBorderImageSlice, CSSPropertyBorderImage, slice ? *slice : *CSSInitialValue::createLegacyImplicit(), important);
+            addProperty(CSSPropertyBorderImageWidth, CSSPropertyBorderImage, width ? *width : *CSSInitialValue::createLegacyImplicit(), important);
+            addProperty(CSSPropertyBorderImageOutset, CSSPropertyBorderImage, outset ? *outset : *CSSInitialValue::createLegacyImplicit(), important);
+            addProperty(CSSPropertyBorderImageRepeat, CSSPropertyBorderImage, repeat ? *repeat : *CSSInitialValue::createLegacyImplicit(), important);
+            return true;
+        default:
+            ASSERT_NOT_REACHED();
+            return false;
+        }
+    }
+    return false;
+}
+
+static inline CSSValueID mapFromPageBreakBetween(CSSValueID value)
+{
+    if (value == CSSValueAlways)
+        return CSSValuePage;
+    if (value == CSSValueAuto || value == CSSValueAvoid || value == CSSValueLeft || value == CSSValueRight)
+        return value;
+    return CSSValueInvalid;
+}
+
+static inline CSSValueID mapFromColumnBreakBetween(CSSValueID value)
+{
+    if (value == CSSValueAlways)
+        return CSSValueColumn;
+    if (value == CSSValueAuto || value == CSSValueAvoid)
+        return value;
+    return CSSValueInvalid;
+}
+
+static inline CSSValueID mapFromColumnOrPageBreakInside(CSSValueID value)
+{
+    if (value == CSSValueAuto || value == CSSValueAvoid)
+        return value;
+    return CSSValueInvalid;
+}
+
+static inline CSSPropertyID mapFromLegacyBreakProperty(CSSPropertyID property)
+{
+    if (property == CSSPropertyPageBreakAfter || property == CSSPropertyWebkitColumnBreakAfter)
+        return CSSPropertyBreakAfter;
+    if (property == CSSPropertyPageBreakBefore || property == CSSPropertyWebkitColumnBreakBefore)
+        return CSSPropertyBreakBefore;
+    ASSERT(property == CSSPropertyPageBreakInside || property == CSSPropertyWebkitColumnBreakInside);
+    return CSSPropertyBreakInside;
+}
+
+bool CSSPropertyParser::consumeLegacyBreakProperty(CSSPropertyID property, bool important)
+{
+    // The fragmentation spec says that page-break-(after|before|inside) are to be treated as
+    // shorthands for their break-(after|before|inside) counterparts. We'll do the same for the
+    // non-standard properties -webkit-column-break-(after|before|inside).
+    CSSPrimitiveValue* keyword = consumeIdent(m_range);
+    if (!keyword)
+        return false;
+    if (!m_range.atEnd())
+        return false;
+    CSSValueID value = keyword->getValueID();
+    switch (property) {
+    case CSSPropertyPageBreakAfter:
+    case CSSPropertyPageBreakBefore:
+        value = mapFromPageBreakBetween(value);
+        break;
+    case CSSPropertyWebkitColumnBreakAfter:
+    case CSSPropertyWebkitColumnBreakBefore:
+        value = mapFromColumnBreakBetween(value);
+        break;
+    case CSSPropertyPageBreakInside:
+    case CSSPropertyWebkitColumnBreakInside:
+        value = mapFromColumnOrPageBreakInside(value);
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+    }
+    if (value == CSSValueInvalid)
+        return false;
+
+    CSSPropertyID genericBreakProperty = mapFromLegacyBreakProperty(property);
+    addProperty(genericBreakProperty, property, *CSSPrimitiveValue::createIdentifier(value), important);
+    return true;
+}
+
+static bool consumeBackgroundPosition(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless, CSSValue*& resultX, CSSValue*& resultY)
+{
+    do {
+        CSSValue* positionX = nullptr;
+        CSSValue* positionY = nullptr;
+        if (!consumePosition(range, context.mode(), unitless, positionX, positionY))
+            return false;
+        addBackgroundValue(resultX, positionX);
+        addBackgroundValue(resultY, positionY);
+    } while (consumeCommaIncludingWhitespace(range));
+    return true;
+}
+
+static bool consumeRepeatStyleComponent(CSSParserTokenRange& range, CSSValue*& value1, CSSValue*& value2, bool& implicit)
+{
+    if (consumeIdent<CSSValueRepeatX>(range)) {
+        value1 = CSSPrimitiveValue::createIdentifier(CSSValueRepeat);
+        value2 = CSSPrimitiveValue::createIdentifier(CSSValueNoRepeat);
+        implicit = true;
+        return true;
+    }
+    if (consumeIdent<CSSValueRepeatY>(range)) {
+        value1 = CSSPrimitiveValue::createIdentifier(CSSValueNoRepeat);
+        value2 = CSSPrimitiveValue::createIdentifier(CSSValueRepeat);
+        implicit = true;
+        return true;
+    }
+    value1 = consumeIdent<CSSValueRepeat, CSSValueNoRepeat, CSSValueRound, CSSValueSpace>(range);
+    if (!value1)
+        return false;
+
+    value2 = consumeIdent<CSSValueRepeat, CSSValueNoRepeat, CSSValueRound, CSSValueSpace>(range);
+    if (!value2) {
+        value2 = value1;
+        implicit = true;
+    }
+    return true;
+}
+
+static bool consumeRepeatStyle(CSSParserTokenRange& range, CSSValue*& resultX, CSSValue*& resultY, bool& implicit)
+{
+    do {
+        CSSValue* repeatX = nullptr;
+        CSSValue* repeatY = nullptr;
+        if (!consumeRepeatStyleComponent(range, repeatX, repeatY, implicit))
+            return false;
+        addBackgroundValue(resultX, repeatX);
+        addBackgroundValue(resultY, repeatY);
+    } while (consumeCommaIncludingWhitespace(range));
+    return true;
+}
+
+// Note: consumeBackgroundShorthand assumes y properties (for example background-position-y) follow
+// the x properties in the shorthand array.
+bool CSSPropertyParser::consumeBackgroundShorthand(const StylePropertyShorthand& shorthand, bool important)
+{
+    const unsigned longhandCount = shorthand.length();
+    CSSValue* longhands[10] = { 0 };
+    ASSERT(longhandCount <= 10);
+
+    bool implicit = false;
+    do {
+        bool parsedLonghand[10] = { false };
+        CSSValue* originValue = nullptr;
+        do {
+            bool foundProperty = false;
+            for (size_t i = 0; i < longhandCount; ++i) {
+                if (parsedLonghand[i])
+                    continue;
+
+                CSSValue* value = nullptr;
+                CSSValue* valueY = nullptr;
+                CSSPropertyID property = shorthand.properties()[i];
+                if (property == CSSPropertyBackgroundRepeatX || property == CSSPropertyWebkitMaskRepeatX) {
+                    consumeRepeatStyleComponent(m_range, value, valueY, implicit);
+                } else if (property == CSSPropertyBackgroundPositionX || property == CSSPropertyWebkitMaskPositionX) {
+                    CSSParserTokenRange rangeCopy = m_range;
+                    if (!consumePosition(rangeCopy, m_context.mode(), UnitlessQuirk::Forbid, value, valueY))
+                        continue;
+                    m_range = rangeCopy;
+                } else if (property == CSSPropertyBackgroundSize || property == CSSPropertyWebkitMaskSize) {
+                    if (!consumeSlashIncludingWhitespace(m_range))
+                        continue;
+                    value = consumeBackgroundSize(property, m_range, m_context.mode());
+                    if (!value || !parsedLonghand[i - 1]) // Position must have been parsed in the current layer.
+                        return false;
+                } else if (property == CSSPropertyBackgroundPositionY || property == CSSPropertyBackgroundRepeatY
+                    || property == CSSPropertyWebkitMaskPositionY || property == CSSPropertyWebkitMaskRepeatY) {
+                    continue;
+                } else {
+                    value = consumeBackgroundComponent(property, m_range, m_context);
+                }
+                if (value) {
+                    if (property == CSSPropertyBackgroundOrigin || property == CSSPropertyWebkitMaskOrigin)
+                        originValue = value;
+                    parsedLonghand[i] = true;
+                    foundProperty = true;
+                    addBackgroundValue(longhands[i], value);
+                    if (valueY) {
+                        parsedLonghand[i + 1] = true;
+                        addBackgroundValue(longhands[i + 1], valueY);
+                    }
+                }
+            }
+            if (!foundProperty)
+                return false;
+        } while (!m_range.atEnd() && m_range.peek().type() != CommaToken);
+
+        // FIXME: This will make invalid longhands, see crbug.com/386459
+        for (size_t i = 0; i < longhandCount; ++i) {
+            CSSPropertyID property = shorthand.properties()[i];
+            if (property == CSSPropertyBackgroundColor && !m_range.atEnd()) {
+                if (parsedLonghand[i])
+                    return false; // Colors are only allowed in the last layer.
+                continue;
+            }
+            if ((property == CSSPropertyBackgroundClip || property == CSSPropertyWebkitMaskClip) && !parsedLonghand[i] && originValue) {
+                addBackgroundValue(longhands[i], originValue);
+                continue;
+            }
+            if (!parsedLonghand[i])
+                addBackgroundValue(longhands[i], CSSInitialValue::createLegacyImplicit());
+        }
+    } while (consumeCommaIncludingWhitespace(m_range));
+    if (!m_range.atEnd())
+        return false;
+
+    for (size_t i = 0; i < longhandCount; ++i) {
+        CSSPropertyID property = shorthand.properties()[i];
+        if (property == CSSPropertyBackgroundSize && longhands[i] && m_context.useLegacyBackgroundSizeShorthandBehavior())
+            continue;
+        addProperty(property, shorthand.id(), *longhands[i], important, implicit);
+    }
+    return true;
+}
+
+bool CSSPropertyParser::consumeGridItemPositionShorthand(CSSPropertyID shorthandId, bool important)
+{
+    ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+    const StylePropertyShorthand& shorthand = shorthandForProperty(shorthandId);
+    ASSERT(shorthand.length() == 2);
+    CSSValue* startValue = consumeGridLine(m_range);
+    if (!startValue)
+        return false;
+
+    CSSValue* endValue = nullptr;
+    if (consumeSlashIncludingWhitespace(m_range)) {
+        endValue = consumeGridLine(m_range);
+        if (!endValue)
+            return false;
+    } else {
+        endValue = startValue->isCustomIdentValue() ? startValue : CSSPrimitiveValue::createIdentifier(CSSValueAuto);
+    }
+    if (!m_range.atEnd())
+        return false;
+    addProperty(shorthand.properties()[0], shorthandId, *startValue, important);
+    addProperty(shorthand.properties()[1], shorthandId, *endValue, important);
+    return true;
+}
+
+bool CSSPropertyParser::consumeGridAreaShorthand(bool important)
+{
+    ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+    ASSERT(gridAreaShorthand().length() == 4);
+    CSSValue* rowStartValue = consumeGridLine(m_range);
+    if (!rowStartValue)
+        return false;
+    CSSValue* columnStartValue = nullptr;
+    CSSValue* rowEndValue = nullptr;
+    CSSValue* columnEndValue = nullptr;
+    if (consumeSlashIncludingWhitespace(m_range)) {
+        columnStartValue = consumeGridLine(m_range);
+        if (!columnStartValue)
+            return false;
+        if (consumeSlashIncludingWhitespace(m_range)) {
+            rowEndValue = consumeGridLine(m_range);
+            if (!rowEndValue)
+                return false;
+            if (consumeSlashIncludingWhitespace(m_range)) {
+                columnEndValue = consumeGridLine(m_range);
+                if (!columnEndValue)
+                    return false;
+            }
+        }
+    }
+    if (!m_range.atEnd())
+        return false;
+    if (!columnStartValue)
+        columnStartValue = rowStartValue->isCustomIdentValue() ? rowStartValue : CSSPrimitiveValue::createIdentifier(CSSValueAuto);
+    if (!rowEndValue)
+        rowEndValue = rowStartValue->isCustomIdentValue() ? rowStartValue : CSSPrimitiveValue::createIdentifier(CSSValueAuto);
+    if (!columnEndValue)
+        columnEndValue = columnStartValue->isCustomIdentValue() ? columnStartValue : CSSPrimitiveValue::createIdentifier(CSSValueAuto);
+
+    addProperty(CSSPropertyGridRowStart, CSSPropertyGridArea, *rowStartValue, important);
+    addProperty(CSSPropertyGridColumnStart, CSSPropertyGridArea, *columnStartValue, important);
+    addProperty(CSSPropertyGridRowEnd, CSSPropertyGridArea, *rowEndValue, important);
+    addProperty(CSSPropertyGridColumnEnd, CSSPropertyGridArea, *columnEndValue, important);
+    return true;
+}
+
+bool CSSPropertyParser::consumeGridTemplateRowsAndAreasAndColumns(CSSPropertyID shorthandId, bool important)
+{
+    NamedGridAreaMap gridAreaMap;
+    size_t rowCount = 0;
+    size_t columnCount = 0;
+    CSSValueList* templateRows = CSSValueList::createSpaceSeparated();
+
+    // Persists between loop iterations so we can use the same value for
+    // consecutive <line-names> values
+    CSSGridLineNamesValue* lineNames = nullptr;
+
+    do {
+        // Handle leading <custom-ident>*.
+        bool hasPreviousLineNames = lineNames;
+        lineNames = consumeGridLineNames(m_range, lineNames);
+        if (lineNames && !hasPreviousLineNames)
+            templateRows->append(*lineNames);
+
+        // Handle a template-area's row.
+        if (m_range.peek().type() != StringToken || !parseGridTemplateAreasRow(m_range.consumeIncludingWhitespace().value().toString(), gridAreaMap, rowCount, columnCount))
+            return false;
+        ++rowCount;
+
+        // Handle template-rows's track-size.
+        CSSValue* value = consumeGridTrackSize(m_range, m_context.mode());
+        if (!value)
+            value = CSSPrimitiveValue::createIdentifier(CSSValueAuto);
+        templateRows->append(*value);
+
+        // This will handle the trailing/leading <custom-ident>* in the grammar.
+        lineNames = consumeGridLineNames(m_range);
+        if (lineNames)
+            templateRows->append(*lineNames);
+    } while (!m_range.atEnd() && !(m_range.peek().type() == DelimiterToken && m_range.peek().delimiter() == '/'));
+
+    CSSValue* columnsValue = nullptr;
+    if (!m_range.atEnd()) {
+        if (!consumeSlashIncludingWhitespace(m_range))
+            return false;
+        columnsValue = consumeGridTrackList(m_range, m_context.mode(), GridTemplateNoRepeat);
+        if (!columnsValue || !m_range.atEnd())
+            return false;
+    } else {
+        columnsValue = CSSPrimitiveValue::createIdentifier(CSSValueNone);
+    }
+    addProperty(CSSPropertyGridTemplateRows, shorthandId, *templateRows, important);
+    addProperty(CSSPropertyGridTemplateColumns, shorthandId, *columnsValue, important);
+    addProperty(CSSPropertyGridTemplateAreas, shorthandId, *CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount), important);
+    return true;
+}
+
+bool CSSPropertyParser::consumeGridTemplateShorthand(CSSPropertyID shorthandId, bool important)
+{
+    ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+    ASSERT(gridTemplateShorthand().length() == 3);
+
+    CSSParserTokenRange rangeCopy = m_range;
+    CSSValue* rowsValue = consumeIdent<CSSValueNone>(m_range);
+
+    // 1- 'none' case.
+    if (rowsValue && m_range.atEnd()) {
+        addProperty(CSSPropertyGridTemplateRows, shorthandId, *CSSPrimitiveValue::createIdentifier(CSSValueNone), important);
+        addProperty(CSSPropertyGridTemplateColumns, shorthandId, *CSSPrimitiveValue::createIdentifier(CSSValueNone), important);
+        addProperty(CSSPropertyGridTemplateAreas, shorthandId, *CSSPrimitiveValue::createIdentifier(CSSValueNone), important);
+        return true;
+    }
+
+    // 2- <grid-template-rows> / <grid-template-columns>
+    if (!rowsValue)
+        rowsValue = consumeGridTrackList(m_range, m_context.mode(), GridTemplate);
+
+    if (rowsValue) {
+        if (!consumeSlashIncludingWhitespace(m_range))
+            return false;
+        CSSValue* columnsValue = consumeGridTemplatesRowsOrColumns(m_range, m_context.mode());
+        if (!columnsValue || !m_range.atEnd())
+            return false;
+
+        addProperty(CSSPropertyGridTemplateRows, shorthandId, *rowsValue, important);
+        addProperty(CSSPropertyGridTemplateColumns, shorthandId, *columnsValue, important);
+        addProperty(CSSPropertyGridTemplateAreas, shorthandId, *CSSPrimitiveValue::createIdentifier(CSSValueNone), important);
+        return true;
+    }
+
+    // 3- [ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <track-list> ]?
+    m_range = rangeCopy;
+    return consumeGridTemplateRowsAndAreasAndColumns(shorthandId, important);
+}
+
+bool CSSPropertyParser::consumeGridShorthand(bool important)
+{
+    ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
+    ASSERT(shorthandForProperty(CSSPropertyGrid).length() == 8);
+
+    CSSParserTokenRange rangeCopy = m_range;
+
+    // 1- <grid-template>
+    if (consumeGridTemplateShorthand(CSSPropertyGrid, important)) {
+        // It can only be specified the explicit or the implicit grid properties in a single grid declaration.
+        // The sub-properties not specified are set to their initial value, as normal for shorthands.
+        addProperty(CSSPropertyGridAutoFlow, CSSPropertyGrid, *CSSInitialValue::createLegacyImplicit(), important);
+        addProperty(CSSPropertyGridAutoColumns, CSSPropertyGrid, *CSSInitialValue::createLegacyImplicit(), important);
+        addProperty(CSSPropertyGridAutoRows, CSSPropertyGrid, *CSSInitialValue::createLegacyImplicit(), important);
+        addProperty(CSSPropertyGridColumnGap, CSSPropertyGrid, *CSSInitialValue::createLegacyImplicit(), important);
+        addProperty(CSSPropertyGridRowGap, CSSPropertyGrid, *CSSInitialValue::createLegacyImplicit(), important);
+        return true;
+    }
+
+    m_range = rangeCopy;
+
+    // 2- <grid-auto-flow> [ <grid-auto-rows> [ / <grid-auto-columns> ]? ]
+    CSSValueList* gridAutoFlow = consumeGridAutoFlow(m_range);
+    if (!gridAutoFlow)
+        return false;
+
+    CSSValue* autoColumnsValue = nullptr;
+    CSSValue* autoRowsValue = nullptr;
+
+    if (!m_range.atEnd()) {
+        autoRowsValue = consumeGridTrackList(m_range, m_context.mode(), GridAuto);
+        if (!autoRowsValue)
+            return false;
+        if (consumeSlashIncludingWhitespace(m_range)) {
+            autoColumnsValue = consumeGridTrackList(m_range, m_context.mode(), GridAuto);
+            if (!autoColumnsValue)
+                return false;
+        }
+        if (!m_range.atEnd())
+            return false;
+    } else {
+        // Other omitted values are set to their initial values.
+        autoColumnsValue = CSSInitialValue::createLegacyImplicit();
+        autoRowsValue = CSSInitialValue::createLegacyImplicit();
+    }
+
+    // if <grid-auto-columns> value is omitted, it is set to the value specified for grid-auto-rows.
+    if (!autoColumnsValue)
+        autoColumnsValue = autoRowsValue;
+
+    // It can only be specified the explicit or the implicit grid properties in a single grid declaration.
+    // The sub-properties not specified are set to their initial value, as normal for shorthands.
+    addProperty(CSSPropertyGridTemplateColumns, CSSPropertyGrid, *CSSInitialValue::createLegacyImplicit(), important);
+    addProperty(CSSPropertyGridTemplateRows, CSSPropertyGrid, *CSSInitialValue::createLegacyImplicit(), important);
+    addProperty(CSSPropertyGridTemplateAreas, CSSPropertyGrid, *CSSInitialValue::createLegacyImplicit(), important);
+    addProperty(CSSPropertyGridAutoFlow, CSSPropertyGrid, *gridAutoFlow, important);
+    addProperty(CSSPropertyGridAutoColumns, CSSPropertyGrid, *autoColumnsValue, important);
+    addProperty(CSSPropertyGridAutoRows, CSSPropertyGrid, *autoRowsValue, important);
+    addProperty(CSSPropertyGridColumnGap, CSSPropertyGrid, *CSSInitialValue::createLegacyImplicit(), important);
+    addProperty(CSSPropertyGridRowGap, CSSPropertyGrid, *CSSInitialValue::createLegacyImplicit(), important);
+    return true;
+}
+
+bool CSSPropertyParser::parseShorthand(CSSPropertyID unresolvedProperty, bool important)
+{
+    CSSPropertyID property = resolveCSSPropertyID(unresolvedProperty);
+
+    switch (property) {
+    case CSSPropertyWebkitMarginCollapse: {
+        CSSValueID id = m_range.consumeIncludingWhitespace().id();
+        if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyWebkitMarginBeforeCollapse, id, m_context.mode()))
+            return false;
+        CSSValue* beforeCollapse = CSSPrimitiveValue::createIdentifier(id);
+        addProperty(CSSPropertyWebkitMarginBeforeCollapse, CSSPropertyWebkitMarginCollapse, *beforeCollapse, important);
+        if (m_range.atEnd()) {
+            addProperty(CSSPropertyWebkitMarginAfterCollapse, CSSPropertyWebkitMarginCollapse, *beforeCollapse, important);
+            return true;
+        }
+        id = m_range.consumeIncludingWhitespace().id();
+        if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyWebkitMarginAfterCollapse, id, m_context.mode()))
+            return false;
+        addProperty(CSSPropertyWebkitMarginAfterCollapse, CSSPropertyWebkitMarginCollapse, *CSSPrimitiveValue::createIdentifier(id), important);
+        return true;
+    }
+    case CSSPropertyOverflow: {
+        CSSValueID id = m_range.consumeIncludingWhitespace().id();
+        if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyOverflowY, id, m_context.mode()))
+            return false;
+        if (!m_range.atEnd())
+            return false;
+        CSSValue* overflowYValue = CSSPrimitiveValue::createIdentifier(id);
+
+        CSSValue* overflowXValue = nullptr;
+
+        // FIXME: -webkit-paged-x or -webkit-paged-y only apply to overflow-y. If this value has been
+        // set using the shorthand, then for now overflow-x will default to auto, but once we implement
+        // pagination controls, it should default to hidden. If the overflow-y value is anything but
+        // paged-x or paged-y, then overflow-x and overflow-y should have the same value.
+        if (id == CSSValueWebkitPagedX || id == CSSValueWebkitPagedY)
+            overflowXValue = CSSPrimitiveValue::createIdentifier(CSSValueAuto);
+        else
+            overflowXValue = overflowYValue;
+        addProperty(CSSPropertyOverflowX, CSSPropertyOverflow, *overflowXValue, important);
+        addProperty(CSSPropertyOverflowY, CSSPropertyOverflow, *overflowYValue, important);
+        return true;
+    }
+    case CSSPropertyFont: {
+        const CSSParserToken& token = m_range.peek();
+        if (token.id() >= CSSValueCaption && token.id() <= CSSValueStatusBar)
+            return consumeSystemFont(important);
+        return consumeFont(important);
+    }
+    case CSSPropertyFontVariant:
+        return consumeFontVariantShorthand(important);
+    case CSSPropertyBorderSpacing:
+        return consumeBorderSpacing(important);
+    case CSSPropertyColumns:
+        return consumeColumns(important);
+    case CSSPropertyAnimation:
+        return consumeAnimationShorthand(animationShorthandForParsing(), unresolvedProperty == CSSPropertyAliasWebkitAnimation, important);
+    case CSSPropertyTransition:
+        return consumeAnimationShorthand(transitionShorthandForParsing(), false, important);
+    case CSSPropertyTextDecoration:
+        ASSERT(RuntimeEnabledFeatures::css3TextDecorationsEnabled());
+        return consumeShorthandGreedily(textDecorationShorthand(), important);
+    case CSSPropertyMargin:
+        return consume4Values(marginShorthand(), important);
+    case CSSPropertyPadding:
+        return consume4Values(paddingShorthand(), important);
+    case CSSPropertyMotion:
+        return consumeShorthandGreedily(motionShorthand(), important);
+    case CSSPropertyWebkitTextEmphasis:
+        return consumeShorthandGreedily(webkitTextEmphasisShorthand(), important);
+    case CSSPropertyOutline:
+        return consumeShorthandGreedily(outlineShorthand(), important);
+    case CSSPropertyWebkitBorderStart:
+        return consumeShorthandGreedily(webkitBorderStartShorthand(), important);
+    case CSSPropertyWebkitBorderEnd:
+        return consumeShorthandGreedily(webkitBorderEndShorthand(), important);
+    case CSSPropertyWebkitBorderBefore:
+        return consumeShorthandGreedily(webkitBorderBeforeShorthand(), important);
+    case CSSPropertyWebkitBorderAfter:
+        return consumeShorthandGreedily(webkitBorderAfterShorthand(), important);
+    case CSSPropertyWebkitTextStroke:
+        return consumeShorthandGreedily(webkitTextStrokeShorthand(), important);
+    case CSSPropertyMarker: {
+        const CSSValue* marker = parseSingleValue(CSSPropertyMarkerStart);
+        if (!marker || !m_range.atEnd())
+            return false;
+        addProperty(CSSPropertyMarkerStart, CSSPropertyMarker, *marker, important);
+        addProperty(CSSPropertyMarkerMid, CSSPropertyMarker, *marker, important);
+        addProperty(CSSPropertyMarkerEnd, CSSPropertyMarker, *marker, important);
+        return true;
+    }
+    case CSSPropertyFlex:
+        return consumeFlex(important);
+    case CSSPropertyFlexFlow:
+        return consumeShorthandGreedily(flexFlowShorthand(), important);
+    case CSSPropertyColumnRule:
+        return consumeShorthandGreedily(columnRuleShorthand(), important);
+    case CSSPropertyListStyle:
+        return consumeShorthandGreedily(listStyleShorthand(), important);
+    case CSSPropertyBorderRadius: {
+        CSSPrimitiveValue* horizontalRadii[4] = { 0 };
+        CSSPrimitiveValue* verticalRadii[4] = { 0 };
+        if (!consumeRadii(horizontalRadii, verticalRadii, m_range, m_context.mode(), unresolvedProperty == CSSPropertyAliasWebkitBorderRadius))
+            return false;
+        addProperty(CSSPropertyBorderTopLeftRadius, CSSPropertyBorderRadius, *CSSValuePair::create(horizontalRadii[0], verticalRadii[0], CSSValuePair::DropIdenticalValues), important);
+        addProperty(CSSPropertyBorderTopRightRadius, CSSPropertyBorderRadius, *CSSValuePair::create(horizontalRadii[1], verticalRadii[1], CSSValuePair::DropIdenticalValues), important);
+        addProperty(CSSPropertyBorderBottomRightRadius, CSSPropertyBorderRadius, *CSSValuePair::create(horizontalRadii[2], verticalRadii[2], CSSValuePair::DropIdenticalValues), important);
+        addProperty(CSSPropertyBorderBottomLeftRadius, CSSPropertyBorderRadius, *CSSValuePair::create(horizontalRadii[3], verticalRadii[3], CSSValuePair::DropIdenticalValues), important);
+        return true;
+    }
+    case CSSPropertyBorderColor:
+        return consume4Values(borderColorShorthand(), important);
+    case CSSPropertyBorderStyle:
+        return consume4Values(borderStyleShorthand(), important);
+    case CSSPropertyBorderWidth:
+        return consume4Values(borderWidthShorthand(), important);
+    case CSSPropertyBorderTop:
+        return consumeShorthandGreedily(borderTopShorthand(), important);
+    case CSSPropertyBorderRight:
+        return consumeShorthandGreedily(borderRightShorthand(), important);
+    case CSSPropertyBorderBottom:
+        return consumeShorthandGreedily(borderBottomShorthand(), important);
+    case CSSPropertyBorderLeft:
+        return consumeShorthandGreedily(borderLeftShorthand(), important);
+    case CSSPropertyBorder:
+        return consumeBorder(important);
+    case CSSPropertyBorderImage:
+    case CSSPropertyWebkitMaskBoxImage:
+        return consumeBorderImage(property, important);
+    case CSSPropertyPageBreakAfter:
+    case CSSPropertyPageBreakBefore:
+    case CSSPropertyPageBreakInside:
+    case CSSPropertyWebkitColumnBreakAfter:
+    case CSSPropertyWebkitColumnBreakBefore:
+    case CSSPropertyWebkitColumnBreakInside:
+        return consumeLegacyBreakProperty(property, important);
+    case CSSPropertyWebkitMaskPosition:
+    case CSSPropertyBackgroundPosition: {
+        CSSValue* resultX = nullptr;
+        CSSValue* resultY = nullptr;
+        if (!consumeBackgroundPosition(m_range, m_context, UnitlessQuirk::Allow, resultX, resultY) || !m_range.atEnd())
+            return false;
+        addProperty(property == CSSPropertyBackgroundPosition ? CSSPropertyBackgroundPositionX : CSSPropertyWebkitMaskPositionX, property, *resultX, important);
+        addProperty(property == CSSPropertyBackgroundPosition ? CSSPropertyBackgroundPositionY : CSSPropertyWebkitMaskPositionY, property, *resultY, important);
+        return true;
+    }
+    case CSSPropertyBackgroundRepeat:
+    case CSSPropertyWebkitMaskRepeat: {
+        CSSValue* resultX = nullptr;
+        CSSValue* resultY = nullptr;
+        bool implicit = false;
+        if (!consumeRepeatStyle(m_range, resultX, resultY, implicit) || !m_range.atEnd())
+            return false;
+        addProperty(property == CSSPropertyBackgroundRepeat ? CSSPropertyBackgroundRepeatX : CSSPropertyWebkitMaskRepeatX, property, *resultX, important, implicit);
+        addProperty(property == CSSPropertyBackgroundRepeat ? CSSPropertyBackgroundRepeatY : CSSPropertyWebkitMaskRepeatY, property, *resultY, important, implicit);
+        return true;
+    }
+    case CSSPropertyBackground:
+        return consumeBackgroundShorthand(backgroundShorthand(), important);
+    case CSSPropertyWebkitMask:
+        return consumeBackgroundShorthand(webkitMaskShorthand(), important);
+    case CSSPropertyGridGap: {
+        ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled() && shorthandForProperty(CSSPropertyGridGap).length() == 2);
+        CSSValue* rowGap = consumeLength(m_range, m_context.mode(), ValueRangeNonNegative);
+        CSSValue* columnGap = consumeLength(m_range, m_context.mode(), ValueRangeNonNegative);
+        if (!rowGap || !m_range.atEnd())
+            return false;
+        if (!columnGap)
+            columnGap = rowGap;
+        addProperty(CSSPropertyGridRowGap, CSSPropertyGridGap, *rowGap, important);
+        addProperty(CSSPropertyGridColumnGap, CSSPropertyGridGap, *columnGap, important);
+        return true;
+    }
+    case CSSPropertyGridColumn:
+    case CSSPropertyGridRow:
+        return consumeGridItemPositionShorthand(property, important);
+    case CSSPropertyGridArea:
+        return consumeGridAreaShorthand(important);
+    case CSSPropertyGridTemplate:
+        return consumeGridTemplateShorthand(CSSPropertyGridTemplate, important);
+    case CSSPropertyGrid:
+        return consumeGridShorthand(important);
+    default:
+        return false;
+    }
+}
+*/
+
+} // namespace WebCore
diff --git a/Source/WebCore/css/parser/CSSPropertyParser.h b/Source/WebCore/css/parser/CSSPropertyParser.h
new file mode 100644 (file)
index 0000000..b353188
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2003 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 - 2010  Torch Mobile (Beijing) Co. Ltd. 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef CSSPropertyParser_h
+#define CSSPropertyParser_h
+
+#include "CSSParserTokenRange.h"
+#include "StyleRule.h"
+#include <wtf/text/StringView.h>
+
+namespace WebCore {
+
+class CSSProperty;
+class CSSValue;
+class StylePropertyShorthand;
+
+// Inputs: PropertyID, isImportant bool, CSSParserTokenRange.
+// Outputs: Vector of CSSProperties
+
+class CSSPropertyParser {
+    WTF_MAKE_NONCOPYABLE(CSSPropertyParser);
+public:
+    static bool parseValue(CSSPropertyID, bool important,
+        const CSSParserTokenRange&, const CSSParserContext&,
+        Vector<CSSProperty, 256>&, StyleRule::Type);
+
+    // Parses a non-shorthand CSS property
+    static const CSSValue* parseSingleValue(CSSPropertyID, const CSSParserTokenRange&, const CSSParserContext&);
+
+private:
+    CSSPropertyParser(const CSSParserTokenRange&, const CSSParserContext&,
+        Vector<CSSProperty, 256>*);
+
+    // FIXME: Rename once the CSSParserValue-based parseValue is removed
+    bool parseValueStart(CSSPropertyID unresolvedProperty, bool important);
+    bool consumeCSSWideKeyword(CSSPropertyID unresolvedProperty, bool important);
+    const CSSValue* parseSingleValue(CSSPropertyID, CSSPropertyID = CSSPropertyInvalid);
+
+    bool inQuirksMode() const { return m_context.mode == CSSQuirksMode; }
+
+    bool parseViewportDescriptor(CSSPropertyID propId, bool important);
+    bool parseFontFaceDescriptor(CSSPropertyID);
+
+    void addProperty(CSSPropertyID, CSSPropertyID, const CSSValue&, bool important, bool implicit = false);
+    void addExpandedPropertyForValue(CSSPropertyID propId, const CSSValue&, bool);
+
+    bool consumeBorder(bool important);
+
+    bool parseShorthand(CSSPropertyID, bool important);
+    bool consumeShorthandGreedily(const StylePropertyShorthand&, bool important);
+    bool consume4Values(const StylePropertyShorthand&, bool important);
+
+    // Legacy parsing allows <string>s for animation-name
+    bool consumeAnimationShorthand(const StylePropertyShorthand&, bool useLegacyParsing, bool important);
+    bool consumeBackgroundShorthand(const StylePropertyShorthand&, bool important);
+
+    bool consumeColumns(bool important);
+
+    bool consumeGridItemPositionShorthand(CSSPropertyID, bool important);
+   &nb