06dc72f6d47cd52e18b2319bfb342aa248f5ec12
[WebKit-https.git] / Source / WebCore / css / parser / CSSPropertyParser.cpp
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Copyright (C) 2016 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 "CSSPropertyParser.h"
32
33 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
34 #include "CSSAnimationTriggerScrollValue.h"
35 #endif
36 #include "CSSAspectRatioValue.h"
37 #include "CSSBasicShapes.h"
38 #include "CSSBorderImage.h"
39 #include "CSSBorderImageSliceValue.h"
40 #include "CSSContentDistributionValue.h"
41 #include "CSSCursorImageValue.h"
42 #include "CSSCustomIdentValue.h"
43 #include "CSSFontFaceSrcValue.h"
44 #include "CSSFontFeatureValue.h"
45 #if ENABLE(VARIATION_FONTS)
46 #include "CSSFontVariationValue.h"
47 #endif
48 #include "CSSFunctionValue.h"
49 #include "CSSGridAutoRepeatValue.h"
50 #include "CSSGridLineNamesValue.h"
51 #include "CSSGridTemplateAreasValue.h"
52 #include "CSSInheritedValue.h"
53 #include "CSSInitialValue.h"
54 #include "CSSLineBoxContainValue.h"
55 #include "CSSParserFastPaths.h"
56 #include "CSSParserIdioms.h"
57 #include "CSSPendingSubstitutionValue.h"
58 #include "CSSPrimitiveValueMappings.h"
59 #include "CSSPropertyParserHelpers.h"
60 #include "CSSReflectValue.h"
61 #include "CSSRevertValue.h"
62 #include "CSSShadowValue.h"
63 #include "CSSTimingFunctionValue.h"
64 #include "CSSUnicodeRangeValue.h"
65 #include "CSSUnsetValue.h"
66 #include "CSSVariableParser.h"
67 #include "CSSVariableReferenceValue.h"
68 #include "Counter.h"
69 #include "FontFace.h"
70 #include "HashTools.h"
71 // FIXME-NEWPARSER: Replace Pair and Rect with actual CSSValue subclasses (CSSValuePair and CSSQuadValue).
72 #include "Pair.h"
73 #include "Rect.h"
74 #include "RenderTheme.h"
75 #include "RuntimeEnabledFeatures.h"
76 #include "SVGPathByteStream.h"
77 #include "SVGPathUtilities.h"
78 #include "StylePropertyShorthand.h"
79 #include "StylePropertyShorthandFunctions.h"
80 #include "WebKitCSSTransformValue.h"
81 #include <bitset>
82 #include <memory>
83 #include <wtf/text/StringBuilder.h>
84
85 using namespace WTF;
86
87 namespace WebCore {
88
89 static bool hasPrefix(const char* string, unsigned length, const char* prefix)
90 {
91     for (unsigned i = 0; i < length; ++i) {
92         if (!prefix[i])
93             return true;
94         if (string[i] != prefix[i])
95             return false;
96     }
97     return false;
98 }
99
100 #if PLATFORM(IOS)
101 static void cssPropertyNameIOSAliasing(const char* propertyName, const char*& propertyNameAlias, unsigned& newLength)
102 {
103     if (!strcmp(propertyName, "-webkit-hyphenate-locale")) {
104         // Worked in iOS 4.2.
105         static const char webkitLocale[] = "-webkit-locale";
106         propertyNameAlias = webkitLocale;
107         newLength = strlen(webkitLocale);
108     }
109 }
110 #endif
111
112 template <typename CharacterType>
113 static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length)
114 {
115     char buffer[maxCSSPropertyNameLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
116     
117     for (unsigned i = 0; i != length; ++i) {
118         CharacterType c = propertyName[i];
119         if (!c || c >= 0x7F)
120             return CSSPropertyInvalid; // illegal character
121         buffer[i] = toASCIILower(c);
122     }
123     buffer[length] = '\0';
124     
125     const char* name = buffer;
126     if (buffer[0] == '-') {
127 #if ENABLE(LEGACY_CSS_VENDOR_PREFIXES)
128         // If the prefix is -apple- or -khtml-, change it to -webkit-.
129         // This makes the string one character longer.
130         if (RuntimeEnabledFeatures::sharedFeatures().legacyCSSVendorPrefixesEnabled()
131             && (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-"))) {
132             memmove(buffer + 7, buffer + 6, length + 1 - 6);
133             memcpy(buffer, "-webkit", 7);
134             ++length;
135         }
136 #endif
137 #if PLATFORM(IOS)
138         cssPropertyNameIOSAliasing(buffer, name, length);
139 #endif
140     }
141     
142     const Property* hashTableEntry = findProperty(name, length);
143     return hashTableEntry ? static_cast<CSSPropertyID>(hashTableEntry->id) : CSSPropertyInvalid;
144 }
145
146 static bool isAppleLegacyCssValueKeyword(const char* valueKeyword, unsigned length)
147 {
148     static const char applePrefix[] = "-apple-";
149     static const char appleSystemPrefix[] = "-apple-system";
150     static const char* appleWirelessPlaybackTargetActive = getValueName(CSSValueAppleWirelessPlaybackTargetActive);
151     
152     return hasPrefix(valueKeyword, length, applePrefix)
153     && !hasPrefix(valueKeyword, length, appleSystemPrefix)
154     && !WTF::equal(reinterpret_cast<const LChar*>(valueKeyword), reinterpret_cast<const LChar*>(appleWirelessPlaybackTargetActive), length);
155 }
156
157 template <typename CharacterType>
158 static CSSValueID cssValueKeywordID(const CharacterType* valueKeyword, unsigned length)
159 {
160     char buffer[maxCSSValueKeywordLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
161     
162     for (unsigned i = 0; i != length; ++i) {
163         CharacterType c = valueKeyword[i];
164         if (!c || c >= 0x7F)
165             return CSSValueInvalid; // illegal keyword.
166         buffer[i] = WTF::toASCIILower(c);
167     }
168     buffer[length] = '\0';
169     
170     if (buffer[0] == '-') {
171         // If the prefix is -apple- or -khtml-, change it to -webkit-.
172         // This makes the string one character longer.
173         // On iOS we don't want to change values starting with -apple-system to -webkit-system.
174         // FIXME: Remove this mangling without breaking the web.
175         if (isAppleLegacyCssValueKeyword(buffer, length) || hasPrefix(buffer, length, "-khtml-")) {
176             memmove(buffer + 7, buffer + 6, length + 1 - 6);
177             memcpy(buffer, "-webkit", 7);
178             ++length;
179         }
180     }
181     
182     const Value* hashTableEntry = findValue(buffer, length);
183     return hashTableEntry ? static_cast<CSSValueID>(hashTableEntry->id) : CSSValueInvalid;
184 }
185
186 CSSValueID cssValueKeywordID(StringView string)
187 {
188     unsigned length = string.length();
189     if (!length)
190         return CSSValueInvalid;
191     if (length > maxCSSValueKeywordLength)
192         return CSSValueInvalid;
193     
194     return string.is8Bit() ? cssValueKeywordID(string.characters8(), length) : cssValueKeywordID(string.characters16(), length);
195 }
196
197 CSSPropertyID cssPropertyID(StringView string)
198 {
199     unsigned length = string.length();
200     
201     if (!length)
202         return CSSPropertyInvalid;
203     if (length > maxCSSPropertyNameLength)
204         return CSSPropertyInvalid;
205     
206     return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length);
207 }
208     
209 using namespace CSSPropertyParserHelpers;
210
211 CSSPropertyParser::CSSPropertyParser(const CSSParserTokenRange& range,
212     const CSSParserContext& context, Vector<CSSProperty, 256>* parsedProperties)
213     : m_range(range)
214     , m_context(context)
215     , m_parsedProperties(parsedProperties)
216 {
217     m_range.consumeWhitespace();
218 }
219
220 void CSSPropertyParser::addProperty(CSSPropertyID property, CSSPropertyID currentShorthand, Ref<CSSValue>&& value, bool important, bool implicit)
221 {
222     int shorthandIndex = 0;
223     bool setFromShorthand = false;
224
225     if (currentShorthand) {
226         auto shorthands = matchingShorthandsForLonghand(property);
227         setFromShorthand = true;
228         if (shorthands.size() > 1)
229             shorthandIndex = indexOfShorthandForLonghand(currentShorthand, shorthands);
230     }
231
232     m_parsedProperties->append(CSSProperty(property, WTFMove(value), important, setFromShorthand, shorthandIndex, implicit));
233 }
234
235 void CSSPropertyParser::addExpandedPropertyForValue(CSSPropertyID property, Ref<CSSValue>&& value, bool important)
236 {
237     const StylePropertyShorthand& shorthand = shorthandForProperty(property);
238     unsigned shorthandLength = shorthand.length();
239     ASSERT(shorthandLength);
240     const CSSPropertyID* longhands = shorthand.properties();
241     for (unsigned i = 0; i < shorthandLength; ++i)
242         addProperty(longhands[i], property, value.copyRef(), important);
243 }
244     
245 bool CSSPropertyParser::parseValue(CSSPropertyID propertyID, bool important,
246     const CSSParserTokenRange& range, const CSSParserContext& context,
247     ParsedPropertyVector& parsedProperties, StyleRule::Type ruleType)
248 {
249     int parsedPropertiesSize = parsedProperties.size();
250
251     CSSPropertyParser parser(range, context, &parsedProperties);
252     bool parseSuccess;
253
254 #if ENABLE(CSS_DEVICE_ADAPTATION)
255     if (ruleType == StyleRule::Viewport)
256         parseSuccess = parser.parseViewportDescriptor(propertyID, important);
257     else
258 #endif
259     if (ruleType == StyleRule::FontFace)
260         parseSuccess = parser.parseFontFaceDescriptor(propertyID);
261     else
262         parseSuccess = parser.parseValueStart(propertyID, important);
263
264     if (!parseSuccess)
265         parsedProperties.shrink(parsedPropertiesSize);
266
267     return parseSuccess;
268 }
269
270 RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, const CSSParserTokenRange& range, const CSSParserContext& context)
271 {
272     CSSPropertyParser parser(range, context, nullptr);
273     RefPtr<CSSValue> value = parser.parseSingleValue(property);
274     if (!value || !parser.m_range.atEnd())
275         return nullptr;
276     return value;
277 }
278
279 static bool isLegacyBreakProperty(CSSPropertyID propertyID)
280 {
281     switch (propertyID) {
282     case CSSPropertyPageBreakAfter:
283     case CSSPropertyPageBreakBefore:
284     case CSSPropertyPageBreakInside:
285     case CSSPropertyWebkitColumnBreakAfter:
286     case CSSPropertyWebkitColumnBreakBefore:
287     case CSSPropertyWebkitColumnBreakInside:
288 #if ENABLE(CSS_REGIONS)
289     case CSSPropertyWebkitRegionBreakAfter:
290     case CSSPropertyWebkitRegionBreakBefore:
291     case CSSPropertyWebkitRegionBreakInside:
292 #endif
293         return true;
294     default:
295         break;
296     }
297     return false;
298 }
299
300 bool CSSPropertyParser::parseValueStart(CSSPropertyID propertyID, bool important)
301 {
302     if (consumeCSSWideKeyword(propertyID, important))
303         return true;
304
305     CSSParserTokenRange originalRange = m_range;
306     bool isShorthand = isShorthandCSSProperty(propertyID);
307
308     if (isShorthand) {
309         // Variable references will fail to parse here and will fall out to the variable ref parser below.
310         if (parseShorthand(propertyID, important))
311             return true;
312     } else if (isLegacyBreakProperty(propertyID)) {
313         // FIXME-NEWPARSER: Can turn this into a shorthand once old parser is gone, and then
314         // we don't need the special case.
315         if (consumeLegacyBreakProperty(propertyID, important))
316             return true;
317     } else {
318         RefPtr<CSSValue> parsedValue = parseSingleValue(propertyID);
319         if (parsedValue && m_range.atEnd()) {
320             addProperty(propertyID, CSSPropertyInvalid, *parsedValue, important);
321             return true;
322         }
323     }
324
325     if (CSSVariableParser::containsValidVariableReferences(originalRange)) {
326         RefPtr<CSSVariableReferenceValue> variable = CSSVariableReferenceValue::create(CSSVariableData::create(originalRange));
327
328         if (isShorthand) {
329             RefPtr<CSSPendingSubstitutionValue> pendingValue = CSSPendingSubstitutionValue::create(propertyID, variable.releaseNonNull());
330             addExpandedPropertyForValue(propertyID, pendingValue.releaseNonNull(), important);
331         } else
332             addProperty(propertyID, CSSPropertyInvalid, variable.releaseNonNull(), important);
333         return true;
334     }
335
336     return false;
337 }
338  
339 bool CSSPropertyParser::consumeCSSWideKeyword(CSSPropertyID propertyID, bool important)
340 {
341     CSSParserTokenRange rangeCopy = m_range;
342     CSSValueID valueID = rangeCopy.consumeIncludingWhitespace().id();
343     if (!rangeCopy.atEnd())
344         return false;
345
346     RefPtr<CSSValue> value;
347     if (valueID == CSSValueInherit)
348         value = CSSValuePool::singleton().createInheritedValue();
349     else if (valueID == CSSValueInitial)
350         value = CSSValuePool::singleton().createExplicitInitialValue();
351     else if (valueID == CSSValueUnset)
352         value = CSSValuePool::singleton().createUnsetValue();
353     else if (valueID == CSSValueRevert)
354         value = CSSValuePool::singleton().createRevertValue();
355     else
356         return false;
357     
358     const StylePropertyShorthand& shorthand = shorthandForProperty(propertyID);
359     if (!shorthand.length()) {
360         if (CSSProperty::isDescriptorOnly(propertyID))
361             return false;
362         addProperty(propertyID, CSSPropertyInvalid, value.releaseNonNull(), important);
363     } else
364         addExpandedPropertyForValue(propertyID, value.releaseNonNull(), important);
365     m_range = rangeCopy;
366     return true;
367 }
368
369 bool CSSPropertyParser::consumeTransformOrigin(bool important)
370 {
371     RefPtr<CSSPrimitiveValue> resultX;
372     RefPtr<CSSPrimitiveValue> resultY;
373     if (consumeOneOrTwoValuedPosition(m_range, m_context.mode, UnitlessQuirk::Forbid, resultX, resultY)) {
374         m_range.consumeWhitespace();
375         bool atEnd = m_range.atEnd();
376         RefPtr<CSSPrimitiveValue> resultZ = consumeLength(m_range, m_context.mode, ValueRangeAll);
377         if (!resultZ && !atEnd)
378             return false;
379         if (!resultZ)
380             resultZ = CSSValuePool::singleton().createValue(0, CSSPrimitiveValue::UnitTypes::CSS_PX);
381         addProperty(CSSPropertyTransformOriginX, CSSPropertyTransformOrigin, resultX.releaseNonNull(), important);
382         addProperty(CSSPropertyTransformOriginY, CSSPropertyTransformOrigin, resultY.releaseNonNull(), important);
383         addProperty(CSSPropertyTransformOriginZ, CSSPropertyTransformOrigin, resultZ.releaseNonNull(), important);
384         
385         return true;
386     }
387     return false;
388 }
389
390 bool CSSPropertyParser::consumePerspectiveOrigin(bool important)
391 {
392     RefPtr<CSSPrimitiveValue> resultX;
393     RefPtr<CSSPrimitiveValue> resultY;
394     if (consumePosition(m_range, m_context.mode, UnitlessQuirk::Forbid, resultX, resultY)) {
395         addProperty(CSSPropertyPerspectiveOriginX, CSSPropertyPerspectiveOrigin, resultX.releaseNonNull(), important);
396         addProperty(CSSPropertyPerspectiveOriginY, CSSPropertyPerspectiveOrigin, resultY.releaseNonNull(), important);
397         return true;
398     }
399     return false;
400 }
401
402 // Methods for consuming non-shorthand properties starts here.
403 static RefPtr<CSSValue> consumeWillChange(CSSParserTokenRange& range)
404 {
405     if (range.peek().id() == CSSValueAuto)
406         return consumeIdent(range);
407
408     RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
409     // Every comma-separated list of identifiers is a valid will-change value,
410     // unless the list includes an explicitly disallowed identifier.
411     while (true) {
412         if (range.peek().type() != IdentToken)
413             return nullptr;
414         CSSPropertyID propertyID = cssPropertyID(range.peek().value());
415         if (propertyID != CSSPropertyInvalid) {
416             // Now "all" is used by both CSSValue and CSSPropertyValue.
417             // Need to return nullptr when currentValue is CSSPropertyAll.
418             if (propertyID == CSSPropertyWillChange || propertyID == CSSPropertyAll)
419                 return nullptr;
420             // FIXME-NEWPARSER: Use CSSCustomIdentValue someday.
421             values->append(CSSValuePool::singleton().createIdentifierValue(propertyID));
422             range.consumeIncludingWhitespace();
423         } else {
424             switch (range.peek().id()) {
425             case CSSValueNone:
426             case CSSValueAll:
427             case CSSValueAuto:
428             case CSSValueDefault:
429             case CSSValueInitial:
430             case CSSValueInherit:
431                 return nullptr;
432             case CSSValueContents:
433             case CSSValueScrollPosition:
434                 values->append(consumeIdent(range).releaseNonNull());
435                 break;
436             default:
437                 // Append properties we don't recognize, but that are legal, as strings.
438                 values->append(consumeCustomIdent(range).releaseNonNull());
439                 break;
440             }
441         }
442
443         if (range.atEnd())
444             break;
445         if (!consumeCommaIncludingWhitespace(range))
446             return nullptr;
447     }
448
449     return values;
450 }
451
452 static RefPtr<CSSFontFeatureValue> consumeFontFeatureTag(CSSParserTokenRange& range)
453 {
454     // Feature tag name consists of 4-letter characters.
455     static const unsigned tagNameLength = 4;
456
457     const CSSParserToken& token = range.consumeIncludingWhitespace();
458     // Feature tag name comes first
459     if (token.type() != StringToken)
460         return nullptr;
461     if (token.value().length() != tagNameLength)
462         return nullptr;
463     
464     FontTag tag;
465     for (unsigned i = 0; i < tag.size(); ++i) {
466         // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
467         UChar character = token.value()[i];
468         if (character < 0x20 || character > 0x7E)
469             return nullptr;
470         tag[i] = toASCIILower(character);
471     }
472
473     int tagValue = 1;
474     // Feature tag values could follow: <integer> | on | off
475     if (range.peek().type() == NumberToken && range.peek().numericValueType() == IntegerValueType && range.peek().numericValue() >= 0) {
476         tagValue = clampTo<int>(range.consumeIncludingWhitespace().numericValue());
477         if (tagValue < 0)
478             return nullptr;
479     } else if (range.peek().id() == CSSValueOn || range.peek().id() == CSSValueOff) {
480         tagValue = range.consumeIncludingWhitespace().id() == CSSValueOn;
481     }
482     return CSSFontFeatureValue::create(WTFMove(tag), tagValue);
483 }
484
485 static RefPtr<CSSValue> consumeFontFeatureSettings(CSSParserTokenRange& range)
486 {
487     if (range.peek().id() == CSSValueNormal)
488         return consumeIdent(range);
489     RefPtr<CSSValueList> settings = CSSValueList::createCommaSeparated();
490     do {
491         RefPtr<CSSFontFeatureValue> fontFeatureValue = consumeFontFeatureTag(range);
492         if (!fontFeatureValue)
493             return nullptr;
494         settings->append(fontFeatureValue.releaseNonNull());
495     } while (consumeCommaIncludingWhitespace(range));
496     return settings;
497 }
498
499 #if ENABLE(VARIATION_FONTS)
500 static RefPtr<CSSValue> consumeFontVariationTag(CSSParserTokenRange& range)
501 {
502     if (range.peek().type() != StringToken)
503         return nullptr;
504     
505     auto string = range.consumeIncludingWhitespace().value().toString();
506     
507     FontTag tag;
508     if (string.length() != tag.size())
509         return nullptr;
510     for (unsigned i = 0; i < tag.size(); ++i) {
511         // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
512         UChar character = string[i];
513         if (character < 0x20 || character > 0x7E)
514             return nullptr;
515         tag[i] = character;
516     }
517     
518     if (range.atEnd() || range.peek().type() != NumberToken)
519         return nullptr;
520
521     float tagValue = range.consumeIncludingWhitespace().numericValue();
522     
523     return CSSFontVariationValue::create(tag, tagValue);
524 }
525     
526 static RefPtr<CSSValue> consumeFontVariationSettings(CSSParserTokenRange& range)
527 {
528     if (range.peek().id() == CSSValueNormal)
529         return consumeIdent(range);
530     
531     auto settings = CSSValueList::createCommaSeparated();
532     do {
533         RefPtr<CSSValue> variationValue = consumeFontVariationTag(range);
534         if (!variationValue)
535             return nullptr;
536         settings->append(variationValue.releaseNonNull());
537     } while (consumeCommaIncludingWhitespace(range));
538     
539     if (!settings->length())
540         return nullptr;
541
542     return WTFMove(settings);
543 }
544 #endif // ENABLE(VARIATION_FONTS)
545
546 static RefPtr<CSSValue> consumePage(CSSParserTokenRange& range)
547 {
548     if (range.peek().id() == CSSValueAuto)
549         return consumeIdent(range);
550     return consumeCustomIdent(range);
551 }
552
553 static RefPtr<CSSValue> consumeQuotes(CSSParserTokenRange& range)
554 {
555     if (range.peek().id() == CSSValueNone)
556         return consumeIdent(range);
557     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
558     while (!range.atEnd()) {
559         RefPtr<CSSPrimitiveValue> parsedValue = consumeString(range);
560         if (!parsedValue)
561             return nullptr;
562         values->append(parsedValue.releaseNonNull());
563     }
564     if (values->length() && values->length() % 2 == 0)
565         return values;
566     return nullptr;
567 }
568
569 class FontVariantLigaturesParser {
570 public:
571     FontVariantLigaturesParser()
572         : m_sawCommonLigaturesValue(false)
573         , m_sawDiscretionaryLigaturesValue(false)
574         , m_sawHistoricalLigaturesValue(false)
575         , m_sawContextualLigaturesValue(false)
576         , m_result(CSSValueList::createSpaceSeparated())
577     {
578     }
579
580     enum class ParseResult {
581         ConsumedValue,
582         DisallowedValue,
583         UnknownValue
584     };
585
586     ParseResult consumeLigature(CSSParserTokenRange& range)
587     {
588         CSSValueID valueID = range.peek().id();
589         switch (valueID) {
590         case CSSValueNoCommonLigatures:
591         case CSSValueCommonLigatures:
592             if (m_sawCommonLigaturesValue)
593                 return ParseResult::DisallowedValue;
594             m_sawCommonLigaturesValue = true;
595             break;
596         case CSSValueNoDiscretionaryLigatures:
597         case CSSValueDiscretionaryLigatures:
598             if (m_sawDiscretionaryLigaturesValue)
599                 return ParseResult::DisallowedValue;
600             m_sawDiscretionaryLigaturesValue = true;
601             break;
602         case CSSValueNoHistoricalLigatures:
603         case CSSValueHistoricalLigatures:
604             if (m_sawHistoricalLigaturesValue)
605                 return ParseResult::DisallowedValue;
606             m_sawHistoricalLigaturesValue = true;
607             break;
608         case CSSValueNoContextual:
609         case CSSValueContextual:
610             if (m_sawContextualLigaturesValue)
611                 return ParseResult::DisallowedValue;
612             m_sawContextualLigaturesValue = true;
613             break;
614         default:
615             return ParseResult::UnknownValue;
616         }
617         m_result->append(consumeIdent(range).releaseNonNull());
618         return ParseResult::ConsumedValue;
619     }
620
621     RefPtr<CSSValue> finalizeValue()
622     {
623         if (!m_result->length())
624             return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
625         return m_result.release();
626     }
627
628 private:
629     bool m_sawCommonLigaturesValue;
630     bool m_sawDiscretionaryLigaturesValue;
631     bool m_sawHistoricalLigaturesValue;
632     bool m_sawContextualLigaturesValue;
633     RefPtr<CSSValueList> m_result;
634 };
635
636 static RefPtr<CSSValue> consumeFontVariantLigatures(CSSParserTokenRange& range)
637 {
638     if (range.peek().id() == CSSValueNormal || range.peek().id() == CSSValueNone)
639         return consumeIdent(range);
640
641     FontVariantLigaturesParser ligaturesParser;
642     do {
643         if (ligaturesParser.consumeLigature(range) !=
644             FontVariantLigaturesParser::ParseResult::ConsumedValue)
645             return nullptr;
646     } while (!range.atEnd());
647
648     return ligaturesParser.finalizeValue();
649 }
650
651 static RefPtr<CSSValue> consumeFontVariantEastAsian(CSSParserTokenRange& range)
652 {
653     if (range.peek().id() == CSSValueNormal)
654         return consumeIdent(range);
655     
656     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
657     FontVariantEastAsianVariant variant = FontVariantEastAsianVariant::Normal;
658     FontVariantEastAsianWidth width = FontVariantEastAsianWidth::Normal;
659     FontVariantEastAsianRuby ruby = FontVariantEastAsianRuby::Normal;
660     
661     while (!range.atEnd()) {
662         if (range.peek().type() != IdentToken)
663             return nullptr;
664         
665         auto id = range.peek().id();
666         
667         switch (id) {
668         case CSSValueJis78:
669             variant = FontVariantEastAsianVariant::Jis78;
670             break;
671         case CSSValueJis83:
672             variant = FontVariantEastAsianVariant::Jis83;
673             break;
674         case CSSValueJis90:
675             variant = FontVariantEastAsianVariant::Jis90;
676             break;
677         case CSSValueJis04:
678             variant = FontVariantEastAsianVariant::Jis04;
679             break;
680         case CSSValueSimplified:
681             variant = FontVariantEastAsianVariant::Simplified;
682             break;
683         case CSSValueTraditional:
684             variant = FontVariantEastAsianVariant::Traditional;
685             break;
686         case CSSValueFullWidth:
687             width = FontVariantEastAsianWidth::Full;
688             break;
689         case CSSValueProportionalWidth:
690             width = FontVariantEastAsianWidth::Proportional;
691             break;
692         case CSSValueRuby:
693             ruby = FontVariantEastAsianRuby::Yes;
694             break;
695         default:
696             return nullptr;
697         }
698         
699         range.consumeIncludingWhitespace();
700     }
701         
702     switch (variant) {
703     case FontVariantEastAsianVariant::Normal:
704         break;
705     case FontVariantEastAsianVariant::Jis78:
706         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis78));
707         break;
708     case FontVariantEastAsianVariant::Jis83:
709         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis83));
710         break;
711     case FontVariantEastAsianVariant::Jis90:
712         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis90));
713         break;
714     case FontVariantEastAsianVariant::Jis04:
715         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis04));
716         break;
717     case FontVariantEastAsianVariant::Simplified:
718         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueSimplified));
719         break;
720     case FontVariantEastAsianVariant::Traditional:
721         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueTraditional));
722         break;
723     }
724         
725     switch (width) {
726     case FontVariantEastAsianWidth::Normal:
727         break;
728     case FontVariantEastAsianWidth::Full:
729         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueFullWidth));
730         break;
731     case FontVariantEastAsianWidth::Proportional:
732         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueProportionalWidth));
733         break;
734     }
735         
736     switch (ruby) {
737     case FontVariantEastAsianRuby::Normal:
738         break;
739     case FontVariantEastAsianRuby::Yes:
740         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueRuby));
741     }
742
743     if (!values->length())
744         return nullptr;
745
746     return values;
747 }
748     
749 static RefPtr<CSSPrimitiveValue> consumeFontVariantCaps(CSSParserTokenRange& range)
750 {
751     return consumeIdent<CSSValueNormal, CSSValueSmallCaps, CSSValueAllSmallCaps,
752         CSSValuePetiteCaps, CSSValueAllPetiteCaps,
753         CSSValueUnicase, CSSValueTitlingCaps>(range);
754 }
755
756 static RefPtr<CSSPrimitiveValue> consumeFontVariantAlternates(CSSParserTokenRange& range)
757 {
758     return consumeIdent<CSSValueNormal, CSSValueHistoricalForms>(range);
759 }
760
761 static RefPtr<CSSPrimitiveValue> consumeFontVariantPosition(CSSParserTokenRange& range)
762 {
763     return consumeIdent<CSSValueNormal, CSSValueSub, CSSValueSuper>(range);
764 }
765
766 class FontVariantNumericParser {
767 public:
768     FontVariantNumericParser()
769         : m_sawNumericFigureValue(false)
770         , m_sawNumericSpacingValue(false)
771         , m_sawNumericFractionValue(false)
772         , m_sawOrdinalValue(false)
773         , m_sawSlashedZeroValue(false)
774         , m_result(CSSValueList::createSpaceSeparated())
775     {
776     }
777
778     enum class ParseResult {
779         ConsumedValue,
780         DisallowedValue,
781         UnknownValue
782     };
783
784     ParseResult consumeNumeric(CSSParserTokenRange& range)
785     {
786         CSSValueID valueID = range.peek().id();
787         switch (valueID) {
788         case CSSValueLiningNums:
789         case CSSValueOldstyleNums:
790             if (m_sawNumericFigureValue)
791                 return ParseResult::DisallowedValue;
792             m_sawNumericFigureValue = true;
793             break;
794         case CSSValueProportionalNums:
795         case CSSValueTabularNums:
796             if (m_sawNumericSpacingValue)
797                 return ParseResult::DisallowedValue;
798             m_sawNumericSpacingValue = true;
799             break;
800         case CSSValueDiagonalFractions:
801         case CSSValueStackedFractions:
802             if (m_sawNumericFractionValue)
803                 return ParseResult::DisallowedValue;
804             m_sawNumericFractionValue = true;
805             break;
806         case CSSValueOrdinal:
807             if (m_sawOrdinalValue)
808                 return ParseResult::DisallowedValue;
809             m_sawOrdinalValue = true;
810             break;
811         case CSSValueSlashedZero:
812             if (m_sawSlashedZeroValue)
813                 return ParseResult::DisallowedValue;
814             m_sawSlashedZeroValue = true;
815             break;
816         default:
817             return ParseResult::UnknownValue;
818         }
819         m_result->append(consumeIdent(range).releaseNonNull());
820         return ParseResult::ConsumedValue;
821     }
822
823     RefPtr<CSSValue> finalizeValue()
824     {
825         if (!m_result->length())
826             return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
827         return m_result.release();
828     }
829
830
831 private:
832     bool m_sawNumericFigureValue;
833     bool m_sawNumericSpacingValue;
834     bool m_sawNumericFractionValue;
835     bool m_sawOrdinalValue;
836     bool m_sawSlashedZeroValue;
837     RefPtr<CSSValueList> m_result;
838 };
839
840 static RefPtr<CSSValue> consumeFontVariantNumeric(CSSParserTokenRange& range)
841 {
842     if (range.peek().id() == CSSValueNormal)
843         return consumeIdent(range);
844
845     FontVariantNumericParser numericParser;
846     do {
847         if (numericParser.consumeNumeric(range) !=
848             FontVariantNumericParser::ParseResult::ConsumedValue)
849             return nullptr;
850     } while (!range.atEnd());
851
852     return numericParser.finalizeValue();
853 }
854
855 static RefPtr<CSSPrimitiveValue> consumeFontVariantCSS21(CSSParserTokenRange& range)
856 {
857     return consumeIdent<CSSValueNormal, CSSValueSmallCaps>(range);
858 }
859
860 static RefPtr<CSSPrimitiveValue> consumeFontWeight(CSSParserTokenRange& range)
861 {
862     const CSSParserToken& token = range.peek();
863     if (token.id() >= CSSValueNormal && token.id() <= CSSValueLighter)
864         return consumeIdent(range);
865     if (token.type() != NumberToken || token.numericValueType() != IntegerValueType)
866         return nullptr;
867     int weight = static_cast<int>(token.numericValue());
868     if ((weight % 100) || weight < 100 || weight > 900)
869         return nullptr;
870     range.consumeIncludingWhitespace();
871     return CSSValuePool::singleton().createIdentifierValue(static_cast<CSSValueID>(CSSValue100 + weight / 100 - 1));
872 }
873
874 static String concatenateFamilyName(CSSParserTokenRange& range)
875 {
876     StringBuilder builder;
877     bool addedSpace = false;
878     const CSSParserToken& firstToken = range.peek();
879     while (range.peek().type() == IdentToken) {
880         if (!builder.isEmpty()) {
881             builder.append(' ');
882             addedSpace = true;
883         }
884         builder.append(range.consumeIncludingWhitespace().value());
885     }
886     if (!addedSpace && isCSSWideKeyword(firstToken.id()))
887         return String();
888     return builder.toString();
889 }
890
891 static RefPtr<CSSValue> consumeFamilyName(CSSParserTokenRange& range)
892 {
893     if (range.peek().type() == StringToken)
894         return CSSValuePool::singleton().createFontFamilyValue(range.consumeIncludingWhitespace().value().toString());
895     if (range.peek().type() != IdentToken)
896         return nullptr;
897     String familyName = concatenateFamilyName(range);
898     if (familyName.isNull())
899         return nullptr;
900     return CSSValuePool::singleton().createFontFamilyValue(familyName);
901 }
902
903 static RefPtr<CSSValue> consumeGenericFamily(CSSParserTokenRange& range)
904 {
905     return consumeIdentRange(range, CSSValueSerif, CSSValueWebkitBody);
906 }
907
908 static RefPtr<CSSValueList> consumeFontFamily(CSSParserTokenRange& range)
909 {
910     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
911     do {
912         RefPtr<CSSValue> parsedValue = consumeGenericFamily(range);
913         if (parsedValue) {
914             list->append(parsedValue.releaseNonNull());
915         } else {
916             parsedValue = consumeFamilyName(range);
917             if (parsedValue) {
918                 list->append(parsedValue.releaseNonNull());
919             } else {
920                 return nullptr;
921             }
922         }
923     } while (consumeCommaIncludingWhitespace(range));
924     return list;
925 }
926
927 static RefPtr<CSSValueList> consumeFontFamilyDescriptor(CSSParserTokenRange& range)
928 {
929     // FIXME-NEWPARSER: For compatibility with the old parser, we have to make
930     // a list here, even though the list always contains only a single family name.
931     // Once the old parser is gone, we can delete this function, make the caller
932     // use consumeFamilyName instead, and then patch the @font-face code to
933     // not expect a list with a single name in it.
934     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
935     RefPtr<CSSValue> parsedValue = consumeFamilyName(range);
936     if (parsedValue)
937         list->append(parsedValue.releaseNonNull());
938     
939     if (!range.atEnd() || !list->length())
940         return nullptr;
941
942     return list;
943 }
944
945 static RefPtr<CSSValue> consumeFontSynthesis(CSSParserTokenRange& range)
946 {
947     // none | [ weight || style ]
948     CSSValueID id = range.peek().id();
949     if (id == CSSValueNone)
950         return consumeIdent(range);
951     
952     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
953     while (true) {
954         auto ident = consumeIdent<CSSValueWeight, CSSValueStyle>(range);
955         if (!ident)
956             break;
957         if (list->hasValue(ident.get()))
958             return nullptr;
959         list->append(ident.releaseNonNull());
960     }
961     
962     if (!list->length())
963         return nullptr;
964     return list;
965 }
966
967 static RefPtr<CSSValue> consumeLetterSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
968 {
969     if (range.peek().id() == CSSValueNormal)
970         return consumeIdent(range);
971     
972     return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
973 }
974
975 static RefPtr<CSSValue> consumeWordSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
976 {
977     if (range.peek().id() == CSSValueNormal)
978         return consumeIdent(range);
979     
980     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
981 }
982     
983 static RefPtr<CSSValue> consumeTabSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
984 {
985     RefPtr<CSSPrimitiveValue> parsedValue = consumeInteger(range, 0);
986     if (parsedValue)
987         return parsedValue;
988     return consumeLength(range, cssParserMode, ValueRangeNonNegative);
989 }
990
991 #if ENABLE(TEXT_AUTOSIZING)
992 static RefPtr<CSSValue> consumeTextSizeAdjust(CSSParserTokenRange& range, CSSParserMode /* cssParserMode */)
993 {
994     if (range.peek().id() == CSSValueAuto)
995         return consumeIdent(range);
996     if (range.peek().id() == CSSValueNone)
997         return consumeIdent(range);
998     return consumePercent(range, ValueRangeNonNegative);
999 }
1000 #endif
1001
1002 static RefPtr<CSSValue> consumeFontSize(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1003 {
1004     if (range.peek().id() >= CSSValueXxSmall && range.peek().id() <= CSSValueLarger)
1005         return consumeIdent(range);
1006     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, unitless);
1007 }
1008
1009 static RefPtr<CSSPrimitiveValue> consumeLineHeight(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1010 {
1011     if (range.peek().id() == CSSValueNormal)
1012         return consumeIdent(range);
1013
1014     RefPtr<CSSPrimitiveValue> lineHeight = consumeNumber(range, ValueRangeNonNegative);
1015     if (lineHeight)
1016         return lineHeight;
1017     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
1018 }
1019
1020 template<typename... Args>
1021 static Ref<CSSPrimitiveValue> createPrimitiveValuePair(Args&&... args)
1022 {
1023     return CSSValuePool::singleton().createValue(Pair::create(std::forward<Args>(args)...));
1024 }
1025
1026 static RefPtr<CSSValue> consumeCounter(CSSParserTokenRange& range, int defaultValue)
1027 {
1028     if (range.peek().id() == CSSValueNone)
1029         return consumeIdent(range);
1030
1031     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1032     do {
1033         RefPtr<CSSPrimitiveValue> counterName = consumeCustomIdent(range);
1034         if (!counterName)
1035             return nullptr;
1036         int i = defaultValue;
1037         if (RefPtr<CSSPrimitiveValue> counterValue = consumeInteger(range))
1038             i = counterValue->intValue();
1039         list->append(createPrimitiveValuePair(counterName.releaseNonNull(), CSSPrimitiveValue::create(i, CSSPrimitiveValue::UnitTypes::CSS_NUMBER), Pair::IdenticalValueEncoding::Coalesce));
1040     } while (!range.atEnd());
1041     return list;
1042 }
1043
1044 static RefPtr<CSSValue> consumePageSize(CSSParserTokenRange& range)
1045 {
1046     return consumeIdent<CSSValueA3, CSSValueA4, CSSValueA5, CSSValueB4, CSSValueB5, CSSValueLedger, CSSValueLegal, CSSValueLetter>(range);
1047 }
1048
1049 static RefPtr<CSSValueList> consumeSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1050 {
1051     RefPtr<CSSValueList> result = CSSValueList::createSpaceSeparated();
1052
1053     if (range.peek().id() == CSSValueAuto) {
1054         result->append(consumeIdent(range).releaseNonNull());
1055         return result;
1056     }
1057
1058     if (RefPtr<CSSValue> width = consumeLength(range, cssParserMode, ValueRangeNonNegative)) {
1059         RefPtr<CSSValue> height = consumeLength(range, cssParserMode, ValueRangeNonNegative);
1060         result->append(width.releaseNonNull());
1061         if (height)
1062             result->append(height.releaseNonNull());
1063         return result;
1064     }
1065
1066     RefPtr<CSSValue> pageSize = consumePageSize(range);
1067     RefPtr<CSSValue> orientation = consumeIdent<CSSValuePortrait, CSSValueLandscape>(range);
1068     if (!pageSize)
1069         pageSize = consumePageSize(range);
1070
1071     if (!orientation && !pageSize)
1072         return nullptr;
1073     if (pageSize)
1074         result->append(pageSize.releaseNonNull());
1075     if (orientation)
1076         result->append(orientation.releaseNonNull());
1077     return result;
1078 }
1079
1080 static RefPtr<CSSValue> consumeTextIndent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1081 {
1082     // [ <length> | <percentage> ] && hanging? && each-line?
1083     // Keywords only allowed when css3Text is enabled.
1084     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1085
1086     bool hasLengthOrPercentage = false;
1087 //    bool hasEachLine = false;
1088     bool hasHanging = false;
1089
1090     do {
1091         if (!hasLengthOrPercentage) {
1092             if (RefPtr<CSSValue> textIndent = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow)) {
1093                 list->append(*textIndent);
1094                 hasLengthOrPercentage = true;
1095                 continue;
1096             }
1097         }
1098
1099         CSSValueID id = range.peek().id();
1100  /* FIXME-NEWPARSER: We don't support this yet.
1101         if (!hasEachLine && id == CSSValueEachLine) {
1102             list->append(*consumeIdent(range));
1103             hasEachLine = true;
1104             continue;
1105         }
1106 */
1107         
1108         if (!hasHanging && id == CSSValueHanging) {
1109             list->append(consumeIdent(range).releaseNonNull());
1110             hasHanging = true;
1111             continue;
1112         }
1113         
1114         return nullptr;
1115     } while (!range.atEnd());
1116
1117     if (!hasLengthOrPercentage)
1118         return nullptr;
1119
1120     return list;
1121 }
1122
1123 // FIXME-NEWPARSER: Drop the prefix on min-content, max-content and fit-content.
1124 static bool validWidthOrHeightKeyword(CSSValueID id, const CSSParserContext& /*context*/)
1125 {
1126     if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic || id == CSSValueWebkitMinContent || id == CSSValueWebkitMaxContent || id == CSSValueWebkitFillAvailable || id == CSSValueWebkitFitContent) {
1127         return true;
1128     }
1129     return false;
1130 }
1131
1132 static RefPtr<CSSValue> consumeMaxWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1133 {
1134     if (range.peek().id() == CSSValueNone || validWidthOrHeightKeyword(range.peek().id(), context))
1135         return consumeIdent(range);
1136     return consumeLengthOrPercent(range, context.mode, ValueRangeNonNegative, unitless);
1137 }
1138
1139 static RefPtr<CSSValue> consumeWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1140 {
1141     if (range.peek().id() == CSSValueAuto || validWidthOrHeightKeyword(range.peek().id(), context))
1142         return consumeIdent(range);
1143     return consumeLengthOrPercent(range, context.mode, ValueRangeNonNegative, unitless);
1144 }
1145
1146 static RefPtr<CSSValue> consumeMarginOrOffset(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1147 {
1148     if (range.peek().id() == CSSValueAuto)
1149         return consumeIdent(range);
1150     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, unitless);
1151 }
1152
1153 static RefPtr<CSSPrimitiveValue> consumeClipComponent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1154 {
1155     if (range.peek().id() == CSSValueAuto)
1156         return consumeIdent(range);
1157     return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1158 }
1159
1160 static RefPtr<CSSValue> consumeClip(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1161 {
1162     if (range.peek().id() == CSSValueAuto)
1163         return consumeIdent(range);
1164
1165     if (range.peek().functionId() != CSSValueRect)
1166         return nullptr;
1167
1168     CSSParserTokenRange args = consumeFunction(range);
1169     // rect(t, r, b, l) || rect(t r b l)
1170     RefPtr<CSSPrimitiveValue> top = consumeClipComponent(args, cssParserMode);
1171     if (!top)
1172         return nullptr;
1173     bool needsComma = consumeCommaIncludingWhitespace(args);
1174     RefPtr<CSSPrimitiveValue> right = consumeClipComponent(args, cssParserMode);
1175     if (!right || (needsComma && !consumeCommaIncludingWhitespace(args)))
1176         return nullptr;
1177     RefPtr<CSSPrimitiveValue> bottom = consumeClipComponent(args, cssParserMode);
1178     if (!bottom || (needsComma && !consumeCommaIncludingWhitespace(args)))
1179         return nullptr;
1180     RefPtr<CSSPrimitiveValue> left = consumeClipComponent(args, cssParserMode);
1181     if (!left || !args.atEnd())
1182         return nullptr;
1183     
1184     auto rect = Rect::create();
1185     rect->setLeft(left.releaseNonNull());
1186     rect->setTop(top.releaseNonNull());
1187     rect->setRight(right.releaseNonNull());
1188     rect->setBottom(bottom.releaseNonNull());
1189     return CSSValuePool::singleton().createValue(WTFMove(rect));
1190 }
1191
1192 #if ENABLE(TOUCH_EVENTS)
1193 static RefPtr<CSSValue> consumeTouchAction(CSSParserTokenRange& range)
1194 {
1195     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1196     CSSValueID id = range.peek().id();
1197     if (id == CSSValueAuto || id == CSSValueNone || id == CSSValueManipulation) {
1198         list->append(consumeIdent(range).releaseNonNull());
1199         return list;
1200     }
1201     // FIXME-NEWPARSER: Support pan.
1202     return nullptr;
1203 }
1204 #endif
1205
1206 static RefPtr<CSSPrimitiveValue> consumeLineClamp(CSSParserTokenRange& range)
1207 {
1208     if (range.peek().type() != PercentageToken && range.peek().type() != NumberToken)
1209         return nullptr;
1210     RefPtr<CSSPrimitiveValue> clampValue = consumePercent(range, ValueRangeNonNegative);
1211     if (clampValue)
1212         return clampValue;
1213     // When specifying number of lines, don't allow 0 as a valid value.
1214     return consumePositiveInteger(range);
1215 }
1216
1217 static RefPtr<CSSValue> consumeLocale(CSSParserTokenRange& range)
1218 {
1219     if (range.peek().id() == CSSValueAuto)
1220         return consumeIdent(range);
1221     return consumeString(range);
1222 }
1223
1224 static RefPtr<CSSValue> consumeHyphenateLimit(CSSParserTokenRange& range, CSSValueID valueID)
1225 {
1226     if (range.peek().id() == valueID)
1227         return consumeIdent(range);
1228     return consumeNumber(range, ValueRangeNonNegative);
1229 }
1230
1231 static RefPtr<CSSValue> consumeColumnWidth(CSSParserTokenRange& range)
1232 {
1233     if (range.peek().id() == CSSValueAuto)
1234         return consumeIdent(range);
1235     // Always parse lengths in strict mode here, since it would be ambiguous otherwise when used in
1236     // the 'columns' shorthand property.
1237     RefPtr<CSSPrimitiveValue> columnWidth = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
1238     if (!columnWidth || (!columnWidth->isCalculated() && columnWidth->doubleValue() == 0))
1239         return nullptr;
1240     return columnWidth;
1241 }
1242
1243 static RefPtr<CSSValue> consumeColumnCount(CSSParserTokenRange& range)
1244 {
1245     if (range.peek().id() == CSSValueAuto)
1246         return consumeIdent(range);
1247     return consumePositiveInteger(range);
1248 }
1249
1250 static RefPtr<CSSValue> consumeColumnGap(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1251 {
1252     if (range.peek().id() == CSSValueNormal)
1253         return consumeIdent(range);
1254     return consumeLength(range, cssParserMode, ValueRangeNonNegative);
1255 }
1256
1257 static RefPtr<CSSValue> consumeColumnSpan(CSSParserTokenRange& range)
1258 {
1259     return consumeIdent<CSSValueAll, CSSValueNone>(range);
1260 }
1261
1262 static RefPtr<CSSValue> consumeZoom(CSSParserTokenRange& range, const CSSParserContext& /*context*/)
1263 {
1264     const CSSParserToken& token = range.peek();
1265     RefPtr<CSSPrimitiveValue> zoom;
1266     if (token.type() == IdentToken)
1267         zoom = consumeIdent<CSSValueNormal, CSSValueReset, CSSValueDocument>(range);
1268     else {
1269         zoom = consumePercent(range, ValueRangeNonNegative);
1270         if (!zoom)
1271             zoom = consumeNumber(range, ValueRangeNonNegative);
1272     }
1273     return zoom;
1274 }
1275
1276 static RefPtr<CSSValue> consumeAnimationIterationCount(CSSParserTokenRange& range)
1277 {
1278     if (range.peek().id() == CSSValueInfinite)
1279         return consumeIdent(range);
1280     return consumeNumber(range, ValueRangeNonNegative);
1281 }
1282
1283 static RefPtr<CSSValue> consumeAnimationName(CSSParserTokenRange& range)
1284 {
1285     if (range.peek().id() == CSSValueNone)
1286         return consumeIdent(range);
1287
1288     if (range.peek().type() == StringToken) {
1289         const CSSParserToken& token = range.consumeIncludingWhitespace();
1290         if (equalIgnoringASCIICase(token.value(), "none"))
1291             return CSSValuePool::singleton().createIdentifierValue(CSSValueNone);
1292         // FIXME-NEWPARSER: Want to use a CSSCustomIdentValue here eventually.
1293         return CSSValuePool::singleton().createValue(token.value().toString(), CSSPrimitiveValue::UnitTypes::CSS_STRING);
1294     }
1295
1296     return consumeCustomIdent(range);
1297 }
1298
1299 static RefPtr<CSSValue> consumeTransitionProperty(CSSParserTokenRange& range)
1300 {
1301     const CSSParserToken& token = range.peek();
1302     if (token.type() != IdentToken)
1303         return nullptr;
1304     if (token.id() == CSSValueNone)
1305         return consumeIdent(range);
1306
1307     if (CSSPropertyID property = token.parseAsCSSPropertyID()) {
1308         range.consumeIncludingWhitespace();
1309         
1310         // FIXME-NEWPARSER: No reason why we can't use the "all" property now that it exists.
1311         // The old parser used a value keyword for "all", though, since it predated support for
1312         // the property.
1313         if (property == CSSPropertyAll)
1314             return CSSValuePool::singleton().createIdentifierValue(CSSValueAll);
1315
1316         // FIXME-NEWPARSER: Want to use a CSSCustomIdentValue here eventually.
1317         return CSSValuePool::singleton().createIdentifierValue(property);
1318     }
1319     return consumeCustomIdent(range);
1320 }
1321
1322     
1323 static RefPtr<CSSValue> consumeSteps(CSSParserTokenRange& range) {
1324     ASSERT(range.peek().functionId() == CSSValueSteps);
1325     CSSParserTokenRange rangeCopy = range;
1326     CSSParserTokenRange args = consumeFunction(rangeCopy);
1327     
1328     RefPtr<CSSPrimitiveValue> steps = consumePositiveInteger(args);
1329     if (!steps)
1330         return nullptr;
1331     
1332     // FIXME-NEWPARSER: Support the middle value and change from a boolean to an enum.
1333     bool stepAtStart = false;
1334     if (consumeCommaIncludingWhitespace(args)) {
1335         switch (args.consumeIncludingWhitespace().id()) {
1336             case CSSValueStart:
1337                 stepAtStart = true;
1338             break;
1339             case CSSValueEnd:
1340                 stepAtStart = false;
1341                 break;
1342             default:
1343                 return nullptr;
1344         }
1345     }
1346     
1347     if (!args.atEnd())
1348         return nullptr;
1349     
1350     range = rangeCopy;
1351     return CSSStepsTimingFunctionValue::create(steps->intValue(), stepAtStart);
1352 }
1353
1354 static RefPtr<CSSValue> consumeCubicBezier(CSSParserTokenRange& range)
1355 {
1356     ASSERT(range.peek().functionId() == CSSValueCubicBezier);
1357     CSSParserTokenRange rangeCopy = range;
1358     CSSParserTokenRange args = consumeFunction(rangeCopy);
1359
1360     double x1, y1, x2, y2;
1361     if (consumeNumberRaw(args, x1)
1362         && x1 >= 0 && x1 <= 1
1363         && consumeCommaIncludingWhitespace(args)
1364         && consumeNumberRaw(args, y1)
1365         && consumeCommaIncludingWhitespace(args)
1366         && consumeNumberRaw(args, x2)
1367         && x2 >= 0 && x2 <= 1
1368         && consumeCommaIncludingWhitespace(args)
1369         && consumeNumberRaw(args, y2)
1370         && args.atEnd()) {
1371         range = rangeCopy;
1372         return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2);
1373     }
1374
1375     return nullptr;
1376 }
1377
1378 static RefPtr<CSSValue> consumeSpringFunction(CSSParserTokenRange& range)
1379 {
1380     ASSERT(range.peek().functionId() == CSSValueSpring);
1381     CSSParserTokenRange rangeCopy = range;
1382     CSSParserTokenRange args = consumeFunction(rangeCopy);
1383
1384     // Mass must be greater than 0.
1385     double mass;
1386     if (!consumeNumberRaw(args, mass) || mass <= 0)
1387         return nullptr;
1388     
1389     // Stiffness must be greater than 0.
1390     double stiffness;
1391     if (!consumeNumberRaw(args, stiffness) || stiffness <= 0)
1392         return nullptr;
1393     
1394     // Damping coefficient must be greater than or equal to 0.
1395     double damping;
1396     if (!consumeNumberRaw(args, damping) || damping < 0)
1397         return nullptr;
1398     
1399     // Initial velocity may have any value.
1400     double initialVelocity;
1401     if (!consumeNumberRaw(args, initialVelocity))
1402         return nullptr;
1403
1404     if (!args.atEnd())
1405         return nullptr;
1406
1407     range = rangeCopy;
1408
1409     return CSSSpringTimingFunctionValue::create(mass, stiffness, damping, initialVelocity);
1410 }
1411
1412 static RefPtr<CSSValue> consumeAnimationTimingFunction(CSSParserTokenRange& range, const CSSParserContext& context)
1413 {
1414     CSSValueID id = range.peek().id();
1415     if (id == CSSValueEase || id == CSSValueLinear || id == CSSValueEaseIn
1416         || id == CSSValueEaseOut || id == CSSValueEaseInOut || id == CSSValueStepStart || id == CSSValueStepEnd)
1417         return consumeIdent(range);
1418
1419     CSSValueID function = range.peek().functionId();
1420     if (function == CSSValueCubicBezier)
1421         return consumeCubicBezier(range);
1422     if (function == CSSValueSteps)
1423         return consumeSteps(range);
1424     if (context.springTimingFunctionEnabled && function == CSSValueSpring)
1425         return consumeSpringFunction(range);
1426     return nullptr;
1427 }
1428
1429 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
1430 static RefPtr<CSSValue> consumeWebkitAnimationTrigger(CSSParserTokenRange& range, CSSParserMode mode)
1431 {
1432     if (range.peek().id() == CSSValueAuto)
1433         return consumeIdent(range);
1434     
1435     if (range.peek().functionId() != CSSValueContainerScroll)
1436         return nullptr;
1437     
1438     CSSParserTokenRange rangeCopy = range;
1439     CSSParserTokenRange args = consumeFunction(rangeCopy);
1440
1441     RefPtr<CSSPrimitiveValue> startValue = consumeLength(args, mode, ValueRangeAll, UnitlessQuirk::Forbid);
1442     if (!startValue)
1443         return nullptr;
1444     
1445     if (args.atEnd()) {
1446         range = rangeCopy;
1447         return CSSAnimationTriggerScrollValue::create(startValue.releaseNonNull());
1448     }
1449
1450     if (!consumeCommaIncludingWhitespace(args))
1451         return nullptr;
1452
1453     RefPtr<CSSPrimitiveValue> endValue = consumeLength(args, mode, ValueRangeAll, UnitlessQuirk::Forbid);
1454     if (!endValue || !args.atEnd())
1455         return nullptr;
1456
1457     range = rangeCopy;
1458
1459     return CSSAnimationTriggerScrollValue::create(startValue.releaseNonNull(), endValue.releaseNonNull());
1460 }
1461 #endif
1462     
1463 static RefPtr<CSSValue> consumeAnimationValue(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
1464 {
1465     switch (property) {
1466     case CSSPropertyAnimationDelay:
1467     case CSSPropertyTransitionDelay:
1468         return consumeTime(range, context.mode, ValueRangeAll, UnitlessQuirk::Forbid);
1469     case CSSPropertyAnimationDirection:
1470         return consumeIdent<CSSValueNormal, CSSValueAlternate, CSSValueReverse, CSSValueAlternateReverse>(range);
1471     case CSSPropertyAnimationDuration:
1472     case CSSPropertyTransitionDuration:
1473         return consumeTime(range, context.mode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
1474     case CSSPropertyAnimationFillMode:
1475         return consumeIdent<CSSValueNone, CSSValueForwards, CSSValueBackwards, CSSValueBoth>(range);
1476     case CSSPropertyAnimationIterationCount:
1477         return consumeAnimationIterationCount(range);
1478     case CSSPropertyAnimationName:
1479         return consumeAnimationName(range);
1480     case CSSPropertyAnimationPlayState:
1481         return consumeIdent<CSSValueRunning, CSSValuePaused>(range);
1482     case CSSPropertyTransitionProperty:
1483         return consumeTransitionProperty(range);
1484     case CSSPropertyAnimationTimingFunction:
1485     case CSSPropertyTransitionTimingFunction:
1486         return consumeAnimationTimingFunction(range, context);
1487 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
1488     case CSSPropertyWebkitAnimationTrigger:
1489         return consumeWebkitAnimationTrigger(range, context.mode);
1490 #endif
1491     default:
1492         ASSERT_NOT_REACHED();
1493         return nullptr;
1494     }
1495 }
1496
1497 static bool isValidAnimationPropertyList(CSSPropertyID property, const CSSValueList& valueList)
1498 {
1499     if (property != CSSPropertyTransitionProperty || valueList.length() < 2)
1500         return true;
1501     for (auto& value : valueList) {
1502         if (value->isPrimitiveValue() && downcast<CSSPrimitiveValue>(value.get()).isValueID()
1503             && downcast<CSSPrimitiveValue>(value.get()).valueID() == CSSValueNone)
1504             return false;
1505     }
1506     return true;
1507 }
1508
1509 static RefPtr<CSSValue> consumeAnimationPropertyList(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
1510 {
1511     RefPtr<CSSValueList> list;
1512     RefPtr<CSSValue> singleton;
1513     do {
1514         RefPtr<CSSValue> currentValue = consumeAnimationValue(property, range, context);
1515         if (!currentValue)
1516             return nullptr;
1517         
1518         if (singleton && !list) {
1519             list = CSSValueList::createCommaSeparated();
1520             list->append(singleton.releaseNonNull());
1521         }
1522         
1523         if (list)
1524             list->append(currentValue.releaseNonNull());
1525         else
1526             singleton = WTFMove(currentValue);
1527         
1528     } while (consumeCommaIncludingWhitespace(range));
1529
1530     if (list) {
1531         if (!isValidAnimationPropertyList(property, *list))
1532             return nullptr;
1533     
1534         ASSERT(list->length());
1535         return list;
1536     }
1537     
1538     return singleton;
1539 }
1540
1541 bool CSSPropertyParser::consumeAnimationShorthand(const StylePropertyShorthand& shorthand, bool important)
1542 {
1543     const unsigned longhandCount = shorthand.length();
1544     RefPtr<CSSValueList> longhands[8];
1545     ASSERT(longhandCount <= 8);
1546     for (size_t i = 0; i < longhandCount; ++i)
1547         longhands[i] = CSSValueList::createCommaSeparated();
1548
1549     do {
1550         bool parsedLonghand[8] = { false };
1551         do {
1552             bool foundProperty = false;
1553             for (size_t i = 0; i < longhandCount; ++i) {
1554                 if (parsedLonghand[i])
1555                     continue;
1556
1557                 if (RefPtr<CSSValue> value = consumeAnimationValue(shorthand.properties()[i], m_range, m_context)) {
1558                     parsedLonghand[i] = true;
1559                     foundProperty = true;
1560                     longhands[i]->append(*value);
1561                     break;
1562                 }
1563             }
1564             if (!foundProperty)
1565                 return false;
1566         } while (!m_range.atEnd() && m_range.peek().type() != CommaToken);
1567
1568         // FIXME: This will make invalid longhands, see crbug.com/386459
1569         for (size_t i = 0; i < longhandCount; ++i) {
1570             if (!parsedLonghand[i])
1571                 longhands[i]->append(CSSValuePool::singleton().createImplicitInitialValue());
1572             parsedLonghand[i] = false;
1573         }
1574     } while (consumeCommaIncludingWhitespace(m_range));
1575
1576     for (size_t i = 0; i < longhandCount; ++i) {
1577         if (!isValidAnimationPropertyList(shorthand.properties()[i], *longhands[i]))
1578             return false;
1579     }
1580
1581     for (size_t i = 0; i < longhandCount; ++i)
1582         addProperty(shorthand.properties()[i], shorthand.id(), *longhands[i], important);
1583
1584     return m_range.atEnd();
1585 }
1586
1587 static RefPtr<CSSValue> consumeZIndex(CSSParserTokenRange& range)
1588 {
1589     if (range.peek().id() == CSSValueAuto)
1590         return consumeIdent(range);
1591     return consumeInteger(range);
1592 }
1593
1594 static RefPtr<CSSValue> consumeShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool isBoxShadowProperty)
1595 {
1596     if (range.peek().id() == CSSValueNone)
1597         return consumeIdent(range);
1598
1599     RefPtr<CSSValueList> shadowValueList = CSSValueList::createCommaSeparated();
1600     do {
1601         if (RefPtr<CSSShadowValue> shadowValue = consumeSingleShadow(range, cssParserMode, isBoxShadowProperty, isBoxShadowProperty))
1602             shadowValueList->append(*shadowValue);
1603         else
1604             return nullptr;
1605     } while (consumeCommaIncludingWhitespace(range));
1606     return shadowValueList;
1607 }
1608
1609 static RefPtr<CSSValue> consumeTextDecorationLine(CSSParserTokenRange& range)
1610 {
1611     CSSValueID id = range.peek().id();
1612     if (id == CSSValueNone)
1613         return consumeIdent(range);
1614
1615     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1616     while (true) {
1617         RefPtr<CSSPrimitiveValue> ident = consumeIdent<CSSValueBlink, CSSValueUnderline, CSSValueOverline, CSSValueLineThrough>(range);
1618         if (!ident)
1619             break;
1620         if (list->hasValue(ident.get()))
1621             return nullptr;
1622         list->append(ident.releaseNonNull());
1623     }
1624
1625     if (!list->length())
1626         return nullptr;
1627     return list;
1628 }
1629
1630 static RefPtr<CSSValue> consumeTextDecorationSkip(CSSParserTokenRange& range)
1631 {
1632     CSSValueID id = range.peek().id();
1633     if (id == CSSValueNone)
1634         return consumeIdent(range);
1635
1636     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1637     while (true) {
1638         auto ident = consumeIdent<CSSValueAuto, CSSValueInk, CSSValueObjects>(range);
1639         if (!ident)
1640             break;
1641         if (list->hasValue(ident.get()))
1642             return nullptr;
1643         list->append(ident.releaseNonNull());
1644     }
1645
1646     if (!list->length())
1647         return nullptr;
1648     return list;
1649 }
1650
1651 static RefPtr<CSSValue> consumeTextEmphasisStyle(CSSParserTokenRange& range)
1652 {
1653     CSSValueID id = range.peek().id();
1654     if (id == CSSValueNone)
1655         return consumeIdent(range);
1656
1657     if (RefPtr<CSSValue> textEmphasisStyle = consumeString(range))
1658         return textEmphasisStyle;
1659
1660     RefPtr<CSSPrimitiveValue> fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
1661     RefPtr<CSSPrimitiveValue> shape = consumeIdent<CSSValueDot, CSSValueCircle, CSSValueDoubleCircle, CSSValueTriangle, CSSValueSesame>(range);
1662     if (!fill)
1663         fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
1664     if (fill && shape) {
1665         RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
1666         parsedValues->append(fill.releaseNonNull());
1667         parsedValues->append(shape.releaseNonNull());
1668         return parsedValues;
1669     }
1670     if (fill)
1671         return fill;
1672     if (shape)
1673         return shape;
1674     return nullptr;
1675 }
1676
1677 static RefPtr<CSSValue> consumeOutlineColor(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1678 {
1679     // Allow the special focus color even in HTML Standard parsing mode.
1680     if (range.peek().id() == CSSValueWebkitFocusRingColor)
1681         return consumeIdent(range);
1682     return consumeColor(range, cssParserMode);
1683 }
1684
1685 static RefPtr<CSSPrimitiveValue> consumeLineWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1686 {
1687     CSSValueID id = range.peek().id();
1688     if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick)
1689         return consumeIdent(range);
1690     return consumeLength(range, cssParserMode, ValueRangeNonNegative, unitless);
1691 }
1692
1693 static RefPtr<CSSPrimitiveValue> consumeBorderWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1694 {
1695     return consumeLineWidth(range, cssParserMode, unitless);
1696 }
1697
1698 static RefPtr<CSSPrimitiveValue> consumeTextStrokeWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1699 {
1700     return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
1701 }
1702
1703 static RefPtr<CSSPrimitiveValue> consumeColumnRuleWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1704 {
1705     return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
1706 }
1707
1708 static bool consumeTranslate3d(CSSParserTokenRange& args, CSSParserMode cssParserMode, RefPtr<WebKitCSSTransformValue>& transformValue)
1709 {
1710     unsigned numberOfArguments = 2;
1711     RefPtr<CSSValue> parsedValue;
1712     do {
1713         parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1714         if (!parsedValue)
1715             return false;
1716         transformValue->append(*parsedValue);
1717         if (!consumeCommaIncludingWhitespace(args))
1718             return false;
1719     } while (--numberOfArguments);
1720     parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
1721     if (!parsedValue)
1722         return false;
1723     transformValue->append(*parsedValue);
1724     return true;
1725 }
1726
1727 static bool consumeNumbers(CSSParserTokenRange& args, RefPtr<WebKitCSSTransformValue>& transformValue, unsigned numberOfArguments)
1728 {
1729     do {
1730         RefPtr<CSSPrimitiveValue> parsedValue = consumeNumber(args, ValueRangeAll);
1731         if (!parsedValue)
1732             return false;
1733         transformValue->append(parsedValue.releaseNonNull());
1734         if (--numberOfArguments && !consumeCommaIncludingWhitespace(args))
1735             return false;
1736     } while (numberOfArguments);
1737     return true;
1738 }
1739
1740 static bool consumePerspective(CSSParserTokenRange& args, CSSParserMode cssParserMode, RefPtr<WebKitCSSTransformValue>& transformValue)
1741 {
1742     RefPtr<CSSPrimitiveValue> parsedValue = consumeLength(args, cssParserMode, ValueRangeNonNegative);
1743     if (!parsedValue) {
1744         double perspective;
1745         if (!consumeNumberRaw(args, perspective) || perspective < 0)
1746             return false;
1747         parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitTypes::CSS_PX);
1748     }
1749     if (!parsedValue)
1750         return false;
1751     transformValue->append(parsedValue.releaseNonNull());
1752     return true;
1753 }
1754
1755 // FIXME-NEWPARSER: This has no reason to exist once we eliminate WebkitCSSTransformValue in favor
1756 // of CSSFunctionValue.
1757 static WebKitCSSTransformValue::TransformOperationType transformOperationForCSSValueID(CSSValueID functionId)
1758 {
1759     switch (functionId) {
1760     case CSSValueRotate:
1761         return WebKitCSSTransformValue::RotateTransformOperation;
1762     case CSSValueRotatex:
1763         return WebKitCSSTransformValue::RotateXTransformOperation;
1764     case CSSValueRotatey:
1765         return WebKitCSSTransformValue::RotateYTransformOperation;
1766     case CSSValueRotatez:
1767         return WebKitCSSTransformValue::RotateZTransformOperation;
1768     case CSSValueSkewx:
1769         return WebKitCSSTransformValue::SkewXTransformOperation;
1770     case CSSValueSkewy:
1771         return WebKitCSSTransformValue::SkewYTransformOperation;
1772     case CSSValueSkew:
1773         return WebKitCSSTransformValue::SkewTransformOperation;
1774     case CSSValueScalex:
1775         return WebKitCSSTransformValue::ScaleXTransformOperation;
1776     case CSSValueScaley:
1777         return WebKitCSSTransformValue::ScaleYTransformOperation;
1778     case CSSValueScalez:
1779         return WebKitCSSTransformValue::ScaleZTransformOperation;
1780     case CSSValueScale:
1781         return WebKitCSSTransformValue::ScaleTransformOperation;
1782     case CSSValuePerspective:
1783         return WebKitCSSTransformValue::PerspectiveTransformOperation;
1784     case CSSValueTranslatex:
1785         return WebKitCSSTransformValue::TranslateXTransformOperation;
1786     case CSSValueTranslatey:
1787         return WebKitCSSTransformValue::TranslateYTransformOperation;
1788     case CSSValueTranslate:
1789         return WebKitCSSTransformValue::TranslateTransformOperation;
1790     case CSSValueTranslatez:
1791         return WebKitCSSTransformValue::TranslateZTransformOperation;
1792     case CSSValueMatrix:
1793         return WebKitCSSTransformValue::MatrixTransformOperation;
1794     case CSSValueMatrix3d:
1795         return WebKitCSSTransformValue::Matrix3DTransformOperation;
1796     case CSSValueScale3d:
1797         return WebKitCSSTransformValue::Scale3DTransformOperation;
1798     case CSSValueRotate3d:
1799         return WebKitCSSTransformValue::Rotate3DTransformOperation;
1800     case CSSValueTranslate3d:
1801         return WebKitCSSTransformValue::Translate3DTransformOperation;
1802     default:
1803         return WebKitCSSTransformValue::UnknownTransformOperation;
1804     }
1805 }
1806
1807 static RefPtr<CSSValue> consumeTransformValue(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1808 {
1809     CSSValueID functionId = range.peek().functionId();
1810     if (functionId == CSSValueInvalid)
1811         return nullptr;
1812     CSSParserTokenRange args = consumeFunction(range);
1813     if (args.atEnd())
1814         return nullptr;
1815     
1816     // FIXME-NEWPARSER: Do we really need WebkitCSSTransformValue? A CSSFunctionValue is good
1817     // enough and has the CSSValueID as the operation type. Blink has eliminated it.
1818     RefPtr<WebKitCSSTransformValue> transformValue = WebKitCSSTransformValue::create(transformOperationForCSSValueID(functionId));
1819     RefPtr<CSSValue> parsedValue;
1820     switch (functionId) {
1821     case CSSValueRotate:
1822     case CSSValueRotatex:
1823     case CSSValueRotatey:
1824     case CSSValueRotatez:
1825     case CSSValueSkewx:
1826     case CSSValueSkewy:
1827     case CSSValueSkew:
1828         parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1829         if (!parsedValue)
1830             return nullptr;
1831         if (functionId == CSSValueSkew && consumeCommaIncludingWhitespace(args)) {
1832             transformValue->append(*parsedValue);
1833             parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1834             if (!parsedValue)
1835                 return nullptr;
1836         }
1837         break;
1838     case CSSValueScalex:
1839     case CSSValueScaley:
1840     case CSSValueScalez:
1841     case CSSValueScale:
1842         parsedValue = consumeNumber(args, ValueRangeAll);
1843         if (!parsedValue)
1844             return nullptr;
1845         if (functionId == CSSValueScale && consumeCommaIncludingWhitespace(args)) {
1846             transformValue->append(*parsedValue);
1847             parsedValue = consumeNumber(args, ValueRangeAll);
1848             if (!parsedValue)
1849                 return nullptr;
1850         }
1851         break;
1852     case CSSValuePerspective:
1853         if (!consumePerspective(args, cssParserMode, transformValue))
1854             return nullptr;
1855         break;
1856     case CSSValueTranslatex:
1857     case CSSValueTranslatey:
1858     case CSSValueTranslate:
1859         parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1860         if (!parsedValue)
1861             return nullptr;
1862         if (functionId == CSSValueTranslate && consumeCommaIncludingWhitespace(args)) {
1863             transformValue->append(*parsedValue);
1864             parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1865             if (!parsedValue)
1866                 return nullptr;
1867         }
1868         break;
1869     case CSSValueTranslatez:
1870         parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
1871         break;
1872     case CSSValueMatrix:
1873     case CSSValueMatrix3d:
1874         if (!consumeNumbers(args, transformValue, (functionId == CSSValueMatrix3d) ? 16 : 6))
1875             return nullptr;
1876         break;
1877     case CSSValueScale3d:
1878         if (!consumeNumbers(args, transformValue, 3))
1879             return nullptr;
1880         break;
1881     case CSSValueRotate3d:
1882         if (!consumeNumbers(args, transformValue, 3) || !consumeCommaIncludingWhitespace(args))
1883             return nullptr;
1884         parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1885         if (!parsedValue)
1886             return nullptr;
1887         break;
1888     case CSSValueTranslate3d:
1889         if (!consumeTranslate3d(args, cssParserMode, transformValue))
1890             return nullptr;
1891         break;
1892     default:
1893         return nullptr;
1894     }
1895     if (parsedValue)
1896         transformValue->append(*parsedValue);
1897     if (!args.atEnd())
1898         return nullptr;
1899     return transformValue;
1900 }
1901
1902 static RefPtr<CSSValue> consumeTransform(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1903 {
1904     if (range.peek().id() == CSSValueNone)
1905         return consumeIdent(range);
1906
1907     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1908     do {
1909         RefPtr<CSSValue> parsedTransformValue = consumeTransformValue(range, cssParserMode);
1910         if (!parsedTransformValue)
1911             return nullptr;
1912         list->append(parsedTransformValue.releaseNonNull());
1913     } while (!range.atEnd());
1914
1915     return list;
1916 }
1917
1918 template <CSSValueID start, CSSValueID end>
1919 static RefPtr<CSSPrimitiveValue> consumePositionLonghand(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1920 {
1921     if (range.peek().type() == IdentToken) {
1922         CSSValueID id = range.peek().id();
1923         int percent;
1924         if (id == start)
1925             percent = 0;
1926         else if (id == CSSValueCenter)
1927             percent = 50;
1928         else if (id == end)
1929             percent = 100;
1930         else
1931             return nullptr;
1932         range.consumeIncludingWhitespace();
1933         return CSSPrimitiveValue::create(percent, CSSPrimitiveValue::UnitTypes::CSS_PERCENTAGE);
1934     }
1935     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
1936 }
1937
1938 static RefPtr<CSSPrimitiveValue> consumePositionX(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1939 {
1940     return consumePositionLonghand<CSSValueLeft, CSSValueRight>(range, cssParserMode);
1941 }
1942
1943 static RefPtr<CSSPrimitiveValue> consumePositionY(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1944 {
1945     return consumePositionLonghand<CSSValueTop, CSSValueBottom>(range, cssParserMode);
1946 }
1947
1948 static RefPtr<CSSValue> consumePaintStroke(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1949 {
1950     if (range.peek().id() == CSSValueNone)
1951         return consumeIdent(range);
1952     RefPtr<CSSPrimitiveValue> url = consumeUrl(range);
1953     if (url) {
1954         RefPtr<CSSValue> parsedValue;
1955         if (range.peek().id() == CSSValueNone)
1956             parsedValue = consumeIdent(range);
1957         else
1958             parsedValue = consumeColor(range, cssParserMode);
1959         if (parsedValue) {
1960             RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
1961             values->append(url.releaseNonNull());
1962             values->append(parsedValue.releaseNonNull());
1963             return values;
1964         }
1965         return url;
1966     }
1967     return consumeColor(range, cssParserMode);
1968 }
1969
1970 static RefPtr<CSSValue> consumePaintOrder(CSSParserTokenRange& range)
1971 {
1972     if (range.peek().id() == CSSValueNormal)
1973         return consumeIdent(range);
1974
1975     Vector<CSSValueID, 3> paintTypeList;
1976     RefPtr<CSSPrimitiveValue> fill;
1977     RefPtr<CSSPrimitiveValue> stroke;
1978     RefPtr<CSSPrimitiveValue> markers;
1979     do {
1980         CSSValueID id = range.peek().id();
1981         if (id == CSSValueFill && !fill)
1982             fill = consumeIdent(range);
1983         else if (id == CSSValueStroke && !stroke)
1984             stroke = consumeIdent(range);
1985         else if (id == CSSValueMarkers && !markers)
1986             markers = consumeIdent(range);
1987         else
1988             return nullptr;
1989         paintTypeList.append(id);
1990     } while (!range.atEnd());
1991
1992     // After parsing we serialize the paint-order list. Since it is not possible to
1993     // pop a last list items from CSSValueList without bigger cost, we create the
1994     // list after parsing.
1995     CSSValueID firstPaintOrderType = paintTypeList.at(0);
1996     RefPtr<CSSValueList> paintOrderList = CSSValueList::createSpaceSeparated();
1997     switch (firstPaintOrderType) {
1998     case CSSValueFill:
1999     case CSSValueStroke:
2000         paintOrderList->append(firstPaintOrderType == CSSValueFill ? fill.releaseNonNull() : stroke.releaseNonNull());
2001         if (paintTypeList.size() > 1) {
2002             if (paintTypeList.at(1) == CSSValueMarkers)
2003                 paintOrderList->append(markers.releaseNonNull());
2004         }
2005         break;
2006     case CSSValueMarkers:
2007         paintOrderList->append(markers.releaseNonNull());
2008         if (paintTypeList.size() > 1) {
2009             if (paintTypeList.at(1) == CSSValueStroke)
2010                 paintOrderList->append(stroke.releaseNonNull());
2011         }
2012         break;
2013     default:
2014         ASSERT_NOT_REACHED();
2015     }
2016
2017     return paintOrderList;
2018 }
2019
2020 static RefPtr<CSSValue> consumeNoneOrURI(CSSParserTokenRange& range)
2021 {
2022     if (range.peek().id() == CSSValueNone)
2023         return consumeIdent(range);
2024     return consumeUrl(range);
2025 }
2026
2027 static RefPtr<CSSValue> consumeFlexBasis(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2028 {
2029     // FIXME: Support intrinsic dimensions too.
2030     if (range.peek().id() == CSSValueAuto)
2031         return consumeIdent(range);
2032     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2033 }
2034
2035 static RefPtr<CSSValue> consumeStrokeDasharray(CSSParserTokenRange& range)
2036 {
2037     CSSValueID id = range.peek().id();
2038     if (id == CSSValueNone)
2039         return consumeIdent(range);
2040
2041     RefPtr<CSSValueList> dashes = CSSValueList::createCommaSeparated();
2042     do {
2043         RefPtr<CSSPrimitiveValue> dash = consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeNonNegative);
2044         if (!dash || (consumeCommaIncludingWhitespace(range) && range.atEnd()))
2045             return nullptr;
2046         dashes->append(dash.releaseNonNull());
2047     } while (!range.atEnd());
2048     return dashes;
2049 }
2050
2051 static RefPtr<CSSPrimitiveValue> consumeBaselineShift(CSSParserTokenRange& range)
2052 {
2053     CSSValueID id = range.peek().id();
2054     if (id == CSSValueBaseline || id == CSSValueSub || id == CSSValueSuper)
2055         return consumeIdent(range);
2056     return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll);
2057 }
2058
2059 static RefPtr<CSSPrimitiveValue> consumeRxOrRy(CSSParserTokenRange& range)
2060 {
2061     if (range.peek().id() == CSSValueAuto)
2062         return consumeIdent(range);
2063     return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
2064 }
2065
2066 static RefPtr<CSSValue> consumeCursor(CSSParserTokenRange& range, const CSSParserContext& context, bool inQuirksMode)
2067 {
2068     RefPtr<CSSValueList> list;
2069     while (RefPtr<CSSValue> image = consumeImage(range, context, ConsumeGeneratedImage::Forbid)) {
2070         double num;
2071         IntPoint hotSpot(-1, -1);
2072         bool hotSpotSpecified = false;
2073         if (consumeNumberRaw(range, num)) {
2074             hotSpot.setX(int(num));
2075             if (!consumeNumberRaw(range, num))
2076                 return nullptr;
2077             hotSpot.setY(int(num));
2078             hotSpotSpecified = true;
2079         }
2080
2081         if (!list)
2082             list = CSSValueList::createCommaSeparated();
2083
2084         list->append(CSSCursorImageValue::create(image.releaseNonNull(), hotSpotSpecified, hotSpot));
2085         if (!consumeCommaIncludingWhitespace(range))
2086             return nullptr;
2087     }
2088
2089     CSSValueID id = range.peek().id();
2090     RefPtr<CSSValue> cursorType;
2091     if (id == CSSValueHand) {
2092         if (!inQuirksMode) // Non-standard behavior
2093             return nullptr;
2094         cursorType = CSSValuePool::singleton().createIdentifierValue(CSSValuePointer);
2095         range.consumeIncludingWhitespace();
2096     } else if ((id >= CSSValueAuto && id <= CSSValueWebkitZoomOut) || id == CSSValueCopy || id == CSSValueNone) {
2097         cursorType = consumeIdent(range);
2098     } else {
2099         return nullptr;
2100     }
2101
2102     if (!list)
2103         return cursorType;
2104     list->append(cursorType.releaseNonNull());
2105     return list;
2106 }
2107
2108 static RefPtr<CSSValue> consumeAttr(CSSParserTokenRange args, CSSParserContext context)
2109 {
2110     if (args.peek().type() != IdentToken)
2111         return nullptr;
2112     
2113     CSSParserToken token = args.consumeIncludingWhitespace();
2114     if (context.isHTMLDocument)
2115         token.convertToASCIILowercaseInPlace();
2116
2117     String attrName = token.value().toString();
2118     if (!args.atEnd())
2119         return nullptr;
2120
2121     // FIXME-NEWPARSER: We want to use a CSSCustomIdentValue here eventually for the attrName.
2122     // FIXME-NEWPARSER: We want to use a CSSFunctionValue rather than relying on a custom
2123     // attr() primitive value.
2124     return CSSValuePool::singleton().createValue(attrName, CSSPrimitiveValue::CSS_ATTR);
2125 }
2126
2127 static RefPtr<CSSValue> consumeCounterContent(CSSParserTokenRange args, bool counters)
2128 {
2129     RefPtr<CSSPrimitiveValue> identifier = consumeCustomIdent(args);
2130     if (!identifier)
2131         return nullptr;
2132
2133     RefPtr<CSSPrimitiveValue> separator;
2134     if (!counters)
2135         separator = CSSPrimitiveValue::create(String(), CSSPrimitiveValue::UnitTypes::CSS_STRING);
2136     else {
2137         if (!consumeCommaIncludingWhitespace(args) || args.peek().type() != StringToken)
2138             return nullptr;
2139         separator = CSSPrimitiveValue::create(args.consumeIncludingWhitespace().value().toString(), CSSPrimitiveValue::UnitTypes::CSS_STRING);
2140     }
2141
2142     RefPtr<CSSPrimitiveValue> listStyle;
2143     if (consumeCommaIncludingWhitespace(args)) {
2144         CSSValueID id = args.peek().id();
2145         if ((id != CSSValueNone && (id < CSSValueDisc || id > CSSValueKatakanaIroha)))
2146             return nullptr;
2147         listStyle = consumeIdent(args);
2148     } else
2149         listStyle = CSSValuePool::singleton().createIdentifierValue(CSSValueDecimal);
2150
2151     if (!args.atEnd())
2152         return nullptr;
2153     
2154     // FIXME-NEWPARSER: Should just have a CSSCounterValue.
2155     return CSSValuePool::singleton().createValue(Counter::create(identifier.releaseNonNull(), listStyle.releaseNonNull(), separator.releaseNonNull()));
2156 }
2157
2158 static RefPtr<CSSValue> consumeContent(CSSParserTokenRange& range, CSSParserContext context)
2159 {
2160     if (identMatches<CSSValueNone, CSSValueNormal>(range.peek().id()))
2161         return consumeIdent(range);
2162
2163     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
2164
2165     do {
2166         RefPtr<CSSValue> parsedValue = consumeImage(range, context);
2167         if (!parsedValue)
2168             parsedValue = consumeIdent<CSSValueOpenQuote, CSSValueCloseQuote, CSSValueNoOpenQuote, CSSValueNoCloseQuote>(range);
2169         if (!parsedValue)
2170             parsedValue = consumeString(range);
2171         if (!parsedValue) {
2172             if (range.peek().functionId() == CSSValueAttr)
2173                 parsedValue = consumeAttr(consumeFunction(range), context);
2174             else if (range.peek().functionId() == CSSValueCounter)
2175                 parsedValue = consumeCounterContent(consumeFunction(range), false);
2176             else if (range.peek().functionId() == CSSValueCounters)
2177                 parsedValue = consumeCounterContent(consumeFunction(range), true);
2178             if (!parsedValue)
2179                 return nullptr;
2180         }
2181         values->append(parsedValue.releaseNonNull());
2182     } while (!range.atEnd());
2183
2184     return values;
2185 }
2186
2187 static RefPtr<CSSPrimitiveValue> consumePerspective(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2188 {
2189     if (range.peek().id() == CSSValueNone)
2190         return consumeIdent(range);
2191     RefPtr<CSSPrimitiveValue> parsedValue = consumeLength(range, cssParserMode, ValueRangeAll);
2192     if (!parsedValue) {
2193         // FIXME: Make this quirk only apply to the webkit prefixed version of the property.
2194         double perspective;
2195         if (!consumeNumberRaw(range, perspective))
2196             return nullptr;
2197         parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitTypes::CSS_PX);
2198     }
2199     if (parsedValue && (parsedValue->isCalculated() || parsedValue->doubleValue() > 0))
2200         return parsedValue;
2201     return nullptr;
2202 }
2203
2204 #if ENABLE(CSS_SCROLL_SNAP)
2205
2206 static RefPtr<CSSValueList> consumeSnapPointCoordinateList(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2207 {
2208     RefPtr<CSSValueList> positions = CSSValueList::createSpaceSeparated();
2209     do {
2210         
2211         RefPtr<CSSPrimitiveValue> first = consumePositionX(range, cssParserMode);
2212         if (!first || range.atEnd())
2213             return nullptr;
2214         RefPtr<CSSPrimitiveValue> second = consumePositionY(range, cssParserMode);
2215         if (!second)
2216             return nullptr;
2217         RefPtr<CSSValue> position = createPrimitiveValuePair(first.releaseNonNull(), second.releaseNonNull(), Pair::IdenticalValueEncoding::DoNotCoalesce);
2218         positions->append(position.releaseNonNull());
2219     } while (!range.atEnd());
2220     return positions;
2221 }
2222
2223 static RefPtr<CSSValue> consumeScrollSnapCoordinate(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2224 {
2225     if (range.peek().id() == CSSValueNone)
2226         return consumeIdent(range);
2227     return consumeSnapPointCoordinateList(range, cssParserMode);
2228 }
2229
2230 static RefPtr<CSSValue> consumeScrollSnapDestination(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2231 {
2232     RefPtr<CSSPrimitiveValue> first = consumePositionX(range, cssParserMode);
2233     if (!first || range.atEnd())
2234         return nullptr;
2235     RefPtr<CSSPrimitiveValue> second = consumePositionY(range, cssParserMode);
2236     if (!second)
2237         return nullptr;
2238     return createPrimitiveValuePair(first.releaseNonNull(), second.releaseNonNull(), Pair::IdenticalValueEncoding::DoNotCoalesce);
2239 }
2240
2241 static RefPtr<CSSValue> consumeScrollSnapPoints(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2242 {
2243     if (range.peek().id() == CSSValueNone || range.peek().id() == CSSValueElements)
2244         return consumeIdent(range);
2245     
2246     RefPtr<CSSValueList> points = CSSValueList::createSpaceSeparated();
2247     do {
2248         if (range.peek().functionId() == CSSValueRepeat) {
2249             CSSParserTokenRange args = consumeFunction(range);
2250             RefPtr<CSSPrimitiveValue> parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
2251             if (args.atEnd() && parsedValue && (parsedValue->isCalculated() || parsedValue->doubleValue() > 0)) {
2252                 Ref<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueRepeat);
2253                 result->append(parsedValue.releaseNonNull());
2254                 points->append(WTFMove(result));
2255             }
2256         } else {
2257             RefPtr<CSSValue> length = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid);
2258             if (!length)
2259                 return nullptr;
2260             points->append(length.releaseNonNull());
2261         }
2262     } while (!range.atEnd());
2263     
2264     
2265     return points;
2266 }
2267
2268 #endif
2269
2270 static RefPtr<CSSValue> consumeBorderRadiusCorner(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2271 {
2272     RefPtr<CSSPrimitiveValue> parsedValue1 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2273     if (!parsedValue1)
2274         return nullptr;
2275     RefPtr<CSSPrimitiveValue> parsedValue2 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2276     if (!parsedValue2)
2277         parsedValue2 = parsedValue1;
2278     return createPrimitiveValuePair(parsedValue1.releaseNonNull(), parsedValue2.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2279 }
2280
2281 static RefPtr<CSSPrimitiveValue> consumeVerticalAlign(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2282 {
2283     RefPtr<CSSPrimitiveValue> parsedValue = consumeIdentRange(range, CSSValueBaseline, CSSValueWebkitBaselineMiddle);
2284     if (!parsedValue)
2285         parsedValue = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2286     return parsedValue;
2287 }
2288
2289 static RefPtr<CSSPrimitiveValue> consumeShapeRadius(CSSParserTokenRange& args, CSSParserMode cssParserMode)
2290 {
2291     if (identMatches<CSSValueClosestSide, CSSValueFarthestSide>(args.peek().id()))
2292         return consumeIdent(args);
2293     return consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
2294 }
2295
2296 static RefPtr<CSSBasicShapeCircle> consumeBasicShapeCircle(CSSParserTokenRange& args, const CSSParserContext& context)
2297 {
2298     // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
2299     // circle( [<shape-radius>]? [at <position>]? )
2300     RefPtr<CSSBasicShapeCircle> shape = CSSBasicShapeCircle::create();
2301     if (RefPtr<CSSPrimitiveValue> radius = consumeShapeRadius(args, context.mode))
2302         shape->setRadius(radius.releaseNonNull());
2303     if (consumeIdent<CSSValueAt>(args)) {
2304         RefPtr<CSSPrimitiveValue> centerX;
2305         RefPtr<CSSPrimitiveValue> centerY;
2306         if (!consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY))
2307             return nullptr;
2308         shape->setCenterX(centerX.releaseNonNull());
2309         shape->setCenterY(centerY.releaseNonNull());
2310     }
2311     return shape;
2312 }
2313
2314 static RefPtr<CSSBasicShapeEllipse> consumeBasicShapeEllipse(CSSParserTokenRange& args, const CSSParserContext& context)
2315 {
2316     // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
2317     // ellipse( [<shape-radius>{2}]? [at <position>]? )
2318     RefPtr<CSSBasicShapeEllipse> shape = CSSBasicShapeEllipse::create();
2319     if (RefPtr<CSSPrimitiveValue> radiusX = consumeShapeRadius(args, context.mode)) {
2320         shape->setRadiusX(radiusX.releaseNonNull());
2321         if (RefPtr<CSSPrimitiveValue> radiusY = consumeShapeRadius(args, context.mode))
2322             shape->setRadiusY(radiusY.releaseNonNull());
2323     }
2324     if (consumeIdent<CSSValueAt>(args)) {
2325         RefPtr<CSSPrimitiveValue> centerX;
2326         RefPtr<CSSPrimitiveValue> centerY;
2327         if (!consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY))
2328             return nullptr;
2329         shape->setCenterX(centerX.releaseNonNull());
2330         shape->setCenterY(centerY.releaseNonNull());
2331     }
2332     return shape;
2333 }
2334
2335 static RefPtr<CSSBasicShapePolygon> consumeBasicShapePolygon(CSSParserTokenRange& args, const CSSParserContext& context)
2336 {
2337     RefPtr<CSSBasicShapePolygon> shape = CSSBasicShapePolygon::create();
2338     if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
2339         shape->setWindRule(args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? RULE_EVENODD : RULE_NONZERO);
2340         if (!consumeCommaIncludingWhitespace(args))
2341             return nullptr;
2342     }
2343
2344     do {
2345         RefPtr<CSSPrimitiveValue> xLength = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2346         if (!xLength)
2347             return nullptr;
2348         RefPtr<CSSPrimitiveValue> yLength = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2349         if (!yLength)
2350             return nullptr;
2351         shape->appendPoint(xLength.releaseNonNull(), yLength.releaseNonNull());
2352     } while (consumeCommaIncludingWhitespace(args));
2353     return shape;
2354 }
2355
2356 static RefPtr<CSSBasicShapePath> consumeBasicShapePath(CSSParserTokenRange& args)
2357 {
2358     WindRule windRule = RULE_NONZERO;
2359     if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
2360         windRule = args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? RULE_EVENODD : RULE_NONZERO;
2361         if (!consumeCommaIncludingWhitespace(args))
2362             return nullptr;
2363     }
2364
2365     if (args.peek().type() != StringToken)
2366         return nullptr;
2367     
2368     auto byteStream = std::make_unique<SVGPathByteStream>();
2369     if (!buildSVGPathByteStreamFromString(args.consumeIncludingWhitespace().value().toString(), *byteStream, UnalteredParsing))
2370         return nullptr;
2371     
2372     auto shape = CSSBasicShapePath::create(WTFMove(byteStream));
2373     shape->setWindRule(windRule);
2374     
2375     return WTFMove(shape);
2376 }
2377
2378 static void complete4Sides(RefPtr<CSSPrimitiveValue> side[4])
2379 {
2380     if (side[3])
2381         return;
2382     if (!side[2]) {
2383         if (!side[1])
2384             side[1] = side[0];
2385         side[2] = side[0];
2386     }
2387     side[3] = side[1];
2388 }
2389
2390 static bool consumeRadii(RefPtr<CSSPrimitiveValue> horizontalRadii[4], RefPtr<CSSPrimitiveValue> verticalRadii[4], CSSParserTokenRange& range, CSSParserMode cssParserMode, bool useLegacyParsing)
2391 {
2392     unsigned i = 0;
2393     for (; i < 4 && !range.atEnd() && range.peek().type() != DelimiterToken; ++i) {
2394         horizontalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2395         if (!horizontalRadii[i])
2396             return false;
2397     }
2398     if (!horizontalRadii[0])
2399         return false;
2400     if (range.atEnd()) {
2401         // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2;
2402         if (useLegacyParsing && i == 2) {
2403             verticalRadii[0] = horizontalRadii[1];
2404             horizontalRadii[1] = nullptr;
2405         } else {
2406             complete4Sides(horizontalRadii);
2407             for (unsigned i = 0; i < 4; ++i)
2408                 verticalRadii[i] = horizontalRadii[i];
2409             return true;
2410         }
2411     } else {
2412         if (!consumeSlashIncludingWhitespace(range))
2413             return false;
2414         for (i = 0; i < 4 && !range.atEnd(); ++i) {
2415             verticalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2416             if (!verticalRadii[i])
2417                 return false;
2418         }
2419         if (!verticalRadii[0] || !range.atEnd())
2420             return false;
2421     }
2422     complete4Sides(horizontalRadii);
2423     complete4Sides(verticalRadii);
2424     return true;
2425 }
2426
2427 static RefPtr<CSSBasicShapeInset> consumeBasicShapeInset(CSSParserTokenRange& args, const CSSParserContext& context)
2428 {
2429     RefPtr<CSSBasicShapeInset> shape = CSSBasicShapeInset::create();
2430     RefPtr<CSSPrimitiveValue> top = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2431     if (!top)
2432         return nullptr;
2433     RefPtr<CSSPrimitiveValue> right = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2434     RefPtr<CSSPrimitiveValue> bottom;
2435     RefPtr<CSSPrimitiveValue> left;
2436     if (right) {
2437         bottom = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2438         if (bottom)
2439             left = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2440     }
2441     if (left)
2442         shape->updateShapeSize4Values(top.releaseNonNull(), right.releaseNonNull(), bottom.releaseNonNull(), left.releaseNonNull());
2443     else if (bottom)
2444         shape->updateShapeSize3Values(top.releaseNonNull(), right.releaseNonNull(), bottom.releaseNonNull());
2445     else if (right)
2446         shape->updateShapeSize2Values(top.releaseNonNull(), right.releaseNonNull());
2447     else
2448         shape->updateShapeSize1Value(top.releaseNonNull());
2449
2450     if (consumeIdent<CSSValueRound>(args)) {
2451         RefPtr<CSSPrimitiveValue> horizontalRadii[4] = { 0 };
2452         RefPtr<CSSPrimitiveValue> verticalRadii[4] = { 0 };
2453         if (!consumeRadii(horizontalRadii, verticalRadii, args, context.mode, false))
2454             return nullptr;
2455         shape->setTopLeftRadius(createPrimitiveValuePair(horizontalRadii[0].releaseNonNull(), verticalRadii[0].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2456         shape->setTopRightRadius(createPrimitiveValuePair(horizontalRadii[1].releaseNonNull(), verticalRadii[1].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2457         shape->setBottomRightRadius(createPrimitiveValuePair(horizontalRadii[2].releaseNonNull(), verticalRadii[2].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2458         shape->setBottomLeftRadius(createPrimitiveValuePair(horizontalRadii[3].releaseNonNull(), verticalRadii[3].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2459     }
2460     return shape;
2461 }
2462
2463 static RefPtr<CSSValue> consumeBasicShape(CSSParserTokenRange& range, const CSSParserContext& context)
2464 {
2465     RefPtr<CSSValue> result;
2466     if (range.peek().type() != FunctionToken)
2467         return nullptr;
2468     CSSValueID id = range.peek().functionId();
2469     CSSParserTokenRange rangeCopy = range;
2470     CSSParserTokenRange args = consumeFunction(rangeCopy);
2471     
2472     // FIXME-NEWPARSER: CSSBasicShape should be a CSSValue, and shapes should not be primitive values.
2473     RefPtr<CSSBasicShape> shape;
2474     if (id == CSSValueCircle)
2475         shape = consumeBasicShapeCircle(args, context);
2476     else if (id == CSSValueEllipse)
2477         shape = consumeBasicShapeEllipse(args, context);
2478     else if (id == CSSValuePolygon)
2479         shape = consumeBasicShapePolygon(args, context);
2480     else if (id == CSSValueInset)
2481         shape = consumeBasicShapeInset(args, context);
2482     else if (id == CSSValuePath)
2483         shape = consumeBasicShapePath(args);
2484     if (!shape)
2485         return nullptr;
2486     range = rangeCopy;
2487     
2488     if (!args.atEnd())
2489         return nullptr;
2490
2491     return CSSValuePool::singleton().createValue(shape.releaseNonNull());
2492 }
2493
2494 static RefPtr<CSSValue> consumeBasicShapeOrBox(CSSParserTokenRange& range, const CSSParserContext& context)
2495 {
2496     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
2497     bool shapeFound = false;
2498     bool boxFound = false;
2499     while (!range.atEnd() && !(shapeFound && boxFound)) {
2500         RefPtr<CSSValue> componentValue;
2501         if (range.peek().type() == FunctionToken && !shapeFound) {
2502             componentValue = consumeBasicShape(range, context);
2503             shapeFound = true;
2504         } else if (range.peek().type() == IdentToken && !boxFound) {
2505             componentValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox, CSSValueFill, CSSValueStroke, CSSValueViewBox>(range);
2506             boxFound = true;
2507         }
2508         if (!componentValue)
2509             return nullptr;
2510         list->append(componentValue.releaseNonNull());
2511     }
2512     
2513     if (!range.atEnd())
2514         return nullptr;
2515     
2516     return list;
2517 }
2518     
2519 static RefPtr<CSSValue> consumeWebkitClipPath(CSSParserTokenRange& range, const CSSParserContext& context)
2520 {
2521     if (range.peek().id() == CSSValueNone)
2522         return consumeIdent(range);
2523     if (RefPtr<CSSPrimitiveValue> url = consumeUrl(range))
2524         return url;
2525     return consumeBasicShapeOrBox(range, context);
2526 }
2527
2528 static RefPtr<CSSValue> consumeShapeOutside(CSSParserTokenRange& range, const CSSParserContext& context)
2529 {
2530     if (RefPtr<CSSValue> imageValue = consumeImageOrNone(range, context))
2531         return imageValue;
2532     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
2533     if (RefPtr<CSSValue> boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
2534         list->append(boxValue.releaseNonNull());
2535     if (RefPtr<CSSValue> shapeValue = consumeBasicShape(range, context)) {
2536         list->append(shapeValue.releaseNonNull());
2537         if (list->length() < 2) {
2538             if (RefPtr<CSSValue> boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
2539                 list->append(boxValue.releaseNonNull());
2540         }
2541     }
2542     if (!list->length())
2543         return nullptr;
2544     return list;
2545 }
2546
2547 static RefPtr<CSSValue> consumeContentDistributionOverflowPosition(CSSParserTokenRange& range)
2548 {
2549     if (identMatches<CSSValueNormal, CSSValueBaseline, CSSValueLastBaseline>(range.peek().id()))
2550         return CSSContentDistributionValue::create(CSSValueInvalid, range.consumeIncludingWhitespace().id(), CSSValueInvalid);
2551
2552     CSSValueID distribution = CSSValueInvalid;
2553     CSSValueID position = CSSValueInvalid;
2554     CSSValueID overflow = CSSValueInvalid;
2555     do {
2556         CSSValueID id = range.peek().id();
2557         if (identMatches<CSSValueSpaceBetween, CSSValueSpaceAround, CSSValueSpaceEvenly, CSSValueStretch>(id)) {
2558             if (distribution != CSSValueInvalid)
2559                 return nullptr;
2560             distribution = id;
2561         } else if (identMatches<CSSValueStart, CSSValueEnd, CSSValueCenter, CSSValueFlexStart, CSSValueFlexEnd, CSSValueLeft, CSSValueRight>(id)) {
2562             if (position != CSSValueInvalid)
2563                 return nullptr;
2564             position = id;
2565         } else if (identMatches<CSSValueUnsafe, CSSValueSafe>(id)) {
2566             if (overflow != CSSValueInvalid)
2567                 return nullptr;
2568             overflow = id;
2569         } else {
2570             return nullptr;
2571         }
2572         range.consumeIncludingWhitespace();
2573     } while (!range.atEnd());
2574
2575     // The grammar states that we should have at least <content-distribution> or <content-position>.
2576     if (position == CSSValueInvalid && distribution == CSSValueInvalid)
2577         return nullptr;
2578
2579     // The grammar states that <overflow-position> must be associated to <content-position>.
2580     if (overflow != CSSValueInvalid && position == CSSValueInvalid)
2581         return nullptr;
2582
2583     return CSSContentDistributionValue::create(distribution, position, overflow);
2584 }
2585
2586 static RefPtr<CSSPrimitiveValue> consumeBorderImageRepeatKeyword(CSSParserTokenRange& range)
2587 {
2588     return consumeIdent<CSSValueStretch, CSSValueRepeat, CSSValueSpace, CSSValueRound>(range);
2589 }
2590
2591 static RefPtr<CSSValue> consumeBorderImageRepeat(CSSParserTokenRange& range)
2592 {
2593     RefPtr<CSSPrimitiveValue> horizontal = consumeBorderImageRepeatKeyword(range);
2594     if (!horizontal)
2595         return nullptr;
2596     RefPtr<CSSPrimitiveValue> vertical = consumeBorderImageRepeatKeyword(range);
2597     if (!vertical)
2598         vertical = horizontal;
2599     return createPrimitiveValuePair(horizontal.releaseNonNull(), vertical.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2600 }
2601
2602 static RefPtr<CSSValue> consumeBorderImageSlice(CSSPropertyID property, CSSParserTokenRange& range)
2603 {
2604     bool fill = consumeIdent<CSSValueFill>(range);
2605     RefPtr<CSSPrimitiveValue> slices[4] = { 0 };
2606
2607     for (size_t index = 0; index < 4; ++index) {
2608         RefPtr<CSSPrimitiveValue> value = consumePercent(range, ValueRangeNonNegative);
2609         if (!value)
2610             value = consumeNumber(range, ValueRangeNonNegative);
2611         if (!value)
2612             break;
2613         slices[index] = value;
2614     }
2615     if (!slices[0])
2616         return nullptr;
2617     if (consumeIdent<CSSValueFill>(range)) {
2618         if (fill)
2619             return nullptr;
2620         fill = true;
2621     }
2622     complete4Sides(slices);
2623     // FIXME: For backwards compatibility, -webkit-border-image, -webkit-mask-box-image and -webkit-box-reflect have to do a fill by default.
2624     // FIXME: What do we do with -webkit-box-reflect and -webkit-mask-box-image? Probably just have to leave them filling...
2625     if (property == CSSPropertyWebkitBorderImage || property == CSSPropertyWebkitMaskBoxImage || property == CSSPropertyWebkitBoxReflect)
2626         fill = true;
2627     
2628     // Now build a rect value to hold all four of our primitive values.
2629     // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2630     auto quad = Quad::create();
2631     quad->setTop(slices[0].releaseNonNull());
2632     quad->setRight(slices[1].releaseNonNull());
2633     quad->setBottom(slices[2].releaseNonNull());
2634     quad->setLeft(slices[3].releaseNonNull());
2635     
2636     // Make our new border image value now.
2637     return CSSBorderImageSliceValue::create(CSSValuePool::singleton().createValue(WTFMove(quad)), fill);
2638 }
2639
2640 static RefPtr<CSSValue> consumeBorderImageOutset(CSSParserTokenRange& range)
2641 {
2642     RefPtr<CSSPrimitiveValue> outsets[4] = { 0 };
2643
2644     RefPtr<CSSPrimitiveValue> value;
2645     for (size_t index = 0; index < 4; ++index) {
2646         value = consumeNumber(range, ValueRangeNonNegative);
2647         if (!value)
2648             value = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
2649         if (!value)
2650             break;
2651         outsets[index] = value;
2652     }
2653     if (!outsets[0])
2654         return nullptr;
2655     complete4Sides(outsets);
2656     
2657     // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2658     auto quad = Quad::create();
2659     quad->setTop(outsets[0].releaseNonNull());
2660     quad->setRight(outsets[1].releaseNonNull());
2661     quad->setBottom(outsets[2].releaseNonNull());
2662     quad->setLeft(outsets[3].releaseNonNull());
2663     
2664     return CSSValuePool::singleton().createValue(WTFMove(quad));
2665 }
2666
2667 static RefPtr<CSSValue> consumeBorderImageWidth(CSSParserTokenRange& range)
2668 {
2669     RefPtr<CSSPrimitiveValue> widths[4];
2670
2671     RefPtr<CSSPrimitiveValue> value;
2672     for (size_t index = 0; index < 4; ++index) {
2673         value = consumeNumber(range, ValueRangeNonNegative);
2674         if (!value)
2675             value = consumeLengthOrPercent(range, HTMLStandardMode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
2676         if (!value)
2677             value = consumeIdent<CSSValueAuto>(range);
2678         if (!value)
2679             break;
2680         widths[index] = value;
2681     }
2682     if (!widths[0])
2683         return nullptr;
2684     complete4Sides(widths);
2685     
2686     // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2687     auto quad = Quad::create();
2688     quad->setTop(widths[0].releaseNonNull());
2689     quad->setRight(widths[1].releaseNonNull());
2690     quad->setBottom(widths[2].releaseNonNull());
2691     quad->setLeft(widths[3].releaseNonNull());
2692     
2693     return CSSValuePool::singleton().createValue(WTFMove(quad));
2694 }
2695
2696 static bool consumeBorderImageComponents(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context, RefPtr<CSSValue>& source,
2697     RefPtr<CSSValue>& slice, RefPtr<CSSValue>& width, RefPtr<CSSValue>& outset, RefPtr<CSSValue>& repeat)
2698 {
2699     do {
2700         if (!source) {
2701             source = consumeImageOrNone(range, context);
2702             if (source)
2703                 continue;
2704         }
2705         if (!repeat) {
2706             repeat = consumeBorderImageRepeat(range);
2707             if (repeat)
2708                 continue;
2709         }
2710         if (!slice) {
2711             slice = consumeBorderImageSlice(property, range);
2712             if (slice) {
2713                 ASSERT(!width && !outset);
2714                 if (consumeSlashIncludingWhitespace(range)) {
2715                     width = consumeBorderImageWidth(range);
2716                     if (consumeSlashIncludingWhitespace(range)) {
2717                         outset = consumeBorderImageOutset(range);
2718                         if (!outset)
2719                             return false;
2720                     } else if (!width) {
2721                         return false;
2722                     }
2723                 }
2724             } else {
2725                 return false;
2726             }
2727         } else {
2728             return false;
2729         }
2730     } while (!range.atEnd());
2731     return true;
2732 }
2733
2734 static RefPtr<CSSValue> consumeWebkitBorderImage(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2735 {
2736     RefPtr<CSSValue> source;
2737     RefPtr<CSSValue> slice;
2738     RefPtr<CSSValue> width;
2739     RefPtr<CSSValue> outset;
2740     RefPtr<CSSValue> repeat;
2741     if (consumeBorderImageComponents(property, range, context, source, slice, width, outset, repeat))
2742         return createBorderImageValue(WTFMove(source), WTFMove(slice), WTFMove(width), WTFMove(outset), WTFMove(repeat));
2743     return nullptr;
2744 }
2745
2746 static RefPtr<CSSValue> consumeReflect(CSSParserTokenRange& range, const CSSParserContext& context)
2747 {
2748     if (range.peek().id() == CSSValueNone)
2749         return consumeIdent(range);
2750     
2751     RefPtr<CSSPrimitiveValue> direction = consumeIdent<CSSValueAbove, CSSValueBelow, CSSValueLeft, CSSValueRight>(range);
2752     if (!direction)
2753         return nullptr;
2754
2755     RefPtr<CSSPrimitiveValue> offset;
2756     if (range.atEnd())
2757         offset = CSSValuePool::singleton().createValue(0, CSSPrimitiveValue::UnitTypes::CSS_PX);
2758     else {
2759         offset = consumeLengthOrPercent(range, context.mode, ValueRangeAll, UnitlessQuirk::Forbid);
2760         if (!offset)
2761             return nullptr;
2762     }
2763
2764     RefPtr<CSSValue> mask;
2765     if (!range.atEnd()) {
2766         mask = consumeWebkitBorderImage(CSSPropertyWebkitBoxReflect, range, context);
2767         if (!mask)
2768             return nullptr;
2769     }
2770     return CSSReflectValue::create(direction.releaseNonNull(), offset.releaseNonNull(), WTFMove(mask));
2771 }
2772
2773 #if ENABLE(CSS_IMAGE_ORIENTATION)
2774 static RefPtr<CSSValue> consumeImageOrientation(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
2775 {
2776     if (range.peek().type() != NumberToken) {
2777         RefPtr<CSSPrimitiveValue> angle = consumeAngle(range, cssParserMode, unitless);
2778         if (angle && angle->doubleValue() == 0)
2779             return angle;
2780     }
2781     return nullptr;
2782 }
2783 #endif
2784
2785 static RefPtr<CSSPrimitiveValue> consumeBackgroundBlendMode(CSSParserTokenRange& range)
2786 {
2787     CSSValueID id = range.peek().id();
2788     if (id == CSSValueNormal || id == CSSValueOverlay || (id >= CSSValueMultiply && id <= CSSValueLuminosity))
2789         return consumeIdent(range);
2790     return nullptr;
2791 }
2792
2793 static RefPtr<CSSPrimitiveValue> consumeBackgroundAttachment(CSSParserTokenRange& range)
2794 {
2795     return consumeIdent<CSSValueScroll, CSSValueFixed, CSSValueLocal>(range);
2796 }
2797
2798 static RefPtr<CSSPrimitiveValue> consumeBackgroundBox(CSSParserTokenRange& range)
2799 {
2800     return consumeIdent<CSSValueBorderBox, CSSValuePaddingBox, CSSValueContentBox, CSSValueWebkitText>(range);
2801 }
2802
2803 static RefPtr<CSSPrimitiveValue> consumeBackgroundComposite(CSSParserTokenRange& range)
2804 {
2805     return consumeIdentRange(range, CSSValueClear, CSSValuePlusLighter);
2806 }
2807
2808 static RefPtr<CSSPrimitiveValue> consumeWebkitMaskSourceType(CSSParserTokenRange& range)
2809 {
2810     return consumeIdent<CSSValueAuto, CSSValueAlpha, CSSValueLuminance>(range);
2811 }
2812
2813 static RefPtr<CSSPrimitiveValue> consumePrefixedBackgroundBox(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& /*context*/)
2814 {
2815     // The values 'border', 'padding' and 'content' are deprecated and do not apply to the version of the property that has the -webkit- prefix removed.
2816     if (RefPtr<CSSPrimitiveValue> value = consumeIdentRange(range, CSSValueBorder, CSSValuePaddingBox))
2817         return value;
2818     if (range.peek().id() == CSSValueWebkitText || ((property == CSSPropertyWebkitBackgroundClip || property == CSSPropertyWebkitMaskClip) && range.peek().id() == CSSValueText))
2819         return consumeIdent(range);
2820     return nullptr;
2821 }
2822
2823 static RefPtr<CSSPrimitiveValue> consumeBackgroundSize(CSSPropertyID property, CSSParserTokenRange& range, CSSParserMode cssParserMode)
2824 {
2825     if (identMatches<CSSValueContain, CSSValueCover>(range.peek().id()))
2826         return consumeIdent(range);
2827
2828     // FIXME: We're allowing the unitless quirk on this property because our
2829     // tests assume that. Other browser engines don't allow it though.
2830     RefPtr<CSSPrimitiveValue> horizontal = consumeIdent<CSSValueAuto>(range);
2831     if (!horizontal)
2832         horizontal = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2833
2834     RefPtr<CSSPrimitiveValue> vertical;
2835     if (!range.atEnd()) {
2836         if (range.peek().id() == CSSValueAuto) // `auto' is the default
2837             range.consumeIncludingWhitespace();
2838         else
2839             vertical = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2840     } else if (!vertical && property == CSSPropertyWebkitBackgroundSize) {
2841         // Legacy syntax: "-webkit-background-size: 10px" is equivalent to "background-size: 10px 10px".
2842         vertical = horizontal;
2843     }
2844     if (!vertical)
2845         return horizontal;
2846     return createPrimitiveValuePair(horizontal.releaseNonNull(), vertical.releaseNonNull(), property == CSSPropertyWebkitBackgroundSize ? Pair::IdenticalValueEncoding::Coalesce : Pair::IdenticalValueEncoding::DoNotCoalesce);
2847 }
2848
2849 #if ENABLE(CSS_GRID_LAYOUT)
2850 static RefPtr<CSSValueList> consumeGridAutoFlow(CSSParserTokenRange& range)
2851 {
2852     RefPtr<CSSPrimitiveValue> rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
2853     RefPtr<CSSPrimitiveValue> denseAlgorithm = consumeIdent<CSSValueDense>(range);
2854     if (!rowOrColumnValue) {
2855         rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
2856         if (!rowOrColumnValue && !denseAlgorithm)
2857             return nullptr;
2858     }
2859     RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
2860     if (rowOrColumnValue)
2861         parsedValues->append(rowOrColumnValue.releaseNonNull());
2862     if (denseAlgorithm)
2863         parsedValues->append(denseAlgorithm.releaseNonNull());
2864     return parsedValues;
2865 }
2866 #endif
2867
2868 static RefPtr<CSSValue> consumeBackgroundComponent(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2869 {
2870     switch (property) {
2871     case CSSPropertyBackgroundClip:
2872         return consumeBackgroundBox(range);
2873     case CSSPropertyBackgroundBlendMode:
2874         return consumeBackgroundBlendMode(range);
2875     case CSSPropertyBackgroundAttachment:
2876         return consumeBackgroundAttachment(range);
2877     case CSSPropertyBackgroundOrigin:
2878         return consumeBackgroundBox(range);
2879     case CSSPropertyWebkitMaskComposite:
2880     case CSSPropertyWebkitBackgroundComposite:
2881         return consumeBackgroundComposite(range);
2882     case CSSPropertyWebkitBackgroundClip:
2883     case CSSPropertyWebkitBackgroundOrigin:
2884     case CSSPropertyWebkitMaskClip:
2885     case CSSPropertyWebkitMaskOrigin:
2886         return consumePrefixedBackgroundBox(property, range, context);
2887     case CSSPropertyBackgroundImage:
2888     case CSSPropertyWebkitMaskImage:
2889         return consumeImageOrNone(range, context);
2890     case CSSPropertyWebkitMaskSourceType:
2891         return consumeWebkitMaskSourceType(range);
2892     case CSSPropertyBackgroundPositionX:
2893     case CSSPropertyWebkitMaskPositionX:
2894         return consumePositionX(range, context.mode);
2895     case CSSPropertyBackgroundPositionY:
2896     case CSSPropertyWebkitMaskPositionY:
2897         return consumePositionY(range, context.mode);
2898     case CSSPropertyBackgroundSize:
2899     case CSSPropertyWebkitBackgroundSize:
2900     case CSSPropertyWebkitMaskSize:
2901         return consumeBackgroundSize(property, range, context.mode);
2902     case CSSPropertyBackgroundColor:
2903         return consumeColor(range, context.mode);
2904     default:
2905         break;
2906     };
2907     return nullptr;
2908 }
2909
2910 static void addBackgroundValue(RefPtr<CSSValue>& list, Ref<CSSValue>&& value)
2911 {
2912     if (list) {
2913         if (!list->isBaseValueList()) {
2914             RefPtr<CSSValue> firstValue = list;
2915             list = CSSValueList::createCommaSeparated();
2916             downcast<CSSValueList>(*list).append(firstValue.releaseNonNull());
2917         }
2918         downcast<CSSValueList>(*list).append(WTFMove(value));
2919     } else {
2920         // To conserve memory we don't actually wrap a single value in a list.
2921         list = WTFMove(value);
2922     }
2923 }
2924
2925 static RefPtr<CSSValue> consumeCommaSeparatedBackgroundComponent(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2926 {
2927     RefPtr<CSSValue> result;
2928     do {
2929         RefPtr<CSSValue> value = consumeBackgroundComponent(property, range, context);
2930         if (!value)
2931             return nullptr;
2932         addBackgroundValue(result, value.releaseNonNull());
2933     } while (consumeCommaIncludingWhitespace(range));
2934     return result;
2935 }
2936
2937 #if ENABLE(CSS_GRID_LAYOUT)
2938 static RefPtr<CSSPrimitiveValue> consumeSelfPositionKeyword(CSSParserTokenRange& range)
2939 {
2940     CSSValueID id = range.peek().id();
2941     if (id == CSSValueStart || id == CSSValueEnd || id == CSSValueCenter
2942         || id == CSSValueSelfStart || id == CSSValueSelfEnd || id == CSSValueFlexStart
2943         || id == CSSValueFlexEnd || id == CSSValueLeft || id == CSSValueRight)
2944         return consumeIdent(range);
2945     return nullptr;
2946 }
2947
2948 static RefPtr<CSSValue> consumeSelfPositionOverflowPosition(CSSParserTokenRange& range)
2949 {
2950     if (identMatches<CSSValueAuto, CSSValueNormal, CSSValueStretch, CSSValueBaseline, CSSValueLastBaseline>(range.peek().id()))
2951         return consumeIdent(range);
2952
2953     RefPtr<CSSPrimitiveValue> overflowPosition = consumeIdent<CSSValueUnsafe, CSSValueSafe>(range);
2954     RefPtr<CSSPrimitiveValue> selfPosition = consumeSelfPositionKeyword(range);
2955     if (!selfPosition)
2956         return nullptr;
2957     if (!overflowPosition)
2958         overflowPosition = consumeIdent<CSSValueUnsafe, CSSValueSafe>(range);
2959     if (overflowPosition)
2960         return createPrimitiveValuePair(selfPosition.releaseNonNull(), overflowPosition.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2961     return selfPosition;
2962 }
2963
2964 static RefPtr<CSSValue> consumeAlignItems(CSSParserTokenRange& range)
2965 {
2966     // align-items property does not allow the 'auto' value.
2967     if (identMatches<CSSValueAuto>(range.peek().id()))
2968         return nullptr;
2969     return consumeSelfPositionOverflowPosition(range);
2970 }
2971
2972 static RefPtr<CSSValue> consumeJustifyItems(CSSParserTokenRange& range)
2973 {
2974     CSSParserTokenRange rangeCopy = range;
2975     RefPtr<CSSPrimitiveValue> legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
2976     RefPtr<CSSPrimitiveValue> positionKeyword = consumeIdent<CSSValueCenter, CSSValueLeft, CSSValueRight>(rangeCopy);
2977     if (!legacy)
2978         legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
2979     if (legacy && positionKeyword) {
2980         range = rangeCopy;
2981         return createPrimitiveValuePair(legacy.releaseNonNull(), positionKeyword.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2982     }
2983     return consumeSelfPositionOverflowPosition(range);
2984 }
2985
2986 static RefPtr<CSSValue> consumeFitContent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2987 {
2988     CSSParserTokenRange rangeCopy = range;
2989     CSSParserTokenRange args = consumeFunction(rangeCopy);
2990     RefPtr<CSSPrimitiveValue> length = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
2991     if (!length || !args.atEnd())
2992         return nullptr;
2993     range = rangeCopy;
2994     RefPtr<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueFitContent);
2995     result->append(length.releaseNonNull());
2996     return result;
2997 }
2998
2999 static RefPtr<CSSPrimitiveValue> consumeCustomIdentForGridLine(CSSParserTokenRange& range)
3000 {
3001     if (range.peek().id() == CSSValueAuto || range.peek().id() == CSSValueSpan)
3002         return nullptr;
3003     return consumeCustomIdent(range);
3004 }
3005
3006 static RefPtr<CSSValue> consumeGridLine(CSSParserTokenRange& range)
3007 {
3008     if (range.peek().id() == CSSValueAuto)
3009         return consumeIdent(range);
3010
3011     RefPtr<CSSPrimitiveValue> spanValue;
3012     RefPtr<CSSPrimitiveValue> gridLineName;
3013     RefPtr<CSSPrimitiveValue> numericValue = consumeInteger(range);
3014     if (numericValue) {
3015         gridLineName = consumeCustomIdentForGridLine(range);
3016         spanValue = consumeIdent<CSSValueSpan>(range);
3017     } else {
3018         spanValue = consumeIdent<CSSValueSpan>(range);
3019         if (spanValue) {
3020             numericValue = consumeInteger(range);
3021             gridLineName = consumeCustomIdentForGridLine(range);
3022             if (!numericValue)
3023                 numericValue = consumeInteger(range);
3024         } else {
3025             gridLineName = consumeCustomIdentForGridLine(range);
3026             if (gridLineName) {
3027                 numericValue = consumeInteger(range);
3028                 spanValue = consumeIdent<CSSValueSpan>(range);
3029                 if (!spanValue && !numericValue)
3030                     return gridLineName;
3031             } else {
3032                 return nullptr;
3033             }
3034         }
3035     }
3036
3037     if (spanValue && !numericValue && !gridLineName)
3038         return nullptr; // "span" keyword alone is invalid.
3039     if (spanValue && numericValue && numericValue->intValue() < 0)
3040         return nullptr; // Negative numbers are not allowed for span.
3041     if (numericValue && numericValue->intValue() == 0)
3042         return nullptr; // An <integer> value of zero makes the declaration invalid.
3043
3044     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3045     if (spanValue)
3046         values->append(spanValue.releaseNonNull());
3047     if (numericValue)
3048         values->append(numericValue.releaseNonNull());
3049     if (gridLineName)
3050         values->append(gridLineName.releaseNonNull());
3051     ASSERT(values->length());
3052     return values;
3053 }
3054
3055 static bool isGridTrackFixedSized(const CSSPrimitiveValue& primitiveValue)
3056 {
3057     CSSValueID valueID = primitiveValue.valueID();
3058     if (valueID == CSSValueWebkitMinContent || valueID == CSSValueWebkitMaxContent || valueID == CSSValueAuto || primitiveValue.isFlex())
3059         return false;
3060
3061     return true;
3062 }
3063
3064 static bool isGridTrackFixedSized(const CSSValue& value)
3065 {
3066     if (value.isPrimitiveValue())
3067         return isGridTrackFixedSized(downcast<CSSPrimitiveValue>(value));
3068
3069     ASSERT(value.isFunctionValue());
3070     auto& function = downcast<CSSFunctionValue>(value);
3071     if (function.name() == CSSValueFitContent || function.arguments() == nullptr || function.arguments()->length() < 2)
3072         return false;
3073
3074     CSSValue* minPrimitiveValue = downcast<CSSPrimitiveValue>(function.arguments()->item(0));
3075     CSSValue* maxPrimitiveValue = downcast<CSSPrimitiveValue>(function.arguments()->item(1));
3076     return isGridTrackFixedSized(*minPrimitiveValue) || isGridTrackFixedSized(*maxPrimitiveValue);
3077 }
3078
3079 static Vector<String> parseGridTemplateAreasColumnNames(const String& gridRowNames)
3080 {
3081     ASSERT(!gridRowNames.isEmpty());
3082     Vector<String> columnNames;
3083     // Using StringImpl to avoid checks and indirection in every call to String::operator[].
3084     StringImpl& text = *gridRowNames.impl();
3085
3086     StringBuilder areaName;
3087     for (unsigned i = 0; i < text.length(); ++i) {
3088         if (isCSSSpace(text[i])) {
3089             if (!areaName.isEmpty()) {
3090                 columnNames.append(areaName.toString());
3091                 areaName.clear();
3092             }
3093             continue;
3094         }
3095         if (text[i] == '.') {
3096             if (areaName == ".")
3097                 continue;
3098             if (!areaName.isEmpty()) {
3099                 columnNames.append(areaName.toString());
3100                 areaName.clear();
3101             }
3102         } else {
3103             if (!isNameCodePoint(text[i]))
3104                 return Vector<String>();
3105             if (areaName == ".") {
3106                 columnNames.append(areaName.toString());
3107                 areaName.clear();
3108             }
3109         }
3110
3111         areaName.append(text[i]);
3112     }
3113
3114     if (!areaName.isEmpty())
3115         columnNames.append(areaName.toString());
3116
3117     return columnNames;
3118 }
3119
3120 static bool parseGridTemplateAreasRow(const String& gridRowNames, NamedGridAreaMap& gridAreaMap, const size_t rowCount, size_t& columnCount)
3121 {
3122     if (gridRowNames.isEmpty() || gridRowNames.containsOnlyWhitespace())
3123         return false;
3124
3125     Vector<String> columnNames = parseGridTemplateAreasColumnNames(gridRowNames);
3126     if (rowCount == 0) {
3127         columnCount = columnNames.size();
3128         if (columnCount == 0)
3129             return false;
3130     } else if (columnCount != columnNames.size()) {
3131         // The declaration is invalid if all the rows don't have the number of columns.
3132         return false;
3133     }
3134
3135     for (size_t currentColumn = 0; currentColumn < columnCount; ++currentColumn) {
3136         const String& gridAreaName = columnNames[currentColumn];
3137
3138         // Unamed areas are always valid (we consider them to be 1x1).
3139         if (gridAreaName == ".")
3140             continue;
3141
3142         size_t lookAheadColumn = currentColumn + 1;
3143         while (lookAheadColumn < columnCount && columnNames[lookAheadColumn] == gridAreaName)
3144             lookAheadColumn++;
3145
3146         NamedGridAreaMap::iterator gridAreaIt = gridAreaMap.find(gridAreaName);
3147         if (gridAreaIt == gridAreaMap.end()) {
3148             gridAreaMap.add(gridAreaName, GridArea(GridSpan::translatedDefiniteGridSpan(rowCount, rowCount + 1), GridSpan::translatedDefiniteGridSpan(currentColumn, lookAheadColumn)));
3149         } else {
3150             GridArea& gridArea = gridAreaIt->value;
3151
3152             // The following checks test that the grid area is a single filled-in rectangle.
3153             // 1. The new row is adjacent to the previously parsed row.
3154             if (rowCount != gridArea.rows.endLine())
3155                 return false;
3156
3157             // 2. The new area starts at the same position as the previously parsed area.
3158             if (currentColumn != gridArea.columns.startLine())
3159                 return false;
3160
3161             // 3. The new area ends at the same position as the previously parsed area.
3162             if (lookAheadColumn != gridArea.columns.endLine())
3163                 return false;
3164
3165             gridArea.rows = GridSpan::translatedDefiniteGridSpan(gridArea.rows.startLine(), gridArea.rows.endLine() + 1);
3166         }
3167         currentColumn = lookAheadColumn - 1;
3168     }
3169
3170     return true;
3171 }
3172
3173 static RefPtr<CSSPrimitiveValue> consumeGridBreadth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3174 {
3175     const CSSParserToken& token = range.peek();
3176     if (identMatches<CSSValueWebkitMinContent, CSSValueWebkitMaxContent, CSSValueAuto>(token.id()))
3177         return consumeIdent(range);
3178     if (token.type() == DimensionToken && token.unitType() == CSSPrimitiveValue::UnitTypes::CSS_FR) {
3179         if (range.peek().numericValue() < 0)
3180             return nullptr;
3181         return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitTypes::CSS_FR);
3182     }
3183     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3184 }
3185
3186 static RefPtr<CSSValue> consumeGridTrackSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3187 {
3188     const CSSParserToken& token = range.peek();
3189     if (identMatches<CSSValueAuto>(token.id()))
3190         return consumeIdent(range);
3191
3192     if (token.functionId() == CSSValueMinmax) {
3193         CSSParserTokenRange rangeCopy = range;
3194         CSSParserTokenRange args = consumeFunction(rangeCopy);
3195         RefPtr<CSSPrimitiveValue> minTrackBreadth = consumeGridBreadth(args, cssParserMode);
3196         if (!minTrackBreadth || minTrackBreadth->isFlex() || !consumeCommaIncludingWhitespace(args))
3197             return nullptr;
3198         RefPtr<CSSPrimitiveValue> maxTrackBreadth = consumeGridBreadth(args, cssParserMode);
3199         if (!maxTrackBreadth || !args.atEnd())
3200             return nullptr;
3201         range = rangeCopy;
3202         RefPtr<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueMinmax);
3203         result->append(minTrackBreadth.releaseNonNull());
3204         result->append(maxTrackBreadth.releaseNonNull());
3205         return result;
3206     }
3207
3208     if (token.functionId() == CSSValueFitContent)
3209         return consumeFitContent(range, cssParserMode);
3210
3211     return consumeGridBreadth(range, cssParserMode);
3212 }
3213
3214 // Appends to the passed in CSSGridLineNamesValue if any, otherwise creates a new one.
3215 static RefPtr<CSSGridLineNamesValue> consumeGridLineNames(CSSParserTokenRange& range, CSSGridLineNamesValue* lineNames = nullptr)
3216 {
3217     CSSParserTokenRange rangeCopy = range;
3218     if (rangeCopy.consumeIncludingWhitespace().type() != LeftBracketToken)
3219         return nullptr;
3220     
3221     RefPtr<CSSGridLineNamesValue> result = lineNames;
3222     if (!result)
3223         result = CSSGridLineNamesValue::create();
3224     while (RefPtr<CSSPrimitiveValue> lineName = consumeCustomIdentForGridLine(rangeCopy))
3225         result->append(lineName.releaseNonNull());
3226     if (rangeCopy.consumeIncludingWhitespace().type() != RightBracketToken)
3227         return nullptr;
3228     range = rangeCopy;
3229     return result;
3230 }
3231
3232 static bool consumeGridTrackRepeatFunction(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSValueList& list, bool& isAutoRepeat, bool& allTracksAreFixedSized)
3233 {
3234     CSSParserTokenRange args = consumeFunction(range);
3235     // The number of repetitions for <auto-repeat> is not important at parsing level
3236     // because it will be computed later, let's set it to 1.
3237     size_t repetitions = 1;
3238     isAutoRepeat = identMatches<CSSValueAutoFill, CSSValueAutoFit>(args.peek().id());
3239     RefPtr<CSSValueList> repeatedValues;
3240     if (isAutoRepeat)
3241         repeatedValues = CSSGridAutoRepeatValue::create(args.consumeIncludingWhitespace().id());
3242     else {
3243         // FIXME: a consumeIntegerRaw would be more efficient here.
3244         RefPtr<CSSPrimitiveValue> repetition = consumePositiveInteger(args);
3245         if (!repetition)
3246             return false;
3247         repetitions = clampTo<size_t>(repetition->doubleValue(), 0, kGridMaxTracks);
3248         repeatedValues = CSSValueList::createSpaceSeparated();
3249     }
3250     if (!consumeCommaIncludingWhitespace(args))
3251         return false;
3252     RefPtr<CSSGridLineNamesValue> lineNames = consumeGridLineNames(args);
3253     if (lineNames)
3254         repeatedValues->append(lineNames.releaseNonNull());
3255
3256     size_t numberOfTracks = 0;
3257     while (!args.atEnd()) {
3258         RefPtr<CSSValue> trackSize = consumeGridTrackSize(args, cssParserMode);
3259         if (!trackSize)
3260             return false;
3261         if (allTracksAreFixedSized)
3262             allTracksAreFixedSized = isGridTrackFixedSized(*trackSize);
3263         repeatedValues->append(trackSize.releaseNonNull());
3264         ++numberOfTracks;
3265         lineNames = consumeGridLineNames(args);
3266         if (lineNames)
3267             repeatedValues->append(lineNames.releaseNonNull());
3268     }
3269     // We should have found at least one <track-size> or else it is not a valid <track-list>.
3270     if (!numberOfTracks)
3271         return false;
3272
3273     if (isAutoRepeat)
3274         list.append(repeatedValues.releaseNonNull());
3275     else {
3276         // We clamp the repetitions to a multiple of the repeat() track list's size, while staying below the max grid size.
3277         repetitions = std::min(repetitions, kGridMaxTracks / numberOfTracks);
3278         for (size_t i = 0; i < repetitions; ++i) {
3279             for (size_t j = 0; j < repeatedValues->length(); ++j)
3280                 list.append(*repeatedValues->itemWithoutBoundsCheck(j));
3281         }
3282     }
3283     return true;
3284 }
3285
3286 enum TrackListType { GridTemplate, GridTemplateNoRepeat, GridAuto };
3287
3288 static RefPtr<CSSValue> consumeGridTrackList(CSSParserTokenRange& range, CSSParserMode cssParserMode, TrackListType trackListType)
3289 {
3290     bool allowGridLineNames = trackListType != GridAuto;
3291     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3292     RefPtr<CSSGridLineNamesValue> lineNames = consumeGridLineNames(range);
3293     if (lineNames) {
3294         if (!allowGridLineNames)
3295             return nullptr;
3296         values->append(lineNames.releaseNonNull());
3297     }
3298     
3299     bool allowRepeat = trackListType == GridTemplate;
3300     bool seenAutoRepeat = false;
3301     bool allTracksAreFixedSized = true;
3302     do {
3303         bool isAutoRepeat;
3304         if (range.peek().functionId() == CSSValueRepeat) {
3305             if (!allowRepeat)
3306                 return nullptr;
3307             if (!consumeGridTrackRepeatFunction(range, cssParserMode, *values, isAutoRepeat, allTracksAreFixedSized))
3308                 return nullptr;
3309             if (isAutoRepeat && seenAutoRepeat)
3310                 return nullptr;
3311             seenAutoRepeat = seenAutoRepeat || isAutoRepeat;
3312         } else if (RefPtr<CSSValue> value = consumeGridTrackSize(range, cssParserMode)) {
3313             if (allTracksAreFixedSized)
3314                 allTracksAreFixedSized = isGridTrackFixedSized(*value);
3315             values->append(value.releaseNonNull());
3316         } else {
3317             return nullptr;
3318         }
3319         if (seenAutoRepeat && !allTracksAreFixedSized)
3320             return nullptr;
3321         lineNames = consumeGridLineNames(range);
3322         if (lineNames) {
3323             if (!allowGridLineNames)
3324                 return nullptr;
3325             values->append(lineNames.releaseNonNull());
3326         }
3327     } while (!range.atEnd() && range.peek().type() != DelimiterToken);
3328     return values;
3329 }
3330
3331 static RefPtr<CSSValue> consumeGridTemplatesRowsOrColumns(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3332 {
3333     if (range.peek().id() == CSSValueNone)
3334         return consumeIdent(range);
3335     return consumeGridTrackList(range, cssParserMode, GridTemplate);
3336 }
3337
3338 static RefPtr<CSSValue> consumeGridTemplateAreas(CSSParserTokenRange& range)
3339 {
3340     if (range.peek().id() == CSSValueNone)
3341         return consumeIdent(range);
3342
3343     NamedGridAreaMap gridAreaMap;
3344     size_t rowCount = 0;
3345     size_t columnCount = 0;
3346
3347     while (range.peek().type() == StringToken) {
3348         if (!parseGridTemplateAreasRow(range.consumeIncludingWhitespace().value().toString(), gridAreaMap, rowCount, columnCount))
3349             return nullptr;
3350         ++rowCount;
3351     }
3352
3353     if (rowCount == 0)
3354         return nullptr;
3355     ASSERT(columnCount);
3356     return CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount);
3357 }
3358 #endif
3359
3360 #if ENABLE(CSS_REGIONS)
3361 static RefPtr<CSSValue> consumeFlowProperty(CSSParserTokenRange& range)
3362 {
3363     RefPtr<CSSValue> result;
3364     if (range.peek().id() == CSSValueNone)
3365         result = consumeIdent(range);
3366     else
3367         result = consumeCustomIdent(range);
3368     return result;
3369 }
3370 #endif
3371
3372 static RefPtr<CSSValue> consumeLineBoxContain(CSSParserTokenRange& range)
3373 {
3374     if (range.peek().id() == CSSValueNone)
3375         return consumeIdent(range);
3376
3377     LineBoxContain lineBoxContain = LineBoxContainNone;
3378     
3379     while (range.peek().type() == IdentToken) {
3380         auto id = range.peek().id();
3381         if (id == CSSValueBlock) {
3382             if (lineBoxContain & LineBoxContainBlock)
3383                 return nullptr;
3384             lineBoxContain |= LineBoxContainBlock;
3385         } else if (id == CSSValueInline) {
3386             if (lineBoxContain & LineBoxContainInline)
3387                 return nullptr;
3388             lineBoxContain |= LineBoxContainInline;
3389         } else if (id == CSSValueFont) {
3390             if (lineBoxContain & LineBoxContainFont)
3391                 return nullptr;
3392             lineBoxContain |= LineBoxContainFont;
3393         } else if (id == CSSValueGlyphs) {
3394             if (lineBoxContain & LineBoxContainGlyphs)
3395                 return nullptr;
3396             lineBoxContain |= LineBoxContainGlyphs;
3397         } else if (id == CSSValueReplaced) {
3398             if (lineBoxContain & LineBoxContainReplaced)
3399                 return nullptr;
3400             lineBoxContain |= LineBoxContainReplaced;
3401         } else if (id == CSSValueInlineBox) {
3402             if (lineBoxContain & LineBoxContainInlineBox)
3403                 return nullptr;
3404             lineBoxContain |= LineBoxContainInlineBox;
3405         } else if (id == CSSValueInitialLetter) {
3406             if (lineBoxContain & LineBoxContainInitialLetter)
3407                 return nullptr;
3408             lineBoxContain |= LineBoxContainInitialLetter;
3409         } else
3410             return nullptr;
3411         range.consumeIncludingWhitespace();
3412     }
3413     
3414     if (!lineBoxContain)
3415         return nullptr;
3416     
3417     return CSSLineBoxContainValue::create(lineBoxContain);
3418 }
3419
3420 static RefPtr<CSSValue> consumeLineGrid(CSSParserTokenRange& range)
3421 {
3422     if (range.peek().id() == CSSValueNone)
3423         return consumeIdent(range);
3424     return consumeCustomIdent(range);
3425 }
3426
3427 static RefPtr<CSSValue> consumeInitialLetter(CSSParserTokenRange& range)
3428 {
3429     RefPtr<CSSValue> ident = consumeIdent<CSSValueNormal>(range);
3430     if (ident)
3431         return ident;
3432     
3433     RefPtr<CSSPrimitiveValue> height = consumeNumber(range, ValueRangeNonNegative);
3434     if (!height)
3435         return nullptr;
3436     
3437     RefPtr<CSSPrimitiveValue> position;
3438     if (!range.atEnd()) {
3439         position = consumeNumber(range, ValueRangeNonNegative);
3440         if (!position || !range.atEnd())
3441             return nullptr;
3442     } else
3443         position = height.copyRef();
3444     
3445     return createPrimitiveValuePair(position.releaseNonNull(), WTFMove(height));
3446 }
3447
3448 static RefPtr<CSSValue> consumeHangingPunctuation(CSSParserTokenRange& range)
3449 {
3450     if (range.peek().id() == CSSValueNone)
3451         return consumeIdent(range);
3452     
3453     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3454     std::bitset<numCSSValueKeywords> seenValues;
3455
3456     bool seenForceEnd = false;
3457     bool seenAllowEnd = false;
3458     bool seenFirst = false;
3459     bool seenLast = false;
3460
3461     while (!range.atEnd()) {
3462         CSSValueID valueID = range.peek().id();
3463         if ((valueID == CSSValueFirst && seenFirst)
3464             || (valueID == CSSValueLast && seenLast)
3465             || (valueID == CSSValueAllowEnd && (seenAllowEnd || seenForceEnd))
3466             || (valueID == CSSValueForceEnd && (seenAllowEnd || seenForceEnd)))
3467             return nullptr;
3468         RefPtr<CSSValue> ident = consumeIdent<CSSValueAllowEnd, CSSValueForceEnd, CSSValueFirst, CSSValueLast>(range);
3469         if (!ident)
3470             return nullptr;
3471         switch (valueID) {
3472         case CSSValueAllowEnd:
3473             seenAllowEnd = true;
3474             break;
3475         case CSSValueForceEnd:
3476             seenForceEnd = true;
3477             break;
3478         case CSSValueFirst:
3479             seenFirst = true;
3480             break;
3481         case CSSValueLast:
3482             seenLast = true;
3483             break;
3484         default:
3485             break;
3486         }
3487         list->append(ident.releaseNonNull());
3488     }
3489     
3490     return list->length() ? list : nullptr;
3491 }
3492
3493 static RefPtr<CSSValue> consumeWebkitMarqueeIncrement(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3494 {
3495     if (range.peek().type() == IdentToken)
3496         return consumeIdent<CSSValueSmall, CSSValueMedium, CSSValueLarge>(range);
3497     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
3498 }
3499
3500 static RefPtr<CSSValue> consumeWebkitMarqueeRepetition(CSSParserTokenRange& range)
3501 {
3502     if (range.peek().type() == IdentToken)
3503         return consumeIdent<CSSValueInfinite>(range);
3504     return consumeNumber(range, ValueRangeNonNegative);
3505 }
3506
3507 static RefPtr<CSSValue> consumeWebkitMarqueeSpeed(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3508 {
3509     if (range.peek().type() == IdentToken)
3510         return consumeIdent<CSSValueSlow, CSSValueNormal, CSSValueFast>(range);
3511     return consumeTime(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3512 }
3513
3514 static RefPtr<CSSValue> consumeAlt(CSSParserTokenRange& range, const CSSParserContext& context)
3515 {
3516     if (range.peek().type() == StringToken)
3517         return consumeString(range);
3518     
3519     if (range.peek().functionId() != CSSValueAttr)
3520         return nullptr;
3521     
3522     return consumeAttr(consumeFunction(range), context);
3523 }
3524
3525 static RefPtr<CSSValue> consumeWebkitAspectRatio(CSSParserTokenRange& range)
3526 {
3527     if (range.peek().type() == IdentToken)
3528         return consumeIdent<CSSValueAuto, CSSValueFromDimensions, CSSValueFromIntrinsic>(range);
3529     
3530     RefPtr<CSSPrimitiveValue> leftValue = consumeNumber(range, ValueRangeNonNegative);
3531     if (!leftValue || range.atEnd() || !consumeSlashIncludingWhitespace(range))
3532         return nullptr;
3533     RefPtr<CSSPrimitiveValue> rightValue = consumeNumber(range, ValueRangeNonNegative);
3534     if (!rightValue)
3535         return nullptr;
3536     
3537     return CSSAspectRatioValue::create(leftValue->floatValue(), rightValue->floatValue());
3538 }
3539
3540 static RefPtr<CSSValue> consumeTextEmphasisPosition(CSSParserTokenRange& range)
3541 {
3542     bool foundOverOrUnder = false;
3543     CSSValueID overUnderValueID = CSSValueOver;
3544     bool foundLeftOrRight = false;
3545     CSSValueID leftRightValueID = CSSValueRight;
3546     while (!range.atEnd()) {
3547         switch (range.peek().id()) {
3548         case CSSValueOver:
3549             if (foundOverOrUnder)
3550                 return nullptr;
3551             foundOverOrUnder = true;
3552             overUnderValueID = CSSValueOver;
3553             break;
3554         case CSSValueUnder:
3555             if (foundOverOrUnder)
3556                 return nullptr;
3557             foundOverOrUnder = true;
3558             overUnderValueID = CSSValueUnder;
3559             break;
3560         case CSSValueLeft:
3561             if (foundLeftOrRight)
3562                 return nullptr;
3563             foundLeftOrRight = true;
3564             leftRightValueID = CSSValueLeft;
3565             break;
3566         case CSSValueRight:
3567             if (foundLeftOrRight)
3568                 return nullptr;
3569             foundLeftOrRight = true;
3570             leftRightValueID = CSSValueRight;
3571             break;
3572         default:
3573             return nullptr;
3574         }
3575         
3576         range.consumeIncludingWhitespace();
3577     }
3578     if (!foundOverOrUnder)
3579         return nullptr;
3580     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3581     list->append(CSSValuePool::singleton().createIdentifierValue(overUnderValueID));
3582     if (foundLeftOrRight)
3583         list->append(CSSValuePool::singleton().createIdentifierValue(leftRightValueID));
3584     return list;
3585 }
3586     
3587 RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, CSSPropertyID currentShorthand)
3588 {
3589     if (CSSParserFastPaths::isKeywordPropertyID(property)) {
3590         if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(property, m_range.peek().id(), m_context.mode))
3591             return nullptr;
3592         return consumeIdent(m_range);
3593     }
3594     switch (property) {
3595     case CSSPropertyWillChange:
3596         return consumeWillChange(m_range);
3597     case CSSPropertyPage:
3598         return consumePage(m_range);
3599     case CSSPropertyQuotes:
3600         return consumeQuotes(m_range);
3601     case CSSPropertyFontVariantCaps:
3602         return consumeFontVariantCaps(m_range);
3603     case CSSPropertyFontVariantLigatures:
3604         return consumeFontVariantLigatures(m_range);
3605     case CSSPropertyFontVariantNumeric:
3606         return consumeFontVariantNumeric(m_range);
3607     case CSSPropertyFontVariantEastAsian:
3608         return consumeFontVariantEastAsian(m_range);
3609     case CSSPropertyFontFeatureSettings:
3610         return consumeFontFeatureSettings(m_range);
3611     case CSSPropertyFontFamily:
3612         return consumeFontFamily(m_range);
3613     case CSSPropertyFontWeight:
3614         return consumeFontWeight(m_range);
3615     case CSSPropertyFontSynthesis:
3616         return consumeFontSynthesis(m_range);
3617 #if ENABLE(VARIATION_FONTS)
3618     case CSSPropertyFontVariationSettings:
3619         if (m_context.variationFontsEnabled)
3620             return consumeFontVariationSettings(m_range);
3621         return nullptr;
3622 #endif
3623     case CSSPropertyLetterSpacing:
3624         return consumeLetterSpacing(m_range, m_context.mode);
3625     case CSSPropertyWordSpacing:
3626         return consumeWordSpacing(m_range, m_context.mode);
3627     case CSSPropertyTabSize:
3628         return consumeTabSize(m_range, m_context.mode);
3629 #if ENABLE(TEXT_AUTOSIZING)
3630     case CSSPropertyWebkitTextSizeAdjust:
3631         // FIXME: Support toggling the validation of this property via a runtime setting that is independent of
3632         // whether isTextAutosizingEnabled() is true. We want to enable this property on iOS, when simulating
3633         // a iOS device in Safari's responsive design mode and when optionally enabled in DRT/WTR. Otherwise,
3634         // this property should be disabled by default.
3635 #if !PLATFORM(IOS)
3636         if (!m_context.textAutosizingEnabled)
3637             return nullptr;
3638 #endif
3639         return consumeTextSizeAdjust(m_range, m_context.mode);
3640 #endif
3641     case CSSPropertyFontSize:
3642         return consumeFontSize(m_range, m_context.mode, UnitlessQuirk::Allow);
3643     case CSSPropertyLineHeight:
3644         return consumeLineHeight(m_range, m_context.mode);
3645     case CSSPropertyWebkitBorderHorizontalSpacing:
3646     case CSSPropertyWebkitBorderVerticalSpacing:
3647         return consumeLength(m_range, m_context.mode, ValueRangeNonNegative);
3648     case CSSPropertyCounterIncrement:
3649     case CSSPropertyCounterReset:
3650         return consumeCounter(m_range, property == CSSPropertyCounterIncrement ? 1 : 0);
3651     case CSSPropertySize:
3652         return consumeSize(m_range, m_context.mode);
3653     case CSSPropertyTextIndent:
3654         return consumeTextIndent(m_range, m_context.mode);
3655     case CSSPropertyMaxWidth:
3656     case CSSPropertyMaxHeight:
3657         return consumeMaxWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
3658     case CSSPropertyWebkitMaxLogicalWidth:
3659     case CSSPropertyWebkitMaxLogicalHeight:
3660         return consumeMaxWidthOrHeight(m_range, m_context);
3661     case CSSPropertyMinWidth:
3662     case CSSPropertyMinHeight:
3663     case CSSPropertyWidth:
3664     case CSSPropertyHeight:
3665         return consumeWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
3666     case CSSPropertyWebkitMinLogicalWidth:
3667     case CSSPropertyWebkitMinLogicalHeight:
3668     case CSSPropertyWebkitLogicalWidth:
3669     case CSSPropertyWebkitLogicalHeight:
3670         return consumeWidthOrHeight(m_range, m_context);
3671     case CSSPropertyMarginTop:
3672     case CSSPropertyMarginRight:
3673     case CSSPropertyMarginBottom:
3674     case CSSPropertyMarginLeft:
3675     case CSSPropertyBottom:
3676     case CSSPropertyLeft:
3677     case CSSPropertyRight:
3678     case CSSPropertyTop:
3679         return consumeMarginOrOffset(m_range, m_context.mode, UnitlessQuirk::Allow);
3680     case CSSPropertyWebkitMarginStart:
3681     case CSSPropertyWebkitMarginEnd:
3682     case CSSPropertyWebkitMarginBefore:
3683     case CSSPropertyWebkitMarginAfter:
3684         return consumeMarginOrOffset(m_range, m_context.mode, UnitlessQuirk::Forbid);
3685     case CSSPropertyPaddingTop:
3686     case CSSPropertyPaddingRight:
3687     case CSSPropertyPaddingBottom:
3688     case CSSPropertyPaddingLeft:
3689         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3690     case CSSPropertyWebkitPaddingStart:
3691     case CSSPropertyWebkitPaddingEnd:
3692     case CSSPropertyWebkitPaddingBefore:
3693     case CSSPropertyWebkitPaddingAfter:
3694         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
3695     case CSSPropertyClip:
3696         return consumeClip(m_range, m_context.mode);
3697 #if ENABLE(TOUCH_EVENTS)
3698     case CSSPropertyTouchAction:
3699         return consumeTouchAction(m_range);
3700 #endif
3701     case CSSPropertyObjectPosition:
3702         return consumePosition(m_range, m_context.mode, UnitlessQuirk::Forbid);
3703     case CSSPropertyWebkitLineClamp:
3704         return consumeLineClamp(m_range);
3705     case CSSPropertyWebkitFontSizeDelta:
3706         return consumeLength(m_range, m_context.mode, ValueRangeAll, UnitlessQuirk::Allow);
3707     case CSSPropertyWebkitHyphenateCharacter:
3708     case CSSPropertyWebkitLocale:
3709         return consumeLocale(m_range);
3710     case CSSPropertyWebkitHyphenateLimitBefore:
3711     case CSSPropertyWebkitHyphenateLimitAfter:
3712         return consumeHyphenateLimit(m_range, CSSValueAuto);
3713     case CSSPropertyWebkitHyphenateLimitLines:
3714         return consumeHyphenateLimit(m_range, CSSValueNoLimit);
3715     case CSSPropertyColumnWidth:
3716         return consumeColumnWidth(m_range);
3717     case CSSPropertyColumnCount:
3718         return consumeColumnCount(m_range);
3719     case CSSPropertyColumnGap:
3720         return consumeColumnGap(m_range, m_context.mode);
3721     case CSSPropertyColumnSpan:
3722         return consumeColumnSpan(m_range);
3723     case CSSPropertyZoom:
3724         return consumeZoom(m_range, m_context);
3725     case CSSPropertyAnimationDelay:
3726     case CSSPropertyTransitionDelay:
3727     case CSSPropertyAnimationDirection:
3728     case CSSPropertyAnimationDuration:
3729     case CSSPropertyTransitionDuration:
3730     case CSSPropertyAnimationFillMode:
3731     case CSSPropertyAnimationIterationCount:
3732     case CSSPropertyAnimationName:
3733     case CSSPropertyAnimationPlayState:
3734     case CSSPropertyTransitionProperty:
3735     case CSSPropertyAnimationTimingFunction:
3736     case CSSPropertyTransitionTimingFunction:
3737 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
3738     case CSSPropertyWebkitAnimationTrigger:
3739 #endif
3740         return consumeAnimationPropertyList(property, m_range, m_context);
3741 #if ENABLE(CSS_GRID_LAYOUT)
3742     case CSSPropertyGridColumnGap:
3743     case CSSPropertyGridRowGap:
3744         return consumeLength(m_range, m_context.mode, ValueRangeNonNegative);
3745 #endif
3746     case CSSPropertyShapeMargin:
3747         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative);
3748     case CSSPropertyShapeImageThreshold:
3749         return consumeNumber(m_range, ValueRangeAll);
3750     case CSSPropertyWebkitBoxOrdinalGroup:
3751     case CSSPropertyOrphans:
3752     case CSSPropertyWidows:
3753         return consumePositiveInteger(m_range);
3754     case CSSPropertyWebkitTextDecorationColor:
3755         return consumeColor(m_range, m_context.mode);
3756     case CSSPropertyWebkitTextDecorationSkip:
3757         return consumeTextDecorationSkip(m_range);
3758     case CSSPropertyWebkitTextStrokeWidth:
3759         return consumeTextStrokeWidth(m_range, m_context.mode);
3760     case CSSPropertyWebkitTextFillColor:
3761 #if ENABLE(TOUCH_EVENTS)
3762     case CSSPropertyWebkitTapHighlightColor:
3763 #endif
3764     case CSSPropertyWebkitTextEmphasisColor:
3765     case CSSPropertyWebkitBorderStartColor:
3766     case CSSPropertyWebkitBorderEndColor:
3767     case CSSPropertyWebkitBorderBeforeColor:
3768     case CSSPropertyWebkitBorderAfterColor:
3769     case CSSPropertyWebkitTextStrokeColor:
3770     case CSSPropertyStopColor:
3771     case CSSPropertyFloodColor:
3772     case CSSPropertyLightingColor:
3773     case CSSPropertyColumnRuleColor:
3774         return consumeColor(m_range, m_context.mode);
3775     case CSSPropertyColor:
3776     case CSSPropertyBackgroundColor:
3777         return consumeColor(m_range, m_context.mode, inQuirksMode());
3778     case CSSPropertyWebkitBorderStartWidth:
3779     case CSSPropertyWebkitBorderEndWidth:
3780     case CSSPropertyWebkitBorderBeforeWidth:
3781     case CSSPropertyWebkitBorderAfterWidth:
3782         return consumeBorderWidth(m_range, m_context.mode, UnitlessQuirk::Forbid);
3783     case CSSPropertyBorderBottomColor:
3784     case CSSPropertyBorderLeftColor:
3785     case CSSPropertyBorderRightColor:
3786     case CSSPropertyBorderTopColor: {
3787         bool allowQuirkyColors = inQuirksMode()
3788             && (currentShorthand == CSSPropertyInvalid || currentShorthand == CSSPropertyBorderColor);
3789         return consumeColor(m_range, m_context.mode, allowQuirkyColors);
3790     }
3791     case CSSPropertyBorderBottomWidth:
3792     case CSSPropertyBorderLeftWidth:
3793     case CSSPropertyBorderRightWidth:
3794     case CSSPropertyBorderTopWidth: {
3795         bool allowQuirkyLengths = inQuirksMode()
3796             && (currentShorthand == CSSPropertyInvalid || currentShorthand == CSSPropertyBorderWidth);
3797         UnitlessQuirk unitless = allowQuirkyLengths ? UnitlessQuirk::Allow : UnitlessQuirk::Forbid;
3798         return consumeBorderWidth(m_range, m_context.mode, unitless);
3799     }
3800     case CSSPropertyZIndex:
3801         return consumeZIndex(m_range);
3802     case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3
3803     case CSSPropertyBoxShadow:
3804     case CSSPropertyWebkitBoxShadow:
3805     case CSSPropertyWebkitSvgShadow:
3806         return consumeShadow(m_range, m_context.mode, property == CSSPropertyBoxShadow || property == CSSPropertyWebkitBoxShadow);
3807     case CSSPropertyFilter:
3808 #if ENABLE(FILTERS_LEVEL_2)
3809     case CSSPropertyWebkitBackdropFilter:
3810 #endif
3811         return consumeFilter(m_range, m_context);
3812     case CSSPropertyTextDecoration:
3813     case CSSPropertyWebkitTextDecorationsInEffect:
3814     case CSSPropertyWebkitTextDecorationLine:
3815         return consumeTextDecorationLine(m_range);
3816     case CSSPropertyWebkitTextEmphasisStyle:
3817         return consumeTextEmphasisStyle(m_range);
3818     case CSSPropertyOutlineColor:
3819         return consumeOutlineColor(m_range, m_context.mode);
3820     case CSSPropertyOutlineOffset:
3821         return consumeLength(m_range, m_context.mode, ValueRangeAll);
3822     case CSSPropertyOutlineWidth:
3823         return consumeLineWidth(m_range, m_context.mode, UnitlessQuirk::Forbid);
3824     case CSSPropertyTransform:
3825         return consumeTransform(m_range, m_context.mode);
3826     case CSSPropertyTransformOriginX:
3827     case CSSPropertyPerspectiveOriginX:
3828         return consumePositionX(m_range, m_context.mode);
3829     case CSSPropertyTransformOriginY:
3830     case CSSPropertyPerspectiveOriginY:
3831         return consumePositionY(m_range, m_context.mode);
3832     case CSSPropertyTransformOriginZ:
3833         return consumeLength(m_range, m_context.mode, ValueRangeAll);
3834     case CSSPropertyFill:
3835     case CSSPropertyStroke:
3836         return consumePaintStroke(m_range, m_context.mode);
3837     case CSSPropertyPaintOrder:
3838         return consumePaintOrder(m_range);
3839     case CSSPropertyMarkerStart:
3840     case CSSPropertyMarkerMid:
3841     case CSSPropertyMarkerEnd:
3842     case CSSPropertyClipPath:
3843     case CSSPropertyMask:
3844         return consumeNoneOrURI(m_range);
3845     case CSSPropertyFlexBasis:
3846         return consumeFlexBasis(m_range, m_context.mode);
3847     case CSSPropertyFlexGrow:
3848     case CSSPropertyFlexShrink:
3849         return consumeNumber(m_range, ValueRangeNonNegative);
3850     case CSSPropertyStrokeDasharray:
3851         return consumeStrokeDasharray(m_range);
3852     case CSSPropertyColumnRuleWidth:
3853         return consumeColumnRuleWidth(m_range, m_context.mode);
3854     case CSSPropertyStrokeOpacity:
3855     case CSSPropertyFillOpacity:
3856     case CSSPropertyStopOpacity:
3857     case CSSPropertyFloodOpacity:
3858     case CSSPropertyOpacity:
3859     case CSSPropertyWebkitBoxFlex:
3860         return consumeNumber(m_range, ValueRangeAll);
3861     case CSSPropertyBaselineShift:
3862         return consumeBaselineShift(m_range);
3863     case CSSPropertyStrokeMiterlimit:
3864         return consumeNumber(m_range, ValueRangeNonNegative);
3865     case CSSPropertyStrokeWidth:
3866     case CSSPropertyStrokeDashoffset:
3867     case CSSPropertyCx:
3868     case CSSPropertyCy:
3869     case CSSPropertyX:
3870     case CSSPropertyY:
3871     case CSSPropertyR:
3872         return consumeLengthOrPercent(m_range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
3873     case CSSPropertyRx:
3874     case CSSPropertyRy:
3875         return consumeRxOrRy(m_range);
3876     case CSSPropertyCursor:
3877         return consumeCursor(m_range, m_context, inQuirksMode());
3878     case CSSPropertyContent:
3879         return consumeContent(m_range, m_context);