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