965e2b09e5766ef92e482685afebaf84e67e52f4
[WebKit.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 RefPtr<CSSPrimitiveValue> consumeFontStretchKeywordValue(CSSParserTokenRange& range)
881 {
882     return consumeIdent<CSSValueUltraCondensed, CSSValueExtraCondensed, CSSValueCondensed, CSSValueSemiCondensed, CSSValueNormal, CSSValueSemiExpanded, CSSValueExpanded, CSSValueExtraExpanded, CSSValueUltraExpanded>(range);
883 }
884
885 static RefPtr<CSSPrimitiveValue> consumeFontStretch(CSSParserTokenRange& range)
886 {
887     if (auto result = consumeFontStretchKeywordValue(range))
888         return result;
889     if (auto percent = consumePercent(range, ValueRangeAll))
890         return percent;
891     return consumeNumber(range, ValueRangeAll);
892 }
893
894 static String concatenateFamilyName(CSSParserTokenRange& range)
895 {
896     StringBuilder builder;
897     bool addedSpace = false;
898     const CSSParserToken& firstToken = range.peek();
899     while (range.peek().type() == IdentToken) {
900         if (!builder.isEmpty()) {
901             builder.append(' ');
902             addedSpace = true;
903         }
904         builder.append(range.consumeIncludingWhitespace().value());
905     }
906     if (!addedSpace && isCSSWideKeyword(firstToken.id()))
907         return String();
908     return builder.toString();
909 }
910
911 static RefPtr<CSSValue> consumeFamilyName(CSSParserTokenRange& range)
912 {
913     if (range.peek().type() == StringToken)
914         return CSSValuePool::singleton().createFontFamilyValue(range.consumeIncludingWhitespace().value().toString());
915     if (range.peek().type() != IdentToken)
916         return nullptr;
917     String familyName = concatenateFamilyName(range);
918     if (familyName.isNull())
919         return nullptr;
920     return CSSValuePool::singleton().createFontFamilyValue(familyName);
921 }
922
923 static RefPtr<CSSValue> consumeGenericFamily(CSSParserTokenRange& range)
924 {
925     return consumeIdentRange(range, CSSValueSerif, CSSValueWebkitBody);
926 }
927
928 static RefPtr<CSSValueList> consumeFontFamily(CSSParserTokenRange& range)
929 {
930     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
931     do {
932         RefPtr<CSSValue> parsedValue = consumeGenericFamily(range);
933         if (parsedValue) {
934             list->append(parsedValue.releaseNonNull());
935         } else {
936             parsedValue = consumeFamilyName(range);
937             if (parsedValue) {
938                 list->append(parsedValue.releaseNonNull());
939             } else {
940                 return nullptr;
941             }
942         }
943     } while (consumeCommaIncludingWhitespace(range));
944     return list;
945 }
946
947 static RefPtr<CSSValueList> consumeFontFamilyDescriptor(CSSParserTokenRange& range)
948 {
949     // FIXME-NEWPARSER: For compatibility with the old parser, we have to make
950     // a list here, even though the list always contains only a single family name.
951     // Once the old parser is gone, we can delete this function, make the caller
952     // use consumeFamilyName instead, and then patch the @font-face code to
953     // not expect a list with a single name in it.
954     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
955     RefPtr<CSSValue> parsedValue = consumeFamilyName(range);
956     if (parsedValue)
957         list->append(parsedValue.releaseNonNull());
958     
959     if (!range.atEnd() || !list->length())
960         return nullptr;
961
962     return list;
963 }
964
965 static RefPtr<CSSValue> consumeFontSynthesis(CSSParserTokenRange& range)
966 {
967     // none | [ weight || style || small-caps ]
968     CSSValueID id = range.peek().id();
969     if (id == CSSValueNone)
970         return consumeIdent(range);
971     
972     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
973     while (true) {
974         auto ident = consumeIdent<CSSValueWeight, CSSValueStyle, CSSValueSmallCaps>(range);
975         if (!ident)
976             break;
977         if (list->hasValue(ident.get()))
978             return nullptr;
979         list->append(ident.releaseNonNull());
980     }
981     
982     if (!list->length())
983         return nullptr;
984     return list;
985 }
986
987 static RefPtr<CSSValue> consumeLetterSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
988 {
989     if (range.peek().id() == CSSValueNormal)
990         return consumeIdent(range);
991     
992     return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
993 }
994
995 static RefPtr<CSSValue> consumeWordSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
996 {
997     if (range.peek().id() == CSSValueNormal)
998         return consumeIdent(range);
999     
1000     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1001 }
1002     
1003 static RefPtr<CSSValue> consumeTabSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1004 {
1005     RefPtr<CSSPrimitiveValue> parsedValue = consumeInteger(range, 0);
1006     if (parsedValue)
1007         return parsedValue;
1008     return consumeLength(range, cssParserMode, ValueRangeNonNegative);
1009 }
1010
1011 #if ENABLE(TEXT_AUTOSIZING)
1012 static RefPtr<CSSValue> consumeTextSizeAdjust(CSSParserTokenRange& range, CSSParserMode /* cssParserMode */)
1013 {
1014     if (range.peek().id() == CSSValueAuto)
1015         return consumeIdent(range);
1016     if (range.peek().id() == CSSValueNone)
1017         return consumeIdent(range);
1018     return consumePercent(range, ValueRangeNonNegative);
1019 }
1020 #endif
1021
1022 static RefPtr<CSSValue> consumeFontSize(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1023 {
1024     if (range.peek().id() >= CSSValueXxSmall && range.peek().id() <= CSSValueLarger)
1025         return consumeIdent(range);
1026     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, unitless);
1027 }
1028
1029 static RefPtr<CSSPrimitiveValue> consumeLineHeight(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1030 {
1031     if (range.peek().id() == CSSValueNormal)
1032         return consumeIdent(range);
1033
1034     RefPtr<CSSPrimitiveValue> lineHeight = consumeNumber(range, ValueRangeNonNegative);
1035     if (lineHeight)
1036         return lineHeight;
1037     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
1038 }
1039
1040 template<typename... Args>
1041 static Ref<CSSPrimitiveValue> createPrimitiveValuePair(Args&&... args)
1042 {
1043     return CSSValuePool::singleton().createValue(Pair::create(std::forward<Args>(args)...));
1044 }
1045
1046 static RefPtr<CSSValue> consumeCounter(CSSParserTokenRange& range, int defaultValue)
1047 {
1048     if (range.peek().id() == CSSValueNone)
1049         return consumeIdent(range);
1050
1051     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1052     do {
1053         RefPtr<CSSPrimitiveValue> counterName = consumeCustomIdent(range);
1054         if (!counterName)
1055             return nullptr;
1056         int i = defaultValue;
1057         if (RefPtr<CSSPrimitiveValue> counterValue = consumeInteger(range))
1058             i = counterValue->intValue();
1059         list->append(createPrimitiveValuePair(counterName.releaseNonNull(), CSSPrimitiveValue::create(i, CSSPrimitiveValue::UnitType::CSS_NUMBER), Pair::IdenticalValueEncoding::Coalesce));
1060     } while (!range.atEnd());
1061     return list;
1062 }
1063
1064 static RefPtr<CSSValue> consumePageSize(CSSParserTokenRange& range)
1065 {
1066     return consumeIdent<CSSValueA3, CSSValueA4, CSSValueA5, CSSValueB4, CSSValueB5, CSSValueLedger, CSSValueLegal, CSSValueLetter>(range);
1067 }
1068
1069 static RefPtr<CSSValueList> consumeSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1070 {
1071     RefPtr<CSSValueList> result = CSSValueList::createSpaceSeparated();
1072
1073     if (range.peek().id() == CSSValueAuto) {
1074         result->append(consumeIdent(range).releaseNonNull());
1075         return result;
1076     }
1077
1078     if (RefPtr<CSSValue> width = consumeLength(range, cssParserMode, ValueRangeNonNegative)) {
1079         RefPtr<CSSValue> height = consumeLength(range, cssParserMode, ValueRangeNonNegative);
1080         result->append(width.releaseNonNull());
1081         if (height)
1082             result->append(height.releaseNonNull());
1083         return result;
1084     }
1085
1086     RefPtr<CSSValue> pageSize = consumePageSize(range);
1087     RefPtr<CSSValue> orientation = consumeIdent<CSSValuePortrait, CSSValueLandscape>(range);
1088     if (!pageSize)
1089         pageSize = consumePageSize(range);
1090
1091     if (!orientation && !pageSize)
1092         return nullptr;
1093     if (pageSize)
1094         result->append(pageSize.releaseNonNull());
1095     if (orientation)
1096         result->append(orientation.releaseNonNull());
1097     return result;
1098 }
1099
1100 static RefPtr<CSSValue> consumeTextIndent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1101 {
1102     // [ <length> | <percentage> ] && hanging? && each-line?
1103     // Keywords only allowed when css3Text is enabled.
1104     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1105
1106     bool hasLengthOrPercentage = false;
1107 //    bool hasEachLine = false;
1108     bool hasHanging = false;
1109
1110     do {
1111         if (!hasLengthOrPercentage) {
1112             if (RefPtr<CSSValue> textIndent = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow)) {
1113                 list->append(*textIndent);
1114                 hasLengthOrPercentage = true;
1115                 continue;
1116             }
1117         }
1118
1119         CSSValueID id = range.peek().id();
1120  /* FIXME-NEWPARSER: We don't support this yet.
1121         if (!hasEachLine && id == CSSValueEachLine) {
1122             list->append(*consumeIdent(range));
1123             hasEachLine = true;
1124             continue;
1125         }
1126 */
1127         
1128         if (!hasHanging && id == CSSValueHanging) {
1129             list->append(consumeIdent(range).releaseNonNull());
1130             hasHanging = true;
1131             continue;
1132         }
1133         
1134         return nullptr;
1135     } while (!range.atEnd());
1136
1137     if (!hasLengthOrPercentage)
1138         return nullptr;
1139
1140     return list;
1141 }
1142
1143 // FIXME-NEWPARSER: Drop the prefix on min-content, max-content and fit-content.
1144 static bool validWidthOrHeightKeyword(CSSValueID id, const CSSParserContext& /*context*/)
1145 {
1146     if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic || id == CSSValueWebkitMinContent || id == CSSValueWebkitMaxContent || id == CSSValueWebkitFillAvailable || id == CSSValueWebkitFitContent) {
1147         return true;
1148     }
1149     return false;
1150 }
1151
1152 static RefPtr<CSSValue> consumeMaxWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1153 {
1154     if (range.peek().id() == CSSValueNone || validWidthOrHeightKeyword(range.peek().id(), context))
1155         return consumeIdent(range);
1156     return consumeLengthOrPercent(range, context.mode, ValueRangeNonNegative, unitless);
1157 }
1158
1159 static RefPtr<CSSValue> consumeWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1160 {
1161     if (range.peek().id() == CSSValueAuto || validWidthOrHeightKeyword(range.peek().id(), context))
1162         return consumeIdent(range);
1163     return consumeLengthOrPercent(range, context.mode, ValueRangeNonNegative, unitless);
1164 }
1165
1166 static RefPtr<CSSValue> consumeMarginOrOffset(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1167 {
1168     if (range.peek().id() == CSSValueAuto)
1169         return consumeIdent(range);
1170     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, unitless);
1171 }
1172
1173 static RefPtr<CSSPrimitiveValue> consumeClipComponent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1174 {
1175     if (range.peek().id() == CSSValueAuto)
1176         return consumeIdent(range);
1177     return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1178 }
1179
1180 static RefPtr<CSSValue> consumeClip(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1181 {
1182     if (range.peek().id() == CSSValueAuto)
1183         return consumeIdent(range);
1184
1185     if (range.peek().functionId() != CSSValueRect)
1186         return nullptr;
1187
1188     CSSParserTokenRange args = consumeFunction(range);
1189     // rect(t, r, b, l) || rect(t r b l)
1190     RefPtr<CSSPrimitiveValue> top = consumeClipComponent(args, cssParserMode);
1191     if (!top)
1192         return nullptr;
1193     bool needsComma = consumeCommaIncludingWhitespace(args);
1194     RefPtr<CSSPrimitiveValue> right = consumeClipComponent(args, cssParserMode);
1195     if (!right || (needsComma && !consumeCommaIncludingWhitespace(args)))
1196         return nullptr;
1197     RefPtr<CSSPrimitiveValue> bottom = consumeClipComponent(args, cssParserMode);
1198     if (!bottom || (needsComma && !consumeCommaIncludingWhitespace(args)))
1199         return nullptr;
1200     RefPtr<CSSPrimitiveValue> left = consumeClipComponent(args, cssParserMode);
1201     if (!left || !args.atEnd())
1202         return nullptr;
1203     
1204     auto rect = Rect::create();
1205     rect->setLeft(left.releaseNonNull());
1206     rect->setTop(top.releaseNonNull());
1207     rect->setRight(right.releaseNonNull());
1208     rect->setBottom(bottom.releaseNonNull());
1209     return CSSValuePool::singleton().createValue(WTFMove(rect));
1210 }
1211
1212 #if ENABLE(TOUCH_EVENTS)
1213 static RefPtr<CSSValue> consumeTouchAction(CSSParserTokenRange& range)
1214 {
1215     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1216     CSSValueID id = range.peek().id();
1217     if (id == CSSValueAuto || id == CSSValueNone || id == CSSValueManipulation) {
1218         list->append(consumeIdent(range).releaseNonNull());
1219         return list;
1220     }
1221     // FIXME-NEWPARSER: Support pan.
1222     return nullptr;
1223 }
1224 #endif
1225
1226 static RefPtr<CSSPrimitiveValue> consumeLineClamp(CSSParserTokenRange& range)
1227 {
1228     RefPtr<CSSPrimitiveValue> clampValue = consumePercent(range, ValueRangeNonNegative);
1229     if (clampValue)
1230         return clampValue;
1231     // When specifying number of lines, don't allow 0 as a valid value.
1232     return consumePositiveInteger(range);
1233 }
1234
1235 static RefPtr<CSSValue> consumeLocale(CSSParserTokenRange& range)
1236 {
1237     if (range.peek().id() == CSSValueAuto)
1238         return consumeIdent(range);
1239     return consumeString(range);
1240 }
1241
1242 static RefPtr<CSSValue> consumeHyphenateLimit(CSSParserTokenRange& range, CSSValueID valueID)
1243 {
1244     if (range.peek().id() == valueID)
1245         return consumeIdent(range);
1246     return consumeNumber(range, ValueRangeNonNegative);
1247 }
1248
1249 static RefPtr<CSSValue> consumeColumnWidth(CSSParserTokenRange& range)
1250 {
1251     if (range.peek().id() == CSSValueAuto)
1252         return consumeIdent(range);
1253     // Always parse lengths in strict mode here, since it would be ambiguous otherwise when used in
1254     // the 'columns' shorthand property.
1255     RefPtr<CSSPrimitiveValue> columnWidth = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
1256     if (!columnWidth || (!columnWidth->isCalculated() && !columnWidth->doubleValue()) || (columnWidth->cssCalcValue() && !columnWidth->cssCalcValue()->doubleValue()))
1257         return nullptr;
1258     return columnWidth;
1259 }
1260
1261 static RefPtr<CSSValue> consumeColumnCount(CSSParserTokenRange& range)
1262 {
1263     if (range.peek().id() == CSSValueAuto)
1264         return consumeIdent(range);
1265     return consumePositiveInteger(range);
1266 }
1267
1268 static RefPtr<CSSValue> consumeColumnGap(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1269 {
1270     if (range.peek().id() == CSSValueNormal)
1271         return consumeIdent(range);
1272     return consumeLength(range, cssParserMode, ValueRangeNonNegative);
1273 }
1274
1275 static RefPtr<CSSValue> consumeColumnSpan(CSSParserTokenRange& range)
1276 {
1277     return consumeIdent<CSSValueAll, CSSValueNone>(range);
1278 }
1279
1280 static RefPtr<CSSValue> consumeZoom(CSSParserTokenRange& range, const CSSParserContext& /*context*/)
1281 {
1282     const CSSParserToken& token = range.peek();
1283     RefPtr<CSSPrimitiveValue> zoom;
1284     if (token.type() == IdentToken)
1285         zoom = consumeIdent<CSSValueNormal, CSSValueReset, CSSValueDocument>(range);
1286     else {
1287         zoom = consumePercent(range, ValueRangeNonNegative);
1288         if (!zoom)
1289             zoom = consumeNumber(range, ValueRangeNonNegative);
1290     }
1291     return zoom;
1292 }
1293
1294 static RefPtr<CSSValue> consumeAnimationIterationCount(CSSParserTokenRange& range)
1295 {
1296     if (range.peek().id() == CSSValueInfinite)
1297         return consumeIdent(range);
1298     return consumeNumber(range, ValueRangeNonNegative);
1299 }
1300
1301 static RefPtr<CSSValue> consumeAnimationName(CSSParserTokenRange& range)
1302 {
1303     if (range.peek().id() == CSSValueNone)
1304         return consumeIdent(range);
1305
1306     if (range.peek().type() == StringToken) {
1307         const CSSParserToken& token = range.consumeIncludingWhitespace();
1308         if (equalIgnoringASCIICase(token.value(), "none"))
1309             return CSSValuePool::singleton().createIdentifierValue(CSSValueNone);
1310         // FIXME-NEWPARSER: Want to use a CSSCustomIdentValue here eventually.
1311         return CSSValuePool::singleton().createValue(token.value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
1312     }
1313
1314     return consumeCustomIdent(range);
1315 }
1316
1317 static RefPtr<CSSValue> consumeTransitionProperty(CSSParserTokenRange& range)
1318 {
1319     const CSSParserToken& token = range.peek();
1320     if (token.type() != IdentToken)
1321         return nullptr;
1322     if (token.id() == CSSValueNone)
1323         return consumeIdent(range);
1324
1325     if (CSSPropertyID property = token.parseAsCSSPropertyID()) {
1326         range.consumeIncludingWhitespace();
1327         
1328         // FIXME-NEWPARSER: No reason why we can't use the "all" property now that it exists.
1329         // The old parser used a value keyword for "all", though, since it predated support for
1330         // the property.
1331         if (property == CSSPropertyAll)
1332             return CSSValuePool::singleton().createIdentifierValue(CSSValueAll);
1333
1334         // FIXME-NEWPARSER: Want to use a CSSCustomIdentValue here eventually.
1335         return CSSValuePool::singleton().createIdentifierValue(property);
1336     }
1337     return consumeCustomIdent(range);
1338 }
1339
1340     
1341 static RefPtr<CSSValue> consumeSteps(CSSParserTokenRange& range) {
1342     ASSERT(range.peek().functionId() == CSSValueSteps);
1343     CSSParserTokenRange rangeCopy = range;
1344     CSSParserTokenRange args = consumeFunction(rangeCopy);
1345     
1346     RefPtr<CSSPrimitiveValue> steps = consumePositiveInteger(args);
1347     if (!steps)
1348         return nullptr;
1349     
1350     // FIXME-NEWPARSER: Support the middle value and change from a boolean to an enum.
1351     bool stepAtStart = false;
1352     if (consumeCommaIncludingWhitespace(args)) {
1353         switch (args.consumeIncludingWhitespace().id()) {
1354             case CSSValueStart:
1355                 stepAtStart = true;
1356             break;
1357             case CSSValueEnd:
1358                 stepAtStart = false;
1359                 break;
1360             default:
1361                 return nullptr;
1362         }
1363     }
1364     
1365     if (!args.atEnd())
1366         return nullptr;
1367     
1368     range = rangeCopy;
1369     return CSSStepsTimingFunctionValue::create(steps->intValue(), stepAtStart);
1370 }
1371
1372 static RefPtr<CSSValue> consumeCubicBezier(CSSParserTokenRange& range)
1373 {
1374     ASSERT(range.peek().functionId() == CSSValueCubicBezier);
1375     CSSParserTokenRange rangeCopy = range;
1376     CSSParserTokenRange args = consumeFunction(rangeCopy);
1377
1378     double x1, y1, x2, y2;
1379     if (consumeNumberRaw(args, x1)
1380         && x1 >= 0 && x1 <= 1
1381         && consumeCommaIncludingWhitespace(args)
1382         && consumeNumberRaw(args, y1)
1383         && consumeCommaIncludingWhitespace(args)
1384         && consumeNumberRaw(args, x2)
1385         && x2 >= 0 && x2 <= 1
1386         && consumeCommaIncludingWhitespace(args)
1387         && consumeNumberRaw(args, y2)
1388         && args.atEnd()) {
1389         range = rangeCopy;
1390         return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2);
1391     }
1392
1393     return nullptr;
1394 }
1395
1396 static RefPtr<CSSValue> consumeSpringFunction(CSSParserTokenRange& range)
1397 {
1398     ASSERT(range.peek().functionId() == CSSValueSpring);
1399     CSSParserTokenRange rangeCopy = range;
1400     CSSParserTokenRange args = consumeFunction(rangeCopy);
1401
1402     // Mass must be greater than 0.
1403     double mass;
1404     if (!consumeNumberRaw(args, mass) || mass <= 0)
1405         return nullptr;
1406     
1407     // Stiffness must be greater than 0.
1408     double stiffness;
1409     if (!consumeNumberRaw(args, stiffness) || stiffness <= 0)
1410         return nullptr;
1411     
1412     // Damping coefficient must be greater than or equal to 0.
1413     double damping;
1414     if (!consumeNumberRaw(args, damping) || damping < 0)
1415         return nullptr;
1416     
1417     // Initial velocity may have any value.
1418     double initialVelocity;
1419     if (!consumeNumberRaw(args, initialVelocity))
1420         return nullptr;
1421
1422     if (!args.atEnd())
1423         return nullptr;
1424
1425     range = rangeCopy;
1426
1427     return CSSSpringTimingFunctionValue::create(mass, stiffness, damping, initialVelocity);
1428 }
1429
1430 static RefPtr<CSSValue> consumeAnimationTimingFunction(CSSParserTokenRange& range, const CSSParserContext& context)
1431 {
1432     CSSValueID id = range.peek().id();
1433     if (id == CSSValueEase || id == CSSValueLinear || id == CSSValueEaseIn
1434         || id == CSSValueEaseOut || id == CSSValueEaseInOut || id == CSSValueStepStart || id == CSSValueStepEnd)
1435         return consumeIdent(range);
1436
1437     CSSValueID function = range.peek().functionId();
1438     if (function == CSSValueCubicBezier)
1439         return consumeCubicBezier(range);
1440     if (function == CSSValueSteps)
1441         return consumeSteps(range);
1442     if (context.springTimingFunctionEnabled && function == CSSValueSpring)
1443         return consumeSpringFunction(range);
1444     return nullptr;
1445 }
1446
1447 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
1448 static RefPtr<CSSValue> consumeWebkitAnimationTrigger(CSSParserTokenRange& range, CSSParserMode mode)
1449 {
1450     if (range.peek().id() == CSSValueAuto)
1451         return consumeIdent(range);
1452     
1453     if (range.peek().functionId() != CSSValueContainerScroll)
1454         return nullptr;
1455     
1456     CSSParserTokenRange rangeCopy = range;
1457     CSSParserTokenRange args = consumeFunction(rangeCopy);
1458
1459     RefPtr<CSSPrimitiveValue> startValue = consumeLength(args, mode, ValueRangeAll, UnitlessQuirk::Forbid);
1460     if (!startValue)
1461         return nullptr;
1462     
1463     if (args.atEnd()) {
1464         range = rangeCopy;
1465         return CSSAnimationTriggerScrollValue::create(startValue.releaseNonNull());
1466     }
1467
1468     if (!consumeCommaIncludingWhitespace(args))
1469         return nullptr;
1470
1471     RefPtr<CSSPrimitiveValue> endValue = consumeLength(args, mode, ValueRangeAll, UnitlessQuirk::Forbid);
1472     if (!endValue || !args.atEnd())
1473         return nullptr;
1474
1475     range = rangeCopy;
1476
1477     return CSSAnimationTriggerScrollValue::create(startValue.releaseNonNull(), endValue.releaseNonNull());
1478 }
1479 #endif
1480     
1481 static RefPtr<CSSValue> consumeAnimationValue(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
1482 {
1483     switch (property) {
1484     case CSSPropertyAnimationDelay:
1485     case CSSPropertyTransitionDelay:
1486         return consumeTime(range, context.mode, ValueRangeAll, UnitlessQuirk::Forbid);
1487     case CSSPropertyAnimationDirection:
1488         return consumeIdent<CSSValueNormal, CSSValueAlternate, CSSValueReverse, CSSValueAlternateReverse>(range);
1489     case CSSPropertyAnimationDuration:
1490     case CSSPropertyTransitionDuration:
1491         return consumeTime(range, context.mode, ValueRangeNonNegative, UnitlessQuirk::Allow);
1492     case CSSPropertyAnimationFillMode:
1493         return consumeIdent<CSSValueNone, CSSValueForwards, CSSValueBackwards, CSSValueBoth>(range);
1494     case CSSPropertyAnimationIterationCount:
1495         return consumeAnimationIterationCount(range);
1496     case CSSPropertyAnimationName:
1497         return consumeAnimationName(range);
1498     case CSSPropertyAnimationPlayState:
1499         return consumeIdent<CSSValueRunning, CSSValuePaused>(range);
1500     case CSSPropertyTransitionProperty:
1501         return consumeTransitionProperty(range);
1502     case CSSPropertyAnimationTimingFunction:
1503     case CSSPropertyTransitionTimingFunction:
1504         return consumeAnimationTimingFunction(range, context);
1505 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
1506     case CSSPropertyWebkitAnimationTrigger:
1507         return consumeWebkitAnimationTrigger(range, context.mode);
1508 #endif
1509     default:
1510         ASSERT_NOT_REACHED();
1511         return nullptr;
1512     }
1513 }
1514
1515 static bool isValidAnimationPropertyList(CSSPropertyID property, const CSSValueList& valueList)
1516 {
1517     if (property != CSSPropertyTransitionProperty || valueList.length() < 2)
1518         return true;
1519     for (auto& value : valueList) {
1520         if (value->isPrimitiveValue() && downcast<CSSPrimitiveValue>(value.get()).isValueID()
1521             && downcast<CSSPrimitiveValue>(value.get()).valueID() == CSSValueNone)
1522             return false;
1523     }
1524     return true;
1525 }
1526
1527 static RefPtr<CSSValue> consumeAnimationPropertyList(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
1528 {
1529     RefPtr<CSSValueList> list;
1530     RefPtr<CSSValue> singleton;
1531     do {
1532         RefPtr<CSSValue> currentValue = consumeAnimationValue(property, range, context);
1533         if (!currentValue)
1534             return nullptr;
1535         
1536         if (singleton && !list) {
1537             list = CSSValueList::createCommaSeparated();
1538             list->append(singleton.releaseNonNull());
1539         }
1540         
1541         if (list)
1542             list->append(currentValue.releaseNonNull());
1543         else
1544             singleton = WTFMove(currentValue);
1545         
1546     } while (consumeCommaIncludingWhitespace(range));
1547
1548     if (list) {
1549         if (!isValidAnimationPropertyList(property, *list))
1550             return nullptr;
1551     
1552         ASSERT(list->length());
1553         return list;
1554     }
1555     
1556     return singleton;
1557 }
1558
1559 bool CSSPropertyParser::consumeAnimationShorthand(const StylePropertyShorthand& shorthand, bool important)
1560 {
1561     const unsigned longhandCount = shorthand.length();
1562     RefPtr<CSSValueList> longhands[8];
1563     ASSERT(longhandCount <= 8);
1564     for (size_t i = 0; i < longhandCount; ++i)
1565         longhands[i] = CSSValueList::createCommaSeparated();
1566
1567     do {
1568         bool parsedLonghand[8] = { false };
1569         do {
1570             bool foundProperty = false;
1571             for (size_t i = 0; i < longhandCount; ++i) {
1572                 if (parsedLonghand[i])
1573                     continue;
1574
1575                 if (RefPtr<CSSValue> value = consumeAnimationValue(shorthand.properties()[i], m_range, m_context)) {
1576                     parsedLonghand[i] = true;
1577                     foundProperty = true;
1578                     longhands[i]->append(*value);
1579                     break;
1580                 }
1581             }
1582             if (!foundProperty)
1583                 return false;
1584         } while (!m_range.atEnd() && m_range.peek().type() != CommaToken);
1585
1586         // FIXME: This will make invalid longhands, see crbug.com/386459
1587         for (size_t i = 0; i < longhandCount; ++i) {
1588             if (!parsedLonghand[i])
1589                 longhands[i]->append(CSSValuePool::singleton().createImplicitInitialValue());
1590             parsedLonghand[i] = false;
1591         }
1592     } while (consumeCommaIncludingWhitespace(m_range));
1593
1594     for (size_t i = 0; i < longhandCount; ++i) {
1595         if (!isValidAnimationPropertyList(shorthand.properties()[i], *longhands[i]))
1596             return false;
1597     }
1598
1599     for (size_t i = 0; i < longhandCount; ++i)
1600         addProperty(shorthand.properties()[i], shorthand.id(), *longhands[i], important);
1601
1602     return m_range.atEnd();
1603 }
1604
1605 static RefPtr<CSSValue> consumeZIndex(CSSParserTokenRange& range)
1606 {
1607     if (range.peek().id() == CSSValueAuto)
1608         return consumeIdent(range);
1609     return consumeInteger(range);
1610 }
1611
1612 static RefPtr<CSSValue> consumeShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool isBoxShadowProperty)
1613 {
1614     if (range.peek().id() == CSSValueNone)
1615         return consumeIdent(range);
1616
1617     RefPtr<CSSValueList> shadowValueList = CSSValueList::createCommaSeparated();
1618     do {
1619         if (RefPtr<CSSShadowValue> shadowValue = consumeSingleShadow(range, cssParserMode, isBoxShadowProperty, isBoxShadowProperty))
1620             shadowValueList->append(*shadowValue);
1621         else
1622             return nullptr;
1623     } while (consumeCommaIncludingWhitespace(range));
1624     return shadowValueList;
1625 }
1626
1627 static RefPtr<CSSValue> consumeTextDecorationLine(CSSParserTokenRange& range)
1628 {
1629     CSSValueID id = range.peek().id();
1630     if (id == CSSValueNone)
1631         return consumeIdent(range);
1632
1633     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1634     while (true) {
1635 #if ENABLE(LETTERPRESS)
1636         RefPtr<CSSPrimitiveValue> ident = consumeIdent<CSSValueBlink, CSSValueUnderline, CSSValueOverline, CSSValueLineThrough, CSSValueWebkitLetterpress>(range);
1637 #else
1638         RefPtr<CSSPrimitiveValue> ident = consumeIdent<CSSValueBlink, CSSValueUnderline, CSSValueOverline, CSSValueLineThrough>(range);
1639 #endif
1640         if (!ident)
1641             break;
1642         if (list->hasValue(ident.get()))
1643             return nullptr;
1644         list->append(ident.releaseNonNull());
1645     }
1646
1647     if (!list->length())
1648         return nullptr;
1649     return list;
1650 }
1651
1652 static RefPtr<CSSValue> consumeTextDecorationSkip(CSSParserTokenRange& range)
1653 {
1654     CSSValueID id = range.peek().id();
1655     if (id == CSSValueNone)
1656         return consumeIdent(range);
1657
1658     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1659     while (true) {
1660         auto ident = consumeIdent<CSSValueAuto, CSSValueInk, CSSValueObjects>(range);
1661         if (!ident)
1662             break;
1663         if (list->hasValue(ident.get()))
1664             return nullptr;
1665         list->append(ident.releaseNonNull());
1666     }
1667
1668     if (!list->length())
1669         return nullptr;
1670     return list;
1671 }
1672
1673 static RefPtr<CSSValue> consumeTextEmphasisStyle(CSSParserTokenRange& range)
1674 {
1675     CSSValueID id = range.peek().id();
1676     if (id == CSSValueNone)
1677         return consumeIdent(range);
1678
1679     if (RefPtr<CSSValue> textEmphasisStyle = consumeString(range))
1680         return textEmphasisStyle;
1681
1682     RefPtr<CSSPrimitiveValue> fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
1683     RefPtr<CSSPrimitiveValue> shape = consumeIdent<CSSValueDot, CSSValueCircle, CSSValueDoubleCircle, CSSValueTriangle, CSSValueSesame>(range);
1684     if (!fill)
1685         fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
1686     if (fill && shape) {
1687         RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
1688         parsedValues->append(fill.releaseNonNull());
1689         parsedValues->append(shape.releaseNonNull());
1690         return parsedValues;
1691     }
1692     if (fill)
1693         return fill;
1694     if (shape)
1695         return shape;
1696     return nullptr;
1697 }
1698
1699 static RefPtr<CSSValue> consumeOutlineColor(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1700 {
1701     // Allow the special focus color even in HTML Standard parsing mode.
1702     if (range.peek().id() == CSSValueWebkitFocusRingColor)
1703         return consumeIdent(range);
1704     return consumeColor(range, cssParserMode);
1705 }
1706
1707 static RefPtr<CSSPrimitiveValue> consumeLineWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1708 {
1709     CSSValueID id = range.peek().id();
1710     if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick)
1711         return consumeIdent(range);
1712     return consumeLength(range, cssParserMode, ValueRangeNonNegative, unitless);
1713 }
1714
1715 static RefPtr<CSSPrimitiveValue> consumeBorderWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1716 {
1717     return consumeLineWidth(range, cssParserMode, unitless);
1718 }
1719
1720 static RefPtr<CSSPrimitiveValue> consumeTextStrokeWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1721 {
1722     return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
1723 }
1724
1725 static RefPtr<CSSPrimitiveValue> consumeColumnRuleWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1726 {
1727     return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
1728 }
1729
1730 static bool consumeTranslate3d(CSSParserTokenRange& args, CSSParserMode cssParserMode, RefPtr<CSSFunctionValue>& transformValue)
1731 {
1732     unsigned numberOfArguments = 2;
1733     RefPtr<CSSValue> parsedValue;
1734     do {
1735         parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1736         if (!parsedValue)
1737             return false;
1738         transformValue->append(*parsedValue);
1739         if (!consumeCommaIncludingWhitespace(args))
1740             return false;
1741     } while (--numberOfArguments);
1742     parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
1743     if (!parsedValue)
1744         return false;
1745     transformValue->append(*parsedValue);
1746     return true;
1747 }
1748
1749 static bool consumeNumbers(CSSParserTokenRange& args, RefPtr<CSSFunctionValue>& transformValue, unsigned numberOfArguments)
1750 {
1751     do {
1752         RefPtr<CSSPrimitiveValue> parsedValue = consumeNumber(args, ValueRangeAll);
1753         if (!parsedValue)
1754             return false;
1755         transformValue->append(parsedValue.releaseNonNull());
1756         if (--numberOfArguments && !consumeCommaIncludingWhitespace(args))
1757             return false;
1758     } while (numberOfArguments);
1759     return true;
1760 }
1761
1762 static bool consumePerspective(CSSParserTokenRange& args, CSSParserMode cssParserMode, RefPtr<CSSFunctionValue>& transformValue)
1763 {
1764     RefPtr<CSSPrimitiveValue> parsedValue = consumeLength(args, cssParserMode, ValueRangeNonNegative);
1765     if (!parsedValue) {
1766         double perspective;
1767         if (!consumeNumberRaw(args, perspective) || perspective < 0)
1768             return false;
1769         parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitType::CSS_PX);
1770     }
1771     if (!parsedValue)
1772         return false;
1773     transformValue->append(parsedValue.releaseNonNull());
1774     return true;
1775 }
1776
1777 static RefPtr<CSSValue> consumeTransformValue(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1778 {
1779     CSSValueID functionId = range.peek().functionId();
1780     if (functionId == CSSValueInvalid)
1781         return nullptr;
1782     CSSParserTokenRange args = consumeFunction(range);
1783     if (args.atEnd())
1784         return nullptr;
1785     
1786     RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(functionId);
1787     RefPtr<CSSValue> parsedValue;
1788     switch (functionId) {
1789     case CSSValueRotate:
1790     case CSSValueRotateX:
1791     case CSSValueRotateY:
1792     case CSSValueRotateZ:
1793     case CSSValueSkewX:
1794     case CSSValueSkewY:
1795     case CSSValueSkew:
1796         parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1797         if (!parsedValue)
1798             return nullptr;
1799         if (functionId == CSSValueSkew && consumeCommaIncludingWhitespace(args)) {
1800             transformValue->append(*parsedValue);
1801             parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1802             if (!parsedValue)
1803                 return nullptr;
1804         }
1805         break;
1806     case CSSValueScaleX:
1807     case CSSValueScaleY:
1808     case CSSValueScaleZ:
1809     case CSSValueScale:
1810         parsedValue = consumeNumber(args, ValueRangeAll);
1811         if (!parsedValue)
1812             return nullptr;
1813         if (functionId == CSSValueScale && consumeCommaIncludingWhitespace(args)) {
1814             transformValue->append(*parsedValue);
1815             parsedValue = consumeNumber(args, ValueRangeAll);
1816             if (!parsedValue)
1817                 return nullptr;
1818         }
1819         break;
1820     case CSSValuePerspective:
1821         if (!consumePerspective(args, cssParserMode, transformValue))
1822             return nullptr;
1823         break;
1824     case CSSValueTranslateX:
1825     case CSSValueTranslateY:
1826     case CSSValueTranslate:
1827         parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1828         if (!parsedValue)
1829             return nullptr;
1830         if (functionId == CSSValueTranslate && consumeCommaIncludingWhitespace(args)) {
1831             transformValue->append(*parsedValue);
1832             parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1833             if (!parsedValue)
1834                 return nullptr;
1835         }
1836         break;
1837     case CSSValueTranslateZ:
1838         parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
1839         break;
1840     case CSSValueMatrix:
1841     case CSSValueMatrix3d:
1842         if (!consumeNumbers(args, transformValue, (functionId == CSSValueMatrix3d) ? 16 : 6))
1843             return nullptr;
1844         break;
1845     case CSSValueScale3d:
1846         if (!consumeNumbers(args, transformValue, 3))
1847             return nullptr;
1848         break;
1849     case CSSValueRotate3d:
1850         if (!consumeNumbers(args, transformValue, 3) || !consumeCommaIncludingWhitespace(args))
1851             return nullptr;
1852         parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1853         if (!parsedValue)
1854             return nullptr;
1855         break;
1856     case CSSValueTranslate3d:
1857         if (!consumeTranslate3d(args, cssParserMode, transformValue))
1858             return nullptr;
1859         break;
1860     default:
1861         return nullptr;
1862     }
1863     if (parsedValue)
1864         transformValue->append(*parsedValue);
1865     if (!args.atEnd())
1866         return nullptr;
1867     return transformValue;
1868 }
1869
1870 static RefPtr<CSSValue> consumeTransform(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1871 {
1872     if (range.peek().id() == CSSValueNone)
1873         return consumeIdent(range);
1874
1875     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1876     do {
1877         RefPtr<CSSValue> parsedTransformValue = consumeTransformValue(range, cssParserMode);
1878         if (!parsedTransformValue)
1879             return nullptr;
1880         list->append(parsedTransformValue.releaseNonNull());
1881     } while (!range.atEnd());
1882
1883     return list;
1884 }
1885
1886 template <CSSValueID start, CSSValueID end>
1887 static RefPtr<CSSPrimitiveValue> consumePositionLonghand(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1888 {
1889     if (range.peek().type() == IdentToken) {
1890         CSSValueID id = range.peek().id();
1891         int percent;
1892         if (id == start)
1893             percent = 0;
1894         else if (id == CSSValueCenter)
1895             percent = 50;
1896         else if (id == end)
1897             percent = 100;
1898         else
1899             return nullptr;
1900         range.consumeIncludingWhitespace();
1901         return CSSPrimitiveValue::create(percent, CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
1902     }
1903     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
1904 }
1905
1906 static RefPtr<CSSPrimitiveValue> consumePositionX(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1907 {
1908     return consumePositionLonghand<CSSValueLeft, CSSValueRight>(range, cssParserMode);
1909 }
1910
1911 static RefPtr<CSSPrimitiveValue> consumePositionY(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1912 {
1913     return consumePositionLonghand<CSSValueTop, CSSValueBottom>(range, cssParserMode);
1914 }
1915
1916 static RefPtr<CSSValue> consumePaintStroke(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1917 {
1918     if (range.peek().id() == CSSValueNone)
1919         return consumeIdent(range);
1920     RefPtr<CSSPrimitiveValue> url = consumeUrl(range);
1921     if (url) {
1922         RefPtr<CSSValue> parsedValue;
1923         if (range.peek().id() == CSSValueNone)
1924             parsedValue = consumeIdent(range);
1925         else
1926             parsedValue = consumeColor(range, cssParserMode);
1927         if (parsedValue) {
1928             RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
1929             values->append(url.releaseNonNull());
1930             values->append(parsedValue.releaseNonNull());
1931             return values;
1932         }
1933         return url;
1934     }
1935     return consumeColor(range, cssParserMode);
1936 }
1937
1938 static RefPtr<CSSValue> consumeGlyphOrientation(CSSParserTokenRange& range, CSSParserMode mode, CSSPropertyID property)
1939 {
1940     if (range.peek().id() == CSSValueAuto) {
1941         if (property == CSSPropertyGlyphOrientationVertical)
1942             return consumeIdent(range);
1943         return nullptr;
1944     }
1945     
1946     return consumeAngle(range, mode, UnitlessQuirk::Allow);
1947 }
1948
1949 static RefPtr<CSSValue> consumePaintOrder(CSSParserTokenRange& range)
1950 {
1951     if (range.peek().id() == CSSValueNormal)
1952         return consumeIdent(range);
1953
1954     Vector<CSSValueID, 3> paintTypeList;
1955     RefPtr<CSSPrimitiveValue> fill;
1956     RefPtr<CSSPrimitiveValue> stroke;
1957     RefPtr<CSSPrimitiveValue> markers;
1958     do {
1959         CSSValueID id = range.peek().id();
1960         if (id == CSSValueFill && !fill)
1961             fill = consumeIdent(range);
1962         else if (id == CSSValueStroke && !stroke)
1963             stroke = consumeIdent(range);
1964         else if (id == CSSValueMarkers && !markers)
1965             markers = consumeIdent(range);
1966         else
1967             return nullptr;
1968         paintTypeList.append(id);
1969     } while (!range.atEnd());
1970
1971     // After parsing we serialize the paint-order list. Since it is not possible to
1972     // pop a last list items from CSSValueList without bigger cost, we create the
1973     // list after parsing.
1974     CSSValueID firstPaintOrderType = paintTypeList.at(0);
1975     RefPtr<CSSValueList> paintOrderList = CSSValueList::createSpaceSeparated();
1976     switch (firstPaintOrderType) {
1977     case CSSValueFill:
1978     case CSSValueStroke:
1979         paintOrderList->append(firstPaintOrderType == CSSValueFill ? fill.releaseNonNull() : stroke.releaseNonNull());
1980         if (paintTypeList.size() > 1) {
1981             if (paintTypeList.at(1) == CSSValueMarkers)
1982                 paintOrderList->append(markers.releaseNonNull());
1983         }
1984         break;
1985     case CSSValueMarkers:
1986         paintOrderList->append(markers.releaseNonNull());
1987         if (paintTypeList.size() > 1) {
1988             if (paintTypeList.at(1) == CSSValueStroke)
1989                 paintOrderList->append(stroke.releaseNonNull());
1990         }
1991         break;
1992     default:
1993         ASSERT_NOT_REACHED();
1994     }
1995
1996     return paintOrderList;
1997 }
1998
1999 static RefPtr<CSSValue> consumeNoneOrURI(CSSParserTokenRange& range)
2000 {
2001     if (range.peek().id() == CSSValueNone)
2002         return consumeIdent(range);
2003     return consumeUrl(range);
2004 }
2005
2006 static RefPtr<CSSValue> consumeFlexBasis(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2007 {
2008     // FIXME: Support intrinsic dimensions too.
2009     if (range.peek().id() == CSSValueAuto)
2010         return consumeIdent(range);
2011     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2012 }
2013
2014 static RefPtr<CSSValue> consumeKerning(CSSParserTokenRange& range, CSSParserMode mode)
2015 {
2016     RefPtr<CSSValue> result = consumeIdent<CSSValueAuto, CSSValueNormal>(range);
2017     if (result)
2018         return result;
2019     return consumeLength(range, mode, ValueRangeAll, UnitlessQuirk::Allow);
2020 }
2021
2022 static RefPtr<CSSValue> consumeStrokeDasharray(CSSParserTokenRange& range)
2023 {
2024     CSSValueID id = range.peek().id();
2025     if (id == CSSValueNone)
2026         return consumeIdent(range);
2027
2028     RefPtr<CSSValueList> dashes = CSSValueList::createCommaSeparated();
2029     do {
2030         RefPtr<CSSPrimitiveValue> dash = consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeNonNegative);
2031         if (!dash || (consumeCommaIncludingWhitespace(range) && range.atEnd()))
2032             return nullptr;
2033         dashes->append(dash.releaseNonNull());
2034     } while (!range.atEnd());
2035     return dashes;
2036 }
2037
2038 static RefPtr<CSSPrimitiveValue> consumeBaselineShift(CSSParserTokenRange& range)
2039 {
2040     CSSValueID id = range.peek().id();
2041     if (id == CSSValueBaseline || id == CSSValueSub || id == CSSValueSuper)
2042         return consumeIdent(range);
2043     return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll);
2044 }
2045
2046 static RefPtr<CSSPrimitiveValue> consumeRxOrRy(CSSParserTokenRange& range)
2047 {
2048     // FIXME-NEWPARSER: We don't support auto values when mapping, so for now turn this
2049     // off until we can figure out if we're even supposed to support it.
2050     // if (range.peek().id() == CSSValueAuto)
2051     //     return consumeIdent(range);
2052     return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
2053 }
2054
2055 static RefPtr<CSSValue> consumeCursor(CSSParserTokenRange& range, const CSSParserContext& context, bool inQuirksMode)
2056 {
2057     RefPtr<CSSValueList> list;
2058     while (RefPtr<CSSValue> image = consumeImage(range, context, ConsumeGeneratedImage::Forbid)) {
2059         double num;
2060         IntPoint hotSpot(-1, -1);
2061         bool hotSpotSpecified = false;
2062         if (consumeNumberRaw(range, num)) {
2063             hotSpot.setX(int(num));
2064             if (!consumeNumberRaw(range, num))
2065                 return nullptr;
2066             hotSpot.setY(int(num));
2067             hotSpotSpecified = true;
2068         }
2069
2070         if (!list)
2071             list = CSSValueList::createCommaSeparated();
2072
2073         list->append(CSSCursorImageValue::create(image.releaseNonNull(), hotSpotSpecified, hotSpot));
2074         if (!consumeCommaIncludingWhitespace(range))
2075             return nullptr;
2076     }
2077
2078     CSSValueID id = range.peek().id();
2079     RefPtr<CSSValue> cursorType;
2080     if (id == CSSValueHand) {
2081         if (!inQuirksMode) // Non-standard behavior
2082             return nullptr;
2083         cursorType = CSSValuePool::singleton().createIdentifierValue(CSSValuePointer);
2084         range.consumeIncludingWhitespace();
2085     } else if ((id >= CSSValueAuto && id <= CSSValueWebkitZoomOut) || id == CSSValueCopy || id == CSSValueNone) {
2086         cursorType = consumeIdent(range);
2087     } else {
2088         return nullptr;
2089     }
2090
2091     if (!list)
2092         return cursorType;
2093     list->append(cursorType.releaseNonNull());
2094     return list;
2095 }
2096
2097 static RefPtr<CSSValue> consumeAttr(CSSParserTokenRange args, CSSParserContext context)
2098 {
2099     if (args.peek().type() != IdentToken)
2100         return nullptr;
2101     
2102     CSSParserToken token = args.consumeIncludingWhitespace();
2103     auto attrName = token.value().toAtomicString();
2104     if (context.isHTMLDocument)
2105         attrName = attrName.convertToASCIILowercase();
2106
2107     if (!args.atEnd())
2108         return nullptr;
2109
2110     // FIXME-NEWPARSER: We want to use a CSSCustomIdentValue here eventually for the attrName.
2111     // FIXME-NEWPARSER: We want to use a CSSFunctionValue rather than relying on a custom
2112     // attr() primitive value.
2113     return CSSValuePool::singleton().createValue(attrName, CSSPrimitiveValue::CSS_ATTR);
2114 }
2115
2116 static RefPtr<CSSValue> consumeCounterContent(CSSParserTokenRange args, bool counters)
2117 {
2118     RefPtr<CSSPrimitiveValue> identifier = consumeCustomIdent(args);
2119     if (!identifier)
2120         return nullptr;
2121
2122     RefPtr<CSSPrimitiveValue> separator;
2123     if (!counters)
2124         separator = CSSPrimitiveValue::create(String(), CSSPrimitiveValue::UnitType::CSS_STRING);
2125     else {
2126         if (!consumeCommaIncludingWhitespace(args) || args.peek().type() != StringToken)
2127             return nullptr;
2128         separator = CSSPrimitiveValue::create(args.consumeIncludingWhitespace().value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
2129     }
2130
2131     RefPtr<CSSPrimitiveValue> listStyle;
2132     if (consumeCommaIncludingWhitespace(args)) {
2133         CSSValueID id = args.peek().id();
2134         if ((id != CSSValueNone && (id < CSSValueDisc || id > CSSValueKatakanaIroha)))
2135             return nullptr;
2136         listStyle = consumeIdent(args);
2137     } else
2138         listStyle = CSSValuePool::singleton().createIdentifierValue(CSSValueDecimal);
2139
2140     if (!args.atEnd())
2141         return nullptr;
2142     
2143     // FIXME-NEWPARSER: Should just have a CSSCounterValue.
2144     return CSSValuePool::singleton().createValue(Counter::create(identifier.releaseNonNull(), listStyle.releaseNonNull(), separator.releaseNonNull()));
2145 }
2146
2147 static RefPtr<CSSValue> consumeContent(CSSParserTokenRange& range, CSSParserContext context)
2148 {
2149     if (identMatches<CSSValueNone, CSSValueNormal>(range.peek().id()))
2150         return consumeIdent(range);
2151
2152     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
2153
2154     do {
2155         RefPtr<CSSValue> parsedValue = consumeImage(range, context);
2156         if (!parsedValue)
2157             parsedValue = consumeIdent<CSSValueOpenQuote, CSSValueCloseQuote, CSSValueNoOpenQuote, CSSValueNoCloseQuote>(range);
2158         if (!parsedValue)
2159             parsedValue = consumeString(range);
2160         if (!parsedValue) {
2161             if (range.peek().functionId() == CSSValueAttr)
2162                 parsedValue = consumeAttr(consumeFunction(range), context);
2163             else if (range.peek().functionId() == CSSValueCounter)
2164                 parsedValue = consumeCounterContent(consumeFunction(range), false);
2165             else if (range.peek().functionId() == CSSValueCounters)
2166                 parsedValue = consumeCounterContent(consumeFunction(range), true);
2167             if (!parsedValue)
2168                 return nullptr;
2169         }
2170         values->append(parsedValue.releaseNonNull());
2171     } while (!range.atEnd());
2172
2173     return values;
2174 }
2175
2176 static RefPtr<CSSPrimitiveValue> consumePerspective(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2177 {
2178     if (range.peek().id() == CSSValueNone)
2179         return consumeIdent(range);
2180     RefPtr<CSSPrimitiveValue> parsedValue = consumeLength(range, cssParserMode, ValueRangeAll);
2181     if (!parsedValue) {
2182         // FIXME: Make this quirk only apply to the webkit prefixed version of the property.
2183         double perspective;
2184         if (!consumeNumberRaw(range, perspective))
2185             return nullptr;
2186         parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitType::CSS_PX);
2187     }
2188     if (parsedValue && (parsedValue->isCalculated() || parsedValue->doubleValue() > 0))
2189         return parsedValue;
2190     return nullptr;
2191 }
2192
2193 #if ENABLE(CSS_SCROLL_SNAP)
2194
2195 static RefPtr<CSSValueList> consumeScrollSnapAlign(CSSParserTokenRange& range)
2196 {
2197     RefPtr<CSSValueList> alignmentValue = CSSValueList::createSpaceSeparated();
2198     if (RefPtr<CSSPrimitiveValue> firstValue = consumeIdent<CSSValueNone, CSSValueStart, CSSValueCenter, CSSValueEnd>(range)) {
2199         alignmentValue->append(firstValue.releaseNonNull());
2200         if (auto secondValue = consumeIdent<CSSValueNone, CSSValueStart, CSSValueCenter, CSSValueEnd>(range))
2201             alignmentValue->append(secondValue.releaseNonNull());
2202     }
2203     return alignmentValue->length() ? alignmentValue : nullptr;
2204 }
2205
2206 static RefPtr<CSSValueList> consumeScrollSnapType(CSSParserTokenRange& range)
2207 {
2208     RefPtr<CSSValueList> typeValue = CSSValueList::createSpaceSeparated();
2209     RefPtr<CSSPrimitiveValue> secondValue;
2210
2211     auto firstValue = consumeIdent<CSSValueX, CSSValueY, CSSValueBlock, CSSValueInline, CSSValueBoth>(range);
2212     if (firstValue)
2213         secondValue = consumeIdent<CSSValueProximity, CSSValueMandatory>(range);
2214     else
2215         firstValue = consumeIdent<CSSValueNone, CSSValueProximity, CSSValueMandatory>(range);
2216
2217     if (!firstValue)
2218         return nullptr;
2219
2220     typeValue->append(firstValue.releaseNonNull());
2221     if (secondValue)
2222         typeValue->append(secondValue.releaseNonNull());
2223
2224     return typeValue;
2225 }
2226
2227 #endif
2228
2229 static RefPtr<CSSValue> consumeBorderRadiusCorner(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2230 {
2231     RefPtr<CSSPrimitiveValue> parsedValue1 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2232     if (!parsedValue1)
2233         return nullptr;
2234     RefPtr<CSSPrimitiveValue> parsedValue2 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2235     if (!parsedValue2)
2236         parsedValue2 = parsedValue1;
2237     return createPrimitiveValuePair(parsedValue1.releaseNonNull(), parsedValue2.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2238 }
2239
2240 static RefPtr<CSSPrimitiveValue> consumeVerticalAlign(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2241 {
2242     RefPtr<CSSPrimitiveValue> parsedValue = consumeIdentRange(range, CSSValueBaseline, CSSValueWebkitBaselineMiddle);
2243     if (!parsedValue)
2244         parsedValue = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2245     return parsedValue;
2246 }
2247
2248 static RefPtr<CSSPrimitiveValue> consumeShapeRadius(CSSParserTokenRange& args, CSSParserMode cssParserMode)
2249 {
2250     if (identMatches<CSSValueClosestSide, CSSValueFarthestSide>(args.peek().id()))
2251         return consumeIdent(args);
2252     return consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
2253 }
2254
2255 static RefPtr<CSSBasicShapeCircle> consumeBasicShapeCircle(CSSParserTokenRange& args, const CSSParserContext& context)
2256 {
2257     // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
2258     // circle( [<shape-radius>]? [at <position>]? )
2259     RefPtr<CSSBasicShapeCircle> shape = CSSBasicShapeCircle::create();
2260     if (RefPtr<CSSPrimitiveValue> radius = consumeShapeRadius(args, context.mode))
2261         shape->setRadius(radius.releaseNonNull());
2262     if (consumeIdent<CSSValueAt>(args)) {
2263         RefPtr<CSSPrimitiveValue> centerX;
2264         RefPtr<CSSPrimitiveValue> centerY;
2265         if (!consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY))
2266             return nullptr;
2267         shape->setCenterX(centerX.releaseNonNull());
2268         shape->setCenterY(centerY.releaseNonNull());
2269     }
2270     return shape;
2271 }
2272
2273 static RefPtr<CSSBasicShapeEllipse> consumeBasicShapeEllipse(CSSParserTokenRange& args, const CSSParserContext& context)
2274 {
2275     // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
2276     // ellipse( [<shape-radius>{2}]? [at <position>]? )
2277     RefPtr<CSSBasicShapeEllipse> shape = CSSBasicShapeEllipse::create();
2278     if (RefPtr<CSSPrimitiveValue> radiusX = consumeShapeRadius(args, context.mode)) {
2279         shape->setRadiusX(radiusX.releaseNonNull());
2280         if (RefPtr<CSSPrimitiveValue> radiusY = consumeShapeRadius(args, context.mode))
2281             shape->setRadiusY(radiusY.releaseNonNull());
2282     }
2283     if (consumeIdent<CSSValueAt>(args)) {
2284         RefPtr<CSSPrimitiveValue> centerX;
2285         RefPtr<CSSPrimitiveValue> centerY;
2286         if (!consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY))
2287             return nullptr;
2288         shape->setCenterX(centerX.releaseNonNull());
2289         shape->setCenterY(centerY.releaseNonNull());
2290     }
2291     return shape;
2292 }
2293
2294 static RefPtr<CSSBasicShapePolygon> consumeBasicShapePolygon(CSSParserTokenRange& args, const CSSParserContext& context)
2295 {
2296     RefPtr<CSSBasicShapePolygon> shape = CSSBasicShapePolygon::create();
2297     if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
2298         shape->setWindRule(args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? RULE_EVENODD : RULE_NONZERO);
2299         if (!consumeCommaIncludingWhitespace(args))
2300             return nullptr;
2301     }
2302
2303     do {
2304         RefPtr<CSSPrimitiveValue> xLength = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2305         if (!xLength)
2306             return nullptr;
2307         RefPtr<CSSPrimitiveValue> yLength = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2308         if (!yLength)
2309             return nullptr;
2310         shape->appendPoint(xLength.releaseNonNull(), yLength.releaseNonNull());
2311     } while (consumeCommaIncludingWhitespace(args));
2312     return shape;
2313 }
2314
2315 static RefPtr<CSSBasicShapePath> consumeBasicShapePath(CSSParserTokenRange& args)
2316 {
2317     WindRule windRule = RULE_NONZERO;
2318     if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
2319         windRule = args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? RULE_EVENODD : RULE_NONZERO;
2320         if (!consumeCommaIncludingWhitespace(args))
2321             return nullptr;
2322     }
2323
2324     if (args.peek().type() != StringToken)
2325         return nullptr;
2326     
2327     auto byteStream = std::make_unique<SVGPathByteStream>();
2328     if (!buildSVGPathByteStreamFromString(args.consumeIncludingWhitespace().value().toString(), *byteStream, UnalteredParsing))
2329         return nullptr;
2330     
2331     auto shape = CSSBasicShapePath::create(WTFMove(byteStream));
2332     shape->setWindRule(windRule);
2333     
2334     return WTFMove(shape);
2335 }
2336
2337 static void complete4Sides(RefPtr<CSSPrimitiveValue> side[4])
2338 {
2339     if (side[3])
2340         return;
2341     if (!side[2]) {
2342         if (!side[1])
2343             side[1] = side[0];
2344         side[2] = side[0];
2345     }
2346     side[3] = side[1];
2347 }
2348
2349 static bool consumeRadii(RefPtr<CSSPrimitiveValue> horizontalRadii[4], RefPtr<CSSPrimitiveValue> verticalRadii[4], CSSParserTokenRange& range, CSSParserMode cssParserMode, bool useLegacyParsing)
2350 {
2351     unsigned i = 0;
2352     for (; i < 4 && !range.atEnd() && range.peek().type() != DelimiterToken; ++i) {
2353         horizontalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2354         if (!horizontalRadii[i])
2355             return false;
2356     }
2357     if (!horizontalRadii[0])
2358         return false;
2359     if (range.atEnd()) {
2360         // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2;
2361         if (useLegacyParsing && i == 2) {
2362             verticalRadii[0] = horizontalRadii[1];
2363             horizontalRadii[1] = nullptr;
2364         } else {
2365             complete4Sides(horizontalRadii);
2366             for (unsigned i = 0; i < 4; ++i)
2367                 verticalRadii[i] = horizontalRadii[i];
2368             return true;
2369         }
2370     } else {
2371         if (!consumeSlashIncludingWhitespace(range))
2372             return false;
2373         for (i = 0; i < 4 && !range.atEnd(); ++i) {
2374             verticalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2375             if (!verticalRadii[i])
2376                 return false;
2377         }
2378         if (!verticalRadii[0] || !range.atEnd())
2379             return false;
2380     }
2381     complete4Sides(horizontalRadii);
2382     complete4Sides(verticalRadii);
2383     return true;
2384 }
2385
2386 static RefPtr<CSSBasicShapeInset> consumeBasicShapeInset(CSSParserTokenRange& args, const CSSParserContext& context)
2387 {
2388     RefPtr<CSSBasicShapeInset> shape = CSSBasicShapeInset::create();
2389     RefPtr<CSSPrimitiveValue> top = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2390     if (!top)
2391         return nullptr;
2392     RefPtr<CSSPrimitiveValue> right = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2393     RefPtr<CSSPrimitiveValue> bottom;
2394     RefPtr<CSSPrimitiveValue> left;
2395     if (right) {
2396         bottom = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2397         if (bottom)
2398             left = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2399     }
2400     if (left)
2401         shape->updateShapeSize4Values(top.releaseNonNull(), right.releaseNonNull(), bottom.releaseNonNull(), left.releaseNonNull());
2402     else if (bottom)
2403         shape->updateShapeSize3Values(top.releaseNonNull(), right.releaseNonNull(), bottom.releaseNonNull());
2404     else if (right)
2405         shape->updateShapeSize2Values(top.releaseNonNull(), right.releaseNonNull());
2406     else
2407         shape->updateShapeSize1Value(top.releaseNonNull());
2408
2409     if (consumeIdent<CSSValueRound>(args)) {
2410         RefPtr<CSSPrimitiveValue> horizontalRadii[4] = { 0 };
2411         RefPtr<CSSPrimitiveValue> verticalRadii[4] = { 0 };
2412         if (!consumeRadii(horizontalRadii, verticalRadii, args, context.mode, false))
2413             return nullptr;
2414         shape->setTopLeftRadius(createPrimitiveValuePair(horizontalRadii[0].releaseNonNull(), verticalRadii[0].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2415         shape->setTopRightRadius(createPrimitiveValuePair(horizontalRadii[1].releaseNonNull(), verticalRadii[1].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2416         shape->setBottomRightRadius(createPrimitiveValuePair(horizontalRadii[2].releaseNonNull(), verticalRadii[2].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2417         shape->setBottomLeftRadius(createPrimitiveValuePair(horizontalRadii[3].releaseNonNull(), verticalRadii[3].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2418     }
2419     return shape;
2420 }
2421
2422 static RefPtr<CSSValue> consumeBasicShape(CSSParserTokenRange& range, const CSSParserContext& context)
2423 {
2424     RefPtr<CSSValue> result;
2425     if (range.peek().type() != FunctionToken)
2426         return nullptr;
2427     CSSValueID id = range.peek().functionId();
2428     CSSParserTokenRange rangeCopy = range;
2429     CSSParserTokenRange args = consumeFunction(rangeCopy);
2430     
2431     // FIXME-NEWPARSER: CSSBasicShape should be a CSSValue, and shapes should not be primitive values.
2432     RefPtr<CSSBasicShape> shape;
2433     if (id == CSSValueCircle)
2434         shape = consumeBasicShapeCircle(args, context);
2435     else if (id == CSSValueEllipse)
2436         shape = consumeBasicShapeEllipse(args, context);
2437     else if (id == CSSValuePolygon)
2438         shape = consumeBasicShapePolygon(args, context);
2439     else if (id == CSSValueInset)
2440         shape = consumeBasicShapeInset(args, context);
2441     else if (id == CSSValuePath)
2442         shape = consumeBasicShapePath(args);
2443     if (!shape)
2444         return nullptr;
2445     range = rangeCopy;
2446     
2447     if (!args.atEnd())
2448         return nullptr;
2449
2450     return CSSValuePool::singleton().createValue(shape.releaseNonNull());
2451 }
2452
2453 static RefPtr<CSSValue> consumeBasicShapeOrBox(CSSParserTokenRange& range, const CSSParserContext& context)
2454 {
2455     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
2456     bool shapeFound = false;
2457     bool boxFound = false;
2458     while (!range.atEnd() && !(shapeFound && boxFound)) {
2459         RefPtr<CSSValue> componentValue;
2460         if (range.peek().type() == FunctionToken && !shapeFound) {
2461             componentValue = consumeBasicShape(range, context);
2462             shapeFound = true;
2463         } else if (range.peek().type() == IdentToken && !boxFound) {
2464             componentValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox, CSSValueFill, CSSValueStroke, CSSValueViewBox>(range);
2465             boxFound = true;
2466         }
2467         if (!componentValue)
2468             return nullptr;
2469         list->append(componentValue.releaseNonNull());
2470     }
2471     
2472     if (!range.atEnd() || !list->length())
2473         return nullptr;
2474     
2475     return list;
2476 }
2477     
2478 static RefPtr<CSSValue> consumeWebkitClipPath(CSSParserTokenRange& range, const CSSParserContext& context)
2479 {
2480     if (range.peek().id() == CSSValueNone)
2481         return consumeIdent(range);
2482     if (RefPtr<CSSPrimitiveValue> url = consumeUrl(range))
2483         return url;
2484     return consumeBasicShapeOrBox(range, context);
2485 }
2486
2487 static RefPtr<CSSValue> consumeShapeOutside(CSSParserTokenRange& range, const CSSParserContext& context)
2488 {
2489     if (RefPtr<CSSValue> imageValue = consumeImageOrNone(range, context))
2490         return imageValue;
2491     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
2492     if (RefPtr<CSSValue> boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
2493         list->append(boxValue.releaseNonNull());
2494     if (RefPtr<CSSValue> shapeValue = consumeBasicShape(range, context)) {
2495         list->append(shapeValue.releaseNonNull());
2496         if (list->length() < 2) {
2497             if (RefPtr<CSSValue> boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
2498                 list->append(boxValue.releaseNonNull());
2499         }
2500     }
2501     if (!list->length())
2502         return nullptr;
2503     return list;
2504 }
2505
2506 static RefPtr<CSSValue> consumeContentDistributionOverflowPosition(CSSParserTokenRange& range)
2507 {
2508     if (identMatches<CSSValueNormal, CSSValueBaseline, CSSValueLastBaseline>(range.peek().id()))
2509         return CSSContentDistributionValue::create(CSSValueInvalid, range.consumeIncludingWhitespace().id(), CSSValueInvalid);
2510
2511     CSSValueID distribution = CSSValueInvalid;
2512     CSSValueID position = CSSValueInvalid;
2513     CSSValueID overflow = CSSValueInvalid;
2514     do {
2515         CSSValueID id = range.peek().id();
2516         if (identMatches<CSSValueSpaceBetween, CSSValueSpaceAround, CSSValueSpaceEvenly, CSSValueStretch>(id)) {
2517             if (distribution != CSSValueInvalid)
2518                 return nullptr;
2519             distribution = id;
2520         } else if (identMatches<CSSValueStart, CSSValueEnd, CSSValueCenter, CSSValueFlexStart, CSSValueFlexEnd, CSSValueLeft, CSSValueRight>(id)) {
2521             if (position != CSSValueInvalid)
2522                 return nullptr;
2523             position = id;
2524         } else if (identMatches<CSSValueUnsafe, CSSValueSafe>(id)) {
2525             if (overflow != CSSValueInvalid)
2526                 return nullptr;
2527             overflow = id;
2528         } else {
2529             return nullptr;
2530         }
2531         range.consumeIncludingWhitespace();
2532     } while (!range.atEnd());
2533
2534     // The grammar states that we should have at least <content-distribution> or <content-position>.
2535     if (position == CSSValueInvalid && distribution == CSSValueInvalid)
2536         return nullptr;
2537
2538     // The grammar states that <overflow-position> must be associated to <content-position>.
2539     if (overflow != CSSValueInvalid && position == CSSValueInvalid)
2540         return nullptr;
2541
2542     return CSSContentDistributionValue::create(distribution, position, overflow);
2543 }
2544
2545 static RefPtr<CSSPrimitiveValue> consumeBorderImageRepeatKeyword(CSSParserTokenRange& range)
2546 {
2547     return consumeIdent<CSSValueStretch, CSSValueRepeat, CSSValueSpace, CSSValueRound>(range);
2548 }
2549
2550 static RefPtr<CSSValue> consumeBorderImageRepeat(CSSParserTokenRange& range)
2551 {
2552     RefPtr<CSSPrimitiveValue> horizontal = consumeBorderImageRepeatKeyword(range);
2553     if (!horizontal)
2554         return nullptr;
2555     RefPtr<CSSPrimitiveValue> vertical = consumeBorderImageRepeatKeyword(range);
2556     if (!vertical)
2557         vertical = horizontal;
2558     return createPrimitiveValuePair(horizontal.releaseNonNull(), vertical.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2559 }
2560
2561 static RefPtr<CSSValue> consumeBorderImageSlice(CSSPropertyID property, CSSParserTokenRange& range)
2562 {
2563     bool fill = consumeIdent<CSSValueFill>(range);
2564     RefPtr<CSSPrimitiveValue> slices[4] = { 0 };
2565
2566     for (size_t index = 0; index < 4; ++index) {
2567         RefPtr<CSSPrimitiveValue> value = consumePercent(range, ValueRangeNonNegative);
2568         if (!value)
2569             value = consumeNumber(range, ValueRangeNonNegative);
2570         if (!value)
2571             break;
2572         slices[index] = value;
2573     }
2574     if (!slices[0])
2575         return nullptr;
2576     if (consumeIdent<CSSValueFill>(range)) {
2577         if (fill)
2578             return nullptr;
2579         fill = true;
2580     }
2581     complete4Sides(slices);
2582     // FIXME: For backwards compatibility, -webkit-border-image, -webkit-mask-box-image and -webkit-box-reflect have to do a fill by default.
2583     // FIXME: What do we do with -webkit-box-reflect and -webkit-mask-box-image? Probably just have to leave them filling...
2584     if (property == CSSPropertyWebkitBorderImage || property == CSSPropertyWebkitMaskBoxImage || property == CSSPropertyWebkitBoxReflect)
2585         fill = true;
2586     
2587     // Now build a rect value to hold all four of our primitive values.
2588     // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2589     auto quad = Quad::create();
2590     quad->setTop(slices[0].releaseNonNull());
2591     quad->setRight(slices[1].releaseNonNull());
2592     quad->setBottom(slices[2].releaseNonNull());
2593     quad->setLeft(slices[3].releaseNonNull());
2594     
2595     // Make our new border image value now.
2596     return CSSBorderImageSliceValue::create(CSSValuePool::singleton().createValue(WTFMove(quad)), fill);
2597 }
2598
2599 static RefPtr<CSSValue> consumeBorderImageOutset(CSSParserTokenRange& range)
2600 {
2601     RefPtr<CSSPrimitiveValue> outsets[4] = { 0 };
2602
2603     RefPtr<CSSPrimitiveValue> value;
2604     for (size_t index = 0; index < 4; ++index) {
2605         value = consumeNumber(range, ValueRangeNonNegative);
2606         if (!value)
2607             value = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
2608         if (!value)
2609             break;
2610         outsets[index] = value;
2611     }
2612     if (!outsets[0])
2613         return nullptr;
2614     complete4Sides(outsets);
2615     
2616     // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2617     auto quad = Quad::create();
2618     quad->setTop(outsets[0].releaseNonNull());
2619     quad->setRight(outsets[1].releaseNonNull());
2620     quad->setBottom(outsets[2].releaseNonNull());
2621     quad->setLeft(outsets[3].releaseNonNull());
2622     
2623     return CSSValuePool::singleton().createValue(WTFMove(quad));
2624 }
2625
2626 static RefPtr<CSSValue> consumeBorderImageWidth(CSSParserTokenRange& range)
2627 {
2628     RefPtr<CSSPrimitiveValue> widths[4];
2629
2630     RefPtr<CSSPrimitiveValue> value;
2631     for (size_t index = 0; index < 4; ++index) {
2632         value = consumeNumber(range, ValueRangeNonNegative);
2633         if (!value)
2634             value = consumeLengthOrPercent(range, HTMLStandardMode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
2635         if (!value)
2636             value = consumeIdent<CSSValueAuto>(range);
2637         if (!value)
2638             break;
2639         widths[index] = value;
2640     }
2641     if (!widths[0])
2642         return nullptr;
2643     complete4Sides(widths);
2644     
2645     // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2646     auto quad = Quad::create();
2647     quad->setTop(widths[0].releaseNonNull());
2648     quad->setRight(widths[1].releaseNonNull());
2649     quad->setBottom(widths[2].releaseNonNull());
2650     quad->setLeft(widths[3].releaseNonNull());
2651     
2652     return CSSValuePool::singleton().createValue(WTFMove(quad));
2653 }
2654
2655 static bool consumeBorderImageComponents(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context, RefPtr<CSSValue>& source,
2656     RefPtr<CSSValue>& slice, RefPtr<CSSValue>& width, RefPtr<CSSValue>& outset, RefPtr<CSSValue>& repeat)
2657 {
2658     do {
2659         if (!source) {
2660             source = consumeImageOrNone(range, context);
2661             if (source)
2662                 continue;
2663         }
2664         if (!repeat) {
2665             repeat = consumeBorderImageRepeat(range);
2666             if (repeat)
2667                 continue;
2668         }
2669         if (!slice) {
2670             slice = consumeBorderImageSlice(property, range);
2671             if (slice) {
2672                 ASSERT(!width && !outset);
2673                 if (consumeSlashIncludingWhitespace(range)) {
2674                     width = consumeBorderImageWidth(range);
2675                     if (consumeSlashIncludingWhitespace(range)) {
2676                         outset = consumeBorderImageOutset(range);
2677                         if (!outset)
2678                             return false;
2679                     } else if (!width) {
2680                         return false;
2681                     }
2682                 }
2683             } else {
2684                 return false;
2685             }
2686         } else {
2687             return false;
2688         }
2689     } while (!range.atEnd());
2690     return true;
2691 }
2692
2693 static RefPtr<CSSValue> consumeWebkitBorderImage(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2694 {
2695     RefPtr<CSSValue> source;
2696     RefPtr<CSSValue> slice;
2697     RefPtr<CSSValue> width;
2698     RefPtr<CSSValue> outset;
2699     RefPtr<CSSValue> repeat;
2700     if (consumeBorderImageComponents(property, range, context, source, slice, width, outset, repeat))
2701         return createBorderImageValue(WTFMove(source), WTFMove(slice), WTFMove(width), WTFMove(outset), WTFMove(repeat));
2702     return nullptr;
2703 }
2704
2705 static RefPtr<CSSValue> consumeReflect(CSSParserTokenRange& range, const CSSParserContext& context)
2706 {
2707     if (range.peek().id() == CSSValueNone)
2708         return consumeIdent(range);
2709     
2710     RefPtr<CSSPrimitiveValue> direction = consumeIdent<CSSValueAbove, CSSValueBelow, CSSValueLeft, CSSValueRight>(range);
2711     if (!direction)
2712         return nullptr;
2713
2714     RefPtr<CSSPrimitiveValue> offset;
2715     if (range.atEnd())
2716         offset = CSSValuePool::singleton().createValue(0, CSSPrimitiveValue::UnitType::CSS_PX);
2717     else {
2718         offset = consumeLengthOrPercent(range, context.mode, ValueRangeAll, UnitlessQuirk::Forbid);
2719         if (!offset)
2720             return nullptr;
2721     }
2722
2723     RefPtr<CSSValue> mask;
2724     if (!range.atEnd()) {
2725         mask = consumeWebkitBorderImage(CSSPropertyWebkitBoxReflect, range, context);
2726         if (!mask)
2727             return nullptr;
2728     }
2729     return CSSReflectValue::create(direction.releaseNonNull(), offset.releaseNonNull(), WTFMove(mask));
2730 }
2731
2732 #if ENABLE(CSS_IMAGE_ORIENTATION)
2733 static RefPtr<CSSValue> consumeImageOrientation(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
2734 {
2735     if (range.peek().type() != NumberToken) {
2736         RefPtr<CSSPrimitiveValue> angle = consumeAngle(range, cssParserMode, unitless);
2737         if (angle && angle->doubleValue() == 0)
2738             return angle;
2739     }
2740     return nullptr;
2741 }
2742 #endif
2743
2744 static RefPtr<CSSPrimitiveValue> consumeBackgroundBlendMode(CSSParserTokenRange& range)
2745 {
2746     CSSValueID id = range.peek().id();
2747     if (id == CSSValueNormal || id == CSSValueOverlay || (id >= CSSValueMultiply && id <= CSSValueLuminosity))
2748         return consumeIdent(range);
2749     return nullptr;
2750 }
2751
2752 static RefPtr<CSSPrimitiveValue> consumeBackgroundAttachment(CSSParserTokenRange& range)
2753 {
2754     return consumeIdent<CSSValueScroll, CSSValueFixed, CSSValueLocal>(range);
2755 }
2756
2757 static RefPtr<CSSPrimitiveValue> consumeBackgroundBox(CSSParserTokenRange& range)
2758 {
2759     return consumeIdent<CSSValueBorderBox, CSSValuePaddingBox, CSSValueContentBox, CSSValueWebkitText>(range);
2760 }
2761
2762 static RefPtr<CSSPrimitiveValue> consumeBackgroundComposite(CSSParserTokenRange& range)
2763 {
2764     return consumeIdentRange(range, CSSValueClear, CSSValuePlusLighter);
2765 }
2766
2767 static RefPtr<CSSPrimitiveValue> consumeWebkitMaskSourceType(CSSParserTokenRange& range)
2768 {
2769     return consumeIdent<CSSValueAuto, CSSValueAlpha, CSSValueLuminance>(range);
2770 }
2771
2772 static RefPtr<CSSPrimitiveValue> consumePrefixedBackgroundBox(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& /*context*/)
2773 {
2774     // The values 'border', 'padding' and 'content' are deprecated and do not apply to the version of the property that has the -webkit- prefix removed.
2775     if (RefPtr<CSSPrimitiveValue> value = consumeIdentRange(range, CSSValueBorder, CSSValuePaddingBox))
2776         return value;
2777     if (range.peek().id() == CSSValueWebkitText || ((property == CSSPropertyWebkitBackgroundClip || property == CSSPropertyWebkitMaskClip) && range.peek().id() == CSSValueText))
2778         return consumeIdent(range);
2779     return nullptr;
2780 }
2781
2782 static RefPtr<CSSPrimitiveValue> consumeBackgroundSize(CSSPropertyID property, CSSParserTokenRange& range, CSSParserMode cssParserMode)
2783 {
2784     if (identMatches<CSSValueContain, CSSValueCover>(range.peek().id()))
2785         return consumeIdent(range);
2786
2787     // FIXME: We're allowing the unitless quirk on this property because our
2788     // tests assume that. Other browser engines don't allow it though.
2789     RefPtr<CSSPrimitiveValue> horizontal = consumeIdent<CSSValueAuto>(range);
2790     if (!horizontal)
2791         horizontal = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2792
2793     RefPtr<CSSPrimitiveValue> vertical;
2794     if (!range.atEnd()) {
2795         if (range.peek().id() == CSSValueAuto) // `auto' is the default
2796             range.consumeIncludingWhitespace();
2797         else
2798             vertical = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2799     } else if (!vertical && property == CSSPropertyWebkitBackgroundSize) {
2800         // Legacy syntax: "-webkit-background-size: 10px" is equivalent to "background-size: 10px 10px".
2801         vertical = horizontal;
2802     }
2803     if (!vertical)
2804         return horizontal;
2805     return createPrimitiveValuePair(horizontal.releaseNonNull(), vertical.releaseNonNull(), property == CSSPropertyWebkitBackgroundSize ? Pair::IdenticalValueEncoding::Coalesce : Pair::IdenticalValueEncoding::DoNotCoalesce);
2806 }
2807
2808 static RefPtr<CSSValueList> consumeGridAutoFlow(CSSParserTokenRange& range)
2809 {
2810     RefPtr<CSSPrimitiveValue> rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
2811     RefPtr<CSSPrimitiveValue> denseAlgorithm = consumeIdent<CSSValueDense>(range);
2812     if (!rowOrColumnValue) {
2813         rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
2814         if (!rowOrColumnValue && !denseAlgorithm)
2815             return nullptr;
2816     }
2817     RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
2818     if (rowOrColumnValue)
2819         parsedValues->append(rowOrColumnValue.releaseNonNull());
2820     if (denseAlgorithm)
2821         parsedValues->append(denseAlgorithm.releaseNonNull());
2822     return parsedValues;
2823 }
2824
2825 static RefPtr<CSSValue> consumeBackgroundComponent(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2826 {
2827     switch (property) {
2828     case CSSPropertyBackgroundClip:
2829         return consumeBackgroundBox(range);
2830     case CSSPropertyBackgroundBlendMode:
2831         return consumeBackgroundBlendMode(range);
2832     case CSSPropertyBackgroundAttachment:
2833         return consumeBackgroundAttachment(range);
2834     case CSSPropertyBackgroundOrigin:
2835         return consumeBackgroundBox(range);
2836     case CSSPropertyWebkitMaskComposite:
2837     case CSSPropertyWebkitBackgroundComposite:
2838         return consumeBackgroundComposite(range);
2839     case CSSPropertyWebkitBackgroundClip:
2840     case CSSPropertyWebkitBackgroundOrigin:
2841     case CSSPropertyWebkitMaskClip:
2842     case CSSPropertyWebkitMaskOrigin:
2843         return consumePrefixedBackgroundBox(property, range, context);
2844     case CSSPropertyBackgroundImage:
2845     case CSSPropertyWebkitMaskImage:
2846         return consumeImageOrNone(range, context);
2847     case CSSPropertyWebkitMaskSourceType:
2848         return consumeWebkitMaskSourceType(range);
2849     case CSSPropertyBackgroundPositionX:
2850     case CSSPropertyWebkitMaskPositionX:
2851         return consumePositionX(range, context.mode);
2852     case CSSPropertyBackgroundPositionY:
2853     case CSSPropertyWebkitMaskPositionY:
2854         return consumePositionY(range, context.mode);
2855     case CSSPropertyBackgroundSize:
2856     case CSSPropertyWebkitBackgroundSize:
2857     case CSSPropertyWebkitMaskSize:
2858         return consumeBackgroundSize(property, range, context.mode);
2859     case CSSPropertyBackgroundColor:
2860         return consumeColor(range, context.mode);
2861     default:
2862         break;
2863     };
2864     return nullptr;
2865 }
2866
2867 static void addBackgroundValue(RefPtr<CSSValue>& list, Ref<CSSValue>&& value)
2868 {
2869     if (list) {
2870         if (!list->isBaseValueList()) {
2871             RefPtr<CSSValue> firstValue = list;
2872             list = CSSValueList::createCommaSeparated();
2873             downcast<CSSValueList>(*list).append(firstValue.releaseNonNull());
2874         }
2875         downcast<CSSValueList>(*list).append(WTFMove(value));
2876     } else {
2877         // To conserve memory we don't actually wrap a single value in a list.
2878         list = WTFMove(value);
2879     }
2880 }
2881
2882 static RefPtr<CSSValue> consumeCommaSeparatedBackgroundComponent(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2883 {
2884     RefPtr<CSSValue> result;
2885     do {
2886         RefPtr<CSSValue> value = consumeBackgroundComponent(property, range, context);
2887         if (!value)
2888             return nullptr;
2889         addBackgroundValue(result, value.releaseNonNull());
2890     } while (consumeCommaIncludingWhitespace(range));
2891     return result;
2892 }
2893
2894 static RefPtr<CSSPrimitiveValue> consumeSelfPositionKeyword(CSSParserTokenRange& range)
2895 {
2896     CSSValueID id = range.peek().id();
2897     if (id == CSSValueStart || id == CSSValueEnd || id == CSSValueCenter
2898         || id == CSSValueSelfStart || id == CSSValueSelfEnd || id == CSSValueFlexStart
2899         || id == CSSValueFlexEnd || id == CSSValueLeft || id == CSSValueRight)
2900         return consumeIdent(range);
2901     return nullptr;
2902 }
2903
2904 static RefPtr<CSSValue> consumeSelfPositionOverflowPosition(CSSParserTokenRange& range)
2905 {
2906     if (identMatches<CSSValueAuto, CSSValueNormal, CSSValueStretch, CSSValueBaseline, CSSValueLastBaseline>(range.peek().id()))
2907         return consumeIdent(range);
2908
2909     RefPtr<CSSPrimitiveValue> overflowPosition = consumeIdent<CSSValueUnsafe, CSSValueSafe>(range);
2910     RefPtr<CSSPrimitiveValue> selfPosition = consumeSelfPositionKeyword(range);
2911     if (!selfPosition)
2912         return nullptr;
2913     if (!overflowPosition)
2914         overflowPosition = consumeIdent<CSSValueUnsafe, CSSValueSafe>(range);
2915     if (overflowPosition)
2916         return createPrimitiveValuePair(selfPosition.releaseNonNull(), overflowPosition.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2917     return selfPosition;
2918 }
2919
2920 static RefPtr<CSSValue> consumeAlignItems(CSSParserTokenRange& range)
2921 {
2922     // align-items property does not allow the 'auto' value.
2923     if (identMatches<CSSValueAuto>(range.peek().id()))
2924         return nullptr;
2925     return consumeSelfPositionOverflowPosition(range);
2926 }
2927
2928 static RefPtr<CSSValue> consumeJustifyItems(CSSParserTokenRange& range)
2929 {
2930     CSSParserTokenRange rangeCopy = range;
2931     RefPtr<CSSPrimitiveValue> legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
2932     RefPtr<CSSPrimitiveValue> positionKeyword = consumeIdent<CSSValueCenter, CSSValueLeft, CSSValueRight>(rangeCopy);
2933     if (!legacy)
2934         legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
2935     if (legacy && positionKeyword) {
2936         range = rangeCopy;
2937         return createPrimitiveValuePair(legacy.releaseNonNull(), positionKeyword.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2938     }
2939     return consumeSelfPositionOverflowPosition(range);
2940 }
2941
2942 static RefPtr<CSSValue> consumeFitContent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2943 {
2944     CSSParserTokenRange rangeCopy = range;
2945     CSSParserTokenRange args = consumeFunction(rangeCopy);
2946     RefPtr<CSSPrimitiveValue> length = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
2947     if (!length || !args.atEnd())
2948         return nullptr;
2949     range = rangeCopy;
2950     RefPtr<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueFitContent);
2951     result->append(length.releaseNonNull());
2952     return result;
2953 }
2954
2955 static RefPtr<CSSPrimitiveValue> consumeCustomIdentForGridLine(CSSParserTokenRange& range)
2956 {
2957     if (range.peek().id() == CSSValueAuto || range.peek().id() == CSSValueSpan)
2958         return nullptr;
2959     return consumeCustomIdent(range);
2960 }
2961
2962 static RefPtr<CSSValue> consumeGridLine(CSSParserTokenRange& range)
2963 {
2964     if (range.peek().id() == CSSValueAuto)
2965         return consumeIdent(range);
2966
2967     RefPtr<CSSPrimitiveValue> spanValue;
2968     RefPtr<CSSPrimitiveValue> gridLineName;
2969     RefPtr<CSSPrimitiveValue> numericValue = consumeInteger(range);
2970     if (numericValue) {
2971         gridLineName = consumeCustomIdentForGridLine(range);
2972         spanValue = consumeIdent<CSSValueSpan>(range);
2973     } else {
2974         spanValue = consumeIdent<CSSValueSpan>(range);
2975         if (spanValue) {
2976             numericValue = consumeInteger(range);
2977             gridLineName = consumeCustomIdentForGridLine(range);
2978             if (!numericValue)
2979                 numericValue = consumeInteger(range);
2980         } else {
2981             gridLineName = consumeCustomIdentForGridLine(range);
2982             if (gridLineName) {
2983                 numericValue = consumeInteger(range);
2984                 spanValue = consumeIdent<CSSValueSpan>(range);
2985                 if (!spanValue && !numericValue)
2986                     return gridLineName;
2987             } else {
2988                 return nullptr;
2989             }
2990         }
2991     }
2992
2993     if (spanValue && !numericValue && !gridLineName)
2994         return nullptr; // "span" keyword alone is invalid.
2995     if (spanValue && numericValue && numericValue->intValue() < 0)
2996         return nullptr; // Negative numbers are not allowed for span.
2997     if (numericValue && numericValue->intValue() == 0)
2998         return nullptr; // An <integer> value of zero makes the declaration invalid.
2999
3000     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3001     if (spanValue)
3002         values->append(spanValue.releaseNonNull());
3003     if (numericValue)
3004         values->append(numericValue.releaseNonNull());
3005     if (gridLineName)
3006         values->append(gridLineName.releaseNonNull());
3007     ASSERT(values->length());
3008     return values;
3009 }
3010
3011 static bool isGridTrackFixedSized(const CSSPrimitiveValue& primitiveValue)
3012 {
3013     CSSValueID valueID = primitiveValue.valueID();
3014     if (valueID == CSSValueWebkitMinContent || valueID == CSSValueWebkitMaxContent || valueID == CSSValueAuto || primitiveValue.isFlex())
3015         return false;
3016
3017     return true;
3018 }
3019
3020 static bool isGridTrackFixedSized(const CSSValue& value)
3021 {
3022     if (value.isPrimitiveValue())
3023         return isGridTrackFixedSized(downcast<CSSPrimitiveValue>(value));
3024
3025     ASSERT(value.isFunctionValue());
3026     auto& function = downcast<CSSFunctionValue>(value);
3027     if (function.name() == CSSValueFitContent || function.length() < 2)
3028         return false;
3029
3030     const CSSValue* minPrimitiveValue = downcast<CSSPrimitiveValue>(function.item(0));
3031     const CSSValue* maxPrimitiveValue = downcast<CSSPrimitiveValue>(function.item(1));
3032     return isGridTrackFixedSized(*minPrimitiveValue) || isGridTrackFixedSized(*maxPrimitiveValue);
3033 }
3034
3035 static Vector<String> parseGridTemplateAreasColumnNames(const String& gridRowNames)
3036 {
3037     ASSERT(!gridRowNames.isEmpty());
3038     Vector<String> columnNames;
3039     // Using StringImpl to avoid checks and indirection in every call to String::operator[].
3040     StringImpl& text = *gridRowNames.impl();
3041
3042     StringBuilder areaName;
3043     for (unsigned i = 0; i < text.length(); ++i) {
3044         if (isCSSSpace(text[i])) {
3045             if (!areaName.isEmpty()) {
3046                 columnNames.append(areaName.toString());
3047                 areaName.clear();
3048             }
3049             continue;
3050         }
3051         if (text[i] == '.') {
3052             if (areaName == ".")
3053                 continue;
3054             if (!areaName.isEmpty()) {
3055                 columnNames.append(areaName.toString());
3056                 areaName.clear();
3057             }
3058         } else {
3059             if (!isNameCodePoint(text[i]))
3060                 return Vector<String>();
3061             if (areaName == ".") {
3062                 columnNames.append(areaName.toString());
3063                 areaName.clear();
3064             }
3065         }
3066
3067         areaName.append(text[i]);
3068     }
3069
3070     if (!areaName.isEmpty())
3071         columnNames.append(areaName.toString());
3072
3073     return columnNames;
3074 }
3075
3076 static bool parseGridTemplateAreasRow(const String& gridRowNames, NamedGridAreaMap& gridAreaMap, const size_t rowCount, size_t& columnCount)
3077 {
3078     if (gridRowNames.isEmpty() || gridRowNames.containsOnlyWhitespace())
3079         return false;
3080
3081     Vector<String> columnNames = parseGridTemplateAreasColumnNames(gridRowNames);
3082     if (rowCount == 0) {
3083         columnCount = columnNames.size();
3084         if (columnCount == 0)
3085             return false;
3086     } else if (columnCount != columnNames.size()) {
3087         // The declaration is invalid if all the rows don't have the number of columns.
3088         return false;
3089     }
3090
3091     for (size_t currentColumn = 0; currentColumn < columnCount; ++currentColumn) {
3092         const String& gridAreaName = columnNames[currentColumn];
3093
3094         // Unamed areas are always valid (we consider them to be 1x1).
3095         if (gridAreaName == ".")
3096             continue;
3097
3098         size_t lookAheadColumn = currentColumn + 1;
3099         while (lookAheadColumn < columnCount && columnNames[lookAheadColumn] == gridAreaName)
3100             lookAheadColumn++;
3101
3102         NamedGridAreaMap::iterator gridAreaIt = gridAreaMap.find(gridAreaName);
3103         if (gridAreaIt == gridAreaMap.end()) {
3104             gridAreaMap.add(gridAreaName, GridArea(GridSpan::translatedDefiniteGridSpan(rowCount, rowCount + 1), GridSpan::translatedDefiniteGridSpan(currentColumn, lookAheadColumn)));
3105         } else {
3106             GridArea& gridArea = gridAreaIt->value;
3107
3108             // The following checks test that the grid area is a single filled-in rectangle.
3109             // 1. The new row is adjacent to the previously parsed row.
3110             if (rowCount != gridArea.rows.endLine())
3111                 return false;
3112
3113             // 2. The new area starts at the same position as the previously parsed area.
3114             if (currentColumn != gridArea.columns.startLine())
3115                 return false;
3116
3117             // 3. The new area ends at the same position as the previously parsed area.
3118             if (lookAheadColumn != gridArea.columns.endLine())
3119                 return false;
3120
3121             gridArea.rows = GridSpan::translatedDefiniteGridSpan(gridArea.rows.startLine(), gridArea.rows.endLine() + 1);
3122         }
3123         currentColumn = lookAheadColumn - 1;
3124     }
3125
3126     return true;
3127 }
3128
3129 static RefPtr<CSSPrimitiveValue> consumeGridBreadth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3130 {
3131     const CSSParserToken& token = range.peek();
3132     if (identMatches<CSSValueWebkitMinContent, CSSValueWebkitMaxContent, CSSValueAuto>(token.id()))
3133         return consumeIdent(range);
3134     if (token.type() == DimensionToken && token.unitType() == CSSPrimitiveValue::UnitType::CSS_FR) {
3135         if (range.peek().numericValue() < 0)
3136             return nullptr;
3137         return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::CSS_FR);
3138     }
3139     return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3140 }
3141
3142 static RefPtr<CSSValue> consumeGridTrackSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3143 {
3144     const CSSParserToken& token = range.peek();
3145     if (identMatches<CSSValueAuto>(token.id()))
3146         return consumeIdent(range);
3147
3148     if (token.functionId() == CSSValueMinmax) {
3149         CSSParserTokenRange rangeCopy = range;
3150         CSSParserTokenRange args = consumeFunction(rangeCopy);
3151         RefPtr<CSSPrimitiveValue> minTrackBreadth = consumeGridBreadth(args, cssParserMode);
3152         if (!minTrackBreadth || minTrackBreadth->isFlex() || !consumeCommaIncludingWhitespace(args))
3153             return nullptr;
3154         RefPtr<CSSPrimitiveValue> maxTrackBreadth = consumeGridBreadth(args, cssParserMode);
3155         if (!maxTrackBreadth || !args.atEnd())
3156             return nullptr;
3157         range = rangeCopy;
3158         RefPtr<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueMinmax);
3159         result->append(minTrackBreadth.releaseNonNull());
3160         result->append(maxTrackBreadth.releaseNonNull());
3161         return result;
3162     }
3163
3164     if (token.functionId() == CSSValueFitContent)
3165         return consumeFitContent(range, cssParserMode);
3166
3167     return consumeGridBreadth(range, cssParserMode);
3168 }
3169
3170 // Appends to the passed in CSSGridLineNamesValue if any, otherwise creates a new one.
3171 static RefPtr<CSSGridLineNamesValue> consumeGridLineNames(CSSParserTokenRange& range, CSSGridLineNamesValue* lineNames = nullptr)
3172 {
3173     CSSParserTokenRange rangeCopy = range;
3174     if (rangeCopy.consumeIncludingWhitespace().type() != LeftBracketToken)
3175         return nullptr;
3176     
3177     RefPtr<CSSGridLineNamesValue> result = lineNames;
3178     if (!result)
3179         result = CSSGridLineNamesValue::create();
3180     while (RefPtr<CSSPrimitiveValue> lineName = consumeCustomIdentForGridLine(rangeCopy))
3181         result->append(lineName.releaseNonNull());
3182     if (rangeCopy.consumeIncludingWhitespace().type() != RightBracketToken)
3183         return nullptr;
3184     range = rangeCopy;
3185     return result;
3186 }
3187
3188 static bool consumeGridTrackRepeatFunction(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSValueList& list, bool& isAutoRepeat, bool& allTracksAreFixedSized)
3189 {
3190     CSSParserTokenRange args = consumeFunction(range);
3191     // The number of repetitions for <auto-repeat> is not important at parsing level
3192     // because it will be computed later, let's set it to 1.
3193     size_t repetitions = 1;
3194     isAutoRepeat = identMatches<CSSValueAutoFill, CSSValueAutoFit>(args.peek().id());
3195     RefPtr<CSSValueList> repeatedValues;
3196     if (isAutoRepeat)
3197         repeatedValues = CSSGridAutoRepeatValue::create(args.consumeIncludingWhitespace().id());
3198     else {
3199         // FIXME: a consumeIntegerRaw would be more efficient here.
3200         RefPtr<CSSPrimitiveValue> repetition = consumePositiveInteger(args);
3201         if (!repetition)
3202             return false;
3203         repetitions = clampTo<size_t>(repetition->doubleValue(), 0, kGridMaxTracks);
3204         repeatedValues = CSSValueList::createSpaceSeparated();
3205     }
3206     if (!consumeCommaIncludingWhitespace(args))
3207         return false;
3208     RefPtr<CSSGridLineNamesValue> lineNames = consumeGridLineNames(args);
3209     if (lineNames)
3210         repeatedValues->append(lineNames.releaseNonNull());
3211
3212     size_t numberOfTracks = 0;
3213     while (!args.atEnd()) {
3214         RefPtr<CSSValue> trackSize = consumeGridTrackSize(args, cssParserMode);
3215         if (!trackSize)
3216             return false;
3217         if (allTracksAreFixedSized)
3218             allTracksAreFixedSized = isGridTrackFixedSized(*trackSize);
3219         repeatedValues->append(trackSize.releaseNonNull());
3220         ++numberOfTracks;
3221         lineNames = consumeGridLineNames(args);
3222         if (lineNames)
3223             repeatedValues->append(lineNames.releaseNonNull());
3224     }
3225     // We should have found at least one <track-size> or else it is not a valid <track-list>.
3226     if (!numberOfTracks)
3227         return false;
3228
3229     if (isAutoRepeat)
3230         list.append(repeatedValues.releaseNonNull());
3231     else {
3232         // We clamp the repetitions to a multiple of the repeat() track list's size, while staying below the max grid size.
3233         repetitions = std::min(repetitions, kGridMaxTracks / numberOfTracks);
3234         for (size_t i = 0; i < repetitions; ++i) {
3235             for (size_t j = 0; j < repeatedValues->length(); ++j)
3236                 list.append(*repeatedValues->itemWithoutBoundsCheck(j));
3237         }
3238     }
3239     return true;
3240 }
3241
3242 enum TrackListType { GridTemplate, GridTemplateNoRepeat, GridAuto };
3243
3244 static RefPtr<CSSValue> consumeGridTrackList(CSSParserTokenRange& range, CSSParserMode cssParserMode, TrackListType trackListType)
3245 {
3246     bool allowGridLineNames = trackListType != GridAuto;
3247     RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3248     RefPtr<CSSGridLineNamesValue> lineNames = consumeGridLineNames(range);
3249     if (lineNames) {
3250         if (!allowGridLineNames)
3251             return nullptr;
3252         values->append(lineNames.releaseNonNull());
3253     }
3254     
3255     bool allowRepeat = trackListType == GridTemplate;
3256     bool seenAutoRepeat = false;
3257     bool allTracksAreFixedSized = true;
3258     do {
3259         bool isAutoRepeat;
3260         if (range.peek().functionId() == CSSValueRepeat) {
3261             if (!allowRepeat)
3262                 return nullptr;
3263             if (!consumeGridTrackRepeatFunction(range, cssParserMode, *values, isAutoRepeat, allTracksAreFixedSized))
3264                 return nullptr;
3265             if (isAutoRepeat && seenAutoRepeat)
3266                 return nullptr;
3267             seenAutoRepeat = seenAutoRepeat || isAutoRepeat;
3268         } else if (RefPtr<CSSValue> value = consumeGridTrackSize(range, cssParserMode)) {
3269             if (allTracksAreFixedSized)
3270                 allTracksAreFixedSized = isGridTrackFixedSized(*value);
3271             values->append(value.releaseNonNull());
3272         } else {
3273             return nullptr;
3274         }
3275         if (seenAutoRepeat && !allTracksAreFixedSized)
3276             return nullptr;
3277         lineNames = consumeGridLineNames(range);
3278         if (lineNames) {
3279             if (!allowGridLineNames)
3280                 return nullptr;
3281             values->append(lineNames.releaseNonNull());
3282         }
3283     } while (!range.atEnd() && range.peek().type() != DelimiterToken);
3284     return values;
3285 }
3286
3287 static RefPtr<CSSValue> consumeGridTemplatesRowsOrColumns(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3288 {
3289     if (range.peek().id() == CSSValueNone)
3290         return consumeIdent(range);
3291     return consumeGridTrackList(range, cssParserMode, GridTemplate);
3292 }
3293
3294 static RefPtr<CSSValue> consumeGridTemplateAreas(CSSParserTokenRange& range)
3295 {
3296     if (range.peek().id() == CSSValueNone)
3297         return consumeIdent(range);
3298
3299     NamedGridAreaMap gridAreaMap;
3300     size_t rowCount = 0;
3301     size_t columnCount = 0;
3302
3303     while (range.peek().type() == StringToken) {
3304         if (!parseGridTemplateAreasRow(range.consumeIncludingWhitespace().value().toString(), gridAreaMap, rowCount, columnCount))
3305             return nullptr;
3306         ++rowCount;
3307     }
3308
3309     if (rowCount == 0)
3310         return nullptr;
3311     ASSERT(columnCount);
3312     return CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount);
3313 }
3314
3315 #if ENABLE(CSS_REGIONS)
3316 static RefPtr<CSSValue> consumeFlowProperty(CSSParserTokenRange& range)
3317 {
3318     RefPtr<CSSValue> result;
3319     if (range.peek().id() == CSSValueNone)
3320         result = consumeIdent(range);
3321     else
3322         result = consumeCustomIdent(range);
3323     return result;
3324 }
3325 #endif
3326
3327 static RefPtr<CSSValue> consumeLineBoxContain(CSSParserTokenRange& range)
3328 {
3329     if (range.peek().id() == CSSValueNone)
3330         return consumeIdent(range);
3331
3332     LineBoxContain lineBoxContain = LineBoxContainNone;
3333     
3334     while (range.peek().type() == IdentToken) {
3335         auto id = range.peek().id();
3336         if (id == CSSValueBlock) {
3337             if (lineBoxContain & LineBoxContainBlock)
3338                 return nullptr;
3339             lineBoxContain |= LineBoxContainBlock;
3340         } else if (id == CSSValueInline) {
3341             if (lineBoxContain & LineBoxContainInline)
3342                 return nullptr;
3343             lineBoxContain |= LineBoxContainInline;
3344         } else if (id == CSSValueFont) {
3345             if (lineBoxContain & LineBoxContainFont)
3346                 return nullptr;
3347             lineBoxContain |= LineBoxContainFont;
3348         } else if (id == CSSValueGlyphs) {
3349             if (lineBoxContain & LineBoxContainGlyphs)
3350                 return nullptr;
3351             lineBoxContain |= LineBoxContainGlyphs;
3352         } else if (id == CSSValueReplaced) {
3353             if (lineBoxContain & LineBoxContainReplaced)
3354                 return nullptr;
3355             lineBoxContain |= LineBoxContainReplaced;
3356         } else if (id == CSSValueInlineBox) {
3357             if (lineBoxContain & LineBoxContainInlineBox)
3358                 return nullptr;
3359             lineBoxContain |= LineBoxContainInlineBox;
3360         } else if (id == CSSValueInitialLetter) {
3361             if (lineBoxContain & LineBoxContainInitialLetter)
3362                 return nullptr;
3363             lineBoxContain |= LineBoxContainInitialLetter;
3364         } else
3365             return nullptr;
3366         range.consumeIncludingWhitespace();
3367     }
3368     
3369     if (!lineBoxContain)
3370         return nullptr;
3371     
3372     return CSSLineBoxContainValue::create(lineBoxContain);
3373 }
3374
3375 static RefPtr<CSSValue> consumeLineGrid(CSSParserTokenRange& range)
3376 {
3377     if (range.peek().id() == CSSValueNone)
3378         return consumeIdent(range);
3379     return consumeCustomIdent(range);
3380 }
3381
3382 static RefPtr<CSSValue> consumeInitialLetter(CSSParserTokenRange& range)
3383 {
3384     RefPtr<CSSValue> ident = consumeIdent<CSSValueNormal>(range);
3385     if (ident)
3386         return ident;
3387     
3388     RefPtr<CSSPrimitiveValue> height = consumeNumber(range, ValueRangeNonNegative);
3389     if (!height)
3390         return nullptr;
3391     
3392     RefPtr<CSSPrimitiveValue> position;
3393     if (!range.atEnd()) {
3394         position = consumeNumber(range, ValueRangeNonNegative);
3395         if (!position || !range.atEnd())
3396             return nullptr;
3397     } else
3398         position = height.copyRef();
3399     
3400     return createPrimitiveValuePair(position.releaseNonNull(), WTFMove(height));
3401 }
3402
3403 static RefPtr<CSSValue> consumeHangingPunctuation(CSSParserTokenRange& range)
3404 {
3405     if (range.peek().id() == CSSValueNone)
3406         return consumeIdent(range);
3407     
3408     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3409     std::bitset<numCSSValueKeywords> seenValues;
3410
3411     bool seenForceEnd = false;
3412     bool seenAllowEnd = false;
3413     bool seenFirst = false;
3414     bool seenLast = false;
3415
3416     while (!range.atEnd()) {
3417         CSSValueID valueID = range.peek().id();
3418         if ((valueID == CSSValueFirst && seenFirst)
3419             || (valueID == CSSValueLast && seenLast)
3420             || (valueID == CSSValueAllowEnd && (seenAllowEnd || seenForceEnd))
3421             || (valueID == CSSValueForceEnd && (seenAllowEnd || seenForceEnd)))
3422             return nullptr;
3423         RefPtr<CSSValue> ident = consumeIdent<CSSValueAllowEnd, CSSValueForceEnd, CSSValueFirst, CSSValueLast>(range);
3424         if (!ident)
3425             return nullptr;
3426         switch (valueID) {
3427         case CSSValueAllowEnd:
3428             seenAllowEnd = true;
3429             break;
3430         case CSSValueForceEnd:
3431             seenForceEnd = true;
3432             break;
3433         case CSSValueFirst:
3434             seenFirst = true;
3435             break;
3436         case CSSValueLast:
3437             seenLast = true;
3438             break;
3439         default:
3440             break;
3441         }
3442         list->append(ident.releaseNonNull());
3443     }
3444     
3445     return list->length() ? list : nullptr;
3446 }
3447
3448 static RefPtr<CSSValue> consumeWebkitMarqueeIncrement(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3449 {
3450     if (range.peek().type() == IdentToken)
3451         return consumeIdent<CSSValueSmall, CSSValueMedium, CSSValueLarge>(range);
3452     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
3453 }
3454
3455 static RefPtr<CSSValue> consumeWebkitMarqueeRepetition(CSSParserTokenRange& range)
3456 {
3457     if (range.peek().type() == IdentToken)
3458         return consumeIdent<CSSValueInfinite>(range);
3459     return consumeNumber(range, ValueRangeNonNegative);
3460 }
3461
3462 static RefPtr<CSSValue> consumeWebkitMarqueeSpeed(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3463 {
3464     if (range.peek().type() == IdentToken)
3465         return consumeIdent<CSSValueSlow, CSSValueNormal, CSSValueFast>(range);
3466     return consumeTime(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3467 }
3468
3469 static RefPtr<CSSValue> consumeAlt(CSSParserTokenRange& range, const CSSParserContext& context)
3470 {
3471     if (range.peek().type() == StringToken)
3472         return consumeString(range);
3473     
3474     if (range.peek().functionId() != CSSValueAttr)
3475         return nullptr;
3476     
3477     return consumeAttr(consumeFunction(range), context);
3478 }
3479
3480 static RefPtr<CSSValue> consumeWebkitAspectRatio(CSSParserTokenRange& range)
3481 {
3482     if (range.peek().type() == IdentToken)
3483         return consumeIdent<CSSValueAuto, CSSValueFromDimensions, CSSValueFromIntrinsic>(range);
3484     
3485     RefPtr<CSSPrimitiveValue> leftValue = consumeNumber(range, ValueRangeNonNegative);
3486     if (!leftValue || !leftValue->floatValue() || range.atEnd() || !consumeSlashIncludingWhitespace(range))
3487         return nullptr;
3488     RefPtr<CSSPrimitiveValue> rightValue = consumeNumber(range, ValueRangeNonNegative);
3489     if (!rightValue || !rightValue->floatValue())
3490         return nullptr;
3491     
3492     return CSSAspectRatioValue::create(leftValue->floatValue(), rightValue->floatValue());
3493 }
3494
3495 static RefPtr<CSSValue> consumeTextEmphasisPosition(CSSParserTokenRange& range)
3496 {
3497     bool foundOverOrUnder = false;
3498     CSSValueID overUnderValueID = CSSValueOver;
3499     bool foundLeftOrRight = false;
3500     CSSValueID leftRightValueID = CSSValueRight;
3501     while (!range.atEnd()) {
3502         switch (range.peek().id()) {
3503         case CSSValueOver:
3504             if (foundOverOrUnder)
3505                 return nullptr;
3506             foundOverOrUnder = true;
3507             overUnderValueID = CSSValueOver;
3508             break;
3509         case CSSValueUnder:
3510             if (foundOverOrUnder)
3511                 return nullptr;
3512             foundOverOrUnder = true;
3513             overUnderValueID = CSSValueUnder;
3514             break;
3515         case CSSValueLeft:
3516             if (foundLeftOrRight)
3517                 return nullptr;
3518             foundLeftOrRight = true;
3519             leftRightValueID = CSSValueLeft;
3520             break;
3521         case CSSValueRight:
3522             if (foundLeftOrRight)
3523                 return nullptr;
3524             foundLeftOrRight = true;
3525             leftRightValueID = CSSValueRight;
3526             break;
3527         default:
3528             return nullptr;
3529         }
3530         
3531         range.consumeIncludingWhitespace();
3532     }
3533     if (!foundOverOrUnder)
3534         return nullptr;
3535     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3536     list->append(CSSValuePool::singleton().createIdentifierValue(overUnderValueID));
3537     if (foundLeftOrRight)
3538         list->append(CSSValuePool::singleton().createIdentifierValue(leftRightValueID));
3539     return list;
3540 }
3541     
3542 #if ENABLE(DASHBOARD_SUPPORT)
3543
3544 static RefPtr<CSSValue> consumeWebkitDashboardRegion(CSSParserTokenRange& range, CSSParserMode mode)
3545 {
3546     if (range.atEnd())
3547         return nullptr;
3548     
3549     if (range.peek().id() == CSSValueNone)
3550         return consumeIdent(range);
3551     
3552     auto firstRegion = DashboardRegion::create();
3553     DashboardRegion* region = nullptr;
3554     
3555     bool requireCommas = false;
3556     
3557     while (!range.atEnd()) {
3558         if (!region)
3559             region = firstRegion.ptr();
3560         else {
3561             auto nextRegion = DashboardRegion::create();
3562             region->m_next = nextRegion.copyRef();
3563             region = nextRegion.ptr();
3564         }
3565         
3566         if (range.peek().functionId() != CSSValueDashboardRegion)
3567             return nullptr;
3568         
3569         CSSParserTokenRange rangeCopy = range;
3570         CSSParserTokenRange args = consumeFunction(rangeCopy);
3571         if (rangeCopy.end() == args.end())
3572             return nullptr; // No ) was found. Be strict about this, since tests are.
3573
3574         // First arg is a label.
3575         if (args.peek().type() != IdentToken)
3576             return nullptr;
3577         region->m_label = args.consumeIncludingWhitespace().value().toString();
3578         
3579         // Comma is optional, so don't fail if we can't consume one.
3580         requireCommas = consumeCommaIncludingWhitespace(args);
3581
3582         // Second arg is a type.
3583         if (args.peek().type() != IdentToken)
3584             return nullptr;
3585         region->m_geometryType = args.consumeIncludingWhitespace().value().toString();
3586         if (equalLettersIgnoringASCIICase(region->m_geometryType, "circle"))
3587             region->m_isCircle = true;
3588         else if (equalLettersIgnoringASCIICase(region->m_geometryType, "rectangle"))
3589             region->m_isRectangle = true;
3590         else
3591             return nullptr;
3592         
3593         if (args.atEnd()) {
3594             // This originally used CSSValueInvalid by accident. It might be more logical to use something else.
3595             RefPtr<CSSPrimitiveValue> amount = CSSValuePool::singleton().createIdentifierValue(CSSValueInvalid);
3596             region->setTop(amount.copyRef());
3597             region->setRight(amount.copyRef());
3598             region->setBottom(amount.copyRef());
3599             region->setLeft(WTFMove(amount));
3600             range = rangeCopy;
3601             continue;
3602         }
3603         
3604         // Next four arguments must be offset numbers or auto.
3605         for (int i = 0; i < 4; ++i) {
3606             if (args.atEnd() || (requireCommas && !consumeCommaIncludingWhitespace(args)))
3607                 return nullptr;
3608
3609             if (args.atEnd())
3610                 return nullptr;
3611             
3612             RefPtr<CSSPrimitiveValue> amount;
3613             if (args.peek().id() == CSSValueAuto)
3614                 amount = consumeIdent(args);
3615             else
3616                 amount = consumeLength(args, mode, ValueRangeAll);
3617         
3618             if (!i)
3619                 region->setTop(WTFMove(amount));
3620             else if (i == 1)
3621                 region->setRight(WTFMove(amount));
3622             else if (i == 2)
3623                 region->setBottom(WTFMove(amount));
3624             else
3625                 region->setLeft(WTFMove(amount));
3626         }
3627         
3628         if (!args.atEnd())
3629             return nullptr;
3630         
3631         range = rangeCopy;
3632     }
3633     
3634     return CSSValuePool::singleton().createValue(RefPtr<DashboardRegion>(WTFMove(firstRegion)));
3635 }
3636     
3637 #endif
3638
3639 RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, CSSPropertyID currentShorthand)
3640 {
3641     if (CSSParserFastPaths::isKeywordPropertyID(property)) {
3642         if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(property, m_range.peek().id(), m_context.mode))
3643             return nullptr;
3644         return consumeIdent(m_range);
3645     }
3646     switch (property) {
3647     case CSSPropertyWillChange:
3648         return consumeWillChange(m_range);
3649     case CSSPropertyPage:
3650         return consumePage(m_range);
3651     case CSSPropertyQuotes:
3652         return consumeQuotes(m_range);
3653     case CSSPropertyFontVariantCaps:
3654         return consumeFontVariantCaps(m_range);
3655     case CSSPropertyFontVariantLigatures:
3656         return consumeFontVariantLigatures(m_range);
3657     case CSSPropertyFontVariantNumeric:
3658         return consumeFontVariantNumeric(m_range);
3659     case CSSPropertyFontVariantEastAsian:
3660         return consumeFontVariantEastAsian(m_range);
3661     case CSSPropertyFontFeatureSettings:
3662         return consumeFontFeatureSettings(m_range);
3663     case CSSPropertyFontFamily:
3664         return consumeFontFamily(m_range);
3665     case CSSPropertyFontWeight:
3666         return consumeFontWeight(m_range);
3667     case CSSPropertyFontStretch:
3668         return consumeFontStretch(m_range);
3669     case CSSPropertyFontSynthesis:
3670         return consumeFontSynthesis(m_range);
3671 #if ENABLE(VARIATION_FONTS)
3672     case CSSPropertyFontVariationSettings:
3673         return consumeFontVariationSettings(m_range);
3674 #endif
3675     case CSSPropertyLetterSpacing:
3676         return consumeLetterSpacing(m_range, m_context.mode);
3677     case CSSPropertyWordSpacing:
3678         return consumeWordSpacing(m_range, m_context.mode);
3679     case CSSPropertyTabSize:
3680         return consumeTabSize(m_range, m_context.mode);
3681 #if ENABLE(TEXT_AUTOSIZING)
3682     case CSSPropertyWebkitTextSizeAdjust:
3683         // FIXME: Support toggling the validation of this property via a runtime setting that is independent of
3684         // whether isTextAutosizingEnabled() is true. We want to enable this property on iOS, when simulating
3685         // a iOS device in Safari's responsive design mode and when optionally enabled in DRT/WTR. Otherwise,
3686         // this property should be disabled by default.
3687 #if !PLATFORM(IOS)
3688         if (!m_context.textAutosizingEnabled)
3689             return nullptr;
3690 #endif
3691         return consumeTextSizeAdjust(m_range, m_context.mode);
3692 #endif
3693     case CSSPropertyFontSize:
3694         return consumeFontSize(m_range, m_context.mode, UnitlessQuirk::Allow);
3695     case CSSPropertyLineHeight:
3696         return consumeLineHeight(m_range, m_context.mode);
3697     case CSSPropertyWebkitBorderHorizontalSpacing:
3698     case CSSPropertyWebkitBorderVerticalSpacing:
3699         return consumeLength(m_range, m_context.mode, ValueRangeNonNegative);
3700     case CSSPropertyCounterIncrement:
3701     case CSSPropertyCounterReset:
3702         return consumeCounter(m_range, property == CSSPropertyCounterIncrement ? 1 : 0);
3703     case CSSPropertySize:
3704         return consumeSize(m_range, m_context.mode);
3705     case CSSPropertyTextIndent:
3706         return consumeTextIndent(m_range, m_context.mode);
3707     case CSSPropertyMaxWidth:
3708     case CSSPropertyMaxHeight:
3709         return consumeMaxWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
3710     case CSSPropertyWebkitMaxLogicalWidth:
3711     case CSSPropertyWebkitMaxLogicalHeight:
3712         return consumeMaxWidthOrHeight(m_range, m_context);
3713     case CSSPropertyMinWidth:
3714     case CSSPropertyMinHeight:
3715     case CSSPropertyWidth:
3716     case CSSPropertyHeight:
3717         return consumeWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
3718     case CSSPropertyWebkitMinLogicalWidth:
3719     case CSSPropertyWebkitMinLogicalHeight:
3720     case CSSPropertyWebkitLogicalWidth:
3721     case CSSPropertyWebkitLogicalHeight:
3722         return consumeWidthOrHeight(m_range, m_context);
3723     case CSSPropertyMarginTop:
3724     case CSSPropertyMarginRight:
3725     case CSSPropertyMarginBottom:
3726     case CSSPropertyMarginLeft:
3727     case CSSPropertyBottom:
3728     case CSSPropertyLeft:
3729     case CSSPropertyRight:
3730     case CSSPropertyTop:
3731         return consumeMarginOrOffset(m_range, m_context.mode, UnitlessQuirk::Allow);
3732     case CSSPropertyWebkitMarginStart:
3733     case CSSPropertyWebkitMarginEnd:
3734     case CSSPropertyWebkitMarginBefore:
3735     case CSSPropertyWebkitMarginAfter:
3736         return consumeMarginOrOffset(m_range, m_context.mode, UnitlessQuirk::Forbid);
3737     case CSSPropertyPaddingTop:
3738     case CSSPropertyPaddingRight:
3739     case CSSPropertyPaddingBottom:
3740     case CSSPropertyPaddingLeft:
3741         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3742     case CSSPropertyWebkitPaddingStart:
3743     case CSSPropertyWebkitPaddingEnd:
3744     case CSSPropertyWebkitPaddingBefore:
3745     case CSSPropertyWebkitPaddingAfter:
3746         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
3747 #if ENABLE(CSS_SCROLL_SNAP)
3748     case CSSPropertyScrollSnapMarginBottom:
3749     case CSSPropertyScrollSnapMarginLeft:
3750     case CSSPropertyScrollSnapMarginRight:
3751     case CSSPropertyScrollSnapMarginTop:
3752         return consumeLength(m_range, m_context.mode, ValueRangeAll);
3753     case CSSPropertyScrollPaddingBottom:
3754     case CSSPropertyScrollPaddingLeft:
3755     case CSSPropertyScrollPaddingRight:
3756     case CSSPropertyScrollPaddingTop:
3757         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeAll);
3758     case CSSPropertyScrollSnapAlign:
3759         return consumeScrollSnapAlign(m_range);
3760     case CSSPropertyScrollSnapType:
3761         return consumeScrollSnapType(m_range);
3762 #endif
3763     case CSSPropertyClip:
3764         return consumeClip(m_range, m_context.mode);
3765 #if ENABLE(TOUCH_EVENTS)
3766     case CSSPropertyTouchAction:
3767         return consumeTouchAction(m_range);
3768 #endif
3769     case CSSPropertyObjectPosition:
3770         return consumePosition(m_range, m_context.mode, UnitlessQuirk::Forbid);
3771     case CSSPropertyWebkitLineClamp:
3772         return consumeLineClamp(m_range);
3773     case CSSPropertyWebkitFontSizeDelta:
3774         return consumeLength(m_range, m_context.mode, ValueRangeAll, UnitlessQuirk::Allow);
3775     case CSSPropertyWebkitHyphenateCharacter:
3776     case CSSPropertyWebkitLocale:
3777         return consumeLocale(m_range);
3778     case CSSPropertyWebkitHyphenateLimitBefore:
3779     case CSSPropertyWebkitHyphenateLimitAfter:
3780         return consumeHyphenateLimit(m_range, CSSValueAuto);
3781     case CSSPropertyWebkitHyphenateLimitLines:
3782         return consumeHyphenateLimit(m_range, CSSValueNoLimit);
3783     case CSSPropertyColumnWidth:
3784         return consumeColumnWidth(m_range);
3785     case CSSPropertyColumnCount:
3786         return consumeColumnCount(m_range);
3787     case CSSPropertyColumnGap:
3788         return consumeColumnGap(m_range, m_context.mode);
3789     case CSSPropertyColumnSpan:
3790         return consumeColumnSpan(m_range);
3791     case CSSPropertyZoom:
3792         return consumeZoom(m_range, m_context);
3793     case CSSPropertyAnimationDelay:
3794     case CSSPropertyTransitionDelay:
3795     case CSSPropertyAnimationDirection:
3796     case CSSPropertyAnimationDuration:
3797     case CSSPropertyTransitionDuration:
3798     case CSSPropertyAnimationFillMode:
3799     case CSSPropertyAnimationIterationCount:
3800     case CSSPropertyAnimationName:
3801     case CSSPropertyAnimationPlayState:
3802     case CSSPropertyTransitionProperty:
3803     case CSSPropertyAnimationTimingFunction:
3804     case CSSPropertyTransitionTimingFunction:
3805 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
3806     case CSSPropertyWebkitAnimationTrigger:
3807 #endif
3808         return consumeAnimationPropertyList(property, m_range, m_context);
3809     case CSSPropertyGridColumnGap:
3810     case CSSPropertyGridRowGap:
3811         return consumeLength(m_range, m_context.mode, ValueRangeNonNegative);
3812     case CSSPropertyShapeMargin:
3813         return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative);
3814     case CSSPropertyShapeImageThreshold:
3815         return consumeNumber(m_range, ValueRangeAll);
3816     case CSSPropertyWebkitBoxOrdinalGroup:
3817     case CSSPropertyOrphans:
3818     case CSSPropertyWidows:
3819         return consumePositiveInteger(m_range);
3820     case CSSPropertyWebkitTextDecorationColor:
3821         return consumeColor(m_range, m_context.mode);
3822     case CSSPropertyWebkitTextDecorationSkip:
3823         return consumeTextDecorationSkip(m_range);
3824     case CSSPropertyWebkitTextStrokeWidth:
3825         return consumeTextStrokeWidth(m_range, m_context.mode);
3826     case CSSPropertyWebkitTextFillColor:
3827 #if ENABLE(TOUCH_EVENTS)
3828     case CSSPropertyWebkitTapHighlightColor:
3829 #endif
3830     case CSSPropertyWebkitTextEmphasisColor:
3831     case CSSPropertyWebkitBorderStartColor:
3832     case CSSPropertyWebkitBorderEndColor:
3833     case CSSPropertyWebkitBorderBeforeColor:
3834     case CSSPropertyWebkitBorderAfterColor:
3835     case CSSPropertyWebkitTextStrokeColor:
3836     case CSSPropertyStopColor:
3837     case CSSPropertyFloodColor:
3838     case CSSPropertyLightingColor:
3839     case CSSPropertyColumnRuleColor:
3840         return consumeColor(m_range, m_context.mode);
3841     case CSSPropertyColor:
3842     case CSSPropertyBackgroundColor:
3843         return consumeColor(m_range, m_context.mode, inQuirksMode());
3844     case CSSPropertyWebkitBorderStartWidth:
3845     case CSSPropertyWebkitBorderEndWidth:
3846     case CSSPropertyWebkitBorderBeforeWidth:
3847     case CSSPropertyWebkitBorderAfterWidth:
3848         return consumeBorderWidth(m_range, m_context.mode, UnitlessQuirk::Forbid);
3849     case CSSPropertyBorderBottomColor:
3850     case CSSPropertyBorderLeftColor:
3851     case CSSPropertyBorderRightColor:
3852     case CSSPropertyBorderTopColor: {
3853         bool allowQuirkyColors = inQuirksMode()
3854             && (currentShorthand == CSSPropertyInvalid || currentShorthand == CSSPropertyBorderColor);
3855         return consumeColor(m_range, m_context.mode, allowQuirkyColors);
3856     }
3857     case CSSPropertyBorderBottomWidth:
3858     case CSSPropertyBorderLeftWidth:
3859     case CSSPropertyBorderRightWidth:
3860     case CSSPropertyBorderTopWidth: {
3861         bool allowQuirkyLengths = inQuirksMode()
3862             && (currentShorthand == CSSPropertyInvalid || currentShorthand == CSSPropertyBorderWidth);
3863         UnitlessQuirk unitless = allowQuirkyLengths ? UnitlessQuirk::Allow : UnitlessQuirk::Forbid;
3864         return consumeBorderWidth(m_range, m_context.mode, unitless);
3865     }
3866     case CSSPropertyZIndex:
3867         return consumeZIndex(m_range);
3868     case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3
3869     case CSSPropertyBoxShadow:
3870     case CSSPropertyWebkitBoxShadow:
3871     case CSSPropertyWebkitSvgShadow:
3872         return consumeShadow(m_range, m_context.mode, property == CSSPropertyBoxShadow || property == CSSPropertyWebkitBoxShadow);
3873     case CSSPropertyFilter:
3874 #if ENABLE(FILTERS_LEVEL_2)
3875     case CSSPropertyWebkitBackdropFilter:
3876 #endif
3877         return consumeFilter(m_range, m_context);
3878     case CSSPropertyTextDecoration:
3879     case CSSPropertyWebkitTextDecorationsInEffect:
3880     case CSSPropertyWebkitTextDecorationLine:
3881         return consumeTextDecorationLine(m_range);
3882     case CSSPropertyWebkitTextEmphasisStyle:
3883         return consumeTextEmphasisStyle(m_range);
3884     case CSSPropertyOutlineColor:
3885         return consumeOutlineColor(m_range, m_context.mode);
3886     case CSSPropertyOutlineOffset:
3887         return consumeLength(m_range, m_context.mode, ValueRangeAll);
3888     case CSSPropertyOutlineWidth:
3889         return consumeLineWidth(m_range, m_context.mode, UnitlessQuirk::Forbid);
3890     case CSSPropertyTransform:
3891         return consumeTransform(m_range, m_context.mode);
3892     case CSSPropertyTransformOriginX:
3893     case CSSPropertyPerspectiveOriginX:
3894         return consumePositionX(m_range, m_context.mode);
3895     case CSSPropertyTransformOriginY:
3896     case CSSPropertyPerspectiveOriginY:
3897         return consumePositionY(m_range, m_context.mode);
3898     case CSSPropertyTransformOriginZ:
3899         return consumeLength(m_range, m_context.mode, ValueRangeAll);
3900     case CSSPropertyFill:
3901     case CSSPropertyStroke:
3902         return consumePaintStroke(m_range, m_context.mode);
3903     case CSSPropertyGlyphOrientationVertical:
3904     case CSSPropertyGlyphOrientationHorizontal:
3905         return consumeGlyphOrientation(m_range, m_context.mode, property);
3906     case CSSPropertyPaintOrder:
3907         return consumePaintOrder(m_range);
3908     case CSSPropertyMarkerStart:
3909     case CSSPropertyMarkerMid:
3910     case CSSPropertyMarkerEnd:
3911     case CSSPropertyClipPath:
3912     case CSSPropertyMask:
3913         return consumeNoneOrURI(m_range);
3914     case CSSPropertyFlexBasis:
3915         return consumeFlexBasis(m_range, m_context.mode);
3916     case CSSPropertyFlexGrow:
3917     case CSSPropertyFlexShrink:
3918         return consumeNumber(m_range, ValueRangeNonNegative);
3919     case CSSPropertyStrokeDasharray:
3920         return consumeStrokeDasharray(m_range);
3921     case CSSPropertyColumnRuleWidth:
3922         return consumeColumnRuleWidth(m_range, m_context.mode);
3923     case CSSPropertyStrokeOpacity:
3924     case CSSPropertyFillOpacity:
3925     case CSSPropertyStopOpacity:
3926     case CSSPropertyFloodOpacity:
3927     case CSSPropertyOpacity:
3928     case CSSPropertyWebkitBoxFlex:
3929         return consumeNumber(m_range, ValueRangeAll);
3930     case CSSPropertyBaselineShift:
3931         return consumeBaselineShift(m_range);
3932     case CSSPropertyKerning:
3933         return consumeKerning(m_range, m_context.mode);
3934     case CSSPropertyStrokeMiterlimit:
3935         return consumeNumber(m_range, ValueRangeNonNegative);
3936     case CSSPropertyStrokeWidth:
3937     case CSSPropertyStrokeDashoffset:
3938     case CSSPropertyCx:
3939     case CSSPropertyCy:
3940     case CSSPropertyX:
3941     case CSSPropertyY:
3942     case CSSPropertyR:
3943         return consumeLengthOrPercent(m_range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
3944     case CSSPropertyRx:
3945     case CSSPropertyRy:
3946         return consumeRxOrRy(m_range);
3947     case CSSPropertyCursor:
3948         return consumeCursor(m_range, m_context, inQuirksMode());
3949     case CSSPropertyContent:
3950         return consumeContent(m_range, m_context);
3951     case CSSPropertyListStyleImage:
3952     case CSSPropertyBorderImageSource:
3953     case CSSPropertyWebkitMaskBoxImageSource:
3954         return consumeImageOrNone(m_range, m_context);
3955     case CSSPropertyPerspective:
3956         return consumePerspective(m_range, m_context.mode);
3957     case CSSPropertyBorderTopRightRadius:
3958     case CSSPropertyBorderTopLeftRadius:
3959     case CSSPropertyBorderBottomLeftRadius:
3960     case CSSPropertyBorderBottomRightRadius:
3961         return consumeBorderRadiusCorner(m_range, m_context.mode);
3962     case CSSPropertyWebkitBoxFlexGroup:
3963         return consumeInteger(m_range, 0);
3964     case CSSPropertyOrder:
3965         return consumeInteger(m_range);
3966     case CSSPropertyWebkitTextUnderlinePosition: