LayoutTests/imported/w3c:
[WebKit-https.git] / Source / WebCore / css / parser / CSSPropertyParserHelpers.cpp
1 // Copyright 2016 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 "CSSPropertyParserHelpers.h"
32
33 #include "CSSCalculationValue.h"
34 #include "CSSCanvasValue.h"
35 #include "CSSCrossfadeValue.h"
36 #include "CSSFilterImageValue.h"
37 #include "CSSGradientValue.h"
38 #include "CSSImageSetValue.h"
39 #include "CSSImageValue.h"
40 #include "CSSNamedImageValue.h"
41 #include "CSSPaintImageValue.h"
42 #include "CSSParserIdioms.h"
43 #include "CSSValuePool.h"
44 #include "Pair.h"
45 #include "RuntimeEnabledFeatures.h"
46 #include "StyleColor.h"
47 #include <wtf/text/StringConcatenateNumbers.h>
48
49 namespace WebCore {
50
51 namespace CSSPropertyParserHelpers {
52
53 bool consumeCommaIncludingWhitespace(CSSParserTokenRange& range)
54 {
55     CSSParserToken value = range.peek();
56     if (value.type() != CommaToken)
57         return false;
58     range.consumeIncludingWhitespace();
59     return true;
60 }
61
62 bool consumeSlashIncludingWhitespace(CSSParserTokenRange& range)
63 {
64     CSSParserToken value = range.peek();
65     if (value.type() != DelimiterToken || value.delimiter() != '/')
66         return false;
67     range.consumeIncludingWhitespace();
68     return true;
69 }
70
71 CSSParserTokenRange consumeFunction(CSSParserTokenRange& range)
72 {
73     ASSERT(range.peek().type() == FunctionToken);
74     CSSParserTokenRange contents = range.consumeBlock();
75     range.consumeWhitespace();
76     contents.consumeWhitespace();
77     return contents;
78 }
79
80 // FIXME: consider pulling in the parsing logic from CSSCalculationValue.cpp.
81 class CalcParser {
82 public:
83     explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRangeAll)
84         : m_sourceRange(range)
85         , m_range(range)
86     {
87         const CSSParserToken& token = range.peek();
88         auto functionId = token.functionId();
89         if (functionId == CSSValueCalc || functionId == CSSValueWebkitCalc || functionId == CSSValueMin || functionId == CSSValueMax)
90             m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange);
91     }
92
93     const CSSCalcValue* value() const { return m_calcValue.get(); }
94
95     RefPtr<CSSPrimitiveValue> consumeValue()
96     {
97         if (!m_calcValue)
98             return nullptr;
99         m_sourceRange = m_range;
100         return CSSValuePool::singleton().createValue(WTFMove(m_calcValue));
101     }
102
103     RefPtr<CSSPrimitiveValue> consumeInteger(double minimumValue)
104     {
105         if (!m_calcValue)
106             return nullptr;
107         m_sourceRange = m_range;
108
109         double value = std::max(m_calcValue->doubleValue(), minimumValue);
110         value = std::round(value);
111         return CSSValuePool::singleton().createValue(value, CSSUnitType::CSS_NUMBER);
112     }
113
114     RefPtr<CSSPrimitiveValue> consumeNumber()
115     {
116         if (!m_calcValue)
117             return nullptr;
118         m_sourceRange = m_range;
119         return CSSValuePool::singleton().createValue(m_calcValue->doubleValue(), CSSUnitType::CSS_NUMBER);
120     }
121
122     bool consumeNumberRaw(double& result)
123     {
124         if (!m_calcValue || m_calcValue->category() != CalculationCategory::Number)
125             return false;
126         m_sourceRange = m_range;
127         result = m_calcValue->doubleValue();
128         return true;
129     }
130     
131     bool consumePositiveIntegerRaw(int& result)
132     {
133         if (!m_calcValue || m_calcValue->category() != CalculationCategory::Number || !m_calcValue->isInt())
134             return false;
135         result = static_cast<int>(m_calcValue->doubleValue());
136         if (result < 1)
137             return false;
138         m_sourceRange = m_range;
139         return true;
140     }
141
142 private:
143     CSSParserTokenRange& m_sourceRange;
144     CSSParserTokenRange m_range;
145     RefPtr<CSSCalcValue> m_calcValue;
146 };
147
148 RefPtr<CSSPrimitiveValue> consumeInteger(CSSParserTokenRange& range, double minimumValue)
149 {
150     const CSSParserToken& token = range.peek();
151     if (token.type() == NumberToken) {
152         if (token.numericValueType() == NumberValueType || token.numericValue() < minimumValue)
153             return nullptr;
154         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_NUMBER);
155     }
156
157     if (token.type() != FunctionToken)
158         return nullptr;
159
160     CalcParser calcParser(range, CalculationCategory::Number);
161     if (const CSSCalcValue* calculation = calcParser.value()) {
162         if (calculation->category() != CalculationCategory::Number)
163             return nullptr;
164         return calcParser.consumeInteger(minimumValue);
165     }
166
167     return nullptr;
168 }
169
170 RefPtr<CSSPrimitiveValue> consumePositiveInteger(CSSParserTokenRange& range)
171 {
172     return consumeInteger(range, 1);
173 }
174
175 bool consumePositiveIntegerRaw(CSSParserTokenRange& range, int& result)
176 {
177     const CSSParserToken& token = range.peek();
178     if (token.type() == NumberToken) {
179         if (token.numericValueType() == NumberValueType || token.numericValue() < 1)
180             return false;
181         result = range.consumeIncludingWhitespace().numericValue();
182         return true;
183     }
184
185     if (token.type() != FunctionToken)
186         return false;
187
188     CalcParser calcParser(range, CalculationCategory::Number);
189     return calcParser.consumePositiveIntegerRaw(result);
190 }
191     
192 bool consumeNumberRaw(CSSParserTokenRange& range, double& result)
193 {
194     const CSSParserToken& token = range.peek();
195     if (token.type() == NumberToken) {
196         result = range.consumeIncludingWhitespace().numericValue();
197         return true;
198     }
199
200     if (token.type() != FunctionToken)
201         return false;
202
203     CalcParser calcParser(range, CalculationCategory::Number, ValueRangeAll);
204     return calcParser.consumeNumberRaw(result);
205 }
206
207 // FIXME: Work out if this can just call consumeNumberRaw
208 RefPtr<CSSPrimitiveValue> consumeNumber(CSSParserTokenRange& range, ValueRange valueRange)
209 {
210     const CSSParserToken& token = range.peek();
211     if (token.type() == NumberToken) {
212         if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
213             return nullptr;
214         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
215     }
216
217     if (token.type() != FunctionToken)
218         return nullptr;
219
220     CalcParser calcParser(range, CalculationCategory::Number, ValueRangeAll);
221     if (const CSSCalcValue* calculation = calcParser.value()) {
222         // FIXME: Calcs should not be subject to parse time range checks.
223         // spec: https://drafts.csswg.org/css-values-3/#calc-range
224         if (calculation->category() != CalculationCategory::Number || (valueRange == ValueRangeNonNegative && calculation->isNegative()))
225             return nullptr;
226         return calcParser.consumeNumber();
227     }
228
229     return nullptr;
230 }
231
232 #if !ENABLE(VARIATION_FONTS)
233 static inline bool divisibleBy100(double value)
234 {
235     return static_cast<int>(value / 100) * 100 == value;
236 }
237 #endif
238
239 RefPtr<CSSPrimitiveValue> consumeFontWeightNumber(CSSParserTokenRange& range)
240 {
241     // Values less than or equal to 0 or greater than or equal to 1000 are parse errors.
242     auto& token = range.peek();
243     if (token.type() == NumberToken && token.numericValue() >= 1 && token.numericValue() <= 1000
244 #if !ENABLE(VARIATION_FONTS)
245         && token.numericValueType() == IntegerValueType && divisibleBy100(token.numericValue())
246 #endif
247     )
248         return consumeNumber(range, ValueRangeAll);
249
250     if (token.type() != FunctionToken)
251         return nullptr;
252
253     // "[For calc()], the used value resulting from an expression must be clamped to the range allowed in the target context."
254     CalcParser calcParser(range, CalculationCategory::Number, ValueRangeAll);
255     double result;
256     if (calcParser.consumeNumberRaw(result)
257 #if !ENABLE(VARIATION_FONTS)
258         && result > 0 && result < 1000 && divisibleBy100(result)
259 #endif
260     ) {
261         result = std::min(std::max(result, std::nextafter(0., 1.)), std::nextafter(1000., 0.));
262         return CSSValuePool::singleton().createValue(result, CSSUnitType::CSS_NUMBER);
263     }
264
265     return nullptr;
266 }
267
268 inline bool shouldAcceptUnitlessValue(double value, CSSParserMode cssParserMode, UnitlessQuirk unitless)
269 {
270     // FIXME: Presentational HTML attributes shouldn't use the CSS parser for lengths
271     return value == 0
272         || isUnitLessValueParsingEnabledForMode(cssParserMode)
273         || (cssParserMode == HTMLQuirksMode && unitless == UnitlessQuirk::Allow);
274 }
275
276 RefPtr<CSSPrimitiveValue> consumeLength(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
277 {
278     const CSSParserToken& token = range.peek();
279     if (token.type() == DimensionToken) {
280         switch (token.unitType()) {
281         case CSSUnitType::CSS_QUIRKY_EMS:
282             if (cssParserMode != UASheetMode)
283                 return nullptr;
284             FALLTHROUGH;
285         case CSSUnitType::CSS_EMS:
286         case CSSUnitType::CSS_REMS:
287         case CSSUnitType::CSS_CHS:
288         case CSSUnitType::CSS_EXS:
289         case CSSUnitType::CSS_PX:
290         case CSSUnitType::CSS_CM:
291         case CSSUnitType::CSS_MM:
292         case CSSUnitType::CSS_IN:
293         case CSSUnitType::CSS_PT:
294         case CSSUnitType::CSS_PC:
295         case CSSUnitType::CSS_VW:
296         case CSSUnitType::CSS_VH:
297         case CSSUnitType::CSS_VMIN:
298         case CSSUnitType::CSS_VMAX:
299         case CSSUnitType::CSS_Q:
300             break;
301         default:
302             return nullptr;
303         }
304         if ((valueRange == ValueRangeNonNegative && token.numericValue() < 0) || std::isinf(token.numericValue()))
305             return nullptr;
306         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
307     }
308     if (token.type() == NumberToken) {
309         if (!shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless)
310             || (valueRange == ValueRangeNonNegative && token.numericValue() < 0))
311             return nullptr;
312         if (std::isinf(token.numericValue()))
313             return nullptr;
314         CSSUnitType unitType = CSSUnitType::CSS_PX;
315         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unitType);
316     }
317
318     if (token.type() != FunctionToken)
319         return nullptr;
320
321     CalcParser calcParser(range, CalculationCategory::Length, valueRange);
322     if (calcParser.value() && calcParser.value()->category() == CalculationCategory::Length)
323         return calcParser.consumeValue();
324
325     return nullptr;
326 }
327
328 RefPtr<CSSPrimitiveValue> consumePercent(CSSParserTokenRange& range, ValueRange valueRange)
329 {
330     const CSSParserToken& token = range.peek();
331     if (token.type() == PercentageToken) {
332         if ((valueRange == ValueRangeNonNegative && token.numericValue() < 0) || std::isinf(token.numericValue()))
333             return nullptr;
334         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_PERCENTAGE);
335     }
336
337     if (token.type() != FunctionToken)
338         return nullptr;
339
340     CalcParser calcParser(range, CalculationCategory::Percent, valueRange);
341     if (const CSSCalcValue* calculation = calcParser.value()) {
342         if (calculation->category() == CalculationCategory::Percent)
343             return calcParser.consumeValue();
344     }
345     return nullptr;
346 }
347
348 static bool canConsumeCalcValue(CalculationCategory category, CSSParserMode cssParserMode)
349 {
350     if (category == CalculationCategory::Length || category == CalculationCategory::Percent || category == CalculationCategory::PercentLength)
351         return true;
352
353     if (cssParserMode != SVGAttributeMode)
354         return false;
355
356     if (category == CalculationCategory::Number || category == CalculationCategory::PercentNumber)
357         return true;
358
359     return false;
360 }
361
362 RefPtr<CSSPrimitiveValue> consumeLengthOrPercent(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
363 {
364     const CSSParserToken& token = range.peek();
365     if (token.type() == DimensionToken || token.type() == NumberToken)
366         return consumeLength(range, cssParserMode, valueRange, unitless);
367     if (token.type() == PercentageToken)
368         return consumePercent(range, valueRange);
369
370     if (token.type() != FunctionToken)
371         return nullptr;
372
373     CalcParser calcParser(range, CalculationCategory::Length, valueRange);
374     if (const CSSCalcValue* calculation = calcParser.value()) {
375         if (canConsumeCalcValue(calculation->category(), cssParserMode))
376             return calcParser.consumeValue();
377     }
378     return nullptr;
379 }
380
381 RefPtr<CSSPrimitiveValue> consumeAngle(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
382 {
383     const CSSParserToken& token = range.peek();
384     if (token.type() == DimensionToken) {
385         switch (token.unitType()) {
386         case CSSUnitType::CSS_DEG:
387         case CSSUnitType::CSS_RAD:
388         case CSSUnitType::CSS_GRAD:
389         case CSSUnitType::CSS_TURN:
390             return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
391         default:
392             return nullptr;
393         }
394     }
395
396     if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless))
397         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
398
399     if (token.type() != FunctionToken)
400         return nullptr;
401
402     CalcParser calcParser(range, CalculationCategory::Angle, ValueRangeAll);
403     if (const CSSCalcValue* calculation = calcParser.value()) {
404         if (calculation->category() == CalculationCategory::Angle)
405             return calcParser.consumeValue();
406     }
407     return nullptr;
408 }
409
410 static RefPtr<CSSPrimitiveValue> consumeAngleOrPercent(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
411 {
412     const CSSParserToken& token = range.peek();
413     if (token.type() == DimensionToken) {
414         switch (token.unitType()) {
415         case CSSUnitType::CSS_DEG:
416         case CSSUnitType::CSS_RAD:
417         case CSSUnitType::CSS_GRAD:
418         case CSSUnitType::CSS_TURN:
419             return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
420         default:
421             return nullptr;
422         }
423     }
424     if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless))
425         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
426
427     if (token.type() == PercentageToken)
428         return consumePercent(range, valueRange);
429
430      if (token.type() != FunctionToken)
431          return nullptr;
432
433     CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
434     if (const CSSCalcValue* calculation = angleCalcParser.value()) {
435         if (calculation->category() == CalculationCategory::Angle)
436             return angleCalcParser.consumeValue();
437     }
438
439     CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
440     if (const CSSCalcValue* calculation = percentCalcParser.value()) {
441         if (calculation->category() == CalculationCategory::Percent)
442             return percentCalcParser.consumeValue();
443     }
444     return nullptr;
445 }
446
447 RefPtr<CSSPrimitiveValue> consumeTime(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
448 {
449     const CSSParserToken& token = range.peek();
450     CSSUnitType unit = token.unitType();
451     bool acceptUnitless = token.type() == NumberToken && unitless == UnitlessQuirk::Allow && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless);
452     if (acceptUnitless)
453         unit = CSSUnitType::CSS_MS;
454     if (token.type() == DimensionToken || acceptUnitless) {
455         if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
456             return nullptr;
457         if (unit == CSSUnitType::CSS_MS || unit == CSSUnitType::CSS_S)
458             return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unit);
459         return nullptr;
460     }
461
462     if (token.type() != FunctionToken)
463         return nullptr;
464
465     CalcParser calcParser(range, CalculationCategory::Time, valueRange);
466     if (const CSSCalcValue* calculation = calcParser.value()) {
467         if (calculation->category() == CalculationCategory::Time)
468             return calcParser.consumeValue();
469     }
470     return nullptr;
471 }
472
473 RefPtr<CSSPrimitiveValue> consumeResolution(CSSParserTokenRange& range)
474 {
475     const CSSParserToken& token = range.peek();
476     // Unlike the other types, calc() does not work with <resolution>.
477     if (token.type() != DimensionToken)
478         return nullptr;
479     CSSUnitType unit = token.unitType();
480     if (unit == CSSUnitType::CSS_DPPX || unit == CSSUnitType::CSS_DPI || unit == CSSUnitType::CSS_DPCM)
481         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unit);
482     return nullptr;
483 }
484
485 RefPtr<CSSPrimitiveValue> consumeIdent(CSSParserTokenRange& range)
486 {
487     if (range.peek().type() != IdentToken)
488         return nullptr;
489     return CSSValuePool::singleton().createIdentifierValue(range.consumeIncludingWhitespace().id());
490 }
491
492 RefPtr<CSSPrimitiveValue> consumeIdentRange(CSSParserTokenRange& range, CSSValueID lower, CSSValueID upper)
493 {
494     if (range.peek().id() < lower || range.peek().id() > upper)
495         return nullptr;
496     return consumeIdent(range);
497 }
498
499 // FIXME-NEWPARSER: Eventually we'd like this to use CSSCustomIdentValue, but we need
500 // to do other plumbing work first (like changing Pair to CSSValuePair and make it not
501 // use only primitive values).
502 RefPtr<CSSPrimitiveValue> consumeCustomIdent(CSSParserTokenRange& range)
503 {
504     if (range.peek().type() != IdentToken || isCSSWideKeyword(range.peek().id()))
505         return nullptr;
506     return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().value().toString(), CSSUnitType::CSS_STRING);
507 }
508
509 RefPtr<CSSPrimitiveValue> consumeString(CSSParserTokenRange& range)
510 {
511     if (range.peek().type() != StringToken)
512         return nullptr;
513     return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().value().toString(), CSSUnitType::CSS_STRING);
514 }
515
516 StringView consumeUrlAsStringView(CSSParserTokenRange& range)
517 {
518     const CSSParserToken& token = range.peek();
519     if (token.type() == UrlToken) {
520         range.consumeIncludingWhitespace();
521         return token.value();
522     }
523     if (token.functionId() == CSSValueUrl) {
524         CSSParserTokenRange urlRange = range;
525         CSSParserTokenRange urlArgs = urlRange.consumeBlock();
526         const CSSParserToken& next = urlArgs.consumeIncludingWhitespace();
527         if (next.type() == BadStringToken || !urlArgs.atEnd())
528             return StringView();
529         ASSERT(next.type() == StringToken);
530         range = urlRange;
531         range.consumeWhitespace();
532         return next.value();
533     }
534
535     return StringView();
536 }
537
538 RefPtr<CSSPrimitiveValue> consumeUrl(CSSParserTokenRange& range)
539 {
540     StringView url = consumeUrlAsStringView(range);
541     if (url.isNull())
542         return nullptr;
543     return CSSValuePool::singleton().createValue(url.toString(), CSSUnitType::CSS_URI);
544 }
545
546 static int clampRGBComponent(const CSSPrimitiveValue& value)
547 {
548     double result = value.doubleValue();
549     if (value.isPercentage())
550         result = result / 100.0 * 255.0;
551
552     return clampTo<int>(round(result), 0, 255);
553 }
554
555 static Color parseRGBParameters(CSSParserTokenRange& range)
556 {
557     ASSERT(range.peek().functionId() == CSSValueRgb || range.peek().functionId() == CSSValueRgba);
558     Color result;
559     CSSParserTokenRange args = consumeFunction(range);
560     RefPtr<CSSPrimitiveValue> colorParameter = consumeNumber(args, ValueRangeAll);
561     if (!colorParameter)
562         colorParameter = consumePercent(args, ValueRangeAll);
563     if (!colorParameter)
564         return Color();
565
566     const bool isPercent = colorParameter->isPercentage();
567
568     enum class ColorSyntax {
569         Commas,
570         WhitespaceSlash,
571     };
572
573     ColorSyntax syntax = ColorSyntax::Commas;
574     auto consumeSeparator = [&] {
575         if (syntax == ColorSyntax::Commas)
576             return consumeCommaIncludingWhitespace(args);
577         
578         return true;
579     };
580
581     int colorArray[3];
582     colorArray[0] = clampRGBComponent(*colorParameter);
583     for (int i = 1; i < 3; i++) {
584         if (i == 1)
585             syntax = consumeCommaIncludingWhitespace(args) ? ColorSyntax::Commas : ColorSyntax::WhitespaceSlash;
586         else if (!consumeSeparator())
587             return Color();
588
589         colorParameter = isPercent ? consumePercent(args, ValueRangeAll) : consumeNumber(args, ValueRangeAll);
590         if (!colorParameter)
591             return Color();
592         colorArray[i] = clampRGBComponent(*colorParameter);
593     }
594
595     // Historically, alpha was only parsed for rgba(), but css-color-4 specifies that rgba() is a simple alias for rgb().
596     auto consumeAlphaSeparator = [&] {
597         if (syntax == ColorSyntax::Commas)
598             return consumeCommaIncludingWhitespace(args);
599         
600         return consumeSlashIncludingWhitespace(args);
601     };
602
603     int alphaComponent = 255;
604     if (consumeAlphaSeparator()) {
605         double alpha;
606         if (!consumeNumberRaw(args, alpha)) {
607             auto alphaPercent = consumePercent(args, ValueRangeAll);
608             if (!alphaPercent)
609                 return Color();
610             alpha = alphaPercent->doubleValue() / 100.0;
611         }
612
613         // W3 standard stipulates a 2.55 alpha value multiplication factor.
614         alphaComponent = static_cast<int>(lroundf(clampTo<double>(alpha, 0.0, 1.0) * 255.0f));
615     };
616
617     result = Color(makeRGBA(colorArray[0], colorArray[1], colorArray[2], alphaComponent));
618
619     if (!args.atEnd())
620         return Color();
621
622     return result;
623 }
624
625 static Color parseHSLParameters(CSSParserTokenRange& range, CSSParserMode cssParserMode)
626 {
627     ASSERT(range.peek().functionId() == CSSValueHsl || range.peek().functionId() == CSSValueHsla);
628     CSSParserTokenRange args = consumeFunction(range);
629     auto hslValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
630     double angleInDegrees;
631     if (!hslValue) {
632         hslValue = consumeNumber(args, ValueRangeAll);
633         if (!hslValue)
634             return Color();
635         angleInDegrees = hslValue->doubleValue();
636     } else
637         angleInDegrees = hslValue->computeDegrees();
638
639     double colorArray[3];
640     colorArray[0] = fmod(fmod(angleInDegrees, 360.0) + 360.0, 360.0) / 360.0;
641     bool requiresCommas = false;
642     for (int i = 1; i < 3; i++) {
643         if (consumeCommaIncludingWhitespace(args)) {
644             if (i != 1 && !requiresCommas)
645                 return Color();
646             requiresCommas = true;
647         } else if (requiresCommas || args.atEnd() || (&args.peek() - 1)->type() != WhitespaceToken)
648             return Color();
649         hslValue = consumePercent(args, ValueRangeAll);
650         if (!hslValue)
651             return Color();
652         double doubleValue = hslValue->doubleValue();
653         colorArray[i] = clampTo<double>(doubleValue, 0.0, 100.0) / 100.0; // Needs to be value between 0 and 1.0.
654     }
655
656     double alpha = 1.0;
657     bool commaConsumed = consumeCommaIncludingWhitespace(args);
658     bool slashConsumed = consumeSlashIncludingWhitespace(args);
659     if ((commaConsumed && !requiresCommas) || (slashConsumed && requiresCommas))
660         return Color();
661     if (commaConsumed || slashConsumed) {
662         if (!consumeNumberRaw(args, alpha)) {
663             auto alphaPercent = consumePercent(args, ValueRangeAll);
664             if (!alphaPercent)
665                 return Color();
666             alpha = alphaPercent->doubleValue() / 100.0f;
667         }
668         alpha = clampTo<double>(alpha, 0.0, 1.0);
669     }
670
671     if (!args.atEnd())
672         return Color();
673
674     return Color(makeRGBAFromHSLA(static_cast<float>(colorArray[0]), static_cast<float>(colorArray[1]), static_cast<float>(colorArray[2]), static_cast<float>(alpha)));
675 }
676
677 static Color parseColorFunctionParameters(CSSParserTokenRange& range)
678 {
679     ASSERT(range.peek().functionId() == CSSValueColor);
680     CSSParserTokenRange args = consumeFunction(range);
681
682     ColorSpace colorSpace;
683     switch (args.peek().id()) {
684     case CSSValueSRGB:
685         colorSpace = ColorSpaceSRGB;
686         break;
687     case CSSValueDisplayP3:
688         colorSpace = ColorSpaceDisplayP3;
689         break;
690     default:
691         return Color();
692     }
693     consumeIdent(args);
694
695     double colorChannels[4] = { 0, 0, 0, 1 };
696     for (int i = 0; i < 3; ++i) {
697         double value;
698         if (consumeNumberRaw(args, value))
699             colorChannels[i] = std::max(0.0, std::min(1.0, value));
700         else
701             break;
702     }
703
704     if (consumeSlashIncludingWhitespace(args)) {
705         auto alphaParameter = consumePercent(args, ValueRangeAll);
706         if (!alphaParameter)
707             alphaParameter = consumeNumber(args, ValueRangeAll);
708         if (!alphaParameter)
709             return Color();
710
711         colorChannels[3] = std::max(0.0, std::min(1.0, alphaParameter->isPercentage() ? (alphaParameter->doubleValue() / 100) : alphaParameter->doubleValue()));
712     }
713
714     // FIXME: Support the comma-separated list of fallback color values.
715
716     if (!args.atEnd())
717         return Color();
718     
719     return Color(colorChannels[0], colorChannels[1], colorChannels[2], colorChannels[3], colorSpace);
720 }
721
722 static Color parseHexColor(CSSParserTokenRange& range, bool acceptQuirkyColors)
723 {
724     RGBA32 result;
725     const CSSParserToken& token = range.peek();
726     if (token.type() == HashToken) {
727         if (!Color::parseHexColor(token.value(), result))
728             return Color();
729     } else if (acceptQuirkyColors) {
730         String color;
731         if (token.type() == NumberToken || token.type() == DimensionToken) {
732             if (token.numericValueType() != IntegerValueType
733                 || token.numericValue() < 0. || token.numericValue() >= 1000000.)
734                 return Color();
735             if (token.type() == NumberToken) // e.g. 112233
736                 color = String::number(static_cast<int>(token.numericValue()));
737             else // e.g. 0001FF
738                 color = makeString(static_cast<int>(token.numericValue()), token.value().toString());
739             while (color.length() < 6)
740                 color = "0" + color;
741         } else if (token.type() == IdentToken) { // e.g. FF0000
742             color = token.value().toString();
743         }
744         unsigned length = color.length();
745         if (length != 3 && length != 6)
746             return Color();
747         if (!Color::parseHexColor(color, result))
748             return Color();
749     } else {
750         return Color();
751     }
752     range.consumeIncludingWhitespace();
753     return Color(result);
754 }
755
756 static Color parseColorFunction(CSSParserTokenRange& range, CSSParserMode cssParserMode)
757 {
758     CSSParserTokenRange colorRange = range;
759     CSSValueID functionId = range.peek().functionId();
760     Color color;
761     switch (functionId) {
762     case CSSValueRgb:
763     case CSSValueRgba:
764         color = parseRGBParameters(colorRange);
765         break;
766     case CSSValueHsl:
767     case CSSValueHsla:
768         color = parseHSLParameters(colorRange, cssParserMode);
769         break;
770     case CSSValueColor:
771         color = parseColorFunctionParameters(colorRange);
772         break;
773     default:
774         return Color();
775     }
776     if (color.isValid())
777         range = colorRange;
778     return color;
779 }
780
781 RefPtr<CSSPrimitiveValue> consumeColor(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool acceptQuirkyColors)
782 {
783     CSSValueID id = range.peek().id();
784     if (StyleColor::isColorKeyword(id)) {
785         if (!isValueAllowedInMode(id, cssParserMode))
786             return nullptr;
787         return consumeIdent(range);
788     }
789     Color color = parseHexColor(range, acceptQuirkyColors);
790     if (!color.isValid())
791         color = parseColorFunction(range, cssParserMode);
792     if (!color.isValid())
793         return nullptr;
794     return CSSValuePool::singleton().createValue(color);
795 }
796
797 static RefPtr<CSSPrimitiveValue> consumePositionComponent(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
798 {
799     if (range.peek().type() == IdentToken)
800         return consumeIdent<CSSValueLeft, CSSValueTop, CSSValueBottom, CSSValueRight, CSSValueCenter>(range);
801     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, unitless);
802 }
803
804 static bool isHorizontalPositionKeywordOnly(const CSSPrimitiveValue& value)
805 {
806     return value.isValueID() && (value.valueID() == CSSValueLeft || value.valueID() == CSSValueRight);
807 }
808
809 static bool isVerticalPositionKeywordOnly(const CSSPrimitiveValue& value)
810 {
811     return value.isValueID() && (value.valueID() == CSSValueTop || value.valueID() == CSSValueBottom);
812 }
813
814 static void positionFromOneValue(CSSPrimitiveValue& value, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
815 {
816     bool valueAppliesToYAxisOnly = isVerticalPositionKeywordOnly(value);
817     resultX = &value;
818     resultY = CSSPrimitiveValue::createIdentifier(CSSValueCenter);
819     if (valueAppliesToYAxisOnly)
820         std::swap(resultX, resultY);
821 }
822
823 static bool positionFromTwoValues(CSSPrimitiveValue& value1, CSSPrimitiveValue& value2,
824     RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
825 {
826     bool mustOrderAsXY = isHorizontalPositionKeywordOnly(value1) || isVerticalPositionKeywordOnly(value2)
827         || !value1.isValueID() || !value2.isValueID();
828     bool mustOrderAsYX = isVerticalPositionKeywordOnly(value1) || isHorizontalPositionKeywordOnly(value2);
829     if (mustOrderAsXY && mustOrderAsYX)
830         return false;
831     resultX = &value1;
832     resultY = &value2;
833     if (mustOrderAsYX)
834         std::swap(resultX, resultY);
835     return true;
836 }
837
838 namespace CSSPropertyParserHelpersInternal {
839 template<typename... Args>
840 static Ref<CSSPrimitiveValue> createPrimitiveValuePair(Args&&... args)
841 {
842     return CSSValuePool::singleton().createValue(Pair::create(std::forward<Args>(args)...));
843 }
844 }
845
846 // https://drafts.csswg.org/css-backgrounds-3/#propdef-background-position
847 // background-position has special parsing rules, allowing a 3-value syntax:
848 // <bg-position> =  [ left | center | right | top | bottom | <length-percentage> ]
849 // |
850 //   [ left | center | right | <length-percentage> ]
851 //   [ top | center | bottom | <length-percentage> ]
852 // |
853 //   [ center | [ left | right ] <length-percentage>? ] &&
854 //   [ center | [ top | bottom ] <length-percentage>? ]
855 //
856 static bool backgroundPositionFromThreeValues(const std::array<CSSPrimitiveValue*, 5>& values, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
857 {
858     CSSPrimitiveValue* center = nullptr;
859     for (int i = 0; values[i]; i++) {
860         CSSPrimitiveValue* currentValue = values[i];
861         if (!currentValue->isValueID())
862             return false;
863
864         CSSValueID id = currentValue->valueID();
865         if (id == CSSValueCenter) {
866             if (center)
867                 return false;
868             center = currentValue;
869             continue;
870         }
871
872         RefPtr<CSSPrimitiveValue> result;
873         if (values[i + 1] && !values[i + 1]->isValueID())
874             result = CSSPropertyParserHelpersInternal::createPrimitiveValuePair(currentValue, values[++i]);
875         else
876             result = currentValue;
877
878         if (id == CSSValueLeft || id == CSSValueRight) {
879             if (resultX)
880                 return false;
881             resultX = result;
882         } else {
883             ASSERT(id == CSSValueTop || id == CSSValueBottom);
884             if (resultY)
885                 return false;
886             resultY = result;
887         }
888     }
889
890     if (center) {
891         ASSERT(resultX || resultY);
892         if (resultX && resultY)
893             return false;
894         if (!resultX)
895             resultX = center;
896         else
897             resultY = center;
898     }
899
900     ASSERT(resultX && resultY);
901     return true;
902 }
903
904 // https://drafts.csswg.org/css-values-4/#typedef-position
905 // <position> = [
906 //   [ left | center | right ] || [ top | center | bottom ]
907 // |
908 //   [ left | center | right | <length-percentage> ]
909 //   [ top | center | bottom | <length-percentage> ]?
910 // |
911 //   [ [ left | right ] <length-percentage> ] &&
912 //   [ [ top | bottom ] <length-percentage> ]
913 //
914 static bool positionFromFourValues(const std::array<CSSPrimitiveValue*, 5>& values, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
915 {
916     for (int i = 0; values[i]; i++) {
917         CSSPrimitiveValue* currentValue = values[i];
918         if (!currentValue->isValueID())
919             return false;
920
921         CSSValueID id = currentValue->valueID();
922         if (id == CSSValueCenter)
923             return false;
924
925         RefPtr<CSSPrimitiveValue> result;
926         if (values[i + 1] && !values[i + 1]->isValueID())
927             result = CSSPropertyParserHelpersInternal::createPrimitiveValuePair(currentValue, values[++i]);
928         else
929             result = currentValue;
930
931         if (id == CSSValueLeft || id == CSSValueRight) {
932             if (resultX)
933                 return false;
934             resultX = result;
935         } else {
936             ASSERT(id == CSSValueTop || id == CSSValueBottom);
937             if (resultY)
938                 return false;
939             resultY = result;
940         }
941     }
942
943     ASSERT(resultX && resultY);
944     return true;
945 }
946
947 // FIXME: This may consume from the range upon failure. The background
948 // shorthand works around it, but we should just fix it here.
949 bool consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless, PositionSyntax positionSyntax, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
950 {
951     RefPtr<CSSPrimitiveValue> value1 = consumePositionComponent(range, cssParserMode, unitless);
952     if (!value1)
953         return false;
954
955     RefPtr<CSSPrimitiveValue> value2 = consumePositionComponent(range, cssParserMode, unitless);
956     if (!value2) {
957         positionFromOneValue(*value1, resultX, resultY);
958         return true;
959     }
960
961     RefPtr<CSSPrimitiveValue> value3 = consumePositionComponent(range, cssParserMode, unitless);
962     if (!value3)
963         return positionFromTwoValues(*value1, *value2, resultX, resultY);
964
965     RefPtr<CSSPrimitiveValue> value4 = consumePositionComponent(range, cssParserMode, unitless);
966     
967     std::array<CSSPrimitiveValue*, 5> values;
968     values[0] = value1.get();
969     values[1] = value2.get();
970     values[2] = value3.get();
971     values[3] = value4.get();
972     values[4] = nullptr;
973     
974     if (value4)
975         return positionFromFourValues(values, resultX, resultY);
976     
977     if (positionSyntax != PositionSyntax::BackgroundPosition)
978         return false;
979     
980     return backgroundPositionFromThreeValues(values, resultX, resultY);
981 }
982
983 RefPtr<CSSPrimitiveValue> consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless, PositionSyntax positionSyntax)
984 {
985     RefPtr<CSSPrimitiveValue> resultX;
986     RefPtr<CSSPrimitiveValue> resultY;
987     if (consumePosition(range, cssParserMode, unitless, positionSyntax, resultX, resultY))
988         return CSSPropertyParserHelpersInternal::createPrimitiveValuePair(resultX.releaseNonNull(), resultY.releaseNonNull());
989     return nullptr;
990 }
991
992 bool consumeOneOrTwoValuedPosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
993 {
994     RefPtr<CSSPrimitiveValue> value1 = consumePositionComponent(range, cssParserMode, unitless);
995     if (!value1)
996         return false;
997     RefPtr<CSSPrimitiveValue> value2 = consumePositionComponent(range, cssParserMode, unitless);
998     if (!value2) {
999         positionFromOneValue(*value1, resultX, resultY);
1000         return true;
1001     }
1002     return positionFromTwoValues(*value1, *value2, resultX, resultY);
1003 }
1004
1005 // This should go away once we drop support for -webkit-gradient
1006 static RefPtr<CSSPrimitiveValue> consumeDeprecatedGradientPoint(CSSParserTokenRange& args, bool horizontal)
1007 {
1008     if (args.peek().type() == IdentToken) {
1009         if ((horizontal && consumeIdent<CSSValueLeft>(args)) || (!horizontal && consumeIdent<CSSValueTop>(args)))
1010             return CSSValuePool::singleton().createValue(0., CSSUnitType::CSS_PERCENTAGE);
1011         if ((horizontal && consumeIdent<CSSValueRight>(args)) || (!horizontal && consumeIdent<CSSValueBottom>(args)))
1012             return CSSValuePool::singleton().createValue(100., CSSUnitType::CSS_PERCENTAGE);
1013         if (consumeIdent<CSSValueCenter>(args))
1014             return CSSValuePool::singleton().createValue(50., CSSUnitType::CSS_PERCENTAGE);
1015         return nullptr;
1016     }
1017     RefPtr<CSSPrimitiveValue> result = consumePercent(args, ValueRangeAll);
1018     if (!result)
1019         result = consumeNumber(args, ValueRangeAll);
1020     return result;
1021 }
1022
1023 // Used to parse colors for -webkit-gradient(...).
1024 static RefPtr<CSSPrimitiveValue> consumeDeprecatedGradientStopColor(CSSParserTokenRange& args, CSSParserMode cssParserMode)
1025 {
1026     if (args.peek().id() == CSSValueCurrentcolor)
1027         return nullptr;
1028     return consumeColor(args, cssParserMode);
1029 }
1030
1031 static bool consumeDeprecatedGradientColorStop(CSSParserTokenRange& range, CSSGradientColorStop& stop, CSSParserMode cssParserMode)
1032 {
1033     CSSValueID id = range.peek().functionId();
1034     if (id != CSSValueFrom && id != CSSValueTo && id != CSSValueColorStop)
1035         return false;
1036
1037     CSSParserTokenRange args = consumeFunction(range);
1038     double position;
1039     if (id == CSSValueFrom || id == CSSValueTo) {
1040         position = (id == CSSValueFrom) ? 0 : 1;
1041     } else {
1042         ASSERT(id == CSSValueColorStop);
1043         if (auto percentValue = consumePercent(args, ValueRangeAll))
1044             position = percentValue->doubleValue() / 100.0;
1045         else if (!consumeNumberRaw(args, position))
1046             return false;
1047
1048         if (!consumeCommaIncludingWhitespace(args))
1049             return false;
1050     }
1051
1052     stop.m_position = CSSValuePool::singleton().createValue(position, CSSUnitType::CSS_NUMBER);
1053     stop.m_color = consumeDeprecatedGradientStopColor(args, cssParserMode);
1054     return stop.m_color && args.atEnd();
1055 }
1056
1057 static RefPtr<CSSValue> consumeDeprecatedGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode)
1058 {
1059     RefPtr<CSSGradientValue> result;
1060     CSSValueID id = args.consumeIncludingWhitespace().id();
1061     bool isDeprecatedRadialGradient = (id == CSSValueRadial);
1062     if (isDeprecatedRadialGradient)
1063         result = CSSRadialGradientValue::create(NonRepeating, CSSDeprecatedRadialGradient);
1064     else if (id == CSSValueLinear)
1065         result = CSSLinearGradientValue::create(NonRepeating, CSSDeprecatedLinearGradient);
1066     if (!result || !consumeCommaIncludingWhitespace(args))
1067         return nullptr;
1068
1069     auto point = consumeDeprecatedGradientPoint(args, true);
1070     if (!point)
1071         return nullptr;
1072     result->setFirstX(point.copyRef());
1073     point = consumeDeprecatedGradientPoint(args, false);
1074     if (!point)
1075         return nullptr;
1076     result->setFirstY(point.copyRef());
1077
1078     if (!consumeCommaIncludingWhitespace(args))
1079         return nullptr;
1080
1081     // For radial gradients only, we now expect a numeric radius.
1082     if (isDeprecatedRadialGradient) {
1083         auto radius = consumeNumber(args, ValueRangeNonNegative);
1084         if (!radius || !consumeCommaIncludingWhitespace(args))
1085             return nullptr;
1086         downcast<CSSRadialGradientValue>(result.get())->setFirstRadius(radius.copyRef());
1087     }
1088
1089     point = consumeDeprecatedGradientPoint(args, true);
1090     if (!point)
1091         return nullptr;
1092     result->setSecondX(point.copyRef());
1093     point = consumeDeprecatedGradientPoint(args, false);
1094     if (!point)
1095         return nullptr;
1096     result->setSecondY(point.copyRef());
1097
1098     // For radial gradients only, we now expect the second radius.
1099     if (isDeprecatedRadialGradient) {
1100         if (!consumeCommaIncludingWhitespace(args))
1101             return nullptr;
1102         auto radius = consumeNumber(args, ValueRangeNonNegative);
1103         if (!radius)
1104             return nullptr;
1105         downcast<CSSRadialGradientValue>(result.get())->setSecondRadius(radius.copyRef());
1106     }
1107
1108     CSSGradientColorStop stop;
1109     while (consumeCommaIncludingWhitespace(args)) {
1110         if (!consumeDeprecatedGradientColorStop(args, stop, cssParserMode))
1111             return nullptr;
1112         result->addStop(stop);
1113     }
1114
1115     result->doneAddingStops();
1116     return result;
1117 }
1118
1119 static bool consumeGradientColorStops(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSGradientValue& gradient)
1120 {
1121     bool supportsColorHints = gradient.gradientType() == CSSLinearGradient || gradient.gradientType() == CSSRadialGradient || gradient.gradientType() == CSSConicGradient;
1122     
1123     bool isConicGradient = gradient.gradientType() == CSSConicGradient;
1124
1125     // The first color stop cannot be a color hint.
1126     bool previousStopWasColorHint = true;
1127     do {
1128         CSSGradientColorStop stop;
1129         stop.m_color = consumeColor(range, cssParserMode);
1130         // Two hints in a row are not allowed.
1131         if (!stop.m_color && (!supportsColorHints || previousStopWasColorHint))
1132             return false;
1133         
1134         previousStopWasColorHint = !stop.m_color;
1135         
1136         // FIXME-NEWPARSER: This boolean could be removed. Null checking color would be sufficient.
1137         stop.isMidpoint = !stop.m_color;
1138
1139         if (isConicGradient)
1140             stop.m_position = consumeAngleOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid);
1141         else
1142             stop.m_position = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
1143         
1144         if (!stop.m_color && !stop.m_position)
1145             return false;
1146
1147         gradient.addStop(stop);
1148
1149         if (!stop.m_color || !stop.m_position)
1150             continue;
1151
1152         CSSGradientColorStop secondStop;
1153         if (isConicGradient)
1154             secondStop.m_position = consumeAngleOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid);
1155         else
1156             secondStop.m_position = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
1157         
1158         if (secondStop.m_position)
1159             gradient.addStop(secondStop);
1160         
1161     } while (consumeCommaIncludingWhitespace(range));
1162
1163     gradient.doneAddingStops();
1164
1165     // The last color stop cannot be a color hint.
1166     if (previousStopWasColorHint)
1167         return false;
1168
1169     // Must have 2 or more stops to be valid.
1170     return gradient.stopCount() >= 2;
1171 }
1172
1173 static RefPtr<CSSValue> consumeDeprecatedRadialGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating)
1174 {
1175     RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSPrefixedRadialGradient);
1176     RefPtr<CSSPrimitiveValue> centerX;
1177     RefPtr<CSSPrimitiveValue> centerY;
1178     consumeOneOrTwoValuedPosition(args, cssParserMode, UnitlessQuirk::Forbid, centerX, centerY);
1179     if ((centerX || centerY) && !consumeCommaIncludingWhitespace(args))
1180         return nullptr;
1181
1182     result->setFirstX(centerX.copyRef());
1183     result->setFirstY(centerY.copyRef());
1184     result->setSecondX(centerX.copyRef());
1185     result->setSecondY(centerY.copyRef());
1186
1187     auto shape = consumeIdent<CSSValueCircle, CSSValueEllipse>(args);
1188     auto sizeKeyword = consumeIdent<CSSValueClosestSide, CSSValueClosestCorner, CSSValueFarthestSide, CSSValueFarthestCorner, CSSValueContain, CSSValueCover>(args);
1189     if (!shape)
1190         shape = consumeIdent<CSSValueCircle, CSSValueEllipse>(args);
1191     result->setShape(shape.copyRef());
1192     result->setSizingBehavior(sizeKeyword.copyRef());
1193
1194     // Or, two lengths or percentages
1195     if (!shape && !sizeKeyword) {
1196         auto horizontalSize = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
1197         RefPtr<CSSPrimitiveValue> verticalSize;
1198         if (horizontalSize) {
1199             verticalSize = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
1200             if (!verticalSize)
1201                 return nullptr;
1202             consumeCommaIncludingWhitespace(args);
1203             result->setEndHorizontalSize(horizontalSize.copyRef());
1204             result->setEndVerticalSize(verticalSize.copyRef());
1205         }
1206     } else {
1207         consumeCommaIncludingWhitespace(args);
1208     }
1209     if (!consumeGradientColorStops(args, cssParserMode, *result))
1210         return nullptr;
1211
1212     return result;
1213 }
1214
1215 static RefPtr<CSSValue> consumeRadialGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating)
1216 {
1217     RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient);
1218
1219     RefPtr<CSSPrimitiveValue> shape;
1220     RefPtr<CSSPrimitiveValue> sizeKeyword;
1221     RefPtr<CSSPrimitiveValue> horizontalSize;
1222     RefPtr<CSSPrimitiveValue> verticalSize;
1223
1224     // First part of grammar, the size/shape clause:
1225     // [ circle || <length> ] |
1226     // [ ellipse || [ <length> | <percentage> ]{2} ] |
1227     // [ [ circle | ellipse] || <size-keyword> ]
1228     for (int i = 0; i < 3; ++i) {
1229         if (args.peek().type() == IdentToken) {
1230             CSSValueID id = args.peek().id();
1231             if (id == CSSValueCircle || id == CSSValueEllipse) {
1232                 if (shape)
1233                     return nullptr;
1234                 shape = consumeIdent(args);
1235             } else if (id == CSSValueClosestSide || id == CSSValueClosestCorner || id == CSSValueFarthestSide || id == CSSValueFarthestCorner) {
1236                 if (sizeKeyword)
1237                     return nullptr;
1238                 sizeKeyword = consumeIdent(args);
1239             } else {
1240                 break;
1241             }
1242         } else {
1243             auto center = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
1244             if (!center)
1245                 break;
1246             if (horizontalSize)
1247                 return nullptr;
1248             horizontalSize = center;
1249             center = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
1250             if (center) {
1251                 verticalSize = center;
1252                 ++i;
1253             }
1254         }
1255     }
1256
1257     // You can specify size as a keyword or a length/percentage, not both.
1258     if (sizeKeyword && horizontalSize)
1259         return nullptr;
1260     // Circles must have 0 or 1 lengths.
1261     if (shape && shape->valueID() == CSSValueCircle && verticalSize)
1262         return nullptr;
1263     // Ellipses must have 0 or 2 length/percentages.
1264     if (shape && shape->valueID() == CSSValueEllipse && horizontalSize && !verticalSize)
1265         return nullptr;
1266     // If there's only one size, it must be a length.
1267     if (!verticalSize && horizontalSize && horizontalSize->isPercentage())
1268         return nullptr;
1269     if ((horizontalSize && horizontalSize->isCalculatedPercentageWithLength())
1270         || (verticalSize && verticalSize->isCalculatedPercentageWithLength()))
1271         return nullptr;
1272
1273     result->setShape(shape.copyRef());
1274     result->setSizingBehavior(sizeKeyword.copyRef());
1275     result->setEndHorizontalSize(horizontalSize.copyRef());
1276     result->setEndVerticalSize(verticalSize.copyRef());
1277
1278     RefPtr<CSSPrimitiveValue> centerX;
1279     RefPtr<CSSPrimitiveValue> centerY;
1280     if (args.peek().id() == CSSValueAt) {
1281         args.consumeIncludingWhitespace();
1282         consumePosition(args, cssParserMode, UnitlessQuirk::Forbid, PositionSyntax::Position, centerX, centerY);
1283         if (!(centerX && centerY))
1284             return nullptr;
1285         
1286         result->setFirstX(centerX.copyRef());
1287         result->setFirstY(centerY.copyRef());
1288         
1289         // Right now, CSS radial gradients have the same start and end centers.
1290         result->setSecondX(centerX.copyRef());
1291         result->setSecondY(centerY.copyRef());
1292     }
1293
1294     if ((shape || sizeKeyword || horizontalSize || centerX || centerY) && !consumeCommaIncludingWhitespace(args))
1295         return nullptr;
1296     if (!consumeGradientColorStops(args, cssParserMode, *result))
1297         return nullptr;
1298     return result;
1299 }
1300
1301 static RefPtr<CSSValue> consumeLinearGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating, CSSGradientType gradientType)
1302 {
1303     RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, gradientType);
1304
1305     bool expectComma = true;
1306     RefPtr<CSSPrimitiveValue> angle = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1307     if (angle)
1308         result->setAngle(angle.releaseNonNull());
1309     else if (gradientType == CSSPrefixedLinearGradient || consumeIdent<CSSValueTo>(args)) {
1310         RefPtr<CSSPrimitiveValue> endX = consumeIdent<CSSValueLeft, CSSValueRight>(args);
1311         RefPtr<CSSPrimitiveValue> endY = consumeIdent<CSSValueBottom, CSSValueTop>(args);
1312         if (!endX && !endY) {
1313             if (gradientType == CSSLinearGradient)
1314                 return nullptr;
1315             endY = CSSPrimitiveValue::createIdentifier(CSSValueTop);
1316             expectComma = false;
1317         } else if (!endX) {
1318             endX = consumeIdent<CSSValueLeft, CSSValueRight>(args);
1319         }
1320
1321         result->setFirstX(endX.copyRef());
1322         result->setFirstY(endY.copyRef());
1323     } else {
1324         expectComma = false;
1325     }
1326
1327     if (expectComma && !consumeCommaIncludingWhitespace(args))
1328         return nullptr;
1329     if (!consumeGradientColorStops(args, cssParserMode, *result))
1330         return nullptr;
1331     return result;
1332 }
1333
1334 static RefPtr<CSSValue> consumeConicGradient(CSSParserTokenRange& args, CSSParserContext context, CSSGradientRepeat repeating)
1335 {
1336 #if ENABLE(CSS_CONIC_GRADIENTS)
1337     RefPtr<CSSConicGradientValue> result = CSSConicGradientValue::create(repeating);
1338
1339     bool expectComma = false;
1340     if (args.peek().type() == IdentToken) {
1341         if (consumeIdent<CSSValueFrom>(args)) {
1342             auto angle = consumeAngle(args, context.mode, UnitlessQuirk::Forbid);
1343             if (!angle)
1344                 return nullptr;
1345             result->setAngle(angle.releaseNonNull());
1346             expectComma = true;
1347         }
1348         
1349         if (consumeIdent<CSSValueAt>(args)) {
1350             RefPtr<CSSPrimitiveValue> centerX;
1351             RefPtr<CSSPrimitiveValue> centerY;
1352             consumePosition(args, context.mode, UnitlessQuirk::Forbid, PositionSyntax::Position, centerX, centerY);
1353             if (!(centerX && centerY))
1354                 return nullptr;
1355             
1356             result->setFirstX(centerX.copyRef());
1357             result->setFirstY(centerY.copyRef());
1358             
1359             // Right now, conic gradients have the same start and end centers.
1360             result->setSecondX(centerX.copyRef());
1361             result->setSecondY(centerY.copyRef());
1362     
1363             expectComma = true;
1364         }
1365     }
1366
1367     if (expectComma && !consumeCommaIncludingWhitespace(args))
1368         return nullptr;
1369     if (!consumeGradientColorStops(args, context.mode, *result))
1370         return nullptr;
1371     return result;
1372 #else
1373     UNUSED_PARAM(args);
1374     UNUSED_PARAM(context);
1375     UNUSED_PARAM(repeating);
1376     return nullptr;
1377 #endif
1378 }
1379
1380 RefPtr<CSSValue> consumeImageOrNone(CSSParserTokenRange& range, CSSParserContext context)
1381 {
1382     if (range.peek().id() == CSSValueNone)
1383         return consumeIdent(range);
1384     return consumeImage(range, context);
1385 }
1386
1387 static RefPtr<CSSValue> consumeCrossFade(CSSParserTokenRange& args, CSSParserContext context, bool prefixed)
1388 {
1389     RefPtr<CSSValue> fromImageValue = consumeImageOrNone(args, context);
1390     if (!fromImageValue || !consumeCommaIncludingWhitespace(args))
1391         return nullptr;
1392     RefPtr<CSSValue> toImageValue = consumeImageOrNone(args, context);
1393     if (!toImageValue || !consumeCommaIncludingWhitespace(args))
1394         return nullptr;
1395
1396     RefPtr<CSSPrimitiveValue> percentage;
1397     if (auto percentValue = consumePercent(args, ValueRangeAll))
1398         percentage = CSSValuePool::singleton().createValue(clampTo<double>(percentValue->doubleValue() / 100.0, 0, 1), CSSUnitType::CSS_NUMBER);
1399     else if (auto numberValue = consumeNumber(args, ValueRangeAll))
1400         percentage = CSSValuePool::singleton().createValue(clampTo<double>(numberValue->doubleValue(), 0, 1), CSSUnitType::CSS_NUMBER);
1401
1402     if (!percentage)
1403         return nullptr;
1404     return CSSCrossfadeValue::create(fromImageValue.releaseNonNull(), toImageValue.releaseNonNull(), percentage.releaseNonNull(), prefixed);
1405 }
1406
1407 static RefPtr<CSSValue> consumeWebkitCanvas(CSSParserTokenRange& args)
1408 {
1409     if (args.peek().type() != IdentToken)
1410         return nullptr;
1411     auto canvasName = args.consumeIncludingWhitespace().value().toString();
1412     if (!args.atEnd())
1413         return nullptr;
1414     return CSSCanvasValue::create(canvasName);
1415 }
1416
1417 static RefPtr<CSSValue> consumeWebkitNamedImage(CSSParserTokenRange& args)
1418 {
1419     if (args.peek().type() != IdentToken)
1420         return nullptr;
1421     auto imageName = args.consumeIncludingWhitespace().value().toString();
1422     if (!args.atEnd())
1423         return nullptr;
1424     return CSSNamedImageValue::create(imageName);
1425 }
1426
1427 static RefPtr<CSSValue> consumeFilterImage(CSSParserTokenRange& args, const CSSParserContext& context)
1428 {
1429     auto imageValue = consumeImageOrNone(args, context);
1430     if (!imageValue || !consumeCommaIncludingWhitespace(args))
1431         return nullptr;
1432
1433     auto filterValue = consumeFilter(args, context, AllowedFilterFunctions::PixelFilters);
1434
1435     if (!filterValue)
1436         return nullptr;
1437
1438     if (!args.atEnd())
1439         return nullptr;
1440
1441     return CSSFilterImageValue::create(imageValue.releaseNonNull(), filterValue.releaseNonNull());
1442 }
1443
1444 #if ENABLE(CSS_PAINTING_API)
1445 static RefPtr<CSSValue> consumeCustomPaint(CSSParserTokenRange& args)
1446 {
1447     if (!RuntimeEnabledFeatures::sharedFeatures().cssPaintingAPIEnabled())
1448         return nullptr;
1449     if (args.peek().type() != IdentToken)
1450         return nullptr;
1451     auto name = args.consumeIncludingWhitespace().value().toString();
1452
1453     if (!args.atEnd() && args.peek() != CommaToken)
1454         return nullptr;
1455     if (!args.atEnd())
1456         args.consume();
1457
1458     auto argumentList = CSSVariableData::create(args);
1459
1460     while (!args.atEnd())
1461         args.consume();
1462
1463     return CSSPaintImageValue::create(name, WTFMove(argumentList));
1464 }
1465 #endif
1466
1467 static RefPtr<CSSValue> consumeGeneratedImage(CSSParserTokenRange& range, CSSParserContext context)
1468 {
1469     CSSValueID id = range.peek().functionId();
1470     CSSParserTokenRange rangeCopy = range;
1471     CSSParserTokenRange args = consumeFunction(rangeCopy);
1472     RefPtr<CSSValue> result;
1473     if (id == CSSValueRadialGradient)
1474         result = consumeRadialGradient(args, context.mode, NonRepeating);
1475     else if (id == CSSValueRepeatingRadialGradient)
1476         result = consumeRadialGradient(args, context.mode, Repeating);
1477     else if (id == CSSValueWebkitLinearGradient)
1478         result = consumeLinearGradient(args, context.mode, NonRepeating, CSSPrefixedLinearGradient);
1479     else if (id == CSSValueWebkitRepeatingLinearGradient)
1480         result = consumeLinearGradient(args, context.mode, Repeating, CSSPrefixedLinearGradient);
1481     else if (id == CSSValueRepeatingLinearGradient)
1482         result = consumeLinearGradient(args, context.mode, Repeating, CSSLinearGradient);
1483     else if (id == CSSValueLinearGradient)
1484         result = consumeLinearGradient(args, context.mode, NonRepeating, CSSLinearGradient);
1485     else if (id == CSSValueWebkitGradient)
1486         result = consumeDeprecatedGradient(args, context.mode);
1487     else if (id == CSSValueWebkitRadialGradient)
1488         result = consumeDeprecatedRadialGradient(args, context.mode, NonRepeating);
1489     else if (id == CSSValueWebkitRepeatingRadialGradient)
1490         result = consumeDeprecatedRadialGradient(args, context.mode, Repeating);
1491     else if (id == CSSValueConicGradient)
1492         result = consumeConicGradient(args, context, NonRepeating);
1493     else if (id == CSSValueRepeatingConicGradient)
1494         result = consumeConicGradient(args, context, Repeating);
1495     else if (id == CSSValueWebkitCrossFade || id == CSSValueCrossFade)
1496         result = consumeCrossFade(args, context, id == CSSValueWebkitCrossFade);
1497     else if (id == CSSValueWebkitCanvas)
1498         result = consumeWebkitCanvas(args);
1499     else if (id == CSSValueWebkitNamedImage)
1500         result = consumeWebkitNamedImage(args);
1501     else if (id == CSSValueWebkitFilter || id == CSSValueFilter)
1502         result = consumeFilterImage(args, context);
1503 #if ENABLE(CSS_PAINTING_API)
1504     else if (id == CSSValuePaint)
1505         result = consumeCustomPaint(args);
1506 #endif
1507     if (!result || !args.atEnd())
1508         return nullptr;
1509     range = rangeCopy;
1510     return result;
1511 }
1512
1513 static RefPtr<CSSValue> consumeImageSet(CSSParserTokenRange& range, const CSSParserContext& context)
1514 {
1515     CSSParserTokenRange rangeCopy = range;
1516     CSSParserTokenRange args = consumeFunction(rangeCopy);
1517     RefPtr<CSSImageSetValue> imageSet = CSSImageSetValue::create(context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No);
1518     do {
1519         AtomString urlValue = consumeUrlAsStringView(args).toAtomString();
1520         if (urlValue.isNull())
1521             return nullptr;
1522
1523         RefPtr<CSSValue> image = CSSImageValue::create(completeURL(context, urlValue), context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No);
1524         imageSet->append(image.releaseNonNull());
1525
1526         const CSSParserToken& token = args.consumeIncludingWhitespace();
1527         if (token.type() != DimensionToken)
1528             return nullptr;
1529         if (token.value() != "x")
1530             return nullptr;
1531         ASSERT(token.unitType() == CSSUnitType::CSS_UNKNOWN);
1532         double imageScaleFactor = token.numericValue();
1533         if (imageScaleFactor <= 0)
1534             return nullptr;
1535         imageSet->append(CSSValuePool::singleton().createValue(imageScaleFactor, CSSUnitType::CSS_NUMBER));
1536     } while (consumeCommaIncludingWhitespace(args));
1537     if (!args.atEnd())
1538         return nullptr;
1539     range = rangeCopy;
1540     return imageSet;
1541 }
1542
1543 static bool isGeneratedImage(CSSValueID id)
1544 {
1545     return id == CSSValueLinearGradient
1546         || id == CSSValueRadialGradient
1547         || id == CSSValueConicGradient
1548         || id == CSSValueRepeatingLinearGradient
1549         || id == CSSValueRepeatingRadialGradient
1550         || id == CSSValueRepeatingConicGradient
1551         || id == CSSValueWebkitLinearGradient
1552         || id == CSSValueWebkitRadialGradient
1553         || id == CSSValueWebkitRepeatingLinearGradient
1554         || id == CSSValueWebkitRepeatingRadialGradient
1555         || id == CSSValueWebkitGradient
1556         || id == CSSValueWebkitCrossFade
1557         || id == CSSValueWebkitCanvas
1558         || id == CSSValueCrossFade
1559         || id == CSSValueWebkitNamedImage
1560         || id == CSSValueWebkitFilter
1561 #if ENABLE(CSS_PAINTING_API)
1562         || id == CSSValuePaint
1563 #endif
1564         || id == CSSValueFilter;
1565 }
1566     
1567 static bool isPixelFilterFunction(CSSValueID filterFunction)
1568 {
1569     switch (filterFunction) {
1570     case CSSValueBlur:
1571     case CSSValueBrightness:
1572     case CSSValueContrast:
1573     case CSSValueDropShadow:
1574     case CSSValueGrayscale:
1575     case CSSValueHueRotate:
1576     case CSSValueInvert:
1577     case CSSValueOpacity:
1578     case CSSValueSaturate:
1579     case CSSValueSepia:
1580         return true;
1581     default:
1582         return false;
1583     }
1584 }
1585
1586 static bool isColorFilterFunction(CSSValueID filterFunction)
1587 {
1588     switch (filterFunction) {
1589     case CSSValueBrightness:
1590     case CSSValueContrast:
1591     case CSSValueGrayscale:
1592     case CSSValueHueRotate:
1593     case CSSValueInvert:
1594     case CSSValueOpacity:
1595     case CSSValueSaturate:
1596     case CSSValueSepia:
1597     case CSSValueAppleInvertLightness:
1598         return true;
1599     default:
1600         return false;
1601     }
1602 }
1603
1604 static bool allowsValuesGreaterThanOne(CSSValueID filterFunction)
1605 {
1606     switch (filterFunction) {
1607     case CSSValueBrightness:
1608     case CSSValueContrast:
1609     case CSSValueSaturate:
1610         return true;
1611     default:
1612         return false;
1613     }
1614 }
1615
1616 static RefPtr<CSSFunctionValue> consumeFilterFunction(CSSParserTokenRange& range, const CSSParserContext& context, AllowedFilterFunctions allowedFunctions)
1617 {
1618     CSSValueID filterType = range.peek().functionId();
1619     switch (allowedFunctions) {
1620     case AllowedFilterFunctions::PixelFilters:
1621         if (!isPixelFilterFunction(filterType))
1622             return nullptr;
1623         break;
1624     case AllowedFilterFunctions::ColorFilters:
1625         if (!isColorFilterFunction(filterType))
1626             return nullptr;
1627         break;
1628     }
1629
1630     CSSParserTokenRange args = consumeFunction(range);
1631     RefPtr<CSSFunctionValue> filterValue = CSSFunctionValue::create(filterType);
1632
1633     if (filterType == CSSValueAppleInvertLightness) {
1634         if (!args.atEnd())
1635             return nullptr;
1636         return filterValue;
1637     }
1638
1639     RefPtr<CSSValue> parsedValue;
1640
1641     if (filterType == CSSValueDropShadow)
1642         parsedValue = consumeSingleShadow(args, context.mode, false, false);
1643     else {
1644         if (args.atEnd())
1645             return filterValue;
1646
1647         if (filterType == CSSValueHueRotate)
1648             parsedValue = consumeAngle(args, context.mode, UnitlessQuirk::Forbid);
1649         else if (filterType == CSSValueBlur)
1650             parsedValue = consumeLength(args, HTMLStandardMode, ValueRangeNonNegative);
1651         else {
1652             parsedValue = consumePercent(args, ValueRangeNonNegative);
1653             if (!parsedValue)
1654                 parsedValue = consumeNumber(args, ValueRangeNonNegative);
1655             if (parsedValue && !allowsValuesGreaterThanOne(filterType)) {
1656                 bool isPercentage = downcast<CSSPrimitiveValue>(*parsedValue).isPercentage();
1657                 double maxAllowed = isPercentage ? 100.0 : 1.0;
1658                 if (downcast<CSSPrimitiveValue>(*parsedValue).doubleValue() > maxAllowed)
1659                     parsedValue = CSSPrimitiveValue::create(maxAllowed, isPercentage ? CSSUnitType::CSS_PERCENTAGE : CSSUnitType::CSS_NUMBER);
1660             }
1661         }
1662     }
1663     if (!parsedValue || !args.atEnd())
1664         return nullptr;
1665     filterValue->append(parsedValue.releaseNonNull());
1666     return filterValue;
1667 }
1668
1669 RefPtr<CSSValue> consumeFilter(CSSParserTokenRange& range, const CSSParserContext& context, AllowedFilterFunctions allowedFunctions)
1670 {
1671     if (range.peek().id() == CSSValueNone)
1672         return consumeIdent(range);
1673
1674     bool referenceFiltersAllowed = allowedFunctions == AllowedFilterFunctions::PixelFilters;
1675     auto list = CSSValueList::createSpaceSeparated();
1676     do {
1677         RefPtr<CSSValue> filterValue = referenceFiltersAllowed ? consumeUrl(range) : nullptr;
1678         if (!filterValue) {
1679             filterValue = consumeFilterFunction(range, context, allowedFunctions);
1680             if (!filterValue)
1681                 return nullptr;
1682         }
1683         list->append(filterValue.releaseNonNull());
1684     } while (!range.atEnd());
1685
1686     return list.ptr();
1687 }
1688
1689 RefPtr<CSSShadowValue> consumeSingleShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool allowInset, bool allowSpread)
1690 {
1691     RefPtr<CSSPrimitiveValue> style;
1692     RefPtr<CSSPrimitiveValue> color;
1693
1694     if (range.atEnd())
1695         return nullptr;
1696     if (range.peek().id() == CSSValueInset) {
1697         if (!allowInset)
1698             return nullptr;
1699         style = consumeIdent(range);
1700     }
1701     color = consumeColor(range, cssParserMode);
1702
1703     auto horizontalOffset = consumeLength(range, cssParserMode, ValueRangeAll);
1704     if (!horizontalOffset)
1705         return nullptr;
1706
1707     auto verticalOffset = consumeLength(range, cssParserMode, ValueRangeAll);
1708     if (!verticalOffset)
1709         return nullptr;
1710
1711     auto blurRadius = consumeLength(range, cssParserMode, ValueRangeAll);
1712     RefPtr<CSSPrimitiveValue> spreadDistance;
1713     if (blurRadius) {
1714         // Blur radius must be non-negative.
1715         if (blurRadius->doubleValue() < 0)
1716             return nullptr;
1717         if (allowSpread)
1718             spreadDistance = consumeLength(range, cssParserMode, ValueRangeAll);
1719     }
1720
1721     if (!range.atEnd()) {
1722         if (!color)
1723             color = consumeColor(range, cssParserMode);
1724         if (range.peek().id() == CSSValueInset) {
1725             if (!allowInset || style)
1726                 return nullptr;
1727             style = consumeIdent(range);
1728         }
1729     }
1730
1731     return CSSShadowValue::create(WTFMove(horizontalOffset), WTFMove(verticalOffset), WTFMove(blurRadius), WTFMove(spreadDistance), WTFMove(style), WTFMove(color));
1732 }
1733
1734 RefPtr<CSSValue> consumeImage(CSSParserTokenRange& range, CSSParserContext context, ConsumeGeneratedImage generatedImage)
1735 {
1736     AtomString uri = consumeUrlAsStringView(range).toAtomString();
1737     if (!uri.isNull())
1738         return CSSImageValue::create(completeURL(context, uri), context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No);
1739
1740     if (range.peek().type() == FunctionToken) {
1741         CSSValueID id = range.peek().functionId();
1742         if (id == CSSValueWebkitImageSet || id == CSSValueImageSet)
1743             return consumeImageSet(range, context);
1744         if (generatedImage == ConsumeGeneratedImage::Allow && isGeneratedImage(id))
1745             return consumeGeneratedImage(range, context);
1746     }
1747     return nullptr;
1748 }
1749
1750 } // namespace CSSPropertyParserHelpers
1751
1752 } // namespace WebCore