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