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