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