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