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