Unreviewed, rolling out r234489.
[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> consumeResolution(CSSParserTokenRange& range)
410 {
411     const CSSParserToken& token = range.peek();
412     // Unlike the other types, calc() does not work with <resolution>.
413     if (token.type() != DimensionToken)
414         return nullptr;
415     CSSPrimitiveValue::UnitType unit = token.unitType();
416     if (unit == CSSPrimitiveValue::UnitType::CSS_DPPX || unit == CSSPrimitiveValue::UnitType::CSS_DPI || unit == CSSPrimitiveValue::UnitType::CSS_DPCM)
417         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unit);
418     return nullptr;
419 }
420
421 RefPtr<CSSPrimitiveValue> consumeIdent(CSSParserTokenRange& range)
422 {
423     if (range.peek().type() != IdentToken)
424         return nullptr;
425     return CSSValuePool::singleton().createIdentifierValue(range.consumeIncludingWhitespace().id());
426 }
427
428 RefPtr<CSSPrimitiveValue> consumeIdentRange(CSSParserTokenRange& range, CSSValueID lower, CSSValueID upper)
429 {
430     if (range.peek().id() < lower || range.peek().id() > upper)
431         return nullptr;
432     return consumeIdent(range);
433 }
434
435 // FIXME-NEWPARSER: Eventually we'd like this to use CSSCustomIdentValue, but we need
436 // to do other plumbing work first (like changing Pair to CSSValuePair and make it not
437 // use only primitive values).
438 RefPtr<CSSPrimitiveValue> consumeCustomIdent(CSSParserTokenRange& range)
439 {
440     if (range.peek().type() != IdentToken || isCSSWideKeyword(range.peek().id()))
441         return nullptr;
442     return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
443 }
444
445 RefPtr<CSSPrimitiveValue> consumeString(CSSParserTokenRange& range)
446 {
447     if (range.peek().type() != StringToken)
448         return nullptr;
449     return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
450 }
451
452 StringView consumeUrlAsStringView(CSSParserTokenRange& range)
453 {
454     const CSSParserToken& token = range.peek();
455     if (token.type() == UrlToken) {
456         range.consumeIncludingWhitespace();
457         return token.value();
458     }
459     if (token.functionId() == CSSValueUrl) {
460         CSSParserTokenRange urlRange = range;
461         CSSParserTokenRange urlArgs = urlRange.consumeBlock();
462         const CSSParserToken& next = urlArgs.consumeIncludingWhitespace();
463         if (next.type() == BadStringToken || !urlArgs.atEnd())
464             return StringView();
465         ASSERT(next.type() == StringToken);
466         range = urlRange;
467         range.consumeWhitespace();
468         return next.value();
469     }
470
471     return StringView();
472 }
473
474 RefPtr<CSSPrimitiveValue> consumeUrl(CSSParserTokenRange& range)
475 {
476     StringView url = consumeUrlAsStringView(range);
477     if (url.isNull())
478         return nullptr;
479     return CSSValuePool::singleton().createValue(url.toString(), CSSPrimitiveValue::UnitType::CSS_URI);
480 }
481
482 static int clampRGBComponent(const CSSPrimitiveValue& value)
483 {
484     double result = value.doubleValue();
485     // FIXME: Multiply by 2.55 and round instead of floor.
486     if (value.isPercentage())
487         result *= 2.56;
488     return clampTo<int>(result, 0, 255);
489 }
490
491 static Color parseRGBParameters(CSSParserTokenRange& range, bool parseAlpha)
492 {
493     ASSERT(range.peek().functionId() == CSSValueRgb || range.peek().functionId() == CSSValueRgba);
494     Color result;
495     CSSParserTokenRange args = consumeFunction(range);
496     RefPtr<CSSPrimitiveValue> colorParameter = consumeInteger(args);
497     if (!colorParameter)
498         colorParameter = consumePercent(args, ValueRangeAll);
499     if (!colorParameter)
500         return Color();
501     const bool isPercent = colorParameter->isPercentage();
502     int colorArray[3];
503     colorArray[0] = clampRGBComponent(*colorParameter);
504     for (int i = 1; i < 3; i++) {
505         if (!consumeCommaIncludingWhitespace(args))
506             return Color();
507         colorParameter = isPercent ? consumePercent(args, ValueRangeAll) : consumeInteger(args);
508         if (!colorParameter)
509             return Color();
510         colorArray[i] = clampRGBComponent(*colorParameter);
511     }
512     if (parseAlpha) {
513         if (!consumeCommaIncludingWhitespace(args))
514             return Color();
515         double alpha;
516         if (!consumeNumberRaw(args, alpha))
517             return Color();
518         // Convert the floating pointer number of alpha to an integer in the range [0, 256),
519         // with an equal distribution across all 256 values.
520         int alphaComponent = static_cast<int>(clampTo<double>(alpha, 0.0, 1.0) * nextafter(256.0, 0.0));
521         result = Color(makeRGBA(colorArray[0], colorArray[1], colorArray[2], alphaComponent));
522     } else {
523         result = Color(makeRGB(colorArray[0], colorArray[1], colorArray[2]));
524     }
525
526     if (!args.atEnd())
527         return Color();
528
529     return result;
530 }
531
532 static Color parseHSLParameters(CSSParserTokenRange& range, CSSParserMode cssParserMode)
533 {
534     ASSERT(range.peek().functionId() == CSSValueHsl || range.peek().functionId() == CSSValueHsla);
535     CSSParserTokenRange args = consumeFunction(range);
536     auto hslValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
537     double angleInDegrees;
538     if (!hslValue) {
539         hslValue = consumeNumber(args, ValueRangeAll);
540         if (!hslValue)
541             return Color();
542         angleInDegrees = hslValue->doubleValue();
543     } else
544         angleInDegrees = hslValue->computeDegrees();
545     double colorArray[3];
546     // The hue needs to be in the range [0.0, 6.0) for calcHue()
547     colorArray[0] = fmod(fmod(angleInDegrees, 360.0) + 360.0, 360.0) / 60.0;
548     bool requiresCommas = false;
549     for (int i = 1; i < 3; i++) {
550         if (consumeCommaIncludingWhitespace(args)) {
551             if (i != 1 && !requiresCommas)
552                 return Color();
553             requiresCommas = true;
554         } else if (requiresCommas || args.atEnd() || (&args.peek() - 1)->type() != WhitespaceToken)
555             return Color();
556         hslValue = consumePercent(args, ValueRangeAll);
557         if (!hslValue)
558             return Color();
559         double doubleValue = hslValue->doubleValue();
560         colorArray[i] = clampTo<double>(doubleValue, 0.0, 100.0) / 100.0; // Needs to be value between 0 and 1.0.
561     }
562
563     double alpha = 1.0;
564     bool commaConsumed = consumeCommaIncludingWhitespace(args);
565     bool slashConsumed = consumeSlashIncludingWhitespace(args);
566     if ((commaConsumed && !requiresCommas) || (slashConsumed && requiresCommas))
567         return Color();
568     if (commaConsumed || slashConsumed) {
569         if (!consumeNumberRaw(args, alpha)) {
570             auto alphaPercent = consumePercent(args, ValueRangeAll);
571             if (!alphaPercent)
572                 return Color();
573             alpha = alphaPercent->doubleValue() / 100.0f;
574         }
575         alpha = clampTo<double>(alpha, 0.0, 1.0);
576     }
577
578     if (!args.atEnd())
579         return Color();
580
581     return Color(makeRGBAFromHSLA(colorArray[0], colorArray[1], colorArray[2], alpha));
582 }
583
584 static Color parseColorFunctionParameters(CSSParserTokenRange& range)
585 {
586     ASSERT(range.peek().functionId() == CSSValueColor);
587     CSSParserTokenRange args = consumeFunction(range);
588
589     ColorSpace colorSpace;
590     switch (args.peek().id()) {
591     case CSSValueSRGB:
592         colorSpace = ColorSpaceSRGB;
593         break;
594     case CSSValueDisplayP3:
595         colorSpace = ColorSpaceDisplayP3;
596         break;
597     default:
598         return Color();
599     }
600     consumeIdent(args);
601
602     double colorChannels[4] = { 0, 0, 0, 1 };
603     for (int i = 0; i < 3; ++i) {
604         double value;
605         if (consumeNumberRaw(args, value))
606             colorChannels[i] = std::max(0.0, std::min(1.0, value));
607         else
608             break;
609     }
610
611     if (consumeSlashIncludingWhitespace(args)) {
612         auto alphaParameter = consumePercent(args, ValueRangeAll);
613         if (!alphaParameter)
614             alphaParameter = consumeNumber(args, ValueRangeAll);
615         if (!alphaParameter)
616             return Color();
617
618         colorChannels[3] = std::max(0.0, std::min(1.0, alphaParameter->isPercentage() ? (alphaParameter->doubleValue() / 100) : alphaParameter->doubleValue()));
619     }
620
621     // FIXME: Support the comma-separated list of fallback color values.
622
623     if (!args.atEnd())
624         return Color();
625     
626     return Color(colorChannels[0], colorChannels[1], colorChannels[2], colorChannels[3], colorSpace);
627 }
628
629 static Color parseHexColor(CSSParserTokenRange& range, bool acceptQuirkyColors)
630 {
631     RGBA32 result;
632     const CSSParserToken& token = range.peek();
633     if (token.type() == HashToken) {
634         if (!Color::parseHexColor(token.value(), result))
635             return Color();
636     } else if (acceptQuirkyColors) {
637         String color;
638         if (token.type() == NumberToken || token.type() == DimensionToken) {
639             if (token.numericValueType() != IntegerValueType
640                 || token.numericValue() < 0. || token.numericValue() >= 1000000.)
641                 return Color();
642             if (token.type() == NumberToken) // e.g. 112233
643                 color = String::format("%d", static_cast<int>(token.numericValue()));
644             else // e.g. 0001FF
645                 color = String::number(static_cast<int>(token.numericValue())) + token.value().toString();
646             while (color.length() < 6)
647                 color = "0" + color;
648         } else if (token.type() == IdentToken) { // e.g. FF0000
649             color = token.value().toString();
650         }
651         unsigned length = color.length();
652         if (length != 3 && length != 6)
653             return Color();
654         if (!Color::parseHexColor(color, result))
655             return Color();
656     } else {
657         return Color();
658     }
659     range.consumeIncludingWhitespace();
660     return Color(result);
661 }
662
663 static Color parseColorFunction(CSSParserTokenRange& range, CSSParserMode cssParserMode)
664 {
665     CSSParserTokenRange colorRange = range;
666     CSSValueID functionId = range.peek().functionId();
667     Color color;
668     switch (functionId) {
669     case CSSValueRgb:
670     case CSSValueRgba:
671         color = parseRGBParameters(colorRange, functionId == CSSValueRgba);
672         break;
673     case CSSValueHsl:
674     case CSSValueHsla:
675         color = parseHSLParameters(colorRange, cssParserMode);
676         break;
677     case CSSValueColor:
678         color = parseColorFunctionParameters(colorRange);
679         break;
680     default:
681         return Color();
682     }
683     range = colorRange;
684     return color;
685 }
686
687 RefPtr<CSSPrimitiveValue> consumeColor(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool acceptQuirkyColors)
688 {
689     CSSValueID id = range.peek().id();
690     if (StyleColor::isColorKeyword(id)) {
691         if (!isValueAllowedInMode(id, cssParserMode))
692             return nullptr;
693         return consumeIdent(range);
694     }
695     Color color = parseHexColor(range, acceptQuirkyColors);
696     if (!color.isValid())
697         color = parseColorFunction(range, cssParserMode);
698     if (!color.isValid())
699         return nullptr;
700     return CSSValuePool::singleton().createValue(color);
701 }
702
703 static RefPtr<CSSPrimitiveValue> consumePositionComponent(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
704 {
705     if (range.peek().type() == IdentToken)
706         return consumeIdent<CSSValueLeft, CSSValueTop, CSSValueBottom, CSSValueRight, CSSValueCenter>(range);
707     return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, unitless);
708 }
709
710 static bool isHorizontalPositionKeywordOnly(const CSSPrimitiveValue& value)
711 {
712     return value.isValueID() && (value.valueID() == CSSValueLeft || value.valueID() == CSSValueRight);
713 }
714
715 static bool isVerticalPositionKeywordOnly(const CSSPrimitiveValue& value)
716 {
717     return value.isValueID() && (value.valueID() == CSSValueTop || value.valueID() == CSSValueBottom);
718 }
719
720 static void positionFromOneValue(CSSPrimitiveValue& value, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
721 {
722     bool valueAppliesToYAxisOnly = isVerticalPositionKeywordOnly(value);
723     resultX = &value;
724     resultY = CSSPrimitiveValue::createIdentifier(CSSValueCenter);
725     if (valueAppliesToYAxisOnly)
726         std::swap(resultX, resultY);
727 }
728
729 static bool positionFromTwoValues(CSSPrimitiveValue& value1, CSSPrimitiveValue& value2,
730     RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
731 {
732     bool mustOrderAsXY = isHorizontalPositionKeywordOnly(value1) || isVerticalPositionKeywordOnly(value2)
733         || !value1.isValueID() || !value2.isValueID();
734     bool mustOrderAsYX = isVerticalPositionKeywordOnly(value1) || isHorizontalPositionKeywordOnly(value2);
735     if (mustOrderAsXY && mustOrderAsYX)
736         return false;
737     resultX = &value1;
738     resultY = &value2;
739     if (mustOrderAsYX)
740         std::swap(resultX, resultY);
741     return true;
742 }
743
744 namespace CSSPropertyParserHelpersInternal {
745 template<typename... Args>
746 static Ref<CSSPrimitiveValue> createPrimitiveValuePair(Args&&... args)
747 {
748     return CSSValuePool::singleton().createValue(Pair::create(std::forward<Args>(args)...));
749 }
750 }
751
752 static bool positionFromThreeOrFourValues(CSSPrimitiveValue** values, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
753 {
754     CSSPrimitiveValue* center = nullptr;
755     for (int i = 0; values[i]; i++) {
756         CSSPrimitiveValue* currentValue = values[i];
757         if (!currentValue->isValueID())
758             return false;
759         CSSValueID id = currentValue->valueID();
760
761         if (id == CSSValueCenter) {
762             if (center)
763                 return false;
764             center = currentValue;
765             continue;
766         }
767
768         RefPtr<CSSPrimitiveValue> result;
769         if (values[i + 1] && !values[i + 1]->isValueID())
770             result = CSSPropertyParserHelpersInternal::createPrimitiveValuePair(currentValue, values[++i]);
771         else
772             result = currentValue;
773
774         if (id == CSSValueLeft || id == CSSValueRight) {
775             if (resultX)
776                 return false;
777             resultX = result;
778         } else {
779             ASSERT(id == CSSValueTop || id == CSSValueBottom);
780             if (resultY)
781                 return false;
782             resultY = result;
783         }
784     }
785
786     if (center) {
787         ASSERT(resultX || resultY);
788         if (resultX && resultY)
789             return false;
790         if (!resultX)
791             resultX = center;
792         else
793             resultY = center;
794     }
795
796     ASSERT(resultX && resultY);
797     return true;
798 }
799
800 // FIXME: This may consume from the range upon failure. The background
801 // shorthand works around it, but we should just fix it here.
802 bool consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
803 {
804     RefPtr<CSSPrimitiveValue> value1 = consumePositionComponent(range, cssParserMode, unitless);
805     if (!value1)
806         return false;
807
808     RefPtr<CSSPrimitiveValue> value2 = consumePositionComponent(range, cssParserMode, unitless);
809     if (!value2) {
810         positionFromOneValue(*value1, resultX, resultY);
811         return true;
812     }
813
814     RefPtr<CSSPrimitiveValue> value3 = consumePositionComponent(range, cssParserMode, unitless);
815     if (!value3)
816         return positionFromTwoValues(*value1, *value2, resultX, resultY);
817
818     RefPtr<CSSPrimitiveValue> value4 = consumePositionComponent(range, cssParserMode, unitless);
819     CSSPrimitiveValue* values[5];
820     values[0] = value1.get();
821     values[1] = value2.get();
822     values[2] = value3.get();
823     values[3] = value4.get();
824     values[4] = nullptr;
825     return positionFromThreeOrFourValues(values, resultX, resultY);
826 }
827
828 RefPtr<CSSPrimitiveValue> consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
829 {
830     RefPtr<CSSPrimitiveValue> resultX;
831     RefPtr<CSSPrimitiveValue> resultY;
832     if (consumePosition(range, cssParserMode, unitless, resultX, resultY))
833         return CSSPropertyParserHelpersInternal::createPrimitiveValuePair(resultX.releaseNonNull(), resultY.releaseNonNull());
834     return nullptr;
835 }
836
837 bool consumeOneOrTwoValuedPosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
838 {
839     RefPtr<CSSPrimitiveValue> value1 = consumePositionComponent(range, cssParserMode, unitless);
840     if (!value1)
841         return false;
842     RefPtr<CSSPrimitiveValue> value2 = consumePositionComponent(range, cssParserMode, unitless);
843     if (!value2) {
844         positionFromOneValue(*value1, resultX, resultY);
845         return true;
846     }
847     return positionFromTwoValues(*value1, *value2, resultX, resultY);
848 }
849
850 // This should go away once we drop support for -webkit-gradient
851 static RefPtr<CSSPrimitiveValue> consumeDeprecatedGradientPoint(CSSParserTokenRange& args, bool horizontal)
852 {
853     if (args.peek().type() == IdentToken) {
854         if ((horizontal && consumeIdent<CSSValueLeft>(args)) || (!horizontal && consumeIdent<CSSValueTop>(args)))
855             return CSSValuePool::singleton().createValue(0., CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
856         if ((horizontal && consumeIdent<CSSValueRight>(args)) || (!horizontal && consumeIdent<CSSValueBottom>(args)))
857             return CSSValuePool::singleton().createValue(100., CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
858         if (consumeIdent<CSSValueCenter>(args))
859             return CSSValuePool::singleton().createValue(50., CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
860         return nullptr;
861     }
862     RefPtr<CSSPrimitiveValue> result = consumePercent(args, ValueRangeAll);
863     if (!result)
864         result = consumeNumber(args, ValueRangeAll);
865     return result;
866 }
867
868 // Used to parse colors for -webkit-gradient(...).
869 static RefPtr<CSSPrimitiveValue> consumeDeprecatedGradientStopColor(CSSParserTokenRange& args, CSSParserMode cssParserMode)
870 {
871     if (args.peek().id() == CSSValueCurrentcolor)
872         return nullptr;
873     return consumeColor(args, cssParserMode);
874 }
875
876 static bool consumeDeprecatedGradientColorStop(CSSParserTokenRange& range, CSSGradientColorStop& stop, CSSParserMode cssParserMode)
877 {
878     CSSValueID id = range.peek().functionId();
879     if (id != CSSValueFrom && id != CSSValueTo && id != CSSValueColorStop)
880         return false;
881
882     CSSParserTokenRange args = consumeFunction(range);
883     double position;
884     if (id == CSSValueFrom || id == CSSValueTo) {
885         position = (id == CSSValueFrom) ? 0 : 1;
886     } else {
887         ASSERT(id == CSSValueColorStop);
888         if (auto percentValue = consumePercent(args, ValueRangeAll))
889             position = percentValue->doubleValue() / 100.0;
890         else if (!consumeNumberRaw(args, position))
891             return false;
892
893         if (!consumeCommaIncludingWhitespace(args))
894             return false;
895     }
896
897     stop.m_position = CSSValuePool::singleton().createValue(position, CSSPrimitiveValue::UnitType::CSS_NUMBER);
898     stop.m_color = consumeDeprecatedGradientStopColor(args, cssParserMode);
899     return stop.m_color && args.atEnd();
900 }
901
902 static RefPtr<CSSValue> consumeDeprecatedGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode)
903 {
904     RefPtr<CSSGradientValue> result;
905     CSSValueID id = args.consumeIncludingWhitespace().id();
906     bool isDeprecatedRadialGradient = (id == CSSValueRadial);
907     if (isDeprecatedRadialGradient)
908         result = CSSRadialGradientValue::create(NonRepeating, CSSDeprecatedRadialGradient);
909     else if (id == CSSValueLinear)
910         result = CSSLinearGradientValue::create(NonRepeating, CSSDeprecatedLinearGradient);
911     if (!result || !consumeCommaIncludingWhitespace(args))
912         return nullptr;
913
914     RefPtr<CSSPrimitiveValue> point = consumeDeprecatedGradientPoint(args, true);
915     if (!point)
916         return nullptr;
917     result->setFirstX(point.copyRef());
918     point = consumeDeprecatedGradientPoint(args, false);
919     if (!point)
920         return nullptr;
921     result->setFirstY(point.copyRef());
922
923     if (!consumeCommaIncludingWhitespace(args))
924         return nullptr;
925
926     // For radial gradients only, we now expect a numeric radius.
927     if (isDeprecatedRadialGradient) {
928         RefPtr<CSSPrimitiveValue> radius = consumeNumber(args, ValueRangeAll);
929         if (!radius || !consumeCommaIncludingWhitespace(args))
930             return nullptr;
931         downcast<CSSRadialGradientValue>(result.get())->setFirstRadius(radius.copyRef());
932     }
933
934     point = consumeDeprecatedGradientPoint(args, true);
935     if (!point)
936         return nullptr;
937     result->setSecondX(point.copyRef());
938     point = consumeDeprecatedGradientPoint(args, false);
939     if (!point)
940         return nullptr;
941     result->setSecondY(point.copyRef());
942
943     // For radial gradients only, we now expect the second radius.
944     if (isDeprecatedRadialGradient) {
945         if (!consumeCommaIncludingWhitespace(args))
946             return nullptr;
947         RefPtr<CSSPrimitiveValue> radius = consumeNumber(args, ValueRangeAll);
948         if (!radius)
949             return nullptr;
950         downcast<CSSRadialGradientValue>(result.get())->setSecondRadius(radius.copyRef());
951     }
952
953     CSSGradientColorStop stop;
954     while (consumeCommaIncludingWhitespace(args)) {
955         if (!consumeDeprecatedGradientColorStop(args, stop, cssParserMode))
956             return nullptr;
957         result->addStop(stop);
958     }
959
960     result->doneAddingStops();
961     return result;
962 }
963
964 static bool consumeGradientColorStops(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSGradientValue& gradient)
965 {
966     bool supportsColorHints = gradient.gradientType() == CSSLinearGradient || gradient.gradientType() == CSSRadialGradient;
967
968     // The first color stop cannot be a color hint.
969     bool previousStopWasColorHint = true;
970     do {
971         CSSGradientColorStop stop;
972         stop.m_color = consumeColor(range, cssParserMode);
973         // Two hints in a row are not allowed.
974         if (!stop.m_color && (!supportsColorHints || previousStopWasColorHint))
975             return false;
976         
977         previousStopWasColorHint = !stop.m_color;
978         
979         // FIXME-NEWPARSER: This boolean could be removed. Null checking color would be sufficient.
980         stop.isMidpoint = !stop.m_color;
981
982         stop.m_position = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
983         if (!stop.m_color && !stop.m_position)
984             return false;
985         gradient.addStop(stop);
986     } while (consumeCommaIncludingWhitespace(range));
987
988     gradient.doneAddingStops();
989
990     // The last color stop cannot be a color hint.
991     if (previousStopWasColorHint)
992         return false;
993
994     // Must have 2 or more stops to be valid.
995     return gradient.stopCount() >= 2;
996 }
997
998 // https://www.w3.org/TR/css-images-4/#typedef-angular-color-stop-list
999 // FIXME: This should support up to two position hints per color stop.
1000 static bool consumeAngularGradientColorStops(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSGradientValue& gradient)
1001 {
1002     // The first color stop cannot be a color hint.
1003     bool previousStopWasColorHint = true;
1004     do {
1005         CSSGradientColorStop stop;
1006         stop.m_color = consumeColor(range, cssParserMode);
1007
1008         // Two hints in a row are not allowed.
1009         if (!stop.m_color && previousStopWasColorHint)
1010             return false;
1011         
1012         previousStopWasColorHint = !stop.m_color;
1013         
1014         // FIXME-NEWPARSER: This boolean could be removed. Null checking color would be sufficient.
1015         stop.isMidpoint = !stop.m_color;
1016
1017         stop.m_position = consumeAngleOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid);
1018         if (!stop.m_color && !stop.m_position)
1019             return false;
1020         gradient.addStop(stop);
1021     } while (consumeCommaIncludingWhitespace(range));
1022
1023     gradient.doneAddingStops();
1024
1025     // The last color stop cannot be a color hint.
1026     if (previousStopWasColorHint)
1027         return false;
1028
1029     // Must have 2 or more stops to be valid.
1030     return gradient.stopCount() >= 2;
1031 }
1032
1033 static RefPtr<CSSValue> consumeDeprecatedRadialGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating)
1034 {
1035     RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSPrefixedRadialGradient);
1036     RefPtr<CSSPrimitiveValue> centerX;
1037     RefPtr<CSSPrimitiveValue> centerY;
1038     consumeOneOrTwoValuedPosition(args, cssParserMode, UnitlessQuirk::Forbid, centerX, centerY);
1039     if ((centerX || centerY) && !consumeCommaIncludingWhitespace(args))
1040         return nullptr;
1041
1042     result->setFirstX(centerX.copyRef());
1043     result->setFirstY(centerY.copyRef());
1044     result->setSecondX(centerX.copyRef());
1045     result->setSecondY(centerY.copyRef());
1046
1047     RefPtr<CSSPrimitiveValue> shape = consumeIdent<CSSValueCircle, CSSValueEllipse>(args);
1048     RefPtr<CSSPrimitiveValue> sizeKeyword = consumeIdent<CSSValueClosestSide, CSSValueClosestCorner, CSSValueFarthestSide, CSSValueFarthestCorner, CSSValueContain, CSSValueCover>(args);
1049     if (!shape)
1050         shape = consumeIdent<CSSValueCircle, CSSValueEllipse>(args);
1051     result->setShape(shape.copyRef());
1052     result->setSizingBehavior(sizeKeyword.copyRef());
1053
1054     // Or, two lengths or percentages
1055     if (!shape && !sizeKeyword) {
1056         RefPtr<CSSPrimitiveValue> horizontalSize = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1057         RefPtr<CSSPrimitiveValue> verticalSize;
1058         if (horizontalSize) {
1059             verticalSize = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1060             if (!verticalSize)
1061                 return nullptr;
1062             consumeCommaIncludingWhitespace(args);
1063             result->setEndHorizontalSize(horizontalSize.copyRef());
1064             result->setEndVerticalSize(verticalSize.copyRef());
1065         }
1066     } else {
1067         consumeCommaIncludingWhitespace(args);
1068     }
1069     if (!consumeGradientColorStops(args, cssParserMode, *result))
1070         return nullptr;
1071
1072     return result;
1073 }
1074
1075 static RefPtr<CSSValue> consumeRadialGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating)
1076 {
1077     RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient);
1078
1079     RefPtr<CSSPrimitiveValue> shape;
1080     RefPtr<CSSPrimitiveValue> sizeKeyword;
1081     RefPtr<CSSPrimitiveValue> horizontalSize;
1082     RefPtr<CSSPrimitiveValue> verticalSize;
1083
1084     // First part of grammar, the size/shape clause:
1085     // [ circle || <length> ] |
1086     // [ ellipse || [ <length> | <percentage> ]{2} ] |
1087     // [ [ circle | ellipse] || <size-keyword> ]
1088     for (int i = 0; i < 3; ++i) {
1089         if (args.peek().type() == IdentToken) {
1090             CSSValueID id = args.peek().id();
1091             if (id == CSSValueCircle || id == CSSValueEllipse) {
1092                 if (shape)
1093                     return nullptr;
1094                 shape = consumeIdent(args);
1095             } else if (id == CSSValueClosestSide || id == CSSValueClosestCorner || id == CSSValueFarthestSide || id == CSSValueFarthestCorner) {
1096                 if (sizeKeyword)
1097                     return nullptr;
1098                 sizeKeyword = consumeIdent(args);
1099             } else {
1100                 break;
1101             }
1102         } else {
1103             RefPtr<CSSPrimitiveValue> center = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1104             if (!center)
1105                 break;
1106             if (horizontalSize)
1107                 return nullptr;
1108             horizontalSize = center;
1109             center = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1110             if (center) {
1111                 verticalSize = center;
1112                 ++i;
1113             }
1114         }
1115     }
1116
1117     // You can specify size as a keyword or a length/percentage, not both.
1118     if (sizeKeyword && horizontalSize)
1119         return nullptr;
1120     // Circles must have 0 or 1 lengths.
1121     if (shape && shape->valueID() == CSSValueCircle && verticalSize)
1122         return nullptr;
1123     // Ellipses must have 0 or 2 length/percentages.
1124     if (shape && shape->valueID() == CSSValueEllipse && horizontalSize && !verticalSize)
1125         return nullptr;
1126     // If there's only one size, it must be a length.
1127     if (!verticalSize && horizontalSize && horizontalSize->isPercentage())
1128         return nullptr;
1129     if ((horizontalSize && horizontalSize->isCalculatedPercentageWithLength())
1130         || (verticalSize && verticalSize->isCalculatedPercentageWithLength()))
1131         return nullptr;
1132
1133     result->setShape(shape.copyRef());
1134     result->setSizingBehavior(sizeKeyword.copyRef());
1135     result->setEndHorizontalSize(horizontalSize.copyRef());
1136     result->setEndVerticalSize(verticalSize.copyRef());
1137
1138     RefPtr<CSSPrimitiveValue> centerX;
1139     RefPtr<CSSPrimitiveValue> centerY;
1140     if (args.peek().id() == CSSValueAt) {
1141         args.consumeIncludingWhitespace();
1142         consumePosition(args, cssParserMode, UnitlessQuirk::Forbid, centerX, centerY);
1143         if (!(centerX && centerY))
1144             return nullptr;
1145         
1146         result->setFirstX(centerX.copyRef());
1147         result->setFirstY(centerY.copyRef());
1148         
1149         // Right now, CSS radial gradients have the same start and end centers.
1150         result->setSecondX(centerX.copyRef());
1151         result->setSecondY(centerY.copyRef());
1152     }
1153
1154     if ((shape || sizeKeyword || horizontalSize || centerX || centerY) && !consumeCommaIncludingWhitespace(args))
1155         return nullptr;
1156     if (!consumeGradientColorStops(args, cssParserMode, *result))
1157         return nullptr;
1158     return result;
1159 }
1160
1161 static RefPtr<CSSValue> consumeLinearGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating, CSSGradientType gradientType)
1162 {
1163     RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, gradientType);
1164
1165     bool expectComma = true;
1166     RefPtr<CSSPrimitiveValue> angle = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1167     if (angle)
1168         result->setAngle(angle.releaseNonNull());
1169     else if (gradientType == CSSPrefixedLinearGradient || consumeIdent<CSSValueTo>(args)) {
1170         RefPtr<CSSPrimitiveValue> endX = consumeIdent<CSSValueLeft, CSSValueRight>(args);
1171         RefPtr<CSSPrimitiveValue> endY = consumeIdent<CSSValueBottom, CSSValueTop>(args);
1172         if (!endX && !endY) {
1173             if (gradientType == CSSLinearGradient)
1174                 return nullptr;
1175             endY = CSSPrimitiveValue::createIdentifier(CSSValueTop);
1176             expectComma = false;
1177         } else if (!endX) {
1178             endX = consumeIdent<CSSValueLeft, CSSValueRight>(args);
1179         }
1180
1181         result->setFirstX(endX.copyRef());
1182         result->setFirstY(endY.copyRef());
1183     } else {
1184         expectComma = false;
1185     }
1186
1187     if (expectComma && !consumeCommaIncludingWhitespace(args))
1188         return nullptr;
1189     if (!consumeGradientColorStops(args, cssParserMode, *result))
1190         return nullptr;
1191     return result;
1192 }
1193
1194 static RefPtr<CSSValue> consumeConicGradient(CSSParserTokenRange& args, CSSParserContext context, CSSGradientRepeat repeating)
1195 {
1196     if (!context.conicGradientsEnabled)
1197         return nullptr;
1198
1199     RefPtr<CSSConicGradientValue> result = CSSConicGradientValue::create(repeating);
1200
1201     bool expectComma = false;
1202     if (args.peek().type() == IdentToken) {
1203         if (consumeIdent<CSSValueFrom>(args)) {
1204             auto angle = consumeAngle(args, context.mode, UnitlessQuirk::Forbid);
1205             if (!angle)
1206                 return nullptr;
1207             result->setAngle(angle.releaseNonNull());
1208             expectComma = true;
1209         }
1210         
1211         if (consumeIdent<CSSValueAt>(args)) {
1212             RefPtr<CSSPrimitiveValue> centerX;
1213             RefPtr<CSSPrimitiveValue> centerY;
1214             consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY);
1215             if (!(centerX && centerY))
1216                 return nullptr;
1217             
1218             result->setFirstX(centerX.copyRef());
1219             result->setFirstY(centerY.copyRef());
1220             
1221             // Right now, conic gradients have the same start and end centers.
1222             result->setSecondX(centerX.copyRef());
1223             result->setSecondY(centerY.copyRef());
1224     
1225             expectComma = true;
1226         }
1227     }
1228
1229     if (expectComma && !consumeCommaIncludingWhitespace(args))
1230         return nullptr;
1231     if (!consumeAngularGradientColorStops(args, context.mode, *result))
1232         return nullptr;
1233     return result;
1234 }
1235
1236 RefPtr<CSSValue> consumeImageOrNone(CSSParserTokenRange& range, CSSParserContext context)
1237 {
1238     if (range.peek().id() == CSSValueNone)
1239         return consumeIdent(range);
1240     return consumeImage(range, context);
1241 }
1242
1243 static RefPtr<CSSValue> consumeCrossFade(CSSParserTokenRange& args, CSSParserContext context, bool prefixed)
1244 {
1245     RefPtr<CSSValue> fromImageValue = consumeImageOrNone(args, context);
1246     if (!fromImageValue || !consumeCommaIncludingWhitespace(args))
1247         return nullptr;
1248     RefPtr<CSSValue> toImageValue = consumeImageOrNone(args, context);
1249     if (!toImageValue || !consumeCommaIncludingWhitespace(args))
1250         return nullptr;
1251
1252     RefPtr<CSSPrimitiveValue> percentage;
1253     if (auto percentValue = consumePercent(args, ValueRangeAll))
1254         percentage = CSSValuePool::singleton().createValue(clampTo<double>(percentValue->doubleValue() / 100.0, 0, 1), CSSPrimitiveValue::UnitType::CSS_NUMBER);
1255     else if (auto numberValue = consumeNumber(args, ValueRangeAll))
1256         percentage = CSSValuePool::singleton().createValue(clampTo<double>(numberValue->doubleValue(), 0, 1), CSSPrimitiveValue::UnitType::CSS_NUMBER);
1257
1258     if (!percentage)
1259         return nullptr;
1260     return CSSCrossfadeValue::create(fromImageValue.releaseNonNull(), toImageValue.releaseNonNull(), percentage.releaseNonNull(), prefixed);
1261 }
1262
1263 static RefPtr<CSSValue> consumeWebkitCanvas(CSSParserTokenRange& args)
1264 {
1265     if (args.peek().type() != IdentToken)
1266         return nullptr;
1267     auto canvasName = args.consumeIncludingWhitespace().value().toString();
1268     if (!args.atEnd())
1269         return nullptr;
1270     return CSSCanvasValue::create(canvasName);
1271 }
1272
1273 static RefPtr<CSSValue> consumeWebkitNamedImage(CSSParserTokenRange& args)
1274 {
1275     if (args.peek().type() != IdentToken)
1276         return nullptr;
1277     auto imageName = args.consumeIncludingWhitespace().value().toString();
1278     if (!args.atEnd())
1279         return nullptr;
1280     return CSSNamedImageValue::create(imageName);
1281 }
1282
1283 static RefPtr<CSSValue> consumeFilterImage(CSSParserTokenRange& args, const CSSParserContext& context)
1284 {
1285     auto imageValue = consumeImageOrNone(args, context);
1286     if (!imageValue || !consumeCommaIncludingWhitespace(args))
1287         return nullptr;
1288
1289     auto filterValue = consumeFilter(args, context, AllowedFilterFunctions::PixelFilters);
1290
1291     if (!filterValue)
1292         return nullptr;
1293
1294     if (!args.atEnd())
1295         return nullptr;
1296
1297     return CSSFilterImageValue::create(imageValue.releaseNonNull(), filterValue.releaseNonNull());
1298 }
1299
1300 static RefPtr<CSSValue> consumeGeneratedImage(CSSParserTokenRange& range, CSSParserContext context)
1301 {
1302     CSSValueID id = range.peek().functionId();
1303     CSSParserTokenRange rangeCopy = range;
1304     CSSParserTokenRange args = consumeFunction(rangeCopy);
1305     RefPtr<CSSValue> result;
1306     if (id == CSSValueRadialGradient)
1307         result = consumeRadialGradient(args, context.mode, NonRepeating);
1308     else if (id == CSSValueRepeatingRadialGradient)
1309         result = consumeRadialGradient(args, context.mode, Repeating);
1310     else if (id == CSSValueWebkitLinearGradient)
1311         result = consumeLinearGradient(args, context.mode, NonRepeating, CSSPrefixedLinearGradient);
1312     else if (id == CSSValueWebkitRepeatingLinearGradient)
1313         result = consumeLinearGradient(args, context.mode, Repeating, CSSPrefixedLinearGradient);
1314     else if (id == CSSValueRepeatingLinearGradient)
1315         result = consumeLinearGradient(args, context.mode, Repeating, CSSLinearGradient);
1316     else if (id == CSSValueLinearGradient)
1317         result = consumeLinearGradient(args, context.mode, NonRepeating, CSSLinearGradient);
1318     else if (id == CSSValueWebkitGradient)
1319         result = consumeDeprecatedGradient(args, context.mode);
1320     else if (id == CSSValueWebkitRadialGradient)
1321         result = consumeDeprecatedRadialGradient(args, context.mode, NonRepeating);
1322     else if (id == CSSValueWebkitRepeatingRadialGradient)
1323         result = consumeDeprecatedRadialGradient(args, context.mode, Repeating);
1324     else if (id == CSSValueConicGradient)
1325         result = consumeConicGradient(args, context, NonRepeating);
1326     else if (id == CSSValueRepeatingConicGradient)
1327         result = consumeConicGradient(args, context, Repeating);
1328     else if (id == CSSValueWebkitCrossFade || id == CSSValueCrossFade)
1329         result = consumeCrossFade(args, context, id == CSSValueWebkitCrossFade);
1330     else if (id == CSSValueWebkitCanvas)
1331         result = consumeWebkitCanvas(args);
1332     else if (id == CSSValueWebkitNamedImage)
1333         result = consumeWebkitNamedImage(args);
1334     else if (id == CSSValueWebkitFilter || id == CSSValueFilter)
1335         result = consumeFilterImage(args, context);
1336     if (!result || !args.atEnd())
1337         return nullptr;
1338     range = rangeCopy;
1339     return result;
1340 }
1341
1342 static RefPtr<CSSValue> consumeImageSet(CSSParserTokenRange& range, const CSSParserContext& context)
1343 {
1344     CSSParserTokenRange rangeCopy = range;
1345     CSSParserTokenRange args = consumeFunction(rangeCopy);
1346     RefPtr<CSSImageSetValue> imageSet = CSSImageSetValue::create();
1347     do {
1348         AtomicString urlValue = consumeUrlAsStringView(args).toAtomicString();
1349         if (urlValue.isNull())
1350             return nullptr;
1351
1352         RefPtr<CSSValue> image = CSSImageValue::create(completeURL(context, urlValue));
1353         imageSet->append(image.releaseNonNull());
1354
1355         const CSSParserToken& token = args.consumeIncludingWhitespace();
1356         if (token.type() != DimensionToken)
1357             return nullptr;
1358         if (token.value() != "x")
1359             return nullptr;
1360         ASSERT(token.unitType() == CSSPrimitiveValue::UnitType::CSS_UNKNOWN);
1361         double imageScaleFactor = token.numericValue();
1362         if (imageScaleFactor <= 0)
1363             return nullptr;
1364         imageSet->append(CSSValuePool::singleton().createValue(imageScaleFactor, CSSPrimitiveValue::UnitType::CSS_NUMBER));
1365     } while (consumeCommaIncludingWhitespace(args));
1366     if (!args.atEnd())
1367         return nullptr;
1368     range = rangeCopy;
1369     return imageSet;
1370 }
1371
1372 static bool isGeneratedImage(CSSValueID id)
1373 {
1374     return id == CSSValueLinearGradient
1375         || id == CSSValueRadialGradient
1376         || id == CSSValueConicGradient
1377         || id == CSSValueRepeatingLinearGradient
1378         || id == CSSValueRepeatingRadialGradient
1379         || id == CSSValueRepeatingConicGradient
1380         || id == CSSValueWebkitLinearGradient
1381         || id == CSSValueWebkitRadialGradient
1382         || id == CSSValueWebkitRepeatingLinearGradient
1383         || id == CSSValueWebkitRepeatingRadialGradient
1384         || id == CSSValueWebkitGradient
1385         || id == CSSValueWebkitCrossFade
1386         || id == CSSValueWebkitCanvas
1387         || id == CSSValueCrossFade
1388         || id == CSSValueWebkitNamedImage
1389         || id == CSSValueWebkitFilter
1390         || id == CSSValueFilter;
1391 }
1392     
1393 static bool isPixelFilterFunction(CSSValueID filterFunction)
1394 {
1395     switch (filterFunction) {
1396     case CSSValueBlur:
1397     case CSSValueBrightness:
1398     case CSSValueContrast:
1399     case CSSValueDropShadow:
1400     case CSSValueGrayscale:
1401     case CSSValueHueRotate:
1402     case CSSValueInvert:
1403     case CSSValueOpacity:
1404     case CSSValueSaturate:
1405     case CSSValueSepia:
1406         return true;
1407     default:
1408         return false;
1409     }
1410 }
1411
1412 static bool isColorFilterFunction(CSSValueID filterFunction)
1413 {
1414     switch (filterFunction) {
1415     case CSSValueBrightness:
1416     case CSSValueContrast:
1417     case CSSValueGrayscale:
1418     case CSSValueHueRotate:
1419     case CSSValueInvert:
1420     case CSSValueOpacity:
1421     case CSSValueSaturate:
1422     case CSSValueSepia:
1423     case CSSValueAppleInvertLightness:
1424         return true;
1425     default:
1426         return false;
1427     }
1428 }
1429
1430 static bool allowsValuesGreaterThanOne(CSSValueID filterFunction)
1431 {
1432     switch (filterFunction) {
1433     case CSSValueBrightness:
1434     case CSSValueContrast:
1435     case CSSValueSaturate:
1436         return true;
1437     default:
1438         return false;
1439     }
1440 }
1441
1442 static RefPtr<CSSFunctionValue> consumeFilterFunction(CSSParserTokenRange& range, const CSSParserContext& context, AllowedFilterFunctions allowedFunctions)
1443 {
1444     CSSValueID filterType = range.peek().functionId();
1445     switch (allowedFunctions) {
1446     case AllowedFilterFunctions::PixelFilters:
1447         if (!isPixelFilterFunction(filterType))
1448             return nullptr;
1449         break;
1450     case AllowedFilterFunctions::ColorFilters:
1451         if (!isColorFilterFunction(filterType))
1452             return nullptr;
1453         break;
1454     }
1455
1456     CSSParserTokenRange args = consumeFunction(range);
1457     RefPtr<CSSFunctionValue> filterValue = CSSFunctionValue::create(filterType);
1458
1459     if (filterType == CSSValueAppleInvertLightness) {
1460         if (!args.atEnd())
1461             return nullptr;
1462         return filterValue;
1463     }
1464
1465     RefPtr<CSSValue> parsedValue;
1466
1467     if (filterType == CSSValueDropShadow)
1468         parsedValue = consumeSingleShadow(args, context.mode, false, false);
1469     else {
1470         if (args.atEnd())
1471             return filterValue;
1472
1473         if (filterType == CSSValueHueRotate)
1474             parsedValue = consumeAngle(args, context.mode, UnitlessQuirk::Forbid);
1475         else if (filterType == CSSValueBlur)
1476             parsedValue = consumeLength(args, HTMLStandardMode, ValueRangeNonNegative);
1477         else {
1478             parsedValue = consumePercent(args, ValueRangeNonNegative);
1479             if (!parsedValue)
1480                 parsedValue = consumeNumber(args, ValueRangeNonNegative);
1481             if (parsedValue && !allowsValuesGreaterThanOne(filterType)) {
1482                 bool isPercentage = downcast<CSSPrimitiveValue>(*parsedValue).isPercentage();
1483                 double maxAllowed = isPercentage ? 100.0 : 1.0;
1484                 if (downcast<CSSPrimitiveValue>(*parsedValue).doubleValue() > maxAllowed)
1485                     parsedValue = CSSPrimitiveValue::create(maxAllowed, isPercentage ? CSSPrimitiveValue::UnitType::CSS_PERCENTAGE : CSSPrimitiveValue::UnitType::CSS_NUMBER);
1486             }
1487         }
1488     }
1489     if (!parsedValue || !args.atEnd())
1490         return nullptr;
1491     filterValue->append(parsedValue.releaseNonNull());
1492     return filterValue;
1493 }
1494
1495 RefPtr<CSSValue> consumeFilter(CSSParserTokenRange& range, const CSSParserContext& context, AllowedFilterFunctions allowedFunctions)
1496 {
1497     if (range.peek().id() == CSSValueNone)
1498         return consumeIdent(range);
1499
1500     bool referenceFiltersAllowed = allowedFunctions == AllowedFilterFunctions::PixelFilters;
1501     auto list = CSSValueList::createSpaceSeparated();
1502     do {
1503         RefPtr<CSSValue> filterValue = referenceFiltersAllowed ? consumeUrl(range) : nullptr;
1504         if (!filterValue) {
1505             filterValue = consumeFilterFunction(range, context, allowedFunctions);
1506             if (!filterValue)
1507                 return nullptr;
1508         }
1509         list->append(filterValue.releaseNonNull());
1510     } while (!range.atEnd());
1511
1512     return list.ptr();
1513 }
1514
1515 RefPtr<CSSShadowValue> consumeSingleShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool allowInset, bool allowSpread)
1516 {
1517     RefPtr<CSSPrimitiveValue> style;
1518     RefPtr<CSSPrimitiveValue> color;
1519
1520     if (range.atEnd())
1521         return nullptr;
1522     if (range.peek().id() == CSSValueInset) {
1523         if (!allowInset)
1524             return nullptr;
1525         style = consumeIdent(range);
1526     }
1527     color = consumeColor(range, cssParserMode);
1528
1529     auto horizontalOffset = consumeLength(range, cssParserMode, ValueRangeAll);
1530     if (!horizontalOffset)
1531         return nullptr;
1532
1533     auto verticalOffset = consumeLength(range, cssParserMode, ValueRangeAll);
1534     if (!verticalOffset)
1535         return nullptr;
1536
1537     auto blurRadius = consumeLength(range, cssParserMode, ValueRangeAll);
1538     RefPtr<CSSPrimitiveValue> spreadDistance;
1539     if (blurRadius) {
1540         // Blur radius must be non-negative.
1541         if (blurRadius->doubleValue() < 0)
1542             return nullptr;
1543         if (allowSpread)
1544             spreadDistance = consumeLength(range, cssParserMode, ValueRangeAll);
1545     }
1546
1547     if (!range.atEnd()) {
1548         if (!color)
1549             color = consumeColor(range, cssParserMode);
1550         if (range.peek().id() == CSSValueInset) {
1551             if (!allowInset || style)
1552                 return nullptr;
1553             style = consumeIdent(range);
1554         }
1555     }
1556
1557     return CSSShadowValue::create(WTFMove(horizontalOffset), WTFMove(verticalOffset), WTFMove(blurRadius), WTFMove(spreadDistance), WTFMove(style), WTFMove(color));
1558 }
1559
1560 RefPtr<CSSValue> consumeImage(CSSParserTokenRange& range, CSSParserContext context, ConsumeGeneratedImage generatedImage)
1561 {
1562     AtomicString uri = consumeUrlAsStringView(range).toAtomicString();
1563     if (!uri.isNull())
1564         return CSSImageValue::create(completeURL(context, uri));
1565     if (range.peek().type() == FunctionToken) {
1566         CSSValueID id = range.peek().functionId();
1567         if (id == CSSValueWebkitImageSet || id == CSSValueImageSet)
1568             return consumeImageSet(range, context);
1569         if (generatedImage == ConsumeGeneratedImage::Allow && isGeneratedImage(id))
1570             return consumeGeneratedImage(range, context);
1571     }
1572     return nullptr;
1573 }
1574
1575 } // namespace CSSPropertyParserHelpers
1576
1577 } // namespace WebCore