Extract UTI mapping and allow for additions
[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 namespace WebCore {
86
87 bool isCustomPropertyName(const String& propertyName)
88 {
89     return propertyName.length() > 2 && propertyName.characterAt(0) == '-' && propertyName.characterAt(1) == '-';
90 }
91
92 static bool hasPrefix(const char* string, unsigned length, const char* prefix)
93 {
94     for (unsigned i = 0; i < length; ++i) {
95         if (!prefix[i])
96             return true;
97         if (string[i] != prefix[i])
98             return false;
99     }
100     return false;
101 }
102
103 #if PLATFORM(IOS_FAMILY)
104 void cssPropertyNameIOSAliasing(const char* propertyName, const char*& propertyNameAlias, unsigned& newLength)
105 {
106     if (!strcmp(propertyName, "-webkit-hyphenate-locale")) {
107         // Worked in iOS 4.2.
108         static const char webkitLocale[] = "-webkit-locale";
109         propertyNameAlias = webkitLocale;
110         newLength = strlen(webkitLocale);
111     }
112 }
113 #endif
114
115 template <typename CharacterType>
116 static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length)
117 {
118     char buffer[maxCSSPropertyNameLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
119     
120     for (unsigned i = 0; i != length; ++i) {
121         CharacterType c = propertyName[i];
122         if (!c || c >= 0x7F)
123             return CSSPropertyInvalid; // illegal character
124         buffer[i] = toASCIILower(c);
125     }
126     buffer[length] = '\0';
127     
128     const char* name = buffer;
129     if (buffer[0] == '-') {
130 #if ENABLE(LEGACY_CSS_VENDOR_PREFIXES)
131         // If the prefix is -apple- or -khtml-, change it to -webkit-.
132         // This makes the string one character longer.
133         if (RuntimeEnabledFeatures::sharedFeatures().legacyCSSVendorPrefixesEnabled()
134             && (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-"))) {
135             memmove(buffer + 7, buffer + 6, length + 1 - 6);
136             memcpy(buffer, "-webkit", 7);
137             ++length;
138         }
139 #endif
140 #if PLATFORM(IOS_FAMILY)
141         cssPropertyNameIOSAliasing(buffer, name, length);
142 #endif
143     }
144     
145     const Property* hashTableEntry = findProperty(name, length);
146     if (hashTableEntry) {
147         auto propertyID = static_cast<CSSPropertyID>(hashTableEntry->id);
148         if (isEnabledCSSProperty(propertyID))
149             return propertyID;
150     }
151     return CSSPropertyInvalid;
152 }
153
154 static bool isAppleLegacyCssValueKeyword(const char* valueKeyword, unsigned length)
155 {
156     static const char applePrefix[] = "-apple-";
157     static const char appleSystemPrefix[] = "-apple-system";
158     static const char applePayPrefix[] = "-apple-pay";
159
160 #if PLATFORM(MAC) || PLATFORM(IOS_FAMILY)
161     static const char* appleWirelessPlaybackTargetActive = getValueName(CSSValueAppleWirelessPlaybackTargetActive);
162 #endif
163
164     return hasPrefix(valueKeyword, length, applePrefix)
165     && !hasPrefix(valueKeyword, length, appleSystemPrefix)
166     && !hasPrefix(valueKeyword, length, applePayPrefix)
167 #if PLATFORM(MAC) || PLATFORM(IOS_FAMILY)
168     && !WTF::equal(reinterpret_cast<const LChar*>(valueKeyword), reinterpret_cast<const LChar*>(appleWirelessPlaybackTargetActive), length)
169 #endif
170     ;
171 }
172
173 template <typename CharacterType>
174 static CSSValueID cssValueKeywordID(const CharacterType* valueKeyword, unsigned length)
175 {
176     char buffer[maxCSSValueKeywordLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
177     
178     for (unsigned i = 0; i != length; ++i) {
179         CharacterType c = valueKeyword[i];
180         if (!c || c >= 0x7F)
181             return CSSValueInvalid; // illegal keyword.
182         buffer[i] = WTF::toASCIILower(c);
183     }
184     buffer[length] = '\0';
185     
186     if (buffer[0] == '-') {
187         // If the prefix is -apple- or -khtml-, change it to -webkit-.
188         // This makes the string one character longer.
189         // On iOS we don't want to change values starting with -apple-system to -webkit-system.
190         // FIXME: Remove this mangling without breaking the web.
191         if (isAppleLegacyCssValueKeyword(buffer, length) || hasPrefix(buffer, length, "-khtml-")) {
192             memmove(buffer + 7, buffer + 6, length + 1 - 6);
193             memcpy(buffer, "-webkit", 7);
194             ++length;
195         }
196     }
197     
198     const Value* hashTableEntry = findValue(buffer, length);
199     return hashTableEntry ? static_cast<CSSValueID>(hashTableEntry->id) : CSSValueInvalid;
200 }
201
202 CSSValueID cssValueKeywordID(StringView string)
203 {
204     unsigned length = string.length();
205     if (!length)
206         return CSSValueInvalid;
207     if (length > maxCSSValueKeywordLength)
208         return CSSValueInvalid;
209     
210     return string.is8Bit() ? cssValueKeywordID(string.characters8(), length) : cssValueKeywordID(string.characters16(), length);
211 }
212
213 CSSPropertyID cssPropertyID(StringView string)
214 {
215     unsigned length = string.length();
216     
217     if (!length)
218         return CSSPropertyInvalid;
219     if (length > maxCSSPropertyNameLength)
220         return CSSPropertyInvalid;
221     
222     return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length);
223 }
224     
225 using namespace CSSPropertyParserHelpers;
226
227 CSSPropertyParser::CSSPropertyParser(const CSSParserTokenRange& range, const CSSParserContext& context, Vector<CSSProperty, 256>* parsedProperties, bool consumeWhitespace)
228     : m_range(range)
229     , m_context(context)
230     , m_parsedProperties(parsedProperties)
231 {
232     if (consumeWhitespace)
233         m_range.consumeWhitespace();
234 }
235
236 void CSSPropertyParser::addProperty(CSSPropertyID property, CSSPropertyID currentShorthand, Ref<CSSValue>&& value, bool important, bool implicit)
237 {
238     if (!isEnabledCSSProperty(property))
239         return;
240
241     int shorthandIndex = 0;
242     bool setFromShorthand = false;
243
244     if (currentShorthand) {
245         auto shorthands = matchingShorthandsForLonghand(property);
246         setFromShorthand = true;
247         if (shorthands.size() > 1)
248             shorthandIndex = indexOfShorthandForLonghand(currentShorthand, shorthands);
249     }
250
251     m_parsedProperties->append(CSSProperty(property, WTFMove(value), important, setFromShorthand, shorthandIndex, implicit));
252 }
253
254 void CSSPropertyParser::addExpandedPropertyForValue(CSSPropertyID property, Ref<CSSValue>&& value, bool important)
255 {
256     const StylePropertyShorthand& shorthand = shorthandForProperty(property);
257     unsigned shorthandLength = shorthand.length();
258     ASSERT(shorthandLength);
259     const CSSPropertyID* longhands = shorthand.properties();
260     for (unsigned i = 0; i < shorthandLength; ++i)
261         addProperty(longhands[i], property, value.copyRef(), important);
262 }
263
264 bool CSSPropertyParser::parseValue(CSSPropertyID propertyID, bool important, const CSSParserTokenRange& range, const CSSParserContext& context, ParsedPropertyVector& parsedProperties, StyleRule::Type ruleType)
265 {
266     int parsedPropertiesSize = parsedProperties.size();
267
268     CSSPropertyParser parser(range, context, &parsedProperties);
269     bool parseSuccess;
270
271 #if ENABLE(CSS_DEVICE_ADAPTATION)
272     if (ruleType == StyleRule::Viewport)
273         parseSuccess = parser.parseViewportDescriptor(propertyID, important);
274     else
275 #endif
276     if (ruleType == StyleRule::FontFace)
277         parseSuccess = parser.parseFontFaceDescriptor(propertyID);
278     else
279         parseSuccess = parser.parseValueStart(propertyID, important);
280
281     if (!parseSuccess)
282         parsedProperties.shrink(parsedPropertiesSize);
283
284     return parseSuccess;
285 }
286
287 RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, const CSSParserTokenRange& range, const CSSParserContext& context)
288 {
289     CSSPropertyParser parser(range, context, nullptr);
290     RefPtr<CSSValue> value = parser.parseSingleValue(property);
291     if (!value || !parser.m_range.atEnd())
292         return nullptr;
293     return value;
294 }
295
296 bool CSSPropertyParser::canParseTypedCustomPropertyValue(const String& syntax, const CSSParserTokenRange& tokens, const CSSParserContext& context)
297 {
298     CSSPropertyParser parser(tokens, context, nullptr);
299     return parser.canParseTypedCustomPropertyValue(syntax);
300 }
301
302 RefPtr<CSSCustomPropertyValue> CSSPropertyParser::parseTypedCustomPropertyValue(const String& name, const String& syntax, const CSSParserTokenRange& tokens, const StyleResolver& styleResolver, const CSSParserContext& context)
303 {
304     CSSPropertyParser parser(tokens, context, nullptr, false);
305     RefPtr<CSSCustomPropertyValue> value = parser.parseTypedCustomPropertyValue(name, syntax, styleResolver);
306     if (!value || !parser.m_range.atEnd())
307         return nullptr;
308     return value;
309 }
310
311 void CSSPropertyParser::collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies, const CSSParserTokenRange& tokens, const CSSParserContext& context)
312 {
313     CSSPropertyParser parser(tokens, context, nullptr);
314     parser.collectParsedCustomPropertyValueDependencies(syntax, isRoot, dependencies);
315 }
316
317 static bool isLegacyBreakProperty(CSSPropertyID propertyID)
318 {
319     switch (propertyID) {
320     case CSSPropertyPageBreakAfter:
321     case CSSPropertyPageBreakBefore:
322     case CSSPropertyPageBreakInside:
323     case CSSPropertyWebkitColumnBreakAfter:
324     case CSSPropertyWebkitColumnBreakBefore:
325     case CSSPropertyWebkitColumnBreakInside:
326         return true;
327     default:
328         break;
329     }
330     return false;
331 }
332
333 bool CSSPropertyParser::parseValueStart(CSSPropertyID propertyID, bool important)
334 {
335     if (consumeCSSWideKeyword(propertyID, important))
336         return true;
337
338     CSSParserTokenRange originalRange = m_range;
339     bool isShorthand = isShorthandCSSProperty(propertyID);
340
341     if (isShorthand) {
342         // Variable references will fail to parse here and will fall out to the variable ref parser below.
343         if (parseShorthand(propertyID, important))
344             return true;
345     } else if (isLegacyBreakProperty(propertyID)) {
346         // FIXME-NEWPARSER: Can turn this into a shorthand once old parser is gone, and then
347         // we don't need the special case.
348         if (consumeLegacyBreakProperty(propertyID, important))
349             return true;
350     } else {
351         RefPtr<CSSValue> parsedValue = parseSingleValue(propertyID);
352         if (parsedValue && m_range.atEnd()) {
353             addProperty(propertyID, CSSPropertyInvalid, *parsedValue, important);
354             return true;
355         }
356     }
357
358     if (CSSVariableParser::containsValidVariableReferences(originalRange, m_context)) {
359         RefPtr<CSSVariableReferenceValue> variable = CSSVariableReferenceValue::create(originalRange);
360
361         if (isShorthand) {
362             RefPtr<CSSPendingSubstitutionValue> pendingValue = CSSPendingSubstitutionValue::create(propertyID, variable.releaseNonNull());
363             addExpandedPropertyForValue(propertyID, pendingValue.releaseNonNull(), important);
364         } else
365             addProperty(propertyID, CSSPropertyInvalid, variable.releaseNonNull(), important);
366         return true;
367     }
368
369     return false;
370 }
371  
372 bool CSSPropertyParser::consumeCSSWideKeyword(CSSPropertyID propertyID, bool important)
373 {
374     CSSParserTokenRange rangeCopy = m_range;
375     CSSValueID valueID = rangeCopy.consumeIncludingWhitespace().id();
376     if (!rangeCopy.atEnd())
377         return false;
378
379     RefPtr<CSSValue> value;
380     if (valueID == CSSValueInherit)
381         value = CSSValuePool::singleton().createInheritedValue();
382     else if (valueID == CSSValueInitial)
383         value = CSSValuePool::singleton().createExplicitInitialValue();
384     else if (valueID == CSSValueUnset)
385         value = CSSValuePool::singleton().createUnsetValue();
386     else if (valueID == CSSValueRevert)
387         value = CSSValuePool::singleton().createRevertValue();
388     else
389         return false;
390     
391     const StylePropertyShorthand& shorthand = shorthandForProperty(propertyID);
392     if (!shorthand.length()) {
393         if (CSSProperty::isDescriptorOnly(propertyID))
394             return false;
395         addProperty(propertyID, CSSPropertyInvalid, value.releaseNonNull(), important);
396     } else
397         addExpandedPropertyForValue(propertyID, value.releaseNonNull(), important);
398     m_range = rangeCopy;
399     return true;
400 }
401
402 bool CSSPropertyParser::consumeTransformOrigin(bool important)
403 {
404     RefPtr<CSSPrimitiveValue> resultX;
405     RefPtr<CSSPrimitiveValue> resultY;
406     if (consumeOneOrTwoValuedPosition(m_range, m_context.mode, UnitlessQuirk::Forbid, resultX, resultY)) {
407         m_range.consumeWhitespace();
408         bool atEnd = m_range.atEnd();
409         RefPtr<CSSPrimitiveValue> resultZ = consumeLength(m_range, m_context.mode, ValueRangeAll);
410         bool hasZ = resultZ;
411         if (!hasZ && !atEnd)
412             return false;
413         addProperty(CSSPropertyTransformOriginX, CSSPropertyTransformOrigin, resultX.releaseNonNull(), important);
414         addProperty(CSSPropertyTransformOriginY, CSSPropertyTransformOrigin, resultY.releaseNonNull(), important);
415         addProperty(CSSPropertyTransformOriginZ, CSSPropertyTransformOrigin, resultZ ? resultZ.releaseNonNull() : CSSValuePool::singleton().createValue(0, CSSPrimitiveValue::UnitType::CSS_PX), important, !hasZ);
416         
417         return true;
418     }
419     return false;
420 }
421
422 bool CSSPropertyParser::consumePerspectiveOrigin(bool important)
423 {
424     RefPtr<CSSPrimitiveValue> resultX;
425     RefPtr<CSSPrimitiveValue> resultY;
426     if (consumePosition(m_range, m_context.mode, UnitlessQuirk::Forbid, resultX, resultY)) {
427         addProperty(CSSPropertyPerspectiveOriginX, CSSPropertyPerspectiveOrigin, resultX.releaseNonNull(), important);
428         addProperty(CSSPropertyPerspectiveOriginY, CSSPropertyPerspectiveOrigin, resultY.releaseNonNull(), important);
429         return true;
430     }
431     return false;
432 }
433
434 // Methods for consuming non-shorthand properties starts here.
435 static RefPtr<CSSValue> consumeWillChange(CSSParserTokenRange& range)
436 {
437     if (range.peek().id() == CSSValueAuto)
438         return consumeIdent(range);
439
440     RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
441     // Every comma-separated list of identifiers is a valid will-change value,
442     // unless the list includes an explicitly disallowed identifier.
443     while (true) {
444         if (range.peek().type() != IdentToken)
445             return nullptr;
446         CSSPropertyID propertyID = cssPropertyID(range.peek().value());
447         if (propertyID != CSSPropertyInvalid) {
448             // Now "all" is used by both CSSValue and CSSPropertyValue.
449             // Need to return nullptr when currentValue is CSSPropertyAll.
450             if (propertyID == CSSPropertyWillChange || propertyID == CSSPropertyAll)
451                 return nullptr;
452             // FIXME-NEWPARSER: Use CSSCustomIdentValue someday.
453             values->append(CSSValuePool::singleton().createIdentifierValue(propertyID));
454             range.consumeIncludingWhitespace();
455         } else {
456             switch (range.peek().id()) {
457             case CSSValueNone:
458             case CSSValueAll:
459             case CSSValueAuto:
460             case CSSValueDefault:
461             case CSSValueInitial:
462             case CSSValueInherit:
463                 return nullptr;
464             case CSSValueContents:
465             case CSSValueScrollPosition:
466                 values->append(consumeIdent(range).releaseNonNull());
467                 break;
468             default:
469                 // Append properties we don't recognize, but that are legal, as strings.
470                 values->append(consumeCustomIdent(range).releaseNonNull());
471                 break;
472             }
473         }
474
475         if (range.atEnd())
476             break;
477         if (!consumeCommaIncludingWhitespace(range))
478             return nullptr;
479     }
480
481     return values;
482 }
483
484 static RefPtr<CSSFontFeatureValue> consumeFontFeatureTag(CSSParserTokenRange& range)
485 {
486     // Feature tag name consists of 4-letter characters.
487     static const unsigned tagNameLength = 4;
488
489     const CSSParserToken& token = range.consumeIncludingWhitespace();
490     // Feature tag name comes first
491     if (token.type() != StringToken)
492         return nullptr;
493     if (token.value().length() != tagNameLength)
494         return nullptr;
495     
496     FontTag tag;
497     for (unsigned i = 0; i < tag.size(); ++i) {
498         // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
499         UChar character = token.value()[i];
500         if (character < 0x20 || character > 0x7E)
501             return nullptr;
502         tag[i] = toASCIILower(character);
503     }
504
505     int tagValue = 1;
506     if (!range.atEnd() && range.peek().type() != CommaToken) {
507         // Feature tag values could follow: <integer> | on | off
508         if (auto primitiveValue = consumeInteger(range, 0))
509             tagValue = primitiveValue->intValue();
510         else if (range.peek().id() == CSSValueOn || range.peek().id() == CSSValueOff)
511             tagValue = range.consumeIncludingWhitespace().id() == CSSValueOn;
512         else
513             return nullptr;
514     }
515     return CSSFontFeatureValue::create(WTFMove(tag), tagValue);
516 }
517
518 static RefPtr<CSSValue> consumeFontFeatureSettings(CSSParserTokenRange& range)
519 {
520     if (range.peek().id() == CSSValueNormal)
521         return consumeIdent(range);
522     RefPtr<CSSValueList> settings = CSSValueList::createCommaSeparated();
523     do {
524         RefPtr<CSSFontFeatureValue> fontFeatureValue = consumeFontFeatureTag(range);
525         if (!fontFeatureValue)
526             return nullptr;
527         settings->append(fontFeatureValue.releaseNonNull());
528     } while (consumeCommaIncludingWhitespace(range));
529     return settings;
530 }
531
532 #if ENABLE(VARIATION_FONTS)
533 static RefPtr<CSSValue> consumeFontVariationTag(CSSParserTokenRange& range)
534 {
535     if (range.peek().type() != StringToken)
536         return nullptr;
537     
538     auto string = range.consumeIncludingWhitespace().value().toString();
539     
540     FontTag tag;
541     if (string.length() != tag.size())
542         return nullptr;
543     for (unsigned i = 0; i < tag.size(); ++i) {
544         // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
545         UChar character = string[i];
546         if (character < 0x20 || character > 0x7E)
547             return nullptr;
548         tag[i] = character;
549     }
550     
551     if (range.atEnd())
552         return nullptr;
553
554     double tagValue = 0;
555     auto success = consumeNumberRaw(range, tagValue);
556     if (!success)
557         return nullptr;
558     
559     return CSSFontVariationValue::create(tag, tagValue);
560 }
561     
562 static RefPtr<CSSValue> consumeFontVariationSettings(CSSParserTokenRange& range)
563 {
564     if (range.peek().id() == CSSValueNormal)
565         return consumeIdent(range);
566     
567     auto settings = CSSValueList::createCommaSeparated();
568     do {
569         RefPtr<CSSValue> variationValue = consumeFontVariationTag(range);
570         if (!variationValue)
571             return nullptr;
572         settings->append(variationValue.releaseNonNull());
573     } while (consumeCommaIncludingWhitespace(range));
574     
575     if (!settings->length())
576         return nullptr;
577
578     return settings;
579 }
580 #endif // ENABLE(VARIATION_FONTS)
581
582 static RefPtr<CSSValue> consumePage(CSSParserTokenRange& range)
583 {
584     if (range.peek().id() == CSSValueAuto)
585         return consumeIdent(range);
586     return consumeCustomIdent(range);
587 }
588
589 static RefPtr<CSSValue> consumeQuotes(CSSParserTokenRange& range)
590 {
591     if (range.peek().id() == CSSValueNone)
592         return consumeIdent(range);
593     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
594     while (!range.atEnd()) {
595         RefPtr<CSSPrimitiveValue> parsedValue = consumeString(range);
596         if (!parsedValue)
597             return nullptr;
598         values->append(parsedValue.releaseNonNull());
599     }
600     if (values->length() && values->length() % 2 == 0)
601         return values;
602     return nullptr;
603 }
604
605 class FontVariantLigaturesParser {
606 public:
607     FontVariantLigaturesParser()
608         : m_sawCommonLigaturesValue(false)
609         , m_sawDiscretionaryLigaturesValue(false)
610         , m_sawHistoricalLigaturesValue(false)
611         , m_sawContextualLigaturesValue(false)
612         , m_result(CSSValueList::createSpaceSeparated())
613     {
614     }
615
616     enum class ParseResult {
617         ConsumedValue,
618         DisallowedValue,
619         UnknownValue
620     };
621
622     ParseResult consumeLigature(CSSParserTokenRange& range)
623     {
624         CSSValueID valueID = range.peek().id();
625         switch (valueID) {
626         case CSSValueNoCommonLigatures:
627         case CSSValueCommonLigatures:
628             if (m_sawCommonLigaturesValue)
629                 return ParseResult::DisallowedValue;
630             m_sawCommonLigaturesValue = true;
631             break;
632         case CSSValueNoDiscretionaryLigatures:
633         case CSSValueDiscretionaryLigatures:
634             if (m_sawDiscretionaryLigaturesValue)
635                 return ParseResult::DisallowedValue;
636             m_sawDiscretionaryLigaturesValue = true;
637             break;
638         case CSSValueNoHistoricalLigatures:
639         case CSSValueHistoricalLigatures:
640             if (m_sawHistoricalLigaturesValue)
641                 return ParseResult::DisallowedValue;
642             m_sawHistoricalLigaturesValue = true;
643             break;
644         case CSSValueNoContextual:
645         case CSSValueContextual:
646             if (m_sawContextualLigaturesValue)
647                 return ParseResult::DisallowedValue;
648             m_sawContextualLigaturesValue = true;
649             break;
650         default:
651             return ParseResult::UnknownValue;
652         }
653         m_result->append(consumeIdent(range).releaseNonNull());
654         return ParseResult::ConsumedValue;
655     }
656
657     RefPtr<CSSValue> finalizeValue()
658     {
659         if (!m_result->length())
660             return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
661         return WTFMove(m_result);
662     }
663
664 private:
665     bool m_sawCommonLigaturesValue;
666     bool m_sawDiscretionaryLigaturesValue;
667     bool m_sawHistoricalLigaturesValue;
668     bool m_sawContextualLigaturesValue;
669     RefPtr<CSSValueList> m_result;
670 };
671
672 static RefPtr<CSSValue> consumeFontVariantLigatures(CSSParserTokenRange& range)
673 {
674     if (range.peek().id() == CSSValueNormal || range.peek().id() == CSSValueNone)
675         return consumeIdent(range);
676
677     FontVariantLigaturesParser ligaturesParser;
678     do {
679         if (ligaturesParser.consumeLigature(range) !=
680             FontVariantLigaturesParser::ParseResult::ConsumedValue)
681             return nullptr;
682     } while (!range.atEnd());
683
684     return ligaturesParser.finalizeValue();
685 }
686
687 static RefPtr<CSSValue> consumeFontVariantEastAsian(CSSParserTokenRange& range)
688 {
689     if (range.peek().id() == CSSValueNormal)
690         return consumeIdent(range);
691     
692     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
693     FontVariantEastAsianVariant variant = FontVariantEastAsianVariant::Normal;
694     FontVariantEastAsianWidth width = FontVariantEastAsianWidth::Normal;
695     FontVariantEastAsianRuby ruby = FontVariantEastAsianRuby::Normal;
696     
697     while (!range.atEnd()) {
698         if (range.peek().type() != IdentToken)
699             return nullptr;
700         
701         auto id = range.peek().id();
702         
703         switch (id) {
704         case CSSValueJis78:
705             variant = FontVariantEastAsianVariant::Jis78;
706             break;
707         case CSSValueJis83:
708             variant = FontVariantEastAsianVariant::Jis83;
709             break;
710         case CSSValueJis90:
711             variant = FontVariantEastAsianVariant::Jis90;
712             break;
713         case CSSValueJis04:
714             variant = FontVariantEastAsianVariant::Jis04;
715             break;
716         case CSSValueSimplified:
717             variant = FontVariantEastAsianVariant::Simplified;
718             break;
719         case CSSValueTraditional:
720             variant = FontVariantEastAsianVariant::Traditional;
721             break;
722         case CSSValueFullWidth:
723             width = FontVariantEastAsianWidth::Full;
724             break;
725         case CSSValueProportionalWidth:
726             width = FontVariantEastAsianWidth::Proportional;
727             break;
728         case CSSValueRuby:
729             ruby = FontVariantEastAsianRuby::Yes;
730             break;
731         default:
732             return nullptr;
733         }
734         
735         range.consumeIncludingWhitespace();
736     }
737         
738     switch (variant) {
739     case FontVariantEastAsianVariant::Normal:
740         break;
741     case FontVariantEastAsianVariant::Jis78:
742         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis78));
743         break;
744     case FontVariantEastAsianVariant::Jis83:
745         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis83));
746         break;
747     case FontVariantEastAsianVariant::Jis90:
748         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis90));
749         break;
750     case FontVariantEastAsianVariant::Jis04:
751         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis04));
752         break;
753     case FontVariantEastAsianVariant::Simplified:
754         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueSimplified));
755         break;
756     case FontVariantEastAsianVariant::Traditional:
757         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueTraditional));
758         break;
759     }
760         
761     switch (width) {
762     case FontVariantEastAsianWidth::Normal:
763         break;
764     case FontVariantEastAsianWidth::Full:
765         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueFullWidth));
766         break;
767     case FontVariantEastAsianWidth::Proportional:
768         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueProportionalWidth));
769         break;
770     }
771         
772     switch (ruby) {
773     case FontVariantEastAsianRuby::Normal:
774         break;
775     case FontVariantEastAsianRuby::Yes:
776         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueRuby));
777     }
778
779     if (!values->length())
780         return nullptr;
781
782     return values;
783 }
784     
785 static RefPtr<CSSPrimitiveValue> consumeFontVariantCaps(CSSParserTokenRange& range)
786 {
787     return consumeIdent<CSSValueNormal, CSSValueSmallCaps, CSSValueAllSmallCaps,
788         CSSValuePetiteCaps, CSSValueAllPetiteCaps,
789         CSSValueUnicase, CSSValueTitlingCaps>(range);
790 }
791
792 static RefPtr<CSSPrimitiveValue> consumeFontVariantAlternates(CSSParserTokenRange& range)
793 {
794     return consumeIdent<CSSValueNormal, CSSValueHistoricalForms>(range);
795 }
796
797 static RefPtr<CSSPrimitiveValue> consumeFontVariantPosition(CSSParserTokenRange& range)
798 {
799     return consumeIdent<CSSValueNormal, CSSValueSub, CSSValueSuper>(range);
800 }
801
802 class FontVariantNumericParser {
803 public:
804     FontVariantNumericParser()
805         : m_sawNumericFigureValue(false)
806         , m_sawNumericSpacingValue(false)
807         , m_sawNumericFractionValue(false)
808         , m_sawOrdinalValue(false)
809         , m_sawSlashedZeroValue(false)
810         , m_result(CSSValueList::createSpaceSeparated())
811     {
812     }
813
814     enum class ParseResult {
815         ConsumedValue,
816         DisallowedValue,
817         UnknownValue
818     };
819
820     ParseResult consumeNumeric(CSSParserTokenRange& range)
821     {
822         CSSValueID valueID = range.peek().id();
823         switch (valueID) {
824         case CSSValueLiningNums:
825         case CSSValueOldstyleNums:
826             if (m_sawNumericFigureValue)
827                 return ParseResult::DisallowedValue;
828             m_sawNumericFigureValue = true;
829             break;
830         case CSSValueProportionalNums:
831         case CSSValueTabularNums:
832             if (m_sawNumericSpacingValue)
833                 return ParseResult::DisallowedValue;
834             m_sawNumericSpacingValue = true;
835             break;
836         case CSSValueDiagonalFractions:
837         case CSSValueStackedFractions:
838             if (m_sawNumericFractionValue)
839                 return ParseResult::DisallowedValue;
840             m_sawNumericFractionValue = true;
841             break;
842         case CSSValueOrdinal:
843             if (m_sawOrdinalValue)
844                 return ParseResult::DisallowedValue;
845             m_sawOrdinalValue = true;
846             break;
847         case CSSValueSlashedZero:
848             if (m_sawSlashedZeroValue)
849                 return ParseResult::DisallowedValue;
850             m_sawSlashedZeroValue = true;
851             break;
852         default:
853             return ParseResult::UnknownValue;
854         }
855         m_result->append(consumeIdent(range).releaseNonNull());
856         return ParseResult::ConsumedValue;
857     }
858
859     RefPtr<CSSValue> finalizeValue()
860     {
861         if (!m_result->length())
862             return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
863         return WTFMove(m_result);
864     }
865
866
867 private:
868     bool m_sawNumericFigureValue;
869     bool m_sawNumericSpacingValue;
870     bool m_sawNumericFractionValue;
871     bool m_sawOrdinalValue;
872     bool m_sawSlashedZeroValue;
873     RefPtr<CSSValueList> m_result;
874 };
875
876 static RefPtr<CSSValue> consumeFontVariantNumeric(CSSParserTokenRange& range)
877 {
878     if (range.peek().id() == CSSValueNormal)
879         return consumeIdent(range);
880
881     FontVariantNumericParser numericParser;
882     do {
883         if (numericParser.consumeNumeric(range) !=
884             FontVariantNumericParser::ParseResult::ConsumedValue)
885             return nullptr;
886     } while (!range.atEnd());
887
888     return numericParser.finalizeValue();
889 }
890
891 static RefPtr<CSSPrimitiveValue> consumeFontVariantCSS21(CSSParserTokenRange& range)
892 {
893     return consumeIdent<CSSValueNormal, CSSValueSmallCaps>(range);
894 }
895
896 static RefPtr<CSSPrimitiveValue> consumeFontWeightKeywordValue(CSSParserTokenRange& range)
897 {
898     return consumeIdent<CSSValueNormal, CSSValueBold, CSSValueBolder, CSSValueLighter>(range);
899 }
900
901 static RefPtr<CSSPrimitiveValue> consumeFontWeight(CSSParserTokenRange& range)
902 {
903     if (auto result = consumeFontWeightKeywordValue(range))
904         return result;
905     return consumeFontWeightNumber(range);
906 }
907
908 #if ENABLE(VARIATION_FONTS)
909 static RefPtr<CSSValue> consumeFontWeightRange(CSSParserTokenRange& range)
910 {
911     if (auto result = consumeFontWeightKeywordValue(range))
912         return result;
913     auto firstNumber = consumeFontWeightNumber(range);
914     if (!firstNumber)
915         return nullptr;
916     if (range.atEnd())
917         return firstNumber;
918     auto secondNumber = consumeFontWeightNumber(range);
919     if (!secondNumber || firstNumber->floatValue() > secondNumber->floatValue())
920         return nullptr;
921     auto result = CSSValueList::createSpaceSeparated();
922     result->append(firstNumber.releaseNonNull());
923     result->append(secondNumber.releaseNonNull());
924     return RefPtr<CSSValue>(WTFMove(result));
925 }
926 #endif
927
928 static RefPtr<CSSPrimitiveValue> consumeFontStretchKeywordValue(CSSParserTokenRange& range)
929 {
930     return consumeIdent<CSSValueUltraCondensed, CSSValueExtraCondensed, CSSValueCondensed, CSSValueSemiCondensed, CSSValueNormal, CSSValueSemiExpanded, CSSValueExpanded, CSSValueExtraExpanded, CSSValueUltraExpanded>(range);
931 }
932
933 #if ENABLE(VARIATION_FONTS)
934 static bool fontStretchIsWithinRange(float stretch)
935 {
936     return stretch > 0;
937 }
938 #endif
939
940 static RefPtr<CSSPrimitiveValue> consumeFontStretch(CSSParserTokenRange& range)
941 {
942     if (auto result = consumeFontStretchKeywordValue(range))
943         return result;
944 #if ENABLE(VARIATION_FONTS)
945     if (auto percent = consumePercent(range, ValueRangeNonNegative))
946         return fontStretchIsWithinRange(percent->value<float>()) ? percent : nullptr;
947 #endif
948     return nullptr;
949 }
950
951 #if ENABLE(VARIATION_FONTS)
952 static RefPtr<CSSValue> consumeFontStretchRange(CSSParserTokenRange& range)
953 {
954     if (auto result = consumeFontStretchKeywordValue(range))
955         return result;
956     auto firstPercent = consumePercent(range, ValueRangeNonNegative);
957     if (!firstPercent || !fontStretchIsWithinRange(firstPercent->value<float>()))
958         return nullptr;
959     if (range.atEnd())
960         return firstPercent;
961     auto secondPercent = consumePercent(range, ValueRangeNonNegative);
962     if (!secondPercent || !fontStretchIsWithinRange(secondPercent->value<float>()) || firstPercent->floatValue() > secondPercent->floatValue())
963         return nullptr;
964     auto result = CSSValueList::createSpaceSeparated();
965     result->append(firstPercent.releaseNonNull());
966     result->append(secondPercent.releaseNonNull());
967     return RefPtr<CSSValue>(WTFMove(result));
968 }
969 #endif
970
971 static RefPtr<CSSPrimitiveValue> consumeFontStyleKeywordValue(CSSParserTokenRange& range)
972 {
973     return consumeIdent<CSSValueNormal, CSSValueItalic, CSSValueOblique>(range);
974 }
975
976 #if ENABLE(VARIATION_FONTS)
977 static bool fontStyleIsWithinRange(float oblique)
978 {
979     return oblique > -90 && oblique < 90;
980 }
981 #endif
982
983 static RefPtr<CSSFontStyleValue> consumeFontStyle(CSSParserTokenRange& range, CSSParserMode cssParserMode)
984 {
985     auto result = consumeFontStyleKeywordValue(range);
986     if (!result)
987         return nullptr;
988
989     auto valueID = result->valueID();
990     if (valueID == CSSValueNormal || valueID == CSSValueItalic)
991         return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(valueID));
992     ASSERT(result->valueID() == CSSValueOblique);
993 #if ENABLE(VARIATION_FONTS)
994     if (!range.atEnd()) {
995         if (auto angle = consumeAngle(range, cssParserMode)) {
996             if (fontStyleIsWithinRange(angle->value<float>(CSSPrimitiveValue::CSS_DEG)))
997                 return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueOblique), WTFMove(angle));
998             return nullptr;
999         }
1000     }
1001 #else
1002     UNUSED_PARAM(cssParserMode);
1003 #endif
1004     return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueOblique));
1005 }
1006
1007 #if ENABLE(VARIATION_FONTS)
1008 static RefPtr<CSSFontStyleRangeValue> consumeFontStyleRange(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1009 {
1010     auto keyword = consumeFontStyleKeywordValue(range);
1011     if (!keyword)
1012         return nullptr;
1013
1014     if (keyword->valueID() != CSSValueOblique || range.atEnd())
1015         return CSSFontStyleRangeValue::create(keyword.releaseNonNull());
1016
1017     if (auto firstAngle = consumeAngle(range, cssParserMode)) {
1018         if (!fontStyleIsWithinRange(firstAngle->value<float>(CSSPrimitiveValue::CSS_DEG)))
1019             return nullptr;
1020         if (range.atEnd()) {
1021             auto result = CSSValueList::createSpaceSeparated();
1022             result->append(firstAngle.releaseNonNull());
1023             return CSSFontStyleRangeValue::create(keyword.releaseNonNull(), WTFMove(result));
1024         }
1025         auto secondAngle = consumeAngle(range, cssParserMode);
1026         if (!secondAngle || !fontStyleIsWithinRange(secondAngle->value<float>(CSSPrimitiveValue::CSS_DEG)) || firstAngle->floatValue(CSSPrimitiveValue::CSS_DEG) > secondAngle->floatValue(CSSPrimitiveValue::CSS_DEG))
1027             return nullptr;
1028         auto result = CSSValueList::createSpaceSeparated();
1029         result->append(firstAngle.releaseNonNull());
1030         result->append(secondAngle.releaseNonNull());
1031         return CSSFontStyleRangeValue::create(keyword.releaseNonNull(), WTFMove(result));
1032     }
1033
1034     return nullptr;
1035 }
1036 #endif
1037
1038 static String concatenateFamilyName(CSSParserTokenRange& range)
1039 {
1040     StringBuilder builder;
1041     bool addedSpace = false;
1042     const CSSParserToken& firstToken = range.peek();
1043     while (range.peek().type() == IdentToken) {
1044         if (!builder.isEmpty()) {
1045             builder.append(' ');
1046             addedSpace = true;
1047         }
1048         builder.append(range.consumeIncludingWhitespace().value());
1049     }
1050     if (!addedSpace && isCSSWideKeyword(firstToken.id()))
1051         return String();
1052     return builder.toString();
1053 }
1054
1055 static RefPtr<CSSValue> consumeFamilyName(CSSParserTokenRange& range)
1056 {
1057     if (range.peek().type() == StringToken)
1058         return CSSValuePool::singleton().createFontFamilyValue(range.consumeIncludingWhitespace().value().toString());
1059     if (range.peek().type() != IdentToken)
1060         return nullptr;
1061     String familyName = concatenateFamilyName(range);
1062     if (familyName.isNull())
1063         return nullptr;
1064     return CSSValuePool::singleton().createFontFamilyValue(familyName);
1065 }
1066
1067 static RefPtr<CSSValue> consumeGenericFamily(CSSParserTokenRange& range)
1068 {
1069     return consumeIdentRange(range, CSSValueSerif, CSSValueWebkitBody);
1070 }
1071
1072 static RefPtr<CSSValueList> consumeFontFamily(CSSParserTokenRange& range)
1073 {
1074     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
1075     do {
1076         if (auto parsedValue = consumeGenericFamily(range))
1077             list->append(parsedValue.releaseNonNull());
1078         else {
1079             if (auto parsedValue = consumeFamilyName(range))
1080                 list->append(parsedValue.releaseNonNull());
1081             else
1082                 return nullptr;
1083         }
1084     } while (consumeCommaIncludingWhitespace(range));
1085     return list;
1086 }
1087
1088 static RefPtr<CSSValueList> consumeFontFamilyDescriptor(CSSParserTokenRange& range)
1089 {
1090     // FIXME-NEWPARSER: https://bugs.webkit.org/show_bug.cgi?id=196381 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 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         result->append(minTrackBreadth.releaseNonNull());