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