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