Make "overflow: overlay" a synonym for "overflow: auto"
[WebKit-https.git] / Source / WebCore / css / parser / CSSParserFastPaths.cpp
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Copyright (C) 2016-2018 Apple Inc. All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //    * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //    * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //    * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "config.h"
31 #include "CSSParserFastPaths.h"
32
33 #include "CSSFunctionValue.h"
34 #include "CSSParserIdioms.h"
35 #include "CSSPrimitiveValue.h"
36 #include "CSSPropertyParser.h"
37 #include "CSSValueList.h"
38 #include "CSSValuePool.h"
39 #include "HTMLParserIdioms.h"
40 #include "RuntimeEnabledFeatures.h"
41 #include "StyleColor.h"
42 #include "StylePropertyShorthand.h"
43
44 namespace WebCore {
45
46 static inline bool isSimpleLengthPropertyID(CSSPropertyID propertyId, bool& acceptsNegativeNumbers)
47 {
48     switch (propertyId) {
49     case CSSPropertyFontSize:
50     case CSSPropertyHeight:
51     case CSSPropertyWidth:
52     case CSSPropertyMinHeight:
53     case CSSPropertyMinWidth:
54     case CSSPropertyPaddingBottom:
55     case CSSPropertyPaddingLeft:
56     case CSSPropertyPaddingRight:
57     case CSSPropertyPaddingTop:
58     case CSSPropertyInlineSize:
59     case CSSPropertyBlockSize:
60     case CSSPropertyMinInlineSize:
61     case CSSPropertyMinBlockSize:
62     case CSSPropertyPaddingBlockEnd:
63     case CSSPropertyPaddingBlockStart:
64     case CSSPropertyPaddingInlineEnd:
65     case CSSPropertyPaddingInlineStart:
66     case CSSPropertyShapeMargin:
67         acceptsNegativeNumbers = false;
68         return true;
69     case CSSPropertyBottom:
70     case CSSPropertyCx:
71     case CSSPropertyCy:
72     case CSSPropertyLeft:
73     case CSSPropertyMarginBottom:
74     case CSSPropertyMarginLeft:
75     case CSSPropertyMarginRight:
76     case CSSPropertyMarginTop:
77     case CSSPropertyRight:
78     case CSSPropertyTop:
79     case CSSPropertyMarginBlockEnd:
80     case CSSPropertyMarginBlockStart:
81     case CSSPropertyMarginInlineEnd:
82     case CSSPropertyMarginInlineStart:
83     case CSSPropertyX:
84     case CSSPropertyY:
85     case CSSPropertyR:
86     case CSSPropertyRx:
87     case CSSPropertyRy:
88         acceptsNegativeNumbers = true;
89         return true;
90     default:
91         return false;
92     }
93 }
94
95 template <typename CharacterType>
96 static inline bool parseSimpleLength(const CharacterType* characters, unsigned length, CSSPrimitiveValue::UnitType& unit, double& number)
97 {
98     if (length > 2 && (characters[length - 2] | 0x20) == 'p' && (characters[length - 1] | 0x20) == 'x') {
99         length -= 2;
100         unit = CSSPrimitiveValue::UnitType::CSS_PX;
101     } else if (length > 1 && characters[length - 1] == '%') {
102         length -= 1;
103         unit = CSSPrimitiveValue::UnitType::CSS_PERCENTAGE;
104     }
105
106     // We rely on charactersToDouble for validation as well. The function
107     // will set "ok" to "false" if the entire passed-in character range does
108     // not represent a double.
109     bool ok;
110     number = charactersToDouble(characters, length, &ok);
111     if (!ok)
112         return false;
113     return true;
114 }
115
116 template <typename CharacterType>
117 static inline bool parseSimpleAngle(const CharacterType* characters, unsigned length, CSSPrimitiveValue::UnitType& unit, double& number)
118 {
119     // Just support deg and rad for now.
120     if (length < 4)
121         return false;
122
123     if ((characters[length - 3] | 0x20) == 'd' && (characters[length - 2] | 0x20) == 'e' && (characters[length - 1] | 0x20) == 'g') {
124         length -= 3;
125         unit = CSSPrimitiveValue::UnitType::CSS_DEG;
126     } else if ((characters[length - 3] | 0x20) == 'r' && (characters[length - 2] | 0x20) == 'a' && (characters[length - 1] | 0x20) == 'd') {
127         length -= 3;
128         unit = CSSPrimitiveValue::UnitType::CSS_RAD;
129     } else
130         return false;
131
132     // We rely on charactersToDouble for validation as well. The function
133     // will set "ok" to "false" if the entire passed-in character range does
134     // not represent a double.
135     bool ok;
136     number = charactersToDouble(characters, length, &ok);
137     if (!ok)
138         return false;
139     return true;
140 }
141
142 static RefPtr<CSSValue> parseSimpleLengthValue(CSSPropertyID propertyId, const String& string, CSSParserMode cssParserMode)
143 {
144     ASSERT(!string.isEmpty());
145     bool acceptsNegativeNumbers = false;
146
147     // In @viewport, width and height are shorthands, not simple length values.
148     if (isCSSViewportParsingEnabledForMode(cssParserMode) || !isSimpleLengthPropertyID(propertyId, acceptsNegativeNumbers))
149         return nullptr;
150
151     unsigned length = string.length();
152     double number;
153     CSSPrimitiveValue::UnitType unit = CSSPrimitiveValue::UnitType::CSS_NUMBER;
154
155     if (string.is8Bit()) {
156         if (!parseSimpleLength(string.characters8(), length, unit, number))
157             return nullptr;
158     } else {
159         if (!parseSimpleLength(string.characters16(), length, unit, number))
160             return nullptr;
161     }
162
163     if (unit == CSSPrimitiveValue::UnitType::CSS_NUMBER) {
164         if (number && cssParserMode != SVGAttributeMode)
165             return nullptr;
166         unit = CSSPrimitiveValue::UnitType::CSS_PX;
167     }
168
169     if (number < 0 && !acceptsNegativeNumbers)
170         return nullptr;
171     if (std::isinf(number))
172         return nullptr;
173
174     return CSSPrimitiveValue::create(number, unit);
175 }
176
177 static inline bool isColorPropertyID(CSSPropertyID propertyId)
178 {
179     switch (propertyId) {
180     case CSSPropertyColor:
181     case CSSPropertyBackgroundColor:
182     case CSSPropertyBorderBottomColor:
183     case CSSPropertyBorderLeftColor:
184     case CSSPropertyBorderRightColor:
185     case CSSPropertyBorderTopColor:
186     case CSSPropertyFill:
187     case CSSPropertyFloodColor:
188     case CSSPropertyLightingColor:
189     case CSSPropertyOutlineColor:
190     case CSSPropertyStopColor:
191     case CSSPropertyStroke:
192     case CSSPropertyStrokeColor:
193     case CSSPropertyBorderBlockEndColor:
194     case CSSPropertyBorderBlockStartColor:
195     case CSSPropertyBorderInlineEndColor:
196     case CSSPropertyBorderInlineStartColor:
197     case CSSPropertyColumnRuleColor:
198     case CSSPropertyWebkitTextEmphasisColor:
199     case CSSPropertyWebkitTextFillColor:
200     case CSSPropertyWebkitTextStrokeColor:
201     case CSSPropertyWebkitTextDecorationColor:
202         return true;
203     default:
204         return false;
205     }
206 }
207
208 // Returns the number of characters which form a valid double
209 // and are terminated by the given terminator character
210 template <typename CharacterType>
211 static int checkForValidDouble(const CharacterType* string, const CharacterType* end, const char terminator)
212 {
213     int length = end - string;
214     if (length < 1)
215         return 0;
216
217     bool decimalMarkSeen = false;
218     int processedLength = 0;
219
220     for (int i = 0; i < length; ++i) {
221         if (string[i] == terminator) {
222             processedLength = i;
223             break;
224         }
225         if (!isASCIIDigit(string[i])) {
226             if (!decimalMarkSeen && string[i] == '.')
227                 decimalMarkSeen = true;
228             else
229                 return 0;
230         }
231     }
232
233     if (decimalMarkSeen && processedLength == 1)
234         return 0;
235
236     return processedLength;
237 }
238
239 // Returns the number of characters consumed for parsing a valid double
240 // terminated by the given terminator character
241 template <typename CharacterType>
242 static int parseDouble(const CharacterType* string, const CharacterType* end, const char terminator, double& value)
243 {
244     int length = checkForValidDouble(string, end, terminator);
245     if (!length)
246         return 0;
247
248     int position = 0;
249     double localValue = 0;
250
251     // The consumed characters here are guaranteed to be
252     // ASCII digits with or without a decimal mark
253     for (; position < length; ++position) {
254         if (string[position] == '.')
255             break;
256         localValue = localValue * 10 + string[position] - '0';
257     }
258
259     if (++position == length) {
260         value = localValue;
261         return length;
262     }
263
264     double fraction = 0;
265     double scale = 1;
266
267     const double maxScale = 1000000;
268     while (position < length && scale < maxScale) {
269         fraction = fraction * 10 + string[position++] - '0';
270         scale *= 10;
271     }
272
273     value = localValue + fraction / scale;
274     return length;
275 }
276
277 template <typename CharacterType>
278 static bool parseColorIntOrPercentage(const CharacterType*& string, const CharacterType* end, const char terminator, CSSPrimitiveValue::UnitType& expect, int& value)
279 {
280     const CharacterType* current = string;
281     double localValue = 0;
282     bool negative = false;
283     while (current != end && isHTMLSpace<CharacterType>(*current))
284         current++;
285     if (current != end && *current == '-') {
286         negative = true;
287         current++;
288     }
289     if (current == end || !isASCIIDigit(*current))
290         return false;
291     while (current != end && isASCIIDigit(*current)) {
292         double newValue = localValue * 10 + *current++ - '0';
293         if (newValue >= 255) {
294             // Clamp values at 255.
295             localValue = 255;
296             while (current != end && isASCIIDigit(*current))
297                 ++current;
298             break;
299         }
300         localValue = newValue;
301     }
302
303     if (current == end)
304         return false;
305
306     if (expect == CSSPrimitiveValue::UnitType::CSS_NUMBER && (*current == '.' || *current == '%'))
307         return false;
308
309     if (*current == '.') {
310         // We already parsed the integral part, try to parse
311         // the fraction part of the percentage value.
312         double percentage = 0;
313         int numCharactersParsed = parseDouble(current, end, '%', percentage);
314         if (!numCharactersParsed)
315             return false;
316         current += numCharactersParsed;
317         if (*current != '%')
318             return false;
319         localValue += percentage;
320     }
321
322     if (expect == CSSPrimitiveValue::UnitType::CSS_PERCENTAGE && *current != '%')
323         return false;
324
325     if (*current == '%') {
326         expect = CSSPrimitiveValue::UnitType::CSS_PERCENTAGE;
327         localValue = localValue / 100.0 * 256.0;
328         // Clamp values at 255 for percentages over 100%
329         if (localValue > 255)
330             localValue = 255;
331         current++;
332     } else {
333         expect = CSSPrimitiveValue::UnitType::CSS_NUMBER;
334     }
335
336     while (current != end && isHTMLSpace<CharacterType>(*current))
337         current++;
338     if (current == end || *current++ != terminator)
339         return false;
340     // Clamp negative values at zero.
341     value = negative ? 0 : static_cast<int>(localValue);
342     string = current;
343     return true;
344 }
345
346 template <typename CharacterType>
347 static inline bool isTenthAlpha(const CharacterType* string, const int length)
348 {
349     // "0.X"
350     if (length == 3 && string[0] == '0' && string[1] == '.' && isASCIIDigit(string[2]))
351         return true;
352
353     // ".X"
354     if (length == 2 && string[0] == '.' && isASCIIDigit(string[1]))
355         return true;
356
357     return false;
358 }
359
360 template <typename CharacterType>
361 static inline bool parseAlphaValue(const CharacterType*& string, const CharacterType* end, const char terminator, int& value)
362 {
363     while (string != end && isHTMLSpace<CharacterType>(*string))
364         string++;
365
366     bool negative = false;
367
368     if (string != end && *string == '-') {
369         negative = true;
370         string++;
371     }
372
373     value = 0;
374
375     int length = end - string;
376     if (length < 2)
377         return false;
378
379     if (string[length - 1] != terminator || !isASCIIDigit(string[length - 2]))
380         return false;
381
382     if (string[0] != '0' && string[0] != '1' && string[0] != '.') {
383         if (checkForValidDouble(string, end, terminator)) {
384             value = negative ? 0 : 255;
385             string = end;
386             return true;
387         }
388         return false;
389     }
390
391     if (length == 2 && string[0] != '.') {
392         value = !negative && string[0] == '1' ? 255 : 0;
393         string = end;
394         return true;
395     }
396
397     if (isTenthAlpha(string, length - 1)) {
398         static const int tenthAlphaValues[] = { 0, 25, 51, 76, 102, 127, 153, 179, 204, 230 };
399         value = negative ? 0 : tenthAlphaValues[string[length - 2] - '0'];
400         string = end;
401         return true;
402     }
403
404     double alpha = 0;
405     if (!parseDouble(string, end, terminator, alpha))
406         return false;
407     value = negative ? 0 : static_cast<int>(alpha * nextafter(256.0, 0.0));
408     string = end;
409     return true;
410 }
411
412 template <typename CharacterType>
413 static inline bool mightBeRGBA(const CharacterType* characters, unsigned length)
414 {
415     if (length < 5)
416         return false;
417     return characters[4] == '('
418         && isASCIIAlphaCaselessEqual(characters[0], 'r')
419         && isASCIIAlphaCaselessEqual(characters[1], 'g')
420         && isASCIIAlphaCaselessEqual(characters[2], 'b')
421         && isASCIIAlphaCaselessEqual(characters[3], 'a');
422 }
423
424 template <typename CharacterType>
425 static inline bool mightBeRGB(const CharacterType* characters, unsigned length)
426 {
427     if (length < 4)
428         return false;
429     return characters[3] == '('
430         && isASCIIAlphaCaselessEqual(characters[0], 'r')
431         && isASCIIAlphaCaselessEqual(characters[1], 'g')
432         && isASCIIAlphaCaselessEqual(characters[2], 'b');
433 }
434
435 template <typename CharacterType>
436 static Color fastParseColorInternal(const CharacterType* characters, unsigned length, bool quirksMode)
437 {
438     CSSPrimitiveValue::UnitType expect = CSSPrimitiveValue::UnitType::CSS_UNKNOWN;
439
440     if (length >= 4 && characters[0] == '#') {
441         RGBA32 rgb;
442         if (Color::parseHexColor(characters + 1, length - 1, rgb))
443             return Color(rgb);
444     }
445
446     if (quirksMode && (length == 3 || length == 6)) {
447         RGBA32 rgb;
448         if (Color::parseHexColor(characters, length, rgb))
449             return Color(rgb);
450     }
451
452     // Try rgba() syntax.
453     if (mightBeRGBA(characters, length)) {
454         const CharacterType* current = characters + 5;
455         const CharacterType* end = characters + length;
456         int red;
457         int green;
458         int blue;
459         int alpha;
460
461         if (!parseColorIntOrPercentage(current, end, ',', expect, red))
462             return Color();
463         if (!parseColorIntOrPercentage(current, end, ',', expect, green))
464             return Color();
465         if (!parseColorIntOrPercentage(current, end, ',', expect, blue))
466             return Color();
467         if (!parseAlphaValue(current, end, ')', alpha))
468             return Color();
469         if (current != end)
470             return Color();
471         return Color(makeRGBA(red, green, blue, alpha));
472     }
473
474     // Try rgb() syntax.
475     if (mightBeRGB(characters, length)) {
476         const CharacterType* current = characters + 4;
477         const CharacterType* end = characters + length;
478         int red;
479         int green;
480         int blue;
481         if (!parseColorIntOrPercentage(current, end, ',', expect, red))
482             return Color();
483         if (!parseColorIntOrPercentage(current, end, ',', expect, green))
484             return Color();
485         if (!parseColorIntOrPercentage(current, end, ')', expect, blue))
486             return Color();
487         if (current != end)
488             return Color();
489         return Color(makeRGB(red, green, blue));
490     }
491
492     return Color();
493 }
494
495 RefPtr<CSSValue> CSSParserFastPaths::parseColor(const String& string, CSSParserMode parserMode)
496 {
497     ASSERT(!string.isEmpty());
498     CSSValueID valueID = cssValueKeywordID(string);
499     if (StyleColor::isColorKeyword(valueID)) {
500         if (!isValueAllowedInMode(valueID, parserMode))
501             return nullptr;
502         return CSSValuePool::singleton().createIdentifierValue(valueID);
503     }
504
505     bool quirksMode = isQuirksModeBehavior(parserMode);
506
507     // Fast path for hex colors and rgb()/rgba() colors
508     Color color;
509     if (string.is8Bit())
510         color = fastParseColorInternal(string.characters8(), string.length(), quirksMode);
511     else
512         color = fastParseColorInternal(string.characters16(), string.length(), quirksMode);
513     if (!color.isValid())
514         return nullptr;
515     return CSSValuePool::singleton().createColorValue(color);
516 }
517
518 bool CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyID propertyId, CSSValueID valueID, CSSParserMode parserMode)
519 {
520     if (valueID == CSSValueInvalid || !isValueAllowedInMode(valueID, parserMode))
521         return false;
522
523     switch (propertyId) {
524     case CSSPropertyAlignmentBaseline:
525         // auto | baseline | before-edge | text-before-edge | middle |
526         // central | after-edge | text-after-edge | ideographic | alphabetic |
527         // hanging | mathematical
528         return valueID == CSSValueAuto || valueID == CSSValueAlphabetic || valueID == CSSValueBaseline
529             || valueID == CSSValueMiddle || (valueID >= CSSValueBeforeEdge && valueID <= CSSValueMathematical);
530     case CSSPropertyAll:
531         return false; // Only accepts css-wide keywords
532     case CSSPropertyBackgroundRepeatX: // repeat | no-repeat
533     case CSSPropertyBackgroundRepeatY: // repeat | no-repeat
534         return valueID == CSSValueRepeat || valueID == CSSValueNoRepeat;
535     case CSSPropertyBorderCollapse: // collapse | separate
536         return valueID == CSSValueCollapse || valueID == CSSValueSeparate;
537     case CSSPropertyBorderTopStyle: // <border-style>
538     case CSSPropertyBorderRightStyle: // Defined as: none | hidden | dotted | dashed |
539     case CSSPropertyBorderBottomStyle: // solid | double | groove | ridge | inset | outset
540     case CSSPropertyBorderLeftStyle:
541     case CSSPropertyBorderBlockEndStyle:
542     case CSSPropertyBorderBlockStartStyle:
543     case CSSPropertyBorderInlineEndStyle:
544     case CSSPropertyBorderInlineStartStyle:
545     case CSSPropertyColumnRuleStyle:
546         return valueID >= CSSValueNone && valueID <= CSSValueDouble;
547     case CSSPropertyBoxSizing:
548         return valueID == CSSValueBorderBox || valueID == CSSValueContentBox;
549     case CSSPropertyBufferedRendering:
550         return valueID == CSSValueAuto || valueID == CSSValueDynamic || valueID == CSSValueStatic;
551     case CSSPropertyCaptionSide: // top | bottom | left | right
552         return valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueTop || valueID == CSSValueBottom;
553     case CSSPropertyClear: // none | left | right | both
554         return valueID == CSSValueNone || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueBoth;
555     case CSSPropertyClipRule:
556     case CSSPropertyFillRule:
557         return valueID == CSSValueNonzero || valueID == CSSValueEvenodd;
558     case CSSPropertyColorInterpolation:
559     case CSSPropertyColorInterpolationFilters:
560         return valueID == CSSValueAuto || valueID == CSSValueSRGB || valueID == CSSValueLinearRGB;
561     case CSSPropertyColorRendering:
562         return valueID == CSSValueAuto || valueID == CSSValueOptimizeSpeed || valueID == CSSValueOptimizeQuality;
563     case CSSPropertyDirection: // ltr | rtl
564         return valueID == CSSValueLtr || valueID == CSSValueRtl;
565     case CSSPropertyDisplay:
566         // inline | block | list-item | inline-block | table |
567         // inline-table | table-row-group | table-header-group | table-footer-group | table-row |
568         // table-column-group | table-column | table-cell | table-caption | -webkit-box | -webkit-inline-box | none
569         // flex | inline-flex | -webkit-flex | -webkit-inline-flex | grid | inline-grid
570         return (valueID >= CSSValueInline && valueID <= CSSValueContents) || valueID == CSSValueNone
571             || valueID == CSSValueGrid || valueID == CSSValueInlineGrid;
572     case CSSPropertyDominantBaseline:
573         // auto | use-script | no-change | reset-size | ideographic |
574         // alphabetic | hanging | mathematical | central | middle |
575         // text-after-edge | text-before-edge
576         return valueID == CSSValueAuto || valueID == CSSValueAlphabetic || valueID == CSSValueMiddle
577             || (valueID >= CSSValueUseScript && valueID <= CSSValueResetSize)
578             || (valueID >= CSSValueCentral && valueID <= CSSValueMathematical);
579     case CSSPropertyEmptyCells: // show | hide
580         return valueID == CSSValueShow || valueID == CSSValueHide;
581     case CSSPropertyFloat: // left | right | none
582         return valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueNone;
583     case CSSPropertyImageRendering: // auto | optimizeContrast | pixelated | optimizeSpeed | crispEdges | optimizeQuality | webkit-crispEdges
584         return valueID == CSSValueAuto || valueID == CSSValueOptimizeSpeed || valueID == CSSValueOptimizeQuality || valueID == CSSValueWebkitCrispEdges || valueID == CSSValueWebkitOptimizeContrast || valueID == CSSValueCrispEdges || valueID == CSSValuePixelated;
585 #if ENABLE(CSS_COMPOSITING)
586     case CSSPropertyIsolation: // auto | isolate
587         return valueID == CSSValueAuto || valueID == CSSValueIsolate;
588 #endif
589     case CSSPropertyListStylePosition: // inside | outside
590         return valueID == CSSValueInside || valueID == CSSValueOutside;
591     case CSSPropertyListStyleType:
592         // See section CSS_PROP_LIST_STYLE_TYPE of file CSSValueKeywords.in
593         // for the list of supported list-style-types.
594         return (valueID >= CSSValueDisc && valueID <= CSSValueKatakanaIroha) || valueID == CSSValueNone;
595     case CSSPropertyMaskType:
596         return valueID == CSSValueLuminance || valueID == CSSValueAlpha;
597     case CSSPropertyObjectFit:
598         return valueID == CSSValueFill || valueID == CSSValueContain || valueID == CSSValueCover || valueID == CSSValueNone || valueID == CSSValueScaleDown;
599     case CSSPropertyOutlineStyle: // (<border-style> except hidden) | auto
600         return valueID == CSSValueAuto || valueID == CSSValueNone || (valueID >= CSSValueInset && valueID <= CSSValueDouble);
601     // FIXME-NEWPARSER: Support?
602     // case CSSPropertyOverflowAnchor:
603     //    return valueID == CSSValueVisible || valueID == CSSValueNone || valueID == CSSValueAuto;
604     case CSSPropertyOverflowWrap: // normal | break-word
605     case CSSPropertyWordWrap:
606         return valueID == CSSValueNormal || valueID == CSSValueBreakWord;
607     case CSSPropertyOverflowX: // visible | hidden | scroll | auto | overlay (overlay is a synonym for auto)
608         return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay;
609     case CSSPropertyOverflowY: // visible | hidden | scroll | auto | overlay | -webkit-paged-x | -webkit-paged-y (overlay is a synonym for auto)
610         return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay || valueID == CSSValueWebkitPagedX || valueID == CSSValueWebkitPagedY;
611     case CSSPropertyBreakAfter:
612     case CSSPropertyBreakBefore:
613         return valueID == CSSValueAuto || valueID == CSSValueAvoid || valueID == CSSValueAvoidPage || valueID == CSSValuePage || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueRecto || valueID == CSSValueVerso || valueID == CSSValueAvoidColumn || valueID == CSSValueColumn;
614     case CSSPropertyBreakInside:
615         return valueID == CSSValueAuto || valueID == CSSValueAvoid || valueID == CSSValueAvoidPage || valueID == CSSValueAvoidColumn;
616     case CSSPropertyPointerEvents:
617         // none | visiblePainted | visibleFill | visibleStroke | visible |
618         // painted | fill | stroke | auto | all | bounding-box
619         return valueID == CSSValueVisible || valueID == CSSValueNone || valueID == CSSValueAll || valueID == CSSValueAuto || (valueID >= CSSValueVisiblePainted && valueID <= CSSValueStroke);
620     case CSSPropertyPosition: // static | relative | absolute | fixed | sticky
621         return valueID == CSSValueStatic || valueID == CSSValueRelative || valueID == CSSValueAbsolute || valueID == CSSValueFixed || valueID == CSSValueWebkitSticky;
622     case CSSPropertyResize: // none | both | horizontal | vertical | auto
623         return valueID == CSSValueNone || valueID == CSSValueBoth || valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueAuto;
624     // FIXME-NEWPARSER: Investigate this property.
625     // case CSSPropertyScrollBehavior: // auto | smooth
626     //     ASSERT(RuntimeEnabledFeatures::cssomSmoothScrollEnabled());
627     //   return valueID == CSSValueAuto || valueID == CSSValueSmooth;
628     case CSSPropertyShapeRendering:
629         return valueID == CSSValueAuto || valueID == CSSValueOptimizeSpeed || valueID == CSSValueCrispedges || valueID == CSSValueGeometricPrecision;
630     case CSSPropertyStrokeLinejoin:
631         return valueID == CSSValueMiter || valueID == CSSValueRound || valueID == CSSValueBevel;
632     case CSSPropertyStrokeLinecap:
633         return valueID == CSSValueButt || valueID == CSSValueRound || valueID == CSSValueSquare;
634     case CSSPropertyTableLayout: // auto | fixed
635         return valueID == CSSValueAuto || valueID == CSSValueFixed;
636     case CSSPropertyTextAlign:
637         return (valueID >= CSSValueWebkitAuto && valueID <= CSSValueWebkitMatchParent) || valueID == CSSValueStart || valueID == CSSValueEnd;
638 #if ENABLE(CSS3_TEXT)
639     case CSSPropertyWebkitTextAlignLast:
640         // auto | start | end | left | right | center | justify
641         return (valueID >= CSSValueLeft && valueID <= CSSValueJustify) || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueAuto;
642 #endif
643     case CSSPropertyTextAnchor:
644         return valueID == CSSValueStart || valueID == CSSValueMiddle || valueID == CSSValueEnd;
645 // FIXME-NEWPARSER: Support
646 //    case CSSPropertyTextCombineUpright:
647 //        return valueID == CSSValueNone || valueID == CSSValueAll;
648     case CSSPropertyWebkitTextDecorationStyle:
649         // solid | double | dotted | dashed | wavy
650         return valueID == CSSValueSolid || valueID == CSSValueDouble || valueID == CSSValueDotted || valueID == CSSValueDashed || valueID == CSSValueWavy;
651 #if ENABLE(CSS3_TEXT)
652     case CSSPropertyWebkitTextJustify:
653         // auto | none | inter-word | distribute
654         return valueID == CSSValueInterWord || valueID == CSSValueDistribute || valueID == CSSValueAuto || valueID == CSSValueNone;
655 #endif
656     case CSSPropertyWebkitTextOrientation: // mixed | upright | sideways | sideways-right
657         return valueID == CSSValueMixed || valueID == CSSValueUpright || valueID == CSSValueSideways || valueID == CSSValueSidewaysRight;
658     case CSSPropertyTextOverflow: // clip | ellipsis
659         return valueID == CSSValueClip || valueID == CSSValueEllipsis;
660     case CSSPropertyTextRendering: // auto | optimizeSpeed | optimizeLegibility | geometricPrecision
661         return valueID == CSSValueAuto || valueID == CSSValueOptimizeSpeed || valueID == CSSValueOptimizeLegibility || valueID == CSSValueGeometricPrecision;
662     case CSSPropertyTextTransform: // capitalize | uppercase | lowercase | none
663         return (valueID >= CSSValueCapitalize && valueID <= CSSValueLowercase) || valueID == CSSValueNone;
664     case CSSPropertyUnicodeBidi:
665         return valueID == CSSValueNormal || valueID == CSSValueEmbed
666             || valueID == CSSValueBidiOverride
667             || valueID == CSSValueIsolate || valueID == CSSValueWebkitIsolate
668             || valueID == CSSValueIsolateOverride || valueID == CSSValueWebkitIsolateOverride
669             || valueID == CSSValuePlaintext || valueID == CSSValueWebkitPlaintext;
670     case CSSPropertyVectorEffect:
671         return valueID == CSSValueNone || valueID == CSSValueNonScalingStroke;
672     case CSSPropertyVisibility: // visible | hidden | collapse
673         return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueCollapse;
674     case CSSPropertyWebkitAppearance:
675         return (valueID >= CSSValueCheckbox && valueID <= CSSValueCapsLockIndicator) || valueID == CSSValueNone;
676     case CSSPropertyWebkitBackfaceVisibility:
677         return valueID == CSSValueVisible || valueID == CSSValueHidden;
678 #if ENABLE(CSS_COMPOSITING)
679     case CSSPropertyMixBlendMode:
680         return valueID == CSSValueNormal || valueID == CSSValueMultiply || valueID == CSSValueScreen || valueID == CSSValueOverlay
681             || valueID == CSSValueDarken || valueID == CSSValueLighten || valueID == CSSValueColorDodge || valueID == CSSValueColorBurn
682             || valueID == CSSValueHardLight || valueID == CSSValueSoftLight || valueID == CSSValueDifference || valueID == CSSValueExclusion
683             || valueID == CSSValueHue || valueID == CSSValueSaturation || valueID == CSSValueColor || valueID == CSSValueLuminosity || valueID == CSSValuePlusDarker || valueID == CSSValuePlusLighter;
684 #endif
685     case CSSPropertyWebkitBoxAlign:
686         return valueID == CSSValueStretch || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline;
687 #if ENABLE(CSS_BOX_DECORATION_BREAK)
688     case CSSPropertyWebkitBoxDecorationBreak:
689         return valueID == CSSValueClone || valueID == CSSValueSlice;
690     case CSSPropertyWebkitBoxDirection:
691         return valueID == CSSValueNormal || valueID == CSSValueReverse;
692 #endif
693     case CSSPropertyWebkitBoxLines:
694         return valueID == CSSValueSingle || valueID == CSSValueMultiple;
695     case CSSPropertyWebkitBoxOrient:
696         return valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueInlineAxis || valueID == CSSValueBlockAxis;
697     case CSSPropertyWebkitBoxPack:
698         return valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueJustify;
699 #if ENABLE(CURSOR_VISIBILITY)
700     case CSSPropertyWebkitCursorVisibility:
701         return valueID == CSSValueAuto || valueID == CSSValueAutoHide;
702 #endif
703     case CSSPropertyColumnFill:
704         return valueID == CSSValueAuto || valueID == CSSValueBalance;
705     case CSSPropertyWebkitColumnAxis:
706         return valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueAuto;
707     case CSSPropertyWebkitColumnProgression:
708         return valueID == CSSValueNormal || valueID == CSSValueReverse;
709     case CSSPropertyFlexDirection:
710         return valueID == CSSValueRow || valueID == CSSValueRowReverse || valueID == CSSValueColumn || valueID == CSSValueColumnReverse;
711     case CSSPropertyFlexWrap:
712         return valueID == CSSValueNowrap || valueID == CSSValueWrap || valueID == CSSValueWrapReverse;
713     case CSSPropertyWebkitHyphens:
714         return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueManual;
715     case CSSPropertyWebkitFontKerning:
716         return valueID == CSSValueAuto || valueID == CSSValueNormal || valueID == CSSValueNone;
717     case CSSPropertyWebkitFontSmoothing:
718         return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueAntialiased || valueID == CSSValueSubpixelAntialiased;
719     case CSSPropertyWebkitLineAlign:
720         return valueID == CSSValueNone || valueID == CSSValueEdges;
721     case CSSPropertyLineBreak: // auto | loose | normal | strict | after-white-space
722         return valueID == CSSValueAuto || valueID == CSSValueLoose || valueID == CSSValueNormal || valueID == CSSValueStrict || valueID == CSSValueAfterWhiteSpace;
723     case CSSPropertyWebkitLineSnap:
724         return valueID == CSSValueNone || valueID == CSSValueBaseline || valueID == CSSValueContain;
725     case CSSPropertyWebkitMarginAfterCollapse:
726     case CSSPropertyWebkitMarginBeforeCollapse:
727     case CSSPropertyWebkitMarginBottomCollapse:
728     case CSSPropertyWebkitMarginTopCollapse:
729         return valueID == CSSValueCollapse || valueID == CSSValueSeparate || valueID == CSSValueDiscard;
730     case CSSPropertyWebkitPrintColorAdjust:
731         return valueID == CSSValueExact || valueID == CSSValueEconomy;
732     case CSSPropertyWebkitRtlOrdering:
733         return valueID == CSSValueLogical || valueID == CSSValueVisual;
734     case CSSPropertyWebkitRubyPosition:
735         return valueID == CSSValueBefore || valueID == CSSValueAfter || valueID == CSSValueInterCharacter;
736     case CSSPropertyWebkitTextCombine:
737         return valueID == CSSValueNone || valueID == CSSValueHorizontal;
738     case CSSPropertyWebkitTextSecurity: // disc | circle | square | none
739         return valueID == CSSValueDisc || valueID == CSSValueCircle || valueID == CSSValueSquare || valueID == CSSValueNone;
740     case CSSPropertyTransformStyle:
741     case CSSPropertyWebkitTransformStyle:
742         return valueID == CSSValueFlat || valueID == CSSValuePreserve3d;
743     case CSSPropertyWebkitUserDrag: // auto | none | element
744         return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueElement;
745     case CSSPropertyWebkitUserModify: // read-only | read-write
746         return valueID == CSSValueReadOnly || valueID == CSSValueReadWrite || valueID == CSSValueReadWritePlaintextOnly;
747     case CSSPropertyWebkitUserSelect: // auto | none | text | all
748         return valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueText || valueID == CSSValueAll;
749     case CSSPropertyWritingMode:
750         // Note that horizontal-bt is not supported by the unprefixed version of
751         // the property, only by the -webkit- version.
752         return (valueID >= CSSValueHorizontalTb && valueID <= CSSValueHorizontalBt)
753             || valueID == CSSValueLrTb || valueID == CSSValueRlTb || valueID == CSSValueTbRl
754             || valueID == CSSValueLr || valueID == CSSValueRl || valueID == CSSValueTb;
755     case CSSPropertyWhiteSpace: // normal | pre | nowrap
756         return valueID == CSSValueNormal || valueID == CSSValuePre || valueID == CSSValuePreWrap || valueID == CSSValuePreLine || valueID == CSSValueNowrap;
757     case CSSPropertyWordBreak: // normal | break-all | keep-all | break-word (this is a custom extension)
758         return valueID == CSSValueNormal || valueID == CSSValueBreakAll || valueID == CSSValueKeepAll || valueID == CSSValueBreakWord;
759     case CSSPropertyWebkitBorderFit:
760         return valueID == CSSValueBorder || valueID == CSSValueLines;
761 #if ENABLE(TOUCH_EVENTS)
762     case CSSPropertyTouchAction: // auto | manipulation
763         return valueID == CSSValueAuto || valueID == CSSValueManipulation;
764 #endif
765 #if ENABLE(CSS_TRAILING_WORD)
766     case CSSPropertyAppleTrailingWord: // auto | -apple-partially-balanced
767         return valueID == CSSValueAuto || valueID == CSSValueWebkitPartiallyBalanced;
768 #endif
769 #if ENABLE(APPLE_PAY)
770     case CSSPropertyApplePayButtonStyle: // white | white-outline | black
771         return valueID == CSSValueWhite || valueID == CSSValueWhiteOutline || valueID == CSSValueBlack;
772     case CSSPropertyApplePayButtonType: // plain | buy | set-up | in-store | donate | check-out | book | subscribe
773         if (valueID == CSSValuePlain || valueID == CSSValueBuy || valueID == CSSValueSetUp || valueID == CSSValueInStore || valueID == CSSValueDonate)
774             return true;
775 #if ENABLE(APPLE_PAY_SESSION_V4)
776         return valueID == CSSValueCheckOut || valueID == CSSValueBook || valueID == CSSValueSubscribe;
777 #else
778         return false;
779 #endif
780 #endif
781     case CSSPropertyWebkitNbspMode: // normal | space
782         return valueID == CSSValueNormal || valueID == CSSValueSpace;
783     case CSSPropertyWebkitTextZoom:
784         return valueID == CSSValueNormal || valueID == CSSValueReset;
785 #if PLATFORM(IOS)
786     // Apple specific property. These will never be standardized and is purely to
787     // support custom WebKit-based Apple applications.
788     case CSSPropertyWebkitTouchCallout:
789         return valueID == CSSValueDefault || valueID == CSSValueNone;
790 #endif
791     case CSSPropertyWebkitMarqueeDirection:
792         return valueID == CSSValueForwards || valueID == CSSValueBackwards || valueID == CSSValueAhead || valueID == CSSValueReverse || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueDown
793             || valueID == CSSValueUp || valueID == CSSValueAuto;
794     case CSSPropertyWebkitMarqueeStyle:
795         return valueID == CSSValueNone || valueID == CSSValueSlide || valueID == CSSValueScroll || valueID == CSSValueAlternate;
796     case CSSPropertyFontVariantPosition: // normal | sub | super
797         return valueID == CSSValueNormal || valueID == CSSValueSub || valueID == CSSValueSuper;
798     case CSSPropertyFontVariantCaps: // normal | small-caps | all-small-caps | petite-caps | all-petite-caps | unicase | titling-caps
799         return valueID == CSSValueNormal || valueID == CSSValueSmallCaps || valueID == CSSValueAllSmallCaps || valueID == CSSValuePetiteCaps || valueID == CSSValueAllPetiteCaps || valueID == CSSValueUnicase || valueID == CSSValueTitlingCaps;
800     case CSSPropertyFontVariantAlternates: // We only support the normal and historical-forms values.
801         return valueID == CSSValueNormal || valueID == CSSValueHistoricalForms;
802 #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING)
803     case CSSPropertyWebkitOverflowScrolling:
804         return valueID == CSSValueAuto || valueID == CSSValueTouch;
805 #endif
806 #if ENABLE(VARIATION_FONTS)
807     case CSSPropertyFontOpticalSizing:
808         return valueID == CSSValueAuto || valueID == CSSValueNone;
809 #endif
810     default:
811         ASSERT_NOT_REACHED();
812         return false;
813     }
814 }
815
816 bool CSSParserFastPaths::isKeywordPropertyID(CSSPropertyID propertyId)
817 {
818     switch (propertyId) {
819     case CSSPropertyBorderBlockEndStyle:
820     case CSSPropertyBorderBlockStartStyle:
821     case CSSPropertyBorderBottomStyle:
822     case CSSPropertyBorderCollapse:
823     case CSSPropertyBorderInlineEndStyle:
824     case CSSPropertyBorderInlineStartStyle:
825     case CSSPropertyBorderLeftStyle:
826     case CSSPropertyBorderRightStyle:
827     case CSSPropertyBorderTopStyle:
828     case CSSPropertyBoxSizing:
829     case CSSPropertyBreakAfter:
830     case CSSPropertyBreakBefore:
831     case CSSPropertyBreakInside:
832     case CSSPropertyCaptionSide:
833     case CSSPropertyClear:
834     case CSSPropertyColumnFill:
835     case CSSPropertyWebkitColumnProgression:
836     case CSSPropertyColumnRuleStyle:
837     case CSSPropertyDirection:
838     case CSSPropertyDisplay:
839     case CSSPropertyEmptyCells:
840     case CSSPropertyFlexDirection:
841     case CSSPropertyFlexWrap:
842     case CSSPropertyFloat:
843     case CSSPropertyFontVariantAlternates:
844     case CSSPropertyFontVariantCaps:
845     case CSSPropertyFontVariantPosition:
846     case CSSPropertyImageRendering:
847     case CSSPropertyListStylePosition:
848     case CSSPropertyListStyleType:
849     case CSSPropertyObjectFit:
850     case CSSPropertyOutlineStyle:
851     case CSSPropertyOverflowWrap:
852     case CSSPropertyOverflowX:
853     case CSSPropertyOverflowY:
854     case CSSPropertyPointerEvents:
855     case CSSPropertyPosition:
856     case CSSPropertyResize:
857     case CSSPropertyTableLayout:
858     case CSSPropertyTextAlign:
859     case CSSPropertyTextOverflow:
860     case CSSPropertyTextRendering:
861     case CSSPropertyTextTransform:
862     case CSSPropertyTransformStyle:
863     case CSSPropertyUnicodeBidi:
864     case CSSPropertyVisibility:
865     case CSSPropertyWebkitAppearance:
866     case CSSPropertyWebkitBackfaceVisibility:
867     case CSSPropertyWebkitBorderFit:
868     case CSSPropertyWebkitBoxAlign:
869     case CSSPropertyWebkitBoxDirection:
870     case CSSPropertyWebkitBoxLines:
871     case CSSPropertyWebkitBoxOrient:
872     case CSSPropertyWebkitBoxPack:
873     case CSSPropertyWebkitColumnAxis:
874     case CSSPropertyWebkitFontKerning:
875     case CSSPropertyWebkitFontSmoothing:
876     case CSSPropertyWebkitHyphens:
877     case CSSPropertyWebkitLineAlign:
878     case CSSPropertyLineBreak:
879     case CSSPropertyWebkitLineSnap:
880     case CSSPropertyWebkitMarginAfterCollapse:
881     case CSSPropertyWebkitMarginBeforeCollapse:
882     case CSSPropertyWebkitMarginBottomCollapse:
883     case CSSPropertyWebkitMarginTopCollapse:
884     case CSSPropertyWebkitMarqueeDirection:
885     case CSSPropertyWebkitMarqueeStyle:
886     case CSSPropertyWebkitNbspMode:
887     case CSSPropertyWebkitPrintColorAdjust:
888     case CSSPropertyWebkitRtlOrdering:
889     case CSSPropertyWebkitRubyPosition:
890     case CSSPropertyWebkitTextCombine:
891     case CSSPropertyWebkitTextDecorationStyle:
892     case CSSPropertyWebkitTextOrientation:
893     case CSSPropertyWebkitTextSecurity:
894     case CSSPropertyWebkitTextZoom:
895     case CSSPropertyWebkitTransformStyle:
896     case CSSPropertyWebkitUserDrag:
897     case CSSPropertyWebkitUserModify:
898     case CSSPropertyWebkitUserSelect:
899     case CSSPropertyWhiteSpace:
900     case CSSPropertyWordBreak:
901     case CSSPropertyWordWrap:
902
903     // SVG CSS properties from SVG 1.1, Appendix N: Property Index.
904     case CSSPropertyAlignmentBaseline:
905     case CSSPropertyBufferedRendering:
906     case CSSPropertyClipRule:
907     case CSSPropertyColorInterpolation:
908     case CSSPropertyColorInterpolationFilters:
909     case CSSPropertyColorRendering:
910     case CSSPropertyDominantBaseline:
911     case CSSPropertyFillRule:
912     case CSSPropertyMaskType:
913     case CSSPropertyShapeRendering:
914     case CSSPropertyStrokeLinecap:
915     case CSSPropertyStrokeLinejoin:
916     case CSSPropertyTextAnchor:
917     case CSSPropertyVectorEffect:
918     case CSSPropertyWritingMode:
919
920     // FIXME-NEWPARSER: Treat all as a keyword property.
921     // case CSSPropertyAll:
922
923     // FIXME-NEWPARSER: Add the following unprefixed properties:
924     // case CSSPropertyBackfaceVisibility:
925     // case CSSPropertyFontKerning:
926     // case CSSPropertyHyphens:
927     // case CSSPropertyOverflowAnchor:
928     // case CSSPropertyScrollBehavior:
929     // case CSSPropertyScrollSnapType:
930     // case CSSPropertyTextAlignLast:
931     // case CSSPropertyTextCombineUpright:
932     // case CSSPropertyTextDecorationStyle:
933     // case CSSPropertyTextJustify:
934     // case CSSPropertyTextOrientation:
935     // case CSSPropertyUserSelect:
936 #if ENABLE(CSS_TRAILING_WORD)
937     case CSSPropertyAppleTrailingWord:
938 #endif
939 #if ENABLE(CSS_COMPOSITING)
940     case CSSPropertyIsolation:
941     case CSSPropertyMixBlendMode:
942 #endif
943 #if ENABLE(TOUCH_EVENTS)
944     case CSSPropertyTouchAction:
945 #endif
946 #if ENABLE(CSS_BOX_DECORATION_BREAK)
947     case CSSPropertyWebkitBoxDecorationBreak:
948 #endif
949 #if ENABLE(CURSOR_VISIBILITY)
950     case CSSPropertyWebkitCursorVisibility:
951 #endif
952 #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING)
953     case CSSPropertyWebkitOverflowScrolling:
954 #endif
955 #if ENABLE(CSS3_TEXT)
956     case CSSPropertyWebkitTextAlignLast:
957     case CSSPropertyWebkitTextJustify:
958 #endif
959 #if PLATFORM(IOS)
960     // Apple specific property. This will never be standardized and is purely to
961     // support custom WebKit-based Apple applications.
962     case CSSPropertyWebkitTouchCallout:
963 #endif
964 #if ENABLE(APPLE_PAY)
965     case CSSPropertyApplePayButtonStyle:
966     case CSSPropertyApplePayButtonType:
967 #endif
968 #if ENABLE(VARIATION_FONTS)
969     case CSSPropertyFontOpticalSizing:
970 #endif
971         return true;
972     default:
973         return false;
974     }
975 }
976
977 static bool isUniversalKeyword(const String& string)
978 {
979     // These keywords can be used for all properties.
980     return equalLettersIgnoringASCIICase(string, "initial")
981     || equalLettersIgnoringASCIICase(string, "inherit")
982     || equalLettersIgnoringASCIICase(string, "unset")
983     || equalLettersIgnoringASCIICase(string, "revert");
984 }
985
986 static RefPtr<CSSValue> parseKeywordValue(CSSPropertyID propertyId, const String& string, CSSParserMode parserMode)
987 {
988     ASSERT(!string.isEmpty());
989
990     if (!CSSParserFastPaths::isKeywordPropertyID(propertyId)) {
991         // All properties accept the values of "initial" and "inherit".
992         if (!isUniversalKeyword(string))
993             return nullptr;
994
995         // Parse initial/inherit shorthands using the CSSPropertyParser.
996         if (shorthandForProperty(propertyId).length())
997             return nullptr;
998
999         // Descriptors do not support css wide keywords.
1000         if (CSSProperty::isDescriptorOnly(propertyId))
1001             return nullptr;
1002     }
1003
1004     CSSValueID valueID = cssValueKeywordID(string);
1005
1006     if (!valueID)
1007         return nullptr;
1008
1009     if (valueID == CSSValueInherit)
1010         return CSSValuePool::singleton().createInheritedValue();
1011     if (valueID == CSSValueInitial)
1012         return CSSValuePool::singleton().createExplicitInitialValue();
1013     if (valueID == CSSValueUnset)
1014         return CSSValuePool::singleton().createUnsetValue();
1015     if (valueID == CSSValueRevert)
1016         return CSSValuePool::singleton().createRevertValue();
1017     
1018     if (CSSParserFastPaths::isValidKeywordPropertyAndValue(propertyId, valueID, parserMode))
1019         return CSSPrimitiveValue::createIdentifier(valueID);
1020     return nullptr;
1021 }
1022
1023 template <typename CharType>
1024 static bool parseTransformTranslateArguments(CharType*& pos, CharType* end, unsigned expectedCount, CSSFunctionValue* transformValue)
1025 {
1026     while (expectedCount) {
1027         size_t delimiter = WTF::find(pos, end - pos, expectedCount == 1 ? ')' : ',');
1028         if (delimiter == notFound)
1029             return false;
1030         unsigned argumentLength = static_cast<unsigned>(delimiter);
1031         CSSPrimitiveValue::UnitType unit = CSSPrimitiveValue::UnitType::CSS_NUMBER;
1032         double number;
1033         if (!parseSimpleLength(pos, argumentLength, unit, number))
1034             return false;
1035         if (!number && unit == CSSPrimitiveValue::CSS_NUMBER)
1036             unit = CSSPrimitiveValue::UnitType::CSS_PX;
1037         if (unit == CSSPrimitiveValue::UnitType::CSS_NUMBER || (unit == CSSPrimitiveValue::UnitType::CSS_PERCENTAGE && (transformValue->name() == CSSValueTranslateZ || (transformValue->name() == CSSValueTranslate3d && expectedCount == 1))))
1038             return false;
1039         transformValue->append(CSSPrimitiveValue::create(number, unit));
1040         pos += argumentLength + 1;
1041         --expectedCount;
1042     }
1043     return true;
1044 }
1045
1046 template <typename CharType>
1047 static bool parseTransformAngleArgument(CharType*& pos, CharType* end, CSSFunctionValue* transformValue)
1048 {
1049     size_t delimiter = WTF::find(pos, end - pos, ')');
1050     if (delimiter == notFound)
1051         return false;
1052
1053     unsigned argumentLength = static_cast<unsigned>(delimiter);
1054     CSSPrimitiveValue::UnitType unit = CSSPrimitiveValue::UnitType::CSS_NUMBER;
1055     double number;
1056     if (!parseSimpleAngle(pos, argumentLength, unit, number))
1057         return false;
1058     if (!number && unit == CSSPrimitiveValue::CSS_NUMBER)
1059         unit = CSSPrimitiveValue::UnitType::CSS_DEG;
1060
1061     transformValue->append(CSSPrimitiveValue::create(number, unit));
1062     pos += argumentLength + 1;
1063
1064     return true;
1065 }
1066
1067 template <typename CharType>
1068 static bool parseTransformNumberArguments(CharType*& pos, CharType* end, unsigned expectedCount, CSSFunctionValue* transformValue)
1069 {
1070     while (expectedCount) {
1071         size_t delimiter = WTF::find(pos, end - pos, expectedCount == 1 ? ')' : ',');
1072         if (delimiter == notFound)
1073             return false;
1074         unsigned argumentLength = static_cast<unsigned>(delimiter);
1075         bool ok;
1076         double number = charactersToDouble(pos, argumentLength, &ok);
1077         if (!ok)
1078             return false;
1079         transformValue->append(CSSPrimitiveValue::create(number, CSSPrimitiveValue::UnitType::CSS_NUMBER));
1080         pos += argumentLength + 1;
1081         --expectedCount;
1082     }
1083     return true;
1084 }
1085
1086 static const int kShortestValidTransformStringLength = 9; // "rotate(0)"
1087
1088 template <typename CharType>
1089 static RefPtr<CSSFunctionValue> parseSimpleTransformValue(CharType*& pos, CharType* end)
1090 {
1091     if (end - pos < kShortestValidTransformStringLength)
1092         return nullptr;
1093
1094     const bool isTranslate = toASCIILower(pos[0]) == 't'
1095         && toASCIILower(pos[1]) == 'r'
1096         && toASCIILower(pos[2]) == 'a'
1097         && toASCIILower(pos[3]) == 'n'
1098         && toASCIILower(pos[4]) == 's'
1099         && toASCIILower(pos[5]) == 'l'
1100         && toASCIILower(pos[6]) == 'a'
1101         && toASCIILower(pos[7]) == 't'
1102         && toASCIILower(pos[8]) == 'e';
1103
1104     if (isTranslate) {
1105         CSSValueID transformType;
1106         unsigned expectedArgumentCount = 1;
1107         unsigned argumentStart = 11;
1108         CharType c9 = toASCIILower(pos[9]);
1109         if (c9 == 'x' && pos[10] == '(') {
1110             transformType = CSSValueTranslateX;
1111         } else if (c9 == 'y' && pos[10] == '(') {
1112             transformType = CSSValueTranslateY;
1113         } else if (c9 == 'z' && pos[10] == '(') {
1114             transformType = CSSValueTranslateZ;
1115         } else if (c9 == '(') {
1116             transformType = CSSValueTranslate;
1117             expectedArgumentCount = 2;
1118             argumentStart = 10;
1119         } else if (c9 == '3' && toASCIILower(pos[10]) == 'd' && pos[11] == '(') {
1120             transformType = CSSValueTranslate3d;
1121             expectedArgumentCount = 3;
1122             argumentStart = 12;
1123         } else
1124             return nullptr;
1125
1126         pos += argumentStart;
1127         RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(transformType);
1128         if (!parseTransformTranslateArguments(pos, end, expectedArgumentCount, transformValue.get()))
1129             return nullptr;
1130         return transformValue;
1131     }
1132
1133     const bool isMatrix3d = toASCIILower(pos[0]) == 'm'
1134         && toASCIILower(pos[1]) == 'a'
1135         && toASCIILower(pos[2]) == 't'
1136         && toASCIILower(pos[3]) == 'r'
1137         && toASCIILower(pos[4]) == 'i'
1138         && toASCIILower(pos[5]) == 'x'
1139         && pos[6] == '3'
1140         && toASCIILower(pos[7]) == 'd'
1141         && pos[8] == '(';
1142
1143     if (isMatrix3d) {
1144         pos += 9;
1145         RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(CSSValueMatrix3d);
1146         if (!parseTransformNumberArguments(pos, end, 16, transformValue.get()))
1147             return nullptr;
1148         return transformValue;
1149     }
1150
1151     const bool isScale3d = toASCIILower(pos[0]) == 's'
1152         && toASCIILower(pos[1]) == 'c'
1153         && toASCIILower(pos[2]) == 'a'
1154         && toASCIILower(pos[3]) == 'l'
1155         && toASCIILower(pos[4]) == 'e'
1156         && pos[5] == '3'
1157         && toASCIILower(pos[6]) == 'd'
1158         && pos[7] == '(';
1159
1160     if (isScale3d) {
1161         pos += 8;
1162         RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(CSSValueScale3d);
1163         if (!parseTransformNumberArguments(pos, end, 3, transformValue.get()))
1164             return nullptr;
1165         return transformValue;
1166     }
1167
1168     const bool isRotate = toASCIILower(pos[0]) == 'r'
1169         && toASCIILower(pos[1]) == 'o'
1170         && toASCIILower(pos[2]) == 't'
1171         && toASCIILower(pos[3]) == 'a'
1172         && toASCIILower(pos[4]) == 't'
1173         && toASCIILower(pos[5]) == 'e';
1174
1175     if (isRotate) {
1176         CSSValueID transformType;
1177         unsigned argumentStart = 7;
1178         CharType c6 = toASCIILower(pos[6]);
1179         if (c6 == '(') {
1180             transformType = CSSValueRotate;
1181         } else if (c6 == 'z' && pos[7] == '(') {
1182             transformType = CSSValueRotateZ;
1183             argumentStart = 8;
1184         } else
1185             return nullptr;
1186
1187         pos += argumentStart;
1188         RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(transformType);
1189         if (!parseTransformAngleArgument(pos, end, transformValue.get()))
1190             return nullptr;
1191         return transformValue;
1192     }
1193
1194     return nullptr;
1195 }
1196
1197 template <typename CharType>
1198 static bool transformCanLikelyUseFastPath(const CharType* chars, unsigned length)
1199 {
1200     // Very fast scan that attempts to reject most transforms that couldn't
1201     // take the fast path. This avoids doing the malloc and string->double
1202     // conversions in parseSimpleTransformValue only to discard them when we
1203     // run into a transform component we don't understand.
1204     unsigned i = 0;
1205     while (i < length) {
1206         if (isCSSSpace(chars[i])) {
1207             ++i;
1208             continue;
1209         }
1210
1211         if (length - i < kShortestValidTransformStringLength)
1212             return false;
1213         
1214         switch (toASCIILower(chars[i])) {
1215         case 't':
1216             // translate, translateX, translateY, translateZ, translate3d.
1217             if (toASCIILower(chars[i + 8]) != 'e')
1218                 return false;
1219             i += 9;
1220             break;
1221         case 'm':
1222             // matrix3d.
1223             if (toASCIILower(chars[i + 7]) != 'd')
1224                 return false;
1225             i += 8;
1226             break;
1227         case 's':
1228             // scale3d.
1229             if (toASCIILower(chars[i + 6]) != 'd')
1230                 return false;
1231             i += 7;
1232             break;
1233         case 'r':
1234             // rotate.
1235             if (toASCIILower(chars[i + 5]) != 'e')
1236                 return false;
1237             i += 6;
1238             // rotateZ
1239             if (toASCIILower(chars[i]) == 'z')
1240                 ++i;
1241             break;
1242
1243         default:
1244             return false;
1245         }
1246         size_t argumentsEnd = WTF::find(chars, length, ')', i);
1247         if (argumentsEnd == notFound)
1248             return false;
1249         // Advance to the end of the arguments.
1250         i = argumentsEnd + 1;
1251     }
1252     return i == length;
1253 }
1254
1255 template <typename CharType>
1256 static RefPtr<CSSValueList> parseSimpleTransformList(const CharType* chars, unsigned length)
1257 {
1258     if (!transformCanLikelyUseFastPath(chars, length))
1259         return nullptr;
1260     const CharType*& pos = chars;
1261     const CharType* end = chars + length;
1262     RefPtr<CSSValueList> transformList;
1263     while (pos < end) {
1264         while (pos < end && isCSSSpace(*pos))
1265             ++pos;
1266         if (pos >= end)
1267             break;
1268         RefPtr<CSSFunctionValue> transformValue = parseSimpleTransformValue(pos, end);
1269         if (!transformValue)
1270             return nullptr;
1271         if (!transformList)
1272             transformList = CSSValueList::createSpaceSeparated();
1273         transformList->append(*transformValue);
1274     }
1275     return transformList;
1276 }
1277
1278 static RefPtr<CSSValue> parseSimpleTransform(CSSPropertyID propertyID, const String& string)
1279 {
1280     ASSERT(!string.isEmpty());
1281     if (propertyID != CSSPropertyTransform)
1282         return nullptr;
1283     if (string.is8Bit())
1284         return parseSimpleTransformList(string.characters8(), string.length());
1285     return parseSimpleTransformList(string.characters16(), string.length());
1286 }
1287
1288 static RefPtr<CSSValue> parseCaretColor(const String& string, CSSParserMode parserMode)
1289 {
1290     ASSERT(!string.isEmpty());
1291     CSSValueID valueID = cssValueKeywordID(string);
1292     if (valueID == CSSValueAuto)
1293         return CSSValuePool::singleton().createIdentifierValue(valueID);
1294     return CSSParserFastPaths::parseColor(string, parserMode);
1295 }
1296
1297 RefPtr<CSSValue> CSSParserFastPaths::maybeParseValue(CSSPropertyID propertyID, const String& string, CSSParserMode parserMode)
1298 {
1299     if (auto result = parseSimpleLengthValue(propertyID, string, parserMode))
1300         return result;
1301     if (propertyID == CSSPropertyCaretColor)
1302         return parseCaretColor(string, parserMode);
1303     if (isColorPropertyID(propertyID))
1304         return parseColor(string, parserMode);
1305     if (auto result = parseKeywordValue(propertyID, string, parserMode))
1306         return result;
1307     return parseSimpleTransform(propertyID, string);
1308 }
1309
1310 } // namespace WebCore