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