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