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