diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/css/parser/CSSParserFastPaths.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/css/parser/CSSParserFastPaths.cpp')
-rw-r--r-- | Source/WebCore/css/parser/CSSParserFastPaths.cpp | 1258 |
1 files changed, 1258 insertions, 0 deletions
diff --git a/Source/WebCore/css/parser/CSSParserFastPaths.cpp b/Source/WebCore/css/parser/CSSParserFastPaths.cpp new file mode 100644 index 000000000..f563a9a6d --- /dev/null +++ b/Source/WebCore/css/parser/CSSParserFastPaths.cpp @@ -0,0 +1,1258 @@ +// 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" + +#include "CSSFunctionValue.h" +#include "CSSInheritedValue.h" +#include "CSSInitialValue.h" +#include "CSSParserIdioms.h" +#include "CSSPrimitiveValue.h" +#include "CSSPropertyParser.h" +#include "CSSValueList.h" +#include "CSSValuePool.h" +#include "HTMLParserIdioms.h" +#include "RuntimeEnabledFeatures.h" +#include "StyleColor.h" +#include "StylePropertyShorthand.h" +#include "StyleSheetContents.h" + +namespace WebCore { + +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: + acceptsNegativeNumbers = false; + return true; + case CSSPropertyBottom: + case CSSPropertyCx: + case CSSPropertyCy: + case CSSPropertyLeft: + case CSSPropertyMarginBottom: + case CSSPropertyMarginLeft: + case CSSPropertyMarginRight: + case CSSPropertyMarginTop: + case CSSPropertyRight: + case CSSPropertyTop: + case CSSPropertyWebkitMarginAfter: + case CSSPropertyWebkitMarginBefore: + case CSSPropertyWebkitMarginEnd: + case CSSPropertyWebkitMarginStart: + case CSSPropertyX: + case CSSPropertyY: + case CSSPropertyR: + case CSSPropertyRx: + case CSSPropertyRy: + acceptsNegativeNumbers = true; + return true; + default: + return false; + } +} + +template <typename CharacterType> +static inline bool parseSimpleLength(const CharacterType* characters, unsigned length, CSSPrimitiveValue::UnitType& unit, double& number) +{ + if (length > 2 && (characters[length - 2] | 0x20) == 'p' && (characters[length - 1] | 0x20) == 'x') { + length -= 2; + unit = CSSPrimitiveValue::UnitType::CSS_PX; + } else if (length > 1 && characters[length - 1] == '%') { + length -= 1; + unit = CSSPrimitiveValue::UnitType::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; + return true; +} + +static RefPtr<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::UnitType unit = CSSPrimitiveValue::UnitType::CSS_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::UnitType::CSS_NUMBER) { + if (number && cssParserMode != SVGAttributeMode) + return nullptr; + unit = CSSPrimitiveValue::UnitType::CSS_PX; + } + + if (number < 0 && !acceptsNegativeNumbers) + return nullptr; + if (std::isinf(number)) + 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 CSSPropertyWebkitTextDecorationColor: + 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::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::UnitType::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::UnitType::CSS_PERCENTAGE && *current != '%') + return false; + + if (*current == '%') { + expect = CSSPrimitiveValue::UnitType::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::UnitType::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 Color fastParseColorInternal(const CharacterType* characters, unsigned length, bool quirksMode) +{ + CSSPrimitiveValue::UnitType expect = CSSPrimitiveValue::UnitType::CSS_UNKNOWN; + + if (length >= 4 && characters[0] == '#') { + RGBA32 rgb; + if (Color::parseHexColor(characters + 1, length - 1, rgb)) + return Color(rgb); + } + + if (quirksMode && (length == 3 || length == 6)) { + RGBA32 rgb; + if (Color::parseHexColor(characters, length, rgb)) + return Color(rgb); + } + + // 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 Color(); + if (!parseColorIntOrPercentage(current, end, ',', expect, green)) + return Color(); + if (!parseColorIntOrPercentage(current, end, ',', expect, blue)) + return Color(); + if (!parseAlphaValue(current, end, ')', alpha)) + return Color(); + if (current != end) + return Color(); + return Color(makeRGBA(red, green, blue, alpha)); + } + + // 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 Color(); + if (!parseColorIntOrPercentage(current, end, ',', expect, green)) + return Color(); + if (!parseColorIntOrPercentage(current, end, ')', expect, blue)) + return Color(); + if (current != end) + return Color(); + return Color(makeRGB(red, green, blue)); + } + + return Color(); +} + +RefPtr<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 CSSValuePool::singleton().createIdentifierValue(valueID); + } + + bool quirksMode = isQuirksModeBehavior(parserMode); + + // Fast path for hex colors and rgb()/rgba() colors + Color color; + if (string.is8Bit()) + color = fastParseColorInternal(string.characters8(), string.length(), quirksMode); + else + color = fastParseColorInternal(string.characters16(), string.length(), quirksMode); + if (!color.isValid()) + return nullptr; + return CSSValuePool::singleton().createColorValue(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 <= CSSValueContents) || valueID == CSSValueNone + || (RuntimeEnabledFeatures::sharedFeatures().isCSSGridLayoutEnabled() && (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 | optimizeSpeed | crispEdges | optimizeQuality | webkit-crispEdges + return valueID == CSSValueAuto || valueID == CSSValueOptimizeSpeed || valueID == CSSValueOptimizeQuality || valueID == CSSValueWebkitCrispEdges || valueID == CSSValueWebkitOptimizeContrast || valueID == CSSValueCrispEdges || valueID == CSSValuePixelated; +#if ENABLE(CSS_COMPOSITING) + case CSSPropertyIsolation: // auto | isolate + return valueID == CSSValueAuto || valueID == CSSValueIsolate; +#endif + 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); + // FIXME-NEWPARSER: Support? + // 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 +#if ENABLE(CSS_REGIONS) + || valueID == CSSValueRegion || valueID == CSSValueAvoidRegion +#endif + ; + case CSSPropertyBreakInside: + return valueID == CSSValueAuto || valueID == CSSValueAvoid || valueID == CSSValueAvoidPage || valueID == CSSValueAvoidColumn +#if ENABLE(CSS_REGIONS) + || valueID == CSSValueAvoidRegion +#endif + ; + 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 <= CSSValueStroke); + case CSSPropertyPosition: // static | relative | absolute | fixed | sticky + return valueID == CSSValueStatic || valueID == CSSValueRelative || valueID == CSSValueAbsolute || valueID == CSSValueFixed || valueID == CSSValueWebkitSticky; + case CSSPropertyResize: // none | both | horizontal | vertical | auto + return valueID == CSSValueNone || valueID == CSSValueBoth || valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueAuto; + // FIXME-NEWPARSER: Investigate this property. + // 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 <= CSSValueWebkitMatchParent) || valueID == CSSValueStart || valueID == CSSValueEnd; +#if ENABLE(CSS3_TEXT) + case CSSPropertyWebkitTextAlignLast: + // auto | start | end | left | right | center | justify + return (valueID >= CSSValueLeft && valueID <= CSSValueJustify) || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueAuto; +#endif + case CSSPropertyTextAnchor: + return valueID == CSSValueStart || valueID == CSSValueMiddle || valueID == CSSValueEnd; +// FIXME-NEWPARSER: Support +// case CSSPropertyTextCombineUpright: +// return valueID == CSSValueNone || valueID == CSSValueAll; + case CSSPropertyWebkitTextDecorationStyle: + // solid | double | dotted | dashed | wavy + return valueID == CSSValueSolid || valueID == CSSValueDouble || valueID == CSSValueDotted || valueID == CSSValueDashed || valueID == CSSValueWavy; +#if ENABLE(CSS3_TEXT) + case CSSPropertyWebkitTextJustify: + // auto | none | inter-word | distribute + return valueID == CSSValueInterWord || valueID == CSSValueDistribute || valueID == CSSValueAuto || valueID == CSSValueNone; +#endif + case CSSPropertyWebkitTextOrientation: // mixed | upright | sideways | sideways-right + return valueID == CSSValueMixed || valueID == CSSValueUpright || valueID == CSSValueSideways || valueID == CSSValueSidewaysRight; + 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; + case CSSPropertyVectorEffect: + return valueID == CSSValueNone || valueID == CSSValueNonScalingStroke; + case CSSPropertyVisibility: // visible | hidden | collapse + return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueCollapse; + case CSSPropertyWebkitAppearance: + return (valueID >= CSSValueCheckbox && valueID <= CSSValueCapsLockIndicator) || valueID == CSSValueNone; + case CSSPropertyWebkitBackfaceVisibility: + return valueID == CSSValueVisible || valueID == CSSValueHidden; +#if ENABLE(CSS_COMPOSITING) + 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 || valueID == CSSValuePlusDarker || valueID == CSSValuePlusLighter; +#endif + case CSSPropertyWebkitBoxAlign: + return valueID == CSSValueStretch || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline; +#if ENABLE(CSS_BOX_DECORATION_BREAK) + case CSSPropertyWebkitBoxDecorationBreak: + return valueID == CSSValueClone || valueID == CSSValueSlice; + case CSSPropertyWebkitBoxDirection: + return valueID == CSSValueNormal || valueID == CSSValueReverse; +#endif + 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; +#if ENABLE(CURSOR_VISIBILITY) + case CSSPropertyWebkitCursorVisibility: + return valueID == CSSValueAuto || valueID == CSSValueAutoHide; +#endif + case CSSPropertyColumnFill: + return valueID == CSSValueAuto || valueID == CSSValueBalance; + case CSSPropertyWebkitColumnAxis: + return valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueAuto; + case CSSPropertyWebkitColumnProgression: + return valueID == CSSValueNormal || valueID == CSSValueReverse; + 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 CSSPropertyWebkitHyphens: + 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 CSSPropertyWebkitFontKerning: + return valueID == CSSValueAuto || valueID == CSSValueNormal || valueID == CSSValueNone; + case CSSPropertyWebkitFontSmoothing: + return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueAntialiased || valueID == CSSValueSubpixelAntialiased; + case CSSPropertyWebkitLineAlign: + return valueID == CSSValueNone || valueID == CSSValueEdges; + case CSSPropertyWebkitLineBreak: // auto | loose | normal | strict | after-white-space + return valueID == CSSValueAuto || valueID == CSSValueLoose || valueID == CSSValueNormal || valueID == CSSValueStrict || valueID == CSSValueAfterWhiteSpace; + case CSSPropertyWebkitLineSnap: + return valueID == CSSValueNone || valueID == CSSValueBaseline || valueID == CSSValueContain; + 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 || valueID == CSSValueInterCharacter; + case CSSPropertyWebkitTextCombine: + return valueID == CSSValueNone || valueID == CSSValueHorizontal; + case CSSPropertyWebkitTextSecurity: // disc | circle | square | none + return valueID == CSSValueDisc || valueID == CSSValueCircle || valueID == CSSValueSquare || valueID == CSSValueNone; + case CSSPropertyTransformStyle: + case CSSPropertyWebkitTransformStyle: + 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 CSSPropertyWebkitUserSelect: // auto | none | text | all + return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueText || valueID == CSSValueAll; + case CSSPropertyWritingMode: + // Note that horizontal-bt is not supported by the unprefixed version of + // the property, only by the -webkit- version. + return (valueID >= CSSValueHorizontalTb && valueID <= CSSValueHorizontalBt) + || 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 CSSPropertyWebkitBorderFit: + return valueID == CSSValueBorder || valueID == CSSValueLines; +#if ENABLE(CSS_REGIONS) + case CSSPropertyWebkitRegionFragment: + return valueID == CSSValueAuto || valueID == CSSValueBreak; +#endif +#if ENABLE(TOUCH_EVENTS) + case CSSPropertyTouchAction: // auto | manipulation + return valueID == CSSValueAuto || valueID == CSSValueManipulation; +#endif +#if ENABLE(CSS_TRAILING_WORD) + case CSSPropertyAppleTrailingWord: // auto | -apple-partially-balanced + return valueID == CSSValueAuto || valueID == CSSValueWebkitPartiallyBalanced; +#endif +#if ENABLE(APPLE_PAY) + case CSSPropertyApplePayButtonStyle: // white | white-outline | black + return valueID == CSSValueWhite || valueID == CSSValueWhiteOutline || valueID == CSSValueBlack; + case CSSPropertyApplePayButtonType: // plain | buy | set-up | donate + return valueID == CSSValuePlain || valueID == CSSValueBuy || valueID == CSSValueSetUp || valueID == CSSValueDonate; +#endif + case CSSPropertyWebkitNbspMode: // normal | space + return valueID == CSSValueNormal || valueID == CSSValueSpace; + case CSSPropertyWebkitTextZoom: + return valueID == CSSValueNormal || valueID == CSSValueReset; +#if PLATFORM(IOS) + // Apple specific property. These will never be standardized and is purely to + // support custom WebKit-based Apple applications. + case CSSPropertyWebkitTouchCallout: + return valueID == CSSValueDefault || valueID == CSSValueNone; +#endif + case CSSPropertyWebkitMarqueeDirection: + return valueID == CSSValueForwards || valueID == CSSValueBackwards || valueID == CSSValueAhead || valueID == CSSValueReverse || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueDown + || valueID == CSSValueUp || valueID == CSSValueAuto; + case CSSPropertyWebkitMarqueeStyle: + return valueID == CSSValueNone || valueID == CSSValueSlide || valueID == CSSValueScroll || valueID == CSSValueAlternate; + case CSSPropertyFontVariantPosition: // normal | sub | super + return valueID == CSSValueNormal || valueID == CSSValueSub || valueID == CSSValueSuper; + case CSSPropertyFontVariantCaps: // normal | small-caps | all-small-caps | petite-caps | all-petite-caps | unicase | titling-caps + return valueID == CSSValueNormal || valueID == CSSValueSmallCaps || valueID == CSSValueAllSmallCaps || valueID == CSSValuePetiteCaps || valueID == CSSValueAllPetiteCaps || valueID == CSSValueUnicase || valueID == CSSValueTitlingCaps; + case CSSPropertyFontVariantAlternates: // We only support the normal and historical-forms values. + return valueID == CSSValueNormal || valueID == CSSValueHistoricalForms; +#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) + case CSSPropertyWebkitOverflowScrolling: + return valueID == CSSValueAuto || valueID == CSSValueTouch; +#endif + default: + ASSERT_NOT_REACHED(); + return false; + } +} + +bool CSSParserFastPaths::isKeywordPropertyID(CSSPropertyID propertyId) +{ + switch (propertyId) { + case CSSPropertyBorderBottomStyle: + case CSSPropertyBorderCollapse: + case CSSPropertyBorderLeftStyle: + case CSSPropertyBorderRightStyle: + case CSSPropertyBorderTopStyle: + case CSSPropertyBoxSizing: + case CSSPropertyBreakAfter: + case CSSPropertyBreakBefore: + case CSSPropertyBreakInside: + case CSSPropertyCaptionSide: + case CSSPropertyClear: + case CSSPropertyColumnFill: + case CSSPropertyWebkitColumnProgression: + case CSSPropertyColumnRuleStyle: + case CSSPropertyDirection: + case CSSPropertyDisplay: + case CSSPropertyEmptyCells: + case CSSPropertyFlexDirection: + case CSSPropertyFlexWrap: + case CSSPropertyFloat: + case CSSPropertyFontStretch: + case CSSPropertyFontStyle: + case CSSPropertyFontVariantAlternates: + case CSSPropertyFontVariantCaps: + case CSSPropertyFontVariantPosition: + case CSSPropertyImageRendering: + case CSSPropertyListStylePosition: + case CSSPropertyListStyleType: + case CSSPropertyObjectFit: + case CSSPropertyOutlineStyle: + case CSSPropertyOverflowWrap: + case CSSPropertyOverflowX: + case CSSPropertyOverflowY: + case CSSPropertyPointerEvents: + case CSSPropertyPosition: + case CSSPropertyResize: + case CSSPropertySpeak: + case CSSPropertyTableLayout: + case CSSPropertyTextAlign: + case CSSPropertyTextLineThroughMode: + case CSSPropertyTextLineThroughStyle: + case CSSPropertyTextOverflow: + case CSSPropertyTextOverlineMode: + case CSSPropertyTextOverlineStyle: + case CSSPropertyTextRendering: + case CSSPropertyTextTransform: + case CSSPropertyTextUnderlineMode: + case CSSPropertyTextUnderlineStyle: + case CSSPropertyTransformStyle: + case CSSPropertyUnicodeBidi: + case CSSPropertyVisibility: + case CSSPropertyWebkitAppearance: + case CSSPropertyWebkitBackfaceVisibility: + case CSSPropertyWebkitBorderAfterStyle: + case CSSPropertyWebkitBorderBeforeStyle: + case CSSPropertyWebkitBorderEndStyle: + case CSSPropertyWebkitBorderFit: + case CSSPropertyWebkitBorderStartStyle: + case CSSPropertyWebkitBoxAlign: + case CSSPropertyWebkitBoxDirection: + case CSSPropertyWebkitBoxLines: + case CSSPropertyWebkitBoxOrient: + case CSSPropertyWebkitBoxPack: + case CSSPropertyWebkitColumnAxis: + case CSSPropertyWebkitFontKerning: + case CSSPropertyWebkitFontSmoothing: + case CSSPropertyWebkitHyphens: + case CSSPropertyWebkitLineAlign: + case CSSPropertyWebkitLineBreak: + case CSSPropertyWebkitLineSnap: + case CSSPropertyWebkitMarginAfterCollapse: + case CSSPropertyWebkitMarginBeforeCollapse: + case CSSPropertyWebkitMarginBottomCollapse: + case CSSPropertyWebkitMarginTopCollapse: + case CSSPropertyWebkitMarqueeDirection: + case CSSPropertyWebkitMarqueeStyle: + case CSSPropertyWebkitNbspMode: + case CSSPropertyWebkitPrintColorAdjust: + case CSSPropertyWebkitRtlOrdering: + case CSSPropertyWebkitRubyPosition: + case CSSPropertyWebkitTextCombine: + case CSSPropertyWebkitTextDecorationStyle: + case CSSPropertyWebkitTextOrientation: + case CSSPropertyWebkitTextSecurity: + case CSSPropertyWebkitTextZoom: + case CSSPropertyWebkitTransformStyle: + case CSSPropertyWebkitUserDrag: + case CSSPropertyWebkitUserModify: + case CSSPropertyWebkitUserSelect: + case CSSPropertyWhiteSpace: + case CSSPropertyWordBreak: + case CSSPropertyWordWrap: + + // SVG CSS properties from SVG 1.1, Appendix N: Property Index. + case CSSPropertyAlignmentBaseline: + case CSSPropertyBufferedRendering: + case CSSPropertyClipRule: + case CSSPropertyColorInterpolation: + case CSSPropertyColorInterpolationFilters: + case CSSPropertyColorRendering: + case CSSPropertyDominantBaseline: + case CSSPropertyFillRule: + case CSSPropertyMaskType: + case CSSPropertyShapeRendering: + case CSSPropertyStrokeLinecap: + case CSSPropertyStrokeLinejoin: + case CSSPropertyTextAnchor: + case CSSPropertyVectorEffect: + case CSSPropertyWritingMode: + + // FIXME-NEWPARSER: Treat all as a keyword property. + // case CSSPropertyAll: + + // FIXME-NEWPARSER: Add the following unprefixed properties: + // case CSSPropertyBackfaceVisibility: + // case CSSPropertyFontKerning: + // case CSSPropertyHyphens: + // case CSSPropertyOverflowAnchor: + // case CSSPropertyScrollBehavior: + // case CSSPropertyScrollSnapType: + // case CSSPropertyTextAlignLast: + // case CSSPropertyTextCombineUpright: + // case CSSPropertyTextDecorationStyle: + // case CSSPropertyTextJustify: + // case CSSPropertyTextOrientation: + // case CSSPropertyUserSelect: +#if ENABLE(CSS_TRAILING_WORD) + case CSSPropertyAppleTrailingWord: +#endif +#if ENABLE(CSS_COMPOSITING) + case CSSPropertyIsolation: + case CSSPropertyMixBlendMode: +#endif +#if ENABLE(TOUCH_EVENTS) + case CSSPropertyTouchAction: +#endif +#if ENABLE(CSS_BOX_DECORATION_BREAK) + case CSSPropertyWebkitBoxDecorationBreak: +#endif +#if ENABLE(CURSOR_VISIBILITY) + case CSSPropertyWebkitCursorVisibility: +#endif +#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) + case CSSPropertyWebkitOverflowScrolling: +#endif +#if ENABLE(CSS_REGIONS) + case CSSPropertyWebkitRegionFragment: +#endif +#if ENABLE(CSS3_TEXT) + case CSSPropertyWebkitTextAlignLast: + case CSSPropertyWebkitTextJustify: +#endif +#if PLATFORM(IOS) + // Apple specific property. This will never be standardized and is purely to + // support custom WebKit-based Apple applications. + case CSSPropertyWebkitTouchCallout: +#endif +#if ENABLE(APPLE_PAY) + case CSSPropertyApplePayButtonStyle: + case CSSPropertyApplePayButtonType: +#endif + return true; + case CSSPropertyJustifyContent: + case CSSPropertyAlignContent: + case CSSPropertyAlignItems: + case CSSPropertyAlignSelf: + return !RuntimeEnabledFeatures::sharedFeatures().isCSSGridLayoutEnabled(); + default: + return false; + } +} + +static bool isUniversalKeyword(const String& string) +{ + // These keywords can be used for all properties. + return equalLettersIgnoringASCIICase(string, "initial") + || equalLettersIgnoringASCIICase(string, "inherit") + || equalLettersIgnoringASCIICase(string, "unset") + || equalLettersIgnoringASCIICase(string, "revert"); +} + +static RefPtr<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 (!isUniversalKeyword(string)) + return nullptr; + + // Parse initial/inherit shorthands using the CSSPropertyParser. + if (shorthandForProperty(propertyId).length()) + return nullptr; + + // Descriptors do not support css wide keywords. + if (CSSProperty::isDescriptorOnly(propertyId)) + return nullptr; + } + + CSSValueID valueID = cssValueKeywordID(string); + + if (!valueID) + return nullptr; + + if (valueID == CSSValueInherit) + return CSSValuePool::singleton().createInheritedValue(); + if (valueID == CSSValueInitial) + return CSSValuePool::singleton().createExplicitInitialValue(); + if (valueID == CSSValueUnset) + return CSSValuePool::singleton().createUnsetValue(); + if (valueID == CSSValueRevert) + return CSSValuePool::singleton().createRevertValue(); + + 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 == notFound) + return false; + unsigned argumentLength = static_cast<unsigned>(delimiter); + CSSPrimitiveValue::UnitType unit = CSSPrimitiveValue::UnitType::CSS_NUMBER; + double number; + if (!parseSimpleLength(pos, argumentLength, unit, number)) + return false; + if (!number && unit == CSSPrimitiveValue::CSS_NUMBER) + unit = CSSPrimitiveValue::UnitType::CSS_PX; + if (unit == CSSPrimitiveValue::UnitType::CSS_NUMBER || (unit == CSSPrimitiveValue::UnitType::CSS_PERCENTAGE && (transformValue->name() == CSSValueTranslateZ || (transformValue->name() == CSSValueTranslate3d && expectedCount == 1)))) + return false; + transformValue->append(CSSPrimitiveValue::create(number, unit)); + 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 == notFound) + 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::UnitType::CSS_NUMBER)); + pos += argumentLength + 1; + --expectedCount; + } + return true; +} + +static const int kShortestValidTransformStringLength = 12; + +template <typename CharType> +static RefPtr<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; + RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(transformType); + if (!parseTransformTranslateArguments(pos, end, expectedArgumentCount, transformValue.get())) + 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; + RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(CSSValueMatrix3d); + if (!parseTransformNumberArguments(pos, end, 16, transformValue.get())) + 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; + RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(CSSValueScale3d); + if (!parseTransformNumberArguments(pos, end, 3, transformValue.get())) + 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 == notFound) + return false; + // Advance to the end of the arguments. + i = argumentsEnd + 1; + } + return i == length; +} + +template <typename CharType> +static RefPtr<CSSValueList> parseSimpleTransformList(const CharType* chars, unsigned length) +{ + if (!transformCanLikelyUseFastPath(chars, length)) + return nullptr; + const CharType*& pos = chars; + const CharType* end = chars + length; + RefPtr<CSSValueList> transformList; + while (pos < end) { + while (pos < end && isCSSSpace(*pos)) + ++pos; + if (pos >= end) + break; + RefPtr<CSSFunctionValue> transformValue = parseSimpleTransformValue(pos, end); + if (!transformValue) + return nullptr; + if (!transformList) + transformList = CSSValueList::createSpaceSeparated(); + transformList->append(*transformValue); + } + return transformList; +} + +static RefPtr<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()); +} + +RefPtr<CSSValue> CSSParserFastPaths::maybeParseValue(CSSPropertyID propertyID, const String& string, CSSParserMode parserMode) +{ + RefPtr<CSSValue> result = parseSimpleLengthValue(propertyID, string, parserMode); + if (result) + return result; + if (isColorPropertyID(propertyID)) + return parseColor(string, parserMode); + result = parseKeywordValue(propertyID, string, parserMode); + if (result) + return result; + result = parseSimpleTransform(propertyID, string); + if (result) + return result; + return nullptr; +} + +} // namespace WebCore |