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