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