CSS Transitions and CSS Animations properties should treat unitless 0 as an invalid...
[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, CSSPrimitiveValue::UnitType::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(), CSSPrimitiveValue::UnitType::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(), CSSPrimitiveValue::UnitType::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, CSSPrimitiveValue::UnitType::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 CSSPrimitiveValue::UnitType::CSS_QUIRKY_EMS:
282             if (cssParserMode != UASheetMode)
283                 return nullptr;
284             FALLTHROUGH;
285         case CSSPrimitiveValue::UnitType::CSS_EMS:
286         case CSSPrimitiveValue::UnitType::CSS_REMS:
287         case CSSPrimitiveValue::UnitType::CSS_CHS:
288         case CSSPrimitiveValue::UnitType::CSS_EXS:
289         case CSSPrimitiveValue::UnitType::CSS_PX:
290         case CSSPrimitiveValue::UnitType::CSS_CM:
291         case CSSPrimitiveValue::UnitType::CSS_MM:
292         case CSSPrimitiveValue::UnitType::CSS_IN:
293         case CSSPrimitiveValue::UnitType::CSS_PT:
294         case CSSPrimitiveValue::UnitType::CSS_PC:
295         case CSSPrimitiveValue::UnitType::CSS_VW:
296         case CSSPrimitiveValue::UnitType::CSS_VH:
297         case CSSPrimitiveValue::UnitType::CSS_VMIN:
298         case CSSPrimitiveValue::UnitType::CSS_VMAX:
299             break;
300         default:
301             return nullptr;
302         }
303         if ((valueRange == ValueRangeNonNegative && token.numericValue() < 0) || std::isinf(token.numericValue()))
304             return nullptr;
305         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
306     }
307     if (token.type() == NumberToken) {
308         if (!shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless)
309             || (valueRange == ValueRangeNonNegative && token.numericValue() < 0))
310             return nullptr;
311         if (std::isinf(token.numericValue()))
312             return nullptr;
313         CSSPrimitiveValue::UnitType unitType = CSSPrimitiveValue::UnitType::CSS_PX;
314         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unitType);
315     }
316
317     if (token.type() != FunctionToken)
318         return nullptr;
319
320     CalcParser calcParser(range, CalculationCategory::Length, valueRange);
321     if (calcParser.value() && calcParser.value()->category() == CalculationCategory::Length)
322         return calcParser.consumeValue();
323
324     return nullptr;
325 }
326
327 RefPtr<CSSPrimitiveValue> consumePercent(CSSParserTokenRange& range, ValueRange valueRange)
328 {
329     const CSSParserToken& token = range.peek();
330     if (token.type() == PercentageToken) {
331         if ((valueRange == ValueRangeNonNegative && token.numericValue() < 0) || std::isinf(token.numericValue()))
332             return nullptr;
333         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
334     }
335
336     if (token.type() != FunctionToken)
337         return nullptr;
338
339     CalcParser calcParser(range, CalculationCategory::Percent, valueRange);
340     if (const CSSCalcValue* calculation = calcParser.value()) {
341         if (calculation->category() == CalculationCategory::Percent)
342             return calcParser.consumeValue();
343     }
344     return nullptr;
345 }
346
347 static bool canConsumeCalcValue(CalculationCategory category, CSSParserMode cssParserMode)
348 {
349     if (category == CalculationCategory::Length || category == CalculationCategory::Percent || category == CalculationCategory::PercentLength)
350         return true;
351
352     if (cssParserMode != SVGAttributeMode)
353         return false;
354
355     if (category == CalculationCategory::Number || category == CalculationCategory::PercentNumber)
356         return true;
357
358     return false;
359 }
360
361 RefPtr<CSSPrimitiveValue> consumeLengthOrPercent(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
362 {
363     const CSSParserToken& token = range.peek();
364     if (token.type() == DimensionToken || token.type() == NumberToken)
365         return consumeLength(range, cssParserMode, valueRange, unitless);
366     if (token.type() == PercentageToken)
367         return consumePercent(range, valueRange);
368
369     if (token.type() != FunctionToken)
370         return nullptr;
371
372     CalcParser calcParser(range, CalculationCategory::Length, valueRange);
373     if (const CSSCalcValue* calculation = calcParser.value()) {
374         if (canConsumeCalcValue(calculation->category(), cssParserMode))
375             return calcParser.consumeValue();
376     }
377     return nullptr;
378 }
379
380 RefPtr<CSSPrimitiveValue> consumeAngle(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
381 {
382     const CSSParserToken& token = range.peek();
383     if (token.type() == DimensionToken) {
384         switch (token.unitType()) {
385         case CSSPrimitiveValue::UnitType::CSS_DEG:
386         case CSSPrimitiveValue::UnitType::CSS_RAD:
387         case CSSPrimitiveValue::UnitType::CSS_GRAD:
388         case CSSPrimitiveValue::UnitType::CSS_TURN:
389             return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
390         default:
391             return nullptr;
392         }
393     }
394
395     if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless))
396         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::CSS_DEG);
397
398     if (token.type() != FunctionToken)
399         return nullptr;
400
401     CalcParser calcParser(range, CalculationCategory::Angle, ValueRangeAll);
402     if (const CSSCalcValue* calculation = calcParser.value()) {
403         if (calculation->category() == CalculationCategory::Angle)
404             return calcParser.consumeValue();
405     }
406     return nullptr;
407 }
408
409 static RefPtr<CSSPrimitiveValue> consumeAngleOrPercent(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
410 {
411     const CSSParserToken& token = range.peek();
412     if (token.type() == DimensionToken) {
413         switch (token.unitType()) {
414         case CSSPrimitiveValue::UnitType::CSS_DEG:
415         case CSSPrimitiveValue::UnitType::CSS_RAD:
416         case CSSPrimitiveValue::UnitType::CSS_GRAD:
417         case CSSPrimitiveValue::UnitType::CSS_TURN:
418             return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
419         default:
420             return nullptr;
421         }
422     }
423     if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless))
424         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::CSS_DEG);
425
426     if (token.type() == PercentageToken)
427         return consumePercent(range, valueRange);
428
429      if (token.type() != FunctionToken)
430          return nullptr;
431
432     CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
433     if (const CSSCalcValue* calculation = angleCalcParser.value()) {
434         if (calculation->category() == CalculationCategory::Angle)
435             return angleCalcParser.consumeValue();
436     }
437
438     CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
439     if (const CSSCalcValue* calculation = percentCalcParser.value()) {
440         if (calculation->category() == CalculationCategory::Percent)
441             return percentCalcParser.consumeValue();
442     }
443     return nullptr;
444 }
445
446 RefPtr<CSSPrimitiveValue> consumeTime(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
447 {
448     const CSSParserToken& token = range.peek();
449     CSSPrimitiveValue::UnitType unit = token.unitType();
450     bool acceptUnitless = token.type() == NumberToken && unitless == UnitlessQuirk::Allow && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless);
451     if (acceptUnitless)
452         unit = CSSPrimitiveValue::UnitType::CSS_MS;
453     if (token.type() == DimensionToken || acceptUnitless) {
454         if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
455             return nullptr;
456         if (unit == CSSPrimitiveValue::UnitType::CSS_MS || unit == CSSPrimitiveValue::UnitType::CSS_S)
457             return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unit);
458         return nullptr;
459     }
460
461     if (token.type() != FunctionToken)
462         return nullptr;
463
464     CalcParser calcParser(range, CalculationCategory::Time, valueRange);
465     if (const CSSCalcValue* calculation = calcParser.value()) {
466         if (calculation->category() == CalculationCategory::Time)
467             return calcParser.consumeValue();
468     }
469     return nullptr;
470 }
471
472 RefPtr<CSSPrimitiveValue> consumeResolution(CSSParserTokenRange& range)
473 {
474     const CSSParserToken& token = range.peek();
475     // Unlike the other types, calc() does not work with <resolution>.
476     if (token.type() != DimensionToken)
477         return nullptr;
478     CSSPrimitiveValue::UnitType unit = token.unitType();
479     if (unit == CSSPrimitiveValue::UnitType::CSS_DPPX || unit == CSSPrimitiveValue::UnitType::CSS_DPI || unit == CSSPrimitiveValue::UnitType::CSS_DPCM)
480         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unit);
481     return nullptr;
482 }
483
484 RefPtr<CSSPrimitiveValue> consumeIdent(CSSParserTokenRange& range)
485 {
486     if (range.peek().type() != IdentToken)
487         return nullptr;
488     return CSSValuePool::singleton().createIdentifierValue(range.consumeIncludingWhitespace().id());
489 }
490
491 RefPtr<CSSPrimitiveValue> consumeIdentRange(CSSParserTokenRange& range, CSSValueID lower, CSSValueID upper)
492 {
493     if (range.peek().id() < lower || range.peek().id() > upper)
494         return nullptr;
495     return consumeIdent(range);
496 }
497
498 // FIXME-NEWPARSER: Eventually we'd like this to use CSSCustomIdentValue, but we need
499 // to do other plumbing work first (like changing Pair to CSSValuePair and make it not
500 // use only primitive values).
501 RefPtr<CSSPrimitiveValue> consumeCustomIdent(CSSParserTokenRange& range)
502 {
503     if (range.peek().type() != IdentToken || isCSSWideKeyword(range.peek().id()))
504         return nullptr;
505     return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
506 }
507
508 RefPtr<CSSPrimitiveValue> consumeString(CSSParserTokenRange& range)
509 {
510     if (range.peek().type() != StringToken)
511         return nullptr;
512     return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
513 }
514
515 StringView consumeUrlAsStringView(CSSParserTokenRange& range)
516 {
517     const CSSParserToken& token = range.peek();
518     if (token.type() == UrlToken) {
519         range.consumeIncludingWhitespace();
520         return token.value();
521     }
522     if (token.functionId() == CSSValueUrl) {
523         CSSParserTokenRange urlRange = range;
524         CSSParserTokenRange urlArgs = urlRange.consumeBlock();
525         const CSSParserToken& next = urlArgs.consumeIncludingWhitespace();
526         if (next.type() == BadStringToken || !urlArgs.atEnd())
527             return StringView();
528         ASSERT(next.type() == StringToken);
529         range = urlRange;
530         range.consumeWhitespace();
531         return next.value();
532     }
533
534     return StringView();
535 }
536
537 RefPtr<CSSPrimitiveValue> consumeUrl(CSSParserTokenRange& range)
538 {
539     StringView url = consumeUrlAsStringView(range);
540     if (url.isNull())
541         return nullptr;
542     return CSSValuePool::singleton().createValue(url.toString(), CSSPrimitiveValue::UnitType::CSS_URI);
543 }
544
545 static int clampRGBComponent(const CSSPrimitiveValue& value)
546 {
547     double result = value.doubleValue();
548     if (value.isPercentage())
549         result = result / 100.0 * 255.0;
550
551     return clampTo<int>(round(result), 0, 255);
552 }
553
554 static Color parseRGBParameters(CSSParserTokenRange& range)
555 {
556     ASSERT(range.peek().functionId() == CSSValueRgb || range.peek().functionId() == CSSValueRgba);
557     Color result;
558     CSSParserTokenRange args = consumeFunction(range);
559     RefPtr<CSSPrimitiveValue> colorParameter = consumeNumber(args, ValueRangeAll);
560     if (!colorParameter)
561         colorParameter = consumePercent(args, ValueRangeAll);
562     if (!colorParameter)
563         return Color();
564
565     const bool isPercent = colorParameter->isPercentage();
566
567     enum class ColorSyntax {
568         Commas,
569         WhitespaceSlash,
570     };
571
572     ColorSyntax syntax = ColorSyntax::Commas;
573     auto consumeSeparator = [&] {
574         if (syntax == ColorSyntax::Commas)
575             return consumeCommaIncludingWhitespace(args);
576         
577         return true;
578     };
579
580     int colorArray[3];
581     colorArray[0] = clampRGBComponent(*colorParameter);
582     for (int i = 1; i < 3; i++) {
583         if (i == 1)
584             syntax = consumeCommaIncludingWhitespace(args) ? ColorSyntax::Commas : ColorSyntax::WhitespaceSlash;
585         else if (!consumeSeparator())
586             return Color();
587
588         colorParameter = isPercent ? consumePercent(args, ValueRangeAll) : consumeNumber(args, ValueRangeAll);
589         if (!colorParameter)
590             return Color();
591         colorArray[i] = clampRGBComponent(*colorParameter);
592     }
593
594     // Historically, alpha was only parsed for rgba(), but css-color-4 specifies that rgba() is a simple alias for rgb().
595     auto consumeAlphaSeparator = [&] {
596         if (syntax == ColorSyntax::Commas)
597             return consumeCommaIncludingWhitespace(args);
598         
599         return consumeSlashIncludingWhitespace(args);
600     };
601
602     int alphaComponent = 255;
603     if (consumeAlphaSeparator()) {
604         double alpha;
605         if (!consumeNumberRaw(args, alpha)) {
606             auto alphaPercent = consumePercent(args, ValueRangeAll);
607             if (!alphaPercent)
608                 return Color();
609             alpha = alphaPercent->doubleValue() / 100.0;
610         }
611
612         // Convert the floating pointer number of alpha to an integer in the range [0, 256),
613         // with an equal distribution across all 256 values.
614         alphaComponent = static_cast<int>(clampTo<double>(alpha, 0.0, 1.0) * nextafter(256.0, 0.0));
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     double colorArray[3];
639     // The hue needs to be in the range [0.0, 6.0) for calcHue()
640     colorArray[0] = fmod(fmod(angleInDegrees, 360.0) + 360.0, 360.0) / 60.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(colorArray[0], colorArray[1], colorArray[2], 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 static bool positionFromThreeOrFourValues(CSSPrimitiveValue** values, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
847 {
848     CSSPrimitiveValue* center = nullptr;
849     for (int i = 0; values[i]; i++) {
850         CSSPrimitiveValue* currentValue = values[i];
851         if (!currentValue->isValueID())
852             return false;
853         CSSValueID id = currentValue->valueID();
854
855         if (id == CSSValueCenter) {
856             if (center)
857                 return false;
858             center = currentValue;
859             continue;
860         }
861
862         RefPtr<CSSPrimitiveValue> result;
863         if (values[i + 1] && !values[i + 1]->isValueID())
864             result = CSSPropertyParserHelpersInternal::createPrimitiveValuePair(currentValue, values[++i]);
865         else
866             result = currentValue;
867
868         if (id == CSSValueLeft || id == CSSValueRight) {
869             if (resultX)
870                 return false;
871             resultX = result;
872         } else {
873             ASSERT(id == CSSValueTop || id == CSSValueBottom);
874             if (resultY)
875                 return false;
876             resultY = result;
877         }
878     }
879
880     if (center) {
881         ASSERT(resultX || resultY);
882         if (resultX && resultY)
883             return false;
884         if (!resultX)
885             resultX = center;
886         else
887             resultY = center;
888     }
889
890     ASSERT(resultX && resultY);
891     return true;
892 }
893
894 // FIXME: This may consume from the range upon failure. The background
895 // shorthand works around it, but we should just fix it here.
896 bool consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
897 {
898     RefPtr<CSSPrimitiveValue> value1 = consumePositionComponent(range, cssParserMode, unitless);
899     if (!value1)
900         return false;
901
902     RefPtr<CSSPrimitiveValue> value2 = consumePositionComponent(range, cssParserMode, unitless);
903     if (!value2) {
904         positionFromOneValue(*value1, resultX, resultY);
905         return true;
906     }
907
908     RefPtr<CSSPrimitiveValue> value3 = consumePositionComponent(range, cssParserMode, unitless);
909     if (!value3)
910         return positionFromTwoValues(*value1, *value2, resultX, resultY);
911
912     RefPtr<CSSPrimitiveValue> value4 = consumePositionComponent(range, cssParserMode, unitless);
913     CSSPrimitiveValue* values[5];
914     values[0] = value1.get();
915     values[1] = value2.get();
916     values[2] = value3.get();
917     values[3] = value4.get();
918     values[4] = nullptr;
919     return positionFromThreeOrFourValues(values, resultX, resultY);
920 }
921
922 RefPtr<CSSPrimitiveValue> consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
923 {
924     RefPtr<CSSPrimitiveValue> resultX;
925     RefPtr<CSSPrimitiveValue> resultY;
926     if (consumePosition(range, cssParserMode, unitless, resultX, resultY))
927         return CSSPropertyParserHelpersInternal::createPrimitiveValuePair(resultX.releaseNonNull(), resultY.releaseNonNull());
928     return nullptr;
929 }
930
931 bool consumeOneOrTwoValuedPosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
932 {
933     RefPtr<CSSPrimitiveValue> value1 = consumePositionComponent(range, cssParserMode, unitless);
934     if (!value1)
935         return false;
936     RefPtr<CSSPrimitiveValue> value2 = consumePositionComponent(range, cssParserMode, unitless);
937     if (!value2) {
938         positionFromOneValue(*value1, resultX, resultY);
939         return true;
940     }
941     return positionFromTwoValues(*value1, *value2, resultX, resultY);
942 }
943
944 // This should go away once we drop support for -webkit-gradient
945 static RefPtr<CSSPrimitiveValue> consumeDeprecatedGradientPoint(CSSParserTokenRange& args, bool horizontal)
946 {
947     if (args.peek().type() == IdentToken) {
948         if ((horizontal && consumeIdent<CSSValueLeft>(args)) || (!horizontal && consumeIdent<CSSValueTop>(args)))
949             return CSSValuePool::singleton().createValue(0., CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
950         if ((horizontal && consumeIdent<CSSValueRight>(args)) || (!horizontal && consumeIdent<CSSValueBottom>(args)))
951             return CSSValuePool::singleton().createValue(100., CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
952         if (consumeIdent<CSSValueCenter>(args))
953             return CSSValuePool::singleton().createValue(50., CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
954         return nullptr;
955     }
956     RefPtr<CSSPrimitiveValue> result = consumePercent(args, ValueRangeAll);
957     if (!result)
958         result = consumeNumber(args, ValueRangeAll);
959     return result;
960 }
961
962 // Used to parse colors for -webkit-gradient(...).
963 static RefPtr<CSSPrimitiveValue> consumeDeprecatedGradientStopColor(CSSParserTokenRange& args, CSSParserMode cssParserMode)
964 {
965     if (args.peek().id() == CSSValueCurrentcolor)
966         return nullptr;
967     return consumeColor(args, cssParserMode);
968 }
969
970 static bool consumeDeprecatedGradientColorStop(CSSParserTokenRange& range, CSSGradientColorStop& stop, CSSParserMode cssParserMode)
971 {
972     CSSValueID id = range.peek().functionId();
973     if (id != CSSValueFrom && id != CSSValueTo && id != CSSValueColorStop)
974         return false;
975
976     CSSParserTokenRange args = consumeFunction(range);
977     double position;
978     if (id == CSSValueFrom || id == CSSValueTo) {
979         position = (id == CSSValueFrom) ? 0 : 1;
980     } else {
981         ASSERT(id == CSSValueColorStop);
982         if (auto percentValue = consumePercent(args, ValueRangeAll))
983             position = percentValue->doubleValue() / 100.0;
984         else if (!consumeNumberRaw(args, position))
985             return false;
986
987         if (!consumeCommaIncludingWhitespace(args))
988             return false;
989     }
990
991     stop.m_position = CSSValuePool::singleton().createValue(position, CSSPrimitiveValue::UnitType::CSS_NUMBER);
992     stop.m_color = consumeDeprecatedGradientStopColor(args, cssParserMode);
993     return stop.m_color && args.atEnd();
994 }
995
996 static RefPtr<CSSValue> consumeDeprecatedGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode)
997 {
998     RefPtr<CSSGradientValue> result;
999     CSSValueID id = args.consumeIncludingWhitespace().id();
1000     bool isDeprecatedRadialGradient = (id == CSSValueRadial);
1001     if (isDeprecatedRadialGradient)
1002         result = CSSRadialGradientValue::create(NonRepeating, CSSDeprecatedRadialGradient);
1003     else if (id == CSSValueLinear)
1004         result = CSSLinearGradientValue::create(NonRepeating, CSSDeprecatedLinearGradient);
1005     if (!result || !consumeCommaIncludingWhitespace(args))
1006         return nullptr;
1007
1008     auto point = consumeDeprecatedGradientPoint(args, true);
1009     if (!point)
1010         return nullptr;
1011     result->setFirstX(point.copyRef());
1012     point = consumeDeprecatedGradientPoint(args, false);
1013     if (!point)
1014         return nullptr;
1015     result->setFirstY(point.copyRef());
1016
1017     if (!consumeCommaIncludingWhitespace(args))
1018         return nullptr;
1019
1020     // For radial gradients only, we now expect a numeric radius.
1021     if (isDeprecatedRadialGradient) {
1022         auto radius = consumeNumber(args, ValueRangeNonNegative);
1023         if (!radius || !consumeCommaIncludingWhitespace(args))
1024             return nullptr;
1025         downcast<CSSRadialGradientValue>(result.get())->setFirstRadius(radius.copyRef());
1026     }
1027
1028     point = consumeDeprecatedGradientPoint(args, true);
1029     if (!point)
1030         return nullptr;
1031     result->setSecondX(point.copyRef());
1032     point = consumeDeprecatedGradientPoint(args, false);
1033     if (!point)
1034         return nullptr;
1035     result->setSecondY(point.copyRef());
1036
1037     // For radial gradients only, we now expect the second radius.
1038     if (isDeprecatedRadialGradient) {
1039         if (!consumeCommaIncludingWhitespace(args))
1040             return nullptr;
1041         auto radius = consumeNumber(args, ValueRangeNonNegative);
1042         if (!radius)
1043             return nullptr;
1044         downcast<CSSRadialGradientValue>(result.get())->setSecondRadius(radius.copyRef());
1045     }
1046
1047     CSSGradientColorStop stop;
1048     while (consumeCommaIncludingWhitespace(args)) {
1049         if (!consumeDeprecatedGradientColorStop(args, stop, cssParserMode))
1050             return nullptr;
1051         result->addStop(stop);
1052     }
1053
1054     result->doneAddingStops();
1055     return result;
1056 }
1057
1058 static bool consumeGradientColorStops(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSGradientValue& gradient)
1059 {
1060     bool supportsColorHints = gradient.gradientType() == CSSLinearGradient || gradient.gradientType() == CSSRadialGradient || gradient.gradientType() == CSSConicGradient;
1061     
1062     bool isConicGradient = gradient.gradientType() == CSSConicGradient;
1063
1064     // The first color stop cannot be a color hint.
1065     bool previousStopWasColorHint = true;
1066     do {
1067         CSSGradientColorStop stop;
1068         stop.m_color = consumeColor(range, cssParserMode);
1069         // Two hints in a row are not allowed.
1070         if (!stop.m_color && (!supportsColorHints || previousStopWasColorHint))
1071             return false;
1072         
1073         previousStopWasColorHint = !stop.m_color;
1074         
1075         // FIXME-NEWPARSER: This boolean could be removed. Null checking color would be sufficient.
1076         stop.isMidpoint = !stop.m_color;
1077
1078         if (isConicGradient)
1079             stop.m_position = consumeAngleOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid);
1080         else
1081             stop.m_position = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
1082         
1083         if (!stop.m_color && !stop.m_position)
1084             return false;
1085
1086         gradient.addStop(stop);
1087
1088         if (!stop.m_color || !stop.m_position)
1089             continue;
1090
1091         CSSGradientColorStop secondStop;
1092         if (isConicGradient)
1093             secondStop.m_position = consumeAngleOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid);
1094         else
1095             secondStop.m_position = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
1096         
1097         if (secondStop.m_position)
1098             gradient.addStop(secondStop);
1099         
1100     } while (consumeCommaIncludingWhitespace(range));
1101
1102     gradient.doneAddingStops();
1103
1104     // The last color stop cannot be a color hint.
1105     if (previousStopWasColorHint)
1106         return false;
1107
1108     // Must have 2 or more stops to be valid.
1109     return gradient.stopCount() >= 2;
1110 }
1111
1112 static RefPtr<CSSValue> consumeDeprecatedRadialGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating)
1113 {
1114     RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSPrefixedRadialGradient);
1115     RefPtr<CSSPrimitiveValue> centerX;
1116     RefPtr<CSSPrimitiveValue> centerY;
1117     consumeOneOrTwoValuedPosition(args, cssParserMode, UnitlessQuirk::Forbid, centerX, centerY);
1118     if ((centerX || centerY) && !consumeCommaIncludingWhitespace(args))
1119         return nullptr;
1120
1121     result->setFirstX(centerX.copyRef());
1122     result->setFirstY(centerY.copyRef());
1123     result->setSecondX(centerX.copyRef());
1124     result->setSecondY(centerY.copyRef());
1125
1126     auto shape = consumeIdent<CSSValueCircle, CSSValueEllipse>(args);
1127     auto sizeKeyword = consumeIdent<CSSValueClosestSide, CSSValueClosestCorner, CSSValueFarthestSide, CSSValueFarthestCorner, CSSValueContain, CSSValueCover>(args);
1128     if (!shape)
1129         shape = consumeIdent<CSSValueCircle, CSSValueEllipse>(args);
1130     result->setShape(shape.copyRef());
1131     result->setSizingBehavior(sizeKeyword.copyRef());
1132
1133     // Or, two lengths or percentages
1134     if (!shape && !sizeKeyword) {
1135         auto horizontalSize = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
1136         RefPtr<CSSPrimitiveValue> verticalSize;
1137         if (horizontalSize) {
1138             verticalSize = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
1139             if (!verticalSize)
1140                 return nullptr;
1141             consumeCommaIncludingWhitespace(args);
1142             result->setEndHorizontalSize(horizontalSize.copyRef());
1143             result->setEndVerticalSize(verticalSize.copyRef());
1144         }
1145     } else {
1146         consumeCommaIncludingWhitespace(args);
1147     }
1148     if (!consumeGradientColorStops(args, cssParserMode, *result))
1149         return nullptr;
1150
1151     return result;
1152 }
1153
1154 static RefPtr<CSSValue> consumeRadialGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating)
1155 {
1156     RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient);
1157
1158     RefPtr<CSSPrimitiveValue> shape;
1159     RefPtr<CSSPrimitiveValue> sizeKeyword;
1160     RefPtr<CSSPrimitiveValue> horizontalSize;
1161     RefPtr<CSSPrimitiveValue> verticalSize;
1162
1163     // First part of grammar, the size/shape clause:
1164     // [ circle || <length> ] |
1165     // [ ellipse || [ <length> | <percentage> ]{2} ] |
1166     // [ [ circle | ellipse] || <size-keyword> ]
1167     for (int i = 0; i < 3; ++i) {
1168         if (args.peek().type() == IdentToken) {
1169             CSSValueID id = args.peek().id();
1170             if (id == CSSValueCircle || id == CSSValueEllipse) {
1171                 if (shape)
1172                     return nullptr;
1173                 shape = consumeIdent(args);
1174             } else if (id == CSSValueClosestSide || id == CSSValueClosestCorner || id == CSSValueFarthestSide || id == CSSValueFarthestCorner) {
1175                 if (sizeKeyword)
1176                     return nullptr;
1177                 sizeKeyword = consumeIdent(args);
1178             } else {
1179                 break;
1180             }
1181         } else {
1182             auto center = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
1183             if (!center)
1184                 break;
1185             if (horizontalSize)
1186                 return nullptr;
1187             horizontalSize = center;
1188             center = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
1189             if (center) {
1190                 verticalSize = center;
1191                 ++i;
1192             }
1193         }
1194     }
1195
1196     // You can specify size as a keyword or a length/percentage, not both.
1197     if (sizeKeyword && horizontalSize)
1198         return nullptr;
1199     // Circles must have 0 or 1 lengths.
1200     if (shape && shape->valueID() == CSSValueCircle && verticalSize)
1201         return nullptr;
1202     // Ellipses must have 0 or 2 length/percentages.
1203     if (shape && shape->valueID() == CSSValueEllipse && horizontalSize && !verticalSize)
1204         return nullptr;
1205     // If there's only one size, it must be a length.
1206     if (!verticalSize && horizontalSize && horizontalSize->isPercentage())
1207         return nullptr;
1208     if ((horizontalSize && horizontalSize->isCalculatedPercentageWithLength())
1209         || (verticalSize && verticalSize->isCalculatedPercentageWithLength()))
1210         return nullptr;
1211
1212     result->setShape(shape.copyRef());
1213     result->setSizingBehavior(sizeKeyword.copyRef());
1214     result->setEndHorizontalSize(horizontalSize.copyRef());
1215     result->setEndVerticalSize(verticalSize.copyRef());
1216
1217     RefPtr<CSSPrimitiveValue> centerX;
1218     RefPtr<CSSPrimitiveValue> centerY;
1219     if (args.peek().id() == CSSValueAt) {
1220         args.consumeIncludingWhitespace();
1221         consumePosition(args, cssParserMode, UnitlessQuirk::Forbid, centerX, centerY);
1222         if (!(centerX && centerY))
1223             return nullptr;
1224         
1225         result->setFirstX(centerX.copyRef());
1226         result->setFirstY(centerY.copyRef());
1227         
1228         // Right now, CSS radial gradients have the same start and end centers.
1229         result->setSecondX(centerX.copyRef());
1230         result->setSecondY(centerY.copyRef());
1231     }
1232
1233     if ((shape || sizeKeyword || horizontalSize || centerX || centerY) && !consumeCommaIncludingWhitespace(args))
1234         return nullptr;
1235     if (!consumeGradientColorStops(args, cssParserMode, *result))
1236         return nullptr;
1237     return result;
1238 }
1239
1240 static RefPtr<CSSValue> consumeLinearGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating, CSSGradientType gradientType)
1241 {
1242     RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, gradientType);
1243
1244     bool expectComma = true;
1245     RefPtr<CSSPrimitiveValue> angle = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1246     if (angle)
1247         result->setAngle(angle.releaseNonNull());
1248     else if (gradientType == CSSPrefixedLinearGradient || consumeIdent<CSSValueTo>(args)) {
1249         RefPtr<CSSPrimitiveValue> endX = consumeIdent<CSSValueLeft, CSSValueRight>(args);
1250         RefPtr<CSSPrimitiveValue> endY = consumeIdent<CSSValueBottom, CSSValueTop>(args);
1251         if (!endX && !endY) {
1252             if (gradientType == CSSLinearGradient)
1253                 return nullptr;
1254             endY = CSSPrimitiveValue::createIdentifier(CSSValueTop);
1255             expectComma = false;
1256         } else if (!endX) {
1257             endX = consumeIdent<CSSValueLeft, CSSValueRight>(args);
1258         }
1259
1260         result->setFirstX(endX.copyRef());
1261         result->setFirstY(endY.copyRef());
1262     } else {
1263         expectComma = false;
1264     }
1265
1266     if (expectComma && !consumeCommaIncludingWhitespace(args))
1267         return nullptr;
1268     if (!consumeGradientColorStops(args, cssParserMode, *result))
1269         return nullptr;
1270     return result;
1271 }
1272
1273 static RefPtr<CSSValue> consumeConicGradient(CSSParserTokenRange& args, CSSParserContext context, CSSGradientRepeat repeating)
1274 {
1275 #if ENABLE(CSS_CONIC_GRADIENTS)
1276     RefPtr<CSSConicGradientValue> result = CSSConicGradientValue::create(repeating);
1277
1278     bool expectComma = false;
1279     if (args.peek().type() == IdentToken) {
1280         if (consumeIdent<CSSValueFrom>(args)) {
1281             auto angle = consumeAngle(args, context.mode, UnitlessQuirk::Forbid);
1282             if (!angle)
1283                 return nullptr;
1284             result->setAngle(angle.releaseNonNull());
1285             expectComma = true;
1286         }
1287         
1288         if (consumeIdent<CSSValueAt>(args)) {
1289             RefPtr<CSSPrimitiveValue> centerX;
1290             RefPtr<CSSPrimitiveValue> centerY;
1291             consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY);
1292             if (!(centerX && centerY))
1293                 return nullptr;
1294             
1295             result->setFirstX(centerX.copyRef());
1296             result->setFirstY(centerY.copyRef());
1297             
1298             // Right now, conic gradients have the same start and end centers.
1299             result->setSecondX(centerX.copyRef());
1300             result->setSecondY(centerY.copyRef());
1301     
1302             expectComma = true;
1303         }
1304     }
1305
1306     if (expectComma && !consumeCommaIncludingWhitespace(args))
1307         return nullptr;
1308     if (!consumeGradientColorStops(args, context.mode, *result))
1309         return nullptr;
1310     return result;
1311 #else
1312     UNUSED_PARAM(args);
1313     UNUSED_PARAM(context);
1314     UNUSED_PARAM(repeating);
1315     return nullptr;
1316 #endif
1317 }
1318
1319 RefPtr<CSSValue> consumeImageOrNone(CSSParserTokenRange& range, CSSParserContext context)
1320 {
1321     if (range.peek().id() == CSSValueNone)
1322         return consumeIdent(range);
1323     return consumeImage(range, context);
1324 }
1325
1326 static RefPtr<CSSValue> consumeCrossFade(CSSParserTokenRange& args, CSSParserContext context, bool prefixed)
1327 {
1328     RefPtr<CSSValue> fromImageValue = consumeImageOrNone(args, context);
1329     if (!fromImageValue || !consumeCommaIncludingWhitespace(args))
1330         return nullptr;
1331     RefPtr<CSSValue> toImageValue = consumeImageOrNone(args, context);
1332     if (!toImageValue || !consumeCommaIncludingWhitespace(args))
1333         return nullptr;
1334
1335     RefPtr<CSSPrimitiveValue> percentage;
1336     if (auto percentValue = consumePercent(args, ValueRangeAll))
1337         percentage = CSSValuePool::singleton().createValue(clampTo<double>(percentValue->doubleValue() / 100.0, 0, 1), CSSPrimitiveValue::UnitType::CSS_NUMBER);
1338     else if (auto numberValue = consumeNumber(args, ValueRangeAll))
1339         percentage = CSSValuePool::singleton().createValue(clampTo<double>(numberValue->doubleValue(), 0, 1), CSSPrimitiveValue::UnitType::CSS_NUMBER);
1340
1341     if (!percentage)
1342         return nullptr;
1343     return CSSCrossfadeValue::create(fromImageValue.releaseNonNull(), toImageValue.releaseNonNull(), percentage.releaseNonNull(), prefixed);
1344 }
1345
1346 static RefPtr<CSSValue> consumeWebkitCanvas(CSSParserTokenRange& args)
1347 {
1348     if (args.peek().type() != IdentToken)
1349         return nullptr;
1350     auto canvasName = args.consumeIncludingWhitespace().value().toString();
1351     if (!args.atEnd())
1352         return nullptr;
1353     return CSSCanvasValue::create(canvasName);
1354 }
1355
1356 static RefPtr<CSSValue> consumeWebkitNamedImage(CSSParserTokenRange& args)
1357 {
1358     if (args.peek().type() != IdentToken)
1359         return nullptr;
1360     auto imageName = args.consumeIncludingWhitespace().value().toString();
1361     if (!args.atEnd())
1362         return nullptr;
1363     return CSSNamedImageValue::create(imageName);
1364 }
1365
1366 static RefPtr<CSSValue> consumeFilterImage(CSSParserTokenRange& args, const CSSParserContext& context)
1367 {
1368     auto imageValue = consumeImageOrNone(args, context);
1369     if (!imageValue || !consumeCommaIncludingWhitespace(args))
1370         return nullptr;
1371
1372     auto filterValue = consumeFilter(args, context, AllowedFilterFunctions::PixelFilters);
1373
1374     if (!filterValue)
1375         return nullptr;
1376
1377     if (!args.atEnd())
1378         return nullptr;
1379
1380     return CSSFilterImageValue::create(imageValue.releaseNonNull(), filterValue.releaseNonNull());
1381 }
1382
1383 #if ENABLE(CSS_PAINTING_API)
1384 static RefPtr<CSSValue> consumeCustomPaint(CSSParserTokenRange& args)
1385 {
1386     if (!RuntimeEnabledFeatures::sharedFeatures().cssPaintingAPIEnabled())
1387         return nullptr;
1388     if (args.peek().type() != IdentToken)
1389         return nullptr;
1390     auto name = args.consumeIncludingWhitespace().value().toString();
1391
1392     if (!args.atEnd() && args.peek() != CommaToken)
1393         return nullptr;
1394     if (!args.atEnd())
1395         args.consume();
1396
1397     auto argumentList = CSSVariableData::create(args);
1398
1399     while (!args.atEnd())
1400         args.consume();
1401
1402     return CSSPaintImageValue::create(name, WTFMove(argumentList));
1403 }
1404 #endif
1405
1406 static RefPtr<CSSValue> consumeGeneratedImage(CSSParserTokenRange& range, CSSParserContext context)
1407 {
1408     CSSValueID id = range.peek().functionId();
1409     CSSParserTokenRange rangeCopy = range;
1410     CSSParserTokenRange args = consumeFunction(rangeCopy);
1411     RefPtr<CSSValue> result;
1412     if (id == CSSValueRadialGradient)
1413         result = consumeRadialGradient(args, context.mode, NonRepeating);
1414     else if (id == CSSValueRepeatingRadialGradient)
1415         result = consumeRadialGradient(args, context.mode, Repeating);
1416     else if (id == CSSValueWebkitLinearGradient)
1417         result = consumeLinearGradient(args, context.mode, NonRepeating, CSSPrefixedLinearGradient);
1418     else if (id == CSSValueWebkitRepeatingLinearGradient)
1419         result = consumeLinearGradient(args, context.mode, Repeating, CSSPrefixedLinearGradient);
1420     else if (id == CSSValueRepeatingLinearGradient)
1421         result = consumeLinearGradient(args, context.mode, Repeating, CSSLinearGradient);
1422     else if (id == CSSValueLinearGradient)
1423         result = consumeLinearGradient(args, context.mode, NonRepeating, CSSLinearGradient);
1424     else if (id == CSSValueWebkitGradient)
1425         result = consumeDeprecatedGradient(args, context.mode);
1426     else if (id == CSSValueWebkitRadialGradient)
1427         result = consumeDeprecatedRadialGradient(args, context.mode, NonRepeating);
1428     else if (id == CSSValueWebkitRepeatingRadialGradient)
1429         result = consumeDeprecatedRadialGradient(args, context.mode, Repeating);
1430     else if (id == CSSValueConicGradient)
1431         result = consumeConicGradient(args, context, NonRepeating);
1432     else if (id == CSSValueRepeatingConicGradient)
1433         result = consumeConicGradient(args, context, Repeating);
1434     else if (id == CSSValueWebkitCrossFade || id == CSSValueCrossFade)
1435         result = consumeCrossFade(args, context, id == CSSValueWebkitCrossFade);
1436     else if (id == CSSValueWebkitCanvas)
1437         result = consumeWebkitCanvas(args);
1438     else if (id == CSSValueWebkitNamedImage)
1439         result = consumeWebkitNamedImage(args);
1440     else if (id == CSSValueWebkitFilter || id == CSSValueFilter)
1441         result = consumeFilterImage(args, context);
1442 #if ENABLE(CSS_PAINTING_API)
1443     else if (id == CSSValuePaint)
1444         result = consumeCustomPaint(args);
1445 #endif
1446     if (!result || !args.atEnd())
1447         return nullptr;
1448     range = rangeCopy;
1449     return result;
1450 }
1451
1452 static RefPtr<CSSValue> consumeImageSet(CSSParserTokenRange& range, const CSSParserContext& context)
1453 {
1454     CSSParserTokenRange rangeCopy = range;
1455     CSSParserTokenRange args = consumeFunction(rangeCopy);
1456     RefPtr<CSSImageSetValue> imageSet = CSSImageSetValue::create(context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No);
1457     do {
1458         AtomString urlValue = consumeUrlAsStringView(args).toAtomString();
1459         if (urlValue.isNull())
1460             return nullptr;
1461
1462         RefPtr<CSSValue> image = CSSImageValue::create(completeURL(context, urlValue), context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No);
1463         imageSet->append(image.releaseNonNull());
1464
1465         const CSSParserToken& token = args.consumeIncludingWhitespace();
1466         if (token.type() != DimensionToken)
1467             return nullptr;
1468         if (token.value() != "x")
1469             return nullptr;
1470         ASSERT(token.unitType() == CSSPrimitiveValue::UnitType::CSS_UNKNOWN);
1471         double imageScaleFactor = token.numericValue();
1472         if (imageScaleFactor <= 0)
1473             return nullptr;
1474         imageSet->append(CSSValuePool::singleton().createValue(imageScaleFactor, CSSPrimitiveValue::UnitType::CSS_NUMBER));
1475     } while (consumeCommaIncludingWhitespace(args));
1476     if (!args.atEnd())
1477         return nullptr;
1478     range = rangeCopy;
1479     return imageSet;
1480 }
1481
1482 static bool isGeneratedImage(CSSValueID id)
1483 {
1484     return id == CSSValueLinearGradient
1485         || id == CSSValueRadialGradient
1486         || id == CSSValueConicGradient
1487         || id == CSSValueRepeatingLinearGradient
1488         || id == CSSValueRepeatingRadialGradient
1489         || id == CSSValueRepeatingConicGradient
1490         || id == CSSValueWebkitLinearGradient
1491         || id == CSSValueWebkitRadialGradient
1492         || id == CSSValueWebkitRepeatingLinearGradient
1493         || id == CSSValueWebkitRepeatingRadialGradient
1494         || id == CSSValueWebkitGradient
1495         || id == CSSValueWebkitCrossFade
1496         || id == CSSValueWebkitCanvas
1497         || id == CSSValueCrossFade
1498         || id == CSSValueWebkitNamedImage
1499         || id == CSSValueWebkitFilter
1500 #if ENABLE(CSS_PAINTING_API)
1501         || id == CSSValuePaint
1502 #endif
1503         || id == CSSValueFilter;
1504 }
1505     
1506 static bool isPixelFilterFunction(CSSValueID filterFunction)
1507 {
1508     switch (filterFunction) {
1509     case CSSValueBlur:
1510     case CSSValueBrightness:
1511     case CSSValueContrast:
1512     case CSSValueDropShadow:
1513     case CSSValueGrayscale:
1514     case CSSValueHueRotate:
1515     case CSSValueInvert:
1516     case CSSValueOpacity:
1517     case CSSValueSaturate:
1518     case CSSValueSepia:
1519         return true;
1520     default:
1521         return false;
1522     }
1523 }
1524
1525 static bool isColorFilterFunction(CSSValueID filterFunction)
1526 {
1527     switch (filterFunction) {
1528     case CSSValueBrightness:
1529     case CSSValueContrast:
1530     case CSSValueGrayscale:
1531     case CSSValueHueRotate:
1532     case CSSValueInvert:
1533     case CSSValueOpacity:
1534     case CSSValueSaturate:
1535     case CSSValueSepia:
1536     case CSSValueAppleInvertLightness:
1537         return true;
1538     default:
1539         return false;
1540     }
1541 }
1542
1543 static bool allowsValuesGreaterThanOne(CSSValueID filterFunction)
1544 {
1545     switch (filterFunction) {
1546     case CSSValueBrightness:
1547     case CSSValueContrast:
1548     case CSSValueSaturate:
1549         return true;
1550     default:
1551         return false;
1552     }
1553 }
1554
1555 static RefPtr<CSSFunctionValue> consumeFilterFunction(CSSParserTokenRange& range, const CSSParserContext& context, AllowedFilterFunctions allowedFunctions)
1556 {
1557     CSSValueID filterType = range.peek().functionId();
1558     switch (allowedFunctions) {
1559     case AllowedFilterFunctions::PixelFilters:
1560         if (!isPixelFilterFunction(filterType))
1561             return nullptr;
1562         break;
1563     case AllowedFilterFunctions::ColorFilters:
1564         if (!isColorFilterFunction(filterType))
1565             return nullptr;
1566         break;
1567     }
1568
1569     CSSParserTokenRange args = consumeFunction(range);
1570     RefPtr<CSSFunctionValue> filterValue = CSSFunctionValue::create(filterType);
1571
1572     if (filterType == CSSValueAppleInvertLightness) {
1573         if (!args.atEnd())
1574             return nullptr;
1575         return filterValue;
1576     }
1577
1578     RefPtr<CSSValue> parsedValue;
1579
1580     if (filterType == CSSValueDropShadow)
1581         parsedValue = consumeSingleShadow(args, context.mode, false, false);
1582     else {
1583         if (args.atEnd())
1584             return filterValue;
1585
1586         if (filterType == CSSValueHueRotate)
1587             parsedValue = consumeAngle(args, context.mode, UnitlessQuirk::Forbid);
1588         else if (filterType == CSSValueBlur)
1589             parsedValue = consumeLength(args, HTMLStandardMode, ValueRangeNonNegative);
1590         else {
1591             parsedValue = consumePercent(args, ValueRangeNonNegative);
1592             if (!parsedValue)
1593                 parsedValue = consumeNumber(args, ValueRangeNonNegative);
1594             if (parsedValue && !allowsValuesGreaterThanOne(filterType)) {
1595                 bool isPercentage = downcast<CSSPrimitiveValue>(*parsedValue).isPercentage();
1596                 double maxAllowed = isPercentage ? 100.0 : 1.0;
1597                 if (downcast<CSSPrimitiveValue>(*parsedValue).doubleValue() > maxAllowed)
1598                     parsedValue = CSSPrimitiveValue::create(maxAllowed, isPercentage ? CSSPrimitiveValue::UnitType::CSS_PERCENTAGE : CSSPrimitiveValue::UnitType::CSS_NUMBER);
1599             }
1600         }
1601     }
1602     if (!parsedValue || !args.atEnd())
1603         return nullptr;
1604     filterValue->append(parsedValue.releaseNonNull());
1605     return filterValue;
1606 }
1607
1608 RefPtr<CSSValue> consumeFilter(CSSParserTokenRange& range, const CSSParserContext& context, AllowedFilterFunctions allowedFunctions)
1609 {
1610     if (range.peek().id() == CSSValueNone)
1611         return consumeIdent(range);
1612
1613     bool referenceFiltersAllowed = allowedFunctions == AllowedFilterFunctions::PixelFilters;
1614     auto list = CSSValueList::createSpaceSeparated();
1615     do {
1616         RefPtr<CSSValue> filterValue = referenceFiltersAllowed ? consumeUrl(range) : nullptr;
1617         if (!filterValue) {
1618             filterValue = consumeFilterFunction(range, context, allowedFunctions);
1619             if (!filterValue)
1620                 return nullptr;
1621         }
1622         list->append(filterValue.releaseNonNull());
1623     } while (!range.atEnd());
1624
1625     return list.ptr();
1626 }
1627
1628 RefPtr<CSSShadowValue> consumeSingleShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool allowInset, bool allowSpread)
1629 {
1630     RefPtr<CSSPrimitiveValue> style;
1631     RefPtr<CSSPrimitiveValue> color;
1632
1633     if (range.atEnd())
1634         return nullptr;
1635     if (range.peek().id() == CSSValueInset) {
1636         if (!allowInset)
1637             return nullptr;
1638         style = consumeIdent(range);
1639     }
1640     color = consumeColor(range, cssParserMode);
1641
1642     auto horizontalOffset = consumeLength(range, cssParserMode, ValueRangeAll);
1643     if (!horizontalOffset)
1644         return nullptr;
1645
1646     auto verticalOffset = consumeLength(range, cssParserMode, ValueRangeAll);
1647     if (!verticalOffset)
1648         return nullptr;
1649
1650     auto blurRadius = consumeLength(range, cssParserMode, ValueRangeAll);
1651     RefPtr<CSSPrimitiveValue> spreadDistance;
1652     if (blurRadius) {
1653         // Blur radius must be non-negative.
1654         if (blurRadius->doubleValue() < 0)
1655             return nullptr;
1656         if (allowSpread)
1657             spreadDistance = consumeLength(range, cssParserMode, ValueRangeAll);
1658     }
1659
1660     if (!range.atEnd()) {
1661         if (!color)
1662             color = consumeColor(range, cssParserMode);
1663         if (range.peek().id() == CSSValueInset) {
1664             if (!allowInset || style)
1665                 return nullptr;
1666             style = consumeIdent(range);
1667         }
1668     }
1669
1670     return CSSShadowValue::create(WTFMove(horizontalOffset), WTFMove(verticalOffset), WTFMove(blurRadius), WTFMove(spreadDistance), WTFMove(style), WTFMove(color));
1671 }
1672
1673 RefPtr<CSSValue> consumeImage(CSSParserTokenRange& range, CSSParserContext context, ConsumeGeneratedImage generatedImage)
1674 {
1675     AtomString uri = consumeUrlAsStringView(range).toAtomString();
1676     if (!uri.isNull())
1677         return CSSImageValue::create(completeURL(context, uri), context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No);
1678
1679     if (range.peek().type() == FunctionToken) {
1680         CSSValueID id = range.peek().functionId();
1681         if (id == CSSValueWebkitImageSet || id == CSSValueImageSet)
1682             return consumeImageSet(range, context);
1683         if (generatedImage == ConsumeGeneratedImage::Allow && isGeneratedImage(id))
1684             return consumeGeneratedImage(range, context);
1685     }
1686     return nullptr;
1687 }
1688
1689 } // namespace CSSPropertyParserHelpers
1690
1691 } // namespace WebCore