Chromium Code Reviews| Index: Source/core/css/parser/CSSParserFastPaths.cpp |
| diff --git a/Source/core/css/parser/CSSParserFastPaths.cpp b/Source/core/css/parser/CSSParserFastPaths.cpp |
| index 85143c11097a2e08d49ada99964fea49d4d9bfc8..caa105f12a68d330a705e923159308a5a1dc6585 100644 |
| --- a/Source/core/css/parser/CSSParserFastPaths.cpp |
| +++ b/Source/core/css/parser/CSSParserFastPaths.cpp |
| @@ -11,6 +11,7 @@ |
| #include "core/css/parser/CSSParserIdioms.h" |
| #include "core/css/parser/CSSParserValues.h" |
| #include "core/css/parser/CSSPropertyParser.h" |
| +#include "core/html/parser/HTMLParserIdioms.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| namespace blink { |
| @@ -142,30 +143,328 @@ static inline bool isColorPropertyID(CSSPropertyID propertyId) |
| } |
| } |
| -static PassRefPtrWillBeRawPtr<CSSValue> parseColorValue(CSSPropertyID propertyId, const String& string, CSSParserMode cssParserMode) |
| +// 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::UnitType& 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::CSS_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::CSS_PERCENTAGE && *current != '%') |
| + return false; |
| + |
| + if (*current == '%') { |
| + expect = CSSPrimitiveValue::CSS_PERCENTAGE; |
| + localValue = localValue / 100.0 * 256.0; |
| + // Clamp values at 255 for percentages over 100% |
| + if (localValue > 255) |
| + localValue = 255; |
| + current++; |
| + } else { |
| + expect = CSSPrimitiveValue::CSS_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) |
|
alancutter (OOO until 2018)
2015/06/10 00:31:20
parseRGBA32Internal()?
|
| +{ |
| + CSSPrimitiveValue::UnitType expect = CSSPrimitiveValue::CSS_UNKNOWN; |
| + |
| + if (length >= 4 && characters[0] == '#') |
| + return Color::parseHexColor(characters + 1, length - 1, rgb); |
| + |
| + if (quirksMode && length >= 3) { |
| + 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; |
| +} |
| + |
| +bool CSSParserFastPaths::parseColorAsRGBA32(RGBA32& rgb, const String& name, bool quirksMode) |
| +{ |
| + unsigned length = name.length(); |
| + bool parseResult; |
| + |
| + if (!length) |
| + return false; |
| + |
| + if (name.is8Bit()) |
| + parseResult = fastParseColorInternal(rgb, name.characters8(), length, quirksMode); |
| + else |
| + parseResult = fastParseColorInternal(rgb, name.characters16(), length, quirksMode); |
| + |
| + if (parseResult) |
| + return true; |
| + |
| + // Try named colors. |
| + Color tc; |
| + if (!tc.setNamedColor(name)) |
| + return false; |
| + rgb = tc.rgb(); |
| + return true; |
| +} |
| + |
| +static PassRefPtrWillBeRawPtr<CSSValue> parseColor(const String& string, bool quirksMode) |
| { |
| ASSERT(!string.isEmpty()); |
| - bool quirksMode = isQuirksModeBehavior(cssParserMode); |
| - if (!isColorPropertyID(propertyId)) |
| - return nullptr; |
| CSSParserString cssString; |
| cssString.init(string); |
| CSSValueID valueID = cssValueKeywordID(cssString); |
| - bool validPrimitive = false; |
| - if (valueID == CSSValueWebkitText) { |
| - validPrimitive = true; |
| - } else if (valueID == CSSValueCurrentcolor) { |
| - validPrimitive = true; |
| - } else if ((valueID >= CSSValueAqua && valueID <= CSSValueWindowtext) || valueID == CSSValueMenu |
| - || (quirksMode && valueID >= CSSValueWebkitFocusRingColor && valueID < CSSValueWebkitText)) { |
| - validPrimitive = true; |
| - } |
| - |
| - if (validPrimitive) |
| + if (valueID == CSSValueWebkitText || valueID == CSSValueCurrentcolor |
| + || (valueID >= CSSValueAqua && valueID <= CSSValueWindowtext) || valueID == CSSValueMenu |
| + || (quirksMode && valueID >= CSSValueWebkitFocusRingColor && valueID < CSSValueWebkitText)) |
| return cssValuePool().createIdentifierValue(valueID); |
| RGBA32 color; |
| - if (!CSSPropertyParser::fastParseColor(color, string, !quirksMode && string[0] != '#')) |
| + if (!CSSParserFastPaths::parseColorAsRGBA32(color, string, quirksMode)) |
| return nullptr; |
| return cssValuePool().createColorValue(color); |
| } |
| @@ -660,8 +959,8 @@ PassRefPtrWillBeRawPtr<CSSValue> CSSParserFastPaths::maybeParseValue(CSSProperty |
| { |
| if (RefPtrWillBeRawPtr<CSSValue> length = parseSimpleLengthValue(propertyID, string, parserMode)) |
| return length.release(); |
| - if (RefPtrWillBeRawPtr<CSSValue> color = parseColorValue(propertyID, string, parserMode)) |
| - return color.release(); |
| + if (isColorPropertyID(propertyID)) |
| + return parseColor(string, isQuirksModeBehavior(parserMode)); |
| if (RefPtrWillBeRawPtr<CSSValue> keyword = parseKeywordValue(propertyID, string)) |
| return keyword.release(); |
| if (RefPtrWillBeRawPtr<CSSValue> transform = parseSimpleTransform(propertyID, string)) |