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