• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
4  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
5  * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
6  * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
7  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8  * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
9  * Copyright (C) 2012 Intel Corporation. All rights reserved.
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public License
22  * along with this library; see the file COPYING.LIB.  If not, write to
23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  */
26 
27 #include "config.h"
28 #include "core/css/parser/CSSPropertyParser.h"
29 
30 // FIXME: Way too many!
31 #include "core/CSSValueKeywords.h"
32 #include "core/StylePropertyShorthand.h"
33 #include "core/css/CSSAspectRatioValue.h"
34 #include "core/css/CSSBasicShapes.h"
35 #include "core/css/CSSBorderImage.h"
36 #include "core/css/CSSCanvasValue.h"
37 #include "core/css/CSSCrossfadeValue.h"
38 #include "core/css/CSSCursorImageValue.h"
39 #include "core/css/CSSFontFaceSrcValue.h"
40 #include "core/css/CSSFontFeatureValue.h"
41 #include "core/css/CSSFunctionValue.h"
42 #include "core/css/CSSGradientValue.h"
43 #include "core/css/CSSGridLineNamesValue.h"
44 #include "core/css/CSSGridTemplateAreasValue.h"
45 #include "core/css/CSSImageSetValue.h"
46 #include "core/css/CSSImageValue.h"
47 #include "core/css/CSSInheritedValue.h"
48 #include "core/css/CSSInitialValue.h"
49 #include "core/css/CSSKeyframeRule.h"
50 #include "core/css/CSSKeyframesRule.h"
51 #include "core/css/CSSLineBoxContainValue.h"
52 #include "core/css/CSSPrimitiveValue.h"
53 #include "core/css/CSSPropertyMetadata.h"
54 #include "core/css/CSSPropertySourceData.h"
55 #include "core/css/CSSReflectValue.h"
56 #include "core/css/CSSSVGDocumentValue.h"
57 #include "core/css/CSSSelector.h"
58 #include "core/css/CSSShadowValue.h"
59 #include "core/css/CSSTimingFunctionValue.h"
60 #include "core/css/CSSTransformValue.h"
61 #include "core/css/CSSUnicodeRangeValue.h"
62 #include "core/css/CSSValueList.h"
63 #include "core/css/CSSValuePool.h"
64 #include "core/css/Counter.h"
65 #include "core/css/HashTools.h"
66 #include "core/css/Pair.h"
67 #include "core/css/Rect.h"
68 #include "core/css/parser/CSSParserIdioms.h"
69 #include "core/css/parser/CSSParserValues.h"
70 #include "core/frame/UseCounter.h"
71 #include "core/html/parser/HTMLParserIdioms.h"
72 #include "core/inspector/InspectorInstrumentation.h"
73 #include "core/rendering/RenderTheme.h"
74 #include "platform/FloatConversion.h"
75 #include "platform/RuntimeEnabledFeatures.h"
76 #include "wtf/BitArray.h"
77 #include "wtf/HexNumber.h"
78 #include "wtf/text/StringBuffer.h"
79 #include "wtf/text/StringBuilder.h"
80 #include "wtf/text/StringImpl.h"
81 #include "wtf/text/TextEncoding.h"
82 #include <limits.h>
83 
84 namespace blink {
85 
86 static const double MAX_SCALE = 1000000;
87 
88 template <unsigned N>
equalIgnoringCase(const CSSParserString & a,const char (& b)[N])89 static bool equalIgnoringCase(const CSSParserString& a, const char (&b)[N])
90 {
91     unsigned length = N - 1; // Ignore the trailing null character
92     if (a.length() != length)
93         return false;
94 
95     return a.is8Bit() ? WTF::equalIgnoringCase(b, a.characters8(), length) : WTF::equalIgnoringCase(b, a.characters16(), length);
96 }
97 
98 template <unsigned N>
equalIgnoringCase(CSSParserValue * value,const char (& b)[N])99 static bool equalIgnoringCase(CSSParserValue* value, const char (&b)[N])
100 {
101     ASSERT(value->unit == CSSPrimitiveValue::CSS_IDENT || value->unit == CSSPrimitiveValue::CSS_STRING);
102     return equalIgnoringCase(value->string, b);
103 }
104 
createPrimitiveValuePair(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> first,PassRefPtrWillBeRawPtr<CSSPrimitiveValue> second,Pair::IdenticalValuesPolicy identicalValuesPolicy=Pair::DropIdenticalValues)105 static PassRefPtrWillBeRawPtr<CSSPrimitiveValue> createPrimitiveValuePair(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> first, PassRefPtrWillBeRawPtr<CSSPrimitiveValue> second, Pair::IdenticalValuesPolicy identicalValuesPolicy = Pair::DropIdenticalValues)
106 {
107     return cssValuePool().createValue(Pair::create(first, second, identicalValuesPolicy));
108 }
109 
CSSPropertyParser(CSSParserValueList * valueList,const CSSParserContext & context,bool inViewport,WillBeHeapVector<CSSProperty,256> & parsedProperties,CSSRuleSourceData::Type ruleType)110 CSSPropertyParser::CSSPropertyParser(CSSParserValueList* valueList,
111     const CSSParserContext& context, bool inViewport,
112     WillBeHeapVector<CSSProperty, 256>& parsedProperties,
113     CSSRuleSourceData::Type ruleType)
114     : m_valueList(valueList)
115     , m_context(context)
116     , m_inViewport(inViewport)
117     , m_parsedProperties(parsedProperties)
118     , m_ruleType(ruleType)
119     , m_inParseShorthand(0)
120     , m_currentShorthand(CSSPropertyInvalid)
121     , m_implicitShorthand(false)
122 {
123 }
124 
parseValue(CSSPropertyID property,bool important,CSSParserValueList * valueList,const CSSParserContext & context,bool inViewport,WillBeHeapVector<CSSProperty,256> & parsedProperties,CSSRuleSourceData::Type ruleType)125 bool CSSPropertyParser::parseValue(CSSPropertyID property, bool important,
126     CSSParserValueList* valueList, const CSSParserContext& context, bool inViewport,
127     WillBeHeapVector<CSSProperty, 256>& parsedProperties, CSSRuleSourceData::Type ruleType)
128 {
129     CSSPropertyParser parser(valueList, context, inViewport, parsedProperties, ruleType);
130     return parser.parseValue(property, important);
131 }
132 
addPropertyWithPrefixingVariant(CSSPropertyID propId,PassRefPtrWillBeRawPtr<CSSValue> value,bool important,bool implicit)133 void CSSPropertyParser::addPropertyWithPrefixingVariant(CSSPropertyID propId, PassRefPtrWillBeRawPtr<CSSValue> value, bool important, bool implicit)
134 {
135     RefPtrWillBeRawPtr<CSSValue> val = value.get();
136     addProperty(propId, value, important, implicit);
137 
138     CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(propId);
139     if (prefixingVariant == propId)
140         return;
141 
142     if (m_currentShorthand) {
143         // We can't use ShorthandScope here as we can already be inside one (e.g we are parsing CSSTransition).
144         m_currentShorthand = prefixingVariantForPropertyId(m_currentShorthand);
145         addProperty(prefixingVariant, val.release(), important, implicit);
146         m_currentShorthand = prefixingVariantForPropertyId(m_currentShorthand);
147     } else {
148         addProperty(prefixingVariant, val.release(), important, implicit);
149     }
150 }
151 
addProperty(CSSPropertyID propId,PassRefPtrWillBeRawPtr<CSSValue> value,bool important,bool implicit)152 void CSSPropertyParser::addProperty(CSSPropertyID propId, PassRefPtrWillBeRawPtr<CSSValue> value, bool important, bool implicit)
153 {
154     int shorthandIndex = 0;
155     bool setFromShorthand = false;
156 
157     if (m_currentShorthand) {
158         Vector<StylePropertyShorthand, 4> shorthands;
159         getMatchingShorthandsForLonghand(propId, &shorthands);
160         // Viewport descriptors have width and height as shorthands, but it doesn't
161         // make sense for CSSProperties.in to consider them as such. The shorthand
162         // index is only used by the inspector and doesn't affect viewport
163         // descriptors.
164         if (shorthands.isEmpty())
165             ASSERT(m_currentShorthand == CSSPropertyWidth || m_currentShorthand == CSSPropertyHeight);
166         else
167             setFromShorthand = true;
168 
169         if (shorthands.size() > 1)
170             shorthandIndex = indexOfShorthandForLonghand(m_currentShorthand, shorthands);
171     }
172 
173     m_parsedProperties.append(CSSProperty(propId, value, important, setFromShorthand, shorthandIndex, m_implicitShorthand || implicit));
174 }
175 
rollbackLastProperties(int num)176 void CSSPropertyParser::rollbackLastProperties(int num)
177 {
178     ASSERT(num >= 0);
179     ASSERT(m_parsedProperties.size() >= static_cast<unsigned>(num));
180     m_parsedProperties.shrink(m_parsedProperties.size() - num);
181 }
182 
completeURL(const String & url) const183 KURL CSSPropertyParser::completeURL(const String& url) const
184 {
185     return m_context.completeURL(url);
186 }
187 
validCalculationUnit(CSSParserValue * value,Units unitflags,ReleaseParsedCalcValueCondition releaseCalc)188 bool CSSPropertyParser::validCalculationUnit(CSSParserValue* value, Units unitflags, ReleaseParsedCalcValueCondition releaseCalc)
189 {
190     bool mustBeNonNegative = unitflags & (FNonNeg | FPositiveInteger);
191 
192     if (!parseCalculation(value, mustBeNonNegative ? ValueRangeNonNegative : ValueRangeAll))
193         return false;
194 
195     bool b = false;
196     switch (m_parsedCalculation->category()) {
197     case CalcLength:
198         b = (unitflags & FLength);
199         break;
200     case CalcNumber:
201         b = (unitflags & FNumber);
202         if (!b && (unitflags & (FInteger | FPositiveInteger)) && m_parsedCalculation->isInt())
203             b = true;
204         if (b && mustBeNonNegative && m_parsedCalculation->isNegative())
205             b = false;
206         // Always resolve calc() to a CSS_NUMBER in the CSSParserValue if there are no non-numbers specified in the unitflags.
207         if (b && !(unitflags & ~(FInteger | FNumber | FPositiveInteger | FNonNeg))) {
208             double number = m_parsedCalculation->doubleValue();
209             if ((unitflags & FPositiveInteger) && number <= 0) {
210                 b = false;
211             } else {
212                 delete value->function;
213                 value->unit = CSSPrimitiveValue::CSS_NUMBER;
214                 value->fValue = number;
215                 value->isInt = m_parsedCalculation->isInt();
216             }
217             m_parsedCalculation.release();
218             return b;
219         }
220         break;
221     case CalcPercent:
222         b = (unitflags & FPercent);
223         if (b && mustBeNonNegative && m_parsedCalculation->isNegative())
224             b = false;
225         break;
226     case CalcPercentLength:
227         b = (unitflags & FPercent) && (unitflags & FLength);
228         break;
229     case CalcPercentNumber:
230         b = (unitflags & FPercent) && (unitflags & FNumber);
231         break;
232     case CalcAngle:
233         b = (unitflags & FAngle);
234         break;
235     case CalcTime:
236         b = (unitflags & FTime);
237         break;
238     case CalcFrequency:
239         b = (unitflags & FFrequency);
240         break;
241     case CalcOther:
242         break;
243     }
244     if (!b || releaseCalc == ReleaseParsedCalcValue)
245         m_parsedCalculation.release();
246     return b;
247 }
248 
shouldAcceptUnitLessValues(CSSParserValue * value,Units unitflags,CSSParserMode cssParserMode)249 inline bool CSSPropertyParser::shouldAcceptUnitLessValues(CSSParserValue* value, Units unitflags, CSSParserMode cssParserMode)
250 {
251     // Quirks mode and presentation attributes accept unit less values.
252     return (unitflags & (FLength | FAngle | FTime)) && (!value->fValue || isUnitLessLengthParsingEnabledForMode(cssParserMode));
253 }
254 
validUnit(CSSParserValue * value,Units unitflags,CSSParserMode cssParserMode,ReleaseParsedCalcValueCondition releaseCalc)255 bool CSSPropertyParser::validUnit(CSSParserValue* value, Units unitflags, CSSParserMode cssParserMode, ReleaseParsedCalcValueCondition releaseCalc)
256 {
257     if (isCalculation(value))
258         return validCalculationUnit(value, unitflags, releaseCalc);
259 
260     bool b = false;
261     switch (value->unit) {
262     case CSSPrimitiveValue::CSS_NUMBER:
263         b = (unitflags & FNumber);
264         if (!b && shouldAcceptUnitLessValues(value, unitflags, cssParserMode)) {
265             value->unit = (unitflags & FLength) ? CSSPrimitiveValue::CSS_PX :
266                           ((unitflags & FAngle) ? CSSPrimitiveValue::CSS_DEG : CSSPrimitiveValue::CSS_MS);
267             b = true;
268         }
269         if (!b && (unitflags & FInteger) && value->isInt)
270             b = true;
271         if (!b && (unitflags & FPositiveInteger) && value->isInt && value->fValue > 0)
272             b = true;
273         break;
274     case CSSPrimitiveValue::CSS_PERCENTAGE:
275         b = (unitflags & FPercent);
276         break;
277     case CSSParserValue::Q_EMS:
278     case CSSPrimitiveValue::CSS_EMS:
279     case CSSPrimitiveValue::CSS_REMS:
280     case CSSPrimitiveValue::CSS_CHS:
281     case CSSPrimitiveValue::CSS_EXS:
282     case CSSPrimitiveValue::CSS_PX:
283     case CSSPrimitiveValue::CSS_CM:
284     case CSSPrimitiveValue::CSS_MM:
285     case CSSPrimitiveValue::CSS_IN:
286     case CSSPrimitiveValue::CSS_PT:
287     case CSSPrimitiveValue::CSS_PC:
288     case CSSPrimitiveValue::CSS_VW:
289     case CSSPrimitiveValue::CSS_VH:
290     case CSSPrimitiveValue::CSS_VMIN:
291     case CSSPrimitiveValue::CSS_VMAX:
292         b = (unitflags & FLength);
293         break;
294     case CSSPrimitiveValue::CSS_MS:
295     case CSSPrimitiveValue::CSS_S:
296         b = (unitflags & FTime);
297         break;
298     case CSSPrimitiveValue::CSS_DEG:
299     case CSSPrimitiveValue::CSS_RAD:
300     case CSSPrimitiveValue::CSS_GRAD:
301     case CSSPrimitiveValue::CSS_TURN:
302         b = (unitflags & FAngle);
303         break;
304     case CSSPrimitiveValue::CSS_DPPX:
305     case CSSPrimitiveValue::CSS_DPI:
306     case CSSPrimitiveValue::CSS_DPCM:
307         b = (unitflags & FResolution);
308         break;
309     case CSSPrimitiveValue::CSS_HZ:
310     case CSSPrimitiveValue::CSS_KHZ:
311     case CSSPrimitiveValue::CSS_DIMENSION:
312     default:
313         break;
314     }
315     if (b && unitflags & FNonNeg && value->fValue < 0)
316         b = false;
317     return b;
318 }
319 
createPrimitiveNumericValue(CSSParserValue * value)320 PassRefPtrWillBeRawPtr<CSSPrimitiveValue> CSSPropertyParser::createPrimitiveNumericValue(CSSParserValue* value)
321 {
322     if (m_parsedCalculation) {
323         ASSERT(isCalculation(value));
324         return CSSPrimitiveValue::create(m_parsedCalculation.release());
325     }
326 
327     ASSERT((value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ)
328         || (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_CHS)
329         || (value->unit >= CSSPrimitiveValue::CSS_VW && value->unit <= CSSPrimitiveValue::CSS_VMAX)
330         || (value->unit >= CSSPrimitiveValue::CSS_DPPX && value->unit <= CSSPrimitiveValue::CSS_DPCM));
331     return cssValuePool().createValue(value->fValue, static_cast<CSSPrimitiveValue::UnitType>(value->unit));
332 }
333 
createPrimitiveStringValue(CSSParserValue * value)334 inline PassRefPtrWillBeRawPtr<CSSPrimitiveValue> CSSPropertyParser::createPrimitiveStringValue(CSSParserValue* value)
335 {
336     ASSERT(value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT);
337     return cssValuePool().createValue(value->string, CSSPrimitiveValue::CSS_STRING);
338 }
339 
createCSSImageValueWithReferrer(const String & rawValue,const KURL & url)340 inline PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::createCSSImageValueWithReferrer(const String& rawValue, const KURL& url)
341 {
342     RefPtrWillBeRawPtr<CSSValue> imageValue = CSSImageValue::create(rawValue, url);
343     toCSSImageValue(imageValue.get())->setReferrer(m_context.referrer());
344     return imageValue;
345 }
346 
isComma(CSSParserValue * value)347 static inline bool isComma(CSSParserValue* value)
348 {
349     return value && value->unit == CSSParserValue::Operator && value->iValue == ',';
350 }
351 
consumeComma(CSSParserValueList * valueList)352 static bool consumeComma(CSSParserValueList* valueList)
353 {
354     if (!isComma(valueList->current()))
355         return false;
356     valueList->next();
357     return true;
358 }
359 
isForwardSlashOperator(CSSParserValue * value)360 static inline bool isForwardSlashOperator(CSSParserValue* value)
361 {
362     ASSERT(value);
363     return value->unit == CSSParserValue::Operator && value->iValue == '/';
364 }
365 
isGeneratedImageValue(CSSParserValue * val)366 static bool isGeneratedImageValue(CSSParserValue* val)
367 {
368     if (val->unit != CSSParserValue::Function)
369         return false;
370 
371     return equalIgnoringCase(val->function->name, "-webkit-gradient")
372         || equalIgnoringCase(val->function->name, "-webkit-linear-gradient")
373         || equalIgnoringCase(val->function->name, "linear-gradient")
374         || equalIgnoringCase(val->function->name, "-webkit-repeating-linear-gradient")
375         || equalIgnoringCase(val->function->name, "repeating-linear-gradient")
376         || equalIgnoringCase(val->function->name, "-webkit-radial-gradient")
377         || equalIgnoringCase(val->function->name, "radial-gradient")
378         || equalIgnoringCase(val->function->name, "-webkit-repeating-radial-gradient")
379         || equalIgnoringCase(val->function->name, "repeating-radial-gradient")
380         || equalIgnoringCase(val->function->name, "-webkit-canvas")
381         || equalIgnoringCase(val->function->name, "-webkit-cross-fade");
382 }
383 
validWidthOrHeight(CSSParserValue * value)384 bool CSSPropertyParser::validWidthOrHeight(CSSParserValue* value)
385 {
386     int id = value->id;
387     if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic || id == CSSValueWebkitMinContent || id == CSSValueWebkitMaxContent || id == CSSValueWebkitFillAvailable || id == CSSValueWebkitFitContent)
388         return true;
389     return !id && validUnit(value, FLength | FPercent | FNonNeg);
390 }
391 
parseValidPrimitive(CSSValueID identifier,CSSParserValue * value)392 inline PassRefPtrWillBeRawPtr<CSSPrimitiveValue> CSSPropertyParser::parseValidPrimitive(CSSValueID identifier, CSSParserValue* value)
393 {
394     if (identifier)
395         return cssValuePool().createIdentifierValue(identifier);
396     if (value->unit == CSSPrimitiveValue::CSS_STRING)
397         return createPrimitiveStringValue(value);
398     if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ)
399         return createPrimitiveNumericValue(value);
400     if (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_CHS)
401         return createPrimitiveNumericValue(value);
402     if (value->unit >= CSSPrimitiveValue::CSS_VW && value->unit <= CSSPrimitiveValue::CSS_VMAX)
403         return createPrimitiveNumericValue(value);
404     if (value->unit >= CSSPrimitiveValue::CSS_DPPX && value->unit <= CSSPrimitiveValue::CSS_DPCM)
405         return createPrimitiveNumericValue(value);
406     if (value->unit >= CSSParserValue::Q_EMS)
407         return CSSPrimitiveValue::createAllowingMarginQuirk(value->fValue, CSSPrimitiveValue::CSS_EMS);
408     if (isCalculation(value))
409         return CSSPrimitiveValue::create(m_parsedCalculation.release());
410 
411     return nullptr;
412 }
413 
addExpandedPropertyForValue(CSSPropertyID propId,PassRefPtrWillBeRawPtr<CSSValue> prpValue,bool important)414 void CSSPropertyParser::addExpandedPropertyForValue(CSSPropertyID propId, PassRefPtrWillBeRawPtr<CSSValue> prpValue, bool important)
415 {
416     const StylePropertyShorthand& shorthand = shorthandForProperty(propId);
417     unsigned shorthandLength = shorthand.length();
418     if (!shorthandLength) {
419         addPropertyWithPrefixingVariant(propId, prpValue, important);
420         return;
421     }
422 
423     RefPtrWillBeRawPtr<CSSValue> value = prpValue;
424     ShorthandScope scope(this, propId);
425     const CSSPropertyID* longhands = shorthand.properties();
426     for (unsigned i = 0; i < shorthandLength; ++i)
427         addPropertyWithPrefixingVariant(longhands[i], value, important);
428 }
429 
parseValue(CSSPropertyID propId,bool important)430 bool CSSPropertyParser::parseValue(CSSPropertyID propId, bool important)
431 {
432     if (!isInternalPropertyAndValueParsingEnabledForMode(m_context.mode()) && isInternalProperty(propId))
433         return false;
434 
435     // We don't count the UA style sheet in our statistics.
436     if (m_context.useCounter())
437         m_context.useCounter()->count(m_context, propId);
438 
439     if (!m_valueList)
440         return false;
441 
442     CSSParserValue* value = m_valueList->current();
443 
444     if (!value)
445         return false;
446 
447     if (inViewport()) {
448         // Allow @viewport rules from UA stylesheets even if the feature is disabled.
449         if (!RuntimeEnabledFeatures::cssViewportEnabled() && !isUASheetBehavior(m_context.mode()))
450             return false;
451 
452         return parseViewportProperty(propId, important);
453     }
454 
455     // Note: m_parsedCalculation is used to pass the calc value to validUnit and then cleared at the end of this function.
456     // FIXME: This is to avoid having to pass parsedCalc to all validUnit callers.
457     ASSERT(!m_parsedCalculation);
458 
459     CSSValueID id = value->id;
460 
461     int num = inShorthand() ? 1 : m_valueList->size();
462 
463     if (id == CSSValueInherit) {
464         if (num != 1)
465             return false;
466         addExpandedPropertyForValue(propId, cssValuePool().createInheritedValue(), important);
467         return true;
468     }
469     else if (id == CSSValueInitial) {
470         if (num != 1)
471             return false;
472         addExpandedPropertyForValue(propId, cssValuePool().createExplicitInitialValue(), important);
473         return true;
474     }
475 
476     if (isKeywordPropertyID(propId)) {
477         if (!isValidKeywordPropertyAndValue(propId, id, m_context))
478             return false;
479         if (m_valueList->next() && !inShorthand())
480             return false;
481         addProperty(propId, cssValuePool().createIdentifierValue(id), important);
482         return true;
483     }
484 
485     bool validPrimitive = false;
486     RefPtrWillBeRawPtr<CSSValue> parsedValue = nullptr;
487 
488     switch (propId) {
489     case CSSPropertySize:                 // <length>{1,2} | auto | [ <page-size> || [ portrait | landscape] ]
490         return parseSize(propId, important);
491 
492     case CSSPropertyQuotes: // [<string> <string>]+ | none
493         if (id == CSSValueNone)
494             validPrimitive = true;
495         else
496             parsedValue = parseQuotes();
497         break;
498 
499     case CSSPropertyContent:              // [ <string> | <uri> | <counter> | attr(X) | open-quote |
500         // close-quote | no-open-quote | no-close-quote ]+ | inherit
501         return parseContent(propId, important);
502 
503     case CSSPropertyClip:                 // <shape> | auto | inherit
504         if (id == CSSValueAuto)
505             validPrimitive = true;
506         else if (value->unit == CSSParserValue::Function)
507             return parseClipShape(propId, important);
508         break;
509 
510     /* Start of supported CSS properties with validation. This is needed for parseShorthand to work
511      * correctly and allows optimization in blink::applyRule(..)
512      */
513     case CSSPropertyOverflow: {
514         ShorthandScope scope(this, propId);
515         if (num != 1 || !parseValue(CSSPropertyOverflowY, important))
516             return false;
517 
518         RefPtrWillBeRawPtr<CSSValue> overflowXValue = nullptr;
519 
520         // FIXME: -webkit-paged-x or -webkit-paged-y only apply to overflow-y. If this value has been
521         // set using the shorthand, then for now overflow-x will default to auto, but once we implement
522         // pagination controls, it should default to hidden. If the overflow-y value is anything but
523         // paged-x or paged-y, then overflow-x and overflow-y should have the same value.
524         if (id == CSSValueWebkitPagedX || id == CSSValueWebkitPagedY)
525             overflowXValue = cssValuePool().createIdentifierValue(CSSValueAuto);
526         else
527             overflowXValue = m_parsedProperties.last().value();
528         addProperty(CSSPropertyOverflowX, overflowXValue.release(), important);
529         return true;
530     }
531 
532     case CSSPropertyTextAlign:
533         // left | right | center | justify | -webkit-left | -webkit-right | -webkit-center | -webkit-match-parent
534         // | start | end | <string> | inherit | -webkit-auto (converted to start)
535         if ((id >= CSSValueWebkitAuto && id <= CSSValueWebkitMatchParent) || id == CSSValueStart || id == CSSValueEnd
536             || value->unit == CSSPrimitiveValue::CSS_STRING)
537             validPrimitive = true;
538         break;
539 
540     case CSSPropertyFontWeight:  { // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit
541         if (m_valueList->size() != 1)
542             return false;
543         return parseFontWeight(important);
544     }
545 
546     case CSSPropertyBorderSpacing: {
547         if (num == 1) {
548             ShorthandScope scope(this, CSSPropertyBorderSpacing);
549             if (!parseValue(CSSPropertyWebkitBorderHorizontalSpacing, important))
550                 return false;
551             CSSValue* value = m_parsedProperties.last().value();
552             addProperty(CSSPropertyWebkitBorderVerticalSpacing, value, important);
553             return true;
554         }
555         else if (num == 2) {
556             ShorthandScope scope(this, CSSPropertyBorderSpacing);
557             if (!parseValue(CSSPropertyWebkitBorderHorizontalSpacing, important) || !parseValue(CSSPropertyWebkitBorderVerticalSpacing, important))
558                 return false;
559             return true;
560         }
561         return false;
562     }
563     case CSSPropertyWebkitBorderHorizontalSpacing:
564     case CSSPropertyWebkitBorderVerticalSpacing:
565         validPrimitive = validUnit(value, FLength | FNonNeg);
566         break;
567     case CSSPropertyOutlineColor:        // <color> | invert | inherit
568         // Outline color has "invert" as additional keyword.
569         // Also, we want to allow the special focus color even in HTML Standard parsing mode.
570         if (id == CSSValueInvert || id == CSSValueWebkitFocusRingColor) {
571             validPrimitive = true;
572             break;
573         }
574         /* nobreak */
575     case CSSPropertyBackgroundColor: // <color> | inherit
576     case CSSPropertyBorderTopColor: // <color> | inherit
577     case CSSPropertyBorderRightColor:
578     case CSSPropertyBorderBottomColor:
579     case CSSPropertyBorderLeftColor:
580     case CSSPropertyWebkitBorderStartColor:
581     case CSSPropertyWebkitBorderEndColor:
582     case CSSPropertyWebkitBorderBeforeColor:
583     case CSSPropertyWebkitBorderAfterColor:
584     case CSSPropertyColor: // <color> | inherit
585     case CSSPropertyTextDecorationColor: // CSS3 text decoration colors
586     case CSSPropertyWebkitColumnRuleColor:
587     case CSSPropertyWebkitTextEmphasisColor:
588     case CSSPropertyWebkitTextFillColor:
589     case CSSPropertyWebkitTextStrokeColor:
590         ASSERT(propId != CSSPropertyTextDecorationColor || RuntimeEnabledFeatures::css3TextDecorationsEnabled());
591 
592         if ((id >= CSSValueAqua && id <= CSSValueWebkitText) || id == CSSValueMenu) {
593             validPrimitive = isValueAllowedInMode(id, m_context.mode());
594         } else {
595             if (!inQuirksMode()) {
596                 parsedValue = parseColor();
597                 if (parsedValue)
598                     m_valueList->next();
599                 break;
600             }
601 
602             bool acceptQuirkyColors = false;
603             switch (propId) {
604             case CSSPropertyBackgroundColor:
605                 if (!inShorthand())
606                     acceptQuirkyColors = true;
607                 break;
608             case CSSPropertyBorderBottomColor:
609             case CSSPropertyBorderLeftColor:
610             case CSSPropertyBorderRightColor:
611             case CSSPropertyBorderTopColor:
612             case CSSPropertyColor:
613                 acceptQuirkyColors = true;
614                 break;
615             default:
616                 break;
617             }
618             parsedValue = parseColor(0, acceptQuirkyColors);
619             if (parsedValue)
620                 m_valueList->next();
621         }
622         break;
623 
624     case CSSPropertyCursor: {
625         // Grammar defined by CSS3 UI and modified by CSS4 images:
626         // [ [<image> [<x> <y>]?,]*
627         // [ auto | crosshair | default | pointer | progress | move | e-resize | ne-resize |
628         // nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize | ew-resize |
629         // ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | text | wait | help |
630         // vertical-text | cell | context-menu | alias | copy | no-drop | not-allowed | all-scroll |
631         // zoom-in | zoom-out | -webkit-grab | -webkit-grabbing | -webkit-zoom-in | -webkit-zoom-out ] ] | inherit
632         RefPtrWillBeRawPtr<CSSValueList> list = nullptr;
633         while (value) {
634             RefPtrWillBeRawPtr<CSSValue> image = nullptr;
635             if (value->unit == CSSPrimitiveValue::CSS_URI) {
636                 String uri = value->string;
637                 if (!uri.isNull())
638                     image = createCSSImageValueWithReferrer(uri, completeURL(uri));
639             } else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "-webkit-image-set")) {
640                 image = parseImageSet(m_valueList);
641                 if (!image)
642                     break;
643             } else
644                 break;
645 
646             Vector<int> coords;
647             value = m_valueList->next();
648             while (value && validUnit(value, FNumber)) {
649                 coords.append(int(value->fValue));
650                 value = m_valueList->next();
651             }
652             bool hasHotSpot = false;
653             IntPoint hotSpot(-1, -1);
654             int nrcoords = coords.size();
655             if (nrcoords > 0 && nrcoords != 2)
656                 return false;
657             if (nrcoords == 2) {
658                 hasHotSpot = true;
659                 hotSpot = IntPoint(coords[0], coords[1]);
660             }
661 
662             if (!list)
663                 list = CSSValueList::createCommaSeparated();
664 
665             if (image)
666                 list->append(CSSCursorImageValue::create(image, hasHotSpot, hotSpot));
667 
668             if (!consumeComma(m_valueList))
669                 return false;
670             value = m_valueList->current();
671         }
672         if (value && m_context.useCounter()) {
673             if (value->id == CSSValueWebkitZoomIn)
674                 m_context.useCounter()->count(UseCounter::PrefixedCursorZoomIn);
675             else if (value->id == CSSValueWebkitZoomOut)
676                 m_context.useCounter()->count(UseCounter::PrefixedCursorZoomOut);
677         }
678         if (list) {
679             if (!value)
680                 return false;
681             if (inQuirksMode() && value->id == CSSValueHand) // MSIE 5 compatibility :/
682                 list->append(cssValuePool().createIdentifierValue(CSSValuePointer));
683             else if ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitZoomOut) || value->id == CSSValueCopy || value->id == CSSValueNone)
684                 list->append(cssValuePool().createIdentifierValue(value->id));
685             m_valueList->next();
686             parsedValue = list.release();
687             break;
688         } else if (value) {
689             id = value->id;
690             if (inQuirksMode() && value->id == CSSValueHand) { // MSIE 5 compatibility :/
691                 id = CSSValuePointer;
692                 validPrimitive = true;
693             } else if ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitZoomOut) || value->id == CSSValueCopy || value->id == CSSValueNone)
694                 validPrimitive = true;
695         } else {
696             ASSERT_NOT_REACHED();
697             return false;
698         }
699         break;
700     }
701 
702     case CSSPropertyBackgroundBlendMode:
703     case CSSPropertyBackgroundAttachment:
704     case CSSPropertyBackgroundClip:
705     case CSSPropertyWebkitBackgroundClip:
706     case CSSPropertyWebkitBackgroundComposite:
707     case CSSPropertyBackgroundImage:
708     case CSSPropertyBackgroundOrigin:
709     case CSSPropertyMaskSourceType:
710     case CSSPropertyWebkitBackgroundOrigin:
711     case CSSPropertyBackgroundPosition:
712     case CSSPropertyBackgroundPositionX:
713     case CSSPropertyBackgroundPositionY:
714     case CSSPropertyBackgroundSize:
715     case CSSPropertyWebkitBackgroundSize:
716     case CSSPropertyBackgroundRepeat:
717     case CSSPropertyWebkitMaskClip:
718     case CSSPropertyWebkitMaskComposite:
719     case CSSPropertyWebkitMaskImage:
720     case CSSPropertyWebkitMaskOrigin:
721     case CSSPropertyWebkitMaskPosition:
722     case CSSPropertyWebkitMaskPositionX:
723     case CSSPropertyWebkitMaskPositionY:
724     case CSSPropertyWebkitMaskSize:
725     case CSSPropertyWebkitMaskRepeat:
726     case CSSPropertyWebkitMaskRepeatX:
727     case CSSPropertyWebkitMaskRepeatY:
728     {
729         RefPtrWillBeRawPtr<CSSValue> val1 = nullptr;
730         RefPtrWillBeRawPtr<CSSValue> val2 = nullptr;
731         CSSPropertyID propId1, propId2;
732         bool result = false;
733         if (parseFillProperty(propId, propId1, propId2, val1, val2)) {
734             if (propId == CSSPropertyBackgroundPosition ||
735                 propId == CSSPropertyBackgroundRepeat ||
736                 propId == CSSPropertyWebkitMaskPosition ||
737                 propId == CSSPropertyWebkitMaskRepeat) {
738                 ShorthandScope scope(this, propId);
739                 addProperty(propId1, val1.release(), important);
740                 if (val2)
741                     addProperty(propId2, val2.release(), important);
742             } else {
743                 addProperty(propId1, val1.release(), important);
744                 if (val2)
745                     addProperty(propId2, val2.release(), important);
746             }
747             result = true;
748         }
749         m_implicitShorthand = false;
750         return result;
751     }
752     case CSSPropertyObjectPosition:
753         parsedValue = parseObjectPosition();
754         break;
755     case CSSPropertyListStyleImage:     // <uri> | none | inherit
756     case CSSPropertyBorderImageSource:
757     case CSSPropertyWebkitMaskBoxImageSource:
758         if (id == CSSValueNone) {
759             parsedValue = cssValuePool().createIdentifierValue(CSSValueNone);
760             m_valueList->next();
761         } else if (value->unit == CSSPrimitiveValue::CSS_URI) {
762             parsedValue = createCSSImageValueWithReferrer(value->string, completeURL(value->string));
763             m_valueList->next();
764         } else if (isGeneratedImageValue(value)) {
765             if (parseGeneratedImage(m_valueList, parsedValue))
766                 m_valueList->next();
767             else
768                 return false;
769         }
770         else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "-webkit-image-set")) {
771             parsedValue = parseImageSet(m_valueList);
772             if (!parsedValue)
773                 return false;
774             m_valueList->next();
775         }
776         break;
777 
778     case CSSPropertyWebkitTextStrokeWidth:
779     case CSSPropertyOutlineWidth:        // <border-width> | inherit
780     case CSSPropertyBorderTopWidth:     //// <border-width> | inherit
781     case CSSPropertyBorderRightWidth:   //   Which is defined as
782     case CSSPropertyBorderBottomWidth:  //   thin | medium | thick | <length>
783     case CSSPropertyBorderLeftWidth:
784     case CSSPropertyWebkitBorderStartWidth:
785     case CSSPropertyWebkitBorderEndWidth:
786     case CSSPropertyWebkitBorderBeforeWidth:
787     case CSSPropertyWebkitBorderAfterWidth:
788     case CSSPropertyWebkitColumnRuleWidth:
789         if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick)
790             validPrimitive = true;
791         else
792             validPrimitive = validUnit(value, FLength | FNonNeg);
793         break;
794 
795     case CSSPropertyLetterSpacing:       // normal | <length> | inherit
796     case CSSPropertyWordSpacing:         // normal | <length> | inherit
797         if (id == CSSValueNormal)
798             validPrimitive = true;
799         else
800             validPrimitive = validUnit(value, FLength);
801         break;
802 
803     case CSSPropertyTextIndent:
804         parsedValue = parseTextIndent();
805         break;
806 
807     case CSSPropertyPaddingTop:          //// <padding-width> | inherit
808     case CSSPropertyPaddingRight:        //   Which is defined as
809     case CSSPropertyPaddingBottom:       //   <length> | <percentage>
810     case CSSPropertyPaddingLeft:         ////
811     case CSSPropertyWebkitPaddingStart:
812     case CSSPropertyWebkitPaddingEnd:
813     case CSSPropertyWebkitPaddingBefore:
814     case CSSPropertyWebkitPaddingAfter:
815         validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg));
816         break;
817 
818     case CSSPropertyMaxWidth:
819     case CSSPropertyWebkitMaxLogicalWidth:
820     case CSSPropertyMaxHeight:
821     case CSSPropertyWebkitMaxLogicalHeight:
822         validPrimitive = (id == CSSValueNone || validWidthOrHeight(value));
823         break;
824 
825     case CSSPropertyMinWidth:
826     case CSSPropertyWebkitMinLogicalWidth:
827     case CSSPropertyMinHeight:
828     case CSSPropertyWebkitMinLogicalHeight:
829         validPrimitive = validWidthOrHeight(value);
830         break;
831 
832     case CSSPropertyWidth:
833     case CSSPropertyWebkitLogicalWidth:
834     case CSSPropertyHeight:
835     case CSSPropertyWebkitLogicalHeight:
836         validPrimitive = (id == CSSValueAuto || validWidthOrHeight(value));
837         break;
838 
839     case CSSPropertyFontSize:
840         return parseFontSize(important);
841 
842     case CSSPropertyFontVariant:         // normal | small-caps | inherit
843         return parseFontVariant(important);
844 
845     case CSSPropertyVerticalAlign:
846         // baseline | sub | super | top | text-top | middle | bottom | text-bottom |
847         // <percentage> | <length> | inherit
848 
849         if (id >= CSSValueBaseline && id <= CSSValueWebkitBaselineMiddle)
850             validPrimitive = true;
851         else
852             validPrimitive = (!id && validUnit(value, FLength | FPercent));
853         break;
854 
855     case CSSPropertyBottom:               // <length> | <percentage> | auto | inherit
856     case CSSPropertyLeft:                 // <length> | <percentage> | auto | inherit
857     case CSSPropertyRight:                // <length> | <percentage> | auto | inherit
858     case CSSPropertyTop:                  // <length> | <percentage> | auto | inherit
859     case CSSPropertyMarginTop:           //// <margin-width> | inherit
860     case CSSPropertyMarginRight:         //   Which is defined as
861     case CSSPropertyMarginBottom:        //   <length> | <percentage> | auto | inherit
862     case CSSPropertyMarginLeft:          ////
863     case CSSPropertyWebkitMarginStart:
864     case CSSPropertyWebkitMarginEnd:
865     case CSSPropertyWebkitMarginBefore:
866     case CSSPropertyWebkitMarginAfter:
867         if (id == CSSValueAuto)
868             validPrimitive = true;
869         else
870             validPrimitive = (!id && validUnit(value, FLength | FPercent));
871         break;
872 
873     case CSSPropertyOrphans: // <integer> | inherit | auto (We've added support for auto for backwards compatibility)
874     case CSSPropertyWidows: // <integer> | inherit | auto (Ditto)
875         if (id == CSSValueAuto)
876             validPrimitive = true;
877         else
878             validPrimitive = (!id && validUnit(value, FPositiveInteger));
879         break;
880 
881     case CSSPropertyZIndex: // auto | <integer> | inherit
882         if (id == CSSValueAuto)
883             validPrimitive = true;
884         else
885             validPrimitive = (!id && validUnit(value, FInteger));
886         break;
887 
888     case CSSPropertyLineHeight:
889         return parseLineHeight(important);
890     case CSSPropertyCounterIncrement:
891         if (id == CSSValueNone)
892             validPrimitive = true;
893         else
894             parsedValue = parseCounter(1);
895         break;
896     case CSSPropertyCounterReset:
897         if (id == CSSValueNone)
898             validPrimitive = true;
899         else
900             parsedValue = parseCounter(0);
901         break;
902     case CSSPropertyFontFamily:
903         // [[ <family-name> | <generic-family> ],]* [<family-name> | <generic-family>] | inherit
904     {
905         parsedValue = parseFontFamily();
906         break;
907     }
908 
909     case CSSPropertyTextDecoration:
910         // Fall through 'text-decoration-line' parsing if CSS 3 Text Decoration
911         // is disabled to match CSS 2.1 rules for parsing 'text-decoration'.
912         if (RuntimeEnabledFeatures::css3TextDecorationsEnabled()) {
913             // [ <text-decoration-line> || <text-decoration-style> || <text-decoration-color> ] | inherit
914             return parseShorthand(CSSPropertyTextDecoration, textDecorationShorthand(), important);
915         }
916     case CSSPropertyWebkitTextDecorationsInEffect:
917     case CSSPropertyTextDecorationLine:
918         // none | [ underline || overline || line-through || blink ] | inherit
919         return parseTextDecoration(propId, important);
920 
921     case CSSPropertyTextUnderlinePosition:
922         // auto | under | inherit
923         ASSERT(RuntimeEnabledFeatures::css3TextDecorationsEnabled());
924         return parseTextUnderlinePosition(important);
925 
926     case CSSPropertyZoom:          // normal | reset | document | <number> | <percentage> | inherit
927         if (id == CSSValueNormal || id == CSSValueReset || id == CSSValueDocument)
928             validPrimitive = true;
929         else
930             validPrimitive = (!id && validUnit(value, FNumber | FPercent | FNonNeg));
931         break;
932 
933     case CSSPropertySrc: // Only used within @font-face and @-webkit-filter, so cannot use inherit | initial or be !important. This is a list of urls or local references.
934         parsedValue = parseFontFaceSrc();
935         break;
936 
937     case CSSPropertyUnicodeRange:
938         parsedValue = parseFontFaceUnicodeRange();
939         break;
940 
941     /* CSS3 properties */
942 
943     case CSSPropertyBorderImage:
944     case CSSPropertyWebkitMaskBoxImage:
945         return parseBorderImageShorthand(propId, important);
946     case CSSPropertyWebkitBorderImage: {
947         if (RefPtrWillBeRawPtr<CSSValue> result = parseBorderImage(propId)) {
948             addProperty(propId, result, important);
949             return true;
950         }
951         return false;
952     }
953 
954     case CSSPropertyBorderImageOutset:
955     case CSSPropertyWebkitMaskBoxImageOutset: {
956         RefPtrWillBeRawPtr<CSSPrimitiveValue> result = nullptr;
957         if (parseBorderImageOutset(result)) {
958             addProperty(propId, result, important);
959             return true;
960         }
961         break;
962     }
963     case CSSPropertyBorderImageRepeat:
964     case CSSPropertyWebkitMaskBoxImageRepeat: {
965         RefPtrWillBeRawPtr<CSSValue> result = nullptr;
966         if (parseBorderImageRepeat(result)) {
967             addProperty(propId, result, important);
968             return true;
969         }
970         break;
971     }
972     case CSSPropertyBorderImageSlice:
973     case CSSPropertyWebkitMaskBoxImageSlice: {
974         RefPtrWillBeRawPtr<CSSBorderImageSliceValue> result = nullptr;
975         if (parseBorderImageSlice(propId, result)) {
976             addProperty(propId, result, important);
977             return true;
978         }
979         break;
980     }
981     case CSSPropertyBorderImageWidth:
982     case CSSPropertyWebkitMaskBoxImageWidth: {
983         RefPtrWillBeRawPtr<CSSPrimitiveValue> result = nullptr;
984         if (parseBorderImageWidth(result)) {
985             addProperty(propId, result, important);
986             return true;
987         }
988         break;
989     }
990     case CSSPropertyBorderTopRightRadius:
991     case CSSPropertyBorderTopLeftRadius:
992     case CSSPropertyBorderBottomLeftRadius:
993     case CSSPropertyBorderBottomRightRadius: {
994         if (num != 1 && num != 2)
995             return false;
996         validPrimitive = validUnit(value, FLength | FPercent | FNonNeg);
997         if (!validPrimitive)
998             return false;
999         RefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue1 = createPrimitiveNumericValue(value);
1000         RefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue2 = nullptr;
1001         if (num == 2) {
1002             value = m_valueList->next();
1003             validPrimitive = validUnit(value, FLength | FPercent | FNonNeg);
1004             if (!validPrimitive)
1005                 return false;
1006             parsedValue2 = createPrimitiveNumericValue(value);
1007         } else
1008             parsedValue2 = parsedValue1;
1009 
1010         addProperty(propId, createPrimitiveValuePair(parsedValue1.release(), parsedValue2.release()), important);
1011         return true;
1012     }
1013     case CSSPropertyTabSize:
1014         validPrimitive = validUnit(value, FInteger | FNonNeg);
1015         break;
1016     case CSSPropertyWebkitAspectRatio:
1017         parsedValue = parseAspectRatio();
1018         break;
1019     case CSSPropertyBorderRadius:
1020     case CSSPropertyWebkitBorderRadius:
1021         return parseBorderRadius(propId, important);
1022     case CSSPropertyOutlineOffset:
1023         validPrimitive = validUnit(value, FLength);
1024         break;
1025     case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3
1026     case CSSPropertyBoxShadow:
1027     case CSSPropertyWebkitBoxShadow:
1028         if (id == CSSValueNone)
1029             validPrimitive = true;
1030         else {
1031             RefPtrWillBeRawPtr<CSSValueList> shadowValueList = parseShadow(m_valueList, propId);
1032             if (shadowValueList) {
1033                 addProperty(propId, shadowValueList.release(), important);
1034                 m_valueList->next();
1035                 return true;
1036             }
1037             return false;
1038         }
1039         break;
1040     case CSSPropertyWebkitBoxReflect:
1041         if (id == CSSValueNone)
1042             validPrimitive = true;
1043         else
1044             parsedValue = parseReflect();
1045         break;
1046     case CSSPropertyOpacity:
1047         validPrimitive = validUnit(value, FNumber);
1048         break;
1049     case CSSPropertyWebkitBoxFlex:
1050         validPrimitive = validUnit(value, FNumber);
1051         break;
1052     case CSSPropertyWebkitBoxFlexGroup:
1053         validPrimitive = validUnit(value, FInteger | FNonNeg);
1054         break;
1055     case CSSPropertyWebkitBoxOrdinalGroup:
1056         validPrimitive = validUnit(value, FInteger | FNonNeg) && value->fValue;
1057         break;
1058     case CSSPropertyWebkitFilter:
1059         if (id == CSSValueNone)
1060             validPrimitive = true;
1061         else {
1062             RefPtrWillBeRawPtr<CSSValue> val = parseFilter();
1063             if (val) {
1064                 addProperty(propId, val, important);
1065                 return true;
1066             }
1067             return false;
1068         }
1069         break;
1070     case CSSPropertyFlex: {
1071         ShorthandScope scope(this, propId);
1072         if (id == CSSValueNone) {
1073             addProperty(CSSPropertyFlexGrow, cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER), important);
1074             addProperty(CSSPropertyFlexShrink, cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER), important);
1075             addProperty(CSSPropertyFlexBasis, cssValuePool().createIdentifierValue(CSSValueAuto), important);
1076             return true;
1077         }
1078         return parseFlex(m_valueList, important);
1079     }
1080     case CSSPropertyFlexBasis:
1081         // FIXME: Support intrinsic dimensions too.
1082         if (id == CSSValueAuto)
1083             validPrimitive = true;
1084         else
1085             validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg));
1086         break;
1087     case CSSPropertyFlexGrow:
1088     case CSSPropertyFlexShrink:
1089         validPrimitive = validUnit(value, FNumber | FNonNeg);
1090         break;
1091     case CSSPropertyOrder:
1092         validPrimitive = validUnit(value, FInteger);
1093         break;
1094     case CSSPropertyInternalMarqueeIncrement:
1095         validPrimitive = validUnit(value, FLength | FPercent);
1096         break;
1097     case CSSPropertyInternalMarqueeRepetition:
1098         if (id == CSSValueInfinite)
1099             validPrimitive = true;
1100         else
1101             validPrimitive = validUnit(value, FInteger | FNonNeg);
1102         break;
1103     case CSSPropertyInternalMarqueeSpeed:
1104         validPrimitive = validUnit(value, FInteger | FNonNeg);
1105         break;
1106     case CSSPropertyTransform:
1107     case CSSPropertyWebkitTransform:
1108         if (id == CSSValueNone)
1109             validPrimitive = true;
1110         else {
1111             RefPtrWillBeRawPtr<CSSValue> transformValue = parseTransform(propId);
1112             if (transformValue) {
1113                 addProperty(propId, transformValue.release(), important);
1114                 return true;
1115             }
1116             return false;
1117         }
1118         break;
1119     case CSSPropertyTransformOrigin: {
1120         RefPtrWillBeRawPtr<CSSValueList> list = parseTransformOrigin();
1121         if (!list)
1122             return false;
1123         // These values are added to match gecko serialization.
1124         if (list->length() == 1)
1125             list->append(cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE));
1126         if (list->length() == 2)
1127             list->append(cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX));
1128         addProperty(propId, list.release(), important);
1129         return true;
1130     }
1131     case CSSPropertyWebkitPerspectiveOriginX:
1132     case CSSPropertyWebkitTransformOriginX:
1133         parsedValue = parseFillPositionX(m_valueList);
1134         if (parsedValue)
1135             m_valueList->next();
1136         break;
1137     case CSSPropertyWebkitPerspectiveOriginY:
1138     case CSSPropertyWebkitTransformOriginY:
1139         parsedValue = parseFillPositionY(m_valueList);
1140         if (parsedValue)
1141             m_valueList->next();
1142         break;
1143     case CSSPropertyWebkitTransformOriginZ:
1144         validPrimitive = validUnit(value, FLength);
1145         break;
1146     case CSSPropertyWebkitTransformOrigin:
1147         return parseWebkitTransformOriginShorthand(important);
1148     case CSSPropertyPerspective:
1149         if (id == CSSValueNone) {
1150             validPrimitive = true;
1151         } else if (validUnit(value, FLength | FNonNeg)) {
1152             addProperty(propId, createPrimitiveNumericValue(value), important);
1153             return true;
1154         }
1155         break;
1156     case CSSPropertyWebkitPerspective:
1157         if (id == CSSValueNone) {
1158             validPrimitive = true;
1159         } else if (validUnit(value, FNumber | FLength | FNonNeg)) {
1160             // Accepting valueless numbers is a quirk of the -webkit prefixed version of the property.
1161             addProperty(propId, createPrimitiveNumericValue(value), important);
1162             return true;
1163         }
1164         break;
1165     case CSSPropertyPerspectiveOrigin:
1166     case CSSPropertyWebkitPerspectiveOrigin: {
1167         RefPtrWillBeRawPtr<CSSValueList> list = parseTransformOrigin();
1168         if (!list || list->length() == 3)
1169             return false;
1170         // This values are added to match gecko serialization.
1171         if (list->length() == 1)
1172             list->append(cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE));
1173         addProperty(propId, list.release(), important);
1174         return true;
1175     }
1176     case CSSPropertyAnimationDelay:
1177     case CSSPropertyAnimationDirection:
1178     case CSSPropertyAnimationDuration:
1179     case CSSPropertyAnimationFillMode:
1180     case CSSPropertyAnimationName:
1181     case CSSPropertyAnimationPlayState:
1182     case CSSPropertyAnimationIterationCount:
1183     case CSSPropertyAnimationTimingFunction:
1184         ASSERT(RuntimeEnabledFeatures::cssAnimationUnprefixedEnabled());
1185     case CSSPropertyWebkitAnimationDelay:
1186     case CSSPropertyWebkitAnimationDirection:
1187     case CSSPropertyWebkitAnimationDuration:
1188     case CSSPropertyWebkitAnimationFillMode:
1189     case CSSPropertyWebkitAnimationName:
1190     case CSSPropertyWebkitAnimationPlayState:
1191     case CSSPropertyWebkitAnimationIterationCount:
1192     case CSSPropertyWebkitAnimationTimingFunction:
1193     case CSSPropertyTransitionDelay:
1194     case CSSPropertyTransitionDuration:
1195     case CSSPropertyTransitionTimingFunction:
1196     case CSSPropertyTransitionProperty:
1197     case CSSPropertyWebkitTransitionDelay:
1198     case CSSPropertyWebkitTransitionDuration:
1199     case CSSPropertyWebkitTransitionTimingFunction:
1200     case CSSPropertyWebkitTransitionProperty: {
1201         if (RefPtrWillBeRawPtr<CSSValueList> val = parseAnimationPropertyList(propId)) {
1202             addPropertyWithPrefixingVariant(propId, val.release(), important);
1203             return true;
1204         }
1205         return false;
1206     }
1207 
1208     case CSSPropertyJustifySelf:
1209         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1210         return parseItemPositionOverflowPosition(propId, important);
1211     case CSSPropertyJustifyItems:
1212         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1213 
1214         if (parseLegacyPosition(propId, important))
1215             return true;
1216 
1217         m_valueList->setCurrentIndex(0);
1218         return parseItemPositionOverflowPosition(propId, important);
1219     case CSSPropertyGridAutoFlow:
1220         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1221         parsedValue = parseGridAutoFlow(*m_valueList);
1222         break;
1223     case CSSPropertyGridAutoColumns:
1224     case CSSPropertyGridAutoRows:
1225         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1226         parsedValue = parseGridTrackSize(*m_valueList);
1227         break;
1228 
1229     case CSSPropertyGridTemplateColumns:
1230     case CSSPropertyGridTemplateRows:
1231         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1232         parsedValue = parseGridTrackList();
1233         break;
1234 
1235     case CSSPropertyGridColumnEnd:
1236     case CSSPropertyGridColumnStart:
1237     case CSSPropertyGridRowEnd:
1238     case CSSPropertyGridRowStart:
1239         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1240         parsedValue = parseGridPosition();
1241         break;
1242 
1243     case CSSPropertyGridColumn:
1244     case CSSPropertyGridRow:
1245         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1246         return parseGridItemPositionShorthand(propId, important);
1247 
1248     case CSSPropertyGridArea:
1249         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1250         return parseGridAreaShorthand(important);
1251 
1252     case CSSPropertyGridTemplateAreas:
1253         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1254         parsedValue = parseGridTemplateAreas();
1255         break;
1256 
1257     case CSSPropertyGridTemplate:
1258         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1259         return parseGridTemplateShorthand(important);
1260 
1261     case CSSPropertyGrid:
1262         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1263         return parseGridShorthand(important);
1264 
1265     case CSSPropertyWebkitMarginCollapse: {
1266         if (num == 1) {
1267             ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse);
1268             if (!parseValue(webkitMarginCollapseShorthand().properties()[0], important))
1269                 return false;
1270             CSSValue* value = m_parsedProperties.last().value();
1271             addProperty(webkitMarginCollapseShorthand().properties()[1], value, important);
1272             return true;
1273         }
1274         else if (num == 2) {
1275             ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse);
1276             if (!parseValue(webkitMarginCollapseShorthand().properties()[0], important) || !parseValue(webkitMarginCollapseShorthand().properties()[1], important))
1277                 return false;
1278             return true;
1279         }
1280         return false;
1281     }
1282     case CSSPropertyWebkitColumnCount:
1283         parsedValue = parseColumnCount();
1284         break;
1285     case CSSPropertyWebkitColumnGap:         // normal | <length>
1286         if (id == CSSValueNormal)
1287             validPrimitive = true;
1288         else
1289             validPrimitive = validUnit(value, FLength | FNonNeg);
1290         break;
1291     case CSSPropertyWebkitColumnSpan: // none | all | 1 (will be dropped in the unprefixed property)
1292         validPrimitive = id == CSSValueAll || id == CSSValueNone || (value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue == 1);
1293         break;
1294     case CSSPropertyWebkitColumnWidth:         // auto | <length>
1295         parsedValue = parseColumnWidth();
1296         break;
1297     case CSSPropertyWillChange:
1298         parsedValue = parseWillChange();
1299         break;
1300     // End of CSS3 properties
1301 
1302     // Apple specific properties.  These will never be standardized and are purely to
1303     // support custom WebKit-based Apple applications.
1304     case CSSPropertyWebkitLineClamp:
1305         // When specifying number of lines, don't allow 0 as a valid value
1306         // When specifying either type of unit, require non-negative integers
1307         validPrimitive = (!id && (value->unit == CSSPrimitiveValue::CSS_PERCENTAGE || value->fValue) && validUnit(value, FInteger | FPercent | FNonNeg));
1308         break;
1309 
1310     case CSSPropertyWebkitFontSizeDelta:           // <length>
1311         validPrimitive = validUnit(value, FLength);
1312         break;
1313 
1314     case CSSPropertyWebkitHighlight:
1315         if (id == CSSValueNone || value->unit == CSSPrimitiveValue::CSS_STRING)
1316             validPrimitive = true;
1317         break;
1318 
1319     case CSSPropertyWebkitHyphenateCharacter:
1320         if (id == CSSValueAuto || value->unit == CSSPrimitiveValue::CSS_STRING)
1321             validPrimitive = true;
1322         break;
1323 
1324     case CSSPropertyWebkitLocale:
1325         if (id == CSSValueAuto || value->unit == CSSPrimitiveValue::CSS_STRING)
1326             validPrimitive = true;
1327         break;
1328 
1329     // End Apple-specific properties
1330 
1331     case CSSPropertyWebkitAppRegion:
1332         if (id >= CSSValueDrag && id <= CSSValueNoDrag)
1333             validPrimitive = true;
1334         break;
1335 
1336     case CSSPropertyWebkitTapHighlightColor:
1337         if ((id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu
1338             || (id >= CSSValueWebkitFocusRingColor && id < CSSValueWebkitText && inQuirksMode())) {
1339             validPrimitive = true;
1340         } else {
1341             parsedValue = parseColor();
1342             if (parsedValue)
1343                 m_valueList->next();
1344         }
1345         break;
1346 
1347         /* shorthand properties */
1348     case CSSPropertyBackground: {
1349         // Position must come before color in this array because a plain old "0" is a legal color
1350         // in quirks mode but it's usually the X coordinate of a position.
1351         const CSSPropertyID properties[] = { CSSPropertyBackgroundImage, CSSPropertyBackgroundRepeat,
1352                                    CSSPropertyBackgroundAttachment, CSSPropertyBackgroundPosition, CSSPropertyBackgroundOrigin,
1353                                    CSSPropertyBackgroundClip, CSSPropertyBackgroundColor, CSSPropertyBackgroundSize };
1354         return parseFillShorthand(propId, properties, WTF_ARRAY_LENGTH(properties), important);
1355     }
1356     case CSSPropertyWebkitMask: {
1357         const CSSPropertyID properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskRepeat,
1358             CSSPropertyWebkitMaskPosition, CSSPropertyWebkitMaskOrigin, CSSPropertyWebkitMaskClip, CSSPropertyWebkitMaskSize };
1359         return parseFillShorthand(propId, properties, WTF_ARRAY_LENGTH(properties), important);
1360     }
1361     case CSSPropertyBorder:
1362         // [ 'border-width' || 'border-style' || <color> ] | inherit
1363     {
1364         if (parseShorthand(propId, parsingShorthandForProperty(CSSPropertyBorder), important)) {
1365             // The CSS3 Borders and Backgrounds specification says that border also resets border-image. It's as
1366             // though a value of none was specified for the image.
1367             addExpandedPropertyForValue(CSSPropertyBorderImage, cssValuePool().createImplicitInitialValue(), important);
1368             return true;
1369         }
1370         return false;
1371     }
1372     case CSSPropertyBorderTop:
1373         // [ 'border-top-width' || 'border-style' || <color> ] | inherit
1374         return parseShorthand(propId, borderTopShorthand(), important);
1375     case CSSPropertyBorderRight:
1376         // [ 'border-right-width' || 'border-style' || <color> ] | inherit
1377         return parseShorthand(propId, borderRightShorthand(), important);
1378     case CSSPropertyBorderBottom:
1379         // [ 'border-bottom-width' || 'border-style' || <color> ] | inherit
1380         return parseShorthand(propId, borderBottomShorthand(), important);
1381     case CSSPropertyBorderLeft:
1382         // [ 'border-left-width' || 'border-style' || <color> ] | inherit
1383         return parseShorthand(propId, borderLeftShorthand(), important);
1384     case CSSPropertyWebkitBorderStart:
1385         return parseShorthand(propId, webkitBorderStartShorthand(), important);
1386     case CSSPropertyWebkitBorderEnd:
1387         return parseShorthand(propId, webkitBorderEndShorthand(), important);
1388     case CSSPropertyWebkitBorderBefore:
1389         return parseShorthand(propId, webkitBorderBeforeShorthand(), important);
1390     case CSSPropertyWebkitBorderAfter:
1391         return parseShorthand(propId, webkitBorderAfterShorthand(), important);
1392     case CSSPropertyOutline:
1393         // [ 'outline-color' || 'outline-style' || 'outline-width' ] | inherit
1394         return parseShorthand(propId, outlineShorthand(), important);
1395     case CSSPropertyBorderColor:
1396         // <color>{1,4} | inherit
1397         return parse4Values(propId, borderColorShorthand().properties(), important);
1398     case CSSPropertyBorderWidth:
1399         // <border-width>{1,4} | inherit
1400         return parse4Values(propId, borderWidthShorthand().properties(), important);
1401     case CSSPropertyBorderStyle:
1402         // <border-style>{1,4} | inherit
1403         return parse4Values(propId, borderStyleShorthand().properties(), important);
1404     case CSSPropertyMargin:
1405         // <margin-width>{1,4} | inherit
1406         return parse4Values(propId, marginShorthand().properties(), important);
1407     case CSSPropertyPadding:
1408         // <padding-width>{1,4} | inherit
1409         return parse4Values(propId, paddingShorthand().properties(), important);
1410     case CSSPropertyFlexFlow:
1411         return parseShorthand(propId, flexFlowShorthand(), important);
1412     case CSSPropertyFont:
1413         // [ [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]?
1414         // 'font-family' ] | caption | icon | menu | message-box | small-caption | status-bar | inherit
1415         if (id >= CSSValueCaption && id <= CSSValueStatusBar)
1416             validPrimitive = true;
1417         else
1418             return parseFont(important);
1419         break;
1420     case CSSPropertyListStyle:
1421         return parseShorthand(propId, listStyleShorthand(), important);
1422     case CSSPropertyWebkitColumns:
1423         return parseColumnsShorthand(important);
1424     case CSSPropertyWebkitColumnRule:
1425         return parseShorthand(propId, webkitColumnRuleShorthand(), important);
1426     case CSSPropertyWebkitTextStroke:
1427         return parseShorthand(propId, webkitTextStrokeShorthand(), important);
1428     case CSSPropertyAnimation:
1429         ASSERT(RuntimeEnabledFeatures::cssAnimationUnprefixedEnabled());
1430     case CSSPropertyWebkitAnimation:
1431         return parseAnimationShorthand(propId, important);
1432     case CSSPropertyTransition:
1433     case CSSPropertyWebkitTransition:
1434         return parseTransitionShorthand(propId, important);
1435     case CSSPropertyInvalid:
1436         return false;
1437     case CSSPropertyPage:
1438         return parsePage(propId, important);
1439     // CSS Text Layout Module Level 3: Vertical writing support
1440     case CSSPropertyWebkitTextEmphasis:
1441         return parseShorthand(propId, webkitTextEmphasisShorthand(), important);
1442 
1443     case CSSPropertyWebkitTextEmphasisStyle:
1444         return parseTextEmphasisStyle(important);
1445 
1446     case CSSPropertyWebkitTextOrientation:
1447         // FIXME: For now just support sideways, sideways-right, upright and vertical-right.
1448         if (id == CSSValueSideways || id == CSSValueSidewaysRight || id == CSSValueVerticalRight || id == CSSValueUpright)
1449             validPrimitive = true;
1450         break;
1451 
1452     case CSSPropertyWebkitLineBoxContain:
1453         if (id == CSSValueNone)
1454             validPrimitive = true;
1455         else
1456             return parseLineBoxContain(important);
1457         break;
1458     case CSSPropertyWebkitFontFeatureSettings:
1459         if (id == CSSValueNormal)
1460             validPrimitive = true;
1461         else
1462             return parseFontFeatureSettings(important);
1463         break;
1464 
1465     case CSSPropertyFontVariantLigatures:
1466         if (id == CSSValueNormal)
1467             validPrimitive = true;
1468         else
1469             return parseFontVariantLigatures(important);
1470         break;
1471     case CSSPropertyWebkitClipPath:
1472         if (id == CSSValueNone) {
1473             validPrimitive = true;
1474         } else if (value->unit == CSSParserValue::Function) {
1475             parsedValue = parseBasicShape();
1476         } else if (value->unit == CSSPrimitiveValue::CSS_URI) {
1477             parsedValue = CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_URI);
1478             addProperty(propId, parsedValue.release(), important);
1479             return true;
1480         }
1481         break;
1482     case CSSPropertyShapeOutside:
1483         parsedValue = parseShapeProperty(propId);
1484         break;
1485     case CSSPropertyShapeMargin:
1486         validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg));
1487         break;
1488     case CSSPropertyShapeImageThreshold:
1489         validPrimitive = (!id && validUnit(value, FNumber));
1490         break;
1491 
1492     case CSSPropertyTouchAction:
1493         parsedValue = parseTouchAction();
1494         break;
1495 
1496     case CSSPropertyAlignSelf:
1497         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1498         return parseItemPositionOverflowPosition(propId, important);
1499 
1500     case CSSPropertyAlignItems:
1501         ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
1502         return parseItemPositionOverflowPosition(propId, important);
1503 
1504     // Properties below are validated inside parseViewportProperty, because we
1505     // check for parser state. We need to invalidate if someone adds them outside
1506     // a @viewport rule.
1507     case CSSPropertyMaxZoom:
1508     case CSSPropertyMinZoom:
1509     case CSSPropertyOrientation:
1510     case CSSPropertyUserZoom:
1511         validPrimitive = false;
1512         break;
1513 
1514     default:
1515         return parseSVGValue(propId, important);
1516     }
1517 
1518     if (validPrimitive) {
1519         parsedValue = parseValidPrimitive(id, value);
1520         m_valueList->next();
1521     }
1522     ASSERT(!m_parsedCalculation);
1523     if (parsedValue) {
1524         if (!m_valueList->current() || inShorthand()) {
1525             addProperty(propId, parsedValue.release(), important);
1526             return true;
1527         }
1528     }
1529     return false;
1530 }
1531 
addFillValue(RefPtrWillBeRawPtr<CSSValue> & lval,PassRefPtrWillBeRawPtr<CSSValue> rval)1532 void CSSPropertyParser::addFillValue(RefPtrWillBeRawPtr<CSSValue>& lval, PassRefPtrWillBeRawPtr<CSSValue> rval)
1533 {
1534     if (lval) {
1535         if (lval->isBaseValueList())
1536             toCSSValueList(lval.get())->append(rval);
1537         else {
1538             PassRefPtrWillBeRawPtr<CSSValue> oldlVal(lval.release());
1539             PassRefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
1540             list->append(oldlVal);
1541             list->append(rval);
1542             lval = list;
1543         }
1544     }
1545     else
1546         lval = rval;
1547 }
1548 
parseBackgroundClip(CSSParserValue * parserValue,RefPtrWillBeRawPtr<CSSValue> & cssValue)1549 static bool parseBackgroundClip(CSSParserValue* parserValue, RefPtrWillBeRawPtr<CSSValue>& cssValue)
1550 {
1551     if (parserValue->id == CSSValueBorderBox || parserValue->id == CSSValuePaddingBox
1552         || parserValue->id == CSSValueContentBox || parserValue->id == CSSValueWebkitText) {
1553         cssValue = cssValuePool().createIdentifierValue(parserValue->id);
1554         return true;
1555     }
1556     return false;
1557 }
1558 
1559 const int cMaxFillProperties = 9;
1560 
parseFillShorthand(CSSPropertyID propId,const CSSPropertyID * properties,int numProperties,bool important)1561 bool CSSPropertyParser::parseFillShorthand(CSSPropertyID propId, const CSSPropertyID* properties, int numProperties, bool important)
1562 {
1563     ASSERT(numProperties <= cMaxFillProperties);
1564     if (numProperties > cMaxFillProperties)
1565         return false;
1566 
1567     ShorthandScope scope(this, propId);
1568 
1569     bool parsedProperty[cMaxFillProperties] = { false };
1570     RefPtrWillBeRawPtr<CSSValue> values[cMaxFillProperties];
1571 #if ENABLE(OILPAN)
1572     // Zero initialize the array of raw pointers.
1573     memset(&values, 0, sizeof(values));
1574 #endif
1575     RefPtrWillBeRawPtr<CSSValue> clipValue = nullptr;
1576     RefPtrWillBeRawPtr<CSSValue> positionYValue = nullptr;
1577     RefPtrWillBeRawPtr<CSSValue> repeatYValue = nullptr;
1578     bool foundClip = false;
1579     int i;
1580     bool foundPositionCSSProperty = false;
1581 
1582     while (m_valueList->current()) {
1583         CSSParserValue* val = m_valueList->current();
1584         if (val->unit == CSSParserValue::Operator && val->iValue == ',') {
1585             // We hit the end.  Fill in all remaining values with the initial value.
1586             m_valueList->next();
1587             for (i = 0; i < numProperties; ++i) {
1588                 if (properties[i] == CSSPropertyBackgroundColor && parsedProperty[i])
1589                     // Color is not allowed except as the last item in a list for backgrounds.
1590                     // Reject the entire property.
1591                     return false;
1592 
1593                 if (!parsedProperty[i] && properties[i] != CSSPropertyBackgroundColor) {
1594                     addFillValue(values[i], cssValuePool().createImplicitInitialValue());
1595                     if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition)
1596                         addFillValue(positionYValue, cssValuePool().createImplicitInitialValue());
1597                     if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat)
1598                         addFillValue(repeatYValue, cssValuePool().createImplicitInitialValue());
1599                     if ((properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) && !parsedProperty[i]) {
1600                         // If background-origin wasn't present, then reset background-clip also.
1601                         addFillValue(clipValue, cssValuePool().createImplicitInitialValue());
1602                     }
1603                 }
1604                 parsedProperty[i] = false;
1605             }
1606             if (!m_valueList->current())
1607                 break;
1608         }
1609 
1610         bool sizeCSSPropertyExpected = false;
1611         if (isForwardSlashOperator(val) && foundPositionCSSProperty) {
1612             sizeCSSPropertyExpected = true;
1613             m_valueList->next();
1614         }
1615 
1616         foundPositionCSSProperty = false;
1617         bool found = false;
1618         for (i = 0; !found && i < numProperties; ++i) {
1619 
1620             if (sizeCSSPropertyExpected && (properties[i] != CSSPropertyBackgroundSize && properties[i] != CSSPropertyWebkitMaskSize))
1621                 continue;
1622             if (!sizeCSSPropertyExpected && (properties[i] == CSSPropertyBackgroundSize || properties[i] == CSSPropertyWebkitMaskSize))
1623                 continue;
1624 
1625             if (!parsedProperty[i]) {
1626                 RefPtrWillBeRawPtr<CSSValue> val1 = nullptr;
1627                 RefPtrWillBeRawPtr<CSSValue> val2 = nullptr;
1628                 CSSPropertyID propId1, propId2;
1629                 CSSParserValue* parserValue = m_valueList->current();
1630                 // parseFillProperty() may modify m_implicitShorthand, so we MUST reset it
1631                 // before EACH return below.
1632                 if (parseFillProperty(properties[i], propId1, propId2, val1, val2)) {
1633                     parsedProperty[i] = found = true;
1634                     addFillValue(values[i], val1.release());
1635                     if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition)
1636                         addFillValue(positionYValue, val2.release());
1637                     if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat)
1638                         addFillValue(repeatYValue, val2.release());
1639                     if (properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) {
1640                         // Reparse the value as a clip, and see if we succeed.
1641                         if (parseBackgroundClip(parserValue, val1))
1642                             addFillValue(clipValue, val1.release()); // The property parsed successfully.
1643                         else
1644                             addFillValue(clipValue, cssValuePool().createImplicitInitialValue()); // Some value was used for origin that is not supported by clip. Just reset clip instead.
1645                     }
1646                     if (properties[i] == CSSPropertyBackgroundClip || properties[i] == CSSPropertyWebkitMaskClip) {
1647                         // Update clipValue
1648                         addFillValue(clipValue, val1.release());
1649                         foundClip = true;
1650                     }
1651                     if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition)
1652                         foundPositionCSSProperty = true;
1653                 }
1654             }
1655         }
1656 
1657         // if we didn't find at least one match, this is an
1658         // invalid shorthand and we have to ignore it
1659         if (!found) {
1660             m_implicitShorthand = false;
1661             return false;
1662         }
1663     }
1664 
1665     // Now add all of the properties we found.
1666     for (i = 0; i < numProperties; i++) {
1667         // Fill in any remaining properties with the initial value.
1668         if (!parsedProperty[i]) {
1669             addFillValue(values[i], cssValuePool().createImplicitInitialValue());
1670             if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition)
1671                 addFillValue(positionYValue, cssValuePool().createImplicitInitialValue());
1672             if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat)
1673                 addFillValue(repeatYValue, cssValuePool().createImplicitInitialValue());
1674             if (properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) {
1675                 // If background-origin wasn't present, then reset background-clip also.
1676                 addFillValue(clipValue, cssValuePool().createImplicitInitialValue());
1677             }
1678         }
1679         if (properties[i] == CSSPropertyBackgroundPosition) {
1680             addProperty(CSSPropertyBackgroundPositionX, values[i].release(), important);
1681             // it's OK to call positionYValue.release() since we only see CSSPropertyBackgroundPosition once
1682             addProperty(CSSPropertyBackgroundPositionY, positionYValue.release(), important);
1683         } else if (properties[i] == CSSPropertyWebkitMaskPosition) {
1684             addProperty(CSSPropertyWebkitMaskPositionX, values[i].release(), important);
1685             // it's OK to call positionYValue.release() since we only see CSSPropertyWebkitMaskPosition once
1686             addProperty(CSSPropertyWebkitMaskPositionY, positionYValue.release(), important);
1687         } else if (properties[i] == CSSPropertyBackgroundRepeat) {
1688             addProperty(CSSPropertyBackgroundRepeatX, values[i].release(), important);
1689             // it's OK to call repeatYValue.release() since we only see CSSPropertyBackgroundPosition once
1690             addProperty(CSSPropertyBackgroundRepeatY, repeatYValue.release(), important);
1691         } else if (properties[i] == CSSPropertyWebkitMaskRepeat) {
1692             addProperty(CSSPropertyWebkitMaskRepeatX, values[i].release(), important);
1693             // it's OK to call repeatYValue.release() since we only see CSSPropertyBackgroundPosition once
1694             addProperty(CSSPropertyWebkitMaskRepeatY, repeatYValue.release(), important);
1695         } else if ((properties[i] == CSSPropertyBackgroundClip || properties[i] == CSSPropertyWebkitMaskClip) && !foundClip)
1696             // Value is already set while updating origin
1697             continue;
1698         else if (properties[i] == CSSPropertyBackgroundSize && !parsedProperty[i] && m_context.useLegacyBackgroundSizeShorthandBehavior())
1699             continue;
1700         else
1701             addProperty(properties[i], values[i].release(), important);
1702 
1703         // Add in clip values when we hit the corresponding origin property.
1704         if (properties[i] == CSSPropertyBackgroundOrigin && !foundClip)
1705             addProperty(CSSPropertyBackgroundClip, clipValue.release(), important);
1706         else if (properties[i] == CSSPropertyWebkitMaskOrigin && !foundClip)
1707             addProperty(CSSPropertyWebkitMaskClip, clipValue.release(), important);
1708     }
1709 
1710     m_implicitShorthand = false;
1711     return true;
1712 }
1713 
isValidTransitionPropertyList(CSSValueList * value)1714 static bool isValidTransitionPropertyList(CSSValueList* value)
1715 {
1716     if (value->length() < 2)
1717         return true;
1718     for (CSSValueListIterator i = value; i.hasMore(); i.advance()) {
1719         // FIXME: Shorthand parsing shouldn't add initial to the list since it won't round-trip
1720         if (i.value()->isInitialValue())
1721             continue;
1722         CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(i.value());
1723         if (primitiveValue->isValueID() && primitiveValue->getValueID() == CSSValueNone)
1724             return false;
1725     }
1726     return true;
1727 }
1728 
parseAnimationShorthand(CSSPropertyID propId,bool important)1729 bool CSSPropertyParser::parseAnimationShorthand(CSSPropertyID propId, bool important)
1730 {
1731     const StylePropertyShorthand& animationProperties = parsingShorthandForProperty(propId);
1732     const unsigned numProperties = 8;
1733 
1734     // The list of properties in the shorthand should be the same
1735     // length as the list with animation name in last position, even though they are
1736     // in a different order.
1737     ASSERT(numProperties == animationProperties.length());
1738     ASSERT(numProperties == shorthandForProperty(propId).length());
1739 
1740     ShorthandScope scope(this, propId);
1741 
1742     bool parsedProperty[numProperties] = { false };
1743     RefPtrWillBeRawPtr<CSSValueList> values[numProperties];
1744     for (size_t i = 0; i < numProperties; ++i)
1745         values[i] = CSSValueList::createCommaSeparated();
1746 
1747     while (m_valueList->current()) {
1748         if (consumeComma(m_valueList)) {
1749             // We hit the end. Fill in all remaining values with the initial value.
1750             for (size_t i = 0; i < numProperties; ++i) {
1751                 if (!parsedProperty[i])
1752                     values[i]->append(cssValuePool().createImplicitInitialValue());
1753                 parsedProperty[i] = false;
1754             }
1755             if (!m_valueList->current())
1756                 break;
1757         }
1758 
1759         bool found = false;
1760         for (size_t i = 0; i < numProperties; ++i) {
1761             if (parsedProperty[i])
1762                 continue;
1763             if (RefPtrWillBeRawPtr<CSSValue> val = parseAnimationProperty(animationProperties.properties()[i])) {
1764                 parsedProperty[i] = found = true;
1765                 values[i]->append(val.release());
1766                 break;
1767             }
1768         }
1769 
1770         // if we didn't find at least one match, this is an
1771         // invalid shorthand and we have to ignore it
1772         if (!found)
1773             return false;
1774     }
1775 
1776     for (size_t i = 0; i < numProperties; ++i) {
1777         // If we didn't find the property, set an intial value.
1778         if (!parsedProperty[i])
1779             values[i]->append(cssValuePool().createImplicitInitialValue());
1780 
1781         if (RuntimeEnabledFeatures::cssAnimationUnprefixedEnabled())
1782             addPropertyWithPrefixingVariant(animationProperties.properties()[i], values[i].release(), important);
1783         else
1784             addProperty(animationProperties.properties()[i], values[i].release(), important);
1785     }
1786 
1787     return true;
1788 }
1789 
parseTransitionShorthand(CSSPropertyID propId,bool important)1790 bool CSSPropertyParser::parseTransitionShorthand(CSSPropertyID propId, bool important)
1791 {
1792     const unsigned numProperties = 4;
1793     const StylePropertyShorthand& shorthand = parsingShorthandForProperty(propId);
1794     ASSERT(numProperties == shorthand.length());
1795 
1796     ShorthandScope scope(this, propId);
1797 
1798     bool parsedProperty[numProperties] = { false };
1799     RefPtrWillBeRawPtr<CSSValueList> values[numProperties];
1800     for (size_t i = 0; i < numProperties; ++i)
1801         values[i] = CSSValueList::createCommaSeparated();
1802 
1803     while (m_valueList->current()) {
1804         if (consumeComma(m_valueList)) {
1805             // We hit the end. Fill in all remaining values with the initial value.
1806             for (size_t i = 0; i < numProperties; ++i) {
1807                 if (!parsedProperty[i])
1808                     values[i]->append(cssValuePool().createImplicitInitialValue());
1809                 parsedProperty[i] = false;
1810             }
1811             if (!m_valueList->current())
1812                 break;
1813         }
1814 
1815         bool found = false;
1816         for (size_t i = 0; i < numProperties; ++i) {
1817             if (parsedProperty[i])
1818                 continue;
1819             if (RefPtrWillBeRawPtr<CSSValue> val = parseAnimationProperty(shorthand.properties()[i])) {
1820                 parsedProperty[i] = found = true;
1821                 values[i]->append(val.release());
1822                 break;
1823             }
1824         }
1825 
1826         // if we didn't find at least one match, this is an
1827         // invalid shorthand and we have to ignore it
1828         if (!found)
1829             return false;
1830     }
1831 
1832     ASSERT(shorthand.properties()[3] == CSSPropertyTransitionProperty || shorthand.properties()[3] == CSSPropertyWebkitTransitionProperty);
1833     if (!isValidTransitionPropertyList(values[3].get()))
1834         return false;
1835 
1836     // Fill in any remaining properties with the initial value and add
1837     for (size_t i = 0; i < numProperties; ++i) {
1838         if (!parsedProperty[i])
1839             values[i]->append(cssValuePool().createImplicitInitialValue());
1840         addPropertyWithPrefixingVariant(shorthand.properties()[i], values[i].release(), important);
1841     }
1842 
1843     return true;
1844 }
1845 
parseColumnWidth()1846 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseColumnWidth()
1847 {
1848     CSSParserValue* value = m_valueList->current();
1849     // Always parse lengths in strict mode here, since it would be ambiguous otherwise when used in
1850     // the 'columns' shorthand property.
1851     if (value->id == CSSValueAuto || (validUnit(value, FLength | FNonNeg, HTMLStandardMode) && (m_parsedCalculation || value->fValue != 0))) {
1852         RefPtrWillBeRawPtr<CSSValue> parsedValue = parseValidPrimitive(value->id, value);
1853         m_valueList->next();
1854         return parsedValue;
1855     }
1856     return nullptr;
1857 }
1858 
parseColumnCount()1859 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseColumnCount()
1860 {
1861     CSSParserValue* value = m_valueList->current();
1862     if (value->id == CSSValueAuto
1863         || (!value->id && validUnit(value, FPositiveInteger))) {
1864         RefPtrWillBeRawPtr<CSSValue> parsedValue = parseValidPrimitive(value->id, value);
1865         m_valueList->next();
1866         return parsedValue;
1867     }
1868     return nullptr;
1869 }
1870 
parseColumnsShorthand(bool important)1871 bool CSSPropertyParser::parseColumnsShorthand(bool important)
1872 {
1873     RefPtrWillBeRawPtr<CSSValue> columnWidth = nullptr;
1874     RefPtrWillBeRawPtr<CSSValue> columnCount = nullptr;
1875     bool hasPendingExplicitAuto = false;
1876 
1877     for (unsigned propertiesParsed = 0; CSSParserValue* value = m_valueList->current(); propertiesParsed++) {
1878         if (propertiesParsed >= 2)
1879             return false; // Too many values for this shorthand. Invalid declaration.
1880         if (!propertiesParsed && value->id == CSSValueAuto) {
1881             // 'auto' is a valid value for any of the two longhands, and at this point we
1882             // don't know which one(s) it is meant for. We need to see if there are other
1883             // values first.
1884             m_valueList->next();
1885             hasPendingExplicitAuto = true;
1886         } else {
1887             if (!columnWidth) {
1888                 if ((columnWidth = parseColumnWidth()))
1889                     continue;
1890             }
1891             if (!columnCount) {
1892                 if ((columnCount = parseColumnCount()))
1893                     continue;
1894             }
1895             // If we didn't find at least one match, this is an
1896             // invalid shorthand and we have to ignore it.
1897             return false;
1898         }
1899     }
1900     if (hasPendingExplicitAuto) {
1901         // Time to assign the previously skipped 'auto' value to a property. If both properties are
1902         // unassigned at this point (i.e. 'columns:auto'), it doesn't matter that much which one we
1903         // set (although it does make a slight difference to web-inspector). The one we don't set
1904         // here will get an implicit 'auto' value further down.
1905         if (!columnWidth) {
1906             columnWidth = cssValuePool().createIdentifierValue(CSSValueAuto);
1907         } else {
1908             ASSERT(!columnCount);
1909             columnCount = cssValuePool().createIdentifierValue(CSSValueAuto);
1910         }
1911     }
1912     ASSERT(columnCount || columnWidth);
1913 
1914     // Any unassigned property at this point will become implicit 'auto'.
1915     if (columnWidth)
1916         addProperty(CSSPropertyWebkitColumnWidth, columnWidth, important);
1917     else
1918         addProperty(CSSPropertyWebkitColumnWidth, cssValuePool().createIdentifierValue(CSSValueAuto), important, true /* implicit */);
1919     if (columnCount)
1920         addProperty(CSSPropertyWebkitColumnCount, columnCount, important);
1921     else
1922         addProperty(CSSPropertyWebkitColumnCount, cssValuePool().createIdentifierValue(CSSValueAuto), important, true /* implicit */);
1923     return true;
1924 }
1925 
parseShorthand(CSSPropertyID propId,const StylePropertyShorthand & shorthand,bool important)1926 bool CSSPropertyParser::parseShorthand(CSSPropertyID propId, const StylePropertyShorthand& shorthand, bool important)
1927 {
1928     // We try to match as many properties as possible
1929     // We set up an array of booleans to mark which property has been found,
1930     // and we try to search for properties until it makes no longer any sense.
1931     ShorthandScope scope(this, propId);
1932 
1933     bool found = false;
1934     unsigned propertiesParsed = 0;
1935     bool propertyFound[6] = { false, false, false, false, false, false }; // 6 is enough size.
1936 
1937     while (m_valueList->current()) {
1938         found = false;
1939         for (unsigned propIndex = 0; !found && propIndex < shorthand.length(); ++propIndex) {
1940             if (!propertyFound[propIndex] && parseValue(shorthand.properties()[propIndex], important)) {
1941                 propertyFound[propIndex] = found = true;
1942                 propertiesParsed++;
1943             }
1944         }
1945 
1946         // if we didn't find at least one match, this is an
1947         // invalid shorthand and we have to ignore it
1948         if (!found)
1949             return false;
1950     }
1951 
1952     if (propertiesParsed == shorthand.length())
1953         return true;
1954 
1955     // Fill in any remaining properties with the initial value.
1956     ImplicitScope implicitScope(this);
1957     const StylePropertyShorthand* const* const propertiesForInitialization = shorthand.propertiesForInitialization();
1958     for (unsigned i = 0; i < shorthand.length(); ++i) {
1959         if (propertyFound[i])
1960             continue;
1961 
1962         if (propertiesForInitialization) {
1963             const StylePropertyShorthand& initProperties = *(propertiesForInitialization[i]);
1964             for (unsigned propIndex = 0; propIndex < initProperties.length(); ++propIndex)
1965                 addProperty(initProperties.properties()[propIndex], cssValuePool().createImplicitInitialValue(), important);
1966         } else
1967             addProperty(shorthand.properties()[i], cssValuePool().createImplicitInitialValue(), important);
1968     }
1969 
1970     return true;
1971 }
1972 
parse4Values(CSSPropertyID propId,const CSSPropertyID * properties,bool important)1973 bool CSSPropertyParser::parse4Values(CSSPropertyID propId, const CSSPropertyID *properties,  bool important)
1974 {
1975     /* From the CSS 2 specs, 8.3
1976      * If there is only one value, it applies to all sides. If there are two values, the top and
1977      * bottom margins are set to the first value and the right and left margins are set to the second.
1978      * If there are three values, the top is set to the first value, the left and right are set to the
1979      * second, and the bottom is set to the third. If there are four values, they apply to the top,
1980      * right, bottom, and left, respectively.
1981      */
1982 
1983     int num = inShorthand() ? 1 : m_valueList->size();
1984 
1985     ShorthandScope scope(this, propId);
1986 
1987     // the order is top, right, bottom, left
1988     switch (num) {
1989         case 1: {
1990             if (!parseValue(properties[0], important))
1991                 return false;
1992             CSSValue* value = m_parsedProperties.last().value();
1993             ImplicitScope implicitScope(this);
1994             addProperty(properties[1], value, important);
1995             addProperty(properties[2], value, important);
1996             addProperty(properties[3], value, important);
1997             break;
1998         }
1999         case 2: {
2000             if (!parseValue(properties[0], important) || !parseValue(properties[1], important))
2001                 return false;
2002             CSSValue* value = m_parsedProperties[m_parsedProperties.size() - 2].value();
2003             ImplicitScope implicitScope(this);
2004             addProperty(properties[2], value, important);
2005             value = m_parsedProperties[m_parsedProperties.size() - 2].value();
2006             addProperty(properties[3], value, important);
2007             break;
2008         }
2009         case 3: {
2010             if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || !parseValue(properties[2], important))
2011                 return false;
2012             CSSValue* value = m_parsedProperties[m_parsedProperties.size() - 2].value();
2013             ImplicitScope implicitScope(this);
2014             addProperty(properties[3], value, important);
2015             break;
2016         }
2017         case 4: {
2018             if (!parseValue(properties[0], important) || !parseValue(properties[1], important) ||
2019                 !parseValue(properties[2], important) || !parseValue(properties[3], important))
2020                 return false;
2021             break;
2022         }
2023         default: {
2024             return false;
2025         }
2026     }
2027 
2028     return true;
2029 }
2030 
2031 // auto | <identifier>
parsePage(CSSPropertyID propId,bool important)2032 bool CSSPropertyParser::parsePage(CSSPropertyID propId, bool important)
2033 {
2034     ASSERT(propId == CSSPropertyPage);
2035 
2036     if (m_valueList->size() != 1)
2037         return false;
2038 
2039     CSSParserValue* value = m_valueList->current();
2040     if (!value)
2041         return false;
2042 
2043     if (value->id == CSSValueAuto) {
2044         addProperty(propId, cssValuePool().createIdentifierValue(value->id), important);
2045         return true;
2046     } else if (value->id == 0 && value->unit == CSSPrimitiveValue::CSS_IDENT) {
2047         addProperty(propId, createPrimitiveStringValue(value), important);
2048         return true;
2049     }
2050     return false;
2051 }
2052 
2053 // <length>{1,2} | auto | [ <page-size> || [ portrait | landscape] ]
parseSize(CSSPropertyID propId,bool important)2054 bool CSSPropertyParser::parseSize(CSSPropertyID propId, bool important)
2055 {
2056     ASSERT(propId == CSSPropertySize);
2057 
2058     if (m_valueList->size() > 2)
2059         return false;
2060 
2061     CSSParserValue* value = m_valueList->current();
2062     if (!value)
2063         return false;
2064 
2065     RefPtrWillBeRawPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
2066 
2067     // First parameter.
2068     SizeParameterType paramType = parseSizeParameter(parsedValues.get(), value, None);
2069     if (paramType == None)
2070         return false;
2071 
2072     // Second parameter, if any.
2073     value = m_valueList->next();
2074     if (value) {
2075         paramType = parseSizeParameter(parsedValues.get(), value, paramType);
2076         if (paramType == None)
2077             return false;
2078     }
2079 
2080     addProperty(propId, parsedValues.release(), important);
2081     return true;
2082 }
2083 
parseSizeParameter(CSSValueList * parsedValues,CSSParserValue * value,SizeParameterType prevParamType)2084 CSSPropertyParser::SizeParameterType CSSPropertyParser::parseSizeParameter(CSSValueList* parsedValues, CSSParserValue* value, SizeParameterType prevParamType)
2085 {
2086     switch (value->id) {
2087     case CSSValueAuto:
2088         if (prevParamType == None) {
2089             parsedValues->append(cssValuePool().createIdentifierValue(value->id));
2090             return Auto;
2091         }
2092         return None;
2093     case CSSValueLandscape:
2094     case CSSValuePortrait:
2095         if (prevParamType == None || prevParamType == PageSize) {
2096             parsedValues->append(cssValuePool().createIdentifierValue(value->id));
2097             return Orientation;
2098         }
2099         return None;
2100     case CSSValueA3:
2101     case CSSValueA4:
2102     case CSSValueA5:
2103     case CSSValueB4:
2104     case CSSValueB5:
2105     case CSSValueLedger:
2106     case CSSValueLegal:
2107     case CSSValueLetter:
2108         if (prevParamType == None || prevParamType == Orientation) {
2109             // Normalize to Page Size then Orientation order by prepending.
2110             // This is not specified by the CSS3 Paged Media specification, but for simpler processing later (StyleResolver::applyPageSizeProperty).
2111             parsedValues->prepend(cssValuePool().createIdentifierValue(value->id));
2112             return PageSize;
2113         }
2114         return None;
2115     case 0:
2116         if (validUnit(value, FLength | FNonNeg) && (prevParamType == None || prevParamType == Length)) {
2117             parsedValues->append(createPrimitiveNumericValue(value));
2118             return Length;
2119         }
2120         return None;
2121     default:
2122         return None;
2123     }
2124 }
2125 
2126 // [ <string> <string> ]+ | none, but none is handled in parseValue
parseQuotes()2127 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseQuotes()
2128 {
2129     RefPtrWillBeRawPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
2130     while (CSSParserValue* val = m_valueList->current()) {
2131         RefPtrWillBeRawPtr<CSSValue> parsedValue = nullptr;
2132         if (val->unit != CSSPrimitiveValue::CSS_STRING)
2133             return nullptr;
2134         parsedValue = CSSPrimitiveValue::create(val->string, CSSPrimitiveValue::CSS_STRING);
2135         values->append(parsedValue.release());
2136         m_valueList->next();
2137     }
2138     if (values->length() && values->length() % 2 == 0)
2139         return values.release();
2140     return nullptr;
2141 }
2142 
2143 // [ <string> | <uri> | <counter> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit
2144 // in CSS 2.1 this got somewhat reduced:
2145 // [ <string> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit
parseContent(CSSPropertyID propId,bool important)2146 bool CSSPropertyParser::parseContent(CSSPropertyID propId, bool important)
2147 {
2148     RefPtrWillBeRawPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
2149 
2150     while (CSSParserValue* val = m_valueList->current()) {
2151         RefPtrWillBeRawPtr<CSSValue> parsedValue = nullptr;
2152         if (val->unit == CSSPrimitiveValue::CSS_URI) {
2153             // url
2154             parsedValue = createCSSImageValueWithReferrer(val->string, completeURL(val->string));
2155         } else if (val->unit == CSSParserValue::Function) {
2156             // attr(X) | counter(X [,Y]) | counters(X, Y, [,Z]) | -webkit-gradient(...)
2157             CSSParserValueList* args = val->function->args.get();
2158             if (!args)
2159                 return false;
2160             if (equalIgnoringCase(val->function->name, "attr")) {
2161                 parsedValue = parseAttr(args);
2162                 if (!parsedValue)
2163                     return false;
2164             } else if (equalIgnoringCase(val->function->name, "counter")) {
2165                 parsedValue = parseCounterContent(args, false);
2166                 if (!parsedValue)
2167                     return false;
2168             } else if (equalIgnoringCase(val->function->name, "counters")) {
2169                 parsedValue = parseCounterContent(args, true);
2170                 if (!parsedValue)
2171                     return false;
2172             } else if (equalIgnoringCase(val->function->name, "-webkit-image-set")) {
2173                 parsedValue = parseImageSet(m_valueList);
2174                 if (!parsedValue)
2175                     return false;
2176             } else if (isGeneratedImageValue(val)) {
2177                 if (!parseGeneratedImage(m_valueList, parsedValue))
2178                     return false;
2179             } else
2180                 return false;
2181         } else if (val->unit == CSSPrimitiveValue::CSS_IDENT) {
2182             switch (val->id) {
2183             case CSSValueOpenQuote:
2184             case CSSValueCloseQuote:
2185             case CSSValueNoOpenQuote:
2186             case CSSValueNoCloseQuote:
2187             case CSSValueNone:
2188             case CSSValueNormal:
2189                 parsedValue = cssValuePool().createIdentifierValue(val->id);
2190             default:
2191                 break;
2192             }
2193         } else if (val->unit == CSSPrimitiveValue::CSS_STRING) {
2194             parsedValue = createPrimitiveStringValue(val);
2195         }
2196         if (!parsedValue)
2197             break;
2198         values->append(parsedValue.release());
2199         m_valueList->next();
2200     }
2201 
2202     if (values->length()) {
2203         addProperty(propId, values.release(), important);
2204         m_valueList->next();
2205         return true;
2206     }
2207 
2208     return false;
2209 }
2210 
parseAttr(CSSParserValueList * args)2211 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAttr(CSSParserValueList* args)
2212 {
2213     if (args->size() != 1)
2214         return nullptr;
2215 
2216     CSSParserValue* a = args->current();
2217 
2218     if (a->unit != CSSPrimitiveValue::CSS_IDENT)
2219         return nullptr;
2220 
2221     String attrName = a->string;
2222     // CSS allows identifiers with "-" at the start, like "-webkit-mask-image".
2223     // But HTML attribute names can't have those characters, and we should not
2224     // even parse them inside attr().
2225     if (attrName[0] == '-')
2226         return nullptr;
2227 
2228     if (m_context.isHTMLDocument())
2229         attrName = attrName.lower();
2230 
2231     return cssValuePool().createValue(attrName, CSSPrimitiveValue::CSS_ATTR);
2232 }
2233 
parseBackgroundColor()2234 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseBackgroundColor()
2235 {
2236     CSSValueID id = m_valueList->current()->id;
2237     if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || id == CSSValueCurrentcolor ||
2238         (id >= CSSValueGrey && id < CSSValueWebkitText && inQuirksMode()))
2239         return cssValuePool().createIdentifierValue(id);
2240     return parseColor();
2241 }
2242 
parseFillImage(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & value)2243 bool CSSPropertyParser::parseFillImage(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& value)
2244 {
2245     if (valueList->current()->id == CSSValueNone) {
2246         value = cssValuePool().createIdentifierValue(CSSValueNone);
2247         return true;
2248     }
2249     if (valueList->current()->unit == CSSPrimitiveValue::CSS_URI) {
2250         value = createCSSImageValueWithReferrer(valueList->current()->string, completeURL(valueList->current()->string));
2251         return true;
2252     }
2253 
2254     if (isGeneratedImageValue(valueList->current()))
2255         return parseGeneratedImage(valueList, value);
2256 
2257     if (valueList->current()->unit == CSSParserValue::Function && equalIgnoringCase(valueList->current()->function->name, "-webkit-image-set")) {
2258         value = parseImageSet(m_valueList);
2259         if (value)
2260             return true;
2261     }
2262 
2263     return false;
2264 }
2265 
parseFillPositionX(CSSParserValueList * valueList)2266 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseFillPositionX(CSSParserValueList* valueList)
2267 {
2268     int id = valueList->current()->id;
2269     if (id == CSSValueLeft || id == CSSValueRight || id == CSSValueCenter) {
2270         int percent = 0;
2271         if (id == CSSValueRight)
2272             percent = 100;
2273         else if (id == CSSValueCenter)
2274             percent = 50;
2275         return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE);
2276     }
2277     if (validUnit(valueList->current(), FPercent | FLength))
2278         return createPrimitiveNumericValue(valueList->current());
2279     return nullptr;
2280 }
2281 
parseFillPositionY(CSSParserValueList * valueList)2282 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseFillPositionY(CSSParserValueList* valueList)
2283 {
2284     int id = valueList->current()->id;
2285     if (id == CSSValueTop || id == CSSValueBottom || id == CSSValueCenter) {
2286         int percent = 0;
2287         if (id == CSSValueBottom)
2288             percent = 100;
2289         else if (id == CSSValueCenter)
2290             percent = 50;
2291         return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE);
2292     }
2293     if (validUnit(valueList->current(), FPercent | FLength))
2294         return createPrimitiveNumericValue(valueList->current());
2295     return nullptr;
2296 }
2297 
parseFillPositionComponent(CSSParserValueList * valueList,unsigned & cumulativeFlags,FillPositionFlag & individualFlag,FillPositionParsingMode parsingMode)2298 PassRefPtrWillBeRawPtr<CSSPrimitiveValue> CSSPropertyParser::parseFillPositionComponent(CSSParserValueList* valueList, unsigned& cumulativeFlags, FillPositionFlag& individualFlag, FillPositionParsingMode parsingMode)
2299 {
2300     CSSValueID id = valueList->current()->id;
2301     if (id == CSSValueLeft || id == CSSValueTop || id == CSSValueRight || id == CSSValueBottom || id == CSSValueCenter) {
2302         int percent = 0;
2303         if (id == CSSValueLeft || id == CSSValueRight) {
2304             if (cumulativeFlags & XFillPosition)
2305                 return nullptr;
2306             cumulativeFlags |= XFillPosition;
2307             individualFlag = XFillPosition;
2308             if (id == CSSValueRight)
2309                 percent = 100;
2310         }
2311         else if (id == CSSValueTop || id == CSSValueBottom) {
2312             if (cumulativeFlags & YFillPosition)
2313                 return nullptr;
2314             cumulativeFlags |= YFillPosition;
2315             individualFlag = YFillPosition;
2316             if (id == CSSValueBottom)
2317                 percent = 100;
2318         } else if (id == CSSValueCenter) {
2319             // Center is ambiguous, so we're not sure which position we've found yet, an x or a y.
2320             percent = 50;
2321             cumulativeFlags |= AmbiguousFillPosition;
2322             individualFlag = AmbiguousFillPosition;
2323         }
2324 
2325         if (parsingMode == ResolveValuesAsKeyword)
2326             return cssValuePool().createIdentifierValue(id);
2327 
2328         return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE);
2329     }
2330     if (validUnit(valueList->current(), FPercent | FLength)) {
2331         if (!cumulativeFlags) {
2332             cumulativeFlags |= XFillPosition;
2333             individualFlag = XFillPosition;
2334         } else if (cumulativeFlags & (XFillPosition | AmbiguousFillPosition)) {
2335             cumulativeFlags |= YFillPosition;
2336             individualFlag = YFillPosition;
2337         } else {
2338             if (m_parsedCalculation)
2339                 m_parsedCalculation.release();
2340             return nullptr;
2341         }
2342         return createPrimitiveNumericValue(valueList->current());
2343     }
2344     return nullptr;
2345 }
2346 
isValueConflictingWithCurrentEdge(int value1,int value2)2347 static bool isValueConflictingWithCurrentEdge(int value1, int value2)
2348 {
2349     if ((value1 == CSSValueLeft || value1 == CSSValueRight) && (value2 == CSSValueLeft || value2 == CSSValueRight))
2350         return true;
2351 
2352     if ((value1 == CSSValueTop || value1 == CSSValueBottom) && (value2 == CSSValueTop || value2 == CSSValueBottom))
2353         return true;
2354 
2355     return false;
2356 }
2357 
isFillPositionKeyword(CSSValueID value)2358 static bool isFillPositionKeyword(CSSValueID value)
2359 {
2360     return value == CSSValueLeft || value == CSSValueTop || value == CSSValueBottom || value == CSSValueRight || value == CSSValueCenter;
2361 }
2362 
parse4ValuesFillPosition(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & value1,RefPtrWillBeRawPtr<CSSValue> & value2,PassRefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue1,PassRefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue2)2363 void CSSPropertyParser::parse4ValuesFillPosition(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& value1, RefPtrWillBeRawPtr<CSSValue>& value2, PassRefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue1, PassRefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue2)
2364 {
2365     // [ left | right ] [ <percentage] | <length> ] && [ top | bottom ] [ <percentage> | <length> ]
2366     // In the case of 4 values <position> requires the second value to be a length or a percentage.
2367     if (isFillPositionKeyword(parsedValue2->getValueID()))
2368         return;
2369 
2370     unsigned cumulativeFlags = 0;
2371     FillPositionFlag value3Flag = InvalidFillPosition;
2372     RefPtrWillBeRawPtr<CSSPrimitiveValue> value3 = parseFillPositionComponent(valueList, cumulativeFlags, value3Flag, ResolveValuesAsKeyword);
2373     if (!value3)
2374         return;
2375 
2376     CSSValueID ident1 = parsedValue1->getValueID();
2377     CSSValueID ident3 = value3->getValueID();
2378 
2379     if (ident1 == CSSValueCenter)
2380         return;
2381 
2382     if (!isFillPositionKeyword(ident3) || ident3 == CSSValueCenter)
2383         return;
2384 
2385     // We need to check if the values are not conflicting, e.g. they are not on the same edge. It is
2386     // needed as the second call to parseFillPositionComponent was on purpose not checking it. In the
2387     // case of two values top 20px is invalid but in the case of 4 values it becomes valid.
2388     if (isValueConflictingWithCurrentEdge(ident1, ident3))
2389         return;
2390 
2391     valueList->next();
2392 
2393     cumulativeFlags = 0;
2394     FillPositionFlag value4Flag = InvalidFillPosition;
2395     RefPtrWillBeRawPtr<CSSPrimitiveValue> value4 = parseFillPositionComponent(valueList, cumulativeFlags, value4Flag, ResolveValuesAsKeyword);
2396     if (!value4)
2397         return;
2398 
2399     // 4th value must be a length or a percentage.
2400     if (isFillPositionKeyword(value4->getValueID()))
2401         return;
2402 
2403     value1 = createPrimitiveValuePair(parsedValue1, parsedValue2);
2404     value2 = createPrimitiveValuePair(value3, value4);
2405 
2406     if (ident1 == CSSValueTop || ident1 == CSSValueBottom)
2407         value1.swap(value2);
2408 
2409     valueList->next();
2410 }
parse3ValuesFillPosition(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & value1,RefPtrWillBeRawPtr<CSSValue> & value2,PassRefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue1,PassRefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue2)2411 void CSSPropertyParser::parse3ValuesFillPosition(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& value1, RefPtrWillBeRawPtr<CSSValue>& value2, PassRefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue1, PassRefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue2)
2412 {
2413     unsigned cumulativeFlags = 0;
2414     FillPositionFlag value3Flag = InvalidFillPosition;
2415     RefPtrWillBeRawPtr<CSSPrimitiveValue> value3 = parseFillPositionComponent(valueList, cumulativeFlags, value3Flag, ResolveValuesAsKeyword);
2416 
2417     // value3 is not an expected value, we return.
2418     if (!value3)
2419         return;
2420 
2421     valueList->next();
2422 
2423     bool swapNeeded = false;
2424     CSSValueID ident1 = parsedValue1->getValueID();
2425     CSSValueID ident2 = parsedValue2->getValueID();
2426     CSSValueID ident3 = value3->getValueID();
2427 
2428     CSSValueID firstPositionKeyword;
2429     CSSValueID secondPositionKeyword;
2430 
2431     if (ident1 == CSSValueCenter) {
2432         // <position> requires the first 'center' to be followed by a keyword.
2433         if (!isFillPositionKeyword(ident2))
2434             return;
2435 
2436         // If 'center' is the first keyword then the last one needs to be a length.
2437         if (isFillPositionKeyword(ident3))
2438             return;
2439 
2440         firstPositionKeyword = CSSValueLeft;
2441         if (ident2 == CSSValueLeft || ident2 == CSSValueRight) {
2442             firstPositionKeyword = CSSValueTop;
2443             swapNeeded = true;
2444         }
2445         value1 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(firstPositionKeyword), cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE));
2446         value2 = createPrimitiveValuePair(parsedValue2, value3);
2447     } else if (ident3 == CSSValueCenter) {
2448         if (isFillPositionKeyword(ident2))
2449             return;
2450 
2451         secondPositionKeyword = CSSValueTop;
2452         if (ident1 == CSSValueTop || ident1 == CSSValueBottom) {
2453             secondPositionKeyword = CSSValueLeft;
2454             swapNeeded = true;
2455         }
2456         value1 = createPrimitiveValuePair(parsedValue1, parsedValue2);
2457         value2 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(secondPositionKeyword), cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE));
2458     } else {
2459         RefPtrWillBeRawPtr<CSSPrimitiveValue> firstPositionValue = nullptr;
2460         RefPtrWillBeRawPtr<CSSPrimitiveValue> secondPositionValue = nullptr;
2461 
2462         if (isFillPositionKeyword(ident2)) {
2463             // To match CSS grammar, we should only accept: [ center | left | right | bottom | top ] [ left | right | top | bottom ] [ <percentage> | <length> ].
2464             ASSERT(ident2 != CSSValueCenter);
2465 
2466             if (isFillPositionKeyword(ident3))
2467                 return;
2468 
2469             secondPositionValue = value3;
2470             secondPositionKeyword = ident2;
2471             firstPositionValue = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE);
2472         } else {
2473             // Per CSS, we should only accept: [ right | left | top | bottom ] [ <percentage> | <length> ] [ center | left | right | bottom | top ].
2474             if (!isFillPositionKeyword(ident3))
2475                 return;
2476 
2477             firstPositionValue = parsedValue2;
2478             secondPositionKeyword = ident3;
2479             secondPositionValue = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE);
2480         }
2481 
2482         if (isValueConflictingWithCurrentEdge(ident1, secondPositionKeyword))
2483             return;
2484 
2485         value1 = createPrimitiveValuePair(parsedValue1, firstPositionValue);
2486         value2 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(secondPositionKeyword), secondPositionValue);
2487     }
2488 
2489     if (ident1 == CSSValueTop || ident1 == CSSValueBottom || swapNeeded)
2490         value1.swap(value2);
2491 
2492 #if ENABLE(ASSERT)
2493     CSSPrimitiveValue* first = toCSSPrimitiveValue(value1.get());
2494     CSSPrimitiveValue* second = toCSSPrimitiveValue(value2.get());
2495     ident1 = first->getPairValue()->first()->getValueID();
2496     ident2 = second->getPairValue()->first()->getValueID();
2497     ASSERT(ident1 == CSSValueLeft || ident1 == CSSValueRight);
2498     ASSERT(ident2 == CSSValueBottom || ident2 == CSSValueTop);
2499 #endif
2500 }
2501 
isPotentialPositionValue(CSSParserValue * value)2502 inline bool CSSPropertyParser::isPotentialPositionValue(CSSParserValue* value)
2503 {
2504     return isFillPositionKeyword(value->id) || validUnit(value, FPercent | FLength, ReleaseParsedCalcValue);
2505 }
2506 
parseFillPosition(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & value1,RefPtrWillBeRawPtr<CSSValue> & value2)2507 void CSSPropertyParser::parseFillPosition(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& value1, RefPtrWillBeRawPtr<CSSValue>& value2)
2508 {
2509     unsigned numberOfValues = 0;
2510     for (unsigned i = valueList->currentIndex(); i < valueList->size(); ++i, ++numberOfValues) {
2511         CSSParserValue* current = valueList->valueAt(i);
2512         if (isComma(current) || !current || isForwardSlashOperator(current) || !isPotentialPositionValue(current))
2513             break;
2514     }
2515 
2516     if (numberOfValues > 4)
2517         return;
2518 
2519     // If we are parsing two values, we can safely call the CSS 2.1 parsing function and return.
2520     if (numberOfValues <= 2) {
2521         parse2ValuesFillPosition(valueList, value1, value2);
2522         return;
2523     }
2524 
2525     ASSERT(numberOfValues > 2 && numberOfValues <= 4);
2526 
2527     CSSParserValue* value = valueList->current();
2528 
2529     // <position> requires the first value to be a background keyword.
2530     if (!isFillPositionKeyword(value->id))
2531         return;
2532 
2533     // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length.
2534     unsigned cumulativeFlags = 0;
2535     FillPositionFlag value1Flag = InvalidFillPosition;
2536     FillPositionFlag value2Flag = InvalidFillPosition;
2537     value1 = parseFillPositionComponent(valueList, cumulativeFlags, value1Flag, ResolveValuesAsKeyword);
2538     if (!value1)
2539         return;
2540 
2541     valueList->next();
2542 
2543     // In case we are parsing more than two values, relax the check inside of parseFillPositionComponent. top 20px is
2544     // a valid start for <position>.
2545     cumulativeFlags = AmbiguousFillPosition;
2546     value2 = parseFillPositionComponent(valueList, cumulativeFlags, value2Flag, ResolveValuesAsKeyword);
2547     if (value2)
2548         valueList->next();
2549     else {
2550         value1.clear();
2551         return;
2552     }
2553 
2554     RefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue1 = toCSSPrimitiveValue(value1.get());
2555     RefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue2 = toCSSPrimitiveValue(value2.get());
2556 
2557     value1.clear();
2558     value2.clear();
2559 
2560     // Per CSS3 syntax, <position> can't have 'center' as its second keyword as we have more arguments to follow.
2561     if (parsedValue2->getValueID() == CSSValueCenter)
2562         return;
2563 
2564     if (numberOfValues == 3)
2565         parse3ValuesFillPosition(valueList, value1, value2, parsedValue1.release(), parsedValue2.release());
2566     else
2567         parse4ValuesFillPosition(valueList, value1, value2, parsedValue1.release(), parsedValue2.release());
2568 }
2569 
parse2ValuesFillPosition(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & value1,RefPtrWillBeRawPtr<CSSValue> & value2)2570 void CSSPropertyParser::parse2ValuesFillPosition(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& value1, RefPtrWillBeRawPtr<CSSValue>& value2)
2571 {
2572     // Parse the first value.  We're just making sure that it is one of the valid keywords or a percentage/length.
2573     unsigned cumulativeFlags = 0;
2574     FillPositionFlag value1Flag = InvalidFillPosition;
2575     FillPositionFlag value2Flag = InvalidFillPosition;
2576     value1 = parseFillPositionComponent(valueList, cumulativeFlags, value1Flag);
2577     if (!value1)
2578         return;
2579 
2580     // It only takes one value for background-position to be correctly parsed if it was specified in a shorthand (since we
2581     // can assume that any other values belong to the rest of the shorthand).  If we're not parsing a shorthand, though, the
2582     // value was explicitly specified for our property.
2583     CSSParserValue* value = valueList->next();
2584 
2585     // First check for the comma.  If so, we are finished parsing this value or value pair.
2586     if (isComma(value))
2587         value = 0;
2588 
2589     if (value) {
2590         value2 = parseFillPositionComponent(valueList, cumulativeFlags, value2Flag);
2591         if (value2)
2592             valueList->next();
2593         else {
2594             if (!inShorthand()) {
2595                 value1.clear();
2596                 return;
2597             }
2598         }
2599     }
2600 
2601     if (!value2)
2602         // Only one value was specified. If that value was not a keyword, then it sets the x position, and the y position
2603         // is simply 50%. This is our default.
2604         // For keywords, the keyword was either an x-keyword (left/right), a y-keyword (top/bottom), or an ambiguous keyword (center).
2605         // For left/right/center, the default of 50% in the y is still correct.
2606         value2 = cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE);
2607 
2608     if (value1Flag == YFillPosition || value2Flag == XFillPosition)
2609         value1.swap(value2);
2610 }
2611 
parseFillRepeat(RefPtrWillBeRawPtr<CSSValue> & value1,RefPtrWillBeRawPtr<CSSValue> & value2)2612 void CSSPropertyParser::parseFillRepeat(RefPtrWillBeRawPtr<CSSValue>& value1, RefPtrWillBeRawPtr<CSSValue>& value2)
2613 {
2614     CSSValueID id = m_valueList->current()->id;
2615     if (id == CSSValueRepeatX) {
2616         m_implicitShorthand = true;
2617         value1 = cssValuePool().createIdentifierValue(CSSValueRepeat);
2618         value2 = cssValuePool().createIdentifierValue(CSSValueNoRepeat);
2619         m_valueList->next();
2620         return;
2621     }
2622     if (id == CSSValueRepeatY) {
2623         m_implicitShorthand = true;
2624         value1 = cssValuePool().createIdentifierValue(CSSValueNoRepeat);
2625         value2 = cssValuePool().createIdentifierValue(CSSValueRepeat);
2626         m_valueList->next();
2627         return;
2628     }
2629     if (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace)
2630         value1 = cssValuePool().createIdentifierValue(id);
2631     else {
2632         value1 = nullptr;
2633         return;
2634     }
2635 
2636     CSSParserValue* value = m_valueList->next();
2637 
2638     // Parse the second value if one is available
2639     if (value && !isComma(value)) {
2640         id = value->id;
2641         if (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace) {
2642             value2 = cssValuePool().createIdentifierValue(id);
2643             m_valueList->next();
2644             return;
2645         }
2646     }
2647 
2648     // If only one value was specified, value2 is the same as value1.
2649     m_implicitShorthand = true;
2650     value2 = cssValuePool().createIdentifierValue(toCSSPrimitiveValue(value1.get())->getValueID());
2651 }
2652 
parseFillSize(CSSPropertyID propId,bool & allowComma)2653 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseFillSize(CSSPropertyID propId, bool& allowComma)
2654 {
2655     allowComma = true;
2656     CSSParserValue* value = m_valueList->current();
2657 
2658     if (value->id == CSSValueContain || value->id == CSSValueCover)
2659         return cssValuePool().createIdentifierValue(value->id);
2660 
2661     RefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue1 = nullptr;
2662 
2663     if (value->id == CSSValueAuto)
2664         parsedValue1 = cssValuePool().createIdentifierValue(CSSValueAuto);
2665     else {
2666         if (!validUnit(value, FLength | FPercent))
2667             return nullptr;
2668         parsedValue1 = createPrimitiveNumericValue(value);
2669     }
2670 
2671     RefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue2 = nullptr;
2672     value = m_valueList->next();
2673     if (value) {
2674         if (value->unit == CSSParserValue::Operator && value->iValue == ',')
2675             allowComma = false;
2676         else if (value->id != CSSValueAuto) {
2677             if (!validUnit(value, FLength | FPercent)) {
2678                 if (!inShorthand())
2679                     return nullptr;
2680                 // We need to rewind the value list, so that when it is advanced we'll end up back at this value.
2681                 m_valueList->previous();
2682             } else
2683                 parsedValue2 = createPrimitiveNumericValue(value);
2684         }
2685     } else if (!parsedValue2 && propId == CSSPropertyWebkitBackgroundSize) {
2686         // For backwards compatibility we set the second value to the first if it is omitted.
2687         // We only need to do this for -webkit-background-size. It should be safe to let masks match
2688         // the real property.
2689         parsedValue2 = parsedValue1;
2690     }
2691 
2692     if (!parsedValue2)
2693         return parsedValue1;
2694 
2695     Pair::IdenticalValuesPolicy policy = propId == CSSPropertyWebkitBackgroundSize ?
2696         Pair::DropIdenticalValues : Pair::KeepIdenticalValues;
2697 
2698     return createPrimitiveValuePair(parsedValue1.release(), parsedValue2.release(), policy);
2699 }
2700 
parseFillProperty(CSSPropertyID propId,CSSPropertyID & propId1,CSSPropertyID & propId2,RefPtrWillBeRawPtr<CSSValue> & retValue1,RefPtrWillBeRawPtr<CSSValue> & retValue2)2701 bool CSSPropertyParser::parseFillProperty(CSSPropertyID propId, CSSPropertyID& propId1, CSSPropertyID& propId2,
2702     RefPtrWillBeRawPtr<CSSValue>& retValue1, RefPtrWillBeRawPtr<CSSValue>& retValue2)
2703 {
2704     RefPtrWillBeRawPtr<CSSValueList> values = nullptr;
2705     RefPtrWillBeRawPtr<CSSValueList> values2 = nullptr;
2706     RefPtrWillBeRawPtr<CSSValue> value = nullptr;
2707     RefPtrWillBeRawPtr<CSSValue> value2 = nullptr;
2708 
2709     bool allowComma = false;
2710 
2711     retValue1 = retValue2 = nullptr;
2712     propId1 = propId;
2713     propId2 = propId;
2714     if (propId == CSSPropertyBackgroundPosition) {
2715         propId1 = CSSPropertyBackgroundPositionX;
2716         propId2 = CSSPropertyBackgroundPositionY;
2717     } else if (propId == CSSPropertyWebkitMaskPosition) {
2718         propId1 = CSSPropertyWebkitMaskPositionX;
2719         propId2 = CSSPropertyWebkitMaskPositionY;
2720     } else if (propId == CSSPropertyBackgroundRepeat) {
2721         propId1 = CSSPropertyBackgroundRepeatX;
2722         propId2 = CSSPropertyBackgroundRepeatY;
2723     } else if (propId == CSSPropertyWebkitMaskRepeat) {
2724         propId1 = CSSPropertyWebkitMaskRepeatX;
2725         propId2 = CSSPropertyWebkitMaskRepeatY;
2726     }
2727 
2728     for (CSSParserValue* val = m_valueList->current(); val; val = m_valueList->current()) {
2729         RefPtrWillBeRawPtr<CSSValue> currValue = nullptr;
2730         RefPtrWillBeRawPtr<CSSValue> currValue2 = nullptr;
2731 
2732         if (allowComma) {
2733             if (!isComma(val))
2734                 return false;
2735             m_valueList->next();
2736             allowComma = false;
2737         } else {
2738             allowComma = true;
2739             switch (propId) {
2740                 case CSSPropertyBackgroundColor:
2741                     currValue = parseBackgroundColor();
2742                     if (currValue)
2743                         m_valueList->next();
2744                     break;
2745                 case CSSPropertyBackgroundAttachment:
2746                     if (val->id == CSSValueScroll || val->id == CSSValueFixed || val->id == CSSValueLocal) {
2747                         currValue = cssValuePool().createIdentifierValue(val->id);
2748                         m_valueList->next();
2749                     }
2750                     break;
2751                 case CSSPropertyBackgroundImage:
2752                 case CSSPropertyWebkitMaskImage:
2753                     if (parseFillImage(m_valueList, currValue))
2754                         m_valueList->next();
2755                     break;
2756                 case CSSPropertyWebkitBackgroundClip:
2757                 case CSSPropertyWebkitBackgroundOrigin:
2758                 case CSSPropertyWebkitMaskClip:
2759                 case CSSPropertyWebkitMaskOrigin:
2760                     // The first three values here are deprecated and do not apply to the version of the property that has
2761                     // the -webkit- prefix removed.
2762                     if (val->id == CSSValueBorder || val->id == CSSValuePadding || val->id == CSSValueContent ||
2763                         val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox ||
2764                         ((propId == CSSPropertyWebkitBackgroundClip || propId == CSSPropertyWebkitMaskClip) &&
2765                          (val->id == CSSValueText || val->id == CSSValueWebkitText))) {
2766                         currValue = cssValuePool().createIdentifierValue(val->id);
2767                         m_valueList->next();
2768                     }
2769                     break;
2770                 case CSSPropertyBackgroundClip:
2771                     if (parseBackgroundClip(val, currValue))
2772                         m_valueList->next();
2773                     break;
2774                 case CSSPropertyBackgroundOrigin:
2775                     if (val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox) {
2776                         currValue = cssValuePool().createIdentifierValue(val->id);
2777                         m_valueList->next();
2778                     }
2779                     break;
2780                 case CSSPropertyBackgroundPosition:
2781                 case CSSPropertyWebkitMaskPosition:
2782                     parseFillPosition(m_valueList, currValue, currValue2);
2783                     // parseFillPosition advances the m_valueList pointer.
2784                     break;
2785                 case CSSPropertyBackgroundPositionX:
2786                 case CSSPropertyWebkitMaskPositionX: {
2787                     currValue = parseFillPositionX(m_valueList);
2788                     if (currValue)
2789                         m_valueList->next();
2790                     break;
2791                 }
2792                 case CSSPropertyBackgroundPositionY:
2793                 case CSSPropertyWebkitMaskPositionY: {
2794                     currValue = parseFillPositionY(m_valueList);
2795                     if (currValue)
2796                         m_valueList->next();
2797                     break;
2798                 }
2799                 case CSSPropertyWebkitBackgroundComposite:
2800                 case CSSPropertyWebkitMaskComposite:
2801                     if (val->id >= CSSValueClear && val->id <= CSSValuePlusLighter) {
2802                         currValue = cssValuePool().createIdentifierValue(val->id);
2803                         m_valueList->next();
2804                     }
2805                     break;
2806                 case CSSPropertyBackgroundBlendMode:
2807                     if (val->id == CSSValueNormal || val->id == CSSValueMultiply
2808                         || val->id == CSSValueScreen || val->id == CSSValueOverlay || val->id == CSSValueDarken
2809                         || val->id == CSSValueLighten ||  val->id == CSSValueColorDodge || val->id == CSSValueColorBurn
2810                         || val->id == CSSValueHardLight || val->id == CSSValueSoftLight || val->id == CSSValueDifference
2811                         || val->id == CSSValueExclusion || val->id == CSSValueHue || val->id == CSSValueSaturation
2812                         || val->id == CSSValueColor || val->id == CSSValueLuminosity) {
2813                         currValue = cssValuePool().createIdentifierValue(val->id);
2814                         m_valueList->next();
2815                     }
2816                     break;
2817                 case CSSPropertyBackgroundRepeat:
2818                 case CSSPropertyWebkitMaskRepeat:
2819                     parseFillRepeat(currValue, currValue2);
2820                     // parseFillRepeat advances the m_valueList pointer
2821                     break;
2822                 case CSSPropertyBackgroundSize:
2823                 case CSSPropertyWebkitBackgroundSize:
2824                 case CSSPropertyWebkitMaskSize: {
2825                     currValue = parseFillSize(propId, allowComma);
2826                     if (currValue)
2827                         m_valueList->next();
2828                     break;
2829                 }
2830                 case CSSPropertyMaskSourceType: {
2831                     ASSERT(RuntimeEnabledFeatures::cssMaskSourceTypeEnabled());
2832                     if (val->id == CSSValueAuto || val->id == CSSValueAlpha || val->id == CSSValueLuminance) {
2833                         currValue = cssValuePool().createIdentifierValue(val->id);
2834                         m_valueList->next();
2835                     } else {
2836                         currValue = nullptr;
2837                     }
2838                     break;
2839                 }
2840                 default:
2841                     break;
2842             }
2843             if (!currValue)
2844                 return false;
2845 
2846             if (value && !values) {
2847                 values = CSSValueList::createCommaSeparated();
2848                 values->append(value.release());
2849             }
2850 
2851             if (value2 && !values2) {
2852                 values2 = CSSValueList::createCommaSeparated();
2853                 values2->append(value2.release());
2854             }
2855 
2856             if (values)
2857                 values->append(currValue.release());
2858             else
2859                 value = currValue.release();
2860             if (currValue2) {
2861                 if (values2)
2862                     values2->append(currValue2.release());
2863                 else
2864                     value2 = currValue2.release();
2865             }
2866         }
2867 
2868         // When parsing any fill shorthand property, we let it handle building up the lists for all
2869         // properties.
2870         if (inShorthand())
2871             break;
2872     }
2873 
2874     if (values && values->length()) {
2875         retValue1 = values.release();
2876         if (values2 && values2->length())
2877             retValue2 = values2.release();
2878         return true;
2879     }
2880     if (value) {
2881         retValue1 = value.release();
2882         retValue2 = value2.release();
2883         return true;
2884     }
2885     return false;
2886 }
2887 
parseAnimationDelay()2888 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAnimationDelay()
2889 {
2890     CSSParserValue* value = m_valueList->current();
2891     if (validUnit(value, FTime))
2892         return createPrimitiveNumericValue(value);
2893     return nullptr;
2894 }
2895 
parseAnimationDirection()2896 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAnimationDirection()
2897 {
2898     CSSParserValue* value = m_valueList->current();
2899     if (value->id == CSSValueNormal || value->id == CSSValueAlternate || value->id == CSSValueReverse || value->id == CSSValueAlternateReverse)
2900         return cssValuePool().createIdentifierValue(value->id);
2901     return nullptr;
2902 }
2903 
parseAnimationDuration()2904 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAnimationDuration()
2905 {
2906     CSSParserValue* value = m_valueList->current();
2907     if (validUnit(value, FTime | FNonNeg))
2908         return createPrimitiveNumericValue(value);
2909     return nullptr;
2910 }
2911 
parseAnimationFillMode()2912 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAnimationFillMode()
2913 {
2914     CSSParserValue* value = m_valueList->current();
2915     if (value->id == CSSValueNone || value->id == CSSValueForwards || value->id == CSSValueBackwards || value->id == CSSValueBoth)
2916         return cssValuePool().createIdentifierValue(value->id);
2917     return nullptr;
2918 }
2919 
parseAnimationIterationCount()2920 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAnimationIterationCount()
2921 {
2922     CSSParserValue* value = m_valueList->current();
2923     if (value->id == CSSValueInfinite)
2924         return cssValuePool().createIdentifierValue(value->id);
2925     if (validUnit(value, FNumber | FNonNeg))
2926         return createPrimitiveNumericValue(value);
2927     return nullptr;
2928 }
2929 
parseAnimationName()2930 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAnimationName()
2931 {
2932     CSSParserValue* value = m_valueList->current();
2933     if (value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT) {
2934         if (value->id == CSSValueNone || (value->unit == CSSPrimitiveValue::CSS_STRING && equalIgnoringCase(value, "none"))) {
2935             return cssValuePool().createIdentifierValue(CSSValueNone);
2936         } else {
2937             return createPrimitiveStringValue(value);
2938         }
2939     }
2940     return nullptr;
2941 }
2942 
parseAnimationPlayState()2943 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAnimationPlayState()
2944 {
2945     CSSParserValue* value = m_valueList->current();
2946     if (value->id == CSSValueRunning || value->id == CSSValuePaused)
2947         return cssValuePool().createIdentifierValue(value->id);
2948     return nullptr;
2949 }
2950 
parseAnimationProperty()2951 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAnimationProperty()
2952 {
2953     CSSParserValue* value = m_valueList->current();
2954     if (value->unit != CSSPrimitiveValue::CSS_IDENT)
2955         return nullptr;
2956     // Since all is valid css property keyword, cssPropertyID for all
2957     // returns non-null value. We need to check "all" before
2958     // cssPropertyID check.
2959     if (value->id == CSSValueAll)
2960         return cssValuePool().createIdentifierValue(CSSValueAll);
2961     CSSPropertyID property = cssPropertyID(value->string);
2962     if (property) {
2963         ASSERT(CSSPropertyMetadata::isEnabledProperty(property));
2964         return cssValuePool().createIdentifierValue(property);
2965     }
2966     if (value->id == CSSValueNone)
2967         return cssValuePool().createIdentifierValue(CSSValueNone);
2968     if (value->id == CSSValueInitial || value->id == CSSValueInherit)
2969         return nullptr;
2970     return createPrimitiveStringValue(value);
2971 }
2972 
parseWebkitTransformOriginShorthand(bool important)2973 bool CSSPropertyParser::parseWebkitTransformOriginShorthand(bool important)
2974 {
2975     RefPtrWillBeRawPtr<CSSValue> originX = nullptr;
2976     RefPtrWillBeRawPtr<CSSValue> originY = nullptr;
2977     RefPtrWillBeRawPtr<CSSValue> originZ = nullptr;
2978 
2979     parse2ValuesFillPosition(m_valueList, originX, originY);
2980 
2981     if (m_valueList->current()) {
2982         if (!validUnit(m_valueList->current(), FLength))
2983             return false;
2984         originZ = createPrimitiveNumericValue(m_valueList->current());
2985         m_valueList->next();
2986     } else {
2987         originZ = cssValuePool().createImplicitInitialValue();
2988     }
2989 
2990     addProperty(CSSPropertyWebkitTransformOriginX, originX.release(), important);
2991     addProperty(CSSPropertyWebkitTransformOriginY, originY.release(), important);
2992     addProperty(CSSPropertyWebkitTransformOriginZ, originZ.release(), important);
2993 
2994     return true;
2995 }
2996 
parseCubicBezierTimingFunctionValue(CSSParserValueList * & args,double & result)2997 bool CSSPropertyParser::parseCubicBezierTimingFunctionValue(CSSParserValueList*& args, double& result)
2998 {
2999     CSSParserValue* v = args->current();
3000     if (!validUnit(v, FNumber))
3001         return false;
3002     result = v->fValue;
3003     v = args->next();
3004     if (!v)
3005         // The last number in the function has no comma after it, so we're done.
3006         return true;
3007     return consumeComma(args);
3008 }
3009 
parseAnimationTimingFunction()3010 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAnimationTimingFunction()
3011 {
3012     CSSParserValue* value = m_valueList->current();
3013     if (value->id == CSSValueEase || value->id == CSSValueLinear || value->id == CSSValueEaseIn || value->id == CSSValueEaseOut
3014         || value->id == CSSValueEaseInOut || value->id == CSSValueStepStart || value->id == CSSValueStepEnd
3015         || value->id == CSSValueStepMiddle)
3016         return cssValuePool().createIdentifierValue(value->id);
3017 
3018     // We must be a function.
3019     if (value->unit != CSSParserValue::Function)
3020         return nullptr;
3021 
3022     CSSParserValueList* args = value->function->args.get();
3023 
3024     if (equalIgnoringCase(value->function->name, "steps")) {
3025         // For steps, 1 or 2 params must be specified (comma-separated)
3026         if (!args || (args->size() != 1 && args->size() != 3))
3027             return nullptr;
3028 
3029         // There are two values.
3030         int numSteps;
3031         StepsTimingFunction::StepAtPosition stepAtPosition = StepsTimingFunction::End;
3032 
3033         CSSParserValue* v = args->current();
3034         if (!validUnit(v, FInteger))
3035             return nullptr;
3036         numSteps = clampToInteger(v->fValue);
3037         if (numSteps < 1)
3038             return nullptr;
3039 
3040         if (args->next()) {
3041             // There is a comma so we need to parse the second value
3042             if (!consumeComma(args))
3043                 return nullptr;
3044             switch (args->current()->id) {
3045             case CSSValueMiddle:
3046                 if (!RuntimeEnabledFeatures::webAnimationsAPIEnabled())
3047                     return nullptr;
3048                 stepAtPosition = StepsTimingFunction::Middle;
3049                 break;
3050             case CSSValueStart:
3051                 stepAtPosition = StepsTimingFunction::Start;
3052                 break;
3053             case CSSValueEnd:
3054                 stepAtPosition = StepsTimingFunction::End;
3055                 break;
3056             default:
3057                 return nullptr;
3058             }
3059         }
3060 
3061         return CSSStepsTimingFunctionValue::create(numSteps, stepAtPosition);
3062     }
3063 
3064     if (equalIgnoringCase(value->function->name, "cubic-bezier")) {
3065         // For cubic bezier, 4 values must be specified.
3066         if (!args || args->size() != 7)
3067             return nullptr;
3068 
3069         // There are two points specified. The x values must be between 0 and 1 but the y values can exceed this range.
3070         double x1, y1, x2, y2;
3071 
3072         if (!parseCubicBezierTimingFunctionValue(args, x1))
3073             return nullptr;
3074         if (x1 < 0 || x1 > 1)
3075             return nullptr;
3076         if (!parseCubicBezierTimingFunctionValue(args, y1))
3077             return nullptr;
3078         if (!parseCubicBezierTimingFunctionValue(args, x2))
3079             return nullptr;
3080         if (x2 < 0 || x2 > 1)
3081             return nullptr;
3082         if (!parseCubicBezierTimingFunctionValue(args, y2))
3083             return nullptr;
3084 
3085         return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2);
3086     }
3087 
3088     return nullptr;
3089 }
3090 
parseAnimationProperty(CSSPropertyID propId)3091 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAnimationProperty(CSSPropertyID propId)
3092 {
3093     RefPtrWillBeRawPtr<CSSValue> value = nullptr;
3094     switch (propId) {
3095     case CSSPropertyAnimationDelay:
3096     case CSSPropertyWebkitAnimationDelay:
3097     case CSSPropertyTransitionDelay:
3098     case CSSPropertyWebkitTransitionDelay:
3099         value = parseAnimationDelay();
3100         break;
3101     case CSSPropertyAnimationDirection:
3102     case CSSPropertyWebkitAnimationDirection:
3103         value = parseAnimationDirection();
3104         break;
3105     case CSSPropertyAnimationDuration:
3106     case CSSPropertyWebkitAnimationDuration:
3107     case CSSPropertyTransitionDuration:
3108     case CSSPropertyWebkitTransitionDuration:
3109         value = parseAnimationDuration();
3110         break;
3111     case CSSPropertyAnimationFillMode:
3112     case CSSPropertyWebkitAnimationFillMode:
3113         value = parseAnimationFillMode();
3114         break;
3115     case CSSPropertyAnimationIterationCount:
3116     case CSSPropertyWebkitAnimationIterationCount:
3117         value = parseAnimationIterationCount();
3118         break;
3119     case CSSPropertyAnimationName:
3120     case CSSPropertyWebkitAnimationName:
3121         value = parseAnimationName();
3122         break;
3123     case CSSPropertyAnimationPlayState:
3124     case CSSPropertyWebkitAnimationPlayState:
3125         value = parseAnimationPlayState();
3126         break;
3127     case CSSPropertyTransitionProperty:
3128     case CSSPropertyWebkitTransitionProperty:
3129         value = parseAnimationProperty();
3130         break;
3131     case CSSPropertyAnimationTimingFunction:
3132     case CSSPropertyWebkitAnimationTimingFunction:
3133     case CSSPropertyTransitionTimingFunction:
3134     case CSSPropertyWebkitTransitionTimingFunction:
3135         value = parseAnimationTimingFunction();
3136         break;
3137     default:
3138         ASSERT_NOT_REACHED();
3139         return nullptr;
3140     }
3141 
3142     if (value)
3143         m_valueList->next();
3144     return value.release();
3145 }
3146 
parseAnimationPropertyList(CSSPropertyID propId)3147 PassRefPtrWillBeRawPtr<CSSValueList> CSSPropertyParser::parseAnimationPropertyList(CSSPropertyID propId)
3148 {
3149     RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
3150     while (m_valueList->current()) {
3151         RefPtrWillBeRawPtr<CSSValue> value = parseAnimationProperty(propId);
3152         if (!value)
3153             return nullptr;
3154         list->append(value.release());
3155         if (CSSParserValue* parserValue = m_valueList->current()) {
3156             if (!isComma(parserValue))
3157                 return nullptr;
3158             m_valueList->next();
3159             ASSERT(m_valueList->current());
3160         }
3161     }
3162     if ((propId == CSSPropertyTransitionProperty || propId == CSSPropertyWebkitTransitionProperty) && !isValidTransitionPropertyList(list.get()))
3163         return nullptr;
3164     ASSERT(list->length());
3165     return list.release();
3166 }
3167 
isCSSWideKeyword(CSSParserValue & value)3168 static inline bool isCSSWideKeyword(CSSParserValue& value)
3169 {
3170     return value.id == CSSValueInitial || value.id == CSSValueInherit || value.id == CSSValueDefault;
3171 }
3172 
isValidCustomIdentForGridPositions(CSSParserValue & value)3173 static inline bool isValidCustomIdentForGridPositions(CSSParserValue& value)
3174 {
3175     // FIXME: we need a more general solution for <custom-ident> in all properties.
3176     return value.unit == CSSPrimitiveValue::CSS_IDENT && value.id != CSSValueSpan && value.id != CSSValueAuto && !isCSSWideKeyword(value);
3177 }
3178 
3179 // The function parses [ <integer> || <custom-ident> ] in <grid-line> (which can be stand alone or with 'span').
parseIntegerOrCustomIdentFromGridPosition(RefPtrWillBeRawPtr<CSSPrimitiveValue> & numericValue,RefPtrWillBeRawPtr<CSSPrimitiveValue> & gridLineName)3180 bool CSSPropertyParser::parseIntegerOrCustomIdentFromGridPosition(RefPtrWillBeRawPtr<CSSPrimitiveValue>& numericValue, RefPtrWillBeRawPtr<CSSPrimitiveValue>& gridLineName)
3181 {
3182     CSSParserValue* value = m_valueList->current();
3183     if (validUnit(value, FInteger) && value->fValue) {
3184         numericValue = createPrimitiveNumericValue(value);
3185         value = m_valueList->next();
3186         if (value && isValidCustomIdentForGridPositions(*value)) {
3187             gridLineName = createPrimitiveStringValue(m_valueList->current());
3188             m_valueList->next();
3189         }
3190         return true;
3191     }
3192 
3193     if (isValidCustomIdentForGridPositions(*value)) {
3194         gridLineName = createPrimitiveStringValue(m_valueList->current());
3195         value = m_valueList->next();
3196         if (value && validUnit(value, FInteger) && value->fValue) {
3197             numericValue = createPrimitiveNumericValue(value);
3198             m_valueList->next();
3199         }
3200         return true;
3201     }
3202 
3203     return false;
3204 }
3205 
parseGridPosition()3206 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseGridPosition()
3207 {
3208     ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
3209 
3210     CSSParserValue* value = m_valueList->current();
3211     if (value->id == CSSValueAuto) {
3212         m_valueList->next();
3213         return cssValuePool().createIdentifierValue(CSSValueAuto);
3214     }
3215 
3216     RefPtrWillBeRawPtr<CSSPrimitiveValue> numericValue = nullptr;
3217     RefPtrWillBeRawPtr<CSSPrimitiveValue> gridLineName = nullptr;
3218     bool hasSeenSpanKeyword = false;
3219 
3220     if (parseIntegerOrCustomIdentFromGridPosition(numericValue, gridLineName)) {
3221         value = m_valueList->current();
3222         if (value && value->id == CSSValueSpan) {
3223             hasSeenSpanKeyword = true;
3224             m_valueList->next();
3225         }
3226     } else if (value->id == CSSValueSpan) {
3227         hasSeenSpanKeyword = true;
3228         if (CSSParserValue* nextValue = m_valueList->next()) {
3229             if (!isForwardSlashOperator(nextValue) && !parseIntegerOrCustomIdentFromGridPosition(numericValue, gridLineName))
3230                 return nullptr;
3231         }
3232     }
3233 
3234     // Check that we have consumed all the value list. For shorthands, the parser will pass
3235     // the whole value list (including the opposite position).
3236     if (m_valueList->current() && !isForwardSlashOperator(m_valueList->current()))
3237         return nullptr;
3238 
3239     // If we didn't parse anything, this is not a valid grid position.
3240     if (!hasSeenSpanKeyword && !gridLineName && !numericValue)
3241         return nullptr;
3242 
3243     // Negative numbers are not allowed for span (but are for <integer>).
3244     if (hasSeenSpanKeyword && numericValue && numericValue->getIntValue() < 0)
3245         return nullptr;
3246 
3247     // For the <custom-ident> case.
3248     if (gridLineName && !numericValue && !hasSeenSpanKeyword)
3249         return cssValuePool().createValue(gridLineName->getStringValue(), CSSPrimitiveValue::CSS_STRING);
3250 
3251     RefPtrWillBeRawPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3252     if (hasSeenSpanKeyword)
3253         values->append(cssValuePool().createIdentifierValue(CSSValueSpan));
3254     if (numericValue)
3255         values->append(numericValue.release());
3256     if (gridLineName)
3257         values->append(gridLineName.release());
3258     ASSERT(values->length());
3259     return values.release();
3260 }
3261 
gridMissingGridPositionValue(CSSValue * value)3262 static PassRefPtrWillBeRawPtr<CSSValue> gridMissingGridPositionValue(CSSValue* value)
3263 {
3264     if (value->isPrimitiveValue() && toCSSPrimitiveValue(value)->isString())
3265         return value;
3266 
3267     return cssValuePool().createIdentifierValue(CSSValueAuto);
3268 }
3269 
parseGridItemPositionShorthand(CSSPropertyID shorthandId,bool important)3270 bool CSSPropertyParser::parseGridItemPositionShorthand(CSSPropertyID shorthandId, bool important)
3271 {
3272     ShorthandScope scope(this, shorthandId);
3273     const StylePropertyShorthand& shorthand = shorthandForProperty(shorthandId);
3274     ASSERT(shorthand.length() == 2);
3275 
3276     RefPtrWillBeRawPtr<CSSValue> startValue = parseGridPosition();
3277     if (!startValue)
3278         return false;
3279 
3280     RefPtrWillBeRawPtr<CSSValue> endValue = nullptr;
3281     if (m_valueList->current()) {
3282         if (!isForwardSlashOperator(m_valueList->current()))
3283             return false;
3284 
3285         if (!m_valueList->next())
3286             return false;
3287 
3288         endValue = parseGridPosition();
3289         if (!endValue || m_valueList->current())
3290             return false;
3291     } else {
3292         endValue = gridMissingGridPositionValue(startValue.get());
3293     }
3294 
3295     addProperty(shorthand.properties()[0], startValue, important);
3296     addProperty(shorthand.properties()[1], endValue, important);
3297     return true;
3298 }
3299 
parseGridTemplateRowsAndAreas(PassRefPtrWillBeRawPtr<CSSValue> templateColumns,bool important)3300 bool CSSPropertyParser::parseGridTemplateRowsAndAreas(PassRefPtrWillBeRawPtr<CSSValue> templateColumns, bool important)
3301 {
3302     NamedGridAreaMap gridAreaMap;
3303     size_t rowCount = 0;
3304     size_t columnCount = 0;
3305     bool trailingIdentWasAdded = false;
3306     RefPtrWillBeRawPtr<CSSValueList> templateRows = CSSValueList::createSpaceSeparated();
3307 
3308     // At least template-areas strings must be defined.
3309     if (!m_valueList->current())
3310         return false;
3311 
3312     while (m_valueList->current()) {
3313         // Handle leading <custom-ident>*.
3314         if (m_valueList->current()->unit == CSSParserValue::ValueList) {
3315             if (trailingIdentWasAdded) {
3316                 // A row's trailing ident must be concatenated with the next row's leading one.
3317                 parseGridLineNames(*m_valueList, *templateRows, toCSSGridLineNamesValue(templateRows->item(templateRows->length() - 1)));
3318             } else {
3319                 parseGridLineNames(*m_valueList, *templateRows);
3320             }
3321         }
3322 
3323         // Handle a template-area's row.
3324         if (!parseGridTemplateAreasRow(gridAreaMap, rowCount, columnCount))
3325             return false;
3326         ++rowCount;
3327 
3328         // Handle template-rows's track-size.
3329         if (m_valueList->current() && m_valueList->current()->unit != CSSParserValue::ValueList && m_valueList->current()->unit != CSSPrimitiveValue::CSS_STRING) {
3330             RefPtrWillBeRawPtr<CSSValue> value = parseGridTrackSize(*m_valueList);
3331             if (!value)
3332                 return false;
3333             templateRows->append(value);
3334         } else {
3335             templateRows->append(cssValuePool().createIdentifierValue(CSSValueAuto));
3336         }
3337 
3338         // This will handle the trailing/leading <custom-ident>* in the grammar.
3339         trailingIdentWasAdded = false;
3340         if (m_valueList->current() && m_valueList->current()->unit == CSSParserValue::ValueList)
3341             trailingIdentWasAdded = parseGridLineNames(*m_valueList, *templateRows);
3342     }
3343 
3344     // [<track-list> /]?
3345     if (templateColumns)
3346         addProperty(CSSPropertyGridTemplateColumns, templateColumns, important);
3347     else
3348         addProperty(CSSPropertyGridTemplateColumns,  cssValuePool().createIdentifierValue(CSSValueNone), important);
3349 
3350     // [<line-names>? <string> [<track-size> <line-names>]? ]+
3351     RefPtrWillBeRawPtr<CSSValue> templateAreas = CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount);
3352     addProperty(CSSPropertyGridTemplateAreas, templateAreas.release(), important);
3353     addProperty(CSSPropertyGridTemplateRows, templateRows.release(), important);
3354 
3355 
3356     return true;
3357 }
3358 
3359 
parseGridTemplateShorthand(bool important)3360 bool CSSPropertyParser::parseGridTemplateShorthand(bool important)
3361 {
3362     ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
3363 
3364     ShorthandScope scope(this, CSSPropertyGridTemplate);
3365     ASSERT(gridTemplateShorthand().length() == 3);
3366 
3367     // At least "none" must be defined.
3368     if (!m_valueList->current())
3369         return false;
3370 
3371     bool firstValueIsNone = m_valueList->current()->id == CSSValueNone;
3372 
3373     // 1- 'none' case.
3374     if (firstValueIsNone && !m_valueList->next()) {
3375         addProperty(CSSPropertyGridTemplateColumns, cssValuePool().createIdentifierValue(CSSValueNone), important);
3376         addProperty(CSSPropertyGridTemplateRows, cssValuePool().createIdentifierValue(CSSValueNone), important);
3377         addProperty(CSSPropertyGridTemplateAreas, cssValuePool().createIdentifierValue(CSSValueNone), important);
3378         return true;
3379     }
3380 
3381     unsigned index = 0;
3382     RefPtrWillBeRawPtr<CSSValue> columnsValue = nullptr;
3383     if (firstValueIsNone) {
3384         columnsValue = cssValuePool().createIdentifierValue(CSSValueNone);
3385     } else {
3386         columnsValue = parseGridTrackList();
3387     }
3388 
3389     // 2- <grid-template-columns> / <grid-template-columns> syntax.
3390     if (columnsValue) {
3391         if (!(m_valueList->current() && isForwardSlashOperator(m_valueList->current()) && m_valueList->next()))
3392             return false;
3393         index = m_valueList->currentIndex();
3394         if (RefPtrWillBeRawPtr<CSSValue> rowsValue = parseGridTrackList()) {
3395             if (m_valueList->current())
3396                 return false;
3397             addProperty(CSSPropertyGridTemplateColumns, columnsValue, important);
3398             addProperty(CSSPropertyGridTemplateRows, rowsValue, important);
3399             addProperty(CSSPropertyGridTemplateAreas, cssValuePool().createIdentifierValue(CSSValueNone), important);
3400             return true;
3401         }
3402     }
3403 
3404 
3405     // 3- [<track-list> /]? [<line-names>? <string> [<track-size> <line-names>]? ]+ syntax.
3406     // The template-columns <track-list> can't be 'none'.
3407     if (firstValueIsNone)
3408         return false;
3409     // It requires to rewind parsing due to previous syntax failures.
3410     m_valueList->setCurrentIndex(index);
3411     return parseGridTemplateRowsAndAreas(columnsValue, important);
3412 }
3413 
parseGridShorthand(bool important)3414 bool CSSPropertyParser::parseGridShorthand(bool important)
3415 {
3416     ShorthandScope scope(this, CSSPropertyGrid);
3417     ASSERT(shorthandForProperty(CSSPropertyGrid).length() == 6);
3418 
3419     // 1- <grid-template>
3420     if (parseGridTemplateShorthand(important)) {
3421         // It can only be specified the explicit or the implicit grid properties in a single grid declaration.
3422         // The sub-properties not specified are set to their initial value, as normal for shorthands.
3423         addProperty(CSSPropertyGridAutoFlow, cssValuePool().createImplicitInitialValue(), important);
3424         addProperty(CSSPropertyGridAutoColumns, cssValuePool().createImplicitInitialValue(), important);
3425         addProperty(CSSPropertyGridAutoRows, cssValuePool().createImplicitInitialValue(), important);
3426         return true;
3427     }
3428 
3429     // Need to rewind parsing to explore the alternative syntax of this shorthand.
3430     m_valueList->setCurrentIndex(0);
3431 
3432     // 2- <grid-auto-flow> [ <grid-auto-columns> [ / <grid-auto-rows> ]? ]
3433     if (!parseValue(CSSPropertyGridAutoFlow, important))
3434         return false;
3435 
3436     RefPtrWillBeRawPtr<CSSValue> autoColumnsValue = nullptr;
3437     RefPtrWillBeRawPtr<CSSValue> autoRowsValue = nullptr;
3438 
3439     if (m_valueList->current()) {
3440         autoColumnsValue = parseGridTrackSize(*m_valueList);
3441         if (!autoColumnsValue)
3442             return false;
3443         if (m_valueList->current()) {
3444             if (!isForwardSlashOperator(m_valueList->current()) || !m_valueList->next())
3445                 return false;
3446             autoRowsValue = parseGridTrackSize(*m_valueList);
3447             if (!autoRowsValue)
3448                 return false;
3449         }
3450         if (m_valueList->current())
3451             return false;
3452     } else {
3453         // Other omitted values are set to their initial values.
3454         autoColumnsValue = cssValuePool().createImplicitInitialValue();
3455         autoRowsValue = cssValuePool().createImplicitInitialValue();
3456     }
3457 
3458     // if <grid-auto-rows> value is omitted, it is set to the value specified for grid-auto-columns.
3459     if (!autoRowsValue)
3460         autoRowsValue = autoColumnsValue;
3461 
3462     addProperty(CSSPropertyGridAutoColumns, autoColumnsValue, important);
3463     addProperty(CSSPropertyGridAutoRows, autoRowsValue, important);
3464 
3465     // It can only be specified the explicit or the implicit grid properties in a single grid declaration.
3466     // The sub-properties not specified are set to their initial value, as normal for shorthands.
3467     addProperty(CSSPropertyGridTemplateColumns, cssValuePool().createImplicitInitialValue(), important);
3468     addProperty(CSSPropertyGridTemplateRows, cssValuePool().createImplicitInitialValue(), important);
3469     addProperty(CSSPropertyGridTemplateAreas, cssValuePool().createImplicitInitialValue(), important);
3470 
3471     return true;
3472 }
3473 
parseGridAreaShorthand(bool important)3474 bool CSSPropertyParser::parseGridAreaShorthand(bool important)
3475 {
3476     ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
3477 
3478     ShorthandScope scope(this, CSSPropertyGridArea);
3479     const StylePropertyShorthand& shorthand = gridAreaShorthand();
3480     ASSERT_UNUSED(shorthand, shorthand.length() == 4);
3481 
3482     RefPtrWillBeRawPtr<CSSValue> rowStartValue = parseGridPosition();
3483     if (!rowStartValue)
3484         return false;
3485 
3486     RefPtrWillBeRawPtr<CSSValue> columnStartValue = nullptr;
3487     if (!parseSingleGridAreaLonghand(columnStartValue))
3488         return false;
3489 
3490     RefPtrWillBeRawPtr<CSSValue> rowEndValue = nullptr;
3491     if (!parseSingleGridAreaLonghand(rowEndValue))
3492         return false;
3493 
3494     RefPtrWillBeRawPtr<CSSValue> columnEndValue = nullptr;
3495     if (!parseSingleGridAreaLonghand(columnEndValue))
3496         return false;
3497 
3498     if (!columnStartValue)
3499         columnStartValue = gridMissingGridPositionValue(rowStartValue.get());
3500 
3501     if (!rowEndValue)
3502         rowEndValue = gridMissingGridPositionValue(rowStartValue.get());
3503 
3504     if (!columnEndValue)
3505         columnEndValue = gridMissingGridPositionValue(columnStartValue.get());
3506 
3507     addProperty(CSSPropertyGridRowStart, rowStartValue, important);
3508     addProperty(CSSPropertyGridColumnStart, columnStartValue, important);
3509     addProperty(CSSPropertyGridRowEnd, rowEndValue, important);
3510     addProperty(CSSPropertyGridColumnEnd, columnEndValue, important);
3511     return true;
3512 }
3513 
parseSingleGridAreaLonghand(RefPtrWillBeRawPtr<CSSValue> & property)3514 bool CSSPropertyParser::parseSingleGridAreaLonghand(RefPtrWillBeRawPtr<CSSValue>& property)
3515 {
3516     if (!m_valueList->current())
3517         return true;
3518 
3519     if (!isForwardSlashOperator(m_valueList->current()))
3520         return false;
3521 
3522     if (!m_valueList->next())
3523         return false;
3524 
3525     property = parseGridPosition();
3526     return true;
3527 }
3528 
parseGridLineNames(CSSParserValueList & inputList,CSSValueList & valueList,CSSGridLineNamesValue * previousNamedAreaTrailingLineNames)3529 bool CSSPropertyParser::parseGridLineNames(CSSParserValueList& inputList, CSSValueList& valueList, CSSGridLineNamesValue* previousNamedAreaTrailingLineNames)
3530 {
3531     ASSERT(inputList.current() && inputList.current()->unit == CSSParserValue::ValueList);
3532 
3533     CSSParserValueList* identList = inputList.current()->valueList;
3534     if (!identList->size()) {
3535         inputList.next();
3536         return false;
3537     }
3538 
3539     // Need to ensure the identList is at the heading index, since the parserList might have been rewound.
3540     identList->setCurrentIndex(0);
3541 
3542     RefPtrWillBeRawPtr<CSSGridLineNamesValue> lineNames = previousNamedAreaTrailingLineNames;
3543     if (!lineNames)
3544         lineNames = CSSGridLineNamesValue::create();
3545     while (CSSParserValue* identValue = identList->current()) {
3546         ASSERT(identValue->unit == CSSPrimitiveValue::CSS_IDENT);
3547         RefPtrWillBeRawPtr<CSSPrimitiveValue> lineName = createPrimitiveStringValue(identValue);
3548         lineNames->append(lineName.release());
3549         identList->next();
3550     }
3551     if (!previousNamedAreaTrailingLineNames)
3552         valueList.append(lineNames.release());
3553 
3554     inputList.next();
3555     return true;
3556 }
3557 
parseGridTrackList()3558 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseGridTrackList()
3559 {
3560     ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
3561 
3562     CSSParserValue* value = m_valueList->current();
3563     if (value->id == CSSValueNone) {
3564         m_valueList->next();
3565         return cssValuePool().createIdentifierValue(CSSValueNone);
3566     }
3567 
3568     RefPtrWillBeRawPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3569     // Handle leading  <ident>*.
3570     value = m_valueList->current();
3571     if (value && value->unit == CSSParserValue::ValueList)
3572         parseGridLineNames(*m_valueList, *values);
3573 
3574     bool seenTrackSizeOrRepeatFunction = false;
3575     while (CSSParserValue* currentValue = m_valueList->current()) {
3576         if (isForwardSlashOperator(currentValue))
3577             break;
3578         if (currentValue->unit == CSSParserValue::Function && equalIgnoringCase(currentValue->function->name, "repeat")) {
3579             if (!parseGridTrackRepeatFunction(*values))
3580                 return nullptr;
3581             seenTrackSizeOrRepeatFunction = true;
3582         } else {
3583             RefPtrWillBeRawPtr<CSSValue> value = parseGridTrackSize(*m_valueList);
3584             if (!value)
3585                 return nullptr;
3586             values->append(value);
3587             seenTrackSizeOrRepeatFunction = true;
3588         }
3589         // This will handle the trailing <ident>* in the grammar.
3590         value = m_valueList->current();
3591         if (value && value->unit == CSSParserValue::ValueList)
3592             parseGridLineNames(*m_valueList, *values);
3593     }
3594 
3595     // We should have found a <track-size> or else it is not a valid <track-list>
3596     if (!seenTrackSizeOrRepeatFunction)
3597         return nullptr;
3598 
3599     return values;
3600 }
3601 
parseGridTrackRepeatFunction(CSSValueList & list)3602 bool CSSPropertyParser::parseGridTrackRepeatFunction(CSSValueList& list)
3603 {
3604     CSSParserValueList* arguments = m_valueList->current()->function->args.get();
3605     if (!arguments || arguments->size() < 3 || !validUnit(arguments->valueAt(0), FPositiveInteger) || !isComma(arguments->valueAt(1)))
3606         return false;
3607 
3608     ASSERT_WITH_SECURITY_IMPLICATION(arguments->valueAt(0)->fValue > 0);
3609     size_t repetitions = arguments->valueAt(0)->fValue;
3610 
3611     // The spec allows us to clamp the number of repetitions: http://www.w3.org/TR/css-grid-1/#repeat-notation
3612     const size_t maxRepetitions = 10000;
3613     repetitions = std::min(repetitions, maxRepetitions);
3614 
3615     RefPtrWillBeRawPtr<CSSValueList> repeatedValues = CSSValueList::createSpaceSeparated();
3616     arguments->next(); // Skip the repetition count.
3617     arguments->next(); // Skip the comma.
3618 
3619     // Handle leading <ident>*.
3620     CSSParserValue* currentValue = arguments->current();
3621     if (currentValue && currentValue->unit == CSSParserValue::ValueList)
3622         parseGridLineNames(*arguments, *repeatedValues);
3623 
3624     bool seenTrackSize = false;
3625     while (arguments->current()) {
3626         RefPtrWillBeRawPtr<CSSValue> trackSize = parseGridTrackSize(*arguments);
3627         if (!trackSize)
3628             return false;
3629 
3630         repeatedValues->append(trackSize);
3631         seenTrackSize = true;
3632 
3633         // This takes care of any trailing <ident>* in the grammar.
3634         currentValue = arguments->current();
3635         if (currentValue && currentValue->unit == CSSParserValue::ValueList)
3636             parseGridLineNames(*arguments, *repeatedValues);
3637     }
3638 
3639     // We should have found at least one <track-size> or else it is not a valid <track-list>.
3640     if (!seenTrackSize)
3641         return false;
3642 
3643     for (size_t i = 0; i < repetitions; ++i) {
3644         for (size_t j = 0; j < repeatedValues->length(); ++j)
3645             list.append(repeatedValues->item(j));
3646     }
3647 
3648     // parseGridTrackSize iterated over the repeat arguments, move to the next value.
3649     m_valueList->next();
3650     return true;
3651 }
3652 
3653 
parseGridTrackSize(CSSParserValueList & inputList)3654 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseGridTrackSize(CSSParserValueList& inputList)
3655 {
3656     ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
3657 
3658     CSSParserValue* currentValue = inputList.current();
3659     inputList.next();
3660 
3661     if (currentValue->id == CSSValueAuto)
3662         return cssValuePool().createIdentifierValue(CSSValueAuto);
3663 
3664     if (currentValue->unit == CSSParserValue::Function && equalIgnoringCase(currentValue->function->name, "minmax")) {
3665         // The spec defines the following grammar: minmax( <track-breadth> , <track-breadth> )
3666         CSSParserValueList* arguments = currentValue->function->args.get();
3667         if (!arguments || arguments->size() != 3 || !isComma(arguments->valueAt(1)))
3668             return nullptr;
3669 
3670         RefPtrWillBeRawPtr<CSSPrimitiveValue> minTrackBreadth = parseGridBreadth(arguments->valueAt(0));
3671         if (!minTrackBreadth)
3672             return nullptr;
3673 
3674         RefPtrWillBeRawPtr<CSSPrimitiveValue> maxTrackBreadth = parseGridBreadth(arguments->valueAt(2));
3675         if (!maxTrackBreadth)
3676             return nullptr;
3677 
3678         RefPtrWillBeRawPtr<CSSValueList> parsedArguments = CSSValueList::createCommaSeparated();
3679         parsedArguments->append(minTrackBreadth);
3680         parsedArguments->append(maxTrackBreadth);
3681         return CSSFunctionValue::create("minmax(", parsedArguments);
3682     }
3683 
3684     return parseGridBreadth(currentValue);
3685 }
3686 
parseGridBreadth(CSSParserValue * currentValue)3687 PassRefPtrWillBeRawPtr<CSSPrimitiveValue> CSSPropertyParser::parseGridBreadth(CSSParserValue* currentValue)
3688 {
3689     if (currentValue->id == CSSValueMinContent || currentValue->id == CSSValueMaxContent)
3690         return cssValuePool().createIdentifierValue(currentValue->id);
3691 
3692     if (currentValue->unit == CSSPrimitiveValue::CSS_FR) {
3693         double flexValue = currentValue->fValue;
3694 
3695         // Fractional unit is a non-negative dimension.
3696         if (flexValue <= 0)
3697             return nullptr;
3698 
3699         return cssValuePool().createValue(flexValue, CSSPrimitiveValue::CSS_FR);
3700     }
3701 
3702     if (!validUnit(currentValue, FNonNeg | FLength | FPercent))
3703         return nullptr;
3704 
3705     return createPrimitiveNumericValue(currentValue);
3706 }
3707 
parseGridTemplateAreasRow(NamedGridAreaMap & gridAreaMap,const size_t rowCount,size_t & columnCount)3708 bool CSSPropertyParser::parseGridTemplateAreasRow(NamedGridAreaMap& gridAreaMap, const size_t rowCount, size_t& columnCount)
3709 {
3710     CSSParserValue* currentValue = m_valueList->current();
3711     if (!currentValue || currentValue->unit != CSSPrimitiveValue::CSS_STRING)
3712         return false;
3713 
3714     String gridRowNames = currentValue->string;
3715     if (gridRowNames.isEmpty() || gridRowNames.containsOnlyWhitespace())
3716         return false;
3717 
3718     Vector<String> columnNames;
3719     gridRowNames.split(' ', columnNames);
3720 
3721     if (!columnCount) {
3722         columnCount = columnNames.size();
3723         ASSERT(columnCount);
3724     } else if (columnCount != columnNames.size()) {
3725         // The declaration is invalid is all the rows don't have the number of columns.
3726         return false;
3727     }
3728 
3729     for (size_t currentCol = 0; currentCol < columnCount; ++currentCol) {
3730         const String& gridAreaName = columnNames[currentCol];
3731 
3732         // Unamed areas are always valid (we consider them to be 1x1).
3733         if (gridAreaName == ".")
3734             continue;
3735 
3736         // We handle several grid areas with the same name at once to simplify the validation code.
3737         size_t lookAheadCol;
3738         for (lookAheadCol = currentCol; lookAheadCol < (columnCount - 1); ++lookAheadCol) {
3739             if (columnNames[lookAheadCol + 1] != gridAreaName)
3740                 break;
3741         }
3742 
3743         NamedGridAreaMap::iterator gridAreaIt = gridAreaMap.find(gridAreaName);
3744         if (gridAreaIt == gridAreaMap.end()) {
3745             gridAreaMap.add(gridAreaName, GridCoordinate(GridSpan(rowCount, rowCount), GridSpan(currentCol, lookAheadCol)));
3746         } else {
3747             GridCoordinate& gridCoordinate = gridAreaIt->value;
3748 
3749             // The following checks test that the grid area is a single filled-in rectangle.
3750             // 1. The new row is adjacent to the previously parsed row.
3751             if (rowCount != gridCoordinate.rows.resolvedFinalPosition.next().toInt())
3752                 return false;
3753 
3754             // 2. The new area starts at the same position as the previously parsed area.
3755             if (currentCol != gridCoordinate.columns.resolvedInitialPosition.toInt())
3756                 return false;
3757 
3758             // 3. The new area ends at the same position as the previously parsed area.
3759             if (lookAheadCol != gridCoordinate.columns.resolvedFinalPosition.toInt())
3760                 return false;
3761 
3762             ++gridCoordinate.rows.resolvedFinalPosition;
3763         }
3764         currentCol = lookAheadCol;
3765     }
3766 
3767     m_valueList->next();
3768     return true;
3769 }
3770 
parseGridTemplateAreas()3771 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseGridTemplateAreas()
3772 {
3773     NamedGridAreaMap gridAreaMap;
3774     size_t rowCount = 0;
3775     size_t columnCount = 0;
3776 
3777     while (m_valueList->current()) {
3778         if (!parseGridTemplateAreasRow(gridAreaMap, rowCount, columnCount))
3779             return nullptr;
3780         ++rowCount;
3781     }
3782 
3783     if (!rowCount || !columnCount)
3784         return nullptr;
3785 
3786     return CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount);
3787 }
3788 
parseGridAutoFlow(CSSParserValueList & list)3789 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseGridAutoFlow(CSSParserValueList& list)
3790 {
3791     // [ row | column ] && dense? | stack && [ row | column ]?
3792     ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled());
3793 
3794     CSSParserValue* value = list.current();
3795     if (!value)
3796         return nullptr;
3797 
3798     RefPtrWillBeRawPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
3799 
3800     // First parameter.
3801     CSSValueID firstId = value->id;
3802     if (firstId != CSSValueRow && firstId != CSSValueColumn && firstId != CSSValueDense && firstId != CSSValueStack)
3803         return nullptr;
3804     parsedValues->append(cssValuePool().createIdentifierValue(firstId));
3805 
3806     // Second parameter, if any.
3807     value = list.next();
3808     if (!value && firstId == CSSValueDense)
3809         return nullptr;
3810 
3811     if (value) {
3812         switch (firstId) {
3813         case CSSValueRow:
3814         case CSSValueColumn:
3815             if (value->id != CSSValueDense && value->id != CSSValueStack)
3816                 return parsedValues;
3817             break;
3818         case CSSValueDense:
3819         case CSSValueStack:
3820             if (value->id != CSSValueRow && value->id != CSSValueColumn)
3821                 return parsedValues;
3822             break;
3823         default:
3824             return parsedValues;
3825         }
3826         parsedValues->append(cssValuePool().createIdentifierValue(value->id));
3827         list.next();
3828     }
3829 
3830     return parsedValues;
3831 }
3832 
parseCounterContent(CSSParserValueList * args,bool counters)3833 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseCounterContent(CSSParserValueList* args, bool counters)
3834 {
3835     unsigned numArgs = args->size();
3836     if (counters && numArgs != 3 && numArgs != 5)
3837         return nullptr;
3838     if (!counters && numArgs != 1 && numArgs != 3)
3839         return nullptr;
3840 
3841     CSSParserValue* i = args->current();
3842     if (i->unit != CSSPrimitiveValue::CSS_IDENT)
3843         return nullptr;
3844     RefPtrWillBeRawPtr<CSSPrimitiveValue> identifier = createPrimitiveStringValue(i);
3845 
3846     RefPtrWillBeRawPtr<CSSPrimitiveValue> separator = nullptr;
3847     if (!counters)
3848         separator = cssValuePool().createValue(String(), CSSPrimitiveValue::CSS_STRING);
3849     else {
3850         args->next();
3851         if (!consumeComma(args))
3852             return nullptr;
3853 
3854         i = args->current();
3855         if (i->unit != CSSPrimitiveValue::CSS_STRING)
3856             return nullptr;
3857 
3858         separator = createPrimitiveStringValue(i);
3859     }
3860 
3861     RefPtrWillBeRawPtr<CSSPrimitiveValue> listStyle = nullptr;
3862     i = args->next();
3863     if (!i) // Make the list style default decimal
3864         listStyle = cssValuePool().createIdentifierValue(CSSValueDecimal);
3865     else {
3866         if (!consumeComma(args))
3867             return nullptr;
3868 
3869         i = args->current();
3870         if (i->unit != CSSPrimitiveValue::CSS_IDENT)
3871             return nullptr;
3872 
3873         CSSValueID listStyleID = CSSValueInvalid;
3874         if (i->id == CSSValueNone || (i->id >= CSSValueDisc && i->id <= CSSValueKatakanaIroha))
3875             listStyleID = i->id;
3876         else
3877             return nullptr;
3878 
3879         listStyle = cssValuePool().createIdentifierValue(listStyleID);
3880     }
3881 
3882     return cssValuePool().createValue(Counter::create(identifier.release(), listStyle.release(), separator.release()));
3883 }
3884 
parseClipShape(CSSPropertyID propId,bool important)3885 bool CSSPropertyParser::parseClipShape(CSSPropertyID propId, bool important)
3886 {
3887     CSSParserValue* value = m_valueList->current();
3888     CSSParserValueList* args = value->function->args.get();
3889 
3890     if (!equalIgnoringCase(value->function->name, "rect") || !args)
3891         return false;
3892 
3893     // rect(t, r, b, l) || rect(t r b l)
3894     if (args->size() != 4 && args->size() != 7)
3895         return false;
3896     RefPtrWillBeRawPtr<Rect> rect = Rect::create();
3897     int i = 0;
3898     CSSParserValue* a = args->current();
3899     while (a) {
3900         if (a->id != CSSValueAuto && !validUnit(a, FLength))
3901             return false;
3902         RefPtrWillBeRawPtr<CSSPrimitiveValue> length = a->id == CSSValueAuto ?
3903             cssValuePool().createIdentifierValue(CSSValueAuto) :
3904             createPrimitiveNumericValue(a);
3905         if (i == 0)
3906             rect->setTop(length);
3907         else if (i == 1)
3908             rect->setRight(length);
3909         else if (i == 2)
3910             rect->setBottom(length);
3911         else
3912             rect->setLeft(length);
3913         a = args->next();
3914         if (a && args->size() == 7) {
3915             if (!consumeComma(args))
3916                 return false;
3917             a = args->current();
3918         }
3919         i++;
3920     }
3921     addProperty(propId, cssValuePool().createValue(rect.release()), important);
3922     m_valueList->next();
3923     return true;
3924 }
3925 
completeBorderRadii(RefPtrWillBeRawPtr<CSSPrimitiveValue> radii[4])3926 static void completeBorderRadii(RefPtrWillBeRawPtr<CSSPrimitiveValue> radii[4])
3927 {
3928     if (radii[3])
3929         return;
3930     if (!radii[2]) {
3931         if (!radii[1])
3932             radii[1] = radii[0];
3933         radii[2] = radii[0];
3934     }
3935     radii[3] = radii[1];
3936 }
3937 
3938 // FIXME: This should be refactored with parseBorderRadius.
3939 // parseBorderRadius contains support for some legacy radius construction.
parseInsetRoundedCorners(PassRefPtrWillBeRawPtr<CSSBasicShapeInset> shape,CSSParserValueList * args)3940 PassRefPtrWillBeRawPtr<CSSBasicShape> CSSPropertyParser::parseInsetRoundedCorners(PassRefPtrWillBeRawPtr<CSSBasicShapeInset> shape, CSSParserValueList* args)
3941 {
3942     CSSParserValue* argument = args->next();
3943 
3944     if (!argument)
3945         return nullptr;
3946 
3947     Vector<CSSParserValue*> radiusArguments;
3948     while (argument) {
3949         radiusArguments.append(argument);
3950         argument = args->next();
3951     }
3952 
3953     unsigned num = radiusArguments.size();
3954     if (!num || num > 9)
3955         return nullptr;
3956 
3957     // FIXME: Refactor completeBorderRadii and the array
3958     RefPtrWillBeRawPtr<CSSPrimitiveValue> radii[2][4];
3959 #if ENABLE(OILPAN)
3960     // Zero initialize the array of raw pointers.
3961     memset(&radii, 0, sizeof(radii));
3962 #endif
3963 
3964     unsigned indexAfterSlash = 0;
3965     for (unsigned i = 0; i < num; ++i) {
3966         CSSParserValue* value = radiusArguments.at(i);
3967         if (value->unit == CSSParserValue::Operator) {
3968             if (value->iValue != '/')
3969                 return nullptr;
3970 
3971             if (!i || indexAfterSlash || i + 1 == num)
3972                 return nullptr;
3973 
3974             indexAfterSlash = i + 1;
3975             completeBorderRadii(radii[0]);
3976             continue;
3977         }
3978 
3979         if (i - indexAfterSlash >= 4)
3980             return nullptr;
3981 
3982         if (!validUnit(value, FLength | FPercent | FNonNeg))
3983             return nullptr;
3984 
3985         RefPtrWillBeRawPtr<CSSPrimitiveValue> radius = createPrimitiveNumericValue(value);
3986 
3987         if (!indexAfterSlash)
3988             radii[0][i] = radius;
3989         else
3990             radii[1][i - indexAfterSlash] = radius.release();
3991     }
3992 
3993     if (!indexAfterSlash) {
3994         completeBorderRadii(radii[0]);
3995         for (unsigned i = 0; i < 4; ++i)
3996             radii[1][i] = radii[0][i];
3997     } else {
3998         completeBorderRadii(radii[1]);
3999     }
4000     shape->setTopLeftRadius(createPrimitiveValuePair(radii[0][0].release(), radii[1][0].release()));
4001     shape->setTopRightRadius(createPrimitiveValuePair(radii[0][1].release(), radii[1][1].release()));
4002     shape->setBottomRightRadius(createPrimitiveValuePair(radii[0][2].release(), radii[1][2].release()));
4003     shape->setBottomLeftRadius(createPrimitiveValuePair(radii[0][3].release(), radii[1][3].release()));
4004 
4005     return shape;
4006 }
4007 
parseBasicShapeInset(CSSParserValueList * args)4008 PassRefPtrWillBeRawPtr<CSSBasicShape> CSSPropertyParser::parseBasicShapeInset(CSSParserValueList* args)
4009 {
4010     ASSERT(args);
4011 
4012     RefPtrWillBeRawPtr<CSSBasicShapeInset> shape = CSSBasicShapeInset::create();
4013 
4014     CSSParserValue* argument = args->current();
4015     WillBeHeapVector<RefPtrWillBeMember<CSSPrimitiveValue> > widthArguments;
4016     bool hasRoundedInset = false;
4017 
4018     while (argument) {
4019         if (argument->unit == CSSPrimitiveValue::CSS_IDENT && argument->id == CSSValueRound) {
4020             hasRoundedInset = true;
4021             break;
4022         }
4023 
4024         Units unitFlags = FLength | FPercent;
4025         if (!validUnit(argument, unitFlags) || widthArguments.size() > 4)
4026             return nullptr;
4027 
4028         widthArguments.append(createPrimitiveNumericValue(argument));
4029         argument = args->next();
4030     }
4031 
4032     switch (widthArguments.size()) {
4033     case 1: {
4034         shape->updateShapeSize1Value(widthArguments[0].get());
4035         break;
4036     }
4037     case 2: {
4038         shape->updateShapeSize2Values(widthArguments[0].get(), widthArguments[1].get());
4039         break;
4040         }
4041     case 3: {
4042         shape->updateShapeSize3Values(widthArguments[0].get(), widthArguments[1].get(), widthArguments[2].get());
4043         break;
4044     }
4045     case 4: {
4046         shape->updateShapeSize4Values(widthArguments[0].get(), widthArguments[1].get(), widthArguments[2].get(), widthArguments[3].get());
4047         break;
4048     }
4049     default:
4050         return nullptr;
4051     }
4052 
4053     if (hasRoundedInset)
4054         return parseInsetRoundedCorners(shape, args);
4055     return shape;
4056 }
4057 
isBaselinePositionKeyword(CSSValueID id)4058 static bool isBaselinePositionKeyword(CSSValueID id)
4059 {
4060     return id == CSSValueBaseline || id == CSSValueLastBaseline;
4061 }
4062 
isItemPositionKeyword(CSSValueID id)4063 static bool isItemPositionKeyword(CSSValueID id)
4064 {
4065     return id == CSSValueStart || id == CSSValueEnd || id == CSSValueCenter
4066         || id == CSSValueSelfStart || id == CSSValueSelfEnd || id == CSSValueFlexStart
4067         || id == CSSValueFlexEnd || id == CSSValueLeft || id == CSSValueRight;
4068 }
4069 
parseLegacyPosition(CSSPropertyID propId,bool important)4070 bool CSSPropertyParser::parseLegacyPosition(CSSPropertyID propId, bool important)
4071 {
4072     // [ legacy && [ left | right | center ]
4073 
4074     CSSParserValue* value = m_valueList->current();
4075     if (!value)
4076         return false;
4077 
4078     if (value->id == CSSValueLegacy) {
4079         value = m_valueList->next();
4080         if (!value)
4081             return false;
4082         if (value->id != CSSValueCenter && value->id != CSSValueLeft && value->id != CSSValueRight)
4083             return false;
4084     } else if (value->id == CSSValueCenter || value->id == CSSValueLeft || value->id == CSSValueRight) {
4085         if (!m_valueList->next() || m_valueList->current()->id != CSSValueLegacy)
4086             return false;
4087     } else {
4088         return false;
4089     }
4090 
4091     addProperty(propId, createPrimitiveValuePair(cssValuePool().createIdentifierValue(CSSValueLegacy), cssValuePool().createIdentifierValue(value->id)), important);
4092     return !m_valueList->next();
4093 }
4094 
parseItemPositionOverflowPosition(CSSPropertyID propId,bool important)4095 bool CSSPropertyParser::parseItemPositionOverflowPosition(CSSPropertyID propId, bool important)
4096 {
4097     // auto | stretch | <baseline-position> | [<item-position> && <overflow-position>? ]
4098     // <baseline-position> = baseline | last-baseline;
4099     // <item-position> = center | start | end | self-start | self-end | flex-start | flex-end | left | right;
4100     // <overflow-position> = true | safe
4101 
4102     CSSParserValue* value = m_valueList->current();
4103     if (!value)
4104         return false;
4105 
4106     if (value->id == CSSValueAuto || value->id == CSSValueStretch || isBaselinePositionKeyword(value->id)) {
4107         if (m_valueList->next())
4108             return false;
4109 
4110         addProperty(propId, cssValuePool().createIdentifierValue(value->id), important);
4111         return true;
4112     }
4113 
4114     RefPtrWillBeRawPtr<CSSPrimitiveValue> position = nullptr;
4115     RefPtrWillBeRawPtr<CSSPrimitiveValue> overflowAlignmentKeyword = nullptr;
4116     if (isItemPositionKeyword(value->id)) {
4117         position = cssValuePool().createIdentifierValue(value->id);
4118         value = m_valueList->next();
4119         if (value) {
4120             if (value->id == CSSValueTrue || value->id == CSSValueSafe)
4121                 overflowAlignmentKeyword = cssValuePool().createIdentifierValue(value->id);
4122             else
4123                 return false;
4124         }
4125     } else if (value->id == CSSValueTrue || value->id == CSSValueSafe) {
4126         overflowAlignmentKeyword = cssValuePool().createIdentifierValue(value->id);
4127         value = m_valueList->next();
4128         if (value && isItemPositionKeyword(value->id))
4129             position = cssValuePool().createIdentifierValue(value->id);
4130         else
4131             return false;
4132     } else {
4133         return false;
4134     }
4135 
4136     if (m_valueList->next())
4137         return false;
4138 
4139     ASSERT(position);
4140     if (overflowAlignmentKeyword)
4141         addProperty(propId, createPrimitiveValuePair(position, overflowAlignmentKeyword), important);
4142     else
4143         addProperty(propId, position.release(), important);
4144 
4145     return true;
4146 }
4147 
parseShapeRadius(CSSParserValue * value)4148 PassRefPtrWillBeRawPtr<CSSPrimitiveValue> CSSPropertyParser::parseShapeRadius(CSSParserValue* value)
4149 {
4150     if (value->id == CSSValueClosestSide || value->id == CSSValueFarthestSide)
4151         return cssValuePool().createIdentifierValue(value->id);
4152 
4153     if (!validUnit(value, FLength | FPercent | FNonNeg))
4154         return nullptr;
4155 
4156     return createPrimitiveNumericValue(value);
4157 }
4158 
parseBasicShapeCircle(CSSParserValueList * args)4159 PassRefPtrWillBeRawPtr<CSSBasicShape> CSSPropertyParser::parseBasicShapeCircle(CSSParserValueList* args)
4160 {
4161     ASSERT(args);
4162 
4163     // circle(radius)
4164     // circle(radius at <position>)
4165     // circle(at <position>)
4166     // where position defines centerX and centerY using a CSS <position> data type.
4167     RefPtrWillBeRawPtr<CSSBasicShapeCircle> shape = CSSBasicShapeCircle::create();
4168 
4169     for (CSSParserValue* argument = args->current(); argument; argument = args->next()) {
4170         // The call to parseFillPosition below should consume all of the
4171         // arguments except the first two. Thus, and index greater than one
4172         // indicates an invalid production.
4173         if (args->currentIndex() > 1)
4174             return nullptr;
4175 
4176         if (!args->currentIndex() && argument->id != CSSValueAt) {
4177             if (RefPtrWillBeRawPtr<CSSPrimitiveValue> radius = parseShapeRadius(argument)) {
4178                 shape->setRadius(radius);
4179                 continue;
4180             }
4181 
4182             return nullptr;
4183         }
4184 
4185         if (argument->id == CSSValueAt && args->next()) {
4186             RefPtrWillBeRawPtr<CSSValue> centerX = nullptr;
4187             RefPtrWillBeRawPtr<CSSValue> centerY = nullptr;
4188             parseFillPosition(args, centerX, centerY);
4189             if (centerX && centerY && !args->current()) {
4190                 ASSERT(centerX->isPrimitiveValue());
4191                 ASSERT(centerY->isPrimitiveValue());
4192                 shape->setCenterX(toCSSPrimitiveValue(centerX.get()));
4193                 shape->setCenterY(toCSSPrimitiveValue(centerY.get()));
4194             } else {
4195                 return nullptr;
4196             }
4197         } else {
4198             return nullptr;
4199         }
4200     }
4201 
4202     return shape;
4203 }
4204 
parseBasicShapeEllipse(CSSParserValueList * args)4205 PassRefPtrWillBeRawPtr<CSSBasicShape> CSSPropertyParser::parseBasicShapeEllipse(CSSParserValueList* args)
4206 {
4207     ASSERT(args);
4208 
4209     // ellipse(radiusX)
4210     // ellipse(radiusX at <position>)
4211     // ellipse(radiusX radiusY)
4212     // ellipse(radiusX radiusY at <position>)
4213     // ellipse(at <position>)
4214     // where position defines centerX and centerY using a CSS <position> data type.
4215     RefPtrWillBeRawPtr<CSSBasicShapeEllipse> shape = CSSBasicShapeEllipse::create();
4216 
4217     for (CSSParserValue* argument = args->current(); argument; argument = args->next()) {
4218         // The call to parseFillPosition below should consume all of the
4219         // arguments except the first three. Thus, an index greater than two
4220         // indicates an invalid production.
4221         if (args->currentIndex() > 2)
4222             return nullptr;
4223 
4224         if (args->currentIndex() < 2 && argument->id != CSSValueAt) {
4225             if (RefPtrWillBeRawPtr<CSSPrimitiveValue> radius = parseShapeRadius(argument)) {
4226                 if (!shape->radiusX())
4227                     shape->setRadiusX(radius);
4228                 else
4229                     shape->setRadiusY(radius);
4230                 continue;
4231             }
4232 
4233             return nullptr;
4234         }
4235 
4236         if (argument->id != CSSValueAt || !args->next()) // expecting ellipse(.. at <position>)
4237             return nullptr;
4238         RefPtrWillBeRawPtr<CSSValue> centerX = nullptr;
4239         RefPtrWillBeRawPtr<CSSValue> centerY = nullptr;
4240         parseFillPosition(args, centerX, centerY);
4241         if (!centerX || !centerY || args->current())
4242             return nullptr;
4243 
4244         ASSERT(centerX->isPrimitiveValue());
4245         ASSERT(centerY->isPrimitiveValue());
4246         shape->setCenterX(toCSSPrimitiveValue(centerX.get()));
4247         shape->setCenterY(toCSSPrimitiveValue(centerY.get()));
4248     }
4249 
4250     return shape;
4251 }
4252 
parseBasicShapePolygon(CSSParserValueList * args)4253 PassRefPtrWillBeRawPtr<CSSBasicShape> CSSPropertyParser::parseBasicShapePolygon(CSSParserValueList* args)
4254 {
4255     ASSERT(args);
4256 
4257     unsigned size = args->size();
4258     if (!size)
4259         return nullptr;
4260 
4261     RefPtrWillBeRawPtr<CSSBasicShapePolygon> shape = CSSBasicShapePolygon::create();
4262 
4263     CSSParserValue* argument = args->current();
4264     if (argument->id == CSSValueEvenodd || argument->id == CSSValueNonzero) {
4265         shape->setWindRule(argument->id == CSSValueEvenodd ? RULE_EVENODD : RULE_NONZERO);
4266         args->next();
4267 
4268         if (!consumeComma(args))
4269             return nullptr;
4270 
4271         size -= 2;
4272     }
4273 
4274     // <length> <length>, ... <length> <length> -> each pair has 3 elements except the last one
4275     if (!size || (size % 3) - 2)
4276         return nullptr;
4277 
4278     while (true) {
4279         CSSParserValue* argumentX = args->current();
4280         if (!argumentX || !validUnit(argumentX, FLength | FPercent))
4281             return nullptr;
4282         RefPtrWillBeRawPtr<CSSPrimitiveValue> xLength = createPrimitiveNumericValue(argumentX);
4283 
4284         CSSParserValue* argumentY = args->next();
4285         if (!argumentY || !validUnit(argumentY, FLength | FPercent))
4286             return nullptr;
4287         RefPtrWillBeRawPtr<CSSPrimitiveValue> yLength = createPrimitiveNumericValue(argumentY);
4288 
4289         shape->appendPoint(xLength.release(), yLength.release());
4290 
4291         if (!args->next())
4292             break;
4293         if (!consumeComma(args))
4294             return nullptr;
4295     }
4296 
4297     return shape;
4298 }
4299 
isBoxValue(CSSValueID valueId)4300 static bool isBoxValue(CSSValueID valueId)
4301 {
4302     switch (valueId) {
4303     case CSSValueContentBox:
4304     case CSSValuePaddingBox:
4305     case CSSValueBorderBox:
4306     case CSSValueMarginBox:
4307         return true;
4308     default:
4309         break;
4310     }
4311 
4312     return false;
4313 }
4314 
parseShapeProperty(CSSPropertyID propId)4315 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseShapeProperty(CSSPropertyID propId)
4316 {
4317     CSSParserValue* value = m_valueList->current();
4318     CSSValueID valueId = value->id;
4319 
4320     if (valueId == CSSValueNone) {
4321         RefPtrWillBeRawPtr<CSSPrimitiveValue> keywordValue = parseValidPrimitive(valueId, value);
4322         m_valueList->next();
4323         return keywordValue.release();
4324     }
4325 
4326     RefPtrWillBeRawPtr<CSSValue> imageValue = nullptr;
4327     if (valueId != CSSValueNone && parseFillImage(m_valueList, imageValue)) {
4328         m_valueList->next();
4329         return imageValue.release();
4330     }
4331 
4332     return parseBasicShapeAndOrBox();
4333 }
4334 
parseBasicShapeAndOrBox()4335 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseBasicShapeAndOrBox()
4336 {
4337     CSSParserValue* value = m_valueList->current();
4338 
4339     bool shapeFound = false;
4340     bool boxFound = false;
4341     CSSValueID valueId;
4342 
4343     RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
4344     for (unsigned i = 0; i < 2; ++i) {
4345         if (!value)
4346             break;
4347         valueId = value->id;
4348         if (value->unit == CSSParserValue::Function && !shapeFound) {
4349             // parseBasicShape already asks for the next value list item.
4350             RefPtrWillBeRawPtr<CSSPrimitiveValue> shapeValue = parseBasicShape();
4351             if (!shapeValue)
4352                 return nullptr;
4353             list->append(shapeValue.release());
4354             shapeFound = true;
4355         } else if (isBoxValue(valueId) && !boxFound) {
4356             list->append(parseValidPrimitive(valueId, value));
4357             boxFound = true;
4358             m_valueList->next();
4359         } else {
4360             return nullptr;
4361         }
4362 
4363         value = m_valueList->current();
4364     }
4365 
4366     if (m_valueList->current())
4367         return nullptr;
4368     return list.release();
4369 }
4370 
parseBasicShape()4371 PassRefPtrWillBeRawPtr<CSSPrimitiveValue> CSSPropertyParser::parseBasicShape()
4372 {
4373     CSSParserValue* value = m_valueList->current();
4374     ASSERT(value->unit == CSSParserValue::Function);
4375     CSSParserValueList* args = value->function->args.get();
4376 
4377     if (!args)
4378         return nullptr;
4379 
4380     RefPtrWillBeRawPtr<CSSBasicShape> shape = nullptr;
4381     if (equalIgnoringCase(value->function->name, "circle"))
4382         shape = parseBasicShapeCircle(args);
4383     else if (equalIgnoringCase(value->function->name, "ellipse"))
4384         shape = parseBasicShapeEllipse(args);
4385     else if (equalIgnoringCase(value->function->name, "polygon"))
4386         shape = parseBasicShapePolygon(args);
4387     else if (equalIgnoringCase(value->function->name, "inset"))
4388         shape = parseBasicShapeInset(args);
4389 
4390     if (!shape)
4391         return nullptr;
4392 
4393     m_valueList->next();
4394 
4395     return cssValuePool().createValue(shape.release());
4396 }
4397 
4398 // [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 'font-family'
parseFont(bool important)4399 bool CSSPropertyParser::parseFont(bool important)
4400 {
4401     // Let's check if there is an inherit or initial somewhere in the shorthand.
4402     for (unsigned i = 0; i < m_valueList->size(); ++i) {
4403         if (m_valueList->valueAt(i)->id == CSSValueInherit || m_valueList->valueAt(i)->id == CSSValueInitial)
4404             return false;
4405     }
4406 
4407     ShorthandScope scope(this, CSSPropertyFont);
4408     // Optional font-style, font-variant and font-weight.
4409     bool fontStyleParsed = false;
4410     bool fontVariantParsed = false;
4411     bool fontWeightParsed = false;
4412     bool fontStretchParsed = false;
4413     CSSParserValue* value = m_valueList->current();
4414     for (; value; value = m_valueList->next()) {
4415         if (!fontStyleParsed && isValidKeywordPropertyAndValue(CSSPropertyFontStyle, value->id, m_context)) {
4416             addProperty(CSSPropertyFontStyle, cssValuePool().createIdentifierValue(value->id), important);
4417             fontStyleParsed = true;
4418         } else if (!fontVariantParsed && (value->id == CSSValueNormal || value->id == CSSValueSmallCaps)) {
4419             // Font variant in the shorthand is particular, it only accepts normal or small-caps.
4420             addProperty(CSSPropertyFontVariant, cssValuePool().createIdentifierValue(value->id), important);
4421             fontVariantParsed = true;
4422         } else if (!fontWeightParsed && parseFontWeight(important)) {
4423             fontWeightParsed = true;
4424         } else if (!fontStretchParsed && isValidKeywordPropertyAndValue(CSSPropertyFontStretch, value->id, m_context)) {
4425             addProperty(CSSPropertyFontStretch, cssValuePool().createIdentifierValue(value->id), important);
4426             fontStretchParsed = true;
4427         } else {
4428             break;
4429         }
4430     }
4431 
4432     if (!value)
4433         return false;
4434 
4435     if (!fontStyleParsed)
4436         addProperty(CSSPropertyFontStyle, cssValuePool().createIdentifierValue(CSSValueNormal), important, true);
4437     if (!fontVariantParsed)
4438         addProperty(CSSPropertyFontVariant, cssValuePool().createIdentifierValue(CSSValueNormal), important, true);
4439     if (!fontWeightParsed)
4440         addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(CSSValueNormal), important, true);
4441     if (!fontStretchParsed)
4442         addProperty(CSSPropertyFontStretch, cssValuePool().createIdentifierValue(CSSValueNormal), important, true);
4443 
4444     // Now a font size _must_ come.
4445     // <absolute-size> | <relative-size> | <length> | <percentage> | inherit
4446     if (!parseFontSize(important))
4447         return false;
4448 
4449     value = m_valueList->current();
4450     if (!value)
4451         return false;
4452 
4453     if (isForwardSlashOperator(value)) {
4454         // The line-height property.
4455         value = m_valueList->next();
4456         if (!value)
4457             return false;
4458         if (!parseLineHeight(important))
4459             return false;
4460     } else
4461         addProperty(CSSPropertyLineHeight, cssValuePool().createIdentifierValue(CSSValueNormal), important, true);
4462 
4463     // Font family must come now.
4464     RefPtrWillBeRawPtr<CSSValue> parsedFamilyValue = parseFontFamily();
4465     if (!parsedFamilyValue)
4466         return false;
4467 
4468     addProperty(CSSPropertyFontFamily, parsedFamilyValue.release(), important);
4469 
4470     // FIXME: http://www.w3.org/TR/2011/WD-css3-fonts-20110324/#font-prop requires that
4471     // "font-stretch", "font-size-adjust", and "font-kerning" be reset to their initial values
4472     // but we don't seem to support them at the moment. They should also be added here once implemented.
4473     if (m_valueList->current())
4474         return false;
4475 
4476     return true;
4477 }
4478 
4479 class FontFamilyValueBuilder {
4480     DISALLOW_ALLOCATION();
4481 public:
FontFamilyValueBuilder(CSSValueList * list)4482     FontFamilyValueBuilder(CSSValueList* list)
4483         : m_list(list)
4484     {
4485     }
4486 
add(const CSSParserString & string)4487     void add(const CSSParserString& string)
4488     {
4489         if (!m_builder.isEmpty())
4490             m_builder.append(' ');
4491 
4492         if (string.is8Bit()) {
4493             m_builder.append(string.characters8(), string.length());
4494             return;
4495         }
4496 
4497         m_builder.append(string.characters16(), string.length());
4498     }
4499 
commit()4500     void commit()
4501     {
4502         if (m_builder.isEmpty())
4503             return;
4504         m_list->append(cssValuePool().createFontFamilyValue(m_builder.toString()));
4505         m_builder.clear();
4506     }
4507 
4508 private:
4509     StringBuilder m_builder;
4510     CSSValueList* m_list;
4511 };
4512 
parseFontFamily()4513 PassRefPtrWillBeRawPtr<CSSValueList> CSSPropertyParser::parseFontFamily()
4514 {
4515     RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
4516     CSSParserValue* value = m_valueList->current();
4517 
4518     FontFamilyValueBuilder familyBuilder(list.get());
4519     bool inFamily = false;
4520 
4521     while (value) {
4522         CSSParserValue* nextValue = m_valueList->next();
4523         bool nextValBreaksFont = !nextValue ||
4524                                  (nextValue->unit == CSSParserValue::Operator && nextValue->iValue == ',');
4525         bool nextValIsFontName = nextValue &&
4526             ((nextValue->id >= CSSValueSerif && nextValue->id <= CSSValueWebkitBody) ||
4527             (nextValue->unit == CSSPrimitiveValue::CSS_STRING || nextValue->unit == CSSPrimitiveValue::CSS_IDENT));
4528 
4529         if (isCSSWideKeyword(*value) && !inFamily) {
4530             if (nextValBreaksFont)
4531                 value = m_valueList->next();
4532             else if (nextValIsFontName)
4533                 value = nextValue;
4534             continue;
4535         }
4536 
4537         if (value->id >= CSSValueSerif && value->id <= CSSValueWebkitBody) {
4538             if (inFamily)
4539                 familyBuilder.add(value->string);
4540             else if (nextValBreaksFont || !nextValIsFontName)
4541                 list->append(cssValuePool().createIdentifierValue(value->id));
4542             else {
4543                 familyBuilder.commit();
4544                 familyBuilder.add(value->string);
4545                 inFamily = true;
4546             }
4547         } else if (value->unit == CSSPrimitiveValue::CSS_STRING) {
4548             // Strings never share in a family name.
4549             inFamily = false;
4550             familyBuilder.commit();
4551             list->append(cssValuePool().createFontFamilyValue(value->string));
4552         } else if (value->unit == CSSPrimitiveValue::CSS_IDENT) {
4553             if (inFamily)
4554                 familyBuilder.add(value->string);
4555             else if (nextValBreaksFont || !nextValIsFontName)
4556                 list->append(cssValuePool().createFontFamilyValue(value->string));
4557             else {
4558                 familyBuilder.commit();
4559                 familyBuilder.add(value->string);
4560                 inFamily = true;
4561             }
4562         } else {
4563             break;
4564         }
4565 
4566         if (!nextValue)
4567             break;
4568 
4569         if (nextValBreaksFont) {
4570             value = m_valueList->next();
4571             familyBuilder.commit();
4572             inFamily = false;
4573         }
4574         else if (nextValIsFontName)
4575             value = nextValue;
4576         else
4577             break;
4578     }
4579     familyBuilder.commit();
4580 
4581     if (!list->length())
4582         list = nullptr;
4583     return list.release();
4584 }
4585 
parseLineHeight(bool important)4586 bool CSSPropertyParser::parseLineHeight(bool important)
4587 {
4588     CSSParserValue* value = m_valueList->current();
4589     CSSValueID id = value->id;
4590     bool validPrimitive = false;
4591     // normal | <number> | <length> | <percentage> | inherit
4592     if (id == CSSValueNormal)
4593         validPrimitive = true;
4594     else
4595         validPrimitive = (!id && validUnit(value, FNumber | FLength | FPercent | FNonNeg));
4596     if (validPrimitive && (!m_valueList->next() || inShorthand()))
4597         addProperty(CSSPropertyLineHeight, parseValidPrimitive(id, value), important);
4598     return validPrimitive;
4599 }
4600 
parseFontSize(bool important)4601 bool CSSPropertyParser::parseFontSize(bool important)
4602 {
4603     CSSParserValue* value = m_valueList->current();
4604     CSSValueID id = value->id;
4605     bool validPrimitive = false;
4606     // <absolute-size> | <relative-size> | <length> | <percentage> | inherit
4607     if (id >= CSSValueXxSmall && id <= CSSValueLarger)
4608         validPrimitive = true;
4609     else
4610         validPrimitive = validUnit(value, FLength | FPercent | FNonNeg);
4611     if (validPrimitive && (!m_valueList->next() || inShorthand()))
4612         addProperty(CSSPropertyFontSize, parseValidPrimitive(id, value), important);
4613     return validPrimitive;
4614 }
4615 
parseFontVariant(bool important)4616 bool CSSPropertyParser::parseFontVariant(bool important)
4617 {
4618     RefPtrWillBeRawPtr<CSSValueList> values = nullptr;
4619     if (m_valueList->size() > 1)
4620         values = CSSValueList::createCommaSeparated();
4621     bool expectComma = false;
4622     for (CSSParserValue* val = m_valueList->current(); val; val = m_valueList->current()) {
4623         RefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue = nullptr;
4624         if (!expectComma) {
4625             expectComma = true;
4626             if (val->id == CSSValueNormal || val->id == CSSValueSmallCaps)
4627                 parsedValue = cssValuePool().createIdentifierValue(val->id);
4628             else if (val->id == CSSValueAll && !values) {
4629                 // FIXME: CSSPropertyParser::parseFontVariant() implements
4630                 // the old css3 draft:
4631                 // http://www.w3.org/TR/2002/WD-css3-webfonts-20020802/#font-variant
4632                 // 'all' is only allowed in @font-face and with no other values. Make a value list to
4633                 // indicate that we are in the @font-face case.
4634                 values = CSSValueList::createCommaSeparated();
4635                 parsedValue = cssValuePool().createIdentifierValue(val->id);
4636             }
4637         } else if (consumeComma(m_valueList)) {
4638             expectComma = false;
4639             continue;
4640         }
4641 
4642         if (!parsedValue)
4643             return false;
4644 
4645         m_valueList->next();
4646 
4647         if (values)
4648             values->append(parsedValue.release());
4649         else {
4650             addProperty(CSSPropertyFontVariant, parsedValue.release(), important);
4651             return true;
4652         }
4653     }
4654 
4655     if (values && values->length()) {
4656         if (m_ruleType != CSSRuleSourceData::FONT_FACE_RULE)
4657             return false;
4658         addProperty(CSSPropertyFontVariant, values.release(), important);
4659         return true;
4660     }
4661 
4662     return false;
4663 }
4664 
parseFontWeight(bool important)4665 bool CSSPropertyParser::parseFontWeight(bool important)
4666 {
4667     CSSParserValue* value = m_valueList->current();
4668     if (value->id >= CSSValueNormal && value->id <= CSSValueLighter) {
4669         addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(value->id), important);
4670         return true;
4671     }
4672     if (value->unit == CSSPrimitiveValue::CSS_NUMBER) {
4673         int weight = static_cast<int>(value->fValue);
4674         if (!(weight % 100) && weight >= 100 && weight <= 900) {
4675             addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(static_cast<CSSValueID>(CSSValue100 + weight / 100 - 1)), important);
4676             return true;
4677         }
4678     }
4679     return false;
4680 }
4681 
parseFontFaceSrcURI(CSSValueList * valueList)4682 bool CSSPropertyParser::parseFontFaceSrcURI(CSSValueList* valueList)
4683 {
4684     RefPtrWillBeRawPtr<CSSFontFaceSrcValue> uriValue(CSSFontFaceSrcValue::create(completeURL(m_valueList->current()->string)));
4685     uriValue->setReferrer(m_context.referrer());
4686 
4687     CSSParserValue* value = m_valueList->next();
4688     if (!value) {
4689         valueList->append(uriValue.release());
4690         return true;
4691     }
4692     if (value->unit == CSSParserValue::Operator && value->iValue == ',') {
4693         m_valueList->next();
4694         valueList->append(uriValue.release());
4695         return true;
4696     }
4697 
4698     if (value->unit != CSSParserValue::Function || !equalIgnoringCase(value->function->name, "format"))
4699         return false;
4700 
4701     // FIXME: http://www.w3.org/TR/2011/WD-css3-fonts-20111004/ says that format() contains a comma-separated list of strings,
4702     // but CSSFontFaceSrcValue stores only one format. Allowing one format for now.
4703     CSSParserValueList* args = value->function->args.get();
4704     if (!args || args->size() != 1 || (args->current()->unit != CSSPrimitiveValue::CSS_STRING && args->current()->unit != CSSPrimitiveValue::CSS_IDENT))
4705         return false;
4706     uriValue->setFormat(args->current()->string);
4707     valueList->append(uriValue.release());
4708     value = m_valueList->next();
4709     if (value && value->unit == CSSParserValue::Operator && value->iValue == ',')
4710         m_valueList->next();
4711     return true;
4712 }
4713 
parseFontFaceSrcLocal(CSSValueList * valueList)4714 bool CSSPropertyParser::parseFontFaceSrcLocal(CSSValueList* valueList)
4715 {
4716     CSSParserValueList* args = m_valueList->current()->function->args.get();
4717     if (!args || !args->size())
4718         return false;
4719 
4720     if (args->size() == 1 && args->current()->unit == CSSPrimitiveValue::CSS_STRING)
4721         valueList->append(CSSFontFaceSrcValue::createLocal(args->current()->string));
4722     else if (args->current()->unit == CSSPrimitiveValue::CSS_IDENT) {
4723         StringBuilder builder;
4724         for (CSSParserValue* localValue = args->current(); localValue; localValue = args->next()) {
4725             if (localValue->unit != CSSPrimitiveValue::CSS_IDENT)
4726                 return false;
4727             if (!builder.isEmpty())
4728                 builder.append(' ');
4729             builder.append(localValue->string);
4730         }
4731         valueList->append(CSSFontFaceSrcValue::createLocal(builder.toString()));
4732     } else
4733         return false;
4734 
4735     if (CSSParserValue* value = m_valueList->next()) {
4736         if (value->unit == CSSParserValue::Operator && value->iValue == ',')
4737             m_valueList->next();
4738     }
4739     return true;
4740 }
4741 
parseFontFaceSrc()4742 PassRefPtrWillBeRawPtr<CSSValueList> CSSPropertyParser::parseFontFaceSrc()
4743 {
4744     RefPtrWillBeRawPtr<CSSValueList> values(CSSValueList::createCommaSeparated());
4745 
4746     while (CSSParserValue* value = m_valueList->current()) {
4747         if (value->unit == CSSPrimitiveValue::CSS_URI) {
4748             if (!parseFontFaceSrcURI(values.get()))
4749                 return nullptr;
4750         } else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "local")) {
4751             if (!parseFontFaceSrcLocal(values.get()))
4752                 return nullptr;
4753         } else {
4754             return nullptr;
4755         }
4756     }
4757     if (!values->length())
4758         return nullptr;
4759 
4760     m_valueList->next();
4761     return values.release();
4762 }
4763 
parseFontFaceUnicodeRange()4764 PassRefPtrWillBeRawPtr<CSSValueList> CSSPropertyParser::parseFontFaceUnicodeRange()
4765 {
4766     RefPtrWillBeRawPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
4767 
4768     do {
4769         CSSParserValue* current = m_valueList->current();
4770         if (!current || current->unit != CSSPrimitiveValue::CSS_UNICODE_RANGE)
4771             return nullptr;
4772 
4773         String rangeString = current->string;
4774         UChar32 from = 0;
4775         UChar32 to = 0;
4776         unsigned length = rangeString.length();
4777 
4778         if (length < 3)
4779             return nullptr;
4780 
4781         unsigned i = 2;
4782         while (i < length) {
4783             UChar c = rangeString[i];
4784             if (c == '-' || c == '?')
4785                 break;
4786             from *= 16;
4787             if (c >= '0' && c <= '9')
4788                 from += c - '0';
4789             else if (c >= 'A' && c <= 'F')
4790                 from += 10 + c - 'A';
4791             else if (c >= 'a' && c <= 'f')
4792                 from += 10 + c - 'a';
4793             else
4794                 return nullptr;
4795             i++;
4796         }
4797 
4798         if (i == length)
4799             to = from;
4800         else if (rangeString[i] == '?') {
4801             unsigned span = 1;
4802             while (i < length && rangeString[i] == '?') {
4803                 span *= 16;
4804                 from *= 16;
4805                 i++;
4806             }
4807             if (i < length)
4808                 return nullptr;
4809             to = from + span - 1;
4810         } else {
4811             if (length < i + 2)
4812                 return nullptr;
4813             i++;
4814             while (i < length) {
4815                 UChar c = rangeString[i];
4816                 to *= 16;
4817                 if (c >= '0' && c <= '9')
4818                     to += c - '0';
4819                 else if (c >= 'A' && c <= 'F')
4820                     to += 10 + c - 'A';
4821                 else if (c >= 'a' && c <= 'f')
4822                     to += 10 + c - 'a';
4823                 else
4824                     return nullptr;
4825                 i++;
4826             }
4827         }
4828         if (from <= to)
4829             values->append(CSSUnicodeRangeValue::create(from, to));
4830         m_valueList->next();
4831     } while (consumeComma(m_valueList));
4832 
4833     return values.release();
4834 }
4835 
4836 // Returns the number of characters which form a valid double
4837 // and are terminated by the given terminator character
4838 template <typename CharacterType>
checkForValidDouble(const CharacterType * string,const CharacterType * end,const char terminator)4839 static int checkForValidDouble(const CharacterType* string, const CharacterType* end, const char terminator)
4840 {
4841     int length = end - string;
4842     if (length < 1)
4843         return 0;
4844 
4845     bool decimalMarkSeen = false;
4846     int processedLength = 0;
4847 
4848     for (int i = 0; i < length; ++i) {
4849         if (string[i] == terminator) {
4850             processedLength = i;
4851             break;
4852         }
4853         if (!isASCIIDigit(string[i])) {
4854             if (!decimalMarkSeen && string[i] == '.')
4855                 decimalMarkSeen = true;
4856             else
4857                 return 0;
4858         }
4859     }
4860 
4861     if (decimalMarkSeen && processedLength == 1)
4862         return 0;
4863 
4864     return processedLength;
4865 }
4866 
4867 // Returns the number of characters consumed for parsing a valid double
4868 // terminated by the given terminator character
4869 template <typename CharacterType>
parseDouble(const CharacterType * string,const CharacterType * end,const char terminator,double & value)4870 static int parseDouble(const CharacterType* string, const CharacterType* end, const char terminator, double& value)
4871 {
4872     int length = checkForValidDouble(string, end, terminator);
4873     if (!length)
4874         return 0;
4875 
4876     int position = 0;
4877     double localValue = 0;
4878 
4879     // The consumed characters here are guaranteed to be
4880     // ASCII digits with or without a decimal mark
4881     for (; position < length; ++position) {
4882         if (string[position] == '.')
4883             break;
4884         localValue = localValue * 10 + string[position] - '0';
4885     }
4886 
4887     if (++position == length) {
4888         value = localValue;
4889         return length;
4890     }
4891 
4892     double fraction = 0;
4893     double scale = 1;
4894 
4895     while (position < length && scale < MAX_SCALE) {
4896         fraction = fraction * 10 + string[position++] - '0';
4897         scale *= 10;
4898     }
4899 
4900     value = localValue + fraction / scale;
4901     return length;
4902 }
4903 
4904 template <typename CharacterType>
parseColorIntOrPercentage(const CharacterType * & string,const CharacterType * end,const char terminator,CSSPrimitiveValue::UnitType & expect,int & value)4905 static bool parseColorIntOrPercentage(const CharacterType*& string, const CharacterType* end, const char terminator, CSSPrimitiveValue::UnitType& expect, int& value)
4906 {
4907     const CharacterType* current = string;
4908     double localValue = 0;
4909     bool negative = false;
4910     while (current != end && isHTMLSpace<CharacterType>(*current))
4911         current++;
4912     if (current != end && *current == '-') {
4913         negative = true;
4914         current++;
4915     }
4916     if (current == end || !isASCIIDigit(*current))
4917         return false;
4918     while (current != end && isASCIIDigit(*current)) {
4919         double newValue = localValue * 10 + *current++ - '0';
4920         if (newValue >= 255) {
4921             // Clamp values at 255.
4922             localValue = 255;
4923             while (current != end && isASCIIDigit(*current))
4924                 ++current;
4925             break;
4926         }
4927         localValue = newValue;
4928     }
4929 
4930     if (current == end)
4931         return false;
4932 
4933     if (expect == CSSPrimitiveValue::CSS_NUMBER && (*current == '.' || *current == '%'))
4934         return false;
4935 
4936     if (*current == '.') {
4937         // We already parsed the integral part, try to parse
4938         // the fraction part of the percentage value.
4939         double percentage = 0;
4940         int numCharactersParsed = parseDouble(current, end, '%', percentage);
4941         if (!numCharactersParsed)
4942             return false;
4943         current += numCharactersParsed;
4944         if (*current != '%')
4945             return false;
4946         localValue += percentage;
4947     }
4948 
4949     if (expect == CSSPrimitiveValue::CSS_PERCENTAGE && *current != '%')
4950         return false;
4951 
4952     if (*current == '%') {
4953         expect = CSSPrimitiveValue::CSS_PERCENTAGE;
4954         localValue = localValue / 100.0 * 256.0;
4955         // Clamp values at 255 for percentages over 100%
4956         if (localValue > 255)
4957             localValue = 255;
4958         current++;
4959     } else
4960         expect = CSSPrimitiveValue::CSS_NUMBER;
4961 
4962     while (current != end && isHTMLSpace<CharacterType>(*current))
4963         current++;
4964     if (current == end || *current++ != terminator)
4965         return false;
4966     // Clamp negative values at zero.
4967     value = negative ? 0 : static_cast<int>(localValue);
4968     string = current;
4969     return true;
4970 }
4971 
4972 template <typename CharacterType>
isTenthAlpha(const CharacterType * string,const int length)4973 static inline bool isTenthAlpha(const CharacterType* string, const int length)
4974 {
4975     // "0.X"
4976     if (length == 3 && string[0] == '0' && string[1] == '.' && isASCIIDigit(string[2]))
4977         return true;
4978 
4979     // ".X"
4980     if (length == 2 && string[0] == '.' && isASCIIDigit(string[1]))
4981         return true;
4982 
4983     return false;
4984 }
4985 
4986 template <typename CharacterType>
parseAlphaValue(const CharacterType * & string,const CharacterType * end,const char terminator,int & value)4987 static inline bool parseAlphaValue(const CharacterType*& string, const CharacterType* end, const char terminator, int& value)
4988 {
4989     while (string != end && isHTMLSpace<CharacterType>(*string))
4990         string++;
4991 
4992     bool negative = false;
4993 
4994     if (string != end && *string == '-') {
4995         negative = true;
4996         string++;
4997     }
4998 
4999     value = 0;
5000 
5001     int length = end - string;
5002     if (length < 2)
5003         return false;
5004 
5005     if (string[length - 1] != terminator || !isASCIIDigit(string[length - 2]))
5006         return false;
5007 
5008     if (string[0] != '0' && string[0] != '1' && string[0] != '.') {
5009         if (checkForValidDouble(string, end, terminator)) {
5010             value = negative ? 0 : 255;
5011             string = end;
5012             return true;
5013         }
5014         return false;
5015     }
5016 
5017     if (length == 2 && string[0] != '.') {
5018         value = !negative && string[0] == '1' ? 255 : 0;
5019         string = end;
5020         return true;
5021     }
5022 
5023     if (isTenthAlpha(string, length - 1)) {
5024         static const int tenthAlphaValues[] = { 0, 25, 51, 76, 102, 127, 153, 179, 204, 230 };
5025         value = negative ? 0 : tenthAlphaValues[string[length - 2] - '0'];
5026         string = end;
5027         return true;
5028     }
5029 
5030     double alpha = 0;
5031     if (!parseDouble(string, end, terminator, alpha))
5032         return false;
5033     value = negative ? 0 : static_cast<int>(alpha * nextafter(256.0, 0.0));
5034     string = end;
5035     return true;
5036 }
5037 
5038 template <typename CharacterType>
mightBeRGBA(const CharacterType * characters,unsigned length)5039 static inline bool mightBeRGBA(const CharacterType* characters, unsigned length)
5040 {
5041     if (length < 5)
5042         return false;
5043     return characters[4] == '('
5044         && isASCIIAlphaCaselessEqual(characters[0], 'r')
5045         && isASCIIAlphaCaselessEqual(characters[1], 'g')
5046         && isASCIIAlphaCaselessEqual(characters[2], 'b')
5047         && isASCIIAlphaCaselessEqual(characters[3], 'a');
5048 }
5049 
5050 template <typename CharacterType>
mightBeRGB(const CharacterType * characters,unsigned length)5051 static inline bool mightBeRGB(const CharacterType* characters, unsigned length)
5052 {
5053     if (length < 4)
5054         return false;
5055     return characters[3] == '('
5056         && isASCIIAlphaCaselessEqual(characters[0], 'r')
5057         && isASCIIAlphaCaselessEqual(characters[1], 'g')
5058         && isASCIIAlphaCaselessEqual(characters[2], 'b');
5059 }
5060 
5061 template <typename CharacterType>
fastParseColorInternal(RGBA32 & rgb,const CharacterType * characters,unsigned length,bool strict)5062 static inline bool fastParseColorInternal(RGBA32& rgb, const CharacterType* characters, unsigned length , bool strict)
5063 {
5064     CSSPrimitiveValue::UnitType expect = CSSPrimitiveValue::CSS_UNKNOWN;
5065 
5066     if (length >= 4 && characters[0] == '#')
5067         return Color::parseHexColor(characters + 1, length - 1, rgb);
5068 
5069     if (!strict && length >= 3) {
5070         if (Color::parseHexColor(characters, length, rgb))
5071             return true;
5072     }
5073 
5074     // Try rgba() syntax.
5075     if (mightBeRGBA(characters, length)) {
5076         const CharacterType* current = characters + 5;
5077         const CharacterType* end = characters + length;
5078         int red;
5079         int green;
5080         int blue;
5081         int alpha;
5082 
5083         if (!parseColorIntOrPercentage(current, end, ',', expect, red))
5084             return false;
5085         if (!parseColorIntOrPercentage(current, end, ',', expect, green))
5086             return false;
5087         if (!parseColorIntOrPercentage(current, end, ',', expect, blue))
5088             return false;
5089         if (!parseAlphaValue(current, end, ')', alpha))
5090             return false;
5091         if (current != end)
5092             return false;
5093         rgb = makeRGBA(red, green, blue, alpha);
5094         return true;
5095     }
5096 
5097     // Try rgb() syntax.
5098     if (mightBeRGB(characters, length)) {
5099         const CharacterType* current = characters + 4;
5100         const CharacterType* end = characters + length;
5101         int red;
5102         int green;
5103         int blue;
5104         if (!parseColorIntOrPercentage(current, end, ',', expect, red))
5105             return false;
5106         if (!parseColorIntOrPercentage(current, end, ',', expect, green))
5107             return false;
5108         if (!parseColorIntOrPercentage(current, end, ')', expect, blue))
5109             return false;
5110         if (current != end)
5111             return false;
5112         rgb = makeRGB(red, green, blue);
5113         return true;
5114     }
5115 
5116     return false;
5117 }
5118 
5119 template<typename StringType>
fastParseColor(RGBA32 & rgb,const StringType & name,bool strict)5120 bool CSSPropertyParser::fastParseColor(RGBA32& rgb, const StringType& name, bool strict)
5121 {
5122     unsigned length = name.length();
5123     bool parseResult;
5124 
5125     if (!length)
5126         return false;
5127 
5128     if (name.is8Bit())
5129         parseResult = fastParseColorInternal(rgb, name.characters8(), length, strict);
5130     else
5131         parseResult = fastParseColorInternal(rgb, name.characters16(), length, strict);
5132 
5133     if (parseResult)
5134         return true;
5135 
5136     // Try named colors.
5137     Color tc;
5138     if (!tc.setNamedColor(name))
5139         return false;
5140     rgb = tc.rgb();
5141     return true;
5142 }
5143 
5144 template bool CSSPropertyParser::fastParseColor(RGBA32&, const String&, bool strict);
5145 
isCalculation(CSSParserValue * value)5146 bool CSSPropertyParser::isCalculation(CSSParserValue* value)
5147 {
5148     return (value->unit == CSSParserValue::Function)
5149         && (equalIgnoringCase(value->function->name, "calc")
5150             || equalIgnoringCase(value->function->name, "-webkit-calc"));
5151 }
5152 
colorIntFromValue(CSSParserValue * v)5153 inline int CSSPropertyParser::colorIntFromValue(CSSParserValue* v)
5154 {
5155     bool isPercent;
5156     double value;
5157 
5158     if (m_parsedCalculation) {
5159         isPercent = m_parsedCalculation->category() == CalcPercent;
5160         value = m_parsedCalculation->doubleValue();
5161         m_parsedCalculation.release();
5162     } else {
5163         isPercent = v->unit == CSSPrimitiveValue::CSS_PERCENTAGE;
5164         value = v->fValue;
5165     }
5166 
5167     if (value <= 0.0)
5168         return 0;
5169 
5170     if (isPercent) {
5171         if (value >= 100.0)
5172             return 255;
5173         return static_cast<int>(value * 256.0 / 100.0);
5174     }
5175 
5176     if (value >= 255.0)
5177         return 255;
5178 
5179     return static_cast<int>(value);
5180 }
5181 
parseColorParameters(CSSParserValue * value,int * colorArray,bool parseAlpha)5182 bool CSSPropertyParser::parseColorParameters(CSSParserValue* value, int* colorArray, bool parseAlpha)
5183 {
5184     CSSParserValueList* args = value->function->args.get();
5185     CSSParserValue* v = args->current();
5186     Units unitType = FUnknown;
5187     // Get the first value and its type
5188     if (validUnit(v, FInteger))
5189         unitType = FInteger;
5190     else if (validUnit(v, FPercent))
5191         unitType = FPercent;
5192     else
5193         return false;
5194 
5195     colorArray[0] = colorIntFromValue(v);
5196     for (int i = 1; i < 3; i++) {
5197         args->next();
5198         if (!consumeComma(args))
5199             return false;
5200         v = args->current();
5201         if (!validUnit(v, unitType))
5202             return false;
5203         colorArray[i] = colorIntFromValue(v);
5204     }
5205     if (parseAlpha) {
5206         args->next();
5207         if (!consumeComma(args))
5208             return false;
5209         v = args->current();
5210         if (!validUnit(v, FNumber))
5211             return false;
5212         // Convert the floating pointer number of alpha to an integer in the range [0, 256),
5213         // with an equal distribution across all 256 values.
5214         colorArray[3] = static_cast<int>(std::max(0.0, std::min(1.0, v->fValue)) * nextafter(256.0, 0.0));
5215     }
5216     return true;
5217 }
5218 
5219 // The CSS3 specification defines the format of a HSL color as
5220 // hsl(<number>, <percent>, <percent>)
5221 // and with alpha, the format is
5222 // hsla(<number>, <percent>, <percent>, <number>)
5223 // The first value, HUE, is in an angle with a value between 0 and 360
parseHSLParameters(CSSParserValue * value,double * colorArray,bool parseAlpha)5224 bool CSSPropertyParser::parseHSLParameters(CSSParserValue* value, double* colorArray, bool parseAlpha)
5225 {
5226     CSSParserValueList* args = value->function->args.get();
5227     CSSParserValue* v = args->current();
5228     // Get the first value
5229     if (!validUnit(v, FNumber))
5230         return false;
5231     // normalize the Hue value and change it to be between 0 and 1.0
5232     colorArray[0] = (((static_cast<int>(v->fValue) % 360) + 360) % 360) / 360.0;
5233     for (int i = 1; i < 3; i++) {
5234         args->next();
5235         if (!consumeComma(args))
5236             return false;
5237         v = args->current();
5238         if (!validUnit(v, FPercent))
5239             return false;
5240         double percentValue = m_parsedCalculation ? m_parsedCalculation.release()->doubleValue() : v->fValue;
5241         colorArray[i] = std::max(0.0, std::min(100.0, percentValue)) / 100.0; // needs to be value between 0 and 1.0
5242     }
5243     if (parseAlpha) {
5244         args->next();
5245         if (!consumeComma(args))
5246             return false;
5247         v = args->current();
5248         if (!validUnit(v, FNumber))
5249             return false;
5250         colorArray[3] = std::max(0.0, std::min(1.0, v->fValue));
5251     }
5252     return true;
5253 }
5254 
parseColor(CSSParserValue * value,bool acceptQuirkyColors)5255 PassRefPtrWillBeRawPtr<CSSPrimitiveValue> CSSPropertyParser::parseColor(CSSParserValue* value, bool acceptQuirkyColors)
5256 {
5257     RGBA32 c = Color::transparent;
5258     if (!parseColorFromValue(value ? value : m_valueList->current(), c, acceptQuirkyColors))
5259         return nullptr;
5260     return cssValuePool().createColorValue(c);
5261 }
5262 
parseColorFromValue(CSSParserValue * value,RGBA32 & c,bool acceptQuirkyColors)5263 bool CSSPropertyParser::parseColorFromValue(CSSParserValue* value, RGBA32& c, bool acceptQuirkyColors)
5264 {
5265     if (acceptQuirkyColors && value->unit == CSSPrimitiveValue::CSS_NUMBER
5266         && value->fValue >= 0. && value->fValue < 1000000.) {
5267         String str = String::format("%06d", static_cast<int>((value->fValue+.5)));
5268         // FIXME: This should be strict parsing for SVG as well.
5269         if (!fastParseColor(c, str, !acceptQuirkyColors))
5270             return false;
5271     } else if (value->unit == CSSPrimitiveValue::CSS_PARSER_HEXCOLOR
5272         || value->unit == CSSPrimitiveValue::CSS_IDENT
5273         || (acceptQuirkyColors && value->unit == CSSPrimitiveValue::CSS_DIMENSION)) {
5274         if (!fastParseColor(c, value->string, !acceptQuirkyColors && value->unit == CSSPrimitiveValue::CSS_IDENT))
5275             return false;
5276     } else if (value->unit == CSSParserValue::Function &&
5277                 value->function->args != 0 &&
5278                 value->function->args->size() == 5 /* rgb + two commas */ &&
5279                 equalIgnoringCase(value->function->name, "rgb")) {
5280         int colorValues[3];
5281         if (!parseColorParameters(value, colorValues, false))
5282             return false;
5283         c = makeRGB(colorValues[0], colorValues[1], colorValues[2]);
5284     } else {
5285         if (value->unit == CSSParserValue::Function &&
5286                 value->function->args != 0 &&
5287                 value->function->args->size() == 7 /* rgba + three commas */ &&
5288                 equalIgnoringCase(value->function->name, "rgba")) {
5289             int colorValues[4];
5290             if (!parseColorParameters(value, colorValues, true))
5291                 return false;
5292             c = makeRGBA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]);
5293         } else if (value->unit == CSSParserValue::Function &&
5294                     value->function->args != 0 &&
5295                     value->function->args->size() == 5 /* hsl + two commas */ &&
5296                     equalIgnoringCase(value->function->name, "hsl")) {
5297             double colorValues[3];
5298             if (!parseHSLParameters(value, colorValues, false))
5299                 return false;
5300             c = makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], 1.0);
5301         } else if (value->unit == CSSParserValue::Function &&
5302                     value->function->args != 0 &&
5303                     value->function->args->size() == 7 /* hsla + three commas */ &&
5304                     equalIgnoringCase(value->function->name, "hsla")) {
5305             double colorValues[4];
5306             if (!parseHSLParameters(value, colorValues, true))
5307                 return false;
5308             c = makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]);
5309         } else
5310             return false;
5311     }
5312 
5313     return true;
5314 }
5315 
5316 // This class tracks parsing state for shadow values.  If it goes out of scope (e.g., due to an early return)
5317 // without the allowBreak bit being set, then it will clean up all of the objects and destroy them.
5318 class ShadowParseContext {
5319     STACK_ALLOCATED();
5320 public:
ShadowParseContext(CSSPropertyID prop,CSSPropertyParser * parser)5321     ShadowParseContext(CSSPropertyID prop, CSSPropertyParser* parser)
5322         : property(prop)
5323         , m_parser(parser)
5324         , allowX(true)
5325         , allowY(false)
5326         , allowBlur(false)
5327         , allowSpread(false)
5328         , allowColor(true)
5329         , allowStyle(prop == CSSPropertyWebkitBoxShadow || prop == CSSPropertyBoxShadow)
5330         , allowBreak(true)
5331     {
5332     }
5333 
allowLength()5334     bool allowLength() { return allowX || allowY || allowBlur || allowSpread; }
5335 
commitValue()5336     void commitValue()
5337     {
5338         // Handle the ,, case gracefully by doing nothing.
5339         if (x || y || blur || spread || color || style) {
5340             if (!values)
5341                 values = CSSValueList::createCommaSeparated();
5342 
5343             // Construct the current shadow value and add it to the list.
5344             values->append(CSSShadowValue::create(x.release(), y.release(), blur.release(), spread.release(), style.release(), color.release()));
5345         }
5346 
5347         // Now reset for the next shadow value.
5348         x = nullptr;
5349         y = nullptr;
5350         blur = nullptr;
5351         spread = nullptr;
5352         style = nullptr;
5353         color = nullptr;
5354 
5355         allowX = true;
5356         allowColor = true;
5357         allowBreak = true;
5358         allowY = false;
5359         allowBlur = false;
5360         allowSpread = false;
5361         allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow;
5362     }
5363 
commitLength(CSSParserValue * v)5364     void commitLength(CSSParserValue* v)
5365     {
5366         RefPtrWillBeRawPtr<CSSPrimitiveValue> val = m_parser->createPrimitiveNumericValue(v);
5367 
5368         if (allowX) {
5369             x = val.release();
5370             allowX = false;
5371             allowY = true;
5372             allowColor = false;
5373             allowStyle = false;
5374             allowBreak = false;
5375         } else if (allowY) {
5376             y = val.release();
5377             allowY = false;
5378             allowBlur = true;
5379             allowColor = true;
5380             allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow;
5381             allowBreak = true;
5382         } else if (allowBlur) {
5383             blur = val.release();
5384             allowBlur = false;
5385             allowSpread = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow;
5386         } else if (allowSpread) {
5387             spread = val.release();
5388             allowSpread = false;
5389         }
5390     }
5391 
commitColor(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> val)5392     void commitColor(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> val)
5393     {
5394         color = val;
5395         allowColor = false;
5396         if (allowX) {
5397             allowStyle = false;
5398             allowBreak = false;
5399         } else {
5400             allowBlur = false;
5401             allowSpread = false;
5402             allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow;
5403         }
5404     }
5405 
commitStyle(CSSParserValue * v)5406     void commitStyle(CSSParserValue* v)
5407     {
5408         style = cssValuePool().createIdentifierValue(v->id);
5409         allowStyle = false;
5410         if (allowX)
5411             allowBreak = false;
5412         else {
5413             allowBlur = false;
5414             allowSpread = false;
5415             allowColor = false;
5416         }
5417     }
5418 
5419     CSSPropertyID property;
5420     CSSPropertyParser* m_parser;
5421 
5422     RefPtrWillBeMember<CSSValueList> values;
5423     RefPtrWillBeMember<CSSPrimitiveValue> x;
5424     RefPtrWillBeMember<CSSPrimitiveValue> y;
5425     RefPtrWillBeMember<CSSPrimitiveValue> blur;
5426     RefPtrWillBeMember<CSSPrimitiveValue> spread;
5427     RefPtrWillBeMember<CSSPrimitiveValue> style;
5428     RefPtrWillBeMember<CSSPrimitiveValue> color;
5429 
5430     bool allowX;
5431     bool allowY;
5432     bool allowBlur;
5433     bool allowSpread;
5434     bool allowColor;
5435     bool allowStyle; // inset or not.
5436     bool allowBreak;
5437 };
5438 
parseShadow(CSSParserValueList * valueList,CSSPropertyID propId)5439 PassRefPtrWillBeRawPtr<CSSValueList> CSSPropertyParser::parseShadow(CSSParserValueList* valueList, CSSPropertyID propId)
5440 {
5441     ShadowParseContext context(propId, this);
5442     for (CSSParserValue* val = valueList->current(); val; val = valueList->next()) {
5443         // Check for a comma break first.
5444         if (val->unit == CSSParserValue::Operator) {
5445             if (val->iValue != ',' || !context.allowBreak) {
5446                 // Other operators aren't legal or we aren't done with the current shadow
5447                 // value.  Treat as invalid.
5448                 return nullptr;
5449             }
5450             // The value is good.  Commit it.
5451             context.commitValue();
5452         } else if (validUnit(val, FLength, HTMLStandardMode)) {
5453             // We required a length and didn't get one. Invalid.
5454             if (!context.allowLength())
5455                 return nullptr;
5456 
5457             // Blur radius must be non-negative.
5458             if (context.allowBlur && !validUnit(val, FLength | FNonNeg, HTMLStandardMode))
5459                 return nullptr;
5460 
5461             // A length is allowed here.  Construct the value and add it.
5462             context.commitLength(val);
5463         } else if (val->id == CSSValueInset) {
5464             if (!context.allowStyle)
5465                 return nullptr;
5466 
5467             context.commitStyle(val);
5468         } else {
5469             // The only other type of value that's ok is a color value.
5470             RefPtrWillBeRawPtr<CSSPrimitiveValue> parsedColor = nullptr;
5471             bool isColor = ((val->id >= CSSValueAqua && val->id <= CSSValueWindowtext) || val->id == CSSValueMenu
5472                             || (val->id >= CSSValueWebkitFocusRingColor && val->id <= CSSValueWebkitText && inQuirksMode())
5473                             || val->id == CSSValueCurrentcolor);
5474             if (isColor) {
5475                 if (!context.allowColor)
5476                     return nullptr;
5477                 parsedColor = cssValuePool().createIdentifierValue(val->id);
5478             }
5479 
5480             if (!parsedColor)
5481                 // It's not built-in. Try to parse it as a color.
5482                 parsedColor = parseColor(val);
5483 
5484             if (!parsedColor || !context.allowColor)
5485                 return nullptr; // This value is not a color or length and is invalid or
5486                           // it is a color, but a color isn't allowed at this point.
5487 
5488             context.commitColor(parsedColor.release());
5489         }
5490     }
5491 
5492     if (context.allowBreak) {
5493         context.commitValue();
5494         if (context.values && context.values->length())
5495             return context.values.release();
5496     }
5497 
5498     return nullptr;
5499 }
5500 
parseReflect()5501 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseReflect()
5502 {
5503     // box-reflect: <direction> <offset> <mask>
5504 
5505     // Direction comes first.
5506     CSSParserValue* val = m_valueList->current();
5507     RefPtrWillBeRawPtr<CSSPrimitiveValue> direction = nullptr;
5508     switch (val->id) {
5509     case CSSValueAbove:
5510     case CSSValueBelow:
5511     case CSSValueLeft:
5512     case CSSValueRight:
5513         direction = cssValuePool().createIdentifierValue(val->id);
5514         break;
5515     default:
5516         return nullptr;
5517     }
5518 
5519     // The offset comes next.
5520     val = m_valueList->next();
5521     RefPtrWillBeRawPtr<CSSPrimitiveValue> offset = nullptr;
5522     if (!val)
5523         offset = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX);
5524     else {
5525         if (!validUnit(val, FLength | FPercent))
5526             return nullptr;
5527         offset = createPrimitiveNumericValue(val);
5528     }
5529 
5530     // Now for the mask.
5531     RefPtrWillBeRawPtr<CSSValue> mask = nullptr;
5532     val = m_valueList->next();
5533     if (val) {
5534         mask = parseBorderImage(CSSPropertyWebkitBoxReflect);
5535         if (!mask)
5536             return nullptr;
5537     }
5538 
5539     return CSSReflectValue::create(direction.release(), offset.release(), mask.release());
5540 }
5541 
isFlexBasisMiddleArg(double flexGrow,double flexShrink,double unsetValue,int argSize)5542 static bool isFlexBasisMiddleArg(double flexGrow, double flexShrink, double unsetValue, int argSize)
5543 {
5544     return flexGrow != unsetValue && flexShrink == unsetValue &&  argSize == 3;
5545 }
5546 
parseFlex(CSSParserValueList * args,bool important)5547 bool CSSPropertyParser::parseFlex(CSSParserValueList* args, bool important)
5548 {
5549     if (!args || !args->size() || args->size() > 3)
5550         return false;
5551     static const double unsetValue = -1;
5552     double flexGrow = unsetValue;
5553     double flexShrink = unsetValue;
5554     RefPtrWillBeRawPtr<CSSPrimitiveValue> flexBasis = nullptr;
5555 
5556     while (CSSParserValue* arg = args->current()) {
5557         if (validUnit(arg, FNumber | FNonNeg)) {
5558             if (flexGrow == unsetValue)
5559                 flexGrow = arg->fValue;
5560             else if (flexShrink == unsetValue)
5561                 flexShrink = arg->fValue;
5562             else if (!arg->fValue) {
5563                 // flex only allows a basis of 0 (sans units) if flex-grow and flex-shrink values have already been set.
5564                 flexBasis = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX);
5565             } else {
5566                 // We only allow 3 numbers without units if the last value is 0. E.g., flex:1 1 1 is invalid.
5567                 return false;
5568             }
5569         } else if (!flexBasis && (arg->id == CSSValueAuto || validUnit(arg, FLength | FPercent | FNonNeg)) && !isFlexBasisMiddleArg(flexGrow, flexShrink, unsetValue, args->size()))
5570             flexBasis = parseValidPrimitive(arg->id, arg);
5571         else {
5572             // Not a valid arg for flex.
5573             return false;
5574         }
5575         args->next();
5576     }
5577 
5578     if (flexGrow == unsetValue)
5579         flexGrow = 1;
5580     if (flexShrink == unsetValue)
5581         flexShrink = 1;
5582     if (!flexBasis)
5583         flexBasis = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE);
5584 
5585     addProperty(CSSPropertyFlexGrow, cssValuePool().createValue(clampToFloat(flexGrow), CSSPrimitiveValue::CSS_NUMBER), important);
5586     addProperty(CSSPropertyFlexShrink, cssValuePool().createValue(clampToFloat(flexShrink), CSSPrimitiveValue::CSS_NUMBER), important);
5587     addProperty(CSSPropertyFlexBasis, flexBasis, important);
5588     return true;
5589 }
5590 
parseObjectPosition()5591 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseObjectPosition()
5592 {
5593     RefPtrWillBeRawPtr<CSSValue> xValue = nullptr;
5594     RefPtrWillBeRawPtr<CSSValue> yValue = nullptr;
5595     parseFillPosition(m_valueList, xValue, yValue);
5596     if (!xValue || !yValue)
5597         return nullptr;
5598     return createPrimitiveValuePair(toCSSPrimitiveValue(xValue.get()), toCSSPrimitiveValue(yValue.get()), Pair::KeepIdenticalValues);
5599 }
5600 
5601 class BorderImageParseContext {
5602     STACK_ALLOCATED();
5603 public:
BorderImageParseContext()5604     BorderImageParseContext()
5605     : m_canAdvance(false)
5606     , m_allowCommit(true)
5607     , m_allowImage(true)
5608     , m_allowImageSlice(true)
5609     , m_allowRepeat(true)
5610     , m_allowForwardSlashOperator(false)
5611     , m_requireWidth(false)
5612     , m_requireOutset(false)
5613     {}
5614 
canAdvance() const5615     bool canAdvance() const { return m_canAdvance; }
setCanAdvance(bool canAdvance)5616     void setCanAdvance(bool canAdvance) { m_canAdvance = canAdvance; }
5617 
allowCommit() const5618     bool allowCommit() const { return m_allowCommit; }
allowImage() const5619     bool allowImage() const { return m_allowImage; }
allowImageSlice() const5620     bool allowImageSlice() const { return m_allowImageSlice; }
allowRepeat() const5621     bool allowRepeat() const { return m_allowRepeat; }
allowForwardSlashOperator() const5622     bool allowForwardSlashOperator() const { return m_allowForwardSlashOperator; }
5623 
requireWidth() const5624     bool requireWidth() const { return m_requireWidth; }
requireOutset() const5625     bool requireOutset() const { return m_requireOutset; }
5626 
commitImage(PassRefPtrWillBeRawPtr<CSSValue> image)5627     void commitImage(PassRefPtrWillBeRawPtr<CSSValue> image)
5628     {
5629         m_image = image;
5630         m_canAdvance = true;
5631         m_allowCommit = true;
5632         m_allowImage = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false;
5633         m_allowImageSlice = !m_imageSlice;
5634         m_allowRepeat = !m_repeat;
5635     }
commitImageSlice(PassRefPtrWillBeRawPtr<CSSBorderImageSliceValue> slice)5636     void commitImageSlice(PassRefPtrWillBeRawPtr<CSSBorderImageSliceValue> slice)
5637     {
5638         m_imageSlice = slice;
5639         m_canAdvance = true;
5640         m_allowCommit = m_allowForwardSlashOperator = true;
5641         m_allowImageSlice = m_requireWidth = m_requireOutset = false;
5642         m_allowImage = !m_image;
5643         m_allowRepeat = !m_repeat;
5644     }
commitForwardSlashOperator()5645     void commitForwardSlashOperator()
5646     {
5647         m_canAdvance = true;
5648         m_allowCommit = m_allowImage = m_allowImageSlice = m_allowRepeat = m_allowForwardSlashOperator = false;
5649         if (!m_borderWidth) {
5650             m_requireWidth = true;
5651             m_requireOutset = false;
5652         } else {
5653             m_requireOutset = true;
5654             m_requireWidth = false;
5655         }
5656     }
commitBorderWidth(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> width)5657     void commitBorderWidth(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> width)
5658     {
5659         m_borderWidth = width;
5660         m_canAdvance = true;
5661         m_allowCommit = m_allowForwardSlashOperator = true;
5662         m_allowImageSlice = m_requireWidth = m_requireOutset = false;
5663         m_allowImage = !m_image;
5664         m_allowRepeat = !m_repeat;
5665     }
commitBorderOutset(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> outset)5666     void commitBorderOutset(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> outset)
5667     {
5668         m_outset = outset;
5669         m_canAdvance = true;
5670         m_allowCommit = true;
5671         m_allowImageSlice = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false;
5672         m_allowImage = !m_image;
5673         m_allowRepeat = !m_repeat;
5674     }
commitRepeat(PassRefPtrWillBeRawPtr<CSSValue> repeat)5675     void commitRepeat(PassRefPtrWillBeRawPtr<CSSValue> repeat)
5676     {
5677         m_repeat = repeat;
5678         m_canAdvance = true;
5679         m_allowCommit = true;
5680         m_allowRepeat = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false;
5681         m_allowImageSlice = !m_imageSlice;
5682         m_allowImage = !m_image;
5683     }
5684 
commitCSSValue()5685     PassRefPtrWillBeRawPtr<CSSValue> commitCSSValue()
5686     {
5687         return createBorderImageValue(m_image, m_imageSlice.get(), m_borderWidth.get(), m_outset.get(), m_repeat.get());
5688     }
5689 
commitMaskBoxImage(CSSPropertyParser * parser,bool important)5690     void commitMaskBoxImage(CSSPropertyParser* parser, bool important)
5691     {
5692         commitBorderImageProperty(CSSPropertyWebkitMaskBoxImageSource, parser, m_image, important);
5693         commitBorderImageProperty(CSSPropertyWebkitMaskBoxImageSlice, parser, m_imageSlice.get(), important);
5694         commitBorderImageProperty(CSSPropertyWebkitMaskBoxImageWidth, parser, m_borderWidth.get(), important);
5695         commitBorderImageProperty(CSSPropertyWebkitMaskBoxImageOutset, parser, m_outset.get(), important);
5696         commitBorderImageProperty(CSSPropertyWebkitMaskBoxImageRepeat, parser, m_repeat.get(), important);
5697     }
5698 
commitBorderImage(CSSPropertyParser * parser,bool important)5699     void commitBorderImage(CSSPropertyParser* parser, bool important)
5700     {
5701         commitBorderImageProperty(CSSPropertyBorderImageSource, parser, m_image, important);
5702         commitBorderImageProperty(CSSPropertyBorderImageSlice, parser, m_imageSlice.get(), important);
5703         commitBorderImageProperty(CSSPropertyBorderImageWidth, parser, m_borderWidth.get(), important);
5704         commitBorderImageProperty(CSSPropertyBorderImageOutset, parser, m_outset.get(), important);
5705         commitBorderImageProperty(CSSPropertyBorderImageRepeat, parser, m_repeat, important);
5706     }
5707 
commitBorderImageProperty(CSSPropertyID propId,CSSPropertyParser * parser,PassRefPtrWillBeRawPtr<CSSValue> value,bool important)5708     void commitBorderImageProperty(CSSPropertyID propId, CSSPropertyParser* parser, PassRefPtrWillBeRawPtr<CSSValue> value, bool important)
5709     {
5710         if (value)
5711             parser->addProperty(propId, value, important);
5712         else
5713             parser->addProperty(propId, cssValuePool().createImplicitInitialValue(), important, true);
5714     }
5715 
5716     static bool buildFromParser(CSSPropertyParser&, CSSPropertyID, BorderImageParseContext&);
5717 
5718     bool m_canAdvance;
5719 
5720     bool m_allowCommit;
5721     bool m_allowImage;
5722     bool m_allowImageSlice;
5723     bool m_allowRepeat;
5724     bool m_allowForwardSlashOperator;
5725 
5726     bool m_requireWidth;
5727     bool m_requireOutset;
5728 
5729     RefPtrWillBeMember<CSSValue> m_image;
5730     RefPtrWillBeMember<CSSBorderImageSliceValue> m_imageSlice;
5731     RefPtrWillBeMember<CSSPrimitiveValue> m_borderWidth;
5732     RefPtrWillBeMember<CSSPrimitiveValue> m_outset;
5733 
5734     RefPtrWillBeMember<CSSValue> m_repeat;
5735 };
5736 
buildFromParser(CSSPropertyParser & parser,CSSPropertyID propId,BorderImageParseContext & context)5737 bool BorderImageParseContext::buildFromParser(CSSPropertyParser& parser, CSSPropertyID propId, BorderImageParseContext& context)
5738 {
5739     CSSPropertyParser::ShorthandScope scope(&parser, propId);
5740     while (CSSParserValue* val = parser.m_valueList->current()) {
5741         context.setCanAdvance(false);
5742 
5743         if (!context.canAdvance() && context.allowForwardSlashOperator() && isForwardSlashOperator(val))
5744             context.commitForwardSlashOperator();
5745 
5746         if (!context.canAdvance() && context.allowImage()) {
5747             if (val->unit == CSSPrimitiveValue::CSS_URI) {
5748                 context.commitImage(parser.createCSSImageValueWithReferrer(val->string, parser.m_context.completeURL(val->string)));
5749             } else if (isGeneratedImageValue(val)) {
5750                 RefPtrWillBeRawPtr<CSSValue> value = nullptr;
5751                 if (parser.parseGeneratedImage(parser.m_valueList, value))
5752                     context.commitImage(value.release());
5753                 else
5754                     return false;
5755             } else if (val->unit == CSSParserValue::Function && equalIgnoringCase(val->function->name, "-webkit-image-set")) {
5756                 RefPtrWillBeRawPtr<CSSValue> value = parser.parseImageSet(parser.m_valueList);
5757                 if (value)
5758                     context.commitImage(value.release());
5759                 else
5760                     return false;
5761             } else if (val->id == CSSValueNone)
5762                 context.commitImage(cssValuePool().createIdentifierValue(CSSValueNone));
5763         }
5764 
5765         if (!context.canAdvance() && context.allowImageSlice()) {
5766             RefPtrWillBeRawPtr<CSSBorderImageSliceValue> imageSlice = nullptr;
5767             if (parser.parseBorderImageSlice(propId, imageSlice))
5768                 context.commitImageSlice(imageSlice.release());
5769         }
5770 
5771         if (!context.canAdvance() && context.allowRepeat()) {
5772             RefPtrWillBeRawPtr<CSSValue> repeat = nullptr;
5773             if (parser.parseBorderImageRepeat(repeat))
5774                 context.commitRepeat(repeat.release());
5775         }
5776 
5777         if (!context.canAdvance() && context.requireWidth()) {
5778             RefPtrWillBeRawPtr<CSSPrimitiveValue> borderWidth = nullptr;
5779             if (parser.parseBorderImageWidth(borderWidth))
5780                 context.commitBorderWidth(borderWidth.release());
5781         }
5782 
5783         if (!context.canAdvance() && context.requireOutset()) {
5784             RefPtrWillBeRawPtr<CSSPrimitiveValue> borderOutset = nullptr;
5785             if (parser.parseBorderImageOutset(borderOutset))
5786                 context.commitBorderOutset(borderOutset.release());
5787         }
5788 
5789         if (!context.canAdvance())
5790             return false;
5791 
5792         parser.m_valueList->next();
5793     }
5794 
5795     return context.allowCommit();
5796 }
5797 
parseBorderImageShorthand(CSSPropertyID propId,bool important)5798 bool CSSPropertyParser::parseBorderImageShorthand(CSSPropertyID propId, bool important)
5799 {
5800     BorderImageParseContext context;
5801     if (BorderImageParseContext::buildFromParser(*this, propId, context)) {
5802         switch (propId) {
5803         case CSSPropertyWebkitMaskBoxImage:
5804             context.commitMaskBoxImage(this, important);
5805             return true;
5806         case CSSPropertyBorderImage:
5807             context.commitBorderImage(this, important);
5808             return true;
5809         default:
5810             ASSERT_NOT_REACHED();
5811             return false;
5812         }
5813     }
5814     return false;
5815 }
5816 
parseBorderImage(CSSPropertyID propId)5817 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseBorderImage(CSSPropertyID propId)
5818 {
5819     BorderImageParseContext context;
5820     if (BorderImageParseContext::buildFromParser(*this, propId, context)) {
5821         return context.commitCSSValue();
5822     }
5823     return nullptr;
5824 }
5825 
isBorderImageRepeatKeyword(int id)5826 static bool isBorderImageRepeatKeyword(int id)
5827 {
5828     return id == CSSValueStretch || id == CSSValueRepeat || id == CSSValueSpace || id == CSSValueRound;
5829 }
5830 
parseBorderImageRepeat(RefPtrWillBeRawPtr<CSSValue> & result)5831 bool CSSPropertyParser::parseBorderImageRepeat(RefPtrWillBeRawPtr<CSSValue>& result)
5832 {
5833     RefPtrWillBeRawPtr<CSSPrimitiveValue> firstValue = nullptr;
5834     RefPtrWillBeRawPtr<CSSPrimitiveValue> secondValue = nullptr;
5835     CSSParserValue* val = m_valueList->current();
5836     if (!val)
5837         return false;
5838     if (isBorderImageRepeatKeyword(val->id))
5839         firstValue = cssValuePool().createIdentifierValue(val->id);
5840     else
5841         return false;
5842 
5843     val = m_valueList->next();
5844     if (val) {
5845         if (isBorderImageRepeatKeyword(val->id))
5846             secondValue = cssValuePool().createIdentifierValue(val->id);
5847         else if (!inShorthand()) {
5848             // If we're not parsing a shorthand then we are invalid.
5849             return false;
5850         } else {
5851             // We need to rewind the value list, so that when its advanced we'll
5852             // end up back at this value.
5853             m_valueList->previous();
5854             secondValue = firstValue;
5855         }
5856     } else
5857         secondValue = firstValue;
5858 
5859     result = createPrimitiveValuePair(firstValue, secondValue);
5860     return true;
5861 }
5862 
5863 class BorderImageSliceParseContext {
5864     STACK_ALLOCATED();
5865 public:
BorderImageSliceParseContext(CSSPropertyParser * parser)5866     BorderImageSliceParseContext(CSSPropertyParser* parser)
5867     : m_parser(parser)
5868     , m_allowNumber(true)
5869     , m_allowFill(true)
5870     , m_allowFinalCommit(false)
5871     , m_fill(false)
5872     { }
5873 
allowNumber() const5874     bool allowNumber() const { return m_allowNumber; }
allowFill() const5875     bool allowFill() const { return m_allowFill; }
allowFinalCommit() const5876     bool allowFinalCommit() const { return m_allowFinalCommit; }
top() const5877     CSSPrimitiveValue* top() const { return m_top.get(); }
5878 
commitNumber(CSSParserValue * v)5879     void commitNumber(CSSParserValue* v)
5880     {
5881         RefPtrWillBeRawPtr<CSSPrimitiveValue> val = m_parser->createPrimitiveNumericValue(v);
5882         if (!m_top)
5883             m_top = val;
5884         else if (!m_right)
5885             m_right = val;
5886         else if (!m_bottom)
5887             m_bottom = val;
5888         else {
5889             ASSERT(!m_left);
5890             m_left = val;
5891         }
5892 
5893         m_allowNumber = !m_left;
5894         m_allowFinalCommit = true;
5895     }
5896 
commitFill()5897     void commitFill() { m_fill = true; m_allowFill = false; m_allowNumber = !m_top; }
5898 
commitBorderImageSlice()5899     PassRefPtrWillBeRawPtr<CSSBorderImageSliceValue> commitBorderImageSlice()
5900     {
5901         // We need to clone and repeat values for any omissions.
5902         ASSERT(m_top);
5903         if (!m_right) {
5904             m_right = m_top;
5905             m_bottom = m_top;
5906             m_left = m_top;
5907         }
5908         if (!m_bottom) {
5909             m_bottom = m_top;
5910             m_left = m_right;
5911         }
5912         if (!m_left)
5913             m_left = m_right;
5914 
5915         // Now build a rect value to hold all four of our primitive values.
5916         RefPtrWillBeRawPtr<Quad> quad = Quad::create();
5917         quad->setTop(m_top);
5918         quad->setRight(m_right);
5919         quad->setBottom(m_bottom);
5920         quad->setLeft(m_left);
5921 
5922         // Make our new border image value now.
5923         return CSSBorderImageSliceValue::create(cssValuePool().createValue(quad.release()), m_fill);
5924     }
5925 
5926 private:
5927     CSSPropertyParser* m_parser;
5928 
5929     bool m_allowNumber;
5930     bool m_allowFill;
5931     bool m_allowFinalCommit;
5932 
5933     RefPtrWillBeMember<CSSPrimitiveValue> m_top;
5934     RefPtrWillBeMember<CSSPrimitiveValue> m_right;
5935     RefPtrWillBeMember<CSSPrimitiveValue> m_bottom;
5936     RefPtrWillBeMember<CSSPrimitiveValue> m_left;
5937 
5938     bool m_fill;
5939 };
5940 
parseBorderImageSlice(CSSPropertyID propId,RefPtrWillBeRawPtr<CSSBorderImageSliceValue> & result)5941 bool CSSPropertyParser::parseBorderImageSlice(CSSPropertyID propId, RefPtrWillBeRawPtr<CSSBorderImageSliceValue>& result)
5942 {
5943     BorderImageSliceParseContext context(this);
5944     for (CSSParserValue* val = m_valueList->current(); val; val = m_valueList->next()) {
5945         // FIXME calc() http://webkit.org/b/16662 : calc is parsed but values are not created yet.
5946         if (context.allowNumber() && !isCalculation(val) && validUnit(val, FInteger | FNonNeg | FPercent)) {
5947             context.commitNumber(val);
5948         } else if (context.allowFill() && val->id == CSSValueFill) {
5949             context.commitFill();
5950         } else if (!inShorthand()) {
5951             // If we're not parsing a shorthand then we are invalid.
5952             return false;
5953         } else {
5954             if (context.allowFinalCommit()) {
5955                 // We're going to successfully parse, but we don't want to consume this token.
5956                 m_valueList->previous();
5957             }
5958             break;
5959         }
5960     }
5961 
5962     if (context.allowFinalCommit()) {
5963         // FIXME: For backwards compatibility, -webkit-border-image, -webkit-mask-box-image and -webkit-box-reflect have to do a fill by default.
5964         // FIXME: What do we do with -webkit-box-reflect and -webkit-mask-box-image? Probably just have to leave them filling...
5965         if (propId == CSSPropertyWebkitBorderImage || propId == CSSPropertyWebkitMaskBoxImage || propId == CSSPropertyWebkitBoxReflect)
5966             context.commitFill();
5967 
5968         // Need to fully commit as a single value.
5969         result = context.commitBorderImageSlice();
5970         return true;
5971     }
5972 
5973     return false;
5974 }
5975 
5976 class BorderImageQuadParseContext {
5977     STACK_ALLOCATED();
5978 public:
BorderImageQuadParseContext(CSSPropertyParser * parser)5979     BorderImageQuadParseContext(CSSPropertyParser* parser)
5980     : m_parser(parser)
5981     , m_allowNumber(true)
5982     , m_allowFinalCommit(false)
5983     { }
5984 
allowNumber() const5985     bool allowNumber() const { return m_allowNumber; }
allowFinalCommit() const5986     bool allowFinalCommit() const { return m_allowFinalCommit; }
top() const5987     CSSPrimitiveValue* top() const { return m_top.get(); }
5988 
commitNumber(CSSParserValue * v)5989     void commitNumber(CSSParserValue* v)
5990     {
5991         RefPtrWillBeRawPtr<CSSPrimitiveValue> val = nullptr;
5992         if (v->id == CSSValueAuto)
5993             val = cssValuePool().createIdentifierValue(v->id);
5994         else
5995             val = m_parser->createPrimitiveNumericValue(v);
5996 
5997         if (!m_top)
5998             m_top = val;
5999         else if (!m_right)
6000             m_right = val;
6001         else if (!m_bottom)
6002             m_bottom = val;
6003         else {
6004             ASSERT(!m_left);
6005             m_left = val;
6006         }
6007 
6008         m_allowNumber = !m_left;
6009         m_allowFinalCommit = true;
6010     }
6011 
setTop(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> val)6012     void setTop(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> val) { m_top = val; }
6013 
commitBorderImageQuad()6014     PassRefPtrWillBeRawPtr<CSSPrimitiveValue> commitBorderImageQuad()
6015     {
6016         // We need to clone and repeat values for any omissions.
6017         ASSERT(m_top);
6018         if (!m_right) {
6019             m_right = m_top;
6020             m_bottom = m_top;
6021             m_left = m_top;
6022         }
6023         if (!m_bottom) {
6024             m_bottom = m_top;
6025             m_left = m_right;
6026         }
6027         if (!m_left)
6028             m_left = m_right;
6029 
6030         // Now build a quad value to hold all four of our primitive values.
6031         RefPtrWillBeRawPtr<Quad> quad = Quad::create();
6032         quad->setTop(m_top);
6033         quad->setRight(m_right);
6034         quad->setBottom(m_bottom);
6035         quad->setLeft(m_left);
6036 
6037         // Make our new value now.
6038         return cssValuePool().createValue(quad.release());
6039     }
6040 
6041 private:
6042     CSSPropertyParser* m_parser;
6043 
6044     bool m_allowNumber;
6045     bool m_allowFinalCommit;
6046 
6047     RefPtrWillBeMember<CSSPrimitiveValue> m_top;
6048     RefPtrWillBeMember<CSSPrimitiveValue> m_right;
6049     RefPtrWillBeMember<CSSPrimitiveValue> m_bottom;
6050     RefPtrWillBeMember<CSSPrimitiveValue> m_left;
6051 };
6052 
parseBorderImageQuad(Units validUnits,RefPtrWillBeRawPtr<CSSPrimitiveValue> & result)6053 bool CSSPropertyParser::parseBorderImageQuad(Units validUnits, RefPtrWillBeRawPtr<CSSPrimitiveValue>& result)
6054 {
6055     BorderImageQuadParseContext context(this);
6056     for (CSSParserValue* val = m_valueList->current(); val; val = m_valueList->next()) {
6057         if (context.allowNumber() && (validUnit(val, validUnits, HTMLStandardMode) || val->id == CSSValueAuto)) {
6058             context.commitNumber(val);
6059         } else if (!inShorthand()) {
6060             // If we're not parsing a shorthand then we are invalid.
6061             return false;
6062         } else {
6063             if (context.allowFinalCommit())
6064                 m_valueList->previous(); // The shorthand loop will advance back to this point.
6065             break;
6066         }
6067     }
6068 
6069     if (context.allowFinalCommit()) {
6070         // Need to fully commit as a single value.
6071         result = context.commitBorderImageQuad();
6072         return true;
6073     }
6074     return false;
6075 }
6076 
parseBorderImageWidth(RefPtrWillBeRawPtr<CSSPrimitiveValue> & result)6077 bool CSSPropertyParser::parseBorderImageWidth(RefPtrWillBeRawPtr<CSSPrimitiveValue>& result)
6078 {
6079     return parseBorderImageQuad(FLength | FNumber | FNonNeg | FPercent, result);
6080 }
6081 
parseBorderImageOutset(RefPtrWillBeRawPtr<CSSPrimitiveValue> & result)6082 bool CSSPropertyParser::parseBorderImageOutset(RefPtrWillBeRawPtr<CSSPrimitiveValue>& result)
6083 {
6084     return parseBorderImageQuad(FLength | FNumber | FNonNeg, result);
6085 }
6086 
parseBorderRadius(CSSPropertyID propId,bool important)6087 bool CSSPropertyParser::parseBorderRadius(CSSPropertyID propId, bool important)
6088 {
6089     unsigned num = m_valueList->size();
6090     if (num > 9)
6091         return false;
6092 
6093     ShorthandScope scope(this, propId);
6094     RefPtrWillBeRawPtr<CSSPrimitiveValue> radii[2][4];
6095 #if ENABLE(OILPAN)
6096     // Zero initialize the array of raw pointers.
6097     memset(&radii, 0, sizeof(radii));
6098 #endif
6099 
6100     unsigned indexAfterSlash = 0;
6101     for (unsigned i = 0; i < num; ++i) {
6102         CSSParserValue* value = m_valueList->valueAt(i);
6103         if (value->unit == CSSParserValue::Operator) {
6104             if (value->iValue != '/')
6105                 return false;
6106 
6107             if (!i || indexAfterSlash || i + 1 == num || num > i + 5)
6108                 return false;
6109 
6110             indexAfterSlash = i + 1;
6111             completeBorderRadii(radii[0]);
6112             continue;
6113         }
6114 
6115         if (i - indexAfterSlash >= 4)
6116             return false;
6117 
6118         if (!validUnit(value, FLength | FPercent | FNonNeg))
6119             return false;
6120 
6121         RefPtrWillBeRawPtr<CSSPrimitiveValue> radius = createPrimitiveNumericValue(value);
6122 
6123         if (!indexAfterSlash) {
6124             radii[0][i] = radius;
6125 
6126             // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2;
6127             if (num == 2 && propId == CSSPropertyWebkitBorderRadius) {
6128                 indexAfterSlash = 1;
6129                 completeBorderRadii(radii[0]);
6130             }
6131         } else
6132             radii[1][i - indexAfterSlash] = radius.release();
6133     }
6134 
6135     if (!indexAfterSlash) {
6136         completeBorderRadii(radii[0]);
6137         for (unsigned i = 0; i < 4; ++i)
6138             radii[1][i] = radii[0][i];
6139     } else
6140         completeBorderRadii(radii[1]);
6141 
6142     ImplicitScope implicitScope(this);
6143     addProperty(CSSPropertyBorderTopLeftRadius, createPrimitiveValuePair(radii[0][0].release(), radii[1][0].release()), important);
6144     addProperty(CSSPropertyBorderTopRightRadius, createPrimitiveValuePair(radii[0][1].release(), radii[1][1].release()), important);
6145     addProperty(CSSPropertyBorderBottomRightRadius, createPrimitiveValuePair(radii[0][2].release(), radii[1][2].release()), important);
6146     addProperty(CSSPropertyBorderBottomLeftRadius, createPrimitiveValuePair(radii[0][3].release(), radii[1][3].release()), important);
6147     return true;
6148 }
6149 
parseAspectRatio()6150 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseAspectRatio()
6151 {
6152     unsigned num = m_valueList->size();
6153     if (num == 1 && m_valueList->valueAt(0)->id == CSSValueNone) {
6154         m_valueList->next();
6155         return cssValuePool().createIdentifierValue(CSSValueNone);
6156     }
6157 
6158     if (num != 3)
6159         return nullptr;
6160 
6161     CSSParserValue* lvalue = m_valueList->current();
6162     CSSParserValue* op = m_valueList->next();
6163     CSSParserValue* rvalue = m_valueList->next();
6164     m_valueList->next();
6165 
6166     if (!isForwardSlashOperator(op))
6167         return nullptr;
6168 
6169     if (!validUnit(lvalue, FNumber | FNonNeg) || !validUnit(rvalue, FNumber | FNonNeg))
6170         return nullptr;
6171 
6172     if (!lvalue->fValue || !rvalue->fValue)
6173         return nullptr;
6174 
6175     return CSSAspectRatioValue::create(narrowPrecisionToFloat(lvalue->fValue), narrowPrecisionToFloat(rvalue->fValue));
6176 }
6177 
parseCounter(int defaultValue)6178 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseCounter(int defaultValue)
6179 {
6180     RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
6181 
6182     while (m_valueList->current()) {
6183         CSSParserValue* val = m_valueList->current();
6184         if (val->unit != CSSPrimitiveValue::CSS_IDENT)
6185             return nullptr;
6186         RefPtrWillBeRawPtr<CSSPrimitiveValue> counterName = createPrimitiveStringValue(val);
6187         m_valueList->next();
6188 
6189         val = m_valueList->current();
6190         int i = defaultValue;
6191         if (val && validUnit(val, FInteger)) {
6192             i = clampToInteger(val->fValue);
6193             m_valueList->next();
6194         }
6195 
6196         list->append(createPrimitiveValuePair(counterName.release(),
6197             cssValuePool().createValue(i, CSSPrimitiveValue::CSS_NUMBER)));
6198     }
6199 
6200     if (!list->length())
6201         return nullptr;
6202     return list.release();
6203 }
6204 
6205 // This should go away once we drop support for -webkit-gradient
parseDeprecatedGradientPoint(CSSParserValue * a,bool horizontal)6206 static PassRefPtrWillBeRawPtr<CSSPrimitiveValue> parseDeprecatedGradientPoint(CSSParserValue* a, bool horizontal)
6207 {
6208     RefPtrWillBeRawPtr<CSSPrimitiveValue> result = nullptr;
6209     if (a->unit == CSSPrimitiveValue::CSS_IDENT) {
6210         if ((a->id == CSSValueLeft && horizontal)
6211             || (a->id == CSSValueTop && !horizontal))
6212             result = cssValuePool().createValue(0., CSSPrimitiveValue::CSS_PERCENTAGE);
6213         else if ((a->id == CSSValueRight && horizontal)
6214             || (a->id == CSSValueBottom && !horizontal))
6215             result = cssValuePool().createValue(100., CSSPrimitiveValue::CSS_PERCENTAGE);
6216         else if (a->id == CSSValueCenter)
6217             result = cssValuePool().createValue(50., CSSPrimitiveValue::CSS_PERCENTAGE);
6218     } else if (a->unit == CSSPrimitiveValue::CSS_NUMBER || a->unit == CSSPrimitiveValue::CSS_PERCENTAGE) {
6219         result = cssValuePool().createValue(a->fValue, static_cast<CSSPrimitiveValue::UnitType>(a->unit));
6220     }
6221     return result;
6222 }
6223 
parseDeprecatedGradientColorStop(CSSPropertyParser * p,CSSParserValue * a,CSSGradientColorStop & stop)6224 bool parseDeprecatedGradientColorStop(CSSPropertyParser* p, CSSParserValue* a, CSSGradientColorStop& stop)
6225 {
6226     if (a->unit != CSSParserValue::Function)
6227         return false;
6228 
6229     if (!equalIgnoringCase(a->function->name, "from") &&
6230         !equalIgnoringCase(a->function->name, "to") &&
6231         !equalIgnoringCase(a->function->name, "color-stop"))
6232         return false;
6233 
6234     CSSParserValueList* args = a->function->args.get();
6235     if (!args)
6236         return false;
6237 
6238     if (equalIgnoringCase(a->function->name, "from")
6239         || equalIgnoringCase(a->function->name, "to")) {
6240         // The "from" and "to" stops expect 1 argument.
6241         if (args->size() != 1)
6242             return false;
6243 
6244         if (equalIgnoringCase(a->function->name, "from"))
6245             stop.m_position = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER);
6246         else
6247             stop.m_position = cssValuePool().createValue(1, CSSPrimitiveValue::CSS_NUMBER);
6248 
6249         CSSValueID id = args->current()->id;
6250         if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu)
6251             stop.m_color = cssValuePool().createIdentifierValue(id);
6252         else
6253             stop.m_color = p->parseColor(args->current());
6254         if (!stop.m_color)
6255             return false;
6256     }
6257 
6258     // The "color-stop" function expects 3 arguments.
6259     if (equalIgnoringCase(a->function->name, "color-stop")) {
6260         if (args->size() != 3)
6261             return false;
6262 
6263         CSSParserValue* stopArg = args->current();
6264         if (stopArg->unit == CSSPrimitiveValue::CSS_PERCENTAGE)
6265             stop.m_position = cssValuePool().createValue(stopArg->fValue / 100, CSSPrimitiveValue::CSS_NUMBER);
6266         else if (stopArg->unit == CSSPrimitiveValue::CSS_NUMBER)
6267             stop.m_position = cssValuePool().createValue(stopArg->fValue, CSSPrimitiveValue::CSS_NUMBER);
6268         else
6269             return false;
6270 
6271         args->next();
6272         if (!consumeComma(args))
6273             return false;
6274 
6275         stopArg = args->current();
6276         CSSValueID id = stopArg->id;
6277         if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu)
6278             stop.m_color = cssValuePool().createIdentifierValue(id);
6279         else
6280             stop.m_color = p->parseColor(stopArg);
6281         if (!stop.m_color)
6282             return false;
6283     }
6284 
6285     return true;
6286 }
6287 
parseDeprecatedGradient(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & gradient)6288 bool CSSPropertyParser::parseDeprecatedGradient(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& gradient)
6289 {
6290     // Walk the arguments.
6291     CSSParserValueList* args = valueList->current()->function->args.get();
6292     if (!args || args->size() == 0)
6293         return false;
6294 
6295     // The first argument is the gradient type.  It is an identifier.
6296     CSSGradientType gradientType;
6297     CSSParserValue* a = args->current();
6298     if (!a || a->unit != CSSPrimitiveValue::CSS_IDENT)
6299         return false;
6300     if (a->id == CSSValueLinear)
6301         gradientType = CSSDeprecatedLinearGradient;
6302     else if (equalIgnoringCase(a, "radial"))
6303         gradientType = CSSDeprecatedRadialGradient;
6304     else
6305         return false;
6306 
6307     RefPtrWillBeRawPtr<CSSGradientValue> result = nullptr;
6308     switch (gradientType) {
6309     case CSSDeprecatedLinearGradient:
6310         result = CSSLinearGradientValue::create(NonRepeating, gradientType);
6311         break;
6312     case CSSDeprecatedRadialGradient:
6313         result = CSSRadialGradientValue::create(NonRepeating, gradientType);
6314         break;
6315     default:
6316         // The rest of the gradient types shouldn't appear here.
6317         ASSERT_NOT_REACHED();
6318     }
6319     args->next();
6320 
6321     if (!consumeComma(args))
6322         return false;
6323 
6324     // Next comes the starting point for the gradient as an x y pair.  There is no
6325     // comma between the x and the y values.
6326     // First X.  It can be left, right, number or percent.
6327     a = args->current();
6328     if (!a)
6329         return false;
6330     RefPtrWillBeRawPtr<CSSPrimitiveValue> point = parseDeprecatedGradientPoint(a, true);
6331     if (!point)
6332         return false;
6333     result->setFirstX(point.release());
6334 
6335     // First Y.  It can be top, bottom, number or percent.
6336     a = args->next();
6337     if (!a)
6338         return false;
6339     point = parseDeprecatedGradientPoint(a, false);
6340     if (!point)
6341         return false;
6342     result->setFirstY(point.release());
6343 
6344     // Comma after the first point.
6345     args->next();
6346     if (!consumeComma(args))
6347         return false;
6348 
6349     // For radial gradients only, we now expect a numeric radius.
6350     if (gradientType == CSSDeprecatedRadialGradient) {
6351         a = args->current();
6352         if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER)
6353             return false;
6354         toCSSRadialGradientValue(result.get())->setFirstRadius(createPrimitiveNumericValue(a));
6355 
6356         // Comma after the first radius.
6357         args->next();
6358         if (!consumeComma(args))
6359             return false;
6360     }
6361 
6362     // Next is the ending point for the gradient as an x, y pair.
6363     // Second X.  It can be left, right, number or percent.
6364     a = args->current();
6365     if (!a)
6366         return false;
6367     point = parseDeprecatedGradientPoint(a, true);
6368     if (!point)
6369         return false;
6370     result->setSecondX(point.release());
6371 
6372     // Second Y.  It can be top, bottom, number or percent.
6373     a = args->next();
6374     if (!a)
6375         return false;
6376     point = parseDeprecatedGradientPoint(a, false);
6377     if (!point)
6378         return false;
6379     result->setSecondY(point.release());
6380     args->next();
6381 
6382     // For radial gradients only, we now expect the second radius.
6383     if (gradientType == CSSDeprecatedRadialGradient) {
6384         // Comma after the second point.
6385         if (!consumeComma(args))
6386             return false;
6387 
6388         a = args->current();
6389         if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER)
6390             return false;
6391         toCSSRadialGradientValue(result.get())->setSecondRadius(createPrimitiveNumericValue(a));
6392         args->next();
6393     }
6394 
6395     // We now will accept any number of stops (0 or more).
6396     a = args->current();
6397     while (a) {
6398         // Look for the comma before the next stop.
6399         if (!consumeComma(args))
6400             return false;
6401 
6402         // Now examine the stop itself.
6403         a = args->current();
6404         if (!a)
6405             return false;
6406 
6407         // The function name needs to be one of "from", "to", or "color-stop."
6408         CSSGradientColorStop stop;
6409         if (!parseDeprecatedGradientColorStop(this, a, stop))
6410             return false;
6411         result->addStop(stop);
6412 
6413         // Advance
6414         a = args->next();
6415     }
6416 
6417     gradient = result.release();
6418     return true;
6419 }
6420 
valueFromSideKeyword(CSSParserValue * a,bool & isHorizontal)6421 static PassRefPtrWillBeRawPtr<CSSPrimitiveValue> valueFromSideKeyword(CSSParserValue* a, bool& isHorizontal)
6422 {
6423     if (a->unit != CSSPrimitiveValue::CSS_IDENT)
6424         return nullptr;
6425 
6426     switch (a->id) {
6427         case CSSValueLeft:
6428         case CSSValueRight:
6429             isHorizontal = true;
6430             break;
6431         case CSSValueTop:
6432         case CSSValueBottom:
6433             isHorizontal = false;
6434             break;
6435         default:
6436             return nullptr;
6437     }
6438     return cssValuePool().createIdentifierValue(a->id);
6439 }
6440 
parseGradientColorOrKeyword(CSSPropertyParser * p,CSSParserValue * value)6441 PassRefPtrWillBeRawPtr<CSSPrimitiveValue> parseGradientColorOrKeyword(CSSPropertyParser* p, CSSParserValue* value)
6442 {
6443     CSSValueID id = value->id;
6444     if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || id == CSSValueCurrentcolor)
6445         return cssValuePool().createIdentifierValue(id);
6446 
6447     return p->parseColor(value);
6448 }
6449 
parseDeprecatedLinearGradient(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & gradient,CSSGradientRepeat repeating)6450 bool CSSPropertyParser::parseDeprecatedLinearGradient(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& gradient, CSSGradientRepeat repeating)
6451 {
6452     RefPtrWillBeRawPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, CSSPrefixedLinearGradient);
6453 
6454     // Walk the arguments.
6455     CSSParserValueList* args = valueList->current()->function->args.get();
6456     if (!args || !args->size())
6457         return false;
6458 
6459     CSSParserValue* a = args->current();
6460     if (!a)
6461         return false;
6462 
6463     bool expectComma = false;
6464     // Look for angle.
6465     if (validUnit(a, FAngle, HTMLStandardMode)) {
6466         result->setAngle(createPrimitiveNumericValue(a));
6467 
6468         args->next();
6469         expectComma = true;
6470     } else {
6471         // Look one or two optional keywords that indicate a side or corner.
6472         RefPtrWillBeRawPtr<CSSPrimitiveValue> startX = nullptr;
6473         RefPtrWillBeRawPtr<CSSPrimitiveValue> startY = nullptr;
6474 
6475         RefPtrWillBeRawPtr<CSSPrimitiveValue> location = nullptr;
6476         bool isHorizontal = false;
6477         if ((location = valueFromSideKeyword(a, isHorizontal))) {
6478             if (isHorizontal)
6479                 startX = location;
6480             else
6481                 startY = location;
6482 
6483             a = args->next();
6484             if (a) {
6485                 if ((location = valueFromSideKeyword(a, isHorizontal))) {
6486                     if (isHorizontal) {
6487                         if (startX)
6488                             return false;
6489                         startX = location;
6490                     } else {
6491                         if (startY)
6492                             return false;
6493                         startY = location;
6494                     }
6495 
6496                     args->next();
6497                 }
6498             }
6499 
6500             expectComma = true;
6501         }
6502 
6503         if (!startX && !startY)
6504             startY = cssValuePool().createIdentifierValue(CSSValueTop);
6505 
6506         result->setFirstX(startX.release());
6507         result->setFirstY(startY.release());
6508     }
6509 
6510     if (!parseGradientColorStops(args, result.get(), expectComma))
6511         return false;
6512 
6513     if (!result->stopCount())
6514         return false;
6515 
6516     gradient = result.release();
6517     return true;
6518 }
6519 
parseDeprecatedRadialGradient(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & gradient,CSSGradientRepeat repeating)6520 bool CSSPropertyParser::parseDeprecatedRadialGradient(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& gradient, CSSGradientRepeat repeating)
6521 {
6522     RefPtrWillBeRawPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSPrefixedRadialGradient);
6523 
6524     // Walk the arguments.
6525     CSSParserValueList* args = valueList->current()->function->args.get();
6526     if (!args || !args->size())
6527         return false;
6528 
6529     CSSParserValue* a = args->current();
6530     if (!a)
6531         return false;
6532 
6533     bool expectComma = false;
6534 
6535     // Optional background-position
6536     RefPtrWillBeRawPtr<CSSValue> centerX = nullptr;
6537     RefPtrWillBeRawPtr<CSSValue> centerY = nullptr;
6538     // parse2ValuesFillPosition advances the args next pointer.
6539     parse2ValuesFillPosition(args, centerX, centerY);
6540 
6541     if ((centerX || centerY) && !consumeComma(args))
6542         return false;
6543 
6544     a = args->current();
6545     if (!a)
6546         return false;
6547 
6548     result->setFirstX(toCSSPrimitiveValue(centerX.get()));
6549     result->setSecondX(toCSSPrimitiveValue(centerX.get()));
6550     // CSS3 radial gradients always share the same start and end point.
6551     result->setFirstY(toCSSPrimitiveValue(centerY.get()));
6552     result->setSecondY(toCSSPrimitiveValue(centerY.get()));
6553 
6554     RefPtrWillBeRawPtr<CSSPrimitiveValue> shapeValue = nullptr;
6555     RefPtrWillBeRawPtr<CSSPrimitiveValue> sizeValue = nullptr;
6556 
6557     // Optional shape and/or size in any order.
6558     for (int i = 0; i < 2; ++i) {
6559         if (a->unit != CSSPrimitiveValue::CSS_IDENT)
6560             break;
6561 
6562         bool foundValue = false;
6563         switch (a->id) {
6564         case CSSValueCircle:
6565         case CSSValueEllipse:
6566             shapeValue = cssValuePool().createIdentifierValue(a->id);
6567             foundValue = true;
6568             break;
6569         case CSSValueClosestSide:
6570         case CSSValueClosestCorner:
6571         case CSSValueFarthestSide:
6572         case CSSValueFarthestCorner:
6573         case CSSValueContain:
6574         case CSSValueCover:
6575             sizeValue = cssValuePool().createIdentifierValue(a->id);
6576             foundValue = true;
6577             break;
6578         default:
6579             break;
6580         }
6581 
6582         if (foundValue) {
6583             a = args->next();
6584             if (!a)
6585                 return false;
6586 
6587             expectComma = true;
6588         }
6589     }
6590 
6591     result->setShape(shapeValue);
6592     result->setSizingBehavior(sizeValue);
6593 
6594     // Or, two lengths or percentages
6595     RefPtrWillBeRawPtr<CSSPrimitiveValue> horizontalSize = nullptr;
6596     RefPtrWillBeRawPtr<CSSPrimitiveValue> verticalSize = nullptr;
6597 
6598     if (!shapeValue && !sizeValue) {
6599         if (validUnit(a, FLength | FPercent)) {
6600             horizontalSize = createPrimitiveNumericValue(a);
6601             a = args->next();
6602             if (!a)
6603                 return false;
6604 
6605             expectComma = true;
6606         }
6607 
6608         if (validUnit(a, FLength | FPercent)) {
6609             verticalSize = createPrimitiveNumericValue(a);
6610 
6611             a = args->next();
6612             if (!a)
6613                 return false;
6614             expectComma = true;
6615         }
6616     }
6617 
6618     // Must have neither or both.
6619     if (!horizontalSize != !verticalSize)
6620         return false;
6621 
6622     result->setEndHorizontalSize(horizontalSize);
6623     result->setEndVerticalSize(verticalSize);
6624 
6625     if (!parseGradientColorStops(args, result.get(), expectComma))
6626         return false;
6627 
6628     gradient = result.release();
6629     return true;
6630 }
6631 
parseLinearGradient(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & gradient,CSSGradientRepeat repeating)6632 bool CSSPropertyParser::parseLinearGradient(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& gradient, CSSGradientRepeat repeating)
6633 {
6634     RefPtrWillBeRawPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, CSSLinearGradient);
6635 
6636     CSSParserValueList* args = valueList->current()->function->args.get();
6637     if (!args || !args->size())
6638         return false;
6639 
6640     CSSParserValue* a = args->current();
6641     if (!a)
6642         return false;
6643 
6644     bool expectComma = false;
6645     // Look for angle.
6646     if (validUnit(a, FAngle, HTMLStandardMode)) {
6647         result->setAngle(createPrimitiveNumericValue(a));
6648 
6649         args->next();
6650         expectComma = true;
6651     } else if (a->unit == CSSPrimitiveValue::CSS_IDENT && equalIgnoringCase(a, "to")) {
6652         // to [ [left | right] || [top | bottom] ]
6653         a = args->next();
6654         if (!a)
6655             return false;
6656 
6657         RefPtrWillBeRawPtr<CSSPrimitiveValue> endX = nullptr;
6658         RefPtrWillBeRawPtr<CSSPrimitiveValue> endY = nullptr;
6659         RefPtrWillBeRawPtr<CSSPrimitiveValue> location = nullptr;
6660         bool isHorizontal = false;
6661 
6662         location = valueFromSideKeyword(a, isHorizontal);
6663         if (!location)
6664             return false;
6665 
6666         if (isHorizontal)
6667             endX = location;
6668         else
6669             endY = location;
6670 
6671         a = args->next();
6672         if (!a)
6673             return false;
6674 
6675         location = valueFromSideKeyword(a, isHorizontal);
6676         if (location) {
6677             if (isHorizontal) {
6678                 if (endX)
6679                     return false;
6680                 endX = location;
6681             } else {
6682                 if (endY)
6683                     return false;
6684                 endY = location;
6685             }
6686 
6687             args->next();
6688         }
6689 
6690         expectComma = true;
6691         result->setFirstX(endX.release());
6692         result->setFirstY(endY.release());
6693     }
6694 
6695     if (!parseGradientColorStops(args, result.get(), expectComma))
6696         return false;
6697 
6698     if (!result->stopCount())
6699         return false;
6700 
6701     gradient = result.release();
6702     return true;
6703 }
6704 
parseRadialGradient(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & gradient,CSSGradientRepeat repeating)6705 bool CSSPropertyParser::parseRadialGradient(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& gradient, CSSGradientRepeat repeating)
6706 {
6707     RefPtrWillBeRawPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient);
6708 
6709     CSSParserValueList* args = valueList->current()->function->args.get();
6710     if (!args || !args->size())
6711         return false;
6712 
6713     CSSParserValue* a = args->current();
6714     if (!a)
6715         return false;
6716 
6717     bool expectComma = false;
6718 
6719     RefPtrWillBeRawPtr<CSSPrimitiveValue> shapeValue = nullptr;
6720     RefPtrWillBeRawPtr<CSSPrimitiveValue> sizeValue = nullptr;
6721     RefPtrWillBeRawPtr<CSSPrimitiveValue> horizontalSize = nullptr;
6722     RefPtrWillBeRawPtr<CSSPrimitiveValue> verticalSize = nullptr;
6723 
6724     // First part of grammar, the size/shape clause:
6725     // [ circle || <length> ] |
6726     // [ ellipse || [ <length> | <percentage> ]{2} ] |
6727     // [ [ circle | ellipse] || <size-keyword> ]
6728     for (int i = 0; i < 3; ++i) {
6729         if (a->unit == CSSPrimitiveValue::CSS_IDENT) {
6730             bool badIdent = false;
6731             switch (a->id) {
6732             case CSSValueCircle:
6733             case CSSValueEllipse:
6734                 if (shapeValue)
6735                     return false;
6736                 shapeValue = cssValuePool().createIdentifierValue(a->id);
6737                 break;
6738             case CSSValueClosestSide:
6739             case CSSValueClosestCorner:
6740             case CSSValueFarthestSide:
6741             case CSSValueFarthestCorner:
6742                 if (sizeValue || horizontalSize)
6743                     return false;
6744                 sizeValue = cssValuePool().createIdentifierValue(a->id);
6745                 break;
6746             default:
6747                 badIdent = true;
6748             }
6749 
6750             if (badIdent)
6751                 break;
6752 
6753             a = args->next();
6754             if (!a)
6755                 return false;
6756         } else if (validUnit(a, FLength | FPercent)) {
6757 
6758             if (sizeValue || horizontalSize)
6759                 return false;
6760             horizontalSize = createPrimitiveNumericValue(a);
6761 
6762             a = args->next();
6763             if (!a)
6764                 return false;
6765 
6766             if (validUnit(a, FLength | FPercent)) {
6767                 verticalSize = createPrimitiveNumericValue(a);
6768                 ++i;
6769                 a = args->next();
6770                 if (!a)
6771                     return false;
6772             }
6773         } else
6774             break;
6775     }
6776 
6777     // You can specify size as a keyword or a length/percentage, not both.
6778     if (sizeValue && horizontalSize)
6779         return false;
6780     // Circles must have 0 or 1 lengths.
6781     if (shapeValue && shapeValue->getValueID() == CSSValueCircle && verticalSize)
6782         return false;
6783     // Ellipses must have 0 or 2 length/percentages.
6784     if (shapeValue && shapeValue->getValueID() == CSSValueEllipse && horizontalSize && !verticalSize)
6785         return false;
6786     // If there's only one size, it must be a length.
6787     if (!verticalSize && horizontalSize && horizontalSize->isPercentage())
6788         return false;
6789 
6790     result->setShape(shapeValue);
6791     result->setSizingBehavior(sizeValue);
6792     result->setEndHorizontalSize(horizontalSize);
6793     result->setEndVerticalSize(verticalSize);
6794 
6795     // Second part of grammar, the center-position clause:
6796     // at <position>
6797     RefPtrWillBeRawPtr<CSSValue> centerX = nullptr;
6798     RefPtrWillBeRawPtr<CSSValue> centerY = nullptr;
6799     if (a->unit == CSSPrimitiveValue::CSS_IDENT && a->id == CSSValueAt) {
6800         a = args->next();
6801         if (!a)
6802             return false;
6803 
6804         parseFillPosition(args, centerX, centerY);
6805         if (!(centerX && centerY))
6806             return false;
6807 
6808         a = args->current();
6809         if (!a)
6810             return false;
6811         result->setFirstX(toCSSPrimitiveValue(centerX.get()));
6812         result->setFirstY(toCSSPrimitiveValue(centerY.get()));
6813         // Right now, CSS radial gradients have the same start and end centers.
6814         result->setSecondX(toCSSPrimitiveValue(centerX.get()));
6815         result->setSecondY(toCSSPrimitiveValue(centerY.get()));
6816     }
6817 
6818     if (shapeValue || sizeValue || horizontalSize || centerX || centerY)
6819         expectComma = true;
6820 
6821     if (!parseGradientColorStops(args, result.get(), expectComma))
6822         return false;
6823 
6824     gradient = result.release();
6825     return true;
6826 }
6827 
parseGradientColorStops(CSSParserValueList * valueList,CSSGradientValue * gradient,bool expectComma)6828 bool CSSPropertyParser::parseGradientColorStops(CSSParserValueList* valueList, CSSGradientValue* gradient, bool expectComma)
6829 {
6830     CSSParserValue* a = valueList->current();
6831 
6832     // Now look for color stops.
6833     while (a) {
6834         // Look for the comma before the next stop.
6835         if (expectComma) {
6836             if (!isComma(a))
6837                 return false;
6838 
6839             a = valueList->next();
6840             if (!a)
6841                 return false;
6842         }
6843 
6844         // <color-stop> = <color> [ <percentage> | <length> ]?
6845         CSSGradientColorStop stop;
6846         stop.m_color = parseGradientColorOrKeyword(this, a);
6847         if (!stop.m_color)
6848             return false;
6849 
6850         a = valueList->next();
6851         if (a) {
6852             if (validUnit(a, FLength | FPercent)) {
6853                 stop.m_position = createPrimitiveNumericValue(a);
6854                 a = valueList->next();
6855             }
6856         }
6857 
6858         gradient->addStop(stop);
6859         expectComma = true;
6860     }
6861 
6862     // Must have 2 or more stops to be valid.
6863     return gradient->stopCount() >= 2;
6864 }
6865 
parseGeneratedImage(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & value)6866 bool CSSPropertyParser::parseGeneratedImage(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& value)
6867 {
6868     CSSParserValue* val = valueList->current();
6869 
6870     if (val->unit != CSSParserValue::Function)
6871         return false;
6872 
6873     if (equalIgnoringCase(val->function->name, "-webkit-gradient")) {
6874         // FIXME: This should send a deprecation message.
6875         if (m_context.useCounter())
6876             m_context.useCounter()->count(UseCounter::DeprecatedWebKitGradient);
6877         return parseDeprecatedGradient(valueList, value);
6878     }
6879 
6880     if (equalIgnoringCase(val->function->name, "-webkit-linear-gradient")) {
6881         // FIXME: This should send a deprecation message.
6882         if (m_context.useCounter())
6883             m_context.useCounter()->count(UseCounter::DeprecatedWebKitLinearGradient);
6884         return parseDeprecatedLinearGradient(valueList, value, NonRepeating);
6885     }
6886 
6887     if (equalIgnoringCase(val->function->name, "linear-gradient"))
6888         return parseLinearGradient(valueList, value, NonRepeating);
6889 
6890     if (equalIgnoringCase(val->function->name, "-webkit-repeating-linear-gradient")) {
6891         // FIXME: This should send a deprecation message.
6892         if (m_context.useCounter())
6893             m_context.useCounter()->count(UseCounter::DeprecatedWebKitRepeatingLinearGradient);
6894         return parseDeprecatedLinearGradient(valueList, value, Repeating);
6895     }
6896 
6897     if (equalIgnoringCase(val->function->name, "repeating-linear-gradient"))
6898         return parseLinearGradient(valueList, value, Repeating);
6899 
6900     if (equalIgnoringCase(val->function->name, "-webkit-radial-gradient")) {
6901         // FIXME: This should send a deprecation message.
6902         if (m_context.useCounter())
6903             m_context.useCounter()->count(UseCounter::DeprecatedWebKitRadialGradient);
6904         return parseDeprecatedRadialGradient(valueList, value, NonRepeating);
6905     }
6906 
6907     if (equalIgnoringCase(val->function->name, "radial-gradient"))
6908         return parseRadialGradient(valueList, value, NonRepeating);
6909 
6910     if (equalIgnoringCase(val->function->name, "-webkit-repeating-radial-gradient")) {
6911         if (m_context.useCounter())
6912             m_context.useCounter()->count(UseCounter::DeprecatedWebKitRepeatingRadialGradient);
6913         return parseDeprecatedRadialGradient(valueList, value, Repeating);
6914     }
6915 
6916     if (equalIgnoringCase(val->function->name, "repeating-radial-gradient"))
6917         return parseRadialGradient(valueList, value, Repeating);
6918 
6919     if (equalIgnoringCase(val->function->name, "-webkit-canvas"))
6920         return parseCanvas(valueList, value);
6921 
6922     if (equalIgnoringCase(val->function->name, "-webkit-cross-fade"))
6923         return parseCrossfade(valueList, value);
6924 
6925     return false;
6926 }
6927 
parseCrossfade(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & crossfade)6928 bool CSSPropertyParser::parseCrossfade(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& crossfade)
6929 {
6930     // Walk the arguments.
6931     CSSParserValueList* args = valueList->current()->function->args.get();
6932     if (!args || args->size() != 5)
6933         return false;
6934     RefPtrWillBeRawPtr<CSSValue> fromImageValue = nullptr;
6935     RefPtrWillBeRawPtr<CSSValue> toImageValue = nullptr;
6936 
6937     // The first argument is the "from" image. It is a fill image.
6938     if (!args->current() || !parseFillImage(args, fromImageValue))
6939         return false;
6940     args->next();
6941 
6942     if (!consumeComma(args))
6943         return false;
6944 
6945     // The second argument is the "to" image. It is a fill image.
6946     if (!args->current() || !parseFillImage(args, toImageValue))
6947         return false;
6948     args->next();
6949 
6950     if (!consumeComma(args))
6951         return false;
6952 
6953     // The third argument is the crossfade value. It is a percentage or a fractional number.
6954     RefPtrWillBeRawPtr<CSSPrimitiveValue> percentage = nullptr;
6955     CSSParserValue* value = args->current();
6956     if (!value)
6957         return false;
6958 
6959     if (value->unit == CSSPrimitiveValue::CSS_PERCENTAGE)
6960         percentage = cssValuePool().createValue(clampTo<double>(value->fValue / 100, 0, 1), CSSPrimitiveValue::CSS_NUMBER);
6961     else if (value->unit == CSSPrimitiveValue::CSS_NUMBER)
6962         percentage = cssValuePool().createValue(clampTo<double>(value->fValue, 0, 1), CSSPrimitiveValue::CSS_NUMBER);
6963     else
6964         return false;
6965 
6966     RefPtrWillBeRawPtr<CSSCrossfadeValue> result = CSSCrossfadeValue::create(fromImageValue, toImageValue);
6967     result->setPercentage(percentage);
6968 
6969     crossfade = result;
6970 
6971     return true;
6972 }
6973 
parseCanvas(CSSParserValueList * valueList,RefPtrWillBeRawPtr<CSSValue> & canvas)6974 bool CSSPropertyParser::parseCanvas(CSSParserValueList* valueList, RefPtrWillBeRawPtr<CSSValue>& canvas)
6975 {
6976     // Walk the arguments.
6977     CSSParserValueList* args = valueList->current()->function->args.get();
6978     if (!args || args->size() != 1)
6979         return false;
6980 
6981     // The first argument is the canvas name.  It is an identifier.
6982     CSSParserValue* value = args->current();
6983     if (!value || value->unit != CSSPrimitiveValue::CSS_IDENT)
6984         return false;
6985 
6986     canvas = CSSCanvasValue::create(value->string);
6987     return true;
6988 }
6989 
parseImageSet(CSSParserValueList * valueList)6990 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseImageSet(CSSParserValueList* valueList)
6991 {
6992     CSSParserValue* function = valueList->current();
6993 
6994     if (function->unit != CSSParserValue::Function)
6995         return nullptr;
6996 
6997     CSSParserValueList* functionArgs = valueList->current()->function->args.get();
6998     if (!functionArgs || !functionArgs->size() || !functionArgs->current())
6999         return nullptr;
7000 
7001     RefPtrWillBeRawPtr<CSSImageSetValue> imageSet = CSSImageSetValue::create();
7002 
7003     while (functionArgs->current()) {
7004         CSSParserValue* arg = functionArgs->current();
7005         if (arg->unit != CSSPrimitiveValue::CSS_URI)
7006             return nullptr;
7007 
7008         RefPtrWillBeRawPtr<CSSValue> image = createCSSImageValueWithReferrer(arg->string, completeURL(arg->string));
7009         imageSet->append(image);
7010 
7011         arg = functionArgs->next();
7012         if (!arg || arg->unit != CSSPrimitiveValue::CSS_DIMENSION)
7013             return nullptr;
7014 
7015         double imageScaleFactor = 0;
7016         const String& string = arg->string;
7017         unsigned length = string.length();
7018         if (!length)
7019             return nullptr;
7020         if (string.is8Bit()) {
7021             const LChar* start = string.characters8();
7022             parseDouble(start, start + length, 'x', imageScaleFactor);
7023         } else {
7024             const UChar* start = string.characters16();
7025             parseDouble(start, start + length, 'x', imageScaleFactor);
7026         }
7027         if (imageScaleFactor <= 0)
7028             return nullptr;
7029         imageSet->append(cssValuePool().createValue(imageScaleFactor, CSSPrimitiveValue::CSS_NUMBER));
7030         functionArgs->next();
7031 
7032         // If there are no more arguments, we're done.
7033         if (!functionArgs->current())
7034             break;
7035 
7036         // If there are more arguments, they should be after a comma.
7037         if (!consumeComma(functionArgs))
7038             return nullptr;
7039     }
7040 
7041     return imageSet.release();
7042 }
7043 
parseWillChange()7044 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseWillChange()
7045 {
7046     RefPtrWillBeRawPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
7047     if (m_valueList->current()->id == CSSValueAuto) {
7048         // FIXME: This will be read back as an empty string instead of auto
7049         return values.release();
7050     }
7051 
7052     // Every comma-separated list of CSS_IDENTs is a valid will-change value,
7053     // unless the list includes an explicitly disallowed CSS_IDENT.
7054     while (true) {
7055         CSSParserValue* currentValue = m_valueList->current();
7056         if (!currentValue || currentValue->unit != CSSPrimitiveValue::CSS_IDENT)
7057             return nullptr;
7058 
7059         CSSPropertyID property = cssPropertyID(currentValue->string);
7060         if (property) {
7061             ASSERT(CSSPropertyMetadata::isEnabledProperty(property));
7062             // Now "all" is used by both CSSValue and CSSPropertyValue.
7063             // Need to return nullptr when currentValue is CSSPropertyAll.
7064             if (property == CSSPropertyWillChange || property == CSSPropertyAll)
7065                 return nullptr;
7066             values->append(cssValuePool().createIdentifierValue(property));
7067         } else {
7068             switch (currentValue->id) {
7069             case CSSValueNone:
7070             case CSSValueAll:
7071             case CSSValueAuto:
7072             case CSSValueDefault:
7073             case CSSValueInitial:
7074             case CSSValueInherit:
7075                 return nullptr;
7076             case CSSValueContents:
7077             case CSSValueScrollPosition:
7078                 values->append(cssValuePool().createIdentifierValue(currentValue->id));
7079                 break;
7080             default:
7081                 break;
7082             }
7083         }
7084 
7085         if (!m_valueList->next())
7086             break;
7087         if (!consumeComma(m_valueList))
7088             return nullptr;
7089     }
7090 
7091     return values.release();
7092 }
7093 
filterInfoForName(const CSSParserString & name,CSSFilterValue::FilterOperationType & filterType,unsigned & maximumArgumentCount)7094 static void filterInfoForName(const CSSParserString& name, CSSFilterValue::FilterOperationType& filterType, unsigned& maximumArgumentCount)
7095 {
7096     if (equalIgnoringCase(name, "grayscale"))
7097         filterType = CSSFilterValue::GrayscaleFilterOperation;
7098     else if (equalIgnoringCase(name, "sepia"))
7099         filterType = CSSFilterValue::SepiaFilterOperation;
7100     else if (equalIgnoringCase(name, "saturate"))
7101         filterType = CSSFilterValue::SaturateFilterOperation;
7102     else if (equalIgnoringCase(name, "hue-rotate"))
7103         filterType = CSSFilterValue::HueRotateFilterOperation;
7104     else if (equalIgnoringCase(name, "invert"))
7105         filterType = CSSFilterValue::InvertFilterOperation;
7106     else if (equalIgnoringCase(name, "opacity"))
7107         filterType = CSSFilterValue::OpacityFilterOperation;
7108     else if (equalIgnoringCase(name, "brightness"))
7109         filterType = CSSFilterValue::BrightnessFilterOperation;
7110     else if (equalIgnoringCase(name, "contrast"))
7111         filterType = CSSFilterValue::ContrastFilterOperation;
7112     else if (equalIgnoringCase(name, "blur"))
7113         filterType = CSSFilterValue::BlurFilterOperation;
7114     else if (equalIgnoringCase(name, "drop-shadow")) {
7115         filterType = CSSFilterValue::DropShadowFilterOperation;
7116         maximumArgumentCount = 4;  // x-offset, y-offset, blur-radius, color -- spread and inset style not allowed.
7117     }
7118 }
7119 
parseBuiltinFilterArguments(CSSParserValueList * args,CSSFilterValue::FilterOperationType filterType)7120 PassRefPtrWillBeRawPtr<CSSFilterValue> CSSPropertyParser::parseBuiltinFilterArguments(CSSParserValueList* args, CSSFilterValue::FilterOperationType filterType)
7121 {
7122     RefPtrWillBeRawPtr<CSSFilterValue> filterValue = CSSFilterValue::create(filterType);
7123     ASSERT(args);
7124 
7125     switch (filterType) {
7126     case CSSFilterValue::GrayscaleFilterOperation:
7127     case CSSFilterValue::SepiaFilterOperation:
7128     case CSSFilterValue::SaturateFilterOperation:
7129     case CSSFilterValue::InvertFilterOperation:
7130     case CSSFilterValue::OpacityFilterOperation:
7131     case CSSFilterValue::ContrastFilterOperation: {
7132         // One optional argument, 0-1 or 0%-100%, if missing use 100%.
7133         if (args->size()) {
7134             CSSParserValue* value = args->current();
7135             // FIXME (crbug.com/397061): Support calc expressions like calc(10% + 0.5)
7136             if (value->unit != CSSPrimitiveValue::CSS_PERCENTAGE && !validUnit(value, FNumber | FNonNeg))
7137                 return nullptr;
7138 
7139             double amount = value->fValue;
7140             if (amount < 0)
7141                 return nullptr;
7142 
7143             // Saturate and Contrast allow values over 100%.
7144             if (filterType != CSSFilterValue::SaturateFilterOperation
7145                 && filterType != CSSFilterValue::ContrastFilterOperation) {
7146                 double maxAllowed = value->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 100.0 : 1.0;
7147                 if (amount > maxAllowed)
7148                     return nullptr;
7149             }
7150 
7151             filterValue->append(cssValuePool().createValue(amount, static_cast<CSSPrimitiveValue::UnitType>(value->unit)));
7152         }
7153         break;
7154     }
7155     case CSSFilterValue::BrightnessFilterOperation: {
7156         // One optional argument, if missing use 100%.
7157         if (args->size()) {
7158             CSSParserValue* value = args->current();
7159             // FIXME (crbug.com/397061): Support calc expressions like calc(10% + 0.5)
7160             if (value->unit != CSSPrimitiveValue::CSS_PERCENTAGE && !validUnit(value, FNumber))
7161                 return nullptr;
7162 
7163             filterValue->append(cssValuePool().createValue(value->fValue, static_cast<CSSPrimitiveValue::UnitType>(value->unit)));
7164         }
7165         break;
7166     }
7167     case CSSFilterValue::HueRotateFilterOperation: {
7168         // hue-rotate() takes one optional angle.
7169         if (args->size()) {
7170             CSSParserValue* argument = args->current();
7171             if (!validUnit(argument, FAngle, HTMLStandardMode))
7172                 return nullptr;
7173 
7174             filterValue->append(createPrimitiveNumericValue(argument));
7175         }
7176         break;
7177     }
7178     case CSSFilterValue::BlurFilterOperation: {
7179         // Blur takes a single length. Zero parameters are allowed.
7180         if (args->size()) {
7181             CSSParserValue* argument = args->current();
7182             if (!validUnit(argument, FLength | FNonNeg, HTMLStandardMode))
7183                 return nullptr;
7184 
7185             filterValue->append(createPrimitiveNumericValue(argument));
7186         }
7187         break;
7188     }
7189     case CSSFilterValue::DropShadowFilterOperation: {
7190         // drop-shadow() takes a single shadow.
7191         RefPtrWillBeRawPtr<CSSValueList> shadowValueList = parseShadow(args, CSSPropertyWebkitFilter);
7192         if (!shadowValueList || shadowValueList->length() != 1)
7193             return nullptr;
7194 
7195         filterValue->append((shadowValueList.release())->item(0));
7196         break;
7197     }
7198     default:
7199         ASSERT_NOT_REACHED();
7200     }
7201     return filterValue.release();
7202 }
7203 
parseFilter()7204 PassRefPtrWillBeRawPtr<CSSValueList> CSSPropertyParser::parseFilter()
7205 {
7206     if (!m_valueList)
7207         return nullptr;
7208 
7209     // The filter is a list of functional primitives that specify individual operations.
7210     RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
7211     for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) {
7212         if (value->unit != CSSPrimitiveValue::CSS_URI && (value->unit != CSSParserValue::Function || !value->function))
7213             return nullptr;
7214 
7215         CSSFilterValue::FilterOperationType filterType = CSSFilterValue::UnknownFilterOperation;
7216 
7217         // See if the specified primitive is one we understand.
7218         if (value->unit == CSSPrimitiveValue::CSS_URI) {
7219             RefPtrWillBeRawPtr<CSSFilterValue> referenceFilterValue = CSSFilterValue::create(CSSFilterValue::ReferenceFilterOperation);
7220             list->append(referenceFilterValue);
7221             referenceFilterValue->append(CSSSVGDocumentValue::create(value->string));
7222         } else {
7223             const CSSParserString name = value->function->name;
7224             unsigned maximumArgumentCount = 1;
7225 
7226             filterInfoForName(name, filterType, maximumArgumentCount);
7227 
7228             if (filterType == CSSFilterValue::UnknownFilterOperation)
7229                 return nullptr;
7230 
7231             CSSParserValueList* args = value->function->args.get();
7232             if (!args || args->size() > maximumArgumentCount)
7233                 return nullptr;
7234 
7235             RefPtrWillBeRawPtr<CSSFilterValue> filterValue = parseBuiltinFilterArguments(args, filterType);
7236             if (!filterValue)
7237                 return nullptr;
7238 
7239             list->append(filterValue);
7240         }
7241     }
7242 
7243     return list.release();
7244 }
parseTransformOrigin()7245 PassRefPtrWillBeRawPtr<CSSValueList> CSSPropertyParser::parseTransformOrigin()
7246 {
7247     CSSParserValue* value = m_valueList->current();
7248     CSSValueID id = value->id;
7249     RefPtrWillBeRawPtr<CSSValue> xValue = nullptr;
7250     RefPtrWillBeRawPtr<CSSValue> yValue = nullptr;
7251     RefPtrWillBeRawPtr<CSSValue> zValue = nullptr;
7252     if (id == CSSValueLeft || id == CSSValueRight) {
7253         xValue = cssValuePool().createIdentifierValue(id);
7254     } else if (id == CSSValueTop || id == CSSValueBottom) {
7255         yValue = cssValuePool().createIdentifierValue(id);
7256     } else if (id == CSSValueCenter) {
7257         // Unresolved as to whether this is X or Y.
7258     } else if (validUnit(value, FPercent | FLength)) {
7259         xValue = createPrimitiveNumericValue(value);
7260     } else {
7261         return nullptr;
7262     }
7263 
7264     value = m_valueList->next();
7265     if (value) {
7266         id = value->id;
7267         if (!xValue && (id == CSSValueLeft || id == CSSValueRight)) {
7268             xValue = cssValuePool().createIdentifierValue(id);
7269         } else if (!yValue && (id == CSSValueTop || id == CSSValueBottom)) {
7270             yValue = cssValuePool().createIdentifierValue(id);
7271         } else if (id == CSSValueCenter) {
7272             // Resolved below.
7273         } else if (!yValue && validUnit(value, FPercent | FLength)) {
7274             yValue = createPrimitiveNumericValue(value);
7275         } else {
7276             return nullptr;
7277         }
7278 
7279         // If X or Y have not been resolved, they must be center.
7280         if (!xValue)
7281             xValue = cssValuePool().createIdentifierValue(CSSValueCenter);
7282         if (!yValue)
7283             yValue = cssValuePool().createIdentifierValue(CSSValueCenter);
7284 
7285         value = m_valueList->next();
7286         if (value) {
7287             if (!validUnit(value, FLength))
7288                 return nullptr;
7289             zValue = createPrimitiveNumericValue(value);
7290 
7291             value = m_valueList->next();
7292             if (value)
7293                 return nullptr;
7294         }
7295     } else if (!xValue) {
7296         if (yValue) {
7297             xValue = cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE);
7298         } else {
7299             xValue = cssValuePool().createIdentifierValue(CSSValueCenter);
7300         }
7301     }
7302 
7303     RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
7304     list->append(xValue.release());
7305     if (yValue)
7306         list->append(yValue.release());
7307     if (zValue)
7308         list->append(zValue.release());
7309     return list.release();
7310 }
7311 
parseTouchAction()7312 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseTouchAction()
7313 {
7314     CSSParserValue* value = m_valueList->current();
7315     RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
7316     if (m_valueList->size() == 1 && value && (value->id == CSSValueAuto || value->id == CSSValueNone || value->id == CSSValueManipulation)) {
7317         list->append(cssValuePool().createIdentifierValue(value->id));
7318         m_valueList->next();
7319         return list.release();
7320     }
7321 
7322     while (value) {
7323         switch (value->id) {
7324         case CSSValuePanX:
7325         case CSSValuePanY: {
7326             RefPtrWillBeRawPtr<CSSValue> panValue = cssValuePool().createIdentifierValue(value->id);
7327             if (list->hasValue(panValue.get()))
7328                 return nullptr;
7329             list->append(panValue.release());
7330             break;
7331         }
7332         default:
7333             return nullptr;
7334         }
7335         value = m_valueList->next();
7336     }
7337 
7338     if (list->length())
7339         return list.release();
7340 
7341     return nullptr;
7342 }
7343 
addTextDecorationProperty(CSSPropertyID propId,PassRefPtrWillBeRawPtr<CSSValue> value,bool important)7344 void CSSPropertyParser::addTextDecorationProperty(CSSPropertyID propId, PassRefPtrWillBeRawPtr<CSSValue> value, bool important)
7345 {
7346     // The text-decoration-line property takes priority over text-decoration, unless the latter has important priority set.
7347     if (propId == CSSPropertyTextDecoration && !important && !inShorthand()) {
7348         for (unsigned i = 0; i < m_parsedProperties.size(); ++i) {
7349             if (m_parsedProperties[i].id() == CSSPropertyTextDecorationLine)
7350                 return;
7351         }
7352     }
7353     addProperty(propId, value, important);
7354 }
7355 
parseTextDecoration(CSSPropertyID propId,bool important)7356 bool CSSPropertyParser::parseTextDecoration(CSSPropertyID propId, bool important)
7357 {
7358     ASSERT(propId != CSSPropertyTextDecorationLine || RuntimeEnabledFeatures::css3TextDecorationsEnabled());
7359 
7360     CSSParserValue* value = m_valueList->current();
7361     if (value && value->id == CSSValueNone) {
7362         addTextDecorationProperty(propId, cssValuePool().createIdentifierValue(CSSValueNone), important);
7363         m_valueList->next();
7364         return true;
7365     }
7366 
7367     RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
7368     bool isValid = true;
7369     while (isValid && value) {
7370         switch (value->id) {
7371         case CSSValueUnderline:
7372         case CSSValueOverline:
7373         case CSSValueLineThrough:
7374         case CSSValueBlink:
7375             list->append(cssValuePool().createIdentifierValue(value->id));
7376             break;
7377         default:
7378             isValid = false;
7379             break;
7380         }
7381         if (isValid)
7382             value = m_valueList->next();
7383     }
7384 
7385     // Values are either valid or in shorthand scope.
7386     if (list->length() && (isValid || inShorthand())) {
7387         addTextDecorationProperty(propId, list.release(), important);
7388         return true;
7389     }
7390 
7391     return false;
7392 }
7393 
parseTextUnderlinePosition(bool important)7394 bool CSSPropertyParser::parseTextUnderlinePosition(bool important)
7395 {
7396     // The text-underline-position property has syntax "auto | [ under || [ left | right ] ]".
7397     // However, values 'left' and 'right' are not implemented yet, so we will parse syntax
7398     // "auto | under" for now.
7399     CSSParserValue* value = m_valueList->current();
7400     switch (value->id) {
7401     case CSSValueAuto:
7402     case CSSValueUnder:
7403         if (m_valueList->next())
7404             return false;
7405         addProperty(CSSPropertyTextUnderlinePosition, cssValuePool().createIdentifierValue(value->id), important);
7406         return true;
7407     default:
7408         return false;
7409     }
7410 }
7411 
parseTextEmphasisStyle(bool important)7412 bool CSSPropertyParser::parseTextEmphasisStyle(bool important)
7413 {
7414     unsigned valueListSize = m_valueList->size();
7415 
7416     RefPtrWillBeRawPtr<CSSPrimitiveValue> fill = nullptr;
7417     RefPtrWillBeRawPtr<CSSPrimitiveValue> shape = nullptr;
7418 
7419     for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) {
7420         if (value->unit == CSSPrimitiveValue::CSS_STRING) {
7421             if (fill || shape || (valueListSize != 1 && !inShorthand()))
7422                 return false;
7423             addProperty(CSSPropertyWebkitTextEmphasisStyle, createPrimitiveStringValue(value), important);
7424             m_valueList->next();
7425             return true;
7426         }
7427 
7428         if (value->id == CSSValueNone) {
7429             if (fill || shape || (valueListSize != 1 && !inShorthand()))
7430                 return false;
7431             addProperty(CSSPropertyWebkitTextEmphasisStyle, cssValuePool().createIdentifierValue(CSSValueNone), important);
7432             m_valueList->next();
7433             return true;
7434         }
7435 
7436         if (value->id == CSSValueOpen || value->id == CSSValueFilled) {
7437             if (fill)
7438                 return false;
7439             fill = cssValuePool().createIdentifierValue(value->id);
7440         } else if (value->id == CSSValueDot || value->id == CSSValueCircle || value->id == CSSValueDoubleCircle || value->id == CSSValueTriangle || value->id == CSSValueSesame) {
7441             if (shape)
7442                 return false;
7443             shape = cssValuePool().createIdentifierValue(value->id);
7444         } else if (!inShorthand())
7445             return false;
7446         else
7447             break;
7448     }
7449 
7450     if (fill && shape) {
7451         RefPtrWillBeRawPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
7452         parsedValues->append(fill.release());
7453         parsedValues->append(shape.release());
7454         addProperty(CSSPropertyWebkitTextEmphasisStyle, parsedValues.release(), important);
7455         return true;
7456     }
7457     if (fill) {
7458         addProperty(CSSPropertyWebkitTextEmphasisStyle, fill.release(), important);
7459         return true;
7460     }
7461     if (shape) {
7462         addProperty(CSSPropertyWebkitTextEmphasisStyle, shape.release(), important);
7463         return true;
7464     }
7465 
7466     return false;
7467 }
7468 
parseTextIndent()7469 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseTextIndent()
7470 {
7471     RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
7472 
7473     bool hasLengthOrPercentage = false;
7474     bool hasEachLine = false;
7475     bool hasHanging = false;
7476 
7477     for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) {
7478         // <length> | <percentage> | inherit when RuntimeEnabledFeatures::css3TextEnabled() returns false
7479         if (!hasLengthOrPercentage && validUnit(value, FLength | FPercent)) {
7480             list->append(createPrimitiveNumericValue(value));
7481             hasLengthOrPercentage = true;
7482             continue;
7483         }
7484 
7485         // [ <length> | <percentage> ] && hanging? && each-line? | inherit
7486         // when RuntimeEnabledFeatures::css3TextEnabled() returns true
7487         if (RuntimeEnabledFeatures::css3TextEnabled()) {
7488             if (!hasEachLine && value->id == CSSValueEachLine) {
7489                 list->append(cssValuePool().createIdentifierValue(CSSValueEachLine));
7490                 hasEachLine = true;
7491                 continue;
7492             }
7493             if (!hasHanging && value->id == CSSValueHanging) {
7494                 list->append(cssValuePool().createIdentifierValue(CSSValueHanging));
7495                 hasHanging = true;
7496                 continue;
7497             }
7498         }
7499         return nullptr;
7500     }
7501 
7502     if (!hasLengthOrPercentage)
7503         return nullptr;
7504 
7505     return list.release();
7506 }
7507 
parseLineBoxContain(bool important)7508 bool CSSPropertyParser::parseLineBoxContain(bool important)
7509 {
7510     LineBoxContain lineBoxContain = LineBoxContainNone;
7511 
7512     for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) {
7513         LineBoxContainFlags flag;
7514         if (value->id == CSSValueBlock) {
7515             flag = LineBoxContainBlock;
7516         } else if (value->id == CSSValueInline) {
7517             flag = LineBoxContainInline;
7518         } else if (value->id == CSSValueFont) {
7519             flag = LineBoxContainFont;
7520         } else if (value->id == CSSValueGlyphs) {
7521             flag = LineBoxContainGlyphs;
7522         } else if (value->id == CSSValueReplaced) {
7523             flag = LineBoxContainReplaced;
7524         } else if (value->id == CSSValueInlineBox) {
7525             flag = LineBoxContainInlineBox;
7526         } else {
7527             return false;
7528         }
7529         if (lineBoxContain & flag)
7530             return false;
7531         lineBoxContain |= flag;
7532     }
7533 
7534     if (!lineBoxContain)
7535         return false;
7536 
7537     addProperty(CSSPropertyWebkitLineBoxContain, CSSLineBoxContainValue::create(lineBoxContain), important);
7538     return true;
7539 }
7540 
parseFontFeatureTag(CSSValueList * settings)7541 bool CSSPropertyParser::parseFontFeatureTag(CSSValueList* settings)
7542 {
7543     // Feature tag name consists of 4-letter characters.
7544     static const unsigned tagNameLength = 4;
7545 
7546     CSSParserValue* value = m_valueList->current();
7547     // Feature tag name comes first
7548     if (value->unit != CSSPrimitiveValue::CSS_STRING)
7549         return false;
7550     if (value->string.length() != tagNameLength)
7551         return false;
7552     for (unsigned i = 0; i < tagNameLength; ++i) {
7553         // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
7554         UChar character = value->string[i];
7555         if (character < 0x20 || character > 0x7E)
7556             return false;
7557     }
7558 
7559     AtomicString tag = value->string;
7560     int tagValue = 1;
7561     // Feature tag values could follow: <integer> | on | off
7562     value = m_valueList->next();
7563     if (value) {
7564         if (value->unit == CSSPrimitiveValue::CSS_NUMBER && value->isInt && value->fValue >= 0) {
7565             tagValue = clampToInteger(value->fValue);
7566             if (tagValue < 0)
7567                 return false;
7568             m_valueList->next();
7569         } else if (value->id == CSSValueOn || value->id == CSSValueOff) {
7570             tagValue = value->id == CSSValueOn;
7571             m_valueList->next();
7572         }
7573     }
7574     settings->append(CSSFontFeatureValue::create(tag, tagValue));
7575     return true;
7576 }
7577 
parseFontFeatureSettings(bool important)7578 bool CSSPropertyParser::parseFontFeatureSettings(bool important)
7579 {
7580     if (m_valueList->size() == 1 && m_valueList->current()->id == CSSValueNormal) {
7581         RefPtrWillBeRawPtr<CSSPrimitiveValue> normalValue = cssValuePool().createIdentifierValue(CSSValueNormal);
7582         m_valueList->next();
7583         addProperty(CSSPropertyWebkitFontFeatureSettings, normalValue.release(), important);
7584         return true;
7585     }
7586 
7587     RefPtrWillBeRawPtr<CSSValueList> settings = CSSValueList::createCommaSeparated();
7588     while (true) {
7589         if (!m_valueList->current() || !parseFontFeatureTag(settings.get()))
7590             return false;
7591         if (!m_valueList->current())
7592             break;
7593         if (!consumeComma(m_valueList))
7594             return false;
7595     }
7596     addProperty(CSSPropertyWebkitFontFeatureSettings, settings.release(), important);
7597     return true;
7598 }
7599 
parseFontVariantLigatures(bool important)7600 bool CSSPropertyParser::parseFontVariantLigatures(bool important)
7601 {
7602     RefPtrWillBeRawPtr<CSSValueList> ligatureValues = CSSValueList::createSpaceSeparated();
7603     bool sawCommonLigaturesValue = false;
7604     bool sawDiscretionaryLigaturesValue = false;
7605     bool sawHistoricalLigaturesValue = false;
7606     bool sawContextualLigaturesValue = false;
7607 
7608     for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) {
7609         if (value->unit != CSSPrimitiveValue::CSS_IDENT)
7610             return false;
7611 
7612         switch (value->id) {
7613         case CSSValueNoCommonLigatures:
7614         case CSSValueCommonLigatures:
7615             if (sawCommonLigaturesValue)
7616                 return false;
7617             sawCommonLigaturesValue = true;
7618             ligatureValues->append(cssValuePool().createIdentifierValue(value->id));
7619             break;
7620         case CSSValueNoDiscretionaryLigatures:
7621         case CSSValueDiscretionaryLigatures:
7622             if (sawDiscretionaryLigaturesValue)
7623                 return false;
7624             sawDiscretionaryLigaturesValue = true;
7625             ligatureValues->append(cssValuePool().createIdentifierValue(value->id));
7626             break;
7627         case CSSValueNoHistoricalLigatures:
7628         case CSSValueHistoricalLigatures:
7629             if (sawHistoricalLigaturesValue)
7630                 return false;
7631             sawHistoricalLigaturesValue = true;
7632             ligatureValues->append(cssValuePool().createIdentifierValue(value->id));
7633             break;
7634         case CSSValueNoContextual:
7635         case CSSValueContextual:
7636             if (sawContextualLigaturesValue)
7637                 return false;
7638             sawContextualLigaturesValue = true;
7639             ligatureValues->append(cssValuePool().createIdentifierValue(value->id));
7640             break;
7641         default:
7642             return false;
7643         }
7644     }
7645 
7646     if (!ligatureValues->length())
7647         return false;
7648 
7649     addProperty(CSSPropertyFontVariantLigatures, ligatureValues.release(), important);
7650     return true;
7651 }
7652 
parseCalculation(CSSParserValue * value,ValueRange range)7653 bool CSSPropertyParser::parseCalculation(CSSParserValue* value, ValueRange range)
7654 {
7655     ASSERT(isCalculation(value));
7656 
7657     CSSParserValueList* args = value->function->args.get();
7658     if (!args || !args->size())
7659         return false;
7660 
7661     ASSERT(!m_parsedCalculation);
7662     m_parsedCalculation = CSSCalcValue::create(value->function->name, args, range);
7663 
7664     if (!m_parsedCalculation)
7665         return false;
7666 
7667     return true;
7668 }
7669 
parseViewportProperty(CSSPropertyID propId,bool important)7670 bool CSSPropertyParser::parseViewportProperty(CSSPropertyID propId, bool important)
7671 {
7672     ASSERT(RuntimeEnabledFeatures::cssViewportEnabled() || isUASheetBehavior(m_context.mode()));
7673 
7674     CSSParserValue* value = m_valueList->current();
7675     if (!value)
7676         return false;
7677 
7678     CSSValueID id = value->id;
7679     bool validPrimitive = false;
7680 
7681     switch (propId) {
7682     case CSSPropertyMinWidth: // auto | extend-to-zoom | <length> | <percentage>
7683     case CSSPropertyMaxWidth:
7684     case CSSPropertyMinHeight:
7685     case CSSPropertyMaxHeight:
7686         if (id == CSSValueAuto || id == CSSValueInternalExtendToZoom)
7687             validPrimitive = true;
7688         else
7689             validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg));
7690         break;
7691     case CSSPropertyWidth: // shorthand
7692         return parseViewportShorthand(propId, CSSPropertyMinWidth, CSSPropertyMaxWidth, important);
7693     case CSSPropertyHeight:
7694         return parseViewportShorthand(propId, CSSPropertyMinHeight, CSSPropertyMaxHeight, important);
7695     case CSSPropertyMinZoom: // auto | <number> | <percentage>
7696     case CSSPropertyMaxZoom:
7697     case CSSPropertyZoom:
7698         if (id == CSSValueAuto)
7699             validPrimitive = true;
7700         else
7701             validPrimitive = (!id && validUnit(value, FNumber | FPercent | FNonNeg));
7702         break;
7703     case CSSPropertyUserZoom: // zoom | fixed
7704         if (id == CSSValueZoom || id == CSSValueFixed)
7705             validPrimitive = true;
7706         break;
7707     case CSSPropertyOrientation: // auto | portrait | landscape
7708         if (id == CSSValueAuto || id == CSSValuePortrait || id == CSSValueLandscape)
7709             validPrimitive = true;
7710     default:
7711         break;
7712     }
7713 
7714     RefPtrWillBeRawPtr<CSSValue> parsedValue = nullptr;
7715     if (validPrimitive) {
7716         parsedValue = parseValidPrimitive(id, value);
7717         m_valueList->next();
7718     }
7719 
7720     if (parsedValue) {
7721         if (!m_valueList->current() || inShorthand()) {
7722             addProperty(propId, parsedValue.release(), important);
7723             return true;
7724         }
7725     }
7726 
7727     return false;
7728 }
7729 
parseViewportShorthand(CSSPropertyID propId,CSSPropertyID first,CSSPropertyID second,bool important)7730 bool CSSPropertyParser::parseViewportShorthand(CSSPropertyID propId, CSSPropertyID first, CSSPropertyID second, bool important)
7731 {
7732     ASSERT(RuntimeEnabledFeatures::cssViewportEnabled() || isUASheetBehavior(m_context.mode()));
7733     unsigned numValues = m_valueList->size();
7734 
7735     if (numValues > 2)
7736         return false;
7737 
7738     ShorthandScope scope(this, propId);
7739 
7740     if (!parseViewportProperty(first, important))
7741         return false;
7742 
7743     // If just one value is supplied, the second value
7744     // is implicitly initialized with the first value.
7745     if (numValues == 1)
7746         m_valueList->previous();
7747 
7748     return parseViewportProperty(second, important);
7749 }
7750 
7751 template <typename CharacterType>
cssPropertyID(const CharacterType * propertyName,unsigned length)7752 static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length)
7753 {
7754     char buffer[maxCSSPropertyNameLength + 1]; // 1 for null character
7755 
7756     for (unsigned i = 0; i != length; ++i) {
7757         CharacterType c = propertyName[i];
7758         if (c == 0 || c >= 0x7F)
7759             return CSSPropertyInvalid; // illegal character
7760         buffer[i] = toASCIILower(c);
7761     }
7762     buffer[length] = '\0';
7763 
7764     const char* name = buffer;
7765     const Property* hashTableEntry = findProperty(name, length);
7766     if (!hashTableEntry)
7767         return CSSPropertyInvalid;
7768     CSSPropertyID property = static_cast<CSSPropertyID>(hashTableEntry->id);
7769     if (!CSSPropertyMetadata::isEnabledProperty(property))
7770         return CSSPropertyInvalid;
7771     return property;
7772 }
7773 
cssPropertyID(const String & string)7774 CSSPropertyID cssPropertyID(const String& string)
7775 {
7776     unsigned length = string.length();
7777 
7778     if (!length)
7779         return CSSPropertyInvalid;
7780     if (length > maxCSSPropertyNameLength)
7781         return CSSPropertyInvalid;
7782 
7783     return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length);
7784 }
7785 
cssPropertyID(const CSSParserString & string)7786 CSSPropertyID cssPropertyID(const CSSParserString& string)
7787 {
7788     unsigned length = string.length();
7789 
7790     if (!length)
7791         return CSSPropertyInvalid;
7792     if (length > maxCSSPropertyNameLength)
7793         return CSSPropertyInvalid;
7794 
7795     return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length);
7796 }
7797 
7798 template <typename CharacterType>
cssValueKeywordID(const CharacterType * valueKeyword,unsigned length)7799 static CSSValueID cssValueKeywordID(const CharacterType* valueKeyword, unsigned length)
7800 {
7801     char buffer[maxCSSValueKeywordLength + 1]; // 1 for null character
7802 
7803     for (unsigned i = 0; i != length; ++i) {
7804         CharacterType c = valueKeyword[i];
7805         if (c == 0 || c >= 0x7F)
7806             return CSSValueInvalid; // illegal character
7807         buffer[i] = WTF::toASCIILower(c);
7808     }
7809     buffer[length] = '\0';
7810 
7811     const Value* hashTableEntry = findValue(buffer, length);
7812     return hashTableEntry ? static_cast<CSSValueID>(hashTableEntry->id) : CSSValueInvalid;
7813 }
7814 
cssValueKeywordID(const CSSParserString & string)7815 CSSValueID cssValueKeywordID(const CSSParserString& string)
7816 {
7817     unsigned length = string.length();
7818     if (!length)
7819         return CSSValueInvalid;
7820     if (length > maxCSSValueKeywordLength)
7821         return CSSValueInvalid;
7822 
7823     return string.is8Bit() ? cssValueKeywordID(string.characters8(), length) : cssValueKeywordID(string.characters16(), length);
7824 }
7825 
isValidNthToken(const CSSParserString & token)7826 bool isValidNthToken(const CSSParserString& token)
7827 {
7828     // The tokenizer checks for the construct of an+b.
7829     // However, since the {ident} rule precedes the {nth} rule, some of those
7830     // tokens are identified as string literal. Furthermore we need to accept
7831     // "odd" and "even" which does not match to an+b.
7832     return equalIgnoringCase(token, "odd") || equalIgnoringCase(token, "even")
7833         || equalIgnoringCase(token, "n") || equalIgnoringCase(token, "-n");
7834 }
7835 
isSystemColor(int id)7836 bool CSSPropertyParser::isSystemColor(int id)
7837 {
7838     return (id >= CSSValueActiveborder && id <= CSSValueWindowtext) || id == CSSValueMenu;
7839 }
7840 
parseSVGValue(CSSPropertyID propId,bool important)7841 bool CSSPropertyParser::parseSVGValue(CSSPropertyID propId, bool important)
7842 {
7843     CSSParserValue* value = m_valueList->current();
7844     if (!value)
7845         return false;
7846 
7847     CSSValueID id = value->id;
7848 
7849     bool validPrimitive = false;
7850     RefPtrWillBeRawPtr<CSSValue> parsedValue = nullptr;
7851 
7852     switch (propId) {
7853     /* The comment to the right defines all valid value of these
7854      * properties as defined in SVG 1.1, Appendix N. Property index */
7855     case CSSPropertyAlignmentBaseline:
7856     // auto | baseline | before-edge | text-before-edge | middle |
7857     // central | after-edge | text-after-edge | ideographic | alphabetic |
7858     // hanging | mathematical | inherit
7859         if (id == CSSValueAuto || id == CSSValueBaseline || id == CSSValueMiddle
7860             || (id >= CSSValueBeforeEdge && id <= CSSValueMathematical))
7861             validPrimitive = true;
7862         break;
7863 
7864     case CSSPropertyBaselineShift:
7865     // baseline | super | sub | <percentage> | <length> | inherit
7866         if (id == CSSValueBaseline || id == CSSValueSub || id == CSSValueSuper)
7867             validPrimitive = true;
7868         else
7869             validPrimitive = validUnit(value, FLength | FPercent, SVGAttributeMode);
7870         break;
7871 
7872     case CSSPropertyDominantBaseline:
7873     // auto | use-script | no-change | reset-size | ideographic |
7874     // alphabetic | hanging | mathematical | central | middle |
7875     // text-after-edge | text-before-edge | inherit
7876         if (id == CSSValueAuto || id == CSSValueMiddle
7877             || (id >= CSSValueUseScript && id <= CSSValueResetSize)
7878             || (id >= CSSValueCentral && id <= CSSValueMathematical))
7879             validPrimitive = true;
7880         break;
7881 
7882     case CSSPropertyEnableBackground:
7883     // accumulate | new [x] [y] [width] [height] | inherit
7884         if (id == CSSValueAccumulate) // TODO : new
7885             validPrimitive = true;
7886         break;
7887 
7888     case CSSPropertyClipPath:
7889     case CSSPropertyFilter:
7890     case CSSPropertyMarkerStart:
7891     case CSSPropertyMarkerMid:
7892     case CSSPropertyMarkerEnd:
7893     case CSSPropertyMask:
7894         if (id == CSSValueNone) {
7895             validPrimitive = true;
7896         } else if (value->unit == CSSPrimitiveValue::CSS_URI) {
7897             parsedValue = CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_URI);
7898             if (parsedValue)
7899                 m_valueList->next();
7900         }
7901         break;
7902 
7903     case CSSPropertyClipRule: // nonzero | evenodd | inherit
7904     case CSSPropertyFillRule:
7905         if (id == CSSValueNonzero || id == CSSValueEvenodd)
7906             validPrimitive = true;
7907         break;
7908 
7909     case CSSPropertyStrokeMiterlimit: // <miterlimit> | inherit
7910         validPrimitive = validUnit(value, FNumber | FNonNeg, SVGAttributeMode);
7911         break;
7912 
7913     case CSSPropertyStrokeLinejoin: // miter | round | bevel | inherit
7914         if (id == CSSValueMiter || id == CSSValueRound || id == CSSValueBevel)
7915             validPrimitive = true;
7916         break;
7917 
7918     case CSSPropertyStrokeLinecap: // butt | round | square | inherit
7919         if (id == CSSValueButt || id == CSSValueRound || id == CSSValueSquare)
7920             validPrimitive = true;
7921         break;
7922 
7923     case CSSPropertyStrokeOpacity: // <opacity-value> | inherit
7924     case CSSPropertyFillOpacity:
7925     case CSSPropertyStopOpacity:
7926     case CSSPropertyFloodOpacity:
7927         validPrimitive = (!id && validUnit(value, FNumber | FPercent, SVGAttributeMode));
7928         break;
7929 
7930     case CSSPropertyShapeRendering:
7931     // auto | optimizeSpeed | crispEdges | geometricPrecision | inherit
7932         if (id == CSSValueAuto || id == CSSValueOptimizespeed
7933             || id == CSSValueCrispedges || id == CSSValueGeometricprecision)
7934             validPrimitive = true;
7935         break;
7936 
7937     case CSSPropertyColorRendering: // optimizeQuality | inherit
7938         if (id == CSSValueAuto || id == CSSValueOptimizespeed
7939             || id == CSSValueOptimizequality)
7940             validPrimitive = true;
7941         break;
7942 
7943     case CSSPropertyBufferedRendering: // auto | dynamic | static
7944         if (id == CSSValueAuto || id == CSSValueDynamic || id == CSSValueStatic)
7945             validPrimitive = true;
7946         break;
7947 
7948     case CSSPropertyColorInterpolation: // auto | sRGB | linearRGB | inherit
7949     case CSSPropertyColorInterpolationFilters:
7950         if (id == CSSValueAuto || id == CSSValueSrgb || id == CSSValueLinearrgb)
7951             validPrimitive = true;
7952         break;
7953 
7954     /* Start of supported CSS properties with validation. This is needed for parseShortHand to work
7955      * correctly and allows optimization in applyRule(..)
7956      */
7957 
7958     case CSSPropertyTextAnchor: // start | middle | end | inherit
7959         if (id == CSSValueStart || id == CSSValueMiddle || id == CSSValueEnd)
7960             validPrimitive = true;
7961         break;
7962 
7963     case CSSPropertyGlyphOrientationVertical: // auto | <angle> | inherit
7964         if (id == CSSValueAuto) {
7965             validPrimitive = true;
7966             break;
7967         }
7968     /* fallthrough intentional */
7969     case CSSPropertyGlyphOrientationHorizontal: // <angle> (restricted to _deg_ per SVG 1.1 spec) | inherit
7970         if (value->unit == CSSPrimitiveValue::CSS_DEG || value->unit == CSSPrimitiveValue::CSS_NUMBER) {
7971             parsedValue = CSSPrimitiveValue::create(value->fValue, CSSPrimitiveValue::CSS_DEG);
7972 
7973             if (parsedValue)
7974                 m_valueList->next();
7975         }
7976         break;
7977 
7978     case CSSPropertyFill: // <paint> | inherit
7979     case CSSPropertyStroke: // <paint> | inherit
7980         {
7981             if (id == CSSValueNone || id == CSSValueCurrentcolor) {
7982                 parsedValue = cssValuePool().createIdentifierValue(id);
7983             } else if (isSystemColor(id)) {
7984                 parsedValue = cssValuePool().createColorValue(RenderTheme::theme().systemColor(id).rgb());
7985             } else if (value->unit == CSSPrimitiveValue::CSS_URI) {
7986                 RGBA32 c = Color::transparent;
7987                 if (m_valueList->next()) {
7988                     RefPtrWillBeRawPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
7989                     values->append(CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_URI));
7990                     if (parseColorFromValue(m_valueList->current(), c))
7991                         parsedValue = cssValuePool().createColorValue(c);
7992                     else if (m_valueList->current()->id == CSSValueNone || m_valueList->current()->id == CSSValueCurrentcolor)
7993                         parsedValue = cssValuePool().createIdentifierValue(m_valueList->current()->id);
7994                     if (parsedValue) {
7995                         values->append(parsedValue);
7996                         parsedValue = values;
7997                     }
7998                 }
7999                 if (!parsedValue)
8000                     parsedValue = CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_URI);
8001             } else {
8002                 parsedValue = parseColor();
8003             }
8004 
8005             if (parsedValue)
8006                 m_valueList->next();
8007         }
8008         break;
8009 
8010     case CSSPropertyStopColor: // TODO : icccolor
8011     case CSSPropertyFloodColor:
8012     case CSSPropertyLightingColor:
8013         if (isSystemColor(id))
8014             parsedValue = cssValuePool().createColorValue(RenderTheme::theme().systemColor(id).rgb());
8015         else if (id == CSSValueCurrentcolor)
8016             parsedValue = cssValuePool().createIdentifierValue(id);
8017         else // TODO : svgcolor (iccColor)
8018             parsedValue = parseColor();
8019 
8020         if (parsedValue)
8021             m_valueList->next();
8022 
8023         break;
8024 
8025     case CSSPropertyPaintOrder:
8026         if (m_valueList->size() == 1 && id == CSSValueNormal)
8027             validPrimitive = true;
8028         else if ((parsedValue = parsePaintOrder()))
8029             m_valueList->next();
8030         break;
8031 
8032     case CSSPropertyVectorEffect: // none | non-scaling-stroke | inherit
8033         if (id == CSSValueNone || id == CSSValueNonScalingStroke)
8034             validPrimitive = true;
8035         break;
8036 
8037     case CSSPropertyWritingMode:
8038     // lr-tb | rl_tb | tb-rl | lr | rl | tb | inherit
8039         if (id == CSSValueLrTb || id == CSSValueRlTb || id == CSSValueTbRl || id == CSSValueLr || id == CSSValueRl || id == CSSValueTb)
8040             validPrimitive = true;
8041         break;
8042 
8043     case CSSPropertyStrokeWidth: // <length> | inherit
8044     case CSSPropertyStrokeDashoffset:
8045         validPrimitive = validUnit(value, FLength | FPercent, SVGAttributeMode);
8046         break;
8047     case CSSPropertyStrokeDasharray: // none | <dasharray> | inherit
8048         if (id == CSSValueNone)
8049             validPrimitive = true;
8050         else
8051             parsedValue = parseSVGStrokeDasharray();
8052         break;
8053 
8054     case CSSPropertyMaskType: // luminance | alpha | inherit
8055         if (id == CSSValueLuminance || id == CSSValueAlpha)
8056             validPrimitive = true;
8057         break;
8058 
8059     /* shorthand properties */
8060     case CSSPropertyMarker: {
8061         ShorthandScope scope(this, propId);
8062         CSSPropertyParser::ImplicitScope implicitScope(this);
8063         if (!parseValue(CSSPropertyMarkerStart, important))
8064             return false;
8065         if (m_valueList->current()) {
8066             rollbackLastProperties(1);
8067             return false;
8068         }
8069         CSSValue* value = m_parsedProperties.last().value();
8070         addProperty(CSSPropertyMarkerMid, value, important);
8071         addProperty(CSSPropertyMarkerEnd, value, important);
8072         return true;
8073     }
8074     default:
8075         // If you crash here, it's because you added a css property and are not handling it
8076         // in either this switch statement or the one in CSSPropertyParser::parseValue
8077         ASSERT_WITH_MESSAGE(0, "unimplemented propertyID: %d", propId);
8078         return false;
8079     }
8080 
8081     if (validPrimitive) {
8082         if (id)
8083             parsedValue = CSSPrimitiveValue::createIdentifier(id);
8084         else if (value->unit == CSSPrimitiveValue::CSS_STRING)
8085             parsedValue = CSSPrimitiveValue::create(value->string, (CSSPrimitiveValue::UnitType) value->unit);
8086         else if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ)
8087             parsedValue = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitType) value->unit);
8088         else if (value->unit >= CSSParserValue::Q_EMS)
8089             parsedValue = CSSPrimitiveValue::createAllowingMarginQuirk(value->fValue, CSSPrimitiveValue::CSS_EMS);
8090         if (isCalculation(value)) {
8091             // FIXME calc() http://webkit.org/b/16662 : actually create a CSSPrimitiveValue here, ie
8092             // parsedValue = CSSPrimitiveValue::create(m_parsedCalculation.release());
8093             m_parsedCalculation.release();
8094             parsedValue = nullptr;
8095         }
8096         m_valueList->next();
8097     }
8098     if (!parsedValue || (m_valueList->current() && !inShorthand()))
8099         return false;
8100 
8101     addProperty(propId, parsedValue.release(), important);
8102     return true;
8103 }
8104 
parseSVGStrokeDasharray()8105 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseSVGStrokeDasharray()
8106 {
8107     RefPtrWillBeRawPtr<CSSValueList> ret = CSSValueList::createCommaSeparated();
8108     CSSParserValue* value = m_valueList->current();
8109     bool validPrimitive = true;
8110     while (value) {
8111         validPrimitive = validUnit(value, FLength | FPercent | FNonNeg, SVGAttributeMode);
8112         if (!validPrimitive)
8113             break;
8114         if (value->id)
8115             ret->append(CSSPrimitiveValue::createIdentifier(value->id));
8116         else if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ)
8117             ret->append(CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitType) value->unit));
8118         value = m_valueList->next();
8119         if (value && value->unit == CSSParserValue::Operator && value->iValue == ',')
8120             value = m_valueList->next();
8121     }
8122     if (!validPrimitive)
8123         return nullptr;
8124     return ret.release();
8125 }
8126 
8127 // normal | [ fill || stroke || markers ]
parsePaintOrder() const8128 PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parsePaintOrder() const
8129 {
8130     if (m_valueList->size() > 3)
8131         return nullptr;
8132 
8133     CSSParserValue* value = m_valueList->current();
8134     if (!value)
8135         return nullptr;
8136 
8137     RefPtrWillBeRawPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
8138 
8139     // The default paint-order is: Fill, Stroke, Markers.
8140     bool seenFill = false, seenStroke = false, seenMarkers = false;
8141 
8142     for (; value; value = m_valueList->next()) {
8143         switch (value->id) {
8144         case CSSValueNormal:
8145             // normal inside [fill || stroke || markers] not valid
8146             return nullptr;
8147         case CSSValueFill:
8148             if (seenFill)
8149                 return nullptr;
8150 
8151             seenFill = true;
8152             break;
8153         case CSSValueStroke:
8154             if (seenStroke)
8155                 return nullptr;
8156 
8157             seenStroke = true;
8158             break;
8159         case CSSValueMarkers:
8160             if (seenMarkers)
8161                 return nullptr;
8162 
8163             seenMarkers = true;
8164             break;
8165         default:
8166             return nullptr;
8167         }
8168 
8169         parsedValues->append(CSSPrimitiveValue::createIdentifier(value->id));
8170     }
8171 
8172     // fill out the rest of the paint order
8173     if (!seenFill)
8174         parsedValues->append(CSSPrimitiveValue::createIdentifier(CSSValueFill));
8175     if (!seenStroke)
8176         parsedValues->append(CSSPrimitiveValue::createIdentifier(CSSValueStroke));
8177     if (!seenMarkers)
8178         parsedValues->append(CSSPrimitiveValue::createIdentifier(CSSValueMarkers));
8179 
8180     return parsedValues.release();
8181 }
8182 
8183 } // namespace blink
8184