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