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