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