a92b7c04a6c6bb5398aff3470a681fd9076881a3
[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
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
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 | donate
773         return valueID == CSSValuePlain || valueID == CSSValueBuy || valueID == CSSValueSetUp || valueID == CSSValueDonate;
774 #endif
775     case CSSPropertyWebkitNbspMode: // normal | space
776         return valueID == CSSValueNormal || valueID == CSSValueSpace;
777     case CSSPropertyWebkitTextZoom:
778         return valueID == CSSValueNormal || valueID == CSSValueReset;
779 #if PLATFORM(IOS)
780     // Apple specific property. These will never be standardized and is purely to
781     // support custom WebKit-based Apple applications.
782     case CSSPropertyWebkitTouchCallout:
783         return valueID == CSSValueDefault || valueID == CSSValueNone;
784 #endif
785     case CSSPropertyWebkitMarqueeDirection:
786         return valueID == CSSValueForwards || valueID == CSSValueBackwards || valueID == CSSValueAhead || valueID == CSSValueReverse || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueDown
787             || valueID == CSSValueUp || valueID == CSSValueAuto;
788     case CSSPropertyWebkitMarqueeStyle:
789         return valueID == CSSValueNone || valueID == CSSValueSlide || valueID == CSSValueScroll || valueID == CSSValueAlternate;
790     case CSSPropertyFontVariantPosition: // normal | sub | super
791         return valueID == CSSValueNormal || valueID == CSSValueSub || valueID == CSSValueSuper;
792     case CSSPropertyFontVariantCaps: // normal | small-caps | all-small-caps | petite-caps | all-petite-caps | unicase | titling-caps
793         return valueID == CSSValueNormal || valueID == CSSValueSmallCaps || valueID == CSSValueAllSmallCaps || valueID == CSSValuePetiteCaps || valueID == CSSValueAllPetiteCaps || valueID == CSSValueUnicase || valueID == CSSValueTitlingCaps;
794     case CSSPropertyFontVariantAlternates: // We only support the normal and historical-forms values.
795         return valueID == CSSValueNormal || valueID == CSSValueHistoricalForms;
796 #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING)
797     case CSSPropertyWebkitOverflowScrolling:
798         return valueID == CSSValueAuto || valueID == CSSValueTouch;
799 #endif
800 #if ENABLE(VARIATION_FONTS)
801     case CSSPropertyFontOpticalSizing:
802         return valueID == CSSValueAuto || valueID == CSSValueNone;
803 #endif
804     default:
805         ASSERT_NOT_REACHED();
806         return false;
807     }
808 }
809
810 bool CSSParserFastPaths::isKeywordPropertyID(CSSPropertyID propertyId)
811 {
812     switch (propertyId) {
813     case CSSPropertyBorderBlockEndStyle:
814     case CSSPropertyBorderBlockStartStyle:
815     case CSSPropertyBorderBottomStyle:
816     case CSSPropertyBorderCollapse:
817     case CSSPropertyBorderInlineEndStyle:
818     case CSSPropertyBorderInlineStartStyle:
819     case CSSPropertyBorderLeftStyle:
820     case CSSPropertyBorderRightStyle:
821     case CSSPropertyBorderTopStyle:
822     case CSSPropertyBoxSizing:
823     case CSSPropertyBreakAfter:
824     case CSSPropertyBreakBefore:
825     case CSSPropertyBreakInside:
826     case CSSPropertyCaptionSide:
827     case CSSPropertyClear:
828     case CSSPropertyColumnFill:
829     case CSSPropertyWebkitColumnProgression:
830     case CSSPropertyColumnRuleStyle:
831     case CSSPropertyDirection:
832     case CSSPropertyDisplay:
833     case CSSPropertyEmptyCells:
834     case CSSPropertyFlexDirection:
835     case CSSPropertyFlexWrap:
836     case CSSPropertyFloat:
837     case CSSPropertyFontVariantAlternates:
838     case CSSPropertyFontVariantCaps:
839     case CSSPropertyFontVariantPosition:
840     case CSSPropertyImageRendering:
841     case CSSPropertyListStylePosition:
842     case CSSPropertyListStyleType:
843     case CSSPropertyObjectFit:
844     case CSSPropertyOutlineStyle:
845     case CSSPropertyOverflowWrap:
846     case CSSPropertyOverflowX:
847     case CSSPropertyOverflowY:
848     case CSSPropertyPointerEvents:
849     case CSSPropertyPosition:
850     case CSSPropertyResize:
851     case CSSPropertyTableLayout:
852     case CSSPropertyTextAlign:
853     case CSSPropertyTextOverflow:
854     case CSSPropertyTextRendering:
855     case CSSPropertyTextTransform:
856     case CSSPropertyTransformStyle:
857     case CSSPropertyUnicodeBidi:
858     case CSSPropertyVisibility:
859     case CSSPropertyWebkitAppearance:
860     case CSSPropertyWebkitBackfaceVisibility:
861     case CSSPropertyWebkitBorderFit:
862     case CSSPropertyWebkitBoxAlign:
863     case CSSPropertyWebkitBoxDirection:
864     case CSSPropertyWebkitBoxLines:
865     case CSSPropertyWebkitBoxOrient:
866     case CSSPropertyWebkitBoxPack:
867     case CSSPropertyWebkitColumnAxis:
868     case CSSPropertyWebkitFontKerning:
869     case CSSPropertyWebkitFontSmoothing:
870     case CSSPropertyWebkitHyphens:
871     case CSSPropertyWebkitLineAlign:
872     case CSSPropertyLineBreak:
873     case CSSPropertyWebkitLineSnap:
874     case CSSPropertyWebkitMarginAfterCollapse:
875     case CSSPropertyWebkitMarginBeforeCollapse:
876     case CSSPropertyWebkitMarginBottomCollapse:
877     case CSSPropertyWebkitMarginTopCollapse:
878     case CSSPropertyWebkitMarqueeDirection:
879     case CSSPropertyWebkitMarqueeStyle:
880     case CSSPropertyWebkitNbspMode:
881     case CSSPropertyWebkitPrintColorAdjust:
882     case CSSPropertyWebkitRtlOrdering:
883     case CSSPropertyWebkitRubyPosition:
884     case CSSPropertyWebkitTextCombine:
885     case CSSPropertyWebkitTextDecorationStyle:
886     case CSSPropertyWebkitTextOrientation:
887     case CSSPropertyWebkitTextSecurity:
888     case CSSPropertyWebkitTextZoom:
889     case CSSPropertyWebkitTransformStyle:
890     case CSSPropertyWebkitUserDrag:
891     case CSSPropertyWebkitUserModify:
892     case CSSPropertyWebkitUserSelect:
893     case CSSPropertyWhiteSpace:
894     case CSSPropertyWordBreak:
895     case CSSPropertyWordWrap:
896
897     // SVG CSS properties from SVG 1.1, Appendix N: Property Index.
898     case CSSPropertyAlignmentBaseline:
899     case CSSPropertyBufferedRendering:
900     case CSSPropertyClipRule:
901     case CSSPropertyColorInterpolation:
902     case CSSPropertyColorInterpolationFilters:
903     case CSSPropertyColorRendering:
904     case CSSPropertyDominantBaseline:
905     case CSSPropertyFillRule:
906     case CSSPropertyMaskType:
907     case CSSPropertyShapeRendering:
908     case CSSPropertyStrokeLinecap:
909     case CSSPropertyStrokeLinejoin:
910     case CSSPropertyTextAnchor:
911     case CSSPropertyVectorEffect:
912     case CSSPropertyWritingMode:
913
914     // FIXME-NEWPARSER: Treat all as a keyword property.
915     // case CSSPropertyAll:
916
917     // FIXME-NEWPARSER: Add the following unprefixed properties:
918     // case CSSPropertyBackfaceVisibility:
919     // case CSSPropertyFontKerning:
920     // case CSSPropertyHyphens:
921     // case CSSPropertyOverflowAnchor:
922     // case CSSPropertyScrollBehavior:
923     // case CSSPropertyScrollSnapType:
924     // case CSSPropertyTextAlignLast:
925     // case CSSPropertyTextCombineUpright:
926     // case CSSPropertyTextDecorationStyle:
927     // case CSSPropertyTextJustify:
928     // case CSSPropertyTextOrientation:
929     // case CSSPropertyUserSelect:
930 #if ENABLE(CSS_TRAILING_WORD)
931     case CSSPropertyAppleTrailingWord:
932 #endif
933 #if ENABLE(CSS_COMPOSITING)
934     case CSSPropertyIsolation:
935     case CSSPropertyMixBlendMode:
936 #endif
937 #if ENABLE(TOUCH_EVENTS)
938     case CSSPropertyTouchAction:
939 #endif
940 #if ENABLE(CSS_BOX_DECORATION_BREAK)
941     case CSSPropertyWebkitBoxDecorationBreak:
942 #endif
943 #if ENABLE(CURSOR_VISIBILITY)
944     case CSSPropertyWebkitCursorVisibility:
945 #endif
946 #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING)
947     case CSSPropertyWebkitOverflowScrolling:
948 #endif
949 #if ENABLE(CSS3_TEXT)
950     case CSSPropertyWebkitTextAlignLast:
951     case CSSPropertyWebkitTextJustify:
952 #endif
953 #if PLATFORM(IOS)
954     // Apple specific property. This will never be standardized and is purely to
955     // support custom WebKit-based Apple applications.
956     case CSSPropertyWebkitTouchCallout:
957 #endif
958 #if ENABLE(APPLE_PAY)
959     case CSSPropertyApplePayButtonStyle:
960     case CSSPropertyApplePayButtonType:
961 #endif
962 #if ENABLE(VARIATION_FONTS)
963     case CSSPropertyFontOpticalSizing:
964 #endif
965         return true;
966     default:
967         return false;
968     }
969 }
970
971 static bool isUniversalKeyword(const String& string)
972 {
973     // These keywords can be used for all properties.
974     return equalLettersIgnoringASCIICase(string, "initial")
975     || equalLettersIgnoringASCIICase(string, "inherit")
976     || equalLettersIgnoringASCIICase(string, "unset")
977     || equalLettersIgnoringASCIICase(string, "revert");
978 }
979
980 static RefPtr<CSSValue> parseKeywordValue(CSSPropertyID propertyId, const String& string, CSSParserMode parserMode)
981 {
982     ASSERT(!string.isEmpty());
983
984     if (!CSSParserFastPaths::isKeywordPropertyID(propertyId)) {
985         // All properties accept the values of "initial" and "inherit".
986         if (!isUniversalKeyword(string))
987             return nullptr;
988
989         // Parse initial/inherit shorthands using the CSSPropertyParser.
990         if (shorthandForProperty(propertyId).length())
991             return nullptr;
992
993         // Descriptors do not support css wide keywords.
994         if (CSSProperty::isDescriptorOnly(propertyId))
995             return nullptr;
996     }
997
998     CSSValueID valueID = cssValueKeywordID(string);
999
1000     if (!valueID)
1001         return nullptr;
1002
1003     if (valueID == CSSValueInherit)
1004         return CSSValuePool::singleton().createInheritedValue();
1005     if (valueID == CSSValueInitial)
1006         return CSSValuePool::singleton().createExplicitInitialValue();
1007     if (valueID == CSSValueUnset)
1008         return CSSValuePool::singleton().createUnsetValue();
1009     if (valueID == CSSValueRevert)
1010         return CSSValuePool::singleton().createRevertValue();
1011     
1012     if (CSSParserFastPaths::isValidKeywordPropertyAndValue(propertyId, valueID, parserMode))
1013         return CSSPrimitiveValue::createIdentifier(valueID);
1014     return nullptr;
1015 }
1016
1017 template <typename CharType>
1018 static bool parseTransformTranslateArguments(CharType*& pos, CharType* end, unsigned expectedCount, CSSFunctionValue* transformValue)
1019 {
1020     while (expectedCount) {
1021         size_t delimiter = WTF::find(pos, end - pos, expectedCount == 1 ? ')' : ',');
1022         if (delimiter == notFound)
1023             return false;
1024         unsigned argumentLength = static_cast<unsigned>(delimiter);
1025         CSSPrimitiveValue::UnitType unit = CSSPrimitiveValue::UnitType::CSS_NUMBER;
1026         double number;
1027         if (!parseSimpleLength(pos, argumentLength, unit, number))
1028             return false;
1029         if (!number && unit == CSSPrimitiveValue::CSS_NUMBER)
1030             unit = CSSPrimitiveValue::UnitType::CSS_PX;
1031         if (unit == CSSPrimitiveValue::UnitType::CSS_NUMBER || (unit == CSSPrimitiveValue::UnitType::CSS_PERCENTAGE && (transformValue->name() == CSSValueTranslateZ || (transformValue->name() == CSSValueTranslate3d && expectedCount == 1))))
1032             return false;
1033         transformValue->append(CSSPrimitiveValue::create(number, unit));
1034         pos += argumentLength + 1;
1035         --expectedCount;
1036     }
1037     return true;
1038 }
1039
1040 template <typename CharType>
1041 static bool parseTransformAngleArgument(CharType*& pos, CharType* end, CSSFunctionValue* transformValue)
1042 {
1043     size_t delimiter = WTF::find(pos, end - pos, ')');
1044     if (delimiter == notFound)
1045         return false;
1046
1047     unsigned argumentLength = static_cast<unsigned>(delimiter);
1048     CSSPrimitiveValue::UnitType unit = CSSPrimitiveValue::UnitType::CSS_NUMBER;
1049     double number;
1050     if (!parseSimpleAngle(pos, argumentLength, unit, number))
1051         return false;
1052     if (!number && unit == CSSPrimitiveValue::CSS_NUMBER)
1053         unit = CSSPrimitiveValue::UnitType::CSS_DEG;
1054
1055     transformValue->append(CSSPrimitiveValue::create(number, unit));
1056     pos += argumentLength + 1;
1057
1058     return true;
1059 }
1060
1061 template <typename CharType>
1062 static bool parseTransformNumberArguments(CharType*& pos, CharType* end, unsigned expectedCount, CSSFunctionValue* transformValue)
1063 {
1064     while (expectedCount) {
1065         size_t delimiter = WTF::find(pos, end - pos, expectedCount == 1 ? ')' : ',');
1066         if (delimiter == notFound)
1067             return false;
1068         unsigned argumentLength = static_cast<unsigned>(delimiter);
1069         bool ok;
1070         double number = charactersToDouble(pos, argumentLength, &ok);
1071         if (!ok)
1072             return false;
1073         transformValue->append(CSSPrimitiveValue::create(number, CSSPrimitiveValue::UnitType::CSS_NUMBER));
1074         pos += argumentLength + 1;
1075         --expectedCount;
1076     }
1077     return true;
1078 }
1079
1080 static const int kShortestValidTransformStringLength = 9; // "rotate(0)"
1081
1082 template <typename CharType>
1083 static RefPtr<CSSFunctionValue> parseSimpleTransformValue(CharType*& pos, CharType* end)
1084 {
1085     if (end - pos < kShortestValidTransformStringLength)
1086         return nullptr;
1087
1088     const bool isTranslate = toASCIILower(pos[0]) == 't'
1089         && toASCIILower(pos[1]) == 'r'
1090         && toASCIILower(pos[2]) == 'a'
1091         && toASCIILower(pos[3]) == 'n'
1092         && toASCIILower(pos[4]) == 's'
1093         && toASCIILower(pos[5]) == 'l'
1094         && toASCIILower(pos[6]) == 'a'
1095         && toASCIILower(pos[7]) == 't'
1096         && toASCIILower(pos[8]) == 'e';
1097
1098     if (isTranslate) {
1099         CSSValueID transformType;
1100         unsigned expectedArgumentCount = 1;
1101         unsigned argumentStart = 11;
1102         CharType c9 = toASCIILower(pos[9]);
1103         if (c9 == 'x' && pos[10] == '(') {
1104             transformType = CSSValueTranslateX;
1105         } else if (c9 == 'y' && pos[10] == '(') {
1106             transformType = CSSValueTranslateY;
1107         } else if (c9 == 'z' && pos[10] == '(') {
1108             transformType = CSSValueTranslateZ;
1109         } else if (c9 == '(') {
1110             transformType = CSSValueTranslate;
1111             expectedArgumentCount = 2;
1112             argumentStart = 10;
1113         } else if (c9 == '3' && toASCIILower(pos[10]) == 'd' && pos[11] == '(') {
1114             transformType = CSSValueTranslate3d;
1115             expectedArgumentCount = 3;
1116             argumentStart = 12;
1117         } else
1118             return nullptr;
1119
1120         pos += argumentStart;
1121         RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(transformType);
1122         if (!parseTransformTranslateArguments(pos, end, expectedArgumentCount, transformValue.get()))
1123             return nullptr;
1124         return transformValue;
1125     }
1126
1127     const bool isMatrix3d = toASCIILower(pos[0]) == 'm'
1128         && toASCIILower(pos[1]) == 'a'
1129         && toASCIILower(pos[2]) == 't'
1130         && toASCIILower(pos[3]) == 'r'
1131         && toASCIILower(pos[4]) == 'i'
1132         && toASCIILower(pos[5]) == 'x'
1133         && pos[6] == '3'
1134         && toASCIILower(pos[7]) == 'd'
1135         && pos[8] == '(';
1136
1137     if (isMatrix3d) {
1138         pos += 9;
1139         RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(CSSValueMatrix3d);
1140         if (!parseTransformNumberArguments(pos, end, 16, transformValue.get()))
1141             return nullptr;
1142         return transformValue;
1143     }
1144
1145     const bool isScale3d = toASCIILower(pos[0]) == 's'
1146         && toASCIILower(pos[1]) == 'c'
1147         && toASCIILower(pos[2]) == 'a'
1148         && toASCIILower(pos[3]) == 'l'
1149         && toASCIILower(pos[4]) == 'e'
1150         && pos[5] == '3'
1151         && toASCIILower(pos[6]) == 'd'
1152         && pos[7] == '(';
1153
1154     if (isScale3d) {
1155         pos += 8;
1156         RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(CSSValueScale3d);
1157         if (!parseTransformNumberArguments(pos, end, 3, transformValue.get()))
1158             return nullptr;
1159         return transformValue;
1160     }
1161
1162     const bool isRotate = toASCIILower(pos[0]) == 'r'
1163         && toASCIILower(pos[1]) == 'o'
1164         && toASCIILower(pos[2]) == 't'
1165         && toASCIILower(pos[3]) == 'a'
1166         && toASCIILower(pos[4]) == 't'
1167         && toASCIILower(pos[5]) == 'e';
1168
1169     if (isRotate) {
1170         CSSValueID transformType;
1171         unsigned argumentStart = 7;
1172         CharType c6 = toASCIILower(pos[6]);
1173         if (c6 == '(') {
1174             transformType = CSSValueRotate;
1175         } else if (c6 == 'z' && pos[7] == '(') {
1176             transformType = CSSValueRotateZ;
1177             argumentStart = 8;
1178         } else
1179             return nullptr;
1180
1181         pos += argumentStart;
1182         RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(transformType);
1183         if (!parseTransformAngleArgument(pos, end, transformValue.get()))
1184             return nullptr;
1185         return transformValue;
1186     }
1187
1188     return nullptr;
1189 }
1190
1191 template <typename CharType>
1192 static bool transformCanLikelyUseFastPath(const CharType* chars, unsigned length)
1193 {
1194     // Very fast scan that attempts to reject most transforms that couldn't
1195     // take the fast path. This avoids doing the malloc and string->double
1196     // conversions in parseSimpleTransformValue only to discard them when we
1197     // run into a transform component we don't understand.
1198     unsigned i = 0;
1199     while (i < length) {
1200         if (isCSSSpace(chars[i])) {
1201             ++i;
1202             continue;
1203         }
1204
1205         if (length - i < kShortestValidTransformStringLength)
1206             return false;
1207         
1208         switch (toASCIILower(chars[i])) {
1209         case 't':
1210             // translate, translateX, translateY, translateZ, translate3d.
1211             if (toASCIILower(chars[i + 8]) != 'e')
1212                 return false;
1213             i += 9;
1214             break;
1215         case 'm':
1216             // matrix3d.
1217             if (toASCIILower(chars[i + 7]) != 'd')
1218                 return false;
1219             i += 8;
1220             break;
1221         case 's':
1222             // scale3d.
1223             if (toASCIILower(chars[i + 6]) != 'd')
1224                 return false;
1225             i += 7;
1226             break;
1227         case 'r':
1228             // rotate.
1229             if (toASCIILower(chars[i + 5]) != 'e')
1230                 return false;
1231             i += 6;
1232             // rotateZ
1233             if (toASCIILower(chars[i]) == 'z')
1234                 ++i;
1235             break;
1236
1237         default:
1238             return false;
1239         }
1240         size_t argumentsEnd = WTF::find(chars, length, ')', i);
1241         if (argumentsEnd == notFound)
1242             return false;
1243         // Advance to the end of the arguments.
1244         i = argumentsEnd + 1;
1245     }
1246     return i == length;
1247 }
1248
1249 template <typename CharType>
1250 static RefPtr<CSSValueList> parseSimpleTransformList(const CharType* chars, unsigned length)
1251 {
1252     if (!transformCanLikelyUseFastPath(chars, length))
1253         return nullptr;
1254     const CharType*& pos = chars;
1255     const CharType* end = chars + length;
1256     RefPtr<CSSValueList> transformList;
1257     while (pos < end) {
1258         while (pos < end && isCSSSpace(*pos))
1259             ++pos;
1260         if (pos >= end)
1261             break;
1262         RefPtr<CSSFunctionValue> transformValue = parseSimpleTransformValue(pos, end);
1263         if (!transformValue)
1264             return nullptr;
1265         if (!transformList)
1266             transformList = CSSValueList::createSpaceSeparated();
1267         transformList->append(*transformValue);
1268     }
1269     return transformList;
1270 }
1271
1272 static RefPtr<CSSValue> parseSimpleTransform(CSSPropertyID propertyID, const String& string)
1273 {
1274     ASSERT(!string.isEmpty());
1275     if (propertyID != CSSPropertyTransform)
1276         return nullptr;
1277     if (string.is8Bit())
1278         return parseSimpleTransformList(string.characters8(), string.length());
1279     return parseSimpleTransformList(string.characters16(), string.length());
1280 }
1281
1282 static RefPtr<CSSValue> parseCaretColor(const String& string, CSSParserMode parserMode)
1283 {
1284     ASSERT(!string.isEmpty());
1285     CSSValueID valueID = cssValueKeywordID(string);
1286     if (valueID == CSSValueAuto)
1287         return CSSValuePool::singleton().createIdentifierValue(valueID);
1288     return CSSParserFastPaths::parseColor(string, parserMode);
1289 }
1290
1291 RefPtr<CSSValue> CSSParserFastPaths::maybeParseValue(CSSPropertyID propertyID, const String& string, CSSParserMode parserMode)
1292 {
1293     if (auto result = parseSimpleLengthValue(propertyID, string, parserMode))
1294         return result;
1295     if (propertyID == CSSPropertyCaretColor)
1296         return parseCaretColor(string, parserMode);
1297     if (isColorPropertyID(propertyID))
1298         return parseColor(string, parserMode);
1299     if (auto result = parseKeywordValue(propertyID, string, parserMode))
1300         return result;
1301     return parseSimpleTransform(propertyID, string);
1302 }
1303
1304 } // namespace WebCore