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