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