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/CSSParser.h" 29 30 #include "CSSValueKeywords.h" 31 #include "RuntimeEnabledFeatures.h" 32 #include "StylePropertyShorthand.h" 33 #include "core/css/CSSArrayFunctionValue.h" 34 #include "core/css/CSSAspectRatioValue.h" 35 #include "core/css/CSSBasicShapes.h" 36 #include "core/css/CSSBorderImage.h" 37 #include "core/css/CSSCanvasValue.h" 38 #include "core/css/CSSCrossfadeValue.h" 39 #include "core/css/CSSCursorImageValue.h" 40 #include "core/css/CSSFontFaceSrcValue.h" 41 #include "core/css/CSSFontFeatureValue.h" 42 #include "core/css/CSSFunctionValue.h" 43 #include "core/css/CSSGradientValue.h" 44 #include "core/css/CSSGridLineNamesValue.h" 45 #include "core/css/CSSGridTemplateValue.h" 46 #include "core/css/CSSImageSetValue.h" 47 #include "core/css/CSSImageValue.h" 48 #include "core/css/CSSInheritedValue.h" 49 #include "core/css/CSSInitialValue.h" 50 #include "core/css/CSSKeyframeRule.h" 51 #include "core/css/CSSKeyframesRule.h" 52 #include "core/css/CSSLineBoxContainValue.h" 53 #include "core/css/CSSMixFunctionValue.h" 54 #include "core/css/CSSPrimitiveValue.h" 55 #include "core/css/CSSPropertySourceData.h" 56 #include "core/css/CSSReflectValue.h" 57 #include "core/css/CSSSVGDocumentValue.h" 58 #include "core/css/CSSSelector.h" 59 #include "core/css/CSSShaderValue.h" 60 #include "core/css/CSSShadowValue.h" 61 #include "core/css/CSSStyleSheet.h" 62 #include "core/css/CSSTimingFunctionValue.h" 63 #include "core/css/CSSTransformValue.h" 64 #include "core/css/CSSUnicodeRangeValue.h" 65 #include "core/css/CSSValueList.h" 66 #include "core/css/CSSValuePool.h" 67 #include "core/css/CSSVariableValue.h" 68 #include "core/css/Counter.h" 69 #include "core/css/HashTools.h" 70 #include "core/css/MediaList.h" 71 #include "core/css/MediaQueryExp.h" 72 #include "core/css/Pair.h" 73 #include "core/css/Rect.h" 74 #include "core/css/StylePropertySet.h" 75 #include "core/css/StyleRule.h" 76 #include "core/css/StyleRuleImport.h" 77 #include "core/css/StyleSheetContents.h" 78 #include "core/dom/Document.h" 79 #include "core/html/parser/HTMLParserIdioms.h" 80 #include "core/inspector/InspectorInstrumentation.h" 81 #include "core/page/PageConsole.h" 82 #include "core/frame/Settings.h" 83 #include "core/rendering/RenderTheme.h" 84 #include "core/svg/SVGParserUtilities.h" 85 #include "platform/FloatConversion.h" 86 #include "wtf/BitArray.h" 87 #include "wtf/HexNumber.h" 88 #include "wtf/text/StringBuffer.h" 89 #include "wtf/text/StringBuilder.h" 90 #include "wtf/text/StringImpl.h" 91 #include "wtf/text/TextEncoding.h" 92 #include <limits.h> 93 94 #define YYDEBUG 0 95 96 #if YYDEBUG > 0 97 extern int cssyydebug; 98 #endif 99 100 extern int cssyyparse(WebCore::CSSParser*); 101 102 using namespace std; 103 using namespace WTF; 104 105 namespace WebCore { 106 107 static const unsigned INVALID_NUM_PARSED_PROPERTIES = UINT_MAX; 108 static const double MAX_SCALE = 1000000; 109 110 template <unsigned N> 111 static bool equal(const CSSParserString& a, const char (&b)[N]) 112 { 113 unsigned length = N - 1; // Ignore the trailing null character 114 if (a.length() != length) 115 return false; 116 117 return a.is8Bit() ? WTF::equal(a.characters8(), reinterpret_cast<const LChar*>(b), length) : WTF::equal(a.characters16(), reinterpret_cast<const LChar*>(b), length); 118 } 119 120 template <unsigned N> 121 static bool equalIgnoringCase(const CSSParserString& a, const char (&b)[N]) 122 { 123 unsigned length = N - 1; // Ignore the trailing null character 124 if (a.length() != length) 125 return false; 126 127 return a.is8Bit() ? WTF::equalIgnoringCase(b, a.characters8(), length) : WTF::equalIgnoringCase(b, a.characters16(), length); 128 } 129 130 template <unsigned N> 131 static bool equalIgnoringCase(CSSParserValue* value, const char (&b)[N]) 132 { 133 ASSERT(value->unit == CSSPrimitiveValue::CSS_IDENT || value->unit == CSSPrimitiveValue::CSS_STRING); 134 return equalIgnoringCase(value->string, b); 135 } 136 137 static PassRefPtr<CSSPrimitiveValue> createPrimitiveValuePair(PassRefPtr<CSSPrimitiveValue> first, PassRefPtr<CSSPrimitiveValue> second, Pair::IdenticalValuesPolicy identicalValuesPolicy = Pair::DropIdenticalValues) 138 { 139 return cssValuePool().createValue(Pair::create(first, second, identicalValuesPolicy)); 140 } 141 142 class AnimationParseContext { 143 public: 144 AnimationParseContext() 145 : m_animationPropertyKeywordAllowed(true) 146 , m_firstAnimationCommitted(false) 147 , m_hasSeenAnimationPropertyKeyword(false) 148 { 149 } 150 151 void commitFirstAnimation() 152 { 153 m_firstAnimationCommitted = true; 154 } 155 156 bool hasCommittedFirstAnimation() const 157 { 158 return m_firstAnimationCommitted; 159 } 160 161 void commitAnimationPropertyKeyword() 162 { 163 m_animationPropertyKeywordAllowed = false; 164 } 165 166 bool animationPropertyKeywordAllowed() const 167 { 168 return m_animationPropertyKeywordAllowed; 169 } 170 171 bool hasSeenAnimationPropertyKeyword() const 172 { 173 return m_hasSeenAnimationPropertyKeyword; 174 } 175 176 void sawAnimationPropertyKeyword() 177 { 178 m_hasSeenAnimationPropertyKeyword = true; 179 } 180 181 private: 182 bool m_animationPropertyKeywordAllowed; 183 bool m_firstAnimationCommitted; 184 bool m_hasSeenAnimationPropertyKeyword; 185 }; 186 187 CSSParser::CSSParser(const CSSParserContext& context, UseCounter* counter) 188 : m_context(context) 189 , m_important(false) 190 , m_id(CSSPropertyInvalid) 191 , m_styleSheet(0) 192 , m_supportsCondition(false) 193 , m_selectorListForParseSelector(0) 194 , m_numParsedPropertiesBeforeMarginBox(INVALID_NUM_PARSED_PROPERTIES) 195 , m_inParseShorthand(0) 196 , m_currentShorthand(CSSPropertyInvalid) 197 , m_implicitShorthand(false) 198 , m_hasFontFaceOnlyValues(false) 199 , m_hadSyntacticallyValidCSSRule(false) 200 , m_logErrors(false) 201 , m_ignoreErrors(false) 202 , m_inFilterRule(false) 203 , m_defaultNamespace(starAtom) 204 , m_sourceDataHandler(0) 205 , m_source(0) 206 , m_ruleHeaderType(CSSRuleSourceData::UNKNOWN_RULE) 207 , m_allowImportRules(true) 208 , m_allowNamespaceDeclarations(true) 209 , m_inViewport(false) 210 , m_useCounter(counter) 211 , m_tokenizer(*this) 212 { 213 #if YYDEBUG > 0 214 cssyydebug = 1; 215 #endif 216 CSSPropertySourceData::init(); 217 } 218 219 CSSParser::~CSSParser() 220 { 221 clearProperties(); 222 223 deleteAllValues(m_floatingSelectors); 224 deleteAllValues(m_floatingSelectorVectors); 225 deleteAllValues(m_floatingValueLists); 226 deleteAllValues(m_floatingFunctions); 227 } 228 229 void CSSParser::setupParser(const char* prefix, unsigned prefixLength, const String& string, const char* suffix, unsigned suffixLength) 230 { 231 m_tokenizer.setupTokenizer(prefix, prefixLength, string, suffix, suffixLength); 232 m_ruleHasHeader = true; 233 } 234 235 void CSSParser::parseSheet(StyleSheetContents* sheet, const String& string, const TextPosition& startPosition, SourceDataHandler* sourceDataHandler, bool logErrors) 236 { 237 setStyleSheet(sheet); 238 m_defaultNamespace = starAtom; // Reset the default namespace. 239 m_sourceDataHandler = sourceDataHandler; 240 m_logErrors = logErrors && sheet->singleOwnerDocument() && !sheet->baseURL().isEmpty() && sheet->singleOwnerDocument()->page(); 241 m_ignoreErrors = false; 242 m_tokenizer.m_lineNumber = 0; 243 m_startPosition = startPosition; 244 m_source = &string; 245 m_tokenizer.m_internal = false; 246 setupParser("", string, ""); 247 cssyyparse(this); 248 sheet->shrinkToFit(); 249 m_source = 0; 250 m_sourceDataHandler = 0; 251 m_rule = 0; 252 m_lineEndings.clear(); 253 m_ignoreErrors = false; 254 m_logErrors = false; 255 m_tokenizer.m_internal = true; 256 } 257 258 PassRefPtr<StyleRuleBase> CSSParser::parseRule(StyleSheetContents* sheet, const String& string) 259 { 260 setStyleSheet(sheet); 261 m_allowNamespaceDeclarations = false; 262 setupParser("@-internal-rule ", string, ""); 263 cssyyparse(this); 264 return m_rule.release(); 265 } 266 267 PassRefPtr<StyleKeyframe> CSSParser::parseKeyframeRule(StyleSheetContents* sheet, const String& string) 268 { 269 setStyleSheet(sheet); 270 setupParser("@-internal-keyframe-rule ", string, ""); 271 cssyyparse(this); 272 return m_keyframe.release(); 273 } 274 275 PassOwnPtr<Vector<double> > CSSParser::parseKeyframeKeyList(const String& string) 276 { 277 setupParser("@-internal-keyframe-key-list ", string, ""); 278 cssyyparse(this); 279 ASSERT(m_valueList); 280 return StyleKeyframe::createKeyList(m_valueList.get()); 281 } 282 283 bool CSSParser::parseSupportsCondition(const String& string) 284 { 285 m_supportsCondition = false; 286 setupParser("@-internal-supports-condition ", string, ""); 287 cssyyparse(this); 288 return m_supportsCondition; 289 } 290 291 static inline bool isColorPropertyID(CSSPropertyID propertyId) 292 { 293 switch (propertyId) { 294 case CSSPropertyColor: 295 case CSSPropertyBackgroundColor: 296 case CSSPropertyBorderBottomColor: 297 case CSSPropertyBorderLeftColor: 298 case CSSPropertyBorderRightColor: 299 case CSSPropertyBorderTopColor: 300 case CSSPropertyOutlineColor: 301 case CSSPropertyTextLineThroughColor: 302 case CSSPropertyTextOverlineColor: 303 case CSSPropertyTextUnderlineColor: 304 case CSSPropertyWebkitBorderAfterColor: 305 case CSSPropertyWebkitBorderBeforeColor: 306 case CSSPropertyWebkitBorderEndColor: 307 case CSSPropertyWebkitBorderStartColor: 308 case CSSPropertyWebkitColumnRuleColor: 309 case CSSPropertyWebkitTextEmphasisColor: 310 case CSSPropertyWebkitTextFillColor: 311 case CSSPropertyWebkitTextStrokeColor: 312 return true; 313 case CSSPropertyTextDecorationColor: 314 return RuntimeEnabledFeatures::css3TextDecorationsEnabled(); 315 default: 316 return false; 317 } 318 } 319 320 static bool parseColorValue(MutableStylePropertySet* declaration, CSSPropertyID propertyId, const String& string, bool important, CSSParserMode cssParserMode) 321 { 322 ASSERT(!string.isEmpty()); 323 bool quirksMode = isQuirksModeBehavior(cssParserMode); 324 if (!isColorPropertyID(propertyId)) 325 return false; 326 CSSParserString cssString; 327 cssString.init(string); 328 CSSValueID valueID = cssValueKeywordID(cssString); 329 bool validPrimitive = false; 330 if (valueID == CSSValueWebkitText) { 331 validPrimitive = true; 332 } else if (valueID == CSSValueCurrentcolor) { 333 validPrimitive = true; 334 } else if ((valueID >= CSSValueAqua && valueID <= CSSValueWindowtext) || valueID == CSSValueMenu 335 || (quirksMode && valueID >= CSSValueWebkitFocusRingColor && valueID < CSSValueWebkitText)) { 336 validPrimitive = true; 337 } 338 339 if (validPrimitive) { 340 RefPtr<CSSValue> value = cssValuePool().createIdentifierValue(valueID); 341 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 342 return true; 343 } 344 RGBA32 color; 345 if (!CSSParser::fastParseColor(color, string, !quirksMode && string[0] != '#')) 346 return false; 347 RefPtr<CSSValue> value = cssValuePool().createColorValue(color); 348 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 349 return true; 350 } 351 352 static inline bool isSimpleLengthPropertyID(CSSPropertyID propertyId, bool& acceptsNegativeNumbers) 353 { 354 switch (propertyId) { 355 case CSSPropertyFontSize: 356 case CSSPropertyHeight: 357 case CSSPropertyWidth: 358 case CSSPropertyMinHeight: 359 case CSSPropertyMinWidth: 360 case CSSPropertyPaddingBottom: 361 case CSSPropertyPaddingLeft: 362 case CSSPropertyPaddingRight: 363 case CSSPropertyPaddingTop: 364 case CSSPropertyWebkitLogicalWidth: 365 case CSSPropertyWebkitLogicalHeight: 366 case CSSPropertyWebkitMinLogicalWidth: 367 case CSSPropertyWebkitMinLogicalHeight: 368 case CSSPropertyWebkitPaddingAfter: 369 case CSSPropertyWebkitPaddingBefore: 370 case CSSPropertyWebkitPaddingEnd: 371 case CSSPropertyWebkitPaddingStart: 372 acceptsNegativeNumbers = false; 373 return true; 374 case CSSPropertyShapeMargin: 375 case CSSPropertyShapePadding: 376 acceptsNegativeNumbers = false; 377 return RuntimeEnabledFeatures::cssShapesEnabled(); 378 case CSSPropertyBottom: 379 case CSSPropertyLeft: 380 case CSSPropertyMarginBottom: 381 case CSSPropertyMarginLeft: 382 case CSSPropertyMarginRight: 383 case CSSPropertyMarginTop: 384 case CSSPropertyRight: 385 case CSSPropertyTop: 386 case CSSPropertyWebkitMarginAfter: 387 case CSSPropertyWebkitMarginBefore: 388 case CSSPropertyWebkitMarginEnd: 389 case CSSPropertyWebkitMarginStart: 390 acceptsNegativeNumbers = true; 391 return true; 392 default: 393 return false; 394 } 395 } 396 397 template <typename CharacterType> 398 static inline bool parseSimpleLength(const CharacterType* characters, unsigned& length, CSSPrimitiveValue::UnitTypes& unit, double& number) 399 { 400 if (length > 2 && (characters[length - 2] | 0x20) == 'p' && (characters[length - 1] | 0x20) == 'x') { 401 length -= 2; 402 unit = CSSPrimitiveValue::CSS_PX; 403 } else if (length > 1 && characters[length - 1] == '%') { 404 length -= 1; 405 unit = CSSPrimitiveValue::CSS_PERCENTAGE; 406 } 407 408 // We rely on charactersToDouble for validation as well. The function 409 // will set "ok" to "false" if the entire passed-in character range does 410 // not represent a double. 411 bool ok; 412 number = charactersToDouble(characters, length, &ok); 413 return ok; 414 } 415 416 static bool parseSimpleLengthValue(MutableStylePropertySet* declaration, CSSPropertyID propertyId, const String& string, bool important, CSSParserMode cssParserMode) 417 { 418 ASSERT(!string.isEmpty()); 419 bool acceptsNegativeNumbers; 420 421 // In @viewport, width and height are shorthands, not simple length values. 422 if (isCSSViewportParsingEnabledForMode(cssParserMode) || !isSimpleLengthPropertyID(propertyId, acceptsNegativeNumbers)) 423 return false; 424 425 unsigned length = string.length(); 426 double number; 427 CSSPrimitiveValue::UnitTypes unit = CSSPrimitiveValue::CSS_NUMBER; 428 429 if (string.is8Bit()) { 430 if (!parseSimpleLength(string.characters8(), length, unit, number)) 431 return false; 432 } else { 433 if (!parseSimpleLength(string.characters16(), length, unit, number)) 434 return false; 435 } 436 437 if (unit == CSSPrimitiveValue::CSS_NUMBER) { 438 bool quirksMode = isQuirksModeBehavior(cssParserMode); 439 if (number && !quirksMode) 440 return false; 441 unit = CSSPrimitiveValue::CSS_PX; 442 } 443 if (number < 0 && !acceptsNegativeNumbers) 444 return false; 445 446 RefPtr<CSSValue> value = cssValuePool().createValue(number, unit); 447 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 448 return true; 449 } 450 451 static inline bool isValidKeywordPropertyAndValue(CSSPropertyID propertyId, int valueID, const CSSParserContext& parserContext) 452 { 453 if (!valueID) 454 return false; 455 456 switch (propertyId) { 457 case CSSPropertyBorderCollapse: // collapse | separate | inherit 458 if (valueID == CSSValueCollapse || valueID == CSSValueSeparate) 459 return true; 460 break; 461 case CSSPropertyBorderTopStyle: // <border-style> | inherit 462 case CSSPropertyBorderRightStyle: // Defined as: none | hidden | dotted | dashed | 463 case CSSPropertyBorderBottomStyle: // solid | double | groove | ridge | inset | outset 464 case CSSPropertyBorderLeftStyle: 465 case CSSPropertyWebkitBorderAfterStyle: 466 case CSSPropertyWebkitBorderBeforeStyle: 467 case CSSPropertyWebkitBorderEndStyle: 468 case CSSPropertyWebkitBorderStartStyle: 469 case CSSPropertyWebkitColumnRuleStyle: 470 if (valueID >= CSSValueNone && valueID <= CSSValueDouble) 471 return true; 472 break; 473 case CSSPropertyBoxSizing: 474 if (valueID == CSSValueBorderBox || valueID == CSSValueContentBox) 475 return true; 476 break; 477 case CSSPropertyCaptionSide: // top | bottom | left | right | inherit 478 if (valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueTop || valueID == CSSValueBottom) 479 return true; 480 break; 481 case CSSPropertyClear: // none | left | right | both | inherit 482 if (valueID == CSSValueNone || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueBoth) 483 return true; 484 break; 485 case CSSPropertyDirection: // ltr | rtl | inherit 486 if (valueID == CSSValueLtr || valueID == CSSValueRtl) 487 return true; 488 break; 489 case CSSPropertyDisplay: 490 // inline | block | list-item | inline-block | table | 491 // inline-table | table-row-group | table-header-group | table-footer-group | table-row | 492 // table-column-group | table-column | table-cell | table-caption | -webkit-box | -webkit-inline-box | none | inherit 493 // flex | inline-flex | -webkit-flex | -webkit-inline-flex | grid | inline-grid | lazy-block 494 if ((valueID >= CSSValueInline && valueID <= CSSValueInlineFlex) || valueID == CSSValueWebkitFlex || valueID == CSSValueWebkitInlineFlex || valueID == CSSValueNone) 495 return true; 496 if (valueID == CSSValueGrid || valueID == CSSValueInlineGrid) 497 return RuntimeEnabledFeatures::cssGridLayoutEnabled(); 498 break; 499 500 case CSSPropertyEmptyCells: // show | hide | inherit 501 if (valueID == CSSValueShow || valueID == CSSValueHide) 502 return true; 503 break; 504 case CSSPropertyFloat: // left | right | none | center (for buggy CSS, maps to none) 505 if (valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueNone || valueID == CSSValueCenter) 506 return true; 507 break; 508 case CSSPropertyFontStyle: // normal | italic | oblique | inherit 509 if (valueID == CSSValueNormal || valueID == CSSValueItalic || valueID == CSSValueOblique) 510 return true; 511 break; 512 case CSSPropertyImageRendering: // auto | optimizeContrast 513 if (valueID == CSSValueAuto || valueID == CSSValueWebkitOptimizeContrast) 514 return true; 515 break; 516 case CSSPropertyIsolation: // auto | isolate 517 if (valueID == CSSValueAuto || valueID == CSSValueIsolate) 518 return RuntimeEnabledFeatures::cssCompositingEnabled(); 519 break; 520 case CSSPropertyListStylePosition: // inside | outside | inherit 521 if (valueID == CSSValueInside || valueID == CSSValueOutside) 522 return true; 523 break; 524 case CSSPropertyListStyleType: 525 // See section CSS_PROP_LIST_STYLE_TYPE of file CSSValueKeywords.in 526 // for the list of supported list-style-types. 527 if ((valueID >= CSSValueDisc && valueID <= CSSValueKatakanaIroha) || valueID == CSSValueNone) 528 return true; 529 break; 530 case CSSPropertyObjectFit: 531 if (RuntimeEnabledFeatures::objectFitPositionEnabled()) { 532 if (valueID == CSSValueFill || valueID == CSSValueContain || valueID == CSSValueCover || valueID == CSSValueNone || valueID == CSSValueScaleDown) 533 return true; 534 } 535 break; 536 case CSSPropertyOutlineStyle: // (<border-style> except hidden) | auto | inherit 537 if (valueID == CSSValueAuto || valueID == CSSValueNone || (valueID >= CSSValueInset && valueID <= CSSValueDouble)) 538 return true; 539 break; 540 case CSSPropertyOverflowWrap: // normal | break-word 541 case CSSPropertyWordWrap: 542 if (valueID == CSSValueNormal || valueID == CSSValueBreakWord) 543 return true; 544 break; 545 case CSSPropertyOverflowX: // visible | hidden | scroll | auto | overlay | inherit 546 if (valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay) 547 return true; 548 break; 549 case CSSPropertyOverflowY: // visible | hidden | scroll | auto | overlay | inherit | -webkit-paged-x | -webkit-paged-y 550 if (valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay || valueID == CSSValueWebkitPagedX || valueID == CSSValueWebkitPagedY) 551 return true; 552 break; 553 case CSSPropertyPageBreakAfter: // auto | always | avoid | left | right | inherit 554 case CSSPropertyPageBreakBefore: 555 case CSSPropertyWebkitColumnBreakAfter: 556 case CSSPropertyWebkitColumnBreakBefore: 557 if (valueID == CSSValueAuto || valueID == CSSValueAlways || valueID == CSSValueAvoid || valueID == CSSValueLeft || valueID == CSSValueRight) 558 return true; 559 break; 560 case CSSPropertyPageBreakInside: // avoid | auto | inherit 561 case CSSPropertyWebkitColumnBreakInside: 562 if (valueID == CSSValueAuto || valueID == CSSValueAvoid) 563 return true; 564 break; 565 case CSSPropertyPointerEvents: 566 // none | visiblePainted | visibleFill | visibleStroke | visible | 567 // painted | fill | stroke | auto | all | bounding-box | inherit 568 if (valueID == CSSValueVisible || valueID == CSSValueNone || valueID == CSSValueAll || valueID == CSSValueAuto || (valueID >= CSSValueVisiblepainted && valueID <= CSSValueBoundingBox)) 569 return true; 570 break; 571 case CSSPropertyPosition: // static | relative | absolute | fixed | sticky | inherit 572 if (valueID == CSSValueStatic || valueID == CSSValueRelative || valueID == CSSValueAbsolute || valueID == CSSValueFixed 573 || (RuntimeEnabledFeatures::cssStickyPositionEnabled() && valueID == CSSValueSticky)) 574 return true; 575 break; 576 case CSSPropertyResize: // none | both | horizontal | vertical | auto 577 if (valueID == CSSValueNone || valueID == CSSValueBoth || valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueAuto) 578 return true; 579 break; 580 case CSSPropertySpeak: // none | normal | spell-out | digits | literal-punctuation | no-punctuation | inherit 581 if (valueID == CSSValueNone || valueID == CSSValueNormal || valueID == CSSValueSpellOut || valueID == CSSValueDigits || valueID == CSSValueLiteralPunctuation || valueID == CSSValueNoPunctuation) 582 return true; 583 break; 584 case CSSPropertyTableLayout: // auto | fixed | inherit 585 if (valueID == CSSValueAuto || valueID == CSSValueFixed) 586 return true; 587 break; 588 case CSSPropertyTextAlignLast: 589 // auto | start | end | left | right | center | justify 590 if (RuntimeEnabledFeatures::css3TextEnabled() 591 && ((valueID >= CSSValueLeft && valueID <= CSSValueJustify) || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueAuto)) 592 return true; 593 break; 594 case CSSPropertyTextJustify: 595 // auto | none | inter-word | distribute 596 if (RuntimeEnabledFeatures::css3TextEnabled() 597 && (valueID == CSSValueInterWord || valueID == CSSValueDistribute || valueID == CSSValueAuto || valueID == CSSValueNone)) 598 return true; 599 break; 600 case CSSPropertyTextLineThroughMode: 601 case CSSPropertyTextOverlineMode: 602 case CSSPropertyTextUnderlineMode: 603 if (valueID == CSSValueContinuous || valueID == CSSValueSkipWhiteSpace) 604 return true; 605 break; 606 case CSSPropertyTextLineThroughStyle: 607 case CSSPropertyTextOverlineStyle: 608 case CSSPropertyTextUnderlineStyle: 609 if (valueID == CSSValueNone || valueID == CSSValueSolid || valueID == CSSValueDouble || valueID == CSSValueDashed || valueID == CSSValueDotDash || valueID == CSSValueDotDotDash || valueID == CSSValueWave) 610 return true; 611 break; 612 case CSSPropertyTextOverflow: // clip | ellipsis 613 if (valueID == CSSValueClip || valueID == CSSValueEllipsis) 614 return true; 615 break; 616 case CSSPropertyTextRendering: // auto | optimizeSpeed | optimizeLegibility | geometricPrecision 617 if (valueID == CSSValueAuto || valueID == CSSValueOptimizespeed || valueID == CSSValueOptimizelegibility || valueID == CSSValueGeometricprecision) 618 return true; 619 break; 620 case CSSPropertyTextTransform: // capitalize | uppercase | lowercase | none | inherit 621 if ((valueID >= CSSValueCapitalize && valueID <= CSSValueLowercase) || valueID == CSSValueNone) 622 return true; 623 break; 624 case CSSPropertyTouchActionDelay: // none | script 625 if (RuntimeEnabledFeatures::cssTouchActionEnabled() && (valueID == CSSValueScript || valueID == CSSValueNone)) 626 return true; 627 break; 628 case CSSPropertyVisibility: // visible | hidden | collapse | inherit 629 if (valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueCollapse) 630 return true; 631 break; 632 case CSSPropertyWebkitAppearance: 633 if ((valueID >= CSSValueCheckbox && valueID <= CSSValueTextarea) || valueID == CSSValueNone) 634 return true; 635 break; 636 case CSSPropertyWebkitBackfaceVisibility: 637 if (valueID == CSSValueVisible || valueID == CSSValueHidden) 638 return true; 639 break; 640 case CSSPropertyMixBlendMode: 641 if (RuntimeEnabledFeatures::cssCompositingEnabled() && (valueID == CSSValueNormal || valueID == CSSValueMultiply || valueID == CSSValueScreen 642 || valueID == CSSValueOverlay || valueID == CSSValueDarken || valueID == CSSValueLighten || valueID == CSSValueColorDodge 643 || valueID == CSSValueColorBurn || valueID == CSSValueHardLight || valueID == CSSValueSoftLight || valueID == CSSValueDifference 644 || valueID == CSSValueExclusion || valueID == CSSValueHue || valueID == CSSValueSaturation || valueID == CSSValueColor 645 || valueID == CSSValueLuminosity)) 646 return true; 647 break; 648 case CSSPropertyWebkitBorderFit: 649 if (valueID == CSSValueBorder || valueID == CSSValueLines) 650 return true; 651 break; 652 case CSSPropertyWebkitBoxAlign: 653 if (valueID == CSSValueStretch || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline) 654 return true; 655 break; 656 case CSSPropertyWebkitBoxDecorationBreak: 657 if (valueID == CSSValueClone || valueID == CSSValueSlice) 658 return true; 659 break; 660 case CSSPropertyWebkitBoxDirection: 661 if (valueID == CSSValueNormal || valueID == CSSValueReverse) 662 return true; 663 break; 664 case CSSPropertyWebkitBoxLines: 665 if (valueID == CSSValueSingle || valueID == CSSValueMultiple) 666 return true; 667 break; 668 case CSSPropertyWebkitBoxOrient: 669 if (valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueInlineAxis || valueID == CSSValueBlockAxis) 670 return true; 671 break; 672 case CSSPropertyWebkitBoxPack: 673 if (valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueJustify) 674 return true; 675 break; 676 case CSSPropertyInternalCallback: 677 // This property is only injected programmatically, not parsed from stylesheets. 678 return false; 679 case CSSPropertyColumnFill: 680 if (RuntimeEnabledFeatures::regionBasedColumnsEnabled()) { 681 if (valueID == CSSValueAuto || valueID == CSSValueBalance) 682 return true; 683 } 684 break; 685 case CSSPropertyAlignContent: 686 if (valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueSpaceBetween || valueID == CSSValueSpaceAround || valueID == CSSValueStretch) 687 return true; 688 break; 689 case CSSPropertyAlignItems: 690 if (valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch) 691 return true; 692 break; 693 case CSSPropertyAlignSelf: 694 if (valueID == CSSValueAuto || valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch) 695 return true; 696 break; 697 case CSSPropertyFlexDirection: 698 if (valueID == CSSValueRow || valueID == CSSValueRowReverse || valueID == CSSValueColumn || valueID == CSSValueColumnReverse) 699 return true; 700 break; 701 case CSSPropertyFlexWrap: 702 if (valueID == CSSValueNowrap || valueID == CSSValueWrap || valueID == CSSValueWrapReverse) 703 return true; 704 break; 705 case CSSPropertyJustifyContent: 706 if (valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueSpaceBetween || valueID == CSSValueSpaceAround) 707 return true; 708 break; 709 case CSSPropertyFontKerning: 710 if (valueID == CSSValueAuto || valueID == CSSValueNormal || valueID == CSSValueNone) 711 return true; 712 break; 713 case CSSPropertyWebkitFontSmoothing: 714 if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueAntialiased || valueID == CSSValueSubpixelAntialiased) 715 return true; 716 break; 717 case CSSPropertyGridAutoFlow: 718 if (valueID == CSSValueNone || valueID == CSSValueRow || valueID == CSSValueColumn) 719 return RuntimeEnabledFeatures::cssGridLayoutEnabled(); 720 break; 721 case CSSPropertyWebkitLineAlign: 722 if (valueID == CSSValueNone || valueID == CSSValueEdges) 723 return true; 724 break; 725 case CSSPropertyWebkitLineBreak: // auto | loose | normal | strict | after-white-space 726 if (valueID == CSSValueAuto || valueID == CSSValueLoose || valueID == CSSValueNormal || valueID == CSSValueStrict || valueID == CSSValueAfterWhiteSpace) 727 return true; 728 break; 729 case CSSPropertyWebkitLineSnap: 730 if (valueID == CSSValueNone || valueID == CSSValueBaseline || valueID == CSSValueContain) 731 return true; 732 break; 733 case CSSPropertyWebkitMarginAfterCollapse: 734 case CSSPropertyWebkitMarginBeforeCollapse: 735 case CSSPropertyWebkitMarginBottomCollapse: 736 case CSSPropertyWebkitMarginTopCollapse: 737 if (valueID == CSSValueCollapse || valueID == CSSValueSeparate || valueID == CSSValueDiscard) 738 return true; 739 break; 740 case CSSPropertyInternalMarqueeDirection: 741 if (valueID == CSSValueForwards || valueID == CSSValueBackwards || valueID == CSSValueAhead || valueID == CSSValueReverse || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueDown 742 || valueID == CSSValueUp || valueID == CSSValueAuto) 743 return true; 744 break; 745 case CSSPropertyInternalMarqueeStyle: 746 if (valueID == CSSValueNone || valueID == CSSValueSlide || valueID == CSSValueScroll || valueID == CSSValueAlternate) 747 return true; 748 break; 749 case CSSPropertyWebkitPrintColorAdjust: 750 if (valueID == CSSValueExact || valueID == CSSValueEconomy) 751 return true; 752 break; 753 case CSSPropertyWebkitRegionBreakAfter: 754 case CSSPropertyWebkitRegionBreakBefore: 755 if (RuntimeEnabledFeatures::cssRegionsEnabled() && (valueID == CSSValueAuto || valueID == CSSValueAlways || valueID == CSSValueAvoid || valueID == CSSValueLeft || valueID == CSSValueRight)) 756 return true; 757 break; 758 case CSSPropertyWebkitRegionBreakInside: 759 if (RuntimeEnabledFeatures::cssRegionsEnabled() && (valueID == CSSValueAuto || valueID == CSSValueAvoid)) 760 return true; 761 break; 762 case CSSPropertyWebkitRegionFragment: 763 if (RuntimeEnabledFeatures::cssRegionsEnabled() && (valueID == CSSValueAuto || valueID == CSSValueBreak)) 764 return true; 765 break; 766 case CSSPropertyWebkitRtlOrdering: 767 if (valueID == CSSValueLogical || valueID == CSSValueVisual) 768 return true; 769 break; 770 771 case CSSPropertyWebkitRubyPosition: 772 if (valueID == CSSValueBefore || valueID == CSSValueAfter) 773 return true; 774 break; 775 776 case CSSPropertyWebkitTextCombine: 777 if (valueID == CSSValueNone || valueID == CSSValueHorizontal) 778 return true; 779 break; 780 case CSSPropertyWebkitTextEmphasisPosition: 781 if (valueID == CSSValueOver || valueID == CSSValueUnder) 782 return true; 783 break; 784 case CSSPropertyWebkitTextSecurity: 785 // disc | circle | square | none | inherit 786 if (valueID == CSSValueDisc || valueID == CSSValueCircle || valueID == CSSValueSquare || valueID == CSSValueNone) 787 return true; 788 break; 789 case CSSPropertyWebkitTransformStyle: 790 if (valueID == CSSValueFlat || valueID == CSSValuePreserve3d) 791 return true; 792 break; 793 case CSSPropertyWebkitUserDrag: // auto | none | element 794 if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueElement) 795 return true; 796 break; 797 case CSSPropertyWebkitUserModify: // read-only | read-write 798 if (valueID == CSSValueReadOnly || valueID == CSSValueReadWrite || valueID == CSSValueReadWritePlaintextOnly) 799 return true; 800 break; 801 case CSSPropertyWebkitUserSelect: // auto | none | text | all 802 if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueText || valueID == CSSValueAll) 803 return true; 804 break; 805 case CSSPropertyWebkitWrapFlow: 806 if (!RuntimeEnabledFeatures::cssExclusionsEnabled()) 807 return false; 808 if (valueID == CSSValueAuto || valueID == CSSValueBoth || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueMaximum || valueID == CSSValueClear) 809 return true; 810 break; 811 case CSSPropertyWebkitWrapThrough: 812 if (!RuntimeEnabledFeatures::cssExclusionsEnabled()) 813 return false; 814 if (valueID == CSSValueWrap || valueID == CSSValueNone) 815 return true; 816 break; 817 case CSSPropertyWebkitWritingMode: 818 if (valueID >= CSSValueHorizontalTb && valueID <= CSSValueHorizontalBt) 819 return true; 820 break; 821 case CSSPropertyWhiteSpace: // normal | pre | nowrap | inherit 822 if (valueID == CSSValueNormal || valueID == CSSValuePre || valueID == CSSValuePreWrap || valueID == CSSValuePreLine || valueID == CSSValueNowrap) 823 return true; 824 break; 825 case CSSPropertyWordBreak: // normal | break-all | break-word (this is a custom extension) 826 if (valueID == CSSValueNormal || valueID == CSSValueBreakAll || valueID == CSSValueBreakWord) 827 return true; 828 break; 829 default: 830 ASSERT_NOT_REACHED(); 831 return false; 832 } 833 return false; 834 } 835 836 static inline bool isKeywordPropertyID(CSSPropertyID propertyId) 837 { 838 switch (propertyId) { 839 case CSSPropertyMixBlendMode: 840 case CSSPropertyIsolation: 841 case CSSPropertyBorderBottomStyle: 842 case CSSPropertyBorderCollapse: 843 case CSSPropertyBorderLeftStyle: 844 case CSSPropertyBorderRightStyle: 845 case CSSPropertyBorderTopStyle: 846 case CSSPropertyBoxSizing: 847 case CSSPropertyCaptionSide: 848 case CSSPropertyClear: 849 case CSSPropertyDirection: 850 case CSSPropertyDisplay: 851 case CSSPropertyEmptyCells: 852 case CSSPropertyFloat: 853 case CSSPropertyFontStyle: 854 case CSSPropertyImageRendering: 855 case CSSPropertyListStylePosition: 856 case CSSPropertyListStyleType: 857 case CSSPropertyObjectFit: 858 case CSSPropertyOutlineStyle: 859 case CSSPropertyOverflowWrap: 860 case CSSPropertyOverflowX: 861 case CSSPropertyOverflowY: 862 case CSSPropertyPageBreakAfter: 863 case CSSPropertyPageBreakBefore: 864 case CSSPropertyPageBreakInside: 865 case CSSPropertyPointerEvents: 866 case CSSPropertyPosition: 867 case CSSPropertyResize: 868 case CSSPropertySpeak: 869 case CSSPropertyTableLayout: 870 case CSSPropertyTextAlignLast: 871 case CSSPropertyTextJustify: 872 case CSSPropertyTextLineThroughMode: 873 case CSSPropertyTextLineThroughStyle: 874 case CSSPropertyTextOverflow: 875 case CSSPropertyTextOverlineMode: 876 case CSSPropertyTextOverlineStyle: 877 case CSSPropertyTextRendering: 878 case CSSPropertyTextTransform: 879 case CSSPropertyTextUnderlineMode: 880 case CSSPropertyTextUnderlineStyle: 881 case CSSPropertyTouchActionDelay: 882 case CSSPropertyVisibility: 883 case CSSPropertyWebkitAppearance: 884 case CSSPropertyWebkitBackfaceVisibility: 885 case CSSPropertyWebkitBorderAfterStyle: 886 case CSSPropertyWebkitBorderBeforeStyle: 887 case CSSPropertyWebkitBorderEndStyle: 888 case CSSPropertyWebkitBorderFit: 889 case CSSPropertyWebkitBorderStartStyle: 890 case CSSPropertyWebkitBoxAlign: 891 case CSSPropertyWebkitBoxDecorationBreak: 892 case CSSPropertyWebkitBoxDirection: 893 case CSSPropertyWebkitBoxLines: 894 case CSSPropertyWebkitBoxOrient: 895 case CSSPropertyWebkitBoxPack: 896 case CSSPropertyInternalCallback: 897 case CSSPropertyWebkitColumnBreakAfter: 898 case CSSPropertyWebkitColumnBreakBefore: 899 case CSSPropertyWebkitColumnBreakInside: 900 case CSSPropertyColumnFill: 901 case CSSPropertyWebkitColumnRuleStyle: 902 case CSSPropertyAlignContent: 903 case CSSPropertyAlignItems: 904 case CSSPropertyAlignSelf: 905 case CSSPropertyFlexDirection: 906 case CSSPropertyFlexWrap: 907 case CSSPropertyJustifyContent: 908 case CSSPropertyFontKerning: 909 case CSSPropertyWebkitFontSmoothing: 910 case CSSPropertyGridAutoFlow: 911 case CSSPropertyWebkitLineAlign: 912 case CSSPropertyWebkitLineBreak: 913 case CSSPropertyWebkitLineSnap: 914 case CSSPropertyWebkitMarginAfterCollapse: 915 case CSSPropertyWebkitMarginBeforeCollapse: 916 case CSSPropertyWebkitMarginBottomCollapse: 917 case CSSPropertyWebkitMarginTopCollapse: 918 case CSSPropertyInternalMarqueeDirection: 919 case CSSPropertyInternalMarqueeStyle: 920 case CSSPropertyWebkitPrintColorAdjust: 921 case CSSPropertyWebkitRegionBreakAfter: 922 case CSSPropertyWebkitRegionBreakBefore: 923 case CSSPropertyWebkitRegionBreakInside: 924 case CSSPropertyWebkitRegionFragment: 925 case CSSPropertyWebkitRtlOrdering: 926 case CSSPropertyWebkitRubyPosition: 927 case CSSPropertyWebkitTextCombine: 928 case CSSPropertyWebkitTextEmphasisPosition: 929 case CSSPropertyWebkitTextSecurity: 930 case CSSPropertyWebkitTransformStyle: 931 case CSSPropertyWebkitUserDrag: 932 case CSSPropertyWebkitUserModify: 933 case CSSPropertyWebkitUserSelect: 934 case CSSPropertyWebkitWrapFlow: 935 case CSSPropertyWebkitWrapThrough: 936 case CSSPropertyWebkitWritingMode: 937 case CSSPropertyWhiteSpace: 938 case CSSPropertyWordBreak: 939 case CSSPropertyWordWrap: 940 return true; 941 default: 942 return false; 943 } 944 } 945 946 static bool parseKeywordValue(MutableStylePropertySet* declaration, CSSPropertyID propertyId, const String& string, bool important, const CSSParserContext& parserContext) 947 { 948 ASSERT(!string.isEmpty()); 949 950 if (!isKeywordPropertyID(propertyId)) { 951 // All properties accept the values of "initial" and "inherit". 952 String lowerCaseString = string.lower(); 953 if (lowerCaseString != "initial" && lowerCaseString != "inherit") 954 return false; 955 956 // Parse initial/inherit shorthands using the CSSParser. 957 if (shorthandForProperty(propertyId).length()) 958 return false; 959 } 960 961 CSSParserString cssString; 962 cssString.init(string); 963 CSSValueID valueID = cssValueKeywordID(cssString); 964 965 if (!valueID) 966 return false; 967 968 RefPtr<CSSValue> value; 969 if (valueID == CSSValueInherit) 970 value = cssValuePool().createInheritedValue(); 971 else if (valueID == CSSValueInitial) 972 value = cssValuePool().createExplicitInitialValue(); 973 else if (isValidKeywordPropertyAndValue(propertyId, valueID, parserContext)) 974 value = cssValuePool().createIdentifierValue(valueID); 975 else 976 return false; 977 978 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 979 return true; 980 } 981 982 template <typename CharacterType> 983 static bool parseTransformTranslateArguments(CSSTransformValue* transformValue, CharacterType* characters, unsigned length, unsigned start, unsigned expectedCount) 984 { 985 while (expectedCount) { 986 size_t end = WTF::find(characters, length, expectedCount == 1 ? ')' : ',', start); 987 if (end == kNotFound || (expectedCount == 1 && end != length - 1)) 988 return false; 989 unsigned argumentLength = end - start; 990 CSSPrimitiveValue::UnitTypes unit = CSSPrimitiveValue::CSS_NUMBER; 991 double number; 992 if (!parseSimpleLength(characters + start, argumentLength, unit, number)) 993 return false; 994 if (unit != CSSPrimitiveValue::CSS_PX && (number || unit != CSSPrimitiveValue::CSS_NUMBER)) 995 return false; 996 transformValue->append(cssValuePool().createValue(number, CSSPrimitiveValue::CSS_PX)); 997 start = end + 1; 998 --expectedCount; 999 } 1000 return true; 1001 } 1002 1003 static bool parseTranslateTransformValue(MutableStylePropertySet* properties, CSSPropertyID propertyID, const String& string, bool important) 1004 { 1005 if (propertyID != CSSPropertyWebkitTransform) 1006 return false; 1007 static const unsigned shortestValidTransformStringLength = 12; 1008 static const unsigned likelyMultipartTransformStringLengthCutoff = 32; 1009 if (string.length() < shortestValidTransformStringLength || string.length() > likelyMultipartTransformStringLengthCutoff) 1010 return false; 1011 if (!string.startsWith("translate", false)) 1012 return false; 1013 UChar c9 = toASCIILower(string[9]); 1014 UChar c10 = toASCIILower(string[10]); 1015 1016 CSSTransformValue::TransformOperationType transformType; 1017 unsigned expectedArgumentCount = 1; 1018 unsigned argumentStart = 11; 1019 if (c9 == 'x' && c10 == '(') 1020 transformType = CSSTransformValue::TranslateXTransformOperation; 1021 else if (c9 == 'y' && c10 == '(') 1022 transformType = CSSTransformValue::TranslateYTransformOperation; 1023 else if (c9 == 'z' && c10 == '(') 1024 transformType = CSSTransformValue::TranslateZTransformOperation; 1025 else if (c9 == '(') { 1026 transformType = CSSTransformValue::TranslateTransformOperation; 1027 expectedArgumentCount = 2; 1028 argumentStart = 10; 1029 } else if (c9 == '3' && c10 == 'd' && string[11] == '(') { 1030 transformType = CSSTransformValue::Translate3DTransformOperation; 1031 expectedArgumentCount = 3; 1032 argumentStart = 12; 1033 } else 1034 return false; 1035 1036 RefPtr<CSSTransformValue> transformValue = CSSTransformValue::create(transformType); 1037 bool success; 1038 if (string.is8Bit()) 1039 success = parseTransformTranslateArguments(transformValue.get(), string.characters8(), string.length(), argumentStart, expectedArgumentCount); 1040 else 1041 success = parseTransformTranslateArguments(transformValue.get(), string.characters16(), string.length(), argumentStart, expectedArgumentCount); 1042 if (!success) 1043 return false; 1044 RefPtr<CSSValueList> result = CSSValueList::createSpaceSeparated(); 1045 result->append(transformValue.release()); 1046 properties->addParsedProperty(CSSProperty(CSSPropertyWebkitTransform, result.release(), important)); 1047 return true; 1048 } 1049 1050 PassRefPtr<CSSValueList> CSSParser::parseFontFaceValue(const AtomicString& string) 1051 { 1052 if (string.isEmpty()) 1053 return 0; 1054 RefPtr<MutableStylePropertySet> dummyStyle = MutableStylePropertySet::create(); 1055 if (!parseValue(dummyStyle.get(), CSSPropertyFontFamily, string, false, HTMLQuirksMode, 0)) 1056 return 0; 1057 1058 RefPtr<CSSValue> fontFamily = dummyStyle->getPropertyCSSValue(CSSPropertyFontFamily); 1059 if (!fontFamily->isValueList()) 1060 return 0; 1061 1062 return toCSSValueList(dummyStyle->getPropertyCSSValue(CSSPropertyFontFamily).get()); 1063 } 1064 1065 bool CSSParser::parseValue(MutableStylePropertySet* declaration, CSSPropertyID propertyID, const String& string, bool important, const Document& document) 1066 { 1067 ASSERT(!string.isEmpty()); 1068 1069 CSSParserContext context(document); 1070 1071 if (parseSimpleLengthValue(declaration, propertyID, string, important, context.mode())) 1072 return true; 1073 if (parseColorValue(declaration, propertyID, string, important, context.mode())) 1074 return true; 1075 if (parseKeywordValue(declaration, propertyID, string, important, context)) 1076 return true; 1077 1078 CSSParser parser(context, UseCounter::getFrom(&document)); 1079 return parser.parseValue(declaration, propertyID, string, important, static_cast<StyleSheetContents*>(0)); 1080 } 1081 1082 bool CSSParser::parseValue(MutableStylePropertySet* declaration, CSSPropertyID propertyID, const String& string, bool important, CSSParserMode cssParserMode, StyleSheetContents* contextStyleSheet) 1083 { 1084 ASSERT(!string.isEmpty()); 1085 if (parseSimpleLengthValue(declaration, propertyID, string, important, cssParserMode)) 1086 return true; 1087 if (parseColorValue(declaration, propertyID, string, important, cssParserMode)) 1088 return true; 1089 1090 CSSParserContext context(cssParserMode); 1091 if (contextStyleSheet) { 1092 context = contextStyleSheet->parserContext(); 1093 context.setMode(cssParserMode); 1094 } 1095 1096 if (parseKeywordValue(declaration, propertyID, string, important, context)) 1097 return true; 1098 if (parseTranslateTransformValue(declaration, propertyID, string, important)) 1099 return true; 1100 1101 CSSParser parser(context); 1102 return parser.parseValue(declaration, propertyID, string, important, contextStyleSheet); 1103 } 1104 1105 bool CSSParser::parseValue(MutableStylePropertySet* declaration, CSSPropertyID propertyID, const String& string, bool important, StyleSheetContents* contextStyleSheet) 1106 { 1107 // FIXME: Check RuntimeCSSEnabled::isPropertyEnabled or isValueEnabledForProperty. 1108 1109 if (m_useCounter) 1110 m_useCounter->count(m_context, propertyID); 1111 1112 setStyleSheet(contextStyleSheet); 1113 1114 setupParser("@-internal-value ", string, ""); 1115 1116 m_id = propertyID; 1117 m_important = important; 1118 1119 { 1120 StyleDeclarationScope scope(this, declaration); 1121 cssyyparse(this); 1122 } 1123 1124 m_rule = 0; 1125 m_id = CSSPropertyInvalid; 1126 1127 bool ok = false; 1128 if (m_hasFontFaceOnlyValues) 1129 deleteFontFaceOnlyValues(); 1130 if (!m_parsedProperties.isEmpty()) { 1131 ok = true; 1132 declaration->addParsedProperties(m_parsedProperties); 1133 clearProperties(); 1134 } 1135 1136 return ok; 1137 } 1138 1139 // The color will only be changed when string contains a valid CSS color, so callers 1140 // can set it to a default color and ignore the boolean result. 1141 bool CSSParser::parseColor(RGBA32& color, const String& string, bool strict) 1142 { 1143 // First try creating a color specified by name, rgba(), rgb() or "#" syntax. 1144 if (fastParseColor(color, string, strict)) 1145 return true; 1146 1147 CSSParser parser(HTMLStandardMode); 1148 1149 // In case the fast-path parser didn't understand the color, try the full parser. 1150 if (!parser.parseColor(string)) 1151 return false; 1152 1153 CSSValue* value = parser.m_parsedProperties.first().value(); 1154 if (!value->isPrimitiveValue()) 1155 return false; 1156 1157 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 1158 if (!primitiveValue->isRGBColor()) 1159 return false; 1160 1161 color = primitiveValue->getRGBA32Value(); 1162 return true; 1163 } 1164 1165 bool CSSParser::parseColor(const String& string) 1166 { 1167 setupParser("@-internal-decls color:", string, ""); 1168 cssyyparse(this); 1169 m_rule = 0; 1170 1171 return !m_parsedProperties.isEmpty() && m_parsedProperties.first().id() == CSSPropertyColor; 1172 } 1173 1174 bool CSSParser::parseSystemColor(RGBA32& color, const String& string, Document* document) 1175 { 1176 if (!document || !document->page()) 1177 return false; 1178 1179 CSSParserString cssColor; 1180 cssColor.init(string); 1181 CSSValueID id = cssValueKeywordID(cssColor); 1182 if (id <= 0) 1183 return false; 1184 1185 color = RenderTheme::theme().systemColor(id).rgb(); 1186 return true; 1187 } 1188 1189 void CSSParser::parseSelector(const String& string, CSSSelectorList& selectorList) 1190 { 1191 m_selectorListForParseSelector = &selectorList; 1192 1193 setupParser("@-internal-selector ", string, ""); 1194 1195 cssyyparse(this); 1196 1197 m_selectorListForParseSelector = 0; 1198 } 1199 1200 PassRefPtr<ImmutableStylePropertySet> CSSParser::parseInlineStyleDeclaration(const String& string, Element* element) 1201 { 1202 Document& document = element->document(); 1203 CSSParserContext context = document.elementSheet()->contents()->parserContext(); 1204 context.setMode((element->isHTMLElement() && !document.inQuirksMode()) ? HTMLStandardMode : HTMLQuirksMode); 1205 return CSSParser(context, UseCounter::getFrom(&document)).parseDeclaration(string, document.elementSheet()->contents()); 1206 } 1207 1208 PassRefPtr<ImmutableStylePropertySet> CSSParser::parseDeclaration(const String& string, StyleSheetContents* contextStyleSheet) 1209 { 1210 setStyleSheet(contextStyleSheet); 1211 1212 setupParser("@-internal-decls ", string, ""); 1213 cssyyparse(this); 1214 m_rule = 0; 1215 1216 if (m_hasFontFaceOnlyValues) 1217 deleteFontFaceOnlyValues(); 1218 1219 RefPtr<ImmutableStylePropertySet> style = createStylePropertySet(); 1220 clearProperties(); 1221 return style.release(); 1222 } 1223 1224 1225 bool CSSParser::parseDeclaration(MutableStylePropertySet* declaration, const String& string, SourceDataHandler* sourceDataHandler, StyleSheetContents* contextStyleSheet) 1226 { 1227 setStyleSheet(contextStyleSheet); 1228 1229 m_sourceDataHandler = sourceDataHandler; 1230 1231 setupParser("@-internal-decls ", string, ""); 1232 if (m_sourceDataHandler) { 1233 m_sourceDataHandler->startRuleHeader(CSSRuleSourceData::STYLE_RULE, 0); 1234 m_sourceDataHandler->endRuleHeader(1); 1235 m_sourceDataHandler->startRuleBody(0); 1236 } 1237 1238 { 1239 StyleDeclarationScope scope(this, declaration); 1240 cssyyparse(this); 1241 } 1242 1243 m_rule = 0; 1244 1245 bool ok = false; 1246 if (m_hasFontFaceOnlyValues) 1247 deleteFontFaceOnlyValues(); 1248 if (!m_parsedProperties.isEmpty()) { 1249 ok = true; 1250 declaration->addParsedProperties(m_parsedProperties); 1251 clearProperties(); 1252 } 1253 1254 if (m_sourceDataHandler) 1255 m_sourceDataHandler->endRuleBody(string.length(), false); 1256 m_sourceDataHandler = 0; 1257 1258 return ok; 1259 } 1260 1261 PassRefPtr<MediaQuerySet> CSSParser::parseMediaQueryList(const String& string) 1262 { 1263 ASSERT(!m_mediaList); 1264 1265 // can't use { because tokenizer state switches from mediaquery to initial state when it sees { token. 1266 // instead insert one " " (which is caught by maybe_space in CSSGrammar.y) 1267 setupParser("@-internal-medialist ", string, ""); 1268 cssyyparse(this); 1269 1270 ASSERT(m_mediaList); 1271 return m_mediaList.release(); 1272 } 1273 1274 static inline void filterProperties(bool important, const CSSParser::ParsedPropertyVector& input, Vector<CSSProperty, 256>& output, size_t& unusedEntries, BitArray<numCSSProperties>& seenProperties, HashSet<AtomicString>& seenVariables) 1275 { 1276 // Add properties in reverse order so that highest priority definitions are reached first. Duplicate definitions can then be ignored when found. 1277 for (int i = input.size() - 1; i >= 0; --i) { 1278 const CSSProperty& property = input[i]; 1279 if (property.isImportant() != important) 1280 continue; 1281 if (property.id() == CSSPropertyVariable) { 1282 const AtomicString& name = toCSSVariableValue(property.value())->name(); 1283 if (!seenVariables.add(name).isNewEntry) 1284 continue; 1285 output[--unusedEntries] = property; 1286 continue; 1287 } 1288 const unsigned propertyIDIndex = property.id() - firstCSSProperty; 1289 if (seenProperties.get(propertyIDIndex)) 1290 continue; 1291 seenProperties.set(propertyIDIndex); 1292 output[--unusedEntries] = property; 1293 } 1294 } 1295 1296 PassRefPtr<ImmutableStylePropertySet> CSSParser::createStylePropertySet() 1297 { 1298 BitArray<numCSSProperties> seenProperties; 1299 size_t unusedEntries = m_parsedProperties.size(); 1300 Vector<CSSProperty, 256> results(unusedEntries); 1301 1302 // Important properties have higher priority, so add them first. Duplicate definitions can then be ignored when found. 1303 HashSet<AtomicString> seenVariables; 1304 filterProperties(true, m_parsedProperties, results, unusedEntries, seenProperties, seenVariables); 1305 filterProperties(false, m_parsedProperties, results, unusedEntries, seenProperties, seenVariables); 1306 if (unusedEntries) 1307 results.remove(0, unusedEntries); 1308 1309 CSSParserMode mode = inViewport() ? CSSViewportRuleMode : m_context.mode(); 1310 1311 return ImmutableStylePropertySet::create(results.data(), results.size(), mode); 1312 } 1313 1314 void CSSParser::addPropertyWithPrefixingVariant(CSSPropertyID propId, PassRefPtr<CSSValue> value, bool important, bool implicit) 1315 { 1316 RefPtr<CSSValue> val = value.get(); 1317 addProperty(propId, value, important, implicit); 1318 1319 CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(propId); 1320 if (prefixingVariant == propId) 1321 return; 1322 1323 if (m_currentShorthand) { 1324 // We can't use ShorthandScope here as we can already be inside one (e.g we are parsing CSSTransition). 1325 m_currentShorthand = prefixingVariantForPropertyId(m_currentShorthand); 1326 addProperty(prefixingVariant, val.release(), important, implicit); 1327 m_currentShorthand = prefixingVariantForPropertyId(m_currentShorthand); 1328 } else { 1329 addProperty(prefixingVariant, val.release(), important, implicit); 1330 } 1331 } 1332 1333 void CSSParser::addProperty(CSSPropertyID propId, PassRefPtr<CSSValue> value, bool important, bool implicit) 1334 { 1335 CSSPrimitiveValue* primitiveValue = value->isPrimitiveValue() ? toCSSPrimitiveValue(value.get()) : 0; 1336 // This property doesn't belong to a shorthand or is a CSS variable (which will be resolved later). 1337 if (!m_currentShorthand || (primitiveValue && primitiveValue->isVariableName())) { 1338 m_parsedProperties.append(CSSProperty(propId, value, important, false, CSSPropertyInvalid, m_implicitShorthand || implicit)); 1339 return; 1340 } 1341 1342 Vector<StylePropertyShorthand, 4> shorthands; 1343 getMatchingShorthandsForLonghand(propId, &shorthands); 1344 // The longhand does not belong to multiple shorthands. 1345 if (shorthands.size() == 1) 1346 m_parsedProperties.append(CSSProperty(propId, value, important, true, CSSPropertyInvalid, m_implicitShorthand || implicit)); 1347 else 1348 m_parsedProperties.append(CSSProperty(propId, value, important, true, indexOfShorthandForLonghand(m_currentShorthand, shorthands), m_implicitShorthand || implicit)); 1349 } 1350 1351 void CSSParser::rollbackLastProperties(int num) 1352 { 1353 ASSERT(num >= 0); 1354 ASSERT(m_parsedProperties.size() >= static_cast<unsigned>(num)); 1355 m_parsedProperties.shrink(m_parsedProperties.size() - num); 1356 } 1357 1358 void CSSParser::clearProperties() 1359 { 1360 m_parsedProperties.clear(); 1361 m_numParsedPropertiesBeforeMarginBox = INVALID_NUM_PARSED_PROPERTIES; 1362 m_hasFontFaceOnlyValues = false; 1363 } 1364 1365 // FIXME: Move to CSSParserContext? 1366 KURL CSSParser::completeURL(const CSSParserContext& context, const String& url) 1367 { 1368 if (url.isNull()) 1369 return KURL(); 1370 if (context.charset().isEmpty()) 1371 return KURL(context.baseURL(), url); 1372 return KURL(context.baseURL(), url, context.charset()); 1373 } 1374 1375 KURL CSSParser::completeURL(const String& url) const 1376 { 1377 return completeURL(m_context, url); 1378 } 1379 1380 bool CSSParser::validCalculationUnit(CSSParserValue* value, Units unitflags, ReleaseParsedCalcValueCondition releaseCalc) 1381 { 1382 bool mustBeNonNegative = unitflags & FNonNeg; 1383 1384 if (!parseCalculation(value, mustBeNonNegative ? ValueRangeNonNegative : ValueRangeAll)) 1385 return false; 1386 1387 bool b = false; 1388 switch (m_parsedCalculation->category()) { 1389 case CalcLength: 1390 b = (unitflags & FLength); 1391 break; 1392 case CalcPercent: 1393 b = (unitflags & FPercent); 1394 if (b && mustBeNonNegative && m_parsedCalculation->isNegative()) 1395 b = false; 1396 break; 1397 case CalcNumber: 1398 b = (unitflags & FNumber); 1399 if (!b && (unitflags & FInteger) && m_parsedCalculation->isInt()) 1400 b = true; 1401 if (b && mustBeNonNegative && m_parsedCalculation->isNegative()) 1402 b = false; 1403 break; 1404 case CalcPercentLength: 1405 b = (unitflags & FPercent) && (unitflags & FLength); 1406 break; 1407 case CalcPercentNumber: 1408 b = (unitflags & FPercent) && (unitflags & FNumber); 1409 break; 1410 case CalcVariable: 1411 b = true; 1412 break; 1413 case CalcOther: 1414 break; 1415 } 1416 if (!b || releaseCalc == ReleaseParsedCalcValue) 1417 m_parsedCalculation.release(); 1418 return b; 1419 } 1420 1421 inline bool CSSParser::shouldAcceptUnitLessValues(CSSParserValue* value, Units unitflags, CSSParserMode cssParserMode) 1422 { 1423 // Quirks mode and presentation attributes accept unit less values. 1424 return (unitflags & (FLength | FAngle | FTime)) && (!value->fValue || isUnitLessLengthParsingEnabledForMode(cssParserMode)); 1425 } 1426 1427 bool CSSParser::validUnit(CSSParserValue* value, Units unitflags, CSSParserMode cssParserMode, ReleaseParsedCalcValueCondition releaseCalc) 1428 { 1429 if (isCalculation(value)) 1430 return validCalculationUnit(value, unitflags, releaseCalc); 1431 1432 bool b = false; 1433 switch (value->unit) { 1434 case CSSPrimitiveValue::CSS_VARIABLE_NAME: 1435 // Variables are checked at the point they are dereferenced because unit type is not available here. 1436 b = true; 1437 break; 1438 case CSSPrimitiveValue::CSS_NUMBER: 1439 b = (unitflags & FNumber); 1440 if (!b && shouldAcceptUnitLessValues(value, unitflags, cssParserMode)) { 1441 value->unit = (unitflags & FLength) ? CSSPrimitiveValue::CSS_PX : 1442 ((unitflags & FAngle) ? CSSPrimitiveValue::CSS_DEG : CSSPrimitiveValue::CSS_MS); 1443 b = true; 1444 } 1445 if (!b && (unitflags & FInteger) && value->isInt) 1446 b = true; 1447 if (!b && (unitflags & FPositiveInteger) && value->isInt && value->fValue > 0) 1448 b = true; 1449 break; 1450 case CSSPrimitiveValue::CSS_PERCENTAGE: 1451 b = (unitflags & FPercent); 1452 break; 1453 case CSSParserValue::Q_EMS: 1454 case CSSPrimitiveValue::CSS_EMS: 1455 case CSSPrimitiveValue::CSS_REMS: 1456 case CSSPrimitiveValue::CSS_CHS: 1457 case CSSPrimitiveValue::CSS_EXS: 1458 case CSSPrimitiveValue::CSS_PX: 1459 case CSSPrimitiveValue::CSS_CM: 1460 case CSSPrimitiveValue::CSS_MM: 1461 case CSSPrimitiveValue::CSS_IN: 1462 case CSSPrimitiveValue::CSS_PT: 1463 case CSSPrimitiveValue::CSS_PC: 1464 case CSSPrimitiveValue::CSS_VW: 1465 case CSSPrimitiveValue::CSS_VH: 1466 case CSSPrimitiveValue::CSS_VMIN: 1467 case CSSPrimitiveValue::CSS_VMAX: 1468 b = (unitflags & FLength); 1469 break; 1470 case CSSPrimitiveValue::CSS_MS: 1471 case CSSPrimitiveValue::CSS_S: 1472 b = (unitflags & FTime); 1473 break; 1474 case CSSPrimitiveValue::CSS_DEG: 1475 case CSSPrimitiveValue::CSS_RAD: 1476 case CSSPrimitiveValue::CSS_GRAD: 1477 case CSSPrimitiveValue::CSS_TURN: 1478 b = (unitflags & FAngle); 1479 break; 1480 case CSSPrimitiveValue::CSS_DPPX: 1481 case CSSPrimitiveValue::CSS_DPI: 1482 case CSSPrimitiveValue::CSS_DPCM: 1483 b = (unitflags & FResolution); 1484 break; 1485 case CSSPrimitiveValue::CSS_HZ: 1486 case CSSPrimitiveValue::CSS_KHZ: 1487 case CSSPrimitiveValue::CSS_DIMENSION: 1488 default: 1489 break; 1490 } 1491 if (b && unitflags & FNonNeg && value->fValue < 0) 1492 b = false; 1493 return b; 1494 } 1495 1496 inline PassRefPtr<CSSPrimitiveValue> CSSParser::createPrimitiveNumericValue(CSSParserValue* value) 1497 { 1498 if (value->unit == CSSPrimitiveValue::CSS_VARIABLE_NAME) 1499 return createPrimitiveVariableNameValue(value); 1500 1501 if (m_parsedCalculation) { 1502 ASSERT(isCalculation(value)); 1503 return CSSPrimitiveValue::create(m_parsedCalculation.release()); 1504 } 1505 1506 ASSERT((value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) 1507 || (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_CHS) 1508 || (value->unit >= CSSPrimitiveValue::CSS_VW && value->unit <= CSSPrimitiveValue::CSS_VMAX) 1509 || (value->unit >= CSSPrimitiveValue::CSS_DPPX && value->unit <= CSSPrimitiveValue::CSS_DPCM)); 1510 return cssValuePool().createValue(value->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(value->unit)); 1511 } 1512 1513 inline PassRefPtr<CSSPrimitiveValue> CSSParser::createPrimitiveStringValue(CSSParserValue* value) 1514 { 1515 ASSERT(value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT); 1516 return cssValuePool().createValue(value->string, CSSPrimitiveValue::CSS_STRING); 1517 } 1518 1519 inline PassRefPtr<CSSPrimitiveValue> CSSParser::createPrimitiveVariableNameValue(CSSParserValue* value) 1520 { 1521 ASSERT(value->unit == CSSPrimitiveValue::CSS_VARIABLE_NAME); 1522 return CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_VARIABLE_NAME); 1523 } 1524 1525 static inline bool isComma(CSSParserValue* value) 1526 { 1527 return value && value->unit == CSSParserValue::Operator && value->iValue == ','; 1528 } 1529 1530 static inline bool isForwardSlashOperator(CSSParserValue* value) 1531 { 1532 ASSERT(value); 1533 return value->unit == CSSParserValue::Operator && value->iValue == '/'; 1534 } 1535 1536 static bool isGeneratedImageValue(CSSParserValue* val) 1537 { 1538 if (val->unit != CSSParserValue::Function) 1539 return false; 1540 1541 return equalIgnoringCase(val->function->name, "-webkit-gradient(") 1542 || equalIgnoringCase(val->function->name, "-webkit-linear-gradient(") 1543 || equalIgnoringCase(val->function->name, "linear-gradient(") 1544 || equalIgnoringCase(val->function->name, "-webkit-repeating-linear-gradient(") 1545 || equalIgnoringCase(val->function->name, "repeating-linear-gradient(") 1546 || equalIgnoringCase(val->function->name, "-webkit-radial-gradient(") 1547 || equalIgnoringCase(val->function->name, "radial-gradient(") 1548 || equalIgnoringCase(val->function->name, "-webkit-repeating-radial-gradient(") 1549 || equalIgnoringCase(val->function->name, "repeating-radial-gradient(") 1550 || equalIgnoringCase(val->function->name, "-webkit-canvas(") 1551 || equalIgnoringCase(val->function->name, "-webkit-cross-fade("); 1552 } 1553 1554 bool CSSParser::validWidthOrHeight(CSSParserValue* value) 1555 { 1556 int id = value->id; 1557 if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic || id == CSSValueWebkitMinContent || id == CSSValueWebkitMaxContent || id == CSSValueWebkitFillAvailable || id == CSSValueWebkitFitContent) 1558 return true; 1559 return !id && validUnit(value, FLength | FPercent | FNonNeg); 1560 } 1561 1562 inline PassRefPtr<CSSPrimitiveValue> CSSParser::parseValidPrimitive(CSSValueID identifier, CSSParserValue* value) 1563 { 1564 if (identifier) 1565 return cssValuePool().createIdentifierValue(identifier); 1566 if (value->unit == CSSPrimitiveValue::CSS_STRING) 1567 return createPrimitiveStringValue(value); 1568 if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) 1569 return createPrimitiveNumericValue(value); 1570 if (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_CHS) 1571 return createPrimitiveNumericValue(value); 1572 if (value->unit >= CSSPrimitiveValue::CSS_VW && value->unit <= CSSPrimitiveValue::CSS_VMAX) 1573 return createPrimitiveNumericValue(value); 1574 if (value->unit >= CSSPrimitiveValue::CSS_DPPX && value->unit <= CSSPrimitiveValue::CSS_DPCM) 1575 return createPrimitiveNumericValue(value); 1576 if (value->unit == CSSPrimitiveValue::CSS_VARIABLE_NAME) 1577 return createPrimitiveVariableNameValue(value); 1578 if (value->unit >= CSSParserValue::Q_EMS) 1579 return CSSPrimitiveValue::createAllowingMarginQuirk(value->fValue, CSSPrimitiveValue::CSS_EMS); 1580 if (isCalculation(value)) 1581 return CSSPrimitiveValue::create(m_parsedCalculation.release()); 1582 1583 return 0; 1584 } 1585 1586 void CSSParser::addExpandedPropertyForValue(CSSPropertyID propId, PassRefPtr<CSSValue> prpValue, bool important) 1587 { 1588 const StylePropertyShorthand& shorthand = shorthandForProperty(propId); 1589 unsigned shorthandLength = shorthand.length(); 1590 if (!shorthandLength) { 1591 addProperty(propId, prpValue, important); 1592 return; 1593 } 1594 1595 RefPtr<CSSValue> value = prpValue; 1596 ShorthandScope scope(this, propId); 1597 const CSSPropertyID* longhands = shorthand.properties(); 1598 for (unsigned i = 0; i < shorthandLength; ++i) 1599 addProperty(longhands[i], value, important); 1600 } 1601 1602 void CSSParser::setCurrentProperty(CSSPropertyID propId) 1603 { 1604 m_id = propId; 1605 } 1606 1607 bool CSSParser::parseValue(CSSPropertyID propId, bool important) 1608 { 1609 if (!isInternalPropertyAndValueParsingEnabledForMode(m_context.mode()) && isInternalProperty(propId)) 1610 return false; 1611 1612 // We don't count the UA style sheet in our statistics. 1613 if (m_useCounter) 1614 m_useCounter->count(m_context, propId); 1615 1616 if (!m_valueList) 1617 return false; 1618 1619 CSSParserValue* value = m_valueList->current(); 1620 1621 if (!value) 1622 return false; 1623 1624 if (inViewport()) { 1625 // Allow @viewport rules from UA stylesheets even if the feature is disabled. 1626 if (!RuntimeEnabledFeatures::cssViewportEnabled() && !isUASheetBehavior(m_context.mode())) 1627 return false; 1628 1629 return parseViewportProperty(propId, important); 1630 } 1631 1632 // Note: m_parsedCalculation is used to pass the calc value to validUnit and then cleared at the end of this function. 1633 // FIXME: This is to avoid having to pass parsedCalc to all validUnit callers. 1634 ASSERT(!m_parsedCalculation); 1635 1636 CSSValueID id = value->id; 1637 1638 int num = inShorthand() ? 1 : m_valueList->size(); 1639 1640 if (id == CSSValueInherit) { 1641 if (num != 1) 1642 return false; 1643 addExpandedPropertyForValue(propId, cssValuePool().createInheritedValue(), important); 1644 return true; 1645 } 1646 else if (id == CSSValueInitial) { 1647 if (num != 1) 1648 return false; 1649 addExpandedPropertyForValue(propId, cssValuePool().createExplicitInitialValue(), important); 1650 return true; 1651 } 1652 1653 if (!id && value->unit == CSSPrimitiveValue::CSS_VARIABLE_NAME && num == 1) { 1654 addProperty(propId, createPrimitiveVariableNameValue(value), important); 1655 m_valueList->next(); 1656 return true; 1657 } 1658 ASSERT(propId != CSSPropertyVariable); 1659 1660 if (isKeywordPropertyID(propId)) { 1661 if (!isValidKeywordPropertyAndValue(propId, id, m_context)) 1662 return false; 1663 if (m_valueList->next() && !inShorthand()) 1664 return false; 1665 addProperty(propId, cssValuePool().createIdentifierValue(id), important); 1666 return true; 1667 } 1668 1669 bool validPrimitive = false; 1670 RefPtr<CSSValue> parsedValue; 1671 1672 switch (propId) { 1673 case CSSPropertySize: // <length>{1,2} | auto | [ <page-size> || [ portrait | landscape] ] 1674 return parseSize(propId, important); 1675 1676 case CSSPropertyQuotes: // [<string> <string>]+ | none | inherit 1677 if (id) 1678 validPrimitive = true; 1679 else 1680 return parseQuotes(propId, important); 1681 break; 1682 case CSSPropertyUnicodeBidi: // normal | embed | bidi-override | isolate | isolate-override | plaintext | inherit 1683 if (id == CSSValueNormal 1684 || id == CSSValueEmbed 1685 || id == CSSValueBidiOverride 1686 || id == CSSValueWebkitIsolate 1687 || id == CSSValueWebkitIsolateOverride 1688 || id == CSSValueWebkitPlaintext) 1689 validPrimitive = true; 1690 break; 1691 1692 case CSSPropertyContent: // [ <string> | <uri> | <counter> | attr(X) | open-quote | 1693 // close-quote | no-open-quote | no-close-quote ]+ | inherit 1694 return parseContent(propId, important); 1695 1696 case CSSPropertyClip: // <shape> | auto | inherit 1697 if (id == CSSValueAuto) 1698 validPrimitive = true; 1699 else if (value->unit == CSSParserValue::Function) 1700 return parseClipShape(propId, important); 1701 break; 1702 1703 /* Start of supported CSS properties with validation. This is needed for parseShorthand to work 1704 * correctly and allows optimization in WebCore::applyRule(..) 1705 */ 1706 case CSSPropertyOverflow: { 1707 ShorthandScope scope(this, propId); 1708 if (num != 1 || !parseValue(CSSPropertyOverflowY, important)) 1709 return false; 1710 1711 RefPtr<CSSValue> overflowXValue; 1712 1713 // FIXME: -webkit-paged-x or -webkit-paged-y only apply to overflow-y. If this value has been 1714 // set using the shorthand, then for now overflow-x will default to auto, but once we implement 1715 // pagination controls, it should default to hidden. If the overflow-y value is anything but 1716 // paged-x or paged-y, then overflow-x and overflow-y should have the same value. 1717 if (id == CSSValueWebkitPagedX || id == CSSValueWebkitPagedY) 1718 overflowXValue = cssValuePool().createIdentifierValue(CSSValueAuto); 1719 else 1720 overflowXValue = m_parsedProperties.last().value(); 1721 addProperty(CSSPropertyOverflowX, overflowXValue.release(), important); 1722 return true; 1723 } 1724 1725 case CSSPropertyTextAlign: 1726 // left | right | center | justify | -webkit-left | -webkit-right | -webkit-center | -webkit-match-parent 1727 // | start | end | <string> | inherit | -webkit-auto (converted to start) 1728 if ((id >= CSSValueWebkitAuto && id <= CSSValueWebkitMatchParent) || id == CSSValueStart || id == CSSValueEnd 1729 || value->unit == CSSPrimitiveValue::CSS_STRING) 1730 validPrimitive = true; 1731 break; 1732 1733 case CSSPropertyFontWeight: { // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit 1734 if (m_valueList->size() != 1) 1735 return false; 1736 return parseFontWeight(important); 1737 } 1738 case CSSPropertyBorderSpacing: { 1739 if (num == 1) { 1740 ShorthandScope scope(this, CSSPropertyBorderSpacing); 1741 if (!parseValue(CSSPropertyWebkitBorderHorizontalSpacing, important)) 1742 return false; 1743 CSSValue* value = m_parsedProperties.last().value(); 1744 addProperty(CSSPropertyWebkitBorderVerticalSpacing, value, important); 1745 return true; 1746 } 1747 else if (num == 2) { 1748 ShorthandScope scope(this, CSSPropertyBorderSpacing); 1749 if (!parseValue(CSSPropertyWebkitBorderHorizontalSpacing, important) || !parseValue(CSSPropertyWebkitBorderVerticalSpacing, important)) 1750 return false; 1751 return true; 1752 } 1753 return false; 1754 } 1755 case CSSPropertyWebkitBorderHorizontalSpacing: 1756 case CSSPropertyWebkitBorderVerticalSpacing: 1757 validPrimitive = validUnit(value, FLength | FNonNeg); 1758 break; 1759 case CSSPropertyOutlineColor: // <color> | invert | inherit 1760 // Outline color has "invert" as additional keyword. 1761 // Also, we want to allow the special focus color even in HTML Standard parsing mode. 1762 if (id == CSSValueInvert || id == CSSValueWebkitFocusRingColor) { 1763 validPrimitive = true; 1764 break; 1765 } 1766 /* nobreak */ 1767 case CSSPropertyBackgroundColor: // <color> | inherit 1768 case CSSPropertyBorderTopColor: // <color> | inherit 1769 case CSSPropertyBorderRightColor: 1770 case CSSPropertyBorderBottomColor: 1771 case CSSPropertyBorderLeftColor: 1772 case CSSPropertyWebkitBorderStartColor: 1773 case CSSPropertyWebkitBorderEndColor: 1774 case CSSPropertyWebkitBorderBeforeColor: 1775 case CSSPropertyWebkitBorderAfterColor: 1776 case CSSPropertyColor: // <color> | inherit 1777 case CSSPropertyTextDecorationColor: // CSS3 text decoration colors 1778 case CSSPropertyTextLineThroughColor: 1779 case CSSPropertyTextUnderlineColor: 1780 case CSSPropertyTextOverlineColor: 1781 case CSSPropertyWebkitColumnRuleColor: 1782 case CSSPropertyWebkitTextEmphasisColor: 1783 case CSSPropertyWebkitTextFillColor: 1784 case CSSPropertyWebkitTextStrokeColor: 1785 if (propId == CSSPropertyTextDecorationColor 1786 && !RuntimeEnabledFeatures::css3TextDecorationsEnabled()) 1787 return false; 1788 1789 if ((id >= CSSValueAqua && id <= CSSValueWebkitText) || id == CSSValueMenu) { 1790 validPrimitive = isValueAllowedInMode(id, m_context.mode()); 1791 } else { 1792 parsedValue = parseColor(); 1793 if (parsedValue) 1794 m_valueList->next(); 1795 } 1796 break; 1797 1798 case CSSPropertyCursor: { 1799 // Grammar defined by CSS3 UI and modified by CSS4 images: 1800 // [ [<image> [<x> <y>]?,]* 1801 // [ auto | crosshair | default | pointer | progress | move | e-resize | ne-resize | 1802 // nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize | ew-resize | 1803 // ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | text | wait | help | 1804 // vertical-text | cell | context-menu | alias | copy | no-drop | not-allowed | -webkit-zoom-in 1805 // -webkit-zoom-out | all-scroll | -webkit-grab | -webkit-grabbing ] ] | inherit 1806 RefPtr<CSSValueList> list; 1807 while (value) { 1808 RefPtr<CSSValue> image = 0; 1809 if (value->unit == CSSPrimitiveValue::CSS_URI) { 1810 String uri = value->string; 1811 if (!uri.isNull()) 1812 image = CSSImageValue::create(completeURL(uri)); 1813 } else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "-webkit-image-set(")) { 1814 image = parseImageSet(m_valueList.get()); 1815 if (!image) 1816 break; 1817 } else 1818 break; 1819 1820 Vector<int> coords; 1821 value = m_valueList->next(); 1822 while (value && value->unit == CSSPrimitiveValue::CSS_NUMBER) { 1823 coords.append(int(value->fValue)); 1824 value = m_valueList->next(); 1825 } 1826 bool hasHotSpot = false; 1827 IntPoint hotSpot(-1, -1); 1828 int nrcoords = coords.size(); 1829 if (nrcoords > 0 && nrcoords != 2) 1830 return false; 1831 if (nrcoords == 2) { 1832 hasHotSpot = true; 1833 hotSpot = IntPoint(coords[0], coords[1]); 1834 } 1835 1836 if (!list) 1837 list = CSSValueList::createCommaSeparated(); 1838 1839 if (image) 1840 list->append(CSSCursorImageValue::create(image, hasHotSpot, hotSpot)); 1841 1842 if (!value || !(value->unit == CSSParserValue::Operator && value->iValue == ',')) 1843 return false; 1844 value = m_valueList->next(); // comma 1845 } 1846 if (list) { 1847 if (!value) 1848 return false; 1849 if (inQuirksMode() && value->id == CSSValueHand) // MSIE 5 compatibility :/ 1850 list->append(cssValuePool().createIdentifierValue(CSSValuePointer)); 1851 else if ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitGrabbing) || value->id == CSSValueCopy || value->id == CSSValueNone) 1852 list->append(cssValuePool().createIdentifierValue(value->id)); 1853 m_valueList->next(); 1854 parsedValue = list.release(); 1855 break; 1856 } else if (value) { 1857 id = value->id; 1858 if (inQuirksMode() && value->id == CSSValueHand) { // MSIE 5 compatibility :/ 1859 id = CSSValuePointer; 1860 validPrimitive = true; 1861 } else if ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitGrabbing) || value->id == CSSValueCopy || value->id == CSSValueNone) 1862 validPrimitive = true; 1863 } else { 1864 ASSERT_NOT_REACHED(); 1865 return false; 1866 } 1867 break; 1868 } 1869 1870 case CSSPropertyBackgroundBlendMode: 1871 case CSSPropertyBackgroundAttachment: 1872 case CSSPropertyBackgroundClip: 1873 case CSSPropertyWebkitBackgroundClip: 1874 case CSSPropertyWebkitBackgroundComposite: 1875 case CSSPropertyBackgroundImage: 1876 case CSSPropertyBackgroundOrigin: 1877 case CSSPropertyMaskSourceType: 1878 case CSSPropertyWebkitBackgroundOrigin: 1879 case CSSPropertyBackgroundPosition: 1880 case CSSPropertyBackgroundPositionX: 1881 case CSSPropertyBackgroundPositionY: 1882 case CSSPropertyBackgroundSize: 1883 case CSSPropertyWebkitBackgroundSize: 1884 case CSSPropertyBackgroundRepeat: 1885 case CSSPropertyBackgroundRepeatX: 1886 case CSSPropertyBackgroundRepeatY: 1887 case CSSPropertyWebkitMaskClip: 1888 case CSSPropertyWebkitMaskComposite: 1889 case CSSPropertyWebkitMaskImage: 1890 case CSSPropertyWebkitMaskOrigin: 1891 case CSSPropertyWebkitMaskPosition: 1892 case CSSPropertyWebkitMaskPositionX: 1893 case CSSPropertyWebkitMaskPositionY: 1894 case CSSPropertyWebkitMaskSize: 1895 case CSSPropertyWebkitMaskRepeat: 1896 case CSSPropertyWebkitMaskRepeatX: 1897 case CSSPropertyWebkitMaskRepeatY: 1898 { 1899 RefPtr<CSSValue> val1; 1900 RefPtr<CSSValue> val2; 1901 CSSPropertyID propId1, propId2; 1902 bool result = false; 1903 if (parseFillProperty(propId, propId1, propId2, val1, val2)) { 1904 OwnPtr<ShorthandScope> shorthandScope; 1905 if (propId == CSSPropertyBackgroundPosition || 1906 propId == CSSPropertyBackgroundRepeat || 1907 propId == CSSPropertyWebkitMaskPosition || 1908 propId == CSSPropertyWebkitMaskRepeat) { 1909 shorthandScope = adoptPtr(new ShorthandScope(this, propId)); 1910 } 1911 addProperty(propId1, val1.release(), important); 1912 if (val2) 1913 addProperty(propId2, val2.release(), important); 1914 result = true; 1915 } 1916 m_implicitShorthand = false; 1917 return result; 1918 } 1919 case CSSPropertyObjectPosition: 1920 return RuntimeEnabledFeatures::objectFitPositionEnabled() && parseObjectPosition(important); 1921 case CSSPropertyListStyleImage: // <uri> | none | inherit 1922 case CSSPropertyBorderImageSource: 1923 case CSSPropertyWebkitMaskBoxImageSource: 1924 if (id == CSSValueNone) { 1925 parsedValue = cssValuePool().createIdentifierValue(CSSValueNone); 1926 m_valueList->next(); 1927 } else if (value->unit == CSSPrimitiveValue::CSS_URI) { 1928 parsedValue = CSSImageValue::create(completeURL(value->string)); 1929 m_valueList->next(); 1930 } else if (isGeneratedImageValue(value)) { 1931 if (parseGeneratedImage(m_valueList.get(), parsedValue)) 1932 m_valueList->next(); 1933 else 1934 return false; 1935 } 1936 else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "-webkit-image-set(")) { 1937 parsedValue = parseImageSet(m_valueList.get()); 1938 if (!parsedValue) 1939 return false; 1940 m_valueList->next(); 1941 } 1942 break; 1943 1944 case CSSPropertyWebkitTextStrokeWidth: 1945 case CSSPropertyOutlineWidth: // <border-width> | inherit 1946 case CSSPropertyBorderTopWidth: //// <border-width> | inherit 1947 case CSSPropertyBorderRightWidth: // Which is defined as 1948 case CSSPropertyBorderBottomWidth: // thin | medium | thick | <length> 1949 case CSSPropertyBorderLeftWidth: 1950 case CSSPropertyWebkitBorderStartWidth: 1951 case CSSPropertyWebkitBorderEndWidth: 1952 case CSSPropertyWebkitBorderBeforeWidth: 1953 case CSSPropertyWebkitBorderAfterWidth: 1954 case CSSPropertyWebkitColumnRuleWidth: 1955 if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick) 1956 validPrimitive = true; 1957 else 1958 validPrimitive = validUnit(value, FLength | FNonNeg); 1959 break; 1960 1961 case CSSPropertyLetterSpacing: // normal | <length> | inherit 1962 case CSSPropertyWordSpacing: // normal | <length> | inherit 1963 if (id == CSSValueNormal) 1964 validPrimitive = true; 1965 else 1966 validPrimitive = validUnit(value, FLength); 1967 break; 1968 1969 case CSSPropertyTextIndent: 1970 parsedValue = parseTextIndent(); 1971 break; 1972 1973 case CSSPropertyPaddingTop: //// <padding-width> | inherit 1974 case CSSPropertyPaddingRight: // Which is defined as 1975 case CSSPropertyPaddingBottom: // <length> | <percentage> 1976 case CSSPropertyPaddingLeft: //// 1977 case CSSPropertyWebkitPaddingStart: 1978 case CSSPropertyWebkitPaddingEnd: 1979 case CSSPropertyWebkitPaddingBefore: 1980 case CSSPropertyWebkitPaddingAfter: 1981 validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg)); 1982 break; 1983 1984 case CSSPropertyMaxWidth: 1985 case CSSPropertyWebkitMaxLogicalWidth: 1986 case CSSPropertyMaxHeight: 1987 case CSSPropertyWebkitMaxLogicalHeight: 1988 validPrimitive = (id == CSSValueNone || validWidthOrHeight(value)); 1989 break; 1990 1991 case CSSPropertyMinWidth: 1992 case CSSPropertyWebkitMinLogicalWidth: 1993 case CSSPropertyMinHeight: 1994 case CSSPropertyWebkitMinLogicalHeight: 1995 validPrimitive = validWidthOrHeight(value); 1996 break; 1997 1998 case CSSPropertyWidth: 1999 case CSSPropertyWebkitLogicalWidth: 2000 case CSSPropertyHeight: 2001 case CSSPropertyWebkitLogicalHeight: 2002 validPrimitive = (id == CSSValueAuto || validWidthOrHeight(value)); 2003 break; 2004 2005 case CSSPropertyFontSize: 2006 return parseFontSize(important); 2007 2008 case CSSPropertyFontVariant: // normal | small-caps | inherit 2009 return parseFontVariant(important); 2010 2011 case CSSPropertyVerticalAlign: 2012 // baseline | sub | super | top | text-top | middle | bottom | text-bottom | 2013 // <percentage> | <length> | inherit 2014 2015 if (id >= CSSValueBaseline && id <= CSSValueWebkitBaselineMiddle) 2016 validPrimitive = true; 2017 else 2018 validPrimitive = (!id && validUnit(value, FLength | FPercent)); 2019 break; 2020 2021 case CSSPropertyBottom: // <length> | <percentage> | auto | inherit 2022 case CSSPropertyLeft: // <length> | <percentage> | auto | inherit 2023 case CSSPropertyRight: // <length> | <percentage> | auto | inherit 2024 case CSSPropertyTop: // <length> | <percentage> | auto | inherit 2025 case CSSPropertyMarginTop: //// <margin-width> | inherit 2026 case CSSPropertyMarginRight: // Which is defined as 2027 case CSSPropertyMarginBottom: // <length> | <percentage> | auto | inherit 2028 case CSSPropertyMarginLeft: //// 2029 case CSSPropertyWebkitMarginStart: 2030 case CSSPropertyWebkitMarginEnd: 2031 case CSSPropertyWebkitMarginBefore: 2032 case CSSPropertyWebkitMarginAfter: 2033 if (id == CSSValueAuto) 2034 validPrimitive = true; 2035 else 2036 validPrimitive = (!id && validUnit(value, FLength | FPercent)); 2037 break; 2038 2039 case CSSPropertyZIndex: // auto | <integer> | inherit 2040 if (id == CSSValueAuto) { 2041 validPrimitive = true; 2042 break; 2043 } 2044 /* nobreak */ 2045 case CSSPropertyOrphans: // <integer> | inherit | auto (We've added support for auto for backwards compatibility) 2046 case CSSPropertyWidows: // <integer> | inherit | auto (Ditto) 2047 if (id == CSSValueAuto) 2048 validPrimitive = true; 2049 else 2050 validPrimitive = (!id && validUnit(value, FInteger, HTMLQuirksMode)); 2051 break; 2052 2053 case CSSPropertyLineHeight: 2054 return parseLineHeight(important); 2055 case CSSPropertyCounterIncrement: // [ <identifier> <integer>? ]+ | none | inherit 2056 if (id != CSSValueNone) 2057 return parseCounter(propId, 1, important); 2058 validPrimitive = true; 2059 break; 2060 case CSSPropertyCounterReset: // [ <identifier> <integer>? ]+ | none | inherit 2061 if (id != CSSValueNone) 2062 return parseCounter(propId, 0, important); 2063 validPrimitive = true; 2064 break; 2065 case CSSPropertyFontFamily: 2066 // [[ <family-name> | <generic-family> ],]* [<family-name> | <generic-family>] | inherit 2067 { 2068 parsedValue = parseFontFamily(); 2069 break; 2070 } 2071 2072 case CSSPropertyTextDecoration: 2073 // Fall through 'text-decoration-line' parsing if CSS 3 Text Decoration 2074 // is disabled to match CSS 2.1 rules for parsing 'text-decoration'. 2075 if (RuntimeEnabledFeatures::css3TextDecorationsEnabled()) { 2076 // [ <text-decoration-line> || <text-decoration-style> || <text-decoration-color> ] | inherit 2077 return parseShorthand(CSSPropertyTextDecoration, textDecorationShorthand(), important); 2078 } 2079 case CSSPropertyWebkitTextDecorationsInEffect: 2080 case CSSPropertyTextDecorationLine: 2081 // none | [ underline || overline || line-through || blink ] | inherit 2082 return parseTextDecoration(propId, important); 2083 2084 case CSSPropertyTextDecorationStyle: 2085 // solid | double | dotted | dashed | wavy 2086 if (RuntimeEnabledFeatures::css3TextDecorationsEnabled() 2087 && (id == CSSValueSolid || id == CSSValueDouble || id == CSSValueDotted || id == CSSValueDashed || id == CSSValueWavy)) 2088 validPrimitive = true; 2089 break; 2090 2091 case CSSPropertyTextUnderlinePosition: 2092 // auto | under | inherit 2093 if (RuntimeEnabledFeatures::css3TextDecorationsEnabled()) 2094 return parseTextUnderlinePosition(important); 2095 return false; 2096 2097 case CSSPropertyZoom: // normal | reset | document | <number> | <percentage> | inherit 2098 if (id == CSSValueNormal || id == CSSValueReset || id == CSSValueDocument) 2099 validPrimitive = true; 2100 else 2101 validPrimitive = (!id && validUnit(value, FNumber | FPercent | FNonNeg, HTMLStandardMode)); 2102 break; 2103 2104 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. 2105 if (m_inFilterRule) 2106 return parseFilterRuleSrc(); 2107 return parseFontFaceSrc(); 2108 2109 case CSSPropertyUnicodeRange: 2110 return parseFontFaceUnicodeRange(); 2111 2112 /* CSS3 properties */ 2113 2114 case CSSPropertyBorderImage: 2115 case CSSPropertyWebkitMaskBoxImage: 2116 return parseBorderImageShorthand(propId, important); 2117 case CSSPropertyWebkitBorderImage: { 2118 if (RefPtr<CSSValue> result = parseBorderImage(propId)) { 2119 addProperty(propId, result, important); 2120 return true; 2121 } 2122 return false; 2123 } 2124 2125 case CSSPropertyBorderImageOutset: 2126 case CSSPropertyWebkitMaskBoxImageOutset: { 2127 RefPtr<CSSPrimitiveValue> result; 2128 if (parseBorderImageOutset(result)) { 2129 addProperty(propId, result, important); 2130 return true; 2131 } 2132 break; 2133 } 2134 case CSSPropertyBorderImageRepeat: 2135 case CSSPropertyWebkitMaskBoxImageRepeat: { 2136 RefPtr<CSSValue> result; 2137 if (parseBorderImageRepeat(result)) { 2138 addProperty(propId, result, important); 2139 return true; 2140 } 2141 break; 2142 } 2143 case CSSPropertyBorderImageSlice: 2144 case CSSPropertyWebkitMaskBoxImageSlice: { 2145 RefPtr<CSSBorderImageSliceValue> result; 2146 if (parseBorderImageSlice(propId, result)) { 2147 addProperty(propId, result, important); 2148 return true; 2149 } 2150 break; 2151 } 2152 case CSSPropertyBorderImageWidth: 2153 case CSSPropertyWebkitMaskBoxImageWidth: { 2154 RefPtr<CSSPrimitiveValue> result; 2155 if (parseBorderImageWidth(result)) { 2156 addProperty(propId, result, important); 2157 return true; 2158 } 2159 break; 2160 } 2161 case CSSPropertyBorderTopRightRadius: 2162 case CSSPropertyBorderTopLeftRadius: 2163 case CSSPropertyBorderBottomLeftRadius: 2164 case CSSPropertyBorderBottomRightRadius: { 2165 if (num != 1 && num != 2) 2166 return false; 2167 validPrimitive = validUnit(value, FLength | FPercent | FNonNeg); 2168 if (!validPrimitive) 2169 return false; 2170 RefPtr<CSSPrimitiveValue> parsedValue1 = createPrimitiveNumericValue(value); 2171 RefPtr<CSSPrimitiveValue> parsedValue2; 2172 if (num == 2) { 2173 value = m_valueList->next(); 2174 validPrimitive = validUnit(value, FLength | FPercent | FNonNeg); 2175 if (!validPrimitive) 2176 return false; 2177 parsedValue2 = createPrimitiveNumericValue(value); 2178 } else 2179 parsedValue2 = parsedValue1; 2180 2181 addProperty(propId, createPrimitiveValuePair(parsedValue1.release(), parsedValue2.release()), important); 2182 return true; 2183 } 2184 case CSSPropertyTabSize: 2185 validPrimitive = validUnit(value, FInteger | FNonNeg); 2186 break; 2187 case CSSPropertyWebkitAspectRatio: 2188 return parseAspectRatio(important); 2189 case CSSPropertyBorderRadius: 2190 case CSSPropertyWebkitBorderRadius: 2191 return parseBorderRadius(propId, important); 2192 case CSSPropertyOutlineOffset: 2193 validPrimitive = validUnit(value, FLength); 2194 break; 2195 case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3 2196 case CSSPropertyBoxShadow: 2197 case CSSPropertyWebkitBoxShadow: 2198 if (id == CSSValueNone) 2199 validPrimitive = true; 2200 else { 2201 RefPtr<CSSValueList> shadowValueList = parseShadow(m_valueList.get(), propId); 2202 if (shadowValueList) { 2203 addProperty(propId, shadowValueList.release(), important); 2204 m_valueList->next(); 2205 return true; 2206 } 2207 return false; 2208 } 2209 break; 2210 case CSSPropertyWebkitBoxReflect: 2211 if (id == CSSValueNone) 2212 validPrimitive = true; 2213 else 2214 return parseReflect(propId, important); 2215 break; 2216 case CSSPropertyOpacity: 2217 validPrimitive = validUnit(value, FNumber); 2218 break; 2219 case CSSPropertyWebkitBoxFlex: 2220 validPrimitive = validUnit(value, FNumber); 2221 break; 2222 case CSSPropertyWebkitBoxFlexGroup: 2223 validPrimitive = validUnit(value, FInteger | FNonNeg, HTMLStandardMode); 2224 break; 2225 case CSSPropertyWebkitBoxOrdinalGroup: 2226 validPrimitive = validUnit(value, FInteger | FNonNeg, HTMLStandardMode) && value->fValue; 2227 break; 2228 case CSSPropertyWebkitFilter: 2229 if (id == CSSValueNone) 2230 validPrimitive = true; 2231 else { 2232 RefPtr<CSSValue> val = parseFilter(); 2233 if (val) { 2234 addProperty(propId, val, important); 2235 return true; 2236 } 2237 return false; 2238 } 2239 break; 2240 case CSSPropertyFlex: { 2241 ShorthandScope scope(this, propId); 2242 if (id == CSSValueNone) { 2243 addProperty(CSSPropertyFlexGrow, cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER), important); 2244 addProperty(CSSPropertyFlexShrink, cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER), important); 2245 addProperty(CSSPropertyFlexBasis, cssValuePool().createIdentifierValue(CSSValueAuto), important); 2246 return true; 2247 } 2248 return parseFlex(m_valueList.get(), important); 2249 } 2250 case CSSPropertyFlexBasis: 2251 // FIXME: Support intrinsic dimensions too. 2252 if (id == CSSValueAuto) 2253 validPrimitive = true; 2254 else 2255 validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg)); 2256 break; 2257 case CSSPropertyFlexGrow: 2258 case CSSPropertyFlexShrink: 2259 validPrimitive = validUnit(value, FNumber | FNonNeg); 2260 break; 2261 case CSSPropertyOrder: 2262 if (validUnit(value, FInteger, HTMLStandardMode)) { 2263 if (value->unit != CSSPrimitiveValue::CSS_VARIABLE_NAME) { 2264 // We restrict the smallest value to int min + 2 because we use int min and int min + 1 as special values in a hash set. 2265 parsedValue = cssValuePool().createValue(max(static_cast<double>(std::numeric_limits<int>::min() + 2), value->fValue), 2266 static_cast<CSSPrimitiveValue::UnitTypes>(value->unit)); 2267 m_valueList->next(); 2268 } else { 2269 validPrimitive = true; 2270 } 2271 } 2272 break; 2273 case CSSPropertyInternalMarqueeIncrement: 2274 if (id == CSSValueSmall || id == CSSValueLarge || id == CSSValueMedium) 2275 validPrimitive = true; 2276 else 2277 validPrimitive = validUnit(value, FLength | FPercent); 2278 break; 2279 case CSSPropertyInternalMarqueeRepetition: 2280 if (id == CSSValueInfinite) 2281 validPrimitive = true; 2282 else 2283 validPrimitive = validUnit(value, FInteger | FNonNeg); 2284 break; 2285 case CSSPropertyInternalMarqueeSpeed: 2286 if (id == CSSValueNormal || id == CSSValueSlow || id == CSSValueFast) 2287 validPrimitive = true; 2288 else 2289 validPrimitive = validUnit(value, FTime | FInteger | FNonNeg); 2290 break; 2291 case CSSPropertyWebkitFlowInto: 2292 if (!RuntimeEnabledFeatures::cssRegionsEnabled()) 2293 return false; 2294 return parseFlowThread(propId, important); 2295 case CSSPropertyWebkitFlowFrom: 2296 if (!RuntimeEnabledFeatures::cssRegionsEnabled()) 2297 return false; 2298 return parseRegionThread(propId, important); 2299 case CSSPropertyWebkitTransform: 2300 if (id == CSSValueNone) 2301 validPrimitive = true; 2302 else { 2303 RefPtr<CSSValue> transformValue = parseTransform(); 2304 if (transformValue) { 2305 addProperty(propId, transformValue.release(), important); 2306 return true; 2307 } 2308 return false; 2309 } 2310 break; 2311 case CSSPropertyWebkitTransformOrigin: 2312 case CSSPropertyWebkitTransformOriginX: 2313 case CSSPropertyWebkitTransformOriginY: 2314 case CSSPropertyWebkitTransformOriginZ: { 2315 RefPtr<CSSValue> val1; 2316 RefPtr<CSSValue> val2; 2317 RefPtr<CSSValue> val3; 2318 CSSPropertyID propId1, propId2, propId3; 2319 if (parseTransformOrigin(propId, propId1, propId2, propId3, val1, val2, val3)) { 2320 addProperty(propId1, val1.release(), important); 2321 if (val2) 2322 addProperty(propId2, val2.release(), important); 2323 if (val3) 2324 addProperty(propId3, val3.release(), important); 2325 return true; 2326 } 2327 return false; 2328 } 2329 case CSSPropertyWebkitPerspective: 2330 if (id == CSSValueNone) 2331 validPrimitive = true; 2332 else { 2333 // Accepting valueless numbers is a quirk of the -webkit prefixed version of the property. 2334 if (validUnit(value, FNumber | FLength | FNonNeg)) { 2335 RefPtr<CSSValue> val = createPrimitiveNumericValue(value); 2336 if (val) { 2337 addProperty(propId, val.release(), important); 2338 return true; 2339 } 2340 return false; 2341 } 2342 } 2343 break; 2344 case CSSPropertyWebkitPerspectiveOrigin: 2345 case CSSPropertyWebkitPerspectiveOriginX: 2346 case CSSPropertyWebkitPerspectiveOriginY: { 2347 RefPtr<CSSValue> val1; 2348 RefPtr<CSSValue> val2; 2349 CSSPropertyID propId1, propId2; 2350 if (parsePerspectiveOrigin(propId, propId1, propId2, val1, val2)) { 2351 addProperty(propId1, val1.release(), important); 2352 if (val2) 2353 addProperty(propId2, val2.release(), important); 2354 return true; 2355 } 2356 return false; 2357 } 2358 case CSSPropertyAnimationDelay: 2359 case CSSPropertyAnimationDirection: 2360 case CSSPropertyAnimationDuration: 2361 case CSSPropertyAnimationFillMode: 2362 case CSSPropertyAnimationName: 2363 case CSSPropertyAnimationPlayState: 2364 case CSSPropertyAnimationIterationCount: 2365 case CSSPropertyAnimationTimingFunction: 2366 if (!RuntimeEnabledFeatures::cssAnimationUnprefixedEnabled()) 2367 break; 2368 case CSSPropertyWebkitAnimationDelay: 2369 case CSSPropertyWebkitAnimationDirection: 2370 case CSSPropertyWebkitAnimationDuration: 2371 case CSSPropertyWebkitAnimationFillMode: 2372 case CSSPropertyWebkitAnimationName: 2373 case CSSPropertyWebkitAnimationPlayState: 2374 case CSSPropertyWebkitAnimationIterationCount: 2375 case CSSPropertyWebkitAnimationTimingFunction: 2376 case CSSPropertyTransitionDelay: 2377 case CSSPropertyTransitionDuration: 2378 case CSSPropertyTransitionTimingFunction: 2379 case CSSPropertyTransitionProperty: 2380 case CSSPropertyWebkitTransitionDelay: 2381 case CSSPropertyWebkitTransitionDuration: 2382 case CSSPropertyWebkitTransitionTimingFunction: 2383 case CSSPropertyWebkitTransitionProperty: { 2384 RefPtr<CSSValue> val; 2385 AnimationParseContext context; 2386 if (parseAnimationProperty(propId, val, context)) { 2387 addPropertyWithPrefixingVariant(propId, val.release(), important); 2388 return true; 2389 } 2390 return false; 2391 } 2392 2393 case CSSPropertyGridAutoColumns: 2394 case CSSPropertyGridAutoRows: 2395 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled()) 2396 return false; 2397 parsedValue = parseGridTrackSize(*m_valueList); 2398 break; 2399 2400 case CSSPropertyGridDefinitionColumns: 2401 case CSSPropertyGridDefinitionRows: 2402 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled()) 2403 return false; 2404 return parseGridTrackList(propId, important); 2405 2406 case CSSPropertyGridColumnEnd: 2407 case CSSPropertyGridColumnStart: 2408 case CSSPropertyGridRowEnd: 2409 case CSSPropertyGridRowStart: 2410 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled()) 2411 return false; 2412 parsedValue = parseGridPosition(); 2413 break; 2414 2415 case CSSPropertyGridColumn: 2416 case CSSPropertyGridRow: 2417 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled()) 2418 return false; 2419 return parseGridItemPositionShorthand(propId, important); 2420 2421 case CSSPropertyGridArea: 2422 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled()) 2423 return false; 2424 return parseGridAreaShorthand(important); 2425 2426 case CSSPropertyGridTemplate: 2427 if (!RuntimeEnabledFeatures::cssGridLayoutEnabled()) 2428 return false; 2429 parsedValue = parseGridTemplate(); 2430 break; 2431 2432 case CSSPropertyWebkitMarginCollapse: { 2433 if (num == 1) { 2434 ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse); 2435 if (!parseValue(webkitMarginCollapseShorthand().properties()[0], important)) 2436 return false; 2437 CSSValue* value = m_parsedProperties.last().value(); 2438 addProperty(webkitMarginCollapseShorthand().properties()[1], value, important); 2439 return true; 2440 } 2441 else if (num == 2) { 2442 ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse); 2443 if (!parseValue(webkitMarginCollapseShorthand().properties()[0], important) || !parseValue(webkitMarginCollapseShorthand().properties()[1], important)) 2444 return false; 2445 return true; 2446 } 2447 return false; 2448 } 2449 case CSSPropertyTextLineThroughWidth: 2450 case CSSPropertyTextOverlineWidth: 2451 case CSSPropertyTextUnderlineWidth: 2452 if (id == CSSValueAuto || id == CSSValueNormal || id == CSSValueThin || 2453 id == CSSValueMedium || id == CSSValueThick) 2454 validPrimitive = true; 2455 else 2456 validPrimitive = !id && validUnit(value, FNumber | FLength | FPercent); 2457 break; 2458 case CSSPropertyWebkitColumnCount: 2459 parsedValue = parseColumnCount(); 2460 break; 2461 case CSSPropertyWebkitColumnGap: // normal | <length> 2462 if (id == CSSValueNormal) 2463 validPrimitive = true; 2464 else 2465 validPrimitive = validUnit(value, FLength | FNonNeg); 2466 break; 2467 case CSSPropertyWebkitColumnAxis: 2468 if (id == CSSValueHorizontal || id == CSSValueVertical || id == CSSValueAuto) 2469 validPrimitive = true; 2470 break; 2471 case CSSPropertyWebkitColumnProgression: 2472 if (id == CSSValueNormal || id == CSSValueReverse) 2473 validPrimitive = true; 2474 break; 2475 case CSSPropertyWebkitColumnSpan: // none | all | 1 (will be dropped in the unprefixed property) 2476 if (id == CSSValueAll || id == CSSValueNone) 2477 validPrimitive = true; 2478 else 2479 validPrimitive = validUnit(value, FNumber | FNonNeg) && value->fValue == 1; 2480 break; 2481 case CSSPropertyWebkitColumnWidth: // auto | <length> 2482 parsedValue = parseColumnWidth(); 2483 break; 2484 // End of CSS3 properties 2485 2486 // Apple specific properties. These will never be standardized and are purely to 2487 // support custom WebKit-based Apple applications. 2488 case CSSPropertyWebkitLineClamp: 2489 // When specifying number of lines, don't allow 0 as a valid value 2490 // When specifying either type of unit, require non-negative integers 2491 validPrimitive = (!id && (value->unit == CSSPrimitiveValue::CSS_PERCENTAGE || value->fValue) && validUnit(value, FInteger | FPercent | FNonNeg, HTMLQuirksMode)); 2492 break; 2493 2494 case CSSPropertyWebkitFontSizeDelta: // <length> 2495 validPrimitive = validUnit(value, FLength); 2496 break; 2497 2498 case CSSPropertyWebkitHighlight: 2499 if (id == CSSValueNone || value->unit == CSSPrimitiveValue::CSS_STRING) 2500 validPrimitive = true; 2501 break; 2502 2503 case CSSPropertyWebkitHyphenateCharacter: 2504 if (id == CSSValueAuto || value->unit == CSSPrimitiveValue::CSS_STRING) 2505 validPrimitive = true; 2506 break; 2507 2508 case CSSPropertyWebkitLineGrid: 2509 if (id == CSSValueNone) 2510 validPrimitive = true; 2511 else if (value->unit == CSSPrimitiveValue::CSS_IDENT) { 2512 String lineGridValue = String(value->string); 2513 if (!lineGridValue.isEmpty()) { 2514 addProperty(propId, cssValuePool().createValue(lineGridValue, CSSPrimitiveValue::CSS_STRING), important); 2515 return true; 2516 } 2517 } 2518 break; 2519 case CSSPropertyWebkitLocale: 2520 if (id == CSSValueAuto || value->unit == CSSPrimitiveValue::CSS_STRING) 2521 validPrimitive = true; 2522 break; 2523 2524 // End Apple-specific properties 2525 2526 case CSSPropertyWebkitAppRegion: 2527 if (id >= CSSValueDrag && id <= CSSValueNoDrag) 2528 validPrimitive = true; 2529 break; 2530 2531 case CSSPropertyWebkitTapHighlightColor: 2532 if ((id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu 2533 || (id >= CSSValueWebkitFocusRingColor && id < CSSValueWebkitText && inQuirksMode())) { 2534 validPrimitive = true; 2535 } else { 2536 parsedValue = parseColor(); 2537 if (parsedValue) 2538 m_valueList->next(); 2539 } 2540 break; 2541 2542 /* shorthand properties */ 2543 case CSSPropertyBackground: { 2544 // Position must come before color in this array because a plain old "0" is a legal color 2545 // in quirks mode but it's usually the X coordinate of a position. 2546 const CSSPropertyID properties[] = { CSSPropertyBackgroundImage, CSSPropertyBackgroundRepeat, 2547 CSSPropertyBackgroundAttachment, CSSPropertyBackgroundPosition, CSSPropertyBackgroundOrigin, 2548 CSSPropertyBackgroundClip, CSSPropertyBackgroundColor, CSSPropertyBackgroundSize }; 2549 return parseFillShorthand(propId, properties, WTF_ARRAY_LENGTH(properties), important); 2550 } 2551 case CSSPropertyWebkitMask: { 2552 const CSSPropertyID properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskRepeat, 2553 CSSPropertyWebkitMaskPosition, CSSPropertyWebkitMaskOrigin, CSSPropertyWebkitMaskClip, CSSPropertyWebkitMaskSize }; 2554 return parseFillShorthand(propId, properties, WTF_ARRAY_LENGTH(properties), important); 2555 } 2556 case CSSPropertyBorder: 2557 // [ 'border-width' || 'border-style' || <color> ] | inherit 2558 { 2559 if (parseShorthand(propId, parsingShorthandForProperty(CSSPropertyBorder), important)) { 2560 // The CSS3 Borders and Backgrounds specification says that border also resets border-image. It's as 2561 // though a value of none was specified for the image. 2562 addExpandedPropertyForValue(CSSPropertyBorderImage, cssValuePool().createImplicitInitialValue(), important); 2563 return true; 2564 } 2565 return false; 2566 } 2567 case CSSPropertyBorderTop: 2568 // [ 'border-top-width' || 'border-style' || <color> ] | inherit 2569 return parseShorthand(propId, borderTopShorthand(), important); 2570 case CSSPropertyBorderRight: 2571 // [ 'border-right-width' || 'border-style' || <color> ] | inherit 2572 return parseShorthand(propId, borderRightShorthand(), important); 2573 case CSSPropertyBorderBottom: 2574 // [ 'border-bottom-width' || 'border-style' || <color> ] | inherit 2575 return parseShorthand(propId, borderBottomShorthand(), important); 2576 case CSSPropertyBorderLeft: 2577 // [ 'border-left-width' || 'border-style' || <color> ] | inherit 2578 return parseShorthand(propId, borderLeftShorthand(), important); 2579 case CSSPropertyWebkitBorderStart: 2580 return parseShorthand(propId, webkitBorderStartShorthand(), important); 2581 case CSSPropertyWebkitBorderEnd: 2582 return parseShorthand(propId, webkitBorderEndShorthand(), important); 2583 case CSSPropertyWebkitBorderBefore: 2584 return parseShorthand(propId, webkitBorderBeforeShorthand(), important); 2585 case CSSPropertyWebkitBorderAfter: 2586 return parseShorthand(propId, webkitBorderAfterShorthand(), important); 2587 case CSSPropertyOutline: 2588 // [ 'outline-color' || 'outline-style' || 'outline-width' ] | inherit 2589 return parseShorthand(propId, outlineShorthand(), important); 2590 case CSSPropertyBorderColor: 2591 // <color>{1,4} | inherit 2592 return parse4Values(propId, borderColorShorthand().properties(), important); 2593 case CSSPropertyBorderWidth: 2594 // <border-width>{1,4} | inherit 2595 return parse4Values(propId, borderWidthShorthand().properties(), important); 2596 case CSSPropertyBorderStyle: 2597 // <border-style>{1,4} | inherit 2598 return parse4Values(propId, borderStyleShorthand().properties(), important); 2599 case CSSPropertyMargin: 2600 // <margin-width>{1,4} | inherit 2601 return parse4Values(propId, marginShorthand().properties(), important); 2602 case CSSPropertyPadding: 2603 // <padding-width>{1,4} | inherit 2604 return parse4Values(propId, paddingShorthand().properties(), important); 2605 case CSSPropertyFlexFlow: 2606 return parseShorthand(propId, flexFlowShorthand(), important); 2607 case CSSPropertyFont: 2608 // [ [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 2609 // 'font-family' ] | caption | icon | menu | message-box | small-caption | status-bar | inherit 2610 if (id >= CSSValueCaption && id <= CSSValueStatusBar) 2611 validPrimitive = true; 2612 else 2613 return parseFont(important); 2614 break; 2615 case CSSPropertyListStyle: 2616 return parseShorthand(propId, listStyleShorthand(), important); 2617 case CSSPropertyWebkitColumns: 2618 return parseColumnsShorthand(important); 2619 case CSSPropertyWebkitColumnRule: 2620 return parseShorthand(propId, webkitColumnRuleShorthand(), important); 2621 case CSSPropertyWebkitTextStroke: 2622 return parseShorthand(propId, webkitTextStrokeShorthand(), important); 2623 case CSSPropertyAnimation: 2624 if (!RuntimeEnabledFeatures::cssAnimationUnprefixedEnabled()) 2625 break; 2626 case CSSPropertyWebkitAnimation: 2627 return parseAnimationShorthand(propId, important); 2628 case CSSPropertyTransition: 2629 case CSSPropertyWebkitTransition: 2630 return parseTransitionShorthand(propId, important); 2631 case CSSPropertyInvalid: 2632 return false; 2633 case CSSPropertyPage: 2634 return parsePage(propId, important); 2635 case CSSPropertyFontStretch: 2636 return false; 2637 // CSS Text Layout Module Level 3: Vertical writing support 2638 case CSSPropertyWebkitTextEmphasis: 2639 return parseShorthand(propId, webkitTextEmphasisShorthand(), important); 2640 2641 case CSSPropertyWebkitTextEmphasisStyle: 2642 return parseTextEmphasisStyle(important); 2643 2644 case CSSPropertyWebkitTextOrientation: 2645 // FIXME: For now just support sideways, sideways-right, upright and vertical-right. 2646 if (id == CSSValueSideways || id == CSSValueSidewaysRight || id == CSSValueVerticalRight || id == CSSValueUpright) 2647 validPrimitive = true; 2648 break; 2649 2650 case CSSPropertyWebkitLineBoxContain: 2651 if (id == CSSValueNone) 2652 validPrimitive = true; 2653 else 2654 return parseLineBoxContain(important); 2655 break; 2656 case CSSPropertyWebkitFontFeatureSettings: 2657 if (id == CSSValueNormal) 2658 validPrimitive = true; 2659 else 2660 return parseFontFeatureSettings(important); 2661 break; 2662 2663 case CSSPropertyWebkitFontVariantLigatures: 2664 if (id == CSSValueNormal) 2665 validPrimitive = true; 2666 else 2667 return parseFontVariantLigatures(important); 2668 break; 2669 case CSSPropertyWebkitClipPath: 2670 if (id == CSSValueNone) { 2671 validPrimitive = true; 2672 } else if (value->unit == CSSParserValue::Function) { 2673 return parseBasicShape(propId, important); 2674 } else if (value->unit == CSSPrimitiveValue::CSS_URI) { 2675 parsedValue = CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_URI); 2676 addProperty(propId, parsedValue.release(), important); 2677 return true; 2678 } 2679 break; 2680 case CSSPropertyShapeInside: 2681 case CSSPropertyShapeOutside: 2682 if (!RuntimeEnabledFeatures::cssShapesEnabled()) 2683 return false; 2684 if (id == CSSValueAuto) 2685 validPrimitive = true; 2686 else if (id == CSSValueContentBox || id == CSSValuePaddingBox || id == CSSValueBorderBox || id == CSSValueMarginBox) 2687 validPrimitive = true; 2688 else if (propId == CSSPropertyShapeInside && id == CSSValueOutsideShape) 2689 validPrimitive = true; 2690 else if (value->unit == CSSParserValue::Function) 2691 return parseBasicShape(propId, important); 2692 else if (value->unit == CSSPrimitiveValue::CSS_URI) { 2693 parsedValue = CSSImageValue::create(completeURL(value->string)); 2694 m_valueList->next(); 2695 } 2696 break; 2697 case CSSPropertyShapeMargin: 2698 case CSSPropertyShapePadding: 2699 validPrimitive = (RuntimeEnabledFeatures::cssShapesEnabled() && !id && validUnit(value, FLength | FNonNeg)); 2700 break; 2701 case CSSPropertyShapeImageThreshold: 2702 validPrimitive = (RuntimeEnabledFeatures::cssShapesEnabled() && !id && validUnit(value, FNumber)); 2703 break; 2704 2705 case CSSPropertyTouchAction: 2706 // auto | none | [pan-x || pan-y] 2707 return parseTouchAction(important); 2708 2709 case CSSPropertyBorderBottomStyle: 2710 case CSSPropertyBorderCollapse: 2711 case CSSPropertyBorderLeftStyle: 2712 case CSSPropertyBorderRightStyle: 2713 case CSSPropertyBorderTopStyle: 2714 case CSSPropertyBoxSizing: 2715 case CSSPropertyCaptionSide: 2716 case CSSPropertyClear: 2717 case CSSPropertyDirection: 2718 case CSSPropertyDisplay: 2719 case CSSPropertyEmptyCells: 2720 case CSSPropertyFloat: 2721 case CSSPropertyFontStyle: 2722 case CSSPropertyImageRendering: 2723 case CSSPropertyListStylePosition: 2724 case CSSPropertyListStyleType: 2725 case CSSPropertyObjectFit: 2726 case CSSPropertyOutlineStyle: 2727 case CSSPropertyOverflowWrap: 2728 case CSSPropertyOverflowX: 2729 case CSSPropertyOverflowY: 2730 case CSSPropertyPageBreakAfter: 2731 case CSSPropertyPageBreakBefore: 2732 case CSSPropertyPageBreakInside: 2733 case CSSPropertyPointerEvents: 2734 case CSSPropertyPosition: 2735 case CSSPropertyResize: 2736 case CSSPropertySpeak: 2737 case CSSPropertyTableLayout: 2738 case CSSPropertyTextAlignLast: 2739 case CSSPropertyTextJustify: 2740 case CSSPropertyTextLineThroughMode: 2741 case CSSPropertyTextLineThroughStyle: 2742 case CSSPropertyTextOverflow: 2743 case CSSPropertyTextOverlineMode: 2744 case CSSPropertyTextOverlineStyle: 2745 case CSSPropertyTextRendering: 2746 case CSSPropertyTextTransform: 2747 case CSSPropertyTextUnderlineMode: 2748 case CSSPropertyTextUnderlineStyle: 2749 case CSSPropertyTouchActionDelay: 2750 case CSSPropertyVariable: 2751 case CSSPropertyVisibility: 2752 case CSSPropertyWebkitAppearance: 2753 case CSSPropertyWebkitBackfaceVisibility: 2754 case CSSPropertyWebkitBorderAfterStyle: 2755 case CSSPropertyWebkitBorderBeforeStyle: 2756 case CSSPropertyWebkitBorderEndStyle: 2757 case CSSPropertyWebkitBorderFit: 2758 case CSSPropertyWebkitBorderStartStyle: 2759 case CSSPropertyWebkitBoxAlign: 2760 case CSSPropertyWebkitBoxDecorationBreak: 2761 case CSSPropertyWebkitBoxDirection: 2762 case CSSPropertyWebkitBoxLines: 2763 case CSSPropertyWebkitBoxOrient: 2764 case CSSPropertyWebkitBoxPack: 2765 case CSSPropertyInternalCallback: 2766 case CSSPropertyWebkitColumnBreakAfter: 2767 case CSSPropertyWebkitColumnBreakBefore: 2768 case CSSPropertyWebkitColumnBreakInside: 2769 case CSSPropertyColumnFill: 2770 case CSSPropertyWebkitColumnRuleStyle: 2771 case CSSPropertyAlignContent: 2772 case CSSPropertyAlignItems: 2773 case CSSPropertyAlignSelf: 2774 case CSSPropertyFlexDirection: 2775 case CSSPropertyFlexWrap: 2776 case CSSPropertyJustifyContent: 2777 case CSSPropertyFontKerning: 2778 case CSSPropertyWebkitFontSmoothing: 2779 case CSSPropertyGridAutoFlow: 2780 case CSSPropertyWebkitLineAlign: 2781 case CSSPropertyWebkitLineBreak: 2782 case CSSPropertyWebkitLineSnap: 2783 case CSSPropertyWebkitMarginAfterCollapse: 2784 case CSSPropertyWebkitMarginBeforeCollapse: 2785 case CSSPropertyWebkitMarginBottomCollapse: 2786 case CSSPropertyWebkitMarginTopCollapse: 2787 case CSSPropertyInternalMarqueeDirection: 2788 case CSSPropertyInternalMarqueeStyle: 2789 case CSSPropertyWebkitPrintColorAdjust: 2790 case CSSPropertyWebkitRegionBreakAfter: 2791 case CSSPropertyWebkitRegionBreakBefore: 2792 case CSSPropertyWebkitRegionBreakInside: 2793 case CSSPropertyWebkitRegionFragment: 2794 case CSSPropertyWebkitRtlOrdering: 2795 case CSSPropertyWebkitRubyPosition: 2796 case CSSPropertyWebkitTextCombine: 2797 case CSSPropertyWebkitTextEmphasisPosition: 2798 case CSSPropertyWebkitTextSecurity: 2799 case CSSPropertyWebkitTransformStyle: 2800 case CSSPropertyWebkitUserDrag: 2801 case CSSPropertyWebkitUserModify: 2802 case CSSPropertyWebkitUserSelect: 2803 case CSSPropertyWebkitWrapFlow: 2804 case CSSPropertyWebkitWrapThrough: 2805 case CSSPropertyWebkitWritingMode: 2806 case CSSPropertyWhiteSpace: 2807 case CSSPropertyWordBreak: 2808 case CSSPropertyWordWrap: 2809 case CSSPropertyMixBlendMode: 2810 case CSSPropertyIsolation: 2811 // These properties should be handled before in isValidKeywordPropertyAndValue(). 2812 ASSERT_NOT_REACHED(); 2813 return false; 2814 // Properties below are validated inside parseViewportProperty, because we 2815 // check for parser state. We need to invalidate if someone adds them outside 2816 // a @viewport rule. 2817 case CSSPropertyMaxZoom: 2818 case CSSPropertyMinZoom: 2819 case CSSPropertyOrientation: 2820 case CSSPropertyUserZoom: 2821 validPrimitive = false; 2822 break; 2823 default: 2824 return parseSVGValue(propId, important); 2825 } 2826 2827 if (validPrimitive) { 2828 parsedValue = parseValidPrimitive(id, value); 2829 m_valueList->next(); 2830 } 2831 ASSERT(!m_parsedCalculation); 2832 if (parsedValue) { 2833 if (!m_valueList->current() || inShorthand()) { 2834 addProperty(propId, parsedValue.release(), important); 2835 return true; 2836 } 2837 } 2838 return false; 2839 } 2840 2841 void CSSParser::addFillValue(RefPtr<CSSValue>& lval, PassRefPtr<CSSValue> rval) 2842 { 2843 if (lval) { 2844 if (lval->isBaseValueList()) 2845 toCSSValueList(lval.get())->append(rval); 2846 else { 2847 PassRefPtr<CSSValue> oldlVal(lval.release()); 2848 PassRefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 2849 list->append(oldlVal); 2850 list->append(rval); 2851 lval = list; 2852 } 2853 } 2854 else 2855 lval = rval; 2856 } 2857 2858 static bool parseBackgroundClip(CSSParserValue* parserValue, RefPtr<CSSValue>& cssValue) 2859 { 2860 if (parserValue->id == CSSValueBorderBox || parserValue->id == CSSValuePaddingBox 2861 || parserValue->id == CSSValueContentBox || parserValue->id == CSSValueWebkitText) { 2862 cssValue = cssValuePool().createIdentifierValue(parserValue->id); 2863 return true; 2864 } 2865 return false; 2866 } 2867 2868 bool CSSParser::useLegacyBackgroundSizeShorthandBehavior() const 2869 { 2870 return m_context.useLegacyBackgroundSizeShorthandBehavior(); 2871 } 2872 2873 const int cMaxFillProperties = 9; 2874 2875 bool CSSParser::parseFillShorthand(CSSPropertyID propId, const CSSPropertyID* properties, int numProperties, bool important) 2876 { 2877 ASSERT(numProperties <= cMaxFillProperties); 2878 if (numProperties > cMaxFillProperties) 2879 return false; 2880 2881 ShorthandScope scope(this, propId); 2882 2883 bool parsedProperty[cMaxFillProperties] = { false }; 2884 RefPtr<CSSValue> values[cMaxFillProperties]; 2885 RefPtr<CSSValue> clipValue; 2886 RefPtr<CSSValue> positionYValue; 2887 RefPtr<CSSValue> repeatYValue; 2888 bool foundClip = false; 2889 int i; 2890 bool foundPositionCSSProperty = false; 2891 2892 while (m_valueList->current()) { 2893 CSSParserValue* val = m_valueList->current(); 2894 if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 2895 // We hit the end. Fill in all remaining values with the initial value. 2896 m_valueList->next(); 2897 for (i = 0; i < numProperties; ++i) { 2898 if (properties[i] == CSSPropertyBackgroundColor && parsedProperty[i]) 2899 // Color is not allowed except as the last item in a list for backgrounds. 2900 // Reject the entire property. 2901 return false; 2902 2903 if (!parsedProperty[i] && properties[i] != CSSPropertyBackgroundColor) { 2904 addFillValue(values[i], cssValuePool().createImplicitInitialValue()); 2905 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 2906 addFillValue(positionYValue, cssValuePool().createImplicitInitialValue()); 2907 if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) 2908 addFillValue(repeatYValue, cssValuePool().createImplicitInitialValue()); 2909 if ((properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) && !parsedProperty[i]) { 2910 // If background-origin wasn't present, then reset background-clip also. 2911 addFillValue(clipValue, cssValuePool().createImplicitInitialValue()); 2912 } 2913 } 2914 parsedProperty[i] = false; 2915 } 2916 if (!m_valueList->current()) 2917 break; 2918 } 2919 2920 bool sizeCSSPropertyExpected = false; 2921 if (isForwardSlashOperator(val) && foundPositionCSSProperty) { 2922 sizeCSSPropertyExpected = true; 2923 m_valueList->next(); 2924 } 2925 2926 foundPositionCSSProperty = false; 2927 bool found = false; 2928 for (i = 0; !found && i < numProperties; ++i) { 2929 2930 if (sizeCSSPropertyExpected && (properties[i] != CSSPropertyBackgroundSize && properties[i] != CSSPropertyWebkitMaskSize)) 2931 continue; 2932 if (!sizeCSSPropertyExpected && (properties[i] == CSSPropertyBackgroundSize || properties[i] == CSSPropertyWebkitMaskSize)) 2933 continue; 2934 2935 if (!parsedProperty[i]) { 2936 RefPtr<CSSValue> val1; 2937 RefPtr<CSSValue> val2; 2938 CSSPropertyID propId1, propId2; 2939 CSSParserValue* parserValue = m_valueList->current(); 2940 // parseFillProperty() may modify m_implicitShorthand, so we MUST reset it 2941 // before EACH return below. 2942 if (parseFillProperty(properties[i], propId1, propId2, val1, val2)) { 2943 parsedProperty[i] = found = true; 2944 addFillValue(values[i], val1.release()); 2945 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 2946 addFillValue(positionYValue, val2.release()); 2947 if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) 2948 addFillValue(repeatYValue, val2.release()); 2949 if (properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) { 2950 // Reparse the value as a clip, and see if we succeed. 2951 if (parseBackgroundClip(parserValue, val1)) 2952 addFillValue(clipValue, val1.release()); // The property parsed successfully. 2953 else 2954 addFillValue(clipValue, cssValuePool().createImplicitInitialValue()); // Some value was used for origin that is not supported by clip. Just reset clip instead. 2955 } 2956 if (properties[i] == CSSPropertyBackgroundClip || properties[i] == CSSPropertyWebkitMaskClip) { 2957 // Update clipValue 2958 addFillValue(clipValue, val1.release()); 2959 foundClip = true; 2960 } 2961 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 2962 foundPositionCSSProperty = true; 2963 } 2964 } 2965 } 2966 2967 // if we didn't find at least one match, this is an 2968 // invalid shorthand and we have to ignore it 2969 if (!found) { 2970 m_implicitShorthand = false; 2971 return false; 2972 } 2973 } 2974 2975 // Now add all of the properties we found. 2976 for (i = 0; i < numProperties; i++) { 2977 // Fill in any remaining properties with the initial value. 2978 if (!parsedProperty[i]) { 2979 addFillValue(values[i], cssValuePool().createImplicitInitialValue()); 2980 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 2981 addFillValue(positionYValue, cssValuePool().createImplicitInitialValue()); 2982 if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) 2983 addFillValue(repeatYValue, cssValuePool().createImplicitInitialValue()); 2984 if (properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) { 2985 // If background-origin wasn't present, then reset background-clip also. 2986 addFillValue(clipValue, cssValuePool().createImplicitInitialValue()); 2987 } 2988 } 2989 if (properties[i] == CSSPropertyBackgroundPosition) { 2990 addProperty(CSSPropertyBackgroundPositionX, values[i].release(), important); 2991 // it's OK to call positionYValue.release() since we only see CSSPropertyBackgroundPosition once 2992 addProperty(CSSPropertyBackgroundPositionY, positionYValue.release(), important); 2993 } else if (properties[i] == CSSPropertyWebkitMaskPosition) { 2994 addProperty(CSSPropertyWebkitMaskPositionX, values[i].release(), important); 2995 // it's OK to call positionYValue.release() since we only see CSSPropertyWebkitMaskPosition once 2996 addProperty(CSSPropertyWebkitMaskPositionY, positionYValue.release(), important); 2997 } else if (properties[i] == CSSPropertyBackgroundRepeat) { 2998 addProperty(CSSPropertyBackgroundRepeatX, values[i].release(), important); 2999 // it's OK to call repeatYValue.release() since we only see CSSPropertyBackgroundPosition once 3000 addProperty(CSSPropertyBackgroundRepeatY, repeatYValue.release(), important); 3001 } else if (properties[i] == CSSPropertyWebkitMaskRepeat) { 3002 addProperty(CSSPropertyWebkitMaskRepeatX, values[i].release(), important); 3003 // it's OK to call repeatYValue.release() since we only see CSSPropertyBackgroundPosition once 3004 addProperty(CSSPropertyWebkitMaskRepeatY, repeatYValue.release(), important); 3005 } else if ((properties[i] == CSSPropertyBackgroundClip || properties[i] == CSSPropertyWebkitMaskClip) && !foundClip) 3006 // Value is already set while updating origin 3007 continue; 3008 else if (properties[i] == CSSPropertyBackgroundSize && !parsedProperty[i] && useLegacyBackgroundSizeShorthandBehavior()) 3009 continue; 3010 else 3011 addProperty(properties[i], values[i].release(), important); 3012 3013 // Add in clip values when we hit the corresponding origin property. 3014 if (properties[i] == CSSPropertyBackgroundOrigin && !foundClip) 3015 addProperty(CSSPropertyBackgroundClip, clipValue.release(), important); 3016 else if (properties[i] == CSSPropertyWebkitMaskOrigin && !foundClip) 3017 addProperty(CSSPropertyWebkitMaskClip, clipValue.release(), important); 3018 } 3019 3020 m_implicitShorthand = false; 3021 return true; 3022 } 3023 3024 void CSSParser::storeVariableDeclaration(const CSSParserString& name, PassOwnPtr<CSSParserValueList> value, bool important) 3025 { 3026 // When CSSGrammar.y encounters an invalid declaration it passes null for the CSSParserValueList, just bail. 3027 if (!value) 3028 return; 3029 3030 static const unsigned prefixLength = sizeof("var-") - 1; 3031 3032 ASSERT(name.length() > prefixLength); 3033 AtomicString variableName = name.atomicSubstring(prefixLength, name.length() - prefixLength); 3034 3035 StringBuilder builder; 3036 for (unsigned i = 0, size = value->size(); i < size; i++) { 3037 if (i) 3038 builder.append(' '); 3039 RefPtr<CSSValue> cssValue = value->valueAt(i)->createCSSValue(); 3040 if (!cssValue) 3041 return; 3042 builder.append(cssValue->cssText()); 3043 } 3044 3045 addProperty(CSSPropertyVariable, CSSVariableValue::create(variableName, builder.toString()), important, false); 3046 } 3047 3048 void CSSParser::addAnimationValue(RefPtr<CSSValue>& lval, PassRefPtr<CSSValue> rval) 3049 { 3050 if (lval) { 3051 if (lval->isValueList()) 3052 toCSSValueList(lval.get())->append(rval); 3053 else { 3054 PassRefPtr<CSSValue> oldVal(lval.release()); 3055 PassRefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 3056 list->append(oldVal); 3057 list->append(rval); 3058 lval = list; 3059 } 3060 } 3061 else 3062 lval = rval; 3063 } 3064 3065 bool CSSParser::parseAnimationShorthand(CSSPropertyID propId, bool important) 3066 { 3067 const StylePropertyShorthand& animationProperties = parsingShorthandForProperty(propId); 3068 const unsigned numProperties = 8; 3069 3070 // The list of properties in the shorthand should be the same 3071 // length as the list with animation name in last position, even though they are 3072 // in a different order. 3073 ASSERT(numProperties == animationProperties.length()); 3074 ASSERT(numProperties == shorthandForProperty(propId).length()); 3075 3076 ShorthandScope scope(this, propId); 3077 3078 bool parsedProperty[numProperties] = { false }; 3079 AnimationParseContext context; 3080 RefPtr<CSSValue> values[numProperties]; 3081 3082 unsigned i; 3083 while (m_valueList->current()) { 3084 CSSParserValue* val = m_valueList->current(); 3085 if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 3086 // We hit the end. Fill in all remaining values with the initial value. 3087 m_valueList->next(); 3088 for (i = 0; i < numProperties; ++i) { 3089 if (!parsedProperty[i]) 3090 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3091 parsedProperty[i] = false; 3092 } 3093 if (!m_valueList->current()) 3094 break; 3095 context.commitFirstAnimation(); 3096 } 3097 3098 bool found = false; 3099 for (i = 0; i < numProperties; ++i) { 3100 if (!parsedProperty[i]) { 3101 RefPtr<CSSValue> val; 3102 if (parseAnimationProperty(animationProperties.properties()[i], val, context)) { 3103 parsedProperty[i] = found = true; 3104 addAnimationValue(values[i], val.release()); 3105 break; 3106 } 3107 } 3108 } 3109 3110 // if we didn't find at least one match, this is an 3111 // invalid shorthand and we have to ignore it 3112 if (!found) 3113 return false; 3114 } 3115 3116 for (i = 0; i < numProperties; ++i) { 3117 // If we didn't find the property, set an intial value. 3118 if (!parsedProperty[i]) 3119 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3120 3121 addProperty(animationProperties.properties()[i], values[i].release(), important); 3122 } 3123 3124 return true; 3125 } 3126 3127 bool CSSParser::parseTransitionShorthand(CSSPropertyID propId, bool important) 3128 { 3129 const unsigned numProperties = 4; 3130 const StylePropertyShorthand& shorthand = shorthandForProperty(propId); 3131 ASSERT(numProperties == shorthand.length()); 3132 3133 ShorthandScope scope(this, propId); 3134 3135 bool parsedProperty[numProperties] = { false }; 3136 AnimationParseContext context; 3137 RefPtr<CSSValue> values[numProperties]; 3138 3139 unsigned i; 3140 while (m_valueList->current()) { 3141 CSSParserValue* val = m_valueList->current(); 3142 if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 3143 // We hit the end. Fill in all remaining values with the initial value. 3144 m_valueList->next(); 3145 for (i = 0; i < numProperties; ++i) { 3146 if (!parsedProperty[i]) 3147 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3148 parsedProperty[i] = false; 3149 } 3150 if (!m_valueList->current()) 3151 break; 3152 context.commitFirstAnimation(); 3153 } 3154 3155 bool found = false; 3156 for (i = 0; !found && i < numProperties; ++i) { 3157 if (!parsedProperty[i]) { 3158 RefPtr<CSSValue> val; 3159 if (parseAnimationProperty(shorthand.properties()[i], val, context)) { 3160 parsedProperty[i] = found = true; 3161 addAnimationValue(values[i], val.release()); 3162 } 3163 3164 // There are more values to process but 'none' or 'all' were already defined as the animation property, the declaration becomes invalid. 3165 if (!context.animationPropertyKeywordAllowed() && context.hasCommittedFirstAnimation()) 3166 return false; 3167 } 3168 } 3169 3170 // if we didn't find at least one match, this is an 3171 // invalid shorthand and we have to ignore it 3172 if (!found) 3173 return false; 3174 } 3175 3176 // Fill in any remaining properties with the initial value. 3177 for (i = 0; i < numProperties; ++i) { 3178 if (!parsedProperty[i]) 3179 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3180 } 3181 3182 // Now add all of the properties we found. 3183 for (i = 0; i < numProperties; i++) 3184 addPropertyWithPrefixingVariant(shorthand.properties()[i], values[i].release(), important); 3185 3186 return true; 3187 } 3188 3189 PassRefPtr<CSSValue> CSSParser::parseColumnWidth() 3190 { 3191 CSSParserValue* value = m_valueList->current(); 3192 // Always parse lengths in strict mode here, since it would be ambiguous otherwise when used in 3193 // the 'columns' shorthand property. 3194 if (value->id == CSSValueAuto 3195 || (validUnit(value, FLength | FNonNeg, HTMLStandardMode) && value->fValue)) { 3196 RefPtr<CSSValue> parsedValue = parseValidPrimitive(value->id, value); 3197 m_valueList->next(); 3198 return parsedValue; 3199 } 3200 return 0; 3201 } 3202 3203 PassRefPtr<CSSValue> CSSParser::parseColumnCount() 3204 { 3205 CSSParserValue* value = m_valueList->current(); 3206 if (value->id == CSSValueAuto 3207 || (!value->id && validUnit(value, FPositiveInteger, HTMLQuirksMode))) { 3208 RefPtr<CSSValue> parsedValue = parseValidPrimitive(value->id, value); 3209 m_valueList->next(); 3210 return parsedValue; 3211 } 3212 return 0; 3213 } 3214 3215 bool CSSParser::parseColumnsShorthand(bool important) 3216 { 3217 RefPtr <CSSValue> columnWidth; 3218 RefPtr <CSSValue> columnCount; 3219 bool hasPendingExplicitAuto = false; 3220 3221 for (unsigned propertiesParsed = 0; CSSParserValue* value = m_valueList->current(); propertiesParsed++) { 3222 if (propertiesParsed >= 2) 3223 return false; // Too many values for this shorthand. Invalid declaration. 3224 if (!propertiesParsed && value->id == CSSValueAuto) { 3225 // 'auto' is a valid value for any of the two longhands, and at this point we 3226 // don't know which one(s) it is meant for. We need to see if there are other 3227 // values first. 3228 m_valueList->next(); 3229 hasPendingExplicitAuto = true; 3230 } else { 3231 if (!columnWidth) { 3232 if ((columnWidth = parseColumnWidth())) 3233 continue; 3234 } 3235 if (!columnCount) { 3236 if ((columnCount = parseColumnCount())) 3237 continue; 3238 } 3239 // If we didn't find at least one match, this is an 3240 // invalid shorthand and we have to ignore it. 3241 return false; 3242 } 3243 } 3244 if (hasPendingExplicitAuto) { 3245 // Time to assign the previously skipped 'auto' value to a property. If both properties are 3246 // unassigned at this point (i.e. 'columns:auto'), it doesn't matter that much which one we 3247 // set (although it does make a slight difference to web-inspector). The one we don't set 3248 // here will get an implicit 'auto' value further down. 3249 if (!columnWidth) { 3250 columnWidth = cssValuePool().createIdentifierValue(CSSValueAuto); 3251 } else { 3252 ASSERT(!columnCount); 3253 columnCount = cssValuePool().createIdentifierValue(CSSValueAuto); 3254 } 3255 } 3256 ASSERT(columnCount || columnWidth); 3257 3258 // Any unassigned property at this point will become implicit 'auto'. 3259 if (columnWidth) 3260 addProperty(CSSPropertyWebkitColumnWidth, columnWidth, important); 3261 else 3262 addProperty(CSSPropertyWebkitColumnWidth, cssValuePool().createIdentifierValue(CSSValueAuto), important, true /* implicit */); 3263 if (columnCount) 3264 addProperty(CSSPropertyWebkitColumnCount, columnCount, important); 3265 else 3266 addProperty(CSSPropertyWebkitColumnCount, cssValuePool().createIdentifierValue(CSSValueAuto), important, true /* implicit */); 3267 return true; 3268 } 3269 3270 bool CSSParser::parseShorthand(CSSPropertyID propId, const StylePropertyShorthand& shorthand, bool important) 3271 { 3272 // We try to match as many properties as possible 3273 // We set up an array of booleans to mark which property has been found, 3274 // and we try to search for properties until it makes no longer any sense. 3275 ShorthandScope scope(this, propId); 3276 3277 bool found = false; 3278 unsigned propertiesParsed = 0; 3279 bool propertyFound[6]= { false, false, false, false, false, false }; // 6 is enough size. 3280 3281 while (m_valueList->current()) { 3282 found = false; 3283 for (unsigned propIndex = 0; !found && propIndex < shorthand.length(); ++propIndex) { 3284 if (!propertyFound[propIndex] && parseValue(shorthand.properties()[propIndex], important)) { 3285 propertyFound[propIndex] = found = true; 3286 propertiesParsed++; 3287 } 3288 } 3289 3290 // if we didn't find at least one match, this is an 3291 // invalid shorthand and we have to ignore it 3292 if (!found) 3293 return false; 3294 } 3295 3296 if (propertiesParsed == shorthand.length()) 3297 return true; 3298 3299 // Fill in any remaining properties with the initial value. 3300 ImplicitScope implicitScope(this, PropertyImplicit); 3301 const StylePropertyShorthand* const* const propertiesForInitialization = shorthand.propertiesForInitialization(); 3302 for (unsigned i = 0; i < shorthand.length(); ++i) { 3303 if (propertyFound[i]) 3304 continue; 3305 3306 if (propertiesForInitialization) { 3307 const StylePropertyShorthand& initProperties = *(propertiesForInitialization[i]); 3308 for (unsigned propIndex = 0; propIndex < initProperties.length(); ++propIndex) 3309 addProperty(initProperties.properties()[propIndex], cssValuePool().createImplicitInitialValue(), important); 3310 } else 3311 addProperty(shorthand.properties()[i], cssValuePool().createImplicitInitialValue(), important); 3312 } 3313 3314 return true; 3315 } 3316 3317 bool CSSParser::parse4Values(CSSPropertyID propId, const CSSPropertyID *properties, bool important) 3318 { 3319 /* From the CSS 2 specs, 8.3 3320 * If there is only one value, it applies to all sides. If there are two values, the top and 3321 * bottom margins are set to the first value and the right and left margins are set to the second. 3322 * If there are three values, the top is set to the first value, the left and right are set to the 3323 * second, and the bottom is set to the third. If there are four values, they apply to the top, 3324 * right, bottom, and left, respectively. 3325 */ 3326 3327 int num = inShorthand() ? 1 : m_valueList->size(); 3328 3329 ShorthandScope scope(this, propId); 3330 3331 // the order is top, right, bottom, left 3332 switch (num) { 3333 case 1: { 3334 if (!parseValue(properties[0], important)) 3335 return false; 3336 CSSValue* value = m_parsedProperties.last().value(); 3337 ImplicitScope implicitScope(this, PropertyImplicit); 3338 addProperty(properties[1], value, important); 3339 addProperty(properties[2], value, important); 3340 addProperty(properties[3], value, important); 3341 break; 3342 } 3343 case 2: { 3344 if (!parseValue(properties[0], important) || !parseValue(properties[1], important)) 3345 return false; 3346 CSSValue* value = m_parsedProperties[m_parsedProperties.size() - 2].value(); 3347 ImplicitScope implicitScope(this, PropertyImplicit); 3348 addProperty(properties[2], value, important); 3349 value = m_parsedProperties[m_parsedProperties.size() - 2].value(); 3350 addProperty(properties[3], value, important); 3351 break; 3352 } 3353 case 3: { 3354 if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || !parseValue(properties[2], important)) 3355 return false; 3356 CSSValue* value = m_parsedProperties[m_parsedProperties.size() - 2].value(); 3357 ImplicitScope implicitScope(this, PropertyImplicit); 3358 addProperty(properties[3], value, important); 3359 break; 3360 } 3361 case 4: { 3362 if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || 3363 !parseValue(properties[2], important) || !parseValue(properties[3], important)) 3364 return false; 3365 break; 3366 } 3367 default: { 3368 return false; 3369 } 3370 } 3371 3372 return true; 3373 } 3374 3375 // auto | <identifier> 3376 bool CSSParser::parsePage(CSSPropertyID propId, bool important) 3377 { 3378 ASSERT(propId == CSSPropertyPage); 3379 3380 if (m_valueList->size() != 1) 3381 return false; 3382 3383 CSSParserValue* value = m_valueList->current(); 3384 if (!value) 3385 return false; 3386 3387 if (value->id == CSSValueAuto) { 3388 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 3389 return true; 3390 } else if (value->id == 0 && value->unit == CSSPrimitiveValue::CSS_IDENT) { 3391 addProperty(propId, createPrimitiveStringValue(value), important); 3392 return true; 3393 } 3394 return false; 3395 } 3396 3397 // <length>{1,2} | auto | [ <page-size> || [ portrait | landscape] ] 3398 bool CSSParser::parseSize(CSSPropertyID propId, bool important) 3399 { 3400 ASSERT(propId == CSSPropertySize); 3401 3402 if (m_valueList->size() > 2) 3403 return false; 3404 3405 CSSParserValue* value = m_valueList->current(); 3406 if (!value) 3407 return false; 3408 3409 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated(); 3410 3411 // First parameter. 3412 SizeParameterType paramType = parseSizeParameter(parsedValues.get(), value, None); 3413 if (paramType == None) 3414 return false; 3415 3416 // Second parameter, if any. 3417 value = m_valueList->next(); 3418 if (value) { 3419 paramType = parseSizeParameter(parsedValues.get(), value, paramType); 3420 if (paramType == None) 3421 return false; 3422 } 3423 3424 addProperty(propId, parsedValues.release(), important); 3425 return true; 3426 } 3427 3428 CSSParser::SizeParameterType CSSParser::parseSizeParameter(CSSValueList* parsedValues, CSSParserValue* value, SizeParameterType prevParamType) 3429 { 3430 switch (value->id) { 3431 case CSSValueAuto: 3432 if (prevParamType == None) { 3433 parsedValues->append(cssValuePool().createIdentifierValue(value->id)); 3434 return Auto; 3435 } 3436 return None; 3437 case CSSValueLandscape: 3438 case CSSValuePortrait: 3439 if (prevParamType == None || prevParamType == PageSize) { 3440 parsedValues->append(cssValuePool().createIdentifierValue(value->id)); 3441 return Orientation; 3442 } 3443 return None; 3444 case CSSValueA3: 3445 case CSSValueA4: 3446 case CSSValueA5: 3447 case CSSValueB4: 3448 case CSSValueB5: 3449 case CSSValueLedger: 3450 case CSSValueLegal: 3451 case CSSValueLetter: 3452 if (prevParamType == None || prevParamType == Orientation) { 3453 // Normalize to Page Size then Orientation order by prepending. 3454 // This is not specified by the CSS3 Paged Media specification, but for simpler processing later (StyleResolver::applyPageSizeProperty). 3455 parsedValues->prepend(cssValuePool().createIdentifierValue(value->id)); 3456 return PageSize; 3457 } 3458 return None; 3459 case 0: 3460 if (validUnit(value, FLength | FNonNeg) && (prevParamType == None || prevParamType == Length)) { 3461 parsedValues->append(createPrimitiveNumericValue(value)); 3462 return Length; 3463 } 3464 return None; 3465 default: 3466 return None; 3467 } 3468 } 3469 3470 // [ <string> <string> ]+ | inherit | none 3471 // inherit and none are handled in parseValue. 3472 bool CSSParser::parseQuotes(CSSPropertyID propId, bool important) 3473 { 3474 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); 3475 while (CSSParserValue* val = m_valueList->current()) { 3476 RefPtr<CSSValue> parsedValue; 3477 if (val->unit == CSSPrimitiveValue::CSS_STRING) 3478 parsedValue = CSSPrimitiveValue::create(val->string, CSSPrimitiveValue::CSS_STRING); 3479 else 3480 break; 3481 values->append(parsedValue.release()); 3482 m_valueList->next(); 3483 } 3484 if (values->length()) { 3485 addProperty(propId, values.release(), important); 3486 m_valueList->next(); 3487 return true; 3488 } 3489 return false; 3490 } 3491 3492 // [ <string> | <uri> | <counter> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit 3493 // in CSS 2.1 this got somewhat reduced: 3494 // [ <string> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit 3495 bool CSSParser::parseContent(CSSPropertyID propId, bool important) 3496 { 3497 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); 3498 3499 while (CSSParserValue* val = m_valueList->current()) { 3500 RefPtr<CSSValue> parsedValue; 3501 if (val->unit == CSSPrimitiveValue::CSS_URI) { 3502 // url 3503 parsedValue = CSSImageValue::create(completeURL(val->string)); 3504 } else if (val->unit == CSSParserValue::Function) { 3505 // attr(X) | counter(X [,Y]) | counters(X, Y, [,Z]) | -webkit-gradient(...) 3506 CSSParserValueList* args = val->function->args.get(); 3507 if (!args) 3508 return false; 3509 if (equalIgnoringCase(val->function->name, "attr(")) { 3510 parsedValue = parseAttr(args); 3511 if (!parsedValue) 3512 return false; 3513 } else if (equalIgnoringCase(val->function->name, "counter(")) { 3514 parsedValue = parseCounterContent(args, false); 3515 if (!parsedValue) 3516 return false; 3517 } else if (equalIgnoringCase(val->function->name, "counters(")) { 3518 parsedValue = parseCounterContent(args, true); 3519 if (!parsedValue) 3520 return false; 3521 } else if (equalIgnoringCase(val->function->name, "-webkit-image-set(")) { 3522 parsedValue = parseImageSet(m_valueList.get()); 3523 if (!parsedValue) 3524 return false; 3525 } else if (isGeneratedImageValue(val)) { 3526 if (!parseGeneratedImage(m_valueList.get(), parsedValue)) 3527 return false; 3528 } else 3529 return false; 3530 } else if (val->unit == CSSPrimitiveValue::CSS_IDENT) { 3531 // open-quote 3532 // close-quote 3533 // no-open-quote 3534 // no-close-quote 3535 // inherit 3536 // FIXME: These are not yet implemented (http://bugs.webkit.org/show_bug.cgi?id=6503). 3537 // none 3538 // normal 3539 switch (val->id) { 3540 case CSSValueOpenQuote: 3541 case CSSValueCloseQuote: 3542 case CSSValueNoOpenQuote: 3543 case CSSValueNoCloseQuote: 3544 case CSSValueNone: 3545 case CSSValueNormal: 3546 parsedValue = cssValuePool().createIdentifierValue(val->id); 3547 default: 3548 break; 3549 } 3550 } else if (val->unit == CSSPrimitiveValue::CSS_STRING) { 3551 parsedValue = createPrimitiveStringValue(val); 3552 } 3553 if (!parsedValue) 3554 break; 3555 values->append(parsedValue.release()); 3556 m_valueList->next(); 3557 } 3558 3559 if (values->length()) { 3560 addProperty(propId, values.release(), important); 3561 m_valueList->next(); 3562 return true; 3563 } 3564 3565 return false; 3566 } 3567 3568 PassRefPtr<CSSValue> CSSParser::parseAttr(CSSParserValueList* args) 3569 { 3570 if (args->size() != 1) 3571 return 0; 3572 3573 CSSParserValue* a = args->current(); 3574 3575 if (a->unit != CSSPrimitiveValue::CSS_IDENT) 3576 return 0; 3577 3578 String attrName = a->string; 3579 // CSS allows identifiers with "-" at the start, like "-webkit-mask-image". 3580 // But HTML attribute names can't have those characters, and we should not 3581 // even parse them inside attr(). 3582 if (attrName[0] == '-') 3583 return 0; 3584 3585 if (m_context.isHTMLDocument()) 3586 attrName = attrName.lower(); 3587 3588 return cssValuePool().createValue(attrName, CSSPrimitiveValue::CSS_ATTR); 3589 } 3590 3591 PassRefPtr<CSSValue> CSSParser::parseBackgroundColor() 3592 { 3593 CSSValueID id = m_valueList->current()->id; 3594 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || id == CSSValueCurrentcolor || 3595 (id >= CSSValueGrey && id < CSSValueWebkitText && inQuirksMode())) 3596 return cssValuePool().createIdentifierValue(id); 3597 return parseColor(); 3598 } 3599 3600 bool CSSParser::parseFillImage(CSSParserValueList* valueList, RefPtr<CSSValue>& value) 3601 { 3602 if (valueList->current()->id == CSSValueNone) { 3603 value = cssValuePool().createIdentifierValue(CSSValueNone); 3604 return true; 3605 } 3606 if (valueList->current()->unit == CSSPrimitiveValue::CSS_URI) { 3607 value = CSSImageValue::create(completeURL(valueList->current()->string)); 3608 return true; 3609 } 3610 3611 if (isGeneratedImageValue(valueList->current())) 3612 return parseGeneratedImage(valueList, value); 3613 3614 if (valueList->current()->unit == CSSParserValue::Function && equalIgnoringCase(valueList->current()->function->name, "-webkit-image-set(")) { 3615 value = parseImageSet(m_valueList.get()); 3616 if (value) 3617 return true; 3618 } 3619 3620 return false; 3621 } 3622 3623 PassRefPtr<CSSValue> CSSParser::parseFillPositionX(CSSParserValueList* valueList) 3624 { 3625 int id = valueList->current()->id; 3626 if (id == CSSValueLeft || id == CSSValueRight || id == CSSValueCenter) { 3627 int percent = 0; 3628 if (id == CSSValueRight) 3629 percent = 100; 3630 else if (id == CSSValueCenter) 3631 percent = 50; 3632 return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); 3633 } 3634 if (validUnit(valueList->current(), FPercent | FLength)) 3635 return createPrimitiveNumericValue(valueList->current()); 3636 return 0; 3637 } 3638 3639 PassRefPtr<CSSValue> CSSParser::parseFillPositionY(CSSParserValueList* valueList) 3640 { 3641 int id = valueList->current()->id; 3642 if (id == CSSValueTop || id == CSSValueBottom || id == CSSValueCenter) { 3643 int percent = 0; 3644 if (id == CSSValueBottom) 3645 percent = 100; 3646 else if (id == CSSValueCenter) 3647 percent = 50; 3648 return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); 3649 } 3650 if (validUnit(valueList->current(), FPercent | FLength)) 3651 return createPrimitiveNumericValue(valueList->current()); 3652 return 0; 3653 } 3654 3655 PassRefPtr<CSSPrimitiveValue> CSSParser::parseFillPositionComponent(CSSParserValueList* valueList, unsigned& cumulativeFlags, FillPositionFlag& individualFlag, FillPositionParsingMode parsingMode) 3656 { 3657 CSSValueID id = valueList->current()->id; 3658 if (id == CSSValueLeft || id == CSSValueTop || id == CSSValueRight || id == CSSValueBottom || id == CSSValueCenter) { 3659 int percent = 0; 3660 if (id == CSSValueLeft || id == CSSValueRight) { 3661 if (cumulativeFlags & XFillPosition) 3662 return 0; 3663 cumulativeFlags |= XFillPosition; 3664 individualFlag = XFillPosition; 3665 if (id == CSSValueRight) 3666 percent = 100; 3667 } 3668 else if (id == CSSValueTop || id == CSSValueBottom) { 3669 if (cumulativeFlags & YFillPosition) 3670 return 0; 3671 cumulativeFlags |= YFillPosition; 3672 individualFlag = YFillPosition; 3673 if (id == CSSValueBottom) 3674 percent = 100; 3675 } else if (id == CSSValueCenter) { 3676 // Center is ambiguous, so we're not sure which position we've found yet, an x or a y. 3677 percent = 50; 3678 cumulativeFlags |= AmbiguousFillPosition; 3679 individualFlag = AmbiguousFillPosition; 3680 } 3681 3682 if (parsingMode == ResolveValuesAsKeyword) 3683 return cssValuePool().createIdentifierValue(id); 3684 3685 return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); 3686 } 3687 if (validUnit(valueList->current(), FPercent | FLength)) { 3688 if (!cumulativeFlags) { 3689 cumulativeFlags |= XFillPosition; 3690 individualFlag = XFillPosition; 3691 } else if (cumulativeFlags & (XFillPosition | AmbiguousFillPosition)) { 3692 cumulativeFlags |= YFillPosition; 3693 individualFlag = YFillPosition; 3694 } else { 3695 if (m_parsedCalculation) 3696 m_parsedCalculation.release(); 3697 return 0; 3698 } 3699 return createPrimitiveNumericValue(valueList->current()); 3700 } 3701 return 0; 3702 } 3703 3704 static bool isValueConflictingWithCurrentEdge(int value1, int value2) 3705 { 3706 if ((value1 == CSSValueLeft || value1 == CSSValueRight) && (value2 == CSSValueLeft || value2 == CSSValueRight)) 3707 return true; 3708 3709 if ((value1 == CSSValueTop || value1 == CSSValueBottom) && (value2 == CSSValueTop || value2 == CSSValueBottom)) 3710 return true; 3711 3712 return false; 3713 } 3714 3715 static bool isFillPositionKeyword(CSSValueID value) 3716 { 3717 return value == CSSValueLeft || value == CSSValueTop || value == CSSValueBottom || value == CSSValueRight || value == CSSValueCenter; 3718 } 3719 3720 void CSSParser::parse4ValuesFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, PassRefPtr<CSSPrimitiveValue> parsedValue1, PassRefPtr<CSSPrimitiveValue> parsedValue2) 3721 { 3722 // [ left | right ] [ <percentage] | <length> ] && [ top | bottom ] [ <percentage> | <length> ] 3723 // In the case of 4 values <position> requires the second value to be a length or a percentage. 3724 if (isFillPositionKeyword(parsedValue2->getValueID())) 3725 return; 3726 3727 unsigned cumulativeFlags = 0; 3728 FillPositionFlag value3Flag = InvalidFillPosition; 3729 RefPtr<CSSPrimitiveValue> value3 = parseFillPositionComponent(valueList, cumulativeFlags, value3Flag, ResolveValuesAsKeyword); 3730 if (!value3) 3731 return; 3732 3733 CSSValueID ident1 = parsedValue1->getValueID(); 3734 CSSValueID ident3 = value3->getValueID(); 3735 3736 if (ident1 == CSSValueCenter) 3737 return; 3738 3739 if (!isFillPositionKeyword(ident3) || ident3 == CSSValueCenter) 3740 return; 3741 3742 // We need to check if the values are not conflicting, e.g. they are not on the same edge. It is 3743 // needed as the second call to parseFillPositionComponent was on purpose not checking it. In the 3744 // case of two values top 20px is invalid but in the case of 4 values it becomes valid. 3745 if (isValueConflictingWithCurrentEdge(ident1, ident3)) 3746 return; 3747 3748 valueList->next(); 3749 3750 cumulativeFlags = 0; 3751 FillPositionFlag value4Flag = InvalidFillPosition; 3752 RefPtr<CSSPrimitiveValue> value4 = parseFillPositionComponent(valueList, cumulativeFlags, value4Flag, ResolveValuesAsKeyword); 3753 if (!value4) 3754 return; 3755 3756 // 4th value must be a length or a percentage. 3757 if (isFillPositionKeyword(value4->getValueID())) 3758 return; 3759 3760 value1 = createPrimitiveValuePair(parsedValue1, parsedValue2); 3761 value2 = createPrimitiveValuePair(value3, value4); 3762 3763 if (ident1 == CSSValueTop || ident1 == CSSValueBottom) 3764 value1.swap(value2); 3765 3766 valueList->next(); 3767 } 3768 void CSSParser::parse3ValuesFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, PassRefPtr<CSSPrimitiveValue> parsedValue1, PassRefPtr<CSSPrimitiveValue> parsedValue2) 3769 { 3770 unsigned cumulativeFlags = 0; 3771 FillPositionFlag value3Flag = InvalidFillPosition; 3772 RefPtr<CSSPrimitiveValue> value3 = parseFillPositionComponent(valueList, cumulativeFlags, value3Flag, ResolveValuesAsKeyword); 3773 3774 // value3 is not an expected value, we return. 3775 if (!value3) 3776 return; 3777 3778 valueList->next(); 3779 3780 bool swapNeeded = false; 3781 CSSValueID ident1 = parsedValue1->getValueID(); 3782 CSSValueID ident2 = parsedValue2->getValueID(); 3783 CSSValueID ident3 = value3->getValueID(); 3784 3785 CSSValueID firstPositionKeyword; 3786 CSSValueID secondPositionKeyword; 3787 3788 if (ident1 == CSSValueCenter) { 3789 // <position> requires the first 'center' to be followed by a keyword. 3790 if (!isFillPositionKeyword(ident2)) 3791 return; 3792 3793 // If 'center' is the first keyword then the last one needs to be a length. 3794 if (isFillPositionKeyword(ident3)) 3795 return; 3796 3797 firstPositionKeyword = CSSValueLeft; 3798 if (ident2 == CSSValueLeft || ident2 == CSSValueRight) { 3799 firstPositionKeyword = CSSValueTop; 3800 swapNeeded = true; 3801 } 3802 value1 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(firstPositionKeyword), cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE)); 3803 value2 = createPrimitiveValuePair(parsedValue2, value3); 3804 } else if (ident3 == CSSValueCenter) { 3805 if (isFillPositionKeyword(ident2)) 3806 return; 3807 3808 secondPositionKeyword = CSSValueTop; 3809 if (ident1 == CSSValueTop || ident1 == CSSValueBottom) { 3810 secondPositionKeyword = CSSValueLeft; 3811 swapNeeded = true; 3812 } 3813 value1 = createPrimitiveValuePair(parsedValue1, parsedValue2); 3814 value2 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(secondPositionKeyword), cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE)); 3815 } else { 3816 RefPtr<CSSPrimitiveValue> firstPositionValue; 3817 RefPtr<CSSPrimitiveValue> secondPositionValue; 3818 3819 if (isFillPositionKeyword(ident2)) { 3820 // To match CSS grammar, we should only accept: [ center | left | right | bottom | top ] [ left | right | top | bottom ] [ <percentage> | <length> ]. 3821 ASSERT(ident2 != CSSValueCenter); 3822 3823 if (isFillPositionKeyword(ident3)) 3824 return; 3825 3826 secondPositionValue = value3; 3827 secondPositionKeyword = ident2; 3828 firstPositionValue = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE); 3829 } else { 3830 // Per CSS, we should only accept: [ right | left | top | bottom ] [ <percentage> | <length> ] [ center | left | right | bottom | top ]. 3831 if (!isFillPositionKeyword(ident3)) 3832 return; 3833 3834 firstPositionValue = parsedValue2; 3835 secondPositionKeyword = ident3; 3836 secondPositionValue = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE); 3837 } 3838 3839 if (isValueConflictingWithCurrentEdge(ident1, secondPositionKeyword)) 3840 return; 3841 3842 value1 = createPrimitiveValuePair(parsedValue1, firstPositionValue); 3843 value2 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(secondPositionKeyword), secondPositionValue); 3844 } 3845 3846 if (ident1 == CSSValueTop || ident1 == CSSValueBottom || swapNeeded) 3847 value1.swap(value2); 3848 3849 #ifndef NDEBUG 3850 CSSPrimitiveValue* first = toCSSPrimitiveValue(value1.get()); 3851 CSSPrimitiveValue* second = toCSSPrimitiveValue(value2.get()); 3852 ident1 = first->getPairValue()->first()->getValueID(); 3853 ident2 = second->getPairValue()->first()->getValueID(); 3854 ASSERT(ident1 == CSSValueLeft || ident1 == CSSValueRight); 3855 ASSERT(ident2 == CSSValueBottom || ident2 == CSSValueTop); 3856 #endif 3857 } 3858 3859 inline bool CSSParser::isPotentialPositionValue(CSSParserValue* value) 3860 { 3861 return isFillPositionKeyword(value->id) || validUnit(value, FPercent | FLength, ReleaseParsedCalcValue); 3862 } 3863 3864 void CSSParser::parseFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2) 3865 { 3866 unsigned numberOfValues = 0; 3867 for (unsigned i = valueList->currentIndex(); i < valueList->size(); ++i, ++numberOfValues) { 3868 CSSParserValue* current = valueList->valueAt(i); 3869 if (isComma(current) || !current || isForwardSlashOperator(current) || !isPotentialPositionValue(current)) 3870 break; 3871 } 3872 3873 if (numberOfValues > 4) 3874 return; 3875 3876 // If we are parsing two values, we can safely call the CSS 2.1 parsing function and return. 3877 if (numberOfValues <= 2) { 3878 parse2ValuesFillPosition(valueList, value1, value2); 3879 return; 3880 } 3881 3882 ASSERT(numberOfValues > 2 && numberOfValues <= 4); 3883 3884 CSSParserValue* value = valueList->current(); 3885 3886 // <position> requires the first value to be a background keyword. 3887 if (!isFillPositionKeyword(value->id)) 3888 return; 3889 3890 // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length. 3891 unsigned cumulativeFlags = 0; 3892 FillPositionFlag value1Flag = InvalidFillPosition; 3893 FillPositionFlag value2Flag = InvalidFillPosition; 3894 value1 = parseFillPositionComponent(valueList, cumulativeFlags, value1Flag, ResolveValuesAsKeyword); 3895 if (!value1) 3896 return; 3897 3898 value = valueList->next(); 3899 3900 // In case we are parsing more than two values, relax the check inside of parseFillPositionComponent. top 20px is 3901 // a valid start for <position>. 3902 cumulativeFlags = AmbiguousFillPosition; 3903 value2 = parseFillPositionComponent(valueList, cumulativeFlags, value2Flag, ResolveValuesAsKeyword); 3904 if (value2) 3905 valueList->next(); 3906 else { 3907 value1.clear(); 3908 return; 3909 } 3910 3911 RefPtr<CSSPrimitiveValue> parsedValue1 = toCSSPrimitiveValue(value1.get()); 3912 RefPtr<CSSPrimitiveValue> parsedValue2 = toCSSPrimitiveValue(value2.get()); 3913 3914 value1.clear(); 3915 value2.clear(); 3916 3917 // Per CSS3 syntax, <position> can't have 'center' as its second keyword as we have more arguments to follow. 3918 if (parsedValue2->getValueID() == CSSValueCenter) 3919 return; 3920 3921 if (numberOfValues == 3) 3922 parse3ValuesFillPosition(valueList, value1, value2, parsedValue1.release(), parsedValue2.release()); 3923 else 3924 parse4ValuesFillPosition(valueList, value1, value2, parsedValue1.release(), parsedValue2.release()); 3925 } 3926 3927 void CSSParser::parse2ValuesFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2) 3928 { 3929 CSSParserValue* value = valueList->current(); 3930 3931 // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length. 3932 unsigned cumulativeFlags = 0; 3933 FillPositionFlag value1Flag = InvalidFillPosition; 3934 FillPositionFlag value2Flag = InvalidFillPosition; 3935 value1 = parseFillPositionComponent(valueList, cumulativeFlags, value1Flag); 3936 if (!value1) 3937 return; 3938 3939 // It only takes one value for background-position to be correctly parsed if it was specified in a shorthand (since we 3940 // can assume that any other values belong to the rest of the shorthand). If we're not parsing a shorthand, though, the 3941 // value was explicitly specified for our property. 3942 value = valueList->next(); 3943 3944 // First check for the comma. If so, we are finished parsing this value or value pair. 3945 if (isComma(value)) 3946 value = 0; 3947 3948 if (value) { 3949 value2 = parseFillPositionComponent(valueList, cumulativeFlags, value2Flag); 3950 if (value2) 3951 valueList->next(); 3952 else { 3953 if (!inShorthand()) { 3954 value1.clear(); 3955 return; 3956 } 3957 } 3958 } 3959 3960 if (!value2) 3961 // Only one value was specified. If that value was not a keyword, then it sets the x position, and the y position 3962 // is simply 50%. This is our default. 3963 // For keywords, the keyword was either an x-keyword (left/right), a y-keyword (top/bottom), or an ambiguous keyword (center). 3964 // For left/right/center, the default of 50% in the y is still correct. 3965 value2 = cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE); 3966 3967 if (value1Flag == YFillPosition || value2Flag == XFillPosition) 3968 value1.swap(value2); 3969 } 3970 3971 void CSSParser::parseFillRepeat(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2) 3972 { 3973 CSSValueID id = m_valueList->current()->id; 3974 if (id == CSSValueRepeatX) { 3975 m_implicitShorthand = true; 3976 value1 = cssValuePool().createIdentifierValue(CSSValueRepeat); 3977 value2 = cssValuePool().createIdentifierValue(CSSValueNoRepeat); 3978 m_valueList->next(); 3979 return; 3980 } 3981 if (id == CSSValueRepeatY) { 3982 m_implicitShorthand = true; 3983 value1 = cssValuePool().createIdentifierValue(CSSValueNoRepeat); 3984 value2 = cssValuePool().createIdentifierValue(CSSValueRepeat); 3985 m_valueList->next(); 3986 return; 3987 } 3988 if (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace) 3989 value1 = cssValuePool().createIdentifierValue(id); 3990 else { 3991 value1 = 0; 3992 return; 3993 } 3994 3995 CSSParserValue* value = m_valueList->next(); 3996 3997 // Parse the second value if one is available 3998 if (value && !isComma(value)) { 3999 id = value->id; 4000 if (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace) { 4001 value2 = cssValuePool().createIdentifierValue(id); 4002 m_valueList->next(); 4003 return; 4004 } 4005 } 4006 4007 // If only one value was specified, value2 is the same as value1. 4008 m_implicitShorthand = true; 4009 value2 = cssValuePool().createIdentifierValue(toCSSPrimitiveValue(value1.get())->getValueID()); 4010 } 4011 4012 PassRefPtr<CSSValue> CSSParser::parseFillSize(CSSPropertyID propId, bool& allowComma) 4013 { 4014 allowComma = true; 4015 CSSParserValue* value = m_valueList->current(); 4016 4017 if (value->id == CSSValueContain || value->id == CSSValueCover) 4018 return cssValuePool().createIdentifierValue(value->id); 4019 4020 RefPtr<CSSPrimitiveValue> parsedValue1; 4021 4022 if (value->id == CSSValueAuto) 4023 parsedValue1 = cssValuePool().createIdentifierValue(CSSValueAuto); 4024 else { 4025 if (!validUnit(value, FLength | FPercent)) 4026 return 0; 4027 parsedValue1 = createPrimitiveNumericValue(value); 4028 } 4029 4030 RefPtr<CSSPrimitiveValue> parsedValue2; 4031 if ((value = m_valueList->next())) { 4032 if (value->unit == CSSParserValue::Operator && value->iValue == ',') 4033 allowComma = false; 4034 else if (value->id != CSSValueAuto) { 4035 if (!validUnit(value, FLength | FPercent)) { 4036 if (!inShorthand()) 4037 return 0; 4038 // We need to rewind the value list, so that when it is advanced we'll end up back at this value. 4039 m_valueList->previous(); 4040 } else 4041 parsedValue2 = createPrimitiveNumericValue(value); 4042 } 4043 } else if (!parsedValue2 && propId == CSSPropertyWebkitBackgroundSize) { 4044 // For backwards compatibility we set the second value to the first if it is omitted. 4045 // We only need to do this for -webkit-background-size. It should be safe to let masks match 4046 // the real property. 4047 parsedValue2 = parsedValue1; 4048 } 4049 4050 if (!parsedValue2) 4051 return parsedValue1; 4052 return createPrimitiveValuePair(parsedValue1.release(), parsedValue2.release()); 4053 } 4054 4055 bool CSSParser::parseFillProperty(CSSPropertyID propId, CSSPropertyID& propId1, CSSPropertyID& propId2, 4056 RefPtr<CSSValue>& retValue1, RefPtr<CSSValue>& retValue2) 4057 { 4058 RefPtr<CSSValueList> values; 4059 RefPtr<CSSValueList> values2; 4060 CSSParserValue* val; 4061 RefPtr<CSSValue> value; 4062 RefPtr<CSSValue> value2; 4063 4064 bool allowComma = false; 4065 4066 retValue1 = retValue2 = 0; 4067 propId1 = propId; 4068 propId2 = propId; 4069 if (propId == CSSPropertyBackgroundPosition) { 4070 propId1 = CSSPropertyBackgroundPositionX; 4071 propId2 = CSSPropertyBackgroundPositionY; 4072 } else if (propId == CSSPropertyWebkitMaskPosition) { 4073 propId1 = CSSPropertyWebkitMaskPositionX; 4074 propId2 = CSSPropertyWebkitMaskPositionY; 4075 } else if (propId == CSSPropertyBackgroundRepeat) { 4076 propId1 = CSSPropertyBackgroundRepeatX; 4077 propId2 = CSSPropertyBackgroundRepeatY; 4078 } else if (propId == CSSPropertyWebkitMaskRepeat) { 4079 propId1 = CSSPropertyWebkitMaskRepeatX; 4080 propId2 = CSSPropertyWebkitMaskRepeatY; 4081 } 4082 4083 while ((val = m_valueList->current())) { 4084 RefPtr<CSSValue> currValue; 4085 RefPtr<CSSValue> currValue2; 4086 4087 if (allowComma) { 4088 if (!isComma(val)) 4089 return false; 4090 m_valueList->next(); 4091 allowComma = false; 4092 } else { 4093 allowComma = true; 4094 switch (propId) { 4095 case CSSPropertyBackgroundColor: 4096 currValue = parseBackgroundColor(); 4097 if (currValue) 4098 m_valueList->next(); 4099 break; 4100 case CSSPropertyBackgroundAttachment: 4101 if (val->id == CSSValueScroll || val->id == CSSValueFixed || val->id == CSSValueLocal) { 4102 currValue = cssValuePool().createIdentifierValue(val->id); 4103 m_valueList->next(); 4104 } 4105 break; 4106 case CSSPropertyBackgroundImage: 4107 case CSSPropertyWebkitMaskImage: 4108 if (parseFillImage(m_valueList.get(), currValue)) 4109 m_valueList->next(); 4110 break; 4111 case CSSPropertyWebkitBackgroundClip: 4112 case CSSPropertyWebkitBackgroundOrigin: 4113 case CSSPropertyWebkitMaskClip: 4114 case CSSPropertyWebkitMaskOrigin: 4115 // The first three values here are deprecated and do not apply to the version of the property that has 4116 // the -webkit- prefix removed. 4117 if (val->id == CSSValueBorder || val->id == CSSValuePadding || val->id == CSSValueContent || 4118 val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox || 4119 ((propId == CSSPropertyWebkitBackgroundClip || propId == CSSPropertyWebkitMaskClip) && 4120 (val->id == CSSValueText || val->id == CSSValueWebkitText))) { 4121 currValue = cssValuePool().createIdentifierValue(val->id); 4122 m_valueList->next(); 4123 } 4124 break; 4125 case CSSPropertyBackgroundClip: 4126 if (parseBackgroundClip(val, currValue)) 4127 m_valueList->next(); 4128 break; 4129 case CSSPropertyBackgroundOrigin: 4130 if (val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox) { 4131 currValue = cssValuePool().createIdentifierValue(val->id); 4132 m_valueList->next(); 4133 } 4134 break; 4135 case CSSPropertyBackgroundPosition: 4136 case CSSPropertyWebkitMaskPosition: 4137 parseFillPosition(m_valueList.get(), currValue, currValue2); 4138 // parseFillPosition advances the m_valueList pointer. 4139 break; 4140 case CSSPropertyBackgroundPositionX: 4141 case CSSPropertyWebkitMaskPositionX: { 4142 currValue = parseFillPositionX(m_valueList.get()); 4143 if (currValue) 4144 m_valueList->next(); 4145 break; 4146 } 4147 case CSSPropertyBackgroundPositionY: 4148 case CSSPropertyWebkitMaskPositionY: { 4149 currValue = parseFillPositionY(m_valueList.get()); 4150 if (currValue) 4151 m_valueList->next(); 4152 break; 4153 } 4154 case CSSPropertyWebkitBackgroundComposite: 4155 case CSSPropertyWebkitMaskComposite: 4156 if (val->id >= CSSValueClear && val->id <= CSSValuePlusLighter) { 4157 currValue = cssValuePool().createIdentifierValue(val->id); 4158 m_valueList->next(); 4159 } 4160 break; 4161 case CSSPropertyBackgroundBlendMode: 4162 if (RuntimeEnabledFeatures::cssCompositingEnabled() && (val->id == CSSValueNormal || val->id == CSSValueMultiply 4163 || val->id == CSSValueScreen || val->id == CSSValueOverlay || val->id == CSSValueDarken 4164 || val->id == CSSValueLighten || val->id == CSSValueColorDodge || val->id == CSSValueColorBurn 4165 || val->id == CSSValueHardLight || val->id == CSSValueSoftLight || val->id == CSSValueDifference 4166 || val->id == CSSValueExclusion || val->id == CSSValueHue || val->id == CSSValueSaturation 4167 || val->id == CSSValueColor || val->id == CSSValueLuminosity)) { 4168 currValue = cssValuePool().createIdentifierValue(val->id); 4169 m_valueList->next(); 4170 } 4171 break; 4172 case CSSPropertyBackgroundRepeat: 4173 case CSSPropertyWebkitMaskRepeat: 4174 parseFillRepeat(currValue, currValue2); 4175 // parseFillRepeat advances the m_valueList pointer 4176 break; 4177 case CSSPropertyBackgroundSize: 4178 case CSSPropertyWebkitBackgroundSize: 4179 case CSSPropertyWebkitMaskSize: { 4180 currValue = parseFillSize(propId, allowComma); 4181 if (currValue) 4182 m_valueList->next(); 4183 break; 4184 } 4185 case CSSPropertyMaskSourceType: { 4186 if (RuntimeEnabledFeatures::cssMaskSourceTypeEnabled()) { 4187 if (val->id == CSSValueAuto || val->id == CSSValueAlpha || val->id == CSSValueLuminance) { 4188 currValue = cssValuePool().createIdentifierValue(val->id); 4189 m_valueList->next(); 4190 } else { 4191 currValue = 0; 4192 } 4193 } 4194 break; 4195 } 4196 default: 4197 break; 4198 } 4199 if (!currValue) 4200 return false; 4201 4202 if (value && !values) { 4203 values = CSSValueList::createCommaSeparated(); 4204 values->append(value.release()); 4205 } 4206 4207 if (value2 && !values2) { 4208 values2 = CSSValueList::createCommaSeparated(); 4209 values2->append(value2.release()); 4210 } 4211 4212 if (values) 4213 values->append(currValue.release()); 4214 else 4215 value = currValue.release(); 4216 if (currValue2) { 4217 if (values2) 4218 values2->append(currValue2.release()); 4219 else 4220 value2 = currValue2.release(); 4221 } 4222 } 4223 4224 // When parsing any fill shorthand property, we let it handle building up the lists for all 4225 // properties. 4226 if (inShorthand()) 4227 break; 4228 } 4229 4230 if (values && values->length()) { 4231 retValue1 = values.release(); 4232 if (values2 && values2->length()) 4233 retValue2 = values2.release(); 4234 return true; 4235 } 4236 if (value) { 4237 retValue1 = value.release(); 4238 retValue2 = value2.release(); 4239 return true; 4240 } 4241 return false; 4242 } 4243 4244 PassRefPtr<CSSValue> CSSParser::parseAnimationDelay() 4245 { 4246 CSSParserValue* value = m_valueList->current(); 4247 if (validUnit(value, FTime)) 4248 return createPrimitiveNumericValue(value); 4249 return 0; 4250 } 4251 4252 PassRefPtr<CSSValue> CSSParser::parseAnimationDirection() 4253 { 4254 CSSParserValue* value = m_valueList->current(); 4255 if (value->id == CSSValueNormal || value->id == CSSValueAlternate || value->id == CSSValueReverse || value->id == CSSValueAlternateReverse) 4256 return cssValuePool().createIdentifierValue(value->id); 4257 return 0; 4258 } 4259 4260 PassRefPtr<CSSValue> CSSParser::parseAnimationDuration() 4261 { 4262 CSSParserValue* value = m_valueList->current(); 4263 if (validUnit(value, FTime | FNonNeg)) 4264 return createPrimitiveNumericValue(value); 4265 return 0; 4266 } 4267 4268 PassRefPtr<CSSValue> CSSParser::parseAnimationFillMode() 4269 { 4270 CSSParserValue* value = m_valueList->current(); 4271 if (value->id == CSSValueNone || value->id == CSSValueForwards || value->id == CSSValueBackwards || value->id == CSSValueBoth) 4272 return cssValuePool().createIdentifierValue(value->id); 4273 return 0; 4274 } 4275 4276 PassRefPtr<CSSValue> CSSParser::parseAnimationIterationCount() 4277 { 4278 CSSParserValue* value = m_valueList->current(); 4279 if (value->id == CSSValueInfinite) 4280 return cssValuePool().createIdentifierValue(value->id); 4281 if (validUnit(value, FNumber | FNonNeg)) 4282 return createPrimitiveNumericValue(value); 4283 return 0; 4284 } 4285 4286 PassRefPtr<CSSValue> CSSParser::parseAnimationName() 4287 { 4288 CSSParserValue* value = m_valueList->current(); 4289 if (value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT) { 4290 if (value->id == CSSValueNone || (value->unit == CSSPrimitiveValue::CSS_STRING && equalIgnoringCase(value, "none"))) { 4291 return cssValuePool().createIdentifierValue(CSSValueNone); 4292 } else { 4293 return createPrimitiveStringValue(value); 4294 } 4295 } 4296 return 0; 4297 } 4298 4299 PassRefPtr<CSSValue> CSSParser::parseAnimationPlayState() 4300 { 4301 CSSParserValue* value = m_valueList->current(); 4302 if (value->id == CSSValueRunning || value->id == CSSValuePaused) 4303 return cssValuePool().createIdentifierValue(value->id); 4304 return 0; 4305 } 4306 4307 PassRefPtr<CSSValue> CSSParser::parseAnimationProperty(AnimationParseContext& context) 4308 { 4309 CSSParserValue* value = m_valueList->current(); 4310 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 4311 return 0; 4312 CSSPropertyID result = cssPropertyID(value->string); 4313 if (result) 4314 return cssValuePool().createIdentifierValue(result); 4315 if (equalIgnoringCase(value, "all")) { 4316 context.sawAnimationPropertyKeyword(); 4317 return cssValuePool().createIdentifierValue(CSSValueAll); 4318 } 4319 if (equalIgnoringCase(value, "none")) { 4320 context.commitAnimationPropertyKeyword(); 4321 context.sawAnimationPropertyKeyword(); 4322 return cssValuePool().createIdentifierValue(CSSValueNone); 4323 } 4324 return 0; 4325 } 4326 4327 bool CSSParser::parseTransformOriginShorthand(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3) 4328 { 4329 parse2ValuesFillPosition(m_valueList.get(), value1, value2); 4330 4331 // now get z 4332 if (m_valueList->current()) { 4333 if (validUnit(m_valueList->current(), FLength)) { 4334 value3 = createPrimitiveNumericValue(m_valueList->current()); 4335 m_valueList->next(); 4336 return true; 4337 } 4338 return false; 4339 } 4340 value3 = cssValuePool().createImplicitInitialValue(); 4341 return true; 4342 } 4343 4344 bool CSSParser::parseCubicBezierTimingFunctionValue(CSSParserValueList*& args, double& result) 4345 { 4346 CSSParserValue* v = args->current(); 4347 if (!validUnit(v, FNumber)) 4348 return false; 4349 result = v->fValue; 4350 v = args->next(); 4351 if (!v) 4352 // The last number in the function has no comma after it, so we're done. 4353 return true; 4354 if (!isComma(v)) 4355 return false; 4356 args->next(); 4357 return true; 4358 } 4359 4360 PassRefPtr<CSSValue> CSSParser::parseAnimationTimingFunction() 4361 { 4362 CSSParserValue* value = m_valueList->current(); 4363 if (value->id == CSSValueEase || value->id == CSSValueLinear || value->id == CSSValueEaseIn || value->id == CSSValueEaseOut 4364 || value->id == CSSValueEaseInOut || value->id == CSSValueStepStart || value->id == CSSValueStepEnd) 4365 return cssValuePool().createIdentifierValue(value->id); 4366 4367 // We must be a function. 4368 if (value->unit != CSSParserValue::Function) 4369 return 0; 4370 4371 CSSParserValueList* args = value->function->args.get(); 4372 4373 if (equalIgnoringCase(value->function->name, "steps(")) { 4374 // For steps, 1 or 2 params must be specified (comma-separated) 4375 if (!args || (args->size() != 1 && args->size() != 3)) 4376 return 0; 4377 4378 // There are two values. 4379 int numSteps; 4380 bool stepAtStart = false; 4381 4382 CSSParserValue* v = args->current(); 4383 if (!validUnit(v, FInteger)) 4384 return 0; 4385 numSteps = clampToInteger(v->fValue); 4386 if (numSteps < 1) 4387 return 0; 4388 v = args->next(); 4389 4390 if (v) { 4391 // There is a comma so we need to parse the second value 4392 if (!isComma(v)) 4393 return 0; 4394 v = args->next(); 4395 if (v->id != CSSValueStart && v->id != CSSValueEnd) 4396 return 0; 4397 stepAtStart = v->id == CSSValueStart; 4398 } 4399 4400 return CSSStepsTimingFunctionValue::create(numSteps, stepAtStart); 4401 } 4402 4403 if (equalIgnoringCase(value->function->name, "cubic-bezier(")) { 4404 // For cubic bezier, 4 values must be specified. 4405 if (!args || args->size() != 7) 4406 return 0; 4407 4408 // There are two points specified. The x values must be between 0 and 1 but the y values can exceed this range. 4409 double x1, y1, x2, y2; 4410 4411 if (!parseCubicBezierTimingFunctionValue(args, x1)) 4412 return 0; 4413 if (x1 < 0 || x1 > 1) 4414 return 0; 4415 if (!parseCubicBezierTimingFunctionValue(args, y1)) 4416 return 0; 4417 if (!parseCubicBezierTimingFunctionValue(args, x2)) 4418 return 0; 4419 if (x2 < 0 || x2 > 1) 4420 return 0; 4421 if (!parseCubicBezierTimingFunctionValue(args, y2)) 4422 return 0; 4423 4424 return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2); 4425 } 4426 4427 return 0; 4428 } 4429 4430 bool CSSParser::parseAnimationProperty(CSSPropertyID propId, RefPtr<CSSValue>& result, AnimationParseContext& context) 4431 { 4432 RefPtr<CSSValueList> values; 4433 CSSParserValue* val; 4434 RefPtr<CSSValue> value; 4435 bool allowComma = false; 4436 4437 result = 0; 4438 4439 while ((val = m_valueList->current())) { 4440 RefPtr<CSSValue> currValue; 4441 if (allowComma) { 4442 if (!isComma(val)) 4443 return false; 4444 m_valueList->next(); 4445 allowComma = false; 4446 } 4447 else { 4448 switch (propId) { 4449 case CSSPropertyAnimationDelay: 4450 case CSSPropertyWebkitAnimationDelay: 4451 case CSSPropertyTransitionDelay: 4452 case CSSPropertyWebkitTransitionDelay: 4453 currValue = parseAnimationDelay(); 4454 if (currValue) 4455 m_valueList->next(); 4456 break; 4457 case CSSPropertyAnimationDirection: 4458 case CSSPropertyWebkitAnimationDirection: 4459 currValue = parseAnimationDirection(); 4460 if (currValue) 4461 m_valueList->next(); 4462 break; 4463 case CSSPropertyAnimationDuration: 4464 case CSSPropertyWebkitAnimationDuration: 4465 case CSSPropertyTransitionDuration: 4466 case CSSPropertyWebkitTransitionDuration: 4467 currValue = parseAnimationDuration(); 4468 if (currValue) 4469 m_valueList->next(); 4470 break; 4471 case CSSPropertyAnimationFillMode: 4472 case CSSPropertyWebkitAnimationFillMode: 4473 currValue = parseAnimationFillMode(); 4474 if (currValue) 4475 m_valueList->next(); 4476 break; 4477 case CSSPropertyAnimationIterationCount: 4478 case CSSPropertyWebkitAnimationIterationCount: 4479 currValue = parseAnimationIterationCount(); 4480 if (currValue) 4481 m_valueList->next(); 4482 break; 4483 case CSSPropertyAnimationName: 4484 case CSSPropertyWebkitAnimationName: 4485 currValue = parseAnimationName(); 4486 if (currValue) 4487 m_valueList->next(); 4488 break; 4489 case CSSPropertyAnimationPlayState: 4490 case CSSPropertyWebkitAnimationPlayState: 4491 currValue = parseAnimationPlayState(); 4492 if (currValue) 4493 m_valueList->next(); 4494 break; 4495 case CSSPropertyTransitionProperty: 4496 case CSSPropertyWebkitTransitionProperty: 4497 currValue = parseAnimationProperty(context); 4498 if (value && !context.animationPropertyKeywordAllowed()) 4499 return false; 4500 if (currValue) 4501 m_valueList->next(); 4502 break; 4503 case CSSPropertyAnimationTimingFunction: 4504 case CSSPropertyWebkitAnimationTimingFunction: 4505 case CSSPropertyTransitionTimingFunction: 4506 case CSSPropertyWebkitTransitionTimingFunction: 4507 currValue = parseAnimationTimingFunction(); 4508 if (currValue) 4509 m_valueList->next(); 4510 break; 4511 default: 4512 ASSERT_NOT_REACHED(); 4513 return false; 4514 } 4515 4516 if (!currValue) 4517 return false; 4518 4519 if (value && !values) { 4520 values = CSSValueList::createCommaSeparated(); 4521 values->append(value.release()); 4522 } 4523 4524 if (values) 4525 values->append(currValue.release()); 4526 else 4527 value = currValue.release(); 4528 4529 allowComma = true; 4530 } 4531 4532 // When parsing the 'transition' shorthand property, we let it handle building up the lists for all 4533 // properties. 4534 if (inShorthand()) 4535 break; 4536 } 4537 4538 if (values && values->length()) { 4539 result = values.release(); 4540 return true; 4541 } 4542 if (value) { 4543 result = value.release(); 4544 return true; 4545 } 4546 return false; 4547 } 4548 4549 // The function parses [ <integer> || <string> ] in <grid-line> (which can be stand alone or with 'span'). 4550 bool CSSParser::parseIntegerOrStringFromGridPosition(RefPtr<CSSPrimitiveValue>& numericValue, RefPtr<CSSPrimitiveValue>& gridLineName) 4551 { 4552 CSSParserValue* value = m_valueList->current(); 4553 if (validUnit(value, FInteger) && value->fValue) { 4554 numericValue = createPrimitiveNumericValue(value); 4555 value = m_valueList->next(); 4556 if (value && value->unit == CSSPrimitiveValue::CSS_STRING) { 4557 gridLineName = createPrimitiveStringValue(m_valueList->current()); 4558 m_valueList->next(); 4559 } 4560 return true; 4561 } 4562 4563 if (value->unit == CSSPrimitiveValue::CSS_STRING) { 4564 gridLineName = createPrimitiveStringValue(m_valueList->current()); 4565 value = m_valueList->next(); 4566 if (value && validUnit(value, FInteger) && value->fValue) { 4567 numericValue = createPrimitiveNumericValue(value); 4568 m_valueList->next(); 4569 } 4570 return true; 4571 } 4572 4573 return false; 4574 } 4575 4576 PassRefPtr<CSSValue> CSSParser::parseGridPosition() 4577 { 4578 ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled()); 4579 4580 CSSParserValue* value = m_valueList->current(); 4581 if (value->id == CSSValueAuto) { 4582 m_valueList->next(); 4583 return cssValuePool().createIdentifierValue(CSSValueAuto); 4584 } 4585 4586 if (value->id != CSSValueSpan && value->unit == CSSPrimitiveValue::CSS_IDENT) { 4587 m_valueList->next(); 4588 return cssValuePool().createValue(value->string, CSSPrimitiveValue::CSS_STRING); 4589 } 4590 4591 RefPtr<CSSPrimitiveValue> numericValue; 4592 RefPtr<CSSPrimitiveValue> gridLineName; 4593 bool hasSeenSpanKeyword = false; 4594 4595 if (parseIntegerOrStringFromGridPosition(numericValue, gridLineName)) { 4596 value = m_valueList->current(); 4597 if (value && value->id == CSSValueSpan) { 4598 hasSeenSpanKeyword = true; 4599 m_valueList->next(); 4600 } 4601 } else if (value->id == CSSValueSpan) { 4602 hasSeenSpanKeyword = true; 4603 if (m_valueList->next()) 4604 parseIntegerOrStringFromGridPosition(numericValue, gridLineName); 4605 } 4606 4607 // Check that we have consumed all the value list. For shorthands, the parser will pass 4608 // the whole value list (including the opposite position). 4609 if (m_valueList->current() && !isForwardSlashOperator(m_valueList->current())) 4610 return 0; 4611 4612 // If we didn't parse anything, this is not a valid grid position. 4613 if (!hasSeenSpanKeyword && !gridLineName && !numericValue) 4614 return 0; 4615 4616 // Negative numbers are not allowed for span (but are for <integer>). 4617 if (hasSeenSpanKeyword && numericValue && numericValue->getIntValue() < 0) 4618 return 0; 4619 4620 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated(); 4621 if (hasSeenSpanKeyword) 4622 values->append(cssValuePool().createIdentifierValue(CSSValueSpan)); 4623 if (numericValue) 4624 values->append(numericValue.release()); 4625 if (gridLineName) 4626 values->append(gridLineName.release()); 4627 ASSERT(values->length()); 4628 return values.release(); 4629 } 4630 4631 static PassRefPtr<CSSValue> gridMissingGridPositionValue(CSSValue* value) 4632 { 4633 if (value->isPrimitiveValue() && toCSSPrimitiveValue(value)->isString()) 4634 return value; 4635 4636 return cssValuePool().createIdentifierValue(CSSValueAuto); 4637 } 4638 4639 bool CSSParser::parseGridItemPositionShorthand(CSSPropertyID shorthandId, bool important) 4640 { 4641 ShorthandScope scope(this, shorthandId); 4642 const StylePropertyShorthand& shorthand = shorthandForProperty(shorthandId); 4643 ASSERT(shorthand.length() == 2); 4644 4645 RefPtr<CSSValue> startValue = parseGridPosition(); 4646 if (!startValue) 4647 return false; 4648 4649 RefPtr<CSSValue> endValue; 4650 if (m_valueList->current()) { 4651 if (!isForwardSlashOperator(m_valueList->current())) 4652 return false; 4653 4654 if (!m_valueList->next()) 4655 return false; 4656 4657 endValue = parseGridPosition(); 4658 if (!endValue || m_valueList->current()) 4659 return false; 4660 } else { 4661 endValue = gridMissingGridPositionValue(startValue.get()); 4662 } 4663 4664 addProperty(shorthand.properties()[0], startValue, important); 4665 addProperty(shorthand.properties()[1], endValue, important); 4666 return true; 4667 } 4668 4669 bool CSSParser::parseGridAreaShorthand(bool important) 4670 { 4671 ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled()); 4672 4673 ShorthandScope scope(this, CSSPropertyGridArea); 4674 const StylePropertyShorthand& shorthand = gridAreaShorthand(); 4675 ASSERT_UNUSED(shorthand, shorthand.length() == 4); 4676 4677 RefPtr<CSSValue> rowStartValue = parseGridPosition(); 4678 if (!rowStartValue) 4679 return false; 4680 4681 RefPtr<CSSValue> columnStartValue; 4682 if (!parseSingleGridAreaLonghand(columnStartValue)) 4683 return false; 4684 4685 RefPtr<CSSValue> rowEndValue; 4686 if (!parseSingleGridAreaLonghand(rowEndValue)) 4687 return false; 4688 4689 RefPtr<CSSValue> columnEndValue; 4690 if (!parseSingleGridAreaLonghand(columnEndValue)) 4691 return false; 4692 4693 if (!columnStartValue) 4694 columnStartValue = gridMissingGridPositionValue(rowStartValue.get()); 4695 4696 if (!rowEndValue) 4697 rowEndValue = gridMissingGridPositionValue(rowStartValue.get()); 4698 4699 if (!columnEndValue) 4700 columnEndValue = gridMissingGridPositionValue(columnStartValue.get()); 4701 4702 addProperty(CSSPropertyGridRowStart, rowStartValue, important); 4703 addProperty(CSSPropertyGridColumnStart, columnStartValue, important); 4704 addProperty(CSSPropertyGridRowEnd, rowEndValue, important); 4705 addProperty(CSSPropertyGridColumnEnd, columnEndValue, important); 4706 return true; 4707 } 4708 4709 bool CSSParser::parseSingleGridAreaLonghand(RefPtr<CSSValue>& property) 4710 { 4711 if (!m_valueList->current()) 4712 return true; 4713 4714 if (!isForwardSlashOperator(m_valueList->current())) 4715 return false; 4716 4717 if (!m_valueList->next()) 4718 return false; 4719 4720 property = parseGridPosition(); 4721 return true; 4722 } 4723 4724 void CSSParser::parseGridLineNames(CSSParserValueList* parserValueList, CSSValueList& valueList) 4725 { 4726 ASSERT(parserValueList->current() && parserValueList->current()->unit == CSSParserValue::ValueList); 4727 4728 CSSParserValueList* identList = parserValueList->current()->valueList; 4729 if (!identList->size()) { 4730 parserValueList->next(); 4731 return; 4732 } 4733 4734 RefPtr<CSSGridLineNamesValue> lineNames = CSSGridLineNamesValue::create(); 4735 while (CSSParserValue* identValue = identList->current()) { 4736 ASSERT(identValue->unit == CSSPrimitiveValue::CSS_IDENT); 4737 RefPtr<CSSPrimitiveValue> lineName = createPrimitiveStringValue(identValue); 4738 lineNames->append(lineName.release()); 4739 identList->next(); 4740 } 4741 valueList.append(lineNames.release()); 4742 4743 parserValueList->next(); 4744 } 4745 4746 bool CSSParser::parseGridTrackList(CSSPropertyID propId, bool important) 4747 { 4748 ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled()); 4749 4750 CSSParserValue* value = m_valueList->current(); 4751 if (value->id == CSSValueNone) { 4752 if (m_valueList->next()) 4753 return false; 4754 4755 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 4756 return true; 4757 } 4758 4759 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated(); 4760 // Handle leading <ident>*. 4761 value = m_valueList->current(); 4762 if (value && value->unit == CSSParserValue::ValueList) 4763 parseGridLineNames(m_valueList.get(), *values); 4764 4765 bool seenTrackSizeOrRepeatFunction = false; 4766 while (CSSParserValue* currentValue = m_valueList->current()) { 4767 if (currentValue->unit == CSSParserValue::Function && equalIgnoringCase(currentValue->function->name, "repeat(")) { 4768 if (!parseGridTrackRepeatFunction(*values)) 4769 return false; 4770 seenTrackSizeOrRepeatFunction = true; 4771 } else { 4772 RefPtr<CSSValue> value = parseGridTrackSize(*m_valueList); 4773 if (!value) 4774 return false; 4775 values->append(value); 4776 seenTrackSizeOrRepeatFunction = true; 4777 } 4778 // This will handle the trailing <ident>* in the grammar. 4779 value = m_valueList->current(); 4780 if (value && value->unit == CSSParserValue::ValueList) 4781 parseGridLineNames(m_valueList.get(), *values); 4782 } 4783 4784 // We should have found a <track-size> or else it is not a valid <track-list> 4785 if (!seenTrackSizeOrRepeatFunction) 4786 return false; 4787 4788 addProperty(propId, values.release(), important); 4789 return true; 4790 } 4791 4792 bool CSSParser::parseGridTrackRepeatFunction(CSSValueList& list) 4793 { 4794 CSSParserValueList* arguments = m_valueList->current()->function->args.get(); 4795 if (!arguments || arguments->size() < 3 || !validUnit(arguments->valueAt(0), FPositiveInteger) || !isComma(arguments->valueAt(1))) 4796 return false; 4797 4798 ASSERT_WITH_SECURITY_IMPLICATION(arguments->valueAt(0)->fValue > 0); 4799 size_t repetitions = arguments->valueAt(0)->fValue; 4800 RefPtr<CSSValueList> repeatedValues = CSSValueList::createSpaceSeparated(); 4801 arguments->next(); // Skip the repetition count. 4802 arguments->next(); // Skip the comma. 4803 4804 // Handle leading <ident>*. 4805 CSSParserValue* currentValue = arguments->current(); 4806 if (currentValue && currentValue->unit == CSSParserValue::ValueList) 4807 parseGridLineNames(arguments, *repeatedValues); 4808 4809 while (arguments->current()) { 4810 RefPtr<CSSValue> trackSize = parseGridTrackSize(*arguments); 4811 if (!trackSize) 4812 return false; 4813 4814 repeatedValues->append(trackSize); 4815 4816 // This takes care of any trailing <ident>* in the grammar. 4817 currentValue = arguments->current(); 4818 if (currentValue && currentValue->unit == CSSParserValue::ValueList) 4819 parseGridLineNames(arguments, *repeatedValues); 4820 } 4821 4822 for (size_t i = 0; i < repetitions; ++i) { 4823 for (size_t j = 0; j < repeatedValues->length(); ++j) 4824 list.append(repeatedValues->itemWithoutBoundsCheck(j)); 4825 } 4826 4827 // parseGridTrackSize iterated over the repeat arguments, move to the next value. 4828 m_valueList->next(); 4829 return true; 4830 } 4831 4832 PassRefPtr<CSSValue> CSSParser::parseGridTrackSize(CSSParserValueList& inputList) 4833 { 4834 ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled()); 4835 4836 CSSParserValue* currentValue = inputList.current(); 4837 inputList.next(); 4838 4839 if (currentValue->id == CSSValueAuto) 4840 return cssValuePool().createIdentifierValue(CSSValueAuto); 4841 4842 if (currentValue->unit == CSSParserValue::Function && equalIgnoringCase(currentValue->function->name, "minmax(")) { 4843 // The spec defines the following grammar: minmax( <track-breadth> , <track-breadth> ) 4844 CSSParserValueList* arguments = currentValue->function->args.get(); 4845 if (!arguments || arguments->size() != 3 || !isComma(arguments->valueAt(1))) 4846 return 0; 4847 4848 RefPtr<CSSPrimitiveValue> minTrackBreadth = parseGridBreadth(arguments->valueAt(0)); 4849 if (!minTrackBreadth) 4850 return 0; 4851 4852 RefPtr<CSSPrimitiveValue> maxTrackBreadth = parseGridBreadth(arguments->valueAt(2)); 4853 if (!maxTrackBreadth) 4854 return 0; 4855 4856 RefPtr<CSSValueList> parsedArguments = CSSValueList::createCommaSeparated(); 4857 parsedArguments->append(minTrackBreadth); 4858 parsedArguments->append(maxTrackBreadth); 4859 return CSSFunctionValue::create("minmax(", parsedArguments); 4860 } 4861 4862 return parseGridBreadth(currentValue); 4863 } 4864 4865 PassRefPtr<CSSPrimitiveValue> CSSParser::parseGridBreadth(CSSParserValue* currentValue) 4866 { 4867 if (currentValue->id == CSSValueMinContent || currentValue->id == CSSValueMaxContent) 4868 return cssValuePool().createIdentifierValue(currentValue->id); 4869 4870 if (currentValue->unit == CSSPrimitiveValue::CSS_FR) { 4871 double flexValue = currentValue->fValue; 4872 4873 // Fractional unit is a non-negative dimension. 4874 if (flexValue <= 0) 4875 return 0; 4876 4877 return cssValuePool().createValue(flexValue, CSSPrimitiveValue::CSS_FR); 4878 } 4879 4880 if (!validUnit(currentValue, FNonNeg | FLength | FPercent)) 4881 return 0; 4882 4883 return createPrimitiveNumericValue(currentValue); 4884 } 4885 4886 PassRefPtr<CSSValue> CSSParser::parseGridTemplate() 4887 { 4888 NamedGridAreaMap gridAreaMap; 4889 size_t rowCount = 0; 4890 size_t columnCount = 0; 4891 4892 while (CSSParserValue* currentValue = m_valueList->current()) { 4893 if (currentValue->unit != CSSPrimitiveValue::CSS_STRING) 4894 return 0; 4895 4896 String gridRowNames = currentValue->string; 4897 if (!gridRowNames.length()) 4898 return 0; 4899 4900 Vector<String> columnNames; 4901 gridRowNames.split(' ', columnNames); 4902 4903 if (!columnCount) { 4904 columnCount = columnNames.size(); 4905 ASSERT(columnCount); 4906 } else if (columnCount != columnNames.size()) { 4907 // The declaration is invalid is all the rows don't have the number of columns. 4908 return 0; 4909 } 4910 4911 for (size_t currentCol = 0; currentCol < columnCount; ++currentCol) { 4912 const String& gridAreaName = columnNames[currentCol]; 4913 4914 // Unamed areas are always valid (we consider them to be 1x1). 4915 if (gridAreaName == ".") 4916 continue; 4917 4918 // We handle several grid areas with the same name at once to simplify the validation code. 4919 size_t lookAheadCol; 4920 for (lookAheadCol = currentCol; lookAheadCol < (columnCount - 1); ++lookAheadCol) { 4921 if (columnNames[lookAheadCol + 1] != gridAreaName) 4922 break; 4923 } 4924 4925 NamedGridAreaMap::iterator gridAreaIt = gridAreaMap.find(gridAreaName); 4926 if (gridAreaIt == gridAreaMap.end()) { 4927 gridAreaMap.add(gridAreaName, GridCoordinate(GridSpan(rowCount, rowCount), GridSpan(currentCol, lookAheadCol))); 4928 } else { 4929 GridCoordinate& gridCoordinate = gridAreaIt->value; 4930 4931 // The following checks test that the grid area is a single filled-in rectangle. 4932 // 1. The new row is adjacent to the previously parsed row. 4933 if (rowCount != gridCoordinate.rows.initialPositionIndex + 1) 4934 return 0; 4935 4936 // 2. The new area starts at the same position as the previously parsed area. 4937 if (currentCol != gridCoordinate.columns.initialPositionIndex) 4938 return 0; 4939 4940 // 3. The new area ends at the same position as the previously parsed area. 4941 if (lookAheadCol != gridCoordinate.columns.finalPositionIndex) 4942 return 0; 4943 4944 ++gridCoordinate.rows.finalPositionIndex; 4945 } 4946 currentCol = lookAheadCol; 4947 } 4948 4949 ++rowCount; 4950 m_valueList->next(); 4951 } 4952 4953 if (!rowCount || !columnCount) 4954 return 0; 4955 4956 return CSSGridTemplateValue::create(gridAreaMap, rowCount, columnCount); 4957 } 4958 4959 PassRefPtr<CSSValue> CSSParser::parseCounterContent(CSSParserValueList* args, bool counters) 4960 { 4961 unsigned numArgs = args->size(); 4962 if (counters && numArgs != 3 && numArgs != 5) 4963 return 0; 4964 if (!counters && numArgs != 1 && numArgs != 3) 4965 return 0; 4966 4967 CSSParserValue* i = args->current(); 4968 if (i->unit != CSSPrimitiveValue::CSS_IDENT) 4969 return 0; 4970 RefPtr<CSSPrimitiveValue> identifier = createPrimitiveStringValue(i); 4971 4972 RefPtr<CSSPrimitiveValue> separator; 4973 if (!counters) 4974 separator = cssValuePool().createValue(String(), CSSPrimitiveValue::CSS_STRING); 4975 else { 4976 i = args->next(); 4977 if (i->unit != CSSParserValue::Operator || i->iValue != ',') 4978 return 0; 4979 4980 i = args->next(); 4981 if (i->unit != CSSPrimitiveValue::CSS_STRING) 4982 return 0; 4983 4984 separator = createPrimitiveStringValue(i); 4985 } 4986 4987 RefPtr<CSSPrimitiveValue> listStyle; 4988 i = args->next(); 4989 if (!i) // Make the list style default decimal 4990 listStyle = cssValuePool().createIdentifierValue(CSSValueDecimal); 4991 else { 4992 if (i->unit != CSSParserValue::Operator || i->iValue != ',') 4993 return 0; 4994 4995 i = args->next(); 4996 if (i->unit != CSSPrimitiveValue::CSS_IDENT) 4997 return 0; 4998 4999 CSSValueID listStyleID = CSSValueInvalid; 5000 if (i->id == CSSValueNone || (i->id >= CSSValueDisc && i->id <= CSSValueKatakanaIroha)) 5001 listStyleID = i->id; 5002 else 5003 return 0; 5004 5005 listStyle = cssValuePool().createIdentifierValue(listStyleID); 5006 } 5007 5008 return cssValuePool().createValue(Counter::create(identifier.release(), listStyle.release(), separator.release())); 5009 } 5010 5011 bool CSSParser::parseClipShape(CSSPropertyID propId, bool important) 5012 { 5013 CSSParserValue* value = m_valueList->current(); 5014 CSSParserValueList* args = value->function->args.get(); 5015 5016 if (!equalIgnoringCase(value->function->name, "rect(") || !args) 5017 return false; 5018 5019 // rect(t, r, b, l) || rect(t r b l) 5020 if (args->size() != 4 && args->size() != 7) 5021 return false; 5022 RefPtr<Rect> rect = Rect::create(); 5023 bool valid = true; 5024 int i = 0; 5025 CSSParserValue* a = args->current(); 5026 while (a) { 5027 valid = a->id == CSSValueAuto || validUnit(a, FLength); 5028 if (!valid) 5029 break; 5030 RefPtr<CSSPrimitiveValue> length = a->id == CSSValueAuto ? 5031 cssValuePool().createIdentifierValue(CSSValueAuto) : 5032 createPrimitiveNumericValue(a); 5033 if (i == 0) 5034 rect->setTop(length); 5035 else if (i == 1) 5036 rect->setRight(length); 5037 else if (i == 2) 5038 rect->setBottom(length); 5039 else 5040 rect->setLeft(length); 5041 a = args->next(); 5042 if (a && args->size() == 7) { 5043 if (a->unit == CSSParserValue::Operator && a->iValue == ',') { 5044 a = args->next(); 5045 } else { 5046 valid = false; 5047 break; 5048 } 5049 } 5050 i++; 5051 } 5052 if (valid) { 5053 addProperty(propId, cssValuePool().createValue(rect.release()), important); 5054 m_valueList->next(); 5055 return true; 5056 } 5057 return false; 5058 } 5059 5060 PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeRectangle(CSSParserValueList* args) 5061 { 5062 ASSERT(args); 5063 5064 // rect(x, y, width, height, [[rx], ry]) 5065 if (args->size() != 7 && args->size() != 9 && args->size() != 11) 5066 return 0; 5067 5068 RefPtr<CSSBasicShapeRectangle> shape = CSSBasicShapeRectangle::create(); 5069 5070 unsigned argumentNumber = 0; 5071 CSSParserValue* argument = args->current(); 5072 while (argument) { 5073 Units unitFlags = FLength | FPercent; 5074 if (argumentNumber > 1) { 5075 // Arguments width, height, rx, and ry cannot be negative. 5076 unitFlags = unitFlags | FNonNeg; 5077 } 5078 if (!validUnit(argument, unitFlags)) 5079 return 0; 5080 5081 RefPtr<CSSPrimitiveValue> length = createPrimitiveNumericValue(argument); 5082 ASSERT(argumentNumber < 6); 5083 switch (argumentNumber) { 5084 case 0: 5085 shape->setX(length); 5086 break; 5087 case 1: 5088 shape->setY(length); 5089 break; 5090 case 2: 5091 shape->setWidth(length); 5092 break; 5093 case 3: 5094 shape->setHeight(length); 5095 break; 5096 case 4: 5097 shape->setRadiusX(length); 5098 break; 5099 case 5: 5100 shape->setRadiusY(length); 5101 break; 5102 } 5103 argument = args->next(); 5104 if (argument) { 5105 if (!isComma(argument)) 5106 return 0; 5107 5108 argument = args->next(); 5109 } 5110 argumentNumber++; 5111 } 5112 5113 if (argumentNumber < 4) 5114 return 0; 5115 return shape; 5116 } 5117 5118 PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeInsetRectangle(CSSParserValueList* args) 5119 { 5120 ASSERT(args); 5121 5122 // inset-rectangle(top, right, bottom, left, [[rx], ry]) 5123 if (args->size() != 7 && args->size() != 9 && args->size() != 11) 5124 return 0; 5125 5126 RefPtr<CSSBasicShapeInsetRectangle> shape = CSSBasicShapeInsetRectangle::create(); 5127 5128 unsigned argumentNumber = 0; 5129 CSSParserValue* argument = args->current(); 5130 while (argument) { 5131 Units unitFlags = FLength | FPercent | FNonNeg; 5132 if (!validUnit(argument, unitFlags)) 5133 return 0; 5134 5135 RefPtr<CSSPrimitiveValue> length = createPrimitiveNumericValue(argument); 5136 ASSERT(argumentNumber < 6); 5137 switch (argumentNumber) { 5138 case 0: 5139 shape->setTop(length); 5140 break; 5141 case 1: 5142 shape->setRight(length); 5143 break; 5144 case 2: 5145 shape->setBottom(length); 5146 break; 5147 case 3: 5148 shape->setLeft(length); 5149 break; 5150 case 4: 5151 shape->setRadiusX(length); 5152 break; 5153 case 5: 5154 shape->setRadiusY(length); 5155 break; 5156 } 5157 argument = args->next(); 5158 if (argument) { 5159 if (!isComma(argument)) 5160 return 0; 5161 5162 argument = args->next(); 5163 } 5164 argumentNumber++; 5165 } 5166 5167 if (argumentNumber < 4) 5168 return 0; 5169 return shape; 5170 } 5171 5172 PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeCircle(CSSParserValueList* args) 5173 { 5174 ASSERT(args); 5175 5176 // circle(centerX, centerY, radius) 5177 if (args->size() != 5) 5178 return 0; 5179 5180 RefPtr<CSSBasicShapeCircle> shape = CSSBasicShapeCircle::create(); 5181 5182 unsigned argumentNumber = 0; 5183 CSSParserValue* argument = args->current(); 5184 while (argument) { 5185 Units unitFlags = FLength | FPercent; 5186 if (argumentNumber == 2) { 5187 // Argument radius cannot be negative. 5188 unitFlags = unitFlags | FNonNeg; 5189 } 5190 5191 if (!validUnit(argument, unitFlags)) 5192 return 0; 5193 5194 RefPtr<CSSPrimitiveValue> length = createPrimitiveNumericValue(argument); 5195 ASSERT(argumentNumber < 3); 5196 switch (argumentNumber) { 5197 case 0: 5198 shape->setCenterX(length); 5199 break; 5200 case 1: 5201 shape->setCenterY(length); 5202 break; 5203 case 2: 5204 shape->setRadius(length); 5205 break; 5206 } 5207 5208 argument = args->next(); 5209 if (argument) { 5210 if (!isComma(argument)) 5211 return 0; 5212 argument = args->next(); 5213 } 5214 argumentNumber++; 5215 } 5216 5217 if (argumentNumber < 3) 5218 return 0; 5219 return shape; 5220 } 5221 5222 PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeEllipse(CSSParserValueList* args) 5223 { 5224 ASSERT(args); 5225 5226 // ellipse(centerX, centerY, radiusX, radiusY) 5227 if (args->size() != 7) 5228 return 0; 5229 5230 RefPtr<CSSBasicShapeEllipse> shape = CSSBasicShapeEllipse::create(); 5231 unsigned argumentNumber = 0; 5232 CSSParserValue* argument = args->current(); 5233 while (argument) { 5234 Units unitFlags = FLength | FPercent; 5235 if (argumentNumber > 1) { 5236 // Arguments radiusX and radiusY cannot be negative. 5237 unitFlags = unitFlags | FNonNeg; 5238 } 5239 if (!validUnit(argument, unitFlags)) 5240 return 0; 5241 5242 RefPtr<CSSPrimitiveValue> length = createPrimitiveNumericValue(argument); 5243 ASSERT(argumentNumber < 4); 5244 switch (argumentNumber) { 5245 case 0: 5246 shape->setCenterX(length); 5247 break; 5248 case 1: 5249 shape->setCenterY(length); 5250 break; 5251 case 2: 5252 shape->setRadiusX(length); 5253 break; 5254 case 3: 5255 shape->setRadiusY(length); 5256 break; 5257 } 5258 5259 argument = args->next(); 5260 if (argument) { 5261 if (!isComma(argument)) 5262 return 0; 5263 argument = args->next(); 5264 } 5265 argumentNumber++; 5266 } 5267 5268 if (argumentNumber < 4) 5269 return 0; 5270 return shape; 5271 } 5272 5273 PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapePolygon(CSSParserValueList* args) 5274 { 5275 ASSERT(args); 5276 5277 unsigned size = args->size(); 5278 if (!size) 5279 return 0; 5280 5281 RefPtr<CSSBasicShapePolygon> shape = CSSBasicShapePolygon::create(); 5282 5283 CSSParserValue* argument = args->current(); 5284 if (argument->id == CSSValueEvenodd || argument->id == CSSValueNonzero) { 5285 shape->setWindRule(argument->id == CSSValueEvenodd ? RULE_EVENODD : RULE_NONZERO); 5286 5287 if (!isComma(args->next())) 5288 return 0; 5289 5290 argument = args->next(); 5291 size -= 2; 5292 } 5293 5294 // <length> <length>, ... <length> <length> -> each pair has 3 elements except the last one 5295 if (!size || (size % 3) - 2) 5296 return 0; 5297 5298 CSSParserValue* argumentX = argument; 5299 while (argumentX) { 5300 if (!validUnit(argumentX, FLength | FPercent)) 5301 return 0; 5302 5303 CSSParserValue* argumentY = args->next(); 5304 if (!argumentY || !validUnit(argumentY, FLength | FPercent)) 5305 return 0; 5306 5307 RefPtr<CSSPrimitiveValue> xLength = createPrimitiveNumericValue(argumentX); 5308 RefPtr<CSSPrimitiveValue> yLength = createPrimitiveNumericValue(argumentY); 5309 5310 shape->appendPoint(xLength.release(), yLength.release()); 5311 5312 CSSParserValue* commaOrNull = args->next(); 5313 if (!commaOrNull) 5314 argumentX = 0; 5315 else if (!isComma(commaOrNull)) 5316 return 0; 5317 else 5318 argumentX = args->next(); 5319 } 5320 5321 return shape; 5322 } 5323 5324 bool CSSParser::parseBasicShape(CSSPropertyID propId, bool important) 5325 { 5326 CSSParserValue* value = m_valueList->current(); 5327 ASSERT(value->unit == CSSParserValue::Function); 5328 CSSParserValueList* args = value->function->args.get(); 5329 5330 if (!args) 5331 return false; 5332 5333 RefPtr<CSSBasicShape> shape; 5334 if (equalIgnoringCase(value->function->name, "rectangle(")) 5335 shape = parseBasicShapeRectangle(args); 5336 else if (equalIgnoringCase(value->function->name, "circle(")) 5337 shape = parseBasicShapeCircle(args); 5338 else if (equalIgnoringCase(value->function->name, "ellipse(")) 5339 shape = parseBasicShapeEllipse(args); 5340 else if (equalIgnoringCase(value->function->name, "polygon(")) 5341 shape = parseBasicShapePolygon(args); 5342 else if (equalIgnoringCase(value->function->name, "inset-rectangle(")) 5343 shape = parseBasicShapeInsetRectangle(args); 5344 5345 if (!shape) 5346 return false; 5347 5348 addProperty(propId, cssValuePool().createValue(shape.release()), important); 5349 m_valueList->next(); 5350 return true; 5351 } 5352 5353 // [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 'font-family' 5354 bool CSSParser::parseFont(bool important) 5355 { 5356 // Let's check if there is an inherit or initial somewhere in the shorthand. 5357 for (unsigned i = 0; i < m_valueList->size(); ++i) { 5358 if (m_valueList->valueAt(i)->id == CSSValueInherit || m_valueList->valueAt(i)->id == CSSValueInitial) 5359 return false; 5360 } 5361 5362 ShorthandScope scope(this, CSSPropertyFont); 5363 // Optional font-style, font-variant and font-weight. 5364 bool fontStyleParsed = false; 5365 bool fontVariantParsed = false; 5366 bool fontWeightParsed = false; 5367 CSSParserValue* value; 5368 while ((value = m_valueList->current())) { 5369 if (!fontStyleParsed && isValidKeywordPropertyAndValue(CSSPropertyFontStyle, value->id, m_context)) { 5370 addProperty(CSSPropertyFontStyle, cssValuePool().createIdentifierValue(value->id), important); 5371 fontStyleParsed = true; 5372 } else if (!fontVariantParsed && (value->id == CSSValueNormal || value->id == CSSValueSmallCaps)) { 5373 // Font variant in the shorthand is particular, it only accepts normal or small-caps. 5374 addProperty(CSSPropertyFontVariant, cssValuePool().createIdentifierValue(value->id), important); 5375 fontVariantParsed = true; 5376 } else if (!fontWeightParsed && parseFontWeight(important)) 5377 fontWeightParsed = true; 5378 else 5379 break; 5380 m_valueList->next(); 5381 } 5382 5383 if (!value) 5384 return false; 5385 5386 if (!fontStyleParsed) 5387 addProperty(CSSPropertyFontStyle, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 5388 if (!fontVariantParsed) 5389 addProperty(CSSPropertyFontVariant, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 5390 if (!fontWeightParsed) 5391 addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 5392 5393 // Now a font size _must_ come. 5394 // <absolute-size> | <relative-size> | <length> | <percentage> | inherit 5395 if (!parseFontSize(important)) 5396 return false; 5397 5398 value = m_valueList->current(); 5399 if (!value) 5400 return false; 5401 5402 if (isForwardSlashOperator(value)) { 5403 // The line-height property. 5404 value = m_valueList->next(); 5405 if (!value) 5406 return false; 5407 if (!parseLineHeight(important)) 5408 return false; 5409 } else 5410 addProperty(CSSPropertyLineHeight, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 5411 5412 // Font family must come now. 5413 RefPtr<CSSValue> parsedFamilyValue = parseFontFamily(); 5414 if (!parsedFamilyValue) 5415 return false; 5416 5417 addProperty(CSSPropertyFontFamily, parsedFamilyValue.release(), important); 5418 5419 // FIXME: http://www.w3.org/TR/2011/WD-css3-fonts-20110324/#font-prop requires that 5420 // "font-stretch", "font-size-adjust", and "font-kerning" be reset to their initial values 5421 // but we don't seem to support them at the moment. They should also be added here once implemented. 5422 if (m_valueList->current()) 5423 return false; 5424 5425 return true; 5426 } 5427 5428 class FontFamilyValueBuilder { 5429 public: 5430 FontFamilyValueBuilder(CSSValueList* list) 5431 : m_list(list) 5432 { 5433 } 5434 5435 void add(const CSSParserString& string) 5436 { 5437 if (!m_builder.isEmpty()) 5438 m_builder.append(' '); 5439 5440 if (string.is8Bit()) { 5441 m_builder.append(string.characters8(), string.length()); 5442 return; 5443 } 5444 5445 m_builder.append(string.characters16(), string.length()); 5446 } 5447 5448 void commit() 5449 { 5450 if (m_builder.isEmpty()) 5451 return; 5452 m_list->append(cssValuePool().createFontFamilyValue(m_builder.toString())); 5453 m_builder.clear(); 5454 } 5455 5456 private: 5457 StringBuilder m_builder; 5458 CSSValueList* m_list; 5459 }; 5460 5461 PassRefPtr<CSSValueList> CSSParser::parseFontFamily() 5462 { 5463 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 5464 CSSParserValue* value = m_valueList->current(); 5465 5466 FontFamilyValueBuilder familyBuilder(list.get()); 5467 bool inFamily = false; 5468 5469 while (value) { 5470 CSSParserValue* nextValue = m_valueList->next(); 5471 bool nextValBreaksFont = !nextValue || 5472 (nextValue->unit == CSSParserValue::Operator && nextValue->iValue == ','); 5473 bool nextValIsFontName = nextValue && 5474 ((nextValue->id >= CSSValueSerif && nextValue->id <= CSSValueWebkitBody) || 5475 (nextValue->unit == CSSPrimitiveValue::CSS_STRING || nextValue->unit == CSSPrimitiveValue::CSS_IDENT)); 5476 5477 bool valueIsKeyword = value->id == CSSValueInitial || value->id == CSSValueInherit || value->id == CSSValueDefault; 5478 if (valueIsKeyword && !inFamily) { 5479 if (nextValBreaksFont) 5480 value = m_valueList->next(); 5481 else if (nextValIsFontName) 5482 value = nextValue; 5483 continue; 5484 } 5485 5486 if (value->id >= CSSValueSerif && value->id <= CSSValueWebkitBody) { 5487 if (inFamily) 5488 familyBuilder.add(value->string); 5489 else if (nextValBreaksFont || !nextValIsFontName) 5490 list->append(cssValuePool().createIdentifierValue(value->id)); 5491 else { 5492 familyBuilder.commit(); 5493 familyBuilder.add(value->string); 5494 inFamily = true; 5495 } 5496 } else if (value->unit == CSSPrimitiveValue::CSS_STRING) { 5497 // Strings never share in a family name. 5498 inFamily = false; 5499 familyBuilder.commit(); 5500 list->append(cssValuePool().createFontFamilyValue(value->string)); 5501 } else if (value->unit == CSSPrimitiveValue::CSS_IDENT) { 5502 if (inFamily) 5503 familyBuilder.add(value->string); 5504 else if (nextValBreaksFont || !nextValIsFontName) 5505 list->append(cssValuePool().createFontFamilyValue(value->string)); 5506 else { 5507 familyBuilder.commit(); 5508 familyBuilder.add(value->string); 5509 inFamily = true; 5510 } 5511 } else { 5512 break; 5513 } 5514 5515 if (!nextValue) 5516 break; 5517 5518 if (nextValBreaksFont) { 5519 value = m_valueList->next(); 5520 familyBuilder.commit(); 5521 inFamily = false; 5522 } 5523 else if (nextValIsFontName) 5524 value = nextValue; 5525 else 5526 break; 5527 } 5528 familyBuilder.commit(); 5529 5530 if (!list->length()) 5531 list = 0; 5532 return list.release(); 5533 } 5534 5535 bool CSSParser::parseLineHeight(bool important) 5536 { 5537 CSSParserValue* value = m_valueList->current(); 5538 CSSValueID id = value->id; 5539 bool validPrimitive = false; 5540 // normal | <number> | <length> | <percentage> | inherit 5541 if (id == CSSValueNormal) 5542 validPrimitive = true; 5543 else 5544 validPrimitive = (!id && validUnit(value, FNumber | FLength | FPercent | FNonNeg)); 5545 if (validPrimitive && (!m_valueList->next() || inShorthand())) 5546 addProperty(CSSPropertyLineHeight, parseValidPrimitive(id, value), important); 5547 return validPrimitive; 5548 } 5549 5550 bool CSSParser::parseFontSize(bool important) 5551 { 5552 CSSParserValue* value = m_valueList->current(); 5553 CSSValueID id = value->id; 5554 bool validPrimitive = false; 5555 // <absolute-size> | <relative-size> | <length> | <percentage> | inherit 5556 if (id >= CSSValueXxSmall && id <= CSSValueLarger) 5557 validPrimitive = true; 5558 else 5559 validPrimitive = validUnit(value, FLength | FPercent | FNonNeg); 5560 if (validPrimitive && (!m_valueList->next() || inShorthand())) 5561 addProperty(CSSPropertyFontSize, parseValidPrimitive(id, value), important); 5562 return validPrimitive; 5563 } 5564 5565 bool CSSParser::parseFontVariant(bool important) 5566 { 5567 RefPtr<CSSValueList> values; 5568 if (m_valueList->size() > 1) 5569 values = CSSValueList::createCommaSeparated(); 5570 CSSParserValue* val; 5571 bool expectComma = false; 5572 while ((val = m_valueList->current())) { 5573 RefPtr<CSSPrimitiveValue> parsedValue; 5574 if (!expectComma) { 5575 expectComma = true; 5576 if (val->id == CSSValueNormal || val->id == CSSValueSmallCaps) 5577 parsedValue = cssValuePool().createIdentifierValue(val->id); 5578 else if (val->id == CSSValueAll && !values) { 5579 // 'all' is only allowed in @font-face and with no other values. Make a value list to 5580 // indicate that we are in the @font-face case. 5581 values = CSSValueList::createCommaSeparated(); 5582 parsedValue = cssValuePool().createIdentifierValue(val->id); 5583 } 5584 } else if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 5585 expectComma = false; 5586 m_valueList->next(); 5587 continue; 5588 } 5589 5590 if (!parsedValue) 5591 return false; 5592 5593 m_valueList->next(); 5594 5595 if (values) 5596 values->append(parsedValue.release()); 5597 else { 5598 addProperty(CSSPropertyFontVariant, parsedValue.release(), important); 5599 return true; 5600 } 5601 } 5602 5603 if (values && values->length()) { 5604 m_hasFontFaceOnlyValues = true; 5605 addProperty(CSSPropertyFontVariant, values.release(), important); 5606 return true; 5607 } 5608 5609 return false; 5610 } 5611 5612 bool CSSParser::parseFontWeight(bool important) 5613 { 5614 CSSParserValue* value = m_valueList->current(); 5615 if ((value->id >= CSSValueNormal) && (value->id <= CSSValue900)) { 5616 addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(value->id), important); 5617 return true; 5618 } 5619 if (validUnit(value, FInteger | FNonNeg, HTMLQuirksMode)) { 5620 int weight = static_cast<int>(value->fValue); 5621 if (!(weight % 100) && weight >= 100 && weight <= 900) { 5622 addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(static_cast<CSSValueID>(CSSValue100 + weight / 100 - 1)), important); 5623 return true; 5624 } 5625 } 5626 return false; 5627 } 5628 5629 bool CSSParser::parseFontFaceSrcURI(CSSValueList* valueList) 5630 { 5631 RefPtr<CSSFontFaceSrcValue> uriValue(CSSFontFaceSrcValue::create(completeURL(m_valueList->current()->string))); 5632 5633 CSSParserValue* value = m_valueList->next(); 5634 if (!value) { 5635 valueList->append(uriValue.release()); 5636 return true; 5637 } 5638 if (value->unit == CSSParserValue::Operator && value->iValue == ',') { 5639 m_valueList->next(); 5640 valueList->append(uriValue.release()); 5641 return true; 5642 } 5643 5644 if (value->unit != CSSParserValue::Function || !equalIgnoringCase(value->function->name, "format(")) 5645 return false; 5646 5647 // FIXME: http://www.w3.org/TR/2011/WD-css3-fonts-20111004/ says that format() contains a comma-separated list of strings, 5648 // but CSSFontFaceSrcValue stores only one format. Allowing one format for now. 5649 CSSParserValueList* args = value->function->args.get(); 5650 if (!args || args->size() != 1 || (args->current()->unit != CSSPrimitiveValue::CSS_STRING && args->current()->unit != CSSPrimitiveValue::CSS_IDENT)) 5651 return false; 5652 uriValue->setFormat(args->current()->string); 5653 valueList->append(uriValue.release()); 5654 value = m_valueList->next(); 5655 if (value && value->unit == CSSParserValue::Operator && value->iValue == ',') 5656 m_valueList->next(); 5657 return true; 5658 } 5659 5660 bool CSSParser::parseFontFaceSrcLocal(CSSValueList* valueList) 5661 { 5662 CSSParserValueList* args = m_valueList->current()->function->args.get(); 5663 if (!args || !args->size()) 5664 return false; 5665 5666 if (args->size() == 1 && args->current()->unit == CSSPrimitiveValue::CSS_STRING) 5667 valueList->append(CSSFontFaceSrcValue::createLocal(args->current()->string)); 5668 else if (args->current()->unit == CSSPrimitiveValue::CSS_IDENT) { 5669 StringBuilder builder; 5670 for (CSSParserValue* localValue = args->current(); localValue; localValue = args->next()) { 5671 if (localValue->unit != CSSPrimitiveValue::CSS_IDENT) 5672 return false; 5673 if (!builder.isEmpty()) 5674 builder.append(' '); 5675 builder.append(localValue->string); 5676 } 5677 valueList->append(CSSFontFaceSrcValue::createLocal(builder.toString())); 5678 } else 5679 return false; 5680 5681 if (CSSParserValue* value = m_valueList->next()) { 5682 if (value->unit == CSSParserValue::Operator && value->iValue == ',') 5683 m_valueList->next(); 5684 } 5685 return true; 5686 } 5687 5688 bool CSSParser::parseFontFaceSrc() 5689 { 5690 RefPtr<CSSValueList> values(CSSValueList::createCommaSeparated()); 5691 5692 while (CSSParserValue* value = m_valueList->current()) { 5693 if (value->unit == CSSPrimitiveValue::CSS_URI) { 5694 if (!parseFontFaceSrcURI(values.get())) 5695 return false; 5696 } else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "local(")) { 5697 if (!parseFontFaceSrcLocal(values.get())) 5698 return false; 5699 } else 5700 return false; 5701 } 5702 if (!values->length()) 5703 return false; 5704 5705 addProperty(CSSPropertySrc, values.release(), m_important); 5706 m_valueList->next(); 5707 return true; 5708 } 5709 5710 bool CSSParser::parseFontFaceUnicodeRange() 5711 { 5712 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); 5713 bool failed = false; 5714 bool operatorExpected = false; 5715 for (; m_valueList->current(); m_valueList->next(), operatorExpected = !operatorExpected) { 5716 if (operatorExpected) { 5717 if (m_valueList->current()->unit == CSSParserValue::Operator && m_valueList->current()->iValue == ',') 5718 continue; 5719 failed = true; 5720 break; 5721 } 5722 if (m_valueList->current()->unit != CSSPrimitiveValue::CSS_UNICODE_RANGE) { 5723 failed = true; 5724 break; 5725 } 5726 5727 String rangeString = m_valueList->current()->string; 5728 UChar32 from = 0; 5729 UChar32 to = 0; 5730 unsigned length = rangeString.length(); 5731 5732 if (length < 3) { 5733 failed = true; 5734 break; 5735 } 5736 5737 unsigned i = 2; 5738 while (i < length) { 5739 UChar c = rangeString[i]; 5740 if (c == '-' || c == '?') 5741 break; 5742 from *= 16; 5743 if (c >= '0' && c <= '9') 5744 from += c - '0'; 5745 else if (c >= 'A' && c <= 'F') 5746 from += 10 + c - 'A'; 5747 else if (c >= 'a' && c <= 'f') 5748 from += 10 + c - 'a'; 5749 else { 5750 failed = true; 5751 break; 5752 } 5753 i++; 5754 } 5755 if (failed) 5756 break; 5757 5758 if (i == length) 5759 to = from; 5760 else if (rangeString[i] == '?') { 5761 unsigned span = 1; 5762 while (i < length && rangeString[i] == '?') { 5763 span *= 16; 5764 from *= 16; 5765 i++; 5766 } 5767 if (i < length) 5768 failed = true; 5769 to = from + span - 1; 5770 } else { 5771 if (length < i + 2) { 5772 failed = true; 5773 break; 5774 } 5775 i++; 5776 while (i < length) { 5777 UChar c = rangeString[i]; 5778 to *= 16; 5779 if (c >= '0' && c <= '9') 5780 to += c - '0'; 5781 else if (c >= 'A' && c <= 'F') 5782 to += 10 + c - 'A'; 5783 else if (c >= 'a' && c <= 'f') 5784 to += 10 + c - 'a'; 5785 else { 5786 failed = true; 5787 break; 5788 } 5789 i++; 5790 } 5791 if (failed) 5792 break; 5793 } 5794 if (from <= to) 5795 values->append(CSSUnicodeRangeValue::create(from, to)); 5796 } 5797 if (failed || !values->length()) 5798 return false; 5799 addProperty(CSSPropertyUnicodeRange, values.release(), m_important); 5800 return true; 5801 } 5802 5803 // Returns the number of characters which form a valid double 5804 // and are terminated by the given terminator character 5805 template <typename CharacterType> 5806 static int checkForValidDouble(const CharacterType* string, const CharacterType* end, const char terminator) 5807 { 5808 int length = end - string; 5809 if (length < 1) 5810 return 0; 5811 5812 bool decimalMarkSeen = false; 5813 int processedLength = 0; 5814 5815 for (int i = 0; i < length; ++i) { 5816 if (string[i] == terminator) { 5817 processedLength = i; 5818 break; 5819 } 5820 if (!isASCIIDigit(string[i])) { 5821 if (!decimalMarkSeen && string[i] == '.') 5822 decimalMarkSeen = true; 5823 else 5824 return 0; 5825 } 5826 } 5827 5828 if (decimalMarkSeen && processedLength == 1) 5829 return 0; 5830 5831 return processedLength; 5832 } 5833 5834 // Returns the number of characters consumed for parsing a valid double 5835 // terminated by the given terminator character 5836 template <typename CharacterType> 5837 static int parseDouble(const CharacterType* string, const CharacterType* end, const char terminator, double& value) 5838 { 5839 int length = checkForValidDouble(string, end, terminator); 5840 if (!length) 5841 return 0; 5842 5843 int position = 0; 5844 double localValue = 0; 5845 5846 // The consumed characters here are guaranteed to be 5847 // ASCII digits with or without a decimal mark 5848 for (; position < length; ++position) { 5849 if (string[position] == '.') 5850 break; 5851 localValue = localValue * 10 + string[position] - '0'; 5852 } 5853 5854 if (++position == length) { 5855 value = localValue; 5856 return length; 5857 } 5858 5859 double fraction = 0; 5860 double scale = 1; 5861 5862 while (position < length && scale < MAX_SCALE) { 5863 fraction = fraction * 10 + string[position++] - '0'; 5864 scale *= 10; 5865 } 5866 5867 value = localValue + fraction / scale; 5868 return length; 5869 } 5870 5871 template <typename CharacterType> 5872 static bool parseColorIntOrPercentage(const CharacterType*& string, const CharacterType* end, const char terminator, CSSPrimitiveValue::UnitTypes& expect, int& value) 5873 { 5874 const CharacterType* current = string; 5875 double localValue = 0; 5876 bool negative = false; 5877 while (current != end && isHTMLSpace<CharacterType>(*current)) 5878 current++; 5879 if (current != end && *current == '-') { 5880 negative = true; 5881 current++; 5882 } 5883 if (current == end || !isASCIIDigit(*current)) 5884 return false; 5885 while (current != end && isASCIIDigit(*current)) { 5886 double newValue = localValue * 10 + *current++ - '0'; 5887 if (newValue >= 255) { 5888 // Clamp values at 255. 5889 localValue = 255; 5890 while (current != end && isASCIIDigit(*current)) 5891 ++current; 5892 break; 5893 } 5894 localValue = newValue; 5895 } 5896 5897 if (current == end) 5898 return false; 5899 5900 if (expect == CSSPrimitiveValue::CSS_NUMBER && (*current == '.' || *current == '%')) 5901 return false; 5902 5903 if (*current == '.') { 5904 // We already parsed the integral part, try to parse 5905 // the fraction part of the percentage value. 5906 double percentage = 0; 5907 int numCharactersParsed = parseDouble(current, end, '%', percentage); 5908 if (!numCharactersParsed) 5909 return false; 5910 current += numCharactersParsed; 5911 if (*current != '%') 5912 return false; 5913 localValue += percentage; 5914 } 5915 5916 if (expect == CSSPrimitiveValue::CSS_PERCENTAGE && *current != '%') 5917 return false; 5918 5919 if (*current == '%') { 5920 expect = CSSPrimitiveValue::CSS_PERCENTAGE; 5921 localValue = localValue / 100.0 * 256.0; 5922 // Clamp values at 255 for percentages over 100% 5923 if (localValue > 255) 5924 localValue = 255; 5925 current++; 5926 } else 5927 expect = CSSPrimitiveValue::CSS_NUMBER; 5928 5929 while (current != end && isHTMLSpace<CharacterType>(*current)) 5930 current++; 5931 if (current == end || *current++ != terminator) 5932 return false; 5933 // Clamp negative values at zero. 5934 value = negative ? 0 : static_cast<int>(localValue); 5935 string = current; 5936 return true; 5937 } 5938 5939 template <typename CharacterType> 5940 static inline bool isTenthAlpha(const CharacterType* string, const int length) 5941 { 5942 // "0.X" 5943 if (length == 3 && string[0] == '0' && string[1] == '.' && isASCIIDigit(string[2])) 5944 return true; 5945 5946 // ".X" 5947 if (length == 2 && string[0] == '.' && isASCIIDigit(string[1])) 5948 return true; 5949 5950 return false; 5951 } 5952 5953 template <typename CharacterType> 5954 static inline bool parseAlphaValue(const CharacterType*& string, const CharacterType* end, const char terminator, int& value) 5955 { 5956 while (string != end && isHTMLSpace<CharacterType>(*string)) 5957 string++; 5958 5959 bool negative = false; 5960 5961 if (string != end && *string == '-') { 5962 negative = true; 5963 string++; 5964 } 5965 5966 value = 0; 5967 5968 int length = end - string; 5969 if (length < 2) 5970 return false; 5971 5972 if (string[length - 1] != terminator || !isASCIIDigit(string[length - 2])) 5973 return false; 5974 5975 if (string[0] != '0' && string[0] != '1' && string[0] != '.') { 5976 if (checkForValidDouble(string, end, terminator)) { 5977 value = negative ? 0 : 255; 5978 string = end; 5979 return true; 5980 } 5981 return false; 5982 } 5983 5984 if (length == 2 && string[0] != '.') { 5985 value = !negative && string[0] == '1' ? 255 : 0; 5986 string = end; 5987 return true; 5988 } 5989 5990 if (isTenthAlpha(string, length - 1)) { 5991 static const int tenthAlphaValues[] = { 0, 25, 51, 76, 102, 127, 153, 179, 204, 230 }; 5992 value = negative ? 0 : tenthAlphaValues[string[length - 2] - '0']; 5993 string = end; 5994 return true; 5995 } 5996 5997 double alpha = 0; 5998 if (!parseDouble(string, end, terminator, alpha)) 5999 return false; 6000 value = negative ? 0 : static_cast<int>(alpha * nextafter(256.0, 0.0)); 6001 string = end; 6002 return true; 6003 } 6004 6005 template <typename CharacterType> 6006 static inline bool mightBeRGBA(const CharacterType* characters, unsigned length) 6007 { 6008 if (length < 5) 6009 return false; 6010 return characters[4] == '(' 6011 && isASCIIAlphaCaselessEqual(characters[0], 'r') 6012 && isASCIIAlphaCaselessEqual(characters[1], 'g') 6013 && isASCIIAlphaCaselessEqual(characters[2], 'b') 6014 && isASCIIAlphaCaselessEqual(characters[3], 'a'); 6015 } 6016 6017 template <typename CharacterType> 6018 static inline bool mightBeRGB(const CharacterType* characters, unsigned length) 6019 { 6020 if (length < 4) 6021 return false; 6022 return characters[3] == '(' 6023 && isASCIIAlphaCaselessEqual(characters[0], 'r') 6024 && isASCIIAlphaCaselessEqual(characters[1], 'g') 6025 && isASCIIAlphaCaselessEqual(characters[2], 'b'); 6026 } 6027 6028 template <typename CharacterType> 6029 static inline bool fastParseColorInternal(RGBA32& rgb, const CharacterType* characters, unsigned length , bool strict) 6030 { 6031 CSSPrimitiveValue::UnitTypes expect = CSSPrimitiveValue::CSS_UNKNOWN; 6032 6033 if (!strict && length >= 3) { 6034 if (characters[0] == '#') { 6035 if (Color::parseHexColor(characters + 1, length - 1, rgb)) 6036 return true; 6037 } else { 6038 if (Color::parseHexColor(characters, length, rgb)) 6039 return true; 6040 } 6041 } 6042 6043 // Try rgba() syntax. 6044 if (mightBeRGBA(characters, length)) { 6045 const CharacterType* current = characters + 5; 6046 const CharacterType* end = characters + length; 6047 int red; 6048 int green; 6049 int blue; 6050 int alpha; 6051 6052 if (!parseColorIntOrPercentage(current, end, ',', expect, red)) 6053 return false; 6054 if (!parseColorIntOrPercentage(current, end, ',', expect, green)) 6055 return false; 6056 if (!parseColorIntOrPercentage(current, end, ',', expect, blue)) 6057 return false; 6058 if (!parseAlphaValue(current, end, ')', alpha)) 6059 return false; 6060 if (current != end) 6061 return false; 6062 rgb = makeRGBA(red, green, blue, alpha); 6063 return true; 6064 } 6065 6066 // Try rgb() syntax. 6067 if (mightBeRGB(characters, length)) { 6068 const CharacterType* current = characters + 4; 6069 const CharacterType* end = characters + length; 6070 int red; 6071 int green; 6072 int blue; 6073 if (!parseColorIntOrPercentage(current, end, ',', expect, red)) 6074 return false; 6075 if (!parseColorIntOrPercentage(current, end, ',', expect, green)) 6076 return false; 6077 if (!parseColorIntOrPercentage(current, end, ')', expect, blue)) 6078 return false; 6079 if (current != end) 6080 return false; 6081 rgb = makeRGB(red, green, blue); 6082 return true; 6083 } 6084 6085 return false; 6086 } 6087 6088 template<typename StringType> 6089 bool CSSParser::fastParseColor(RGBA32& rgb, const StringType& name, bool strict) 6090 { 6091 unsigned length = name.length(); 6092 bool parseResult; 6093 6094 if (!length) 6095 return false; 6096 6097 if (name.is8Bit()) 6098 parseResult = fastParseColorInternal(rgb, name.characters8(), length, strict); 6099 else 6100 parseResult = fastParseColorInternal(rgb, name.characters16(), length, strict); 6101 6102 if (parseResult) 6103 return true; 6104 6105 // Try named colors. 6106 Color tc; 6107 tc.setNamedColor(name); 6108 if (tc.isValid()) { 6109 rgb = tc.rgb(); 6110 return true; 6111 } 6112 return false; 6113 } 6114 6115 inline double CSSParser::parsedDouble(CSSParserValue *v, ReleaseParsedCalcValueCondition releaseCalc) 6116 { 6117 const double result = m_parsedCalculation ? m_parsedCalculation->doubleValue() : v->fValue; 6118 if (releaseCalc == ReleaseParsedCalcValue) 6119 m_parsedCalculation.release(); 6120 return result; 6121 } 6122 6123 bool CSSParser::isCalculation(CSSParserValue* value) 6124 { 6125 return (value->unit == CSSParserValue::Function) 6126 && (equalIgnoringCase(value->function->name, "calc(") 6127 || equalIgnoringCase(value->function->name, "-webkit-calc(") 6128 || equalIgnoringCase(value->function->name, "-webkit-min(") 6129 || equalIgnoringCase(value->function->name, "-webkit-max(")); 6130 } 6131 6132 inline int CSSParser::colorIntFromValue(CSSParserValue* v) 6133 { 6134 bool isPercent; 6135 6136 if (m_parsedCalculation) 6137 isPercent = m_parsedCalculation->category() == CalcPercent; 6138 else 6139 isPercent = v->unit == CSSPrimitiveValue::CSS_PERCENTAGE; 6140 6141 const double value = parsedDouble(v, ReleaseParsedCalcValue); 6142 6143 if (value <= 0.0) 6144 return 0; 6145 6146 if (isPercent) { 6147 if (value >= 100.0) 6148 return 255; 6149 return static_cast<int>(value * 256.0 / 100.0); 6150 } 6151 6152 if (value >= 255.0) 6153 return 255; 6154 6155 return static_cast<int>(value); 6156 } 6157 6158 bool CSSParser::parseColorParameters(CSSParserValue* value, int* colorArray, bool parseAlpha) 6159 { 6160 CSSParserValueList* args = value->function->args.get(); 6161 CSSParserValue* v = args->current(); 6162 Units unitType = FUnknown; 6163 // Get the first value and its type 6164 if (validUnit(v, FInteger, HTMLStandardMode)) 6165 unitType = FInteger; 6166 else if (validUnit(v, FPercent, HTMLStandardMode)) 6167 unitType = FPercent; 6168 else 6169 return false; 6170 6171 colorArray[0] = colorIntFromValue(v); 6172 for (int i = 1; i < 3; i++) { 6173 v = args->next(); 6174 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6175 return false; 6176 v = args->next(); 6177 if (!validUnit(v, unitType, HTMLStandardMode)) 6178 return false; 6179 colorArray[i] = colorIntFromValue(v); 6180 } 6181 if (parseAlpha) { 6182 v = args->next(); 6183 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6184 return false; 6185 v = args->next(); 6186 if (!validUnit(v, FNumber, HTMLStandardMode)) 6187 return false; 6188 const double value = parsedDouble(v, ReleaseParsedCalcValue); 6189 // Convert the floating pointer number of alpha to an integer in the range [0, 256), 6190 // with an equal distribution across all 256 values. 6191 colorArray[3] = static_cast<int>(max(0.0, min(1.0, value)) * nextafter(256.0, 0.0)); 6192 } 6193 return true; 6194 } 6195 6196 // The CSS3 specification defines the format of a HSL color as 6197 // hsl(<number>, <percent>, <percent>) 6198 // and with alpha, the format is 6199 // hsla(<number>, <percent>, <percent>, <number>) 6200 // The first value, HUE, is in an angle with a value between 0 and 360 6201 bool CSSParser::parseHSLParameters(CSSParserValue* value, double* colorArray, bool parseAlpha) 6202 { 6203 CSSParserValueList* args = value->function->args.get(); 6204 CSSParserValue* v = args->current(); 6205 // Get the first value 6206 if (!validUnit(v, FNumber, HTMLStandardMode)) 6207 return false; 6208 // normalize the Hue value and change it to be between 0 and 1.0 6209 colorArray[0] = (((static_cast<int>(parsedDouble(v, ReleaseParsedCalcValue)) % 360) + 360) % 360) / 360.0; 6210 for (int i = 1; i < 3; i++) { 6211 v = args->next(); 6212 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6213 return false; 6214 v = args->next(); 6215 if (!validUnit(v, FPercent, HTMLStandardMode)) 6216 return false; 6217 colorArray[i] = max(0.0, min(100.0, parsedDouble(v, ReleaseParsedCalcValue))) / 100.0; // needs to be value between 0 and 1.0 6218 } 6219 if (parseAlpha) { 6220 v = args->next(); 6221 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6222 return false; 6223 v = args->next(); 6224 if (!validUnit(v, FNumber, HTMLStandardMode)) 6225 return false; 6226 colorArray[3] = max(0.0, min(1.0, parsedDouble(v, ReleaseParsedCalcValue))); 6227 } 6228 return true; 6229 } 6230 6231 PassRefPtr<CSSPrimitiveValue> CSSParser::parseColor(CSSParserValue* value) 6232 { 6233 RGBA32 c = Color::transparent; 6234 if (!parseColorFromValue(value ? value : m_valueList->current(), c)) 6235 return 0; 6236 return cssValuePool().createColorValue(c); 6237 } 6238 6239 bool CSSParser::parseColorFromValue(CSSParserValue* value, RGBA32& c) 6240 { 6241 if (inQuirksMode() && value->unit == CSSPrimitiveValue::CSS_NUMBER 6242 && value->fValue >= 0. && value->fValue < 1000000.) { 6243 String str = String::format("%06d", static_cast<int>((value->fValue+.5))); 6244 // FIXME: This should be strict parsing for SVG as well. 6245 if (!fastParseColor(c, str, !inQuirksMode())) 6246 return false; 6247 } else if (value->unit == CSSPrimitiveValue::CSS_PARSER_HEXCOLOR || 6248 value->unit == CSSPrimitiveValue::CSS_IDENT || 6249 (inQuirksMode() && value->unit == CSSPrimitiveValue::CSS_DIMENSION)) { 6250 if (!fastParseColor(c, value->string, !inQuirksMode() && value->unit == CSSPrimitiveValue::CSS_IDENT)) 6251 return false; 6252 } else if (value->unit == CSSParserValue::Function && 6253 value->function->args != 0 && 6254 value->function->args->size() == 5 /* rgb + two commas */ && 6255 equalIgnoringCase(value->function->name, "rgb(")) { 6256 int colorValues[3]; 6257 if (!parseColorParameters(value, colorValues, false)) 6258 return false; 6259 c = makeRGB(colorValues[0], colorValues[1], colorValues[2]); 6260 } else { 6261 if (value->unit == CSSParserValue::Function && 6262 value->function->args != 0 && 6263 value->function->args->size() == 7 /* rgba + three commas */ && 6264 equalIgnoringCase(value->function->name, "rgba(")) { 6265 int colorValues[4]; 6266 if (!parseColorParameters(value, colorValues, true)) 6267 return false; 6268 c = makeRGBA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); 6269 } else if (value->unit == CSSParserValue::Function && 6270 value->function->args != 0 && 6271 value->function->args->size() == 5 /* hsl + two commas */ && 6272 equalIgnoringCase(value->function->name, "hsl(")) { 6273 double colorValues[3]; 6274 if (!parseHSLParameters(value, colorValues, false)) 6275 return false; 6276 c = makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], 1.0); 6277 } else if (value->unit == CSSParserValue::Function && 6278 value->function->args != 0 && 6279 value->function->args->size() == 7 /* hsla + three commas */ && 6280 equalIgnoringCase(value->function->name, "hsla(")) { 6281 double colorValues[4]; 6282 if (!parseHSLParameters(value, colorValues, true)) 6283 return false; 6284 c = makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); 6285 } else 6286 return false; 6287 } 6288 6289 return true; 6290 } 6291 6292 // This class tracks parsing state for shadow values. If it goes out of scope (e.g., due to an early return) 6293 // without the allowBreak bit being set, then it will clean up all of the objects and destroy them. 6294 struct ShadowParseContext { 6295 ShadowParseContext(CSSPropertyID prop, CSSParser* parser) 6296 : property(prop) 6297 , m_parser(parser) 6298 , allowX(true) 6299 , allowY(false) 6300 , allowBlur(false) 6301 , allowSpread(false) 6302 , allowColor(true) 6303 , allowStyle(prop == CSSPropertyWebkitBoxShadow || prop == CSSPropertyBoxShadow) 6304 , allowBreak(true) 6305 { 6306 } 6307 6308 bool allowLength() { return allowX || allowY || allowBlur || allowSpread; } 6309 6310 void commitValue() 6311 { 6312 // Handle the ,, case gracefully by doing nothing. 6313 if (x || y || blur || spread || color || style) { 6314 if (!values) 6315 values = CSSValueList::createCommaSeparated(); 6316 6317 // Construct the current shadow value and add it to the list. 6318 values->append(CSSShadowValue::create(x.release(), y.release(), blur.release(), spread.release(), style.release(), color.release())); 6319 } 6320 6321 // Now reset for the next shadow value. 6322 x = 0; 6323 y = 0; 6324 blur = 0; 6325 spread = 0; 6326 style = 0; 6327 color = 0; 6328 6329 allowX = true; 6330 allowColor = true; 6331 allowBreak = true; 6332 allowY = false; 6333 allowBlur = false; 6334 allowSpread = false; 6335 allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 6336 } 6337 6338 void commitLength(CSSParserValue* v) 6339 { 6340 RefPtr<CSSPrimitiveValue> val = m_parser->createPrimitiveNumericValue(v); 6341 6342 if (allowX) { 6343 x = val.release(); 6344 allowX = false; 6345 allowY = true; 6346 allowColor = false; 6347 allowStyle = false; 6348 allowBreak = false; 6349 } else if (allowY) { 6350 y = val.release(); 6351 allowY = false; 6352 allowBlur = true; 6353 allowColor = true; 6354 allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 6355 allowBreak = true; 6356 } else if (allowBlur) { 6357 blur = val.release(); 6358 allowBlur = false; 6359 allowSpread = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 6360 } else if (allowSpread) { 6361 spread = val.release(); 6362 allowSpread = false; 6363 } 6364 } 6365 6366 void commitColor(PassRefPtr<CSSPrimitiveValue> val) 6367 { 6368 color = val; 6369 allowColor = false; 6370 if (allowX) { 6371 allowStyle = false; 6372 allowBreak = false; 6373 } else { 6374 allowBlur = false; 6375 allowSpread = false; 6376 allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 6377 } 6378 } 6379 6380 void commitStyle(CSSParserValue* v) 6381 { 6382 style = cssValuePool().createIdentifierValue(v->id); 6383 allowStyle = false; 6384 if (allowX) 6385 allowBreak = false; 6386 else { 6387 allowBlur = false; 6388 allowSpread = false; 6389 allowColor = false; 6390 } 6391 } 6392 6393 CSSPropertyID property; 6394 CSSParser* m_parser; 6395 6396 RefPtr<CSSValueList> values; 6397 RefPtr<CSSPrimitiveValue> x; 6398 RefPtr<CSSPrimitiveValue> y; 6399 RefPtr<CSSPrimitiveValue> blur; 6400 RefPtr<CSSPrimitiveValue> spread; 6401 RefPtr<CSSPrimitiveValue> style; 6402 RefPtr<CSSPrimitiveValue> color; 6403 6404 bool allowX; 6405 bool allowY; 6406 bool allowBlur; 6407 bool allowSpread; 6408 bool allowColor; 6409 bool allowStyle; // inset or not. 6410 bool allowBreak; 6411 }; 6412 6413 PassRefPtr<CSSValueList> CSSParser::parseShadow(CSSParserValueList* valueList, CSSPropertyID propId) 6414 { 6415 ShadowParseContext context(propId, this); 6416 CSSParserValue* val; 6417 while ((val = valueList->current())) { 6418 // Check for a comma break first. 6419 if (val->unit == CSSParserValue::Operator) { 6420 if (val->iValue != ',' || !context.allowBreak) 6421 // Other operators aren't legal or we aren't done with the current shadow 6422 // value. Treat as invalid. 6423 return 0; 6424 // The value is good. Commit it. 6425 context.commitValue(); 6426 } else if (validUnit(val, FLength, HTMLStandardMode)) { 6427 // We required a length and didn't get one. Invalid. 6428 if (!context.allowLength()) 6429 return 0; 6430 6431 // Blur radius must be non-negative. 6432 if (context.allowBlur && !validUnit(val, FLength | FNonNeg, HTMLStandardMode)) 6433 return 0; 6434 6435 // A length is allowed here. Construct the value and add it. 6436 context.commitLength(val); 6437 } else if (val->id == CSSValueInset) { 6438 if (!context.allowStyle) 6439 return 0; 6440 6441 context.commitStyle(val); 6442 } else { 6443 // The only other type of value that's ok is a color value. 6444 RefPtr<CSSPrimitiveValue> parsedColor; 6445 bool isColor = ((val->id >= CSSValueAqua && val->id <= CSSValueWindowtext) || val->id == CSSValueMenu 6446 || (val->id >= CSSValueWebkitFocusRingColor && val->id <= CSSValueWebkitText && inQuirksMode()) 6447 || val->id == CSSValueCurrentcolor); 6448 if (isColor) { 6449 if (!context.allowColor) 6450 return 0; 6451 parsedColor = cssValuePool().createIdentifierValue(val->id); 6452 } 6453 6454 if (!parsedColor) 6455 // It's not built-in. Try to parse it as a color. 6456 parsedColor = parseColor(val); 6457 6458 if (!parsedColor || !context.allowColor) 6459 return 0; // This value is not a color or length and is invalid or 6460 // it is a color, but a color isn't allowed at this point. 6461 6462 context.commitColor(parsedColor.release()); 6463 } 6464 6465 valueList->next(); 6466 } 6467 6468 if (context.allowBreak) { 6469 context.commitValue(); 6470 if (context.values && context.values->length()) 6471 return context.values.release(); 6472 } 6473 6474 return 0; 6475 } 6476 6477 bool CSSParser::parseReflect(CSSPropertyID propId, bool important) 6478 { 6479 // box-reflect: <direction> <offset> <mask> 6480 6481 // Direction comes first. 6482 CSSParserValue* val = m_valueList->current(); 6483 RefPtr<CSSPrimitiveValue> direction; 6484 if (val->unit == CSSPrimitiveValue::CSS_VARIABLE_NAME) 6485 direction = createPrimitiveVariableNameValue(val); 6486 else 6487 switch (val->id) { 6488 case CSSValueAbove: 6489 case CSSValueBelow: 6490 case CSSValueLeft: 6491 case CSSValueRight: 6492 direction = cssValuePool().createIdentifierValue(val->id); 6493 break; 6494 default: 6495 return false; 6496 } 6497 6498 // The offset comes next. 6499 val = m_valueList->next(); 6500 RefPtr<CSSPrimitiveValue> offset; 6501 if (!val) 6502 offset = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX); 6503 else { 6504 if (!validUnit(val, FLength | FPercent)) 6505 return false; 6506 offset = createPrimitiveNumericValue(val); 6507 } 6508 6509 // Now for the mask. 6510 RefPtr<CSSValue> mask; 6511 val = m_valueList->next(); 6512 if (val) { 6513 mask = parseBorderImage(propId); 6514 if (!mask) 6515 return false; 6516 } 6517 6518 RefPtr<CSSReflectValue> reflectValue = CSSReflectValue::create(direction.release(), offset.release(), mask.release()); 6519 addProperty(propId, reflectValue.release(), important); 6520 m_valueList->next(); 6521 return true; 6522 } 6523 6524 bool CSSParser::parseFlex(CSSParserValueList* args, bool important) 6525 { 6526 if (!args || !args->size() || args->size() > 3) 6527 return false; 6528 static const double unsetValue = -1; 6529 double flexGrow = unsetValue; 6530 double flexShrink = unsetValue; 6531 RefPtr<CSSPrimitiveValue> flexBasis; 6532 6533 while (CSSParserValue* arg = args->current()) { 6534 if (validUnit(arg, FNumber | FNonNeg)) { 6535 if (flexGrow == unsetValue) 6536 flexGrow = arg->fValue; 6537 else if (flexShrink == unsetValue) 6538 flexShrink = arg->fValue; 6539 else if (!arg->fValue) { 6540 // flex only allows a basis of 0 (sans units) if flex-grow and flex-shrink values have already been set. 6541 flexBasis = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX); 6542 } else { 6543 // We only allow 3 numbers without units if the last value is 0. E.g., flex:1 1 1 is invalid. 6544 return false; 6545 } 6546 } else if (!flexBasis && (arg->id == CSSValueAuto || validUnit(arg, FLength | FPercent | FNonNeg))) 6547 flexBasis = parseValidPrimitive(arg->id, arg); 6548 else { 6549 // Not a valid arg for flex. 6550 return false; 6551 } 6552 args->next(); 6553 } 6554 6555 if (flexGrow == unsetValue) 6556 flexGrow = 1; 6557 if (flexShrink == unsetValue) 6558 flexShrink = 1; 6559 if (!flexBasis) 6560 flexBasis = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX); 6561 6562 addProperty(CSSPropertyFlexGrow, cssValuePool().createValue(clampToFloat(flexGrow), CSSPrimitiveValue::CSS_NUMBER), important); 6563 addProperty(CSSPropertyFlexShrink, cssValuePool().createValue(clampToFloat(flexShrink), CSSPrimitiveValue::CSS_NUMBER), important); 6564 addProperty(CSSPropertyFlexBasis, flexBasis, important); 6565 return true; 6566 } 6567 6568 bool CSSParser::parseObjectPosition(bool important) 6569 { 6570 RefPtr<CSSValue> xValue; 6571 RefPtr<CSSValue> yValue; 6572 parseFillPosition(m_valueList.get(), xValue, yValue); 6573 if (!xValue || !yValue) 6574 return false; 6575 addProperty( 6576 CSSPropertyObjectPosition, 6577 createPrimitiveValuePair(toCSSPrimitiveValue(xValue.get()), toCSSPrimitiveValue(yValue.get()), Pair::KeepIdenticalValues), 6578 important); 6579 return true; 6580 } 6581 6582 struct BorderImageParseContext { 6583 BorderImageParseContext() 6584 : m_canAdvance(false) 6585 , m_allowCommit(true) 6586 , m_allowImage(true) 6587 , m_allowImageSlice(true) 6588 , m_allowRepeat(true) 6589 , m_allowForwardSlashOperator(false) 6590 , m_requireWidth(false) 6591 , m_requireOutset(false) 6592 {} 6593 6594 bool canAdvance() const { return m_canAdvance; } 6595 void setCanAdvance(bool canAdvance) { m_canAdvance = canAdvance; } 6596 6597 bool allowCommit() const { return m_allowCommit; } 6598 bool allowImage() const { return m_allowImage; } 6599 bool allowImageSlice() const { return m_allowImageSlice; } 6600 bool allowRepeat() const { return m_allowRepeat; } 6601 bool allowForwardSlashOperator() const { return m_allowForwardSlashOperator; } 6602 6603 bool requireWidth() const { return m_requireWidth; } 6604 bool requireOutset() const { return m_requireOutset; } 6605 6606 void commitImage(PassRefPtr<CSSValue> image) 6607 { 6608 m_image = image; 6609 m_canAdvance = true; 6610 m_allowCommit = true; 6611 m_allowImage = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false; 6612 m_allowImageSlice = !m_imageSlice; 6613 m_allowRepeat = !m_repeat; 6614 } 6615 void commitImageSlice(PassRefPtr<CSSBorderImageSliceValue> slice) 6616 { 6617 m_imageSlice = slice; 6618 m_canAdvance = true; 6619 m_allowCommit = m_allowForwardSlashOperator = true; 6620 m_allowImageSlice = m_requireWidth = m_requireOutset = false; 6621 m_allowImage = !m_image; 6622 m_allowRepeat = !m_repeat; 6623 } 6624 void commitForwardSlashOperator() 6625 { 6626 m_canAdvance = true; 6627 m_allowCommit = m_allowImage = m_allowImageSlice = m_allowRepeat = m_allowForwardSlashOperator = false; 6628 if (!m_borderSlice) { 6629 m_requireWidth = true; 6630 m_requireOutset = false; 6631 } else { 6632 m_requireOutset = true; 6633 m_requireWidth = false; 6634 } 6635 } 6636 void commitBorderWidth(PassRefPtr<CSSPrimitiveValue> slice) 6637 { 6638 m_borderSlice = slice; 6639 m_canAdvance = true; 6640 m_allowCommit = m_allowForwardSlashOperator = true; 6641 m_allowImageSlice = m_requireWidth = m_requireOutset = false; 6642 m_allowImage = !m_image; 6643 m_allowRepeat = !m_repeat; 6644 } 6645 void commitBorderOutset(PassRefPtr<CSSPrimitiveValue> outset) 6646 { 6647 m_outset = outset; 6648 m_canAdvance = true; 6649 m_allowCommit = true; 6650 m_allowImageSlice = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false; 6651 m_allowImage = !m_image; 6652 m_allowRepeat = !m_repeat; 6653 } 6654 void commitRepeat(PassRefPtr<CSSValue> repeat) 6655 { 6656 m_repeat = repeat; 6657 m_canAdvance = true; 6658 m_allowCommit = true; 6659 m_allowRepeat = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false; 6660 m_allowImageSlice = !m_imageSlice; 6661 m_allowImage = !m_image; 6662 } 6663 6664 PassRefPtr<CSSValue> commitCSSValue() 6665 { 6666 return createBorderImageValue(m_image, m_imageSlice, m_borderSlice, m_outset, m_repeat); 6667 } 6668 6669 void commitMaskBoxImage(CSSParser* parser, bool important) 6670 { 6671 commitBorderImageProperty(CSSPropertyWebkitMaskBoxImageSource, parser, m_image, important); 6672 commitBorderImageProperty(CSSPropertyWebkitMaskBoxImageSlice, parser, m_imageSlice, important); 6673 commitBorderImageProperty(CSSPropertyWebkitMaskBoxImageWidth, parser, m_borderSlice, important); 6674 commitBorderImageProperty(CSSPropertyWebkitMaskBoxImageOutset, parser, m_outset, important); 6675 commitBorderImageProperty(CSSPropertyWebkitMaskBoxImageRepeat, parser, m_repeat, important); 6676 } 6677 6678 void commitBorderImage(CSSParser* parser, bool important) 6679 { 6680 commitBorderImageProperty(CSSPropertyBorderImageSource, parser, m_image, important); 6681 commitBorderImageProperty(CSSPropertyBorderImageSlice, parser, m_imageSlice, important); 6682 commitBorderImageProperty(CSSPropertyBorderImageWidth, parser, m_borderSlice, important); 6683 commitBorderImageProperty(CSSPropertyBorderImageOutset, parser, m_outset, important); 6684 commitBorderImageProperty(CSSPropertyBorderImageRepeat, parser, m_repeat, important); 6685 } 6686 6687 void commitBorderImageProperty(CSSPropertyID propId, CSSParser* parser, PassRefPtr<CSSValue> value, bool important) 6688 { 6689 if (value) 6690 parser->addProperty(propId, value, important); 6691 else 6692 parser->addProperty(propId, cssValuePool().createImplicitInitialValue(), important, true); 6693 } 6694 6695 bool m_canAdvance; 6696 6697 bool m_allowCommit; 6698 bool m_allowImage; 6699 bool m_allowImageSlice; 6700 bool m_allowRepeat; 6701 bool m_allowForwardSlashOperator; 6702 6703 bool m_requireWidth; 6704 bool m_requireOutset; 6705 6706 RefPtr<CSSValue> m_image; 6707 RefPtr<CSSBorderImageSliceValue> m_imageSlice; 6708 RefPtr<CSSPrimitiveValue> m_borderSlice; 6709 RefPtr<CSSPrimitiveValue> m_outset; 6710 6711 RefPtr<CSSValue> m_repeat; 6712 }; 6713 6714 static bool buildBorderImageParseContext(CSSParser& parser, CSSPropertyID propId, BorderImageParseContext& context) 6715 { 6716 ShorthandScope scope(&parser, propId); 6717 while (CSSParserValue* val = parser.m_valueList->current()) { 6718 context.setCanAdvance(false); 6719 6720 if (!context.canAdvance() && context.allowForwardSlashOperator() && isForwardSlashOperator(val)) 6721 context.commitForwardSlashOperator(); 6722 6723 if (!context.canAdvance() && context.allowImage()) { 6724 if (val->unit == CSSPrimitiveValue::CSS_URI) { 6725 context.commitImage(CSSImageValue::create(parser.completeURL(parser.m_context, val->string))); 6726 } else if (isGeneratedImageValue(val)) { 6727 RefPtr<CSSValue> value; 6728 if (parser.parseGeneratedImage(parser.m_valueList.get(), value)) 6729 context.commitImage(value.release()); 6730 else 6731 return false; 6732 } else if (val->unit == CSSParserValue::Function && equalIgnoringCase(val->function->name, "-webkit-image-set(")) { 6733 RefPtr<CSSValue> value = parser.parseImageSet(parser.m_valueList.get()); 6734 if (value) 6735 context.commitImage(value.release()); 6736 else 6737 return false; 6738 } else if (val->id == CSSValueNone) 6739 context.commitImage(cssValuePool().createIdentifierValue(CSSValueNone)); 6740 } 6741 6742 if (!context.canAdvance() && context.allowImageSlice()) { 6743 RefPtr<CSSBorderImageSliceValue> imageSlice; 6744 if (parser.parseBorderImageSlice(propId, imageSlice)) 6745 context.commitImageSlice(imageSlice.release()); 6746 } 6747 6748 if (!context.canAdvance() && context.allowRepeat()) { 6749 RefPtr<CSSValue> repeat; 6750 if (parser.parseBorderImageRepeat(repeat)) 6751 context.commitRepeat(repeat.release()); 6752 } 6753 6754 if (!context.canAdvance() && context.requireWidth()) { 6755 RefPtr<CSSPrimitiveValue> borderSlice; 6756 if (parser.parseBorderImageWidth(borderSlice)) 6757 context.commitBorderWidth(borderSlice.release()); 6758 } 6759 6760 if (!context.canAdvance() && context.requireOutset()) { 6761 RefPtr<CSSPrimitiveValue> borderOutset; 6762 if (parser.parseBorderImageOutset(borderOutset)) 6763 context.commitBorderOutset(borderOutset.release()); 6764 } 6765 6766 if (!context.canAdvance()) 6767 return false; 6768 6769 parser.m_valueList->next(); 6770 } 6771 6772 return context.allowCommit(); 6773 } 6774 6775 bool CSSParser::parseBorderImageShorthand(CSSPropertyID propId, bool important) 6776 { 6777 BorderImageParseContext context; 6778 if (buildBorderImageParseContext(*this, propId, context)) { 6779 switch (propId) { 6780 case CSSPropertyWebkitMaskBoxImage: 6781 context.commitMaskBoxImage(this, important); 6782 return true; 6783 case CSSPropertyBorderImage: 6784 context.commitBorderImage(this, important); 6785 return true; 6786 default: 6787 ASSERT_NOT_REACHED(); 6788 return false; 6789 } 6790 } 6791 return false; 6792 } 6793 6794 PassRefPtr<CSSValue> CSSParser::parseBorderImage(CSSPropertyID propId) 6795 { 6796 BorderImageParseContext context; 6797 if (buildBorderImageParseContext(*this, propId, context)) { 6798 return context.commitCSSValue(); 6799 } 6800 return 0; 6801 } 6802 6803 static bool isBorderImageRepeatKeyword(int id) 6804 { 6805 return id == CSSValueStretch || id == CSSValueRepeat || id == CSSValueSpace || id == CSSValueRound; 6806 } 6807 6808 bool CSSParser::parseBorderImageRepeat(RefPtr<CSSValue>& result) 6809 { 6810 RefPtr<CSSPrimitiveValue> firstValue; 6811 RefPtr<CSSPrimitiveValue> secondValue; 6812 CSSParserValue* val = m_valueList->current(); 6813 if (!val) 6814 return false; 6815 if (isBorderImageRepeatKeyword(val->id)) 6816 firstValue = cssValuePool().createIdentifierValue(val->id); 6817 else 6818 return false; 6819 6820 val = m_valueList->next(); 6821 if (val) { 6822 if (isBorderImageRepeatKeyword(val->id)) 6823 secondValue = cssValuePool().createIdentifierValue(val->id); 6824 else if (!inShorthand()) { 6825 // If we're not parsing a shorthand then we are invalid. 6826 return false; 6827 } else { 6828 // We need to rewind the value list, so that when its advanced we'll 6829 // end up back at this value. 6830 m_valueList->previous(); 6831 secondValue = firstValue; 6832 } 6833 } else 6834 secondValue = firstValue; 6835 6836 result = createPrimitiveValuePair(firstValue, secondValue); 6837 return true; 6838 } 6839 6840 class BorderImageSliceParseContext { 6841 public: 6842 BorderImageSliceParseContext(CSSParser* parser) 6843 : m_parser(parser) 6844 , m_allowNumber(true) 6845 , m_allowFill(true) 6846 , m_allowFinalCommit(false) 6847 , m_fill(false) 6848 { } 6849 6850 bool allowNumber() const { return m_allowNumber; } 6851 bool allowFill() const { return m_allowFill; } 6852 bool allowFinalCommit() const { return m_allowFinalCommit; } 6853 CSSPrimitiveValue* top() const { return m_top.get(); } 6854 6855 void commitNumber(CSSParserValue* v) 6856 { 6857 RefPtr<CSSPrimitiveValue> val = m_parser->createPrimitiveNumericValue(v); 6858 if (!m_top) 6859 m_top = val; 6860 else if (!m_right) 6861 m_right = val; 6862 else if (!m_bottom) 6863 m_bottom = val; 6864 else { 6865 ASSERT(!m_left); 6866 m_left = val; 6867 } 6868 6869 m_allowNumber = !m_left; 6870 m_allowFinalCommit = true; 6871 } 6872 6873 void commitFill() { m_fill = true; m_allowFill = false; m_allowNumber = !m_top; } 6874 6875 PassRefPtr<CSSBorderImageSliceValue> commitBorderImageSlice() 6876 { 6877 // We need to clone and repeat values for any omissions. 6878 ASSERT(m_top); 6879 if (!m_right) { 6880 m_right = m_top; 6881 m_bottom = m_top; 6882 m_left = m_top; 6883 } 6884 if (!m_bottom) { 6885 m_bottom = m_top; 6886 m_left = m_right; 6887 } 6888 if (!m_left) 6889 m_left = m_right; 6890 6891 // Now build a rect value to hold all four of our primitive values. 6892 RefPtr<Quad> quad = Quad::create(); 6893 quad->setTop(m_top); 6894 quad->setRight(m_right); 6895 quad->setBottom(m_bottom); 6896 quad->setLeft(m_left); 6897 6898 // Make our new border image value now. 6899 return CSSBorderImageSliceValue::create(cssValuePool().createValue(quad.release()), m_fill); 6900 } 6901 6902 private: 6903 CSSParser* m_parser; 6904 6905 bool m_allowNumber; 6906 bool m_allowFill; 6907 bool m_allowFinalCommit; 6908 6909 RefPtr<CSSPrimitiveValue> m_top; 6910 RefPtr<CSSPrimitiveValue> m_right; 6911 RefPtr<CSSPrimitiveValue> m_bottom; 6912 RefPtr<CSSPrimitiveValue> m_left; 6913 6914 bool m_fill; 6915 }; 6916 6917 bool CSSParser::parseBorderImageSlice(CSSPropertyID propId, RefPtr<CSSBorderImageSliceValue>& result) 6918 { 6919 BorderImageSliceParseContext context(this); 6920 CSSParserValue* val; 6921 while ((val = m_valueList->current())) { 6922 // FIXME calc() http://webkit.org/b/16662 : calc is parsed but values are not created yet. 6923 if (context.allowNumber() && !isCalculation(val) && validUnit(val, FInteger | FNonNeg | FPercent, HTMLStandardMode)) { 6924 context.commitNumber(val); 6925 } else if (context.allowFill() && val->id == CSSValueFill) 6926 context.commitFill(); 6927 else if (!inShorthand()) { 6928 // If we're not parsing a shorthand then we are invalid. 6929 return false; 6930 } else { 6931 if (context.allowFinalCommit()) { 6932 // We're going to successfully parse, but we don't want to consume this token. 6933 m_valueList->previous(); 6934 } 6935 break; 6936 } 6937 m_valueList->next(); 6938 } 6939 6940 if (context.allowFinalCommit()) { 6941 // FIXME: For backwards compatibility, -webkit-border-image, -webkit-mask-box-image and -webkit-box-reflect have to do a fill by default. 6942 // FIXME: What do we do with -webkit-box-reflect and -webkit-mask-box-image? Probably just have to leave them filling... 6943 if (propId == CSSPropertyWebkitBorderImage || propId == CSSPropertyWebkitMaskBoxImage || propId == CSSPropertyWebkitBoxReflect) 6944 context.commitFill(); 6945 6946 // Need to fully commit as a single value. 6947 result = context.commitBorderImageSlice(); 6948 return true; 6949 } 6950 6951 return false; 6952 } 6953 6954 class BorderImageQuadParseContext { 6955 public: 6956 BorderImageQuadParseContext(CSSParser* parser) 6957 : m_parser(parser) 6958 , m_allowNumber(true) 6959 , m_allowFinalCommit(false) 6960 { } 6961 6962 bool allowNumber() const { return m_allowNumber; } 6963 bool allowFinalCommit() const { return m_allowFinalCommit; } 6964 CSSPrimitiveValue* top() const { return m_top.get(); } 6965 6966 void commitNumber(CSSParserValue* v) 6967 { 6968 RefPtr<CSSPrimitiveValue> val; 6969 if (v->id == CSSValueAuto) 6970 val = cssValuePool().createIdentifierValue(v->id); 6971 else 6972 val = m_parser->createPrimitiveNumericValue(v); 6973 6974 if (!m_top) 6975 m_top = val; 6976 else if (!m_right) 6977 m_right = val; 6978 else if (!m_bottom) 6979 m_bottom = val; 6980 else { 6981 ASSERT(!m_left); 6982 m_left = val; 6983 } 6984 6985 m_allowNumber = !m_left; 6986 m_allowFinalCommit = true; 6987 } 6988 6989 void setAllowFinalCommit() { m_allowFinalCommit = true; } 6990 void setTop(PassRefPtr<CSSPrimitiveValue> val) { m_top = val; } 6991 6992 PassRefPtr<CSSPrimitiveValue> commitBorderImageQuad() 6993 { 6994 // We need to clone and repeat values for any omissions. 6995 ASSERT(m_top); 6996 if (!m_right) { 6997 m_right = m_top; 6998 m_bottom = m_top; 6999 m_left = m_top; 7000 } 7001 if (!m_bottom) { 7002 m_bottom = m_top; 7003 m_left = m_right; 7004 } 7005 if (!m_left) 7006 m_left = m_right; 7007 7008 // Now build a quad value to hold all four of our primitive values. 7009 RefPtr<Quad> quad = Quad::create(); 7010 quad->setTop(m_top); 7011 quad->setRight(m_right); 7012 quad->setBottom(m_bottom); 7013 quad->setLeft(m_left); 7014 7015 // Make our new value now. 7016 return cssValuePool().createValue(quad.release()); 7017 } 7018 7019 private: 7020 CSSParser* m_parser; 7021 7022 bool m_allowNumber; 7023 bool m_allowFinalCommit; 7024 7025 RefPtr<CSSPrimitiveValue> m_top; 7026 RefPtr<CSSPrimitiveValue> m_right; 7027 RefPtr<CSSPrimitiveValue> m_bottom; 7028 RefPtr<CSSPrimitiveValue> m_left; 7029 }; 7030 7031 bool CSSParser::parseBorderImageQuad(Units validUnits, RefPtr<CSSPrimitiveValue>& result) 7032 { 7033 BorderImageQuadParseContext context(this); 7034 CSSParserValue* val; 7035 while ((val = m_valueList->current())) { 7036 if (context.allowNumber() && (validUnit(val, validUnits, HTMLStandardMode) || val->id == CSSValueAuto)) { 7037 context.commitNumber(val); 7038 } else if (!inShorthand()) { 7039 // If we're not parsing a shorthand then we are invalid. 7040 return false; 7041 } else { 7042 if (context.allowFinalCommit()) 7043 m_valueList->previous(); // The shorthand loop will advance back to this point. 7044 break; 7045 } 7046 m_valueList->next(); 7047 } 7048 7049 if (context.allowFinalCommit()) { 7050 // Need to fully commit as a single value. 7051 result = context.commitBorderImageQuad(); 7052 return true; 7053 } 7054 return false; 7055 } 7056 7057 bool CSSParser::parseBorderImageWidth(RefPtr<CSSPrimitiveValue>& result) 7058 { 7059 return parseBorderImageQuad(FLength | FNumber | FNonNeg | FPercent, result); 7060 } 7061 7062 bool CSSParser::parseBorderImageOutset(RefPtr<CSSPrimitiveValue>& result) 7063 { 7064 return parseBorderImageQuad(FLength | FNumber | FNonNeg, result); 7065 } 7066 7067 static void completeBorderRadii(RefPtr<CSSPrimitiveValue> radii[4]) 7068 { 7069 if (radii[3]) 7070 return; 7071 if (!radii[2]) { 7072 if (!radii[1]) 7073 radii[1] = radii[0]; 7074 radii[2] = radii[0]; 7075 } 7076 radii[3] = radii[1]; 7077 } 7078 7079 bool CSSParser::parseBorderRadius(CSSPropertyID propId, bool important) 7080 { 7081 unsigned num = m_valueList->size(); 7082 if (num > 9) 7083 return false; 7084 7085 ShorthandScope scope(this, propId); 7086 RefPtr<CSSPrimitiveValue> radii[2][4]; 7087 7088 unsigned indexAfterSlash = 0; 7089 for (unsigned i = 0; i < num; ++i) { 7090 CSSParserValue* value = m_valueList->valueAt(i); 7091 if (value->unit == CSSParserValue::Operator) { 7092 if (value->iValue != '/') 7093 return false; 7094 7095 if (!i || indexAfterSlash || i + 1 == num || num > i + 5) 7096 return false; 7097 7098 indexAfterSlash = i + 1; 7099 completeBorderRadii(radii[0]); 7100 continue; 7101 } 7102 7103 if (i - indexAfterSlash >= 4) 7104 return false; 7105 7106 if (!validUnit(value, FLength | FPercent | FNonNeg)) 7107 return false; 7108 7109 RefPtr<CSSPrimitiveValue> radius = createPrimitiveNumericValue(value); 7110 7111 if (!indexAfterSlash) { 7112 radii[0][i] = radius; 7113 7114 // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2; 7115 if (num == 2 && propId == CSSPropertyWebkitBorderRadius) { 7116 indexAfterSlash = 1; 7117 completeBorderRadii(radii[0]); 7118 } 7119 } else 7120 radii[1][i - indexAfterSlash] = radius.release(); 7121 } 7122 7123 if (!indexAfterSlash) { 7124 completeBorderRadii(radii[0]); 7125 for (unsigned i = 0; i < 4; ++i) 7126 radii[1][i] = radii[0][i]; 7127 } else 7128 completeBorderRadii(radii[1]); 7129 7130 ImplicitScope implicitScope(this, PropertyImplicit); 7131 addProperty(CSSPropertyBorderTopLeftRadius, createPrimitiveValuePair(radii[0][0].release(), radii[1][0].release()), important); 7132 addProperty(CSSPropertyBorderTopRightRadius, createPrimitiveValuePair(radii[0][1].release(), radii[1][1].release()), important); 7133 addProperty(CSSPropertyBorderBottomRightRadius, createPrimitiveValuePair(radii[0][2].release(), radii[1][2].release()), important); 7134 addProperty(CSSPropertyBorderBottomLeftRadius, createPrimitiveValuePair(radii[0][3].release(), radii[1][3].release()), important); 7135 return true; 7136 } 7137 7138 bool CSSParser::parseAspectRatio(bool important) 7139 { 7140 unsigned num = m_valueList->size(); 7141 if (num == 1 && m_valueList->valueAt(0)->id == CSSValueNone) { 7142 addProperty(CSSPropertyWebkitAspectRatio, cssValuePool().createIdentifierValue(CSSValueNone), important); 7143 return true; 7144 } 7145 7146 if (num != 3) 7147 return false; 7148 7149 CSSParserValue* lvalue = m_valueList->valueAt(0); 7150 CSSParserValue* op = m_valueList->valueAt(1); 7151 CSSParserValue* rvalue = m_valueList->valueAt(2); 7152 7153 if (!isForwardSlashOperator(op)) 7154 return false; 7155 7156 if (!validUnit(lvalue, FNumber | FNonNeg) || !validUnit(rvalue, FNumber | FNonNeg)) 7157 return false; 7158 7159 if (!lvalue->fValue || !rvalue->fValue) 7160 return false; 7161 7162 addProperty(CSSPropertyWebkitAspectRatio, CSSAspectRatioValue::create(narrowPrecisionToFloat(lvalue->fValue), narrowPrecisionToFloat(rvalue->fValue)), important); 7163 7164 return true; 7165 } 7166 7167 bool CSSParser::parseCounter(CSSPropertyID propId, int defaultValue, bool important) 7168 { 7169 enum { ID, VAL } state = ID; 7170 7171 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 7172 RefPtr<CSSPrimitiveValue> counterName; 7173 7174 while (true) { 7175 CSSParserValue* val = m_valueList->current(); 7176 switch (state) { 7177 case ID: 7178 if (val && val->unit == CSSPrimitiveValue::CSS_IDENT) { 7179 counterName = createPrimitiveStringValue(val); 7180 state = VAL; 7181 m_valueList->next(); 7182 continue; 7183 } 7184 break; 7185 case VAL: { 7186 int i = defaultValue; 7187 if (val && val->unit == CSSPrimitiveValue::CSS_NUMBER) { 7188 i = clampToInteger(val->fValue); 7189 m_valueList->next(); 7190 } 7191 7192 list->append(createPrimitiveValuePair(counterName.release(), 7193 cssValuePool().createValue(i, CSSPrimitiveValue::CSS_NUMBER))); 7194 state = ID; 7195 continue; 7196 } 7197 } 7198 break; 7199 } 7200 7201 if (list->length() > 0) { 7202 addProperty(propId, list.release(), important); 7203 return true; 7204 } 7205 7206 return false; 7207 } 7208 7209 // This should go away once we drop support for -webkit-gradient 7210 static PassRefPtr<CSSPrimitiveValue> parseDeprecatedGradientPoint(CSSParserValue* a, bool horizontal) 7211 { 7212 RefPtr<CSSPrimitiveValue> result; 7213 if (a->unit == CSSPrimitiveValue::CSS_IDENT) { 7214 if ((equalIgnoringCase(a, "left") && horizontal) 7215 || (equalIgnoringCase(a, "top") && !horizontal)) 7216 result = cssValuePool().createValue(0., CSSPrimitiveValue::CSS_PERCENTAGE); 7217 else if ((equalIgnoringCase(a, "right") && horizontal) 7218 || (equalIgnoringCase(a, "bottom") && !horizontal)) 7219 result = cssValuePool().createValue(100., CSSPrimitiveValue::CSS_PERCENTAGE); 7220 else if (equalIgnoringCase(a, "center")) 7221 result = cssValuePool().createValue(50., CSSPrimitiveValue::CSS_PERCENTAGE); 7222 } else if (a->unit == CSSPrimitiveValue::CSS_NUMBER || a->unit == CSSPrimitiveValue::CSS_PERCENTAGE) 7223 result = cssValuePool().createValue(a->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(a->unit)); 7224 return result; 7225 } 7226 7227 static bool parseDeprecatedGradientColorStop(CSSParser* p, CSSParserValue* a, CSSGradientColorStop& stop) 7228 { 7229 if (a->unit != CSSParserValue::Function) 7230 return false; 7231 7232 if (!equalIgnoringCase(a->function->name, "from(") && 7233 !equalIgnoringCase(a->function->name, "to(") && 7234 !equalIgnoringCase(a->function->name, "color-stop(")) 7235 return false; 7236 7237 CSSParserValueList* args = a->function->args.get(); 7238 if (!args) 7239 return false; 7240 7241 if (equalIgnoringCase(a->function->name, "from(") 7242 || equalIgnoringCase(a->function->name, "to(")) { 7243 // The "from" and "to" stops expect 1 argument. 7244 if (args->size() != 1) 7245 return false; 7246 7247 if (equalIgnoringCase(a->function->name, "from(")) 7248 stop.m_position = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER); 7249 else 7250 stop.m_position = cssValuePool().createValue(1, CSSPrimitiveValue::CSS_NUMBER); 7251 7252 CSSValueID id = args->current()->id; 7253 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu) 7254 stop.m_color = cssValuePool().createIdentifierValue(id); 7255 else 7256 stop.m_color = p->parseColor(args->current()); 7257 if (!stop.m_color) 7258 return false; 7259 } 7260 7261 // The "color-stop" function expects 3 arguments. 7262 if (equalIgnoringCase(a->function->name, "color-stop(")) { 7263 if (args->size() != 3) 7264 return false; 7265 7266 CSSParserValue* stopArg = args->current(); 7267 if (stopArg->unit == CSSPrimitiveValue::CSS_PERCENTAGE) 7268 stop.m_position = cssValuePool().createValue(stopArg->fValue / 100, CSSPrimitiveValue::CSS_NUMBER); 7269 else if (stopArg->unit == CSSPrimitiveValue::CSS_NUMBER) 7270 stop.m_position = cssValuePool().createValue(stopArg->fValue, CSSPrimitiveValue::CSS_NUMBER); 7271 else 7272 return false; 7273 7274 stopArg = args->next(); 7275 if (stopArg->unit != CSSParserValue::Operator || stopArg->iValue != ',') 7276 return false; 7277 7278 stopArg = args->next(); 7279 CSSValueID id = stopArg->id; 7280 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu) 7281 stop.m_color = cssValuePool().createIdentifierValue(id); 7282 else 7283 stop.m_color = p->parseColor(stopArg); 7284 if (!stop.m_color) 7285 return false; 7286 } 7287 7288 return true; 7289 } 7290 7291 bool CSSParser::parseDeprecatedGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient) 7292 { 7293 // Walk the arguments. 7294 CSSParserValueList* args = valueList->current()->function->args.get(); 7295 if (!args || args->size() == 0) 7296 return false; 7297 7298 // The first argument is the gradient type. It is an identifier. 7299 CSSGradientType gradientType; 7300 CSSParserValue* a = args->current(); 7301 if (!a || a->unit != CSSPrimitiveValue::CSS_IDENT) 7302 return false; 7303 if (equalIgnoringCase(a, "linear")) 7304 gradientType = CSSDeprecatedLinearGradient; 7305 else if (equalIgnoringCase(a, "radial")) 7306 gradientType = CSSDeprecatedRadialGradient; 7307 else 7308 return false; 7309 7310 RefPtr<CSSGradientValue> result; 7311 switch (gradientType) { 7312 case CSSDeprecatedLinearGradient: 7313 result = CSSLinearGradientValue::create(NonRepeating, gradientType); 7314 break; 7315 case CSSDeprecatedRadialGradient: 7316 result = CSSRadialGradientValue::create(NonRepeating, gradientType); 7317 break; 7318 default: 7319 // The rest of the gradient types shouldn't appear here. 7320 ASSERT_NOT_REACHED(); 7321 } 7322 7323 // Comma. 7324 a = args->next(); 7325 if (!isComma(a)) 7326 return false; 7327 7328 // Next comes the starting point for the gradient as an x y pair. There is no 7329 // comma between the x and the y values. 7330 // First X. It can be left, right, number or percent. 7331 a = args->next(); 7332 if (!a) 7333 return false; 7334 RefPtr<CSSPrimitiveValue> point = parseDeprecatedGradientPoint(a, true); 7335 if (!point) 7336 return false; 7337 result->setFirstX(point.release()); 7338 7339 // First Y. It can be top, bottom, number or percent. 7340 a = args->next(); 7341 if (!a) 7342 return false; 7343 point = parseDeprecatedGradientPoint(a, false); 7344 if (!point) 7345 return false; 7346 result->setFirstY(point.release()); 7347 7348 // Comma after the first point. 7349 a = args->next(); 7350 if (!isComma(a)) 7351 return false; 7352 7353 // For radial gradients only, we now expect a numeric radius. 7354 if (gradientType == CSSDeprecatedRadialGradient) { 7355 a = args->next(); 7356 if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER) 7357 return false; 7358 toCSSRadialGradientValue(result.get())->setFirstRadius(createPrimitiveNumericValue(a)); 7359 7360 // Comma after the first radius. 7361 a = args->next(); 7362 if (!isComma(a)) 7363 return false; 7364 } 7365 7366 // Next is the ending point for the gradient as an x, y pair. 7367 // Second X. It can be left, right, number or percent. 7368 a = args->next(); 7369 if (!a) 7370 return false; 7371 point = parseDeprecatedGradientPoint(a, true); 7372 if (!point) 7373 return false; 7374 result->setSecondX(point.release()); 7375 7376 // Second Y. It can be top, bottom, number or percent. 7377 a = args->next(); 7378 if (!a) 7379 return false; 7380 point = parseDeprecatedGradientPoint(a, false); 7381 if (!point) 7382 return false; 7383 result->setSecondY(point.release()); 7384 7385 // For radial gradients only, we now expect the second radius. 7386 if (gradientType == CSSDeprecatedRadialGradient) { 7387 // Comma after the second point. 7388 a = args->next(); 7389 if (!isComma(a)) 7390 return false; 7391 7392 a = args->next(); 7393 if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER) 7394 return false; 7395 toCSSRadialGradientValue(result.get())->setSecondRadius(createPrimitiveNumericValue(a)); 7396 } 7397 7398 // We now will accept any number of stops (0 or more). 7399 a = args->next(); 7400 while (a) { 7401 // Look for the comma before the next stop. 7402 if (!isComma(a)) 7403 return false; 7404 7405 // Now examine the stop itself. 7406 a = args->next(); 7407 if (!a) 7408 return false; 7409 7410 // The function name needs to be one of "from", "to", or "color-stop." 7411 CSSGradientColorStop stop; 7412 if (!parseDeprecatedGradientColorStop(this, a, stop)) 7413 return false; 7414 result->addStop(stop); 7415 7416 // Advance 7417 a = args->next(); 7418 } 7419 7420 gradient = result.release(); 7421 return true; 7422 } 7423 7424 static PassRefPtr<CSSPrimitiveValue> valueFromSideKeyword(CSSParserValue* a, bool& isHorizontal) 7425 { 7426 if (a->unit != CSSPrimitiveValue::CSS_IDENT) 7427 return 0; 7428 7429 switch (a->id) { 7430 case CSSValueLeft: 7431 case CSSValueRight: 7432 isHorizontal = true; 7433 break; 7434 case CSSValueTop: 7435 case CSSValueBottom: 7436 isHorizontal = false; 7437 break; 7438 default: 7439 return 0; 7440 } 7441 return cssValuePool().createIdentifierValue(a->id); 7442 } 7443 7444 static PassRefPtr<CSSPrimitiveValue> parseGradientColorOrKeyword(CSSParser* p, CSSParserValue* value) 7445 { 7446 CSSValueID id = value->id; 7447 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || id == CSSValueCurrentcolor) 7448 return cssValuePool().createIdentifierValue(id); 7449 7450 return p->parseColor(value); 7451 } 7452 7453 bool CSSParser::parseDeprecatedLinearGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 7454 { 7455 RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, CSSPrefixedLinearGradient); 7456 7457 // Walk the arguments. 7458 CSSParserValueList* args = valueList->current()->function->args.get(); 7459 if (!args || !args->size()) 7460 return false; 7461 7462 CSSParserValue* a = args->current(); 7463 if (!a) 7464 return false; 7465 7466 bool expectComma = false; 7467 // Look for angle. 7468 if (validUnit(a, FAngle, HTMLStandardMode)) { 7469 result->setAngle(createPrimitiveNumericValue(a)); 7470 7471 args->next(); 7472 expectComma = true; 7473 } else { 7474 // Look one or two optional keywords that indicate a side or corner. 7475 RefPtr<CSSPrimitiveValue> startX, startY; 7476 7477 RefPtr<CSSPrimitiveValue> location; 7478 bool isHorizontal = false; 7479 if ((location = valueFromSideKeyword(a, isHorizontal))) { 7480 if (isHorizontal) 7481 startX = location; 7482 else 7483 startY = location; 7484 7485 if ((a = args->next())) { 7486 if ((location = valueFromSideKeyword(a, isHorizontal))) { 7487 if (isHorizontal) { 7488 if (startX) 7489 return false; 7490 startX = location; 7491 } else { 7492 if (startY) 7493 return false; 7494 startY = location; 7495 } 7496 7497 args->next(); 7498 } 7499 } 7500 7501 expectComma = true; 7502 } 7503 7504 if (!startX && !startY) 7505 startY = cssValuePool().createIdentifierValue(CSSValueTop); 7506 7507 result->setFirstX(startX.release()); 7508 result->setFirstY(startY.release()); 7509 } 7510 7511 if (!parseGradientColorStops(args, result.get(), expectComma)) 7512 return false; 7513 7514 if (!result->stopCount()) 7515 return false; 7516 7517 gradient = result.release(); 7518 return true; 7519 } 7520 7521 bool CSSParser::parseDeprecatedRadialGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 7522 { 7523 RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSPrefixedRadialGradient); 7524 7525 // Walk the arguments. 7526 CSSParserValueList* args = valueList->current()->function->args.get(); 7527 if (!args || !args->size()) 7528 return false; 7529 7530 CSSParserValue* a = args->current(); 7531 if (!a) 7532 return false; 7533 7534 bool expectComma = false; 7535 7536 // Optional background-position 7537 RefPtr<CSSValue> centerX; 7538 RefPtr<CSSValue> centerY; 7539 // parse2ValuesFillPosition advances the args next pointer. 7540 parse2ValuesFillPosition(args, centerX, centerY); 7541 a = args->current(); 7542 if (!a) 7543 return false; 7544 7545 if (centerX || centerY) { 7546 // Comma 7547 if (!isComma(a)) 7548 return false; 7549 7550 a = args->next(); 7551 if (!a) 7552 return false; 7553 } 7554 7555 result->setFirstX(toCSSPrimitiveValue(centerX.get())); 7556 result->setSecondX(toCSSPrimitiveValue(centerX.get())); 7557 // CSS3 radial gradients always share the same start and end point. 7558 result->setFirstY(toCSSPrimitiveValue(centerY.get())); 7559 result->setSecondY(toCSSPrimitiveValue(centerY.get())); 7560 7561 RefPtr<CSSPrimitiveValue> shapeValue; 7562 RefPtr<CSSPrimitiveValue> sizeValue; 7563 7564 // Optional shape and/or size in any order. 7565 for (int i = 0; i < 2; ++i) { 7566 if (a->unit != CSSPrimitiveValue::CSS_IDENT) 7567 break; 7568 7569 bool foundValue = false; 7570 switch (a->id) { 7571 case CSSValueCircle: 7572 case CSSValueEllipse: 7573 shapeValue = cssValuePool().createIdentifierValue(a->id); 7574 foundValue = true; 7575 break; 7576 case CSSValueClosestSide: 7577 case CSSValueClosestCorner: 7578 case CSSValueFarthestSide: 7579 case CSSValueFarthestCorner: 7580 case CSSValueContain: 7581 case CSSValueCover: 7582 sizeValue = cssValuePool().createIdentifierValue(a->id); 7583 foundValue = true; 7584 break; 7585 default: 7586 break; 7587 } 7588 7589 if (foundValue) { 7590 a = args->next(); 7591 if (!a) 7592 return false; 7593 7594 expectComma = true; 7595 } 7596 } 7597 7598 result->setShape(shapeValue); 7599 result->setSizingBehavior(sizeValue); 7600 7601 // Or, two lengths or percentages 7602 RefPtr<CSSPrimitiveValue> horizontalSize; 7603 RefPtr<CSSPrimitiveValue> verticalSize; 7604 7605 if (!shapeValue && !sizeValue) { 7606 if (validUnit(a, FLength | FPercent)) { 7607 horizontalSize = createPrimitiveNumericValue(a); 7608 a = args->next(); 7609 if (!a) 7610 return false; 7611 7612 expectComma = true; 7613 } 7614 7615 if (validUnit(a, FLength | FPercent)) { 7616 verticalSize = createPrimitiveNumericValue(a); 7617 7618 a = args->next(); 7619 if (!a) 7620 return false; 7621 expectComma = true; 7622 } 7623 } 7624 7625 // Must have neither or both. 7626 if (!horizontalSize != !verticalSize) 7627 return false; 7628 7629 result->setEndHorizontalSize(horizontalSize); 7630 result->setEndVerticalSize(verticalSize); 7631 7632 if (!parseGradientColorStops(args, result.get(), expectComma)) 7633 return false; 7634 7635 gradient = result.release(); 7636 return true; 7637 } 7638 7639 bool CSSParser::parseLinearGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 7640 { 7641 RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, CSSLinearGradient); 7642 7643 CSSParserValueList* args = valueList->current()->function->args.get(); 7644 if (!args || !args->size()) 7645 return false; 7646 7647 CSSParserValue* a = args->current(); 7648 if (!a) 7649 return false; 7650 7651 bool expectComma = false; 7652 // Look for angle. 7653 if (validUnit(a, FAngle, HTMLStandardMode)) { 7654 result->setAngle(createPrimitiveNumericValue(a)); 7655 7656 args->next(); 7657 expectComma = true; 7658 } else if (a->unit == CSSPrimitiveValue::CSS_IDENT && equalIgnoringCase(a, "to")) { 7659 // to [ [left | right] || [top | bottom] ] 7660 a = args->next(); 7661 if (!a) 7662 return false; 7663 7664 RefPtr<CSSPrimitiveValue> endX, endY; 7665 RefPtr<CSSPrimitiveValue> location; 7666 bool isHorizontal = false; 7667 7668 location = valueFromSideKeyword(a, isHorizontal); 7669 if (!location) 7670 return false; 7671 7672 if (isHorizontal) 7673 endX = location; 7674 else 7675 endY = location; 7676 7677 a = args->next(); 7678 if (!a) 7679 return false; 7680 7681 location = valueFromSideKeyword(a, isHorizontal); 7682 if (location) { 7683 if (isHorizontal) { 7684 if (endX) 7685 return false; 7686 endX = location; 7687 } else { 7688 if (endY) 7689 return false; 7690 endY = location; 7691 } 7692 7693 args->next(); 7694 } 7695 7696 expectComma = true; 7697 result->setFirstX(endX.release()); 7698 result->setFirstY(endY.release()); 7699 } 7700 7701 if (!parseGradientColorStops(args, result.get(), expectComma)) 7702 return false; 7703 7704 if (!result->stopCount()) 7705 return false; 7706 7707 gradient = result.release(); 7708 return true; 7709 } 7710 7711 bool CSSParser::parseRadialGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 7712 { 7713 RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient); 7714 7715 CSSParserValueList* args = valueList->current()->function->args.get(); 7716 if (!args || !args->size()) 7717 return false; 7718 7719 CSSParserValue* a = args->current(); 7720 if (!a) 7721 return false; 7722 7723 bool expectComma = false; 7724 7725 RefPtr<CSSPrimitiveValue> shapeValue; 7726 RefPtr<CSSPrimitiveValue> sizeValue; 7727 RefPtr<CSSPrimitiveValue> horizontalSize; 7728 RefPtr<CSSPrimitiveValue> verticalSize; 7729 7730 // First part of grammar, the size/shape clause: 7731 // [ circle || <length> ] | 7732 // [ ellipse || [ <length> | <percentage> ]{2} ] | 7733 // [ [ circle | ellipse] || <size-keyword> ] 7734 for (int i = 0; i < 3; ++i) { 7735 if (a->unit == CSSPrimitiveValue::CSS_IDENT) { 7736 bool badIdent = false; 7737 switch (a->id) { 7738 case CSSValueCircle: 7739 case CSSValueEllipse: 7740 if (shapeValue) 7741 return false; 7742 shapeValue = cssValuePool().createIdentifierValue(a->id); 7743 break; 7744 case CSSValueClosestSide: 7745 case CSSValueClosestCorner: 7746 case CSSValueFarthestSide: 7747 case CSSValueFarthestCorner: 7748 if (sizeValue || horizontalSize) 7749 return false; 7750 sizeValue = cssValuePool().createIdentifierValue(a->id); 7751 break; 7752 default: 7753 badIdent = true; 7754 } 7755 7756 if (badIdent) 7757 break; 7758 7759 a = args->next(); 7760 if (!a) 7761 return false; 7762 } else if (validUnit(a, FLength | FPercent)) { 7763 7764 if (sizeValue || horizontalSize) 7765 return false; 7766 horizontalSize = createPrimitiveNumericValue(a); 7767 7768 a = args->next(); 7769 if (!a) 7770 return false; 7771 7772 if (validUnit(a, FLength | FPercent)) { 7773 verticalSize = createPrimitiveNumericValue(a); 7774 ++i; 7775 a = args->next(); 7776 if (!a) 7777 return false; 7778 } 7779 } else 7780 break; 7781 } 7782 7783 // You can specify size as a keyword or a length/percentage, not both. 7784 if (sizeValue && horizontalSize) 7785 return false; 7786 // Circles must have 0 or 1 lengths. 7787 if (shapeValue && shapeValue->getValueID() == CSSValueCircle && verticalSize) 7788 return false; 7789 // Ellipses must have 0 or 2 length/percentages. 7790 if (shapeValue && shapeValue->getValueID() == CSSValueEllipse && horizontalSize && !verticalSize) 7791 return false; 7792 // If there's only one size, it must be a length. 7793 if (!verticalSize && horizontalSize && horizontalSize->isPercentage()) 7794 return false; 7795 7796 result->setShape(shapeValue); 7797 result->setSizingBehavior(sizeValue); 7798 result->setEndHorizontalSize(horizontalSize); 7799 result->setEndVerticalSize(verticalSize); 7800 7801 // Second part of grammar, the center-position clause: 7802 // at <position> 7803 RefPtr<CSSValue> centerX; 7804 RefPtr<CSSValue> centerY; 7805 if (a->unit == CSSPrimitiveValue::CSS_IDENT && equalIgnoringCase(a, "at")) { 7806 a = args->next(); 7807 if (!a) 7808 return false; 7809 7810 parseFillPosition(args, centerX, centerY); 7811 if (!(centerX && centerY)) 7812 return false; 7813 7814 a = args->current(); 7815 if (!a) 7816 return false; 7817 result->setFirstX(toCSSPrimitiveValue(centerX.get())); 7818 result->setFirstY(toCSSPrimitiveValue(centerY.get())); 7819 // Right now, CSS radial gradients have the same start and end centers. 7820 result->setSecondX(toCSSPrimitiveValue(centerX.get())); 7821 result->setSecondY(toCSSPrimitiveValue(centerY.get())); 7822 } 7823 7824 if (shapeValue || sizeValue || horizontalSize || centerX || centerY) 7825 expectComma = true; 7826 7827 if (!parseGradientColorStops(args, result.get(), expectComma)) 7828 return false; 7829 7830 gradient = result.release(); 7831 return true; 7832 } 7833 7834 bool CSSParser::parseGradientColorStops(CSSParserValueList* valueList, CSSGradientValue* gradient, bool expectComma) 7835 { 7836 CSSParserValue* a = valueList->current(); 7837 7838 // Now look for color stops. 7839 while (a) { 7840 // Look for the comma before the next stop. 7841 if (expectComma) { 7842 if (!isComma(a)) 7843 return false; 7844 7845 a = valueList->next(); 7846 if (!a) 7847 return false; 7848 } 7849 7850 // <color-stop> = <color> [ <percentage> | <length> ]? 7851 CSSGradientColorStop stop; 7852 stop.m_color = parseGradientColorOrKeyword(this, a); 7853 if (!stop.m_color) 7854 return false; 7855 7856 a = valueList->next(); 7857 if (a) { 7858 if (validUnit(a, FLength | FPercent)) { 7859 stop.m_position = createPrimitiveNumericValue(a); 7860 a = valueList->next(); 7861 } 7862 } 7863 7864 gradient->addStop(stop); 7865 expectComma = true; 7866 } 7867 7868 // Must have 2 or more stops to be valid. 7869 return gradient->stopCount() >= 2; 7870 } 7871 7872 bool CSSParser::parseGeneratedImage(CSSParserValueList* valueList, RefPtr<CSSValue>& value) 7873 { 7874 CSSParserValue* val = valueList->current(); 7875 7876 if (val->unit != CSSParserValue::Function) 7877 return false; 7878 7879 if (equalIgnoringCase(val->function->name, "-webkit-gradient(")) 7880 return parseDeprecatedGradient(valueList, value); 7881 7882 if (equalIgnoringCase(val->function->name, "-webkit-linear-gradient(")) 7883 return parseDeprecatedLinearGradient(valueList, value, NonRepeating); 7884 7885 if (equalIgnoringCase(val->function->name, "linear-gradient(")) 7886 return parseLinearGradient(valueList, value, NonRepeating); 7887 7888 if (equalIgnoringCase(val->function->name, "-webkit-repeating-linear-gradient(")) 7889 return parseDeprecatedLinearGradient(valueList, value, Repeating); 7890 7891 if (equalIgnoringCase(val->function->name, "repeating-linear-gradient(")) 7892 return parseLinearGradient(valueList, value, Repeating); 7893 7894 if (equalIgnoringCase(val->function->name, "-webkit-radial-gradient(")) 7895 return parseDeprecatedRadialGradient(valueList, value, NonRepeating); 7896 7897 if (equalIgnoringCase(val->function->name, "radial-gradient(")) 7898 return parseRadialGradient(valueList, value, NonRepeating); 7899 7900 if (equalIgnoringCase(val->function->name, "-webkit-repeating-radial-gradient(")) 7901 return parseDeprecatedRadialGradient(valueList, value, Repeating); 7902 7903 if (equalIgnoringCase(val->function->name, "repeating-radial-gradient(")) 7904 return parseRadialGradient(valueList, value, Repeating); 7905 7906 if (equalIgnoringCase(val->function->name, "-webkit-canvas(")) 7907 return parseCanvas(valueList, value); 7908 7909 if (equalIgnoringCase(val->function->name, "-webkit-cross-fade(")) 7910 return parseCrossfade(valueList, value); 7911 7912 return false; 7913 } 7914 7915 bool CSSParser::parseCrossfade(CSSParserValueList* valueList, RefPtr<CSSValue>& crossfade) 7916 { 7917 RefPtr<CSSCrossfadeValue> result; 7918 7919 // Walk the arguments. 7920 CSSParserValueList* args = valueList->current()->function->args.get(); 7921 if (!args || args->size() != 5) 7922 return false; 7923 CSSParserValue* a = args->current(); 7924 RefPtr<CSSValue> fromImageValue; 7925 RefPtr<CSSValue> toImageValue; 7926 7927 // The first argument is the "from" image. It is a fill image. 7928 if (!a || !parseFillImage(args, fromImageValue)) 7929 return false; 7930 a = args->next(); 7931 7932 // Skip a comma 7933 if (!isComma(a)) 7934 return false; 7935 a = args->next(); 7936 7937 // The second argument is the "to" image. It is a fill image. 7938 if (!a || !parseFillImage(args, toImageValue)) 7939 return false; 7940 a = args->next(); 7941 7942 // Skip a comma 7943 if (!isComma(a)) 7944 return false; 7945 a = args->next(); 7946 7947 // The third argument is the crossfade value. It is a percentage or a fractional number. 7948 RefPtr<CSSPrimitiveValue> percentage; 7949 if (!a) 7950 return false; 7951 7952 if (a->unit == CSSPrimitiveValue::CSS_PERCENTAGE) 7953 percentage = cssValuePool().createValue(clampTo<double>(a->fValue / 100, 0, 1), CSSPrimitiveValue::CSS_NUMBER); 7954 else if (a->unit == CSSPrimitiveValue::CSS_NUMBER) 7955 percentage = cssValuePool().createValue(clampTo<double>(a->fValue, 0, 1), CSSPrimitiveValue::CSS_NUMBER); 7956 else 7957 return false; 7958 7959 result = CSSCrossfadeValue::create(fromImageValue, toImageValue); 7960 result->setPercentage(percentage); 7961 7962 crossfade = result; 7963 7964 return true; 7965 } 7966 7967 bool CSSParser::parseCanvas(CSSParserValueList* valueList, RefPtr<CSSValue>& canvas) 7968 { 7969 // Walk the arguments. 7970 CSSParserValueList* args = valueList->current()->function->args.get(); 7971 if (!args || args->size() != 1) 7972 return false; 7973 7974 // The first argument is the canvas name. It is an identifier. 7975 CSSParserValue* value = args->current(); 7976 if (!value || value->unit != CSSPrimitiveValue::CSS_IDENT) 7977 return false; 7978 7979 canvas = CSSCanvasValue::create(value->string); 7980 return true; 7981 } 7982 7983 PassRefPtr<CSSValue> CSSParser::parseImageSet(CSSParserValueList* valueList) 7984 { 7985 CSSParserValue* function = valueList->current(); 7986 7987 if (function->unit != CSSParserValue::Function) 7988 return 0; 7989 7990 CSSParserValueList* functionArgs = valueList->current()->function->args.get(); 7991 if (!functionArgs || !functionArgs->size() || !functionArgs->current()) 7992 return 0; 7993 7994 RefPtr<CSSImageSetValue> imageSet = CSSImageSetValue::create(); 7995 7996 CSSParserValue* arg = functionArgs->current(); 7997 while (arg) { 7998 if (arg->unit != CSSPrimitiveValue::CSS_URI) 7999 return 0; 8000 8001 RefPtr<CSSImageValue> image = CSSImageValue::create(completeURL(arg->string)); 8002 imageSet->append(image); 8003 8004 arg = functionArgs->next(); 8005 if (!arg || arg->unit != CSSPrimitiveValue::CSS_DIMENSION) 8006 return 0; 8007 8008 double imageScaleFactor = 0; 8009 const String& string = arg->string; 8010 unsigned length = string.length(); 8011 if (!length) 8012 return 0; 8013 if (string.is8Bit()) { 8014 const LChar* start = string.characters8(); 8015 parseDouble(start, start + length, 'x', imageScaleFactor); 8016 } else { 8017 const UChar* start = string.characters16(); 8018 parseDouble(start, start + length, 'x', imageScaleFactor); 8019 } 8020 if (imageScaleFactor <= 0) 8021 return 0; 8022 imageSet->append(cssValuePool().createValue(imageScaleFactor, CSSPrimitiveValue::CSS_NUMBER)); 8023 8024 // If there are no more arguments, we're done. 8025 arg = functionArgs->next(); 8026 if (!arg) 8027 break; 8028 8029 // If there are more arguments, they should be after a comma. 8030 if (!isComma(arg)) 8031 return 0; 8032 8033 // Skip the comma and move on to the next argument. 8034 arg = functionArgs->next(); 8035 } 8036 8037 return imageSet.release(); 8038 } 8039 8040 class TransformOperationInfo { 8041 public: 8042 TransformOperationInfo(const CSSParserString& name) 8043 : m_type(CSSTransformValue::UnknownTransformOperation) 8044 , m_argCount(1) 8045 , m_allowSingleArgument(false) 8046 , m_unit(CSSParser::FUnknown) 8047 { 8048 const UChar* characters; 8049 unsigned nameLength = name.length(); 8050 8051 const unsigned longestNameLength = 12; 8052 UChar characterBuffer[longestNameLength]; 8053 if (name.is8Bit()) { 8054 unsigned length = std::min(longestNameLength, nameLength); 8055 const LChar* characters8 = name.characters8(); 8056 for (unsigned i = 0; i < length; ++i) 8057 characterBuffer[i] = characters8[i]; 8058 characters = characterBuffer; 8059 } else 8060 characters = name.characters16(); 8061 8062 SWITCH(characters, nameLength) { 8063 CASE("skew(") { 8064 m_unit = CSSParser::FAngle; 8065 m_type = CSSTransformValue::SkewTransformOperation; 8066 m_allowSingleArgument = true; 8067 m_argCount = 3; 8068 } 8069 CASE("scale(") { 8070 m_unit = CSSParser::FNumber; 8071 m_type = CSSTransformValue::ScaleTransformOperation; 8072 m_allowSingleArgument = true; 8073 m_argCount = 3; 8074 } 8075 CASE("skewx(") { 8076 m_unit = CSSParser::FAngle; 8077 m_type = CSSTransformValue::SkewXTransformOperation; 8078 } 8079 CASE("skewy(") { 8080 m_unit = CSSParser::FAngle; 8081 m_type = CSSTransformValue::SkewYTransformOperation; 8082 } 8083 CASE("matrix(") { 8084 m_unit = CSSParser::FNumber; 8085 m_type = CSSTransformValue::MatrixTransformOperation; 8086 m_argCount = 11; 8087 } 8088 CASE("rotate(") { 8089 m_unit = CSSParser::FAngle; 8090 m_type = CSSTransformValue::RotateTransformOperation; 8091 } 8092 CASE("scalex(") { 8093 m_unit = CSSParser::FNumber; 8094 m_type = CSSTransformValue::ScaleXTransformOperation; 8095 } 8096 CASE("scaley(") { 8097 m_unit = CSSParser::FNumber; 8098 m_type = CSSTransformValue::ScaleYTransformOperation; 8099 } 8100 CASE("scalez(") { 8101 m_unit = CSSParser::FNumber; 8102 m_type = CSSTransformValue::ScaleZTransformOperation; 8103 } 8104 CASE("scale3d(") { 8105 m_unit = CSSParser::FNumber; 8106 m_type = CSSTransformValue::Scale3DTransformOperation; 8107 m_argCount = 5; 8108 } 8109 CASE("rotatex(") { 8110 m_unit = CSSParser::FAngle; 8111 m_type = CSSTransformValue::RotateXTransformOperation; 8112 } 8113 CASE("rotatey(") { 8114 m_unit = CSSParser::FAngle; 8115 m_type = CSSTransformValue::RotateYTransformOperation; 8116 } 8117 CASE("rotatez(") { 8118 m_unit = CSSParser::FAngle; 8119 m_type = CSSTransformValue::RotateZTransformOperation; 8120 } 8121 CASE("matrix3d(") { 8122 m_unit = CSSParser::FNumber; 8123 m_type = CSSTransformValue::Matrix3DTransformOperation; 8124 m_argCount = 31; 8125 } 8126 CASE("rotate3d(") { 8127 m_unit = CSSParser::FNumber; 8128 m_type = CSSTransformValue::Rotate3DTransformOperation; 8129 m_argCount = 7; 8130 } 8131 CASE("translate(") { 8132 m_unit = CSSParser::FLength | CSSParser::FPercent; 8133 m_type = CSSTransformValue::TranslateTransformOperation; 8134 m_allowSingleArgument = true; 8135 m_argCount = 3; 8136 } 8137 CASE("translatex(") { 8138 m_unit = CSSParser::FLength | CSSParser::FPercent; 8139 m_type = CSSTransformValue::TranslateXTransformOperation; 8140 } 8141 CASE("translatey(") { 8142 m_unit = CSSParser::FLength | CSSParser::FPercent; 8143 m_type = CSSTransformValue::TranslateYTransformOperation; 8144 } 8145 CASE("translatez(") { 8146 m_unit = CSSParser::FLength | CSSParser::FPercent; 8147 m_type = CSSTransformValue::TranslateZTransformOperation; 8148 } 8149 CASE("perspective(") { 8150 m_unit = CSSParser::FNumber; 8151 m_type = CSSTransformValue::PerspectiveTransformOperation; 8152 } 8153 CASE("translate3d(") { 8154 m_unit = CSSParser::FLength | CSSParser::FPercent; 8155 m_type = CSSTransformValue::Translate3DTransformOperation; 8156 m_argCount = 5; 8157 } 8158 } 8159 } 8160 8161 CSSTransformValue::TransformOperationType type() const { return m_type; } 8162 unsigned argCount() const { return m_argCount; } 8163 CSSParser::Units unit() const { return m_unit; } 8164 8165 bool unknown() const { return m_type == CSSTransformValue::UnknownTransformOperation; } 8166 bool hasCorrectArgCount(unsigned argCount) { return m_argCount == argCount || (m_allowSingleArgument && argCount == 1); } 8167 8168 private: 8169 CSSTransformValue::TransformOperationType m_type; 8170 unsigned m_argCount; 8171 bool m_allowSingleArgument; 8172 CSSParser::Units m_unit; 8173 }; 8174 8175 PassRefPtr<CSSValueList> CSSParser::parseTransform() 8176 { 8177 if (!m_valueList) 8178 return 0; 8179 8180 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 8181 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 8182 RefPtr<CSSValue> parsedTransformValue = parseTransformValue(value); 8183 if (!parsedTransformValue) 8184 return 0; 8185 8186 list->append(parsedTransformValue.release()); 8187 } 8188 8189 return list.release(); 8190 } 8191 8192 PassRefPtr<CSSValue> CSSParser::parseTransformValue(CSSParserValue *value) 8193 { 8194 if (value->unit != CSSParserValue::Function || !value->function) 8195 return 0; 8196 8197 // Every primitive requires at least one argument. 8198 CSSParserValueList* args = value->function->args.get(); 8199 if (!args) 8200 return 0; 8201 8202 // See if the specified primitive is one we understand. 8203 TransformOperationInfo info(value->function->name); 8204 if (info.unknown()) 8205 return 0; 8206 8207 if (!info.hasCorrectArgCount(args->size())) 8208 return 0; 8209 8210 // The transform is a list of functional primitives that specify transform operations. 8211 // We collect a list of CSSTransformValues, where each value specifies a single operation. 8212 8213 // Create the new CSSTransformValue for this operation and add it to our list. 8214 RefPtr<CSSTransformValue> transformValue = CSSTransformValue::create(info.type()); 8215 8216 // Snag our values. 8217 CSSParserValue* a = args->current(); 8218 unsigned argNumber = 0; 8219 while (a) { 8220 CSSParser::Units unit = info.unit(); 8221 8222 if (info.type() == CSSTransformValue::Rotate3DTransformOperation && argNumber == 3) { 8223 // 4th param of rotate3d() is an angle rather than a bare number, validate it as such 8224 if (!validUnit(a, FAngle, HTMLStandardMode)) 8225 return 0; 8226 } else if (info.type() == CSSTransformValue::Translate3DTransformOperation && argNumber == 2) { 8227 // 3rd param of translate3d() cannot be a percentage 8228 if (!validUnit(a, FLength, HTMLStandardMode)) 8229 return 0; 8230 } else if (info.type() == CSSTransformValue::TranslateZTransformOperation && !argNumber) { 8231 // 1st param of translateZ() cannot be a percentage 8232 if (!validUnit(a, FLength, HTMLStandardMode)) 8233 return 0; 8234 } else if (info.type() == CSSTransformValue::PerspectiveTransformOperation && !argNumber) { 8235 // 1st param of perspective() must be a non-negative number (deprecated) or length. 8236 if (!validUnit(a, FNumber | FLength | FNonNeg, HTMLStandardMode)) 8237 return 0; 8238 } else if (!validUnit(a, unit, HTMLStandardMode)) 8239 return 0; 8240 8241 // Add the value to the current transform operation. 8242 transformValue->append(createPrimitiveNumericValue(a)); 8243 8244 a = args->next(); 8245 if (!a) 8246 break; 8247 if (a->unit != CSSParserValue::Operator || a->iValue != ',') 8248 return 0; 8249 a = args->next(); 8250 8251 argNumber++; 8252 } 8253 8254 return transformValue.release(); 8255 } 8256 8257 bool CSSParser::isBlendMode(CSSValueID valueID) 8258 { 8259 return (valueID >= CSSValueMultiply && valueID <= CSSValueLuminosity) 8260 || valueID == CSSValueNormal 8261 || valueID == CSSValueOverlay; 8262 } 8263 8264 bool CSSParser::isCompositeOperator(CSSValueID valueID) 8265 { 8266 // FIXME: Add CSSValueDestination and CSSValueLighter when the Compositing spec updates. 8267 return valueID >= CSSValueClear && valueID <= CSSValueXor; 8268 } 8269 8270 static void filterInfoForName(const CSSParserString& name, CSSFilterValue::FilterOperationType& filterType, unsigned& maximumArgumentCount) 8271 { 8272 if (equalIgnoringCase(name, "grayscale(")) 8273 filterType = CSSFilterValue::GrayscaleFilterOperation; 8274 else if (equalIgnoringCase(name, "sepia(")) 8275 filterType = CSSFilterValue::SepiaFilterOperation; 8276 else if (equalIgnoringCase(name, "saturate(")) 8277 filterType = CSSFilterValue::SaturateFilterOperation; 8278 else if (equalIgnoringCase(name, "hue-rotate(")) 8279 filterType = CSSFilterValue::HueRotateFilterOperation; 8280 else if (equalIgnoringCase(name, "invert(")) 8281 filterType = CSSFilterValue::InvertFilterOperation; 8282 else if (equalIgnoringCase(name, "opacity(")) 8283 filterType = CSSFilterValue::OpacityFilterOperation; 8284 else if (equalIgnoringCase(name, "brightness(")) 8285 filterType = CSSFilterValue::BrightnessFilterOperation; 8286 else if (equalIgnoringCase(name, "contrast(")) 8287 filterType = CSSFilterValue::ContrastFilterOperation; 8288 else if (equalIgnoringCase(name, "blur(")) 8289 filterType = CSSFilterValue::BlurFilterOperation; 8290 else if (equalIgnoringCase(name, "drop-shadow(")) { 8291 filterType = CSSFilterValue::DropShadowFilterOperation; 8292 maximumArgumentCount = 4; // x-offset, y-offset, blur-radius, color -- spread and inset style not allowed. 8293 } 8294 else if (equalIgnoringCase(name, "custom(")) 8295 filterType = CSSFilterValue::CustomFilterOperation; 8296 } 8297 8298 static bool acceptCommaOperator(CSSParserValueList* argsList) 8299 { 8300 if (CSSParserValue* arg = argsList->current()) { 8301 if (!isComma(arg)) 8302 return false; 8303 argsList->next(); 8304 } 8305 return true; 8306 } 8307 8308 PassRefPtr<CSSArrayFunctionValue> CSSParser::parseCustomFilterArrayFunction(CSSParserValue* value) 8309 { 8310 ASSERT(value->unit == CSSParserValue::Function && value->function); 8311 8312 if (!equalIgnoringCase(value->function->name, "array(")) 8313 return 0; 8314 8315 CSSParserValueList* arrayArgsParserValueList = value->function->args.get(); 8316 if (!arrayArgsParserValueList || !arrayArgsParserValueList->size()) 8317 return 0; 8318 8319 // array() values are comma separated. 8320 RefPtr<CSSArrayFunctionValue> arrayFunction = CSSArrayFunctionValue::create(); 8321 while (true) { 8322 // We parse pairs <Value, Comma> at each step. 8323 CSSParserValue* currentParserValue = arrayArgsParserValueList->current(); 8324 if (!currentParserValue || !validUnit(currentParserValue, FNumber, HTMLStandardMode)) 8325 return 0; 8326 8327 RefPtr<CSSValue> arrayValue = cssValuePool().createValue(currentParserValue->fValue, CSSPrimitiveValue::CSS_NUMBER); 8328 arrayFunction->append(arrayValue.release()); 8329 8330 CSSParserValue* nextParserValue = arrayArgsParserValueList->next(); 8331 if (!nextParserValue) 8332 break; 8333 8334 if (!isComma(nextParserValue)) 8335 return 0; 8336 8337 arrayArgsParserValueList->next(); 8338 } 8339 8340 return arrayFunction; 8341 } 8342 8343 PassRefPtr<CSSMixFunctionValue> CSSParser::parseMixFunction(CSSParserValue* value) 8344 { 8345 ASSERT(value->unit == CSSParserValue::Function && value->function); 8346 8347 if (!equalIgnoringCase(value->function->name, "mix(")) 8348 return 0; 8349 8350 CSSParserValueList* argsList = value->function->args.get(); 8351 if (!argsList) 8352 return 0; 8353 8354 unsigned numArgs = argsList->size(); 8355 if (numArgs < 1 || numArgs > 3) 8356 return 0; 8357 8358 RefPtr<CSSMixFunctionValue> mixFunction = CSSMixFunctionValue::create(); 8359 8360 bool hasBlendMode = false; 8361 bool hasAlphaCompositing = false; 8362 8363 for (CSSParserValue* arg = argsList->current(); arg; arg = argsList->next()) { 8364 RefPtr<CSSValue> value; 8365 8366 unsigned argNumber = argsList->currentIndex(); 8367 if (!argNumber) { 8368 if (arg->unit == CSSPrimitiveValue::CSS_URI) { 8369 KURL shaderURL = completeURL(arg->string); 8370 value = CSSShaderValue::create(shaderURL.string()); 8371 } 8372 } else if (argNumber == 1 || argNumber == 2) { 8373 if (!hasBlendMode && isBlendMode(arg->id)) { 8374 hasBlendMode = true; 8375 value = cssValuePool().createIdentifierValue(arg->id); 8376 } else if (!hasAlphaCompositing && isCompositeOperator(arg->id)) { 8377 hasAlphaCompositing = true; 8378 value = cssValuePool().createIdentifierValue(arg->id); 8379 } 8380 } 8381 8382 if (!value) 8383 return 0; 8384 8385 mixFunction->append(value.release()); 8386 } 8387 8388 return mixFunction; 8389 } 8390 8391 PassRefPtr<CSSValueList> CSSParser::parseCustomFilterParameters(CSSParserValueList* argsList) 8392 { 8393 // 8394 // params: [<param-def>[,<param-def>*]] 8395 // param-def: <param-name>wsp<param-value> 8396 // param-name: <ident> 8397 // param-value: true|false[wsp+true|false]{0-3} | 8398 // <number>[wsp+<number>]{0-3} | 8399 // <array> | 8400 // <transform> | 8401 // <texture(<uri>)> 8402 // array: 'array('<number>[wsp<number>]*')' 8403 // css-3d-transform: <transform-function>;[<transform-function>]* 8404 // transform: <css-3d-transform> | <mat> 8405 // mat: 'mat2('<number>(,<number>){3}')' | 8406 // 'mat3('<number>(,<number>){8}')' | 8407 // 'mat4('<number>(,<number>){15}')' ) 8408 // 8409 8410 RefPtr<CSSValueList> paramList = CSSValueList::createCommaSeparated(); 8411 8412 while (CSSParserValue* arg = argsList->current()) { 8413 if (arg->unit != CSSPrimitiveValue::CSS_IDENT) 8414 return 0; 8415 8416 RefPtr<CSSValueList> parameter = CSSValueList::createSpaceSeparated(); 8417 parameter->append(createPrimitiveStringValue(arg)); 8418 8419 arg = argsList->next(); 8420 if (!arg) 8421 return 0; 8422 8423 RefPtr<CSSValue> parameterValue; 8424 8425 if (arg->unit == CSSParserValue::Function && arg->function) { 8426 // FIXME: Implement parsing for the other parameter types. 8427 // textures: https://bugs.webkit.org/show_bug.cgi?id=71442 8428 // mat2, mat3, mat4: https://bugs.webkit.org/show_bug.cgi?id=71444 8429 if (equalIgnoringCase(arg->function->name, "array(")) { 8430 parameterValue = parseCustomFilterArrayFunction(arg); 8431 // This parsing step only consumes function arguments, 8432 // argsList is therefore moved forward explicitely. 8433 argsList->next(); 8434 } else 8435 parameterValue = parseCustomFilterTransform(argsList); 8436 } else { 8437 RefPtr<CSSValueList> paramValueList = CSSValueList::createSpaceSeparated(); 8438 arg = argsList->current(); 8439 while (arg) { 8440 // If we hit a comma, it means that we finished this parameter's values. 8441 if (isComma(arg)) 8442 break; 8443 if (!validUnit(arg, FNumber, HTMLStandardMode)) 8444 return 0; 8445 paramValueList->append(cssValuePool().createValue(arg->fValue, CSSPrimitiveValue::CSS_NUMBER)); 8446 arg = argsList->next(); 8447 } 8448 if (!paramValueList->length() || paramValueList->length() > 4) 8449 return 0; 8450 parameterValue = paramValueList.release(); 8451 } 8452 8453 if (!parameterValue || !acceptCommaOperator(argsList)) 8454 return 0; 8455 8456 parameter->append(parameterValue.release()); 8457 paramList->append(parameter.release()); 8458 } 8459 8460 return paramList; 8461 } 8462 8463 PassRefPtr<CSSFilterValue> CSSParser::parseCustomFilterFunctionWithAtRuleReferenceSyntax(CSSParserValue* value) 8464 { 8465 // 8466 // Custom filter function "at-rule reference" syntax: 8467 // 8468 // custom(<filter-name>wsp[,wsp<params>]) 8469 // 8470 // filter-name: <filter-name> 8471 // params: See the comment in CSSParser::parseCustomFilterParameters. 8472 // 8473 8474 ASSERT(value->function); 8475 8476 CSSParserValueList* argsList = value->function->args.get(); 8477 if (!argsList || !argsList->size()) 8478 return 0; 8479 8480 // 1. Parse the filter name. 8481 CSSParserValue* arg = argsList->current(); 8482 if (arg->unit != CSSPrimitiveValue::CSS_IDENT) 8483 return 0; 8484 8485 RefPtr<CSSFilterValue> filterValue = CSSFilterValue::create(CSSFilterValue::CustomFilterOperation); 8486 8487 RefPtr<CSSValue> filterName = createPrimitiveStringValue(arg); 8488 filterValue->append(filterName); 8489 argsList->next(); 8490 8491 if (!acceptCommaOperator(argsList)) 8492 return 0; 8493 8494 // 2. Parse the parameters. 8495 RefPtr<CSSValueList> paramList = parseCustomFilterParameters(argsList); 8496 if (!paramList) 8497 return 0; 8498 8499 if (paramList->length()) 8500 filterValue->append(paramList.release()); 8501 8502 return filterValue; 8503 } 8504 8505 // FIXME: The custom filters "inline" syntax is deprecated. We will remove it eventually. 8506 PassRefPtr<CSSFilterValue> CSSParser::parseCustomFilterFunctionWithInlineSyntax(CSSParserValue* value) 8507 { 8508 // 8509 // Custom filter function "inline" syntax: 8510 // 8511 // custom(<vertex-shader>[wsp<fragment-shader>][,<vertex-mesh>][,<params>]) 8512 // 8513 // vertexShader: <uri> | none 8514 // fragmentShader: <uri> | none | mix(<uri> [ <blend-mode> || <alpha-compositing> ]?) 8515 // 8516 // blend-mode: normal | multiply | screen | overlay | darken | lighten | color-dodge | 8517 // color-burn | hard-light | soft-light | difference | exclusion | hue | 8518 // saturation | color | luminosity 8519 // alpha-compositing: clear | src | dst | src-over | dst-over | src-in | dst-in | 8520 // src-out | dst-out | src-atop | dst-atop | xor | plus 8521 // 8522 // vertexMesh: +<integer>{1,2}[wsp<box>][wsp'detached'] 8523 // box: filter-box | border-box | padding-box | content-box 8524 // 8525 // params: See the comment in CSSParser::parseCustomFilterParameters. 8526 // 8527 8528 ASSERT(value->function); 8529 8530 CSSParserValueList* argsList = value->function->args.get(); 8531 if (!argsList) 8532 return 0; 8533 8534 RefPtr<CSSFilterValue> filterValue = CSSFilterValue::create(CSSFilterValue::CustomFilterOperation); 8535 8536 // 1. Parse the shader URLs: <vertex-shader>[wsp<fragment-shader>] 8537 RefPtr<CSSValueList> shadersList = CSSValueList::createSpaceSeparated(); 8538 bool hadAtLeastOneCustomShader = false; 8539 CSSParserValue* arg; 8540 for (arg = argsList->current(); arg; arg = argsList->next()) { 8541 RefPtr<CSSValue> value; 8542 if (arg->id == CSSValueNone) 8543 value = cssValuePool().createIdentifierValue(CSSValueNone); 8544 else if (arg->unit == CSSPrimitiveValue::CSS_URI) { 8545 KURL shaderURL = completeURL(arg->string); 8546 value = CSSShaderValue::create(shaderURL.string()); 8547 hadAtLeastOneCustomShader = true; 8548 } else if (argsList->currentIndex() == 1 && arg->unit == CSSParserValue::Function) { 8549 if (!(value = parseMixFunction(arg))) 8550 return 0; 8551 hadAtLeastOneCustomShader = true; 8552 } 8553 8554 if (!value) 8555 break; 8556 shadersList->append(value.release()); 8557 } 8558 8559 if (!shadersList->length() || !hadAtLeastOneCustomShader || shadersList->length() > 2 || !acceptCommaOperator(argsList)) 8560 return 0; 8561 8562 filterValue->append(shadersList.release()); 8563 8564 // 2. Parse the mesh size <vertex-mesh> 8565 RefPtr<CSSValueList> meshSizeList = CSSValueList::createSpaceSeparated(); 8566 8567 for (arg = argsList->current(); arg; arg = argsList->next()) { 8568 if (!validUnit(arg, FInteger | FNonNeg, HTMLStandardMode)) 8569 break; 8570 int integerValue = clampToInteger(arg->fValue); 8571 // According to the specification we can only accept positive non-zero values. 8572 if (integerValue < 1) 8573 return 0; 8574 meshSizeList->append(cssValuePool().createValue(integerValue, CSSPrimitiveValue::CSS_NUMBER)); 8575 } 8576 8577 if (meshSizeList->length() > 2) 8578 return 0; 8579 8580 // FIXME: For legacy content, we accept the mesh box types. We don't do anything else with them. 8581 // Eventually, we'll remove them completely. 8582 // https://bugs.webkit.org/show_bug.cgi?id=103778 8583 if ((arg = argsList->current()) && (arg->id == CSSValueBorderBox || arg->id == CSSValuePaddingBox 8584 || arg->id == CSSValueContentBox || arg->id == CSSValueFilterBox)) 8585 argsList->next(); 8586 8587 if ((arg = argsList->current()) && arg->id == CSSValueDetached) { 8588 meshSizeList->append(cssValuePool().createIdentifierValue(arg->id)); 8589 argsList->next(); 8590 } 8591 8592 if (meshSizeList->length()) { 8593 if (!acceptCommaOperator(argsList)) 8594 return 0; 8595 filterValue->append(meshSizeList.release()); 8596 } 8597 8598 // 3. Parse the parameters. 8599 RefPtr<CSSValueList> paramList = parseCustomFilterParameters(argsList); 8600 if (!paramList) 8601 return 0; 8602 8603 if (paramList->length()) 8604 filterValue->append(paramList.release()); 8605 8606 return filterValue; 8607 } 8608 8609 PassRefPtr<CSSFilterValue> CSSParser::parseCustomFilterFunction(CSSParserValue* value) 8610 { 8611 ASSERT(value->function); 8612 8613 // Look ahead to determine which syntax the custom function is using. 8614 // Both the at-rule reference syntax and the inline syntax require at least one argument. 8615 CSSParserValueList* argsList = value->function->args.get(); 8616 if (!argsList || !argsList->size()) 8617 return 0; 8618 8619 // The at-rule reference syntax expects a single ident or an ident followed by a comma. 8620 // e.g. custom(my-filter) or custom(my-filter, ...) 8621 // In contrast, when the inline syntax starts with an ident like "none", it expects a uri or a mix function next. 8622 // e.g. custom(none url(...)) or custom(none mix(...) 8623 bool isAtRuleReferenceSyntax = argsList->valueAt(0)->unit == CSSPrimitiveValue::CSS_IDENT 8624 && (argsList->size() == 1 || isComma(argsList->valueAt(1))); 8625 return isAtRuleReferenceSyntax ? parseCustomFilterFunctionWithAtRuleReferenceSyntax(value) : parseCustomFilterFunctionWithInlineSyntax(value); 8626 } 8627 8628 PassRefPtr<CSSValueList> CSSParser::parseCustomFilterTransform(CSSParserValueList* valueList) 8629 { 8630 if (!valueList) 8631 return 0; 8632 8633 // CSS Shaders' custom() transforms are space separated and comma terminated. 8634 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 8635 for (CSSParserValue* value = valueList->current(); value; value = valueList->next()) { 8636 if (isComma(value)) 8637 break; 8638 8639 RefPtr<CSSValue> parsedTransformValue = parseTransformValue(value); 8640 if (!parsedTransformValue) 8641 return 0; 8642 8643 list->append(parsedTransformValue.release()); 8644 } 8645 8646 return list.release(); 8647 } 8648 8649 PassRefPtr<CSSShaderValue> CSSParser::parseFilterRuleSrcUriAndFormat(CSSParserValueList* valueList) 8650 { 8651 CSSParserValue* value = valueList->current(); 8652 ASSERT(value && value->unit == CSSPrimitiveValue::CSS_URI); 8653 RefPtr<CSSShaderValue> shaderValue = CSSShaderValue::create(completeURL(value->string)); 8654 8655 value = valueList->next(); 8656 if (value && value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "format(")) { 8657 CSSParserValueList* args = value->function->args.get(); 8658 if (!args || args->size() != 1) 8659 return 0; 8660 8661 CSSParserValue* arg = args->current(); 8662 if (arg->unit != CSSPrimitiveValue::CSS_STRING) 8663 return 0; 8664 8665 shaderValue->setFormat(arg->string); 8666 valueList->next(); 8667 } 8668 8669 return shaderValue.release(); 8670 } 8671 8672 bool CSSParser::parseFilterRuleSrc() 8673 { 8674 RefPtr<CSSValueList> srcList = CSSValueList::createCommaSeparated(); 8675 8676 CSSParserValue* value = m_valueList->current(); 8677 while (value) { 8678 if (value->unit != CSSPrimitiveValue::CSS_URI) 8679 return false; 8680 8681 RefPtr<CSSShaderValue> shaderValue = parseFilterRuleSrcUriAndFormat(m_valueList.get()); 8682 if (!shaderValue) 8683 return false; 8684 srcList->append(shaderValue.release()); 8685 8686 if (!acceptCommaOperator(m_valueList.get())) 8687 return false; 8688 8689 value = m_valueList->current(); 8690 } 8691 8692 if (!srcList->length()) 8693 return false; 8694 8695 addProperty(CSSPropertySrc, srcList.release(), m_important); 8696 return true; 8697 } 8698 8699 StyleRuleBase* CSSParser::createFilterRule(const CSSParserString& filterName) 8700 { 8701 RefPtr<StyleRuleFilter> rule = StyleRuleFilter::create(filterName); 8702 rule->setProperties(createStylePropertySet()); 8703 clearProperties(); 8704 StyleRuleFilter* result = rule.get(); 8705 m_parsedRules.append(rule.release()); 8706 return result; 8707 } 8708 8709 8710 PassRefPtr<CSSFilterValue> CSSParser::parseBuiltinFilterArguments(CSSParserValueList* args, CSSFilterValue::FilterOperationType filterType) 8711 { 8712 RefPtr<CSSFilterValue> filterValue = CSSFilterValue::create(filterType); 8713 ASSERT(args); 8714 8715 switch (filterType) { 8716 case CSSFilterValue::GrayscaleFilterOperation: 8717 case CSSFilterValue::SepiaFilterOperation: 8718 case CSSFilterValue::SaturateFilterOperation: 8719 case CSSFilterValue::InvertFilterOperation: 8720 case CSSFilterValue::OpacityFilterOperation: 8721 case CSSFilterValue::ContrastFilterOperation: { 8722 // One optional argument, 0-1 or 0%-100%, if missing use 100%. 8723 if (args->size() > 1) 8724 return 0; 8725 8726 if (args->size()) { 8727 CSSParserValue* value = args->current(); 8728 if (!validUnit(value, FNumber | FPercent | FNonNeg, HTMLStandardMode)) 8729 return 0; 8730 8731 double amount = value->fValue; 8732 8733 // Saturate and Contrast allow values over 100%. 8734 if (filterType != CSSFilterValue::SaturateFilterOperation 8735 && filterType != CSSFilterValue::ContrastFilterOperation) { 8736 double maxAllowed = value->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 100.0 : 1.0; 8737 if (amount > maxAllowed) 8738 return 0; 8739 } 8740 8741 filterValue->append(cssValuePool().createValue(amount, static_cast<CSSPrimitiveValue::UnitTypes>(value->unit))); 8742 } 8743 break; 8744 } 8745 case CSSFilterValue::BrightnessFilterOperation: { 8746 // One optional argument, if missing use 100%. 8747 if (args->size() > 1) 8748 return 0; 8749 8750 if (args->size()) { 8751 CSSParserValue* value = args->current(); 8752 if (!validUnit(value, FNumber | FPercent, HTMLStandardMode)) 8753 return 0; 8754 8755 filterValue->append(cssValuePool().createValue(value->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(value->unit))); 8756 } 8757 break; 8758 } 8759 case CSSFilterValue::HueRotateFilterOperation: { 8760 // hue-rotate() takes one optional angle. 8761 if (args->size() > 1) 8762 return 0; 8763 8764 if (args->size()) { 8765 CSSParserValue* argument = args->current(); 8766 if (!validUnit(argument, FAngle, HTMLStandardMode)) 8767 return 0; 8768 8769 filterValue->append(createPrimitiveNumericValue(argument)); 8770 } 8771 break; 8772 } 8773 case CSSFilterValue::BlurFilterOperation: { 8774 // Blur takes a single length. Zero parameters are allowed. 8775 if (args->size() > 1) 8776 return 0; 8777 8778 if (args->size()) { 8779 CSSParserValue* argument = args->current(); 8780 if (!validUnit(argument, FLength | FNonNeg, HTMLStandardMode)) 8781 return 0; 8782 8783 filterValue->append(createPrimitiveNumericValue(argument)); 8784 } 8785 break; 8786 } 8787 case CSSFilterValue::DropShadowFilterOperation: { 8788 // drop-shadow() takes a single shadow. 8789 RefPtr<CSSValueList> shadowValueList = parseShadow(args, CSSPropertyWebkitFilter); 8790 if (!shadowValueList || shadowValueList->length() != 1) 8791 return 0; 8792 8793 filterValue->append((shadowValueList.release())->itemWithoutBoundsCheck(0)); 8794 break; 8795 } 8796 default: 8797 ASSERT_NOT_REACHED(); 8798 } 8799 return filterValue.release(); 8800 } 8801 8802 PassRefPtr<CSSValueList> CSSParser::parseFilter() 8803 { 8804 if (!m_valueList) 8805 return 0; 8806 8807 // The filter is a list of functional primitives that specify individual operations. 8808 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 8809 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 8810 if (value->unit != CSSPrimitiveValue::CSS_URI && (value->unit != CSSParserValue::Function || !value->function)) 8811 return 0; 8812 8813 CSSFilterValue::FilterOperationType filterType = CSSFilterValue::UnknownFilterOperation; 8814 8815 // See if the specified primitive is one we understand. 8816 if (value->unit == CSSPrimitiveValue::CSS_URI) { 8817 RefPtr<CSSFilterValue> referenceFilterValue = CSSFilterValue::create(CSSFilterValue::ReferenceFilterOperation); 8818 list->append(referenceFilterValue); 8819 referenceFilterValue->append(CSSSVGDocumentValue::create(value->string)); 8820 } else { 8821 const CSSParserString name = value->function->name; 8822 unsigned maximumArgumentCount = 1; 8823 8824 filterInfoForName(name, filterType, maximumArgumentCount); 8825 8826 if (filterType == CSSFilterValue::UnknownFilterOperation) 8827 return 0; 8828 8829 if (filterType == CSSFilterValue::CustomFilterOperation) { 8830 // Make sure parsing fails if custom filters are disabled. 8831 if (!RuntimeEnabledFeatures::cssCustomFilterEnabled()) 8832 return 0; 8833 8834 RefPtr<CSSFilterValue> filterValue = parseCustomFilterFunction(value); 8835 if (!filterValue) 8836 return 0; 8837 list->append(filterValue.release()); 8838 continue; 8839 } 8840 CSSParserValueList* args = value->function->args.get(); 8841 if (!args) 8842 return 0; 8843 8844 RefPtr<CSSFilterValue> filterValue = parseBuiltinFilterArguments(args, filterType); 8845 if (!filterValue) 8846 return 0; 8847 8848 list->append(filterValue); 8849 } 8850 } 8851 8852 return list.release(); 8853 } 8854 8855 static bool validFlowName(const String& flowName) 8856 { 8857 return !(equalIgnoringCase(flowName, "auto") 8858 || equalIgnoringCase(flowName, "default") 8859 || equalIgnoringCase(flowName, "inherit") 8860 || equalIgnoringCase(flowName, "initial") 8861 || equalIgnoringCase(flowName, "none")); 8862 } 8863 8864 bool CSSParser::parseFlowThread(const String& flowName) 8865 { 8866 setupParser("@-internal-decls -webkit-flow-into:", flowName, ""); 8867 cssyyparse(this); 8868 8869 m_rule = 0; 8870 8871 return ((m_parsedProperties.size() == 1) && (m_parsedProperties.first().id() == CSSPropertyWebkitFlowInto)); 8872 } 8873 8874 // none | <ident> 8875 bool CSSParser::parseFlowThread(CSSPropertyID propId, bool important) 8876 { 8877 ASSERT(propId == CSSPropertyWebkitFlowInto); 8878 ASSERT(RuntimeEnabledFeatures::cssRegionsEnabled()); 8879 8880 if (m_valueList->size() != 1) 8881 return false; 8882 8883 CSSParserValue* value = m_valueList->current(); 8884 if (!value) 8885 return false; 8886 8887 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 8888 return false; 8889 8890 if (value->id == CSSValueNone) { 8891 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 8892 return true; 8893 } 8894 8895 String inputProperty = String(value->string); 8896 if (!inputProperty.isEmpty()) { 8897 if (!validFlowName(inputProperty)) 8898 return false; 8899 addProperty(propId, cssValuePool().createValue(inputProperty, CSSPrimitiveValue::CSS_STRING), important); 8900 } else 8901 addProperty(propId, cssValuePool().createIdentifierValue(CSSValueNone), important); 8902 8903 return true; 8904 } 8905 8906 // -webkit-flow-from: none | <ident> 8907 bool CSSParser::parseRegionThread(CSSPropertyID propId, bool important) 8908 { 8909 ASSERT(propId == CSSPropertyWebkitFlowFrom); 8910 ASSERT(RuntimeEnabledFeatures::cssRegionsEnabled()); 8911 8912 if (m_valueList->size() != 1) 8913 return false; 8914 8915 CSSParserValue* value = m_valueList->current(); 8916 if (!value) 8917 return false; 8918 8919 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 8920 return false; 8921 8922 if (value->id == CSSValueNone) 8923 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 8924 else { 8925 String inputProperty = String(value->string); 8926 if (!inputProperty.isEmpty()) { 8927 if (!validFlowName(inputProperty)) 8928 return false; 8929 addProperty(propId, cssValuePool().createValue(inputProperty, CSSPrimitiveValue::CSS_STRING), important); 8930 } else 8931 addProperty(propId, cssValuePool().createIdentifierValue(CSSValueNone), important); 8932 } 8933 8934 return true; 8935 } 8936 8937 bool CSSParser::parseTransformOrigin(CSSPropertyID propId, CSSPropertyID& propId1, CSSPropertyID& propId2, CSSPropertyID& propId3, RefPtr<CSSValue>& value, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3) 8938 { 8939 propId1 = propId; 8940 propId2 = propId; 8941 propId3 = propId; 8942 if (propId == CSSPropertyWebkitTransformOrigin) { 8943 propId1 = CSSPropertyWebkitTransformOriginX; 8944 propId2 = CSSPropertyWebkitTransformOriginY; 8945 propId3 = CSSPropertyWebkitTransformOriginZ; 8946 } 8947 8948 switch (propId) { 8949 case CSSPropertyWebkitTransformOrigin: 8950 if (!parseTransformOriginShorthand(value, value2, value3)) 8951 return false; 8952 // parseTransformOriginShorthand advances the m_valueList pointer 8953 break; 8954 case CSSPropertyWebkitTransformOriginX: { 8955 value = parseFillPositionX(m_valueList.get()); 8956 if (value) 8957 m_valueList->next(); 8958 break; 8959 } 8960 case CSSPropertyWebkitTransformOriginY: { 8961 value = parseFillPositionY(m_valueList.get()); 8962 if (value) 8963 m_valueList->next(); 8964 break; 8965 } 8966 case CSSPropertyWebkitTransformOriginZ: { 8967 if (validUnit(m_valueList->current(), FLength)) 8968 value = createPrimitiveNumericValue(m_valueList->current()); 8969 if (value) 8970 m_valueList->next(); 8971 break; 8972 } 8973 default: 8974 ASSERT_NOT_REACHED(); 8975 return false; 8976 } 8977 8978 return value; 8979 } 8980 8981 bool CSSParser::parsePerspectiveOrigin(CSSPropertyID propId, CSSPropertyID& propId1, CSSPropertyID& propId2, RefPtr<CSSValue>& value, RefPtr<CSSValue>& value2) 8982 { 8983 propId1 = propId; 8984 propId2 = propId; 8985 if (propId == CSSPropertyWebkitPerspectiveOrigin) { 8986 propId1 = CSSPropertyWebkitPerspectiveOriginX; 8987 propId2 = CSSPropertyWebkitPerspectiveOriginY; 8988 } 8989 8990 switch (propId) { 8991 case CSSPropertyWebkitPerspectiveOrigin: 8992 if (m_valueList->size() > 2) 8993 return false; 8994 parse2ValuesFillPosition(m_valueList.get(), value, value2); 8995 break; 8996 case CSSPropertyWebkitPerspectiveOriginX: { 8997 value = parseFillPositionX(m_valueList.get()); 8998 if (value) 8999 m_valueList->next(); 9000 break; 9001 } 9002 case CSSPropertyWebkitPerspectiveOriginY: { 9003 value = parseFillPositionY(m_valueList.get()); 9004 if (value) 9005 m_valueList->next(); 9006 break; 9007 } 9008 default: 9009 ASSERT_NOT_REACHED(); 9010 return false; 9011 } 9012 9013 return value; 9014 } 9015 9016 bool CSSParser::parseTouchAction(bool important) 9017 { 9018 if (!RuntimeEnabledFeatures::cssTouchActionEnabled()) 9019 return false; 9020 9021 CSSParserValue* value = m_valueList->current(); 9022 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9023 if (m_valueList->size() == 1 && value && (value->id == CSSValueAuto || value->id == CSSValueNone)) { 9024 list->append(cssValuePool().createIdentifierValue(value->id)); 9025 addProperty(CSSPropertyTouchAction, list.release(), important); 9026 m_valueList->next(); 9027 return true; 9028 } 9029 9030 bool isValid = true; 9031 while (isValid && value) { 9032 switch (value->id) { 9033 case CSSValuePanX: 9034 case CSSValuePanY: { 9035 RefPtr<CSSValue> panValue = cssValuePool().createIdentifierValue(value->id); 9036 if (list->hasValue(panValue.get())) { 9037 isValid = false; 9038 break; 9039 } 9040 list->append(panValue.release()); 9041 break; 9042 } 9043 default: 9044 isValid = false; 9045 break; 9046 } 9047 if (isValid) 9048 value = m_valueList->next(); 9049 } 9050 9051 if (list->length() && isValid) { 9052 addProperty(CSSPropertyTouchAction, list.release(), important); 9053 return true; 9054 } 9055 9056 return false; 9057 } 9058 9059 void CSSParser::addTextDecorationProperty(CSSPropertyID propId, PassRefPtr<CSSValue> value, bool important) 9060 { 9061 // The text-decoration-line property takes priority over text-decoration, unless the latter has important priority set. 9062 if (propId == CSSPropertyTextDecoration && !important && !inShorthand()) { 9063 for (unsigned i = 0; i < m_parsedProperties.size(); ++i) { 9064 if (m_parsedProperties[i].id() == CSSPropertyTextDecorationLine) 9065 return; 9066 } 9067 } 9068 addProperty(propId, value, important); 9069 } 9070 9071 bool CSSParser::parseTextDecoration(CSSPropertyID propId, bool important) 9072 { 9073 if (propId == CSSPropertyTextDecorationLine 9074 && !RuntimeEnabledFeatures::css3TextDecorationsEnabled()) 9075 return false; 9076 9077 CSSParserValue* value = m_valueList->current(); 9078 if (value && value->id == CSSValueNone) { 9079 addTextDecorationProperty(propId, cssValuePool().createIdentifierValue(CSSValueNone), important); 9080 m_valueList->next(); 9081 return true; 9082 } 9083 9084 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9085 bool isValid = true; 9086 while (isValid && value) { 9087 switch (value->id) { 9088 case CSSValueUnderline: 9089 case CSSValueOverline: 9090 case CSSValueLineThrough: 9091 case CSSValueBlink: 9092 list->append(cssValuePool().createIdentifierValue(value->id)); 9093 break; 9094 default: 9095 isValid = false; 9096 break; 9097 } 9098 if (isValid) 9099 value = m_valueList->next(); 9100 } 9101 9102 // Values are either valid or in shorthand scope. 9103 if (list->length() && (isValid || inShorthand())) { 9104 addTextDecorationProperty(propId, list.release(), important); 9105 return true; 9106 } 9107 9108 return false; 9109 } 9110 9111 bool CSSParser::parseTextUnderlinePosition(bool important) 9112 { 9113 // The text-underline-position property has syntax "auto | [ under || [ left | right ] ]". 9114 // However, values 'left' and 'right' are not implemented yet, so we will parse syntax 9115 // "auto | under" for now. 9116 CSSParserValue* value = m_valueList->current(); 9117 switch (value->id) { 9118 case CSSValueAuto: 9119 case CSSValueUnder: 9120 if (m_valueList->next()) 9121 return false; 9122 addProperty(CSSPropertyTextUnderlinePosition, cssValuePool().createIdentifierValue(value->id), important); 9123 return true; 9124 default: 9125 return false; 9126 } 9127 } 9128 9129 bool CSSParser::parseTextEmphasisStyle(bool important) 9130 { 9131 unsigned valueListSize = m_valueList->size(); 9132 9133 RefPtr<CSSPrimitiveValue> fill; 9134 RefPtr<CSSPrimitiveValue> shape; 9135 9136 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9137 if (value->unit == CSSPrimitiveValue::CSS_STRING) { 9138 if (fill || shape || (valueListSize != 1 && !inShorthand())) 9139 return false; 9140 addProperty(CSSPropertyWebkitTextEmphasisStyle, createPrimitiveStringValue(value), important); 9141 m_valueList->next(); 9142 return true; 9143 } 9144 9145 if (value->id == CSSValueNone) { 9146 if (fill || shape || (valueListSize != 1 && !inShorthand())) 9147 return false; 9148 addProperty(CSSPropertyWebkitTextEmphasisStyle, cssValuePool().createIdentifierValue(CSSValueNone), important); 9149 m_valueList->next(); 9150 return true; 9151 } 9152 9153 if (value->id == CSSValueOpen || value->id == CSSValueFilled) { 9154 if (fill) 9155 return false; 9156 fill = cssValuePool().createIdentifierValue(value->id); 9157 } else if (value->id == CSSValueDot || value->id == CSSValueCircle || value->id == CSSValueDoubleCircle || value->id == CSSValueTriangle || value->id == CSSValueSesame) { 9158 if (shape) 9159 return false; 9160 shape = cssValuePool().createIdentifierValue(value->id); 9161 } else if (!inShorthand()) 9162 return false; 9163 else 9164 break; 9165 } 9166 9167 if (fill && shape) { 9168 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated(); 9169 parsedValues->append(fill.release()); 9170 parsedValues->append(shape.release()); 9171 addProperty(CSSPropertyWebkitTextEmphasisStyle, parsedValues.release(), important); 9172 return true; 9173 } 9174 if (fill) { 9175 addProperty(CSSPropertyWebkitTextEmphasisStyle, fill.release(), important); 9176 return true; 9177 } 9178 if (shape) { 9179 addProperty(CSSPropertyWebkitTextEmphasisStyle, shape.release(), important); 9180 return true; 9181 } 9182 9183 return false; 9184 } 9185 9186 PassRefPtr<CSSValue> CSSParser::parseTextIndent() 9187 { 9188 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9189 9190 // <length> | <percentage> | inherit 9191 if (m_valueList->size() == 1) { 9192 CSSParserValue* value = m_valueList->current(); 9193 if (!value->id && validUnit(value, FLength | FPercent)) { 9194 list->append(createPrimitiveNumericValue(value)); 9195 m_valueList->next(); 9196 return list.release(); 9197 } 9198 } 9199 9200 if (!RuntimeEnabledFeatures::css3TextEnabled()) 9201 return 0; 9202 9203 // The case where text-indent has only <length>(or <percentage>) value 9204 // is handled above if statement even though css3TextEnabled() returns true. 9205 9206 // [ [ <length> | <percentage> ] && each-line ] | inherit 9207 if (m_valueList->size() != 2) 9208 return 0; 9209 9210 CSSParserValue* firstValue = m_valueList->current(); 9211 CSSParserValue* secondValue = m_valueList->next(); 9212 CSSParserValue* lengthOrPercentageValue = 0; 9213 9214 // [ <length> | <percentage> ] each-line 9215 if (validUnit(firstValue, FLength | FPercent) && secondValue->id == CSSValueEachLine) 9216 lengthOrPercentageValue = firstValue; 9217 // each-line [ <length> | <percentage> ] 9218 else if (firstValue->id == CSSValueEachLine && validUnit(secondValue, FLength | FPercent)) 9219 lengthOrPercentageValue = secondValue; 9220 9221 if (lengthOrPercentageValue) { 9222 list->append(createPrimitiveNumericValue(lengthOrPercentageValue)); 9223 list->append(cssValuePool().createIdentifierValue(CSSValueEachLine)); 9224 m_valueList->next(); 9225 return list.release(); 9226 } 9227 9228 return 0; 9229 } 9230 9231 bool CSSParser::parseLineBoxContain(bool important) 9232 { 9233 LineBoxContain lineBoxContain = LineBoxContainNone; 9234 9235 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9236 if (value->id == CSSValueBlock) { 9237 if (lineBoxContain & LineBoxContainBlock) 9238 return false; 9239 lineBoxContain |= LineBoxContainBlock; 9240 } else if (value->id == CSSValueInline) { 9241 if (lineBoxContain & LineBoxContainInline) 9242 return false; 9243 lineBoxContain |= LineBoxContainInline; 9244 } else if (value->id == CSSValueFont) { 9245 if (lineBoxContain & LineBoxContainFont) 9246 return false; 9247 lineBoxContain |= LineBoxContainFont; 9248 } else if (value->id == CSSValueGlyphs) { 9249 if (lineBoxContain & LineBoxContainGlyphs) 9250 return false; 9251 lineBoxContain |= LineBoxContainGlyphs; 9252 } else if (value->id == CSSValueReplaced) { 9253 if (lineBoxContain & LineBoxContainReplaced) 9254 return false; 9255 lineBoxContain |= LineBoxContainReplaced; 9256 } else if (value->id == CSSValueInlineBox) { 9257 if (lineBoxContain & LineBoxContainInlineBox) 9258 return false; 9259 lineBoxContain |= LineBoxContainInlineBox; 9260 } else 9261 return false; 9262 } 9263 9264 if (!lineBoxContain) 9265 return false; 9266 9267 addProperty(CSSPropertyWebkitLineBoxContain, CSSLineBoxContainValue::create(lineBoxContain), important); 9268 return true; 9269 } 9270 9271 bool CSSParser::parseFontFeatureTag(CSSValueList* settings) 9272 { 9273 // Feature tag name consists of 4-letter characters. 9274 static const unsigned tagNameLength = 4; 9275 9276 CSSParserValue* value = m_valueList->current(); 9277 // Feature tag name comes first 9278 if (value->unit != CSSPrimitiveValue::CSS_STRING) 9279 return false; 9280 if (value->string.length() != tagNameLength) 9281 return false; 9282 for (unsigned i = 0; i < tagNameLength; ++i) { 9283 // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification. 9284 UChar character = value->string[i]; 9285 if (character < 0x20 || character > 0x7E) 9286 return false; 9287 } 9288 9289 AtomicString tag = value->string; 9290 int tagValue = 1; 9291 // Feature tag values could follow: <integer> | on | off 9292 value = m_valueList->next(); 9293 if (value) { 9294 if (value->unit == CSSPrimitiveValue::CSS_NUMBER && value->isInt && value->fValue >= 0) { 9295 tagValue = clampToInteger(value->fValue); 9296 if (tagValue < 0) 9297 return false; 9298 m_valueList->next(); 9299 } else if (value->id == CSSValueOn || value->id == CSSValueOff) { 9300 tagValue = value->id == CSSValueOn; 9301 m_valueList->next(); 9302 } 9303 } 9304 settings->append(CSSFontFeatureValue::create(tag, tagValue)); 9305 return true; 9306 } 9307 9308 bool CSSParser::parseFontFeatureSettings(bool important) 9309 { 9310 if (m_valueList->size() == 1 && m_valueList->current()->id == CSSValueNormal) { 9311 RefPtr<CSSPrimitiveValue> normalValue = cssValuePool().createIdentifierValue(CSSValueNormal); 9312 m_valueList->next(); 9313 addProperty(CSSPropertyWebkitFontFeatureSettings, normalValue.release(), important); 9314 return true; 9315 } 9316 9317 RefPtr<CSSValueList> settings = CSSValueList::createCommaSeparated(); 9318 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9319 if (!parseFontFeatureTag(settings.get())) 9320 return false; 9321 9322 // If the list isn't parsed fully, the current value should be comma. 9323 value = m_valueList->current(); 9324 if (value && !isComma(value)) 9325 return false; 9326 } 9327 if (settings->length()) { 9328 addProperty(CSSPropertyWebkitFontFeatureSettings, settings.release(), important); 9329 return true; 9330 } 9331 return false; 9332 } 9333 9334 bool CSSParser::parseFontVariantLigatures(bool important) 9335 { 9336 RefPtr<CSSValueList> ligatureValues = CSSValueList::createSpaceSeparated(); 9337 bool sawCommonLigaturesValue = false; 9338 bool sawDiscretionaryLigaturesValue = false; 9339 bool sawHistoricalLigaturesValue = false; 9340 9341 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9342 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 9343 return false; 9344 9345 switch (value->id) { 9346 case CSSValueNoCommonLigatures: 9347 case CSSValueCommonLigatures: 9348 if (sawCommonLigaturesValue) 9349 return false; 9350 sawCommonLigaturesValue = true; 9351 ligatureValues->append(cssValuePool().createIdentifierValue(value->id)); 9352 break; 9353 case CSSValueNoDiscretionaryLigatures: 9354 case CSSValueDiscretionaryLigatures: 9355 if (sawDiscretionaryLigaturesValue) 9356 return false; 9357 sawDiscretionaryLigaturesValue = true; 9358 ligatureValues->append(cssValuePool().createIdentifierValue(value->id)); 9359 break; 9360 case CSSValueNoHistoricalLigatures: 9361 case CSSValueHistoricalLigatures: 9362 if (sawHistoricalLigaturesValue) 9363 return false; 9364 sawHistoricalLigaturesValue = true; 9365 ligatureValues->append(cssValuePool().createIdentifierValue(value->id)); 9366 break; 9367 default: 9368 return false; 9369 } 9370 } 9371 9372 if (!ligatureValues->length()) 9373 return false; 9374 9375 addProperty(CSSPropertyWebkitFontVariantLigatures, ligatureValues.release(), important); 9376 return true; 9377 } 9378 9379 bool CSSParser::parseCalculation(CSSParserValue* value, ValueRange range) 9380 { 9381 ASSERT(isCalculation(value)); 9382 9383 CSSParserValueList* args = value->function->args.get(); 9384 if (!args || !args->size()) 9385 return false; 9386 9387 ASSERT(!m_parsedCalculation); 9388 m_parsedCalculation = CSSCalcValue::create(value->function->name, args, range); 9389 9390 if (!m_parsedCalculation) 9391 return false; 9392 9393 return true; 9394 } 9395 9396 #define END_TOKEN 0 9397 9398 void CSSParser::ensureLineEndings() 9399 { 9400 if (!m_lineEndings) 9401 m_lineEndings = lineEndings(*m_source); 9402 } 9403 9404 CSSParserSelector* CSSParser::createFloatingSelectorWithTagName(const QualifiedName& tagQName) 9405 { 9406 CSSParserSelector* selector = new CSSParserSelector(tagQName); 9407 m_floatingSelectors.append(selector); 9408 return selector; 9409 } 9410 9411 CSSParserSelector* CSSParser::createFloatingSelector() 9412 { 9413 CSSParserSelector* selector = new CSSParserSelector; 9414 m_floatingSelectors.append(selector); 9415 return selector; 9416 } 9417 9418 PassOwnPtr<CSSParserSelector> CSSParser::sinkFloatingSelector(CSSParserSelector* selector) 9419 { 9420 if (selector) { 9421 size_t index = m_floatingSelectors.reverseFind(selector); 9422 ASSERT(index != kNotFound); 9423 m_floatingSelectors.remove(index); 9424 } 9425 return adoptPtr(selector); 9426 } 9427 9428 Vector<OwnPtr<CSSParserSelector> >* CSSParser::createFloatingSelectorVector() 9429 { 9430 Vector<OwnPtr<CSSParserSelector> >* selectorVector = new Vector<OwnPtr<CSSParserSelector> >; 9431 m_floatingSelectorVectors.append(selectorVector); 9432 return selectorVector; 9433 } 9434 9435 PassOwnPtr<Vector<OwnPtr<CSSParserSelector> > > CSSParser::sinkFloatingSelectorVector(Vector<OwnPtr<CSSParserSelector> >* selectorVector) 9436 { 9437 if (selectorVector) { 9438 size_t index = m_floatingSelectorVectors.reverseFind(selectorVector); 9439 ASSERT(index != kNotFound); 9440 m_floatingSelectorVectors.remove(index); 9441 } 9442 return adoptPtr(selectorVector); 9443 } 9444 9445 CSSParserValueList* CSSParser::createFloatingValueList() 9446 { 9447 CSSParserValueList* list = new CSSParserValueList; 9448 m_floatingValueLists.append(list); 9449 return list; 9450 } 9451 9452 PassOwnPtr<CSSParserValueList> CSSParser::sinkFloatingValueList(CSSParserValueList* list) 9453 { 9454 if (list) { 9455 size_t index = m_floatingValueLists.reverseFind(list); 9456 ASSERT(index != kNotFound); 9457 m_floatingValueLists.remove(index); 9458 } 9459 return adoptPtr(list); 9460 } 9461 9462 CSSParserFunction* CSSParser::createFloatingFunction() 9463 { 9464 CSSParserFunction* function = new CSSParserFunction; 9465 m_floatingFunctions.append(function); 9466 return function; 9467 } 9468 9469 CSSParserFunction* CSSParser::createFloatingFunction(const CSSParserString& name, PassOwnPtr<CSSParserValueList> args) 9470 { 9471 CSSParserFunction* function = createFloatingFunction(); 9472 function->name = name; 9473 function->args = args; 9474 return function; 9475 } 9476 9477 PassOwnPtr<CSSParserFunction> CSSParser::sinkFloatingFunction(CSSParserFunction* function) 9478 { 9479 if (function) { 9480 size_t index = m_floatingFunctions.reverseFind(function); 9481 ASSERT(index != kNotFound); 9482 m_floatingFunctions.remove(index); 9483 } 9484 return adoptPtr(function); 9485 } 9486 9487 CSSParserValue& CSSParser::sinkFloatingValue(CSSParserValue& value) 9488 { 9489 if (value.unit == CSSParserValue::Function) { 9490 size_t index = m_floatingFunctions.reverseFind(value.function); 9491 ASSERT(index != kNotFound); 9492 m_floatingFunctions.remove(index); 9493 } 9494 return value; 9495 } 9496 9497 MediaQueryExp* CSSParser::createFloatingMediaQueryExp(const AtomicString& mediaFeature, CSSParserValueList* values) 9498 { 9499 m_floatingMediaQueryExp = MediaQueryExp::create(mediaFeature, values); 9500 return m_floatingMediaQueryExp.get(); 9501 } 9502 9503 PassOwnPtr<MediaQueryExp> CSSParser::sinkFloatingMediaQueryExp(MediaQueryExp* expression) 9504 { 9505 ASSERT_UNUSED(expression, expression == m_floatingMediaQueryExp); 9506 return m_floatingMediaQueryExp.release(); 9507 } 9508 9509 Vector<OwnPtr<MediaQueryExp> >* CSSParser::createFloatingMediaQueryExpList() 9510 { 9511 m_floatingMediaQueryExpList = adoptPtr(new Vector<OwnPtr<MediaQueryExp> >); 9512 return m_floatingMediaQueryExpList.get(); 9513 } 9514 9515 PassOwnPtr<Vector<OwnPtr<MediaQueryExp> > > CSSParser::sinkFloatingMediaQueryExpList(Vector<OwnPtr<MediaQueryExp> >* list) 9516 { 9517 ASSERT_UNUSED(list, list == m_floatingMediaQueryExpList); 9518 return m_floatingMediaQueryExpList.release(); 9519 } 9520 9521 MediaQuery* CSSParser::createFloatingMediaQuery(MediaQuery::Restrictor restrictor, const AtomicString& mediaType, PassOwnPtr<Vector<OwnPtr<MediaQueryExp> > > expressions) 9522 { 9523 m_floatingMediaQuery = adoptPtr(new MediaQuery(restrictor, mediaType, expressions)); 9524 return m_floatingMediaQuery.get(); 9525 } 9526 9527 MediaQuery* CSSParser::createFloatingMediaQuery(PassOwnPtr<Vector<OwnPtr<MediaQueryExp> > > expressions) 9528 { 9529 return createFloatingMediaQuery(MediaQuery::None, AtomicString("all", AtomicString::ConstructFromLiteral), expressions); 9530 } 9531 9532 MediaQuery* CSSParser::createFloatingNotAllQuery() 9533 { 9534 return createFloatingMediaQuery(MediaQuery::Not, AtomicString("all", AtomicString::ConstructFromLiteral), sinkFloatingMediaQueryExpList(createFloatingMediaQueryExpList())); 9535 } 9536 9537 PassOwnPtr<MediaQuery> CSSParser::sinkFloatingMediaQuery(MediaQuery* query) 9538 { 9539 ASSERT_UNUSED(query, query == m_floatingMediaQuery); 9540 return m_floatingMediaQuery.release(); 9541 } 9542 9543 Vector<RefPtr<StyleKeyframe> >* CSSParser::createFloatingKeyframeVector() 9544 { 9545 m_floatingKeyframeVector = adoptPtr(new Vector<RefPtr<StyleKeyframe> >()); 9546 return m_floatingKeyframeVector.get(); 9547 } 9548 9549 PassOwnPtr<Vector<RefPtr<StyleKeyframe> > > CSSParser::sinkFloatingKeyframeVector(Vector<RefPtr<StyleKeyframe> >* keyframeVector) 9550 { 9551 ASSERT_UNUSED(keyframeVector, m_floatingKeyframeVector == keyframeVector); 9552 return m_floatingKeyframeVector.release(); 9553 } 9554 9555 MediaQuerySet* CSSParser::createMediaQuerySet() 9556 { 9557 RefPtr<MediaQuerySet> queries = MediaQuerySet::create(); 9558 MediaQuerySet* result = queries.get(); 9559 m_parsedMediaQuerySets.append(queries.release()); 9560 return result; 9561 } 9562 9563 StyleRuleBase* CSSParser::createImportRule(const CSSParserString& url, MediaQuerySet* media) 9564 { 9565 if (!media || !m_allowImportRules) 9566 return 0; 9567 RefPtr<StyleRuleImport> rule = StyleRuleImport::create(url, media); 9568 StyleRuleImport* result = rule.get(); 9569 m_parsedRules.append(rule.release()); 9570 return result; 9571 } 9572 9573 StyleRuleBase* CSSParser::createMediaRule(MediaQuerySet* media, RuleList* rules) 9574 { 9575 m_allowImportRules = m_allowNamespaceDeclarations = false; 9576 RefPtr<StyleRuleMedia> rule; 9577 if (rules) { 9578 rule = StyleRuleMedia::create(media ? media : MediaQuerySet::create(), *rules); 9579 } else { 9580 RuleList emptyRules; 9581 rule = StyleRuleMedia::create(media ? media : MediaQuerySet::create(), emptyRules); 9582 } 9583 StyleRuleMedia* result = rule.get(); 9584 m_parsedRules.append(rule.release()); 9585 return result; 9586 } 9587 9588 StyleRuleBase* CSSParser::createSupportsRule(bool conditionIsSupported, RuleList* rules) 9589 { 9590 m_allowImportRules = m_allowNamespaceDeclarations = false; 9591 9592 RefPtr<CSSRuleSourceData> data = popSupportsRuleData(); 9593 RefPtr<StyleRuleSupports> rule; 9594 String conditionText; 9595 unsigned conditionOffset = data->ruleHeaderRange.start + 9; 9596 unsigned conditionLength = data->ruleHeaderRange.length() - 9; 9597 9598 if (m_tokenizer.is8BitSource()) 9599 conditionText = String(m_tokenizer.m_dataStart8.get() + conditionOffset, conditionLength).stripWhiteSpace(); 9600 else 9601 conditionText = String(m_tokenizer.m_dataStart16.get() + conditionOffset, conditionLength).stripWhiteSpace(); 9602 9603 if (rules) { 9604 rule = StyleRuleSupports::create(conditionText, conditionIsSupported, *rules); 9605 } else { 9606 RuleList emptyRules; 9607 rule = StyleRuleSupports::create(conditionText, conditionIsSupported, emptyRules); 9608 } 9609 9610 StyleRuleSupports* result = rule.get(); 9611 m_parsedRules.append(rule.release()); 9612 9613 return result; 9614 } 9615 9616 void CSSParser::markSupportsRuleHeaderStart() 9617 { 9618 if (!m_supportsRuleDataStack) 9619 m_supportsRuleDataStack = adoptPtr(new RuleSourceDataList()); 9620 9621 RefPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(CSSRuleSourceData::SUPPORTS_RULE); 9622 data->ruleHeaderRange.start = m_tokenizer.tokenStartOffset(); 9623 m_supportsRuleDataStack->append(data); 9624 } 9625 9626 void CSSParser::markSupportsRuleHeaderEnd() 9627 { 9628 ASSERT(m_supportsRuleDataStack && !m_supportsRuleDataStack->isEmpty()); 9629 9630 if (m_tokenizer.is8BitSource()) 9631 m_supportsRuleDataStack->last()->ruleHeaderRange.end = m_tokenizer.tokenStart<LChar>() - m_tokenizer.m_dataStart8.get(); 9632 else 9633 m_supportsRuleDataStack->last()->ruleHeaderRange.end = m_tokenizer.tokenStart<UChar>() - m_tokenizer.m_dataStart16.get(); 9634 } 9635 9636 PassRefPtr<CSSRuleSourceData> CSSParser::popSupportsRuleData() 9637 { 9638 ASSERT(m_supportsRuleDataStack && !m_supportsRuleDataStack->isEmpty()); 9639 RefPtr<CSSRuleSourceData> data = m_supportsRuleDataStack->last(); 9640 m_supportsRuleDataStack->removeLast(); 9641 return data.release(); 9642 } 9643 9644 CSSParser::RuleList* CSSParser::createRuleList() 9645 { 9646 OwnPtr<RuleList> list = adoptPtr(new RuleList); 9647 RuleList* listPtr = list.get(); 9648 9649 m_parsedRuleLists.append(list.release()); 9650 return listPtr; 9651 } 9652 9653 CSSParser::RuleList* CSSParser::appendRule(RuleList* ruleList, StyleRuleBase* rule) 9654 { 9655 if (rule) { 9656 if (!ruleList) 9657 ruleList = createRuleList(); 9658 ruleList->append(rule); 9659 } 9660 return ruleList; 9661 } 9662 9663 template <typename CharacterType> 9664 ALWAYS_INLINE static void makeLower(const CharacterType* input, CharacterType* output, unsigned length) 9665 { 9666 // FIXME: If we need Unicode lowercasing here, then we probably want the real kind 9667 // that can potentially change the length of the string rather than the character 9668 // by character kind. If we don't need Unicode lowercasing, it would be good to 9669 // simplify this function. 9670 9671 if (charactersAreAllASCII(input, length)) { 9672 // Fast case for all-ASCII. 9673 for (unsigned i = 0; i < length; i++) 9674 output[i] = toASCIILower(input[i]); 9675 } else { 9676 for (unsigned i = 0; i < length; i++) 9677 output[i] = Unicode::toLower(input[i]); 9678 } 9679 } 9680 9681 void CSSParser::tokenToLowerCase(const CSSParserString& token) 9682 { 9683 size_t length = token.length(); 9684 if (m_tokenizer.is8BitSource()) { 9685 size_t offset = token.characters8() - m_tokenizer.m_dataStart8.get(); 9686 makeLower(token.characters8(), m_tokenizer.m_dataStart8.get() + offset, length); 9687 } else { 9688 size_t offset = token.characters16() - m_tokenizer.m_dataStart16.get(); 9689 makeLower(token.characters16(), m_tokenizer.m_dataStart16.get() + offset, length); 9690 } 9691 } 9692 9693 void CSSParser::endInvalidRuleHeader() 9694 { 9695 if (m_ruleHeaderType == CSSRuleSourceData::UNKNOWN_RULE) 9696 return; 9697 9698 CSSParserLocation location; 9699 location.lineNumber = m_tokenizer.m_lineNumber; 9700 location.offset = m_ruleHeaderStartOffset; 9701 if (m_tokenizer.is8BitSource()) 9702 location.token.init(m_tokenizer.m_dataStart8.get() + m_ruleHeaderStartOffset, 0); 9703 else 9704 location.token.init(m_tokenizer.m_dataStart16.get() + m_ruleHeaderStartOffset, 0); 9705 9706 reportError(location, m_ruleHeaderType == CSSRuleSourceData::STYLE_RULE ? InvalidSelectorError : InvalidRuleError); 9707 9708 endRuleHeader(); 9709 } 9710 9711 void CSSParser::reportError(const CSSParserLocation&, ErrorType) 9712 { 9713 // FIXME: error reporting temporatily disabled. 9714 } 9715 9716 bool CSSParser::isLoggingErrors() 9717 { 9718 return m_logErrors && !m_ignoreErrors; 9719 } 9720 9721 void CSSParser::logError(const String& message, const CSSParserLocation& location) 9722 { 9723 unsigned lineNumberInStyleSheet; 9724 unsigned columnNumber = 0; 9725 PageConsole& console = m_styleSheet->singleOwnerDocument()->page()->console(); 9726 if (InspectorInstrumentation::hasFrontends()) { 9727 ensureLineEndings(); 9728 TextPosition tokenPosition = TextPosition::fromOffsetAndLineEndings(location.offset, *m_lineEndings); 9729 lineNumberInStyleSheet = tokenPosition.m_line.zeroBasedInt(); 9730 columnNumber = (lineNumberInStyleSheet ? 0 : m_startPosition.m_column.zeroBasedInt()) + tokenPosition.m_column.zeroBasedInt(); 9731 } else { 9732 lineNumberInStyleSheet = location.lineNumber; 9733 } 9734 console.addMessage(CSSMessageSource, WarningMessageLevel, message, m_styleSheet->baseURL().string(), lineNumberInStyleSheet + m_startPosition.m_line.zeroBasedInt() + 1, columnNumber + 1); 9735 } 9736 9737 StyleRuleKeyframes* CSSParser::createKeyframesRule(const String& name, PassOwnPtr<Vector<RefPtr<StyleKeyframe> > > popKeyframes, bool isPrefixed) 9738 { 9739 OwnPtr<Vector<RefPtr<StyleKeyframe> > > keyframes = popKeyframes; 9740 m_allowImportRules = m_allowNamespaceDeclarations = false; 9741 RefPtr<StyleRuleKeyframes> rule = StyleRuleKeyframes::create(); 9742 for (size_t i = 0; i < keyframes->size(); ++i) 9743 rule->parserAppendKeyframe(keyframes->at(i)); 9744 rule->setName(name); 9745 rule->setVendorPrefixed(isPrefixed); 9746 StyleRuleKeyframes* rulePtr = rule.get(); 9747 m_parsedRules.append(rule.release()); 9748 return rulePtr; 9749 } 9750 9751 StyleRuleBase* CSSParser::createStyleRule(Vector<OwnPtr<CSSParserSelector> >* selectors) 9752 { 9753 StyleRule* result = 0; 9754 if (selectors) { 9755 m_allowImportRules = m_allowNamespaceDeclarations = false; 9756 RefPtr<StyleRule> rule = StyleRule::create(); 9757 rule->parserAdoptSelectorVector(*selectors); 9758 if (m_hasFontFaceOnlyValues) 9759 deleteFontFaceOnlyValues(); 9760 rule->setProperties(createStylePropertySet()); 9761 result = rule.get(); 9762 m_parsedRules.append(rule.release()); 9763 } 9764 clearProperties(); 9765 return result; 9766 } 9767 9768 StyleRuleBase* CSSParser::createFontFaceRule() 9769 { 9770 m_allowImportRules = m_allowNamespaceDeclarations = false; 9771 for (unsigned i = 0; i < m_parsedProperties.size(); ++i) { 9772 CSSProperty& property = m_parsedProperties[i]; 9773 if (property.id() == CSSPropertyFontVariant && property.value()->isPrimitiveValue()) 9774 property.wrapValueInCommaSeparatedList(); 9775 else if (property.id() == CSSPropertyFontFamily && (!property.value()->isValueList() || toCSSValueList(property.value())->length() != 1)) { 9776 // Unlike font-family property, font-family descriptor in @font-face rule 9777 // has to be a value list with exactly one family name. It cannot have a 9778 // have 'initial' value and cannot 'inherit' from parent. 9779 // See http://dev.w3.org/csswg/css3-fonts/#font-family-desc 9780 clearProperties(); 9781 return 0; 9782 } 9783 } 9784 RefPtr<StyleRuleFontFace> rule = StyleRuleFontFace::create(); 9785 rule->setProperties(createStylePropertySet()); 9786 clearProperties(); 9787 StyleRuleFontFace* result = rule.get(); 9788 m_parsedRules.append(rule.release()); 9789 if (m_styleSheet) 9790 m_styleSheet->setHasFontFaceRule(true); 9791 return result; 9792 } 9793 9794 void CSSParser::addNamespace(const AtomicString& prefix, const AtomicString& uri) 9795 { 9796 if (!m_styleSheet || !m_allowNamespaceDeclarations) 9797 return; 9798 m_allowImportRules = false; 9799 m_styleSheet->parserAddNamespace(prefix, uri); 9800 if (prefix.isEmpty() && !uri.isNull()) 9801 m_defaultNamespace = uri; 9802 } 9803 9804 QualifiedName CSSParser::determineNameInNamespace(const AtomicString& prefix, const AtomicString& localName) 9805 { 9806 if (!m_styleSheet) 9807 return QualifiedName(prefix, localName, m_defaultNamespace); 9808 return QualifiedName(prefix, localName, m_styleSheet->determineNamespace(prefix)); 9809 } 9810 9811 CSSParserSelector* CSSParser::rewriteSpecifiersWithNamespaceIfNeeded(CSSParserSelector* specifiers) 9812 { 9813 if (m_defaultNamespace != starAtom || specifiers->needsCrossingTreeScopeBoundary()) 9814 return rewriteSpecifiersWithElementName(nullAtom, starAtom, specifiers, /*tagIsForNamespaceRule*/true); 9815 if (CSSParserSelector* distributedPseudoElementSelector = specifiers->findDistributedPseudoElementSelector()) { 9816 specifiers->prependTagSelector(QualifiedName(nullAtom, starAtom, m_defaultNamespace), /*tagIsForNamespaceRule*/true); 9817 return rewriteSpecifiersForShadowDistributed(specifiers, distributedPseudoElementSelector); 9818 } 9819 return specifiers; 9820 } 9821 9822 CSSParserSelector* CSSParser::rewriteSpecifiersWithElementName(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector* specifiers, bool tagIsForNamespaceRule) 9823 { 9824 AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleSheet ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace; 9825 QualifiedName tag(namespacePrefix, elementName, determinedNamespace); 9826 9827 if (CSSParserSelector* distributedPseudoElementSelector = specifiers->findDistributedPseudoElementSelector()) { 9828 specifiers->prependTagSelector(tag, tagIsForNamespaceRule); 9829 return rewriteSpecifiersForShadowDistributed(specifiers, distributedPseudoElementSelector); 9830 } 9831 9832 if (specifiers->needsCrossingTreeScopeBoundary()) 9833 return rewriteSpecifiersWithElementNameForCustomPseudoElement(tag, elementName, specifiers, tagIsForNamespaceRule); 9834 9835 if (specifiers->isContentPseudoElement()) 9836 return rewriteSpecifiersWithElementNameForContentPseudoElement(tag, elementName, specifiers, tagIsForNamespaceRule); 9837 9838 if (tag == anyQName()) 9839 return specifiers; 9840 if (!(specifiers->pseudoType() == CSSSelector::PseudoCue)) 9841 specifiers->prependTagSelector(tag, tagIsForNamespaceRule); 9842 return specifiers; 9843 } 9844 9845 CSSParserSelector* CSSParser::rewriteSpecifiersWithElementNameForCustomPseudoElement(const QualifiedName& tag, const AtomicString& elementName, CSSParserSelector* specifiers, bool tagIsForNamespaceRule) 9846 { 9847 if (m_useCounter && specifiers->pseudoType() == CSSSelector::PseudoUserAgentCustomElement) 9848 m_useCounter->count(UseCounter::CSSPseudoElementUserAgentCustomPseudo); 9849 9850 CSSParserSelector* lastShadowPseudo = specifiers; 9851 CSSParserSelector* history = specifiers; 9852 while (history->tagHistory()) { 9853 history = history->tagHistory(); 9854 if (history->needsCrossingTreeScopeBoundary() || history->hasShadowPseudo()) 9855 lastShadowPseudo = history; 9856 } 9857 9858 if (lastShadowPseudo->tagHistory()) { 9859 if (tag != anyQName()) 9860 lastShadowPseudo->tagHistory()->prependTagSelector(tag, tagIsForNamespaceRule); 9861 return specifiers; 9862 } 9863 9864 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo combinator has to be used. 9865 // We therefore create a new Selector with that combinator here in any case, even if matching any (host) element in any namespace (i.e. '*'). 9866 OwnPtr<CSSParserSelector> elementNameSelector = adoptPtr(new CSSParserSelector(tag)); 9867 lastShadowPseudo->setTagHistory(elementNameSelector.release()); 9868 lastShadowPseudo->setRelation(CSSSelector::ShadowPseudo); 9869 return specifiers; 9870 } 9871 9872 CSSParserSelector* CSSParser::rewriteSpecifiersWithElementNameForContentPseudoElement(const QualifiedName& tag, const AtomicString& elementName, CSSParserSelector* specifiers, bool tagIsForNamespaceRule) 9873 { 9874 CSSParserSelector* last = specifiers; 9875 CSSParserSelector* history = specifiers; 9876 while (history->tagHistory()) { 9877 history = history->tagHistory(); 9878 if (history->isContentPseudoElement() || history->relationIsAffectedByPseudoContent()) 9879 last = history; 9880 } 9881 9882 if (last->tagHistory()) { 9883 if (tag != anyQName()) 9884 last->tagHistory()->prependTagSelector(tag, tagIsForNamespaceRule); 9885 return specifiers; 9886 } 9887 9888 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo combinator has to be used. 9889 // We therefore create a new Selector with that combinator here in any case, even if matching any (host) element in any namespace (i.e. '*'). 9890 OwnPtr<CSSParserSelector> elementNameSelector = adoptPtr(new CSSParserSelector(tag)); 9891 last->setTagHistory(elementNameSelector.release()); 9892 last->setRelation(CSSSelector::SubSelector); 9893 return specifiers; 9894 } 9895 9896 CSSParserSelector* CSSParser::rewriteSpecifiersForShadowDistributed(CSSParserSelector* specifiers, CSSParserSelector* distributedPseudoElementSelector) 9897 { 9898 if (m_useCounter) 9899 m_useCounter->count(UseCounter::CSSPseudoElementPrefixedDistributed); 9900 CSSParserSelector* argumentSelector = distributedPseudoElementSelector->functionArgumentSelector(); 9901 ASSERT(argumentSelector); 9902 ASSERT(!specifiers->isDistributedPseudoElement()); 9903 for (CSSParserSelector* end = specifiers; end->tagHistory(); end = end->tagHistory()) { 9904 if (end->tagHistory()->isDistributedPseudoElement()) { 9905 end->clearTagHistory(); 9906 break; 9907 } 9908 } 9909 CSSParserSelector* end = argumentSelector; 9910 while (end->tagHistory()) 9911 end = end->tagHistory(); 9912 9913 switch (end->relation()) { 9914 case CSSSelector::Child: 9915 case CSSSelector::Descendant: 9916 end->setTagHistory(sinkFloatingSelector(specifiers)); 9917 end->setRelationIsAffectedByPseudoContent(); 9918 return argumentSelector; 9919 default: 9920 return 0; 9921 } 9922 } 9923 9924 CSSParserSelector* CSSParser::rewriteSpecifiers(CSSParserSelector* specifiers, CSSParserSelector* newSpecifier) 9925 { 9926 if (newSpecifier->needsCrossingTreeScopeBoundary()) { 9927 // Unknown pseudo element always goes at the top of selector chain. 9928 newSpecifier->appendTagHistory(CSSSelector::ShadowPseudo, sinkFloatingSelector(specifiers)); 9929 return newSpecifier; 9930 } 9931 if (newSpecifier->isContentPseudoElement()) { 9932 newSpecifier->appendTagHistory(CSSSelector::SubSelector, sinkFloatingSelector(specifiers)); 9933 return newSpecifier; 9934 } 9935 if (specifiers->needsCrossingTreeScopeBoundary()) { 9936 // Specifiers for unknown pseudo element go right behind it in the chain. 9937 specifiers->insertTagHistory(CSSSelector::SubSelector, sinkFloatingSelector(newSpecifier), CSSSelector::ShadowPseudo); 9938 return specifiers; 9939 } 9940 if (specifiers->isContentPseudoElement()) { 9941 specifiers->insertTagHistory(CSSSelector::SubSelector, sinkFloatingSelector(newSpecifier), CSSSelector::SubSelector); 9942 return specifiers; 9943 } 9944 specifiers->appendTagHistory(CSSSelector::SubSelector, sinkFloatingSelector(newSpecifier)); 9945 return specifiers; 9946 } 9947 9948 StyleRuleBase* CSSParser::createPageRule(PassOwnPtr<CSSParserSelector> pageSelector) 9949 { 9950 // FIXME: Margin at-rules are ignored. 9951 m_allowImportRules = m_allowNamespaceDeclarations = false; 9952 StyleRulePage* pageRule = 0; 9953 if (pageSelector) { 9954 RefPtr<StyleRulePage> rule = StyleRulePage::create(); 9955 Vector<OwnPtr<CSSParserSelector> > selectorVector; 9956 selectorVector.append(pageSelector); 9957 rule->parserAdoptSelectorVector(selectorVector); 9958 rule->setProperties(createStylePropertySet()); 9959 pageRule = rule.get(); 9960 m_parsedRules.append(rule.release()); 9961 } 9962 clearProperties(); 9963 return pageRule; 9964 } 9965 9966 void CSSParser::setReusableRegionSelectorVector(Vector<OwnPtr<CSSParserSelector> >* selectors) 9967 { 9968 if (selectors) 9969 m_reusableRegionSelectorVector.swap(*selectors); 9970 } 9971 9972 StyleRuleBase* CSSParser::createRegionRule(Vector<OwnPtr<CSSParserSelector> >* regionSelector, RuleList* rules) 9973 { 9974 if (m_useCounter) 9975 m_useCounter->count(UseCounter::CSSWebkitRegionAtRule); 9976 9977 if (!RuntimeEnabledFeatures::cssRegionsEnabled() || !regionSelector || !rules) 9978 return 0; 9979 9980 m_allowImportRules = m_allowNamespaceDeclarations = false; 9981 9982 RefPtr<StyleRuleRegion> regionRule = StyleRuleRegion::create(regionSelector, *rules); 9983 9984 StyleRuleRegion* result = regionRule.get(); 9985 m_parsedRules.append(regionRule.release()); 9986 if (m_sourceDataHandler) 9987 m_sourceDataHandler->startEndUnknownRule(); 9988 9989 return result; 9990 } 9991 9992 StyleRuleBase* CSSParser::createMarginAtRule(CSSSelector::MarginBoxType /* marginBox */) 9993 { 9994 // FIXME: Implement margin at-rule here, using: 9995 // - marginBox: margin box 9996 // - m_parsedProperties: properties at [m_numParsedPropertiesBeforeMarginBox, m_parsedProperties.size()] are for this at-rule. 9997 // Don't forget to also update the action for page symbol in CSSGrammar.y such that margin at-rule data is cleared if page_selector is invalid. 9998 9999 endDeclarationsForMarginBox(); 10000 return 0; // until this method is implemented. 10001 } 10002 10003 void CSSParser::startDeclarationsForMarginBox() 10004 { 10005 m_numParsedPropertiesBeforeMarginBox = m_parsedProperties.size(); 10006 } 10007 10008 void CSSParser::endDeclarationsForMarginBox() 10009 { 10010 rollbackLastProperties(m_parsedProperties.size() - m_numParsedPropertiesBeforeMarginBox); 10011 m_numParsedPropertiesBeforeMarginBox = INVALID_NUM_PARSED_PROPERTIES; 10012 } 10013 10014 void CSSParser::deleteFontFaceOnlyValues() 10015 { 10016 ASSERT(m_hasFontFaceOnlyValues); 10017 for (unsigned i = 0; i < m_parsedProperties.size();) { 10018 CSSProperty& property = m_parsedProperties[i]; 10019 if (property.id() == CSSPropertyFontVariant && property.value()->isValueList()) { 10020 m_parsedProperties.remove(i); 10021 continue; 10022 } 10023 ++i; 10024 } 10025 } 10026 10027 StyleKeyframe* CSSParser::createKeyframe(CSSParserValueList* keys) 10028 { 10029 OwnPtr<Vector<double> > keyVector = StyleKeyframe::createKeyList(keys); 10030 if (keyVector->isEmpty()) 10031 return 0; 10032 10033 RefPtr<StyleKeyframe> keyframe = StyleKeyframe::create(); 10034 keyframe->setKeys(keyVector.release()); 10035 keyframe->setProperties(createStylePropertySet()); 10036 10037 clearProperties(); 10038 10039 StyleKeyframe* keyframePtr = keyframe.get(); 10040 m_parsedKeyframes.append(keyframe.release()); 10041 return keyframePtr; 10042 } 10043 10044 void CSSParser::invalidBlockHit() 10045 { 10046 if (m_styleSheet && !m_hadSyntacticallyValidCSSRule) 10047 m_styleSheet->setHasSyntacticallyValidCSSHeader(false); 10048 } 10049 10050 void CSSParser::startRule() 10051 { 10052 if (!m_sourceDataHandler) 10053 return; 10054 10055 ASSERT(m_ruleHasHeader); 10056 m_ruleHasHeader = false; 10057 } 10058 10059 void CSSParser::endRule(bool valid) 10060 { 10061 if (!m_sourceDataHandler) 10062 return; 10063 10064 if (m_ruleHasHeader) 10065 m_sourceDataHandler->endRuleBody(m_tokenizer.safeUserStringTokenOffset(), !valid); 10066 m_ruleHasHeader = true; 10067 } 10068 10069 void CSSParser::startRuleHeader(CSSRuleSourceData::Type ruleType) 10070 { 10071 resumeErrorLogging(); 10072 m_ruleHeaderType = ruleType; 10073 m_ruleHeaderStartOffset = m_tokenizer.safeUserStringTokenOffset(); 10074 m_ruleHeaderStartLineNumber = m_tokenizer.m_tokenStartLineNumber; 10075 if (m_sourceDataHandler) { 10076 ASSERT(!m_ruleHasHeader); 10077 m_sourceDataHandler->startRuleHeader(ruleType, m_ruleHeaderStartOffset); 10078 m_ruleHasHeader = true; 10079 } 10080 } 10081 10082 void CSSParser::endRuleHeader() 10083 { 10084 ASSERT(m_ruleHeaderType != CSSRuleSourceData::UNKNOWN_RULE); 10085 m_ruleHeaderType = CSSRuleSourceData::UNKNOWN_RULE; 10086 if (m_sourceDataHandler) { 10087 ASSERT(m_ruleHasHeader); 10088 m_sourceDataHandler->endRuleHeader(m_tokenizer.safeUserStringTokenOffset()); 10089 } 10090 } 10091 10092 void CSSParser::startSelector() 10093 { 10094 if (m_sourceDataHandler) 10095 m_sourceDataHandler->startSelector(m_tokenizer.safeUserStringTokenOffset()); 10096 } 10097 10098 void CSSParser::endSelector() 10099 { 10100 if (m_sourceDataHandler) 10101 m_sourceDataHandler->endSelector(m_tokenizer.safeUserStringTokenOffset()); 10102 } 10103 10104 void CSSParser::startRuleBody() 10105 { 10106 if (m_sourceDataHandler) 10107 m_sourceDataHandler->startRuleBody(m_tokenizer.safeUserStringTokenOffset()); 10108 } 10109 10110 void CSSParser::startProperty() 10111 { 10112 resumeErrorLogging(); 10113 if (m_sourceDataHandler) 10114 m_sourceDataHandler->startProperty(m_tokenizer.safeUserStringTokenOffset()); 10115 } 10116 10117 void CSSParser::endProperty(bool isImportantFound, bool isPropertyParsed, ErrorType errorType) 10118 { 10119 m_id = CSSPropertyInvalid; 10120 if (m_sourceDataHandler) 10121 m_sourceDataHandler->endProperty(isImportantFound, isPropertyParsed, m_tokenizer.safeUserStringTokenOffset(), errorType); 10122 } 10123 10124 void CSSParser::startEndUnknownRule() 10125 { 10126 if (m_sourceDataHandler) 10127 m_sourceDataHandler->startEndUnknownRule(); 10128 } 10129 10130 StyleRuleBase* CSSParser::createViewportRule() 10131 { 10132 // Allow @viewport rules from UA stylesheets even if the feature is disabled. 10133 if (!RuntimeEnabledFeatures::cssViewportEnabled() && !isUASheetBehavior(m_context.mode())) 10134 return 0; 10135 10136 m_allowImportRules = m_allowNamespaceDeclarations = false; 10137 10138 RefPtr<StyleRuleViewport> rule = StyleRuleViewport::create(); 10139 10140 rule->setProperties(createStylePropertySet()); 10141 clearProperties(); 10142 10143 StyleRuleViewport* result = rule.get(); 10144 m_parsedRules.append(rule.release()); 10145 10146 return result; 10147 } 10148 10149 bool CSSParser::parseViewportProperty(CSSPropertyID propId, bool important) 10150 { 10151 ASSERT(RuntimeEnabledFeatures::cssViewportEnabled() || isUASheetBehavior(m_context.mode())); 10152 10153 CSSParserValue* value = m_valueList->current(); 10154 if (!value) 10155 return false; 10156 10157 CSSValueID id = value->id; 10158 bool validPrimitive = false; 10159 10160 switch (propId) { 10161 case CSSPropertyMinWidth: // auto | extend-to-zoom | <length> | <percentage> 10162 case CSSPropertyMaxWidth: 10163 case CSSPropertyMinHeight: 10164 case CSSPropertyMaxHeight: 10165 if (id == CSSValueAuto || id == CSSValueInternalExtendToZoom) 10166 validPrimitive = true; 10167 else 10168 validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg)); 10169 break; 10170 case CSSPropertyWidth: // shorthand 10171 return parseViewportShorthand(propId, CSSPropertyMinWidth, CSSPropertyMaxWidth, important); 10172 case CSSPropertyHeight: 10173 return parseViewportShorthand(propId, CSSPropertyMinHeight, CSSPropertyMaxHeight, important); 10174 case CSSPropertyMinZoom: // auto | <number> | <percentage> 10175 case CSSPropertyMaxZoom: 10176 case CSSPropertyZoom: 10177 if (id == CSSValueAuto) 10178 validPrimitive = true; 10179 else 10180 validPrimitive = (!id && validUnit(value, FNumber | FPercent | FNonNeg)); 10181 break; 10182 case CSSPropertyUserZoom: // zoom | fixed 10183 if (id == CSSValueZoom || id == CSSValueFixed) 10184 validPrimitive = true; 10185 break; 10186 case CSSPropertyOrientation: // auto | portrait | landscape 10187 if (id == CSSValueAuto || id == CSSValuePortrait || id == CSSValueLandscape) 10188 validPrimitive = true; 10189 default: 10190 break; 10191 } 10192 10193 RefPtr<CSSValue> parsedValue; 10194 if (validPrimitive) { 10195 parsedValue = parseValidPrimitive(id, value); 10196 m_valueList->next(); 10197 } 10198 10199 if (parsedValue) { 10200 if (!m_valueList->current() || inShorthand()) { 10201 addProperty(propId, parsedValue.release(), important); 10202 return true; 10203 } 10204 } 10205 10206 return false; 10207 } 10208 10209 bool CSSParser::parseViewportShorthand(CSSPropertyID propId, CSSPropertyID first, CSSPropertyID second, bool important) 10210 { 10211 ASSERT(RuntimeEnabledFeatures::cssViewportEnabled() || isUASheetBehavior(m_context.mode())); 10212 unsigned numValues = m_valueList->size(); 10213 10214 if (numValues > 2) 10215 return false; 10216 10217 ShorthandScope scope(this, propId); 10218 10219 if (!parseViewportProperty(first, important)) 10220 return false; 10221 10222 // If just one value is supplied, the second value 10223 // is implicitly initialized with the first value. 10224 if (numValues == 1) 10225 m_valueList->previous(); 10226 10227 return parseViewportProperty(second, important); 10228 } 10229 10230 template <typename CharacterType> 10231 static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length) 10232 { 10233 char buffer[maxCSSPropertyNameLength + 1]; // 1 for null character 10234 10235 for (unsigned i = 0; i != length; ++i) { 10236 CharacterType c = propertyName[i]; 10237 if (c == 0 || c >= 0x7F) 10238 return CSSPropertyInvalid; // illegal character 10239 buffer[i] = toASCIILower(c); 10240 } 10241 buffer[length] = '\0'; 10242 10243 const char* name = buffer; 10244 const Property* hashTableEntry = findProperty(name, length); 10245 return hashTableEntry ? static_cast<CSSPropertyID>(hashTableEntry->id) : CSSPropertyInvalid; 10246 } 10247 10248 CSSPropertyID cssPropertyID(const String& string) 10249 { 10250 unsigned length = string.length(); 10251 10252 if (!length) 10253 return CSSPropertyInvalid; 10254 if (length > maxCSSPropertyNameLength) 10255 return CSSPropertyInvalid; 10256 10257 return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length); 10258 } 10259 10260 CSSPropertyID cssPropertyID(const CSSParserString& string) 10261 { 10262 unsigned length = string.length(); 10263 10264 if (!length) 10265 return CSSPropertyInvalid; 10266 if (length > maxCSSPropertyNameLength) 10267 return CSSPropertyInvalid; 10268 10269 return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length); 10270 } 10271 10272 template <typename CharacterType> 10273 static CSSValueID cssValueKeywordID(const CharacterType* valueKeyword, unsigned length) 10274 { 10275 char buffer[maxCSSValueKeywordLength + 1]; // 1 for null character 10276 10277 for (unsigned i = 0; i != length; ++i) { 10278 CharacterType c = valueKeyword[i]; 10279 if (c == 0 || c >= 0x7F) 10280 return CSSValueInvalid; // illegal character 10281 buffer[i] = WTF::toASCIILower(c); 10282 } 10283 buffer[length] = '\0'; 10284 10285 const Value* hashTableEntry = findValue(buffer, length); 10286 return hashTableEntry ? static_cast<CSSValueID>(hashTableEntry->id) : CSSValueInvalid; 10287 } 10288 10289 CSSValueID cssValueKeywordID(const CSSParserString& string) 10290 { 10291 unsigned length = string.length(); 10292 if (!length) 10293 return CSSValueInvalid; 10294 if (length > maxCSSValueKeywordLength) 10295 return CSSValueInvalid; 10296 10297 return string.is8Bit() ? cssValueKeywordID(string.characters8(), length) : cssValueKeywordID(string.characters16(), length); 10298 } 10299 10300 template <typename CharacterType> 10301 static inline bool isCSSTokenizerIdentifier(const CharacterType* characters, unsigned length) 10302 { 10303 const CharacterType* end = characters + length; 10304 10305 // -? 10306 if (characters != end && characters[0] == '-') 10307 ++characters; 10308 10309 // {nmstart} 10310 if (characters == end || !(characters[0] == '_' || characters[0] >= 128 || isASCIIAlpha(characters[0]))) 10311 return false; 10312 ++characters; 10313 10314 // {nmchar}* 10315 for (; characters != end; ++characters) { 10316 if (!(characters[0] == '_' || characters[0] == '-' || characters[0] >= 128 || isASCIIAlphanumeric(characters[0]))) 10317 return false; 10318 } 10319 10320 return true; 10321 } 10322 10323 // "ident" from the CSS tokenizer, minus backslash-escape sequences 10324 static bool isCSSTokenizerIdentifier(const String& string) 10325 { 10326 unsigned length = string.length(); 10327 10328 if (!length) 10329 return false; 10330 10331 if (string.is8Bit()) 10332 return isCSSTokenizerIdentifier(string.characters8(), length); 10333 return isCSSTokenizerIdentifier(string.characters16(), length); 10334 } 10335 10336 template <typename CharacterType> 10337 static inline bool isCSSTokenizerURL(const CharacterType* characters, unsigned length) 10338 { 10339 const CharacterType* end = characters + length; 10340 10341 for (; characters != end; ++characters) { 10342 CharacterType c = characters[0]; 10343 switch (c) { 10344 case '!': 10345 case '#': 10346 case '$': 10347 case '%': 10348 case '&': 10349 break; 10350 default: 10351 if (c < '*') 10352 return false; 10353 if (c <= '~') 10354 break; 10355 if (c < 128) 10356 return false; 10357 } 10358 } 10359 10360 return true; 10361 } 10362 10363 // "url" from the CSS tokenizer, minus backslash-escape sequences 10364 static bool isCSSTokenizerURL(const String& string) 10365 { 10366 unsigned length = string.length(); 10367 10368 if (!length) 10369 return true; 10370 10371 if (string.is8Bit()) 10372 return isCSSTokenizerURL(string.characters8(), length); 10373 return isCSSTokenizerURL(string.characters16(), length); 10374 } 10375 10376 10377 template <typename CharacterType> 10378 static inline String quoteCSSStringInternal(const CharacterType* characters, unsigned length) 10379 { 10380 // For efficiency, we first pre-calculate the length of the quoted string, then we build the actual one. 10381 // Please see below for the actual logic. 10382 unsigned quotedStringSize = 2; // Two quotes surrounding the entire string. 10383 bool afterEscape = false; 10384 for (unsigned i = 0; i < length; ++i) { 10385 CharacterType ch = characters[i]; 10386 if (ch == '\\' || ch == '\'') { 10387 quotedStringSize += 2; 10388 afterEscape = false; 10389 } else if (ch < 0x20 || ch == 0x7F) { 10390 quotedStringSize += 2 + (ch >= 0x10); 10391 afterEscape = true; 10392 } else { 10393 quotedStringSize += 1 + (afterEscape && (isASCIIHexDigit(ch) || ch == ' ')); 10394 afterEscape = false; 10395 } 10396 } 10397 10398 StringBuffer<CharacterType> buffer(quotedStringSize); 10399 unsigned index = 0; 10400 buffer[index++] = '\''; 10401 afterEscape = false; 10402 for (unsigned i = 0; i < length; ++i) { 10403 CharacterType ch = characters[i]; 10404 if (ch == '\\' || ch == '\'') { 10405 buffer[index++] = '\\'; 10406 buffer[index++] = ch; 10407 afterEscape = false; 10408 } else if (ch < 0x20 || ch == 0x7F) { // Control characters. 10409 buffer[index++] = '\\'; 10410 placeByteAsHexCompressIfPossible(ch, buffer, index, Lowercase); 10411 afterEscape = true; 10412 } else { 10413 // Space character may be required to separate backslash-escape sequence and normal characters. 10414 if (afterEscape && (isASCIIHexDigit(ch) || ch == ' ')) 10415 buffer[index++] = ' '; 10416 buffer[index++] = ch; 10417 afterEscape = false; 10418 } 10419 } 10420 buffer[index++] = '\''; 10421 10422 ASSERT(quotedStringSize == index); 10423 return String::adopt(buffer); 10424 } 10425 10426 // We use single quotes for now because markup.cpp uses double quotes. 10427 String quoteCSSString(const String& string) 10428 { 10429 // This function expands each character to at most 3 characters ('\u0010' -> '\' '1' '0') as well as adds 10430 // 2 quote characters (before and after). Make sure the resulting size (3 * length + 2) will not overflow unsigned. 10431 10432 unsigned length = string.length(); 10433 10434 if (!length) 10435 return String("\'\'"); 10436 10437 if (length > std::numeric_limits<unsigned>::max() / 3 - 2) 10438 return emptyString(); 10439 10440 if (string.is8Bit()) 10441 return quoteCSSStringInternal(string.characters8(), length); 10442 return quoteCSSStringInternal(string.characters16(), length); 10443 } 10444 10445 String quoteCSSStringIfNeeded(const String& string) 10446 { 10447 return isCSSTokenizerIdentifier(string) ? string : quoteCSSString(string); 10448 } 10449 10450 String quoteCSSURLIfNeeded(const String& string) 10451 { 10452 return isCSSTokenizerURL(string) ? string : quoteCSSString(string); 10453 } 10454 10455 bool isValidNthToken(const CSSParserString& token) 10456 { 10457 // The tokenizer checks for the construct of an+b. 10458 // However, since the {ident} rule precedes the {nth} rule, some of those 10459 // tokens are identified as string literal. Furthermore we need to accept 10460 // "odd" and "even" which does not match to an+b. 10461 return equalIgnoringCase(token, "odd") || equalIgnoringCase(token, "even") 10462 || equalIgnoringCase(token, "n") || equalIgnoringCase(token, "-n"); 10463 } 10464 10465 } 10466