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