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