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