[GTK][WebInspector] Support for saving data
[WebKit-https.git] / Source / WebCore / css / parser / CSSPropertyParser.cpp
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Copyright (C) 2016 Apple Inc. All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //    * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //    * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //    * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "config.h"
31 #include "CSSPropertyParser.h"
32
33 #include "CSSAspectRatioValue.h"
34 #include "CSSBasicShapes.h"
35 #include "CSSBorderImage.h"
36 #include "CSSBorderImageSliceValue.h"
37 #include "CSSContentDistributionValue.h"
38 #include "CSSCursorImageValue.h"
39 #include "CSSCustomIdentValue.h"
40 #include "CSSCustomPropertyValue.h"
41 #include "CSSFontFaceSrcValue.h"
42 #include "CSSFontFeatureValue.h"
43 #if ENABLE(VARIATION_FONTS)
44 #include "CSSFontVariationValue.h"
45 #endif
46 #include "CSSFontStyleRangeValue.h"
47 #include "CSSFontStyleValue.h"
48 #include "CSSFunctionValue.h"
49 #include "CSSGridAutoRepeatValue.h"
50 #include "CSSGridIntegerRepeatValue.h"
51 #include "CSSGridLineNamesValue.h"
52 #include "CSSGridTemplateAreasValue.h"
53 #include "CSSLineBoxContainValue.h"
54 #include "CSSParserFastPaths.h"
55 #include "CSSParserIdioms.h"
56 #include "CSSPendingSubstitutionValue.h"
57 #include "CSSPrimitiveValueMappings.h"
58 #include "CSSPropertyParserHelpers.h"
59 #include "CSSReflectValue.h"
60 #include "CSSShadowValue.h"
61 #include "CSSTimingFunctionValue.h"
62 #include "CSSUnicodeRangeValue.h"
63 #include "CSSVariableParser.h"
64 #include "CSSVariableReferenceValue.h"
65 #include "Counter.h"
66 #include "FontFace.h"
67 #include "HashTools.h"
68 // FIXME-NEWPARSER: Replace Pair and Rect with actual CSSValue subclasses (CSSValuePair and CSSQuadValue).
69 #include "Pair.h"
70 #include "Rect.h"
71 #include "RenderTheme.h"
72 #include "RuntimeEnabledFeatures.h"
73 #include "SVGPathByteStream.h"
74 #include "SVGPathUtilities.h"
75 #include "StyleBuilderConverter.h"
76 #include "StylePropertyShorthand.h"
77 #include "StylePropertyShorthandFunctions.h"
78 #include "StyleResolver.h"
79 #include <bitset>
80 #include <memory>
81 #include <wtf/text/StringBuilder.h>
82
83 namespace WebCore {
84
85 bool isCustomPropertyName(const String& propertyName)
86 {
87     return propertyName.length() > 2 && propertyName.characterAt(0) == '-' && propertyName.characterAt(1) == '-';
88 }
89
90 static bool hasPrefix(const char* string, unsigned length, const char* prefix)
91 {
92     for (unsigned i = 0; i < length; ++i) {
93         if (!prefix[i])
94             return true;
95         if (string[i] != prefix[i])
96             return false;
97     }
98     return false;
99 }
100
101 #if PLATFORM(IOS_FAMILY)
102 void cssPropertyNameIOSAliasing(const char* propertyName, const char*& propertyNameAlias, unsigned& newLength)
103 {
104     if (!strcmp(propertyName, "-webkit-hyphenate-locale")) {
105         // Worked in iOS 4.2.
106         static const char webkitLocale[] = "-webkit-locale";
107         propertyNameAlias = webkitLocale;
108         newLength = strlen(webkitLocale);
109     }
110 }
111 #endif
112
113 template <typename CharacterType>
114 static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length)
115 {
116     char buffer[maxCSSPropertyNameLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
117     
118     for (unsigned i = 0; i != length; ++i) {
119         CharacterType c = propertyName[i];
120         if (!c || c >= 0x7F)
121             return CSSPropertyInvalid; // illegal character
122         buffer[i] = toASCIILower(c);
123     }
124     buffer[length] = '\0';
125     
126     const char* name = buffer;
127     if (buffer[0] == '-') {
128 #if ENABLE(LEGACY_CSS_VENDOR_PREFIXES)
129         // If the prefix is -apple- or -khtml-, change it to -webkit-.
130         // This makes the string one character longer.
131         if (RuntimeEnabledFeatures::sharedFeatures().legacyCSSVendorPrefixesEnabled()
132             && (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-"))) {
133             memmove(buffer + 7, buffer + 6, length + 1 - 6);
134             memcpy(buffer, "-webkit", 7);
135             ++length;
136         }
137 #endif
138 #if PLATFORM(IOS_FAMILY)
139         cssPropertyNameIOSAliasing(buffer, name, length);
140 #endif
141     }
142     
143     const Property* hashTableEntry = findProperty(name, length);
144     if (hashTableEntry) {
145         auto propertyID = static_cast<CSSPropertyID>(hashTableEntry->id);
146         if (isEnabledCSSProperty(propertyID))
147             return propertyID;
148     }
149     return CSSPropertyInvalid;
150 }
151
152 static bool isAppleLegacyCssValueKeyword(const char* valueKeyword, unsigned length)
153 {
154     static const char applePrefix[] = "-apple-";
155     static const char appleSystemPrefix[] = "-apple-system";
156     static const char applePayPrefix[] = "-apple-pay";
157
158 #if PLATFORM(COCOA)
159     static const char* appleWirelessPlaybackTargetActive = getValueName(CSSValueAppleWirelessPlaybackTargetActive);
160 #endif
161
162     return hasPrefix(valueKeyword, length, applePrefix)
163     && !hasPrefix(valueKeyword, length, appleSystemPrefix)
164     && !hasPrefix(valueKeyword, length, applePayPrefix)
165 #if PLATFORM(COCOA)
166     && !WTF::equal(reinterpret_cast<const LChar*>(valueKeyword), reinterpret_cast<const LChar*>(appleWirelessPlaybackTargetActive), length)
167 #endif
168     ;
169 }
170
171 template <typename CharacterType>
172 static CSSValueID cssValueKeywordID(const CharacterType* valueKeyword, unsigned length)
173 {
174     char buffer[maxCSSValueKeywordLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
175     
176     for (unsigned i = 0; i != length; ++i) {
177         CharacterType c = valueKeyword[i];
178         if (!c || c >= 0x7F)
179             return CSSValueInvalid; // illegal keyword.
180         buffer[i] = WTF::toASCIILower(c);
181     }
182     buffer[length] = '\0';
183     
184     if (buffer[0] == '-') {
185         // If the prefix is -apple- or -khtml-, change it to -webkit-.
186         // This makes the string one character longer.
187         // On iOS we don't want to change values starting with -apple-system to -webkit-system.
188         // FIXME: Remove this mangling without breaking the web.
189         if (isAppleLegacyCssValueKeyword(buffer, length) || hasPrefix(buffer, length, "-khtml-")) {
190             memmove(buffer + 7, buffer + 6, length + 1 - 6);
191             memcpy(buffer, "-webkit", 7);
192             ++length;
193         }
194     }
195     
196     const Value* hashTableEntry = findValue(buffer, length);
197     return hashTableEntry ? static_cast<CSSValueID>(hashTableEntry->id) : CSSValueInvalid;
198 }
199
200 CSSValueID cssValueKeywordID(StringView string)
201 {
202     unsigned length = string.length();
203     if (!length)
204         return CSSValueInvalid;
205     if (length > maxCSSValueKeywordLength)
206         return CSSValueInvalid;
207     
208     return string.is8Bit() ? cssValueKeywordID(string.characters8(), length) : cssValueKeywordID(string.characters16(), length);
209 }
210
211 CSSPropertyID cssPropertyID(StringView string)
212 {
213     unsigned length = string.length();
214     
215     if (!length)
216         return CSSPropertyInvalid;
217     if (length > maxCSSPropertyNameLength)
218         return CSSPropertyInvalid;
219     
220     return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length);
221 }
222     
223 using namespace CSSPropertyParserHelpers;
224
225 CSSPropertyParser::CSSPropertyParser(const CSSParserTokenRange& range, const CSSParserContext& context, Vector<CSSProperty, 256>* parsedProperties, bool consumeWhitespace)
226     : m_range(range)
227     , m_context(context)
228     , m_parsedProperties(parsedProperties)
229 {
230     if (consumeWhitespace)
231         m_range.consumeWhitespace();
232 }
233
234 void CSSPropertyParser::addProperty(CSSPropertyID property, CSSPropertyID currentShorthand, Ref<CSSValue>&& value, bool important, bool implicit)
235 {
236     if (!isEnabledCSSProperty(property))
237         return;
238
239     int shorthandIndex = 0;
240     bool setFromShorthand = false;
241
242     if (currentShorthand) {
243         auto shorthands = matchingShorthandsForLonghand(property);
244         setFromShorthand = true;
245         if (shorthands.size() > 1)
246             shorthandIndex = indexOfShorthandForLonghand(currentShorthand, shorthands);
247     }
248
249     m_parsedProperties->append(CSSProperty(property, WTFMove(value), important, setFromShorthand, shorthandIndex, implicit));
250 }
251
252 void CSSPropertyParser::addExpandedPropertyForValue(CSSPropertyID property, Ref<CSSValue>&& value, bool important)
253 {
254     const StylePropertyShorthand& shorthand = shorthandForProperty(property);
255     unsigned shorthandLength = shorthand.length();
256     ASSERT(shorthandLength);
257     const CSSPropertyID* longhands = shorthand.properties();
258     for (unsigned i = 0; i < shorthandLength; ++i)
259         addProperty(longhands[i], property, value.copyRef(), important);
260 }
261
262 bool CSSPropertyParser::parseValue(CSSPropertyID propertyID, bool important, const CSSParserTokenRange& range, const CSSParserContext& context, ParsedPropertyVector& parsedProperties, StyleRule::Type ruleType)
263 {
264     int parsedPropertiesSize = parsedProperties.size();
265
266     CSSPropertyParser parser(range, context, &parsedProperties);
267     bool parseSuccess;
268
269 #if ENABLE(CSS_DEVICE_ADAPTATION)
270     if (ruleType == StyleRule::Viewport)
271         parseSuccess = parser.parseViewportDescriptor(propertyID, important);
272     else
273 #endif
274     if (ruleType == StyleRule::FontFace)
275         parseSuccess = parser.parseFontFaceDescriptor(propertyID);
276     else
277         parseSuccess = parser.parseValueStart(propertyID, important);
278
279     if (!parseSuccess)
280         parsedProperties.shrink(parsedPropertiesSize);
281
282     return parseSuccess;
283 }
284
285 RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, const CSSParserTokenRange& range, const CSSParserContext& context)
286 {
287     CSSPropertyParser parser(range, context, nullptr);
288     RefPtr<CSSValue> value = parser.parseSingleValue(property);
289     if (!value || !parser.m_range.atEnd())
290         return nullptr;
291     return value;
292 }
293
294 bool CSSPropertyParser::canParseTypedCustomPropertyValue(const String& syntax, const CSSParserTokenRange& tokens, const CSSParserContext& context)
295 {
296     CSSPropertyParser parser(tokens, context, nullptr);
297     return parser.canParseTypedCustomPropertyValue(syntax);
298 }
299
300 RefPtr<CSSCustomPropertyValue> CSSPropertyParser::parseTypedCustomPropertyValue(const String& name, const String& syntax, const CSSParserTokenRange& tokens, const StyleResolver& styleResolver, const CSSParserContext& context)
301 {
302     CSSPropertyParser parser(tokens, context, nullptr, false);
303     RefPtr<CSSCustomPropertyValue> value = parser.parseTypedCustomPropertyValue(name, syntax, styleResolver);
304     if (!value || !parser.m_range.atEnd())
305         return nullptr;
306     return value;
307 }
308
309 void CSSPropertyParser::collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies, const CSSParserTokenRange& tokens, const CSSParserContext& context)
310 {
311     CSSPropertyParser parser(tokens, context, nullptr);
312     parser.collectParsedCustomPropertyValueDependencies(syntax, isRoot, dependencies);
313 }
314
315 bool CSSPropertyParser::parseValueStart(CSSPropertyID propertyID, bool important)
316 {
317     if (consumeCSSWideKeyword(propertyID, important))
318         return true;
319
320     CSSParserTokenRange originalRange = m_range;
321     bool isShorthand = isShorthandCSSProperty(propertyID);
322
323     if (isShorthand) {
324         // Variable references will fail to parse here and will fall out to the variable ref parser below.
325         if (parseShorthand(propertyID, important))
326             return true;
327     } else {
328         RefPtr<CSSValue> parsedValue = parseSingleValue(propertyID);
329         if (parsedValue && m_range.atEnd()) {
330             addProperty(propertyID, CSSPropertyInvalid, *parsedValue, important);
331             return true;
332         }
333     }
334
335     if (CSSVariableParser::containsValidVariableReferences(originalRange, m_context)) {
336         RefPtr<CSSVariableReferenceValue> variable = CSSVariableReferenceValue::create(originalRange);
337
338         if (isShorthand) {
339             RefPtr<CSSPendingSubstitutionValue> pendingValue = CSSPendingSubstitutionValue::create(propertyID, variable.releaseNonNull());
340             addExpandedPropertyForValue(propertyID, pendingValue.releaseNonNull(), important);
341         } else
342             addProperty(propertyID, CSSPropertyInvalid, variable.releaseNonNull(), important);
343         return true;
344     }
345
346     return false;
347 }
348  
349 bool CSSPropertyParser::consumeCSSWideKeyword(CSSPropertyID propertyID, bool important)
350 {
351     CSSParserTokenRange rangeCopy = m_range;
352     CSSValueID valueID = rangeCopy.consumeIncludingWhitespace().id();
353     if (!rangeCopy.atEnd())
354         return false;
355
356     RefPtr<CSSValue> value;
357     if (valueID == CSSValueInherit)
358         value = CSSValuePool::singleton().createInheritedValue();
359     else if (valueID == CSSValueInitial)
360         value = CSSValuePool::singleton().createExplicitInitialValue();
361     else if (valueID == CSSValueUnset)
362         value = CSSValuePool::singleton().createUnsetValue();
363     else if (valueID == CSSValueRevert)
364         value = CSSValuePool::singleton().createRevertValue();
365     else
366         return false;
367     
368     const StylePropertyShorthand& shorthand = shorthandForProperty(propertyID);
369     if (!shorthand.length()) {
370         if (CSSProperty::isDescriptorOnly(propertyID))
371             return false;
372         addProperty(propertyID, CSSPropertyInvalid, value.releaseNonNull(), important);
373     } else
374         addExpandedPropertyForValue(propertyID, value.releaseNonNull(), important);
375     m_range = rangeCopy;
376     return true;
377 }
378
379 bool CSSPropertyParser::consumeTransformOrigin(bool important)
380 {
381     RefPtr<CSSPrimitiveValue> resultX;
382     RefPtr<CSSPrimitiveValue> resultY;
383     if (consumeOneOrTwoValuedPosition(m_range, m_context.mode, UnitlessQuirk::Forbid, resultX, resultY)) {
384         m_range.consumeWhitespace();
385         bool atEnd = m_range.atEnd();
386         RefPtr<CSSPrimitiveValue> resultZ = consumeLength(m_range, m_context.mode, ValueRangeAll);
387         bool hasZ = resultZ;
388         if (!hasZ && !atEnd)
389             return false;
390         addProperty(CSSPropertyTransformOriginX, CSSPropertyTransformOrigin, resultX.releaseNonNull(), important);
391         addProperty(CSSPropertyTransformOriginY, CSSPropertyTransformOrigin, resultY.releaseNonNull(), important);
392         addProperty(CSSPropertyTransformOriginZ, CSSPropertyTransformOrigin, resultZ ? resultZ.releaseNonNull() : CSSValuePool::singleton().createValue(0, CSSPrimitiveValue::UnitType::CSS_PX), important, !hasZ);
393         
394         return true;
395     }
396     return false;
397 }
398
399 bool CSSPropertyParser::consumePerspectiveOrigin(bool important)
400 {
401     RefPtr<CSSPrimitiveValue> resultX;
402     RefPtr<CSSPrimitiveValue> resultY;
403     if (consumePosition(m_range, m_context.mode, UnitlessQuirk::Forbid, resultX, resultY)) {
404         addProperty(CSSPropertyPerspectiveOriginX, CSSPropertyPerspectiveOrigin, resultX.releaseNonNull(), important);
405         addProperty(CSSPropertyPerspectiveOriginY, CSSPropertyPerspectiveOrigin, resultY.releaseNonNull(), important);
406         return true;
407     }
408     return false;
409 }
410
411 // Methods for consuming non-shorthand properties starts here.
412 static RefPtr<CSSValue> consumeWillChange(CSSParserTokenRange& range)
413 {
414     if (range.peek().id() == CSSValueAuto)
415         return consumeIdent(range);
416
417     RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
418     // Every comma-separated list of identifiers is a valid will-change value,
419     // unless the list includes an explicitly disallowed identifier.
420     while (true) {
421         if (range.peek().type() != IdentToken)
422             return nullptr;
423         CSSPropertyID propertyID = cssPropertyID(range.peek().value());
424         if (propertyID != CSSPropertyInvalid) {
425             // Now "all" is used by both CSSValue and CSSPropertyValue.
426             // Need to return nullptr when currentValue is CSSPropertyAll.
427             if (propertyID == CSSPropertyWillChange || propertyID == CSSPropertyAll)
428                 return nullptr;
429             // FIXME-NEWPARSER: Use CSSCustomIdentValue someday.
430             values->append(CSSValuePool::singleton().createIdentifierValue(propertyID));
431             range.consumeIncludingWhitespace();
432         } else {
433             switch (range.peek().id()) {
434             case CSSValueNone:
435             case CSSValueAll:
436             case CSSValueAuto:
437             case CSSValueDefault:
438             case CSSValueInitial:
439             case CSSValueInherit:
440                 return nullptr;
441             case CSSValueContents:
442             case CSSValueScrollPosition:
443                 values->append(consumeIdent(range).releaseNonNull());
444                 break;
445             default:
446                 // Append properties we don't recognize, but that are legal, as strings.
447                 values->append(consumeCustomIdent(range).releaseNonNull());
448                 break;
449             }
450         }
451
452         if (range.atEnd())
453             break;
454         if (!consumeCommaIncludingWhitespace(range))
455             return nullptr;
456     }
457
458     return values;
459 }
460
461 static RefPtr<CSSFontFeatureValue> consumeFontFeatureTag(CSSParserTokenRange& range)
462 {
463     // Feature tag name consists of 4-letter characters.
464     static const unsigned tagNameLength = 4;
465
466     const CSSParserToken& token = range.consumeIncludingWhitespace();
467     // Feature tag name comes first
468     if (token.type() != StringToken)
469         return nullptr;
470     if (token.value().length() != tagNameLength)
471         return nullptr;
472     
473     FontTag tag;
474     for (unsigned i = 0; i < tag.size(); ++i) {
475         // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
476         UChar character = token.value()[i];
477         if (character < 0x20 || character > 0x7E)
478             return nullptr;
479         tag[i] = toASCIILower(character);
480     }
481
482     int tagValue = 1;
483     if (!range.atEnd() && range.peek().type() != CommaToken) {
484         // Feature tag values could follow: <integer> | on | off
485         if (auto primitiveValue = consumeInteger(range, 0))
486             tagValue = primitiveValue->intValue();
487         else if (range.peek().id() == CSSValueOn || range.peek().id() == CSSValueOff)
488             tagValue = range.consumeIncludingWhitespace().id() == CSSValueOn;
489         else
490             return nullptr;
491     }
492     return CSSFontFeatureValue::create(WTFMove(tag), tagValue);
493 }
494
495 static RefPtr<CSSValue> consumeFontFeatureSettings(CSSParserTokenRange& range)
496 {
497     if (range.peek().id() == CSSValueNormal)
498         return consumeIdent(range);
499     RefPtr<CSSValueList> settings = CSSValueList::createCommaSeparated();
500     do {
501         RefPtr<CSSFontFeatureValue> fontFeatureValue = consumeFontFeatureTag(range);
502         if (!fontFeatureValue)
503             return nullptr;
504         settings->append(fontFeatureValue.releaseNonNull());
505     } while (consumeCommaIncludingWhitespace(range));
506     return settings;
507 }
508
509 #if ENABLE(VARIATION_FONTS)
510 static RefPtr<CSSValue> consumeFontVariationTag(CSSParserTokenRange& range)
511 {
512     if (range.peek().type() != StringToken)
513         return nullptr;
514     
515     auto string = range.consumeIncludingWhitespace().value().toString();
516     
517     FontTag tag;
518     if (string.length() != tag.size())
519         return nullptr;
520     for (unsigned i = 0; i < tag.size(); ++i) {
521         // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
522         UChar character = string[i];
523         if (character < 0x20 || character > 0x7E)
524             return nullptr;
525         tag[i] = character;
526     }
527     
528     if (range.atEnd())
529         return nullptr;
530
531     double tagValue = 0;
532     auto success = consumeNumberRaw(range, tagValue);
533     if (!success)
534         return nullptr;
535     
536     return CSSFontVariationValue::create(tag, tagValue);
537 }
538     
539 static RefPtr<CSSValue> consumeFontVariationSettings(CSSParserTokenRange& range)
540 {
541     if (range.peek().id() == CSSValueNormal)
542         return consumeIdent(range);
543     
544     auto settings = CSSValueList::createCommaSeparated();
545     do {
546         RefPtr<CSSValue> variationValue = consumeFontVariationTag(range);
547         if (!variationValue)
548             return nullptr;
549         settings->append(variationValue.releaseNonNull());
550     } while (consumeCommaIncludingWhitespace(range));
551     
552     if (!settings->length())
553         return nullptr;
554
555     return settings;
556 }
557 #endif // ENABLE(VARIATION_FONTS)
558
559 static RefPtr<CSSValue> consumePage(CSSParserTokenRange& range)
560 {
561     if (range.peek().id() == CSSValueAuto)
562         return consumeIdent(range);
563     return consumeCustomIdent(range);
564 }
565
566 static RefPtr<CSSValue> consumeQuotes(CSSParserTokenRange& range)
567 {
568     if (range.peek().id() == CSSValueNone)
569         return consumeIdent(range);
570     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
571     while (!range.atEnd()) {
572         RefPtr<CSSPrimitiveValue> parsedValue = consumeString(range);
573         if (!parsedValue)
574             return nullptr;
575         values->append(parsedValue.releaseNonNull());
576     }
577     if (values->length() && values->length() % 2 == 0)
578         return values;
579     return nullptr;
580 }
581
582 class FontVariantLigaturesParser {
583 public:
584     FontVariantLigaturesParser()
585         : m_sawCommonLigaturesValue(false)
586         , m_sawDiscretionaryLigaturesValue(false)
587         , m_sawHistoricalLigaturesValue(false)
588         , m_sawContextualLigaturesValue(false)
589         , m_result(CSSValueList::createSpaceSeparated())
590     {
591     }
592
593     enum class ParseResult {
594         ConsumedValue,
595         DisallowedValue,
596         UnknownValue
597     };
598
599     ParseResult consumeLigature(CSSParserTokenRange& range)
600     {
601         CSSValueID valueID = range.peek().id();
602         switch (valueID) {
603         case CSSValueNoCommonLigatures:
604         case CSSValueCommonLigatures:
605             if (m_sawCommonLigaturesValue)
606                 return ParseResult::DisallowedValue;
607             m_sawCommonLigaturesValue = true;
608             break;
609         case CSSValueNoDiscretionaryLigatures:
610         case CSSValueDiscretionaryLigatures:
611             if (m_sawDiscretionaryLigaturesValue)
612                 return ParseResult::DisallowedValue;
613             m_sawDiscretionaryLigaturesValue = true;
614             break;
615         case CSSValueNoHistoricalLigatures:
616         case CSSValueHistoricalLigatures:
617             if (m_sawHistoricalLigaturesValue)
618                 return ParseResult::DisallowedValue;
619             m_sawHistoricalLigaturesValue = true;
620             break;
621         case CSSValueNoContextual:
622         case CSSValueContextual:
623             if (m_sawContextualLigaturesValue)
624                 return ParseResult::DisallowedValue;
625             m_sawContextualLigaturesValue = true;
626             break;
627         default:
628             return ParseResult::UnknownValue;
629         }
630         m_result->append(consumeIdent(range).releaseNonNull());
631         return ParseResult::ConsumedValue;
632     }
633
634     RefPtr<CSSValue> finalizeValue()
635     {
636         if (!m_result->length())
637             return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
638         return WTFMove(m_result);
639     }
640
641 private:
642     bool m_sawCommonLigaturesValue;
643     bool m_sawDiscretionaryLigaturesValue;
644     bool m_sawHistoricalLigaturesValue;
645     bool m_sawContextualLigaturesValue;
646     RefPtr<CSSValueList> m_result;
647 };
648
649 static RefPtr<CSSValue> consumeFontVariantLigatures(CSSParserTokenRange& range)
650 {
651     if (range.peek().id() == CSSValueNormal || range.peek().id() == CSSValueNone)
652         return consumeIdent(range);
653
654     FontVariantLigaturesParser ligaturesParser;
655     do {
656         if (ligaturesParser.consumeLigature(range) !=
657             FontVariantLigaturesParser::ParseResult::ConsumedValue)
658             return nullptr;
659     } while (!range.atEnd());
660
661     return ligaturesParser.finalizeValue();
662 }
663
664 static RefPtr<CSSValue> consumeFontVariantEastAsian(CSSParserTokenRange& range)
665 {
666     if (range.peek().id() == CSSValueNormal)
667         return consumeIdent(range);
668     
669     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
670     FontVariantEastAsianVariant variant = FontVariantEastAsianVariant::Normal;
671     FontVariantEastAsianWidth width = FontVariantEastAsianWidth::Normal;
672     FontVariantEastAsianRuby ruby = FontVariantEastAsianRuby::Normal;
673     
674     while (!range.atEnd()) {
675         if (range.peek().type() != IdentToken)
676             return nullptr;
677         
678         auto id = range.peek().id();
679         
680         switch (id) {
681         case CSSValueJis78:
682             variant = FontVariantEastAsianVariant::Jis78;
683             break;
684         case CSSValueJis83:
685             variant = FontVariantEastAsianVariant::Jis83;
686             break;
687         case CSSValueJis90:
688             variant = FontVariantEastAsianVariant::Jis90;
689             break;
690         case CSSValueJis04:
691             variant = FontVariantEastAsianVariant::Jis04;
692             break;
693         case CSSValueSimplified:
694             variant = FontVariantEastAsianVariant::Simplified;
695             break;
696         case CSSValueTraditional:
697             variant = FontVariantEastAsianVariant::Traditional;
698             break;
699         case CSSValueFullWidth:
700             width = FontVariantEastAsianWidth::Full;
701             break;
702         case CSSValueProportionalWidth:
703             width = FontVariantEastAsianWidth::Proportional;
704             break;
705         case CSSValueRuby:
706             ruby = FontVariantEastAsianRuby::Yes;
707             break;
708         default:
709             return nullptr;
710         }
711         
712         range.consumeIncludingWhitespace();
713     }
714         
715     switch (variant) {
716     case FontVariantEastAsianVariant::Normal:
717         break;
718     case FontVariantEastAsianVariant::Jis78:
719         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis78));
720         break;
721     case FontVariantEastAsianVariant::Jis83:
722         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis83));
723         break;
724     case FontVariantEastAsianVariant::Jis90:
725         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis90));
726         break;
727     case FontVariantEastAsianVariant::Jis04:
728         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis04));
729         break;
730     case FontVariantEastAsianVariant::Simplified:
731         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueSimplified));
732         break;
733     case FontVariantEastAsianVariant::Traditional:
734         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueTraditional));
735         break;
736     }
737         
738     switch (width) {
739     case FontVariantEastAsianWidth::Normal:
740         break;
741     case FontVariantEastAsianWidth::Full:
742         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueFullWidth));
743         break;
744     case FontVariantEastAsianWidth::Proportional:
745         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueProportionalWidth));
746         break;
747     }
748         
749     switch (ruby) {
750     case FontVariantEastAsianRuby::Normal:
751         break;
752     case FontVariantEastAsianRuby::Yes:
753         values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueRuby));
754     }
755
756     if (!values->length())
757         return nullptr;
758
759     return values;
760 }
761     
762 static RefPtr<CSSPrimitiveValue> consumeFontVariantCaps(CSSParserTokenRange& range)
763 {
764     return consumeIdent<CSSValueNormal, CSSValueSmallCaps, CSSValueAllSmallCaps,
765         CSSValuePetiteCaps, CSSValueAllPetiteCaps,
766         CSSValueUnicase, CSSValueTitlingCaps>(range);
767 }
768
769 static RefPtr<CSSPrimitiveValue> consumeFontVariantAlternates(CSSParserTokenRange& range)
770 {
771     return consumeIdent<CSSValueNormal, CSSValueHistoricalForms>(range);
772 }
773
774 static RefPtr<CSSPrimitiveValue> consumeFontVariantPosition(CSSParserTokenRange& range)
775 {
776     return consumeIdent<CSSValueNormal, CSSValueSub, CSSValueSuper>(range);
777 }
778
779 class FontVariantNumericParser {
780 public:
781     FontVariantNumericParser()
782         : m_sawNumericFigureValue(false)
783         , m_sawNumericSpacingValue(false)
784         , m_sawNumericFractionValue(false)
785         , m_sawOrdinalValue(false)
786         , m_sawSlashedZeroValue(false)
787         , m_result(CSSValueList::createSpaceSeparated())
788     {
789     }
790
791     enum class ParseResult {
792         ConsumedValue,
793         DisallowedValue,
794         UnknownValue
795     };
796
797     ParseResult consumeNumeric(CSSParserTokenRange& range)
798     {
799         CSSValueID valueID = range.peek().id();
800         switch (valueID) {
801         case CSSValueLiningNums:
802         case CSSValueOldstyleNums:
803             if (m_sawNumericFigureValue)
804                 return ParseResult::DisallowedValue;
805             m_sawNumericFigureValue = true;
806             break;
807         case CSSValueProportionalNums:
808         case CSSValueTabularNums:
809             if (m_sawNumericSpacingValue)
810                 return ParseResult::DisallowedValue;
811             m_sawNumericSpacingValue = true;
812             break;
813         case CSSValueDiagonalFractions:
814         case CSSValueStackedFractions:
815             if (m_sawNumericFractionValue)
816                 return ParseResult::DisallowedValue;
817             m_sawNumericFractionValue = true;
818             break;
819         case CSSValueOrdinal:
820             if (m_sawOrdinalValue)
821                 return ParseResult::DisallowedValue;
822             m_sawOrdinalValue = true;
823             break;
824         case CSSValueSlashedZero:
825             if (m_sawSlashedZeroValue)
826                 return ParseResult::DisallowedValue;
827             m_sawSlashedZeroValue = true;
828             break;
829         default:
830             return ParseResult::UnknownValue;
831         }
832         m_result->append(consumeIdent(range).releaseNonNull());
833         return ParseResult::ConsumedValue;
834     }
835
836     RefPtr<CSSValue> finalizeValue()
837     {
838         if (!m_result->length())
839             return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
840         return WTFMove(m_result);
841     }
842
843
844 private:
845     bool m_sawNumericFigureValue;
846     bool m_sawNumericSpacingValue;
847     bool m_sawNumericFractionValue;
848     bool m_sawOrdinalValue;
849     bool m_sawSlashedZeroValue;
850     RefPtr<CSSValueList> m_result;
851 };
852
853 static RefPtr<CSSValue> consumeFontVariantNumeric(CSSParserTokenRange& range)
854 {
855     if (range.peek().id() == CSSValueNormal)
856         return consumeIdent(range);
857
858     FontVariantNumericParser numericParser;
859     do {
860         if (numericParser.consumeNumeric(range) !=
861             FontVariantNumericParser::ParseResult::ConsumedValue)
862             return nullptr;
863     } while (!range.atEnd());
864
865     return numericParser.finalizeValue();
866 }
867
868 static RefPtr<CSSPrimitiveValue> consumeFontVariantCSS21(CSSParserTokenRange& range)
869 {
870     return consumeIdent<CSSValueNormal, CSSValueSmallCaps>(range);
871 }
872
873 static RefPtr<CSSPrimitiveValue> consumeFontWeightKeywordValue(CSSParserTokenRange& range)
874 {
875     return consumeIdent<CSSValueNormal, CSSValueBold, CSSValueBolder, CSSValueLighter>(range);
876 }
877
878 static RefPtr<CSSPrimitiveValue> consumeFontWeight(CSSParserTokenRange& range)
879 {
880     if (auto result = consumeFontWeightKeywordValue(range))
881         return result;
882     return consumeFontWeightNumber(range);
883 }
884
885 #if ENABLE(VARIATION_FONTS)
886 static RefPtr<CSSValue> consumeFontWeightRange(CSSParserTokenRange& range)
887 {
888     if (auto result = consumeFontWeightKeywordValue(range))
889         return result;
890     auto firstNumber = consumeFontWeightNumber(range);
891     if (!firstNumber)
892         return nullptr;
893     if (range.atEnd())
894         return firstNumber;
895     auto secondNumber = consumeFontWeightNumber(range);
896     if (!secondNumber || firstNumber->floatValue() > secondNumber->floatValue())
897         return nullptr;
898     auto result = CSSValueList::createSpaceSeparated();
899     result->append(firstNumber.releaseNonNull());
900     result->append(secondNumber.releaseNonNull());
901     return RefPtr<CSSValue>(WTFMove(result));
902 }
903 #endif
904
905 static RefPtr<CSSPrimitiveValue> consumeFontStretchKeywordValue(CSSParserTokenRange& range)
906 {
907     return consumeIdent<CSSValueUltraCondensed, CSSValueExtraCondensed, CSSValueCondensed, CSSValueSemiCondensed, CSSValueNormal, CSSValueSemiExpanded, CSSValueExpanded, CSSValueExtraExpanded, CSSValueUltraExpanded>(range);
908 }
909
910 #if ENABLE(VARIATION_FONTS)
911 static bool fontStretchIsWithinRange(float stretch)
912 {
913     return stretch > 0;
914 }
915 #endif
916
917 static RefPtr<CSSPrimitiveValue> consumeFontStretch(CSSParserTokenRange& range)
918 {
919     if (auto result = consumeFontStretchKeywordValue(range))
920         return result;
921 #if ENABLE(VARIATION_FONTS)
922     if (auto percent = consumePercent(range, ValueRangeNonNegative))
923         return fontStretchIsWithinRange(percent->value<float>()) ? percent : nullptr;
924 #endif
925     return nullptr;
926 }
927
928 #if ENABLE(VARIATION_FONTS)
929 static RefPtr<CSSValue> consumeFontStretchRange(CSSParserTokenRange& range)
930 {
931     if (auto result = consumeFontStretchKeywordValue(range))
932         return result;
933     auto firstPercent = consumePercent(range, ValueRangeNonNegative);
934     if (!firstPercent || !fontStretchIsWithinRange(firstPercent->value<float>()))
935         return nullptr;
936     if (range.atEnd())
937         return firstPercent;
938     auto secondPercent = consumePercent(range, ValueRangeNonNegative);
939     if (!secondPercent || !fontStretchIsWithinRange(secondPercent->value<float>()) || firstPercent->floatValue() > secondPercent->floatValue())
940         return nullptr;
941     auto result = CSSValueList::createSpaceSeparated();
942     result->append(firstPercent.releaseNonNull());
943     result->append(secondPercent.releaseNonNull());
944     return RefPtr<CSSValue>(WTFMove(result));
945 }
946 #endif
947
948 static RefPtr<CSSPrimitiveValue> consumeFontStyleKeywordValue(CSSParserTokenRange& range)
949 {
950     return consumeIdent<CSSValueNormal, CSSValueItalic, CSSValueOblique>(range);
951 }
952
953 #if ENABLE(VARIATION_FONTS)
954 static bool fontStyleIsWithinRange(float oblique)
955 {
956     return oblique > -90 && oblique < 90;
957 }
958 #endif
959
960 static RefPtr<CSSFontStyleValue> consumeFontStyle(CSSParserTokenRange& range, CSSParserMode cssParserMode)
961 {
962     auto result = consumeFontStyleKeywordValue(range);
963     if (!result)
964         return nullptr;
965
966     auto valueID = result->valueID();
967     if (valueID == CSSValueNormal || valueID == CSSValueItalic)
968         return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(valueID));
969     ASSERT(result->valueID() == CSSValueOblique);
970 #if ENABLE(VARIATION_FONTS)
971     if (!range.atEnd()) {
972         if (auto angle = consumeAngle(range, cssParserMode)) {
973             if (fontStyleIsWithinRange(angle->value<float>(CSSPrimitiveValue::CSS_DEG)))
974                 return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueOblique), WTFMove(angle));
975             return nullptr;
976         }
977     }
978 #else
979     UNUSED_PARAM(cssParserMode);
980 #endif
981     return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueOblique));
982 }
983
984 #if ENABLE(VARIATION_FONTS)
985 static RefPtr<CSSFontStyleRangeValue> consumeFontStyleRange(CSSParserTokenRange& range, CSSParserMode cssParserMode)
986 {
987     auto keyword = consumeFontStyleKeywordValue(range);
988     if (!keyword)
989         return nullptr;
990
991     if (keyword->valueID() != CSSValueOblique || range.atEnd())
992         return CSSFontStyleRangeValue::create(keyword.releaseNonNull());
993
994     if (auto firstAngle = consumeAngle(range, cssParserMode)) {
995         if (!fontStyleIsWithinRange(firstAngle->value<float>(CSSPrimitiveValue::CSS_DEG)))
996             return nullptr;
997         if (range.atEnd()) {
998             auto result = CSSValueList::createSpaceSeparated();
999             result->append(firstAngle.releaseNonNull());
1000             return CSSFontStyleRangeValue::create(keyword.releaseNonNull(), WTFMove(result));
1001         }
1002         auto secondAngle = consumeAngle(range, cssParserMode);
1003         if (!secondAngle || !fontStyleIsWithinRange(secondAngle->value<float>(CSSPrimitiveValue::CSS_DEG)) || firstAngle->floatValue(CSSPrimitiveValue::CSS_DEG) > secondAngle->floatValue(CSSPrimitiveValue::CSS_DEG))
1004             return nullptr;
1005         auto result = CSSValueList::createSpaceSeparated();
1006         result->append(firstAngle.releaseNonNull());
1007         result->append(secondAngle.releaseNonNull());
1008         return CSSFontStyleRangeValue::create(keyword.releaseNonNull(), WTFMove(result));
1009     }
1010
1011     return nullptr;
1012 }
1013 #endif
1014
1015 static String concatenateFamilyName(CSSParserTokenRange& range)
1016 {
1017     StringBuilder builder;
1018     bool addedSpace = false;
1019     const CSSParserToken& firstToken = range.peek();
1020     while (range.peek().type() == IdentToken) {
1021         if (!builder.isEmpty()) {
1022             builder.append(' ');
1023             addedSpace = true;
1024         }
1025         builder.append(range.consumeIncludingWhitespace().value());
1026     }
1027     if (!addedSpace && isCSSWideKeyword(firstToken.id()))
1028         return String();
1029     return builder.toString();
1030 }
1031
1032 static RefPtr<CSSValue> consumeFamilyName(CSSParserTokenRange& range)
1033 {
1034     if (range.peek().type() == StringToken)
1035         return CSSValuePool::singleton().createFontFamilyValue(range.consumeIncludingWhitespace().value().toString());
1036     if (range.peek().type() != IdentToken)
1037         return nullptr;
1038     String familyName = concatenateFamilyName(range);
1039     if (familyName.isNull())
1040         return nullptr;
1041     return CSSValuePool::singleton().createFontFamilyValue(familyName);
1042 }
1043
1044 static RefPtr<CSSValue> consumeGenericFamily(CSSParserTokenRange& range)
1045 {
1046     return consumeIdentRange(range, CSSValueSerif, CSSValueWebkitBody);
1047 }
1048
1049 static RefPtr<CSSValueList> consumeFontFamily(CSSParserTokenRange& range)
1050 {
1051     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
1052     do {
1053         if (auto parsedValue = consumeGenericFamily(range))
1054             list->append(parsedValue.releaseNonNull());
1055         else {
1056             if (auto parsedValue = consumeFamilyName(range))
1057                 list->append(parsedValue.releaseNonNull());
1058             else
1059                 return nullptr;
1060         }
1061     } while (consumeCommaIncludingWhitespace(range));
1062     return list;
1063 }
1064
1065 static RefPtr<CSSValueList> consumeFontFamilyDescriptor(CSSParserTokenRange& range)
1066 {
1067     // FIXME-NEWPARSER: https://bugs.webkit.org/show_bug.cgi?id=196381 For compatibility with the old parser, we have to make
1068     // a list here, even though the list always contains only a single family name.
1069     // Once the old parser is gone, we can delete this function, make the caller
1070     // use consumeFamilyName instead, and then patch the @font-face code to
1071     // not expect a list with a single name in it.
1072     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
1073     RefPtr<CSSValue> parsedValue = consumeFamilyName(range);
1074     if (parsedValue)
1075         list->append(parsedValue.releaseNonNull());
1076     
1077     if (!range.atEnd() || !list->length())
1078         return nullptr;
1079
1080     return list;
1081 }
1082
1083 static RefPtr<CSSValue> consumeFontSynthesis(CSSParserTokenRange& range)
1084 {
1085     // none | [ weight || style || small-caps ]
1086     CSSValueID id = range.peek().id();
1087     if (id == CSSValueNone)
1088         return consumeIdent(range);
1089     
1090     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1091     while (true) {
1092         auto ident = consumeIdent<CSSValueWeight, CSSValueStyle, CSSValueSmallCaps>(range);
1093         if (!ident)
1094             break;
1095         if (list->hasValue(ident.get()))
1096             return nullptr;
1097         list->append(ident.releaseNonNull());
1098     }
1099     
1100     if (!list->length())
1101         return nullptr;
1102     return list;
1103 }
1104
1105 static RefPtr<CSSValue> consumeLetterSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1106 {
1107     if (range.peek().id() == CSSValueNormal)
1108         return consumeIdent(range);
1109     
1110     return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1111 }
1112
1113 static RefPtr<CSSValue> consumeWordSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1114 {
1115     if (range.peek().id() == CSSValueNormal)
1116         return consumeIdent(range);
1117     
1118     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1119 }
1120     
1121 static RefPtr<CSSValue> consumeTabSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1122 {
1123     auto tabSize = consumeNumber(range, ValueRangeNonNegative);
1124     if (tabSize)
1125         return tabSize;
1126     return consumeLength(range, cssParserMode, ValueRangeNonNegative);
1127 }
1128
1129 #if ENABLE(TEXT_AUTOSIZING)
1130 static RefPtr<CSSValue> consumeTextSizeAdjust(CSSParserTokenRange& range, CSSParserMode /* cssParserMode */)
1131 {
1132     if (range.peek().id() == CSSValueAuto)
1133         return consumeIdent(range);
1134     if (range.peek().id() == CSSValueNone)
1135         return consumeIdent(range);
1136     return consumePercent(range, ValueRangeNonNegative);
1137 }
1138 #endif
1139
1140 static RefPtr<CSSValue> consumeFontSize(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1141 {
1142     if (range.peek().id() >= CSSValueXxSmall && range.peek().id() <= CSSValueLarger)
1143         return consumeIdent(range);
1144     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, unitless);
1145 }
1146
1147 static RefPtr<CSSPrimitiveValue> consumeLineHeight(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1148 {
1149     if (range.peek().id() == CSSValueNormal)
1150         return consumeIdent(range);
1151
1152     RefPtr<CSSPrimitiveValue> lineHeight = consumeNumber(range, ValueRangeNonNegative);
1153     if (lineHeight)
1154         return lineHeight;
1155     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
1156 }
1157
1158 template<typename... Args>
1159 static Ref<CSSPrimitiveValue> createPrimitiveValuePair(Args&&... args)
1160 {
1161     return CSSValuePool::singleton().createValue(Pair::create(std::forward<Args>(args)...));
1162 }
1163
1164
1165 static RefPtr<CSSValue> consumeCounter(CSSParserTokenRange& range, int defaultValue)
1166 {
1167     if (range.peek().id() == CSSValueNone)
1168         return consumeIdent(range);
1169
1170     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1171     do {
1172         RefPtr<CSSPrimitiveValue> counterName = consumeCustomIdent(range);
1173         if (!counterName)
1174             return nullptr;
1175         int i = defaultValue;
1176         if (RefPtr<CSSPrimitiveValue> counterValue = consumeInteger(range))
1177             i = counterValue->intValue();
1178         list->append(createPrimitiveValuePair(counterName.releaseNonNull(), CSSPrimitiveValue::create(i, CSSPrimitiveValue::UnitType::CSS_NUMBER), Pair::IdenticalValueEncoding::Coalesce));
1179     } while (!range.atEnd());
1180     return list;
1181 }
1182
1183 static RefPtr<CSSValue> consumePageSize(CSSParserTokenRange& range)
1184 {
1185     return consumeIdent<CSSValueA3, CSSValueA4, CSSValueA5, CSSValueB4, CSSValueB5, CSSValueLedger, CSSValueLegal, CSSValueLetter>(range);
1186 }
1187
1188 static RefPtr<CSSValueList> consumeSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1189 {
1190     RefPtr<CSSValueList> result = CSSValueList::createSpaceSeparated();
1191
1192     if (range.peek().id() == CSSValueAuto) {
1193         result->append(consumeIdent(range).releaseNonNull());
1194         return result;
1195     }
1196
1197     if (RefPtr<CSSValue> width = consumeLength(range, cssParserMode, ValueRangeNonNegative)) {
1198         RefPtr<CSSValue> height = consumeLength(range, cssParserMode, ValueRangeNonNegative);
1199         result->append(width.releaseNonNull());
1200         if (height)
1201             result->append(height.releaseNonNull());
1202         return result;
1203     }
1204
1205     RefPtr<CSSValue> pageSize = consumePageSize(range);
1206     RefPtr<CSSValue> orientation = consumeIdent<CSSValuePortrait, CSSValueLandscape>(range);
1207     if (!pageSize)
1208         pageSize = consumePageSize(range);
1209
1210     if (!orientation && !pageSize)
1211         return nullptr;
1212     if (pageSize)
1213         result->append(pageSize.releaseNonNull());
1214     if (orientation)
1215         result->append(orientation.releaseNonNull());
1216     return result;
1217 }
1218
1219 static RefPtr<CSSValue> consumeTextIndent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1220 {
1221     // [ <length> | <percentage> ] && hanging? && each-line?
1222     // Keywords only allowed when css3Text is enabled.
1223     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1224
1225     bool hasLengthOrPercentage = false;
1226 //    bool hasEachLine = false;
1227     bool hasHanging = false;
1228
1229     do {
1230         if (!hasLengthOrPercentage) {
1231             if (RefPtr<CSSValue> textIndent = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow)) {
1232                 list->append(*textIndent);
1233                 hasLengthOrPercentage = true;
1234                 continue;
1235             }
1236         }
1237
1238         CSSValueID id = range.peek().id();
1239  /* FIXME-NEWPARSER: We don't support this yet.
1240         if (!hasEachLine && id == CSSValueEachLine) {
1241             list->append(*consumeIdent(range));
1242             hasEachLine = true;
1243             continue;
1244         }
1245 */
1246         
1247         if (!hasHanging && id == CSSValueHanging) {
1248             list->append(consumeIdent(range).releaseNonNull());
1249             hasHanging = true;
1250             continue;
1251         }
1252         
1253         return nullptr;
1254     } while (!range.atEnd());
1255
1256     if (!hasLengthOrPercentage)
1257         return nullptr;
1258
1259     return list;
1260 }
1261
1262 static bool validWidthOrHeightKeyword(CSSValueID id, const CSSParserContext& /*context*/)
1263 {
1264     if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic || id == CSSValueMinContent || id == CSSValueWebkitMinContent || id == CSSValueMaxContent || id == CSSValueWebkitMaxContent || id == CSSValueWebkitFillAvailable || id == CSSValueFitContent || id == CSSValueWebkitFitContent) {
1265         return true;
1266     }
1267     return false;
1268 }
1269
1270 static RefPtr<CSSValue> consumeMaxWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1271 {
1272     if (range.peek().id() == CSSValueNone || validWidthOrHeightKeyword(range.peek().id(), context))
1273         return consumeIdent(range);
1274     return consumeLengthOrPercent(range, context.mode, ValueRangeNonNegative, unitless);
1275 }
1276
1277 static RefPtr<CSSValue> consumeWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1278 {
1279     if (range.peek().id() == CSSValueAuto || validWidthOrHeightKeyword(range.peek().id(), context))
1280         return consumeIdent(range);
1281     return consumeLengthOrPercent(range, context.mode, ValueRangeNonNegative, unitless);
1282 }
1283
1284 static RefPtr<CSSValue> consumeMarginOrOffset(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1285 {
1286     if (range.peek().id() == CSSValueAuto)
1287         return consumeIdent(range);
1288     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, unitless);
1289 }
1290
1291 static RefPtr<CSSPrimitiveValue> consumeClipComponent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1292 {
1293     if (range.peek().id() == CSSValueAuto)
1294         return consumeIdent(range);
1295     return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1296 }
1297
1298 static RefPtr<CSSValue> consumeClip(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1299 {
1300     if (range.peek().id() == CSSValueAuto)
1301         return consumeIdent(range);
1302
1303     if (range.peek().functionId() != CSSValueRect)
1304         return nullptr;
1305
1306     CSSParserTokenRange args = consumeFunction(range);
1307     // rect(t, r, b, l) || rect(t r b l)
1308     RefPtr<CSSPrimitiveValue> top = consumeClipComponent(args, cssParserMode);
1309     if (!top)
1310         return nullptr;
1311     bool needsComma = consumeCommaIncludingWhitespace(args);
1312     RefPtr<CSSPrimitiveValue> right = consumeClipComponent(args, cssParserMode);
1313     if (!right || (needsComma && !consumeCommaIncludingWhitespace(args)))
1314         return nullptr;
1315     RefPtr<CSSPrimitiveValue> bottom = consumeClipComponent(args, cssParserMode);
1316     if (!bottom || (needsComma && !consumeCommaIncludingWhitespace(args)))
1317         return nullptr;
1318     RefPtr<CSSPrimitiveValue> left = consumeClipComponent(args, cssParserMode);
1319     if (!left || !args.atEnd())
1320         return nullptr;
1321     
1322     auto rect = Rect::create();
1323     rect->setLeft(left.releaseNonNull());
1324     rect->setTop(top.releaseNonNull());
1325     rect->setRight(right.releaseNonNull());
1326     rect->setBottom(bottom.releaseNonNull());
1327     return CSSValuePool::singleton().createValue(WTFMove(rect));
1328 }
1329
1330 #if ENABLE(POINTER_EVENTS)
1331 static RefPtr<CSSValue> consumeTouchAction(CSSParserTokenRange& range)
1332 {
1333     CSSValueID id = range.peek().id();
1334     if (id == CSSValueNone || id == CSSValueAuto || id == CSSValueManipulation)
1335         return consumeIdent(range);
1336
1337     auto list = CSSValueList::createSpaceSeparated();
1338     while (true) {
1339         auto ident = consumeIdent<CSSValuePanX, CSSValuePanY, CSSValuePinchZoom>(range);
1340         if (!ident)
1341             break;
1342         if (list->hasValue(ident.get()))
1343             return nullptr;
1344         list->append(ident.releaseNonNull());
1345     }
1346
1347     if (!list->length())
1348         return nullptr;
1349     return list;
1350 }
1351 #endif
1352
1353 static RefPtr<CSSPrimitiveValue> consumeLineClamp(CSSParserTokenRange& range)
1354 {
1355     RefPtr<CSSPrimitiveValue> clampValue = consumePercent(range, ValueRangeNonNegative);
1356     if (clampValue)
1357         return clampValue;
1358     // When specifying number of lines, don't allow 0 as a valid value.
1359     return consumePositiveInteger(range);
1360 }
1361
1362 static RefPtr<CSSValue> consumeAutoOrString(CSSParserTokenRange& range)
1363 {
1364     if (range.peek().id() == CSSValueAuto)
1365         return consumeIdent(range);
1366     return consumeString(range);
1367 }
1368
1369 static RefPtr<CSSValue> consumeHyphenateLimit(CSSParserTokenRange& range, CSSValueID valueID)
1370 {
1371     if (range.peek().id() == valueID)
1372         return consumeIdent(range);
1373     return consumeNumber(range, ValueRangeNonNegative);
1374 }
1375
1376 static RefPtr<CSSValue> consumeColumnWidth(CSSParserTokenRange& range)
1377 {
1378     if (range.peek().id() == CSSValueAuto)
1379         return consumeIdent(range);
1380     // Always parse lengths in strict mode here, since it would be ambiguous otherwise when used in
1381     // the 'columns' shorthand property.
1382     RefPtr<CSSPrimitiveValue> columnWidth = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
1383     if (!columnWidth || (!columnWidth->isCalculated() && !columnWidth->doubleValue()) || (columnWidth->cssCalcValue() && !columnWidth->cssCalcValue()->doubleValue()))
1384         return nullptr;
1385     return columnWidth;
1386 }
1387
1388 static RefPtr<CSSValue> consumeColumnCount(CSSParserTokenRange& range)
1389 {
1390     if (range.peek().id() == CSSValueAuto)
1391         return consumeIdent(range);
1392     return consumePositiveInteger(range);
1393 }
1394
1395 static RefPtr<CSSValue> consumeGapLength(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1396 {
1397     if (range.peek().id() == CSSValueNormal)
1398         return consumeIdent(range);
1399     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
1400 }
1401
1402 static RefPtr<CSSValue> consumeColumnSpan(CSSParserTokenRange& range)
1403 {
1404     return consumeIdent<CSSValueAll, CSSValueNone>(range);
1405 }
1406
1407 static RefPtr<CSSValue> consumeZoom(CSSParserTokenRange& range, const CSSParserContext& /*context*/)
1408 {
1409     const CSSParserToken& token = range.peek();
1410     RefPtr<CSSPrimitiveValue> zoom;
1411     if (token.type() == IdentToken)
1412         zoom = consumeIdent<CSSValueNormal, CSSValueReset, CSSValueDocument>(range);
1413     else {
1414         zoom = consumePercent(range, ValueRangeNonNegative);
1415         if (!zoom)
1416             zoom = consumeNumber(range, ValueRangeNonNegative);
1417     }
1418     return zoom;
1419 }
1420
1421 static RefPtr<CSSValue> consumeAnimationIterationCount(CSSParserTokenRange& range)
1422 {
1423     if (range.peek().id() == CSSValueInfinite)
1424         return consumeIdent(range);
1425     return consumeNumber(range, ValueRangeNonNegative);
1426 }
1427
1428 static RefPtr<CSSValue> consumeAnimationName(CSSParserTokenRange& range)
1429 {
1430     if (range.peek().id() == CSSValueNone)
1431         return consumeIdent(range);
1432
1433     if (range.peek().type() == StringToken) {
1434         const CSSParserToken& token = range.consumeIncludingWhitespace();
1435         if (equalIgnoringASCIICase(token.value(), "none"))
1436             return CSSValuePool::singleton().createIdentifierValue(CSSValueNone);
1437         // FIXME-NEWPARSER: Want to use a CSSCustomIdentValue here eventually.
1438         return CSSValuePool::singleton().createValue(token.value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
1439     }
1440
1441     return consumeCustomIdent(range);
1442 }
1443
1444 static RefPtr<CSSValue> consumeTransitionProperty(CSSParserTokenRange& range)
1445 {
1446     const CSSParserToken& token = range.peek();
1447     if (token.type() != IdentToken)
1448         return nullptr;
1449     if (token.id() == CSSValueNone)
1450         return consumeIdent(range);
1451
1452     if (CSSPropertyID property = token.parseAsCSSPropertyID()) {
1453         range.consumeIncludingWhitespace();
1454         
1455         // FIXME-NEWPARSER: No reason why we can't use the "all" property now that it exists.
1456         // The old parser used a value keyword for "all", though, since it predated support for
1457         // the property.
1458         if (property == CSSPropertyAll)
1459             return CSSValuePool::singleton().createIdentifierValue(CSSValueAll);
1460
1461         // FIXME-NEWPARSER: Want to use a CSSCustomIdentValue here eventually.
1462         return CSSValuePool::singleton().createIdentifierValue(property);
1463     }
1464     return consumeCustomIdent(range);
1465 }
1466
1467     
1468 static RefPtr<CSSValue> consumeSteps(CSSParserTokenRange& range)
1469 {
1470     ASSERT(range.peek().functionId() == CSSValueSteps);
1471     CSSParserTokenRange rangeCopy = range;
1472     CSSParserTokenRange args = consumeFunction(rangeCopy);
1473     
1474     RefPtr<CSSPrimitiveValue> steps = consumePositiveInteger(args);
1475     if (!steps)
1476         return nullptr;
1477     
1478     // FIXME-NEWPARSER: Support the middle value and change from a boolean to an enum.
1479     bool stepAtStart = false;
1480     if (consumeCommaIncludingWhitespace(args)) {
1481         switch (args.consumeIncludingWhitespace().id()) {
1482             case CSSValueStart:
1483                 stepAtStart = true;
1484             break;
1485             case CSSValueEnd:
1486                 stepAtStart = false;
1487                 break;
1488             default:
1489                 return nullptr;
1490         }
1491     }
1492     
1493     if (!args.atEnd())
1494         return nullptr;
1495     
1496     range = rangeCopy;
1497     return CSSStepsTimingFunctionValue::create(steps->intValue(), stepAtStart);
1498 }
1499
1500 static RefPtr<CSSValue> consumeCubicBezier(CSSParserTokenRange& range)
1501 {
1502     ASSERT(range.peek().functionId() == CSSValueCubicBezier);
1503     CSSParserTokenRange rangeCopy = range;
1504     CSSParserTokenRange args = consumeFunction(rangeCopy);
1505
1506     double x1, y1, x2, y2;
1507     if (consumeNumberRaw(args, x1)
1508         && x1 >= 0 && x1 <= 1
1509         && consumeCommaIncludingWhitespace(args)
1510         && consumeNumberRaw(args, y1)
1511         && consumeCommaIncludingWhitespace(args)
1512         && consumeNumberRaw(args, x2)
1513         && x2 >= 0 && x2 <= 1
1514         && consumeCommaIncludingWhitespace(args)
1515         && consumeNumberRaw(args, y2)
1516         && args.atEnd()) {
1517         range = rangeCopy;
1518         return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2);
1519     }
1520
1521     return nullptr;
1522 }
1523
1524 static RefPtr<CSSValue> consumeSpringFunction(CSSParserTokenRange& range)
1525 {
1526     ASSERT(range.peek().functionId() == CSSValueSpring);
1527     CSSParserTokenRange rangeCopy = range;
1528     CSSParserTokenRange args = consumeFunction(rangeCopy);
1529
1530     // Mass must be greater than 0.
1531     double mass;
1532     if (!consumeNumberRaw(args, mass) || mass <= 0)
1533         return nullptr;
1534     
1535     // Stiffness must be greater than 0.
1536     double stiffness;
1537     if (!consumeNumberRaw(args, stiffness) || stiffness <= 0)
1538         return nullptr;
1539     
1540     // Damping coefficient must be greater than or equal to 0.
1541     double damping;
1542     if (!consumeNumberRaw(args, damping) || damping < 0)
1543         return nullptr;
1544     
1545     // Initial velocity may have any value.
1546     double initialVelocity;
1547     if (!consumeNumberRaw(args, initialVelocity))
1548         return nullptr;
1549
1550     if (!args.atEnd())
1551         return nullptr;
1552
1553     range = rangeCopy;
1554
1555     return CSSSpringTimingFunctionValue::create(mass, stiffness, damping, initialVelocity);
1556 }
1557
1558 static RefPtr<CSSValue> consumeAnimationTimingFunction(CSSParserTokenRange& range, const CSSParserContext& context)
1559 {
1560     CSSValueID id = range.peek().id();
1561     if (id == CSSValueEase || id == CSSValueLinear || id == CSSValueEaseIn
1562         || id == CSSValueEaseOut || id == CSSValueEaseInOut || id == CSSValueStepStart || id == CSSValueStepEnd)
1563         return consumeIdent(range);
1564
1565     CSSValueID function = range.peek().functionId();
1566     if (function == CSSValueCubicBezier)
1567         return consumeCubicBezier(range);
1568     if (function == CSSValueSteps)
1569         return consumeSteps(range);
1570     if (context.springTimingFunctionEnabled && function == CSSValueSpring)
1571         return consumeSpringFunction(range);
1572     return nullptr;
1573 }
1574
1575 static RefPtr<CSSValue> consumeAnimationValue(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
1576 {
1577     switch (property) {
1578     case CSSPropertyAnimationDelay:
1579     case CSSPropertyTransitionDelay:
1580         return consumeTime(range, context.mode, ValueRangeAll, UnitlessQuirk::Forbid);
1581     case CSSPropertyAnimationDirection:
1582         return consumeIdent<CSSValueNormal, CSSValueAlternate, CSSValueReverse, CSSValueAlternateReverse>(range);
1583     case CSSPropertyAnimationDuration:
1584     case CSSPropertyTransitionDuration:
1585         return consumeTime(range, context.mode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
1586     case CSSPropertyAnimationFillMode:
1587         return consumeIdent<CSSValueNone, CSSValueForwards, CSSValueBackwards, CSSValueBoth>(range);
1588     case CSSPropertyAnimationIterationCount:
1589         return consumeAnimationIterationCount(range);
1590     case CSSPropertyAnimationName:
1591         return consumeAnimationName(range);
1592     case CSSPropertyAnimationPlayState:
1593         return consumeIdent<CSSValueRunning, CSSValuePaused>(range);
1594     case CSSPropertyTransitionProperty:
1595         return consumeTransitionProperty(range);
1596     case CSSPropertyAnimationTimingFunction:
1597     case CSSPropertyTransitionTimingFunction:
1598         return consumeAnimationTimingFunction(range, context);
1599     default:
1600         ASSERT_NOT_REACHED();
1601         return nullptr;
1602     }
1603 }
1604
1605 static bool isValidAnimationPropertyList(CSSPropertyID property, const CSSValueList& valueList)
1606 {
1607     if (property != CSSPropertyTransitionProperty || valueList.length() < 2)
1608         return true;
1609     for (auto& value : valueList) {
1610         if (value->isPrimitiveValue() && downcast<CSSPrimitiveValue>(value.get()).isValueID()
1611             && downcast<CSSPrimitiveValue>(value.get()).valueID() == CSSValueNone)
1612             return false;
1613     }
1614     return true;
1615 }
1616
1617 static RefPtr<CSSValue> consumeAnimationPropertyList(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
1618 {
1619     RefPtr<CSSValueList> list;
1620     RefPtr<CSSValue> singleton;
1621     do {
1622         RefPtr<CSSValue> currentValue = consumeAnimationValue(property, range, context);
1623         if (!currentValue)
1624             return nullptr;
1625         
1626         if (singleton && !list) {
1627             list = CSSValueList::createCommaSeparated();
1628             list->append(singleton.releaseNonNull());
1629         }
1630         
1631         if (list)
1632             list->append(currentValue.releaseNonNull());
1633         else
1634             singleton = WTFMove(currentValue);
1635         
1636     } while (consumeCommaIncludingWhitespace(range));
1637
1638     if (list) {
1639         if (!isValidAnimationPropertyList(property, *list))
1640             return nullptr;
1641     
1642         ASSERT(list->length());
1643         return list;
1644     }
1645     
1646     return singleton;
1647 }
1648
1649 bool CSSPropertyParser::consumeAnimationShorthand(const StylePropertyShorthand& shorthand, bool important)
1650 {
1651     const unsigned longhandCount = shorthand.length();
1652     RefPtr<CSSValueList> longhands[8];
1653     ASSERT(longhandCount <= 8);
1654     for (size_t i = 0; i < longhandCount; ++i)
1655         longhands[i] = CSSValueList::createCommaSeparated();
1656
1657     do {
1658         bool parsedLonghand[8] = { false };
1659         do {
1660             bool foundProperty = false;
1661             for (size_t i = 0; i < longhandCount; ++i) {
1662                 if (parsedLonghand[i])
1663                     continue;
1664
1665                 if (RefPtr<CSSValue> value = consumeAnimationValue(shorthand.properties()[i], m_range, m_context)) {
1666                     parsedLonghand[i] = true;
1667                     foundProperty = true;
1668                     longhands[i]->append(*value);
1669                     break;
1670                 }
1671             }
1672             if (!foundProperty)
1673                 return false;
1674         } while (!m_range.atEnd() && m_range.peek().type() != CommaToken);
1675
1676         // FIXME: This will make invalid longhands, see crbug.com/386459
1677         for (size_t i = 0; i < longhandCount; ++i) {
1678             if (!parsedLonghand[i])
1679                 longhands[i]->append(CSSValuePool::singleton().createImplicitInitialValue());
1680             parsedLonghand[i] = false;
1681         }
1682     } while (consumeCommaIncludingWhitespace(m_range));
1683
1684     for (size_t i = 0; i < longhandCount; ++i) {
1685         if (!isValidAnimationPropertyList(shorthand.properties()[i], *longhands[i]))
1686             return false;
1687     }
1688
1689     for (size_t i = 0; i < longhandCount; ++i)
1690         addProperty(shorthand.properties()[i], shorthand.id(), *longhands[i], important);
1691
1692     return m_range.atEnd();
1693 }
1694
1695 static RefPtr<CSSValue> consumeZIndex(CSSParserTokenRange& range)
1696 {
1697     if (range.peek().id() == CSSValueAuto)
1698         return consumeIdent(range);
1699     return consumeInteger(range);
1700 }
1701
1702 static RefPtr<CSSValue> consumeShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool isBoxShadowProperty)
1703 {
1704     if (range.peek().id() == CSSValueNone)
1705         return consumeIdent(range);
1706
1707     RefPtr<CSSValueList> shadowValueList = CSSValueList::createCommaSeparated();
1708     do {
1709         if (RefPtr<CSSShadowValue> shadowValue = consumeSingleShadow(range, cssParserMode, isBoxShadowProperty, isBoxShadowProperty))
1710             shadowValueList->append(*shadowValue);
1711         else
1712             return nullptr;
1713     } while (consumeCommaIncludingWhitespace(range));
1714     return shadowValueList;
1715 }
1716
1717 static RefPtr<CSSValue> consumeTextDecorationLine(CSSParserTokenRange& range)
1718 {
1719     CSSValueID id = range.peek().id();
1720     if (id == CSSValueNone)
1721         return consumeIdent(range);
1722
1723     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1724     while (true) {
1725 #if ENABLE(LETTERPRESS)
1726         RefPtr<CSSPrimitiveValue> ident = consumeIdent<CSSValueBlink, CSSValueUnderline, CSSValueOverline, CSSValueLineThrough, CSSValueWebkitLetterpress>(range);
1727 #else
1728         RefPtr<CSSPrimitiveValue> ident = consumeIdent<CSSValueBlink, CSSValueUnderline, CSSValueOverline, CSSValueLineThrough>(range);
1729 #endif
1730         if (!ident)
1731             break;
1732         if (list->hasValue(ident.get()))
1733             return nullptr;
1734         list->append(ident.releaseNonNull());
1735     }
1736
1737     if (!list->length())
1738         return nullptr;
1739     return list;
1740 }
1741
1742 static RefPtr<CSSValue> consumeTextDecorationSkip(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         auto ident = consumeIdent<CSSValueAuto, CSSValueInk, CSSValueObjects>(range);
1751         if (!ident)
1752             break;
1753         if (list->hasValue(ident.get()))
1754             return nullptr;
1755         list->append(ident.releaseNonNull());
1756     }
1757
1758     if (!list->length())
1759         return nullptr;
1760     return list;
1761 }
1762
1763 static RefPtr<CSSValue> consumeTextEmphasisStyle(CSSParserTokenRange& range)
1764 {
1765     CSSValueID id = range.peek().id();
1766     if (id == CSSValueNone)
1767         return consumeIdent(range);
1768
1769     if (RefPtr<CSSValue> textEmphasisStyle = consumeString(range))
1770         return textEmphasisStyle;
1771
1772     RefPtr<CSSPrimitiveValue> fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
1773     RefPtr<CSSPrimitiveValue> shape = consumeIdent<CSSValueDot, CSSValueCircle, CSSValueDoubleCircle, CSSValueTriangle, CSSValueSesame>(range);
1774     if (!fill)
1775         fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
1776     if (fill && shape) {
1777         RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
1778         parsedValues->append(fill.releaseNonNull());
1779         parsedValues->append(shape.releaseNonNull());
1780         return parsedValues;
1781     }
1782     if (fill)
1783         return fill;
1784     if (shape)
1785         return shape;
1786     return nullptr;
1787 }
1788
1789 static RefPtr<CSSPrimitiveValue> consumeCaretColor(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1790 {
1791     if (range.peek().id() == CSSValueAuto)
1792         return consumeIdent(range);
1793     return consumeColor(range, cssParserMode);
1794 }
1795
1796 static RefPtr<CSSValue> consumeOutlineColor(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1797 {
1798     // Allow the special focus color even in HTML Standard parsing mode.
1799     if (range.peek().id() == CSSValueWebkitFocusRingColor)
1800         return consumeIdent(range);
1801     return consumeColor(range, cssParserMode);
1802 }
1803
1804 static RefPtr<CSSPrimitiveValue> consumeLineWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1805 {
1806     CSSValueID id = range.peek().id();
1807     if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick)
1808         return consumeIdent(range);
1809     return consumeLength(range, cssParserMode, ValueRangeNonNegative, unitless);
1810 }
1811
1812 static RefPtr<CSSPrimitiveValue> consumeBorderWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1813 {
1814     return consumeLineWidth(range, cssParserMode, unitless);
1815 }
1816
1817 static RefPtr<CSSPrimitiveValue> consumeTextStrokeWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1818 {
1819     return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
1820 }
1821
1822 static RefPtr<CSSPrimitiveValue> consumeColumnRuleWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1823 {
1824     return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
1825 }
1826
1827 static bool consumeTranslate3d(CSSParserTokenRange& args, CSSParserMode cssParserMode, RefPtr<CSSFunctionValue>& transformValue)
1828 {
1829     unsigned numberOfArguments = 2;
1830     RefPtr<CSSValue> parsedValue;
1831     do {
1832         parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1833         if (!parsedValue)
1834             return false;
1835         transformValue->append(*parsedValue);
1836         if (!consumeCommaIncludingWhitespace(args))
1837             return false;
1838     } while (--numberOfArguments);
1839     parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
1840     if (!parsedValue)
1841         return false;
1842     transformValue->append(*parsedValue);
1843     return true;
1844 }
1845
1846 static bool consumeNumbers(CSSParserTokenRange& args, RefPtr<CSSFunctionValue>& transformValue, unsigned numberOfArguments)
1847 {
1848     do {
1849         RefPtr<CSSPrimitiveValue> parsedValue = consumeNumber(args, ValueRangeAll);
1850         if (!parsedValue)
1851             return false;
1852         transformValue->append(parsedValue.releaseNonNull());
1853         if (--numberOfArguments && !consumeCommaIncludingWhitespace(args))
1854             return false;
1855     } while (numberOfArguments);
1856     return true;
1857 }
1858
1859 static bool consumePerspective(CSSParserTokenRange& args, CSSParserMode cssParserMode, RefPtr<CSSFunctionValue>& transformValue)
1860 {
1861     RefPtr<CSSPrimitiveValue> parsedValue = consumeLength(args, cssParserMode, ValueRangeNonNegative);
1862     if (!parsedValue) {
1863         double perspective;
1864         if (!consumeNumberRaw(args, perspective) || perspective < 0)
1865             return false;
1866         parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitType::CSS_PX);
1867     }
1868     if (!parsedValue)
1869         return false;
1870     transformValue->append(parsedValue.releaseNonNull());
1871     return true;
1872 }
1873
1874 static RefPtr<CSSValue> consumeTransformValue(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1875 {
1876     CSSValueID functionId = range.peek().functionId();
1877     if (functionId == CSSValueInvalid)
1878         return nullptr;
1879     CSSParserTokenRange args = consumeFunction(range);
1880     if (args.atEnd())
1881         return nullptr;
1882     
1883     RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(functionId);
1884     RefPtr<CSSValue> parsedValue;
1885     switch (functionId) {
1886     case CSSValueRotate:
1887     case CSSValueRotateX:
1888     case CSSValueRotateY:
1889     case CSSValueRotateZ:
1890     case CSSValueSkewX:
1891     case CSSValueSkewY:
1892     case CSSValueSkew:
1893         parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1894         if (!parsedValue)
1895             return nullptr;
1896         if (functionId == CSSValueSkew && consumeCommaIncludingWhitespace(args)) {
1897             transformValue->append(*parsedValue);
1898             parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1899             if (!parsedValue)
1900                 return nullptr;
1901         }
1902         break;
1903     case CSSValueScaleX:
1904     case CSSValueScaleY:
1905     case CSSValueScaleZ:
1906     case CSSValueScale:
1907         parsedValue = consumeNumber(args, ValueRangeAll);
1908         if (!parsedValue)
1909             return nullptr;
1910         if (functionId == CSSValueScale && consumeCommaIncludingWhitespace(args)) {
1911             transformValue->append(*parsedValue);
1912             parsedValue = consumeNumber(args, ValueRangeAll);
1913             if (!parsedValue)
1914                 return nullptr;
1915         }
1916         break;
1917     case CSSValuePerspective:
1918         if (!consumePerspective(args, cssParserMode, transformValue))
1919             return nullptr;
1920         break;
1921     case CSSValueTranslateX:
1922     case CSSValueTranslateY:
1923     case CSSValueTranslate:
1924         parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1925         if (!parsedValue)
1926             return nullptr;
1927         if (functionId == CSSValueTranslate && consumeCommaIncludingWhitespace(args)) {
1928             transformValue->append(*parsedValue);
1929             parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1930             if (!parsedValue)
1931                 return nullptr;
1932         }
1933         break;
1934     case CSSValueTranslateZ:
1935         parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
1936         break;
1937     case CSSValueMatrix:
1938     case CSSValueMatrix3d:
1939         if (!consumeNumbers(args, transformValue, (functionId == CSSValueMatrix3d) ? 16 : 6))
1940             return nullptr;
1941         break;
1942     case CSSValueScale3d:
1943         if (!consumeNumbers(args, transformValue, 3))
1944             return nullptr;
1945         break;
1946     case CSSValueRotate3d:
1947         if (!consumeNumbers(args, transformValue, 3) || !consumeCommaIncludingWhitespace(args))
1948             return nullptr;
1949         parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1950         if (!parsedValue)
1951             return nullptr;
1952         break;
1953     case CSSValueTranslate3d:
1954         if (!consumeTranslate3d(args, cssParserMode, transformValue))
1955             return nullptr;
1956         break;
1957     default:
1958         return nullptr;
1959     }
1960     if (parsedValue)
1961         transformValue->append(*parsedValue);
1962     if (!args.atEnd())
1963         return nullptr;
1964     return transformValue;
1965 }
1966
1967 static RefPtr<CSSValue> consumeTransform(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1968 {
1969     if (range.peek().id() == CSSValueNone)
1970         return consumeIdent(range);
1971
1972     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1973     do {
1974         RefPtr<CSSValue> parsedTransformValue = consumeTransformValue(range, cssParserMode);
1975         if (!parsedTransformValue)
1976             return nullptr;
1977         list->append(parsedTransformValue.releaseNonNull());
1978     } while (!range.atEnd());
1979
1980     return list;
1981 }
1982
1983 template <CSSValueID start, CSSValueID end>
1984 static RefPtr<CSSPrimitiveValue> consumePositionLonghand(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1985 {
1986     if (range.peek().type() == IdentToken) {
1987         CSSValueID id = range.peek().id();
1988         int percent;
1989         if (id == start)
1990             percent = 0;
1991         else if (id == CSSValueCenter)
1992             percent = 50;
1993         else if (id == end)
1994             percent = 100;
1995         else
1996             return nullptr;
1997         range.consumeIncludingWhitespace();
1998         return CSSPrimitiveValue::create(percent, CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
1999     }
2000     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
2001 }
2002
2003 static RefPtr<CSSPrimitiveValue> consumePositionX(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2004 {
2005     return consumePositionLonghand<CSSValueLeft, CSSValueRight>(range, cssParserMode);
2006 }
2007
2008 static RefPtr<CSSPrimitiveValue> consumePositionY(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2009 {
2010     return consumePositionLonghand<CSSValueTop, CSSValueBottom>(range, cssParserMode);
2011 }
2012
2013 static RefPtr<CSSValue> consumePaintStroke(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2014 {
2015     if (range.peek().id() == CSSValueNone)
2016         return consumeIdent(range);
2017     RefPtr<CSSPrimitiveValue> url = consumeUrl(range);
2018     if (url) {
2019         RefPtr<CSSValue> parsedValue;
2020         if (range.peek().id() == CSSValueNone)
2021             parsedValue = consumeIdent(range);
2022         else
2023             parsedValue = consumeColor(range, cssParserMode);
2024         if (parsedValue) {
2025             RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
2026             values->append(url.releaseNonNull());
2027             values->append(parsedValue.releaseNonNull());
2028             return values;
2029         }
2030         return url;
2031     }
2032     return consumeColor(range, cssParserMode);
2033 }
2034
2035 static RefPtr<CSSValue> consumeGlyphOrientation(CSSParserTokenRange& range, CSSParserMode mode, CSSPropertyID property)
2036 {
2037     if (range.peek().id() == CSSValueAuto) {
2038         if (property == CSSPropertyGlyphOrientationVertical)
2039             return consumeIdent(range);
2040         return nullptr;
2041     }
2042     
2043     return consumeAngle(range, mode, UnitlessQuirk::Allow);
2044 }
2045
2046 static RefPtr<CSSValue> consumePaintOrder(CSSParserTokenRange& range)
2047 {
2048     if (range.peek().id() == CSSValueNormal)
2049         return consumeIdent(range);
2050
2051     Vector<CSSValueID, 3> paintTypeList;
2052     RefPtr<CSSPrimitiveValue> fill;
2053     RefPtr<CSSPrimitiveValue> stroke;
2054     RefPtr<CSSPrimitiveValue> markers;
2055     do {
2056         CSSValueID id = range.peek().id();
2057         if (id == CSSValueFill && !fill)
2058             fill = consumeIdent(range);
2059         else if (id == CSSValueStroke && !stroke)
2060             stroke = consumeIdent(range);
2061         else if (id == CSSValueMarkers && !markers)
2062             markers = consumeIdent(range);
2063         else
2064             return nullptr;
2065         paintTypeList.append(id);
2066     } while (!range.atEnd());
2067
2068     // After parsing we serialize the paint-order list. Since it is not possible to
2069     // pop a last list items from CSSValueList without bigger cost, we create the
2070     // list after parsing.
2071     CSSValueID firstPaintOrderType = paintTypeList.at(0);
2072     RefPtr<CSSValueList> paintOrderList = CSSValueList::createSpaceSeparated();
2073     switch (firstPaintOrderType) {
2074     case CSSValueFill:
2075     case CSSValueStroke:
2076         paintOrderList->append(firstPaintOrderType == CSSValueFill ? fill.releaseNonNull() : stroke.releaseNonNull());
2077         if (paintTypeList.size() > 1) {
2078             if (paintTypeList.at(1) == CSSValueMarkers)
2079                 paintOrderList->append(markers.releaseNonNull());
2080         }
2081         break;
2082     case CSSValueMarkers:
2083         paintOrderList->append(markers.releaseNonNull());
2084         if (paintTypeList.size() > 1) {
2085             if (paintTypeList.at(1) == CSSValueStroke)
2086                 paintOrderList->append(stroke.releaseNonNull());
2087         }
2088         break;
2089     default:
2090         ASSERT_NOT_REACHED();
2091     }
2092
2093     return paintOrderList;
2094 }
2095
2096 static RefPtr<CSSValue> consumeNoneOrURI(CSSParserTokenRange& range)
2097 {
2098     if (range.peek().id() == CSSValueNone)
2099         return consumeIdent(range);
2100     return consumeUrl(range);
2101 }
2102
2103 static RefPtr<CSSValue> consumeFlexBasis(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2104 {
2105     // FIXME: Support intrinsic dimensions too.
2106     if (range.peek().id() == CSSValueAuto)
2107         return consumeIdent(range);
2108     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2109 }
2110
2111 static RefPtr<CSSValue> consumeKerning(CSSParserTokenRange& range, CSSParserMode mode)
2112 {
2113     RefPtr<CSSValue> result = consumeIdent<CSSValueAuto, CSSValueNormal>(range);
2114     if (result)
2115         return result;
2116     return consumeLength(range, mode, ValueRangeAll, UnitlessQuirk::Allow);
2117 }
2118
2119 static RefPtr<CSSValue> consumeStrokeDasharray(CSSParserTokenRange& range)
2120 {
2121     CSSValueID id = range.peek().id();
2122     if (id == CSSValueNone)
2123         return consumeIdent(range);
2124
2125     RefPtr<CSSValueList> dashes = CSSValueList::createCommaSeparated();
2126     do {
2127         RefPtr<CSSPrimitiveValue> dash = consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeNonNegative);
2128         if (!dash || (consumeCommaIncludingWhitespace(range) && range.atEnd()))
2129             return nullptr;
2130         dashes->append(dash.releaseNonNull());
2131     } while (!range.atEnd());
2132     return dashes;
2133 }
2134
2135 static RefPtr<CSSPrimitiveValue> consumeBaselineShift(CSSParserTokenRange& range)
2136 {
2137     CSSValueID id = range.peek().id();
2138     if (id == CSSValueBaseline || id == CSSValueSub || id == CSSValueSuper)
2139         return consumeIdent(range);
2140     return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll);
2141 }
2142
2143 static RefPtr<CSSPrimitiveValue> consumeRxOrRy(CSSParserTokenRange& range)
2144 {
2145     if (range.peek().id() == CSSValueAuto)
2146         return consumeIdent(range);
2147     return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
2148 }
2149
2150 static RefPtr<CSSValue> consumeCursor(CSSParserTokenRange& range, const CSSParserContext& context, bool inQuirksMode)
2151 {
2152     RefPtr<CSSValueList> list;
2153     while (RefPtr<CSSValue> image = consumeImage(range, context, ConsumeGeneratedImage::Forbid)) {
2154         double num;
2155         IntPoint hotSpot(-1, -1);
2156         bool hotSpotSpecified = false;
2157         if (consumeNumberRaw(range, num)) {
2158             hotSpot.setX(int(num));
2159             if (!consumeNumberRaw(range, num))
2160                 return nullptr;
2161             hotSpot.setY(int(num));
2162             hotSpotSpecified = true;
2163         }
2164
2165         if (!list)
2166             list = CSSValueList::createCommaSeparated();
2167
2168         list->append(CSSCursorImageValue::create(image.releaseNonNull(), hotSpotSpecified, hotSpot, context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No));
2169         if (!consumeCommaIncludingWhitespace(range))
2170             return nullptr;
2171     }
2172
2173     CSSValueID id = range.peek().id();
2174     RefPtr<CSSValue> cursorType;
2175     if (id == CSSValueHand) {
2176         if (!inQuirksMode) // Non-standard behavior
2177             return nullptr;
2178         cursorType = CSSValuePool::singleton().createIdentifierValue(CSSValuePointer);
2179         range.consumeIncludingWhitespace();
2180     } else if ((id >= CSSValueAuto && id <= CSSValueWebkitZoomOut) || id == CSSValueCopy || id == CSSValueNone) {
2181         cursorType = consumeIdent(range);
2182     } else {
2183         return nullptr;
2184     }
2185
2186     if (!list)
2187         return cursorType;
2188     list->append(cursorType.releaseNonNull());
2189     return list;
2190 }
2191
2192 static RefPtr<CSSValue> consumeAttr(CSSParserTokenRange args, CSSParserContext context)
2193 {
2194     if (args.peek().type() != IdentToken)
2195         return nullptr;
2196     
2197     CSSParserToken token = args.consumeIncludingWhitespace();
2198     auto attrName = token.value().toAtomString();
2199     if (context.isHTMLDocument)
2200         attrName = attrName.convertToASCIILowercase();
2201
2202     if (!args.atEnd())
2203         return nullptr;
2204
2205     // FIXME-NEWPARSER: We want to use a CSSCustomIdentValue here eventually for the attrName.
2206     // FIXME-NEWPARSER: We want to use a CSSFunctionValue rather than relying on a custom
2207     // attr() primitive value.
2208     return CSSValuePool::singleton().createValue(attrName, CSSPrimitiveValue::CSS_ATTR);
2209 }
2210
2211 static RefPtr<CSSValue> consumeCounterContent(CSSParserTokenRange args, bool counters)
2212 {
2213     RefPtr<CSSPrimitiveValue> identifier = consumeCustomIdent(args);
2214     if (!identifier)
2215         return nullptr;
2216
2217     RefPtr<CSSPrimitiveValue> separator;
2218     if (!counters)
2219         separator = CSSPrimitiveValue::create(String(), CSSPrimitiveValue::UnitType::CSS_STRING);
2220     else {
2221         if (!consumeCommaIncludingWhitespace(args) || args.peek().type() != StringToken)
2222             return nullptr;
2223         separator = CSSPrimitiveValue::create(args.consumeIncludingWhitespace().value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
2224     }
2225
2226     RefPtr<CSSPrimitiveValue> listStyle;
2227     if (consumeCommaIncludingWhitespace(args)) {
2228         CSSValueID id = args.peek().id();
2229         if ((id != CSSValueNone && (id < CSSValueDisc || id > CSSValueKatakanaIroha)))
2230             return nullptr;
2231         listStyle = consumeIdent(args);
2232     } else
2233         listStyle = CSSValuePool::singleton().createIdentifierValue(CSSValueDecimal);
2234
2235     if (!args.atEnd())
2236         return nullptr;
2237     
2238     // FIXME-NEWPARSER: Should just have a CSSCounterValue.
2239     return CSSValuePool::singleton().createValue(Counter::create(identifier.releaseNonNull(), listStyle.releaseNonNull(), separator.releaseNonNull()));
2240 }
2241
2242 static RefPtr<CSSValue> consumeContent(CSSParserTokenRange& range, CSSParserContext context)
2243 {
2244     if (identMatches<CSSValueNone, CSSValueNormal>(range.peek().id()))
2245         return consumeIdent(range);
2246
2247     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
2248
2249     do {
2250         RefPtr<CSSValue> parsedValue = consumeImage(range, context);
2251         if (!parsedValue)
2252             parsedValue = consumeIdent<CSSValueOpenQuote, CSSValueCloseQuote, CSSValueNoOpenQuote, CSSValueNoCloseQuote>(range);
2253         if (!parsedValue)
2254             parsedValue = consumeString(range);
2255         if (!parsedValue) {
2256             if (range.peek().functionId() == CSSValueAttr)
2257                 parsedValue = consumeAttr(consumeFunction(range), context);
2258             else if (range.peek().functionId() == CSSValueCounter)
2259                 parsedValue = consumeCounterContent(consumeFunction(range), false);
2260             else if (range.peek().functionId() == CSSValueCounters)
2261                 parsedValue = consumeCounterContent(consumeFunction(range), true);
2262             if (!parsedValue)
2263                 return nullptr;
2264         }
2265         values->append(parsedValue.releaseNonNull());
2266     } while (!range.atEnd());
2267
2268     return values;
2269 }
2270
2271 static RefPtr<CSSPrimitiveValue> consumePerspective(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2272 {
2273     if (range.peek().id() == CSSValueNone)
2274         return consumeIdent(range);
2275     RefPtr<CSSPrimitiveValue> parsedValue = consumeLength(range, cssParserMode, ValueRangeAll);
2276     if (!parsedValue) {
2277         // FIXME: Make this quirk only apply to the webkit prefixed version of the property.
2278         double perspective;
2279         if (!consumeNumberRaw(range, perspective))
2280             return nullptr;
2281         parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitType::CSS_PX);
2282     }
2283     if (parsedValue && (parsedValue->isCalculated() || parsedValue->doubleValue() > 0))
2284         return parsedValue;
2285     return nullptr;
2286 }
2287
2288 #if ENABLE(CSS_SCROLL_SNAP)
2289
2290 static RefPtr<CSSValueList> consumeScrollSnapAlign(CSSParserTokenRange& range)
2291 {
2292     RefPtr<CSSValueList> alignmentValue = CSSValueList::createSpaceSeparated();
2293     if (RefPtr<CSSPrimitiveValue> firstValue = consumeIdent<CSSValueNone, CSSValueStart, CSSValueCenter, CSSValueEnd>(range)) {
2294         alignmentValue->append(firstValue.releaseNonNull());
2295         if (auto secondValue = consumeIdent<CSSValueNone, CSSValueStart, CSSValueCenter, CSSValueEnd>(range))
2296             alignmentValue->append(secondValue.releaseNonNull());
2297     }
2298     return alignmentValue->length() ? alignmentValue : nullptr;
2299 }
2300
2301 static RefPtr<CSSValueList> consumeScrollSnapType(CSSParserTokenRange& range)
2302 {
2303     RefPtr<CSSValueList> typeValue = CSSValueList::createSpaceSeparated();
2304     RefPtr<CSSPrimitiveValue> secondValue;
2305
2306     auto firstValue = consumeIdent<CSSValueX, CSSValueY, CSSValueBlock, CSSValueInline, CSSValueBoth>(range);
2307     if (firstValue)
2308         secondValue = consumeIdent<CSSValueProximity, CSSValueMandatory>(range);
2309     else
2310         firstValue = consumeIdent<CSSValueNone, CSSValueProximity, CSSValueMandatory>(range);
2311
2312     if (!firstValue)
2313         return nullptr;
2314
2315     typeValue->append(firstValue.releaseNonNull());
2316     if (secondValue)
2317         typeValue->append(secondValue.releaseNonNull());
2318
2319     return typeValue;
2320 }
2321
2322 #endif
2323
2324 static RefPtr<CSSValue> consumeBorderRadiusCorner(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2325 {
2326     RefPtr<CSSPrimitiveValue> parsedValue1 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2327     if (!parsedValue1)
2328         return nullptr;
2329     RefPtr<CSSPrimitiveValue> parsedValue2 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2330     if (!parsedValue2)
2331         parsedValue2 = parsedValue1;
2332     return createPrimitiveValuePair(parsedValue1.releaseNonNull(), parsedValue2.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2333 }
2334
2335 static RefPtr<CSSValue> consumeTextUnderlineOffset(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2336 {
2337     if (auto value = consumeIdent<CSSValueAuto>(range))
2338         return value;
2339     return consumeLength(range, cssParserMode, ValueRangeAll);
2340 }
2341
2342 static RefPtr<CSSValue> consumeTextDecorationThickness(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2343 {
2344     if (auto value = consumeIdent<CSSValueAuto, CSSValueFromFont>(range))
2345         return value;
2346     return consumeLength(range, cssParserMode, ValueRangeAll);
2347 }
2348
2349 static RefPtr<CSSPrimitiveValue> consumeVerticalAlign(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2350 {
2351     RefPtr<CSSPrimitiveValue> parsedValue = consumeIdentRange(range, CSSValueBaseline, CSSValueWebkitBaselineMiddle);
2352     if (!parsedValue)
2353         parsedValue = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2354     return parsedValue;
2355 }
2356
2357 static RefPtr<CSSPrimitiveValue> consumeShapeRadius(CSSParserTokenRange& args, CSSParserMode cssParserMode)
2358 {
2359     if (identMatches<CSSValueClosestSide, CSSValueFarthestSide>(args.peek().id()))
2360         return consumeIdent(args);
2361     return consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
2362 }
2363
2364 static RefPtr<CSSBasicShapeCircle> consumeBasicShapeCircle(CSSParserTokenRange& args, const CSSParserContext& context)
2365 {
2366     // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
2367     // circle( [<shape-radius>]? [at <position>]? )
2368     RefPtr<CSSBasicShapeCircle> shape = CSSBasicShapeCircle::create();
2369     if (RefPtr<CSSPrimitiveValue> radius = consumeShapeRadius(args, context.mode))
2370         shape->setRadius(radius.releaseNonNull());
2371     if (consumeIdent<CSSValueAt>(args)) {
2372         RefPtr<CSSPrimitiveValue> centerX;
2373         RefPtr<CSSPrimitiveValue> centerY;
2374         if (!consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY))
2375             return nullptr;
2376         shape->setCenterX(centerX.releaseNonNull());
2377         shape->setCenterY(centerY.releaseNonNull());
2378     }
2379     return shape;
2380 }
2381
2382 static RefPtr<CSSBasicShapeEllipse> consumeBasicShapeEllipse(CSSParserTokenRange& args, const CSSParserContext& context)
2383 {
2384     // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
2385     // ellipse( [<shape-radius>{2}]? [at <position>]? )
2386     auto shape = CSSBasicShapeEllipse::create();
2387     if (auto radiusX = consumeShapeRadius(args, context.mode)) {
2388         auto radiusY = consumeShapeRadius(args, context.mode);
2389         if (!radiusY)
2390             return nullptr;
2391         shape->setRadiusX(radiusX.releaseNonNull());
2392         shape->setRadiusY(radiusY.releaseNonNull());
2393     }
2394     if (consumeIdent<CSSValueAt>(args)) {
2395         RefPtr<CSSPrimitiveValue> centerX;
2396         RefPtr<CSSPrimitiveValue> centerY;
2397         if (!consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY))
2398             return nullptr;
2399         shape->setCenterX(centerX.releaseNonNull());
2400         shape->setCenterY(centerY.releaseNonNull());
2401     }
2402     return shape;
2403 }
2404
2405 static RefPtr<CSSBasicShapePolygon> consumeBasicShapePolygon(CSSParserTokenRange& args, const CSSParserContext& context)
2406 {
2407     RefPtr<CSSBasicShapePolygon> shape = CSSBasicShapePolygon::create();
2408     if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
2409         shape->setWindRule(args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? WindRule::EvenOdd : WindRule::NonZero);
2410         if (!consumeCommaIncludingWhitespace(args))
2411             return nullptr;
2412     }
2413
2414     do {
2415         RefPtr<CSSPrimitiveValue> xLength = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2416         if (!xLength)
2417             return nullptr;
2418         RefPtr<CSSPrimitiveValue> yLength = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2419         if (!yLength)
2420             return nullptr;
2421         shape->appendPoint(xLength.releaseNonNull(), yLength.releaseNonNull());
2422     } while (consumeCommaIncludingWhitespace(args));
2423     return shape;
2424 }
2425
2426 static RefPtr<CSSBasicShapePath> consumeBasicShapePath(CSSParserTokenRange& args)
2427 {
2428     WindRule windRule = WindRule::NonZero;
2429     if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
2430         windRule = args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? WindRule::EvenOdd : WindRule::NonZero;
2431         if (!consumeCommaIncludingWhitespace(args))
2432             return nullptr;
2433     }
2434
2435     if (args.peek().type() != StringToken)
2436         return nullptr;
2437     
2438     auto byteStream = makeUnique<SVGPathByteStream>();
2439     if (!buildSVGPathByteStreamFromString(args.consumeIncludingWhitespace().value().toString(), *byteStream, UnalteredParsing))
2440         return nullptr;
2441     
2442     auto shape = CSSBasicShapePath::create(WTFMove(byteStream));
2443     shape->setWindRule(windRule);
2444     
2445     return shape;
2446 }
2447
2448 static void complete4Sides(RefPtr<CSSPrimitiveValue> side[4])
2449 {
2450     if (side[3])
2451         return;
2452     if (!side[2]) {
2453         if (!side[1])
2454             side[1] = side[0];
2455         side[2] = side[0];
2456     }
2457     side[3] = side[1];
2458 }
2459
2460 static bool consumeRadii(RefPtr<CSSPrimitiveValue> horizontalRadii[4], RefPtr<CSSPrimitiveValue> verticalRadii[4], CSSParserTokenRange& range, CSSParserMode cssParserMode, bool useLegacyParsing)
2461 {
2462     unsigned i = 0;
2463     for (; i < 4 && !range.atEnd() && range.peek().type() != DelimiterToken; ++i) {
2464         horizontalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2465         if (!horizontalRadii[i])
2466             return false;
2467     }
2468     if (!horizontalRadii[0])
2469         return false;
2470     if (range.atEnd()) {
2471         // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2;
2472         if (useLegacyParsing && i == 2) {
2473             verticalRadii[0] = horizontalRadii[1];
2474             horizontalRadii[1] = nullptr;
2475         } else {
2476             complete4Sides(horizontalRadii);
2477             for (unsigned i = 0; i < 4; ++i)
2478                 verticalRadii[i] = horizontalRadii[i];
2479             return true;
2480         }
2481     } else {
2482         if (!consumeSlashIncludingWhitespace(range))
2483             return false;
2484         for (i = 0; i < 4 && !range.atEnd(); ++i) {
2485             verticalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2486             if (!verticalRadii[i])
2487                 return false;
2488         }
2489         if (!verticalRadii[0] || !range.atEnd())
2490             return false;
2491     }
2492     complete4Sides(horizontalRadii);
2493     complete4Sides(verticalRadii);
2494     return true;
2495 }
2496
2497 static RefPtr<CSSBasicShapeInset> consumeBasicShapeInset(CSSParserTokenRange& args, const CSSParserContext& context)
2498 {
2499     RefPtr<CSSBasicShapeInset> shape = CSSBasicShapeInset::create();
2500     RefPtr<CSSPrimitiveValue> top = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2501     if (!top)
2502         return nullptr;
2503     RefPtr<CSSPrimitiveValue> right = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2504     RefPtr<CSSPrimitiveValue> bottom;
2505     RefPtr<CSSPrimitiveValue> left;
2506     if (right) {
2507         bottom = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2508         if (bottom)
2509             left = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2510     }
2511     if (left)
2512         shape->updateShapeSize4Values(top.releaseNonNull(), right.releaseNonNull(), bottom.releaseNonNull(), left.releaseNonNull());
2513     else if (bottom)
2514         shape->updateShapeSize3Values(top.releaseNonNull(), right.releaseNonNull(), bottom.releaseNonNull());
2515     else if (right)
2516         shape->updateShapeSize2Values(top.releaseNonNull(), right.releaseNonNull());
2517     else
2518         shape->updateShapeSize1Value(top.releaseNonNull());
2519
2520     if (consumeIdent<CSSValueRound>(args)) {
2521         RefPtr<CSSPrimitiveValue> horizontalRadii[4] = { 0 };
2522         RefPtr<CSSPrimitiveValue> verticalRadii[4] = { 0 };
2523         if (!consumeRadii(horizontalRadii, verticalRadii, args, context.mode, false))
2524             return nullptr;
2525         shape->setTopLeftRadius(createPrimitiveValuePair(horizontalRadii[0].releaseNonNull(), verticalRadii[0].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2526         shape->setTopRightRadius(createPrimitiveValuePair(horizontalRadii[1].releaseNonNull(), verticalRadii[1].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2527         shape->setBottomRightRadius(createPrimitiveValuePair(horizontalRadii[2].releaseNonNull(), verticalRadii[2].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2528         shape->setBottomLeftRadius(createPrimitiveValuePair(horizontalRadii[3].releaseNonNull(), verticalRadii[3].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2529     }
2530     return shape;
2531 }
2532
2533 static RefPtr<CSSValue> consumeBasicShape(CSSParserTokenRange& range, const CSSParserContext& context)
2534 {
2535     RefPtr<CSSValue> result;
2536     if (range.peek().type() != FunctionToken)
2537         return nullptr;
2538     CSSValueID id = range.peek().functionId();
2539     CSSParserTokenRange rangeCopy = range;
2540     CSSParserTokenRange args = consumeFunction(rangeCopy);
2541     
2542     // FIXME-NEWPARSER: CSSBasicShape should be a CSSValue, and shapes should not be primitive values.
2543     RefPtr<CSSBasicShape> shape;
2544     if (id == CSSValueCircle)
2545         shape = consumeBasicShapeCircle(args, context);
2546     else if (id == CSSValueEllipse)
2547         shape = consumeBasicShapeEllipse(args, context);
2548     else if (id == CSSValuePolygon)
2549         shape = consumeBasicShapePolygon(args, context);
2550     else if (id == CSSValueInset)
2551         shape = consumeBasicShapeInset(args, context);
2552     else if (id == CSSValuePath)
2553         shape = consumeBasicShapePath(args);
2554     if (!shape)
2555         return nullptr;
2556     range = rangeCopy;
2557     
2558     if (!args.atEnd())
2559         return nullptr;
2560
2561     return CSSValuePool::singleton().createValue(shape.releaseNonNull());
2562 }
2563
2564 static RefPtr<CSSValue> consumeBasicShapeOrBox(CSSParserTokenRange& range, const CSSParserContext& context)
2565 {
2566     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
2567     bool shapeFound = false;
2568     bool boxFound = false;
2569     while (!range.atEnd() && !(shapeFound && boxFound)) {
2570         RefPtr<CSSValue> componentValue;
2571         if (range.peek().type() == FunctionToken && !shapeFound) {
2572             componentValue = consumeBasicShape(range, context);
2573             shapeFound = true;
2574         } else if (range.peek().type() == IdentToken && !boxFound) {
2575             componentValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox, CSSValueFillBox, CSSValueStrokeBox, CSSValueViewBox>(range);
2576             boxFound = true;
2577         }
2578         if (!componentValue)
2579             return nullptr;
2580         list->append(componentValue.releaseNonNull());
2581     }
2582     
2583     if (!range.atEnd() || !list->length())
2584         return nullptr;
2585     
2586     return list;
2587 }
2588     
2589 static RefPtr<CSSValue> consumeWebkitClipPath(CSSParserTokenRange& range, const CSSParserContext& context)
2590 {
2591     if (range.peek().id() == CSSValueNone)
2592         return consumeIdent(range);
2593     if (RefPtr<CSSPrimitiveValue> url = consumeUrl(range))
2594         return url;
2595     return consumeBasicShapeOrBox(range, context);
2596 }
2597
2598 static RefPtr<CSSValue> consumeShapeOutside(CSSParserTokenRange& range, const CSSParserContext& context)
2599 {
2600     if (RefPtr<CSSValue> imageValue = consumeImageOrNone(range, context))
2601         return imageValue;
2602     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
2603     if (RefPtr<CSSValue> boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
2604         list->append(boxValue.releaseNonNull());
2605     if (RefPtr<CSSValue> shapeValue = consumeBasicShape(range, context)) {
2606         list->append(shapeValue.releaseNonNull());
2607         if (list->length() < 2) {
2608             if (RefPtr<CSSValue> boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
2609                 list->append(boxValue.releaseNonNull());
2610         }
2611     }
2612     if (!list->length())
2613         return nullptr;
2614     return list;
2615 }
2616
2617 static bool isAuto(CSSValueID id)
2618 {
2619     return identMatches<CSSValueAuto>(id);
2620 }
2621
2622 static bool isNormalOrStretch(CSSValueID id)
2623 {
2624     return identMatches<CSSValueNormal, CSSValueStretch>(id);
2625 }
2626
2627 static bool isLeftOrRightKeyword(CSSValueID id)
2628 {
2629     return identMatches<CSSValueLeft, CSSValueRight>(id);
2630 }
2631
2632 static bool isContentDistributionKeyword(CSSValueID id)
2633 {
2634     return identMatches<CSSValueSpaceBetween, CSSValueSpaceAround, CSSValueSpaceEvenly, CSSValueStretch>(id);
2635 }
2636
2637 static bool isContentPositionKeyword(CSSValueID id)
2638 {
2639     return identMatches<CSSValueStart, CSSValueEnd, CSSValueCenter, CSSValueFlexStart, CSSValueFlexEnd>(id);
2640 }
2641
2642 static bool isContentPositionOrLeftOrRightKeyword(CSSValueID id)
2643 {
2644     return isContentPositionKeyword(id) || isLeftOrRightKeyword(id);
2645 }
2646
2647 static bool isOverflowKeyword(CSSValueID id)
2648 {
2649     return CSSPropertyParserHelpers::identMatches<CSSValueUnsafe, CSSValueSafe>(id);
2650 }
2651
2652 static bool isBaselineKeyword(CSSValueID id)
2653 {
2654     return identMatches<CSSValueFirst, CSSValueLast, CSSValueBaseline>(id);
2655 }
2656
2657 static RefPtr<CSSPrimitiveValue> consumeOverflowPositionKeyword(CSSParserTokenRange& range)
2658 {
2659     return isOverflowKeyword(range.peek().id()) ? consumeIdent(range) : nullptr;
2660 }
2661
2662 static CSSValueID getBaselineKeyword(RefPtr<CSSValue> value)
2663 {
2664     auto& primitiveValue = downcast<CSSPrimitiveValue>(*value);
2665     if (primitiveValue.pairValue()) {
2666         ASSERT(primitiveValue.pairValue()->first()->valueID() == CSSValueLast);
2667         ASSERT(primitiveValue.pairValue()->second()->valueID() == CSSValueBaseline);
2668         return CSSValueLastBaseline;
2669     }
2670     ASSERT(primitiveValue.valueID() == CSSValueBaseline);
2671     return CSSValueBaseline;
2672 }
2673
2674 static RefPtr<CSSValue> consumeBaselineKeyword(CSSParserTokenRange& range)
2675 {
2676     RefPtr<CSSPrimitiveValue> preference = consumeIdent<CSSValueFirst, CSSValueLast>(range);
2677     RefPtr<CSSPrimitiveValue> baseline = consumeIdent<CSSValueBaseline>(range);
2678     if (!baseline)
2679         return nullptr;
2680     if (preference && preference->valueID() == CSSValueLast)
2681         return createPrimitiveValuePair(preference.releaseNonNull(), baseline.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2682     return baseline;
2683 }
2684
2685 using IsPositionKeyword = bool (*)(CSSValueID);
2686
2687 static RefPtr<CSSValue> consumeContentDistributionOverflowPosition(CSSParserTokenRange& range, IsPositionKeyword isPositionKeyword)
2688 {
2689     ASSERT(isPositionKeyword);
2690     CSSValueID id = range.peek().id();
2691     if (identMatches<CSSValueNormal>(id))
2692         return CSSContentDistributionValue::create(CSSValueInvalid, range.consumeIncludingWhitespace().id(), CSSValueInvalid);
2693
2694     if (isBaselineKeyword(id)) {
2695         RefPtr<CSSValue> baseline = consumeBaselineKeyword(range);
2696         if (!baseline)
2697             return nullptr;
2698         return CSSContentDistributionValue::create(CSSValueInvalid, getBaselineKeyword(baseline), CSSValueInvalid);
2699     }
2700
2701     if (isContentDistributionKeyword(id))
2702         return CSSContentDistributionValue::create(range.consumeIncludingWhitespace().id(), CSSValueInvalid, CSSValueInvalid);
2703
2704     CSSValueID overflow = isOverflowKeyword(id) ? range.consumeIncludingWhitespace().id() : CSSValueInvalid;
2705     if (isPositionKeyword(range.peek().id()))
2706         return CSSContentDistributionValue::create(CSSValueInvalid, range.consumeIncludingWhitespace().id(), overflow);
2707
2708     return nullptr;
2709 }
2710
2711 static RefPtr<CSSPrimitiveValue> consumeBorderImageRepeatKeyword(CSSParserTokenRange& range)
2712 {
2713     return consumeIdent<CSSValueStretch, CSSValueRepeat, CSSValueSpace, CSSValueRound>(range);
2714 }
2715
2716 static RefPtr<CSSValue> consumeBorderImageRepeat(CSSParserTokenRange& range)
2717 {
2718     RefPtr<CSSPrimitiveValue> horizontal = consumeBorderImageRepeatKeyword(range);
2719     if (!horizontal)
2720         return nullptr;
2721     RefPtr<CSSPrimitiveValue> vertical = consumeBorderImageRepeatKeyword(range);
2722     if (!vertical)
2723         vertical = horizontal;
2724     return createPrimitiveValuePair(horizontal.releaseNonNull(), vertical.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2725 }
2726
2727 static RefPtr<CSSValue> consumeBorderImageSlice(CSSPropertyID property, CSSParserTokenRange& range)
2728 {
2729     bool fill = consumeIdent<CSSValueFill>(range);
2730     RefPtr<CSSPrimitiveValue> slices[4] = { 0 };
2731
2732     for (size_t index = 0; index < 4; ++index) {
2733         RefPtr<CSSPrimitiveValue> value = consumePercent(range, ValueRangeNonNegative);
2734         if (!value)
2735             value = consumeNumber(range, ValueRangeNonNegative);
2736         if (!value)
2737             break;
2738         slices[index] = value;
2739     }
2740     if (!slices[0])
2741         return nullptr;
2742     if (consumeIdent<CSSValueFill>(range)) {
2743         if (fill)
2744             return nullptr;
2745         fill = true;
2746     }
2747     complete4Sides(slices);
2748     // FIXME: For backwards compatibility, -webkit-border-image, -webkit-mask-box-image and -webkit-box-reflect have to do a fill by default.
2749     // FIXME: What do we do with -webkit-box-reflect and -webkit-mask-box-image? Probably just have to leave them filling...
2750     if (property == CSSPropertyWebkitBorderImage || property == CSSPropertyWebkitMaskBoxImage || property == CSSPropertyWebkitBoxReflect)
2751         fill = true;
2752     
2753     // Now build a rect value to hold all four of our primitive values.
2754     // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2755     auto quad = Quad::create();
2756     quad->setTop(slices[0].releaseNonNull());
2757     quad->setRight(slices[1].releaseNonNull());
2758     quad->setBottom(slices[2].releaseNonNull());
2759     quad->setLeft(slices[3].releaseNonNull());
2760     
2761     // Make our new border image value now.
2762     return CSSBorderImageSliceValue::create(CSSValuePool::singleton().createValue(WTFMove(quad)), fill);
2763 }
2764
2765 static RefPtr<CSSValue> consumeBorderImageOutset(CSSParserTokenRange& range)
2766 {
2767     RefPtr<CSSPrimitiveValue> outsets[4] = { 0 };
2768
2769     RefPtr<CSSPrimitiveValue> value;
2770     for (size_t index = 0; index < 4; ++index) {
2771         value = consumeNumber(range, ValueRangeNonNegative);
2772         if (!value)
2773             value = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
2774         if (!value)
2775             break;
2776         outsets[index] = value;
2777     }
2778     if (!outsets[0])
2779         return nullptr;
2780     complete4Sides(outsets);
2781     
2782     // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2783     auto quad = Quad::create();
2784     quad->setTop(outsets[0].releaseNonNull());
2785     quad->setRight(outsets[1].releaseNonNull());
2786     quad->setBottom(outsets[2].releaseNonNull());
2787     quad->setLeft(outsets[3].releaseNonNull());
2788     
2789     return CSSValuePool::singleton().createValue(WTFMove(quad));
2790 }
2791
2792 static RefPtr<CSSValue> consumeBorderImageWidth(CSSParserTokenRange& range)
2793 {
2794     RefPtr<CSSPrimitiveValue> widths[4];
2795
2796     RefPtr<CSSPrimitiveValue> value;
2797     for (size_t index = 0; index < 4; ++index) {
2798         value = consumeNumber(range, ValueRangeNonNegative);
2799         if (!value)
2800             value = consumeLengthOrPercent(range, HTMLStandardMode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
2801         if (!value)
2802             value = consumeIdent<CSSValueAuto>(range);
2803         if (!value)
2804             break;
2805         widths[index] = value;
2806     }
2807     if (!widths[0])
2808         return nullptr;
2809     complete4Sides(widths);
2810     
2811     // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2812     auto quad = Quad::create();
2813     quad->setTop(widths[0].releaseNonNull());
2814     quad->setRight(widths[1].releaseNonNull());
2815     quad->setBottom(widths[2].releaseNonNull());
2816     quad->setLeft(widths[3].releaseNonNull());
2817     
2818     return CSSValuePool::singleton().createValue(WTFMove(quad));
2819 }
2820
2821 static bool consumeBorderImageComponents(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context, RefPtr<CSSValue>& source,
2822     RefPtr<CSSValue>& slice, RefPtr<CSSValue>& width, RefPtr<CSSValue>& outset, RefPtr<CSSValue>& repeat)
2823 {
2824     do {
2825         if (!source) {
2826             source = consumeImageOrNone(range, context);
2827             if (source)
2828                 continue;
2829         }
2830         if (!repeat) {
2831             repeat = consumeBorderImageRepeat(range);
2832             if (repeat)
2833                 continue;
2834         }
2835         if (!slice) {
2836             slice = consumeBorderImageSlice(property, range);
2837             if (slice) {
2838                 ASSERT(!width && !outset);
2839                 if (consumeSlashIncludingWhitespace(range)) {
2840                     width = consumeBorderImageWidth(range);
2841                     if (consumeSlashIncludingWhitespace(range)) {
2842                         outset = consumeBorderImageOutset(range);
2843                         if (!outset)
2844                             return false;
2845                     } else if (!width) {
2846                         return false;
2847                     }
2848                 }
2849             } else {
2850                 return false;
2851             }
2852         } else {
2853             return false;
2854         }
2855     } while (!range.atEnd());
2856     return true;
2857 }
2858
2859 static RefPtr<CSSValue> consumeWebkitBorderImage(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2860 {
2861     RefPtr<CSSValue> source;
2862     RefPtr<CSSValue> slice;
2863     RefPtr<CSSValue> width;
2864     RefPtr<CSSValue> outset;
2865     RefPtr<CSSValue> repeat;
2866     if (consumeBorderImageComponents(property, range, context, source, slice, width, outset, repeat))
2867         return createBorderImageValue(WTFMove(source), WTFMove(slice), WTFMove(width), WTFMove(outset), WTFMove(repeat));
2868     return nullptr;
2869 }
2870
2871 static RefPtr<CSSValue> consumeReflect(CSSParserTokenRange& range, const CSSParserContext& context)
2872 {
2873     if (range.peek().id() == CSSValueNone)
2874         return consumeIdent(range);
2875     
2876     RefPtr<CSSPrimitiveValue> direction = consumeIdent<CSSValueAbove, CSSValueBelow, CSSValueLeft, CSSValueRight>(range);
2877     if (!direction)
2878         return nullptr;
2879
2880     RefPtr<CSSPrimitiveValue> offset;
2881     if (range.atEnd())
2882         offset = CSSValuePool::singleton().createValue(0, CSSPrimitiveValue::UnitType::CSS_PX);
2883     else {
2884         offset = consumeLengthOrPercent(range, context.mode, ValueRangeAll, UnitlessQuirk::Forbid);
2885         if (!offset)
2886             return nullptr;
2887     }
2888
2889     RefPtr<CSSValue> mask;
2890     if (!range.atEnd()) {
2891         mask = consumeWebkitBorderImage(CSSPropertyWebkitBoxReflect, range, context);
2892         if (!mask)
2893             return nullptr;
2894     }
2895     return CSSReflectValue::create(direction.releaseNonNull(), offset.releaseNonNull(), WTFMove(mask));
2896 }
2897
2898 #if ENABLE(CSS_IMAGE_ORIENTATION)
2899 static RefPtr<CSSValue> consumeImageOrientation(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
2900 {
2901     if (range.peek().type() != NumberToken) {
2902         RefPtr<CSSPrimitiveValue> angle = consumeAngle(range, cssParserMode, unitless);
2903         if (angle && angle->doubleValue() == 0)
2904             return angle;
2905     }
2906     return nullptr;
2907 }
2908 #endif
2909
2910 static RefPtr<CSSPrimitiveValue> consumeBackgroundBlendMode(CSSParserTokenRange& range)
2911 {
2912     CSSValueID id = range.peek().id();
2913     if (id == CSSValueNormal || id == CSSValueOverlay || (id >= CSSValueMultiply && id <= CSSValueLuminosity))
2914         return consumeIdent(range);
2915     return nullptr;
2916 }
2917
2918 static RefPtr<CSSPrimitiveValue> consumeBackgroundAttachment(CSSParserTokenRange& range)
2919 {
2920     return consumeIdent<CSSValueScroll, CSSValueFixed, CSSValueLocal>(range);
2921 }
2922
2923 static RefPtr<CSSPrimitiveValue> consumeBackgroundBox(CSSParserTokenRange& range)
2924 {
2925     return consumeIdent<CSSValueBorderBox, CSSValuePaddingBox, CSSValueContentBox, CSSValueWebkitText>(range);
2926 }
2927
2928 static RefPtr<CSSPrimitiveValue> consumeBackgroundComposite(CSSParserTokenRange& range)
2929 {
2930     return consumeIdentRange(range, CSSValueClear, CSSValuePlusLighter);
2931 }
2932
2933 static RefPtr<CSSPrimitiveValue> consumeWebkitMaskSourceType(CSSParserTokenRange& range)
2934 {
2935     return consumeIdent<CSSValueAuto, CSSValueAlpha, CSSValueLuminance>(range);
2936 }
2937
2938 static RefPtr<CSSPrimitiveValue> consumePrefixedBackgroundBox(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& /*context*/)
2939 {
2940     // The values 'border', 'padding' and 'content' are deprecated and do not apply to the version of the property that has the -webkit- prefix removed.
2941     if (RefPtr<CSSPrimitiveValue> value = consumeIdentRange(range, CSSValueBorder, CSSValuePaddingBox))
2942         return value;
2943     if (range.peek().id() == CSSValueWebkitText || ((property == CSSPropertyWebkitBackgroundClip || property == CSSPropertyWebkitMaskClip) && range.peek().id() == CSSValueText))
2944         return consumeIdent(range);
2945     return nullptr;
2946 }
2947
2948 static RefPtr<CSSPrimitiveValue> consumeBackgroundSize(CSSPropertyID property, CSSParserTokenRange& range, CSSParserMode cssParserMode)
2949 {
2950     if (identMatches<CSSValueContain, CSSValueCover>(range.peek().id()))
2951         return consumeIdent(range);
2952
2953     // FIXME: We're allowing the unitless quirk on this property because our
2954     // tests assume that. Other browser engines don't allow it though.
2955     RefPtr<CSSPrimitiveValue> horizontal = consumeIdent<CSSValueAuto>(range);
2956     if (!horizontal)
2957         horizontal = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2958
2959     RefPtr<CSSPrimitiveValue> vertical;
2960     if (!range.atEnd()) {
2961         if (range.peek().id() == CSSValueAuto) // `auto' is the default
2962             range.consumeIncludingWhitespace();
2963         else
2964             vertical = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2965     } else if (!vertical && property == CSSPropertyWebkitBackgroundSize) {
2966         // Legacy syntax: "-webkit-background-size: 10px" is equivalent to "background-size: 10px 10px".
2967         vertical = horizontal;
2968     }
2969     if (!vertical)
2970         return horizontal;
2971     return createPrimitiveValuePair(horizontal.releaseNonNull(), vertical.releaseNonNull(), property == CSSPropertyWebkitBackgroundSize ? Pair::IdenticalValueEncoding::Coalesce : Pair::IdenticalValueEncoding::DoNotCoalesce);
2972 }
2973
2974 static RefPtr<CSSValueList> consumeGridAutoFlow(CSSParserTokenRange& range)
2975 {
2976     RefPtr<CSSPrimitiveValue> rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
2977     RefPtr<CSSPrimitiveValue> denseAlgorithm = consumeIdent<CSSValueDense>(range);
2978     if (!rowOrColumnValue) {
2979         rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
2980         if (!rowOrColumnValue && !denseAlgorithm)
2981             return nullptr;
2982     }
2983     RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
2984     if (rowOrColumnValue)
2985         parsedValues->append(rowOrColumnValue.releaseNonNull());
2986     if (denseAlgorithm)
2987         parsedValues->append(denseAlgorithm.releaseNonNull());
2988     return parsedValues;
2989 }
2990
2991 static RefPtr<CSSValue> consumeBackgroundComponent(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2992 {
2993     switch (property) {
2994     case CSSPropertyBackgroundClip:
2995         return consumeBackgroundBox(range);
2996     case CSSPropertyBackgroundBlendMode:
2997         return consumeBackgroundBlendMode(range);
2998     case CSSPropertyBackgroundAttachment:
2999         return consumeBackgroundAttachment(range);
3000     case CSSPropertyBackgroundOrigin:
3001         return consumeBackgroundBox(range);
3002     case CSSPropertyWebkitMaskComposite:
3003     case CSSPropertyWebkitBackgroundComposite:
3004         return consumeBackgroundComposite(range);
3005     case CSSPropertyWebkitBackgroundClip:
3006     case CSSPropertyWebkitBackgroundOrigin:
3007     case CSSPropertyWebkitMaskClip:
3008     case CSSPropertyWebkitMaskOrigin:
3009         return consumePrefixedBackgroundBox(property, range, context);
3010     case CSSPropertyBackgroundImage:
3011     case CSSPropertyWebkitMaskImage:
3012         return consumeImageOrNone(range, context);
3013     case CSSPropertyWebkitMaskSourceType:
3014         return consumeWebkitMaskSourceType(range);
3015     case CSSPropertyBackgroundPositionX:
3016     case CSSPropertyWebkitMaskPositionX:
3017         return consumePositionX(range, context.mode);
3018     case CSSPropertyBackgroundPositionY:
3019     case CSSPropertyWebkitMaskPositionY:
3020         return consumePositionY(range, context.mode);
3021     case CSSPropertyBackgroundSize:
3022     case CSSPropertyWebkitBackgroundSize:
3023     case CSSPropertyWebkitMaskSize:
3024         return consumeBackgroundSize(property, range, context.mode);
3025     case CSSPropertyBackgroundColor:
3026         return consumeColor(range, context.mode);
3027     default:
3028         break;
3029     };
3030     return nullptr;
3031 }
3032
3033 static void addBackgroundValue(RefPtr<CSSValue>& list, Ref<CSSValue>&& value)
3034 {
3035     if (list) {
3036         if (!list->isBaseValueList()) {
3037             RefPtr<CSSValue> firstValue = list;
3038             list = CSSValueList::createCommaSeparated();
3039             downcast<CSSValueList>(*list).append(firstValue.releaseNonNull());
3040         }
3041         downcast<CSSValueList>(*list).append(WTFMove(value));
3042     } else {
3043         // To conserve memory we don't actually wrap a single value in a list.
3044         list = WTFMove(value);
3045     }
3046 }
3047
3048 static RefPtr<CSSValue> consumeCommaSeparatedBackgroundComponent(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
3049 {
3050     RefPtr<CSSValue> result;
3051     do {
3052         RefPtr<CSSValue> value = consumeBackgroundComponent(property, range, context);
3053         if (!value)
3054             return nullptr;
3055         addBackgroundValue(result, value.releaseNonNull());
3056     } while (consumeCommaIncludingWhitespace(range));
3057     return result;
3058 }
3059
3060 static bool isSelfPositionKeyword(CSSValueID id)
3061 {
3062     return identMatches<CSSValueStart, CSSValueEnd, CSSValueCenter, CSSValueSelfStart, CSSValueSelfEnd, CSSValueFlexStart, CSSValueFlexEnd>(id);
3063 }
3064
3065 static bool isSelfPositionOrLeftOrRightKeyword(CSSValueID id)
3066 {
3067     return isSelfPositionKeyword(id) || isLeftOrRightKeyword(id);
3068 }
3069
3070 static RefPtr<CSSValue> consumeSelfPositionOverflowPosition(CSSParserTokenRange& range, IsPositionKeyword isPositionKeyword)
3071 {
3072     ASSERT(isPositionKeyword);
3073     CSSValueID id = range.peek().id();
3074     if (isAuto(id) || isNormalOrStretch(id))
3075         return consumeIdent(range);
3076
3077     if (isBaselineKeyword(id))
3078         return consumeBaselineKeyword(range);
3079
3080     RefPtr<CSSPrimitiveValue> overflowPosition = consumeOverflowPositionKeyword(range);
3081     if (!isPositionKeyword(range.peek().id()))
3082         return nullptr;
3083     RefPtr<CSSPrimitiveValue> selfPosition = consumeIdent(range);
3084     if (overflowPosition)
3085         return createPrimitiveValuePair(overflowPosition.releaseNonNull(), selfPosition.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
3086     return selfPosition;
3087 }
3088
3089 static RefPtr<CSSValue> consumeAlignItems(CSSParserTokenRange& range)
3090 {
3091     // align-items property does not allow the 'auto' value.
3092     if (identMatches<CSSValueAuto>(range.peek().id()))
3093         return nullptr;
3094     return consumeSelfPositionOverflowPosition(range, isSelfPositionKeyword);
3095 }
3096
3097 static RefPtr<CSSValue> consumeJustifyItems(CSSParserTokenRange& range)
3098 {
3099     // justify-items property does not allow the 'auto' value.
3100     if (identMatches<CSSValueAuto>(range.peek().id()))
3101         return nullptr;
3102     CSSParserTokenRange rangeCopy = range;
3103     RefPtr<CSSPrimitiveValue> legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
3104     RefPtr<CSSPrimitiveValue> positionKeyword = consumeIdent<CSSValueCenter, CSSValueLeft, CSSValueRight>(rangeCopy);
3105     if (!legacy)
3106         legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
3107     if (legacy) {
3108         range = rangeCopy;
3109         if (positionKeyword)
3110             return createPrimitiveValuePair(legacy.releaseNonNull(), positionKeyword.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
3111         return legacy;
3112     }
3113     return consumeSelfPositionOverflowPosition(range, isSelfPositionOrLeftOrRightKeyword);
3114 }
3115
3116 static RefPtr<CSSValue> consumeFitContent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3117 {
3118     CSSParserTokenRange rangeCopy = range;
3119     CSSParserTokenRange args = consumeFunction(rangeCopy);
3120     RefPtr<CSSPrimitiveValue> length = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3121     if (!length || !args.atEnd())
3122         return nullptr;
3123     range = rangeCopy;
3124     RefPtr<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueFitContent);
3125     result->append(length.releaseNonNull());
3126     return result;
3127 }
3128
3129 static RefPtr<CSSPrimitiveValue> consumeCustomIdentForGridLine(CSSParserTokenRange& range)
3130 {
3131     if (range.peek().id() == CSSValueAuto || range.peek().id() == CSSValueSpan)
3132         return nullptr;
3133     return consumeCustomIdent(range);
3134 }
3135
3136 static RefPtr<CSSValue> consumeGridLine(CSSParserTokenRange& range)
3137 {
3138     if (range.peek().id() == CSSValueAuto)
3139         return consumeIdent(range);
3140
3141     RefPtr<CSSPrimitiveValue> spanValue;
3142     RefPtr<CSSPrimitiveValue> gridLineName;
3143     RefPtr<CSSPrimitiveValue> numericValue = consumeInteger(range);
3144     if (numericValue) {
3145         gridLineName = consumeCustomIdentForGridLine(range);
3146         spanValue = consumeIdent<CSSValueSpan>(range);
3147     } else {
3148         spanValue = consumeIdent<CSSValueSpan>(range);
3149         if (spanValue) {
3150             numericValue = consumeInteger(range);
3151             gridLineName = consumeCustomIdentForGridLine(range);
3152             if (!numericValue)
3153                 numericValue = consumeInteger(range);
3154         } else {
3155             gridLineName = consumeCustomIdentForGridLine(range);
3156             if (gridLineName) {
3157                 numericValue = consumeInteger(range);
3158                 spanValue = consumeIdent<CSSValueSpan>(range);
3159                 if (!spanValue && !numericValue)
3160                     return gridLineName;
3161             } else {
3162                 return nullptr;
3163             }
3164         }
3165     }
3166
3167     if (spanValue && !numericValue && !gridLineName)
3168         return nullptr; // "span" keyword alone is invalid.
3169     if (spanValue && numericValue && numericValue->intValue() < 0)
3170         return nullptr; // Negative numbers are not allowed for span.
3171     if (numericValue && numericValue->intValue() == 0)
3172         return nullptr; // An <integer> value of zero makes the declaration invalid.
3173
3174     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3175     if (spanValue)
3176         values->append(spanValue.releaseNonNull());
3177     if (numericValue)
3178         values->append(numericValue.releaseNonNull());
3179     if (gridLineName)
3180         values->append(gridLineName.releaseNonNull());
3181     ASSERT(values->length());
3182     return values;
3183 }
3184
3185 static bool isGridTrackFixedSized(const CSSPrimitiveValue& primitiveValue)
3186 {
3187     CSSValueID valueID = primitiveValue.valueID();
3188     if (valueID == CSSValueMinContent || valueID == CSSValueWebkitMinContent || valueID == CSSValueMaxContent || valueID == CSSValueWebkitMaxContent || valueID == CSSValueAuto || primitiveValue.isFlex())
3189         return false;
3190
3191     return true;
3192 }
3193
3194 static bool isGridTrackFixedSized(const CSSValue& value)
3195 {
3196     if (value.isPrimitiveValue())
3197         return isGridTrackFixedSized(downcast<CSSPrimitiveValue>(value));
3198
3199     ASSERT(value.isFunctionValue());
3200     auto& function = downcast<CSSFunctionValue>(value);
3201     if (function.name() == CSSValueFitContent || function.length() < 2)
3202         return false;
3203
3204     const CSSValue* minPrimitiveValue = downcast<CSSPrimitiveValue>(function.item(0));
3205     const CSSValue* maxPrimitiveValue = downcast<CSSPrimitiveValue>(function.item(1));
3206     return isGridTrackFixedSized(*minPrimitiveValue) || isGridTrackFixedSized(*maxPrimitiveValue);
3207 }
3208
3209 static Vector<String> parseGridTemplateAreasColumnNames(const String& gridRowNames)
3210 {
3211     ASSERT(!gridRowNames.isEmpty());
3212     Vector<String> columnNames;
3213     // Using StringImpl to avoid checks and indirection in every call to String::operator[].
3214     StringImpl& text = *gridRowNames.impl();
3215
3216     StringBuilder areaName;
3217     for (unsigned i = 0; i < text.length(); ++i) {
3218         if (isCSSSpace(text[i])) {
3219             if (!areaName.isEmpty()) {
3220                 columnNames.append(areaName.toString());
3221                 areaName.clear();
3222             }
3223             continue;
3224         }
3225         if (text[i] == '.') {
3226             if (areaName == ".")
3227                 continue;
3228             if (!areaName.isEmpty()) {
3229                 columnNames.append(areaName.toString());
3230                 areaName.clear();
3231             }
3232         } else {
3233             if (!isNameCodePoint(text[i]))
3234                 return Vector<String>();
3235             if (areaName == ".") {
3236                 columnNames.append(areaName.toString());
3237                 areaName.clear();
3238             }
3239         }
3240
3241         areaName.append(text[i]);
3242     }
3243
3244     if (!areaName.isEmpty())
3245         columnNames.append(areaName.toString());
3246
3247     return columnNames;
3248 }
3249
3250 static bool parseGridTemplateAreasRow(const String& gridRowNames, NamedGridAreaMap& gridAreaMap, const size_t rowCount, size_t& columnCount)
3251 {
3252     if (gridRowNames.isAllSpecialCharacters<isCSSSpace>())
3253         return false;
3254
3255     Vector<String> columnNames = parseGridTemplateAreasColumnNames(gridRowNames);
3256     if (rowCount == 0) {
3257         columnCount = columnNames.size();
3258         if (columnCount == 0)
3259             return false;
3260     } else if (columnCount != columnNames.size()) {
3261         // The declaration is invalid if all the rows don't have the number of columns.
3262         return false;
3263     }
3264
3265     for (size_t currentColumn = 0; currentColumn < columnCount; ++currentColumn) {
3266         const String& gridAreaName = columnNames[currentColumn];
3267
3268         // Unamed areas are always valid (we consider them to be 1x1).
3269         if (gridAreaName == ".")
3270             continue;
3271
3272         size_t lookAheadColumn = currentColumn + 1;
3273         while (lookAheadColumn < columnCount && columnNames[lookAheadColumn] == gridAreaName)
3274             lookAheadColumn++;
3275
3276         NamedGridAreaMap::iterator gridAreaIt = gridAreaMap.find(gridAreaName);
3277         if (gridAreaIt == gridAreaMap.end()) {
3278             gridAreaMap.add(gridAreaName, GridArea(GridSpan::translatedDefiniteGridSpan(rowCount, rowCount + 1), GridSpan::translatedDefiniteGridSpan(currentColumn, lookAheadColumn)));
3279         } else {
3280             GridArea& gridArea = gridAreaIt->value;
3281
3282             // The following checks test that the grid area is a single filled-in rectangle.
3283             // 1. The new row is adjacent to the previously parsed row.
3284             if (rowCount != gridArea.rows.endLine())
3285                 return false;
3286
3287             // 2. The new area starts at the same position as the previously parsed area.
3288             if (currentColumn != gridArea.columns.startLine())
3289                 return false;
3290
3291             // 3. The new area ends at the same position as the previously parsed area.
3292             if (lookAheadColumn != gridArea.columns.endLine())
3293                 return false;
3294
3295             gridArea.rows = GridSpan::translatedDefiniteGridSpan(gridArea.rows.startLine(), gridArea.rows.endLine() + 1);
3296         }
3297         currentColumn = lookAheadColumn - 1;
3298     }
3299
3300     return true;
3301 }
3302
3303 static RefPtr<CSSPrimitiveValue> consumeGridBreadth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3304 {
3305     const CSSParserToken& token = range.peek();
3306     if (identMatches<CSSValueMinContent, CSSValueWebkitMinContent, CSSValueMaxContent, CSSValueWebkitMaxContent, CSSValueAuto>(token.id()))
3307         return consumeIdent(range);
3308     if (token.type() == DimensionToken && token.unitType() == CSSPrimitiveValue::UnitType::CSS_FR) {
3309         if (range.peek().numericValue() < 0)
3310             return nullptr;
3311         return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::CSS_FR);
3312     }
3313     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3314 }
3315
3316 static RefPtr<CSSValue> consumeGridTrackSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3317 {
3318     const CSSParserToken& token = range.peek();
3319     if (identMatches<CSSValueAuto>(token.id()))
3320         return consumeIdent(range);
3321
3322     if (token.functionId() == CSSValueMinmax) {
3323         CSSParserTokenRange rangeCopy = range;
3324         CSSParserTokenRange args = consumeFunction(rangeCopy);
3325         RefPtr<CSSPrimitiveValue> minTrackBreadth = consumeGridBreadth(args, cssParserMode);
3326         if (!minTrackBreadth || minTrackBreadth->isFlex() || !consumeCommaIncludingWhitespace(args))
3327             return nullptr;
3328         RefPtr<CSSPrimitiveValue> maxTrackBreadth = consumeGridBreadth(args, cssParserMode);
3329         if (!maxTrackBreadth || !args.atEnd())
3330             return nullptr;
3331         range = rangeCopy;
3332         RefPtr<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueMinmax);
3333         result->append(minTrackBreadth.releaseNonNull());
3334         result->append(maxTrackBreadth.releaseNonNull());
3335         return result;
3336     }
3337
3338     if (token.functionId() == CSSValueFitContent)
3339         return consumeFitContent(range, cssParserMode);
3340
3341     return consumeGridBreadth(range, cssParserMode);
3342 }
3343
3344 // Appends to the passed in CSSGridLineNamesValue if any, otherwise creates a new one.
3345 static RefPtr<CSSGridLineNamesValue> consumeGridLineNames(CSSParserTokenRange& range, CSSGridLineNamesValue* lineNames = nullptr)
3346 {
3347     CSSParserTokenRange rangeCopy = range;
3348     if (rangeCopy.consumeIncludingWhitespace().type() != LeftBracketToken)
3349         return nullptr;
3350     
3351     RefPtr<CSSGridLineNamesValue> result = lineNames;
3352     if (!result)
3353         result = CSSGridLineNamesValue::create();
3354     while (RefPtr<CSSPrimitiveValue> lineName = consumeCustomIdentForGridLine(rangeCopy))
3355         result->append(lineName.releaseNonNull());
3356     if (rangeCopy.consumeIncludingWhitespace().type() != RightBracketToken)
3357         return nullptr;
3358     range = rangeCopy;
3359     return result;
3360 }
3361
3362 static bool consumeGridTrackRepeatFunction(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSValueList& list, bool& isAutoRepeat, bool& allTracksAreFixedSized)
3363 {
3364     CSSParserTokenRange args = consumeFunction(range);
3365     // The number of repetitions for <auto-repeat> is not important at parsing level
3366     // because it will be computed later, let's set it to 1.
3367     size_t repetitions = 1;
3368     isAutoRepeat = identMatches<CSSValueAutoFill, CSSValueAutoFit>(args.peek().id());
3369     RefPtr<CSSValueList> repeatedValues;
3370     if (isAutoRepeat)
3371         repeatedValues = CSSGridAutoRepeatValue::create(args.consumeIncludingWhitespace().id());
3372     else {
3373         // FIXME: a consumeIntegerRaw would be more efficient here.
3374         RefPtr<CSSPrimitiveValue> repetition = consumePositiveInteger(args);
3375         if (!repetition)
3376             return false;
3377         repetitions = clampTo<size_t>(repetition->doubleValue(), 0, GridPosition::max());
3378         repeatedValues = CSSValueList::createSpaceSeparated();
3379     }
3380     if (!consumeCommaIncludingWhitespace(args))
3381         return false;
3382     RefPtr<CSSGridLineNamesValue> lineNames = consumeGridLineNames(args);
3383     if (lineNames)
3384         repeatedValues->append(lineNames.releaseNonNull());
3385
3386     size_t numberOfTracks = 0;
3387     while (!args.atEnd()) {
3388         RefPtr<CSSValue> trackSize = consumeGridTrackSize(args, cssParserMode);
3389         if (!trackSize)
3390             return false;
3391         if (allTracksAreFixedSized)
3392             allTracksAreFixedSized = isGridTrackFixedSized(*trackSize);
3393         repeatedValues->append(trackSize.releaseNonNull());
3394         ++numberOfTracks;
3395         lineNames = consumeGridLineNames(args);
3396         if (lineNames)
3397             repeatedValues->append(lineNames.releaseNonNull());
3398     }
3399     // We should have found at least one <track-size> or else it is not a valid <track-list>.
3400     if (!numberOfTracks)
3401         return false;
3402
3403     if (isAutoRepeat)
3404         list.append(repeatedValues.releaseNonNull());
3405     else {
3406         // We clamp the repetitions to a multiple of the repeat() track list's size, while staying below the max grid size.
3407         repetitions = std::min(repetitions, GridPosition::max() / numberOfTracks);
3408         RefPtr<CSSValueList> integerRepeatedValues = CSSGridIntegerRepeatValue::create(repetitions);
3409         for (size_t i = 0; i < repeatedValues->length(); ++i)
3410             integerRepeatedValues->append(*repeatedValues->itemWithoutBoundsCheck(i));
3411         list.append(integerRepeatedValues.releaseNonNull());
3412     }
3413     return true;
3414 }
3415
3416 enum TrackListType { GridTemplate, GridTemplateNoRepeat, GridAuto };
3417
3418 static RefPtr<CSSValue> consumeGridTrackList(CSSParserTokenRange& range, CSSParserMode cssParserMode, TrackListType trackListType)
3419 {
3420     bool allowGridLineNames = trackListType != GridAuto;
3421     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3422     RefPtr<CSSGridLineNamesValue> lineNames = consumeGridLineNames(range);
3423     if (lineNames) {
3424         if (!allowGridLineNames)
3425             return nullptr;
3426         values->append(lineNames.releaseNonNull());
3427     }
3428     
3429     bool allowRepeat = trackListType == GridTemplate;
3430     bool seenAutoRepeat = false;
3431     bool allTracksAreFixedSized = true;
3432     do {
3433         bool isAutoRepeat;
3434         if (range.peek().functionId() == CSSValueRepeat) {
3435             if (!allowRepeat)
3436                 return nullptr;
3437             if (!consumeGridTrackRepeatFunction(range, cssParserMode, *values, isAutoRepeat, allTracksAreFixedSized))
3438                 return nullptr;
3439             if (isAutoRepeat && seenAutoRepeat)
3440                 return nullptr;
3441             seenAutoRepeat = seenAutoRepeat || isAutoRepeat;
3442         } else if (RefPtr<CSSValue> value = consumeGridTrackSize(range, cssParserMode)) {
3443             if (allTracksAreFixedSized)
3444                 allTracksAreFixedSized = isGridTrackFixedSized(*value);
3445             values->append(value.releaseNonNull());
3446         } else {
3447             return nullptr;
3448         }
3449         if (seenAutoRepeat && !allTracksAreFixedSized)
3450             return nullptr;
3451         lineNames = consumeGridLineNames(range);
3452         if (lineNames) {
3453             if (!allowGridLineNames)
3454                 return nullptr;
3455             values->append(lineNames.releaseNonNull());
3456         }
3457     } while (!range.atEnd() && range.peek().type() != DelimiterToken);
3458     return values;
3459 }
3460
3461 static RefPtr<CSSValue> consumeGridTemplatesRowsOrColumns(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3462 {
3463     if (range.peek().id() == CSSValueNone)
3464         return consumeIdent(range);
3465     return consumeGridTrackList(range, cssParserMode, GridTemplate);
3466 }
3467
3468 static RefPtr<CSSValue> consumeGridTemplateAreas(CSSParserTokenRange& range)
3469 {
3470     if (range.peek().id() == CSSValueNone)
3471         return consumeIdent(range);
3472
3473     NamedGridAreaMap gridAreaMap;
3474     size_t rowCount = 0;
3475     size_t columnCount = 0;
3476
3477     while (range.peek().type() == StringToken) {
3478         if (!parseGridTemplateAreasRow(range.consumeIncludingWhitespace().value().toString(), gridAreaMap, rowCount, columnCount))
3479             return nullptr;
3480         ++rowCount;
3481     }
3482
3483     if (rowCount == 0)
3484         return nullptr;
3485     ASSERT(columnCount);
3486     return CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount);
3487 }
3488
3489 static RefPtr<CSSValue> consumeLineBoxContain(CSSParserTokenRange& range)
3490 {
3491     if (range.peek().id() == CSSValueNone)
3492         return consumeIdent(range);
3493
3494     OptionSet<LineBoxContain> lineBoxContain;
3495     
3496     while (range.peek().type() == IdentToken) {
3497         auto id = range.peek().id();
3498         if (id == CSSValueBlock) {
3499             if (lineBoxContain.contains(LineBoxContain::Block))
3500                 return nullptr;
3501             lineBoxContain.add(LineBoxContain::Block);
3502         } else if (id == CSSValueInline) {
3503             if (lineBoxContain.contains(LineBoxContain::Inline))
3504                 return nullptr;
3505             lineBoxContain.add(LineBoxContain::Inline);
3506         } else if (id == CSSValueFont) {
3507             if (lineBoxContain.contains(LineBoxContain::Font))
3508                 return nullptr;
3509             lineBoxContain.add(LineBoxContain::Font);
3510         } else if (id == CSSValueGlyphs) {
3511             if (lineBoxContain.contains(LineBoxContain::Glyphs))
3512                 return nullptr;
3513             lineBoxContain.add(LineBoxContain::Glyphs);
3514         } else if (id == CSSValueReplaced) {
3515             if (lineBoxContain.contains(LineBoxContain::Replaced))
3516                 return nullptr;
3517             lineBoxContain.add(LineBoxContain::Replaced);
3518         } else if (id == CSSValueInlineBox) {
3519             if (lineBoxContain.contains(LineBoxContain::InlineBox))
3520                 return nullptr;
3521             lineBoxContain.add(LineBoxContain::InlineBox);
3522         } else if (id == CSSValueInitialLetter) {
3523             if (lineBoxContain.contains(LineBoxContain::InitialLetter))
3524                 return nullptr;
3525             lineBoxContain.add(LineBoxContain::InitialLetter);
3526         } else
3527             return nullptr;
3528         range.consumeIncludingWhitespace();
3529     }
3530     
3531     if (!lineBoxContain)
3532         return nullptr;
3533     
3534     return CSSLineBoxContainValue::create(lineBoxContain);
3535 }
3536
3537 static RefPtr<CSSValue> consumeLineGrid(CSSParserTokenRange& range)
3538 {
3539     if (range.peek().id() == CSSValueNone)
3540         return consumeIdent(range);
3541     return consumeCustomIdent(range);
3542 }
3543
3544 static RefPtr<CSSValue> consumeInitialLetter(CSSParserTokenRange& range)
3545 {
3546     RefPtr<CSSValue> ident = consumeIdent<CSSValueNormal>(range);
3547     if (ident)
3548         return ident;
3549     
3550     RefPtr<CSSPrimitiveValue> height = consumeNumber(range, ValueRangeNonNegative);
3551     if (!height)
3552         return nullptr;
3553     
3554     RefPtr<CSSPrimitiveValue> position;
3555     if (!range.atEnd()) {
3556         position = consumeNumber(range, ValueRangeNonNegative);
3557         if (!position || !range.atEnd())
3558             return nullptr;
3559     } else
3560         position = height.copyRef();
3561     
3562     return createPrimitiveValuePair(position.releaseNonNull(), WTFMove(height));
3563 }
3564
3565 static RefPtr<CSSValue> consumeSpeakAs(CSSParserTokenRange& range)
3566 {
3567     if (range.peek().id() == CSSValueNone)
3568         return consumeIdent(range);
3569     
3570     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3571     
3572     bool seenNormal = false;
3573     bool seenSpellOut = false;
3574     bool seenLiteralPunctuation = false;
3575     bool seenNoPunctuation = false;
3576
3577     // normal | spell-out || digits || [ literal-punctuation | no-punctuation ]
3578     while (!range.atEnd()) {
3579         CSSValueID valueID = range.peek().id();
3580         if ((valueID == CSSValueNormal && seenSpellOut)
3581             || (valueID == CSSValueSpellOut && seenNormal)
3582             || (valueID == CSSValueLiteralPunctuation && seenNoPunctuation)
3583             || (valueID == CSSValueNoPunctuation && seenLiteralPunctuation))
3584             return nullptr;
3585         RefPtr<CSSValue> ident = consumeIdent<CSSValueNormal, CSSValueSpellOut, CSSValueDigits, CSSValueLiteralPunctuation, CSSValueNoPunctuation>(range);
3586         if (!ident)
3587             return nullptr;
3588         switch (valueID) {
3589         case CSSValueNormal:
3590             seenNormal = true;
3591             break;
3592         case CSSValueSpellOut:
3593             seenSpellOut = true;
3594             break;
3595         case CSSValueLiteralPunctuation:
3596             seenLiteralPunctuation = true;
3597             break;
3598         case CSSValueNoPunctuation:
3599             seenNoPunctuation = true;
3600             break;
3601         default:
3602             break;
3603         }
3604         list->append(ident.releaseNonNull());
3605     }
3606     
3607     return list->length() ? list : nullptr;
3608 }
3609     
3610 static RefPtr<CSSValue> consumeHangingPunctuation(CSSParserTokenRange& range)
3611 {
3612     if (range.peek().id() == CSSValueNone)
3613         return consumeIdent(range);
3614     
3615     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3616
3617     bool seenForceEnd = false;
3618     bool seenAllowEnd = false;
3619     bool seenFirst = false;
3620     bool seenLast = false;
3621
3622     while (!range.atEnd()) {
3623         CSSValueID valueID = range.peek().id();
3624         if ((valueID == CSSValueFirst && seenFirst)
3625             || (valueID == CSSValueLast && seenLast)
3626             || (valueID == CSSValueAllowEnd && (seenAllowEnd || seenForceEnd))
3627             || (valueID == CSSValueForceEnd && (seenAllowEnd || seenForceEnd)))
3628             return nullptr;
3629         RefPtr<CSSValue> ident = consumeIdent<CSSValueAllowEnd, CSSValueForceEnd, CSSValueFirst, CSSValueLast>(range);
3630         if (!ident)
3631             return nullptr;
3632         switch (valueID) {
3633         case CSSValueAllowEnd:
3634             seenAllowEnd = true;
3635             break;
3636         case CSSValueForceEnd:
3637             seenForceEnd = true;
3638             break;
3639         case CSSValueFirst:
3640             seenFirst = true;
3641             break;
3642         case CSSValueLast:
3643             seenLast = true;
3644             break;
3645         default:
3646             break;
3647         }
3648         list->append(ident.releaseNonNull());
3649     }
3650     
3651     return list->length() ? list : nullptr;
3652 }
3653
3654 static RefPtr<CSSValue> consumeWebkitMarqueeIncrement(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3655 {
3656     if (range.peek().type() == IdentToken)
3657         return consumeIdent<CSSValueSmall, CSSValueMedium, CSSValueLarge>(range);
3658     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
3659 }
3660
3661 static RefPtr<CSSValue> consumeWebkitMarqueeRepetition(CSSParserTokenRange& range)
3662 {
3663     if (range.peek().type() == IdentToken)
3664         return consumeIdent<CSSValueInfinite>(range);
3665     return consumeNumber(range, ValueRangeNonNegative);
3666 }
3667
3668 static RefPtr<CSSValue> consumeWebkitMarqueeSpeed(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3669 {
3670     if (range.peek().type() == IdentToken)
3671         return consumeIdent<CSSValueSlow, CSSValueNormal, CSSValueFast>(range);
3672     return consumeTime(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3673 }
3674
3675 static RefPtr<CSSValue> consumeAlt(CSSParserTokenRange& range, const CSSParserContext& context)
3676 {
3677     if (range.peek().type() == StringToken)
3678         return consumeString(range);
3679     
3680     if (range.peek().functionId() != CSSValueAttr)
3681         return nullptr;
3682     
3683     return consumeAttr(consumeFunction(range), context);
3684 }
3685
3686 static RefPtr<CSSValue> consumeWebkitAspectRatio(CSSParserTokenRange& range)
3687 {
3688     if (range.peek().type() == IdentToken)
3689         return consumeIdent<CSSValueAuto, CSSValueFromDimensions, CSSValueFromIntrinsic>(range);
3690     
3691     RefPtr<CSSPrimitiveValue> leftValue = consumeNumber(range, ValueRangeNonNegative);
3692     if (!leftValue || !leftValue->floatValue() || range.atEnd() || !consumeSlashIncludingWhitespace(range))
3693         return nullptr;
3694     RefPtr<CSSPrimitiveValue> rightValue = consumeNumber(range, ValueRangeNonNegative);
3695     if (!rightValue || !rightValue->floatValue())
3696         return nullptr;
3697     
3698     return CSSAspectRatioValue::create(leftValue->floatValue(), rightValue->floatValue());
3699 }
3700
3701 static RefPtr<CSSValue> consumeTextEmphasisPosition(CSSParserTokenRange& range)
3702 {
3703     bool foundOverOrUnder = false;
3704     CSSValueID overUnderValueID = CSSValueOver;
3705     bool foundLeftOrRight = false;
3706     CSSValueID leftRightValueID = CSSValueRight;
3707     while (!range.atEnd()) {
3708         switch (range.peek().id()) {
3709         case CSSValueOver:
3710             if (foundOverOrUnder)
3711                 return nullptr;
3712             foundOverOrUnder = true;
3713             overUnderValueID = CSSValueOver;
3714             break;
3715         case CSSValueUnder:
3716             if (foundOverOrUnder)
3717                 return nullptr;
3718             foundOverOrUnder = true;
3719             overUnderValueID = CSSValueUnder;
3720             break;
3721         case CSSValueLeft:
3722             if (foundLeftOrRight)
3723                 return nullptr;
3724             foundLeftOrRight = true;
3725             leftRightValueID = CSSValueLeft;
3726             break;
3727         case CSSValueRight:
3728             if (foundLeftOrRight)
3729                 return nullptr;
3730             foundLeftOrRight = true;
3731             leftRightValueID = CSSValueRight;
3732             break;
3733         default:
3734             return nullptr;
3735         }
3736         
3737         range.consumeIncludingWhitespace();
3738     }
3739     if (!foundOverOrUnder)
3740         return nullptr;
3741     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3742     list->append(CSSValuePool::singleton().createIdentifierValue(overUnderValueID));
3743     if (foundLeftOrRight)
3744         list->append(CSSValuePool::singleton().createIdentifierValue(leftRightValueID));
3745     return list;
3746 }
3747
3748 #if ENABLE(DARK_MODE_CSS)
3749
3750 static RefPtr<CSSValue> consumeColorScheme(CSSParserTokenRange& range)
3751 {
3752     if (isAuto(range.peek().id()))
3753         return consumeIdent(range);
3754
3755     Vector<CSSValueID, 3> identifiers;
3756
3757     while (!range.atEnd()) {
3758         if (range.peek().type() != IdentToken)
3759             return nullptr;
3760
3761         CSSValueID id = range.peek().id();
3762
3763         switch (id) {
3764         case CSSValueAuto:
3765             // Auto is only allowed as a single value, and was handled earlier.
3766             // Don't allow it in the list.
3767             return nullptr;
3768
3769         case CSSValueOnly:
3770         case CSSValueLight:
3771         case CSSValueDark:
3772             if (!identifiers.appendIfNotContains(id))
3773                 return nullptr;
3774             break;
3775
3776         default:
3777             // Unknown identifiers are allowed and ignored.
3778             break;
3779         }
3780
3781         range.consumeIncludingWhitespace();
3782     }
3783
3784     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3785     for (auto id : identifiers)
3786         list->append(CSSValuePool::singleton().createIdentifierValue(id));
3787     return list;
3788 }
3789
3790 #endif
3791
3792 RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, CSSPropertyID currentShorthand)
3793 {
3794     if (CSSParserFastPaths::isKeywordPropertyID(property)) {
3795         if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(property, m_range.peek().id(), m_context))
3796             return nullptr;
3797
3798         return consumeIdent(m_range);
3799     }
3800     switch (property) {
3801     case CSSPropertyWillChange:
3802         return consumeWillChange(m_range);
3803     case CSSPropertyPage:
3804         return consumePage(m_range);
3805     case CSSPropertyQuotes:
3806         return consumeQuotes(m_range);
3807     case CSSPropertyFontVariantCaps:
3808         return consumeFontVariantCaps(m_range);
3809     case CSSPropertyFontVariantLigatures:
3810         return consumeFontVariantLigatures(m_range);
3811     case CSSPropertyFontVariantNumeric:
3812         return consumeFontVariantNumeric(m_range);
3813     case CSSPropertyFontVariantEastAsian:
3814         return consumeFontVariantEastAsian(m_range);
3815     case CSSPropertyFontFeatureSettings:
3816         return consumeFontFeatureSettings(m_range);
3817     case CSSPropertyFontFamily:
3818         return consumeFontFamily(m_range);
3819     case CSSPropertyFontWeight:
3820         return consumeFontWeight(m_range);
3821     case CSSPropertyFontStretch:
3822         return consumeFontStretch(m_range);
3823     case CSSPropertyFontStyle:
3824         return consumeFontStyle(m_range, m_context.mode);
3825     case CSSPropertyFontSynthesis:
3826         return consumeFontSynthesis(m_range);
3827 #if ENABLE(VARIATION_FONTS)
3828     case CSSPropertyFontVariationSettings:
3829         return consumeFontVariationSettings(m_range);
3830 #endif
3831     case CSSPropertyLetterSpacing:
3832         return consumeLetterSpacing(m_range, m_context.mode);
3833     case CSSPropertyWordSpacing:
3834         return consumeWordSpacing(m_range, m_context.mode);
3835     case CSSPropertyTabSize:
3836         return consumeTabSize(m_range, m_context.mode);
3837 #if ENABLE(TEXT_AUTOSIZING)
3838     case CSSPropertyWebkitTextSizeAdjust:
3839         // FIXME: Support toggling the validation of this property via a runtime setting that is independent of
3840         // whether isTextAutosizingEnabled() is true. We want to enable this property on iOS, when simulating
3841         // a iOS device in Safari's responsive design mode and when optionally enabled in DRT/WTR. Otherwise,
3842         // this property should be disabled by default.
3843 #if !PLATFORM(IOS_FAMILY)
3844         if (!m_context.textAutosizingEnabled)
3845             return nullptr;
3846 #endif
3847         return consumeTextSizeAdjust(m_range, m_context.mode);
3848 #endif
3849     case CSSPropertyFontSize:
3850         return consumeFontSize(m_range, m_context.mode, UnitlessQuirk::Allow);
3851     case CSSPropertyLineHeight:
3852         return consumeLineHeight(m_range, m_context.mode);
3853     case CSSPropertyWebkitBorderHorizontalSpacing:
3854     case CSSPropertyWebkitBorderVerticalSpacing:
3855         return consumeLength(m_range, m_context.mode, ValueRangeNonNegative);
3856     case CSSPropertyCounterIncrement:
3857     case CSSPropertyCounterReset:
3858         return consumeCounter(m_range, property == CSSPropertyCounterIncrement ? 1 : 0);
3859     case CSSPropertySize:
3860         return consumeSize(m_range, m_context.mode);
3861     case CSSPropertyTextIndent:
3862         return consumeTextIndent(m_range, m_context.mode);
3863     case CSSPropertyMaxWidth:
3864     case CSSPropertyMaxHeight:
3865         return consumeMaxWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
3866     case CSSPropertyMaxInlineSize:
3867     case CSSPropertyMaxBlockSize:
3868         return consumeMaxWidthOrHeight(m_range, m_context);
3869     case CSSPropertyMinWidth:
3870     case CSSPropertyMinHeight:
3871     case CSSPropertyWidth:
3872     case CSSPropertyHeight:
3873         return consumeWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
3874     case CSSPropertyMinInlineSize:
3875     case CSSPropertyMinBlockSize:
3876     case CSSPropertyInlineSize:
3877     case CSSPropertyBlockSize:
3878         return consumeWidthOrHeight(m_range, m_context);
3879     case CSSPropertyMarginTop:
3880     case CSSPropertyMarginRight:
3881     case CSSPropertyMarginBottom:
3882     case CSSPropertyMarginLeft:
3883     case CSSPropertyBottom:
3884     case CSSPropertyLeft:
3885     case CSSPropertyRight:
3886     case CSSPropertyTop: {
3887         UnitlessQuirk unitless = currentShorthand != CSSPropertyInset ? UnitlessQuirk::Allow : UnitlessQuirk::Forbid;
3888         return consumeMarginOrOffset(m_range, m_context.mode, unitless);
3889     }
3890     case CSSPropertyInsetInlineStart:
3891     case CSSPropertyInsetInlineEnd:
3892     case CSSPropertyInsetBlockStart:
3893     case CSSPropertyInsetBlockEnd:
3894     case CSSPropertyMarginInlineStart:
3895     case CSSPropertyMarginInlineEnd:
3896     case CSSPropertyMarginBlockStart:
3897     case CSSPropertyMarginBlockEnd:
3898         return consumeMarginOrOffset(m_range, m_context.mode, UnitlessQuirk::Forbid);
3899     case CSSPropertyPaddingTop:
3900     case CSSPropertyPaddingRight:
3901     case CSSPropertyPaddingBottom:
3902     case CSSPropertyPaddingLeft:
3903         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3904     case CSSPropertyPaddingInlineStart:
3905     case CSSPropertyPaddingInlineEnd:
3906     case CSSPropertyPaddingBlockStart:
3907     case CSSPropertyPaddingBlockEnd:
3908         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
3909 #if ENABLE(CSS_SCROLL_SNAP)
3910     case CSSPropertyScrollSnapMarginBottom:
3911     case CSSPropertyScrollSnapMarginLeft:
3912     case CSSPropertyScrollSnapMarginRight:
3913     case CSSPropertyScrollSnapMarginTop:
3914         return consumeLength(m_range, m_context.mode, ValueRangeAll);
3915     case CSSPropertyScrollPaddingBottom:
3916     case CSSPropertyScrollPaddingLeft:
3917     case CSSPropertyScrollPaddingRight:
3918     case CSSPropertyScrollPaddingTop:
3919         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeAll);
3920     case CSSPropertyScrollSnapAlign:
3921         return consumeScrollSnapAlign(m_range);
3922     case CSSPropertyScrollSnapType:
3923         return consumeScrollSnapType(m_range);
3924 #endif
3925     case CSSPropertyClip:
3926         return consumeClip(m_range, m_context.mode);
3927 #if ENABLE(POINTER_EVENTS)
3928     case CSSPropertyTouchAction:
3929         return consumeTouchAction(m_range);
3930 #endif
3931     case CSSPropertyObjectPosition:
3932         return consumePosition(m_range, m_context.mode, UnitlessQuirk::Forbid);
3933     case CSSPropertyWebkitLineClamp:
3934         return consumeLineClamp(m_range);
3935     case CSSPropertyWebkitFontSizeDelta:
3936         return consumeLength(m_range, m_context.mode, ValueRangeAll, UnitlessQuirk::Allow);
3937     case CSSPropertyWebkitHyphenateCharacter:
3938     case CSSPropertyWebkitLocale:
3939         return consumeAutoOrString(m_range);
3940     case CSSPropertyWebkitHyphenateLimitBefore:
3941     case CSSPropertyWebkitHyphenateLimitAfter:
3942         return consumeHyphenateLimit(m_range, CSSValueAuto);
3943     case CSSPropertyWebkitHyphenateLimitLines:
3944         return consumeHyphenateLimit(m_range, CSSValueNoLimit);
3945     case CSSPropertyColumnWidth:
3946         return consumeColumnWidth(m_range);
3947     case CSSPropertyColumnCount:
3948         return consumeColumnCount(m_range);
3949     case CSSPropertyColumnGap:
3950         return consumeGapLength(m_range, m_context.mode);
3951     case CSSPropertyRowGap:
3952         return consumeGapLength(m_range, m_context.mode);
3953     case CSSPropertyColumnSpan:
3954         return consumeColumnSpan(m_range);
3955     case CSSPropertyZoom:
3956         return consumeZoom(m_range, m_context);
3957     case CSSPropertyAnimationDelay:
3958     case CSSPropertyTransitionDelay:
3959     case CSSPropertyAnimationDirection:
3960     case CSSPropertyAnimationDuration:
3961     case CSSPropertyTransitionDuration:
3962     case CSSPropertyAnimationFillMode:
3963     case CSSPropertyAnimationIterationCount:
3964     case CSSPropertyAnimationName:
3965     case CSSPropertyAnimationPlayState:
3966     case CSSPropertyTransitionProperty:
3967     case CSSPropertyAnimationTimingFunction:
3968     case CSSPropertyTransitionTimingFunction:
3969         return consumeAnimationPropertyList(property, m_range, m_context);
3970     case CSSPropertyShapeMargin:
3971         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative);
3972     case CSSPropertyShapeImageThreshold:
3973         return consumeNumber(m_range, ValueRangeAll);
3974     case CSSPropertyWebkitBoxOrdinalGroup:
3975     case CSSPropertyOrphans:
3976     case CSSPropertyWidows:
3977         return consumePositiveInteger(m_range);
3978     case CSSPropertyTextDecorationColor:
3979         return consumeColor(m_range, m_context.mode);
3980     case CSSPropertyTextDecorationSkip:
3981         return consumeTextDecorationSkip(m_range);
3982     case CSSPropertyWebkitTextStrokeWidth:
3983         return consumeTextStrokeWidth(m_range, m_context.mode);
3984     case CSSPropertyWebkitTextFillColor:
3985 #if ENABLE(TOUCH_EVENTS)
3986     case CSSPropertyWebkitTapHighlightColor:
3987 #endif
3988     case CSSPropertyWebkitTextEmphasisColor:
3989     case CSSPropertyBorderInlineStartColor:
3990     case CSSPropertyBorderInlineEndColor:
3991     case CSSPropertyBorderBlockStartColor:
3992     case CSSPropertyBorderBlockEndColor:
3993     case CSSPropertyWebkitTextStrokeColor:
3994     case CSSPropertyStrokeColor:
3995     case CSSPropertyStopColor:
3996     case CSSPropertyFloodColor:
3997     case CSSPropertyLightingColor:
3998     case CSSPropertyColumnRuleColor:
3999         return consumeColor(m_range, m_context.mode);
4000     case CSSPropertyCaretColor:
4001         return consumeCaretColor(m_range, m_context.mode);
4002     case CSSPropertyColor:
4003     case CSSPropertyBackgroundColor:
4004         return consumeColor(m_range, m_context.mode, inQuirksMode());
4005     case CSSPropertyBorderInlineStartWidth:
4006     case CSSPropertyBorderInlineEndWidth:
4007     case CSSPropertyBorderBlockStartWidth:
4008     case CSSPropertyBorderBlockEndWidth:
4009         return consumeBorderWidth(m_range, m_context.mode, UnitlessQuirk::Forbid);
4010     case CSSPropertyBorderBottomColor:
4011     case CSSPropertyBorderLeftColor:
4012     case CSSPropertyBorderRightColor:
4013     case CSSPropertyBorderTopColor: {
4014         bool allowQuirkyColors = inQuirksMode()
4015             && (currentShorthand == CSSPropertyInvalid || currentShorthand == CSSPropertyBorderColor);
4016         return consumeColor(m_range, m_context.mode, allowQuirkyColors);
4017     }
4018     case CSSPropertyBorderBottomWidth:
4019     case CSSPropertyBorderLeftWidth:
4020     case CSSPropertyBorderRightWidth:
4021     case CSSPropertyBorderTopWidth: {
4022         bool allowQuirkyLengths = inQuirksMode()
4023             && (currentShorthand == CSSPropertyInvalid || currentShorthand == CSSPropertyBorderWidth);
4024         UnitlessQuirk unitless = allowQuirkyLengths ? UnitlessQuirk::Allow : UnitlessQuirk::Forbid;
4025         return consumeBorderWidth(m_range, m_context.mode, unitless);
4026     }
4027     case CSSPropertyZIndex:
4028         return consumeZIndex(m_range);
4029     case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3
4030     case CSSPropertyBoxShadow:
4031     case CSSPropertyWebkitBoxShadow:
4032         return consumeShadow(m_range, m_context.mode, property == CSSPropertyBoxShadow || property == CSSPropertyWebkitBoxShadow);
4033     case CSSPropertyFilter:
4034 #if ENABLE(FILTERS_LEVEL_2)
4035     case CSSPropertyWebkitBackdropFilter:
4036 #endif
4037         return consumeFilter(m_range, m_context, AllowedFilterFunctions::PixelFilters);
4038     case CSSPropertyAppleColorFilter:
4039         if (!m_context.colorFilterEnabled)
4040             return nullptr;
4041         return consumeFilter(m_range, m_context, AllowedFilterFunctions::ColorFilters);
4042     case CSSPropertyTextDecoration:
4043     case CSSPropertyWebkitTextDecorationsInEffect:
4044     case CSSPropertyTextDecorationLine:
4045         return consumeTextDecorationLine(m_range);
4046     case CSSPropertyWebkitTextEmphasisStyle:
4047         return consumeTextEmphasisStyle(m_range);
4048     case CSSPropertyOutlineColor:
4049         return consumeOutlineColor(m_range, m_context.mode);
4050     case CSSPropertyOutlineOffset:
4051         return consumeLength(m_range, m_context.mode, ValueRangeAll);
4052     case CSSPropertyOutlineWidth:
4053         return consumeLineWidth(m_range, m_context.mode, UnitlessQuirk::Forbid);
4054     case CSSPropertyTransform:
4055         return consumeTransform(m_range, m_context.mode);
4056     case CSSPropertyTransformBox:
4057         return consumeIdent<CSSValueBorderBox, CSSValueViewBox, CSSValueFillBox>(m_range);
4058     case CSSPropertyTransformOriginX:
4059     case CSSPropertyPerspectiveOriginX:
4060         return consumePositionX(m_range, m_context.mode);
4061     case CSSPropertyTransformOriginY:
4062     case CSSPropertyPerspectiveOriginY:
4063         return consumePositionY(m_range, m_context.mode);
4064     case CSSPropertyTransformOriginZ:
4065         return consumeLength(m_range, m_context.mode, ValueRangeAll);
4066     case CSSPropertyFill:
4067     case CSSPropertyStroke:
4068         return consumePaintStroke(m_range, m_context.mode);
4069     case CSSPropertyGlyphOrientationVertical:
4070     case CSSPropertyGlyphOrientationHorizontal:
4071         return consumeGlyphOrientation(m_range, m_context.mode, property);
4072     case CSSPropertyPaintOrder:
4073         return consumePaintOrder(m_range);
4074     case CSSPropertyMarkerStart:
4075     case CSSPropertyMarkerMid:
4076     case CSSPropertyMarkerEnd:
4077     case CSSPropertyClipPath:
4078     case CSSPropertyMask:
4079         return consumeNoneOrURI(m_range);
4080     case CSSPropertyFlexBasis:
4081         return consumeFlexBasis(m_range, m_context.mode);
4082     case CSSPropertyFlexGrow:
4083     case CSSPropertyFlexShrink:
4084         return consumeNumber(m_range, ValueRangeNonNegative);
4085     case CSSPropertyStrokeDasharray:
4086         return consumeStrokeDasharray(m_range);
4087     case CSSPropertyColumnRuleWidth:
4088         return consumeColumnRuleWidth(m_range, m_context.mode);
4089     case CSSPropertyStrokeOpacity:
4090     case CSSPropertyFillOpacity:
4091     case CSSPropertyStopOpacity:
4092     case CSSPropertyFloodOpacity:
4093     case CSSPropertyOpacity:
4094     case CSSPropertyWebkitBoxFlex:
4095         return consumeNumber(m_range, ValueRangeAll);
4096     case CSSPropertyBaselineShift:
4097         return consumeBaselineShift(m_range);
4098     case CSSPropertyKerning:
4099         return consumeKerning(m_range, m_context.mode);
4100     case CSSPropertyStrokeMiterlimit:
4101         return consumeNumber(m_range, ValueRangeNonNegative);
4102     case CSSPropertyStrokeWidth:
4103     case CSSPropertyStrokeDashoffset:
4104     case CSSPropertyCx:
4105     case CSSPropertyCy:
4106     case CSSPropertyX:
4107     case CSSPropertyY:
4108     case CSSPropertyR:
4109         return consumeLengthOrPercent(m_range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
4110     case CSSPropertyRx:
4111     case CSSPropertyRy:
4112         return consumeRxOrRy(m_range);
4113     case CSSPropertyCursor:
4114         return consumeCursor(m_range, m_context, inQuirksMode());
4115     case CSSPropertyContent:
4116         return consumeContent(m_range, m_context);
4117     case CSSPropertyListStyleImage:
4118     case CSSPropertyBorderImageSource:
4119     case CSSPropertyWebkitMaskBoxImageSource:
4120         return consumeImageOrNone(m_range, m_context);
4121     case CSSPropertyPerspective:
4122         return consumePerspective(m_range, m_context.mode);
4123     case CSSPropertyBorderTopRightRadius:
4124     case CSSPropertyBorderTopLeftRadius:
4125     case CSSPropertyBorderBottomLeftRadius:
4126     case CSSPropertyBorderBottomRightRadius:
4127         return consumeBorderRadiusCorner(m_range, m_context.mode);
4128     case CSSPropertyWebkitBoxFlexGroup:
4129         return consumeInteger(m_range, 0);
4130     case CSSPropertyOrder:
4131         return consumeInteger(m_range);
4132     case CSSPropertyTextUnderlinePosition:
4133         // auto | [ [ under | from-font ] || [ left | right ] ], but we only support auto | under | from-font for now
4134         return consumeIdent<CSSValueAuto, CSSValueUnder, CSSValueFromFont>(m_range);
4135     case CSSPropertyTextUnderlineOffset:
4136         return consumeTextUnderlineOffset(m_range, m_context.mode);
4137     case CSSPropertyTextDecorationThickness:
4138         return consumeTextDecorationThickness(m_range, m_context.mode);
4139     case CSSPropertyVerticalAlign:
4140         return consumeVerticalAlign(m_range, m_context.mode);
4141     case CSSPropertyShapeOutside:
4142         return consumeShapeOutside(m_range, m_context);
4143     case CSSPropertyWebkitClipPath:
4144         return consumeWebkitClipPath(m_range, m_context);
4145     case CSSPropertyJustifyContent:
4146         // justify-content property does not allow the <baseline-position> values.
4147         if (isBaselineKeyword(m_range.peek().id()))
4148             return nullptr;
4149         return consumeContentDistributionOverflowPosition(m_range, isContentPositionOrLeftOrRightKeyword);
4150     case CSSPropertyAlignContent:
4151         return consumeContentDistributionOverflowPosition(m_range, isContentPositionKeyword);
4152     case CSSPropertyBorderImageRepeat:
4153     case CSSPropertyWebkitMaskBoxImageRepeat:
4154         return consumeBorderImageRepeat(m_range);
4155     case CSSPropertyBorderImageSlice:
4156     case CSSPropertyWebkitMaskBoxImageSlice:
4157         return consumeBorderImageSlice(property, m_range);
4158     case CSSPropertyBorderImageOutset:
4159     case CSSPropertyWebkitMaskBoxImageOutset:
4160         return consumeBorderImageOutset(m_range);
4161     case CSSPropertyBorderImageWidth:
4162     case CSSPropertyWebkitMaskBoxImageWidth:
4163         return consumeBorderImageWidth(m_range);
4164     case CSSPropertyWebkitBorderImage:
4165     case CSSPropertyWebkitMaskBoxImage:
4166         return consumeWebkitBorderImage(property, m_range, m_context);
4167     case CSSPropertyWebkitBoxReflect:
4168         return consumeReflect(m_range, m_context);
4169     case CSSPropertyWebkitLineBoxContain:
4170         return consumeLineBoxContain(m_range);
4171 #if ENABLE(CSS_IMAGE_ORIENTATION)
4172     case CSSPropertyImageOrientation:
4173         return consumeImageOrientation(m_range, m_context.mode);
4174 #endif
4175     case CSSPropertyBackgroundAttachment:
4176     case CSSPropertyBackgroundBlendMode:
4177     case CSSPropertyBackgroundClip:
4178     case CSSPropertyBackgroundImage:
4179     case CSSPropertyBackgroundOrigin:
4180     case CSSPropertyBackgroundPositionX:
4181     case CSSPropertyBackgroundPositionY:
4182     case CSSPropertyBackgroundSize:
4183     case CSSPropertyWebkitBackgroundClip:
4184     case CSSPropertyWebkitBackgroundOrigin:
4185     case CSSPropertyWebkitBackgroundComposite:
4186     case CSSPropertyWebkitBackgroundSize:
4187     case CSSPropertyWebkitMaskClip:
4188     case CSSPropertyWebkitMaskComposite:
4189     case CSSPropertyWebkitMaskImage:
4190     case CSSPropertyWebkitMaskOrigin:
4191     case CSSPropertyWebkitMaskPositionX:
4192     case CSSPropertyWebkitMaskPositionY:
4193     case CSSPropertyWebkitMaskSize:
4194     case CSSPropertyWebkitMaskSourceType:
4195         return consumeCommaSeparatedBackgroundComponent(property, m_range, m_context);
4196     case CSSPropertyWebkitMaskRepeatX:
4197     case CSSPropertyWebkitMaskRepeatY:
4198         return nullptr;
4199     case CSSPropertyAlignItems:
4200         return consumeAlignItems(m_range);
4201     case CSSPropertyJustifySelf:
4202         return consumeSelfPositionOverflowPosition(m_range, isSelfPositionOrLeftOrRightKeyword);
4203     case CSSPropertyAlignSelf:
4204         return consumeSelfPositionOverflowPosition(m_range, isSelfPositionKeyword);
4205     case CSSPropertyJustifyItems:
4206         return consumeJustifyItems(m_range);
4207     case CSSPropertyGridColumnEnd:
4208     case CSSPropertyGridColumnStart:
4209     case CSSPropertyGridRowEnd:
4210     case CSSPropertyGridRowStart:
4211         return consumeGridLine(m_range);
4212     case CSSPropertyGridAutoColumns:
4213     case CSSPropertyGridAutoRows:
4214         return consumeGridTrackList(m_range, m_context.mode, GridAuto);
4215     case CSSPropertyGridTemplateColumns:
4216     case CSSPropertyGridTemplateRows:
4217         return consumeGridTemplatesRowsOrColumns(m_range, m_context.mode);
4218     case CSSPropertyGridTemplateAreas:
4219         return consumeGridTemplateAreas(m_range);
4220     case CSSPropertyGridAutoFlow:
4221         return consumeGridAutoFlow(m_range);
4222     case CSSPropertyWebkitLineGrid:
4223         return consumeLineGrid(m_range);
4224     case CSSPropertyWebkitInitialLetter:
4225         return consumeInitialLetter(m_range);
4226     case CSSPropertySpeakAs:
4227         return consumeSpeakAs(m_range);
4228     case CSSPropertyHangingPunctuation:
4229         return consumeHangingPunctuation(m_range);
4230     case CSSPropertyWebkitMarqueeIncrement:
4231         return consumeWebkitMarqueeIncrement(m_range, m_context.mode);
4232     case CSSPropertyWebkitMarqueeRepetition:
4233         return consumeWebkitMarqueeRepetition(m_range);
4234     case CSSPropertyWebkitMarqueeSpeed:
4235         return consumeWebkitMarqueeSpeed(m_range, m_context.mode);
4236     case CSSPropertyAlt:
4237         return consumeAlt(m_range, m_context);
4238     case CSSPropertyWebkitAspectRatio:
4239         return consumeWebkitAspectRatio(m_range);
4240     case CSSPropertyWebkitTextEmphasisPosition:
4241         return consumeTextEmphasisPosition(m_range);
4242 #if ENABLE(DARK_MODE_CSS)
4243     case CSSPropertyColorScheme:
4244         return consumeColorScheme(m_range);
4245 #endif
4246     default:
4247         return nullptr;
4248     }
4249 }
4250
4251 bool CSSPropertyParser::canParseTypedCustomPropertyValue(const String& syntax)
4252 {
4253     if (syntax != "*") {
4254         m_range.consumeWhitespace();
4255
4256         // First check for keywords
4257         CSSValueID id = m_range.peek().id();
4258         if (id == CSSValueInherit || id == CSSValueInitial || id == CSSValueRevert)
4259             return true;
4260
4261         auto localRange = m_range;
4262         while (!localRange.atEnd()) {
4263             auto id = localRange.consume().functionId();
4264             if (id == CSSValueVar || id == CSSValueEnv)
4265                 return true; // For variables, we just permit everything
4266         }
4267
4268         auto primitiveVal = consumeWidthOrHeight(m_range, m_context);
4269         if (primitiveVal && primitiveVal->isPrimitiveValue() && m_range.atEnd())
4270             return true;
4271         return false;
4272     }
4273
4274     return true;
4275 }
4276
4277 void CSSPropertyParser::collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies)
4278 {
4279     if (syntax != "*") {
4280         m_range.consumeWhitespace();
4281         auto primitiveVal = consumeWidthOrHeight(m_range, m_context);
4282         if (!m_range.atEnd())
4283             return;
4284         if (primitiveVal && primitiveVal->isPrimitiveValue()) {
4285             primitiveVal->collectDirectComputationalDependencies(dependencies);
4286             if (isRoot)
4287                 primitiveVal->collectDirectRootComputationalDependencies(dependencies);
4288         }
4289     }
4290 }
4291
4292 RefPtr<CSSCustomPropertyValue> CSSPropertyParser::parseTypedCustomPropertyValue(const String& name, const String& syntax, const StyleResolver& styleResolver)
4293 {
4294     if (syntax != "*") {
4295         m_range.consumeWhitespace();
4296         auto primitiveVal = consumeWidthOrHeight(m_range, m_context);
4297         if (primitiveVal && primitiveVal->isPrimitiveValue() && downcast<CSSPrimitiveValue>(*primitiveVal).isLength()) {
4298             auto length = StyleBuilderConverter::convertLength(styleResolver, *primitiveVal);
4299             if (!length.isCalculated() && !length.isUndefined())
4300                 return CSSCustomPropertyValue::createSyntaxLength(name, WTFMove(length));
4301         }
4302     } else {
4303         auto propertyValue = CSSCustomPropertyValue::createSyntaxAll(name, CSSVariableData::create(m_range));
4304         while (!m_range.atEnd())
4305             m_range.consume();
4306         return { WTFMove(propertyValue) };
4307     }
4308
4309     return nullptr;
4310 }
4311
4312 static RefPtr<CSSValueList> consumeFontFaceUnicodeRange(CSSParserTokenRange& range)
4313 {
4314     RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
4315
4316     do {
4317         const CSSParserToken& token = range.consumeIncludingWhitespace();
4318         if (token.type() != UnicodeRangeToken)
4319             return nullptr;
4320
4321         UChar32 start = token.unicodeRangeStart();
4322         UChar32 end = token.unicodeRangeEnd();
4323         if (start > end)
4324             return nullptr;
4325         values->append(CSSUnicodeRangeValue::create(start, end));
4326     } while (consumeCommaIncludingWhitespace(range));
4327
4328     return values;
4329 }
4330
4331 static RefPtr<CSSPrimitiveValue> consumeFontFaceFontDisplay(CSSParserTokenRange& range)
4332 {
4333     return consumeIdent<CSSValueAuto, CSSValueBlock, CSSValueSwap, CSSValueFallback, CSSValueOptional>(range);
4334 }
4335
4336 static RefPtr<CSSValue> consumeFontFaceSrcURI(CSSParserTokenRange& range, const CSSParserContext& context)
4337 {
4338     String url = consumeUrlAsStringView(range).toString();
4339     if (url.isNull())
4340         return nullptr;
4341
4342     RefPtr<CSSFontFaceSrcValue> uriValue = CSSFontFaceSrcValue::create(context.completeURL(url), context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No);
4343
4344     if (range.peek().functionId() != CSSValueFormat)
4345         return uriValue;
4346
4347     // FIXME: https://drafts.csswg.org/css-fonts says that format() contains a comma-separated list of strings,
4348     // but CSSFontFaceSrcValue stores only one format. Allowing one format for now.
4349     // FIXME: We're allowing the format to be an identifier as well as a string, because the old
4350     // parser did. It's not clear if we need to continue to support this behavior, but we have lots of
4351     // layout tests that rely on it.
4352     CSSParserTokenRange args = consumeFunction(range);
4353     const CSSParserToken& arg = args.consumeIncludingWhitespace();
4354     if ((arg.type() != StringToken && arg.type() != IdentToken) || !args.atEnd())
4355         return nullptr;
4356     uriValue->setFormat(arg.value().toString());
4357     return uriValue;
4358 }
4359
4360 static RefPtr<CSSValue> consumeFontFaceSrcLocal(CSSParserTokenRange& range)
4361 {
4362     CSSParserTokenRange args = consumeFunction(range);
4363     if (args.peek().type() == StringToken) {
4364         const CSSParserToken& arg = args.consumeIncludingWhitespace();
4365         if (!args.atEnd())
4366             return nullptr;
4367         return CSSFontFaceSrcValue::createLocal(arg.value().toString());
4368     }
4369     if (args.peek().type() == IdentToken) {
4370         String familyName = concatenateFamilyName(args);
4371         if (!args.atEnd())
4372             return nullptr;
4373         return CSSFontFaceSrcValue::createLocal(familyName);
4374     }
4375     return nullptr;
4376 }
4377
4378 static RefPtr<CSSValueList> consumeFontFaceSrc(CSSParserTokenRange& range, const CSSParserContext& context)
4379 {
4380     RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
4381
4382     do {
4383         const CSSParserToken& token = range.peek();
4384         RefPtr<CSSValue> parsedValue;
4385         if (token.functionId() == CSSValueLocal)
4386             parsedValue = consumeFontFaceSrcLocal(range);
4387         else
4388             parsedValue = consumeFontFaceSrcURI(range, context);
4389         if (!parsedValue)
4390             return nullptr;
4391         values->append(parsedValue.releaseNonNull());
4392     } while (consumeCommaIncludingWhitespace(range));
4393     return values;
4394 }
4395
4396 bool CSSPropertyParser::parseFontFaceDescriptor(CSSPropertyID propId)
4397 {
4398     RefPtr<CSSValue> parsedValue;
4399     switch (propId) {
4400     case CSSPropertyFontFamily:
4401         parsedValue = consumeFontFamilyDescriptor(m_range);
4402         break;
4403     case CSSPropertySrc: // This is a list of urls or local references.
4404         parsedValue = consumeFontFaceSrc(m_range, m_context);
4405         break;
4406     case CSSPropertyUnicodeRange:
4407         parsedValue = consumeFontFaceUnicodeRange(m_range);
4408         break;
4409     case CSSPropertyFontDisplay:
4410         parsedValue = consumeFontFaceFontDisplay(m_range);
4411         break;
4412     case CSSPropertyFontWeight:
4413 #if ENABLE(VARIATION_FONTS)
4414         parsedValue = consumeFontWeightRange(m_range);
4415 #else
4416         parsedValue = consumeFontWeight(m_range);
4417 #endif
4418         break;
4419     case CSSPropertyFontStretch:
4420 #if ENABLE(VARIATION_FONTS)
4421         parsedValue = consumeFontStretchRange(m_range);
4422 #else
4423         parsedValue = consumeFontStretch(m_range);
4424 #endif
4425         break;
4426     case CSSPropertyFontStyle:
4427 #if ENABLE(VARIATION_FONTS)
4428         parsedValue = consumeFontStyleRange(m_range, m_context.mode);
4429 #else
4430         parsedValue = consumeFontStyle(m_range, m_context.mode);
4431 #endif
4432         break;
4433     case CSSPropertyFontVariantCaps:
4434         parsedValue = consumeFontVariantCaps(m_range);
4435         break;
4436     case CSSPropertyFontVariantLigatures:
4437         parsedValue = consumeFontVariantLigatures(m_range);
4438         break;
4439     case CSSPropertyFontVariantNumeric:
4440         parsedValue = consumeFontVariantNumeric(m_range);
4441         break;
4442     case CSSPropertyFontVariantEastAsian:
4443         parsedValue = consumeFontVariantEastAsian(m_range);
4444         break;
4445     case CSSPropertyFontVariantAlternates:
4446         parsedValue = consumeFontVariantAlternates(m_range);
4447         break;
4448     case CSSPropertyFontVariantPosition:
4449         parsedValue = consumeFontVariantPosition(m_range);
4450         break;
4451     case CSSPropertyFontVariant:
4452         return consumeFontVariantShorthand(false);
4453     case CSSPropertyFontFeatureSettings:
4454         parsedValue = consumeFontFeatureSettings(m_range);
4455         break;
4456     default:
4457         break;
4458     }
4459
4460     if (!parsedValue || !m_range.atEnd())
4461         return false;
4462
4463     addProperty(propId, CSSPropertyInvalid, *parsedValue, false);
4464     return true;
4465 }
4466
4467 bool CSSPropertyParser::consumeSystemFont(bool important)
4468 {
4469     CSSValueID systemFontID = m_range.consumeIncludingWhitespace().id();
4470     ASSERT(systemFontID >= CSSValueCaption && systemFontID <= CSSValueStatusBar);
4471     if (!m_range.atEnd())
4472         return false;
4473     
4474     FontCascadeDescription fontDescription;
4475     RenderTheme::singleton().systemFont(systemFontID,