// 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 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 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 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 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 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(*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(*current)) current++; if (current == end || *current++ != terminator) return false; // Clamp negative values at zero. value = negative ? 0 : static_cast(localValue); string = current; return true; } template 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 static inline bool parseAlphaValue(const CharacterType*& string, const CharacterType* end, const char terminator, int& value) { while (string != end && isHTMLSpace(*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(alpha * nextafter(256.0, 0.0)); string = end; return true; } template 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 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 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 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: // 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: // ( 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 . 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 . 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 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 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(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 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(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 static RefPtr 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 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 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 transformValue = CSSFunctionValue::create(CSSValueScale3d); if (!parseTransformNumberArguments(pos, end, 3, transformValue.get())) return nullptr; return transformValue; } return nullptr; } template 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 static RefPtr parseSimpleTransformList(const CharType* chars, unsigned length) { if (!transformCanLikelyUseFastPath(chars, length)) return nullptr; const CharType*& pos = chars; const CharType* end = chars + length; RefPtr transformList; while (pos < end) { while (pos < end && isCSSSpace(*pos)) ++pos; if (pos >= end) break; RefPtr transformValue = parseSimpleTransformValue(pos, end); if (!transformValue) return nullptr; if (!transformList) transformList = CSSValueList::createSpaceSeparated(); transformList->append(*transformValue); } return transformList; } static RefPtr 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 CSSParserFastPaths::maybeParseValue(CSSPropertyID propertyID, const String& string, CSSParserMode parserMode) { RefPtr 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