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