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