1/* 2 * Copyright (C) 2003 Lars Knoll (knoll@kde.org) 3 * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) 4 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. 5 * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> 6 * Copyright (C) 2008 Eric Seidel <eric@webkit.org> 7 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 8 * Copyright (C) 2012, 2013 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 "CSSParser.h" 29 30#include "CSSAspectRatioValue.h" 31#include "CSSBasicShapes.h" 32#include "CSSBorderImage.h" 33#include "CSSCanvasValue.h" 34#include "CSSCrossfadeValue.h" 35#include "CSSCursorImageValue.h" 36#include "CSSFilterImageValue.h" 37#include "CSSFontFaceRule.h" 38#include "CSSFontFaceSrcValue.h" 39#include "CSSFontFeatureValue.h" 40#include "CSSFontValue.h" 41#include "CSSFunctionValue.h" 42#include "CSSGradientValue.h" 43#include "CSSImageValue.h" 44#include "CSSInheritedValue.h" 45#include "CSSInitialValue.h" 46#include "CSSLineBoxContainValue.h" 47#include "CSSMediaRule.h" 48#include "CSSPageRule.h" 49#include "CSSPrimitiveValue.h" 50#include "CSSPropertySourceData.h" 51#include "CSSReflectValue.h" 52#include "CSSSelector.h" 53#include "CSSShadowValue.h" 54#include "CSSStyleSheet.h" 55#include "CSSTimingFunctionValue.h" 56#include "CSSUnicodeRangeValue.h" 57#include "CSSValueKeywords.h" 58#include "CSSValueList.h" 59#include "CSSValuePool.h" 60#include "Counter.h" 61#include "Document.h" 62#include "FloatConversion.h" 63#include "HTMLParserIdioms.h" 64#include "HashTools.h" 65#include "MediaList.h" 66#include "MediaQueryExp.h" 67#include "Page.h" 68#include "PageConsole.h" 69#include "Pair.h" 70#include "Rect.h" 71#include "RenderTheme.h" 72#include "RuntimeEnabledFeatures.h" 73#include "SVGParserUtilities.h" 74#include "Settings.h" 75#include "StyleProperties.h" 76#include "StylePropertyShorthand.h" 77#include "StyleRule.h" 78#include "StyleRuleImport.h" 79#include "StyleSheetContents.h" 80#include "TextEncoding.h" 81#include "WebKitCSSKeyframeRule.h" 82#include "WebKitCSSKeyframesRule.h" 83#include "WebKitCSSRegionRule.h" 84#include "WebKitCSSTransformValue.h" 85#include <bitset> 86#include <limits.h> 87#include <wtf/HexNumber.h> 88#include <wtf/NeverDestroyed.h> 89#include <wtf/StdLibExtras.h> 90#include <wtf/dtoa.h> 91#include <wtf/text/StringBuffer.h> 92#include <wtf/text/StringBuilder.h> 93#include <wtf/text/StringImpl.h> 94 95#if ENABLE(CSS_GRID_LAYOUT) 96#include "CSSGridLineNamesValue.h" 97#endif 98 99#if ENABLE(CSS_IMAGE_SET) 100#include "CSSImageSetValue.h" 101#endif 102 103#if ENABLE(CSS_FILTERS) 104#include "WebKitCSSFilterValue.h" 105#endif 106 107#if ENABLE(DASHBOARD_SUPPORT) 108#include "DashboardRegion.h" 109#endif 110 111#define YYDEBUG 0 112 113#if YYDEBUG > 0 114extern int cssyydebug; 115#endif 116 117extern int cssyyparse(WebCore::CSSParser*); 118 119using namespace WTF; 120 121namespace { 122 123enum PropertyType { 124 PropertyExplicit, 125 PropertyImplicit 126}; 127 128class ImplicitScope { 129 WTF_MAKE_NONCOPYABLE(ImplicitScope); 130public: 131 ImplicitScope(WebCore::CSSParser* parser, PropertyType propertyType) 132 : m_parser(parser) 133 { 134 m_parser->m_implicitShorthand = propertyType == PropertyImplicit; 135 } 136 137 ~ImplicitScope() 138 { 139 m_parser->m_implicitShorthand = false; 140 } 141 142private: 143 WebCore::CSSParser* m_parser; 144}; 145 146} // namespace 147 148namespace WebCore { 149 150static const unsigned INVALID_NUM_PARSED_PROPERTIES = UINT_MAX; 151static const double MAX_SCALE = 1000000; 152#if ENABLE(CSS_GRID_LAYOUT) 153static const unsigned MAX_GRID_TRACK_REPETITIONS = 10000; 154#endif 155 156template <unsigned N> 157static bool equal(const CSSParserString& a, const char (&b)[N]) 158{ 159 unsigned length = N - 1; // Ignore the trailing null character 160 if (a.length() != length) 161 return false; 162 163 return a.is8Bit() ? WTF::equal(a.characters8(), reinterpret_cast<const LChar*>(b), length) : WTF::equal(a.characters16(), reinterpret_cast<const LChar*>(b), length); 164} 165 166template <unsigned N> 167static bool equalIgnoringCase(const CSSParserString& a, const char (&b)[N]) 168{ 169 unsigned length = N - 1; // Ignore the trailing null character 170 if (a.length() != length) 171 return false; 172 173 return a.is8Bit() ? WTF::equalIgnoringCase(b, a.characters8(), length) : WTF::equalIgnoringCase(b, a.characters16(), length); 174} 175 176template <unsigned N> 177static bool equalIgnoringCase(CSSParserValue* value, const char (&b)[N]) 178{ 179 ASSERT(value->unit == CSSPrimitiveValue::CSS_IDENT || value->unit == CSSPrimitiveValue::CSS_STRING); 180 return equalIgnoringCase(value->string, b); 181} 182 183static bool hasPrefix(const char* string, unsigned length, const char* prefix) 184{ 185 for (unsigned i = 0; i < length; ++i) { 186 if (!prefix[i]) 187 return true; 188 if (string[i] != prefix[i]) 189 return false; 190 } 191 return false; 192} 193 194static PassRefPtr<CSSPrimitiveValue> createPrimitiveValuePair(PassRefPtr<CSSPrimitiveValue> first, PassRefPtr<CSSPrimitiveValue> second) 195{ 196 return cssValuePool().createValue(Pair::create(first, second)); 197} 198 199class AnimationParseContext { 200public: 201 AnimationParseContext() 202 : m_animationPropertyKeywordAllowed(true) 203 , m_firstAnimationCommitted(false) 204 , m_hasSeenAnimationPropertyKeyword(false) 205 { 206 } 207 208 void commitFirstAnimation() 209 { 210 m_firstAnimationCommitted = true; 211 } 212 213 bool hasCommittedFirstAnimation() const 214 { 215 return m_firstAnimationCommitted; 216 } 217 218 void commitAnimationPropertyKeyword() 219 { 220 m_animationPropertyKeywordAllowed = false; 221 } 222 223 bool animationPropertyKeywordAllowed() const 224 { 225 return m_animationPropertyKeywordAllowed; 226 } 227 228 bool hasSeenAnimationPropertyKeyword() const 229 { 230 return m_hasSeenAnimationPropertyKeyword; 231 } 232 233 void sawAnimationPropertyKeyword() 234 { 235 m_hasSeenAnimationPropertyKeyword = true; 236 } 237 238private: 239 bool m_animationPropertyKeywordAllowed; 240 bool m_firstAnimationCommitted; 241 bool m_hasSeenAnimationPropertyKeyword; 242}; 243 244const CSSParserContext& strictCSSParserContext() 245{ 246 static NeverDestroyed<CSSParserContext> strictContext(CSSStrictMode); 247 return strictContext; 248} 249 250CSSParserContext::CSSParserContext(CSSParserMode mode, const URL& baseURL) 251 : baseURL(baseURL) 252 , mode(mode) 253 , isHTMLDocument(false) 254 , isCSSRegionsEnabled(false) 255 , isCSSCompositingEnabled(false) 256 , needsSiteSpecificQuirks(false) 257 , enforcesCSSMIMETypeInNoQuirksMode(true) 258 , useLegacyBackgroundSizeShorthandBehavior(false) 259{ 260#if PLATFORM(IOS) 261 // FIXME: Force the site specific quirk below to work on iOS. Investigating other site specific quirks 262 // to see if we can enable the preference all together is to be handled by: 263 // <rdar://problem/8493309> Investigate Enabling Site Specific Quirks in MobileSafari and UIWebView 264 needsSiteSpecificQuirks = true; 265#endif 266} 267 268CSSParserContext::CSSParserContext(Document& document, const URL& baseURL, const String& charset) 269 : baseURL(baseURL.isNull() ? document.baseURL() : baseURL) 270 , charset(charset) 271 , mode(document.inQuirksMode() ? CSSQuirksMode : CSSStrictMode) 272 , isHTMLDocument(document.isHTMLDocument()) 273 , isCSSRegionsEnabled(document.cssRegionsEnabled()) 274 , isCSSCompositingEnabled(document.cssCompositingEnabled()) 275 , needsSiteSpecificQuirks(document.settings() ? document.settings()->needsSiteSpecificQuirks() : false) 276 , enforcesCSSMIMETypeInNoQuirksMode(!document.settings() || document.settings()->enforceCSSMIMETypeInNoQuirksMode()) 277 , useLegacyBackgroundSizeShorthandBehavior(document.settings() ? document.settings()->useLegacyBackgroundSizeShorthandBehavior() : false) 278{ 279#if PLATFORM(IOS) 280 // FIXME: Force the site specific quirk below to work on iOS. Investigating other site specific quirks 281 // to see if we can enable the preference all together is to be handled by: 282 // <rdar://problem/8493309> Investigate Enabling Site Specific Quirks in MobileSafari and UIWebView 283 needsSiteSpecificQuirks = true; 284#endif 285} 286 287bool operator==(const CSSParserContext& a, const CSSParserContext& b) 288{ 289 return a.baseURL == b.baseURL 290 && a.charset == b.charset 291 && a.mode == b.mode 292 && a.isHTMLDocument == b.isHTMLDocument 293 && a.isCSSRegionsEnabled == b.isCSSRegionsEnabled 294 && a.isCSSCompositingEnabled == b.isCSSCompositingEnabled 295 && a.needsSiteSpecificQuirks == b.needsSiteSpecificQuirks 296 && a.enforcesCSSMIMETypeInNoQuirksMode == b.enforcesCSSMIMETypeInNoQuirksMode 297 && a.useLegacyBackgroundSizeShorthandBehavior == b.useLegacyBackgroundSizeShorthandBehavior; 298} 299 300CSSParser::CSSParser(const CSSParserContext& context) 301 : m_context(context) 302 , m_important(false) 303 , m_id(CSSPropertyInvalid) 304 , m_styleSheet(0) 305#if ENABLE(CSS3_CONDITIONAL_RULES) 306 , m_supportsCondition(false) 307#endif 308 , m_selectorListForParseSelector(0) 309 , m_numParsedPropertiesBeforeMarginBox(INVALID_NUM_PARSED_PROPERTIES) 310 , m_inParseShorthand(0) 311 , m_currentShorthand(CSSPropertyInvalid) 312 , m_implicitShorthand(false) 313 , m_hasFontFaceOnlyValues(false) 314 , m_hadSyntacticallyValidCSSRule(false) 315 , m_logErrors(false) 316 , m_ignoreErrorsInDeclaration(false) 317 , m_defaultNamespace(starAtom) 318 , m_parsedTextPrefixLength(0) 319 , m_propertyRange(UINT_MAX, UINT_MAX) 320 , m_ruleSourceDataResult(0) 321 , m_parsingMode(NormalMode) 322 , m_is8BitSource(false) 323 , m_currentCharacter8(0) 324 , m_currentCharacter16(0) 325 , m_length(0) 326 , m_token(0) 327 , m_lineNumber(0) 328 , m_tokenStartLineNumber(0) 329 , m_lastSelectorLineNumber(0) 330 , m_allowImportRules(true) 331 , m_allowNamespaceDeclarations(true) 332#if ENABLE(CSS_DEVICE_ADAPTATION) 333 , m_inViewport(false) 334#endif 335{ 336#if YYDEBUG > 0 337 cssyydebug = 1; 338#endif 339 m_tokenStart.ptr8 = 0; 340} 341 342CSSParser::~CSSParser() 343{ 344 clearProperties(); 345} 346 347template <typename CharacterType> 348ALWAYS_INLINE static void makeLower(const CharacterType* input, CharacterType* output, unsigned length) 349{ 350 // FIXME: If we need Unicode lowercasing here, then we probably want the real kind 351 // that can potentially change the length of the string rather than the character 352 // by character kind. If we don't need Unicode lowercasing, it would be good to 353 // simplify this function. 354 355 if (charactersAreAllASCII(input, length)) { 356 // Fast case for all-ASCII. 357 for (unsigned i = 0; i < length; i++) 358 output[i] = toASCIILower(input[i]); 359 } else { 360 for (unsigned i = 0; i < length; i++) { 361 ASSERT(u_tolower(input[i]) <= 0xFFFF); 362 output[i] = u_tolower(input[i]); 363 } 364 } 365} 366 367void CSSParserString::lower() 368{ 369 if (is8Bit()) { 370 makeLower(characters8(), characters8(), length()); 371 return; 372 } 373 374 makeLower(characters16(), characters16(), length()); 375} 376 377void CSSParser::setupParser(const char* prefix, unsigned prefixLength, const String& string, const char* suffix, unsigned suffixLength) 378{ 379 m_parsedTextPrefixLength = prefixLength; 380 unsigned stringLength = string.length(); 381 unsigned length = stringLength + m_parsedTextPrefixLength + suffixLength + 1; 382 m_length = length; 383 384 if (!stringLength || string.is8Bit()) { 385 m_dataStart8 = std::make_unique<LChar[]>(length); 386 for (unsigned i = 0; i < m_parsedTextPrefixLength; i++) 387 m_dataStart8[i] = prefix[i]; 388 389 if (stringLength) 390 memcpy(m_dataStart8.get() + m_parsedTextPrefixLength, string.characters8(), stringLength * sizeof(LChar)); 391 392 unsigned start = m_parsedTextPrefixLength + stringLength; 393 unsigned end = start + suffixLength; 394 for (unsigned i = start; i < end; i++) 395 m_dataStart8[i] = suffix[i - start]; 396 397 m_dataStart8[length - 1] = 0; 398 399 m_is8BitSource = true; 400 m_currentCharacter8 = m_dataStart8.get(); 401 m_currentCharacter16 = 0; 402 setTokenStart<LChar>(m_currentCharacter8); 403 m_lexFunc = &CSSParser::realLex<LChar>; 404 return; 405 } 406 407 m_dataStart16 = std::make_unique<UChar[]>(length); 408 for (unsigned i = 0; i < m_parsedTextPrefixLength; i++) 409 m_dataStart16[i] = prefix[i]; 410 411 ASSERT(stringLength); 412 memcpy(m_dataStart16.get() + m_parsedTextPrefixLength, string.characters16(), stringLength * sizeof(UChar)); 413 414 unsigned start = m_parsedTextPrefixLength + stringLength; 415 unsigned end = start + suffixLength; 416 for (unsigned i = start; i < end; i++) 417 m_dataStart16[i] = suffix[i - start]; 418 419 m_dataStart16[length - 1] = 0; 420 421 m_is8BitSource = false; 422 m_currentCharacter8 = 0; 423 m_currentCharacter16 = m_dataStart16.get(); 424 setTokenStart<UChar>(m_currentCharacter16); 425 m_lexFunc = &CSSParser::realLex<UChar>; 426} 427 428void CSSParser::parseSheet(StyleSheetContents* sheet, const String& string, int startLineNumber, RuleSourceDataList* ruleSourceDataResult, bool logErrors) 429{ 430 setStyleSheet(sheet); 431 m_defaultNamespace = starAtom; // Reset the default namespace. 432 if (ruleSourceDataResult) 433 m_currentRuleDataStack = std::make_unique<RuleSourceDataList>(); 434 m_ruleSourceDataResult = ruleSourceDataResult; 435 436 m_logErrors = logErrors && sheet->singleOwnerDocument() && !sheet->baseURL().isEmpty() && sheet->singleOwnerDocument()->page(); 437 m_ignoreErrorsInDeclaration = false; 438 m_lineNumber = startLineNumber; 439 setupParser("", string, ""); 440 cssyyparse(this); 441 sheet->shrinkToFit(); 442 m_currentRuleDataStack.reset(); 443 m_ruleSourceDataResult = 0; 444 m_rule = 0; 445 m_ignoreErrorsInDeclaration = false; 446 m_logErrors = false; 447} 448 449PassRefPtr<StyleRuleBase> CSSParser::parseRule(StyleSheetContents* sheet, const String& string) 450{ 451 setStyleSheet(sheet); 452 m_allowNamespaceDeclarations = false; 453 setupParser("@-webkit-rule{", string, "} "); 454 cssyyparse(this); 455 return m_rule.release(); 456} 457 458PassRefPtr<StyleKeyframe> CSSParser::parseKeyframeRule(StyleSheetContents* sheet, const String& string) 459{ 460 setStyleSheet(sheet); 461 setupParser("@-webkit-keyframe-rule{ ", string, "} "); 462 cssyyparse(this); 463 return m_keyframe.release(); 464} 465 466#if ENABLE(CSS3_CONDITIONAL_RULES) 467bool CSSParser::parseSupportsCondition(const String& string) 468{ 469 m_supportsCondition = false; 470 setupParser("@-webkit-supports-condition{ ", string, "} "); 471 cssyyparse(this); 472 return m_supportsCondition; 473} 474#endif 475 476static inline bool isColorPropertyID(CSSPropertyID propertyId) 477{ 478 switch (propertyId) { 479 case CSSPropertyColor: 480 case CSSPropertyBackgroundColor: 481 case CSSPropertyBorderBottomColor: 482 case CSSPropertyBorderLeftColor: 483 case CSSPropertyBorderRightColor: 484 case CSSPropertyBorderTopColor: 485 case CSSPropertyOutlineColor: 486 case CSSPropertyTextLineThroughColor: 487 case CSSPropertyTextOverlineColor: 488 case CSSPropertyTextUnderlineColor: 489 case CSSPropertyWebkitBorderAfterColor: 490 case CSSPropertyWebkitBorderBeforeColor: 491 case CSSPropertyWebkitBorderEndColor: 492 case CSSPropertyWebkitBorderStartColor: 493 case CSSPropertyWebkitColumnRuleColor: 494 case CSSPropertyWebkitTextDecorationColor: 495 case CSSPropertyWebkitTextEmphasisColor: 496 case CSSPropertyWebkitTextFillColor: 497 case CSSPropertyWebkitTextStrokeColor: 498 return true; 499 default: 500 return false; 501 } 502} 503 504static bool validPrimitiveValueColor(CSSValueID valueID, bool strict = false) 505{ 506 return (valueID == CSSValueWebkitText || valueID == CSSValueCurrentcolor || valueID == CSSValueMenu 507 || (valueID >= CSSValueAlpha && valueID <= CSSValueWindowtext) 508 || (valueID >= CSSValueWebkitFocusRingColor && valueID < CSSValueWebkitText && !strict)); 509} 510 511static bool parseColorValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String& string, bool important, CSSParserMode cssParserMode) 512{ 513 ASSERT(!string.isEmpty()); 514 bool strict = isStrictParserMode(cssParserMode); 515 if (!isColorPropertyID(propertyId)) 516 return false; 517 CSSParserString cssString; 518 cssString.init(string); 519 CSSValueID valueID = cssValueKeywordID(cssString); 520 if (validPrimitiveValueColor(valueID, strict)) { 521 RefPtr<CSSValue> value = cssValuePool().createIdentifierValue(valueID); 522 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 523 return true; 524 } 525 RGBA32 color; 526 if (!CSSParser::fastParseColor(color, string, strict && string[0] != '#')) 527 return false; 528 RefPtr<CSSValue> value = cssValuePool().createColorValue(color); 529 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 530 return true; 531} 532 533static inline bool isSimpleLengthPropertyID(CSSPropertyID propertyId, bool& acceptsNegativeNumbers) 534{ 535 switch (propertyId) { 536 case CSSPropertyFontSize: 537 case CSSPropertyHeight: 538 case CSSPropertyWidth: 539 case CSSPropertyMinHeight: 540 case CSSPropertyMinWidth: 541 case CSSPropertyPaddingBottom: 542 case CSSPropertyPaddingLeft: 543 case CSSPropertyPaddingRight: 544 case CSSPropertyPaddingTop: 545 case CSSPropertyWebkitLogicalWidth: 546 case CSSPropertyWebkitLogicalHeight: 547 case CSSPropertyWebkitMinLogicalWidth: 548 case CSSPropertyWebkitMinLogicalHeight: 549 case CSSPropertyWebkitPaddingAfter: 550 case CSSPropertyWebkitPaddingBefore: 551 case CSSPropertyWebkitPaddingEnd: 552 case CSSPropertyWebkitPaddingStart: 553 acceptsNegativeNumbers = false; 554 return true; 555#if ENABLE(CSS_SHAPES) 556 case CSSPropertyWebkitShapeMargin: 557 acceptsNegativeNumbers = false; 558 return RuntimeEnabledFeatures::sharedFeatures().cssShapesEnabled(); 559#endif 560 case CSSPropertyBottom: 561 case CSSPropertyLeft: 562 case CSSPropertyMarginBottom: 563 case CSSPropertyMarginLeft: 564 case CSSPropertyMarginRight: 565 case CSSPropertyMarginTop: 566 case CSSPropertyRight: 567 case CSSPropertyTop: 568 case CSSPropertyWebkitMarginAfter: 569 case CSSPropertyWebkitMarginBefore: 570 case CSSPropertyWebkitMarginEnd: 571 case CSSPropertyWebkitMarginStart: 572 acceptsNegativeNumbers = true; 573 return true; 574 default: 575 return false; 576 } 577} 578 579template <typename CharacterType> 580static inline bool parseSimpleLength(const CharacterType* characters, unsigned& length, CSSPrimitiveValue::UnitTypes& unit, double& number) 581{ 582 if (length > 2 && (characters[length - 2] | 0x20) == 'p' && (characters[length - 1] | 0x20) == 'x') { 583 length -= 2; 584 unit = CSSPrimitiveValue::CSS_PX; 585 } else if (length > 1 && characters[length - 1] == '%') { 586 length -= 1; 587 unit = CSSPrimitiveValue::CSS_PERCENTAGE; 588 } 589 590 // We rely on charactersToDouble for validation as well. The function 591 // will set "ok" to "false" if the entire passed-in character range does 592 // not represent a double. 593 bool ok; 594 number = charactersToDouble(characters, length, &ok); 595 return ok; 596} 597 598static bool parseSimpleLengthValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String& string, bool important, CSSParserMode cssParserMode) 599{ 600 ASSERT(!string.isEmpty()); 601 bool acceptsNegativeNumbers; 602 if (!isSimpleLengthPropertyID(propertyId, acceptsNegativeNumbers)) 603 return false; 604 605 unsigned length = string.length(); 606 double number; 607 CSSPrimitiveValue::UnitTypes unit = CSSPrimitiveValue::CSS_NUMBER; 608 609 if (string.is8Bit()) { 610 if (!parseSimpleLength(string.characters8(), length, unit, number)) 611 return false; 612 } else { 613 if (!parseSimpleLength(string.characters16(), length, unit, number)) 614 return false; 615 } 616 617 if (unit == CSSPrimitiveValue::CSS_NUMBER) { 618 if (number && isStrictParserMode(cssParserMode)) 619 return false; 620 unit = CSSPrimitiveValue::CSS_PX; 621 } 622 if (number < 0 && !acceptsNegativeNumbers) 623 return false; 624 625 RefPtr<CSSValue> value = cssValuePool().createValue(number, unit); 626 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 627 return true; 628} 629 630static inline bool isValidKeywordPropertyAndValue(CSSPropertyID propertyId, int valueID, const CSSParserContext& parserContext) 631{ 632 if (!valueID) 633 return false; 634 635 switch (propertyId) { 636 case CSSPropertyBorderCollapse: // collapse | separate | inherit 637 if (valueID == CSSValueCollapse || valueID == CSSValueSeparate) 638 return true; 639 break; 640 case CSSPropertyBorderTopStyle: // <border-style> | inherit 641 case CSSPropertyBorderRightStyle: // Defined as: none | hidden | dotted | dashed | 642 case CSSPropertyBorderBottomStyle: // solid | double | groove | ridge | inset | outset 643 case CSSPropertyBorderLeftStyle: 644 case CSSPropertyWebkitBorderAfterStyle: 645 case CSSPropertyWebkitBorderBeforeStyle: 646 case CSSPropertyWebkitBorderEndStyle: 647 case CSSPropertyWebkitBorderStartStyle: 648 case CSSPropertyWebkitColumnRuleStyle: 649 if (valueID >= CSSValueNone && valueID <= CSSValueDouble) 650 return true; 651 break; 652 case CSSPropertyBoxSizing: 653 if (valueID == CSSValueBorderBox || valueID == CSSValueContentBox) 654 return true; 655 break; 656 case CSSPropertyCaptionSide: // top | bottom | left | right | inherit 657 if (valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueTop || valueID == CSSValueBottom) 658 return true; 659 break; 660 case CSSPropertyClear: // none | left | right | both | inherit 661 if (valueID == CSSValueNone || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueBoth) 662 return true; 663 break; 664 case CSSPropertyDirection: // ltr | rtl | inherit 665 if (valueID == CSSValueLtr || valueID == CSSValueRtl) 666 return true; 667 break; 668 case CSSPropertyDisplay: 669 // inline | block | list-item | inline-block | table | 670 // inline-table | table-row-group | table-header-group | table-footer-group | table-row | 671 // table-column-group | table-column | table-cell | table-caption | -webkit-box | -webkit-inline-box | none | inherit 672 // -webkit-flex | -webkit-inline-flex | -webkit-grid | -webkit-inline-grid 673 if ((valueID >= CSSValueInline && valueID <= CSSValueWebkitInlineFlex) || valueID == CSSValueNone) 674 return true; 675#if ENABLE(CSS_GRID_LAYOUT) 676 if (valueID == CSSValueWebkitGrid || valueID == CSSValueWebkitInlineGrid) 677 return true; 678#endif 679 break; 680 681 case CSSPropertyEmptyCells: // show | hide | inherit 682 if (valueID == CSSValueShow || valueID == CSSValueHide) 683 return true; 684 break; 685 case CSSPropertyFloat: // left | right | none | center (for buggy CSS, maps to none) 686 if (valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueNone || valueID == CSSValueCenter) 687 return true; 688 break; 689 case CSSPropertyFontStyle: // normal | italic | oblique | inherit 690 if (valueID == CSSValueNormal || valueID == CSSValueItalic || valueID == CSSValueOblique) 691 return true; 692 break; 693 case CSSPropertyImageRendering: // auto | optimizeSpeed | optimizeQuality | -webkit-crisp-edges | -webkit-optimize-contrast 694 if (valueID == CSSValueAuto || valueID == CSSValueOptimizespeed || valueID == CSSValueOptimizequality 695 || valueID == CSSValueWebkitCrispEdges || valueID == CSSValueWebkitOptimizeContrast) 696 return true; 697 break; 698 case CSSPropertyListStylePosition: // inside | outside | inherit 699 if (valueID == CSSValueInside || valueID == CSSValueOutside) 700 return true; 701 break; 702 case CSSPropertyListStyleType: 703 // See section CSS_PROP_LIST_STYLE_TYPE of file CSSValueKeywords.in 704 // for the list of supported list-style-types. 705 if ((valueID >= CSSValueDisc && valueID <= CSSValueKatakanaIroha) || valueID == CSSValueNone) 706 return true; 707 break; 708 case CSSPropertyObjectFit: 709 if (valueID == CSSValueFill || valueID == CSSValueContain || valueID == CSSValueCover || valueID == CSSValueNone || valueID == CSSValueScaleDown) 710 return true; 711 break; 712 case CSSPropertyOutlineStyle: // (<border-style> except hidden) | auto | inherit 713 if (valueID == CSSValueAuto || valueID == CSSValueNone || (valueID >= CSSValueInset && valueID <= CSSValueDouble)) 714 return true; 715 break; 716 case CSSPropertyOverflowWrap: // normal | break-word 717 case CSSPropertyWordWrap: 718 if (valueID == CSSValueNormal || valueID == CSSValueBreakWord) 719 return true; 720 break; 721 case CSSPropertyOverflowX: // visible | hidden | scroll | auto | marquee | overlay | inherit 722 if (valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay || valueID == CSSValueWebkitMarquee) 723 return true; 724 break; 725 case CSSPropertyOverflowY: // visible | hidden | scroll | auto | marquee | overlay | inherit | -webkit-paged-x | -webkit-paged-y 726 if (valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay || valueID == CSSValueWebkitMarquee || valueID == CSSValueWebkitPagedX || valueID == CSSValueWebkitPagedY) 727 return true; 728 break; 729 case CSSPropertyPageBreakAfter: // auto | always | avoid | left | right | inherit 730 case CSSPropertyPageBreakBefore: 731 case CSSPropertyWebkitColumnBreakAfter: 732 case CSSPropertyWebkitColumnBreakBefore: 733 if (valueID == CSSValueAuto || valueID == CSSValueAlways || valueID == CSSValueAvoid || valueID == CSSValueLeft || valueID == CSSValueRight) 734 return true; 735 break; 736 case CSSPropertyPageBreakInside: // avoid | auto | inherit 737 case CSSPropertyWebkitColumnBreakInside: 738 if (valueID == CSSValueAuto || valueID == CSSValueAvoid) 739 return true; 740 break; 741 case CSSPropertyPointerEvents: 742 // none | visiblePainted | visibleFill | visibleStroke | visible | 743 // painted | fill | stroke | auto | all | inherit 744 if (valueID == CSSValueVisible || valueID == CSSValueNone || valueID == CSSValueAll || valueID == CSSValueAuto || (valueID >= CSSValueVisiblepainted && valueID <= CSSValueStroke)) 745 return true; 746 break; 747 case CSSPropertyPosition: // static | relative | absolute | fixed | sticky | inherit 748 if (valueID == CSSValueStatic || valueID == CSSValueRelative || valueID == CSSValueAbsolute || valueID == CSSValueFixed || valueID == CSSValueWebkitSticky) 749 return true; 750 break; 751 case CSSPropertyResize: // none | both | horizontal | vertical | auto 752 if (valueID == CSSValueNone || valueID == CSSValueBoth || valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueAuto) 753 return true; 754 break; 755 case CSSPropertySpeak: // none | normal | spell-out | digits | literal-punctuation | no-punctuation | inherit 756 if (valueID == CSSValueNone || valueID == CSSValueNormal || valueID == CSSValueSpellOut || valueID == CSSValueDigits || valueID == CSSValueLiteralPunctuation || valueID == CSSValueNoPunctuation) 757 return true; 758 break; 759 case CSSPropertyTableLayout: // auto | fixed | inherit 760 if (valueID == CSSValueAuto || valueID == CSSValueFixed) 761 return true; 762 break; 763 case CSSPropertyTextLineThroughMode: 764 case CSSPropertyTextOverlineMode: 765 case CSSPropertyTextUnderlineMode: 766 if (valueID == CSSValueContinuous || valueID == CSSValueSkipWhiteSpace) 767 return true; 768 break; 769 case CSSPropertyTextLineThroughStyle: 770 case CSSPropertyTextOverlineStyle: 771 case CSSPropertyTextUnderlineStyle: 772 if (valueID == CSSValueNone || valueID == CSSValueSolid || valueID == CSSValueDouble || valueID == CSSValueDashed || valueID == CSSValueDotDash || valueID == CSSValueDotDotDash || valueID == CSSValueWave) 773 return true; 774 break; 775 case CSSPropertyTextOverflow: // clip | ellipsis 776 if (valueID == CSSValueClip || valueID == CSSValueEllipsis) 777 return true; 778 break; 779 case CSSPropertyTextRendering: // auto | optimizeSpeed | optimizeLegibility | geometricPrecision 780 if (valueID == CSSValueAuto || valueID == CSSValueOptimizespeed || valueID == CSSValueOptimizelegibility || valueID == CSSValueGeometricprecision) 781 return true; 782 break; 783 case CSSPropertyTextTransform: // capitalize | uppercase | lowercase | none | inherit 784 if ((valueID >= CSSValueCapitalize && valueID <= CSSValueLowercase) || valueID == CSSValueNone) 785 return true; 786 break; 787 case CSSPropertyVisibility: // visible | hidden | collapse | inherit 788 if (valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueCollapse) 789 return true; 790 break; 791 case CSSPropertyWebkitAppearance: 792 if ((valueID >= CSSValueCheckbox && valueID <= CSSValueTextarea) || valueID == CSSValueNone) 793 return true; 794 break; 795 case CSSPropertyWebkitBackfaceVisibility: 796 if (valueID == CSSValueVisible || valueID == CSSValueHidden) 797 return true; 798 break; 799#if ENABLE(CSS_COMPOSITING) 800 case CSSPropertyMixBlendMode: 801 if (parserContext.isCSSCompositingEnabled && (valueID == CSSValueNormal || valueID == CSSValueMultiply || valueID == CSSValueScreen 802 || valueID == CSSValueOverlay || valueID == CSSValueDarken || valueID == CSSValueLighten || valueID == CSSValueColorDodge 803 || valueID == CSSValueColorBurn || valueID == CSSValueHardLight || valueID == CSSValueSoftLight || valueID == CSSValueDifference 804 || valueID == CSSValueExclusion)) 805 return true; 806 break; 807 case CSSPropertyIsolation: 808 if (parserContext.isCSSCompositingEnabled && (valueID == CSSValueAuto || valueID == CSSValueIsolate)) 809 return true; 810 break; 811#endif 812 case CSSPropertyWebkitBorderFit: 813 if (valueID == CSSValueBorder || valueID == CSSValueLines) 814 return true; 815 break; 816 case CSSPropertyWebkitBoxAlign: 817 if (valueID == CSSValueStretch || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline) 818 return true; 819 break; 820#if ENABLE(CSS_BOX_DECORATION_BREAK) 821 case CSSPropertyWebkitBoxDecorationBreak: 822 if (valueID == CSSValueClone || valueID == CSSValueSlice) 823 return true; 824 break; 825#endif 826 case CSSPropertyWebkitBoxDirection: 827 if (valueID == CSSValueNormal || valueID == CSSValueReverse) 828 return true; 829 break; 830 case CSSPropertyWebkitBoxLines: 831 if (valueID == CSSValueSingle || valueID == CSSValueMultiple) 832 return true; 833 break; 834 case CSSPropertyWebkitBoxOrient: 835 if (valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueInlineAxis || valueID == CSSValueBlockAxis) 836 return true; 837 break; 838 case CSSPropertyWebkitBoxPack: 839 if (valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueJustify) 840 return true; 841 break; 842 case CSSPropertyWebkitColorCorrection: 843 if (valueID == CSSValueSrgb || valueID == CSSValueDefault) 844 return true; 845 break; 846 case CSSPropertyWebkitColumnFill: 847 if (valueID == CSSValueAuto || valueID == CSSValueBalance) 848 return true; 849 break; 850 case CSSPropertyWebkitAlignContent: 851 // FIXME: Per CSS alignment, this property should accept an optional <overflow-position>. We should share this parsing code with 'justify-self'. 852 if (valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueSpaceBetween || valueID == CSSValueSpaceAround || valueID == CSSValueStretch) 853 return true; 854 break; 855 case CSSPropertyWebkitAlignItems: 856 // FIXME: Per CSS alignment, this property should accept the same arguments as 'justify-self' so we should share its parsing code. 857 if (valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch) 858 return true; 859 break; 860 case CSSPropertyWebkitAlignSelf: 861 // FIXME: Per CSS alignment, this property should accept the same arguments as 'justify-self' so we should share its parsing code. 862 if (valueID == CSSValueAuto || valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch) 863 return true; 864 break; 865 case CSSPropertyWebkitFlexDirection: 866 if (valueID == CSSValueRow || valueID == CSSValueRowReverse || valueID == CSSValueColumn || valueID == CSSValueColumnReverse) 867 return true; 868 break; 869 case CSSPropertyWebkitFlexWrap: 870 if (valueID == CSSValueNowrap || valueID == CSSValueWrap || valueID == CSSValueWrapReverse) 871 return true; 872 break; 873 case CSSPropertyWebkitJustifyContent: 874 // FIXME: Per CSS alignment, this property should accept an optional <overflow-position>. We should share this parsing code with 'justify-self'. 875 if (valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueSpaceBetween || valueID == CSSValueSpaceAround) 876 return true; 877 break; 878 case CSSPropertyWebkitJustifySelf: 879 if (valueID == CSSValueAuto || valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch) 880 return true; 881 break; 882 case CSSPropertyWebkitFontKerning: 883 if (valueID == CSSValueAuto || valueID == CSSValueNormal || valueID == CSSValueNone) 884 return true; 885 break; 886 case CSSPropertyWebkitFontSmoothing: 887 if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueAntialiased || valueID == CSSValueSubpixelAntialiased) 888 return true; 889 break; 890 case CSSPropertyWebkitHyphens: 891 if (valueID == CSSValueNone || valueID == CSSValueManual || valueID == CSSValueAuto) 892 return true; 893 break; 894 case CSSPropertyWebkitLineAlign: 895 if (valueID == CSSValueNone || valueID == CSSValueEdges) 896 return true; 897 break; 898 case CSSPropertyWebkitLineBreak: // auto | loose | normal | strict | after-white-space 899 if (valueID == CSSValueAuto || valueID == CSSValueLoose || valueID == CSSValueNormal || valueID == CSSValueStrict || valueID == CSSValueAfterWhiteSpace) 900 return true; 901 break; 902 case CSSPropertyWebkitLineSnap: 903 if (valueID == CSSValueNone || valueID == CSSValueBaseline || valueID == CSSValueContain) 904 return true; 905 break; 906 case CSSPropertyWebkitMarginAfterCollapse: 907 case CSSPropertyWebkitMarginBeforeCollapse: 908 case CSSPropertyWebkitMarginBottomCollapse: 909 case CSSPropertyWebkitMarginTopCollapse: 910 if (valueID == CSSValueCollapse || valueID == CSSValueSeparate || valueID == CSSValueDiscard) 911 return true; 912 break; 913 case CSSPropertyWebkitMarqueeDirection: 914 if (valueID == CSSValueForwards || valueID == CSSValueBackwards || valueID == CSSValueAhead || valueID == CSSValueReverse || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueDown 915 || valueID == CSSValueUp || valueID == CSSValueAuto) 916 return true; 917 break; 918 case CSSPropertyWebkitMarqueeStyle: 919 if (valueID == CSSValueNone || valueID == CSSValueSlide || valueID == CSSValueScroll || valueID == CSSValueAlternate) 920 return true; 921 break; 922 case CSSPropertyWebkitNbspMode: // normal | space 923 if (valueID == CSSValueNormal || valueID == CSSValueSpace) 924 return true; 925 break; 926#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) 927 case CSSPropertyWebkitOverflowScrolling: 928 if (valueID == CSSValueAuto || valueID == CSSValueTouch) 929 return true; 930 break; 931#endif 932 case CSSPropertyWebkitPrintColorAdjust: 933 if (valueID == CSSValueExact || valueID == CSSValueEconomy) 934 return true; 935 break; 936#if ENABLE(CSS_REGIONS) 937 case CSSPropertyWebkitRegionBreakAfter: 938 case CSSPropertyWebkitRegionBreakBefore: 939 if (parserContext.isCSSRegionsEnabled && (valueID == CSSValueAuto || valueID == CSSValueAlways || valueID == CSSValueAvoid || valueID == CSSValueLeft || valueID == CSSValueRight)) 940 return true; 941 break; 942 case CSSPropertyWebkitRegionBreakInside: 943 if (parserContext.isCSSRegionsEnabled && (valueID == CSSValueAuto || valueID == CSSValueAvoid)) 944 return true; 945 break; 946 case CSSPropertyWebkitRegionFragment: 947 if (parserContext.isCSSRegionsEnabled && (valueID == CSSValueAuto || valueID == CSSValueBreak)) 948 return true; 949 break; 950#endif 951 case CSSPropertyWebkitRtlOrdering: 952 if (valueID == CSSValueLogical || valueID == CSSValueVisual) 953 return true; 954 break; 955 956 case CSSPropertyWebkitRubyPosition: 957 if (valueID == CSSValueBefore || valueID == CSSValueAfter) 958 return true; 959 break; 960 961#if ENABLE(CSS3_TEXT) 962 case CSSPropertyWebkitTextAlignLast: 963 // auto | start | end | left | right | center | justify 964 if ((valueID >= CSSValueLeft && valueID <= CSSValueJustify) || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueAuto) 965 return true; 966 break; 967#endif // CSS3_TEXT 968 case CSSPropertyWebkitTextCombine: 969 if (valueID == CSSValueNone || valueID == CSSValueHorizontal) 970 return true; 971 break; 972#if ENABLE(CSS3_TEXT) 973 case CSSPropertyWebkitTextJustify: 974 // auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida 975 if ((valueID >= CSSValueInterWord && valueID <= CSSValueKashida) || valueID == CSSValueAuto || valueID == CSSValueNone) 976 return true; 977 break; 978#endif // CSS3_TEXT 979 case CSSPropertyWebkitTextSecurity: 980 // disc | circle | square | none | inherit 981 if (valueID == CSSValueDisc || valueID == CSSValueCircle || valueID == CSSValueSquare || valueID == CSSValueNone) 982 return true; 983 break; 984#if ENABLE(IOS_TEXT_AUTOSIZING) 985 case CSSPropertyWebkitTextSizeAdjust: 986 if (valueID == CSSValueAuto || valueID == CSSValueNone) 987 return true; 988 break; 989#endif 990 case CSSPropertyWebkitTransformStyle: 991 if (valueID == CSSValueFlat || valueID == CSSValuePreserve3d) 992 return true; 993 break; 994 case CSSPropertyWebkitUserDrag: // auto | none | element 995 if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueElement) 996 return true; 997 break; 998 case CSSPropertyWebkitUserModify: // read-only | read-write 999 if (valueID == CSSValueReadOnly || valueID == CSSValueReadWrite || valueID == CSSValueReadWritePlaintextOnly) 1000 return true; 1001 break; 1002 case CSSPropertyWebkitUserSelect: // auto | none | text | all 1003 if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueText || valueID == CSSValueAll) 1004 return true; 1005 break; 1006 case CSSPropertyWebkitWritingMode: 1007 if (valueID >= CSSValueHorizontalTb && valueID <= CSSValueHorizontalBt) 1008 return true; 1009 break; 1010 case CSSPropertyWhiteSpace: // normal | pre | nowrap | inherit 1011 if (valueID == CSSValueNormal || valueID == CSSValuePre || valueID == CSSValuePreWrap || valueID == CSSValuePreLine || valueID == CSSValueNowrap) 1012 return true; 1013 break; 1014 case CSSPropertyWordBreak: // normal | break-all | break-word (this is a custom extension) 1015 if (valueID == CSSValueNormal || valueID == CSSValueBreakAll || valueID == CSSValueBreakWord) 1016 return true; 1017 break; 1018 default: 1019 ASSERT_NOT_REACHED(); 1020 return false; 1021 } 1022 return false; 1023} 1024 1025static inline bool isKeywordPropertyID(CSSPropertyID propertyId) 1026{ 1027 switch (propertyId) { 1028 case CSSPropertyBorderBottomStyle: 1029 case CSSPropertyBorderCollapse: 1030 case CSSPropertyBorderLeftStyle: 1031 case CSSPropertyBorderRightStyle: 1032 case CSSPropertyBorderTopStyle: 1033 case CSSPropertyBoxSizing: 1034 case CSSPropertyCaptionSide: 1035 case CSSPropertyClear: 1036 case CSSPropertyDirection: 1037 case CSSPropertyDisplay: 1038 case CSSPropertyEmptyCells: 1039 case CSSPropertyFloat: 1040 case CSSPropertyFontStyle: 1041 case CSSPropertyImageRendering: 1042 case CSSPropertyListStylePosition: 1043 case CSSPropertyListStyleType: 1044 case CSSPropertyObjectFit: 1045 case CSSPropertyOutlineStyle: 1046 case CSSPropertyOverflowWrap: 1047 case CSSPropertyOverflowX: 1048 case CSSPropertyOverflowY: 1049 case CSSPropertyPageBreakAfter: 1050 case CSSPropertyPageBreakBefore: 1051 case CSSPropertyPageBreakInside: 1052 case CSSPropertyPointerEvents: 1053 case CSSPropertyPosition: 1054 case CSSPropertyResize: 1055 case CSSPropertySpeak: 1056 case CSSPropertyTableLayout: 1057 case CSSPropertyTextLineThroughMode: 1058 case CSSPropertyTextLineThroughStyle: 1059 case CSSPropertyTextOverflow: 1060 case CSSPropertyTextOverlineMode: 1061 case CSSPropertyTextOverlineStyle: 1062 case CSSPropertyTextRendering: 1063 case CSSPropertyTextTransform: 1064 case CSSPropertyTextUnderlineMode: 1065 case CSSPropertyTextUnderlineStyle: 1066 case CSSPropertyVisibility: 1067 case CSSPropertyWebkitAppearance: 1068#if ENABLE(CSS_COMPOSITING) 1069 case CSSPropertyMixBlendMode: 1070 case CSSPropertyIsolation: 1071#endif 1072 case CSSPropertyWebkitBackfaceVisibility: 1073 case CSSPropertyWebkitBorderAfterStyle: 1074 case CSSPropertyWebkitBorderBeforeStyle: 1075 case CSSPropertyWebkitBorderEndStyle: 1076 case CSSPropertyWebkitBorderFit: 1077 case CSSPropertyWebkitBorderStartStyle: 1078 case CSSPropertyWebkitBoxAlign: 1079#if ENABLE(CSS_BOX_DECORATION_BREAK) 1080 case CSSPropertyWebkitBoxDecorationBreak: 1081#endif 1082 case CSSPropertyWebkitBoxDirection: 1083 case CSSPropertyWebkitBoxLines: 1084 case CSSPropertyWebkitBoxOrient: 1085 case CSSPropertyWebkitBoxPack: 1086 case CSSPropertyWebkitColorCorrection: 1087 case CSSPropertyWebkitColumnBreakAfter: 1088 case CSSPropertyWebkitColumnBreakBefore: 1089 case CSSPropertyWebkitColumnBreakInside: 1090 case CSSPropertyWebkitColumnFill: 1091 case CSSPropertyWebkitColumnRuleStyle: 1092 case CSSPropertyWebkitAlignContent: 1093 case CSSPropertyWebkitAlignItems: 1094 case CSSPropertyWebkitAlignSelf: 1095 case CSSPropertyWebkitFlexDirection: 1096 case CSSPropertyWebkitFlexWrap: 1097 case CSSPropertyWebkitJustifyContent: 1098 case CSSPropertyWebkitFontKerning: 1099 case CSSPropertyWebkitFontSmoothing: 1100 case CSSPropertyWebkitHyphens: 1101 case CSSPropertyWebkitLineAlign: 1102 case CSSPropertyWebkitLineBreak: 1103 case CSSPropertyWebkitLineSnap: 1104 case CSSPropertyWebkitMarginAfterCollapse: 1105 case CSSPropertyWebkitMarginBeforeCollapse: 1106 case CSSPropertyWebkitMarginBottomCollapse: 1107 case CSSPropertyWebkitMarginTopCollapse: 1108 case CSSPropertyWebkitMarqueeDirection: 1109 case CSSPropertyWebkitMarqueeStyle: 1110 case CSSPropertyWebkitNbspMode: 1111#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) 1112 case CSSPropertyWebkitOverflowScrolling: 1113#endif 1114 case CSSPropertyWebkitPrintColorAdjust: 1115#if ENABLE(CSS_REGIONS) 1116 case CSSPropertyWebkitRegionBreakAfter: 1117 case CSSPropertyWebkitRegionBreakBefore: 1118 case CSSPropertyWebkitRegionBreakInside: 1119 case CSSPropertyWebkitRegionFragment: 1120#endif 1121 case CSSPropertyWebkitRtlOrdering: 1122 case CSSPropertyWebkitRubyPosition: 1123#if ENABLE(CSS3_TEXT) 1124 case CSSPropertyWebkitTextAlignLast: 1125#endif // CSS3_TEXT 1126 case CSSPropertyWebkitTextCombine: 1127#if ENABLE(CSS3_TEXT) 1128 case CSSPropertyWebkitTextJustify: 1129#endif // CSS3_TEXT 1130 case CSSPropertyWebkitTextSecurity: 1131 case CSSPropertyWebkitTransformStyle: 1132 case CSSPropertyWebkitUserDrag: 1133 case CSSPropertyWebkitUserModify: 1134 case CSSPropertyWebkitUserSelect: 1135 case CSSPropertyWebkitWritingMode: 1136 case CSSPropertyWhiteSpace: 1137 case CSSPropertyWordBreak: 1138 case CSSPropertyWordWrap: 1139 return true; 1140 default: 1141 return false; 1142 } 1143} 1144 1145static bool parseKeywordValue(MutableStyleProperties* declaration, CSSPropertyID propertyId, const String& string, bool important, const CSSParserContext& parserContext) 1146{ 1147 ASSERT(!string.isEmpty()); 1148 1149 if (!isKeywordPropertyID(propertyId)) { 1150 // All properties accept the values of "initial" and "inherit". 1151 String lowerCaseString = string.lower(); 1152 if (lowerCaseString != "initial" && lowerCaseString != "inherit") 1153 return false; 1154 1155 // Parse initial/inherit shorthands using the CSSParser. 1156 if (shorthandForProperty(propertyId).length()) 1157 return false; 1158 } 1159 1160 CSSParserString cssString; 1161 cssString.init(string); 1162 CSSValueID valueID = cssValueKeywordID(cssString); 1163 1164 if (!valueID) 1165 return false; 1166 1167 RefPtr<CSSValue> value; 1168 if (valueID == CSSValueInherit) 1169 value = cssValuePool().createInheritedValue(); 1170 else if (valueID == CSSValueInitial) 1171 value = cssValuePool().createExplicitInitialValue(); 1172 else if (isValidKeywordPropertyAndValue(propertyId, valueID, parserContext)) 1173 value = cssValuePool().createIdentifierValue(valueID); 1174 else 1175 return false; 1176 1177 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 1178 return true; 1179} 1180 1181template <typename CharacterType> 1182static bool parseTransformArguments(WebKitCSSTransformValue* transformValue, CharacterType* characters, unsigned length, unsigned start, unsigned expectedCount) 1183{ 1184 while (expectedCount) { 1185 size_t end = WTF::find(characters, length, expectedCount == 1 ? ')' : ',', start); 1186 if (end == notFound || (expectedCount == 1 && end != length - 1)) 1187 return false; 1188 unsigned argumentLength = end - start; 1189 CSSPrimitiveValue::UnitTypes unit = CSSPrimitiveValue::CSS_NUMBER; 1190 double number; 1191 if (!parseSimpleLength(characters + start, argumentLength, unit, number)) 1192 return false; 1193 if (unit != CSSPrimitiveValue::CSS_PX && (number || unit != CSSPrimitiveValue::CSS_NUMBER)) 1194 return false; 1195 transformValue->append(cssValuePool().createValue(number, unit)); 1196 start = end + 1; 1197 --expectedCount; 1198 } 1199 return true; 1200} 1201 1202static bool parseTranslateTransformValue(MutableStyleProperties* properties, CSSPropertyID propertyID, const String& string, bool important) 1203{ 1204 if (propertyID != CSSPropertyWebkitTransform) 1205 return false; 1206 static const unsigned shortestValidTransformStringLength = 12; 1207 static const unsigned likelyMultipartTransformStringLengthCutoff = 32; 1208 if (string.length() < shortestValidTransformStringLength || string.length() > likelyMultipartTransformStringLengthCutoff) 1209 return false; 1210 if (!string.startsWith("translate", false)) 1211 return false; 1212 UChar c9 = toASCIILower(string[9]); 1213 UChar c10 = toASCIILower(string[10]); 1214 1215 WebKitCSSTransformValue::TransformOperationType transformType; 1216 unsigned expectedArgumentCount = 1; 1217 unsigned argumentStart = 11; 1218 if (c9 == 'x' && c10 == '(') 1219 transformType = WebKitCSSTransformValue::TranslateXTransformOperation; 1220 else if (c9 == 'y' && c10 == '(') 1221 transformType = WebKitCSSTransformValue::TranslateYTransformOperation; 1222 else if (c9 == 'z' && c10 == '(') 1223 transformType = WebKitCSSTransformValue::TranslateZTransformOperation; 1224 else if (c9 == '(') { 1225 transformType = WebKitCSSTransformValue::TranslateTransformOperation; 1226 expectedArgumentCount = 2; 1227 argumentStart = 10; 1228 } else if (c9 == '3' && c10 == 'd' && string[11] == '(') { 1229 transformType = WebKitCSSTransformValue::Translate3DTransformOperation; 1230 expectedArgumentCount = 3; 1231 argumentStart = 12; 1232 } else 1233 return false; 1234 1235 RefPtr<WebKitCSSTransformValue> transformValue = WebKitCSSTransformValue::create(transformType); 1236 bool success; 1237 if (string.is8Bit()) 1238 success = parseTransformArguments(transformValue.get(), string.characters8(), string.length(), argumentStart, expectedArgumentCount); 1239 else 1240 success = parseTransformArguments(transformValue.get(), string.characters16(), string.length(), argumentStart, expectedArgumentCount); 1241 if (!success) 1242 return false; 1243 RefPtr<CSSValueList> result = CSSValueList::createSpaceSeparated(); 1244 result->append(transformValue.release()); 1245 properties->addParsedProperty(CSSProperty(CSSPropertyWebkitTransform, result.release(), important)); 1246 return true; 1247} 1248 1249PassRefPtr<CSSValueList> CSSParser::parseFontFaceValue(const AtomicString& string) 1250{ 1251 if (string.isEmpty()) 1252 return 0; 1253 RefPtr<MutableStyleProperties> dummyStyle = MutableStyleProperties::create(); 1254 if (!parseValue(dummyStyle.get(), CSSPropertyFontFamily, string, false, CSSQuirksMode, 0)) 1255 return 0; 1256 1257 RefPtr<CSSValue> fontFamily = dummyStyle->getPropertyCSSValue(CSSPropertyFontFamily); 1258 if (!fontFamily->isValueList()) 1259 return 0; // FIXME: "initial" and "inherit" should be parsed as font names in the face attribute. 1260 return static_pointer_cast<CSSValueList>(fontFamily.release()); 1261} 1262 1263bool CSSParser::parseValue(MutableStyleProperties* declaration, CSSPropertyID propertyID, const String& string, bool important, CSSParserMode cssParserMode, StyleSheetContents* contextStyleSheet) 1264{ 1265 ASSERT(!string.isEmpty()); 1266 if (parseSimpleLengthValue(declaration, propertyID, string, important, cssParserMode)) 1267 return true; 1268 if (parseColorValue(declaration, propertyID, string, important, cssParserMode)) 1269 return true; 1270 1271 CSSParserContext context(cssParserMode); 1272 if (contextStyleSheet) { 1273 context = contextStyleSheet->parserContext(); 1274 context.mode = cssParserMode; 1275 } 1276 1277 if (parseKeywordValue(declaration, propertyID, string, important, context)) 1278 return true; 1279 if (parseTranslateTransformValue(declaration, propertyID, string, important)) 1280 return true; 1281 1282 CSSParser parser(context); 1283 return parser.parseValue(declaration, propertyID, string, important, contextStyleSheet); 1284} 1285 1286bool CSSParser::parseValue(MutableStyleProperties* declaration, CSSPropertyID propertyID, const String& string, bool important, StyleSheetContents* contextStyleSheet) 1287{ 1288 setStyleSheet(contextStyleSheet); 1289 1290 setupParser("@-webkit-value{", string, "} "); 1291 1292 m_id = propertyID; 1293 m_important = important; 1294 1295 cssyyparse(this); 1296 1297 m_rule = 0; 1298 1299 bool ok = false; 1300 if (m_hasFontFaceOnlyValues) 1301 deleteFontFaceOnlyValues(); 1302 if (!m_parsedProperties.isEmpty()) { 1303 ok = true; 1304 declaration->addParsedProperties(m_parsedProperties); 1305 clearProperties(); 1306 } 1307 1308 return ok; 1309} 1310 1311// The color will only be changed when string contains a valid CSS color, so callers 1312// can set it to a default color and ignore the boolean result. 1313bool CSSParser::parseColor(RGBA32& color, const String& string, bool strict) 1314{ 1315 // First try creating a color specified by name, rgba(), rgb() or "#" syntax. 1316 if (fastParseColor(color, string, strict)) 1317 return true; 1318 1319 CSSParser parser(CSSStrictMode); 1320 1321 // In case the fast-path parser didn't understand the color, try the full parser. 1322 if (!parser.parseColor(string)) 1323 return false; 1324 1325 CSSValue* value = parser.m_parsedProperties.first().value(); 1326 if (!value->isPrimitiveValue()) 1327 return false; 1328 1329 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 1330 if (!primitiveValue->isRGBColor()) 1331 return false; 1332 1333 color = primitiveValue->getRGBA32Value(); 1334 return true; 1335} 1336 1337bool CSSParser::parseColor(const String& string) 1338{ 1339 setupParser("@-webkit-decls{color:", string, "} "); 1340 cssyyparse(this); 1341 m_rule = 0; 1342 1343 return !m_parsedProperties.isEmpty() && m_parsedProperties.first().id() == CSSPropertyColor; 1344} 1345 1346bool CSSParser::parseSystemColor(RGBA32& color, const String& string, Document* document) 1347{ 1348 if (!document || !document->page()) 1349 return false; 1350 1351 CSSParserString cssColor; 1352 cssColor.init(string); 1353 CSSValueID id = cssValueKeywordID(cssColor); 1354 if (!validPrimitiveValueColor(id)) 1355 return false; 1356 1357 Color parsedColor = document->page()->theme().systemColor(id); 1358 if (!parsedColor.isValid()) 1359 return false; 1360 1361 color = parsedColor.rgb(); 1362 return true; 1363} 1364 1365void CSSParser::parseSelector(const String& string, CSSSelectorList& selectorList) 1366{ 1367 m_selectorListForParseSelector = &selectorList; 1368 1369 setupParser("@-webkit-selector{", string, "}"); 1370 1371 cssyyparse(this); 1372 1373 m_selectorListForParseSelector = 0; 1374} 1375 1376PassRef<ImmutableStyleProperties> CSSParser::parseInlineStyleDeclaration(const String& string, Element* element) 1377{ 1378 CSSParserContext context = element->document().elementSheet().contents().parserContext(); 1379 context.mode = strictToCSSParserMode(element->isHTMLElement() && !element->document().inQuirksMode()); 1380 return CSSParser(context).parseDeclaration(string, &element->document().elementSheet().contents()); 1381} 1382 1383PassRef<ImmutableStyleProperties> CSSParser::parseDeclaration(const String& string, StyleSheetContents* contextStyleSheet) 1384{ 1385 setStyleSheet(contextStyleSheet); 1386 1387 setupParser("@-webkit-decls{", string, "} "); 1388 cssyyparse(this); 1389 m_rule = 0; 1390 1391 if (m_hasFontFaceOnlyValues) 1392 deleteFontFaceOnlyValues(); 1393 1394 PassRef<ImmutableStyleProperties> style = createStyleProperties(); 1395 clearProperties(); 1396 return style; 1397} 1398 1399 1400bool CSSParser::parseDeclaration(MutableStyleProperties* declaration, const String& string, PassRefPtr<CSSRuleSourceData> prpRuleSourceData, StyleSheetContents* contextStyleSheet) 1401{ 1402 // Length of the "@-webkit-decls{" prefix. 1403 static const unsigned prefixLength = 15; 1404 1405 setStyleSheet(contextStyleSheet); 1406 1407 RefPtr<CSSRuleSourceData> ruleSourceData = prpRuleSourceData; 1408 if (ruleSourceData) { 1409 m_currentRuleDataStack = std::make_unique<RuleSourceDataList>(); 1410 m_currentRuleDataStack->append(ruleSourceData); 1411 } 1412 1413 setupParser("@-webkit-decls{", string, "} "); 1414 cssyyparse(this); 1415 m_rule = 0; 1416 1417 bool ok = false; 1418 if (m_hasFontFaceOnlyValues) 1419 deleteFontFaceOnlyValues(); 1420 if (!m_parsedProperties.isEmpty()) { 1421 ok = true; 1422 declaration->addParsedProperties(m_parsedProperties); 1423 clearProperties(); 1424 } 1425 1426 if (ruleSourceData) { 1427 ASSERT(m_currentRuleDataStack->size() == 1); 1428 ruleSourceData->ruleBodyRange.start = 0; 1429 ruleSourceData->ruleBodyRange.end = string.length(); 1430 for (size_t i = 0, size = ruleSourceData->styleSourceData->propertyData.size(); i < size; ++i) { 1431 CSSPropertySourceData& propertyData = ruleSourceData->styleSourceData->propertyData.at(i); 1432 propertyData.range.start -= prefixLength; 1433 propertyData.range.end -= prefixLength; 1434 } 1435 1436 fixUnparsedPropertyRanges(ruleSourceData.get()); 1437 m_currentRuleDataStack.reset(); 1438 } 1439 1440 return ok; 1441} 1442 1443std::unique_ptr<MediaQuery> CSSParser::parseMediaQuery(const String& string) 1444{ 1445 if (string.isEmpty()) 1446 return nullptr; 1447 1448 ASSERT(!m_mediaQuery); 1449 1450 // can't use { because tokenizer state switches from mediaquery to initial state when it sees { token. 1451 // instead insert one " " (which is WHITESPACE in CSSGrammar.y) 1452 setupParser("@-webkit-mediaquery ", string, "} "); 1453 cssyyparse(this); 1454 1455 return WTF::move(m_mediaQuery); 1456} 1457 1458#if ENABLE(PICTURE_SIZES) 1459std::unique_ptr<SourceSizeList> CSSParser::parseSizesAttribute(const String& string) 1460{ 1461 if (string.isEmpty()) 1462 return nullptr; 1463 1464 ASSERT(!m_sourceSizeList.get()); 1465 1466 setupParser("@-webkit-sizesattr ", string, "}"); 1467 cssyyparse(this); 1468 1469 return WTF::move(m_sourceSizeList); 1470} 1471#endif 1472 1473static inline void filterProperties(bool important, const CSSParser::ParsedPropertyVector& input, Vector<CSSProperty, 256>& output, size_t& unusedEntries, std::bitset<numCSSProperties>& seenProperties) 1474{ 1475 // Add properties in reverse order so that highest priority definitions are reached first. Duplicate definitions can then be ignored when found. 1476 for (int i = input.size() - 1; i >= 0; --i) { 1477 const CSSProperty& property = input[i]; 1478 if (property.isImportant() != important) 1479 continue; 1480 const unsigned propertyIDIndex = property.id() - firstCSSProperty; 1481 ASSERT(propertyIDIndex < seenProperties.size()); 1482 if (seenProperties[propertyIDIndex]) 1483 continue; 1484 seenProperties.set(propertyIDIndex); 1485 output[--unusedEntries] = property; 1486 } 1487} 1488 1489PassRef<ImmutableStyleProperties> CSSParser::createStyleProperties() 1490{ 1491 std::bitset<numCSSProperties> seenProperties; 1492 size_t unusedEntries = m_parsedProperties.size(); 1493 Vector<CSSProperty, 256> results(unusedEntries); 1494 1495 // Important properties have higher priority, so add them first. Duplicate definitions can then be ignored when found. 1496 filterProperties(true, m_parsedProperties, results, unusedEntries, seenProperties); 1497 filterProperties(false, m_parsedProperties, results, unusedEntries, seenProperties); 1498 if (unusedEntries) 1499 results.remove(0, unusedEntries); 1500 1501 return ImmutableStyleProperties::create(results.data(), results.size(), m_context.mode); 1502} 1503 1504void CSSParser::addPropertyWithPrefixingVariant(CSSPropertyID propId, PassRefPtr<CSSValue> value, bool important, bool implicit) 1505{ 1506 RefPtr<CSSValue> val = value.get(); 1507 addProperty(propId, value, important, implicit); 1508 1509 CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(propId); 1510 if (prefixingVariant == propId) 1511 return; 1512 1513 if (m_currentShorthand) { 1514 // We can't use ShorthandScope here as we can already be inside one (e.g we are parsing CSSTransition). 1515 m_currentShorthand = prefixingVariantForPropertyId(m_currentShorthand); 1516 addProperty(prefixingVariant, val.release(), important, implicit); 1517 m_currentShorthand = prefixingVariantForPropertyId(m_currentShorthand); 1518 } else 1519 addProperty(prefixingVariant, val.release(), important, implicit); 1520} 1521 1522void CSSParser::addProperty(CSSPropertyID propId, PassRefPtr<CSSValue> value, bool important, bool implicit) 1523{ 1524 // This property doesn't belong to a shorthand or is a CSS variable (which will be resolved later). 1525 if (!m_currentShorthand) { 1526 m_parsedProperties.append(CSSProperty(propId, value, important, false, CSSPropertyInvalid, m_implicitShorthand || implicit)); 1527 return; 1528 } 1529 1530 Vector<StylePropertyShorthand> shorthands = matchingShorthandsForLonghand(propId); 1531 if (shorthands.size() == 1) 1532 m_parsedProperties.append(CSSProperty(propId, value, important, true, CSSPropertyInvalid, m_implicitShorthand || implicit)); 1533 else 1534 m_parsedProperties.append(CSSProperty(propId, value, important, true, indexOfShorthandForLonghand(m_currentShorthand, shorthands), m_implicitShorthand || implicit)); 1535} 1536 1537void CSSParser::rollbackLastProperties(int num) 1538{ 1539 ASSERT(num >= 0); 1540 ASSERT(m_parsedProperties.size() >= static_cast<unsigned>(num)); 1541 m_parsedProperties.shrink(m_parsedProperties.size() - num); 1542} 1543 1544void CSSParser::clearProperties() 1545{ 1546 m_parsedProperties.clear(); 1547 m_numParsedPropertiesBeforeMarginBox = INVALID_NUM_PARSED_PROPERTIES; 1548 m_hasFontFaceOnlyValues = false; 1549} 1550 1551URL CSSParser::completeURL(const CSSParserContext& context, const String& url) 1552{ 1553 if (url.isNull()) 1554 return URL(); 1555 if (context.charset.isEmpty()) 1556 return URL(context.baseURL, url); 1557 return URL(context.baseURL, url, context.charset); 1558} 1559 1560URL CSSParser::completeURL(const String& url) const 1561{ 1562 return completeURL(m_context, url); 1563} 1564 1565bool CSSParser::validCalculationUnit(CSSParserValue* value, Units unitflags, ReleaseParsedCalcValueCondition releaseCalc) 1566{ 1567 bool mustBeNonNegative = unitflags & FNonNeg; 1568 1569 if (!parseCalculation(value, mustBeNonNegative ? CalculationRangeNonNegative : CalculationRangeAll)) 1570 return false; 1571 1572 bool b = false; 1573 switch (m_parsedCalculation->category()) { 1574 case CalcNumber: 1575 b = (unitflags & FNumber); 1576 if (!b && (unitflags & FInteger) && m_parsedCalculation->isInt()) 1577 b = true; 1578 if (b && mustBeNonNegative && m_parsedCalculation->isNegative()) 1579 b = false; 1580 break; 1581 case CalcLength: 1582 b = (unitflags & FLength); 1583 break; 1584 case CalcPercent: 1585 b = (unitflags & FPercent); 1586 if (b && mustBeNonNegative && m_parsedCalculation->isNegative()) 1587 b = false; 1588 break; 1589 case CalcPercentLength: 1590 b = (unitflags & FPercent) && (unitflags & FLength); 1591 break; 1592 case CalcPercentNumber: 1593 b = (unitflags & FPercent) && (unitflags & FNumber); 1594 break; 1595 case CalcAngle: 1596 b = (unitflags & FAngle); 1597 break; 1598 case CalcTime: 1599 b = (unitflags & FTime); 1600 break; 1601 case CalcFrequency: 1602 b = (unitflags & FFrequency); 1603 break; 1604 case CalcOther: 1605 break; 1606 } 1607 if (!b || releaseCalc == ReleaseParsedCalcValue) 1608 m_parsedCalculation.release(); 1609 return b; 1610} 1611 1612inline bool CSSParser::shouldAcceptUnitLessValues(CSSParserValue* value, Units unitflags, CSSParserMode cssParserMode) 1613{ 1614 // Qirks mode and svg presentation attributes accept unit less values. 1615 return (unitflags & (FLength | FAngle | FTime)) && (!value->fValue || cssParserMode == CSSQuirksMode || cssParserMode == SVGAttributeMode); 1616} 1617 1618bool CSSParser::validUnit(CSSParserValue* value, Units unitflags, CSSParserMode cssParserMode, ReleaseParsedCalcValueCondition releaseCalc) 1619{ 1620 if (isCalculation(value)) 1621 return validCalculationUnit(value, unitflags, releaseCalc); 1622 1623 bool b = false; 1624 switch (value->unit) { 1625 case CSSPrimitiveValue::CSS_NUMBER: 1626 b = (unitflags & FNumber); 1627 if (!b && shouldAcceptUnitLessValues(value, unitflags, cssParserMode)) { 1628 value->unit = (unitflags & FLength) ? CSSPrimitiveValue::CSS_PX : 1629 ((unitflags & FAngle) ? CSSPrimitiveValue::CSS_DEG : CSSPrimitiveValue::CSS_MS); 1630 b = true; 1631 } 1632 if (!b && (unitflags & FInteger) && value->isInt) 1633 b = true; 1634 if (!b && (unitflags & FPositiveInteger) && value->isInt && value->fValue > 0) 1635 b = true; 1636 break; 1637 case CSSPrimitiveValue::CSS_PERCENTAGE: 1638 b = (unitflags & FPercent); 1639 break; 1640 case CSSParserValue::Q_EMS: 1641 case CSSPrimitiveValue::CSS_EMS: 1642 case CSSPrimitiveValue::CSS_REMS: 1643 case CSSPrimitiveValue::CSS_CHS: 1644 case CSSPrimitiveValue::CSS_EXS: 1645 case CSSPrimitiveValue::CSS_PX: 1646 case CSSPrimitiveValue::CSS_CM: 1647 case CSSPrimitiveValue::CSS_MM: 1648 case CSSPrimitiveValue::CSS_IN: 1649 case CSSPrimitiveValue::CSS_PT: 1650 case CSSPrimitiveValue::CSS_PC: 1651 case CSSPrimitiveValue::CSS_VW: 1652 case CSSPrimitiveValue::CSS_VH: 1653 case CSSPrimitiveValue::CSS_VMIN: 1654 case CSSPrimitiveValue::CSS_VMAX: 1655 b = (unitflags & FLength); 1656 break; 1657 case CSSPrimitiveValue::CSS_MS: 1658 case CSSPrimitiveValue::CSS_S: 1659 b = (unitflags & FTime); 1660 break; 1661 case CSSPrimitiveValue::CSS_DEG: 1662 case CSSPrimitiveValue::CSS_RAD: 1663 case CSSPrimitiveValue::CSS_GRAD: 1664 case CSSPrimitiveValue::CSS_TURN: 1665 b = (unitflags & FAngle); 1666 break; 1667#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY) 1668 case CSSPrimitiveValue::CSS_DPPX: 1669 case CSSPrimitiveValue::CSS_DPI: 1670 case CSSPrimitiveValue::CSS_DPCM: 1671 b = (unitflags & FResolution); 1672 break; 1673#endif 1674 case CSSPrimitiveValue::CSS_HZ: 1675 case CSSPrimitiveValue::CSS_KHZ: 1676 case CSSPrimitiveValue::CSS_DIMENSION: 1677 default: 1678 break; 1679 } 1680 if (b && unitflags & FNonNeg && value->fValue < 0) 1681 b = false; 1682 return b; 1683} 1684 1685inline PassRefPtr<CSSPrimitiveValue> CSSParser::createPrimitiveNumericValue(CSSParserValue* value) 1686{ 1687 if (m_parsedCalculation) { 1688 ASSERT(isCalculation(value)); 1689 return CSSPrimitiveValue::create(m_parsedCalculation.release()); 1690 } 1691 1692#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY) 1693 ASSERT((value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) 1694 || (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_CHS) 1695 || (value->unit >= CSSPrimitiveValue::CSS_VW && value->unit <= CSSPrimitiveValue::CSS_VMAX) 1696 || (value->unit >= CSSPrimitiveValue::CSS_DPPX && value->unit <= CSSPrimitiveValue::CSS_DPCM)); 1697#else 1698 ASSERT((value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) 1699 || (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_CHS) 1700 || (value->unit >= CSSPrimitiveValue::CSS_VW && value->unit <= CSSPrimitiveValue::CSS_VMAX)); 1701#endif 1702 return cssValuePool().createValue(value->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(value->unit)); 1703} 1704 1705inline PassRefPtr<CSSPrimitiveValue> CSSParser::createPrimitiveStringValue(CSSParserValue* value) 1706{ 1707 ASSERT(value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT); 1708 return cssValuePool().createValue(value->string, CSSPrimitiveValue::CSS_STRING); 1709} 1710 1711static inline bool isComma(CSSParserValue* value) 1712{ 1713 return value && value->unit == CSSParserValue::Operator && value->iValue == ','; 1714} 1715 1716static inline bool isForwardSlashOperator(CSSParserValue* value) 1717{ 1718 ASSERT(value); 1719 return value->unit == CSSParserValue::Operator && value->iValue == '/'; 1720} 1721 1722bool CSSParser::validWidth(CSSParserValue* value) 1723{ 1724 int id = value->id; 1725 if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic || id == CSSValueWebkitMinContent || id == CSSValueWebkitMaxContent || id == CSSValueWebkitFillAvailable || id == CSSValueWebkitFitContent) 1726 return true; 1727 return !id && validUnit(value, FLength | FPercent | FNonNeg); 1728} 1729 1730// FIXME: Combine this with validWidth when we support fit-content, et al, for heights. 1731bool CSSParser::validHeight(CSSParserValue* value) 1732{ 1733 int id = value->id; 1734 if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic) 1735 return true; 1736 return !id && validUnit(value, FLength | FPercent | FNonNeg); 1737} 1738 1739inline PassRefPtr<CSSPrimitiveValue> CSSParser::parseValidPrimitive(CSSValueID identifier, CSSParserValue* value) 1740{ 1741 if (identifier) 1742 return cssValuePool().createIdentifierValue(identifier); 1743 if (value->unit == CSSPrimitiveValue::CSS_STRING) 1744 return createPrimitiveStringValue(value); 1745 if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) 1746 return createPrimitiveNumericValue(value); 1747 if (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_CHS) 1748 return createPrimitiveNumericValue(value); 1749 if (value->unit >= CSSPrimitiveValue::CSS_VW && value->unit <= CSSPrimitiveValue::CSS_VMAX) 1750 return createPrimitiveNumericValue(value); 1751#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY) 1752 if (value->unit >= CSSPrimitiveValue::CSS_DPPX && value->unit <= CSSPrimitiveValue::CSS_DPCM) 1753 return createPrimitiveNumericValue(value); 1754#endif 1755 if (value->unit >= CSSParserValue::Q_EMS) 1756 return CSSPrimitiveValue::createAllowingMarginQuirk(value->fValue, CSSPrimitiveValue::CSS_EMS); 1757 if (isCalculation(value)) 1758 return CSSPrimitiveValue::create(m_parsedCalculation.release()); 1759 1760 return 0; 1761} 1762 1763void CSSParser::addExpandedPropertyForValue(CSSPropertyID propId, PassRefPtr<CSSValue> prpValue, bool important) 1764{ 1765 const StylePropertyShorthand& shorthand = shorthandForProperty(propId); 1766 unsigned shorthandLength = shorthand.length(); 1767 if (!shorthandLength) { 1768 addProperty(propId, prpValue, important); 1769 return; 1770 } 1771 1772 RefPtr<CSSValue> value = prpValue; 1773 ShorthandScope scope(this, propId); 1774 const CSSPropertyID* longhands = shorthand.properties(); 1775 for (unsigned i = 0; i < shorthandLength; ++i) 1776 addProperty(longhands[i], value, important); 1777} 1778 1779bool CSSParser::parseValue(CSSPropertyID propId, bool important) 1780{ 1781 if (!m_valueList) 1782 return false; 1783 1784 CSSParserValue* value = m_valueList->current(); 1785 1786 if (!value) 1787 return false; 1788 1789 // Note: m_parsedCalculation is used to pass the calc value to validUnit and then cleared at the end of this function. 1790 // FIXME: This is to avoid having to pass parsedCalc to all validUnit callers. 1791 ASSERT(!m_parsedCalculation); 1792 1793 CSSValueID id = value->id; 1794 1795 unsigned num = inShorthand() ? 1 : m_valueList->size(); 1796 1797 if (id == CSSValueInherit) { 1798 if (num != 1) 1799 return false; 1800 addExpandedPropertyForValue(propId, cssValuePool().createInheritedValue(), important); 1801 return true; 1802 } 1803 else if (id == CSSValueInitial) { 1804 if (num != 1) 1805 return false; 1806 addExpandedPropertyForValue(propId, cssValuePool().createExplicitInitialValue(), important); 1807 return true; 1808 } 1809 1810 if (isKeywordPropertyID(propId)) { 1811 if (!isValidKeywordPropertyAndValue(propId, id, m_context)) 1812 return false; 1813 if (m_valueList->next() && !inShorthand()) 1814 return false; 1815 addProperty(propId, cssValuePool().createIdentifierValue(id), important); 1816 return true; 1817 } 1818 1819#if ENABLE(CSS_DEVICE_ADAPTATION) 1820 if (inViewport()) 1821 return parseViewportProperty(propId, important); 1822#endif 1823 1824 bool validPrimitive = false; 1825 RefPtr<CSSValue> parsedValue; 1826 1827 switch (propId) { 1828 case CSSPropertySize: // <length>{1,2} | auto | [ <page-size> || [ portrait | landscape] ] 1829 return parseSize(propId, important); 1830 1831 case CSSPropertyQuotes: // [<string> <string>]+ | none | inherit 1832 if (id) 1833 validPrimitive = true; 1834 else 1835 return parseQuotes(propId, important); 1836 break; 1837 case CSSPropertyUnicodeBidi: // normal | embed | bidi-override | isolate | isolate-override | plaintext | inherit 1838 if (id == CSSValueNormal 1839 || id == CSSValueEmbed 1840 || id == CSSValueBidiOverride 1841 || id == CSSValueWebkitIsolate 1842 || id == CSSValueWebkitIsolateOverride 1843 || id == CSSValueWebkitPlaintext) 1844 validPrimitive = true; 1845 break; 1846 1847 case CSSPropertyContent: // [ <string> | <uri> | <counter> | attr(X) | open-quote | 1848 // close-quote | no-open-quote | no-close-quote ]+ | inherit 1849 return parseContent(propId, important); 1850 1851 case CSSPropertyWebkitAlt: // [ <string> | attr(X) ] 1852 return parseAlt(propId, important); 1853 1854 case CSSPropertyClip: // <shape> | auto | inherit 1855 if (id == CSSValueAuto) 1856 validPrimitive = true; 1857 else if (value->unit == CSSParserValue::Function) 1858 return parseClipShape(propId, important); 1859 break; 1860 1861 /* Start of supported CSS properties with validation. This is needed for parseShorthand to work 1862 * correctly and allows optimization in WebCore::applyRule(..) 1863 */ 1864 case CSSPropertyOverflow: { 1865 ShorthandScope scope(this, propId); 1866 if (num != 1 || !parseValue(CSSPropertyOverflowY, important)) 1867 return false; 1868 1869 RefPtr<CSSValue> overflowXValue; 1870 1871 // FIXME: -webkit-paged-x or -webkit-paged-y only apply to overflow-y. If this value has been 1872 // set using the shorthand, then for now overflow-x will default to auto, but once we implement 1873 // pagination controls, it should default to hidden. If the overflow-y value is anything but 1874 // paged-x or paged-y, then overflow-x and overflow-y should have the same value. 1875 if (id == CSSValueWebkitPagedX || id == CSSValueWebkitPagedY) 1876 overflowXValue = cssValuePool().createIdentifierValue(CSSValueAuto); 1877 else 1878 overflowXValue = m_parsedProperties.last().value(); 1879 addProperty(CSSPropertyOverflowX, overflowXValue.release(), important); 1880 return true; 1881 } 1882 1883 case CSSPropertyTextAlign: 1884 // left | right | center | justify | -webkit-left | -webkit-right | -webkit-center | -webkit-match-parent 1885 // | start | end | inherit | -webkit-auto (converted to start) 1886 // NOTE: <string> is not supported. 1887 if ((id >= CSSValueWebkitAuto && id <= CSSValueWebkitMatchParent) || id == CSSValueStart || id == CSSValueEnd) 1888 validPrimitive = true; 1889 break; 1890 1891 case CSSPropertyFontWeight: { // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit 1892 if (m_valueList->size() != 1) 1893 return false; 1894 return parseFontWeight(important); 1895 } 1896 case CSSPropertyBorderSpacing: { 1897 if (num == 1) { 1898 ShorthandScope scope(this, CSSPropertyBorderSpacing); 1899 if (!parseValue(CSSPropertyWebkitBorderHorizontalSpacing, important)) 1900 return false; 1901 CSSValue* value = m_parsedProperties.last().value(); 1902 addProperty(CSSPropertyWebkitBorderVerticalSpacing, value, important); 1903 return true; 1904 } 1905 else if (num == 2) { 1906 ShorthandScope scope(this, CSSPropertyBorderSpacing); 1907 if (!parseValue(CSSPropertyWebkitBorderHorizontalSpacing, important) || !parseValue(CSSPropertyWebkitBorderVerticalSpacing, important)) 1908 return false; 1909 return true; 1910 } 1911 return false; 1912 } 1913 case CSSPropertyWebkitBorderHorizontalSpacing: 1914 case CSSPropertyWebkitBorderVerticalSpacing: 1915 validPrimitive = validUnit(value, FLength | FNonNeg); 1916 break; 1917 case CSSPropertyOutlineColor: // <color> | invert | inherit 1918 // Outline color has "invert" as additional keyword. 1919 // Also, we want to allow the special focus color even in strict parsing mode. 1920 if (id == CSSValueInvert || id == CSSValueWebkitFocusRingColor) { 1921 validPrimitive = true; 1922 break; 1923 } 1924 FALLTHROUGH; 1925 case CSSPropertyBackgroundColor: // <color> | inherit 1926 case CSSPropertyBorderTopColor: // <color> | inherit 1927 case CSSPropertyBorderRightColor: 1928 case CSSPropertyBorderBottomColor: 1929 case CSSPropertyBorderLeftColor: 1930 case CSSPropertyWebkitBorderStartColor: 1931 case CSSPropertyWebkitBorderEndColor: 1932 case CSSPropertyWebkitBorderBeforeColor: 1933 case CSSPropertyWebkitBorderAfterColor: 1934 case CSSPropertyColor: // <color> | inherit 1935 case CSSPropertyTextLineThroughColor: // CSS3 text decoration colors 1936 case CSSPropertyTextUnderlineColor: 1937 case CSSPropertyTextOverlineColor: 1938 case CSSPropertyWebkitColumnRuleColor: 1939 case CSSPropertyWebkitTextDecorationColor: 1940 case CSSPropertyWebkitTextEmphasisColor: 1941 case CSSPropertyWebkitTextFillColor: 1942 case CSSPropertyWebkitTextStrokeColor: 1943 if (id == CSSValueWebkitText) 1944 validPrimitive = true; // Always allow this, even when strict parsing is on, 1945 // since we use this in our UA sheets. 1946 else if (id == CSSValueCurrentcolor) 1947 validPrimitive = true; 1948 else if ((id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || 1949 (id >= CSSValueWebkitFocusRingColor && id < CSSValueWebkitText && inQuirksMode())) { 1950 validPrimitive = true; 1951 } else { 1952 parsedValue = parseColor(); 1953 if (parsedValue) 1954 m_valueList->next(); 1955 } 1956 break; 1957 1958 case CSSPropertyCursor: { 1959 // Grammar defined by CSS3 UI and modified by CSS4 images: 1960 // [ [<image> [<x> <y>]?,]* 1961 // [ auto | crosshair | default | pointer | progress | move | e-resize | ne-resize | 1962 // nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize | ew-resize | 1963 // ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | text | wait | help | 1964 // vertical-text | cell | context-menu | alias | copy | no-drop | not-allowed | -webkit-zoom-in 1965 // -webkit-zoom-out | all-scroll | -webkit-grab | -webkit-grabbing ] ] | inherit 1966 RefPtr<CSSValueList> list; 1967 while (value) { 1968 RefPtr<CSSValue> image = 0; 1969 if (value->unit == CSSPrimitiveValue::CSS_URI) { 1970 String uri = value->string; 1971 if (!uri.isNull()) 1972 image = CSSImageValue::create(completeURL(uri)); 1973#if ENABLE(CSS_IMAGE_SET) && ENABLE(MOUSE_CURSOR_SCALE) 1974 } else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "-webkit-image-set(")) { 1975 image = parseImageSet(); 1976 if (!image) 1977 break; 1978#endif 1979 } else 1980 break; 1981 1982 Vector<int> coords; 1983 value = m_valueList->next(); 1984 while (value && value->unit == CSSPrimitiveValue::CSS_NUMBER) { 1985 coords.append(int(value->fValue)); 1986 value = m_valueList->next(); 1987 } 1988 bool hasHotSpot = false; 1989 IntPoint hotSpot(-1, -1); 1990 int nrcoords = coords.size(); 1991 if (nrcoords > 0 && nrcoords != 2) 1992 return false; 1993 if (nrcoords == 2) { 1994 hasHotSpot = true; 1995 hotSpot = IntPoint(coords[0], coords[1]); 1996 } 1997 1998 if (!list) 1999 list = CSSValueList::createCommaSeparated(); 2000 2001 if (image) 2002 list->append(CSSCursorImageValue::create(image.releaseNonNull(), hasHotSpot, hotSpot)); 2003 2004 if ((inStrictMode() && !value) || (value && !(value->unit == CSSParserValue::Operator && value->iValue == ','))) 2005 return false; 2006 value = m_valueList->next(); // comma 2007 } 2008 if (list) { 2009 if (!value) { // no value after url list (MSIE 5 compatibility) 2010 if (list->length() != 1) 2011 return false; 2012 } else if (inQuirksMode() && value->id == CSSValueHand) // MSIE 5 compatibility :/ 2013 list->append(cssValuePool().createIdentifierValue(CSSValuePointer)); 2014 else if ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitGrabbing) || value->id == CSSValueCopy || value->id == CSSValueNone) 2015 list->append(cssValuePool().createIdentifierValue(value->id)); 2016 m_valueList->next(); 2017 parsedValue = list.release(); 2018 break; 2019 } else if (value) { 2020 id = value->id; 2021 if (inQuirksMode() && value->id == CSSValueHand) { // MSIE 5 compatibility :/ 2022 id = CSSValuePointer; 2023 validPrimitive = true; 2024 } else if ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitGrabbing) || value->id == CSSValueCopy || value->id == CSSValueNone) 2025 validPrimitive = true; 2026 } else { 2027 ASSERT_NOT_REACHED(); 2028 return false; 2029 } 2030 break; 2031 } 2032 2033#if ENABLE(CURSOR_VISIBILITY) 2034 case CSSPropertyWebkitCursorVisibility: 2035 if (id == CSSValueAuto || id == CSSValueAutoHide) 2036 validPrimitive = true; 2037 break; 2038#endif 2039 2040 case CSSPropertyBackgroundAttachment: 2041 case CSSPropertyBackgroundBlendMode: 2042 case CSSPropertyBackgroundClip: 2043 case CSSPropertyWebkitBackgroundClip: 2044 case CSSPropertyWebkitBackgroundComposite: 2045 case CSSPropertyBackgroundImage: 2046 case CSSPropertyBackgroundOrigin: 2047 case CSSPropertyWebkitBackgroundOrigin: 2048 case CSSPropertyBackgroundPosition: 2049 case CSSPropertyBackgroundPositionX: 2050 case CSSPropertyBackgroundPositionY: 2051 case CSSPropertyBackgroundSize: 2052 case CSSPropertyWebkitBackgroundSize: 2053 case CSSPropertyBackgroundRepeat: 2054 case CSSPropertyBackgroundRepeatX: 2055 case CSSPropertyBackgroundRepeatY: 2056 case CSSPropertyWebkitMaskClip: 2057 case CSSPropertyWebkitMaskComposite: 2058 case CSSPropertyWebkitMaskImage: 2059 case CSSPropertyWebkitMaskOrigin: 2060 case CSSPropertyWebkitMaskPosition: 2061 case CSSPropertyWebkitMaskPositionX: 2062 case CSSPropertyWebkitMaskPositionY: 2063 case CSSPropertyWebkitMaskSize: 2064 case CSSPropertyWebkitMaskSourceType: 2065 case CSSPropertyWebkitMaskRepeat: 2066 case CSSPropertyWebkitMaskRepeatX: 2067 case CSSPropertyWebkitMaskRepeatY: 2068 { 2069 RefPtr<CSSValue> val1; 2070 RefPtr<CSSValue> val2; 2071 CSSPropertyID propId1, propId2; 2072 bool result = false; 2073 if (parseFillProperty(propId, propId1, propId2, val1, val2)) { 2074 std::unique_ptr<ShorthandScope> shorthandScope; 2075 if (propId == CSSPropertyBackgroundPosition || 2076 propId == CSSPropertyBackgroundRepeat || 2077 propId == CSSPropertyWebkitMaskPosition || 2078 propId == CSSPropertyWebkitMaskRepeat) { 2079 shorthandScope = std::make_unique<ShorthandScope>(this, propId); 2080 } 2081 addProperty(propId1, val1.release(), important); 2082 if (val2) 2083 addProperty(propId2, val2.release(), important); 2084 result = true; 2085 } 2086 m_implicitShorthand = false; 2087 return result; 2088 } 2089 case CSSPropertyListStyleImage: // <uri> | none | inherit 2090 case CSSPropertyBorderImageSource: 2091 case CSSPropertyWebkitMaskBoxImageSource: 2092 if (id == CSSValueNone) { 2093 parsedValue = cssValuePool().createIdentifierValue(CSSValueNone); 2094 m_valueList->next(); 2095 } else if (value->unit == CSSPrimitiveValue::CSS_URI) { 2096 parsedValue = CSSImageValue::create(completeURL(value->string)); 2097 m_valueList->next(); 2098 } else if (isGeneratedImageValue(value)) { 2099 if (parseGeneratedImage(m_valueList.get(), parsedValue)) 2100 m_valueList->next(); 2101 else 2102 return false; 2103 } 2104#if ENABLE(CSS_IMAGE_SET) 2105 else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "-webkit-image-set(")) { 2106 parsedValue = parseImageSet(); 2107 if (!parsedValue) 2108 return false; 2109 m_valueList->next(); 2110 } 2111#endif 2112 break; 2113 2114 case CSSPropertyWebkitTextStrokeWidth: 2115 case CSSPropertyOutlineWidth: // <border-width> | inherit 2116 case CSSPropertyBorderTopWidth: //// <border-width> | inherit 2117 case CSSPropertyBorderRightWidth: // Which is defined as 2118 case CSSPropertyBorderBottomWidth: // thin | medium | thick | <length> 2119 case CSSPropertyBorderLeftWidth: 2120 case CSSPropertyWebkitBorderStartWidth: 2121 case CSSPropertyWebkitBorderEndWidth: 2122 case CSSPropertyWebkitBorderBeforeWidth: 2123 case CSSPropertyWebkitBorderAfterWidth: 2124 case CSSPropertyWebkitColumnRuleWidth: 2125 if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick) 2126 validPrimitive = true; 2127 else 2128 validPrimitive = validUnit(value, FLength | FNonNeg); 2129 break; 2130 2131 case CSSPropertyLetterSpacing: // normal | <length> | inherit 2132 if (id == CSSValueNormal) 2133 validPrimitive = true; 2134 else 2135 validPrimitive = validUnit(value, FLength); 2136 break; 2137 2138 case CSSPropertyWordSpacing: // normal | <length> | <percentage> | inherit 2139 if (id == CSSValueNormal) 2140 validPrimitive = true; 2141 else 2142 validPrimitive = validUnit(value, FLength | FPercent); 2143 break; 2144 2145 case CSSPropertyTextIndent: 2146 parsedValue = parseTextIndent(); 2147 break; 2148 2149 case CSSPropertyPaddingTop: //// <padding-width> | inherit 2150 case CSSPropertyPaddingRight: // Which is defined as 2151 case CSSPropertyPaddingBottom: // <length> | <percentage> 2152 case CSSPropertyPaddingLeft: //// 2153 case CSSPropertyWebkitPaddingStart: 2154 case CSSPropertyWebkitPaddingEnd: 2155 case CSSPropertyWebkitPaddingBefore: 2156 case CSSPropertyWebkitPaddingAfter: 2157 validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg)); 2158 break; 2159 2160 case CSSPropertyMaxWidth: 2161 case CSSPropertyWebkitMaxLogicalWidth: 2162 validPrimitive = (id == CSSValueNone || validWidth(value)); 2163 break; 2164 2165 case CSSPropertyMinWidth: 2166 case CSSPropertyWebkitMinLogicalWidth: 2167 validPrimitive = validWidth(value); 2168 break; 2169 2170 case CSSPropertyWidth: 2171 case CSSPropertyWebkitLogicalWidth: 2172 validPrimitive = (id == CSSValueAuto || validWidth(value)); 2173 break; 2174 2175 case CSSPropertyMaxHeight: 2176 case CSSPropertyWebkitMaxLogicalHeight: 2177 validPrimitive = (id == CSSValueNone || validHeight(value)); 2178 break; 2179 2180 case CSSPropertyMinHeight: 2181 case CSSPropertyWebkitMinLogicalHeight: 2182 validPrimitive = validHeight(value); 2183 break; 2184 2185 case CSSPropertyHeight: 2186 case CSSPropertyWebkitLogicalHeight: 2187 validPrimitive = (id == CSSValueAuto || validHeight(value)); 2188 break; 2189 2190 case CSSPropertyFontSize: 2191 return parseFontSize(important); 2192 2193 case CSSPropertyFontVariant: // normal | small-caps | inherit 2194 return parseFontVariant(important); 2195 2196 case CSSPropertyVerticalAlign: 2197 // baseline | sub | super | top | text-top | middle | bottom | text-bottom | 2198 // <percentage> | <length> | inherit 2199 2200 if (id >= CSSValueBaseline && id <= CSSValueWebkitBaselineMiddle) 2201 validPrimitive = true; 2202 else 2203 validPrimitive = (!id && validUnit(value, FLength | FPercent)); 2204 break; 2205 2206 case CSSPropertyBottom: // <length> | <percentage> | auto | inherit 2207 case CSSPropertyLeft: // <length> | <percentage> | auto | inherit 2208 case CSSPropertyRight: // <length> | <percentage> | auto | inherit 2209 case CSSPropertyTop: // <length> | <percentage> | auto | inherit 2210 case CSSPropertyMarginTop: //// <margin-width> | inherit 2211 case CSSPropertyMarginRight: // Which is defined as 2212 case CSSPropertyMarginBottom: // <length> | <percentage> | auto | inherit 2213 case CSSPropertyMarginLeft: //// 2214 case CSSPropertyWebkitMarginStart: 2215 case CSSPropertyWebkitMarginEnd: 2216 case CSSPropertyWebkitMarginBefore: 2217 case CSSPropertyWebkitMarginAfter: 2218 if (id == CSSValueAuto) 2219 validPrimitive = true; 2220 else 2221 validPrimitive = (!id && validUnit(value, FLength | FPercent)); 2222 break; 2223 2224 case CSSPropertyZIndex: // auto | <integer> | inherit 2225 if (id == CSSValueAuto) 2226 validPrimitive = true; 2227 else 2228 validPrimitive = (!id && validUnit(value, FInteger, CSSQuirksMode)); 2229 break; 2230 2231 case CSSPropertyOrphans: // <integer> | inherit | auto (We've added support for auto for backwards compatibility) 2232 case CSSPropertyWidows: // <integer> | inherit | auto (Ditto) 2233 if (id == CSSValueAuto) 2234 validPrimitive = true; 2235 else 2236 validPrimitive = (!id && validUnit(value, FPositiveInteger, CSSQuirksMode)); 2237 break; 2238 2239 case CSSPropertyLineHeight: 2240 return parseLineHeight(important); 2241 case CSSPropertyCounterIncrement: // [ <identifier> <integer>? ]+ | none | inherit 2242 if (id != CSSValueNone) 2243 return parseCounter(propId, 1, important); 2244 validPrimitive = true; 2245 break; 2246 case CSSPropertyCounterReset: // [ <identifier> <integer>? ]+ | none | inherit 2247 if (id != CSSValueNone) 2248 return parseCounter(propId, 0, important); 2249 validPrimitive = true; 2250 break; 2251 case CSSPropertyFontFamily: 2252 // [[ <family-name> | <generic-family> ],]* [<family-name> | <generic-family>] | inherit 2253 { 2254 parsedValue = parseFontFamily(); 2255 break; 2256 } 2257 2258 case CSSPropertyWebkitTextDecoration: 2259 // [ <text-decoration-line> || <text-decoration-style> || <text-decoration-color> ] | inherit 2260 return parseShorthand(CSSPropertyWebkitTextDecoration, webkitTextDecorationShorthand(), important); 2261 2262 case CSSPropertyTextDecoration: 2263 case CSSPropertyWebkitTextDecorationsInEffect: 2264 case CSSPropertyWebkitTextDecorationLine: 2265 // none | [ underline || overline || line-through || blink ] | inherit 2266 return parseTextDecoration(propId, important); 2267 2268 case CSSPropertyWebkitTextDecorationStyle: 2269 // solid | double | dotted | dashed | wavy 2270 if (id == CSSValueSolid || id == CSSValueDouble || id == CSSValueDotted || id == CSSValueDashed || id == CSSValueWavy) 2271 validPrimitive = true; 2272 break; 2273 2274 case CSSPropertyWebkitTextDecorationSkip: 2275 // none | [ objects || spaces || ink || edges || box-decoration ] 2276 return parseTextDecorationSkip(important); 2277 2278 case CSSPropertyWebkitTextUnderlinePosition: 2279 // auto | alphabetic | under 2280 return parseTextUnderlinePosition(important); 2281 2282 case CSSPropertyZoom: // normal | reset | document | <number> | <percentage> | inherit 2283 if (id == CSSValueNormal || id == CSSValueReset || id == CSSValueDocument) 2284 validPrimitive = true; 2285 else 2286 validPrimitive = (!id && validUnit(value, FNumber | FPercent | FNonNeg, CSSStrictMode)); 2287 break; 2288 2289 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. 2290 return parseFontFaceSrc(); 2291 2292 case CSSPropertyUnicodeRange: 2293 return parseFontFaceUnicodeRange(); 2294 2295 /* CSS3 properties */ 2296 2297 case CSSPropertyBorderImage: { 2298 RefPtr<CSSValue> result; 2299 return parseBorderImage(propId, result, important); 2300 } 2301 case CSSPropertyWebkitBorderImage: 2302 case CSSPropertyWebkitMaskBoxImage: { 2303 RefPtr<CSSValue> result; 2304 if (parseBorderImage(propId, result)) { 2305 addProperty(propId, result, important); 2306 return true; 2307 } 2308 break; 2309 } 2310 case CSSPropertyBorderImageOutset: 2311 case CSSPropertyWebkitMaskBoxImageOutset: { 2312 RefPtr<CSSPrimitiveValue> result; 2313 if (parseBorderImageOutset(result)) { 2314 addProperty(propId, result, important); 2315 return true; 2316 } 2317 break; 2318 } 2319 case CSSPropertyBorderImageRepeat: 2320 case CSSPropertyWebkitMaskBoxImageRepeat: { 2321 RefPtr<CSSValue> result; 2322 if (parseBorderImageRepeat(result)) { 2323 addProperty(propId, result, important); 2324 return true; 2325 } 2326 break; 2327 } 2328 case CSSPropertyBorderImageSlice: 2329 case CSSPropertyWebkitMaskBoxImageSlice: { 2330 RefPtr<CSSBorderImageSliceValue> result; 2331 if (parseBorderImageSlice(propId, result)) { 2332 addProperty(propId, result, important); 2333 return true; 2334 } 2335 break; 2336 } 2337 case CSSPropertyBorderImageWidth: 2338 case CSSPropertyWebkitMaskBoxImageWidth: { 2339 RefPtr<CSSPrimitiveValue> result; 2340 if (parseBorderImageWidth(result)) { 2341 addProperty(propId, result, important); 2342 return true; 2343 } 2344 break; 2345 } 2346 case CSSPropertyBorderTopRightRadius: 2347 case CSSPropertyBorderTopLeftRadius: 2348 case CSSPropertyBorderBottomLeftRadius: 2349 case CSSPropertyBorderBottomRightRadius: { 2350 if (num != 1 && num != 2) 2351 return false; 2352 validPrimitive = validUnit(value, FLength | FPercent | FNonNeg); 2353 if (!validPrimitive) 2354 return false; 2355 RefPtr<CSSPrimitiveValue> parsedValue1 = createPrimitiveNumericValue(value); 2356 RefPtr<CSSPrimitiveValue> parsedValue2; 2357 if (num == 2) { 2358 value = m_valueList->next(); 2359 validPrimitive = validUnit(value, FLength | FPercent | FNonNeg); 2360 if (!validPrimitive) 2361 return false; 2362 parsedValue2 = createPrimitiveNumericValue(value); 2363 } else 2364 parsedValue2 = parsedValue1; 2365 2366 addProperty(propId, createPrimitiveValuePair(parsedValue1.release(), parsedValue2.release()), important); 2367 return true; 2368 } 2369 case CSSPropertyTabSize: 2370 validPrimitive = validUnit(value, FInteger | FNonNeg); 2371 break; 2372 case CSSPropertyWebkitAspectRatio: 2373 return parseAspectRatio(important); 2374 case CSSPropertyBorderRadius: 2375 case CSSPropertyWebkitBorderRadius: 2376 return parseBorderRadius(propId, important); 2377 case CSSPropertyOutlineOffset: 2378 validPrimitive = validUnit(value, FLength); 2379 break; 2380 case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3 2381 case CSSPropertyBoxShadow: 2382 case CSSPropertyWebkitBoxShadow: 2383 if (id == CSSValueNone) 2384 validPrimitive = true; 2385 else { 2386 RefPtr<CSSValueList> shadowValueList = parseShadow(m_valueList.get(), propId); 2387 if (shadowValueList) { 2388 addProperty(propId, shadowValueList.release(), important); 2389 m_valueList->next(); 2390 return true; 2391 } 2392 return false; 2393 } 2394 break; 2395 case CSSPropertyWebkitBoxReflect: 2396 if (id == CSSValueNone) 2397 validPrimitive = true; 2398 else 2399 return parseReflect(propId, important); 2400 break; 2401 case CSSPropertyOpacity: 2402 validPrimitive = validUnit(value, FNumber); 2403 break; 2404 case CSSPropertyWebkitBoxFlex: 2405 validPrimitive = validUnit(value, FNumber); 2406 break; 2407 case CSSPropertyWebkitBoxFlexGroup: 2408 validPrimitive = validUnit(value, FInteger | FNonNeg, CSSStrictMode); 2409 break; 2410 case CSSPropertyWebkitBoxOrdinalGroup: 2411 validPrimitive = validUnit(value, FInteger | FNonNeg, CSSStrictMode) && value->fValue; 2412 break; 2413#if ENABLE(CSS_FILTERS) 2414 case CSSPropertyWebkitFilter: 2415 if (id == CSSValueNone) 2416 validPrimitive = true; 2417 else { 2418 RefPtr<CSSValue> currValue; 2419 if (!parseFilter(m_valueList.get(), currValue)) 2420 return false; 2421 addProperty(propId, currValue, important); 2422 return true; 2423 } 2424 break; 2425#endif 2426#if ENABLE(CSS_COMPOSITING) 2427 case CSSPropertyMixBlendMode: 2428 if (cssCompositingEnabled()) 2429 validPrimitive = true; 2430 break; 2431 case CSSPropertyIsolation: 2432 if (cssCompositingEnabled()) 2433 validPrimitive = true; 2434 break; 2435#endif 2436 case CSSPropertyWebkitFlex: { 2437 ShorthandScope scope(this, propId); 2438 if (id == CSSValueNone) { 2439 addProperty(CSSPropertyWebkitFlexGrow, cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER), important); 2440 addProperty(CSSPropertyWebkitFlexShrink, cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER), important); 2441 addProperty(CSSPropertyWebkitFlexBasis, cssValuePool().createIdentifierValue(CSSValueAuto), important); 2442 return true; 2443 } 2444 return parseFlex(m_valueList.get(), important); 2445 } 2446 case CSSPropertyWebkitFlexBasis: 2447 // FIXME: Support intrinsic dimensions too. 2448 if (id == CSSValueAuto) 2449 validPrimitive = true; 2450 else 2451 validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg)); 2452 break; 2453 case CSSPropertyWebkitFlexGrow: 2454 case CSSPropertyWebkitFlexShrink: 2455 validPrimitive = validUnit(value, FNumber | FNonNeg); 2456 break; 2457 case CSSPropertyWebkitOrder: 2458 if (validUnit(value, FInteger, CSSStrictMode)) { 2459 // 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. 2460 parsedValue = cssValuePool().createValue(std::max<double>(std::numeric_limits<int>::min() + 2, value->fValue), static_cast<CSSPrimitiveValue::UnitTypes>(value->unit)); 2461 m_valueList->next(); 2462 } 2463 break; 2464 case CSSPropertyWebkitMarquee: 2465 return parseShorthand(propId, webkitMarqueeShorthand(), important); 2466 case CSSPropertyWebkitMarqueeIncrement: 2467 if (id == CSSValueSmall || id == CSSValueLarge || id == CSSValueMedium) 2468 validPrimitive = true; 2469 else 2470 validPrimitive = validUnit(value, FLength | FPercent); 2471 break; 2472 case CSSPropertyWebkitMarqueeRepetition: 2473 if (id == CSSValueInfinite) 2474 validPrimitive = true; 2475 else 2476 validPrimitive = validUnit(value, FInteger | FNonNeg); 2477 break; 2478 case CSSPropertyWebkitMarqueeSpeed: 2479 if (id == CSSValueNormal || id == CSSValueSlow || id == CSSValueFast) 2480 validPrimitive = true; 2481 else 2482 validPrimitive = validUnit(value, FTime | FInteger | FNonNeg); 2483 break; 2484#if ENABLE(CSS_REGIONS) 2485 case CSSPropertyWebkitFlowInto: 2486 if (!cssRegionsEnabled()) 2487 return false; 2488 return parseFlowThread(propId, important); 2489 case CSSPropertyWebkitFlowFrom: 2490 if (!cssRegionsEnabled()) 2491 return false; 2492 return parseRegionThread(propId, important); 2493#endif 2494 case CSSPropertyWebkitTransform: 2495 if (id == CSSValueNone) 2496 validPrimitive = true; 2497 else { 2498 RefPtr<CSSValue> transformValue = parseTransform(); 2499 if (transformValue) { 2500 addProperty(propId, transformValue.release(), important); 2501 return true; 2502 } 2503 return false; 2504 } 2505 break; 2506 case CSSPropertyWebkitTransformOrigin: 2507 case CSSPropertyWebkitTransformOriginX: 2508 case CSSPropertyWebkitTransformOriginY: 2509 case CSSPropertyWebkitTransformOriginZ: { 2510 RefPtr<CSSValue> val1; 2511 RefPtr<CSSValue> val2; 2512 RefPtr<CSSValue> val3; 2513 CSSPropertyID propId1, propId2, propId3; 2514 if (parseTransformOrigin(propId, propId1, propId2, propId3, val1, val2, val3)) { 2515 addProperty(propId1, val1.release(), important); 2516 if (val2) 2517 addProperty(propId2, val2.release(), important); 2518 if (val3) 2519 addProperty(propId3, val3.release(), important); 2520 return true; 2521 } 2522 return false; 2523 } 2524 case CSSPropertyWebkitPerspective: 2525 if (id == CSSValueNone) 2526 validPrimitive = true; 2527 else { 2528 // Accepting valueless numbers is a quirk of the -webkit prefixed version of the property. 2529 if (validUnit(value, FNumber | FLength | FNonNeg)) { 2530 RefPtr<CSSValue> val = createPrimitiveNumericValue(value); 2531 if (val) { 2532 addProperty(propId, val.release(), important); 2533 return true; 2534 } 2535 return false; 2536 } 2537 } 2538 break; 2539 case CSSPropertyWebkitPerspectiveOrigin: 2540 case CSSPropertyWebkitPerspectiveOriginX: 2541 case CSSPropertyWebkitPerspectiveOriginY: { 2542 RefPtr<CSSValue> val1; 2543 RefPtr<CSSValue> val2; 2544 CSSPropertyID propId1, propId2; 2545 if (parsePerspectiveOrigin(propId, propId1, propId2, val1, val2)) { 2546 addProperty(propId1, val1.release(), important); 2547 if (val2) 2548 addProperty(propId2, val2.release(), important); 2549 return true; 2550 } 2551 return false; 2552 } 2553 case CSSPropertyWebkitAnimationDelay: 2554 case CSSPropertyWebkitAnimationDirection: 2555 case CSSPropertyWebkitAnimationDuration: 2556 case CSSPropertyWebkitAnimationFillMode: 2557 case CSSPropertyWebkitAnimationName: 2558 case CSSPropertyWebkitAnimationPlayState: 2559 case CSSPropertyWebkitAnimationIterationCount: 2560 case CSSPropertyWebkitAnimationTimingFunction: 2561 case CSSPropertyTransitionDelay: 2562 case CSSPropertyTransitionDuration: 2563 case CSSPropertyTransitionTimingFunction: 2564 case CSSPropertyTransitionProperty: 2565 case CSSPropertyWebkitTransitionDelay: 2566 case CSSPropertyWebkitTransitionDuration: 2567 case CSSPropertyWebkitTransitionTimingFunction: 2568 case CSSPropertyWebkitTransitionProperty: { 2569 RefPtr<CSSValue> val; 2570 AnimationParseContext context; 2571 if (parseAnimationProperty(propId, val, context)) { 2572 addPropertyWithPrefixingVariant(propId, val.release(), important); 2573 return true; 2574 } 2575 return false; 2576 } 2577 case CSSPropertyWebkitJustifySelf: 2578 return parseJustifySelf(propId, important); 2579#if ENABLE(CSS_GRID_LAYOUT) 2580 case CSSPropertyWebkitGridAutoColumns: 2581 case CSSPropertyWebkitGridAutoRows: 2582 parsedValue = parseGridTrackSize(*m_valueList); 2583 break; 2584 2585 case CSSPropertyWebkitGridTemplateColumns: 2586 case CSSPropertyWebkitGridTemplateRows: 2587 parsedValue = parseGridTrackList(); 2588 break; 2589 2590 case CSSPropertyWebkitGridColumnStart: 2591 case CSSPropertyWebkitGridColumnEnd: 2592 case CSSPropertyWebkitGridRowStart: 2593 case CSSPropertyWebkitGridRowEnd: 2594 parsedValue = parseGridPosition(); 2595 break; 2596 2597 case CSSPropertyWebkitGridColumn: 2598 case CSSPropertyWebkitGridRow: { 2599 return parseGridItemPositionShorthand(propId, important); 2600 } 2601 2602 case CSSPropertyWebkitGridTemplate: 2603 return parseGridTemplateShorthand(important); 2604 2605 case CSSPropertyWebkitGrid: 2606 return parseGridShorthand(important); 2607 2608 case CSSPropertyWebkitGridArea: 2609 return parseGridAreaShorthand(important); 2610 2611 case CSSPropertyWebkitGridTemplateAreas: 2612 parsedValue = parseGridTemplateAreas(); 2613 break; 2614 case CSSPropertyWebkitGridAutoFlow: 2615 parsedValue = parseGridAutoFlow(*m_valueList); 2616 break; 2617#endif /* ENABLE(CSS_GRID_LAYOUT) */ 2618 case CSSPropertyWebkitMarginCollapse: { 2619 if (num == 1) { 2620 ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse); 2621 if (!parseValue(webkitMarginCollapseShorthand().properties()[0], important)) 2622 return false; 2623 CSSValue* value = m_parsedProperties.last().value(); 2624 addProperty(webkitMarginCollapseShorthand().properties()[1], value, important); 2625 return true; 2626 } 2627 else if (num == 2) { 2628 ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse); 2629 if (!parseValue(webkitMarginCollapseShorthand().properties()[0], important) || !parseValue(webkitMarginCollapseShorthand().properties()[1], important)) 2630 return false; 2631 return true; 2632 } 2633 return false; 2634 } 2635 case CSSPropertyTextLineThroughWidth: 2636 case CSSPropertyTextOverlineWidth: 2637 case CSSPropertyTextUnderlineWidth: 2638 if (id == CSSValueAuto || id == CSSValueNormal || id == CSSValueThin || 2639 id == CSSValueMedium || id == CSSValueThick) 2640 validPrimitive = true; 2641 else 2642 validPrimitive = !id && validUnit(value, FNumber | FLength | FPercent); 2643 break; 2644 case CSSPropertyWebkitColumnCount: 2645 if (id == CSSValueAuto) 2646 validPrimitive = true; 2647 else 2648 validPrimitive = !id && validUnit(value, FPositiveInteger, CSSQuirksMode); 2649 break; 2650 case CSSPropertyWebkitColumnGap: // normal | <length> 2651 if (id == CSSValueNormal) 2652 validPrimitive = true; 2653 else 2654 validPrimitive = validUnit(value, FLength | FNonNeg); 2655 break; 2656 case CSSPropertyWebkitColumnAxis: 2657 if (id == CSSValueHorizontal || id == CSSValueVertical || id == CSSValueAuto) 2658 validPrimitive = true; 2659 break; 2660 case CSSPropertyWebkitColumnProgression: 2661 if (id == CSSValueNormal || id == CSSValueReverse) 2662 validPrimitive = true; 2663 break; 2664 case CSSPropertyWebkitColumnSpan: // none | all | 1 (will be dropped in the unprefixed property) 2665 if (id == CSSValueAll || id == CSSValueNone) 2666 validPrimitive = true; 2667 else 2668 validPrimitive = validUnit(value, FNumber | FNonNeg) && value->fValue == 1; 2669 break; 2670 case CSSPropertyWebkitColumnWidth: // auto | <length> 2671 if (id == CSSValueAuto) 2672 validPrimitive = true; 2673 else // Always parse this property in strict mode, since it would be ambiguous otherwise when used in the 'columns' shorthand property. 2674 validPrimitive = validUnit(value, FLength | FNonNeg, CSSStrictMode) && value->fValue; 2675 break; 2676 // End of CSS3 properties 2677 2678 // Apple specific properties. These will never be standardized and are purely to 2679 // support custom WebKit-based Apple applications. 2680 case CSSPropertyWebkitLineClamp: 2681 // When specifying number of lines, don't allow 0 as a valid value 2682 // When specifying either type of unit, require non-negative integers 2683 validPrimitive = (!id && (value->unit == CSSPrimitiveValue::CSS_PERCENTAGE || value->fValue) && validUnit(value, FInteger | FPercent | FNonNeg, CSSQuirksMode)); 2684 break; 2685#if ENABLE(IOS_TEXT_AUTOSIZING) 2686 case CSSPropertyWebkitTextSizeAdjust: 2687 if (id == CSSValueAuto || id == CSSValueNone) 2688 validPrimitive = true; 2689 else { 2690 // FIXME: Handle multilength case where we allow relative units. 2691 validPrimitive = (!id && validUnit(value, FPercent | FNonNeg, CSSStrictMode)); 2692 } 2693 break; 2694#endif 2695 2696 case CSSPropertyWebkitFontSizeDelta: // <length> 2697 validPrimitive = validUnit(value, FLength); 2698 break; 2699 2700 case CSSPropertyWebkitHyphenateCharacter: 2701 if (id == CSSValueAuto || value->unit == CSSPrimitiveValue::CSS_STRING) 2702 validPrimitive = true; 2703 break; 2704 2705 case CSSPropertyWebkitHyphenateLimitBefore: 2706 case CSSPropertyWebkitHyphenateLimitAfter: 2707 if (id == CSSValueAuto || validUnit(value, FInteger | FNonNeg, CSSStrictMode)) 2708 validPrimitive = true; 2709 break; 2710 2711 case CSSPropertyWebkitHyphenateLimitLines: 2712 if (id == CSSValueNoLimit || validUnit(value, FInteger | FNonNeg, CSSStrictMode)) 2713 validPrimitive = true; 2714 break; 2715 2716 case CSSPropertyWebkitLineGrid: 2717 if (id == CSSValueNone) 2718 validPrimitive = true; 2719 else if (value->unit == CSSPrimitiveValue::CSS_IDENT) { 2720 String lineGridValue = String(value->string); 2721 if (!lineGridValue.isEmpty()) { 2722 addProperty(propId, cssValuePool().createValue(lineGridValue, CSSPrimitiveValue::CSS_STRING), important); 2723 return true; 2724 } 2725 } 2726 break; 2727 case CSSPropertyWebkitLocale: 2728 if (id == CSSValueAuto || value->unit == CSSPrimitiveValue::CSS_STRING) 2729 validPrimitive = true; 2730 break; 2731 2732#if ENABLE(DASHBOARD_SUPPORT) 2733 case CSSPropertyWebkitDashboardRegion: // <dashboard-region> | <dashboard-region> 2734 if (value->unit == CSSParserValue::Function || id == CSSValueNone) 2735 return parseDashboardRegions(propId, important); 2736 break; 2737#endif 2738 2739#if PLATFORM(IOS) 2740 // FIXME: CSSPropertyWebkitCompositionFillColor shouldn't be iOS-specific. Once we fix up its usage in 2741 // InlineTextBox::paintCompositionBackground() we should move it outside the PLATFORM(IOS)-guard. 2742 // See <https://bugs.webkit.org/show_bug.cgi?id=126296>. 2743 case CSSPropertyWebkitCompositionFillColor: 2744 if ((id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu 2745 || (id >= CSSValueWebkitFocusRingColor && id < CSSValueWebkitText && inQuirksMode())) { 2746 validPrimitive = true; 2747 } else { 2748 parsedValue = parseColor(); 2749 if (parsedValue) 2750 m_valueList->next(); 2751 } 2752 break; 2753 case CSSPropertyWebkitTouchCallout: 2754 if (id == CSSValueDefault || id == CSSValueNone) 2755 validPrimitive = true; 2756 break; 2757#endif 2758#if ENABLE(TOUCH_EVENTS) 2759 case CSSPropertyWebkitTapHighlightColor: 2760 if ((id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu 2761 || (id >= CSSValueWebkitFocusRingColor && id < CSSValueWebkitText && inQuirksMode())) { 2762 validPrimitive = true; 2763 } else { 2764 parsedValue = parseColor(); 2765 if (parsedValue) 2766 m_valueList->next(); 2767 } 2768 break; 2769#endif 2770 // End Apple-specific properties 2771 2772 /* shorthand properties */ 2773 case CSSPropertyBackground: { 2774 // Position must come before color in this array because a plain old "0" is a legal color 2775 // in quirks mode but it's usually the X coordinate of a position. 2776 const CSSPropertyID properties[] = { CSSPropertyBackgroundImage, CSSPropertyBackgroundRepeat, 2777 CSSPropertyBackgroundAttachment, CSSPropertyBackgroundPosition, CSSPropertyBackgroundOrigin, 2778 CSSPropertyBackgroundClip, CSSPropertyBackgroundColor, CSSPropertyBackgroundSize }; 2779 return parseFillShorthand(propId, properties, WTF_ARRAY_LENGTH(properties), important); 2780 } 2781 case CSSPropertyWebkitMask: { 2782 const CSSPropertyID properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskSourceType, CSSPropertyWebkitMaskRepeat, 2783 CSSPropertyWebkitMaskPosition, CSSPropertyWebkitMaskOrigin, CSSPropertyWebkitMaskClip, CSSPropertyWebkitMaskSize }; 2784 return parseFillShorthand(propId, properties, WTF_ARRAY_LENGTH(properties), important); 2785 } 2786 case CSSPropertyBorder: 2787 // [ 'border-width' || 'border-style' || <color> ] | inherit 2788 { 2789 if (parseShorthand(propId, borderAbridgedShorthand(), important)) { 2790 // The CSS3 Borders and Backgrounds specification says that border also resets border-image. It's as 2791 // though a value of none was specified for the image. 2792 addExpandedPropertyForValue(CSSPropertyBorderImage, cssValuePool().createImplicitInitialValue(), important); 2793 return true; 2794 } 2795 return false; 2796 } 2797 case CSSPropertyBorderTop: 2798 // [ 'border-top-width' || 'border-style' || <color> ] | inherit 2799 return parseShorthand(propId, borderTopShorthand(), important); 2800 case CSSPropertyBorderRight: 2801 // [ 'border-right-width' || 'border-style' || <color> ] | inherit 2802 return parseShorthand(propId, borderRightShorthand(), important); 2803 case CSSPropertyBorderBottom: 2804 // [ 'border-bottom-width' || 'border-style' || <color> ] | inherit 2805 return parseShorthand(propId, borderBottomShorthand(), important); 2806 case CSSPropertyBorderLeft: 2807 // [ 'border-left-width' || 'border-style' || <color> ] | inherit 2808 return parseShorthand(propId, borderLeftShorthand(), important); 2809 case CSSPropertyWebkitBorderStart: 2810 return parseShorthand(propId, webkitBorderStartShorthand(), important); 2811 case CSSPropertyWebkitBorderEnd: 2812 return parseShorthand(propId, webkitBorderEndShorthand(), important); 2813 case CSSPropertyWebkitBorderBefore: 2814 return parseShorthand(propId, webkitBorderBeforeShorthand(), important); 2815 case CSSPropertyWebkitBorderAfter: 2816 return parseShorthand(propId, webkitBorderAfterShorthand(), important); 2817 case CSSPropertyOutline: 2818 // [ 'outline-color' || 'outline-style' || 'outline-width' ] | inherit 2819 return parseShorthand(propId, outlineShorthand(), important); 2820 case CSSPropertyBorderColor: 2821 // <color>{1,4} | inherit 2822 return parse4Values(propId, borderColorShorthand().properties(), important); 2823 case CSSPropertyBorderWidth: 2824 // <border-width>{1,4} | inherit 2825 return parse4Values(propId, borderWidthShorthand().properties(), important); 2826 case CSSPropertyBorderStyle: 2827 // <border-style>{1,4} | inherit 2828 return parse4Values(propId, borderStyleShorthand().properties(), important); 2829 case CSSPropertyMargin: 2830 // <margin-width>{1,4} | inherit 2831 return parse4Values(propId, marginShorthand().properties(), important); 2832 case CSSPropertyPadding: 2833 // <padding-width>{1,4} | inherit 2834 return parse4Values(propId, paddingShorthand().properties(), important); 2835 case CSSPropertyWebkitFlexFlow: 2836 return parseShorthand(propId, webkitFlexFlowShorthand(), important); 2837 case CSSPropertyFont: 2838 // [ [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 2839 // 'font-family' ] | caption | icon | menu | message-box | small-caption | status-bar | inherit 2840 if (id >= CSSValueCaption && id <= CSSValueStatusBar) 2841 validPrimitive = true; 2842 else 2843 return parseFont(important); 2844 break; 2845 case CSSPropertyListStyle: 2846 return parseShorthand(propId, listStyleShorthand(), important); 2847 case CSSPropertyWebkitColumns: 2848 return parseShorthand(propId, webkitColumnsShorthand(), important); 2849 case CSSPropertyWebkitColumnRule: 2850 return parseShorthand(propId, webkitColumnRuleShorthand(), important); 2851 case CSSPropertyWebkitTextStroke: 2852 return parseShorthand(propId, webkitTextStrokeShorthand(), important); 2853 case CSSPropertyWebkitAnimation: 2854 return parseAnimationShorthand(important); 2855 case CSSPropertyTransition: 2856 case CSSPropertyWebkitTransition: 2857 return parseTransitionShorthand(propId, important); 2858 case CSSPropertyInvalid: 2859 return false; 2860 case CSSPropertyPage: 2861 return parsePage(propId, important); 2862 case CSSPropertyFontStretch: 2863 case CSSPropertyTextLineThrough: 2864 case CSSPropertyTextOverline: 2865 case CSSPropertyTextUnderline: 2866 return false; 2867 // CSS Text Layout Module Level 3: Vertical writing support 2868 case CSSPropertyWebkitTextEmphasis: 2869 return parseShorthand(propId, webkitTextEmphasisShorthand(), important); 2870 2871 case CSSPropertyWebkitTextEmphasisStyle: 2872 return parseTextEmphasisStyle(important); 2873 2874 case CSSPropertyWebkitTextEmphasisPosition: 2875 return parseTextEmphasisPosition(important); 2876 2877 case CSSPropertyWebkitTextOrientation: 2878 // FIXME: For now just support sideways, sideways-right, upright and vertical-right. 2879 if (id == CSSValueSideways || id == CSSValueSidewaysRight || id == CSSValueVerticalRight || id == CSSValueUpright) 2880 validPrimitive = true; 2881 break; 2882 2883 case CSSPropertyWebkitLineBoxContain: 2884 if (id == CSSValueNone) 2885 validPrimitive = true; 2886 else 2887 return parseLineBoxContain(important); 2888 break; 2889 case CSSPropertyWebkitFontFeatureSettings: 2890 if (id == CSSValueNormal) 2891 validPrimitive = true; 2892 else 2893 return parseFontFeatureSettings(important); 2894 break; 2895 2896 case CSSPropertyWebkitFontVariantLigatures: 2897 if (id == CSSValueNormal) 2898 validPrimitive = true; 2899 else 2900 return parseFontVariantLigatures(important); 2901 break; 2902 case CSSPropertyWebkitClipPath: 2903 parsedValue = parseClipPath(); 2904 break; 2905#if ENABLE(CSS_SHAPES) 2906 case CSSPropertyWebkitShapeOutside: 2907 parsedValue = parseShapeProperty(propId); 2908 break; 2909 case CSSPropertyWebkitShapeMargin: 2910 validPrimitive = (RuntimeEnabledFeatures::sharedFeatures().cssShapesEnabled() && !id && validUnit(value, FLength | FPercent | FNonNeg)); 2911 break; 2912 case CSSPropertyWebkitShapeImageThreshold: 2913 validPrimitive = (RuntimeEnabledFeatures::sharedFeatures().cssShapesEnabled() && !id && validUnit(value, FNumber)); 2914 break; 2915#endif 2916#if ENABLE(CSS_IMAGE_ORIENTATION) 2917 case CSSPropertyImageOrientation: 2918 validPrimitive = !id && validUnit(value, FAngle); 2919 break; 2920#endif 2921#if ENABLE(CSS_IMAGE_RESOLUTION) 2922 case CSSPropertyImageResolution: 2923 parsedValue = parseImageResolution(); 2924 break; 2925#endif 2926 case CSSPropertyBorderBottomStyle: 2927 case CSSPropertyBorderCollapse: 2928 case CSSPropertyBorderLeftStyle: 2929 case CSSPropertyBorderRightStyle: 2930 case CSSPropertyBorderTopStyle: 2931 case CSSPropertyBoxSizing: 2932 case CSSPropertyCaptionSide: 2933 case CSSPropertyClear: 2934 case CSSPropertyDirection: 2935 case CSSPropertyDisplay: 2936 case CSSPropertyEmptyCells: 2937 case CSSPropertyFloat: 2938 case CSSPropertyFontStyle: 2939 case CSSPropertyImageRendering: 2940 case CSSPropertyListStylePosition: 2941 case CSSPropertyListStyleType: 2942 case CSSPropertyObjectFit: 2943 case CSSPropertyOutlineStyle: 2944 case CSSPropertyOverflowWrap: 2945 case CSSPropertyOverflowX: 2946 case CSSPropertyOverflowY: 2947 case CSSPropertyPageBreakAfter: 2948 case CSSPropertyPageBreakBefore: 2949 case CSSPropertyPageBreakInside: 2950 case CSSPropertyPointerEvents: 2951 case CSSPropertyPosition: 2952 case CSSPropertyResize: 2953 case CSSPropertySpeak: 2954 case CSSPropertyTableLayout: 2955 case CSSPropertyTextLineThroughMode: 2956 case CSSPropertyTextLineThroughStyle: 2957 case CSSPropertyTextOverflow: 2958 case CSSPropertyTextOverlineMode: 2959 case CSSPropertyTextOverlineStyle: 2960 case CSSPropertyTextRendering: 2961 case CSSPropertyTextTransform: 2962 case CSSPropertyTextUnderlineMode: 2963 case CSSPropertyTextUnderlineStyle: 2964 case CSSPropertyVisibility: 2965 case CSSPropertyWebkitAppearance: 2966 case CSSPropertyWebkitBackfaceVisibility: 2967 case CSSPropertyWebkitBorderAfterStyle: 2968 case CSSPropertyWebkitBorderBeforeStyle: 2969 case CSSPropertyWebkitBorderEndStyle: 2970 case CSSPropertyWebkitBorderFit: 2971 case CSSPropertyWebkitBorderStartStyle: 2972 case CSSPropertyWebkitBoxAlign: 2973#if ENABLE(CSS_BOX_DECORATION_BREAK) 2974 case CSSPropertyWebkitBoxDecorationBreak: 2975#endif 2976 case CSSPropertyWebkitBoxDirection: 2977 case CSSPropertyWebkitBoxLines: 2978 case CSSPropertyWebkitBoxOrient: 2979 case CSSPropertyWebkitBoxPack: 2980 case CSSPropertyWebkitColorCorrection: 2981 case CSSPropertyWebkitColumnBreakAfter: 2982 case CSSPropertyWebkitColumnBreakBefore: 2983 case CSSPropertyWebkitColumnBreakInside: 2984 case CSSPropertyWebkitColumnFill: 2985 case CSSPropertyWebkitColumnRuleStyle: 2986 case CSSPropertyWebkitAlignContent: 2987 case CSSPropertyWebkitAlignItems: 2988 case CSSPropertyWebkitAlignSelf: 2989 case CSSPropertyWebkitFlexDirection: 2990 case CSSPropertyWebkitFlexWrap: 2991 case CSSPropertyWebkitJustifyContent: 2992 case CSSPropertyWebkitFontKerning: 2993 case CSSPropertyWebkitFontSmoothing: 2994 case CSSPropertyWebkitHyphens: 2995 case CSSPropertyWebkitLineAlign: 2996 case CSSPropertyWebkitLineBreak: 2997 case CSSPropertyWebkitLineSnap: 2998 case CSSPropertyWebkitMarginAfterCollapse: 2999 case CSSPropertyWebkitMarginBeforeCollapse: 3000 case CSSPropertyWebkitMarginBottomCollapse: 3001 case CSSPropertyWebkitMarginTopCollapse: 3002 case CSSPropertyWebkitMarqueeDirection: 3003 case CSSPropertyWebkitMarqueeStyle: 3004 case CSSPropertyWebkitNbspMode: 3005#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) 3006 case CSSPropertyWebkitOverflowScrolling: 3007#endif 3008 case CSSPropertyWebkitPrintColorAdjust: 3009#if ENABLE(CSS_REGIONS) 3010 case CSSPropertyWebkitRegionBreakAfter: 3011 case CSSPropertyWebkitRegionBreakBefore: 3012 case CSSPropertyWebkitRegionBreakInside: 3013 case CSSPropertyWebkitRegionFragment: 3014#endif 3015 case CSSPropertyWebkitRtlOrdering: 3016 case CSSPropertyWebkitRubyPosition: 3017#if ENABLE(CSS3_TEXT) 3018 case CSSPropertyWebkitTextAlignLast: 3019#endif // CSS3_TEXT 3020 case CSSPropertyWebkitTextCombine: 3021#if ENABLE(CSS3_TEXT) 3022 case CSSPropertyWebkitTextJustify: 3023#endif // CSS3_TEXT 3024 case CSSPropertyWebkitTextSecurity: 3025 case CSSPropertyWebkitTransformStyle: 3026 case CSSPropertyWebkitUserDrag: 3027 case CSSPropertyWebkitUserModify: 3028 case CSSPropertyWebkitUserSelect: 3029 case CSSPropertyWebkitWritingMode: 3030 case CSSPropertyWhiteSpace: 3031 case CSSPropertyWordBreak: 3032 case CSSPropertyWordWrap: 3033 // These properties should be handled before in isValidKeywordPropertyAndValue(). 3034 ASSERT_NOT_REACHED(); 3035 return false; 3036#if ENABLE(CSS_DEVICE_ADAPTATION) 3037 // Properties bellow are validated inside parseViewportProperty, because we 3038 // check for parser state inViewportScope. We need to invalidate if someone 3039 // adds them outside a @viewport rule. 3040 case CSSPropertyMaxZoom: 3041 case CSSPropertyMinZoom: 3042 case CSSPropertyOrientation: 3043 case CSSPropertyUserZoom: 3044 validPrimitive = false; 3045 break; 3046#endif 3047 default: 3048 return parseSVGValue(propId, important); 3049 } 3050 3051 if (validPrimitive) { 3052 parsedValue = parseValidPrimitive(id, value); 3053 m_valueList->next(); 3054 } 3055 ASSERT(!m_parsedCalculation); 3056 if (parsedValue) { 3057 if (!m_valueList->current() || inShorthand()) { 3058 addProperty(propId, parsedValue.release(), important); 3059 return true; 3060 } 3061 } 3062 return false; 3063} 3064 3065void CSSParser::addFillValue(RefPtr<CSSValue>& lval, PassRefPtr<CSSValue> rval) 3066{ 3067 if (lval) { 3068 if (lval->isBaseValueList()) 3069 toCSSValueList(lval.get())->append(rval); 3070 else { 3071 PassRefPtr<CSSValue> oldlVal(lval.release()); 3072 PassRefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 3073 list->append(oldlVal); 3074 list->append(rval); 3075 lval = list; 3076 } 3077 } 3078 else 3079 lval = rval; 3080} 3081 3082static bool isItemPositionKeyword(CSSValueID id) 3083{ 3084 return id == CSSValueStart || id == CSSValueEnd || id == CSSValueCenter 3085 || id == CSSValueSelfStart || id == CSSValueSelfEnd || id == CSSValueFlexStart 3086 || id == CSSValueFlexEnd || id == CSSValueLeft || id == CSSValueRight; 3087} 3088 3089bool CSSParser::parseJustifySelf(CSSPropertyID propId, bool important) 3090{ 3091 // auto | baseline | stretch | [<item-position> && <overflow-position>? ] 3092 // <item-position> = center | start | end | self-start | self-end | flex-start | flex-end | left | right; 3093 // <overflow-position> = true | safe 3094 3095 CSSParserValue* value = m_valueList->current(); 3096 3097 if (value->id == CSSValueAuto || value->id == CSSValueBaseline || value->id == CSSValueStretch) { 3098 if (m_valueList->next()) 3099 return false; 3100 3101 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 3102 return true; 3103 } 3104 3105 RefPtr<CSSPrimitiveValue> position = 0; 3106 RefPtr<CSSPrimitiveValue> overflowAlignmentKeyword = 0; 3107 if (isItemPositionKeyword(value->id)) { 3108 position = cssValuePool().createIdentifierValue(value->id); 3109 value = m_valueList->next(); 3110 if (value) { 3111 if (value->id != CSSValueTrue && value->id != CSSValueSafe) 3112 return false; 3113 overflowAlignmentKeyword = cssValuePool().createIdentifierValue(value->id); 3114 } 3115 } else if (value->id != CSSValueTrue && value->id != CSSValueSafe) 3116 return false; 3117 else { 3118 overflowAlignmentKeyword = cssValuePool().createIdentifierValue(value->id); 3119 value = m_valueList->next(); 3120 if (value) { 3121 if (!isItemPositionKeyword(value->id)) 3122 return false; 3123 position = cssValuePool().createIdentifierValue(value->id); 3124 } 3125 } 3126 3127 if (m_valueList->next()) 3128 return false; 3129 3130 ASSERT(position); 3131 if (overflowAlignmentKeyword) 3132 addProperty(propId, createPrimitiveValuePair(position.release(), overflowAlignmentKeyword.release()), important); 3133 else 3134 addProperty(propId, position.release(), important); 3135 3136 return true; 3137} 3138 3139static bool parseBackgroundClip(CSSParserValue* parserValue, RefPtr<CSSValue>& cssValue) 3140{ 3141 if (parserValue->id == CSSValueBorderBox || parserValue->id == CSSValuePaddingBox 3142 || parserValue->id == CSSValueContentBox || parserValue->id == CSSValueWebkitText) { 3143 cssValue = cssValuePool().createIdentifierValue(parserValue->id); 3144 return true; 3145 } 3146 return false; 3147} 3148 3149bool CSSParser::useLegacyBackgroundSizeShorthandBehavior() const 3150{ 3151 return m_context.useLegacyBackgroundSizeShorthandBehavior; 3152} 3153 3154const int cMaxFillProperties = 9; 3155 3156bool CSSParser::parseFillShorthand(CSSPropertyID propId, const CSSPropertyID* properties, int numProperties, bool important) 3157{ 3158 ASSERT(numProperties <= cMaxFillProperties); 3159 if (numProperties > cMaxFillProperties) 3160 return false; 3161 3162 ShorthandScope scope(this, propId); 3163 3164 bool parsedProperty[cMaxFillProperties] = { false }; 3165 RefPtr<CSSValue> values[cMaxFillProperties]; 3166 RefPtr<CSSValue> clipValue; 3167 RefPtr<CSSValue> positionYValue; 3168 RefPtr<CSSValue> repeatYValue; 3169 bool foundClip = false; 3170 int i; 3171 bool foundPositionCSSProperty = false; 3172 3173 while (m_valueList->current()) { 3174 CSSParserValue* val = m_valueList->current(); 3175 if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 3176 // We hit the end. Fill in all remaining values with the initial value. 3177 m_valueList->next(); 3178 for (i = 0; i < numProperties; ++i) { 3179 if (properties[i] == CSSPropertyBackgroundColor && parsedProperty[i]) 3180 // Color is not allowed except as the last item in a list for backgrounds. 3181 // Reject the entire property. 3182 return false; 3183 3184 if (!parsedProperty[i] && properties[i] != CSSPropertyBackgroundColor) { 3185 addFillValue(values[i], cssValuePool().createImplicitInitialValue()); 3186 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 3187 addFillValue(positionYValue, cssValuePool().createImplicitInitialValue()); 3188 if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) 3189 addFillValue(repeatYValue, cssValuePool().createImplicitInitialValue()); 3190 if ((properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) && !parsedProperty[i]) { 3191 // If background-origin wasn't present, then reset background-clip also. 3192 addFillValue(clipValue, cssValuePool().createImplicitInitialValue()); 3193 } 3194 } 3195 parsedProperty[i] = false; 3196 } 3197 if (!m_valueList->current()) 3198 break; 3199 } 3200 3201 bool sizeCSSPropertyExpected = false; 3202 if (isForwardSlashOperator(val) && foundPositionCSSProperty) { 3203 sizeCSSPropertyExpected = true; 3204 m_valueList->next(); 3205 } 3206 3207 foundPositionCSSProperty = false; 3208 bool found = false; 3209 for (i = 0; !found && i < numProperties; ++i) { 3210 3211 if (sizeCSSPropertyExpected && (properties[i] != CSSPropertyBackgroundSize && properties[i] != CSSPropertyWebkitMaskSize)) 3212 continue; 3213 if (!sizeCSSPropertyExpected && (properties[i] == CSSPropertyBackgroundSize || properties[i] == CSSPropertyWebkitMaskSize)) 3214 continue; 3215 3216 if (!parsedProperty[i]) { 3217 RefPtr<CSSValue> val1; 3218 RefPtr<CSSValue> val2; 3219 CSSPropertyID propId1, propId2; 3220 CSSParserValue* parserValue = m_valueList->current(); 3221 if (parseFillProperty(properties[i], propId1, propId2, val1, val2)) { 3222 parsedProperty[i] = found = true; 3223 addFillValue(values[i], val1.release()); 3224 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 3225 addFillValue(positionYValue, val2.release()); 3226 if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) 3227 addFillValue(repeatYValue, val2.release()); 3228 if (properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) { 3229 // Reparse the value as a clip, and see if we succeed. 3230 if (parseBackgroundClip(parserValue, val1)) 3231 addFillValue(clipValue, val1.release()); // The property parsed successfully. 3232 else 3233 addFillValue(clipValue, cssValuePool().createImplicitInitialValue()); // Some value was used for origin that is not supported by clip. Just reset clip instead. 3234 } 3235 if (properties[i] == CSSPropertyBackgroundClip || properties[i] == CSSPropertyWebkitMaskClip) 3236 foundClip = true; 3237 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 3238 foundPositionCSSProperty = true; 3239 } 3240 } 3241 } 3242 3243 // if we didn't find at least one match, this is an 3244 // invalid shorthand and we have to ignore it 3245 if (!found) 3246 return false; 3247 } 3248 3249 // Now add all of the properties we found. 3250 for (i = 0; i < numProperties; i++) { 3251 // Fill in any remaining properties with the initial value. 3252 if (!parsedProperty[i]) { 3253 addFillValue(values[i], cssValuePool().createImplicitInitialValue()); 3254 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 3255 addFillValue(positionYValue, cssValuePool().createImplicitInitialValue()); 3256 if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) 3257 addFillValue(repeatYValue, cssValuePool().createImplicitInitialValue()); 3258 if (properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) { 3259 // If background-origin wasn't present, then reset background-clip also. 3260 addFillValue(clipValue, cssValuePool().createImplicitInitialValue()); 3261 } 3262 } 3263 if (properties[i] == CSSPropertyBackgroundPosition) { 3264 addProperty(CSSPropertyBackgroundPositionX, values[i].release(), important); 3265 // it's OK to call positionYValue.release() since we only see CSSPropertyBackgroundPosition once 3266 addProperty(CSSPropertyBackgroundPositionY, positionYValue.release(), important); 3267 } else if (properties[i] == CSSPropertyWebkitMaskPosition) { 3268 addProperty(CSSPropertyWebkitMaskPositionX, values[i].release(), important); 3269 // it's OK to call positionYValue.release() since we only see CSSPropertyWebkitMaskPosition once 3270 addProperty(CSSPropertyWebkitMaskPositionY, positionYValue.release(), important); 3271 } else if (properties[i] == CSSPropertyBackgroundRepeat) { 3272 addProperty(CSSPropertyBackgroundRepeatX, values[i].release(), important); 3273 // it's OK to call repeatYValue.release() since we only see CSSPropertyBackgroundPosition once 3274 addProperty(CSSPropertyBackgroundRepeatY, repeatYValue.release(), important); 3275 } else if (properties[i] == CSSPropertyWebkitMaskRepeat) { 3276 addProperty(CSSPropertyWebkitMaskRepeatX, values[i].release(), important); 3277 // it's OK to call repeatYValue.release() since we only see CSSPropertyBackgroundPosition once 3278 addProperty(CSSPropertyWebkitMaskRepeatY, repeatYValue.release(), important); 3279 } else if ((properties[i] == CSSPropertyBackgroundClip || properties[i] == CSSPropertyWebkitMaskClip) && !foundClip) 3280 // Value is already set while updating origin 3281 continue; 3282 else if (properties[i] == CSSPropertyBackgroundSize && !parsedProperty[i] && useLegacyBackgroundSizeShorthandBehavior()) 3283 continue; 3284 else 3285 addProperty(properties[i], values[i].release(), important); 3286 3287 // Add in clip values when we hit the corresponding origin property. 3288 if (properties[i] == CSSPropertyBackgroundOrigin && !foundClip) 3289 addProperty(CSSPropertyBackgroundClip, clipValue.release(), important); 3290 else if (properties[i] == CSSPropertyWebkitMaskOrigin && !foundClip) 3291 addProperty(CSSPropertyWebkitMaskClip, clipValue.release(), important); 3292 } 3293 3294 return true; 3295} 3296 3297void CSSParser::addAnimationValue(RefPtr<CSSValue>& lval, PassRefPtr<CSSValue> rval) 3298{ 3299 if (lval) { 3300 if (lval->isValueList()) 3301 toCSSValueList(lval.get())->append(rval); 3302 else { 3303 PassRefPtr<CSSValue> oldVal(lval.release()); 3304 PassRefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 3305 list->append(oldVal); 3306 list->append(rval); 3307 lval = list; 3308 } 3309 } 3310 else 3311 lval = rval; 3312} 3313 3314bool CSSParser::parseAnimationShorthand(bool important) 3315{ 3316 const StylePropertyShorthand& animationProperties = webkitAnimationShorthandForParsing(); 3317 const unsigned numProperties = 7; 3318 3319 // The list of properties in the shorthand should be the same 3320 // length as the list with animation name in last position, even though they are 3321 // in a different order. 3322 ASSERT(numProperties == webkitAnimationShorthandForParsing().length()); 3323 ASSERT(numProperties == webkitAnimationShorthand().length()); 3324 3325 ShorthandScope scope(this, CSSPropertyWebkitAnimation); 3326 3327 bool parsedProperty[numProperties] = { false }; 3328 AnimationParseContext context; 3329 RefPtr<CSSValue> values[numProperties]; 3330 3331 unsigned i; 3332 while (m_valueList->current()) { 3333 CSSParserValue* val = m_valueList->current(); 3334 if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 3335 // We hit the end. Fill in all remaining values with the initial value. 3336 m_valueList->next(); 3337 for (i = 0; i < numProperties; ++i) { 3338 if (!parsedProperty[i]) 3339 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3340 parsedProperty[i] = false; 3341 } 3342 if (!m_valueList->current()) 3343 break; 3344 context.commitFirstAnimation(); 3345 } 3346 3347 bool found = false; 3348 for (i = 0; i < numProperties; ++i) { 3349 if (!parsedProperty[i]) { 3350 RefPtr<CSSValue> val; 3351 if (parseAnimationProperty(animationProperties.properties()[i], val, context)) { 3352 parsedProperty[i] = found = true; 3353 addAnimationValue(values[i], val.release()); 3354 break; 3355 } 3356 } 3357 3358 // There are more values to process but 'none' or 'all' were already defined as the animation property, the declaration becomes invalid. 3359 if (!context.animationPropertyKeywordAllowed() && context.hasCommittedFirstAnimation()) 3360 return false; 3361 } 3362 3363 // if we didn't find at least one match, this is an 3364 // invalid shorthand and we have to ignore it 3365 if (!found) 3366 return false; 3367 } 3368 3369 for (i = 0; i < numProperties; ++i) { 3370 // If we didn't find the property, set an intial value. 3371 if (!parsedProperty[i]) 3372 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3373 3374 addProperty(animationProperties.properties()[i], values[i].release(), important); 3375 } 3376 3377 return true; 3378} 3379 3380bool CSSParser::parseTransitionShorthand(CSSPropertyID propId, bool important) 3381{ 3382 const unsigned numProperties = 4; 3383 const StylePropertyShorthand& shorthand = shorthandForProperty(propId); 3384 ASSERT(numProperties == shorthand.length()); 3385 3386 ShorthandScope scope(this, propId); 3387 3388 bool parsedProperty[numProperties] = { false }; 3389 AnimationParseContext context; 3390 RefPtr<CSSValue> values[numProperties]; 3391 3392 unsigned i; 3393 while (m_valueList->current()) { 3394 CSSParserValue* val = m_valueList->current(); 3395 if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 3396 // We hit the end. Fill in all remaining values with the initial value. 3397 m_valueList->next(); 3398 for (i = 0; i < numProperties; ++i) { 3399 if (!parsedProperty[i]) 3400 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3401 parsedProperty[i] = false; 3402 } 3403 if (!m_valueList->current()) 3404 break; 3405 context.commitFirstAnimation(); 3406 } 3407 3408 bool found = false; 3409 for (i = 0; !found && i < numProperties; ++i) { 3410 if (!parsedProperty[i]) { 3411 RefPtr<CSSValue> val; 3412 if (parseAnimationProperty(shorthand.properties()[i], val, context)) { 3413 parsedProperty[i] = found = true; 3414 addAnimationValue(values[i], val.release()); 3415 } 3416 3417 // There are more values to process but 'none' or 'all' were already defined as the animation property, the declaration becomes invalid. 3418 if (!context.animationPropertyKeywordAllowed() && context.hasCommittedFirstAnimation()) 3419 return false; 3420 } 3421 } 3422 3423 // if we didn't find at least one match, this is an 3424 // invalid shorthand and we have to ignore it 3425 if (!found) 3426 return false; 3427 } 3428 3429 // Fill in any remaining properties with the initial value. 3430 for (i = 0; i < numProperties; ++i) { 3431 if (!parsedProperty[i]) 3432 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3433 } 3434 3435 // Now add all of the properties we found. 3436 for (i = 0; i < numProperties; i++) 3437 addPropertyWithPrefixingVariant(shorthand.properties()[i], values[i].release(), important); 3438 3439 return true; 3440} 3441 3442bool CSSParser::parseShorthand(CSSPropertyID propId, const StylePropertyShorthand& shorthand, bool important) 3443{ 3444 // We try to match as many properties as possible 3445 // We set up an array of booleans to mark which property has been found, 3446 // and we try to search for properties until it makes no longer any sense. 3447 ShorthandScope scope(this, propId); 3448 3449 bool found = false; 3450 unsigned propertiesParsed = 0; 3451 bool propertyFound[6]= { false, false, false, false, false, false }; // 6 is enough size. 3452 3453 while (m_valueList->current()) { 3454 found = false; 3455 for (unsigned propIndex = 0; !found && propIndex < shorthand.length(); ++propIndex) { 3456 if (!propertyFound[propIndex] && parseValue(shorthand.properties()[propIndex], important)) { 3457 propertyFound[propIndex] = found = true; 3458 propertiesParsed++; 3459 } 3460 } 3461 3462 // if we didn't find at least one match, this is an 3463 // invalid shorthand and we have to ignore it 3464 if (!found) 3465 return false; 3466 } 3467 3468 if (propertiesParsed == shorthand.length()) 3469 return true; 3470 3471 // Fill in any remaining properties with the initial value. 3472 ImplicitScope implicitScope(this, PropertyImplicit); 3473 const StylePropertyShorthand* propertiesForInitialization = shorthand.propertiesForInitialization(); 3474 for (unsigned i = 0; i < shorthand.length(); ++i) { 3475 if (propertyFound[i]) 3476 continue; 3477 3478 if (propertiesForInitialization) { 3479 const StylePropertyShorthand& initProperties = propertiesForInitialization[i]; 3480 for (unsigned propIndex = 0; propIndex < initProperties.length(); ++propIndex) 3481 addProperty(initProperties.properties()[propIndex], cssValuePool().createImplicitInitialValue(), important); 3482 } else 3483 addProperty(shorthand.properties()[i], cssValuePool().createImplicitInitialValue(), important); 3484 } 3485 3486 return true; 3487} 3488 3489bool CSSParser::parse4Values(CSSPropertyID propId, const CSSPropertyID *properties, bool important) 3490{ 3491 /* From the CSS 2 specs, 8.3 3492 * If there is only one value, it applies to all sides. If there are two values, the top and 3493 * bottom margins are set to the first value and the right and left margins are set to the second. 3494 * If there are three values, the top is set to the first value, the left and right are set to the 3495 * second, and the bottom is set to the third. If there are four values, they apply to the top, 3496 * right, bottom, and left, respectively. 3497 */ 3498 3499 unsigned num = inShorthand() ? 1 : m_valueList->size(); 3500 3501 ShorthandScope scope(this, propId); 3502 3503 // the order is top, right, bottom, left 3504 switch (num) { 3505 case 1: { 3506 if (!parseValue(properties[0], important)) 3507 return false; 3508 CSSValue* value = m_parsedProperties.last().value(); 3509 ImplicitScope implicitScope(this, PropertyImplicit); 3510 addProperty(properties[1], value, important); 3511 addProperty(properties[2], value, important); 3512 addProperty(properties[3], value, important); 3513 break; 3514 } 3515 case 2: { 3516 if (!parseValue(properties[0], important) || !parseValue(properties[1], important)) 3517 return false; 3518 CSSValue* value = m_parsedProperties[m_parsedProperties.size() - 2].value(); 3519 ImplicitScope implicitScope(this, PropertyImplicit); 3520 addProperty(properties[2], value, important); 3521 value = m_parsedProperties[m_parsedProperties.size() - 2].value(); 3522 addProperty(properties[3], value, important); 3523 break; 3524 } 3525 case 3: { 3526 if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || !parseValue(properties[2], important)) 3527 return false; 3528 CSSValue* value = m_parsedProperties[m_parsedProperties.size() - 2].value(); 3529 ImplicitScope implicitScope(this, PropertyImplicit); 3530 addProperty(properties[3], value, important); 3531 break; 3532 } 3533 case 4: { 3534 if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || 3535 !parseValue(properties[2], important) || !parseValue(properties[3], important)) 3536 return false; 3537 break; 3538 } 3539 default: { 3540 return false; 3541 } 3542 } 3543 3544 return true; 3545} 3546 3547// auto | <identifier> 3548bool CSSParser::parsePage(CSSPropertyID propId, bool important) 3549{ 3550 ASSERT(propId == CSSPropertyPage); 3551 3552 if (m_valueList->size() != 1) 3553 return false; 3554 3555 CSSParserValue* value = m_valueList->current(); 3556 if (!value) 3557 return false; 3558 3559 if (value->id == CSSValueAuto) { 3560 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 3561 return true; 3562 } else if (value->id == 0 && value->unit == CSSPrimitiveValue::CSS_IDENT) { 3563 addProperty(propId, createPrimitiveStringValue(value), important); 3564 return true; 3565 } 3566 return false; 3567} 3568 3569// <length>{1,2} | auto | [ <page-size> || [ portrait | landscape] ] 3570bool CSSParser::parseSize(CSSPropertyID propId, bool important) 3571{ 3572 ASSERT(propId == CSSPropertySize); 3573 3574 if (m_valueList->size() > 2) 3575 return false; 3576 3577 CSSParserValue* value = m_valueList->current(); 3578 if (!value) 3579 return false; 3580 3581 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated(); 3582 3583 // First parameter. 3584 SizeParameterType paramType = parseSizeParameter(parsedValues.get(), value, None); 3585 if (paramType == None) 3586 return false; 3587 3588 // Second parameter, if any. 3589 value = m_valueList->next(); 3590 if (value) { 3591 paramType = parseSizeParameter(parsedValues.get(), value, paramType); 3592 if (paramType == None) 3593 return false; 3594 } 3595 3596 addProperty(propId, parsedValues.release(), important); 3597 return true; 3598} 3599 3600CSSParser::SizeParameterType CSSParser::parseSizeParameter(CSSValueList* parsedValues, CSSParserValue* value, SizeParameterType prevParamType) 3601{ 3602 switch (value->id) { 3603 case CSSValueAuto: 3604 if (prevParamType == None) { 3605 parsedValues->append(cssValuePool().createIdentifierValue(value->id)); 3606 return Auto; 3607 } 3608 return None; 3609 case CSSValueLandscape: 3610 case CSSValuePortrait: 3611 if (prevParamType == None || prevParamType == PageSize) { 3612 parsedValues->append(cssValuePool().createIdentifierValue(value->id)); 3613 return Orientation; 3614 } 3615 return None; 3616 case CSSValueA3: 3617 case CSSValueA4: 3618 case CSSValueA5: 3619 case CSSValueB4: 3620 case CSSValueB5: 3621 case CSSValueLedger: 3622 case CSSValueLegal: 3623 case CSSValueLetter: 3624 if (prevParamType == None || prevParamType == Orientation) { 3625 // Normalize to Page Size then Orientation order by prepending. 3626 // This is not specified by the CSS3 Paged Media specification, but for simpler processing later (StyleResolver::applyPageSizeProperty). 3627 parsedValues->prepend(cssValuePool().createIdentifierValue(value->id)); 3628 return PageSize; 3629 } 3630 return None; 3631 case 0: 3632 if (validUnit(value, FLength | FNonNeg) && (prevParamType == None || prevParamType == Length)) { 3633 parsedValues->append(createPrimitiveNumericValue(value)); 3634 return Length; 3635 } 3636 return None; 3637 default: 3638 return None; 3639 } 3640} 3641 3642// [ <string> <string> ]+ | inherit | none 3643// inherit and none are handled in parseValue. 3644bool CSSParser::parseQuotes(CSSPropertyID propId, bool important) 3645{ 3646 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); 3647 while (CSSParserValue* val = m_valueList->current()) { 3648 RefPtr<CSSValue> parsedValue; 3649 if (val->unit == CSSPrimitiveValue::CSS_STRING) 3650 parsedValue = CSSPrimitiveValue::create(val->string, CSSPrimitiveValue::CSS_STRING); 3651 else 3652 break; 3653 values->append(parsedValue.release()); 3654 m_valueList->next(); 3655 } 3656 if (values->length()) { 3657 addProperty(propId, values.release(), important); 3658 m_valueList->next(); 3659 return true; 3660 } 3661 return false; 3662} 3663 3664bool CSSParser::parseAlt(CSSPropertyID propID, bool important) 3665{ 3666 CSSParserValue* val = m_valueList->current(); 3667 RefPtr<CSSValue> parsedValue; 3668 3669 if (val->unit == CSSPrimitiveValue::CSS_STRING) 3670 parsedValue = createPrimitiveStringValue(val); 3671 else if (val->unit == CSSParserValue::Function) { 3672 CSSParserValueList* args = val->function->args.get(); 3673 if (!args) 3674 return false; 3675 if (equalIgnoringCase(val->function->name, "attr(")) 3676 parsedValue = parseAttr(args); 3677 } 3678 3679 if (parsedValue) { 3680 addProperty(propID, parsedValue.release(), important); 3681 m_valueList->next(); 3682 return true; 3683 } 3684 3685 return false; 3686} 3687 3688// [ <string> | <uri> | <counter> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit 3689// in CSS 2.1 this got somewhat reduced: 3690// [ <string> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit 3691bool CSSParser::parseContent(CSSPropertyID propId, bool important) 3692{ 3693 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); 3694 3695 while (CSSParserValue* val = m_valueList->current()) { 3696 RefPtr<CSSValue> parsedValue; 3697 if (val->unit == CSSPrimitiveValue::CSS_URI) { 3698 // url 3699 parsedValue = CSSImageValue::create(completeURL(val->string)); 3700 } else if (val->unit == CSSParserValue::Function) { 3701 // attr(X) | counter(X [,Y]) | counters(X, Y, [,Z]) | -webkit-gradient(...) 3702 CSSParserValueList* args = val->function->args.get(); 3703 if (!args) 3704 return false; 3705 if (equalIgnoringCase(val->function->name, "attr(")) { 3706 parsedValue = parseAttr(args); 3707 if (!parsedValue) 3708 return false; 3709 } else if (equalIgnoringCase(val->function->name, "counter(")) { 3710 parsedValue = parseCounterContent(args, false); 3711 if (!parsedValue) 3712 return false; 3713 } else if (equalIgnoringCase(val->function->name, "counters(")) { 3714 parsedValue = parseCounterContent(args, true); 3715 if (!parsedValue) 3716 return false; 3717#if ENABLE(CSS_IMAGE_SET) 3718 } else if (equalIgnoringCase(val->function->name, "-webkit-image-set(")) { 3719 parsedValue = parseImageSet(); 3720 if (!parsedValue) 3721 return false; 3722#endif 3723 } else if (isGeneratedImageValue(val)) { 3724 if (!parseGeneratedImage(m_valueList.get(), parsedValue)) 3725 return false; 3726 } else 3727 return false; 3728 } else if (val->unit == CSSPrimitiveValue::CSS_IDENT) { 3729 // open-quote 3730 // close-quote 3731 // no-open-quote 3732 // no-close-quote 3733 // inherit 3734 // FIXME: These are not yet implemented (http://bugs.webkit.org/show_bug.cgi?id=6503). 3735 // none 3736 // normal 3737 switch (val->id) { 3738 case CSSValueOpenQuote: 3739 case CSSValueCloseQuote: 3740 case CSSValueNoOpenQuote: 3741 case CSSValueNoCloseQuote: 3742 case CSSValueNone: 3743 case CSSValueNormal: 3744 parsedValue = cssValuePool().createIdentifierValue(val->id); 3745 break; 3746 default: 3747 break; 3748 } 3749 } else if (val->unit == CSSPrimitiveValue::CSS_STRING) { 3750 parsedValue = createPrimitiveStringValue(val); 3751 } 3752 if (!parsedValue) 3753 break; 3754 values->append(parsedValue.release()); 3755 m_valueList->next(); 3756 } 3757 3758 if (values->length()) { 3759 addProperty(propId, values.release(), important); 3760 m_valueList->next(); 3761 return true; 3762 } 3763 3764 return false; 3765} 3766 3767PassRefPtr<CSSValue> CSSParser::parseAttr(CSSParserValueList* args) 3768{ 3769 if (args->size() != 1) 3770 return 0; 3771 3772 CSSParserValue* a = args->current(); 3773 3774 if (a->unit != CSSPrimitiveValue::CSS_IDENT) 3775 return 0; 3776 3777 String attrName = a->string; 3778 // CSS allows identifiers with "-" at the start, like "-webkit-mask-image". 3779 // But HTML attribute names can't have those characters, and we should not 3780 // even parse them inside attr(). 3781 if (attrName[0] == '-') 3782 return 0; 3783 3784 if (m_context.isHTMLDocument) 3785 attrName = attrName.lower(); 3786 3787 return cssValuePool().createValue(attrName, CSSPrimitiveValue::CSS_ATTR); 3788} 3789 3790PassRefPtr<CSSValue> CSSParser::parseBackgroundColor() 3791{ 3792 CSSValueID id = m_valueList->current()->id; 3793 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || id == CSSValueCurrentcolor || 3794 (id >= CSSValueGrey && id < CSSValueWebkitText && inQuirksMode())) 3795 return cssValuePool().createIdentifierValue(id); 3796 return parseColor(); 3797} 3798 3799bool CSSParser::parseFillImage(CSSParserValueList* valueList, RefPtr<CSSValue>& value) 3800{ 3801 if (valueList->current()->id == CSSValueNone) { 3802 value = cssValuePool().createIdentifierValue(CSSValueNone); 3803 return true; 3804 } 3805 if (valueList->current()->unit == CSSPrimitiveValue::CSS_URI) { 3806 value = CSSImageValue::create(completeURL(valueList->current()->string)); 3807 return true; 3808 } 3809 3810 if (isGeneratedImageValue(valueList->current())) 3811 return parseGeneratedImage(valueList, value); 3812 3813#if ENABLE(CSS_IMAGE_SET) 3814 if (valueList->current()->unit == CSSParserValue::Function && equalIgnoringCase(valueList->current()->function->name, "-webkit-image-set(")) { 3815 value = parseImageSet(); 3816 if (value) 3817 return true; 3818 } 3819#endif 3820 3821 return false; 3822} 3823 3824PassRefPtr<CSSValue> CSSParser::parseFillPositionX(CSSParserValueList* valueList) 3825{ 3826 int id = valueList->current()->id; 3827 if (id == CSSValueLeft || id == CSSValueRight || id == CSSValueCenter) { 3828 int percent = 0; 3829 if (id == CSSValueRight) 3830 percent = 100; 3831 else if (id == CSSValueCenter) 3832 percent = 50; 3833 return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); 3834 } 3835 if (validUnit(valueList->current(), FPercent | FLength)) 3836 return createPrimitiveNumericValue(valueList->current()); 3837 return 0; 3838} 3839 3840PassRefPtr<CSSValue> CSSParser::parseFillPositionY(CSSParserValueList* valueList) 3841{ 3842 int id = valueList->current()->id; 3843 if (id == CSSValueTop || id == CSSValueBottom || id == CSSValueCenter) { 3844 int percent = 0; 3845 if (id == CSSValueBottom) 3846 percent = 100; 3847 else if (id == CSSValueCenter) 3848 percent = 50; 3849 return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); 3850 } 3851 if (validUnit(valueList->current(), FPercent | FLength)) 3852 return createPrimitiveNumericValue(valueList->current()); 3853 return 0; 3854} 3855 3856PassRefPtr<CSSPrimitiveValue> CSSParser::parseFillPositionComponent(CSSParserValueList* valueList, unsigned& cumulativeFlags, FillPositionFlag& individualFlag, FillPositionParsingMode parsingMode) 3857{ 3858 CSSValueID id = valueList->current()->id; 3859 if (id == CSSValueLeft || id == CSSValueTop || id == CSSValueRight || id == CSSValueBottom || id == CSSValueCenter) { 3860 int percent = 0; 3861 if (id == CSSValueLeft || id == CSSValueRight) { 3862 if (cumulativeFlags & XFillPosition) 3863 return 0; 3864 cumulativeFlags |= XFillPosition; 3865 individualFlag = XFillPosition; 3866 if (id == CSSValueRight) 3867 percent = 100; 3868 } 3869 else if (id == CSSValueTop || id == CSSValueBottom) { 3870 if (cumulativeFlags & YFillPosition) 3871 return 0; 3872 cumulativeFlags |= YFillPosition; 3873 individualFlag = YFillPosition; 3874 if (id == CSSValueBottom) 3875 percent = 100; 3876 } else if (id == CSSValueCenter) { 3877 // Center is ambiguous, so we're not sure which position we've found yet, an x or a y. 3878 percent = 50; 3879 cumulativeFlags |= AmbiguousFillPosition; 3880 individualFlag = AmbiguousFillPosition; 3881 } 3882 3883 if (parsingMode == ResolveValuesAsKeyword) 3884 return cssValuePool().createIdentifierValue(id); 3885 3886 return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); 3887 } 3888 if (validUnit(valueList->current(), FPercent | FLength)) { 3889 if (!cumulativeFlags) { 3890 cumulativeFlags |= XFillPosition; 3891 individualFlag = XFillPosition; 3892 } else if (cumulativeFlags & (XFillPosition | AmbiguousFillPosition)) { 3893 cumulativeFlags |= YFillPosition; 3894 individualFlag = YFillPosition; 3895 } else { 3896 if (m_parsedCalculation) 3897 m_parsedCalculation.release(); 3898 return 0; 3899 } 3900 return createPrimitiveNumericValue(valueList->current()); 3901 } 3902 return 0; 3903} 3904 3905static bool isValueConflictingWithCurrentEdge(int value1, int value2) 3906{ 3907 if ((value1 == CSSValueLeft || value1 == CSSValueRight) && (value2 == CSSValueLeft || value2 == CSSValueRight)) 3908 return true; 3909 3910 if ((value1 == CSSValueTop || value1 == CSSValueBottom) && (value2 == CSSValueTop || value2 == CSSValueBottom)) 3911 return true; 3912 3913 return false; 3914} 3915 3916static bool isFillPositionKeyword(CSSValueID value) 3917{ 3918 return value == CSSValueLeft || value == CSSValueTop || value == CSSValueBottom || value == CSSValueRight || value == CSSValueCenter; 3919} 3920 3921void CSSParser::parse4ValuesFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, PassRefPtr<CSSPrimitiveValue> parsedValue1, PassRefPtr<CSSPrimitiveValue> parsedValue2) 3922{ 3923 // [ left | right ] [ <percentage] | <length> ] && [ top | bottom ] [ <percentage> | <length> ] 3924 // In the case of 4 values <position> requires the second value to be a length or a percentage. 3925 if (isFillPositionKeyword(parsedValue2->getValueID())) 3926 return; 3927 3928 unsigned cumulativeFlags = 0; 3929 FillPositionFlag value3Flag = InvalidFillPosition; 3930 RefPtr<CSSPrimitiveValue> value3 = parseFillPositionComponent(valueList, cumulativeFlags, value3Flag, ResolveValuesAsKeyword); 3931 if (!value3) 3932 return; 3933 3934 CSSValueID ident1 = parsedValue1->getValueID(); 3935 CSSValueID ident3 = value3->getValueID(); 3936 3937 if (ident1 == CSSValueCenter) 3938 return; 3939 3940 if (!isFillPositionKeyword(ident3) || ident3 == CSSValueCenter) 3941 return; 3942 3943 // We need to check if the values are not conflicting, e.g. they are not on the same edge. It is 3944 // needed as the second call to parseFillPositionComponent was on purpose not checking it. In the 3945 // case of two values top 20px is invalid but in the case of 4 values it becomes valid. 3946 if (isValueConflictingWithCurrentEdge(ident1, ident3)) 3947 return; 3948 3949 valueList->next(); 3950 3951 cumulativeFlags = 0; 3952 FillPositionFlag value4Flag = InvalidFillPosition; 3953 RefPtr<CSSPrimitiveValue> value4 = parseFillPositionComponent(valueList, cumulativeFlags, value4Flag, ResolveValuesAsKeyword); 3954 if (!value4) 3955 return; 3956 3957 // 4th value must be a length or a percentage. 3958 if (isFillPositionKeyword(value4->getValueID())) 3959 return; 3960 3961 value1 = createPrimitiveValuePair(parsedValue1, parsedValue2); 3962 value2 = createPrimitiveValuePair(value3, value4); 3963 3964 if (ident1 == CSSValueTop || ident1 == CSSValueBottom) 3965 value1.swap(value2); 3966 3967 valueList->next(); 3968} 3969void CSSParser::parse3ValuesFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, PassRefPtr<CSSPrimitiveValue> parsedValue1, PassRefPtr<CSSPrimitiveValue> parsedValue2) 3970{ 3971 unsigned cumulativeFlags = 0; 3972 FillPositionFlag value3Flag = InvalidFillPosition; 3973 RefPtr<CSSPrimitiveValue> value3 = parseFillPositionComponent(valueList, cumulativeFlags, value3Flag, ResolveValuesAsKeyword); 3974 3975 // value3 is not an expected value, we return. 3976 if (!value3) 3977 return; 3978 3979 valueList->next(); 3980 3981 bool swapNeeded = false; 3982 CSSValueID ident1 = parsedValue1->getValueID(); 3983 CSSValueID ident2 = parsedValue2->getValueID(); 3984 CSSValueID ident3 = value3->getValueID(); 3985 3986 CSSValueID firstPositionKeyword; 3987 CSSValueID secondPositionKeyword; 3988 3989 if (ident1 == CSSValueCenter) { 3990 // <position> requires the first 'center' to be followed by a keyword. 3991 if (!isFillPositionKeyword(ident2)) 3992 return; 3993 3994 // If 'center' is the first keyword then the last one needs to be a length. 3995 if (isFillPositionKeyword(ident3)) 3996 return; 3997 3998 firstPositionKeyword = CSSValueLeft; 3999 if (ident2 == CSSValueLeft || ident2 == CSSValueRight) { 4000 firstPositionKeyword = CSSValueTop; 4001 swapNeeded = true; 4002 } 4003 value1 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(firstPositionKeyword), cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE)); 4004 value2 = createPrimitiveValuePair(parsedValue2, value3); 4005 } else if (ident3 == CSSValueCenter) { 4006 if (isFillPositionKeyword(ident2)) 4007 return; 4008 4009 secondPositionKeyword = CSSValueTop; 4010 if (ident1 == CSSValueTop || ident1 == CSSValueBottom) { 4011 secondPositionKeyword = CSSValueLeft; 4012 swapNeeded = true; 4013 } 4014 value1 = createPrimitiveValuePair(parsedValue1, parsedValue2); 4015 value2 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(secondPositionKeyword), cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE)); 4016 } else { 4017 RefPtr<CSSPrimitiveValue> firstPositionValue; 4018 RefPtr<CSSPrimitiveValue> secondPositionValue; 4019 4020 if (isFillPositionKeyword(ident2)) { 4021 // To match CSS grammar, we should only accept: [ center | left | right | bottom | top ] [ left | right | top | bottom ] [ <percentage> | <length> ]. 4022 ASSERT(ident2 != CSSValueCenter); 4023 4024 if (isFillPositionKeyword(ident3)) 4025 return; 4026 4027 secondPositionValue = value3; 4028 secondPositionKeyword = ident2; 4029 firstPositionValue = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE); 4030 } else { 4031 // Per CSS, we should only accept: [ right | left | top | bottom ] [ <percentage> | <length> ] [ center | left | right | bottom | top ]. 4032 if (!isFillPositionKeyword(ident3)) 4033 return; 4034 4035 firstPositionValue = parsedValue2; 4036 secondPositionKeyword = ident3; 4037 secondPositionValue = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE); 4038 } 4039 4040 if (isValueConflictingWithCurrentEdge(ident1, secondPositionKeyword)) 4041 return; 4042 4043 value1 = createPrimitiveValuePair(parsedValue1, firstPositionValue); 4044 value2 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(secondPositionKeyword), secondPositionValue); 4045 } 4046 4047 if (ident1 == CSSValueTop || ident1 == CSSValueBottom || swapNeeded) 4048 value1.swap(value2); 4049 4050#ifndef NDEBUG 4051 CSSPrimitiveValue* first = toCSSPrimitiveValue(value1.get()); 4052 CSSPrimitiveValue* second = toCSSPrimitiveValue(value2.get()); 4053 ident1 = first->getPairValue()->first()->getValueID(); 4054 ident2 = second->getPairValue()->first()->getValueID(); 4055 ASSERT(ident1 == CSSValueLeft || ident1 == CSSValueRight); 4056 ASSERT(ident2 == CSSValueBottom || ident2 == CSSValueTop); 4057#endif 4058} 4059 4060inline bool CSSParser::isPotentialPositionValue(CSSParserValue* value) 4061{ 4062 return isFillPositionKeyword(value->id) || validUnit(value, FPercent | FLength, ReleaseParsedCalcValue); 4063} 4064 4065void CSSParser::parseFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2) 4066{ 4067 unsigned numberOfValues = 0; 4068 for (unsigned i = valueList->currentIndex(); i < valueList->size(); ++i, ++numberOfValues) { 4069 CSSParserValue* current = valueList->valueAt(i); 4070 if (isComma(current) || !current || isForwardSlashOperator(current) || !isPotentialPositionValue(current)) 4071 break; 4072 } 4073 4074 if (numberOfValues > 4) 4075 return; 4076 4077 // If we are parsing two values, we can safely call the CSS 2.1 parsing function and return. 4078 if (numberOfValues <= 2) { 4079 parse2ValuesFillPosition(valueList, value1, value2); 4080 return; 4081 } 4082 4083 ASSERT(numberOfValues > 2 && numberOfValues <= 4); 4084 4085 CSSParserValue* value = valueList->current(); 4086 4087 // <position> requires the first value to be a background keyword. 4088 if (!isFillPositionKeyword(value->id)) 4089 return; 4090 4091 // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length. 4092 unsigned cumulativeFlags = 0; 4093 FillPositionFlag value1Flag = InvalidFillPosition; 4094 FillPositionFlag value2Flag = InvalidFillPosition; 4095 value1 = parseFillPositionComponent(valueList, cumulativeFlags, value1Flag, ResolveValuesAsKeyword); 4096 if (!value1) 4097 return; 4098 4099 value = valueList->next(); 4100 4101 // In case we are parsing more than two values, relax the check inside of parseFillPositionComponent. top 20px is 4102 // a valid start for <position>. 4103 cumulativeFlags = AmbiguousFillPosition; 4104 value2 = parseFillPositionComponent(valueList, cumulativeFlags, value2Flag, ResolveValuesAsKeyword); 4105 if (value2) 4106 valueList->next(); 4107 else { 4108 value1.clear(); 4109 return; 4110 } 4111 4112 RefPtr<CSSPrimitiveValue> parsedValue1 = toCSSPrimitiveValue(value1.get()); 4113 RefPtr<CSSPrimitiveValue> parsedValue2 = toCSSPrimitiveValue(value2.get()); 4114 4115 value1.clear(); 4116 value2.clear(); 4117 4118 // Per CSS3 syntax, <position> can't have 'center' as its second keyword as we have more arguments to follow. 4119 if (parsedValue2->getValueID() == CSSValueCenter) 4120 return; 4121 4122 if (numberOfValues == 3) 4123 parse3ValuesFillPosition(valueList, value1, value2, parsedValue1.release(), parsedValue2.release()); 4124 else 4125 parse4ValuesFillPosition(valueList, value1, value2, parsedValue1.release(), parsedValue2.release()); 4126} 4127 4128void CSSParser::parse2ValuesFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2) 4129{ 4130 CSSParserValue* value = valueList->current(); 4131 4132 // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length. 4133 unsigned cumulativeFlags = 0; 4134 FillPositionFlag value1Flag = InvalidFillPosition; 4135 FillPositionFlag value2Flag = InvalidFillPosition; 4136 value1 = parseFillPositionComponent(valueList, cumulativeFlags, value1Flag); 4137 if (!value1) 4138 return; 4139 4140 // It only takes one value for background-position to be correctly parsed if it was specified in a shorthand (since we 4141 // can assume that any other values belong to the rest of the shorthand). If we're not parsing a shorthand, though, the 4142 // value was explicitly specified for our property. 4143 value = valueList->next(); 4144 4145 // First check for the comma. If so, we are finished parsing this value or value pair. 4146 if (isComma(value)) 4147 value = 0; 4148 4149 if (value) { 4150 value2 = parseFillPositionComponent(valueList, cumulativeFlags, value2Flag); 4151 if (value2) 4152 valueList->next(); 4153 else { 4154 if (!inShorthand()) { 4155 value1.clear(); 4156 return; 4157 } 4158 } 4159 } 4160 4161 if (!value2) 4162 // Only one value was specified. If that value was not a keyword, then it sets the x position, and the y position 4163 // is simply 50%. This is our default. 4164 // For keywords, the keyword was either an x-keyword (left/right), a y-keyword (top/bottom), or an ambiguous keyword (center). 4165 // For left/right/center, the default of 50% in the y is still correct. 4166 value2 = cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE); 4167 4168 if (value1Flag == YFillPosition || value2Flag == XFillPosition) 4169 value1.swap(value2); 4170} 4171 4172void CSSParser::parseFillRepeat(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2) 4173{ 4174 CSSValueID id = m_valueList->current()->id; 4175 if (id == CSSValueRepeatX) { 4176 m_implicitShorthand = true; 4177 value1 = cssValuePool().createIdentifierValue(CSSValueRepeat); 4178 value2 = cssValuePool().createIdentifierValue(CSSValueNoRepeat); 4179 m_valueList->next(); 4180 return; 4181 } 4182 if (id == CSSValueRepeatY) { 4183 m_implicitShorthand = true; 4184 value1 = cssValuePool().createIdentifierValue(CSSValueNoRepeat); 4185 value2 = cssValuePool().createIdentifierValue(CSSValueRepeat); 4186 m_valueList->next(); 4187 return; 4188 } 4189 if (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace) 4190 value1 = cssValuePool().createIdentifierValue(id); 4191 else { 4192 value1 = 0; 4193 return; 4194 } 4195 4196 CSSParserValue* value = m_valueList->next(); 4197 4198 // Parse the second value if one is available 4199 if (value && !isComma(value)) { 4200 id = value->id; 4201 if (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace) { 4202 value2 = cssValuePool().createIdentifierValue(id); 4203 m_valueList->next(); 4204 return; 4205 } 4206 } 4207 4208 // If only one value was specified, value2 is the same as value1. 4209 m_implicitShorthand = true; 4210 value2 = cssValuePool().createIdentifierValue(toCSSPrimitiveValue(value1.get())->getValueID()); 4211} 4212 4213PassRefPtr<CSSValue> CSSParser::parseFillSize(CSSPropertyID propId, bool& allowComma) 4214{ 4215 allowComma = true; 4216 CSSParserValue* value = m_valueList->current(); 4217 4218 if (value->id == CSSValueContain || value->id == CSSValueCover) 4219 return cssValuePool().createIdentifierValue(value->id); 4220 4221 RefPtr<CSSPrimitiveValue> parsedValue1; 4222 4223 if (value->id == CSSValueAuto) 4224 parsedValue1 = cssValuePool().createIdentifierValue(CSSValueAuto); 4225 else { 4226 if (!validUnit(value, FLength | FPercent)) 4227 return 0; 4228 parsedValue1 = createPrimitiveNumericValue(value); 4229 } 4230 4231 RefPtr<CSSPrimitiveValue> parsedValue2; 4232 if ((value = m_valueList->next())) { 4233 if (value->unit == CSSParserValue::Operator && value->iValue == ',') 4234 allowComma = false; 4235 else if (value->id != CSSValueAuto) { 4236 if (!validUnit(value, FLength | FPercent)) { 4237 if (!inShorthand()) 4238 return 0; 4239 // We need to rewind the value list, so that when it is advanced we'll end up back at this value. 4240 m_valueList->previous(); 4241 } else 4242 parsedValue2 = createPrimitiveNumericValue(value); 4243 } 4244 } else if (!parsedValue2 && propId == CSSPropertyWebkitBackgroundSize) { 4245 // For backwards compatibility we set the second value to the first if it is omitted. 4246 // We only need to do this for -webkit-background-size. It should be safe to let masks match 4247 // the real property. 4248 parsedValue2 = parsedValue1; 4249 } 4250 4251 if (!parsedValue2) 4252 return parsedValue1; 4253 return createPrimitiveValuePair(parsedValue1.release(), parsedValue2.release()); 4254} 4255 4256bool CSSParser::parseFillProperty(CSSPropertyID propId, CSSPropertyID& propId1, CSSPropertyID& propId2, 4257 RefPtr<CSSValue>& retValue1, RefPtr<CSSValue>& retValue2) 4258{ 4259 RefPtr<CSSValueList> values; 4260 RefPtr<CSSValueList> values2; 4261 CSSParserValue* val; 4262 RefPtr<CSSValue> value; 4263 RefPtr<CSSValue> value2; 4264 4265 bool allowComma = false; 4266 4267 retValue1 = retValue2 = 0; 4268 propId1 = propId; 4269 propId2 = propId; 4270 if (propId == CSSPropertyBackgroundPosition) { 4271 propId1 = CSSPropertyBackgroundPositionX; 4272 propId2 = CSSPropertyBackgroundPositionY; 4273 } else if (propId == CSSPropertyWebkitMaskPosition) { 4274 propId1 = CSSPropertyWebkitMaskPositionX; 4275 propId2 = CSSPropertyWebkitMaskPositionY; 4276 } else if (propId == CSSPropertyBackgroundRepeat) { 4277 propId1 = CSSPropertyBackgroundRepeatX; 4278 propId2 = CSSPropertyBackgroundRepeatY; 4279 } else if (propId == CSSPropertyWebkitMaskRepeat) { 4280 propId1 = CSSPropertyWebkitMaskRepeatX; 4281 propId2 = CSSPropertyWebkitMaskRepeatY; 4282 } 4283 4284 while ((val = m_valueList->current())) { 4285 RefPtr<CSSValue> currValue; 4286 RefPtr<CSSValue> currValue2; 4287 4288 if (allowComma) { 4289 if (!isComma(val)) 4290 return false; 4291 m_valueList->next(); 4292 allowComma = false; 4293 } else { 4294 allowComma = true; 4295 switch (propId) { 4296 case CSSPropertyBackgroundColor: 4297 currValue = parseBackgroundColor(); 4298 if (currValue) 4299 m_valueList->next(); 4300 break; 4301 case CSSPropertyBackgroundAttachment: 4302 if (val->id == CSSValueScroll || val->id == CSSValueFixed || val->id == CSSValueLocal) { 4303 currValue = cssValuePool().createIdentifierValue(val->id); 4304 m_valueList->next(); 4305 } 4306 break; 4307 case CSSPropertyBackgroundImage: 4308 case CSSPropertyWebkitMaskImage: 4309 if (parseFillImage(m_valueList.get(), currValue)) 4310 m_valueList->next(); 4311 break; 4312 case CSSPropertyWebkitBackgroundClip: 4313 case CSSPropertyWebkitBackgroundOrigin: 4314 case CSSPropertyWebkitMaskClip: 4315 case CSSPropertyWebkitMaskOrigin: 4316 // The first three values here are deprecated and do not apply to the version of the property that has 4317 // the -webkit- prefix removed. 4318 if (val->id == CSSValueBorder || val->id == CSSValuePadding || val->id == CSSValueContent || 4319 val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox || 4320 ((propId == CSSPropertyWebkitBackgroundClip || propId == CSSPropertyWebkitMaskClip) && 4321 (val->id == CSSValueText || val->id == CSSValueWebkitText))) { 4322 currValue = cssValuePool().createIdentifierValue(val->id); 4323 m_valueList->next(); 4324 } 4325 break; 4326 case CSSPropertyBackgroundClip: 4327 if (parseBackgroundClip(val, currValue)) 4328 m_valueList->next(); 4329 break; 4330 case CSSPropertyBackgroundOrigin: 4331 if (val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox) { 4332 currValue = cssValuePool().createIdentifierValue(val->id); 4333 m_valueList->next(); 4334 } 4335 break; 4336 case CSSPropertyBackgroundPosition: 4337 case CSSPropertyWebkitMaskPosition: 4338 parseFillPosition(m_valueList.get(), currValue, currValue2); 4339 // parseFillPosition advances the m_valueList pointer. 4340 break; 4341 case CSSPropertyBackgroundPositionX: 4342 case CSSPropertyWebkitMaskPositionX: { 4343 currValue = parseFillPositionX(m_valueList.get()); 4344 if (currValue) 4345 m_valueList->next(); 4346 break; 4347 } 4348 case CSSPropertyBackgroundPositionY: 4349 case CSSPropertyWebkitMaskPositionY: { 4350 currValue = parseFillPositionY(m_valueList.get()); 4351 if (currValue) 4352 m_valueList->next(); 4353 break; 4354 } 4355 case CSSPropertyWebkitBackgroundComposite: 4356 case CSSPropertyWebkitMaskComposite: 4357 if (val->id >= CSSValueClear && val->id <= CSSValuePlusLighter) { 4358 currValue = cssValuePool().createIdentifierValue(val->id); 4359 m_valueList->next(); 4360 } 4361 break; 4362 case CSSPropertyBackgroundBlendMode: 4363 if (cssCompositingEnabled() && (val->id == CSSValueNormal || val->id == CSSValueMultiply 4364 || val->id == CSSValueScreen || val->id == CSSValueOverlay || val->id == CSSValueDarken 4365 || val->id == CSSValueLighten || val->id == CSSValueColorDodge || val->id == CSSValueColorBurn 4366 || val->id == CSSValueHardLight || val->id == CSSValueSoftLight || val->id == CSSValueDifference 4367 || val->id == CSSValueExclusion)) { 4368 currValue = cssValuePool().createIdentifierValue(val->id); 4369 m_valueList->next(); 4370 } 4371 break; 4372 case CSSPropertyBackgroundRepeat: 4373 case CSSPropertyWebkitMaskRepeat: 4374 parseFillRepeat(currValue, currValue2); 4375 // parseFillRepeat advances the m_valueList pointer 4376 break; 4377 case CSSPropertyBackgroundSize: 4378 case CSSPropertyWebkitBackgroundSize: 4379 case CSSPropertyWebkitMaskSize: { 4380 currValue = parseFillSize(propId, allowComma); 4381 if (currValue) 4382 m_valueList->next(); 4383 break; 4384 } 4385 case CSSPropertyWebkitMaskSourceType: { 4386 if (val->id == CSSValueAuto || val->id == CSSValueAlpha || val->id == CSSValueLuminance) { 4387 currValue = cssValuePool().createIdentifierValue(val->id); 4388 m_valueList->next(); 4389 } else 4390 currValue = 0; 4391 break; 4392 } 4393 default: 4394 break; 4395 } 4396 if (!currValue) 4397 return false; 4398 4399 if (value && !values) { 4400 values = CSSValueList::createCommaSeparated(); 4401 values->append(value.release()); 4402 } 4403 4404 if (value2 && !values2) { 4405 values2 = CSSValueList::createCommaSeparated(); 4406 values2->append(value2.release()); 4407 } 4408 4409 if (values) 4410 values->append(currValue.release()); 4411 else 4412 value = currValue.release(); 4413 if (currValue2) { 4414 if (values2) 4415 values2->append(currValue2.release()); 4416 else 4417 value2 = currValue2.release(); 4418 } 4419 } 4420 4421 // When parsing any fill shorthand property, we let it handle building up the lists for all 4422 // properties. 4423 if (inShorthand()) 4424 break; 4425 } 4426 4427 if (values && values->length()) { 4428 retValue1 = values.release(); 4429 if (values2 && values2->length()) 4430 retValue2 = values2.release(); 4431 return true; 4432 } 4433 if (value) { 4434 retValue1 = value.release(); 4435 retValue2 = value2.release(); 4436 return true; 4437 } 4438 return false; 4439} 4440 4441PassRefPtr<CSSValue> CSSParser::parseAnimationDelay() 4442{ 4443 CSSParserValue* value = m_valueList->current(); 4444 if (validUnit(value, FTime)) 4445 return createPrimitiveNumericValue(value); 4446 return 0; 4447} 4448 4449PassRefPtr<CSSValue> CSSParser::parseAnimationDirection() 4450{ 4451 CSSParserValue* value = m_valueList->current(); 4452 if (value->id == CSSValueNormal || value->id == CSSValueAlternate || value->id == CSSValueReverse || value->id == CSSValueAlternateReverse) 4453 return cssValuePool().createIdentifierValue(value->id); 4454 return 0; 4455} 4456 4457PassRefPtr<CSSValue> CSSParser::parseAnimationDuration() 4458{ 4459 CSSParserValue* value = m_valueList->current(); 4460 if (validUnit(value, FTime | FNonNeg)) 4461 return createPrimitiveNumericValue(value); 4462 return 0; 4463} 4464 4465PassRefPtr<CSSValue> CSSParser::parseAnimationFillMode() 4466{ 4467 CSSParserValue* value = m_valueList->current(); 4468 if (value->id == CSSValueNone || value->id == CSSValueForwards || value->id == CSSValueBackwards || value->id == CSSValueBoth) 4469 return cssValuePool().createIdentifierValue(value->id); 4470 return 0; 4471} 4472 4473PassRefPtr<CSSValue> CSSParser::parseAnimationIterationCount() 4474{ 4475 CSSParserValue* value = m_valueList->current(); 4476 if (value->id == CSSValueInfinite) 4477 return cssValuePool().createIdentifierValue(value->id); 4478 if (validUnit(value, FNumber | FNonNeg)) 4479 return createPrimitiveNumericValue(value); 4480 return 0; 4481} 4482 4483PassRefPtr<CSSValue> CSSParser::parseAnimationName() 4484{ 4485 CSSParserValue* value = m_valueList->current(); 4486 if (value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT) { 4487 if (value->id == CSSValueNone || (value->unit == CSSPrimitiveValue::CSS_STRING && equalIgnoringCase(value, "none"))) { 4488 return cssValuePool().createIdentifierValue(CSSValueNone); 4489 } else { 4490 return createPrimitiveStringValue(value); 4491 } 4492 } 4493 return 0; 4494} 4495 4496PassRefPtr<CSSValue> CSSParser::parseAnimationPlayState() 4497{ 4498 CSSParserValue* value = m_valueList->current(); 4499 if (value->id == CSSValueRunning || value->id == CSSValuePaused) 4500 return cssValuePool().createIdentifierValue(value->id); 4501 return 0; 4502} 4503 4504PassRefPtr<CSSValue> CSSParser::parseAnimationProperty(AnimationParseContext& context) 4505{ 4506 CSSParserValue* value = m_valueList->current(); 4507 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 4508 return 0; 4509 CSSPropertyID result = cssPropertyID(value->string); 4510 if (result) 4511 return cssValuePool().createIdentifierValue(result); 4512 if (equalIgnoringCase(value, "all")) { 4513 context.sawAnimationPropertyKeyword(); 4514 return cssValuePool().createIdentifierValue(CSSValueAll); 4515 } 4516 if (equalIgnoringCase(value, "none")) { 4517 context.commitAnimationPropertyKeyword(); 4518 context.sawAnimationPropertyKeyword(); 4519 return cssValuePool().createIdentifierValue(CSSValueNone); 4520 } 4521 return 0; 4522} 4523 4524bool CSSParser::parseTransformOriginShorthand(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3) 4525{ 4526 parse2ValuesFillPosition(m_valueList.get(), value1, value2); 4527 4528 // now get z 4529 if (m_valueList->current()) { 4530 if (validUnit(m_valueList->current(), FLength)) { 4531 value3 = createPrimitiveNumericValue(m_valueList->current()); 4532 m_valueList->next(); 4533 return true; 4534 } 4535 return false; 4536 } 4537 value3 = cssValuePool().createImplicitInitialValue(); 4538 return true; 4539} 4540 4541bool CSSParser::parseCubicBezierTimingFunctionValue(CSSParserValueList*& args, double& result) 4542{ 4543 CSSParserValue* v = args->current(); 4544 if (!validUnit(v, FNumber)) 4545 return false; 4546 result = v->fValue; 4547 v = args->next(); 4548 if (!v) 4549 // The last number in the function has no comma after it, so we're done. 4550 return true; 4551 if (!isComma(v)) 4552 return false; 4553 args->next(); 4554 return true; 4555} 4556 4557PassRefPtr<CSSValue> CSSParser::parseAnimationTimingFunction() 4558{ 4559 CSSParserValue* value = m_valueList->current(); 4560 if (value->id == CSSValueEase || value->id == CSSValueLinear || value->id == CSSValueEaseIn || value->id == CSSValueEaseOut 4561 || value->id == CSSValueEaseInOut || value->id == CSSValueStepStart || value->id == CSSValueStepEnd) 4562 return cssValuePool().createIdentifierValue(value->id); 4563 4564 // We must be a function. 4565 if (value->unit != CSSParserValue::Function) 4566 return 0; 4567 4568 CSSParserValueList* args = value->function->args.get(); 4569 4570 if (equalIgnoringCase(value->function->name, "steps(")) { 4571 // For steps, 1 or 2 params must be specified (comma-separated) 4572 if (!args || (args->size() != 1 && args->size() != 3)) 4573 return 0; 4574 4575 // There are two values. 4576 int numSteps; 4577 bool stepAtStart = false; 4578 4579 CSSParserValue* v = args->current(); 4580 if (!validUnit(v, FInteger)) 4581 return 0; 4582 numSteps = clampToInteger(v->fValue); 4583 if (numSteps < 1) 4584 return 0; 4585 v = args->next(); 4586 4587 if (v) { 4588 // There is a comma so we need to parse the second value 4589 if (!isComma(v)) 4590 return 0; 4591 v = args->next(); 4592 if (v->id != CSSValueStart && v->id != CSSValueEnd) 4593 return 0; 4594 stepAtStart = v->id == CSSValueStart; 4595 } 4596 4597 return CSSStepsTimingFunctionValue::create(numSteps, stepAtStart); 4598 } 4599 4600 if (equalIgnoringCase(value->function->name, "cubic-bezier(")) { 4601 // For cubic bezier, 4 values must be specified. 4602 if (!args || args->size() != 7) 4603 return 0; 4604 4605 // There are two points specified. The x values must be between 0 and 1 but the y values can exceed this range. 4606 double x1, y1, x2, y2; 4607 4608 if (!parseCubicBezierTimingFunctionValue(args, x1)) 4609 return 0; 4610 if (x1 < 0 || x1 > 1) 4611 return 0; 4612 if (!parseCubicBezierTimingFunctionValue(args, y1)) 4613 return 0; 4614 if (!parseCubicBezierTimingFunctionValue(args, x2)) 4615 return 0; 4616 if (x2 < 0 || x2 > 1) 4617 return 0; 4618 if (!parseCubicBezierTimingFunctionValue(args, y2)) 4619 return 0; 4620 4621 return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2); 4622 } 4623 4624 return 0; 4625} 4626 4627bool CSSParser::parseAnimationProperty(CSSPropertyID propId, RefPtr<CSSValue>& result, AnimationParseContext& context) 4628{ 4629 RefPtr<CSSValueList> values; 4630 CSSParserValue* val; 4631 RefPtr<CSSValue> value; 4632 bool allowComma = false; 4633 4634 result = 0; 4635 4636 while ((val = m_valueList->current())) { 4637 RefPtr<CSSValue> currValue; 4638 if (allowComma) { 4639 if (!isComma(val)) 4640 return false; 4641 m_valueList->next(); 4642 allowComma = false; 4643 } 4644 else { 4645 switch (propId) { 4646 case CSSPropertyWebkitAnimationDelay: 4647 case CSSPropertyTransitionDelay: 4648 case CSSPropertyWebkitTransitionDelay: 4649 currValue = parseAnimationDelay(); 4650 if (currValue) 4651 m_valueList->next(); 4652 break; 4653 case CSSPropertyWebkitAnimationDirection: 4654 currValue = parseAnimationDirection(); 4655 if (currValue) 4656 m_valueList->next(); 4657 break; 4658 case CSSPropertyWebkitAnimationDuration: 4659 case CSSPropertyTransitionDuration: 4660 case CSSPropertyWebkitTransitionDuration: 4661 currValue = parseAnimationDuration(); 4662 if (currValue) 4663 m_valueList->next(); 4664 break; 4665 case CSSPropertyWebkitAnimationFillMode: 4666 currValue = parseAnimationFillMode(); 4667 if (currValue) 4668 m_valueList->next(); 4669 break; 4670 case CSSPropertyWebkitAnimationIterationCount: 4671 currValue = parseAnimationIterationCount(); 4672 if (currValue) 4673 m_valueList->next(); 4674 break; 4675 case CSSPropertyWebkitAnimationName: 4676 currValue = parseAnimationName(); 4677 if (currValue) 4678 m_valueList->next(); 4679 break; 4680 case CSSPropertyWebkitAnimationPlayState: 4681 currValue = parseAnimationPlayState(); 4682 if (currValue) 4683 m_valueList->next(); 4684 break; 4685 case CSSPropertyTransitionProperty: 4686 case CSSPropertyWebkitTransitionProperty: 4687 currValue = parseAnimationProperty(context); 4688 if (value && !context.animationPropertyKeywordAllowed()) 4689 return false; 4690 if (currValue) 4691 m_valueList->next(); 4692 break; 4693 case CSSPropertyWebkitAnimationTimingFunction: 4694 case CSSPropertyTransitionTimingFunction: 4695 case CSSPropertyWebkitTransitionTimingFunction: 4696 currValue = parseAnimationTimingFunction(); 4697 if (currValue) 4698 m_valueList->next(); 4699 break; 4700 default: 4701 ASSERT_NOT_REACHED(); 4702 return false; 4703 } 4704 4705 if (!currValue) 4706 return false; 4707 4708 if (value && !values) { 4709 values = CSSValueList::createCommaSeparated(); 4710 values->append(value.release()); 4711 } 4712 4713 if (values) 4714 values->append(currValue.release()); 4715 else 4716 value = currValue.release(); 4717 4718 allowComma = true; 4719 } 4720 4721 // When parsing the 'transition' shorthand property, we let it handle building up the lists for all 4722 // properties. 4723 if (inShorthand()) 4724 break; 4725 } 4726 4727 if (values && values->length()) { 4728 result = values.release(); 4729 return true; 4730 } 4731 if (value) { 4732 result = value.release(); 4733 return true; 4734 } 4735 return false; 4736} 4737 4738#if ENABLE(CSS_GRID_LAYOUT) 4739static inline bool isValidCustomIdent(const CSSParserValue& value) 4740{ 4741 return value.unit == CSSPrimitiveValue::CSS_IDENT && value.id != CSSValueSpan && value.id != CSSValueAuto; 4742} 4743 4744// The function parses [ <integer> || <custom-ident> ] in <grid-line> (which can be stand alone or with 'span'). 4745bool CSSParser::parseIntegerOrCustomIdentFromGridPosition(RefPtr<CSSPrimitiveValue>& numericValue, RefPtr<CSSPrimitiveValue>& gridLineName) 4746{ 4747 CSSParserValue* value = m_valueList->current(); 4748 if (validUnit(value, FInteger) && value->fValue) { 4749 numericValue = createPrimitiveNumericValue(value); 4750 value = m_valueList->next(); 4751 if (value && isValidCustomIdent(*value)) { 4752 gridLineName = createPrimitiveStringValue(m_valueList->current()); 4753 m_valueList->next(); 4754 } 4755 return true; 4756 } 4757 4758 if (isValidCustomIdent(*value)) { 4759 gridLineName = createPrimitiveStringValue(m_valueList->current()); 4760 value = m_valueList->next(); 4761 if (value && validUnit(value, FInteger) && value->fValue) { 4762 numericValue = createPrimitiveNumericValue(value); 4763 m_valueList->next(); 4764 } 4765 return true; 4766 } 4767 4768 return false; 4769} 4770 4771PassRefPtr<CSSValue> CSSParser::parseGridPosition() 4772{ 4773 CSSParserValue* value = m_valueList->current(); 4774 if (value->id == CSSValueAuto) { 4775 m_valueList->next(); 4776 return cssValuePool().createIdentifierValue(CSSValueAuto); 4777 } 4778 4779 RefPtr<CSSPrimitiveValue> numericValue; 4780 RefPtr<CSSPrimitiveValue> gridLineName; 4781 bool hasSeenSpanKeyword = false; 4782 4783 if (value->id == CSSValueSpan) { 4784 hasSeenSpanKeyword = true; 4785 if (auto* nextValue = m_valueList->next()) { 4786 if (!isForwardSlashOperator(nextValue) && !parseIntegerOrCustomIdentFromGridPosition(numericValue, gridLineName)) 4787 return nullptr; 4788 } 4789 } else if (parseIntegerOrCustomIdentFromGridPosition(numericValue, gridLineName)) { 4790 value = m_valueList->current(); 4791 if (value && value->id == CSSValueSpan) { 4792 hasSeenSpanKeyword = true; 4793 m_valueList->next(); 4794 } 4795 } 4796 4797 // Check that we have consumed all the value list. For shorthands, the parser will pass 4798 // the whole value list (including the opposite position). 4799 if (m_valueList->current() && !isForwardSlashOperator(m_valueList->current())) 4800 return nullptr; 4801 4802 // If we didn't parse anything, this is not a valid grid position. 4803 if (!hasSeenSpanKeyword && !gridLineName && !numericValue) 4804 return nullptr; 4805 4806 // Negative numbers are not allowed for span (but are for <integer>). 4807 if (hasSeenSpanKeyword && numericValue && numericValue->getIntValue() < 0) 4808 return nullptr; 4809 4810 // For the <custom-ident> case. 4811 if (gridLineName && !numericValue && !hasSeenSpanKeyword) 4812 return cssValuePool().createValue(gridLineName->getStringValue(), CSSPrimitiveValue::CSS_STRING); 4813 4814 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated(); 4815 if (hasSeenSpanKeyword) 4816 values->append(cssValuePool().createIdentifierValue(CSSValueSpan)); 4817 if (numericValue) 4818 values->append(numericValue.release()); 4819 if (gridLineName) 4820 values->append(gridLineName.release()); 4821 ASSERT(values->length()); 4822 return values.release(); 4823} 4824 4825static PassRefPtr<CSSValue> gridMissingGridPositionValue(CSSValue* value) 4826{ 4827 if (value->isPrimitiveValue() && toCSSPrimitiveValue(value)->isString()) 4828 return value; 4829 4830 return cssValuePool().createIdentifierValue(CSSValueAuto); 4831} 4832 4833bool CSSParser::parseGridItemPositionShorthand(CSSPropertyID shorthandId, bool important) 4834{ 4835 ShorthandScope scope(this, shorthandId); 4836 const StylePropertyShorthand& shorthand = shorthandForProperty(shorthandId); 4837 ASSERT(shorthand.length() == 2); 4838 4839 RefPtr<CSSValue> startValue = parseGridPosition(); 4840 if (!startValue) 4841 return false; 4842 4843 RefPtr<CSSValue> endValue; 4844 if (m_valueList->current()) { 4845 if (!isForwardSlashOperator(m_valueList->current())) 4846 return false; 4847 4848 if (!m_valueList->next()) 4849 return false; 4850 4851 endValue = parseGridPosition(); 4852 if (!endValue || m_valueList->current()) 4853 return false; 4854 } else 4855 endValue = gridMissingGridPositionValue(startValue.get()); 4856 4857 addProperty(shorthand.properties()[0], startValue, important); 4858 addProperty(shorthand.properties()[1], endValue, important); 4859 return true; 4860} 4861 4862bool CSSParser::parseGridTemplateRowsAndAreas(PassRefPtr<CSSValue> templateColumns, bool important) 4863{ 4864 // At least template-areas strings must be defined. 4865 if (!m_valueList->current()) 4866 return false; 4867 4868 NamedGridAreaMap gridAreaMap; 4869 unsigned rowCount = 0; 4870 unsigned columnCount = 0; 4871 bool trailingIdentWasAdded = false; 4872 RefPtr<CSSValueList> templateRows = CSSValueList::createSpaceSeparated(); 4873 4874 do { 4875 // Handle leading <custom-ident>*. 4876 if (m_valueList->current()->unit == CSSParserValue::ValueList) { 4877 if (trailingIdentWasAdded) { 4878 // A row's trailing ident must be concatenated with the next row's leading one. 4879 parseGridLineNames(*m_valueList, *templateRows, static_cast<CSSGridLineNamesValue*>(templateRows->item(templateRows->length() - 1))); 4880 } else 4881 parseGridLineNames(*m_valueList, *templateRows); 4882 } 4883 4884 // Handle a template-area's row. 4885 if (!parseGridTemplateAreasRow(gridAreaMap, rowCount, columnCount)) 4886 return false; 4887 ++rowCount; 4888 4889 // Handle template-rows's track-size. 4890 if (m_valueList->current() && m_valueList->current()->unit != CSSParserValue::ValueList && m_valueList->current()->unit != CSSPrimitiveValue::CSS_STRING) { 4891 RefPtr<CSSValue> value = parseGridTrackSize(*m_valueList); 4892 if (!value) 4893 return false; 4894 templateRows->append(value.release()); 4895 } else 4896 templateRows->append(cssValuePool().createIdentifierValue(CSSValueAuto)); 4897 4898 // This will handle the trailing/leading <custom-ident>* in the grammar. 4899 trailingIdentWasAdded = false; 4900 if (m_valueList->current() && m_valueList->current()->unit == CSSParserValue::ValueList) { 4901 parseGridLineNames(*m_valueList, *templateRows); 4902 trailingIdentWasAdded = true; 4903 } 4904 } while (m_valueList->current()); 4905 4906 // [<track-list> /]? 4907 if (templateColumns) 4908 addProperty(CSSPropertyWebkitGridTemplateColumns, templateColumns, important); 4909 else 4910 addProperty(CSSPropertyWebkitGridTemplateColumns, cssValuePool().createIdentifierValue(CSSValueNone), important); 4911 4912 // [<line-names>? <string> [<track-size> <line-names>]? ]+ 4913 RefPtr<CSSValue> templateAreas = CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount); 4914 addProperty(CSSPropertyWebkitGridTemplateAreas, templateAreas.release(), important); 4915 addProperty(CSSPropertyWebkitGridTemplateRows, templateRows.release(), important); 4916 4917 return true; 4918} 4919 4920bool CSSParser::parseGridTemplateShorthand(bool important) 4921{ 4922 ShorthandScope scope(this, CSSPropertyWebkitGridTemplate); 4923 ASSERT(shorthandForProperty(CSSPropertyWebkitGridTemplate).length() == 3); 4924 4925 // At least "none" must be defined. 4926 if (!m_valueList->current()) 4927 return false; 4928 4929 bool firstValueIsNone = m_valueList->current()->id == CSSValueNone; 4930 4931 // 1- 'none' case. 4932 if (firstValueIsNone && !m_valueList->next()) { 4933 addProperty(CSSPropertyWebkitGridTemplateColumns, cssValuePool().createIdentifierValue(CSSValueNone), important); 4934 addProperty(CSSPropertyWebkitGridTemplateRows, cssValuePool().createIdentifierValue(CSSValueNone), important); 4935 addProperty(CSSPropertyWebkitGridTemplateAreas, cssValuePool().createIdentifierValue(CSSValueNone), important); 4936 return true; 4937 } 4938 4939 unsigned index = 0; 4940 RefPtr<CSSValue> columnsValue = firstValueIsNone ? cssValuePool().createIdentifierValue(CSSValueNone) : parseGridTrackList(); 4941 4942 // 2- <grid-template-columns> / <grid-template-columns> syntax. 4943 if (columnsValue) { 4944 if (!(m_valueList->current() && isForwardSlashOperator(m_valueList->current()) && m_valueList->next())) 4945 return false; 4946 index = m_valueList->currentIndex(); 4947 if (RefPtr<CSSValue> rowsValue = parseGridTrackList()) { 4948 if (m_valueList->current()) 4949 return false; 4950 addProperty(CSSPropertyWebkitGridTemplateColumns, columnsValue.release(), important); 4951 addProperty(CSSPropertyWebkitGridTemplateRows, rowsValue.release(), important); 4952 addProperty(CSSPropertyWebkitGridTemplateAreas, cssValuePool().createIdentifierValue(CSSValueNone), important); 4953 return true; 4954 } 4955 } 4956 4957 4958 // 3- [<track-list> /]? [<line-names>? <string> [<track-size> <line-names>]? ]+ syntax. 4959 // The template-columns <track-list> can't be 'none'. 4960 if (firstValueIsNone) 4961 return false; 4962 // It requires to rewind parsing due to previous syntax failures. 4963 m_valueList->setCurrentIndex(index); 4964 return parseGridTemplateRowsAndAreas(columnsValue, important); 4965} 4966 4967bool CSSParser::parseGridShorthand(bool important) 4968{ 4969 ShorthandScope scope(this, CSSPropertyWebkitGrid); 4970 ASSERT(shorthandForProperty(CSSPropertyWebkitGrid).length() == 6); 4971 4972 // 1- <grid-template> 4973 if (parseGridTemplateShorthand(important)) { 4974 // It can only be specified the explicit or the implicit grid properties in a single grid declaration. 4975 // The sub-properties not specified are set to their initial value, as normal for shorthands. 4976 addProperty(CSSPropertyWebkitGridAutoFlow, cssValuePool().createImplicitInitialValue(), important); 4977 addProperty(CSSPropertyWebkitGridAutoColumns, cssValuePool().createImplicitInitialValue(), important); 4978 addProperty(CSSPropertyWebkitGridAutoRows, cssValuePool().createImplicitInitialValue(), important); 4979 return true; 4980 } 4981 4982 // Need to rewind parsing to explore the alternative syntax of this shorthand. 4983 m_valueList->setCurrentIndex(0); 4984 4985 // 2- <grid-auto-flow> [ <grid-auto-columns> [ / <grid-auto-rows> ]? ] 4986 if (!parseValue(CSSPropertyWebkitGridAutoFlow, important)) 4987 return false; 4988 4989 RefPtr<CSSValue> autoColumnsValue; 4990 RefPtr<CSSValue> autoRowsValue; 4991 4992 if (m_valueList->current()) { 4993 autoColumnsValue = parseGridTrackSize(*m_valueList); 4994 if (!autoColumnsValue) 4995 return false; 4996 if (m_valueList->current()) { 4997 if (!isForwardSlashOperator(m_valueList->current()) || !m_valueList->next()) 4998 return false; 4999 autoRowsValue = parseGridTrackSize(*m_valueList); 5000 if (!autoRowsValue) 5001 return false; 5002 } 5003 if (m_valueList->current()) 5004 return false; 5005 } else { 5006 // Other omitted values are set to their initial values. 5007 autoColumnsValue = cssValuePool().createImplicitInitialValue(); 5008 autoRowsValue = cssValuePool().createImplicitInitialValue(); 5009 } 5010 5011 // if <grid-auto-rows> value is omitted, it is set to the value specified for grid-auto-columns. 5012 if (!autoRowsValue) 5013 autoRowsValue = autoColumnsValue; 5014 5015 addProperty(CSSPropertyWebkitGridAutoColumns, autoColumnsValue.release(), important); 5016 addProperty(CSSPropertyWebkitGridAutoRows, autoRowsValue.release(), important); 5017 5018 // It can only be specified the explicit or the implicit grid properties in a single grid declaration. 5019 // The sub-properties not specified are set to their initial value, as normal for shorthands. 5020 addProperty(CSSPropertyWebkitGridTemplateColumns, cssValuePool().createImplicitInitialValue(), important); 5021 addProperty(CSSPropertyWebkitGridTemplateRows, cssValuePool().createImplicitInitialValue(), important); 5022 addProperty(CSSPropertyWebkitGridTemplateAreas, cssValuePool().createImplicitInitialValue(), important); 5023 5024 return true; 5025} 5026 5027bool CSSParser::parseGridAreaShorthand(bool important) 5028{ 5029 ShorthandScope scope(this, CSSPropertyWebkitGridArea); 5030 ASSERT(shorthandForProperty(CSSPropertyWebkitGridArea).length() == 4); 5031 5032 RefPtr<CSSValue> rowStartValue = parseGridPosition(); 5033 if (!rowStartValue) 5034 return false; 5035 5036 RefPtr<CSSValue> columnStartValue; 5037 if (!parseSingleGridAreaLonghand(columnStartValue)) 5038 return false; 5039 5040 RefPtr<CSSValue> rowEndValue; 5041 if (!parseSingleGridAreaLonghand(rowEndValue)) 5042 return false; 5043 5044 RefPtr<CSSValue> columnEndValue; 5045 if (!parseSingleGridAreaLonghand(columnEndValue)) 5046 return false; 5047 5048 if (!columnStartValue) 5049 columnStartValue = gridMissingGridPositionValue(rowStartValue.get()); 5050 5051 if (!rowEndValue) 5052 rowEndValue = gridMissingGridPositionValue(rowStartValue.get()); 5053 5054 if (!columnEndValue) 5055 columnEndValue = gridMissingGridPositionValue(columnStartValue.get()); 5056 5057 addProperty(CSSPropertyWebkitGridRowStart, rowStartValue, important); 5058 addProperty(CSSPropertyWebkitGridColumnStart, columnStartValue, important); 5059 addProperty(CSSPropertyWebkitGridRowEnd, rowEndValue, important); 5060 addProperty(CSSPropertyWebkitGridColumnEnd, columnEndValue, important); 5061 return true; 5062} 5063 5064bool CSSParser::parseSingleGridAreaLonghand(RefPtr<CSSValue>& property) 5065{ 5066 if (!m_valueList->current()) 5067 return true; 5068 5069 if (!isForwardSlashOperator(m_valueList->current())) 5070 return false; 5071 5072 if (!m_valueList->next()) 5073 return false; 5074 5075 property = parseGridPosition(); 5076 return true; 5077} 5078 5079void CSSParser::parseGridLineNames(CSSParserValueList& inputList, CSSValueList& valueList, CSSGridLineNamesValue* previousNamedAreaTrailingLineNames) 5080{ 5081 ASSERT(inputList.current() && inputList.current()->unit == CSSParserValue::ValueList); 5082 5083 CSSParserValueList* identList = inputList.current()->valueList; 5084 if (!identList->size()) { 5085 inputList.next(); 5086 return; 5087 } 5088 5089 // Need to ensure the identList is at the heading index, since the parserList might have been rewound. 5090 identList->setCurrentIndex(0); 5091 5092 RefPtr<CSSGridLineNamesValue> lineNames = previousNamedAreaTrailingLineNames ? previousNamedAreaTrailingLineNames : CSSGridLineNamesValue::create(); 5093 while (CSSParserValue* identValue = identList->current()) { 5094 ASSERT(identValue->unit == CSSPrimitiveValue::CSS_IDENT); 5095 lineNames->append(createPrimitiveStringValue(identValue)); 5096 identList->next(); 5097 } 5098 if (!previousNamedAreaTrailingLineNames) 5099 valueList.append(lineNames.release()); 5100 5101 inputList.next(); 5102} 5103 5104PassRefPtr<CSSValue> CSSParser::parseGridTrackList() 5105{ 5106 CSSParserValue* value = m_valueList->current(); 5107 if (value->id == CSSValueNone) { 5108 m_valueList->next(); 5109 return cssValuePool().createIdentifierValue(CSSValueNone); 5110 } 5111 5112 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated(); 5113 // Handle leading <custom-ident>*. 5114 value = m_valueList->current(); 5115 if (value && value->unit == CSSParserValue::ValueList) 5116 parseGridLineNames(*m_valueList, *values); 5117 5118 bool seenTrackSizeOrRepeatFunction = false; 5119 while (CSSParserValue* currentValue = m_valueList->current()) { 5120 if (isForwardSlashOperator(currentValue)) 5121 break; 5122 if (currentValue->unit == CSSParserValue::Function && equalIgnoringCase(currentValue->function->name, "repeat(")) { 5123 if (!parseGridTrackRepeatFunction(*values)) 5124 return nullptr; 5125 } else { 5126 RefPtr<CSSValue> value = parseGridTrackSize(*m_valueList); 5127 if (!value) 5128 return nullptr; 5129 values->append(value.release()); 5130 } 5131 seenTrackSizeOrRepeatFunction = true; 5132 5133 // This will handle the trailing <custom-ident>* in the grammar. 5134 value = m_valueList->current(); 5135 if (value && value->unit == CSSParserValue::ValueList) 5136 parseGridLineNames(*m_valueList, *values); 5137 } 5138 5139 if (!seenTrackSizeOrRepeatFunction) 5140 return nullptr; 5141 5142 return values.release(); 5143} 5144 5145bool CSSParser::parseGridTrackRepeatFunction(CSSValueList& list) 5146{ 5147 CSSParserValueList* arguments = m_valueList->current()->function->args.get(); 5148 if (!arguments || arguments->size() < 3 || !validUnit(arguments->valueAt(0), FPositiveInteger) || !isComma(arguments->valueAt(1))) 5149 return false; 5150 5151 ASSERT_WITH_SECURITY_IMPLICATION(arguments->valueAt(0)->fValue > 0); 5152 size_t repetitions = arguments->valueAt(0)->fValue; 5153 // Clamp repetitions at MAX_GRID_TRACK_REPETITIONS. 5154 // http://www.w3.org/TR/css-grid-1/#repeat-notation 5155 if (repetitions > MAX_GRID_TRACK_REPETITIONS) 5156 repetitions = MAX_GRID_TRACK_REPETITIONS; 5157 RefPtr<CSSValueList> repeatedValues = CSSValueList::createSpaceSeparated(); 5158 arguments->next(); // Skip the repetition count. 5159 arguments->next(); // Skip the comma. 5160 5161 // Handle leading <custom-ident>*. 5162 CSSParserValue* currentValue = arguments->current(); 5163 if (currentValue && currentValue->unit == CSSParserValue::ValueList) 5164 parseGridLineNames(*arguments, *repeatedValues); 5165 5166 while (arguments->current()) { 5167 RefPtr<CSSValue> trackSize = parseGridTrackSize(*arguments); 5168 if (!trackSize) 5169 return false; 5170 5171 repeatedValues->append(trackSize.release()); 5172 5173 // This takes care of any trailing <custom-ident>* in the grammar. 5174 currentValue = arguments->current(); 5175 if (currentValue && currentValue->unit == CSSParserValue::ValueList) 5176 parseGridLineNames(*arguments, *repeatedValues); 5177 } 5178 5179 for (size_t i = 0; i < repetitions; ++i) { 5180 for (size_t j = 0; j < repeatedValues->length(); ++j) 5181 list.append(repeatedValues->itemWithoutBoundsCheck(j)); 5182 } 5183 5184 m_valueList->next(); 5185 return true; 5186} 5187 5188PassRefPtr<CSSValue> CSSParser::parseGridTrackSize(CSSParserValueList& inputList) 5189{ 5190 CSSParserValue* currentValue = inputList.current(); 5191 inputList.next(); 5192 5193 if (currentValue->id == CSSValueAuto) 5194 return cssValuePool().createIdentifierValue(CSSValueAuto); 5195 5196 if (currentValue->unit == CSSParserValue::Function && equalIgnoringCase(currentValue->function->name, "minmax(")) { 5197 // The spec defines the following grammar: minmax( <track-breadth> , <track-breadth> ) 5198 CSSParserValueList* arguments = currentValue->function->args.get(); 5199 if (!arguments || arguments->size() != 3 || !isComma(arguments->valueAt(1))) 5200 return 0; 5201 5202 RefPtr<CSSPrimitiveValue> minTrackBreadth = parseGridBreadth(arguments->valueAt(0)); 5203 if (!minTrackBreadth) 5204 return 0; 5205 5206 RefPtr<CSSPrimitiveValue> maxTrackBreadth = parseGridBreadth(arguments->valueAt(2)); 5207 if (!maxTrackBreadth) 5208 return 0; 5209 5210 RefPtr<CSSValueList> parsedArguments = CSSValueList::createCommaSeparated(); 5211 parsedArguments->append(minTrackBreadth); 5212 parsedArguments->append(maxTrackBreadth); 5213 return CSSFunctionValue::create("minmax(", parsedArguments); 5214 } 5215 5216 return parseGridBreadth(currentValue); 5217} 5218 5219PassRefPtr<CSSPrimitiveValue> CSSParser::parseGridBreadth(CSSParserValue* currentValue) 5220{ 5221 if (currentValue->id == CSSValueWebkitMinContent || currentValue->id == CSSValueWebkitMaxContent) 5222 return cssValuePool().createIdentifierValue(currentValue->id); 5223 5224 if (currentValue->unit == CSSPrimitiveValue::CSS_FR) { 5225 double flexValue = currentValue->fValue; 5226 5227 // Fractional unit is a non-negative dimension. 5228 if (flexValue <= 0) 5229 return 0; 5230 5231 return cssValuePool().createValue(flexValue, CSSPrimitiveValue::CSS_FR); 5232 } 5233 5234 if (!validUnit(currentValue, FNonNeg | FLength | FPercent)) 5235 return 0; 5236 5237 return createPrimitiveNumericValue(currentValue); 5238} 5239 5240static inline bool isValidGridAutoFlowId(CSSValueID id) 5241{ 5242 return (id == CSSValueRow || id == CSSValueColumn || id == CSSValueDense || id == CSSValueStack); 5243} 5244 5245PassRefPtr<CSSValue> CSSParser::parseGridAutoFlow(CSSParserValueList& inputList) 5246{ 5247 // [ row | column ] && dense? | stack && [ row | column ]? 5248 CSSParserValue* value = inputList.current(); 5249 if (!value) 5250 return nullptr; 5251 5252 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated(); 5253 5254 // First parameter. 5255 CSSValueID firstId = value->id; 5256 if (!isValidGridAutoFlowId(firstId)) 5257 return nullptr; 5258 5259 // Second parameter, if any. 5260 // If second parameter is not valid we should process anyway the first one as we can be inside the "grid" shorthand. 5261 value = inputList.next(); 5262 if (!value || !isValidGridAutoFlowId(value->id)) { 5263 if (firstId == CSSValueDense) 5264 return nullptr; 5265 5266 if (firstId == CSSValueStack) 5267 parsedValues->append(cssValuePool().createIdentifierValue(CSSValueRow)); 5268 5269 parsedValues->append(cssValuePool().createIdentifierValue(firstId)); 5270 return parsedValues; 5271 } 5272 5273 switch (firstId) { 5274 case CSSValueRow: 5275 case CSSValueColumn: 5276 parsedValues->append(cssValuePool().createIdentifierValue(firstId)); 5277 if (value->id == CSSValueDense || value->id == CSSValueStack) { 5278 parsedValues->append(cssValuePool().createIdentifierValue(value->id)); 5279 inputList.next(); 5280 } 5281 break; 5282 case CSSValueDense: 5283 case CSSValueStack: 5284 if (value->id == CSSValueRow || value->id == CSSValueColumn) { 5285 parsedValues->append(cssValuePool().createIdentifierValue(value->id)); 5286 inputList.next(); 5287 } 5288 parsedValues->append(cssValuePool().createIdentifierValue(firstId)); 5289 break; 5290 default: 5291 ASSERT_NOT_REACHED(); 5292 break; 5293 } 5294 5295 return parsedValues; 5296} 5297#endif /* ENABLE(CSS_GRID_LAYOUT) */ 5298 5299#if ENABLE(DASHBOARD_SUPPORT) 5300 5301#define DASHBOARD_REGION_NUM_PARAMETERS 6 5302#define DASHBOARD_REGION_SHORT_NUM_PARAMETERS 2 5303 5304static CSSParserValue* skipCommaInDashboardRegion(CSSParserValueList *args) 5305{ 5306 if (args->size() == (DASHBOARD_REGION_NUM_PARAMETERS*2-1) || 5307 args->size() == (DASHBOARD_REGION_SHORT_NUM_PARAMETERS*2-1)) { 5308 CSSParserValue* current = args->current(); 5309 if (current->unit == CSSParserValue::Operator && current->iValue == ',') 5310 return args->next(); 5311 } 5312 return args->current(); 5313} 5314 5315bool CSSParser::parseDashboardRegions(CSSPropertyID propId, bool important) 5316{ 5317 bool valid = true; 5318 5319 CSSParserValue* value = m_valueList->current(); 5320 5321 if (value->id == CSSValueNone) { 5322 if (m_valueList->next()) 5323 return false; 5324 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 5325 return valid; 5326 } 5327 5328 RefPtr<DashboardRegion> firstRegion = DashboardRegion::create(); 5329 DashboardRegion* region = 0; 5330 5331 while (value) { 5332 if (region == 0) { 5333 region = firstRegion.get(); 5334 } else { 5335 RefPtr<DashboardRegion> nextRegion = DashboardRegion::create(); 5336 region->m_next = nextRegion; 5337 region = nextRegion.get(); 5338 } 5339 5340 if (value->unit != CSSParserValue::Function) { 5341 valid = false; 5342 break; 5343 } 5344 5345 // Commas count as values, so allow (function name is dashboard-region for DASHBOARD_SUPPORT feature): 5346 // dashboard-region(label, type, t, r, b, l) or dashboard-region(label type t r b l) 5347 // dashboard-region(label, type, t, r, b, l) or dashboard-region(label type t r b l) 5348 // also allow 5349 // dashboard-region(label, type) or dashboard-region(label type) 5350 // dashboard-region(label, type) or dashboard-region(label type) 5351 CSSParserValueList* args = value->function->args.get(); 5352 if (!equalIgnoringCase(value->function->name, "dashboard-region(") || !args) { 5353 valid = false; 5354 break; 5355 } 5356 5357 int numArgs = args->size(); 5358 if ((numArgs != DASHBOARD_REGION_NUM_PARAMETERS && numArgs != (DASHBOARD_REGION_NUM_PARAMETERS*2-1)) && 5359 (numArgs != DASHBOARD_REGION_SHORT_NUM_PARAMETERS && numArgs != (DASHBOARD_REGION_SHORT_NUM_PARAMETERS*2-1))) { 5360 valid = false; 5361 break; 5362 } 5363 5364 // First arg is a label. 5365 CSSParserValue* arg = args->current(); 5366 if (arg->unit != CSSPrimitiveValue::CSS_IDENT) { 5367 valid = false; 5368 break; 5369 } 5370 5371 region->m_label = arg->string; 5372 5373 // Second arg is a type. 5374 arg = args->next(); 5375 arg = skipCommaInDashboardRegion(args); 5376 if (arg->unit != CSSPrimitiveValue::CSS_IDENT) { 5377 valid = false; 5378 break; 5379 } 5380 5381 if (equalIgnoringCase(arg, "circle")) 5382 region->m_isCircle = true; 5383 else if (equalIgnoringCase(arg, "rectangle")) 5384 region->m_isRectangle = true; 5385 else { 5386 valid = false; 5387 break; 5388 } 5389 5390 region->m_geometryType = arg->string; 5391 5392 if (numArgs == DASHBOARD_REGION_SHORT_NUM_PARAMETERS || numArgs == (DASHBOARD_REGION_SHORT_NUM_PARAMETERS*2-1)) { 5393 // This originally used CSSValueInvalid by accident. It might be more logical to use something else. 5394 RefPtr<CSSPrimitiveValue> amount = cssValuePool().createIdentifierValue(CSSValueInvalid); 5395 5396 region->setTop(amount); 5397 region->setRight(amount); 5398 region->setBottom(amount); 5399 region->setLeft(amount); 5400 } else { 5401 // Next four arguments must be offset numbers 5402 int i; 5403 for (i = 0; i < 4; i++) { 5404 arg = args->next(); 5405 arg = skipCommaInDashboardRegion(args); 5406 5407 valid = arg->id == CSSValueAuto || validUnit(arg, FLength); 5408 if (!valid) 5409 break; 5410 5411 RefPtr<CSSPrimitiveValue> amount = arg->id == CSSValueAuto ? 5412 cssValuePool().createIdentifierValue(CSSValueAuto) : 5413 createPrimitiveNumericValue(arg); 5414 5415 if (i == 0) 5416 region->setTop(amount); 5417 else if (i == 1) 5418 region->setRight(amount); 5419 else if (i == 2) 5420 region->setBottom(amount); 5421 else 5422 region->setLeft(amount); 5423 } 5424 } 5425 5426 if (args->next()) 5427 return false; 5428 5429 value = m_valueList->next(); 5430 } 5431 5432 if (valid) 5433 addProperty(propId, cssValuePool().createValue(firstRegion.release()), important); 5434 5435 return valid; 5436} 5437 5438#endif /* ENABLE(DASHBOARD_SUPPORT) */ 5439 5440#if ENABLE(CSS_GRID_LAYOUT) 5441bool CSSParser::parseGridTemplateAreasRow(NamedGridAreaMap& gridAreaMap, const unsigned rowCount, unsigned& columnCount) 5442{ 5443 CSSParserValue* currentValue = m_valueList->current(); 5444 if (!currentValue || currentValue->unit != CSSPrimitiveValue::CSS_STRING) 5445 return false; 5446 5447 String gridRowNames = currentValue->string; 5448 if (gridRowNames.isEmpty()) 5449 return false; 5450 5451 Vector<String> columnNames; 5452 gridRowNames.split(' ', columnNames); 5453 5454 if (!columnCount) { 5455 columnCount = columnNames.size(); 5456 ASSERT(columnCount); 5457 } else if (columnCount != columnNames.size()) { 5458 // The declaration is invalid is all the rows don't have the number of columns. 5459 return false; 5460 } 5461 5462 for (unsigned currentColumn = 0; currentColumn < columnCount; ++currentColumn) { 5463 const String& gridAreaName = columnNames[currentColumn]; 5464 5465 // Unamed areas are always valid (we consider them to be 1x1). 5466 if (gridAreaName == ".") 5467 continue; 5468 5469 // We handle several grid areas with the same name at once to simplify the validation code. 5470 unsigned lookAheadColumn; 5471 for (lookAheadColumn = currentColumn; lookAheadColumn < columnCount - 1; ++lookAheadColumn) { 5472 if (columnNames[lookAheadColumn + 1] != gridAreaName) 5473 break; 5474 } 5475 5476 auto gridAreaIterator = gridAreaMap.find(gridAreaName); 5477 if (gridAreaIterator == gridAreaMap.end()) 5478 gridAreaMap.add(gridAreaName, GridCoordinate(GridSpan(rowCount, rowCount), GridSpan(currentColumn, lookAheadColumn))); 5479 else { 5480 GridCoordinate& gridCoordinate = gridAreaIterator->value; 5481 5482 // The following checks test that the grid area is a single filled-in rectangle. 5483 // 1. The new row is adjacent to the previously parsed row. 5484 if (rowCount != gridCoordinate.rows.resolvedFinalPosition.next().toInt()) 5485 return 0; 5486 5487 // 2. The new area starts at the same position as the previously parsed area. 5488 if (currentColumn != gridCoordinate.columns.resolvedInitialPosition.toInt()) 5489 return 0; 5490 5491 // 3. The new area ends at the same position as the previously parsed area. 5492 if (lookAheadColumn != gridCoordinate.columns.resolvedFinalPosition.toInt()) 5493 return 0; 5494 5495 ++gridCoordinate.rows.resolvedFinalPosition; 5496 } 5497 currentColumn = lookAheadColumn; 5498 } 5499 5500 m_valueList->next(); 5501 return true; 5502} 5503 5504PassRefPtr<CSSValue> CSSParser::parseGridTemplateAreas() 5505{ 5506 NamedGridAreaMap gridAreaMap; 5507 unsigned rowCount = 0; 5508 unsigned columnCount = 0; 5509 5510 while (m_valueList->current()) { 5511 if (!parseGridTemplateAreasRow(gridAreaMap, rowCount, columnCount)) 5512 return 0; 5513 ++rowCount; 5514 } 5515 5516 if (!rowCount || !columnCount) 5517 return 0; 5518 5519 return CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount); 5520} 5521#endif /* ENABLE(CSS_GRID_LAYOUT) */ 5522 5523PassRefPtr<CSSValue> CSSParser::parseCounterContent(CSSParserValueList* args, bool counters) 5524{ 5525 unsigned numArgs = args->size(); 5526 if (counters && numArgs != 3 && numArgs != 5) 5527 return 0; 5528 if (!counters && numArgs != 1 && numArgs != 3) 5529 return 0; 5530 5531 CSSParserValue* i = args->current(); 5532 if (i->unit != CSSPrimitiveValue::CSS_IDENT) 5533 return 0; 5534 RefPtr<CSSPrimitiveValue> identifier = createPrimitiveStringValue(i); 5535 5536 RefPtr<CSSPrimitiveValue> separator; 5537 if (!counters) 5538 separator = cssValuePool().createValue(String(), CSSPrimitiveValue::CSS_STRING); 5539 else { 5540 i = args->next(); 5541 if (i->unit != CSSParserValue::Operator || i->iValue != ',') 5542 return 0; 5543 5544 i = args->next(); 5545 if (i->unit != CSSPrimitiveValue::CSS_STRING) 5546 return 0; 5547 5548 separator = createPrimitiveStringValue(i); 5549 } 5550 5551 RefPtr<CSSPrimitiveValue> listStyle; 5552 i = args->next(); 5553 if (!i) // Make the list style default decimal 5554 listStyle = cssValuePool().createIdentifierValue(CSSValueDecimal); 5555 else { 5556 if (i->unit != CSSParserValue::Operator || i->iValue != ',') 5557 return 0; 5558 5559 i = args->next(); 5560 if (i->unit != CSSPrimitiveValue::CSS_IDENT) 5561 return 0; 5562 5563 CSSValueID listStyleID = CSSValueInvalid; 5564 if (i->id == CSSValueNone || (i->id >= CSSValueDisc && i->id <= CSSValueKatakanaIroha)) 5565 listStyleID = i->id; 5566 else 5567 return 0; 5568 5569 listStyle = cssValuePool().createIdentifierValue(listStyleID); 5570 } 5571 5572 return cssValuePool().createValue(Counter::create(identifier.release(), listStyle.release(), separator.release())); 5573} 5574 5575bool CSSParser::parseClipShape(CSSPropertyID propId, bool important) 5576{ 5577 CSSParserValue* value = m_valueList->current(); 5578 CSSParserValueList* args = value->function->args.get(); 5579 5580 if (!equalIgnoringCase(value->function->name, "rect(") || !args) 5581 return false; 5582 5583 // rect(t, r, b, l) || rect(t r b l) 5584 if (args->size() != 4 && args->size() != 7) 5585 return false; 5586 RefPtr<Rect> rect = Rect::create(); 5587 bool valid = true; 5588 int i = 0; 5589 CSSParserValue* a = args->current(); 5590 while (a) { 5591 valid = a->id == CSSValueAuto || validUnit(a, FLength); 5592 if (!valid) 5593 break; 5594 RefPtr<CSSPrimitiveValue> length = a->id == CSSValueAuto ? 5595 cssValuePool().createIdentifierValue(CSSValueAuto) : 5596 createPrimitiveNumericValue(a); 5597 if (i == 0) 5598 rect->setTop(length); 5599 else if (i == 1) 5600 rect->setRight(length); 5601 else if (i == 2) 5602 rect->setBottom(length); 5603 else 5604 rect->setLeft(length); 5605 a = args->next(); 5606 if (a && args->size() == 7) { 5607 if (a->unit == CSSParserValue::Operator && a->iValue == ',') { 5608 a = args->next(); 5609 } else { 5610 valid = false; 5611 break; 5612 } 5613 } 5614 i++; 5615 } 5616 if (valid) { 5617 addProperty(propId, cssValuePool().createValue(rect.release()), important); 5618 m_valueList->next(); 5619 return true; 5620 } 5621 return false; 5622} 5623 5624static void completeBorderRadii(RefPtr<CSSPrimitiveValue> radii[4]) 5625{ 5626 if (radii[3]) 5627 return; 5628 if (!radii[2]) { 5629 if (!radii[1]) 5630 radii[1] = radii[0]; 5631 radii[2] = radii[0]; 5632 } 5633 radii[3] = radii[1]; 5634} 5635 5636// FIXME: This should be refactored with CSSParser::parseBorderRadius. 5637// CSSParser::parseBorderRadius contains support for some legacy radius construction. 5638PassRefPtr<CSSBasicShape> CSSParser::parseInsetRoundedCorners(PassRefPtr<CSSBasicShapeInset> shape, CSSParserValueList* args) 5639{ 5640 CSSParserValue* argument = args->next(); 5641 5642 if (!argument) 5643 return nullptr; 5644 5645 Vector<CSSParserValue*> radiusArguments; 5646 while (argument) { 5647 radiusArguments.append(argument); 5648 argument = args->next(); 5649 } 5650 5651 unsigned num = radiusArguments.size(); 5652 if (!num || num > 9) 5653 return nullptr; 5654 5655 RefPtr<CSSPrimitiveValue> radii[2][4]; 5656 5657 unsigned indexAfterSlash = 0; 5658 for (unsigned i = 0; i < num; ++i) { 5659 CSSParserValue* value = radiusArguments.at(i); 5660 if (value->unit == CSSParserValue::Operator) { 5661 if (value->iValue != '/') 5662 return nullptr; 5663 5664 if (!i || indexAfterSlash || i + 1 == num || num > i + 5) 5665 return nullptr; 5666 5667 indexAfterSlash = i + 1; 5668 completeBorderRadii(radii[0]); 5669 continue; 5670 } 5671 5672 if (i - indexAfterSlash >= 4) 5673 return nullptr; 5674 5675 if (!validUnit(value, FLength | FPercent | FNonNeg)) 5676 return nullptr; 5677 5678 RefPtr<CSSPrimitiveValue> radius = createPrimitiveNumericValue(value); 5679 5680 if (!indexAfterSlash) 5681 radii[0][i] = radius; 5682 else 5683 radii[1][i - indexAfterSlash] = radius.release(); 5684 } 5685 5686 if (!indexAfterSlash) { 5687 completeBorderRadii(radii[0]); 5688 for (unsigned i = 0; i < 4; ++i) 5689 radii[1][i] = radii[0][i]; 5690 } else 5691 completeBorderRadii(radii[1]); 5692 5693 shape->setTopLeftRadius(createPrimitiveValuePair(radii[0][0].release(), radii[1][0].release())); 5694 shape->setTopRightRadius(createPrimitiveValuePair(radii[0][1].release(), radii[1][1].release())); 5695 shape->setBottomRightRadius(createPrimitiveValuePair(radii[0][2].release(), radii[1][2].release())); 5696 shape->setBottomLeftRadius(createPrimitiveValuePair(radii[0][3].release(), radii[1][3].release())); 5697 5698 return shape; 5699} 5700 5701PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeInset(CSSParserValueList* args) 5702{ 5703 ASSERT(args); 5704 5705 RefPtr<CSSBasicShapeInset> shape = CSSBasicShapeInset::create(); 5706 5707 CSSParserValue* argument = args->current(); 5708 Vector<RefPtr<CSSPrimitiveValue> > widthArguments; 5709 bool hasRoundedInset = false; 5710 while (argument) { 5711 if (argument->unit == CSSPrimitiveValue::CSS_IDENT && equalIgnoringCase(argument->string, "round")) { 5712 hasRoundedInset = true; 5713 break; 5714 } 5715 5716 Units unitFlags = FLength | FPercent; 5717 if (!validUnit(argument, unitFlags) || widthArguments.size() > 4) 5718 return nullptr; 5719 5720 widthArguments.append(createPrimitiveNumericValue(argument)); 5721 argument = args->next(); 5722 } 5723 5724 switch (widthArguments.size()) { 5725 case 1: { 5726 shape->updateShapeSize1Value(widthArguments[0].get()); 5727 break; 5728 } 5729 case 2: { 5730 shape->updateShapeSize2Values(widthArguments[0].get(), widthArguments[1].get()); 5731 break; 5732 } 5733 case 3: { 5734 shape->updateShapeSize3Values(widthArguments[0].get(), widthArguments[1].get(), widthArguments[2].get()); 5735 break; 5736 } 5737 case 4: { 5738 shape->updateShapeSize4Values(widthArguments[0].get(), widthArguments[1].get(), widthArguments[2].get(), widthArguments[3].get()); 5739 break; 5740 } 5741 default: 5742 return nullptr; 5743 } 5744 5745 if (hasRoundedInset) 5746 return parseInsetRoundedCorners(shape, args); 5747 return shape; 5748} 5749 5750PassRefPtr<CSSPrimitiveValue> CSSParser::parseShapeRadius(CSSParserValue* value) 5751{ 5752 if (value->id == CSSValueClosestSide || value->id == CSSValueFarthestSide) 5753 return cssValuePool().createIdentifierValue(value->id); 5754 5755 if (!validUnit(value, FLength | FPercent | FNonNeg)) 5756 return 0; 5757 5758 return createPrimitiveNumericValue(value); 5759} 5760 5761PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeCircle(CSSParserValueList* args) 5762{ 5763 ASSERT(args); 5764 5765 // circle(radius) 5766 // circle(radius at <position>) 5767 // circle(at <position>) 5768 // where position defines centerX and centerY using a CSS <position> data type. 5769 RefPtr<CSSBasicShapeCircle> shape = CSSBasicShapeCircle::create(); 5770 5771 for (CSSParserValue* argument = args->current(); argument; argument = args->next()) { 5772 // The call to parseFillPosition below should consume all of the 5773 // arguments except the first two. Thus, and index greater than one 5774 // indicates an invalid production. 5775 if (args->currentIndex() > 1) 5776 return 0; 5777 5778 if (!args->currentIndex() && argument->id != CSSValueAt) { 5779 if (RefPtr<CSSPrimitiveValue> radius = parseShapeRadius(argument)) { 5780 shape->setRadius(radius); 5781 continue; 5782 } 5783 5784 return 0; 5785 } 5786 5787 if (argument->id == CSSValueAt && args->next()) { 5788 RefPtr<CSSValue> centerX; 5789 RefPtr<CSSValue> centerY; 5790 parseFillPosition(args, centerX, centerY); 5791 if (centerX && centerY && !args->current()) { 5792 ASSERT(centerX->isPrimitiveValue()); 5793 ASSERT(centerY->isPrimitiveValue()); 5794 shape->setCenterX(toCSSPrimitiveValue(centerX.get())); 5795 shape->setCenterY(toCSSPrimitiveValue(centerY.get())); 5796 } else 5797 return 0; 5798 } else 5799 return 0; 5800 } 5801 5802 return shape; 5803} 5804 5805PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeEllipse(CSSParserValueList* args) 5806{ 5807 ASSERT(args); 5808 5809 // ellipse(radiusX) 5810 // ellipse(radiusX at <position>) 5811 // ellipse(radiusX radiusY) 5812 // ellipse(radiusX radiusY at <position>) 5813 // ellipse(at <position>) 5814 // where position defines centerX and centerY using a CSS <position> data type. 5815 RefPtr<CSSBasicShapeEllipse> shape = CSSBasicShapeEllipse::create(); 5816 5817 for (CSSParserValue* argument = args->current(); argument; argument = args->next()) { 5818 // The call to parseFillPosition below should consume all of the 5819 // arguments except the first three. Thus, an index greater than two 5820 // indicates an invalid production. 5821 if (args->currentIndex() > 2) 5822 return 0; 5823 5824 if (args->currentIndex() < 2 && argument->id != CSSValueAt) { 5825 if (RefPtr<CSSPrimitiveValue> radius = parseShapeRadius(argument)) { 5826 if (!shape->radiusX()) 5827 shape->setRadiusX(radius); 5828 else 5829 shape->setRadiusY(radius); 5830 continue; 5831 } 5832 5833 return 0; 5834 } 5835 5836 if (argument->id != CSSValueAt || !args->next()) // expecting ellipse(.. at <position>) 5837 return 0; 5838 5839 RefPtr<CSSValue> centerX; 5840 RefPtr<CSSValue> centerY; 5841 parseFillPosition(args, centerX, centerY); 5842 if (!centerX || !centerY || args->current()) 5843 return 0; 5844 5845 ASSERT(centerX->isPrimitiveValue()); 5846 ASSERT(centerY->isPrimitiveValue()); 5847 shape->setCenterX(toCSSPrimitiveValue(centerX.get())); 5848 shape->setCenterY(toCSSPrimitiveValue(centerY.get())); 5849 } 5850 5851 return shape; 5852} 5853 5854PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapePolygon(CSSParserValueList* args) 5855{ 5856 ASSERT(args); 5857 5858 unsigned size = args->size(); 5859 if (!size) 5860 return 0; 5861 5862 RefPtr<CSSBasicShapePolygon> shape = CSSBasicShapePolygon::create(); 5863 5864 CSSParserValue* argument = args->current(); 5865 if (argument->id == CSSValueEvenodd || argument->id == CSSValueNonzero) { 5866 shape->setWindRule(argument->id == CSSValueEvenodd ? RULE_EVENODD : RULE_NONZERO); 5867 5868 if (!isComma(args->next())) 5869 return 0; 5870 5871 argument = args->next(); 5872 size -= 2; 5873 } 5874 5875 // <length> <length>, ... <length> <length> -> each pair has 3 elements except the last one 5876 if (!size || (size % 3) - 2) 5877 return 0; 5878 5879 CSSParserValue* argumentX = argument; 5880 while (argumentX) { 5881 5882 if (!validUnit(argumentX, FLength | FPercent)) 5883 return 0; 5884 RefPtr<CSSPrimitiveValue> xLength = createPrimitiveNumericValue(argumentX); 5885 5886 CSSParserValue* argumentY = args->next(); 5887 if (!argumentY || !validUnit(argumentY, FLength | FPercent)) 5888 return 0; 5889 RefPtr<CSSPrimitiveValue> yLength = createPrimitiveNumericValue(argumentY); 5890 5891 shape->appendPoint(xLength.release(), yLength.release()); 5892 5893 CSSParserValue* commaOrNull = args->next(); 5894 if (!commaOrNull) 5895 argumentX = 0; 5896 else if (!isComma(commaOrNull)) 5897 return 0; 5898 else 5899 argumentX = args->next(); 5900 } 5901 5902 return shape; 5903} 5904 5905static bool isBoxValue(CSSValueID valueId, CSSPropertyID propId) 5906{ 5907 switch (valueId) { 5908 case CSSValueContentBox: 5909 case CSSValuePaddingBox: 5910 case CSSValueBorderBox: 5911 case CSSValueMarginBox: 5912 return true; 5913 case CSSValueFill: 5914 case CSSValueStroke: 5915 case CSSValueViewBox: 5916 return propId == CSSPropertyWebkitClipPath; 5917 default: break; 5918 } 5919 5920 return false; 5921} 5922 5923PassRefPtr<CSSValue> CSSParser::parseBasicShapeAndOrBox(CSSPropertyID propId) 5924{ 5925 CSSParserValue* value = m_valueList->current(); 5926 5927 bool shapeFound = false; 5928 bool boxFound = false; 5929 CSSValueID valueId; 5930 5931 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 5932 for (unsigned i = 0; i < 2; ++i) { 5933 if (!value) 5934 break; 5935 valueId = value->id; 5936 if (value->unit == CSSParserValue::Function && !shapeFound) { 5937 // parseBasicShape already asks for the next value list item. 5938 RefPtr<CSSPrimitiveValue> shapeValue = parseBasicShape(); 5939 if (!shapeValue) 5940 return nullptr; 5941 list->append(shapeValue.release()); 5942 shapeFound = true; 5943 } else if (isBoxValue(valueId, propId) && !boxFound) { 5944 list->append(parseValidPrimitive(valueId, value)); 5945 boxFound = true; 5946 m_valueList->next(); 5947 } else 5948 return nullptr; 5949 value = m_valueList->current(); 5950 } 5951 5952 if (m_valueList->current()) 5953 return nullptr; 5954 return list.release(); 5955} 5956 5957#if ENABLE(CSS_SHAPES) 5958PassRefPtr<CSSValue> CSSParser::parseShapeProperty(CSSPropertyID propId) 5959{ 5960 if (!RuntimeEnabledFeatures::sharedFeatures().cssShapesEnabled()) 5961 return nullptr; 5962 5963 CSSParserValue* value = m_valueList->current(); 5964 CSSValueID valueId = value->id; 5965 RefPtr<CSSPrimitiveValue> keywordValue; 5966 RefPtr<CSSPrimitiveValue> shapeValue; 5967 5968 if (valueId == CSSValueNone) { 5969 keywordValue = parseValidPrimitive(valueId, value); 5970 m_valueList->next(); 5971 return keywordValue.release(); 5972 } 5973 5974 RefPtr<CSSValue> imageValue; 5975 if (valueId != CSSValueNone && parseFillImage(m_valueList.get(), imageValue)) { 5976 m_valueList->next(); 5977 return imageValue.release(); 5978 } 5979 5980 return parseBasicShapeAndOrBox(propId); 5981} 5982#endif 5983 5984PassRefPtr<CSSValue> CSSParser::parseClipPath() 5985{ 5986 CSSParserValue* value = m_valueList->current(); 5987 CSSValueID valueId = value->id; 5988 5989 if (valueId == CSSValueNone) { 5990 m_valueList->next(); 5991 return parseValidPrimitive(valueId, value); 5992 } 5993 if (value->unit == CSSPrimitiveValue::CSS_URI) { 5994 m_valueList->next(); 5995 return CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_URI); 5996 } 5997 5998 return parseBasicShapeAndOrBox(CSSPropertyWebkitClipPath); 5999} 6000 6001PassRefPtr<CSSPrimitiveValue> CSSParser::parseBasicShape() 6002{ 6003 CSSParserValue* value = m_valueList->current(); 6004 ASSERT(value->unit == CSSParserValue::Function); 6005 CSSParserValueList* args = value->function->args.get(); 6006 6007 if (!args) 6008 return nullptr; 6009 6010 RefPtr<CSSBasicShape> shape; 6011 if (equalIgnoringCase(value->function->name, "circle(")) 6012 shape = parseBasicShapeCircle(args); 6013 else if (equalIgnoringCase(value->function->name, "ellipse(")) 6014 shape = parseBasicShapeEllipse(args); 6015 else if (equalIgnoringCase(value->function->name, "polygon(")) 6016 shape = parseBasicShapePolygon(args); 6017 else if (equalIgnoringCase(value->function->name, "inset(")) 6018 shape = parseBasicShapeInset(args); 6019 6020 if (!shape) 6021 return nullptr; 6022 6023 m_valueList->next(); 6024 return cssValuePool().createValue(shape.release()); 6025} 6026 6027// [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 'font-family' 6028bool CSSParser::parseFont(bool important) 6029{ 6030 // Let's check if there is an inherit or initial somewhere in the shorthand. 6031 for (unsigned i = 0; i < m_valueList->size(); ++i) { 6032 if (m_valueList->valueAt(i)->id == CSSValueInherit || m_valueList->valueAt(i)->id == CSSValueInitial) 6033 return false; 6034 } 6035 6036 ShorthandScope scope(this, CSSPropertyFont); 6037 // Optional font-style, font-variant and font-weight. 6038 bool fontStyleParsed = false; 6039 bool fontVariantParsed = false; 6040 bool fontWeightParsed = false; 6041 CSSParserValue* value; 6042 while ((value = m_valueList->current())) { 6043 if (!fontStyleParsed && isValidKeywordPropertyAndValue(CSSPropertyFontStyle, value->id, m_context)) { 6044 addProperty(CSSPropertyFontStyle, cssValuePool().createIdentifierValue(value->id), important); 6045 fontStyleParsed = true; 6046 } else if (!fontVariantParsed && (value->id == CSSValueNormal || value->id == CSSValueSmallCaps)) { 6047 // Font variant in the shorthand is particular, it only accepts normal or small-caps. 6048 addProperty(CSSPropertyFontVariant, cssValuePool().createIdentifierValue(value->id), important); 6049 fontVariantParsed = true; 6050 } else if (!fontWeightParsed && parseFontWeight(important)) 6051 fontWeightParsed = true; 6052 else 6053 break; 6054 m_valueList->next(); 6055 } 6056 6057 if (!value) 6058 return false; 6059 6060 if (!fontStyleParsed) 6061 addProperty(CSSPropertyFontStyle, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 6062 if (!fontVariantParsed) 6063 addProperty(CSSPropertyFontVariant, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 6064 if (!fontWeightParsed) 6065 addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 6066 6067 // Now a font size _must_ come. 6068 // <absolute-size> | <relative-size> | <length> | <percentage> | inherit 6069 if (!parseFontSize(important)) 6070 return false; 6071 6072 value = m_valueList->current(); 6073 if (!value) 6074 return false; 6075 6076 if (isForwardSlashOperator(value)) { 6077 // The line-height property. 6078 value = m_valueList->next(); 6079 if (!value) 6080 return false; 6081 if (!parseLineHeight(important)) 6082 return false; 6083 } else 6084 addProperty(CSSPropertyLineHeight, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 6085 6086 // Font family must come now. 6087 RefPtr<CSSValue> parsedFamilyValue = parseFontFamily(); 6088 if (!parsedFamilyValue) 6089 return false; 6090 6091 addProperty(CSSPropertyFontFamily, parsedFamilyValue.release(), important); 6092 6093 // FIXME: http://www.w3.org/TR/2011/WD-css3-fonts-20110324/#font-prop requires that 6094 // "font-stretch", "font-size-adjust", and "font-kerning" be reset to their initial values 6095 // but we don't seem to support them at the moment. They should also be added here once implemented. 6096 if (m_valueList->current()) 6097 return false; 6098 6099 return true; 6100} 6101 6102class FontFamilyValueBuilder { 6103public: 6104 FontFamilyValueBuilder(CSSValueList* list) 6105 : m_list(list) 6106 { 6107 } 6108 6109 void add(const CSSParserString& string) 6110 { 6111 if (!m_builder.isEmpty()) 6112 m_builder.append(' '); 6113 6114 if (string.is8Bit()) { 6115 m_builder.append(string.characters8(), string.length()); 6116 return; 6117 } 6118 6119 m_builder.append(string.characters16(), string.length()); 6120 } 6121 6122 void commit() 6123 { 6124 if (m_builder.isEmpty()) 6125 return; 6126 m_list->append(cssValuePool().createFontFamilyValue(m_builder.toString())); 6127 m_builder.clear(); 6128 } 6129 6130private: 6131 StringBuilder m_builder; 6132 CSSValueList* m_list; 6133}; 6134 6135PassRefPtr<CSSValueList> CSSParser::parseFontFamily() 6136{ 6137 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 6138 CSSParserValue* value = m_valueList->current(); 6139 6140 FontFamilyValueBuilder familyBuilder(list.get()); 6141 bool inFamily = false; 6142 6143 while (value) { 6144 CSSParserValue* nextValue = m_valueList->next(); 6145 bool nextValBreaksFont = !nextValue || 6146 (nextValue->unit == CSSParserValue::Operator && nextValue->iValue == ','); 6147 bool nextValIsFontName = nextValue && 6148 ((nextValue->id >= CSSValueSerif && nextValue->id <= CSSValueWebkitBody) || 6149 (nextValue->unit == CSSPrimitiveValue::CSS_STRING || nextValue->unit == CSSPrimitiveValue::CSS_IDENT)); 6150 6151 bool valueIsKeyword = value->id == CSSValueInitial || value->id == CSSValueInherit || value->id == CSSValueDefault; 6152 if (valueIsKeyword && !inFamily) { 6153 if (nextValBreaksFont) 6154 value = m_valueList->next(); 6155 else if (nextValIsFontName) 6156 value = nextValue; 6157 continue; 6158 } 6159 6160 if (value->id >= CSSValueSerif && value->id <= CSSValueWebkitBody) { 6161 if (inFamily) 6162 familyBuilder.add(value->string); 6163 else if (nextValBreaksFont || !nextValIsFontName) 6164 list->append(cssValuePool().createIdentifierValue(value->id)); 6165 else { 6166 familyBuilder.commit(); 6167 familyBuilder.add(value->string); 6168 inFamily = true; 6169 } 6170 } else if (value->unit == CSSPrimitiveValue::CSS_STRING) { 6171 // Strings never share in a family name. 6172 inFamily = false; 6173 familyBuilder.commit(); 6174 list->append(cssValuePool().createFontFamilyValue(value->string)); 6175 } else if (value->unit == CSSPrimitiveValue::CSS_IDENT) { 6176 if (inFamily) 6177 familyBuilder.add(value->string); 6178 else if (nextValBreaksFont || !nextValIsFontName) 6179 list->append(cssValuePool().createFontFamilyValue(value->string)); 6180 else { 6181 familyBuilder.commit(); 6182 familyBuilder.add(value->string); 6183 inFamily = true; 6184 } 6185 } else { 6186 break; 6187 } 6188 6189 if (!nextValue) 6190 break; 6191 6192 if (nextValBreaksFont) { 6193 value = m_valueList->next(); 6194 familyBuilder.commit(); 6195 inFamily = false; 6196 } 6197 else if (nextValIsFontName) 6198 value = nextValue; 6199 else 6200 break; 6201 } 6202 familyBuilder.commit(); 6203 6204 if (!list->length()) 6205 list = 0; 6206 return list.release(); 6207} 6208 6209bool CSSParser::parseLineHeight(bool important) 6210{ 6211 CSSParserValue* value = m_valueList->current(); 6212 CSSValueID id = value->id; 6213 bool validPrimitive = false; 6214 // normal | <number> | <length> | <percentage> | inherit 6215 if (id == CSSValueNormal) 6216 validPrimitive = true; 6217 else 6218 validPrimitive = (!id && validUnit(value, FNumber | FLength | FPercent | FNonNeg)); 6219 if (validPrimitive && (!m_valueList->next() || inShorthand())) 6220 addProperty(CSSPropertyLineHeight, parseValidPrimitive(id, value), important); 6221 return validPrimitive; 6222} 6223 6224bool CSSParser::parseFontSize(bool important) 6225{ 6226 CSSParserValue* value = m_valueList->current(); 6227 CSSValueID id = value->id; 6228 bool validPrimitive = false; 6229 // <absolute-size> | <relative-size> | <length> | <percentage> | inherit 6230 if (id >= CSSValueXxSmall && id <= CSSValueLarger) 6231 validPrimitive = true; 6232 else 6233 validPrimitive = validUnit(value, FLength | FPercent | FNonNeg); 6234 if (validPrimitive && (!m_valueList->next() || inShorthand())) 6235 addProperty(CSSPropertyFontSize, parseValidPrimitive(id, value), important); 6236 return validPrimitive; 6237} 6238 6239bool CSSParser::parseFontVariant(bool important) 6240{ 6241 RefPtr<CSSValueList> values; 6242 if (m_valueList->size() > 1) 6243 values = CSSValueList::createCommaSeparated(); 6244 CSSParserValue* val; 6245 bool expectComma = false; 6246 while ((val = m_valueList->current())) { 6247 RefPtr<CSSPrimitiveValue> parsedValue; 6248 if (!expectComma) { 6249 expectComma = true; 6250 if (val->id == CSSValueNormal || val->id == CSSValueSmallCaps) 6251 parsedValue = cssValuePool().createIdentifierValue(val->id); 6252 else if (val->id == CSSValueAll && !values) { 6253 // 'all' is only allowed in @font-face and with no other values. Make a value list to 6254 // indicate that we are in the @font-face case. 6255 values = CSSValueList::createCommaSeparated(); 6256 parsedValue = cssValuePool().createIdentifierValue(val->id); 6257 } 6258 } else if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 6259 expectComma = false; 6260 m_valueList->next(); 6261 continue; 6262 } 6263 6264 if (!parsedValue) 6265 return false; 6266 6267 m_valueList->next(); 6268 6269 if (values) 6270 values->append(parsedValue.release()); 6271 else { 6272 addProperty(CSSPropertyFontVariant, parsedValue.release(), important); 6273 return true; 6274 } 6275 } 6276 6277 if (values && values->length()) { 6278 m_hasFontFaceOnlyValues = true; 6279 addProperty(CSSPropertyFontVariant, values.release(), important); 6280 return true; 6281 } 6282 6283 return false; 6284} 6285 6286static CSSValueID createFontWeightValueKeyword(int weight) 6287{ 6288 ASSERT(!(weight % 100) && weight >= 100 && weight <= 900); 6289 CSSValueID value = static_cast<CSSValueID>(CSSValue100 + weight / 100 - 1); 6290 ASSERT(value >= CSSValue100 && value <= CSSValue900); 6291 return value; 6292} 6293 6294bool CSSParser::parseFontWeight(bool important) 6295{ 6296 CSSParserValue* value = m_valueList->current(); 6297 if ((value->id >= CSSValueNormal) && (value->id <= CSSValue900)) { 6298 addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(value->id), important); 6299 return true; 6300 } 6301 if (validUnit(value, FInteger | FNonNeg, CSSQuirksMode)) { 6302 int weight = static_cast<int>(value->fValue); 6303 if (!(weight % 100) && weight >= 100 && weight <= 900) { 6304 addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(createFontWeightValueKeyword(weight)), important); 6305 return true; 6306 } 6307 } 6308 return false; 6309} 6310 6311bool CSSParser::parseFontFaceSrcURI(CSSValueList* valueList) 6312{ 6313 RefPtr<CSSFontFaceSrcValue> uriValue(CSSFontFaceSrcValue::create(completeURL(m_valueList->current()->string))); 6314 6315 CSSParserValue* value = m_valueList->next(); 6316 if (!value) { 6317 valueList->append(uriValue.releaseNonNull()); 6318 return true; 6319 } 6320 if (value->unit == CSSParserValue::Operator && value->iValue == ',') { 6321 m_valueList->next(); 6322 valueList->append(uriValue.releaseNonNull()); 6323 return true; 6324 } 6325 6326 if (value->unit != CSSParserValue::Function || !equalIgnoringCase(value->function->name, "format(")) 6327 return false; 6328 6329 // FIXME: http://www.w3.org/TR/2011/WD-css3-fonts-20111004/ says that format() contains a comma-separated list of strings, 6330 // but CSSFontFaceSrcValue stores only one format. Allowing one format for now. 6331 CSSParserValueList* args = value->function->args.get(); 6332 if (!args || args->size() != 1 || (args->current()->unit != CSSPrimitiveValue::CSS_STRING && args->current()->unit != CSSPrimitiveValue::CSS_IDENT)) 6333 return false; 6334 uriValue->setFormat(args->current()->string); 6335 valueList->append(uriValue.releaseNonNull()); 6336 value = m_valueList->next(); 6337 if (value && value->unit == CSSParserValue::Operator && value->iValue == ',') 6338 m_valueList->next(); 6339 return true; 6340} 6341 6342bool CSSParser::parseFontFaceSrcLocal(CSSValueList* valueList) 6343{ 6344 CSSParserValueList* args = m_valueList->current()->function->args.get(); 6345 if (!args || !args->size()) 6346 return false; 6347 6348 if (args->size() == 1 && args->current()->unit == CSSPrimitiveValue::CSS_STRING) 6349 valueList->append(CSSFontFaceSrcValue::createLocal(args->current()->string)); 6350 else if (args->current()->unit == CSSPrimitiveValue::CSS_IDENT) { 6351 StringBuilder builder; 6352 for (CSSParserValue* localValue = args->current(); localValue; localValue = args->next()) { 6353 if (localValue->unit != CSSPrimitiveValue::CSS_IDENT) 6354 return false; 6355 if (!builder.isEmpty()) 6356 builder.append(' '); 6357 builder.append(localValue->string); 6358 } 6359 valueList->append(CSSFontFaceSrcValue::createLocal(builder.toString())); 6360 } else 6361 return false; 6362 6363 if (CSSParserValue* value = m_valueList->next()) { 6364 if (value->unit == CSSParserValue::Operator && value->iValue == ',') 6365 m_valueList->next(); 6366 } 6367 return true; 6368} 6369 6370bool CSSParser::parseFontFaceSrc() 6371{ 6372 RefPtr<CSSValueList> values(CSSValueList::createCommaSeparated()); 6373 6374 while (CSSParserValue* value = m_valueList->current()) { 6375 if (value->unit == CSSPrimitiveValue::CSS_URI) { 6376 if (!parseFontFaceSrcURI(values.get())) 6377 return false; 6378 } else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "local(")) { 6379 if (!parseFontFaceSrcLocal(values.get())) 6380 return false; 6381 } else 6382 return false; 6383 } 6384 if (!values->length()) 6385 return false; 6386 6387 addProperty(CSSPropertySrc, values.release(), m_important); 6388 m_valueList->next(); 6389 return true; 6390} 6391 6392bool CSSParser::parseFontFaceUnicodeRange() 6393{ 6394 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); 6395 bool failed = false; 6396 bool operatorExpected = false; 6397 for (; m_valueList->current(); m_valueList->next(), operatorExpected = !operatorExpected) { 6398 if (operatorExpected) { 6399 if (m_valueList->current()->unit == CSSParserValue::Operator && m_valueList->current()->iValue == ',') 6400 continue; 6401 failed = true; 6402 break; 6403 } 6404 if (m_valueList->current()->unit != CSSPrimitiveValue::CSS_UNICODE_RANGE) { 6405 failed = true; 6406 break; 6407 } 6408 6409 String rangeString = m_valueList->current()->string; 6410 UChar32 from = 0; 6411 UChar32 to = 0; 6412 unsigned length = rangeString.length(); 6413 6414 if (length < 3) { 6415 failed = true; 6416 break; 6417 } 6418 6419 unsigned i = 2; 6420 while (i < length) { 6421 UChar c = rangeString[i]; 6422 if (c == '-' || c == '?') 6423 break; 6424 from *= 16; 6425 if (c >= '0' && c <= '9') 6426 from += c - '0'; 6427 else if (c >= 'A' && c <= 'F') 6428 from += 10 + c - 'A'; 6429 else if (c >= 'a' && c <= 'f') 6430 from += 10 + c - 'a'; 6431 else { 6432 failed = true; 6433 break; 6434 } 6435 i++; 6436 } 6437 if (failed) 6438 break; 6439 6440 if (i == length) 6441 to = from; 6442 else if (rangeString[i] == '?') { 6443 unsigned span = 1; 6444 while (i < length && rangeString[i] == '?') { 6445 span *= 16; 6446 from *= 16; 6447 i++; 6448 } 6449 if (i < length) 6450 failed = true; 6451 to = from + span - 1; 6452 } else { 6453 if (length < i + 2) { 6454 failed = true; 6455 break; 6456 } 6457 i++; 6458 while (i < length) { 6459 UChar c = rangeString[i]; 6460 to *= 16; 6461 if (c >= '0' && c <= '9') 6462 to += c - '0'; 6463 else if (c >= 'A' && c <= 'F') 6464 to += 10 + c - 'A'; 6465 else if (c >= 'a' && c <= 'f') 6466 to += 10 + c - 'a'; 6467 else { 6468 failed = true; 6469 break; 6470 } 6471 i++; 6472 } 6473 if (failed) 6474 break; 6475 } 6476 if (from <= to) 6477 values->append(CSSUnicodeRangeValue::create(from, to)); 6478 } 6479 if (failed || !values->length()) 6480 return false; 6481 addProperty(CSSPropertyUnicodeRange, values.release(), m_important); 6482 return true; 6483} 6484 6485// Returns the number of characters which form a valid double 6486// and are terminated by the given terminator character 6487template <typename CharacterType> 6488static int checkForValidDouble(const CharacterType* string, const CharacterType* end, const char terminator) 6489{ 6490 int length = end - string; 6491 if (length < 1) 6492 return 0; 6493 6494 bool decimalMarkSeen = false; 6495 int processedLength = 0; 6496 6497 for (int i = 0; i < length; ++i) { 6498 if (string[i] == terminator) { 6499 processedLength = i; 6500 break; 6501 } 6502 if (!isASCIIDigit(string[i])) { 6503 if (!decimalMarkSeen && string[i] == '.') 6504 decimalMarkSeen = true; 6505 else 6506 return 0; 6507 } 6508 } 6509 6510 if (decimalMarkSeen && processedLength == 1) 6511 return 0; 6512 6513 return processedLength; 6514} 6515 6516// Returns the number of characters consumed for parsing a valid double 6517// terminated by the given terminator character 6518template <typename CharacterType> 6519static int parseDouble(const CharacterType* string, const CharacterType* end, const char terminator, double& value) 6520{ 6521 int length = checkForValidDouble(string, end, terminator); 6522 if (!length) 6523 return 0; 6524 6525 int position = 0; 6526 double localValue = 0; 6527 6528 // The consumed characters here are guaranteed to be 6529 // ASCII digits with or without a decimal mark 6530 for (; position < length; ++position) { 6531 if (string[position] == '.') 6532 break; 6533 localValue = localValue * 10 + string[position] - '0'; 6534 } 6535 6536 if (++position == length) { 6537 value = localValue; 6538 return length; 6539 } 6540 6541 double fraction = 0; 6542 double scale = 1; 6543 6544 while (position < length && scale < MAX_SCALE) { 6545 fraction = fraction * 10 + string[position++] - '0'; 6546 scale *= 10; 6547 } 6548 6549 value = localValue + fraction / scale; 6550 return length; 6551} 6552 6553template <typename CharacterType> 6554static bool parseColorIntOrPercentage(const CharacterType*& string, const CharacterType* end, const char terminator, CSSPrimitiveValue::UnitTypes& expect, int& value) 6555{ 6556 const CharacterType* current = string; 6557 double localValue = 0; 6558 bool negative = false; 6559 while (current != end && isHTMLSpace(*current)) 6560 current++; 6561 if (current != end && *current == '-') { 6562 negative = true; 6563 current++; 6564 } 6565 if (current == end || !isASCIIDigit(*current)) 6566 return false; 6567 while (current != end && isASCIIDigit(*current)) { 6568 double newValue = localValue * 10 + *current++ - '0'; 6569 if (newValue >= 255) { 6570 // Clamp values at 255. 6571 localValue = 255; 6572 while (current != end && isASCIIDigit(*current)) 6573 ++current; 6574 break; 6575 } 6576 localValue = newValue; 6577 } 6578 6579 if (current == end) 6580 return false; 6581 6582 if (expect == CSSPrimitiveValue::CSS_NUMBER && (*current == '.' || *current == '%')) 6583 return false; 6584 6585 if (*current == '.') { 6586 // We already parsed the integral part, try to parse 6587 // the fraction part of the percentage value. 6588 double percentage = 0; 6589 int numCharactersParsed = parseDouble(current, end, '%', percentage); 6590 if (!numCharactersParsed) 6591 return false; 6592 current += numCharactersParsed; 6593 if (*current != '%') 6594 return false; 6595 localValue += percentage; 6596 } 6597 6598 if (expect == CSSPrimitiveValue::CSS_PERCENTAGE && *current != '%') 6599 return false; 6600 6601 if (*current == '%') { 6602 expect = CSSPrimitiveValue::CSS_PERCENTAGE; 6603 localValue = localValue / 100.0 * 256.0; 6604 // Clamp values at 255 for percentages over 100% 6605 if (localValue > 255) 6606 localValue = 255; 6607 current++; 6608 } else 6609 expect = CSSPrimitiveValue::CSS_NUMBER; 6610 6611 while (current != end && isHTMLSpace(*current)) 6612 current++; 6613 if (current == end || *current++ != terminator) 6614 return false; 6615 // Clamp negative values at zero. 6616 value = negative ? 0 : static_cast<int>(localValue); 6617 string = current; 6618 return true; 6619} 6620 6621template <typename CharacterType> 6622static inline bool isTenthAlpha(const CharacterType* string, const int length) 6623{ 6624 // "0.X" 6625 if (length == 3 && string[0] == '0' && string[1] == '.' && isASCIIDigit(string[2])) 6626 return true; 6627 6628 // ".X" 6629 if (length == 2 && string[0] == '.' && isASCIIDigit(string[1])) 6630 return true; 6631 6632 return false; 6633} 6634 6635template <typename CharacterType> 6636static inline bool parseAlphaValue(const CharacterType*& string, const CharacterType* end, const char terminator, int& value) 6637{ 6638 while (string != end && isHTMLSpace(*string)) 6639 string++; 6640 6641 bool negative = false; 6642 6643 if (string != end && *string == '-') { 6644 negative = true; 6645 string++; 6646 } 6647 6648 value = 0; 6649 6650 int length = end - string; 6651 if (length < 2) 6652 return false; 6653 6654 if (string[length - 1] != terminator || !isASCIIDigit(string[length - 2])) 6655 return false; 6656 6657 if (string[0] != '0' && string[0] != '1' && string[0] != '.') { 6658 if (checkForValidDouble(string, end, terminator)) { 6659 value = negative ? 0 : 255; 6660 string = end; 6661 return true; 6662 } 6663 return false; 6664 } 6665 6666 if (length == 2 && string[0] != '.') { 6667 value = !negative && string[0] == '1' ? 255 : 0; 6668 string = end; 6669 return true; 6670 } 6671 6672 if (isTenthAlpha(string, length - 1)) { 6673 static const int tenthAlphaValues[] = { 0, 25, 51, 76, 102, 127, 153, 179, 204, 230 }; 6674 value = negative ? 0 : tenthAlphaValues[string[length - 2] - '0']; 6675 string = end; 6676 return true; 6677 } 6678 6679 double alpha = 0; 6680 if (!parseDouble(string, end, terminator, alpha)) 6681 return false; 6682 value = negative ? 0 : static_cast<int>(alpha * nextafter(256.0, 0.0)); 6683 string = end; 6684 return true; 6685} 6686 6687template <typename CharacterType> 6688static inline bool mightBeRGBA(const CharacterType* characters, unsigned length) 6689{ 6690 if (length < 5) 6691 return false; 6692 return characters[4] == '(' 6693 && isASCIIAlphaCaselessEqual(characters[0], 'r') 6694 && isASCIIAlphaCaselessEqual(characters[1], 'g') 6695 && isASCIIAlphaCaselessEqual(characters[2], 'b') 6696 && isASCIIAlphaCaselessEqual(characters[3], 'a'); 6697} 6698 6699template <typename CharacterType> 6700static inline bool mightBeRGB(const CharacterType* characters, unsigned length) 6701{ 6702 if (length < 4) 6703 return false; 6704 return characters[3] == '(' 6705 && isASCIIAlphaCaselessEqual(characters[0], 'r') 6706 && isASCIIAlphaCaselessEqual(characters[1], 'g') 6707 && isASCIIAlphaCaselessEqual(characters[2], 'b'); 6708} 6709 6710template <typename CharacterType> 6711static inline bool fastParseColorInternal(RGBA32& rgb, const CharacterType* characters, unsigned length , bool strict) 6712{ 6713 CSSPrimitiveValue::UnitTypes expect = CSSPrimitiveValue::CSS_UNKNOWN; 6714 6715 if (!strict && length >= 3) { 6716 if (characters[0] == '#') { 6717 if (Color::parseHexColor(characters + 1, length - 1, rgb)) 6718 return true; 6719 } else { 6720 if (Color::parseHexColor(characters, length, rgb)) 6721 return true; 6722 } 6723 } 6724 6725 // Try rgba() syntax. 6726 if (mightBeRGBA(characters, length)) { 6727 const CharacterType* current = characters + 5; 6728 const CharacterType* end = characters + length; 6729 int red; 6730 int green; 6731 int blue; 6732 int alpha; 6733 6734 if (!parseColorIntOrPercentage(current, end, ',', expect, red)) 6735 return false; 6736 if (!parseColorIntOrPercentage(current, end, ',', expect, green)) 6737 return false; 6738 if (!parseColorIntOrPercentage(current, end, ',', expect, blue)) 6739 return false; 6740 if (!parseAlphaValue(current, end, ')', alpha)) 6741 return false; 6742 if (current != end) 6743 return false; 6744 rgb = makeRGBA(red, green, blue, alpha); 6745 return true; 6746 } 6747 6748 // Try rgb() syntax. 6749 if (mightBeRGB(characters, length)) { 6750 const CharacterType* current = characters + 4; 6751 const CharacterType* end = characters + length; 6752 int red; 6753 int green; 6754 int blue; 6755 if (!parseColorIntOrPercentage(current, end, ',', expect, red)) 6756 return false; 6757 if (!parseColorIntOrPercentage(current, end, ',', expect, green)) 6758 return false; 6759 if (!parseColorIntOrPercentage(current, end, ')', expect, blue)) 6760 return false; 6761 if (current != end) 6762 return false; 6763 rgb = makeRGB(red, green, blue); 6764 return true; 6765 } 6766 6767 return false; 6768} 6769 6770template<typename StringType> 6771bool CSSParser::fastParseColor(RGBA32& rgb, const StringType& name, bool strict) 6772{ 6773 unsigned length = name.length(); 6774 bool parseResult; 6775 6776 if (!length) 6777 return false; 6778 6779 if (name.is8Bit()) 6780 parseResult = fastParseColorInternal(rgb, name.characters8(), length, strict); 6781 else 6782 parseResult = fastParseColorInternal(rgb, name.characters16(), length, strict); 6783 6784 if (parseResult) 6785 return true; 6786 6787 // Try named colors. 6788 Color tc; 6789 tc.setNamedColor(name); 6790 if (tc.isValid()) { 6791 rgb = tc.rgb(); 6792 return true; 6793 } 6794 return false; 6795} 6796 6797inline double CSSParser::parsedDouble(CSSParserValue *v, ReleaseParsedCalcValueCondition releaseCalc) 6798{ 6799 const double result = m_parsedCalculation ? m_parsedCalculation->doubleValue() : v->fValue; 6800 if (releaseCalc == ReleaseParsedCalcValue) 6801 m_parsedCalculation.release(); 6802 return result; 6803} 6804 6805bool CSSParser::isCalculation(CSSParserValue* value) 6806{ 6807 return (value->unit == CSSParserValue::Function) 6808 && (equalIgnoringCase(value->function->name, "calc(") 6809 || equalIgnoringCase(value->function->name, "-webkit-calc(") 6810 || equalIgnoringCase(value->function->name, "-webkit-min(") 6811 || equalIgnoringCase(value->function->name, "-webkit-max(")); 6812} 6813 6814inline int CSSParser::colorIntFromValue(CSSParserValue* v) 6815{ 6816 bool isPercent; 6817 6818 if (m_parsedCalculation) 6819 isPercent = m_parsedCalculation->category() == CalcPercent; 6820 else 6821 isPercent = v->unit == CSSPrimitiveValue::CSS_PERCENTAGE; 6822 6823 const double value = parsedDouble(v, ReleaseParsedCalcValue); 6824 6825 if (value <= 0.0) 6826 return 0; 6827 6828 if (isPercent) { 6829 if (value >= 100.0) 6830 return 255; 6831 return static_cast<int>(value * 256.0 / 100.0); 6832 } 6833 6834 if (value >= 255.0) 6835 return 255; 6836 6837 return static_cast<int>(value); 6838} 6839 6840bool CSSParser::parseColorParameters(CSSParserValue* value, int* colorArray, bool parseAlpha) 6841{ 6842 CSSParserValueList* args = value->function->args.get(); 6843 CSSParserValue* v = args->current(); 6844 Units unitType = FUnknown; 6845 // Get the first value and its type 6846 if (validUnit(v, FInteger, CSSStrictMode)) 6847 unitType = FInteger; 6848 else if (validUnit(v, FPercent, CSSStrictMode)) 6849 unitType = FPercent; 6850 else 6851 return false; 6852 6853 colorArray[0] = colorIntFromValue(v); 6854 for (int i = 1; i < 3; i++) { 6855 v = args->next(); 6856 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6857 return false; 6858 v = args->next(); 6859 if (!validUnit(v, unitType, CSSStrictMode)) 6860 return false; 6861 colorArray[i] = colorIntFromValue(v); 6862 } 6863 if (parseAlpha) { 6864 v = args->next(); 6865 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6866 return false; 6867 v = args->next(); 6868 if (!validUnit(v, FNumber, CSSStrictMode)) 6869 return false; 6870 const double value = parsedDouble(v, ReleaseParsedCalcValue); 6871 // Convert the floating pointer number of alpha to an integer in the range [0, 256), 6872 // with an equal distribution across all 256 values. 6873 colorArray[3] = static_cast<int>(std::max<double>(0, std::min<double>(1, value)) * nextafter(256.0, 0.0)); 6874 } 6875 return true; 6876} 6877 6878// The CSS3 specification defines the format of a HSL color as 6879// hsl(<number>, <percent>, <percent>) 6880// and with alpha, the format is 6881// hsla(<number>, <percent>, <percent>, <number>) 6882// The first value, HUE, is in an angle with a value between 0 and 360 6883bool CSSParser::parseHSLParameters(CSSParserValue* value, double* colorArray, bool parseAlpha) 6884{ 6885 CSSParserValueList* args = value->function->args.get(); 6886 CSSParserValue* v = args->current(); 6887 // Get the first value 6888 if (!validUnit(v, FNumber, CSSStrictMode)) 6889 return false; 6890 // normalize the Hue value and change it to be between 0 and 1.0 6891 colorArray[0] = (((static_cast<int>(parsedDouble(v, ReleaseParsedCalcValue)) % 360) + 360) % 360) / 360.0; 6892 for (int i = 1; i < 3; i++) { 6893 v = args->next(); 6894 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6895 return false; 6896 v = args->next(); 6897 if (!validUnit(v, FPercent, CSSStrictMode)) 6898 return false; 6899 colorArray[i] = std::max<double>(0, std::min<double>(100, parsedDouble(v, ReleaseParsedCalcValue))) / 100.0; // needs to be value between 0 and 1.0 6900 } 6901 if (parseAlpha) { 6902 v = args->next(); 6903 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6904 return false; 6905 v = args->next(); 6906 if (!validUnit(v, FNumber, CSSStrictMode)) 6907 return false; 6908 colorArray[3] = std::max<double>(0, std::min<double>(1, parsedDouble(v, ReleaseParsedCalcValue))); 6909 } 6910 return true; 6911} 6912 6913PassRefPtr<CSSPrimitiveValue> CSSParser::parseColor(CSSParserValue* value) 6914{ 6915 RGBA32 c = Color::transparent; 6916 if (!parseColorFromValue(value ? value : m_valueList->current(), c)) 6917 return 0; 6918 return cssValuePool().createColorValue(c); 6919} 6920 6921bool CSSParser::parseColorFromValue(CSSParserValue* value, RGBA32& c) 6922{ 6923 if (inQuirksMode() && value->unit == CSSPrimitiveValue::CSS_NUMBER 6924 && value->fValue >= 0. && value->fValue < 1000000.) { 6925 String str = String::format("%06d", static_cast<int>((value->fValue+.5))); 6926 // FIXME: This should be strict parsing for SVG as well. 6927 if (!fastParseColor(c, str, inStrictMode())) 6928 return false; 6929 } else if (value->unit == CSSPrimitiveValue::CSS_PARSER_HEXCOLOR || 6930 value->unit == CSSPrimitiveValue::CSS_IDENT || 6931 (inQuirksMode() && value->unit == CSSPrimitiveValue::CSS_DIMENSION)) { 6932 if (!fastParseColor(c, value->string, inStrictMode() && value->unit == CSSPrimitiveValue::CSS_IDENT)) 6933 return false; 6934 } else if (value->unit == CSSParserValue::Function && 6935 value->function->args != 0 && 6936 value->function->args->size() == 5 /* rgb + two commas */ && 6937 equalIgnoringCase(value->function->name, "rgb(")) { 6938 int colorValues[3]; 6939 if (!parseColorParameters(value, colorValues, false)) 6940 return false; 6941 c = makeRGB(colorValues[0], colorValues[1], colorValues[2]); 6942 } else { 6943 if (value->unit == CSSParserValue::Function && 6944 value->function->args != 0 && 6945 value->function->args->size() == 7 /* rgba + three commas */ && 6946 equalIgnoringCase(value->function->name, "rgba(")) { 6947 int colorValues[4]; 6948 if (!parseColorParameters(value, colorValues, true)) 6949 return false; 6950 c = makeRGBA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); 6951 } else if (value->unit == CSSParserValue::Function && 6952 value->function->args != 0 && 6953 value->function->args->size() == 5 /* hsl + two commas */ && 6954 equalIgnoringCase(value->function->name, "hsl(")) { 6955 double colorValues[3]; 6956 if (!parseHSLParameters(value, colorValues, false)) 6957 return false; 6958 c = makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], 1.0); 6959 } else if (value->unit == CSSParserValue::Function && 6960 value->function->args != 0 && 6961 value->function->args->size() == 7 /* hsla + three commas */ && 6962 equalIgnoringCase(value->function->name, "hsla(")) { 6963 double colorValues[4]; 6964 if (!parseHSLParameters(value, colorValues, true)) 6965 return false; 6966 c = makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); 6967 } else 6968 return false; 6969 } 6970 6971 return true; 6972} 6973 6974// This class tracks parsing state for shadow values. If it goes out of scope (e.g., due to an early return) 6975// without the allowBreak bit being set, then it will clean up all of the objects and destroy them. 6976struct ShadowParseContext { 6977 ShadowParseContext(CSSPropertyID prop, CSSParser* parser) 6978 : property(prop) 6979 , m_parser(parser) 6980 , allowX(true) 6981 , allowY(false) 6982 , allowBlur(false) 6983 , allowSpread(false) 6984 , allowColor(true) 6985 , allowStyle(prop == CSSPropertyWebkitBoxShadow || prop == CSSPropertyBoxShadow) 6986 , allowBreak(true) 6987 { 6988 } 6989 6990 bool allowLength() { return allowX || allowY || allowBlur || allowSpread; } 6991 6992 void commitValue() 6993 { 6994 // Handle the ,, case gracefully by doing nothing. 6995 if (x || y || blur || spread || color || style) { 6996 if (!values) 6997 values = CSSValueList::createCommaSeparated(); 6998 6999 // Construct the current shadow value and add it to the list. 7000 values->append(CSSShadowValue::create(x.release(), y.release(), blur.release(), spread.release(), style.release(), color.release())); 7001 } 7002 7003 // Now reset for the next shadow value. 7004 x = 0; 7005 y = 0; 7006 blur = 0; 7007 spread = 0; 7008 style = 0; 7009 color = 0; 7010 7011 allowX = true; 7012 allowColor = true; 7013 allowBreak = true; 7014 allowY = false; 7015 allowBlur = false; 7016 allowSpread = false; 7017 allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 7018 } 7019 7020 void commitLength(CSSParserValue* v) 7021 { 7022 RefPtr<CSSPrimitiveValue> val = m_parser->createPrimitiveNumericValue(v); 7023 7024 if (allowX) { 7025 x = val.release(); 7026 allowX = false; 7027 allowY = true; 7028 allowColor = false; 7029 allowStyle = false; 7030 allowBreak = false; 7031 } else if (allowY) { 7032 y = val.release(); 7033 allowY = false; 7034 allowBlur = true; 7035 allowColor = true; 7036 allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 7037 allowBreak = true; 7038 } else if (allowBlur) { 7039 blur = val.release(); 7040 allowBlur = false; 7041 allowSpread = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 7042 } else if (allowSpread) { 7043 spread = val.release(); 7044 allowSpread = false; 7045 } 7046 } 7047 7048 void commitColor(PassRefPtr<CSSPrimitiveValue> val) 7049 { 7050 color = val; 7051 allowColor = false; 7052 if (allowX) { 7053 allowStyle = false; 7054 allowBreak = false; 7055 } else { 7056 allowBlur = false; 7057 allowSpread = false; 7058 allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 7059 } 7060 } 7061 7062 void commitStyle(CSSParserValue* v) 7063 { 7064 style = cssValuePool().createIdentifierValue(v->id); 7065 allowStyle = false; 7066 if (allowX) 7067 allowBreak = false; 7068 else { 7069 allowBlur = false; 7070 allowSpread = false; 7071 allowColor = false; 7072 } 7073 } 7074 7075 CSSPropertyID property; 7076 CSSParser* m_parser; 7077 7078 RefPtr<CSSValueList> values; 7079 RefPtr<CSSPrimitiveValue> x; 7080 RefPtr<CSSPrimitiveValue> y; 7081 RefPtr<CSSPrimitiveValue> blur; 7082 RefPtr<CSSPrimitiveValue> spread; 7083 RefPtr<CSSPrimitiveValue> style; 7084 RefPtr<CSSPrimitiveValue> color; 7085 7086 bool allowX; 7087 bool allowY; 7088 bool allowBlur; 7089 bool allowSpread; 7090 bool allowColor; 7091 bool allowStyle; // inset or not. 7092 bool allowBreak; 7093}; 7094 7095PassRefPtr<CSSValueList> CSSParser::parseShadow(CSSParserValueList* valueList, CSSPropertyID propId) 7096{ 7097 ShadowParseContext context(propId, this); 7098 CSSParserValue* val; 7099 while ((val = valueList->current())) { 7100 // Check for a comma break first. 7101 if (val->unit == CSSParserValue::Operator) { 7102 if (val->iValue != ',' || !context.allowBreak) 7103 // Other operators aren't legal or we aren't done with the current shadow 7104 // value. Treat as invalid. 7105 return 0; 7106 // -webkit-svg-shadow does not support multiple values. 7107 if (propId == CSSPropertyWebkitSvgShadow) 7108 return 0; 7109 // The value is good. Commit it. 7110 context.commitValue(); 7111 } else if (validUnit(val, FLength, CSSStrictMode)) { 7112 // We required a length and didn't get one. Invalid. 7113 if (!context.allowLength()) 7114 return 0; 7115 7116 // Blur radius must be non-negative. 7117 if (context.allowBlur && !validUnit(val, FLength | FNonNeg, CSSStrictMode)) 7118 return 0; 7119 7120 // A length is allowed here. Construct the value and add it. 7121 context.commitLength(val); 7122 } else if (val->id == CSSValueInset) { 7123 if (!context.allowStyle) 7124 return 0; 7125 7126 context.commitStyle(val); 7127 } else { 7128 // The only other type of value that's ok is a color value. 7129 RefPtr<CSSPrimitiveValue> parsedColor; 7130 bool isColor = ((val->id >= CSSValueAqua && val->id <= CSSValueWindowtext) || val->id == CSSValueMenu 7131 || (val->id >= CSSValueWebkitFocusRingColor && val->id <= CSSValueWebkitText && inQuirksMode()) 7132 || val->id == CSSValueCurrentcolor); 7133 if (isColor) { 7134 if (!context.allowColor) 7135 return 0; 7136 parsedColor = cssValuePool().createIdentifierValue(val->id); 7137 } 7138 7139 if (!parsedColor) 7140 // It's not built-in. Try to parse it as a color. 7141 parsedColor = parseColor(val); 7142 7143 if (!parsedColor || !context.allowColor) 7144 return 0; // This value is not a color or length and is invalid or 7145 // it is a color, but a color isn't allowed at this point. 7146 7147 context.commitColor(parsedColor.release()); 7148 } 7149 7150 valueList->next(); 7151 } 7152 7153 if (context.allowBreak) { 7154 context.commitValue(); 7155 if (context.values && context.values->length()) 7156 return context.values.release(); 7157 } 7158 7159 return 0; 7160} 7161 7162bool CSSParser::parseReflect(CSSPropertyID propId, bool important) 7163{ 7164 // box-reflect: <direction> <offset> <mask> 7165 7166 // Direction comes first. 7167 CSSParserValue* val = m_valueList->current(); 7168 RefPtr<CSSPrimitiveValue> direction; 7169 switch (val->id) { 7170 case CSSValueAbove: 7171 case CSSValueBelow: 7172 case CSSValueLeft: 7173 case CSSValueRight: 7174 direction = cssValuePool().createIdentifierValue(val->id); 7175 break; 7176 default: 7177 return false; 7178 } 7179 7180 // The offset comes next. 7181 val = m_valueList->next(); 7182 RefPtr<CSSPrimitiveValue> offset; 7183 if (!val) 7184 offset = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX); 7185 else { 7186 if (!validUnit(val, FLength | FPercent)) 7187 return false; 7188 offset = createPrimitiveNumericValue(val); 7189 } 7190 7191 // Now for the mask. 7192 RefPtr<CSSValue> mask; 7193 val = m_valueList->next(); 7194 if (val) { 7195 if (!parseBorderImage(propId, mask)) 7196 return false; 7197 } 7198 7199 addProperty(propId, CSSReflectValue::create(direction.release(), offset.release(), mask.release()), important); 7200 m_valueList->next(); 7201 return true; 7202} 7203 7204bool CSSParser::parseFlex(CSSParserValueList* args, bool important) 7205{ 7206 if (!args || !args->size() || args->size() > 3) 7207 return false; 7208 static const double unsetValue = -1; 7209 double flexGrow = unsetValue; 7210 double flexShrink = unsetValue; 7211 RefPtr<CSSPrimitiveValue> flexBasis; 7212 7213 while (CSSParserValue* arg = args->current()) { 7214 if (validUnit(arg, FNumber | FNonNeg)) { 7215 if (flexGrow == unsetValue) 7216 flexGrow = arg->fValue; 7217 else if (flexShrink == unsetValue) 7218 flexShrink = arg->fValue; 7219 else if (!arg->fValue) { 7220 // flex only allows a basis of 0 (sans units) if flex-grow and flex-shrink values have already been set. 7221 flexBasis = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX); 7222 } else { 7223 // We only allow 3 numbers without units if the last value is 0. E.g., flex:1 1 1 is invalid. 7224 return false; 7225 } 7226 } else if (!flexBasis && (arg->id == CSSValueAuto || validUnit(arg, FLength | FPercent | FNonNeg))) 7227 flexBasis = parseValidPrimitive(arg->id, arg); 7228 else { 7229 // Not a valid arg for flex. 7230 return false; 7231 } 7232 args->next(); 7233 } 7234 7235 if (flexGrow == unsetValue) 7236 flexGrow = 1; 7237 if (flexShrink == unsetValue) 7238 flexShrink = 1; 7239 if (!flexBasis) 7240 flexBasis = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX); 7241 7242 addProperty(CSSPropertyWebkitFlexGrow, cssValuePool().createValue(clampToFloat(flexGrow), CSSPrimitiveValue::CSS_NUMBER), important); 7243 addProperty(CSSPropertyWebkitFlexShrink, cssValuePool().createValue(clampToFloat(flexShrink), CSSPrimitiveValue::CSS_NUMBER), important); 7244 addProperty(CSSPropertyWebkitFlexBasis, flexBasis, important); 7245 return true; 7246} 7247 7248struct BorderImageParseContext { 7249 BorderImageParseContext() 7250 : m_canAdvance(false) 7251 , m_allowCommit(true) 7252 , m_allowImage(true) 7253 , m_allowImageSlice(true) 7254 , m_allowRepeat(true) 7255 , m_allowForwardSlashOperator(false) 7256 , m_requireWidth(false) 7257 , m_requireOutset(false) 7258 {} 7259 7260 bool canAdvance() const { return m_canAdvance; } 7261 void setCanAdvance(bool canAdvance) { m_canAdvance = canAdvance; } 7262 7263 bool allowCommit() const { return m_allowCommit; } 7264 bool allowImage() const { return m_allowImage; } 7265 bool allowImageSlice() const { return m_allowImageSlice; } 7266 bool allowRepeat() const { return m_allowRepeat; } 7267 bool allowForwardSlashOperator() const { return m_allowForwardSlashOperator; } 7268 7269 bool requireWidth() const { return m_requireWidth; } 7270 bool requireOutset() const { return m_requireOutset; } 7271 7272 void commitImage(PassRefPtr<CSSValue> image) 7273 { 7274 m_image = image; 7275 m_canAdvance = true; 7276 m_allowCommit = true; 7277 m_allowImage = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false; 7278 m_allowImageSlice = !m_imageSlice; 7279 m_allowRepeat = !m_repeat; 7280 } 7281 void commitImageSlice(PassRefPtr<CSSBorderImageSliceValue> slice) 7282 { 7283 m_imageSlice = slice; 7284 m_canAdvance = true; 7285 m_allowCommit = m_allowForwardSlashOperator = true; 7286 m_allowImageSlice = m_requireWidth = m_requireOutset = false; 7287 m_allowImage = !m_image; 7288 m_allowRepeat = !m_repeat; 7289 } 7290 void commitForwardSlashOperator() 7291 { 7292 m_canAdvance = true; 7293 m_allowCommit = m_allowImage = m_allowImageSlice = m_allowRepeat = m_allowForwardSlashOperator = false; 7294 if (!m_borderSlice) { 7295 m_requireWidth = true; 7296 m_requireOutset = false; 7297 } else { 7298 m_requireOutset = true; 7299 m_requireWidth = false; 7300 } 7301 } 7302 void commitBorderWidth(PassRefPtr<CSSPrimitiveValue> slice) 7303 { 7304 m_borderSlice = slice; 7305 m_canAdvance = true; 7306 m_allowCommit = m_allowForwardSlashOperator = true; 7307 m_allowImageSlice = m_requireWidth = m_requireOutset = false; 7308 m_allowImage = !m_image; 7309 m_allowRepeat = !m_repeat; 7310 } 7311 void commitBorderOutset(PassRefPtr<CSSPrimitiveValue> outset) 7312 { 7313 m_outset = outset; 7314 m_canAdvance = true; 7315 m_allowCommit = true; 7316 m_allowImageSlice = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false; 7317 m_allowImage = !m_image; 7318 m_allowRepeat = !m_repeat; 7319 } 7320 void commitRepeat(PassRefPtr<CSSValue> repeat) 7321 { 7322 m_repeat = repeat; 7323 m_canAdvance = true; 7324 m_allowCommit = true; 7325 m_allowRepeat = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false; 7326 m_allowImageSlice = !m_imageSlice; 7327 m_allowImage = !m_image; 7328 } 7329 7330 PassRefPtr<CSSValue> commitWebKitBorderImage() 7331 { 7332 return createBorderImageValue(m_image, m_imageSlice, m_borderSlice, m_outset, m_repeat); 7333 } 7334 7335 void commitBorderImage(CSSParser* parser, bool important) 7336 { 7337 commitBorderImageProperty(CSSPropertyBorderImageSource, parser, m_image, important); 7338 commitBorderImageProperty(CSSPropertyBorderImageSlice, parser, m_imageSlice, important); 7339 commitBorderImageProperty(CSSPropertyBorderImageWidth, parser, m_borderSlice, important); 7340 commitBorderImageProperty(CSSPropertyBorderImageOutset, parser, m_outset, important); 7341 commitBorderImageProperty(CSSPropertyBorderImageRepeat, parser, m_repeat, important); 7342 } 7343 7344 void commitBorderImageProperty(CSSPropertyID propId, CSSParser* parser, PassRefPtr<CSSValue> value, bool important) 7345 { 7346 if (value) 7347 parser->addProperty(propId, value, important); 7348 else 7349 parser->addProperty(propId, cssValuePool().createImplicitInitialValue(), important, true); 7350 } 7351 7352 bool m_canAdvance; 7353 7354 bool m_allowCommit; 7355 bool m_allowImage; 7356 bool m_allowImageSlice; 7357 bool m_allowRepeat; 7358 bool m_allowForwardSlashOperator; 7359 7360 bool m_requireWidth; 7361 bool m_requireOutset; 7362 7363 RefPtr<CSSValue> m_image; 7364 RefPtr<CSSBorderImageSliceValue> m_imageSlice; 7365 RefPtr<CSSPrimitiveValue> m_borderSlice; 7366 RefPtr<CSSPrimitiveValue> m_outset; 7367 7368 RefPtr<CSSValue> m_repeat; 7369}; 7370 7371bool CSSParser::parseBorderImage(CSSPropertyID propId, RefPtr<CSSValue>& result, bool important) 7372{ 7373 ShorthandScope scope(this, propId); 7374 BorderImageParseContext context; 7375 while (CSSParserValue* val = m_valueList->current()) { 7376 context.setCanAdvance(false); 7377 7378 if (!context.canAdvance() && context.allowForwardSlashOperator() && isForwardSlashOperator(val)) 7379 context.commitForwardSlashOperator(); 7380 7381 if (!context.canAdvance() && context.allowImage()) { 7382 if (val->unit == CSSPrimitiveValue::CSS_URI) 7383 context.commitImage(CSSImageValue::create(completeURL(val->string))); 7384 else if (isGeneratedImageValue(val)) { 7385 RefPtr<CSSValue> value; 7386 if (parseGeneratedImage(m_valueList.get(), value)) 7387 context.commitImage(value.release()); 7388 else 7389 return false; 7390#if ENABLE(CSS_IMAGE_SET) 7391 } else if (val->unit == CSSParserValue::Function && equalIgnoringCase(val->function->name, "-webkit-image-set(")) { 7392 RefPtr<CSSValue> value = parseImageSet(); 7393 if (value) 7394 context.commitImage(value.release()); 7395 else 7396 return false; 7397#endif 7398 } else if (val->id == CSSValueNone) 7399 context.commitImage(cssValuePool().createIdentifierValue(CSSValueNone)); 7400 } 7401 7402 if (!context.canAdvance() && context.allowImageSlice()) { 7403 RefPtr<CSSBorderImageSliceValue> imageSlice; 7404 if (parseBorderImageSlice(propId, imageSlice)) 7405 context.commitImageSlice(imageSlice.release()); 7406 } 7407 7408 if (!context.canAdvance() && context.allowRepeat()) { 7409 RefPtr<CSSValue> repeat; 7410 if (parseBorderImageRepeat(repeat)) 7411 context.commitRepeat(repeat.release()); 7412 } 7413 7414 if (!context.canAdvance() && context.requireWidth()) { 7415 RefPtr<CSSPrimitiveValue> borderSlice; 7416 if (parseBorderImageWidth(borderSlice)) 7417 context.commitBorderWidth(borderSlice.release()); 7418 } 7419 7420 if (!context.canAdvance() && context.requireOutset()) { 7421 RefPtr<CSSPrimitiveValue> borderOutset; 7422 if (parseBorderImageOutset(borderOutset)) 7423 context.commitBorderOutset(borderOutset.release()); 7424 } 7425 7426 if (!context.canAdvance()) 7427 return false; 7428 7429 m_valueList->next(); 7430 } 7431 7432 if (context.allowCommit()) { 7433 if (propId == CSSPropertyBorderImage) 7434 context.commitBorderImage(this, important); 7435 else 7436 // Need to fully commit as a single value. 7437 result = context.commitWebKitBorderImage(); 7438 return true; 7439 } 7440 7441 return false; 7442} 7443 7444static bool isBorderImageRepeatKeyword(int id) 7445{ 7446 return id == CSSValueStretch || id == CSSValueRepeat || id == CSSValueSpace || id == CSSValueRound; 7447} 7448 7449bool CSSParser::parseBorderImageRepeat(RefPtr<CSSValue>& result) 7450{ 7451 RefPtr<CSSPrimitiveValue> firstValue; 7452 RefPtr<CSSPrimitiveValue> secondValue; 7453 CSSParserValue* val = m_valueList->current(); 7454 if (!val) 7455 return false; 7456 if (isBorderImageRepeatKeyword(val->id)) 7457 firstValue = cssValuePool().createIdentifierValue(val->id); 7458 else 7459 return false; 7460 7461 val = m_valueList->next(); 7462 if (val) { 7463 if (isBorderImageRepeatKeyword(val->id)) 7464 secondValue = cssValuePool().createIdentifierValue(val->id); 7465 else if (!inShorthand()) { 7466 // If we're not parsing a shorthand then we are invalid. 7467 return false; 7468 } else { 7469 // We need to rewind the value list, so that when its advanced we'll 7470 // end up back at this value. 7471 m_valueList->previous(); 7472 secondValue = firstValue; 7473 } 7474 } else 7475 secondValue = firstValue; 7476 7477 result = createPrimitiveValuePair(firstValue, secondValue); 7478 return true; 7479} 7480 7481class BorderImageSliceParseContext { 7482public: 7483 BorderImageSliceParseContext(CSSParser* parser) 7484 : m_parser(parser) 7485 , m_allowNumber(true) 7486 , m_allowFill(true) 7487 , m_allowFinalCommit(false) 7488 , m_fill(false) 7489 { } 7490 7491 bool allowNumber() const { return m_allowNumber; } 7492 bool allowFill() const { return m_allowFill; } 7493 bool allowFinalCommit() const { return m_allowFinalCommit; } 7494 CSSPrimitiveValue* top() const { return m_top.get(); } 7495 7496 void commitNumber(CSSParserValue* v) 7497 { 7498 RefPtr<CSSPrimitiveValue> val = m_parser->createPrimitiveNumericValue(v); 7499 if (!m_top) 7500 m_top = val; 7501 else if (!m_right) 7502 m_right = val; 7503 else if (!m_bottom) 7504 m_bottom = val; 7505 else { 7506 ASSERT(!m_left); 7507 m_left = val; 7508 } 7509 7510 m_allowNumber = !m_left; 7511 m_allowFinalCommit = true; 7512 } 7513 7514 void commitFill() { m_fill = true; m_allowFill = false; m_allowNumber = !m_top; } 7515 7516 PassRefPtr<CSSBorderImageSliceValue> commitBorderImageSlice() 7517 { 7518 // We need to clone and repeat values for any omissions. 7519 ASSERT(m_top); 7520 if (!m_right) { 7521 m_right = m_top; 7522 m_bottom = m_top; 7523 m_left = m_top; 7524 } 7525 if (!m_bottom) { 7526 m_bottom = m_top; 7527 m_left = m_right; 7528 } 7529 if (!m_left) 7530 m_left = m_right; 7531 7532 // Now build a rect value to hold all four of our primitive values. 7533 RefPtr<Quad> quad = Quad::create(); 7534 quad->setTop(m_top); 7535 quad->setRight(m_right); 7536 quad->setBottom(m_bottom); 7537 quad->setLeft(m_left); 7538 7539 // Make our new border image value now. 7540 return CSSBorderImageSliceValue::create(cssValuePool().createValue(quad.release()), m_fill); 7541 } 7542 7543private: 7544 CSSParser* m_parser; 7545 7546 bool m_allowNumber; 7547 bool m_allowFill; 7548 bool m_allowFinalCommit; 7549 7550 RefPtr<CSSPrimitiveValue> m_top; 7551 RefPtr<CSSPrimitiveValue> m_right; 7552 RefPtr<CSSPrimitiveValue> m_bottom; 7553 RefPtr<CSSPrimitiveValue> m_left; 7554 7555 bool m_fill; 7556}; 7557 7558bool CSSParser::parseBorderImageSlice(CSSPropertyID propId, RefPtr<CSSBorderImageSliceValue>& result) 7559{ 7560 BorderImageSliceParseContext context(this); 7561 CSSParserValue* val; 7562 while ((val = m_valueList->current())) { 7563 // FIXME calc() http://webkit.org/b/16662 : calc is parsed but values are not created yet. 7564 if (context.allowNumber() && !isCalculation(val) && validUnit(val, FInteger | FNonNeg | FPercent, CSSStrictMode)) { 7565 context.commitNumber(val); 7566 } else if (context.allowFill() && val->id == CSSValueFill) 7567 context.commitFill(); 7568 else if (!inShorthand()) { 7569 // If we're not parsing a shorthand then we are invalid. 7570 return false; 7571 } else { 7572 if (context.allowFinalCommit()) { 7573 // We're going to successfully parse, but we don't want to consume this token. 7574 m_valueList->previous(); 7575 } 7576 break; 7577 } 7578 m_valueList->next(); 7579 } 7580 7581 if (context.allowFinalCommit()) { 7582 // FIXME: For backwards compatibility, -webkit-border-image, -webkit-mask-box-image and -webkit-box-reflect have to do a fill by default. 7583 // FIXME: What do we do with -webkit-box-reflect and -webkit-mask-box-image? Probably just have to leave them filling... 7584 if (propId == CSSPropertyWebkitBorderImage || propId == CSSPropertyWebkitMaskBoxImage || propId == CSSPropertyWebkitBoxReflect) 7585 context.commitFill(); 7586 7587 // Need to fully commit as a single value. 7588 result = context.commitBorderImageSlice(); 7589 return true; 7590 } 7591 7592 return false; 7593} 7594 7595class BorderImageQuadParseContext { 7596public: 7597 BorderImageQuadParseContext(CSSParser* parser) 7598 : m_parser(parser) 7599 , m_allowNumber(true) 7600 , m_allowFinalCommit(false) 7601 { } 7602 7603 bool allowNumber() const { return m_allowNumber; } 7604 bool allowFinalCommit() const { return m_allowFinalCommit; } 7605 CSSPrimitiveValue* top() const { return m_top.get(); } 7606 7607 void commitNumber(CSSParserValue* v) 7608 { 7609 RefPtr<CSSPrimitiveValue> val; 7610 if (v->id == CSSValueAuto) 7611 val = cssValuePool().createIdentifierValue(v->id); 7612 else 7613 val = m_parser->createPrimitiveNumericValue(v); 7614 7615 if (!m_top) 7616 m_top = val; 7617 else if (!m_right) 7618 m_right = val; 7619 else if (!m_bottom) 7620 m_bottom = val; 7621 else { 7622 ASSERT(!m_left); 7623 m_left = val; 7624 } 7625 7626 m_allowNumber = !m_left; 7627 m_allowFinalCommit = true; 7628 } 7629 7630 void setAllowFinalCommit() { m_allowFinalCommit = true; } 7631 void setTop(PassRefPtr<CSSPrimitiveValue> val) { m_top = val; } 7632 7633 PassRefPtr<CSSPrimitiveValue> commitBorderImageQuad() 7634 { 7635 // We need to clone and repeat values for any omissions. 7636 ASSERT(m_top); 7637 if (!m_right) { 7638 m_right = m_top; 7639 m_bottom = m_top; 7640 m_left = m_top; 7641 } 7642 if (!m_bottom) { 7643 m_bottom = m_top; 7644 m_left = m_right; 7645 } 7646 if (!m_left) 7647 m_left = m_right; 7648 7649 // Now build a quad value to hold all four of our primitive values. 7650 RefPtr<Quad> quad = Quad::create(); 7651 quad->setTop(m_top); 7652 quad->setRight(m_right); 7653 quad->setBottom(m_bottom); 7654 quad->setLeft(m_left); 7655 7656 // Make our new value now. 7657 return cssValuePool().createValue(quad.release()); 7658 } 7659 7660private: 7661 CSSParser* m_parser; 7662 7663 bool m_allowNumber; 7664 bool m_allowFinalCommit; 7665 7666 RefPtr<CSSPrimitiveValue> m_top; 7667 RefPtr<CSSPrimitiveValue> m_right; 7668 RefPtr<CSSPrimitiveValue> m_bottom; 7669 RefPtr<CSSPrimitiveValue> m_left; 7670}; 7671 7672bool CSSParser::parseBorderImageQuad(Units validUnits, RefPtr<CSSPrimitiveValue>& result) 7673{ 7674 BorderImageQuadParseContext context(this); 7675 CSSParserValue* val; 7676 while ((val = m_valueList->current())) { 7677 if (context.allowNumber() && (validUnit(val, validUnits, CSSStrictMode) || val->id == CSSValueAuto)) { 7678 context.commitNumber(val); 7679 } else if (!inShorthand()) { 7680 // If we're not parsing a shorthand then we are invalid. 7681 return false; 7682 } else { 7683 if (context.allowFinalCommit()) 7684 m_valueList->previous(); // The shorthand loop will advance back to this point. 7685 break; 7686 } 7687 m_valueList->next(); 7688 } 7689 7690 if (context.allowFinalCommit()) { 7691 // Need to fully commit as a single value. 7692 result = context.commitBorderImageQuad(); 7693 return true; 7694 } 7695 return false; 7696} 7697 7698bool CSSParser::parseBorderImageWidth(RefPtr<CSSPrimitiveValue>& result) 7699{ 7700 return parseBorderImageQuad(FLength | FInteger | FNonNeg | FPercent, result); 7701} 7702 7703bool CSSParser::parseBorderImageOutset(RefPtr<CSSPrimitiveValue>& result) 7704{ 7705 return parseBorderImageQuad(FLength | FInteger | FNonNeg, result); 7706} 7707 7708bool CSSParser::parseBorderRadius(CSSPropertyID propId, bool important) 7709{ 7710 unsigned num = m_valueList->size(); 7711 if (num > 9) 7712 return false; 7713 7714 ShorthandScope scope(this, propId); 7715 RefPtr<CSSPrimitiveValue> radii[2][4]; 7716 7717 unsigned indexAfterSlash = 0; 7718 for (unsigned i = 0; i < num; ++i) { 7719 CSSParserValue* value = m_valueList->valueAt(i); 7720 if (value->unit == CSSParserValue::Operator) { 7721 if (value->iValue != '/') 7722 return false; 7723 7724 if (!i || indexAfterSlash || i + 1 == num || num > i + 5) 7725 return false; 7726 7727 indexAfterSlash = i + 1; 7728 completeBorderRadii(radii[0]); 7729 continue; 7730 } 7731 7732 if (i - indexAfterSlash >= 4) 7733 return false; 7734 7735 if (!validUnit(value, FLength | FPercent | FNonNeg)) 7736 return false; 7737 7738 RefPtr<CSSPrimitiveValue> radius = createPrimitiveNumericValue(value); 7739 7740 if (!indexAfterSlash) { 7741 radii[0][i] = radius; 7742 7743 // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2; 7744 if (num == 2 && propId == CSSPropertyWebkitBorderRadius) { 7745 indexAfterSlash = 1; 7746 completeBorderRadii(radii[0]); 7747 } 7748 } else 7749 radii[1][i - indexAfterSlash] = radius.release(); 7750 } 7751 7752 if (!indexAfterSlash) { 7753 completeBorderRadii(radii[0]); 7754 for (unsigned i = 0; i < 4; ++i) 7755 radii[1][i] = radii[0][i]; 7756 } else 7757 completeBorderRadii(radii[1]); 7758 7759 ImplicitScope implicitScope(this, PropertyImplicit); 7760 addProperty(CSSPropertyBorderTopLeftRadius, createPrimitiveValuePair(radii[0][0].release(), radii[1][0].release()), important); 7761 addProperty(CSSPropertyBorderTopRightRadius, createPrimitiveValuePair(radii[0][1].release(), radii[1][1].release()), important); 7762 addProperty(CSSPropertyBorderBottomRightRadius, createPrimitiveValuePair(radii[0][2].release(), radii[1][2].release()), important); 7763 addProperty(CSSPropertyBorderBottomLeftRadius, createPrimitiveValuePair(radii[0][3].release(), radii[1][3].release()), important); 7764 return true; 7765} 7766 7767bool CSSParser::parseAspectRatio(bool important) 7768{ 7769 unsigned num = m_valueList->size(); 7770 if (num == 1) { 7771 CSSValueID valueId = m_valueList->valueAt(0)->id; 7772 if (valueId == CSSValueAuto || valueId == CSSValueFromDimensions || valueId == CSSValueFromIntrinsic) { 7773 addProperty(CSSPropertyWebkitAspectRatio, cssValuePool().createIdentifierValue(valueId), important); 7774 return true; 7775 } 7776 } 7777 7778 if (num != 3) 7779 return false; 7780 7781 CSSParserValue* lvalue = m_valueList->valueAt(0); 7782 CSSParserValue* op = m_valueList->valueAt(1); 7783 CSSParserValue* rvalue = m_valueList->valueAt(2); 7784 7785 if (!isForwardSlashOperator(op)) 7786 return false; 7787 7788 if (!validUnit(lvalue, FNumber | FNonNeg) || !validUnit(rvalue, FNumber | FNonNeg)) 7789 return false; 7790 7791 if (!lvalue->fValue || !rvalue->fValue) 7792 return false; 7793 7794 addProperty(CSSPropertyWebkitAspectRatio, CSSAspectRatioValue::create(narrowPrecisionToFloat(lvalue->fValue), narrowPrecisionToFloat(rvalue->fValue)), important); 7795 7796 return true; 7797} 7798 7799bool CSSParser::parseCounter(CSSPropertyID propId, int defaultValue, bool important) 7800{ 7801 enum { ID, VAL } state = ID; 7802 7803 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 7804 RefPtr<CSSPrimitiveValue> counterName; 7805 7806 while (true) { 7807 CSSParserValue* val = m_valueList->current(); 7808 switch (state) { 7809 case ID: 7810 if (val && val->unit == CSSPrimitiveValue::CSS_IDENT) { 7811 counterName = createPrimitiveStringValue(val); 7812 state = VAL; 7813 m_valueList->next(); 7814 continue; 7815 } 7816 break; 7817 case VAL: { 7818 int i = defaultValue; 7819 if (val && val->unit == CSSPrimitiveValue::CSS_NUMBER) { 7820 i = clampToInteger(val->fValue); 7821 m_valueList->next(); 7822 } 7823 7824 list->append(createPrimitiveValuePair(counterName.release(), 7825 cssValuePool().createValue(i, CSSPrimitiveValue::CSS_NUMBER))); 7826 state = ID; 7827 continue; 7828 } 7829 } 7830 break; 7831 } 7832 7833 if (list->length() > 0) { 7834 addProperty(propId, list.release(), important); 7835 return true; 7836 } 7837 7838 return false; 7839} 7840 7841// This should go away once we drop support for -webkit-gradient 7842static PassRefPtr<CSSPrimitiveValue> parseDeprecatedGradientPoint(CSSParserValue* a, bool horizontal) 7843{ 7844 RefPtr<CSSPrimitiveValue> result; 7845 if (a->unit == CSSPrimitiveValue::CSS_IDENT) { 7846 if ((equalIgnoringCase(a, "left") && horizontal) 7847 || (equalIgnoringCase(a, "top") && !horizontal)) 7848 result = cssValuePool().createValue(0., CSSPrimitiveValue::CSS_PERCENTAGE); 7849 else if ((equalIgnoringCase(a, "right") && horizontal) 7850 || (equalIgnoringCase(a, "bottom") && !horizontal)) 7851 result = cssValuePool().createValue(100., CSSPrimitiveValue::CSS_PERCENTAGE); 7852 else if (equalIgnoringCase(a, "center")) 7853 result = cssValuePool().createValue(50., CSSPrimitiveValue::CSS_PERCENTAGE); 7854 } else if (a->unit == CSSPrimitiveValue::CSS_NUMBER || a->unit == CSSPrimitiveValue::CSS_PERCENTAGE) 7855 result = cssValuePool().createValue(a->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(a->unit)); 7856 return result; 7857} 7858 7859static bool parseDeprecatedGradientColorStop(CSSParser* p, CSSParserValue* a, CSSGradientColorStop& stop) 7860{ 7861 if (a->unit != CSSParserValue::Function) 7862 return false; 7863 7864 if (!equalIgnoringCase(a->function->name, "from(") && 7865 !equalIgnoringCase(a->function->name, "to(") && 7866 !equalIgnoringCase(a->function->name, "color-stop(")) 7867 return false; 7868 7869 CSSParserValueList* args = a->function->args.get(); 7870 if (!args) 7871 return false; 7872 7873 if (equalIgnoringCase(a->function->name, "from(") 7874 || equalIgnoringCase(a->function->name, "to(")) { 7875 // The "from" and "to" stops expect 1 argument. 7876 if (args->size() != 1) 7877 return false; 7878 7879 if (equalIgnoringCase(a->function->name, "from(")) 7880 stop.m_position = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER); 7881 else 7882 stop.m_position = cssValuePool().createValue(1, CSSPrimitiveValue::CSS_NUMBER); 7883 7884 CSSValueID id = args->current()->id; 7885 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu) 7886 stop.m_color = cssValuePool().createIdentifierValue(id); 7887 else 7888 stop.m_color = p->parseColor(args->current()); 7889 if (!stop.m_color) 7890 return false; 7891 } 7892 7893 // The "color-stop" function expects 3 arguments. 7894 if (equalIgnoringCase(a->function->name, "color-stop(")) { 7895 if (args->size() != 3) 7896 return false; 7897 7898 CSSParserValue* stopArg = args->current(); 7899 if (stopArg->unit == CSSPrimitiveValue::CSS_PERCENTAGE) 7900 stop.m_position = cssValuePool().createValue(stopArg->fValue / 100, CSSPrimitiveValue::CSS_NUMBER); 7901 else if (stopArg->unit == CSSPrimitiveValue::CSS_NUMBER) 7902 stop.m_position = cssValuePool().createValue(stopArg->fValue, CSSPrimitiveValue::CSS_NUMBER); 7903 else 7904 return false; 7905 7906 stopArg = args->next(); 7907 if (stopArg->unit != CSSParserValue::Operator || stopArg->iValue != ',') 7908 return false; 7909 7910 stopArg = args->next(); 7911 CSSValueID id = stopArg->id; 7912 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu) 7913 stop.m_color = cssValuePool().createIdentifierValue(id); 7914 else 7915 stop.m_color = p->parseColor(stopArg); 7916 if (!stop.m_color) 7917 return false; 7918 } 7919 7920 return true; 7921} 7922 7923bool CSSParser::parseDeprecatedGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient) 7924{ 7925 // Walk the arguments. 7926 CSSParserValueList* args = valueList->current()->function->args.get(); 7927 if (!args || args->size() == 0) 7928 return false; 7929 7930 // The first argument is the gradient type. It is an identifier. 7931 CSSGradientType gradientType; 7932 CSSParserValue* a = args->current(); 7933 if (!a || a->unit != CSSPrimitiveValue::CSS_IDENT) 7934 return false; 7935 if (equalIgnoringCase(a, "linear")) 7936 gradientType = CSSDeprecatedLinearGradient; 7937 else if (equalIgnoringCase(a, "radial")) 7938 gradientType = CSSDeprecatedRadialGradient; 7939 else 7940 return false; 7941 7942 RefPtr<CSSGradientValue> result; 7943 switch (gradientType) { 7944 case CSSDeprecatedLinearGradient: 7945 result = CSSLinearGradientValue::create(NonRepeating, gradientType); 7946 break; 7947 case CSSDeprecatedRadialGradient: 7948 result = CSSRadialGradientValue::create(NonRepeating, gradientType); 7949 break; 7950 default: 7951 // The rest of the gradient types shouldn't appear here. 7952 ASSERT_NOT_REACHED(); 7953 } 7954 7955 // Comma. 7956 a = args->next(); 7957 if (!isComma(a)) 7958 return false; 7959 7960 // Next comes the starting point for the gradient as an x y pair. There is no 7961 // comma between the x and the y values. 7962 // First X. It can be left, right, number or percent. 7963 a = args->next(); 7964 if (!a) 7965 return false; 7966 RefPtr<CSSPrimitiveValue> point = parseDeprecatedGradientPoint(a, true); 7967 if (!point) 7968 return false; 7969 result->setFirstX(point.release()); 7970 7971 // First Y. It can be top, bottom, number or percent. 7972 a = args->next(); 7973 if (!a) 7974 return false; 7975 point = parseDeprecatedGradientPoint(a, false); 7976 if (!point) 7977 return false; 7978 result->setFirstY(point.release()); 7979 7980 // Comma after the first point. 7981 a = args->next(); 7982 if (!isComma(a)) 7983 return false; 7984 7985 // For radial gradients only, we now expect a numeric radius. 7986 if (gradientType == CSSDeprecatedRadialGradient) { 7987 a = args->next(); 7988 if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER) 7989 return false; 7990 toCSSRadialGradientValue(result.get())->setFirstRadius(createPrimitiveNumericValue(a)); 7991 7992 // Comma after the first radius. 7993 a = args->next(); 7994 if (!isComma(a)) 7995 return false; 7996 } 7997 7998 // Next is the ending point for the gradient as an x, y pair. 7999 // Second X. It can be left, right, number or percent. 8000 a = args->next(); 8001 if (!a) 8002 return false; 8003 point = parseDeprecatedGradientPoint(a, true); 8004 if (!point) 8005 return false; 8006 result->setSecondX(point.release()); 8007 8008 // Second Y. It can be top, bottom, number or percent. 8009 a = args->next(); 8010 if (!a) 8011 return false; 8012 point = parseDeprecatedGradientPoint(a, false); 8013 if (!point) 8014 return false; 8015 result->setSecondY(point.release()); 8016 8017 // For radial gradients only, we now expect the second radius. 8018 if (gradientType == CSSDeprecatedRadialGradient) { 8019 // Comma after the second point. 8020 a = args->next(); 8021 if (!isComma(a)) 8022 return false; 8023 8024 a = args->next(); 8025 if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER) 8026 return false; 8027 toCSSRadialGradientValue(result.get())->setSecondRadius(createPrimitiveNumericValue(a)); 8028 } 8029 8030 // We now will accept any number of stops (0 or more). 8031 a = args->next(); 8032 while (a) { 8033 // Look for the comma before the next stop. 8034 if (!isComma(a)) 8035 return false; 8036 8037 // Now examine the stop itself. 8038 a = args->next(); 8039 if (!a) 8040 return false; 8041 8042 // The function name needs to be one of "from", "to", or "color-stop." 8043 CSSGradientColorStop stop; 8044 if (!parseDeprecatedGradientColorStop(this, a, stop)) 8045 return false; 8046 result->addStop(stop); 8047 8048 // Advance 8049 a = args->next(); 8050 } 8051 8052 gradient = result.release(); 8053 return true; 8054} 8055 8056static PassRefPtr<CSSPrimitiveValue> valueFromSideKeyword(CSSParserValue* a, bool& isHorizontal) 8057{ 8058 if (a->unit != CSSPrimitiveValue::CSS_IDENT) 8059 return 0; 8060 8061 switch (a->id) { 8062 case CSSValueLeft: 8063 case CSSValueRight: 8064 isHorizontal = true; 8065 break; 8066 case CSSValueTop: 8067 case CSSValueBottom: 8068 isHorizontal = false; 8069 break; 8070 default: 8071 return 0; 8072 } 8073 return cssValuePool().createIdentifierValue(a->id); 8074} 8075 8076static PassRefPtr<CSSPrimitiveValue> parseGradientColorOrKeyword(CSSParser* p, CSSParserValue* value) 8077{ 8078 CSSValueID id = value->id; 8079 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || id == CSSValueCurrentcolor) 8080 return cssValuePool().createIdentifierValue(id); 8081 8082 return p->parseColor(value); 8083} 8084 8085bool CSSParser::parseDeprecatedLinearGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 8086{ 8087 RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, CSSPrefixedLinearGradient); 8088 8089 // Walk the arguments. 8090 CSSParserValueList* args = valueList->current()->function->args.get(); 8091 if (!args || !args->size()) 8092 return false; 8093 8094 CSSParserValue* a = args->current(); 8095 if (!a) 8096 return false; 8097 8098 bool expectComma = false; 8099 // Look for angle. 8100 if (validUnit(a, FAngle, CSSStrictMode)) { 8101 result->setAngle(createPrimitiveNumericValue(a)); 8102 8103 args->next(); 8104 expectComma = true; 8105 } else { 8106 // Look one or two optional keywords that indicate a side or corner. 8107 RefPtr<CSSPrimitiveValue> startX, startY; 8108 8109 RefPtr<CSSPrimitiveValue> location; 8110 bool isHorizontal = false; 8111 if ((location = valueFromSideKeyword(a, isHorizontal))) { 8112 if (isHorizontal) 8113 startX = location; 8114 else 8115 startY = location; 8116 8117 if ((a = args->next())) { 8118 if ((location = valueFromSideKeyword(a, isHorizontal))) { 8119 if (isHorizontal) { 8120 if (startX) 8121 return false; 8122 startX = location; 8123 } else { 8124 if (startY) 8125 return false; 8126 startY = location; 8127 } 8128 8129 args->next(); 8130 } 8131 } 8132 8133 expectComma = true; 8134 } 8135 8136 if (!startX && !startY) 8137 startY = cssValuePool().createIdentifierValue(CSSValueTop); 8138 8139 result->setFirstX(startX.release()); 8140 result->setFirstY(startY.release()); 8141 } 8142 8143 if (!parseGradientColorStops(args, result.get(), expectComma)) 8144 return false; 8145 8146 if (!result->stopCount()) 8147 return false; 8148 8149 gradient = result.release(); 8150 return true; 8151} 8152 8153bool CSSParser::parseDeprecatedRadialGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 8154{ 8155 RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSPrefixedRadialGradient); 8156 8157 // Walk the arguments. 8158 CSSParserValueList* args = valueList->current()->function->args.get(); 8159 if (!args || !args->size()) 8160 return false; 8161 8162 CSSParserValue* a = args->current(); 8163 if (!a) 8164 return false; 8165 8166 bool expectComma = false; 8167 8168 // Optional background-position 8169 RefPtr<CSSValue> centerX; 8170 RefPtr<CSSValue> centerY; 8171 // parse2ValuesFillPosition advances the args next pointer. 8172 parse2ValuesFillPosition(args, centerX, centerY); 8173 a = args->current(); 8174 if (!a) 8175 return false; 8176 8177 if (centerX || centerY) { 8178 // Comma 8179 if (!isComma(a)) 8180 return false; 8181 8182 a = args->next(); 8183 if (!a) 8184 return false; 8185 } 8186 8187 ASSERT(!centerX || centerX->isPrimitiveValue()); 8188 ASSERT(!centerY || centerY->isPrimitiveValue()); 8189 8190 result->setFirstX(toCSSPrimitiveValue(centerX.get())); 8191 result->setSecondX(toCSSPrimitiveValue(centerX.get())); 8192 // CSS3 radial gradients always share the same start and end point. 8193 result->setFirstY(toCSSPrimitiveValue(centerY.get())); 8194 result->setSecondY(toCSSPrimitiveValue(centerY.get())); 8195 8196 RefPtr<CSSPrimitiveValue> shapeValue; 8197 RefPtr<CSSPrimitiveValue> sizeValue; 8198 8199 // Optional shape and/or size in any order. 8200 for (int i = 0; i < 2; ++i) { 8201 if (a->unit != CSSPrimitiveValue::CSS_IDENT) 8202 break; 8203 8204 bool foundValue = false; 8205 switch (a->id) { 8206 case CSSValueCircle: 8207 case CSSValueEllipse: 8208 shapeValue = cssValuePool().createIdentifierValue(a->id); 8209 foundValue = true; 8210 break; 8211 case CSSValueClosestSide: 8212 case CSSValueClosestCorner: 8213 case CSSValueFarthestSide: 8214 case CSSValueFarthestCorner: 8215 case CSSValueContain: 8216 case CSSValueCover: 8217 sizeValue = cssValuePool().createIdentifierValue(a->id); 8218 foundValue = true; 8219 break; 8220 default: 8221 break; 8222 } 8223 8224 if (foundValue) { 8225 a = args->next(); 8226 if (!a) 8227 return false; 8228 8229 expectComma = true; 8230 } 8231 } 8232 8233 result->setShape(shapeValue); 8234 result->setSizingBehavior(sizeValue); 8235 8236 // Or, two lengths or percentages 8237 RefPtr<CSSPrimitiveValue> horizontalSize; 8238 RefPtr<CSSPrimitiveValue> verticalSize; 8239 8240 if (!shapeValue && !sizeValue) { 8241 if (validUnit(a, FLength | FPercent)) { 8242 horizontalSize = createPrimitiveNumericValue(a); 8243 a = args->next(); 8244 if (!a) 8245 return false; 8246 8247 expectComma = true; 8248 } 8249 8250 if (validUnit(a, FLength | FPercent)) { 8251 verticalSize = createPrimitiveNumericValue(a); 8252 8253 a = args->next(); 8254 if (!a) 8255 return false; 8256 expectComma = true; 8257 } 8258 } 8259 8260 // Must have neither or both. 8261 if (!horizontalSize != !verticalSize) 8262 return false; 8263 8264 result->setEndHorizontalSize(horizontalSize); 8265 result->setEndVerticalSize(verticalSize); 8266 8267 if (!parseGradientColorStops(args, result.get(), expectComma)) 8268 return false; 8269 8270 gradient = result.release(); 8271 return true; 8272} 8273 8274bool CSSParser::parseLinearGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 8275{ 8276 RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, CSSLinearGradient); 8277 8278 CSSParserValueList* args = valueList->current()->function->args.get(); 8279 if (!args || !args->size()) 8280 return false; 8281 8282 CSSParserValue* a = args->current(); 8283 if (!a) 8284 return false; 8285 8286 bool expectComma = false; 8287 // Look for angle. 8288 if (validUnit(a, FAngle, CSSStrictMode)) { 8289 result->setAngle(createPrimitiveNumericValue(a)); 8290 8291 args->next(); 8292 expectComma = true; 8293 } else if (a->unit == CSSPrimitiveValue::CSS_IDENT && equalIgnoringCase(a, "to")) { 8294 // to [ [left | right] || [top | bottom] ] 8295 a = args->next(); 8296 if (!a) 8297 return false; 8298 8299 RefPtr<CSSPrimitiveValue> endX, endY; 8300 RefPtr<CSSPrimitiveValue> location; 8301 bool isHorizontal = false; 8302 8303 location = valueFromSideKeyword(a, isHorizontal); 8304 if (!location) 8305 return false; 8306 8307 if (isHorizontal) 8308 endX = location; 8309 else 8310 endY = location; 8311 8312 a = args->next(); 8313 if (!a) 8314 return false; 8315 8316 location = valueFromSideKeyword(a, isHorizontal); 8317 if (location) { 8318 if (isHorizontal) { 8319 if (endX) 8320 return false; 8321 endX = location; 8322 } else { 8323 if (endY) 8324 return false; 8325 endY = location; 8326 } 8327 8328 args->next(); 8329 } 8330 8331 expectComma = true; 8332 result->setFirstX(endX.release()); 8333 result->setFirstY(endY.release()); 8334 } 8335 8336 if (!parseGradientColorStops(args, result.get(), expectComma)) 8337 return false; 8338 8339 if (!result->stopCount()) 8340 return false; 8341 8342 gradient = result.release(); 8343 return true; 8344} 8345 8346bool CSSParser::parseRadialGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 8347{ 8348 RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient); 8349 8350 CSSParserValueList* args = valueList->current()->function->args.get(); 8351 if (!args || !args->size()) 8352 return false; 8353 8354 CSSParserValue* a = args->current(); 8355 if (!a) 8356 return false; 8357 8358 bool expectComma = false; 8359 8360 RefPtr<CSSPrimitiveValue> shapeValue; 8361 RefPtr<CSSPrimitiveValue> sizeValue; 8362 RefPtr<CSSPrimitiveValue> horizontalSize; 8363 RefPtr<CSSPrimitiveValue> verticalSize; 8364 8365 // First part of grammar, the size/shape clause: 8366 // [ circle || <length> ] | 8367 // [ ellipse || [ <length> | <percentage> ]{2} ] | 8368 // [ [ circle | ellipse] || <size-keyword> ] 8369 for (int i = 0; i < 3; ++i) { 8370 if (a->unit == CSSPrimitiveValue::CSS_IDENT) { 8371 bool badIdent = false; 8372 switch (a->id) { 8373 case CSSValueCircle: 8374 case CSSValueEllipse: 8375 if (shapeValue) 8376 return false; 8377 shapeValue = cssValuePool().createIdentifierValue(a->id); 8378 break; 8379 case CSSValueClosestSide: 8380 case CSSValueClosestCorner: 8381 case CSSValueFarthestSide: 8382 case CSSValueFarthestCorner: 8383 if (sizeValue || horizontalSize) 8384 return false; 8385 sizeValue = cssValuePool().createIdentifierValue(a->id); 8386 break; 8387 default: 8388 badIdent = true; 8389 } 8390 8391 if (badIdent) 8392 break; 8393 8394 a = args->next(); 8395 if (!a) 8396 return false; 8397 } else if (validUnit(a, FLength | FPercent)) { 8398 8399 if (sizeValue || horizontalSize) 8400 return false; 8401 horizontalSize = createPrimitiveNumericValue(a); 8402 8403 a = args->next(); 8404 if (!a) 8405 return false; 8406 8407 if (validUnit(a, FLength | FPercent)) { 8408 verticalSize = createPrimitiveNumericValue(a); 8409 ++i; 8410 a = args->next(); 8411 if (!a) 8412 return false; 8413 } 8414 } else 8415 break; 8416 } 8417 8418 // You can specify size as a keyword or a length/percentage, not both. 8419 if (sizeValue && horizontalSize) 8420 return false; 8421 // Circles must have 0 or 1 lengths. 8422 if (shapeValue && shapeValue->getValueID() == CSSValueCircle && verticalSize) 8423 return false; 8424 // Ellipses must have 0 or 2 length/percentages. 8425 if (shapeValue && shapeValue->getValueID() == CSSValueEllipse && horizontalSize && !verticalSize) 8426 return false; 8427 // If there's only one size, it must be a length. 8428 if (!verticalSize && horizontalSize && horizontalSize->isPercentage()) 8429 return false; 8430 8431 result->setShape(shapeValue); 8432 result->setSizingBehavior(sizeValue); 8433 result->setEndHorizontalSize(horizontalSize); 8434 result->setEndVerticalSize(verticalSize); 8435 8436 // Second part of grammar, the center-position clause: 8437 // at <position> 8438 RefPtr<CSSValue> centerX; 8439 RefPtr<CSSValue> centerY; 8440 if (a->unit == CSSPrimitiveValue::CSS_IDENT && equalIgnoringCase(a, "at")) { 8441 a = args->next(); 8442 if (!a) 8443 return false; 8444 8445 parseFillPosition(args, centerX, centerY); 8446 if (!(centerX && centerY)) 8447 return false; 8448 8449 a = args->current(); 8450 if (!a) 8451 return false; 8452 8453 ASSERT(centerX->isPrimitiveValue()); 8454 ASSERT(centerY->isPrimitiveValue()); 8455 result->setFirstX(toCSSPrimitiveValue(centerX.get())); 8456 result->setFirstY(toCSSPrimitiveValue(centerY.get())); 8457 // Right now, CSS radial gradients have the same start and end centers. 8458 result->setSecondX(toCSSPrimitiveValue(centerX.get())); 8459 result->setSecondY(toCSSPrimitiveValue(centerY.get())); 8460 } 8461 8462 if (shapeValue || sizeValue || horizontalSize || centerX || centerY) 8463 expectComma = true; 8464 8465 if (!parseGradientColorStops(args, result.get(), expectComma)) 8466 return false; 8467 8468 gradient = result.release(); 8469 return true; 8470} 8471 8472bool CSSParser::parseGradientColorStops(CSSParserValueList* valueList, CSSGradientValue* gradient, bool expectComma) 8473{ 8474 CSSParserValue* a = valueList->current(); 8475 8476 // Now look for color stops. 8477 while (a) { 8478 // Look for the comma before the next stop. 8479 if (expectComma) { 8480 if (!isComma(a)) 8481 return false; 8482 8483 a = valueList->next(); 8484 if (!a) 8485 return false; 8486 } 8487 8488 // <color-stop> = <color> [ <percentage> | <length> ]? 8489 CSSGradientColorStop stop; 8490 stop.m_color = parseGradientColorOrKeyword(this, a); 8491 if (!stop.m_color) 8492 return false; 8493 8494 a = valueList->next(); 8495 if (a) { 8496 if (validUnit(a, FLength | FPercent)) { 8497 stop.m_position = createPrimitiveNumericValue(a); 8498 a = valueList->next(); 8499 } 8500 } 8501 8502 gradient->addStop(stop); 8503 expectComma = true; 8504 } 8505 8506 // Must have 2 or more stops to be valid. 8507 return gradient->stopCount() >= 2; 8508} 8509 8510bool CSSParser::isGeneratedImageValue(CSSParserValue* val) const 8511{ 8512 if (val->unit != CSSParserValue::Function) 8513 return false; 8514 8515 return equalIgnoringCase(val->function->name, "-webkit-gradient(") 8516 || equalIgnoringCase(val->function->name, "-webkit-linear-gradient(") 8517 || equalIgnoringCase(val->function->name, "linear-gradient(") 8518 || equalIgnoringCase(val->function->name, "-webkit-repeating-linear-gradient(") 8519 || equalIgnoringCase(val->function->name, "repeating-linear-gradient(") 8520 || equalIgnoringCase(val->function->name, "-webkit-radial-gradient(") 8521 || equalIgnoringCase(val->function->name, "radial-gradient(") 8522 || equalIgnoringCase(val->function->name, "-webkit-repeating-radial-gradient(") 8523 || equalIgnoringCase(val->function->name, "repeating-radial-gradient(") 8524 || equalIgnoringCase(val->function->name, "-webkit-canvas(") 8525 || equalIgnoringCase(val->function->name, "-webkit-cross-fade(") 8526 || equalIgnoringCase(val->function->name, "-webkit-filter("); 8527} 8528 8529bool CSSParser::parseGeneratedImage(CSSParserValueList* valueList, RefPtr<CSSValue>& value) 8530{ 8531 CSSParserValue* val = valueList->current(); 8532 8533 if (val->unit != CSSParserValue::Function) 8534 return false; 8535 8536 if (equalIgnoringCase(val->function->name, "-webkit-gradient(")) 8537 return parseDeprecatedGradient(valueList, value); 8538 8539 if (equalIgnoringCase(val->function->name, "-webkit-linear-gradient(")) 8540 return parseDeprecatedLinearGradient(valueList, value, NonRepeating); 8541 8542 if (equalIgnoringCase(val->function->name, "linear-gradient(")) 8543 return parseLinearGradient(valueList, value, NonRepeating); 8544 8545 if (equalIgnoringCase(val->function->name, "-webkit-repeating-linear-gradient(")) 8546 return parseDeprecatedLinearGradient(valueList, value, Repeating); 8547 8548 if (equalIgnoringCase(val->function->name, "repeating-linear-gradient(")) 8549 return parseLinearGradient(valueList, value, Repeating); 8550 8551 if (equalIgnoringCase(val->function->name, "-webkit-radial-gradient(")) 8552 return parseDeprecatedRadialGradient(valueList, value, NonRepeating); 8553 8554 if (equalIgnoringCase(val->function->name, "radial-gradient(")) 8555 return parseRadialGradient(valueList, value, NonRepeating); 8556 8557 if (equalIgnoringCase(val->function->name, "-webkit-repeating-radial-gradient(")) 8558 return parseDeprecatedRadialGradient(valueList, value, Repeating); 8559 8560 if (equalIgnoringCase(val->function->name, "repeating-radial-gradient(")) 8561 return parseRadialGradient(valueList, value, Repeating); 8562 8563 if (equalIgnoringCase(val->function->name, "-webkit-canvas(")) 8564 return parseCanvas(valueList, value); 8565 8566 if (equalIgnoringCase(val->function->name, "-webkit-cross-fade(")) 8567 return parseCrossfade(valueList, value); 8568 8569#if ENABLE(CSS_FILTERS) 8570 if (equalIgnoringCase(val->function->name, "-webkit-filter(")) 8571 return parseFilterImage(valueList, value); 8572#endif 8573 8574 return false; 8575} 8576 8577#if ENABLE(CSS_FILTERS) 8578bool CSSParser::parseFilterImage(CSSParserValueList* valueList, RefPtr<CSSValue>& filter) 8579{ 8580 RefPtr<CSSFilterImageValue> result; 8581 8582 // Walk the arguments. 8583 CSSParserValueList* args = valueList->current()->function->args.get(); 8584 if (!args) 8585 return false; 8586 CSSParserValue* value = args->current(); 8587 RefPtr<CSSValue> imageValue; 8588 RefPtr<CSSValue> filterValue; 8589 8590 if (!value) 8591 return false; 8592 8593 // The first argument is the image. It is a fill image. 8594 if (!parseFillImage(args, imageValue)) { 8595 if (value->unit == CSSPrimitiveValue::CSS_STRING) 8596 imageValue = CSSImageValue::create(completeURL(value->string)); 8597 else 8598 return false; 8599 } 8600 8601 value = args->next(); 8602 8603 // Skip a comma 8604 if (!isComma(value)) 8605 return false; 8606 value = args->next(); 8607 8608 if (!value || !parseFilter(args, filterValue)) 8609 return false; 8610 value = args->next(); 8611 8612 result = CSSFilterImageValue::create(imageValue.releaseNonNull(), filterValue.releaseNonNull()); 8613 8614 filter = result; 8615 8616 return true; 8617} 8618#endif 8619 8620bool CSSParser::parseCrossfade(CSSParserValueList* valueList, RefPtr<CSSValue>& crossfade) 8621{ 8622 RefPtr<CSSCrossfadeValue> result; 8623 8624 // Walk the arguments. 8625 CSSParserValueList* args = valueList->current()->function->args.get(); 8626 if (!args || args->size() != 5) 8627 return false; 8628 CSSParserValue* a = args->current(); 8629 RefPtr<CSSValue> fromImageValue; 8630 RefPtr<CSSValue> toImageValue; 8631 8632 // The first argument is the "from" image. It is a fill image. 8633 if (!a || !parseFillImage(args, fromImageValue)) 8634 return false; 8635 a = args->next(); 8636 8637 // Skip a comma 8638 if (!isComma(a)) 8639 return false; 8640 a = args->next(); 8641 8642 // The second argument is the "to" image. It is a fill image. 8643 if (!a || !parseFillImage(args, toImageValue)) 8644 return false; 8645 a = args->next(); 8646 8647 // Skip a comma 8648 if (!isComma(a)) 8649 return false; 8650 a = args->next(); 8651 8652 // The third argument is the crossfade value. It is a percentage or a fractional number. 8653 RefPtr<CSSPrimitiveValue> percentage; 8654 if (!a) 8655 return false; 8656 8657 if (a->unit == CSSPrimitiveValue::CSS_PERCENTAGE) 8658 percentage = cssValuePool().createValue(clampTo<double>(a->fValue / 100, 0, 1), CSSPrimitiveValue::CSS_NUMBER); 8659 else if (a->unit == CSSPrimitiveValue::CSS_NUMBER) 8660 percentage = cssValuePool().createValue(clampTo<double>(a->fValue, 0, 1), CSSPrimitiveValue::CSS_NUMBER); 8661 else 8662 return false; 8663 8664 result = CSSCrossfadeValue::create(fromImageValue, toImageValue); 8665 result->setPercentage(percentage); 8666 8667 crossfade = result; 8668 8669 return true; 8670} 8671 8672bool CSSParser::parseCanvas(CSSParserValueList* valueList, RefPtr<CSSValue>& canvas) 8673{ 8674 // Walk the arguments. 8675 CSSParserValueList* args = valueList->current()->function->args.get(); 8676 if (!args || args->size() != 1) 8677 return false; 8678 8679 // The first argument is the canvas name. It is an identifier. 8680 CSSParserValue* value = args->current(); 8681 if (!value || value->unit != CSSPrimitiveValue::CSS_IDENT) 8682 return false; 8683 8684 canvas = CSSCanvasValue::create(value->string); 8685 return true; 8686} 8687 8688#if ENABLE(CSS_IMAGE_RESOLUTION) 8689PassRefPtr<CSSValue> CSSParser::parseImageResolution() 8690{ 8691 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 8692 bool haveResolution = false; 8693 bool haveFromImage = false; 8694 bool haveSnap = false; 8695 8696 CSSParserValue* value = m_valueList->current(); 8697 while (value) { 8698 if (!haveFromImage && value->id == CSSValueFromImage) { 8699 list->append(cssValuePool().createIdentifierValue(value->id)); 8700 haveFromImage = true; 8701 } else if (!haveSnap && value->id == CSSValueSnap) { 8702 list->append(cssValuePool().createIdentifierValue(value->id)); 8703 haveSnap = true; 8704 } else if (!haveResolution && validUnit(value, FResolution | FNonNeg) && value->fValue > 0) { 8705 list->append(createPrimitiveNumericValue(value)); 8706 haveResolution = true; 8707 } else 8708 return 0; 8709 value = m_valueList->next(); 8710 } 8711 if (!list->length()) 8712 return 0; 8713 if (!haveFromImage && !haveResolution) 8714 return 0; 8715 return list.release(); 8716} 8717#endif 8718 8719#if ENABLE(CSS_IMAGE_SET) 8720PassRefPtr<CSSValue> CSSParser::parseImageSet() 8721{ 8722 CSSParserValue* value = m_valueList->current(); 8723 ASSERT(value->unit == CSSParserValue::Function); 8724 8725 CSSParserValueList* functionArgs = value->function->args.get(); 8726 if (!functionArgs || !functionArgs->size() || !functionArgs->current()) 8727 return 0; 8728 8729 RefPtr<CSSImageSetValue> imageSet = CSSImageSetValue::create(); 8730 CSSParserValue* arg = functionArgs->current(); 8731 while (arg) { 8732 if (arg->unit != CSSPrimitiveValue::CSS_URI) 8733 return 0; 8734 8735 imageSet->append(CSSImageValue::create(completeURL(arg->string))); 8736 arg = functionArgs->next(); 8737 if (!arg || arg->unit != CSSPrimitiveValue::CSS_DIMENSION) 8738 return 0; 8739 8740 double imageScaleFactor = 0; 8741 const String& string = arg->string; 8742 unsigned length = string.length(); 8743 if (!length) 8744 return 0; 8745 if (string.is8Bit()) { 8746 const LChar* start = string.characters8(); 8747 parseDouble(start, start + length, 'x', imageScaleFactor); 8748 } else { 8749 const UChar* start = string.characters16(); 8750 parseDouble(start, start + length, 'x', imageScaleFactor); 8751 } 8752 if (imageScaleFactor <= 0) 8753 return 0; 8754 imageSet->append(cssValuePool().createValue(imageScaleFactor, CSSPrimitiveValue::CSS_NUMBER)); 8755 8756 // If there are no more arguments, we're done. 8757 arg = functionArgs->next(); 8758 if (!arg) 8759 break; 8760 8761 // If there are more arguments, they should be after a comma. 8762 if (!isComma(arg)) 8763 return 0; 8764 8765 // Skip the comma and move on to the next argument. 8766 arg = functionArgs->next(); 8767 } 8768 8769 return imageSet.release(); 8770} 8771#endif 8772 8773class TransformOperationInfo { 8774public: 8775 TransformOperationInfo(const CSSParserString& name) 8776 : m_type(WebKitCSSTransformValue::UnknownTransformOperation) 8777 , m_argCount(1) 8778 , m_allowSingleArgument(false) 8779 , m_unit(CSSParser::FUnknown) 8780 { 8781 const UChar* characters; 8782 unsigned nameLength = name.length(); 8783 8784 const unsigned longestNameLength = 12; 8785 UChar characterBuffer[longestNameLength]; 8786 if (name.is8Bit()) { 8787 unsigned length = std::min(longestNameLength, nameLength); 8788 const LChar* characters8 = name.characters8(); 8789 for (unsigned i = 0; i < length; ++i) 8790 characterBuffer[i] = characters8[i]; 8791 characters = characterBuffer; 8792 } else 8793 characters = name.characters16(); 8794 8795 switch (nameLength) { 8796 case 5: 8797 // Valid name: skew(. 8798 if (((characters[0] == 's') || (characters[0] == 'S')) 8799 & ((characters[1] == 'k') || (characters[1] == 'K')) 8800 & ((characters[2] == 'e') || (characters[2] == 'E')) 8801 & ((characters[3] == 'w') || (characters[3] == 'W')) 8802 & (characters[4] == '(')) { 8803 m_unit = CSSParser::FAngle; 8804 m_type = WebKitCSSTransformValue::SkewTransformOperation; 8805 m_allowSingleArgument = true; 8806 m_argCount = 3; 8807 } 8808 break; 8809 case 6: 8810 // Valid names: skewx(, skewy(, scale(. 8811 if ((characters[1] == 'c') || (characters[1] == 'C')) { 8812 if (((characters[0] == 's') || (characters[0] == 'S')) 8813 & ((characters[2] == 'a') || (characters[2] == 'A')) 8814 & ((characters[3] == 'l') || (characters[3] == 'L')) 8815 & ((characters[4] == 'e') || (characters[4] == 'E')) 8816 & (characters[5] == '(')) { 8817 m_unit = CSSParser::FNumber; 8818 m_type = WebKitCSSTransformValue::ScaleTransformOperation; 8819 m_allowSingleArgument = true; 8820 m_argCount = 3; 8821 } 8822 } else if (((characters[0] == 's') || (characters[0] == 'S')) 8823 & ((characters[1] == 'k') || (characters[1] == 'K')) 8824 & ((characters[2] == 'e') || (characters[2] == 'E')) 8825 & ((characters[3] == 'w') || (characters[3] == 'W')) 8826 & (characters[5] == '(')) { 8827 if ((characters[4] == 'x') || (characters[4] == 'X')) { 8828 m_unit = CSSParser::FAngle; 8829 m_type = WebKitCSSTransformValue::SkewXTransformOperation; 8830 } else if ((characters[4] == 'y') || (characters[4] == 'Y')) { 8831 m_unit = CSSParser::FAngle; 8832 m_type = WebKitCSSTransformValue::SkewYTransformOperation; 8833 } 8834 } 8835 break; 8836 case 7: 8837 // Valid names: matrix(, rotate(, scalex(, scaley(, scalez(. 8838 if ((characters[0] == 'm') || (characters[0] == 'M')) { 8839 if (((characters[1] == 'a') || (characters[1] == 'A')) 8840 & ((characters[2] == 't') || (characters[2] == 'T')) 8841 & ((characters[3] == 'r') || (characters[3] == 'R')) 8842 & ((characters[4] == 'i') || (characters[4] == 'I')) 8843 & ((characters[5] == 'x') || (characters[5] == 'X')) 8844 & (characters[6] == '(')) { 8845 m_unit = CSSParser::FNumber; 8846 m_type = WebKitCSSTransformValue::MatrixTransformOperation; 8847 m_argCount = 11; 8848 } 8849 } else if ((characters[0] == 'r') || (characters[0] == 'R')) { 8850 if (((characters[1] == 'o') || (characters[1] == 'O')) 8851 & ((characters[2] == 't') || (characters[2] == 'T')) 8852 & ((characters[3] == 'a') || (characters[3] == 'A')) 8853 & ((characters[4] == 't') || (characters[4] == 'T')) 8854 & ((characters[5] == 'e') || (characters[5] == 'E')) 8855 & (characters[6] == '(')) { 8856 m_unit = CSSParser::FAngle; 8857 m_type = WebKitCSSTransformValue::RotateTransformOperation; 8858 } 8859 } else if (((characters[0] == 's') || (characters[0] == 'S')) 8860 & ((characters[1] == 'c') || (characters[1] == 'C')) 8861 & ((characters[2] == 'a') || (characters[2] == 'A')) 8862 & ((characters[3] == 'l') || (characters[3] == 'L')) 8863 & ((characters[4] == 'e') || (characters[4] == 'E')) 8864 & (characters[6] == '(')) { 8865 if ((characters[5] == 'x') || (characters[5] == 'X')) { 8866 m_unit = CSSParser::FNumber; 8867 m_type = WebKitCSSTransformValue::ScaleXTransformOperation; 8868 } else if ((characters[5] == 'y') || (characters[5] == 'Y')) { 8869 m_unit = CSSParser::FNumber; 8870 m_type = WebKitCSSTransformValue::ScaleYTransformOperation; 8871 } else if ((characters[5] == 'z') || (characters[5] == 'Z')) { 8872 m_unit = CSSParser::FNumber; 8873 m_type = WebKitCSSTransformValue::ScaleZTransformOperation; 8874 } 8875 } 8876 break; 8877 case 8: 8878 // Valid names: rotatex(, rotatey(, rotatez(, scale3d(. 8879 if ((characters[0] == 's') || (characters[0] == 'S')) { 8880 if (((characters[1] == 'c') || (characters[1] == 'C')) 8881 & ((characters[2] == 'a') || (characters[2] == 'A')) 8882 & ((characters[3] == 'l') || (characters[3] == 'L')) 8883 & ((characters[4] == 'e') || (characters[4] == 'E')) 8884 & (characters[5] == '3') 8885 & ((characters[6] == 'd') || (characters[6] == 'D')) 8886 & (characters[7] == '(')) { 8887 m_unit = CSSParser::FNumber; 8888 m_type = WebKitCSSTransformValue::Scale3DTransformOperation; 8889 m_argCount = 5; 8890 } 8891 } else if (((characters[0] == 'r') || (characters[0] == 'R')) 8892 & ((characters[1] == 'o') || (characters[1] == 'O')) 8893 & ((characters[2] == 't') || (characters[2] == 'T')) 8894 & ((characters[3] == 'a') || (characters[3] == 'A')) 8895 & ((characters[4] == 't') || (characters[4] == 'T')) 8896 & ((characters[5] == 'e') || (characters[5] == 'E')) 8897 & (characters[7] == '(')) { 8898 if ((characters[6] == 'x') || (characters[6] == 'X')) { 8899 m_unit = CSSParser::FAngle; 8900 m_type = WebKitCSSTransformValue::RotateXTransformOperation; 8901 } else if ((characters[6] == 'y') || (characters[6] == 'Y')) { 8902 m_unit = CSSParser::FAngle; 8903 m_type = WebKitCSSTransformValue::RotateYTransformOperation; 8904 } else if ((characters[6] == 'z') || (characters[6] == 'Z')) { 8905 m_unit = CSSParser::FAngle; 8906 m_type = WebKitCSSTransformValue::RotateZTransformOperation; 8907 } 8908 } 8909 break; 8910 case 9: 8911 // Valid names: matrix3d(, rotate3d(. 8912 if ((characters[0] == 'm') || (characters[0] == 'M')) { 8913 if (((characters[1] == 'a') || (characters[1] == 'A')) 8914 & ((characters[2] == 't') || (characters[2] == 'T')) 8915 & ((characters[3] == 'r') || (characters[3] == 'R')) 8916 & ((characters[4] == 'i') || (characters[4] == 'I')) 8917 & ((characters[5] == 'x') || (characters[5] == 'X')) 8918 & (characters[6] == '3') 8919 & ((characters[7] == 'd') || (characters[7] == 'D')) 8920 & (characters[8] == '(')) { 8921 m_unit = CSSParser::FNumber; 8922 m_type = WebKitCSSTransformValue::Matrix3DTransformOperation; 8923 m_argCount = 31; 8924 } 8925 } else if (((characters[0] == 'r') || (characters[0] == 'R')) 8926 & ((characters[1] == 'o') || (characters[1] == 'O')) 8927 & ((characters[2] == 't') || (characters[2] == 'T')) 8928 & ((characters[3] == 'a') || (characters[3] == 'A')) 8929 & ((characters[4] == 't') || (characters[4] == 'T')) 8930 & ((characters[5] == 'e') || (characters[5] == 'E')) 8931 & (characters[6] == '3') 8932 & ((characters[7] == 'd') || (characters[7] == 'D')) 8933 & (characters[8] == '(')) { 8934 m_unit = CSSParser::FNumber; 8935 m_type = WebKitCSSTransformValue::Rotate3DTransformOperation; 8936 m_argCount = 7; 8937 } 8938 break; 8939 case 10: 8940 // Valid name: translate(. 8941 if (((characters[0] == 't') || (characters[0] == 'T')) 8942 & ((characters[1] == 'r') || (characters[1] == 'R')) 8943 & ((characters[2] == 'a') || (characters[2] == 'A')) 8944 & ((characters[3] == 'n') || (characters[3] == 'N')) 8945 & ((characters[4] == 's') || (characters[4] == 'S')) 8946 & ((characters[5] == 'l') || (characters[5] == 'L')) 8947 & ((characters[6] == 'a') || (characters[6] == 'A')) 8948 & ((characters[7] == 't') || (characters[7] == 'T')) 8949 & ((characters[8] == 'e') || (characters[8] == 'E')) 8950 & (characters[9] == '(')) { 8951 m_unit = CSSParser::FLength | CSSParser::FPercent; 8952 m_type = WebKitCSSTransformValue::TranslateTransformOperation; 8953 m_allowSingleArgument = true; 8954 m_argCount = 3; 8955 } 8956 break; 8957 case 11: 8958 // Valid names: translatex(, translatey(, translatez(. 8959 if (((characters[0] == 't') || (characters[0] == 'T')) 8960 & ((characters[1] == 'r') || (characters[1] == 'R')) 8961 & ((characters[2] == 'a') || (characters[2] == 'A')) 8962 & ((characters[3] == 'n') || (characters[3] == 'N')) 8963 & ((characters[4] == 's') || (characters[4] == 'S')) 8964 & ((characters[5] == 'l') || (characters[5] == 'L')) 8965 & ((characters[6] == 'a') || (characters[6] == 'A')) 8966 & ((characters[7] == 't') || (characters[7] == 'T')) 8967 & ((characters[8] == 'e') || (characters[8] == 'E')) 8968 & (characters[10] == '(')) { 8969 if ((characters[9] == 'x') || (characters[9] == 'X')) { 8970 m_unit = CSSParser::FLength | CSSParser::FPercent; 8971 m_type = WebKitCSSTransformValue::TranslateXTransformOperation; 8972 } else if ((characters[9] == 'y') || (characters[9] == 'Y')) { 8973 m_unit = CSSParser::FLength | CSSParser::FPercent; 8974 m_type = WebKitCSSTransformValue::TranslateYTransformOperation; 8975 } else if ((characters[9] == 'z') || (characters[9] == 'Z')) { 8976 m_unit = CSSParser::FLength | CSSParser::FPercent; 8977 m_type = WebKitCSSTransformValue::TranslateZTransformOperation; 8978 } 8979 } 8980 break; 8981 case 12: 8982 // Valid names: perspective(, translate3d(. 8983 if ((characters[0] == 'p') || (characters[0] == 'P')) { 8984 if (((characters[1] == 'e') || (characters[1] == 'E')) 8985 & ((characters[2] == 'r') || (characters[2] == 'R')) 8986 & ((characters[3] == 's') || (characters[3] == 'S')) 8987 & ((characters[4] == 'p') || (characters[4] == 'P')) 8988 & ((characters[5] == 'e') || (characters[5] == 'E')) 8989 & ((characters[6] == 'c') || (characters[6] == 'C')) 8990 & ((characters[7] == 't') || (characters[7] == 'T')) 8991 & ((characters[8] == 'i') || (characters[8] == 'I')) 8992 & ((characters[9] == 'v') || (characters[9] == 'V')) 8993 & ((characters[10] == 'e') || (characters[10] == 'E')) 8994 & (characters[11] == '(')) { 8995 m_unit = CSSParser::FNumber; 8996 m_type = WebKitCSSTransformValue::PerspectiveTransformOperation; 8997 } 8998 } else if (((characters[0] == 't') || (characters[0] == 'T')) 8999 & ((characters[1] == 'r') || (characters[1] == 'R')) 9000 & ((characters[2] == 'a') || (characters[2] == 'A')) 9001 & ((characters[3] == 'n') || (characters[3] == 'N')) 9002 & ((characters[4] == 's') || (characters[4] == 'S')) 9003 & ((characters[5] == 'l') || (characters[5] == 'L')) 9004 & ((characters[6] == 'a') || (characters[6] == 'A')) 9005 & ((characters[7] == 't') || (characters[7] == 'T')) 9006 & ((characters[8] == 'e') || (characters[8] == 'E')) 9007 & (characters[9] == '3') 9008 & ((characters[10] == 'd') || (characters[10] == 'D')) 9009 & (characters[11] == '(')) { 9010 m_unit = CSSParser::FLength | CSSParser::FPercent; 9011 m_type = WebKitCSSTransformValue::Translate3DTransformOperation; 9012 m_argCount = 5; 9013 } 9014 break; 9015 } // end switch () 9016 } 9017 9018 WebKitCSSTransformValue::TransformOperationType type() const { return m_type; } 9019 unsigned argCount() const { return m_argCount; } 9020 CSSParser::Units unit() const { return m_unit; } 9021 9022 bool unknown() const { return m_type == WebKitCSSTransformValue::UnknownTransformOperation; } 9023 bool hasCorrectArgCount(unsigned argCount) { return m_argCount == argCount || (m_allowSingleArgument && argCount == 1); } 9024 9025private: 9026 WebKitCSSTransformValue::TransformOperationType m_type; 9027 unsigned m_argCount; 9028 bool m_allowSingleArgument; 9029 CSSParser::Units m_unit; 9030}; 9031 9032PassRefPtr<CSSValueList> CSSParser::parseTransform() 9033{ 9034 if (!m_valueList) 9035 return 0; 9036 9037 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9038 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9039 RefPtr<CSSValue> parsedTransformValue = parseTransformValue(value); 9040 if (!parsedTransformValue) 9041 return 0; 9042 9043 list->append(parsedTransformValue.release()); 9044 } 9045 9046 return list.release(); 9047} 9048 9049PassRefPtr<CSSValue> CSSParser::parseTransformValue(CSSParserValue *value) 9050{ 9051 if (value->unit != CSSParserValue::Function || !value->function) 9052 return 0; 9053 9054 // Every primitive requires at least one argument. 9055 CSSParserValueList* args = value->function->args.get(); 9056 if (!args) 9057 return 0; 9058 9059 // See if the specified primitive is one we understand. 9060 TransformOperationInfo info(value->function->name); 9061 if (info.unknown()) 9062 return 0; 9063 9064 if (!info.hasCorrectArgCount(args->size())) 9065 return 0; 9066 9067 // The transform is a list of functional primitives that specify transform operations. 9068 // We collect a list of WebKitCSSTransformValues, where each value specifies a single operation. 9069 9070 // Create the new WebKitCSSTransformValue for this operation and add it to our list. 9071 RefPtr<WebKitCSSTransformValue> transformValue = WebKitCSSTransformValue::create(info.type()); 9072 9073 // Snag our values. 9074 CSSParserValue* a = args->current(); 9075 unsigned argNumber = 0; 9076 while (a) { 9077 CSSParser::Units unit = info.unit(); 9078 9079 if (info.type() == WebKitCSSTransformValue::Rotate3DTransformOperation && argNumber == 3) { 9080 // 4th param of rotate3d() is an angle rather than a bare number, validate it as such 9081 if (!validUnit(a, FAngle, CSSStrictMode)) 9082 return 0; 9083 } else if (info.type() == WebKitCSSTransformValue::Translate3DTransformOperation && argNumber == 2) { 9084 // 3rd param of translate3d() cannot be a percentage 9085 if (!validUnit(a, FLength, CSSStrictMode)) 9086 return 0; 9087 } else if (info.type() == WebKitCSSTransformValue::TranslateZTransformOperation && !argNumber) { 9088 // 1st param of translateZ() cannot be a percentage 9089 if (!validUnit(a, FLength, CSSStrictMode)) 9090 return 0; 9091 } else if (info.type() == WebKitCSSTransformValue::PerspectiveTransformOperation && !argNumber) { 9092 // 1st param of perspective() must be a non-negative number (deprecated) or length. 9093 if (!validUnit(a, FNumber | FLength | FNonNeg, CSSStrictMode)) 9094 return 0; 9095 } else if (!validUnit(a, unit, CSSStrictMode)) 9096 return 0; 9097 9098 // Add the value to the current transform operation. 9099 transformValue->append(createPrimitiveNumericValue(a)); 9100 9101 a = args->next(); 9102 if (!a) 9103 break; 9104 if (a->unit != CSSParserValue::Operator || a->iValue != ',') 9105 return 0; 9106 a = args->next(); 9107 9108 argNumber++; 9109 } 9110 9111 return transformValue.release(); 9112} 9113 9114bool CSSParser::isBlendMode(CSSValueID valueID) 9115{ 9116 return (valueID >= CSSValueMultiply && valueID <= CSSValueLuminosity) 9117 || valueID == CSSValueNormal 9118 || valueID == CSSValueOverlay; 9119} 9120 9121bool CSSParser::isCompositeOperator(CSSValueID valueID) 9122{ 9123 // FIXME: Add CSSValueDestination and CSSValueLighter when the Compositing spec updates. 9124 return valueID >= CSSValueClear && valueID <= CSSValueXor; 9125} 9126 9127#if ENABLE(CSS_FILTERS) 9128 9129static void filterInfoForName(const CSSParserString& name, WebKitCSSFilterValue::FilterOperationType& filterType, unsigned& maximumArgumentCount) 9130{ 9131 if (equalIgnoringCase(name, "grayscale(")) 9132 filterType = WebKitCSSFilterValue::GrayscaleFilterOperation; 9133 else if (equalIgnoringCase(name, "sepia(")) 9134 filterType = WebKitCSSFilterValue::SepiaFilterOperation; 9135 else if (equalIgnoringCase(name, "saturate(")) 9136 filterType = WebKitCSSFilterValue::SaturateFilterOperation; 9137 else if (equalIgnoringCase(name, "hue-rotate(")) 9138 filterType = WebKitCSSFilterValue::HueRotateFilterOperation; 9139 else if (equalIgnoringCase(name, "invert(")) 9140 filterType = WebKitCSSFilterValue::InvertFilterOperation; 9141 else if (equalIgnoringCase(name, "opacity(")) 9142 filterType = WebKitCSSFilterValue::OpacityFilterOperation; 9143 else if (equalIgnoringCase(name, "brightness(")) 9144 filterType = WebKitCSSFilterValue::BrightnessFilterOperation; 9145 else if (equalIgnoringCase(name, "contrast(")) 9146 filterType = WebKitCSSFilterValue::ContrastFilterOperation; 9147 else if (equalIgnoringCase(name, "blur(")) 9148 filterType = WebKitCSSFilterValue::BlurFilterOperation; 9149 else if (equalIgnoringCase(name, "drop-shadow(")) { 9150 filterType = WebKitCSSFilterValue::DropShadowFilterOperation; 9151 maximumArgumentCount = 4; // x-offset, y-offset, blur-radius, color -- spread and inset style not allowed. 9152 } 9153} 9154 9155PassRefPtr<WebKitCSSFilterValue> CSSParser::parseBuiltinFilterArguments(CSSParserValueList* args, WebKitCSSFilterValue::FilterOperationType filterType) 9156{ 9157 RefPtr<WebKitCSSFilterValue> filterValue = WebKitCSSFilterValue::create(filterType); 9158 ASSERT(args); 9159 9160 switch (filterType) { 9161 case WebKitCSSFilterValue::GrayscaleFilterOperation: 9162 case WebKitCSSFilterValue::SepiaFilterOperation: 9163 case WebKitCSSFilterValue::SaturateFilterOperation: 9164 case WebKitCSSFilterValue::InvertFilterOperation: 9165 case WebKitCSSFilterValue::OpacityFilterOperation: 9166 case WebKitCSSFilterValue::ContrastFilterOperation: { 9167 // One optional argument, 0-1 or 0%-100%, if missing use 100%. 9168 if (args->size() > 1) 9169 return 0; 9170 9171 if (args->size()) { 9172 CSSParserValue* value = args->current(); 9173 if (!validUnit(value, FNumber | FPercent | FNonNeg, CSSStrictMode)) 9174 return 0; 9175 9176 double amount = value->fValue; 9177 9178 // Saturate and Contrast allow values over 100%. 9179 if (filterType != WebKitCSSFilterValue::SaturateFilterOperation 9180 && filterType != WebKitCSSFilterValue::ContrastFilterOperation) { 9181 double maxAllowed = value->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 100.0 : 1.0; 9182 if (amount > maxAllowed) 9183 return 0; 9184 } 9185 9186 filterValue->append(cssValuePool().createValue(amount, static_cast<CSSPrimitiveValue::UnitTypes>(value->unit))); 9187 } 9188 break; 9189 } 9190 case WebKitCSSFilterValue::BrightnessFilterOperation: { 9191 // One optional argument, if missing use 100%. 9192 if (args->size() > 1) 9193 return 0; 9194 9195 if (args->size()) { 9196 CSSParserValue* value = args->current(); 9197 if (!validUnit(value, FNumber | FPercent, CSSStrictMode)) 9198 return 0; 9199 9200 filterValue->append(cssValuePool().createValue(value->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(value->unit))); 9201 } 9202 break; 9203 } 9204 case WebKitCSSFilterValue::HueRotateFilterOperation: { 9205 // hue-rotate() takes one optional angle. 9206 if (args->size() > 1) 9207 return 0; 9208 9209 if (args->size()) { 9210 CSSParserValue* argument = args->current(); 9211 if (!validUnit(argument, FAngle, CSSStrictMode)) 9212 return 0; 9213 9214 filterValue->append(createPrimitiveNumericValue(argument)); 9215 } 9216 break; 9217 } 9218 case WebKitCSSFilterValue::BlurFilterOperation: { 9219 // Blur takes a single length. Zero parameters are allowed. 9220 if (args->size() > 1) 9221 return 0; 9222 9223 if (args->size()) { 9224 CSSParserValue* argument = args->current(); 9225 if (!validUnit(argument, FLength | FNonNeg, CSSStrictMode)) 9226 return 0; 9227 9228 filterValue->append(createPrimitiveNumericValue(argument)); 9229 } 9230 break; 9231 } 9232 case WebKitCSSFilterValue::DropShadowFilterOperation: { 9233 // drop-shadow() takes a single shadow. 9234 RefPtr<CSSValueList> shadowValueList = parseShadow(args, CSSPropertyWebkitFilter); 9235 if (!shadowValueList || shadowValueList->length() != 1) 9236 return 0; 9237 9238 filterValue->append((shadowValueList.release())->itemWithoutBoundsCheck(0)); 9239 break; 9240 } 9241 default: 9242 ASSERT_NOT_REACHED(); 9243 } 9244 return filterValue.release(); 9245} 9246 9247bool CSSParser::parseFilter(CSSParserValueList* valueList, RefPtr<CSSValue>& result) 9248{ 9249 if (!valueList) 9250 return false; 9251 9252 // The filter is a list of functional primitives that specify individual operations. 9253 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9254 for (auto value = valueList->current(); value; value = valueList->next()) { 9255 if (value->unit != CSSPrimitiveValue::CSS_URI && (value->unit != CSSParserValue::Function || !value->function)) 9256 return false; 9257 9258 WebKitCSSFilterValue::FilterOperationType filterType = WebKitCSSFilterValue::UnknownFilterOperation; 9259 9260 // See if the specified primitive is one we understand. 9261 if (value->unit == CSSPrimitiveValue::CSS_URI) { 9262 RefPtr<WebKitCSSFilterValue> referenceFilterValue = WebKitCSSFilterValue::create(WebKitCSSFilterValue::ReferenceFilterOperation); 9263 referenceFilterValue->append(CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_URI)); 9264 list->append(referenceFilterValue.release()); 9265 } else { 9266 const CSSParserString name = value->function->name; 9267 unsigned maximumArgumentCount = 1; 9268 9269 filterInfoForName(name, filterType, maximumArgumentCount); 9270 9271 if (filterType == WebKitCSSFilterValue::UnknownFilterOperation) 9272 return false; 9273 9274 CSSParserValueList* args = value->function->args.get(); 9275 if (!args) 9276 return false; 9277 9278 RefPtr<WebKitCSSFilterValue> filterValue = parseBuiltinFilterArguments(args, filterType); 9279 if (!filterValue) 9280 return false; 9281 9282 list->append(filterValue.release()); 9283 } 9284 } 9285 9286 result = list; 9287 9288 return true; 9289} 9290#endif 9291 9292#if ENABLE(CSS_REGIONS) 9293static bool validFlowName(const String& flowName) 9294{ 9295 return !(equalIgnoringCase(flowName, "auto") 9296 || equalIgnoringCase(flowName, "default") 9297 || equalIgnoringCase(flowName, "inherit") 9298 || equalIgnoringCase(flowName, "initial") 9299 || equalIgnoringCase(flowName, "none")); 9300} 9301#endif 9302 9303bool CSSParser::cssRegionsEnabled() const 9304{ 9305 return m_context.isCSSRegionsEnabled; 9306} 9307 9308bool CSSParser::cssCompositingEnabled() const 9309{ 9310 return m_context.isCSSCompositingEnabled; 9311} 9312 9313#if ENABLE(CSS_REGIONS) 9314 9315// none | <ident> 9316bool CSSParser::parseFlowThread(CSSPropertyID propId, bool important) 9317{ 9318 ASSERT(propId == CSSPropertyWebkitFlowInto); 9319 ASSERT(cssRegionsEnabled()); 9320 9321 if (m_valueList->size() != 1) 9322 return false; 9323 9324 CSSParserValue* value = m_valueList->current(); 9325 if (!value) 9326 return false; 9327 9328 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 9329 return false; 9330 9331 if (value->id == CSSValueNone) { 9332 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 9333 return true; 9334 } 9335 9336 String inputProperty = String(value->string); 9337 if (!inputProperty.isEmpty()) { 9338 if (!validFlowName(inputProperty)) 9339 return false; 9340 addProperty(propId, cssValuePool().createValue(inputProperty, CSSPrimitiveValue::CSS_STRING), important); 9341 } else 9342 addProperty(propId, cssValuePool().createIdentifierValue(CSSValueNone), important); 9343 9344 return true; 9345} 9346 9347// -webkit-flow-from: none | <ident> 9348bool CSSParser::parseRegionThread(CSSPropertyID propId, bool important) 9349{ 9350 ASSERT(propId == CSSPropertyWebkitFlowFrom); 9351 ASSERT(cssRegionsEnabled()); 9352 9353 if (m_valueList->size() != 1) 9354 return false; 9355 9356 CSSParserValue* value = m_valueList->current(); 9357 if (!value) 9358 return false; 9359 9360 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 9361 return false; 9362 9363 if (value->id == CSSValueNone) 9364 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 9365 else { 9366 String inputProperty = String(value->string); 9367 if (!inputProperty.isEmpty()) { 9368 if (!validFlowName(inputProperty)) 9369 return false; 9370 addProperty(propId, cssValuePool().createValue(inputProperty, CSSPrimitiveValue::CSS_STRING), important); 9371 } else 9372 addProperty(propId, cssValuePool().createIdentifierValue(CSSValueNone), important); 9373 } 9374 9375 return true; 9376} 9377#endif 9378 9379bool CSSParser::parseTransformOrigin(CSSPropertyID propId, CSSPropertyID& propId1, CSSPropertyID& propId2, CSSPropertyID& propId3, RefPtr<CSSValue>& value, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3) 9380{ 9381 propId1 = propId; 9382 propId2 = propId; 9383 propId3 = propId; 9384 if (propId == CSSPropertyWebkitTransformOrigin) { 9385 propId1 = CSSPropertyWebkitTransformOriginX; 9386 propId2 = CSSPropertyWebkitTransformOriginY; 9387 propId3 = CSSPropertyWebkitTransformOriginZ; 9388 } 9389 9390 switch (propId) { 9391 case CSSPropertyWebkitTransformOrigin: 9392 if (!parseTransformOriginShorthand(value, value2, value3)) 9393 return false; 9394 // parseTransformOriginShorthand advances the m_valueList pointer 9395 break; 9396 case CSSPropertyWebkitTransformOriginX: { 9397 value = parseFillPositionX(m_valueList.get()); 9398 if (value) 9399 m_valueList->next(); 9400 break; 9401 } 9402 case CSSPropertyWebkitTransformOriginY: { 9403 value = parseFillPositionY(m_valueList.get()); 9404 if (value) 9405 m_valueList->next(); 9406 break; 9407 } 9408 case CSSPropertyWebkitTransformOriginZ: { 9409 if (validUnit(m_valueList->current(), FLength)) 9410 value = createPrimitiveNumericValue(m_valueList->current()); 9411 if (value) 9412 m_valueList->next(); 9413 break; 9414 } 9415 default: 9416 ASSERT_NOT_REACHED(); 9417 return false; 9418 } 9419 9420 return value; 9421} 9422 9423bool CSSParser::parsePerspectiveOrigin(CSSPropertyID propId, CSSPropertyID& propId1, CSSPropertyID& propId2, RefPtr<CSSValue>& value, RefPtr<CSSValue>& value2) 9424{ 9425 propId1 = propId; 9426 propId2 = propId; 9427 if (propId == CSSPropertyWebkitPerspectiveOrigin) { 9428 propId1 = CSSPropertyWebkitPerspectiveOriginX; 9429 propId2 = CSSPropertyWebkitPerspectiveOriginY; 9430 } 9431 9432 switch (propId) { 9433 case CSSPropertyWebkitPerspectiveOrigin: 9434 if (m_valueList->size() > 2) 9435 return false; 9436 parse2ValuesFillPosition(m_valueList.get(), value, value2); 9437 break; 9438 case CSSPropertyWebkitPerspectiveOriginX: { 9439 value = parseFillPositionX(m_valueList.get()); 9440 if (value) 9441 m_valueList->next(); 9442 break; 9443 } 9444 case CSSPropertyWebkitPerspectiveOriginY: { 9445 value = parseFillPositionY(m_valueList.get()); 9446 if (value) 9447 m_valueList->next(); 9448 break; 9449 } 9450 default: 9451 ASSERT_NOT_REACHED(); 9452 return false; 9453 } 9454 9455 return value; 9456} 9457 9458void CSSParser::addTextDecorationProperty(CSSPropertyID propId, PassRefPtr<CSSValue> value, bool important) 9459{ 9460 // The text-decoration-line property takes priority over text-decoration, unless the latter has important priority set. 9461 if (propId == CSSPropertyTextDecoration && !important && !inShorthand()) { 9462 for (unsigned i = 0; i < m_parsedProperties.size(); ++i) { 9463 if (m_parsedProperties[i].id() == CSSPropertyWebkitTextDecorationLine) 9464 return; 9465 } 9466 } 9467 addProperty(propId, value, important); 9468} 9469 9470bool CSSParser::parseTextDecoration(CSSPropertyID propId, bool important) 9471{ 9472 CSSParserValue* value = m_valueList->current(); 9473 if (value && value->id == CSSValueNone) { 9474 addTextDecorationProperty(propId, cssValuePool().createIdentifierValue(CSSValueNone), important); 9475 m_valueList->next(); 9476 return true; 9477 } 9478 9479 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9480 bool isValid = true; 9481 while (isValid && value) { 9482 switch (value->id) { 9483 case CSSValueBlink: 9484 case CSSValueLineThrough: 9485 case CSSValueOverline: 9486 case CSSValueUnderline: 9487#if ENABLE(LETTERPRESS) 9488 case CSSValueWebkitLetterpress: 9489#endif 9490 list->append(cssValuePool().createIdentifierValue(value->id)); 9491 break; 9492 default: 9493 isValid = false; 9494 break; 9495 } 9496 if (isValid) 9497 value = m_valueList->next(); 9498 } 9499 9500 // Values are either valid or in shorthand scope. 9501 if (list->length() && (isValid || inShorthand())) { 9502 addTextDecorationProperty(propId, list.release(), important); 9503 return true; 9504 } 9505 9506 return false; 9507} 9508 9509bool CSSParser::parseTextDecorationSkip(bool important) 9510{ 9511 // The text-decoration-skip property has syntax "none | [ objects || spaces || ink || edges || box-decoration ]". 9512 // However, only 'none' and 'ink' are implemented yet, so we will parse syntax "none | ink" for now. 9513 CSSParserValue* value = m_valueList->current(); 9514 do { 9515 switch (value->id) { 9516 case CSSValueNone: 9517 case CSSValueAuto: 9518 case CSSValueInk: 9519 case CSSValueObjects: 9520 addProperty(CSSPropertyWebkitTextDecorationSkip, cssValuePool().createIdentifierValue(value->id), important); 9521 return true; 9522 default: 9523 break; 9524 } 9525 } while ((value = m_valueList->next())); 9526 return false; 9527} 9528 9529bool CSSParser::parseTextUnderlinePosition(bool important) 9530{ 9531 // The text-underline-position property has sintax "auto | alphabetic | [ under || [ left | right ] ]". 9532 // However, values 'left' and 'right' are not implemented yet, so we will parse sintax 9533 // "auto | alphabetic | under" for now. 9534 CSSParserValue* value = m_valueList->current(); 9535 switch (value->id) { 9536 case CSSValueAuto: 9537 case CSSValueAlphabetic: 9538 case CSSValueUnder: 9539 if (m_valueList->next()) 9540 return false; 9541 9542 addProperty(CSSPropertyWebkitTextUnderlinePosition, cssValuePool().createIdentifierValue(value->id), important); 9543 return true; 9544 default: 9545 break; 9546 } 9547 return false; 9548} 9549 9550bool CSSParser::parseTextEmphasisStyle(bool important) 9551{ 9552 unsigned valueListSize = m_valueList->size(); 9553 9554 RefPtr<CSSPrimitiveValue> fill; 9555 RefPtr<CSSPrimitiveValue> shape; 9556 9557 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9558 if (value->unit == CSSPrimitiveValue::CSS_STRING) { 9559 if (fill || shape || (valueListSize != 1 && !inShorthand())) 9560 return false; 9561 addProperty(CSSPropertyWebkitTextEmphasisStyle, createPrimitiveStringValue(value), important); 9562 m_valueList->next(); 9563 return true; 9564 } 9565 9566 if (value->id == CSSValueNone) { 9567 if (fill || shape || (valueListSize != 1 && !inShorthand())) 9568 return false; 9569 addProperty(CSSPropertyWebkitTextEmphasisStyle, cssValuePool().createIdentifierValue(CSSValueNone), important); 9570 m_valueList->next(); 9571 return true; 9572 } 9573 9574 if (value->id == CSSValueOpen || value->id == CSSValueFilled) { 9575 if (fill) 9576 return false; 9577 fill = cssValuePool().createIdentifierValue(value->id); 9578 } else if (value->id == CSSValueDot || value->id == CSSValueCircle || value->id == CSSValueDoubleCircle || value->id == CSSValueTriangle || value->id == CSSValueSesame) { 9579 if (shape) 9580 return false; 9581 shape = cssValuePool().createIdentifierValue(value->id); 9582 } else if (!inShorthand()) 9583 return false; 9584 else 9585 break; 9586 } 9587 9588 if (fill && shape) { 9589 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated(); 9590 parsedValues->append(fill.release()); 9591 parsedValues->append(shape.release()); 9592 addProperty(CSSPropertyWebkitTextEmphasisStyle, parsedValues.release(), important); 9593 return true; 9594 } 9595 if (fill) { 9596 addProperty(CSSPropertyWebkitTextEmphasisStyle, fill.release(), important); 9597 return true; 9598 } 9599 if (shape) { 9600 addProperty(CSSPropertyWebkitTextEmphasisStyle, shape.release(), important); 9601 return true; 9602 } 9603 9604 return false; 9605} 9606 9607bool CSSParser::parseTextEmphasisPosition(bool important) 9608{ 9609 bool foundOverOrUnder = false; 9610 CSSValueID overUnderValueID = CSSValueOver; 9611 bool foundLeftOrRight = false; 9612 CSSValueID leftRightValueID = CSSValueRight; 9613 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9614 switch (value->id) { 9615 case CSSValueOver: 9616 if (foundOverOrUnder) 9617 return false; 9618 foundOverOrUnder = true; 9619 overUnderValueID = CSSValueOver; 9620 break; 9621 case CSSValueUnder: 9622 if (foundOverOrUnder) 9623 return false; 9624 foundOverOrUnder = true; 9625 overUnderValueID = CSSValueUnder; 9626 break; 9627 case CSSValueLeft: 9628 if (foundLeftOrRight) 9629 return false; 9630 foundLeftOrRight = true; 9631 leftRightValueID = CSSValueLeft; 9632 break; 9633 case CSSValueRight: 9634 if (foundLeftOrRight) 9635 return false; 9636 foundLeftOrRight = true; 9637 leftRightValueID = CSSValueRight; 9638 break; 9639 default: 9640 return false; 9641 } 9642 } 9643 if (!foundOverOrUnder) 9644 return false; 9645 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9646 list->append(cssValuePool().createIdentifierValue(overUnderValueID)); 9647 if (foundLeftOrRight) 9648 list->append(cssValuePool().createIdentifierValue(leftRightValueID)); 9649 addProperty(CSSPropertyWebkitTextEmphasisPosition, list, important); 9650 return true; 9651} 9652 9653PassRefPtr<CSSValue> CSSParser::parseTextIndent() 9654{ 9655 // <length> | <percentage> | inherit when CSS3_TEXT is disabled. 9656 // [ <length> | <percentage> ] && [ -webkit-hanging || -webkit-each-line ]? | inherit when CSS3_TEXT is enabled. 9657 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9658 bool hasLengthOrPercentage = false; 9659#if ENABLE(CSS3_TEXT) 9660 bool hasEachLine = false; 9661 bool hasHanging = false; 9662#endif 9663 9664 CSSParserValue* value = m_valueList->current(); 9665 while (value) { 9666 if (!hasLengthOrPercentage && validUnit(value, FLength | FPercent)) { 9667 list->append(createPrimitiveNumericValue(value)); 9668 hasLengthOrPercentage = true; 9669 } 9670#if ENABLE(CSS3_TEXT) 9671 else if (!hasEachLine && value->id == CSSValueWebkitEachLine) { 9672 list->append(cssValuePool().createIdentifierValue(CSSValueWebkitEachLine)); 9673 hasEachLine = true; 9674 } else if (!hasHanging && value->id == CSSValueWebkitHanging) { 9675 list->append(cssValuePool().createIdentifierValue(CSSValueWebkitHanging)); 9676 hasHanging = true; 9677 } 9678#endif 9679 else 9680 return 0; 9681 9682 value = m_valueList->next(); 9683 } 9684 9685 if (!hasLengthOrPercentage) 9686 return 0; 9687 9688 return list.release(); 9689} 9690 9691bool CSSParser::parseLineBoxContain(bool important) 9692{ 9693 LineBoxContain lineBoxContain = LineBoxContainNone; 9694 9695 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9696 if (value->id == CSSValueBlock) { 9697 if (lineBoxContain & LineBoxContainBlock) 9698 return false; 9699 lineBoxContain |= LineBoxContainBlock; 9700 } else if (value->id == CSSValueInline) { 9701 if (lineBoxContain & LineBoxContainInline) 9702 return false; 9703 lineBoxContain |= LineBoxContainInline; 9704 } else if (value->id == CSSValueFont) { 9705 if (lineBoxContain & LineBoxContainFont) 9706 return false; 9707 lineBoxContain |= LineBoxContainFont; 9708 } else if (value->id == CSSValueGlyphs) { 9709 if (lineBoxContain & LineBoxContainGlyphs) 9710 return false; 9711 lineBoxContain |= LineBoxContainGlyphs; 9712 } else if (value->id == CSSValueReplaced) { 9713 if (lineBoxContain & LineBoxContainReplaced) 9714 return false; 9715 lineBoxContain |= LineBoxContainReplaced; 9716 } else if (value->id == CSSValueInlineBox) { 9717 if (lineBoxContain & LineBoxContainInlineBox) 9718 return false; 9719 lineBoxContain |= LineBoxContainInlineBox; 9720 } else 9721 return false; 9722 } 9723 9724 if (!lineBoxContain) 9725 return false; 9726 9727 addProperty(CSSPropertyWebkitLineBoxContain, CSSLineBoxContainValue::create(lineBoxContain), important); 9728 return true; 9729} 9730 9731bool CSSParser::parseFontFeatureTag(CSSValueList* settings) 9732{ 9733 // Feature tag name consists of 4-letter characters. 9734 static const unsigned tagNameLength = 4; 9735 9736 CSSParserValue* value = m_valueList->current(); 9737 // Feature tag name comes first 9738 if (value->unit != CSSPrimitiveValue::CSS_STRING) 9739 return false; 9740 if (value->string.length() != tagNameLength) 9741 return false; 9742 for (unsigned i = 0; i < tagNameLength; ++i) { 9743 // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification. 9744 UChar character = value->string[i]; 9745 if (character < 0x20 || character > 0x7E) 9746 return false; 9747 } 9748 9749 String tag = value->string; 9750 int tagValue = 1; 9751 // Feature tag values could follow: <integer> | on | off 9752 value = m_valueList->next(); 9753 if (value) { 9754 if (value->unit == CSSPrimitiveValue::CSS_NUMBER && value->isInt && value->fValue >= 0) { 9755 tagValue = clampToInteger(value->fValue); 9756 if (tagValue < 0) 9757 return false; 9758 m_valueList->next(); 9759 } else if (value->id == CSSValueOn || value->id == CSSValueOff) { 9760 tagValue = value->id == CSSValueOn; 9761 m_valueList->next(); 9762 } 9763 } 9764 settings->append(CSSFontFeatureValue::create(tag, tagValue)); 9765 return true; 9766} 9767 9768bool CSSParser::parseFontFeatureSettings(bool important) 9769{ 9770 if (m_valueList->size() == 1 && m_valueList->current()->id == CSSValueNormal) { 9771 RefPtr<CSSPrimitiveValue> normalValue = cssValuePool().createIdentifierValue(CSSValueNormal); 9772 m_valueList->next(); 9773 addProperty(CSSPropertyWebkitFontFeatureSettings, normalValue.release(), important); 9774 return true; 9775 } 9776 9777 RefPtr<CSSValueList> settings = CSSValueList::createCommaSeparated(); 9778 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9779 if (!parseFontFeatureTag(settings.get())) 9780 return false; 9781 9782 // If the list isn't parsed fully, the current value should be comma. 9783 value = m_valueList->current(); 9784 if (value && !isComma(value)) 9785 return false; 9786 } 9787 if (settings->length()) { 9788 addProperty(CSSPropertyWebkitFontFeatureSettings, settings.release(), important); 9789 return true; 9790 } 9791 return false; 9792} 9793 9794bool CSSParser::parseFontVariantLigatures(bool important) 9795{ 9796 RefPtr<CSSValueList> ligatureValues = CSSValueList::createSpaceSeparated(); 9797 bool sawCommonLigaturesValue = false; 9798 bool sawDiscretionaryLigaturesValue = false; 9799 bool sawHistoricalLigaturesValue = false; 9800 9801 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9802 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 9803 return false; 9804 9805 switch (value->id) { 9806 case CSSValueNoCommonLigatures: 9807 case CSSValueCommonLigatures: 9808 if (sawCommonLigaturesValue) 9809 return false; 9810 sawCommonLigaturesValue = true; 9811 ligatureValues->append(cssValuePool().createIdentifierValue(value->id)); 9812 break; 9813 case CSSValueNoDiscretionaryLigatures: 9814 case CSSValueDiscretionaryLigatures: 9815 if (sawDiscretionaryLigaturesValue) 9816 return false; 9817 sawDiscretionaryLigaturesValue = true; 9818 ligatureValues->append(cssValuePool().createIdentifierValue(value->id)); 9819 break; 9820 case CSSValueNoHistoricalLigatures: 9821 case CSSValueHistoricalLigatures: 9822 if (sawHistoricalLigaturesValue) 9823 return false; 9824 sawHistoricalLigaturesValue = true; 9825 ligatureValues->append(cssValuePool().createIdentifierValue(value->id)); 9826 break; 9827 default: 9828 return false; 9829 } 9830 } 9831 9832 if (!ligatureValues->length()) 9833 return false; 9834 9835 addProperty(CSSPropertyWebkitFontVariantLigatures, ligatureValues.release(), important); 9836 return true; 9837} 9838 9839bool CSSParser::parseCalculation(CSSParserValue* value, CalculationPermittedValueRange range) 9840{ 9841 ASSERT(isCalculation(value)); 9842 9843 CSSParserValueList* args = value->function->args.get(); 9844 if (!args || !args->size()) 9845 return false; 9846 9847 ASSERT(!m_parsedCalculation); 9848 m_parsedCalculation = CSSCalcValue::create(value->function->name, *args, range); 9849 9850 if (!m_parsedCalculation) 9851 return false; 9852 9853 return true; 9854} 9855 9856#define END_TOKEN 0 9857 9858#include "CSSGrammar.h" 9859 9860enum CharacterType { 9861 // Types for the main switch. 9862 9863 // The first 4 types must be grouped together, as they 9864 // represent the allowed chars in an identifier. 9865 CharacterCaselessU, 9866 CharacterIdentifierStart, 9867 CharacterNumber, 9868 CharacterDash, 9869 9870 CharacterOther, 9871 CharacterNull, 9872 CharacterWhiteSpace, 9873 CharacterEndMediaQuery, 9874 CharacterEndNthChild, 9875 CharacterQuote, 9876 CharacterExclamationMark, 9877 CharacterHashmark, 9878 CharacterDollar, 9879 CharacterAsterisk, 9880 CharacterPlus, 9881 CharacterDot, 9882 CharacterSlash, 9883 CharacterLess, 9884 CharacterAt, 9885 CharacterBackSlash, 9886 CharacterXor, 9887 CharacterVerticalBar, 9888 CharacterTilde, 9889}; 9890 9891// 128 ASCII codes 9892static const CharacterType typesOfASCIICharacters[128] = { 9893/* 0 - Null */ CharacterNull, 9894/* 1 - Start of Heading */ CharacterOther, 9895/* 2 - Start of Text */ CharacterOther, 9896/* 3 - End of Text */ CharacterOther, 9897/* 4 - End of Transm. */ CharacterOther, 9898/* 5 - Enquiry */ CharacterOther, 9899/* 6 - Acknowledgment */ CharacterOther, 9900/* 7 - Bell */ CharacterOther, 9901/* 8 - Back Space */ CharacterOther, 9902/* 9 - Horizontal Tab */ CharacterWhiteSpace, 9903/* 10 - Line Feed */ CharacterWhiteSpace, 9904/* 11 - Vertical Tab */ CharacterOther, 9905/* 12 - Form Feed */ CharacterWhiteSpace, 9906/* 13 - Carriage Return */ CharacterWhiteSpace, 9907/* 14 - Shift Out */ CharacterOther, 9908/* 15 - Shift In */ CharacterOther, 9909/* 16 - Data Line Escape */ CharacterOther, 9910/* 17 - Device Control 1 */ CharacterOther, 9911/* 18 - Device Control 2 */ CharacterOther, 9912/* 19 - Device Control 3 */ CharacterOther, 9913/* 20 - Device Control 4 */ CharacterOther, 9914/* 21 - Negative Ack. */ CharacterOther, 9915/* 22 - Synchronous Idle */ CharacterOther, 9916/* 23 - End of Transmit */ CharacterOther, 9917/* 24 - Cancel */ CharacterOther, 9918/* 25 - End of Medium */ CharacterOther, 9919/* 26 - Substitute */ CharacterOther, 9920/* 27 - Escape */ CharacterOther, 9921/* 28 - File Separator */ CharacterOther, 9922/* 29 - Group Separator */ CharacterOther, 9923/* 30 - Record Separator */ CharacterOther, 9924/* 31 - Unit Separator */ CharacterOther, 9925/* 32 - Space */ CharacterWhiteSpace, 9926/* 33 - ! */ CharacterExclamationMark, 9927/* 34 - " */ CharacterQuote, 9928/* 35 - # */ CharacterHashmark, 9929/* 36 - $ */ CharacterDollar, 9930/* 37 - % */ CharacterOther, 9931/* 38 - & */ CharacterOther, 9932/* 39 - ' */ CharacterQuote, 9933/* 40 - ( */ CharacterOther, 9934/* 41 - ) */ CharacterEndNthChild, 9935/* 42 - * */ CharacterAsterisk, 9936/* 43 - + */ CharacterPlus, 9937/* 44 - , */ CharacterOther, 9938/* 45 - - */ CharacterDash, 9939/* 46 - . */ CharacterDot, 9940/* 47 - / */ CharacterSlash, 9941/* 48 - 0 */ CharacterNumber, 9942/* 49 - 1 */ CharacterNumber, 9943/* 50 - 2 */ CharacterNumber, 9944/* 51 - 3 */ CharacterNumber, 9945/* 52 - 4 */ CharacterNumber, 9946/* 53 - 5 */ CharacterNumber, 9947/* 54 - 6 */ CharacterNumber, 9948/* 55 - 7 */ CharacterNumber, 9949/* 56 - 8 */ CharacterNumber, 9950/* 57 - 9 */ CharacterNumber, 9951/* 58 - : */ CharacterOther, 9952/* 59 - ; */ CharacterEndMediaQuery, 9953/* 60 - < */ CharacterLess, 9954/* 61 - = */ CharacterOther, 9955/* 62 - > */ CharacterOther, 9956/* 63 - ? */ CharacterOther, 9957/* 64 - @ */ CharacterAt, 9958/* 65 - A */ CharacterIdentifierStart, 9959/* 66 - B */ CharacterIdentifierStart, 9960/* 67 - C */ CharacterIdentifierStart, 9961/* 68 - D */ CharacterIdentifierStart, 9962/* 69 - E */ CharacterIdentifierStart, 9963/* 70 - F */ CharacterIdentifierStart, 9964/* 71 - G */ CharacterIdentifierStart, 9965/* 72 - H */ CharacterIdentifierStart, 9966/* 73 - I */ CharacterIdentifierStart, 9967/* 74 - J */ CharacterIdentifierStart, 9968/* 75 - K */ CharacterIdentifierStart, 9969/* 76 - L */ CharacterIdentifierStart, 9970/* 77 - M */ CharacterIdentifierStart, 9971/* 78 - N */ CharacterIdentifierStart, 9972/* 79 - O */ CharacterIdentifierStart, 9973/* 80 - P */ CharacterIdentifierStart, 9974/* 81 - Q */ CharacterIdentifierStart, 9975/* 82 - R */ CharacterIdentifierStart, 9976/* 83 - S */ CharacterIdentifierStart, 9977/* 84 - T */ CharacterIdentifierStart, 9978/* 85 - U */ CharacterCaselessU, 9979/* 86 - V */ CharacterIdentifierStart, 9980/* 87 - W */ CharacterIdentifierStart, 9981/* 88 - X */ CharacterIdentifierStart, 9982/* 89 - Y */ CharacterIdentifierStart, 9983/* 90 - Z */ CharacterIdentifierStart, 9984/* 91 - [ */ CharacterOther, 9985/* 92 - \ */ CharacterBackSlash, 9986/* 93 - ] */ CharacterOther, 9987/* 94 - ^ */ CharacterXor, 9988/* 95 - _ */ CharacterIdentifierStart, 9989/* 96 - ` */ CharacterOther, 9990/* 97 - a */ CharacterIdentifierStart, 9991/* 98 - b */ CharacterIdentifierStart, 9992/* 99 - c */ CharacterIdentifierStart, 9993/* 100 - d */ CharacterIdentifierStart, 9994/* 101 - e */ CharacterIdentifierStart, 9995/* 102 - f */ CharacterIdentifierStart, 9996/* 103 - g */ CharacterIdentifierStart, 9997/* 104 - h */ CharacterIdentifierStart, 9998/* 105 - i */ CharacterIdentifierStart, 9999/* 106 - j */ CharacterIdentifierStart, 10000/* 107 - k */ CharacterIdentifierStart, 10001/* 108 - l */ CharacterIdentifierStart, 10002/* 109 - m */ CharacterIdentifierStart, 10003/* 110 - n */ CharacterIdentifierStart, 10004/* 111 - o */ CharacterIdentifierStart, 10005/* 112 - p */ CharacterIdentifierStart, 10006/* 113 - q */ CharacterIdentifierStart, 10007/* 114 - r */ CharacterIdentifierStart, 10008/* 115 - s */ CharacterIdentifierStart, 10009/* 116 - t */ CharacterIdentifierStart, 10010/* 117 - u */ CharacterCaselessU, 10011/* 118 - v */ CharacterIdentifierStart, 10012/* 119 - w */ CharacterIdentifierStart, 10013/* 120 - x */ CharacterIdentifierStart, 10014/* 121 - y */ CharacterIdentifierStart, 10015/* 122 - z */ CharacterIdentifierStart, 10016/* 123 - { */ CharacterEndMediaQuery, 10017/* 124 - | */ CharacterVerticalBar, 10018/* 125 - } */ CharacterOther, 10019/* 126 - ~ */ CharacterTilde, 10020/* 127 - Delete */ CharacterOther, 10021}; 10022 10023// Utility functions for the CSS tokenizer. 10024 10025template <typename CharacterType> 10026static inline bool isCSSLetter(CharacterType character) 10027{ 10028 return character >= 128 || typesOfASCIICharacters[character] <= CharacterDash; 10029} 10030 10031template <typename CharacterType> 10032static inline bool isCSSEscape(CharacterType character) 10033{ 10034 return character >= ' ' && character != 127; 10035} 10036 10037template <typename CharacterType> 10038static inline bool isURILetter(CharacterType character) 10039{ 10040 return (character >= '*' && character != 127) || (character >= '#' && character <= '&') || character == '!'; 10041} 10042 10043template <typename CharacterType> 10044static inline bool isIdentifierStartAfterDash(CharacterType* currentCharacter) 10045{ 10046 return isASCIIAlpha(currentCharacter[0]) || currentCharacter[0] == '_' || currentCharacter[0] >= 128 10047 || (currentCharacter[0] == '\\' && isCSSEscape(currentCharacter[1])); 10048} 10049 10050template <typename CharacterType> 10051static inline bool isEqualToCSSIdentifier(CharacterType* cssString, const char* constantString) 10052{ 10053 // Compare an character memory data with a zero terminated string. 10054 do { 10055 // The input must be part of an identifier if constantChar or constString 10056 // contains '-'. Otherwise toASCIILowerUnchecked('\r') would be equal to '-'. 10057 ASSERT((*constantString >= 'a' && *constantString <= 'z') || *constantString == '-'); 10058 ASSERT(*constantString != '-' || isCSSLetter(*cssString)); 10059 if (toASCIILowerUnchecked(*cssString++) != (*constantString++)) 10060 return false; 10061 } while (*constantString); 10062 return true; 10063} 10064 10065template <typename CharacterType> 10066static inline bool isEqualToCSSCaseSensitiveIdentifier(CharacterType* string, const char* constantString) 10067{ 10068 do { 10069 if (*string++ != *constantString++) 10070 return false; 10071 } while (*constantString); 10072 return true; 10073} 10074 10075template <typename CharacterType> 10076static CharacterType* checkAndSkipEscape(CharacterType* currentCharacter) 10077{ 10078 // Returns with 0, if escape check is failed. Otherwise 10079 // it returns with the following character. 10080 ASSERT(*currentCharacter == '\\'); 10081 10082 ++currentCharacter; 10083 if (!isCSSEscape(*currentCharacter)) 10084 return 0; 10085 10086 if (isASCIIHexDigit(*currentCharacter)) { 10087 int length = 6; 10088 10089 do { 10090 ++currentCharacter; 10091 } while (isASCIIHexDigit(*currentCharacter) && --length); 10092 10093 // Optional space after the escape sequence. 10094 if (isHTMLSpace(*currentCharacter)) 10095 ++currentCharacter; 10096 return currentCharacter; 10097 } 10098 return currentCharacter + 1; 10099} 10100 10101template <typename CharacterType> 10102static inline CharacterType* skipWhiteSpace(CharacterType* currentCharacter) 10103{ 10104 while (isHTMLSpace(*currentCharacter)) 10105 ++currentCharacter; 10106 return currentCharacter; 10107} 10108 10109// Main CSS tokenizer functions. 10110 10111template <> 10112LChar* CSSParserString::characters<LChar>() const { return characters8(); } 10113 10114template <> 10115UChar* CSSParserString::characters<UChar>() const { return characters16(); } 10116 10117template <> 10118inline LChar*& CSSParser::currentCharacter<LChar>() 10119{ 10120 return m_currentCharacter8; 10121} 10122 10123template <> 10124inline UChar*& CSSParser::currentCharacter<UChar>() 10125{ 10126 return m_currentCharacter16; 10127} 10128 10129UChar*& CSSParser::currentCharacter16() 10130{ 10131 if (!m_currentCharacter16) { 10132 m_dataStart16 = std::make_unique<UChar[]>(m_length); 10133 m_currentCharacter16 = m_dataStart16.get(); 10134 } 10135 10136 return m_currentCharacter16; 10137} 10138 10139template <> 10140inline LChar* CSSParser::tokenStart<LChar>() 10141{ 10142 return m_tokenStart.ptr8; 10143} 10144 10145template <> 10146inline UChar* CSSParser::tokenStart<UChar>() 10147{ 10148 return m_tokenStart.ptr16; 10149} 10150 10151CSSParser::Location CSSParser::currentLocation() 10152{ 10153 Location location; 10154 location.lineNumber = m_tokenStartLineNumber; 10155 if (is8BitSource()) 10156 location.token.init(tokenStart<LChar>(), currentCharacter<LChar>() - tokenStart<LChar>()); 10157 else 10158 location.token.init(tokenStart<UChar>(), currentCharacter<UChar>() - tokenStart<UChar>()); 10159 return location; 10160} 10161 10162template <typename CharacterType> 10163inline bool CSSParser::isIdentifierStart() 10164{ 10165 // Check whether an identifier is started. 10166 return isIdentifierStartAfterDash((*currentCharacter<CharacterType>() != '-') ? currentCharacter<CharacterType>() : currentCharacter<CharacterType>() + 1); 10167} 10168 10169template <typename CharacterType> 10170static inline CharacterType* checkAndSkipString(CharacterType* currentCharacter, int quote) 10171{ 10172 // Returns with 0, if string check is failed. Otherwise 10173 // it returns with the following character. This is necessary 10174 // since we cannot revert escape sequences, thus strings 10175 // must be validated before parsing. 10176 while (true) { 10177 if (UNLIKELY(*currentCharacter == quote)) { 10178 // String parsing is successful. 10179 return currentCharacter + 1; 10180 } 10181 if (UNLIKELY(!*currentCharacter)) { 10182 // String parsing is successful up to end of input. 10183 return currentCharacter; 10184 } 10185 if (UNLIKELY(*currentCharacter <= '\r' && (*currentCharacter == '\n' || (*currentCharacter | 0x1) == '\r'))) { 10186 // String parsing is failed for character '\n', '\f' or '\r'. 10187 return 0; 10188 } 10189 10190 if (LIKELY(currentCharacter[0] != '\\')) 10191 ++currentCharacter; 10192 else if (currentCharacter[1] == '\n' || currentCharacter[1] == '\f') 10193 currentCharacter += 2; 10194 else if (currentCharacter[1] == '\r') 10195 currentCharacter += currentCharacter[2] == '\n' ? 3 : 2; 10196 else { 10197 currentCharacter = checkAndSkipEscape(currentCharacter); 10198 if (!currentCharacter) 10199 return 0; 10200 } 10201 } 10202} 10203 10204template <typename CharacterType> 10205unsigned CSSParser::parseEscape(CharacterType*& src) 10206{ 10207 ASSERT(*src == '\\' && isCSSEscape(src[1])); 10208 10209 unsigned unicode = 0; 10210 10211 ++src; 10212 if (isASCIIHexDigit(*src)) { 10213 10214 int length = 6; 10215 10216 do { 10217 unicode = (unicode << 4) + toASCIIHexValue(*src++); 10218 } while (--length && isASCIIHexDigit(*src)); 10219 10220 // Characters above 0x10ffff are not handled. 10221 if (unicode > 0x10ffff) 10222 unicode = 0xfffd; 10223 10224 // Optional space after the escape sequence. 10225 if (isHTMLSpace(*src)) 10226 ++src; 10227 10228 return unicode; 10229 } 10230 10231 return *currentCharacter<CharacterType>()++; 10232} 10233 10234template <> 10235inline void CSSParser::UnicodeToChars<LChar>(LChar*& result, unsigned unicode) 10236{ 10237 ASSERT(unicode <= 0xff); 10238 *result = unicode; 10239 10240 ++result; 10241} 10242 10243template <> 10244inline void CSSParser::UnicodeToChars<UChar>(UChar*& result, unsigned unicode) 10245{ 10246 // Replace unicode with a surrogate pairs when it is bigger than 0xffff 10247 if (U16_LENGTH(unicode) == 2) { 10248 *result++ = U16_LEAD(unicode); 10249 *result = U16_TRAIL(unicode); 10250 } else 10251 *result = unicode; 10252 10253 ++result; 10254} 10255 10256template <typename SrcCharacterType, typename DestCharacterType> 10257inline bool CSSParser::parseIdentifierInternal(SrcCharacterType*& src, DestCharacterType*& result, bool& hasEscape) 10258{ 10259 hasEscape = false; 10260 do { 10261 if (LIKELY(*src != '\\')) 10262 *result++ = *src++; 10263 else { 10264 hasEscape = true; 10265 SrcCharacterType* savedEscapeStart = src; 10266 unsigned unicode = parseEscape<SrcCharacterType>(src); 10267 if (unicode > 0xff && sizeof(DestCharacterType) == 1) { 10268 src = savedEscapeStart; 10269 return false; 10270 } 10271 UnicodeToChars(result, unicode); 10272 } 10273 } while (isCSSLetter(src[0]) || (src[0] == '\\' && isCSSEscape(src[1]))); 10274 10275 return true; 10276} 10277 10278template <typename CharacterType> 10279inline void CSSParser::parseIdentifier(CharacterType*& result, CSSParserString& resultString, bool& hasEscape) 10280{ 10281 // If a valid identifier start is found, we can safely 10282 // parse the identifier until the next invalid character. 10283 ASSERT(isIdentifierStart<CharacterType>()); 10284 10285 CharacterType* start = currentCharacter<CharacterType>(); 10286 if (UNLIKELY(!parseIdentifierInternal(currentCharacter<CharacterType>(), result, hasEscape))) { 10287 // Found an escape we couldn't handle with 8 bits, copy what has been recognized and continue 10288 ASSERT(is8BitSource()); 10289 UChar*& result16 = currentCharacter16(); 10290 UChar* start16 = result16; 10291 int i = 0; 10292 for (; i < result - start; i++) 10293 result16[i] = start[i]; 10294 10295 result16 += i; 10296 10297 parseIdentifierInternal(currentCharacter<CharacterType>(), result16, hasEscape); 10298 10299 resultString.init(start16, result16 - start16); 10300 10301 return; 10302 } 10303 10304 resultString.init(start, result - start); 10305} 10306 10307template <typename SrcCharacterType, typename DestCharacterType> 10308inline bool CSSParser::parseStringInternal(SrcCharacterType*& src, DestCharacterType*& result, UChar quote) 10309{ 10310 while (true) { 10311 if (UNLIKELY(*src == quote)) { 10312 // String parsing is done. 10313 ++src; 10314 return true; 10315 } 10316 if (UNLIKELY(!*src)) { 10317 // String parsing is done, but don't advance pointer if at the end of input. 10318 return true; 10319 } 10320 ASSERT(*src > '\r' || (*src < '\n' && *src) || *src == '\v'); 10321 10322 if (LIKELY(src[0] != '\\')) 10323 *result++ = *src++; 10324 else if (src[1] == '\n' || src[1] == '\f') 10325 src += 2; 10326 else if (src[1] == '\r') 10327 src += src[2] == '\n' ? 3 : 2; 10328 else { 10329 SrcCharacterType* savedEscapeStart = src; 10330 unsigned unicode = parseEscape<SrcCharacterType>(src); 10331 if (unicode > 0xff && sizeof(DestCharacterType) == 1) { 10332 src = savedEscapeStart; 10333 return false; 10334 } 10335 UnicodeToChars(result, unicode); 10336 } 10337 } 10338 10339 return true; 10340} 10341 10342template <typename CharacterType> 10343inline void CSSParser::parseString(CharacterType*& result, CSSParserString& resultString, UChar quote) 10344{ 10345 CharacterType* start = currentCharacter<CharacterType>(); 10346 10347 if (UNLIKELY(!parseStringInternal(currentCharacter<CharacterType>(), result, quote))) { 10348 // Found an escape we couldn't handle with 8 bits, copy what has been recognized and continue 10349 ASSERT(is8BitSource()); 10350 UChar*& result16 = currentCharacter16(); 10351 UChar* start16 = result16; 10352 int i = 0; 10353 for (; i < result - start; i++) 10354 result16[i] = start[i]; 10355 10356 result16 += i; 10357 10358 parseStringInternal(currentCharacter<CharacterType>(), result16, quote); 10359 10360 resultString.init(start16, result16 - start16); 10361 return; 10362 } 10363 10364 resultString.init(start, result - start); 10365} 10366 10367template <typename CharacterType> 10368inline bool CSSParser::findURI(CharacterType*& start, CharacterType*& end, UChar& quote) 10369{ 10370 start = skipWhiteSpace(currentCharacter<CharacterType>()); 10371 10372 if (*start == '"' || *start == '\'') { 10373 quote = *start++; 10374 end = checkAndSkipString(start, quote); 10375 if (!end) 10376 return false; 10377 } else { 10378 quote = 0; 10379 end = start; 10380 while (isURILetter(*end)) { 10381 if (LIKELY(*end != '\\')) 10382 ++end; 10383 else { 10384 end = checkAndSkipEscape(end); 10385 if (!end) 10386 return false; 10387 } 10388 } 10389 } 10390 10391 end = skipWhiteSpace(end); 10392 if (*end != ')') 10393 return false; 10394 10395 return true; 10396} 10397 10398template <typename SrcCharacterType, typename DestCharacterType> 10399inline bool CSSParser::parseURIInternal(SrcCharacterType*& src, DestCharacterType*& dest, UChar quote) 10400{ 10401 if (quote) { 10402 ASSERT(quote == '"' || quote == '\''); 10403 return parseStringInternal(src, dest, quote); 10404 } 10405 10406 while (isURILetter(*src)) { 10407 if (LIKELY(*src != '\\')) 10408 *dest++ = *src++; 10409 else { 10410 unsigned unicode = parseEscape<SrcCharacterType>(src); 10411 if (unicode > 0xff && sizeof(SrcCharacterType) == 1) 10412 return false; 10413 UnicodeToChars(dest, unicode); 10414 } 10415 } 10416 10417 return true; 10418} 10419 10420template <typename CharacterType> 10421inline void CSSParser::parseURI(CSSParserString& string) 10422{ 10423 CharacterType* uriStart; 10424 CharacterType* uriEnd; 10425 UChar quote; 10426 if (!findURI(uriStart, uriEnd, quote)) 10427 return; 10428 10429 CharacterType* dest = currentCharacter<CharacterType>() = uriStart; 10430 if (LIKELY(parseURIInternal(currentCharacter<CharacterType>(), dest, quote))) 10431 string.init(uriStart, dest - uriStart); 10432 else { 10433 // An escape sequence was encountered that can't be stored in 8 bits. 10434 // Reset the current character to the start of the URI and re-parse with 10435 // a 16-bit destination. 10436 ASSERT(is8BitSource()); 10437 UChar* uriStart16 = currentCharacter16(); 10438 currentCharacter<CharacterType>() = uriStart; 10439 bool result = parseURIInternal(currentCharacter<CharacterType>(), currentCharacter16(), quote); 10440 ASSERT_UNUSED(result, result); 10441 string.init(uriStart16, currentCharacter16() - uriStart16); 10442 } 10443 10444 currentCharacter<CharacterType>() = uriEnd + 1; 10445 m_token = URI; 10446} 10447 10448template <typename CharacterType> 10449inline bool CSSParser::parseUnicodeRange() 10450{ 10451 CharacterType* character = currentCharacter<CharacterType>() + 1; 10452 int length = 6; 10453 ASSERT(*currentCharacter<CharacterType>() == '+'); 10454 10455 while (isASCIIHexDigit(*character) && length) { 10456 ++character; 10457 --length; 10458 } 10459 10460 if (length && *character == '?') { 10461 // At most 5 hex digit followed by a question mark. 10462 do { 10463 ++character; 10464 --length; 10465 } while (*character == '?' && length); 10466 currentCharacter<CharacterType>() = character; 10467 return true; 10468 } 10469 10470 if (length < 6) { 10471 // At least one hex digit. 10472 if (character[0] == '-' && isASCIIHexDigit(character[1])) { 10473 // Followed by a dash and a hex digit. 10474 ++character; 10475 length = 6; 10476 do { 10477 ++character; 10478 } while (--length && isASCIIHexDigit(*character)); 10479 } 10480 currentCharacter<CharacterType>() = character; 10481 return true; 10482 } 10483 return false; 10484} 10485 10486template <typename CharacterType> 10487bool CSSParser::parseNthChild() 10488{ 10489 CharacterType* character = currentCharacter<CharacterType>(); 10490 10491 while (isASCIIDigit(*character)) 10492 ++character; 10493 if (isASCIIAlphaCaselessEqual(*character, 'n')) { 10494 currentCharacter<CharacterType>() = character + 1; 10495 return true; 10496 } 10497 return false; 10498} 10499 10500template <typename CharacterType> 10501bool CSSParser::parseNthChildExtra() 10502{ 10503 CharacterType* character = skipWhiteSpace(currentCharacter<CharacterType>()); 10504 if (*character != '+' && *character != '-') 10505 return false; 10506 10507 character = skipWhiteSpace(character + 1); 10508 if (!isASCIIDigit(*character)) 10509 return false; 10510 10511 do { 10512 ++character; 10513 } while (isASCIIDigit(*character)); 10514 10515 currentCharacter<CharacterType>() = character; 10516 return true; 10517} 10518 10519template <typename CharacterType> 10520inline bool CSSParser::detectFunctionTypeToken(int length) 10521{ 10522 ASSERT(length > 0); 10523 CharacterType* name = tokenStart<CharacterType>(); 10524 10525 switch (length) { 10526 case 3: 10527 if (isASCIIAlphaCaselessEqual(name[0], 'n') && isASCIIAlphaCaselessEqual(name[1], 'o') && isASCIIAlphaCaselessEqual(name[2], 't')) { 10528 m_token = NOTFUNCTION; 10529 return true; 10530 } 10531 if (isASCIIAlphaCaselessEqual(name[0], 'u') && isASCIIAlphaCaselessEqual(name[1], 'r') && isASCIIAlphaCaselessEqual(name[2], 'l')) { 10532 m_token = URI; 10533 return true; 10534 } 10535#if ENABLE(VIDEO_TRACK) 10536 if (isASCIIAlphaCaselessEqual(name[0], 'c') && isASCIIAlphaCaselessEqual(name[1], 'u') && isASCIIAlphaCaselessEqual(name[2], 'e')) { 10537 m_token = CUEFUNCTION; 10538 return true; 10539 } 10540#endif 10541 return false; 10542 10543 case 4: 10544 if (isEqualToCSSIdentifier(name, "calc")) { 10545 m_token = CALCFUNCTION; 10546 return true; 10547 } 10548 return false; 10549 10550 case 9: 10551 if (isEqualToCSSIdentifier(name, "nth-child")) { 10552 m_parsingMode = NthChildMode; 10553 return true; 10554 } 10555 return false; 10556 10557 case 11: 10558 if (isEqualToCSSIdentifier(name, "nth-of-type")) { 10559 m_parsingMode = NthChildMode; 10560 return true; 10561 } 10562 return false; 10563 10564 case 14: 10565 if (isEqualToCSSIdentifier(name, "nth-last-child")) { 10566 m_parsingMode = NthChildMode; 10567 return true; 10568 } 10569 return false; 10570 10571 case 16: 10572 if (isEqualToCSSIdentifier(name, "nth-last-of-type")) { 10573 m_parsingMode = NthChildMode; 10574 return true; 10575 } 10576 return false; 10577 } 10578 10579 return false; 10580} 10581 10582template <typename CharacterType> 10583inline void CSSParser::detectMediaQueryToken(int length) 10584{ 10585 ASSERT(m_parsingMode == MediaQueryMode); 10586 CharacterType* name = tokenStart<CharacterType>(); 10587 10588 if (length == 3) { 10589 if (isASCIIAlphaCaselessEqual(name[0], 'a') && isASCIIAlphaCaselessEqual(name[1], 'n') && isASCIIAlphaCaselessEqual(name[2], 'd')) 10590 m_token = MEDIA_AND; 10591 else if (isASCIIAlphaCaselessEqual(name[0], 'n') && isASCIIAlphaCaselessEqual(name[1], 'o') && isASCIIAlphaCaselessEqual(name[2], 't')) 10592 m_token = MEDIA_NOT; 10593 } else if (length == 4) { 10594 if (isASCIIAlphaCaselessEqual(name[0], 'o') && isASCIIAlphaCaselessEqual(name[1], 'n') 10595 && isASCIIAlphaCaselessEqual(name[2], 'l') && isASCIIAlphaCaselessEqual(name[3], 'y')) 10596 m_token = MEDIA_ONLY; 10597 } 10598} 10599 10600template <typename CharacterType> 10601inline void CSSParser::detectNumberToken(CharacterType* type, int length) 10602{ 10603 ASSERT(length > 0); 10604 10605 switch (toASCIILowerUnchecked(type[0])) { 10606 case 'c': 10607 if (length == 2 && isASCIIAlphaCaselessEqual(type[1], 'm')) 10608 m_token = CMS; 10609 else if (length == 2 && isASCIIAlphaCaselessEqual(type[1], 'h')) 10610 m_token = CHS; 10611 return; 10612 10613 case 'd': 10614 if (length == 3 && isASCIIAlphaCaselessEqual(type[1], 'e') && isASCIIAlphaCaselessEqual(type[2], 'g')) 10615 m_token = DEGS; 10616#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY) 10617 else if (length > 2 && isASCIIAlphaCaselessEqual(type[1], 'p')) { 10618 if (length == 4) { 10619 // There is a discussion about the name of this unit on www-style. 10620 // Keep this compile time guard in place until that is resolved. 10621 // http://lists.w3.org/Archives/Public/www-style/2012May/0915.html 10622 if (isASCIIAlphaCaselessEqual(type[2], 'p') && isASCIIAlphaCaselessEqual(type[3], 'x')) 10623 m_token = DPPX; 10624 else if (isASCIIAlphaCaselessEqual(type[2], 'c') && isASCIIAlphaCaselessEqual(type[3], 'm')) 10625 m_token = DPCM; 10626 } else if (length == 3 && isASCIIAlphaCaselessEqual(type[2], 'i')) 10627 m_token = DPI; 10628 } 10629#endif 10630 return; 10631 10632 case 'e': 10633 if (length == 2) { 10634 if (isASCIIAlphaCaselessEqual(type[1], 'm')) 10635 m_token = EMS; 10636 else if (isASCIIAlphaCaselessEqual(type[1], 'x')) 10637 m_token = EXS; 10638 } 10639 return; 10640 10641 case 'f': 10642 if (length == 2 && isASCIIAlphaCaselessEqual(type[1], 'r')) 10643 m_token = FR; 10644 return; 10645 case 'g': 10646 if (length == 4 && isASCIIAlphaCaselessEqual(type[1], 'r') 10647 && isASCIIAlphaCaselessEqual(type[2], 'a') && isASCIIAlphaCaselessEqual(type[3], 'd')) 10648 m_token = GRADS; 10649 return; 10650 10651 case 'h': 10652 if (length == 2 && isASCIIAlphaCaselessEqual(type[1], 'z')) 10653 m_token = HERTZ; 10654 return; 10655 10656 case 'i': 10657 if (length == 2 && isASCIIAlphaCaselessEqual(type[1], 'n')) 10658 m_token = INS; 10659 return; 10660 10661 case 'k': 10662 if (length == 3 && isASCIIAlphaCaselessEqual(type[1], 'h') && isASCIIAlphaCaselessEqual(type[2], 'z')) 10663 m_token = KHERTZ; 10664 return; 10665 10666 case 'm': 10667 if (length == 2) { 10668 if (isASCIIAlphaCaselessEqual(type[1], 'm')) 10669 m_token = MMS; 10670 else if (isASCIIAlphaCaselessEqual(type[1], 's')) 10671 m_token = MSECS; 10672 } 10673 return; 10674 10675 case 'p': 10676 if (length == 2) { 10677 if (isASCIIAlphaCaselessEqual(type[1], 'x')) 10678 m_token = PXS; 10679 else if (isASCIIAlphaCaselessEqual(type[1], 't')) 10680 m_token = PTS; 10681 else if (isASCIIAlphaCaselessEqual(type[1], 'c')) 10682 m_token = PCS; 10683 } 10684 return; 10685 10686 case 'r': 10687 if (length == 3) { 10688 if (isASCIIAlphaCaselessEqual(type[1], 'a') && isASCIIAlphaCaselessEqual(type[2], 'd')) 10689 m_token = RADS; 10690 else if (isASCIIAlphaCaselessEqual(type[1], 'e') && isASCIIAlphaCaselessEqual(type[2], 'm')) 10691 m_token = REMS; 10692 } 10693 return; 10694 10695 case 's': 10696 if (length == 1) 10697 m_token = SECS; 10698 return; 10699 10700 case 't': 10701 if (length == 4 && isASCIIAlphaCaselessEqual(type[1], 'u') 10702 && isASCIIAlphaCaselessEqual(type[2], 'r') && isASCIIAlphaCaselessEqual(type[3], 'n')) 10703 m_token = TURNS; 10704 return; 10705 case 'v': 10706 if (length == 2) { 10707 if (isASCIIAlphaCaselessEqual(type[1], 'w')) 10708 m_token = VW; 10709 else if (isASCIIAlphaCaselessEqual(type[1], 'h')) 10710 m_token = VH; 10711 } else if (length == 4 && isASCIIAlphaCaselessEqual(type[1], 'm')) { 10712 if (isASCIIAlphaCaselessEqual(type[2], 'i') && isASCIIAlphaCaselessEqual(type[3], 'n')) 10713 m_token = VMIN; 10714 else if (isASCIIAlphaCaselessEqual(type[2], 'a') && isASCIIAlphaCaselessEqual(type[3], 'x')) 10715 m_token = VMAX; 10716 } 10717 return; 10718 10719 default: 10720 if (type[0] == '_' && length == 5 && type[1] == '_' && isASCIIAlphaCaselessEqual(type[2], 'q') 10721 && isASCIIAlphaCaselessEqual(type[3], 'e') && isASCIIAlphaCaselessEqual(type[4], 'm')) 10722 m_token = QEMS; 10723 return; 10724 } 10725} 10726 10727template <typename CharacterType> 10728inline void CSSParser::detectDashToken(int length) 10729{ 10730 CharacterType* name = tokenStart<CharacterType>(); 10731 10732 if (length == 11) { 10733 if (isASCIIAlphaCaselessEqual(name[10], 'y') && isEqualToCSSIdentifier(name + 1, "webkit-an")) 10734 m_token = ANYFUNCTION; 10735 else if (isASCIIAlphaCaselessEqual(name[10], 'n') && isEqualToCSSIdentifier(name + 1, "webkit-mi")) 10736 m_token = MINFUNCTION; 10737 else if (isASCIIAlphaCaselessEqual(name[10], 'x') && isEqualToCSSIdentifier(name + 1, "webkit-ma")) 10738 m_token = MAXFUNCTION; 10739 } else if (length == 12 && isEqualToCSSIdentifier(name + 1, "webkit-calc")) 10740 m_token = CALCFUNCTION; 10741} 10742 10743template <typename CharacterType> 10744inline void CSSParser::detectAtToken(int length, bool hasEscape) 10745{ 10746 CharacterType* name = tokenStart<CharacterType>(); 10747 ASSERT(name[0] == '@' && length >= 2); 10748 10749 // charset, font-face, import, media, namespace, page, supports, 10750 // -webkit-keyframes, and -webkit-mediaquery are not affected by hasEscape. 10751 switch (toASCIILowerUnchecked(name[1])) { 10752 case 'b': 10753 if (hasEscape) 10754 return; 10755 10756 switch (length) { 10757 case 12: 10758 if (isEqualToCSSIdentifier(name + 2, "ottom-left")) 10759 m_token = BOTTOMLEFT_SYM; 10760 return; 10761 10762 case 13: 10763 if (isEqualToCSSIdentifier(name + 2, "ottom-right")) 10764 m_token = BOTTOMRIGHT_SYM; 10765 return; 10766 10767 case 14: 10768 if (isEqualToCSSIdentifier(name + 2, "ottom-center")) 10769 m_token = BOTTOMCENTER_SYM; 10770 return; 10771 10772 case 19: 10773 if (isEqualToCSSIdentifier(name + 2, "ottom-left-corner")) 10774 m_token = BOTTOMLEFTCORNER_SYM; 10775 return; 10776 10777 case 20: 10778 if (isEqualToCSSIdentifier(name + 2, "ottom-right-corner")) 10779 m_token = BOTTOMRIGHTCORNER_SYM; 10780 return; 10781 } 10782 return; 10783 10784 case 'c': 10785 if (length == 8 && isEqualToCSSIdentifier(name + 2, "harset")) 10786 m_token = CHARSET_SYM; 10787 return; 10788 10789 case 'f': 10790 if (length == 10 && isEqualToCSSIdentifier(name + 2, "ont-face")) 10791 m_token = FONT_FACE_SYM; 10792 return; 10793 10794 case 'i': 10795 if (length == 7 && isEqualToCSSIdentifier(name + 2, "mport")) { 10796 m_parsingMode = MediaQueryMode; 10797 m_token = IMPORT_SYM; 10798 } 10799 return; 10800 10801 case 'l': 10802 if (hasEscape) 10803 return; 10804 10805 if (length == 9) { 10806 if (isEqualToCSSIdentifier(name + 2, "eft-top")) 10807 m_token = LEFTTOP_SYM; 10808 } else if (length == 12) { 10809 // Checking the last character first could further reduce the possibile cases. 10810 if (isASCIIAlphaCaselessEqual(name[11], 'e') && isEqualToCSSIdentifier(name + 2, "eft-middl")) 10811 m_token = LEFTMIDDLE_SYM; 10812 else if (isASCIIAlphaCaselessEqual(name[11], 'm') && isEqualToCSSIdentifier(name + 2, "eft-botto")) 10813 m_token = LEFTBOTTOM_SYM; 10814 } 10815 return; 10816 10817 case 'm': 10818 if (length == 6 && isEqualToCSSIdentifier(name + 2, "edia")) { 10819 m_parsingMode = MediaQueryMode; 10820 m_token = MEDIA_SYM; 10821 } 10822 return; 10823 10824 case 'n': 10825 if (length == 10 && isEqualToCSSIdentifier(name + 2, "amespace")) 10826 m_token = NAMESPACE_SYM; 10827 return; 10828 10829 case 'p': 10830 if (length == 5 && isEqualToCSSIdentifier(name + 2, "age")) 10831 m_token = PAGE_SYM; 10832 return; 10833 10834 case 'r': 10835 if (hasEscape) 10836 return; 10837 10838 if (length == 10) { 10839 if (isEqualToCSSIdentifier(name + 2, "ight-top")) 10840 m_token = RIGHTTOP_SYM; 10841 } else if (length == 13) { 10842 // Checking the last character first could further reduce the possibile cases. 10843 if (isASCIIAlphaCaselessEqual(name[12], 'e') && isEqualToCSSIdentifier(name + 2, "ight-middl")) 10844 m_token = RIGHTMIDDLE_SYM; 10845 else if (isASCIIAlphaCaselessEqual(name[12], 'm') && isEqualToCSSIdentifier(name + 2, "ight-botto")) 10846 m_token = RIGHTBOTTOM_SYM; 10847 } 10848 return; 10849 10850#if ENABLE(CSS3_CONDITIONAL_RULES) 10851 case 's': 10852 if (length == 9 && isEqualToCSSIdentifier(name + 2, "upports")) { 10853 m_parsingMode = SupportsMode; 10854 m_token = SUPPORTS_SYM; 10855 } 10856 return; 10857#endif 10858 10859 case 't': 10860 if (hasEscape) 10861 return; 10862 10863 switch (length) { 10864 case 9: 10865 if (isEqualToCSSIdentifier(name + 2, "op-left")) 10866 m_token = TOPLEFT_SYM; 10867 return; 10868 10869 case 10: 10870 if (isEqualToCSSIdentifier(name + 2, "op-right")) 10871 m_token = TOPRIGHT_SYM; 10872 return; 10873 10874 case 11: 10875 if (isEqualToCSSIdentifier(name + 2, "op-center")) 10876 m_token = TOPCENTER_SYM; 10877 return; 10878 10879 case 16: 10880 if (isEqualToCSSIdentifier(name + 2, "op-left-corner")) 10881 m_token = TOPLEFTCORNER_SYM; 10882 return; 10883 10884 case 17: 10885 if (isEqualToCSSIdentifier(name + 2, "op-right-corner")) 10886 m_token = TOPRIGHTCORNER_SYM; 10887 return; 10888 } 10889 return; 10890 10891 case '-': 10892 switch (length) { 10893 case 13: 10894 if (!hasEscape && isEqualToCSSIdentifier(name + 2, "webkit-rule")) 10895 m_token = WEBKIT_RULE_SYM; 10896 return; 10897 10898 case 14: 10899 if (hasEscape) 10900 return; 10901 10902 // Checking the last character first could further reduce the possibile cases. 10903 if (isASCIIAlphaCaselessEqual(name[13], 's') && isEqualToCSSIdentifier(name + 2, "webkit-decl")) 10904 m_token = WEBKIT_DECLS_SYM; 10905 else if (isASCIIAlphaCaselessEqual(name[13], 'e') && isEqualToCSSIdentifier(name + 2, "webkit-valu")) 10906 m_token = WEBKIT_VALUE_SYM; 10907 return; 10908 10909 case 15: 10910 if (hasEscape) 10911 return; 10912 10913#if ENABLE(CSS_REGIONS) 10914 if (isASCIIAlphaCaselessEqual(name[14], 'n') && isEqualToCSSIdentifier(name + 2, "webkit-regio")) 10915 m_token = WEBKIT_REGION_RULE_SYM; 10916#endif 10917 return; 10918 10919 case 17: 10920 if (hasEscape) 10921 return; 10922 10923 if (isASCIIAlphaCaselessEqual(name[16], 'r') && isEqualToCSSIdentifier(name + 2, "webkit-selecto")) 10924 m_token = WEBKIT_SELECTOR_SYM; 10925#if ENABLE(CSS_DEVICE_ADAPTATION) 10926 else if (isASCIIAlphaCaselessEqual(name[16], 't') && isEqualToCSSIdentifier(name + 2, "webkit-viewpor")) 10927 m_token = WEBKIT_VIEWPORT_RULE_SYM; 10928#endif 10929 return; 10930 10931 case 18: 10932 if (isEqualToCSSIdentifier(name + 2, "webkit-keyframes")) 10933 m_token = WEBKIT_KEYFRAMES_SYM; 10934#if ENABLE(PICTURE_SIZES) 10935 else if (isEqualToCSSIdentifier(name + 2, "webkit-sizesattr")) 10936 m_token = WEBKIT_SIZESATTR_SYM; 10937#endif 10938 return; 10939 10940 case 19: 10941 if (isEqualToCSSIdentifier(name + 2, "webkit-mediaquery")) { 10942 m_parsingMode = MediaQueryMode; 10943 m_token = WEBKIT_MEDIAQUERY_SYM; 10944 } 10945 return; 10946 10947 case 22: 10948 if (!hasEscape && isEqualToCSSIdentifier(name + 2, "webkit-keyframe-rule")) 10949 m_token = WEBKIT_KEYFRAME_RULE_SYM; 10950 return; 10951 10952 case 27: 10953#if ENABLE(CSS3_CONDITIONAL_RULES) 10954 if (isEqualToCSSIdentifier(name + 2, "webkit-supports-condition")) { 10955 m_parsingMode = SupportsMode; 10956 m_token = WEBKIT_SUPPORTS_CONDITION_SYM; 10957 } 10958#endif 10959 return; 10960 } 10961 } 10962} 10963 10964#if ENABLE(CSS3_CONDITIONAL_RULES) 10965template <typename CharacterType> 10966inline void CSSParser::detectSupportsToken(int length) 10967{ 10968 ASSERT(m_parsingMode == SupportsMode); 10969 CharacterType* name = tokenStart<CharacterType>(); 10970 10971 if (length == 2) { 10972 if (isASCIIAlphaCaselessEqual(name[0], 'o') && isASCIIAlphaCaselessEqual(name[1], 'r')) 10973 m_token = SUPPORTS_OR; 10974 } else if (length == 3) { 10975 if (isASCIIAlphaCaselessEqual(name[0], 'a') && isASCIIAlphaCaselessEqual(name[1], 'n') && isASCIIAlphaCaselessEqual(name[2], 'd')) 10976 m_token = SUPPORTS_AND; 10977 else if (isASCIIAlphaCaselessEqual(name[0], 'n') && isASCIIAlphaCaselessEqual(name[1], 'o') && isASCIIAlphaCaselessEqual(name[2], 't')) 10978 m_token = SUPPORTS_NOT; 10979 } 10980} 10981#endif 10982 10983template <typename SrcCharacterType> 10984int CSSParser::realLex(void* yylvalWithoutType) 10985{ 10986 YYSTYPE* yylval = static_cast<YYSTYPE*>(yylvalWithoutType); 10987 // Write pointer for the next character. 10988 SrcCharacterType* result; 10989 CSSParserString resultString; 10990 bool hasEscape; 10991 10992 // The input buffer is terminated by a \0 character, so 10993 // it is safe to read one character ahead of a known non-null. 10994#ifndef NDEBUG 10995 // In debug we check with an ASSERT that the length is > 0 for string types. 10996 yylval->string.clear(); 10997#endif 10998 10999restartAfterComment: 11000 result = currentCharacter<SrcCharacterType>(); 11001 setTokenStart(result); 11002 m_tokenStartLineNumber = m_lineNumber; 11003 m_token = *currentCharacter<SrcCharacterType>(); 11004 ++currentCharacter<SrcCharacterType>(); 11005 11006 switch ((m_token <= 127) ? typesOfASCIICharacters[m_token] : CharacterIdentifierStart) { 11007 case CharacterCaselessU: 11008 if (UNLIKELY(*currentCharacter<SrcCharacterType>() == '+')) { 11009 if (parseUnicodeRange<SrcCharacterType>()) { 11010 m_token = UNICODERANGE; 11011 yylval->string.init(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11012 break; 11013 } 11014 } 11015 FALLTHROUGH; // To CharacterIdentifierStart. 11016 11017 case CharacterIdentifierStart: 11018 --currentCharacter<SrcCharacterType>(); 11019 parseIdentifier(result, yylval->string, hasEscape); 11020 m_token = IDENT; 11021 11022 if (UNLIKELY(*currentCharacter<SrcCharacterType>() == '(')) { 11023#if ENABLE(CSS3_CONDITIONAL_RULES) 11024 if (m_parsingMode == SupportsMode && !hasEscape) { 11025 detectSupportsToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 11026 if (m_token != IDENT) 11027 break; 11028 } 11029#endif 11030 m_token = FUNCTION; 11031 bool shouldSkipParenthesis = true; 11032 if (!hasEscape) { 11033 bool detected = detectFunctionTypeToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 11034 if (!detected && m_parsingMode == MediaQueryMode) { 11035 // ... and(max-width: 480px) ... looks like a function, but in fact it is not, 11036 // so run more detection code in the MediaQueryMode. 11037 detectMediaQueryToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 11038 shouldSkipParenthesis = false; 11039 } 11040 } 11041 11042 if (LIKELY(shouldSkipParenthesis)) { 11043 ++currentCharacter<SrcCharacterType>(); 11044 ++result; 11045 ++yylval->string.m_length; 11046 } 11047 11048 if (token() == URI) { 11049 m_token = FUNCTION; 11050 // Check whether it is really an URI. 11051 if (yylval->string.is8Bit()) 11052 parseURI<LChar>(yylval->string); 11053 else 11054 parseURI<UChar>(yylval->string); 11055 } 11056 } else if (UNLIKELY(m_parsingMode != NormalMode) && !hasEscape) { 11057 if (m_parsingMode == MediaQueryMode) 11058 detectMediaQueryToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 11059#if ENABLE(CSS3_CONDITIONAL_RULES) 11060 else if (m_parsingMode == SupportsMode) 11061 detectSupportsToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 11062#endif 11063 else if (m_parsingMode == NthChildMode && isASCIIAlphaCaselessEqual(tokenStart<SrcCharacterType>()[0], 'n')) { 11064 if (result - tokenStart<SrcCharacterType>() == 1) { 11065 // String "n" is IDENT but "n+1" is NTH. 11066 if (parseNthChildExtra<SrcCharacterType>()) { 11067 m_token = NTH; 11068 yylval->string.m_length = currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>(); 11069 } 11070 } else if (result - tokenStart<SrcCharacterType>() >= 2 && tokenStart<SrcCharacterType>()[1] == '-') { 11071 // String "n-" is IDENT but "n-1" is NTH. 11072 // Set currentCharacter to '-' to continue parsing. 11073 SrcCharacterType* nextCharacter = result; 11074 currentCharacter<SrcCharacterType>() = tokenStart<SrcCharacterType>() + 1; 11075 if (parseNthChildExtra<SrcCharacterType>()) { 11076 m_token = NTH; 11077 yylval->string.setLength(currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11078 } else { 11079 // Revert the change to currentCharacter if unsuccessful. 11080 currentCharacter<SrcCharacterType>() = nextCharacter; 11081 } 11082 } 11083 } 11084 } 11085 break; 11086 11087 case CharacterDot: 11088 if (!isASCIIDigit(currentCharacter<SrcCharacterType>()[0])) 11089 break; 11090 FALLTHROUGH; // To CharacterNumber. 11091 11092 case CharacterNumber: { 11093 bool dotSeen = (m_token == '.'); 11094 11095 while (true) { 11096 if (!isASCIIDigit(currentCharacter<SrcCharacterType>()[0])) { 11097 // Only one dot is allowed for a number, 11098 // and it must be followed by a digit. 11099 if (currentCharacter<SrcCharacterType>()[0] != '.' || dotSeen || !isASCIIDigit(currentCharacter<SrcCharacterType>()[1])) 11100 break; 11101 dotSeen = true; 11102 } 11103 ++currentCharacter<SrcCharacterType>(); 11104 } 11105 11106 if (UNLIKELY(m_parsingMode == NthChildMode) && !dotSeen && isASCIIAlphaCaselessEqual(*currentCharacter<SrcCharacterType>(), 'n')) { 11107 // "[0-9]+n" is always an NthChild. 11108 ++currentCharacter<SrcCharacterType>(); 11109 parseNthChildExtra<SrcCharacterType>(); 11110 m_token = NTH; 11111 yylval->string.init(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11112 break; 11113 } 11114 11115 // Use SVG parser for numbers on SVG presentation attributes. 11116 if (m_context.mode == SVGAttributeMode) { 11117 // We need to take care of units like 'em' or 'ex'. 11118 SrcCharacterType* character = currentCharacter<SrcCharacterType>(); 11119 if (isASCIIAlphaCaselessEqual(*character, 'e')) { 11120 ASSERT(character - tokenStart<SrcCharacterType>() > 0); 11121 ++character; 11122 if (*character == '-' || *character == '+' || isASCIIDigit(*character)) { 11123 ++character; 11124 while (isASCIIDigit(*character)) 11125 ++character; 11126 // Use FLOATTOKEN if the string contains exponents. 11127 dotSeen = true; 11128 currentCharacter<SrcCharacterType>() = character; 11129 } 11130 } 11131 if (!parseSVGNumber(tokenStart<SrcCharacterType>(), character - tokenStart<SrcCharacterType>(), yylval->number)) 11132 break; 11133 } else 11134 yylval->number = charactersToDouble(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11135 11136 // Type of the function. 11137 if (isIdentifierStart<SrcCharacterType>()) { 11138 SrcCharacterType* type = currentCharacter<SrcCharacterType>(); 11139 result = currentCharacter<SrcCharacterType>(); 11140 11141 parseIdentifier(result, resultString, hasEscape); 11142 11143 m_token = DIMEN; 11144 if (!hasEscape) 11145 detectNumberToken(type, currentCharacter<SrcCharacterType>() - type); 11146 11147 if (m_token == DIMEN) { 11148 // The decoded number is overwritten, but this is intentional. 11149 yylval->string.init(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11150 } 11151 } else if (*currentCharacter<SrcCharacterType>() == '%') { 11152 // Although the CSS grammar says {num}% we follow 11153 // webkit at the moment which uses {num}%+. 11154 do { 11155 ++currentCharacter<SrcCharacterType>(); 11156 } while (*currentCharacter<SrcCharacterType>() == '%'); 11157 m_token = PERCENTAGE; 11158 } else 11159 m_token = dotSeen ? FLOATTOKEN : INTEGER; 11160 break; 11161 } 11162 11163 case CharacterDash: 11164 if (isIdentifierStartAfterDash(currentCharacter<SrcCharacterType>())) { 11165 --currentCharacter<SrcCharacterType>(); 11166 parseIdentifier(result, resultString, hasEscape); 11167 m_token = IDENT; 11168 11169 if (*currentCharacter<SrcCharacterType>() == '(') { 11170 m_token = FUNCTION; 11171 if (!hasEscape) 11172 detectDashToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 11173 ++currentCharacter<SrcCharacterType>(); 11174 ++result; 11175 } else if (UNLIKELY(m_parsingMode == NthChildMode) && !hasEscape && isASCIIAlphaCaselessEqual(tokenStart<SrcCharacterType>()[1], 'n')) { 11176 if (result - tokenStart<SrcCharacterType>() == 2) { 11177 // String "-n" is IDENT but "-n+1" is NTH. 11178 if (parseNthChildExtra<SrcCharacterType>()) { 11179 m_token = NTH; 11180 result = currentCharacter<SrcCharacterType>(); 11181 } 11182 } else if (result - tokenStart<SrcCharacterType>() >= 3 && tokenStart<SrcCharacterType>()[2] == '-') { 11183 // String "-n-" is IDENT but "-n-1" is NTH. 11184 // Set currentCharacter to second '-' of '-n-' to continue parsing. 11185 SrcCharacterType* nextCharacter = result; 11186 currentCharacter<SrcCharacterType>() = tokenStart<SrcCharacterType>() + 2; 11187 if (parseNthChildExtra<SrcCharacterType>()) { 11188 m_token = NTH; 11189 result = currentCharacter<SrcCharacterType>(); 11190 } else { 11191 // Revert the change to currentCharacter if unsuccessful. 11192 currentCharacter<SrcCharacterType>() = nextCharacter; 11193 } 11194 } 11195 } 11196 resultString.setLength(result - tokenStart<SrcCharacterType>()); 11197 yylval->string = resultString; 11198 } else if (currentCharacter<SrcCharacterType>()[0] == '-' && currentCharacter<SrcCharacterType>()[1] == '>') { 11199 currentCharacter<SrcCharacterType>() += 2; 11200 m_token = SGML_CD; 11201 } else if (UNLIKELY(m_parsingMode == NthChildMode)) { 11202 // "-[0-9]+n" is always an NthChild. 11203 if (parseNthChild<SrcCharacterType>()) { 11204 parseNthChildExtra<SrcCharacterType>(); 11205 m_token = NTH; 11206 yylval->string.init(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11207 } 11208 } 11209 break; 11210 11211 case CharacterOther: 11212 // m_token is simply the current character. 11213 break; 11214 11215 case CharacterNull: 11216 // Do not advance pointer at the end of input. 11217 --currentCharacter<SrcCharacterType>(); 11218 break; 11219 11220 case CharacterWhiteSpace: 11221 m_token = WHITESPACE; 11222 // Might start with a '\n'. 11223 --currentCharacter<SrcCharacterType>(); 11224 do { 11225 if (*currentCharacter<SrcCharacterType>() == '\n') 11226 ++m_lineNumber; 11227 ++currentCharacter<SrcCharacterType>(); 11228 } while (*currentCharacter<SrcCharacterType>() <= ' ' && (typesOfASCIICharacters[*currentCharacter<SrcCharacterType>()] == CharacterWhiteSpace)); 11229 break; 11230 11231 case CharacterEndMediaQuery: 11232 if (m_parsingMode == MediaQueryMode) 11233 m_parsingMode = NormalMode; 11234 break; 11235 11236 case CharacterEndNthChild: 11237 if (m_parsingMode == NthChildMode) 11238 m_parsingMode = NormalMode; 11239 break; 11240 11241 case CharacterQuote: 11242 if (checkAndSkipString(currentCharacter<SrcCharacterType>(), m_token)) { 11243 ++result; 11244 parseString<SrcCharacterType>(result, yylval->string, m_token); 11245 m_token = STRING; 11246 } 11247 break; 11248 11249 case CharacterExclamationMark: { 11250 SrcCharacterType* start = skipWhiteSpace(currentCharacter<SrcCharacterType>()); 11251 if (isEqualToCSSIdentifier(start, "important")) { 11252 m_token = IMPORTANT_SYM; 11253 currentCharacter<SrcCharacterType>() = start + 9; 11254 } 11255 break; 11256 } 11257 11258 case CharacterHashmark: { 11259 SrcCharacterType* start = currentCharacter<SrcCharacterType>(); 11260 result = currentCharacter<SrcCharacterType>(); 11261 11262 if (isASCIIDigit(*currentCharacter<SrcCharacterType>())) { 11263 // This must be a valid hex number token. 11264 do { 11265 ++currentCharacter<SrcCharacterType>(); 11266 } while (isASCIIHexDigit(*currentCharacter<SrcCharacterType>())); 11267 m_token = HEX; 11268 yylval->string.init(start, currentCharacter<SrcCharacterType>() - start); 11269 } else if (isIdentifierStart<SrcCharacterType>()) { 11270 m_token = IDSEL; 11271 parseIdentifier(result, yylval->string, hasEscape); 11272 if (!hasEscape) { 11273 // Check whether the identifier is also a valid hex number. 11274 SrcCharacterType* current = start; 11275 m_token = HEX; 11276 do { 11277 if (!isASCIIHexDigit(*current)) { 11278 m_token = IDSEL; 11279 break; 11280 } 11281 ++current; 11282 } while (current < result); 11283 } 11284 } 11285 break; 11286 } 11287 11288 case CharacterSlash: 11289 // Ignore comments. They are not even considered as white spaces. 11290 if (*currentCharacter<SrcCharacterType>() == '*') { 11291 ++currentCharacter<SrcCharacterType>(); 11292 while (currentCharacter<SrcCharacterType>()[0] != '*' || currentCharacter<SrcCharacterType>()[1] != '/') { 11293 if (*currentCharacter<SrcCharacterType>() == '\n') 11294 ++m_lineNumber; 11295 if (*currentCharacter<SrcCharacterType>() == '\0') { 11296 // Unterminated comments are simply ignored. 11297 currentCharacter<SrcCharacterType>() -= 2; 11298 break; 11299 } 11300 ++currentCharacter<SrcCharacterType>(); 11301 } 11302 currentCharacter<SrcCharacterType>() += 2; 11303 goto restartAfterComment; 11304 } 11305 break; 11306 11307 case CharacterDollar: 11308 if (*currentCharacter<SrcCharacterType>() == '=') { 11309 ++currentCharacter<SrcCharacterType>(); 11310 m_token = ENDSWITH; 11311 } 11312 break; 11313 11314 case CharacterAsterisk: 11315 if (*currentCharacter<SrcCharacterType>() == '=') { 11316 ++currentCharacter<SrcCharacterType>(); 11317 m_token = CONTAINS; 11318 } 11319 break; 11320 11321 case CharacterPlus: 11322 if (UNLIKELY(m_parsingMode == NthChildMode)) { 11323 // Simplest case. "+[0-9]*n" is always NthChild. 11324 if (parseNthChild<SrcCharacterType>()) { 11325 parseNthChildExtra<SrcCharacterType>(); 11326 m_token = NTH; 11327 yylval->string.init(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11328 } 11329 } 11330 break; 11331 11332 case CharacterLess: 11333 if (currentCharacter<SrcCharacterType>()[0] == '!' && currentCharacter<SrcCharacterType>()[1] == '-' && currentCharacter<SrcCharacterType>()[2] == '-') { 11334 currentCharacter<SrcCharacterType>() += 3; 11335 m_token = SGML_CD; 11336 } 11337 break; 11338 11339 case CharacterAt: 11340 if (isIdentifierStart<SrcCharacterType>()) { 11341 m_token = ATKEYWORD; 11342 ++result; 11343 parseIdentifier(result, resultString, hasEscape); 11344 detectAtToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>(), hasEscape); 11345 } 11346 break; 11347 11348 case CharacterBackSlash: 11349 if (isCSSEscape(*currentCharacter<SrcCharacterType>())) { 11350 --currentCharacter<SrcCharacterType>(); 11351 parseIdentifier(result, yylval->string, hasEscape); 11352 m_token = IDENT; 11353 } 11354 break; 11355 11356 case CharacterXor: 11357 if (*currentCharacter<SrcCharacterType>() == '=') { 11358 ++currentCharacter<SrcCharacterType>(); 11359 m_token = BEGINSWITH; 11360 } 11361 break; 11362 11363 case CharacterVerticalBar: 11364 if (*currentCharacter<SrcCharacterType>() == '=') { 11365 ++currentCharacter<SrcCharacterType>(); 11366 m_token = DASHMATCH; 11367 } 11368 break; 11369 11370 case CharacterTilde: 11371 if (*currentCharacter<SrcCharacterType>() == '=') { 11372 ++currentCharacter<SrcCharacterType>(); 11373 m_token = INCLUDES; 11374 } 11375 break; 11376 11377 default: 11378 ASSERT_NOT_REACHED(); 11379 break; 11380 } 11381 11382 return token(); 11383} 11384 11385PassRefPtr<StyleRuleBase> CSSParser::createImportRule(const CSSParserString& url, PassRefPtr<MediaQuerySet> media) 11386{ 11387 if (!media || !m_allowImportRules) { 11388 popRuleData(); 11389 return 0; 11390 } 11391 RefPtr<StyleRuleImport> rule = StyleRuleImport::create(url, media); 11392 processAndAddNewRuleToSourceTreeIfNeeded(); 11393 return rule.release(); 11394} 11395 11396PassRefPtr<StyleRuleBase> CSSParser::createMediaRule(PassRefPtr<MediaQuerySet> media, RuleList* rules) 11397{ 11398 m_allowImportRules = m_allowNamespaceDeclarations = false; 11399 RefPtr<StyleRuleMedia> rule; 11400 RuleList emptyRules; 11401 if (!media) { 11402 // To comply with w3c test suite expectation, create an empty media query 11403 // even when it is syntactically incorrect. 11404 rule = StyleRuleMedia::create(MediaQuerySet::create(), emptyRules); 11405 } else 11406 rule = StyleRuleMedia::create(media, rules ? *rules : emptyRules); 11407 processAndAddNewRuleToSourceTreeIfNeeded(); 11408 return rule.release(); 11409} 11410 11411PassRefPtr<StyleRuleBase> CSSParser::createEmptyMediaRule(RuleList* rules) 11412{ 11413 return createMediaRule(MediaQuerySet::create(), rules); 11414} 11415 11416#if ENABLE(CSS3_CONDITIONAL_RULES) 11417PassRefPtr<StyleRuleBase> CSSParser::createSupportsRule(bool conditionIsSupported, RuleList* rules) 11418{ 11419 m_allowImportRules = m_allowNamespaceDeclarations = false; 11420 11421 RefPtr<CSSRuleSourceData> data = popSupportsRuleData(); 11422 RefPtr<StyleRuleSupports> rule; 11423 String conditionText; 11424 unsigned conditionOffset = data->ruleHeaderRange.start + 9; 11425 unsigned conditionLength = data->ruleHeaderRange.length() - 9; 11426 11427 if (is8BitSource()) 11428 conditionText = String(m_dataStart8.get() + conditionOffset, conditionLength).stripWhiteSpace(); 11429 else 11430 conditionText = String(m_dataStart16.get() + conditionOffset, conditionLength).stripWhiteSpace(); 11431 11432 if (rules) 11433 rule = StyleRuleSupports::create(conditionText, conditionIsSupported, *rules); 11434 else { 11435 RuleList emptyRules; 11436 rule = StyleRuleSupports::create(conditionText, conditionIsSupported, emptyRules); 11437 } 11438 11439 processAndAddNewRuleToSourceTreeIfNeeded(); 11440 11441 return rule.release(); 11442} 11443 11444void CSSParser::markSupportsRuleHeaderStart() 11445{ 11446 if (!m_supportsRuleDataStack) 11447 m_supportsRuleDataStack = std::make_unique<RuleSourceDataList>(); 11448 11449 RefPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(CSSRuleSourceData::SUPPORTS_RULE); 11450 data->ruleHeaderRange.start = tokenStartOffset(); 11451 m_supportsRuleDataStack->append(data); 11452} 11453 11454void CSSParser::markSupportsRuleHeaderEnd() 11455{ 11456 ASSERT(m_supportsRuleDataStack && !m_supportsRuleDataStack->isEmpty()); 11457 11458 if (is8BitSource()) 11459 m_supportsRuleDataStack->last()->ruleHeaderRange.end = tokenStart<LChar>() - m_dataStart8.get(); 11460 else 11461 m_supportsRuleDataStack->last()->ruleHeaderRange.end = tokenStart<UChar>() - m_dataStart16.get(); 11462} 11463 11464PassRefPtr<CSSRuleSourceData> CSSParser::popSupportsRuleData() 11465{ 11466 ASSERT(m_supportsRuleDataStack && !m_supportsRuleDataStack->isEmpty()); 11467 RefPtr<CSSRuleSourceData> data = m_supportsRuleDataStack->last(); 11468 m_supportsRuleDataStack->removeLast(); 11469 return data.release(); 11470} 11471 11472#endif 11473 11474void CSSParser::processAndAddNewRuleToSourceTreeIfNeeded() 11475{ 11476 if (!isExtractingSourceData()) 11477 return; 11478 markRuleBodyEnd(); 11479 RefPtr<CSSRuleSourceData> rule = popRuleData(); 11480 fixUnparsedPropertyRanges(rule.get()); 11481 addNewRuleToSourceTree(rule.release()); 11482} 11483 11484void CSSParser::addNewRuleToSourceTree(PassRefPtr<CSSRuleSourceData> rule) 11485{ 11486 // Precondition: (isExtractingSourceData()). 11487 if (!m_ruleSourceDataResult) 11488 return; 11489 if (m_currentRuleDataStack->isEmpty()) 11490 m_ruleSourceDataResult->append(rule); 11491 else 11492 m_currentRuleDataStack->last()->childRules.append(rule); 11493} 11494 11495PassRefPtr<CSSRuleSourceData> CSSParser::popRuleData() 11496{ 11497 if (!m_ruleSourceDataResult) 11498 return 0; 11499 11500 ASSERT(!m_currentRuleDataStack->isEmpty()); 11501 m_currentRuleData.clear(); 11502 RefPtr<CSSRuleSourceData> data = m_currentRuleDataStack->last(); 11503 m_currentRuleDataStack->removeLast(); 11504 return data.release(); 11505} 11506 11507void CSSParser::syntaxError(const Location& location, SyntaxErrorType error) 11508{ 11509 if (!isLoggingErrors()) 11510 return; 11511 StringBuilder builder; 11512 switch (error) { 11513 case PropertyDeclarationError: 11514 builder.appendLiteral("Invalid CSS property declaration at: "); 11515 break; 11516 11517 default: 11518 builder.appendLiteral("Unexpected CSS token: "); 11519 } 11520 11521 if (location.token.is8Bit()) 11522 builder.append(location.token.characters8(), location.token.length()); 11523 else 11524 builder.append(location.token.characters16(), location.token.length()); 11525 11526 logError(builder.toString(), location.lineNumber); 11527 11528 m_ignoreErrorsInDeclaration = true; 11529} 11530 11531bool CSSParser::isLoggingErrors() 11532{ 11533 return m_logErrors && !m_ignoreErrorsInDeclaration; 11534} 11535 11536void CSSParser::logError(const String& message, int lineNumber) 11537{ 11538 // FIXME: <http://webkit.org/b/114313> CSS parser console message errors should include column numbers. 11539 PageConsole& console = m_styleSheet->singleOwnerDocument()->page()->console(); 11540 console.addMessage(MessageSource::CSS, MessageLevel::Warning, message, m_styleSheet->baseURL().string(), lineNumber + 1, 0); 11541} 11542 11543PassRefPtr<StyleRuleKeyframes> CSSParser::createKeyframesRule(const String& name, std::unique_ptr<Vector<RefPtr<StyleKeyframe>>> popKeyframes) 11544{ 11545 std::unique_ptr<Vector<RefPtr<StyleKeyframe>>> keyframes = WTF::move(popKeyframes); 11546 m_allowImportRules = m_allowNamespaceDeclarations = false; 11547 RefPtr<StyleRuleKeyframes> rule = StyleRuleKeyframes::create(); 11548 for (size_t i = 0; i < keyframes->size(); ++i) 11549 rule->parserAppendKeyframe(keyframes->at(i)); 11550 rule->setName(name); 11551 processAndAddNewRuleToSourceTreeIfNeeded(); 11552 return rule.release(); 11553} 11554 11555PassRefPtr<StyleRuleBase> CSSParser::createStyleRule(Vector<std::unique_ptr<CSSParserSelector>>* selectors) 11556{ 11557 RefPtr<StyleRule> rule; 11558 if (selectors) { 11559 m_allowImportRules = false; 11560 m_allowNamespaceDeclarations = false; 11561 if (m_hasFontFaceOnlyValues) 11562 deleteFontFaceOnlyValues(); 11563 rule = StyleRule::create(m_lastSelectorLineNumber, createStyleProperties()); 11564 rule->parserAdoptSelectorVector(*selectors); 11565 processAndAddNewRuleToSourceTreeIfNeeded(); 11566 } else 11567 popRuleData(); 11568 clearProperties(); 11569 return rule.release(); 11570} 11571 11572PassRefPtr<StyleRuleBase> CSSParser::createFontFaceRule() 11573{ 11574 m_allowImportRules = m_allowNamespaceDeclarations = false; 11575 for (unsigned i = 0; i < m_parsedProperties.size(); ++i) { 11576 CSSProperty& property = m_parsedProperties[i]; 11577 if (property.id() == CSSPropertyFontVariant && property.value()->isPrimitiveValue()) 11578 property.wrapValueInCommaSeparatedList(); 11579 else if (property.id() == CSSPropertyFontFamily && (!property.value()->isValueList() || toCSSValueList(property.value())->length() != 1)) { 11580 // Unlike font-family property, font-family descriptor in @font-face rule 11581 // has to be a value list with exactly one family name. It cannot have a 11582 // have 'initial' value and cannot 'inherit' from parent. 11583 // See http://dev.w3.org/csswg/css3-fonts/#font-family-desc 11584 clearProperties(); 11585 popRuleData(); 11586 return 0; 11587 } 11588 } 11589 RefPtr<StyleRuleFontFace> rule = StyleRuleFontFace::create(createStyleProperties()); 11590 clearProperties(); 11591 processAndAddNewRuleToSourceTreeIfNeeded(); 11592 return rule.release(); 11593} 11594 11595void CSSParser::addNamespace(const AtomicString& prefix, const AtomicString& uri) 11596{ 11597 if (!m_styleSheet || !m_allowNamespaceDeclarations) 11598 return; 11599 m_allowImportRules = false; 11600 m_styleSheet->parserAddNamespace(prefix, uri); 11601 if (prefix.isEmpty() && !uri.isNull()) 11602 m_defaultNamespace = uri; 11603} 11604 11605QualifiedName CSSParser::determineNameInNamespace(const AtomicString& prefix, const AtomicString& localName) 11606{ 11607 if (!m_styleSheet) 11608 return QualifiedName(prefix, localName, m_defaultNamespace); 11609 return QualifiedName(prefix, localName, m_styleSheet->determineNamespace(prefix)); 11610} 11611 11612void CSSParser::rewriteSpecifiersWithNamespaceIfNeeded(CSSParserSelector& specifiers) 11613{ 11614 if (m_defaultNamespace != starAtom || specifiers.isCustomPseudoElement()) 11615 rewriteSpecifiersWithElementName(nullAtom, starAtom, specifiers, /*tagIsForNamespaceRule*/true); 11616} 11617 11618void CSSParser::rewriteSpecifiersWithElementName(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector& specifiers, bool tagIsForNamespaceRule) 11619{ 11620 AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleSheet ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace; 11621 QualifiedName tag(namespacePrefix, elementName, determinedNamespace); 11622 11623 if (!specifiers.isCustomPseudoElement()) { 11624 if (tag == anyQName()) 11625 return; 11626 if (!specifiers.isPseudoElementCueFunction()) 11627 specifiers.prependTagSelector(tag, tagIsForNamespaceRule); 11628 return; 11629 } 11630 11631 CSSParserSelector* lastShadowDescendant = &specifiers; 11632 CSSParserSelector* history = &specifiers; 11633 while (history->tagHistory()) { 11634 history = history->tagHistory(); 11635 if (history->isCustomPseudoElement() || history->hasShadowDescendant()) 11636 lastShadowDescendant = history; 11637 } 11638 11639 if (lastShadowDescendant->tagHistory()) { 11640 if (tag != anyQName()) 11641 lastShadowDescendant->tagHistory()->prependTagSelector(tag, tagIsForNamespaceRule); 11642 return; 11643 } 11644 11645 // For shadow-ID pseudo-elements to be correctly matched, the ShadowDescendant combinator has to be used. 11646 // We therefore create a new Selector with that combinator here in any case, even if matching any (host) element in any namespace (i.e. '*'). 11647 lastShadowDescendant->setTagHistory(std::make_unique<CSSParserSelector>(tag)); 11648 lastShadowDescendant->setRelation(CSSSelector::ShadowDescendant); 11649} 11650 11651std::unique_ptr<CSSParserSelector> CSSParser::rewriteSpecifiers(std::unique_ptr<CSSParserSelector> specifiers, std::unique_ptr<CSSParserSelector> newSpecifier) 11652{ 11653 if (newSpecifier->isCustomPseudoElement() || newSpecifier->isPseudoElementCueFunction()) { 11654 // Unknown pseudo element always goes at the top of selector chain. 11655 newSpecifier->appendTagHistory(CSSSelector::ShadowDescendant, WTF::move(specifiers)); 11656 return newSpecifier; 11657 } 11658 if (specifiers->isCustomPseudoElement()) { 11659 // Specifiers for unknown pseudo element go right behind it in the chain. 11660 specifiers->insertTagHistory(CSSSelector::SubSelector, WTF::move(newSpecifier), CSSSelector::ShadowDescendant); 11661 return specifiers; 11662 } 11663 specifiers->appendTagHistory(CSSSelector::SubSelector, WTF::move(newSpecifier)); 11664 return specifiers; 11665} 11666 11667PassRefPtr<StyleRuleBase> CSSParser::createPageRule(std::unique_ptr<CSSParserSelector> pageSelector) 11668{ 11669 // FIXME: Margin at-rules are ignored. 11670 m_allowImportRules = m_allowNamespaceDeclarations = false; 11671 RefPtr<StyleRulePage> rule; 11672 if (pageSelector) { 11673 rule = StyleRulePage::create(createStyleProperties()); 11674 Vector<std::unique_ptr<CSSParserSelector>> selectorVector; 11675 selectorVector.append(WTF::move(pageSelector)); 11676 rule->parserAdoptSelectorVector(selectorVector); 11677 processAndAddNewRuleToSourceTreeIfNeeded(); 11678 } else 11679 popRuleData(); 11680 clearProperties(); 11681 return rule.release(); 11682} 11683 11684std::unique_ptr<Vector<std::unique_ptr<CSSParserSelector>>> CSSParser::createSelectorVector() 11685{ 11686 if (m_recycledSelectorVector) { 11687 m_recycledSelectorVector->shrink(0); 11688 return WTF::move(m_recycledSelectorVector); 11689 } 11690 return std::make_unique<Vector<std::unique_ptr<CSSParserSelector>>>(); 11691} 11692 11693void CSSParser::recycleSelectorVector(std::unique_ptr<Vector<std::unique_ptr<CSSParserSelector>>> vector) 11694{ 11695 if (vector && !m_recycledSelectorVector) 11696 m_recycledSelectorVector = WTF::move(vector); 11697} 11698 11699PassRefPtr<StyleRuleBase> CSSParser::createRegionRule(Vector<std::unique_ptr<CSSParserSelector>>* regionSelector, RuleList* rules) 11700{ 11701 if (!cssRegionsEnabled() || !regionSelector || !rules) { 11702 popRuleData(); 11703 return 0; 11704 } 11705 11706 m_allowImportRules = m_allowNamespaceDeclarations = false; 11707 11708 RefPtr<StyleRuleRegion> regionRule = StyleRuleRegion::create(regionSelector, *rules); 11709 11710 if (isExtractingSourceData()) 11711 addNewRuleToSourceTree(CSSRuleSourceData::createUnknown()); 11712 11713 return regionRule.release(); 11714} 11715 11716void CSSParser::createMarginAtRule(CSSSelector::MarginBoxType /* marginBox */) 11717{ 11718 // FIXME: Implement margin at-rule here, using: 11719 // - marginBox: margin box 11720 // - m_parsedProperties: properties at [m_numParsedPropertiesBeforeMarginBox, m_parsedProperties.size()] are for this at-rule. 11721 // 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. 11722 11723 endDeclarationsForMarginBox(); 11724} 11725 11726void CSSParser::startDeclarationsForMarginBox() 11727{ 11728 m_numParsedPropertiesBeforeMarginBox = m_parsedProperties.size(); 11729} 11730 11731void CSSParser::endDeclarationsForMarginBox() 11732{ 11733 rollbackLastProperties(m_parsedProperties.size() - m_numParsedPropertiesBeforeMarginBox); 11734 m_numParsedPropertiesBeforeMarginBox = INVALID_NUM_PARSED_PROPERTIES; 11735} 11736 11737void CSSParser::deleteFontFaceOnlyValues() 11738{ 11739 ASSERT(m_hasFontFaceOnlyValues); 11740 for (unsigned i = 0; i < m_parsedProperties.size();) { 11741 CSSProperty& property = m_parsedProperties[i]; 11742 if (property.id() == CSSPropertyFontVariant && property.value()->isValueList()) { 11743 m_parsedProperties.remove(i); 11744 continue; 11745 } 11746 ++i; 11747 } 11748} 11749 11750PassRefPtr<StyleKeyframe> CSSParser::createKeyframe(CSSParserValueList& keys) 11751{ 11752 // Create a key string from the passed keys 11753 StringBuilder keyString; 11754 for (unsigned i = 0; i < keys.size(); ++i) { 11755 // Just as per the comment below, we ignore keyframes with 11756 // invalid key values (plain numbers or unknown identifiers) 11757 // marked as CSSPrimitiveValue::CSS_UNKNOWN during parsing. 11758 if (keys.valueAt(i)->unit == CSSPrimitiveValue::CSS_UNKNOWN) { 11759 clearProperties(); 11760 return 0; 11761 } 11762 11763 ASSERT(keys.valueAt(i)->unit == CSSPrimitiveValue::CSS_NUMBER); 11764 float key = static_cast<float>(keys.valueAt(i)->fValue); 11765 if (key < 0 || key > 100) { 11766 // As per http://www.w3.org/TR/css3-animations/#keyframes, 11767 // "If a keyframe selector specifies negative percentage values 11768 // or values higher than 100%, then the keyframe will be ignored." 11769 clearProperties(); 11770 return 0; 11771 } 11772 if (i != 0) 11773 keyString.append(','); 11774 keyString.appendNumber(key); 11775 keyString.append('%'); 11776 } 11777 11778 RefPtr<StyleKeyframe> keyframe = StyleKeyframe::create(createStyleProperties()); 11779 keyframe->setKeyText(keyString.toString()); 11780 11781 clearProperties(); 11782 11783 return keyframe.release(); 11784} 11785 11786void CSSParser::invalidBlockHit() 11787{ 11788 if (m_styleSheet && !m_hadSyntacticallyValidCSSRule) 11789 m_styleSheet->setHasSyntacticallyValidCSSHeader(false); 11790} 11791 11792void CSSParser::updateLastSelectorLineAndPosition() 11793{ 11794 m_lastSelectorLineNumber = m_lineNumber; 11795} 11796 11797void CSSParser::updateLastMediaLine(MediaQuerySet* media) 11798{ 11799 media->setLastLine(m_lineNumber); 11800} 11801 11802template <typename CharacterType> 11803static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData) 11804{ 11805 Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData; 11806 unsigned size = propertyData.size(); 11807 if (!size) 11808 return; 11809 11810 unsigned styleStart = ruleData->ruleBodyRange.start; 11811 CSSPropertySourceData* nextData = &(propertyData.at(0)); 11812 for (unsigned i = 0; i < size; ++i) { 11813 CSSPropertySourceData* currentData = nextData; 11814 nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0; 11815 11816 if (currentData->parsedOk) 11817 continue; 11818 if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';') 11819 continue; 11820 11821 unsigned propertyEndInStyleSheet; 11822 if (!nextData) 11823 propertyEndInStyleSheet = ruleData->ruleBodyRange.end - 1; 11824 else 11825 propertyEndInStyleSheet = styleStart + nextData->range.start - 1; 11826 11827 while (isHTMLSpace(characters[propertyEndInStyleSheet])) 11828 --propertyEndInStyleSheet; 11829 11830 // propertyEndInStyleSheet points at the last property text character. 11831 unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character. 11832 if (currentData->range.end != newPropertyEnd) { 11833 currentData->range.end = newPropertyEnd; 11834 unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length(); 11835 while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':') 11836 ++valueStartInStyleSheet; 11837 if (valueStartInStyleSheet < propertyEndInStyleSheet) 11838 ++valueStartInStyleSheet; // Shift past the ':'. 11839 while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace(characters[valueStartInStyleSheet])) 11840 ++valueStartInStyleSheet; 11841 // Need to exclude the trailing ';' from the property value. 11842 currentData->value = String(characters + valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1)); 11843 } 11844 } 11845} 11846 11847void CSSParser::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData) 11848{ 11849 if (!ruleData->styleSourceData) 11850 return; 11851 11852 if (is8BitSource()) { 11853 fixUnparsedProperties<LChar>(m_dataStart8.get() + m_parsedTextPrefixLength, ruleData); 11854 return; 11855 } 11856 11857 fixUnparsedProperties<UChar>(m_dataStart16.get() + m_parsedTextPrefixLength, ruleData); 11858} 11859 11860void CSSParser::markRuleHeaderStart(CSSRuleSourceData::Type ruleType) 11861{ 11862 if (!isExtractingSourceData()) 11863 return; 11864 11865 // Pop off data for a previous invalid rule. 11866 if (m_currentRuleData) 11867 m_currentRuleDataStack->removeLast(); 11868 11869 RefPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(ruleType); 11870 data->ruleHeaderRange.start = tokenStartOffset(); 11871 m_currentRuleData = data; 11872 m_currentRuleDataStack->append(data.release()); 11873} 11874 11875template <typename CharacterType> 11876inline void CSSParser::setRuleHeaderEnd(const CharacterType* dataStart) 11877{ 11878 CharacterType* listEnd = tokenStart<CharacterType>(); 11879 while (listEnd > dataStart + 1) { 11880 if (isHTMLSpace(*(listEnd - 1))) 11881 --listEnd; 11882 else 11883 break; 11884 } 11885 11886 m_currentRuleDataStack->last()->ruleHeaderRange.end = listEnd - dataStart; 11887} 11888 11889void CSSParser::markRuleHeaderEnd() 11890{ 11891 if (!isExtractingSourceData()) 11892 return; 11893 ASSERT(!m_currentRuleDataStack->isEmpty()); 11894 11895 if (is8BitSource()) 11896 setRuleHeaderEnd<LChar>(m_dataStart8.get()); 11897 else 11898 setRuleHeaderEnd<UChar>(m_dataStart16.get()); 11899} 11900 11901void CSSParser::markSelectorStart() 11902{ 11903 if (!isExtractingSourceData()) 11904 return; 11905 ASSERT(!m_selectorRange.end); 11906 11907 m_selectorRange.start = tokenStartOffset(); 11908} 11909 11910void CSSParser::markSelectorEnd() 11911{ 11912 if (!isExtractingSourceData()) 11913 return; 11914 ASSERT(!m_selectorRange.end); 11915 ASSERT(m_currentRuleDataStack->size()); 11916 11917 m_selectorRange.end = tokenStartOffset(); 11918 m_currentRuleDataStack->last()->selectorRanges.append(m_selectorRange); 11919 m_selectorRange.start = 0; 11920 m_selectorRange.end = 0; 11921} 11922 11923void CSSParser::markRuleBodyStart() 11924{ 11925 if (!isExtractingSourceData()) 11926 return; 11927 m_currentRuleData.clear(); 11928 unsigned offset = tokenStartOffset(); 11929 if (tokenStartChar() == '{') 11930 ++offset; // Skip the rule body opening brace. 11931 ASSERT(!m_currentRuleDataStack->isEmpty()); 11932 m_currentRuleDataStack->last()->ruleBodyRange.start = offset; 11933} 11934 11935void CSSParser::markRuleBodyEnd() 11936{ 11937 // Precondition: (!isExtractingSourceData()) 11938 unsigned offset = tokenStartOffset(); 11939 ASSERT(!m_currentRuleDataStack->isEmpty()); 11940 m_currentRuleDataStack->last()->ruleBodyRange.end = offset; 11941} 11942 11943void CSSParser::markPropertyStart() 11944{ 11945 m_ignoreErrorsInDeclaration = false; 11946 if (!isExtractingSourceData()) 11947 return; 11948 if (m_currentRuleDataStack->isEmpty() || !m_currentRuleDataStack->last()->styleSourceData) 11949 return; 11950 11951 m_propertyRange.start = tokenStartOffset(); 11952} 11953 11954void CSSParser::markPropertyEnd(bool isImportantFound, bool isPropertyParsed) 11955{ 11956 if (!isExtractingSourceData()) 11957 return; 11958 if (m_currentRuleDataStack->isEmpty() || !m_currentRuleDataStack->last()->styleSourceData) 11959 return; 11960 11961 unsigned offset = tokenStartOffset(); 11962 if (tokenStartChar() == ';') // Include semicolon into the property text. 11963 ++offset; 11964 m_propertyRange.end = offset; 11965 if (m_propertyRange.start != UINT_MAX && !m_currentRuleDataStack->isEmpty()) { 11966 // This stuff is only executed when the style data retrieval is requested by client. 11967 const unsigned start = m_propertyRange.start; 11968 const unsigned end = m_propertyRange.end; 11969 ASSERT_WITH_SECURITY_IMPLICATION(start < end); 11970 String propertyString; 11971 if (is8BitSource()) 11972 propertyString = String(m_dataStart8.get() + start, end - start).stripWhiteSpace(); 11973 else 11974 propertyString = String(m_dataStart16.get() + start, end - start).stripWhiteSpace(); 11975 if (propertyString.endsWith(';')) 11976 propertyString = propertyString.left(propertyString.length() - 1); 11977 size_t colonIndex = propertyString.find(':'); 11978 ASSERT(colonIndex != notFound); 11979 11980 String name = propertyString.left(colonIndex).stripWhiteSpace(); 11981 String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace(); 11982 // The property range is relative to the declaration start offset. 11983 SourceRange& topRuleBodyRange = m_currentRuleDataStack->last()->ruleBodyRange; 11984 m_currentRuleDataStack->last()->styleSourceData->propertyData.append( 11985 CSSPropertySourceData(name, value, isImportantFound, isPropertyParsed, SourceRange(start - topRuleBodyRange.start, end - topRuleBodyRange.start))); 11986 } 11987 resetPropertyRange(); 11988} 11989 11990#if ENABLE(CSS_DEVICE_ADAPTATION) 11991PassRefPtr<StyleRuleBase> CSSParser::createViewportRule() 11992{ 11993 m_allowImportRules = m_allowNamespaceDeclarations = false; 11994 11995 RefPtr<StyleRuleViewport> rule = StyleRuleViewport::create(createStyleProperties()); 11996 clearProperties(); 11997 11998 processAndAddNewRuleToSourceTreeIfNeeded(); 11999 12000 return rule.release(); 12001} 12002 12003bool CSSParser::parseViewportProperty(CSSPropertyID propId, bool important) 12004{ 12005 CSSParserValue* value = m_valueList->current(); 12006 if (!value) 12007 return false; 12008 12009 CSSValueID id = value->id; 12010 bool validPrimitive = false; 12011 12012 switch (propId) { 12013 case CSSPropertyMinWidth: // auto | device-width | device-height | <length> | <percentage> 12014 case CSSPropertyMaxWidth: 12015 case CSSPropertyMinHeight: 12016 case CSSPropertyMaxHeight: 12017 if (id == CSSValueAuto || id == CSSValueDeviceWidth || id == CSSValueDeviceHeight) 12018 validPrimitive = true; 12019 else 12020 validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg)); 12021 break; 12022 case CSSPropertyWidth: // shorthand 12023 return parseViewportShorthand(propId, CSSPropertyMinWidth, CSSPropertyMaxWidth, important); 12024 case CSSPropertyHeight: 12025 return parseViewportShorthand(propId, CSSPropertyMinHeight, CSSPropertyMaxHeight, important); 12026 case CSSPropertyMinZoom: // auto | <number> | <percentage> 12027 case CSSPropertyMaxZoom: 12028 case CSSPropertyZoom: 12029 if (id == CSSValueAuto) 12030 validPrimitive = true; 12031 else 12032 validPrimitive = (!id && validUnit(value, FNumber | FPercent | FNonNeg)); 12033 break; 12034 case CSSPropertyUserZoom: // zoom | fixed 12035 if (id == CSSValueZoom || id == CSSValueFixed) 12036 validPrimitive = true; 12037 break; 12038 case CSSPropertyOrientation: // auto | portrait | landscape 12039 if (id == CSSValueAuto || id == CSSValuePortrait || id == CSSValueLandscape) 12040 validPrimitive = true; 12041 default: 12042 break; 12043 } 12044 12045 RefPtr<CSSValue> parsedValue; 12046 if (validPrimitive) { 12047 parsedValue = parseValidPrimitive(id, value); 12048 m_valueList->next(); 12049 } 12050 12051 if (parsedValue) { 12052 if (!m_valueList->current() || inShorthand()) { 12053 addProperty(propId, parsedValue.release(), important); 12054 return true; 12055 } 12056 } 12057 12058 return false; 12059} 12060 12061bool CSSParser::parseViewportShorthand(CSSPropertyID propId, CSSPropertyID first, CSSPropertyID second, bool important) 12062{ 12063 unsigned numValues = m_valueList->size(); 12064 12065 if (numValues > 2) 12066 return false; 12067 12068 ShorthandScope scope(this, propId); 12069 12070 if (!parseViewportProperty(first, important)) 12071 return false; 12072 12073 // If just one value is supplied, the second value 12074 // is implicitly initialized with the first value. 12075 if (numValues == 1) 12076 m_valueList->previous(); 12077 12078 return parseViewportProperty(second, important); 12079} 12080#endif 12081 12082template <typename CharacterType> 12083static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length) 12084{ 12085 char buffer[maxCSSPropertyNameLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character 12086 12087 for (unsigned i = 0; i != length; ++i) { 12088 CharacterType c = propertyName[i]; 12089 if (c == 0 || c >= 0x7F) 12090 return CSSPropertyInvalid; // illegal character 12091 buffer[i] = toASCIILower(c); 12092 } 12093 buffer[length] = '\0'; 12094 12095 const char* name = buffer; 12096 if (buffer[0] == '-') { 12097#if ENABLE(LEGACY_CSS_VENDOR_PREFIXES) 12098 // If the prefix is -apple- or -khtml-, change it to -webkit-. 12099 // This makes the string one character longer. 12100 if (RuntimeEnabledFeatures::sharedFeatures().legacyCSSVendorPrefixesEnabled() 12101 && (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-"))) { 12102 memmove(buffer + 7, buffer + 6, length + 1 - 6); 12103 memcpy(buffer, "-webkit", 7); 12104 ++length; 12105 } 12106#endif 12107#if PLATFORM(IOS) 12108 cssPropertyNameIOSAliasing(buffer, name, length); 12109#endif 12110 } 12111 12112 const Property* hashTableEntry = findProperty(name, length); 12113 return hashTableEntry ? static_cast<CSSPropertyID>(hashTableEntry->id) : CSSPropertyInvalid; 12114} 12115 12116CSSPropertyID cssPropertyID(const String& string) 12117{ 12118 unsigned length = string.length(); 12119 12120 if (!length) 12121 return CSSPropertyInvalid; 12122 if (length > maxCSSPropertyNameLength) 12123 return CSSPropertyInvalid; 12124 12125 return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length); 12126} 12127 12128CSSPropertyID cssPropertyID(const CSSParserString& string) 12129{ 12130 unsigned length = string.length(); 12131 12132 if (!length) 12133 return CSSPropertyInvalid; 12134 if (length > maxCSSPropertyNameLength) 12135 return CSSPropertyInvalid; 12136 12137 return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length); 12138} 12139 12140#if PLATFORM(IOS) 12141void cssPropertyNameIOSAliasing(const char* propertyName, const char*& propertyNameAlias, unsigned& newLength) 12142{ 12143 if (!strcmp(propertyName, "-webkit-hyphenate-locale")) { 12144 // Worked in iOS 4.2. 12145 static const char* const webkitLocale = "-webkit-locale"; 12146 propertyNameAlias = webkitLocale; 12147 newLength = strlen(webkitLocale); 12148 } 12149} 12150#endif 12151 12152template <typename CharacterType> 12153static CSSValueID cssValueKeywordID(const CharacterType* valueKeyword, unsigned length) 12154{ 12155 char buffer[maxCSSValueKeywordLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character 12156 12157 for (unsigned i = 0; i != length; ++i) { 12158 CharacterType c = valueKeyword[i]; 12159 if (c == 0 || c >= 0x7F) 12160 return CSSValueInvalid; // illegal keyword. 12161 buffer[i] = WTF::toASCIILower(c); 12162 } 12163 buffer[length] = '\0'; 12164 12165 if (buffer[0] == '-') { 12166 // If the prefix is -apple- or -khtml-, change it to -webkit-. 12167 // This makes the string one character longer. 12168 // On iOS we don't want to change values starting with -apple-system to -webkit-system. 12169 if ((hasPrefix(buffer, length, "-apple-") && !hasPrefix(buffer, length, "-apple-system")) || hasPrefix(buffer, length, "-khtml-")) { 12170 memmove(buffer + 7, buffer + 6, length + 1 - 6); 12171 memcpy(buffer, "-webkit", 7); 12172 ++length; 12173 } 12174 } 12175 12176 const Value* hashTableEntry = findValue(buffer, length); 12177 return hashTableEntry ? static_cast<CSSValueID>(hashTableEntry->id) : CSSValueInvalid; 12178} 12179 12180CSSValueID cssValueKeywordID(const CSSParserString& string) 12181{ 12182 unsigned length = string.length(); 12183 if (!length) 12184 return CSSValueInvalid; 12185 if (length > maxCSSValueKeywordLength) 12186 return CSSValueInvalid; 12187 12188 return string.is8Bit() ? cssValueKeywordID(string.characters8(), length) : cssValueKeywordID(string.characters16(), length); 12189} 12190 12191template <typename CharacterType> 12192static inline bool isCSSTokenizerIdentifier(const CharacterType* characters, unsigned length) 12193{ 12194 const CharacterType* end = characters + length; 12195 12196 // -? 12197 if (characters != end && characters[0] == '-') 12198 ++characters; 12199 12200 // {nmstart} 12201 if (characters == end || !(characters[0] == '_' || characters[0] >= 128 || isASCIIAlpha(characters[0]))) 12202 return false; 12203 ++characters; 12204 12205 // {nmchar}* 12206 for (; characters != end; ++characters) { 12207 if (!(characters[0] == '_' || characters[0] == '-' || characters[0] >= 128 || isASCIIAlphanumeric(characters[0]))) 12208 return false; 12209 } 12210 12211 return true; 12212} 12213 12214// "ident" from the CSS tokenizer, minus backslash-escape sequences 12215static bool isCSSTokenizerIdentifier(const String& string) 12216{ 12217 unsigned length = string.length(); 12218 12219 if (!length) 12220 return false; 12221 12222 if (string.is8Bit()) 12223 return isCSSTokenizerIdentifier(string.characters8(), length); 12224 return isCSSTokenizerIdentifier(string.characters16(), length); 12225} 12226 12227template <typename CharacterType> 12228static inline bool isCSSTokenizerURL(const CharacterType* characters, unsigned length) 12229{ 12230 const CharacterType* end = characters + length; 12231 12232 for (; characters != end; ++characters) { 12233 CharacterType c = characters[0]; 12234 switch (c) { 12235 case '!': 12236 case '#': 12237 case '$': 12238 case '%': 12239 case '&': 12240 break; 12241 default: 12242 if (c < '*') 12243 return false; 12244 if (c <= '~') 12245 break; 12246 if (c < 128) 12247 return false; 12248 } 12249 } 12250 12251 return true; 12252} 12253 12254// "url" from the CSS tokenizer, minus backslash-escape sequences 12255static bool isCSSTokenizerURL(const String& string) 12256{ 12257 unsigned length = string.length(); 12258 12259 if (!length) 12260 return true; 12261 12262 if (string.is8Bit()) 12263 return isCSSTokenizerURL(string.characters8(), length); 12264 return isCSSTokenizerURL(string.characters16(), length); 12265} 12266 12267 12268template <typename CharacterType> 12269static inline String quoteCSSStringInternal(const CharacterType* characters, unsigned length) 12270{ 12271 // For efficiency, we first pre-calculate the length of the quoted string, then we build the actual one. 12272 // Please see below for the actual logic. 12273 unsigned quotedStringSize = 2; // Two quotes surrounding the entire string. 12274 bool afterEscape = false; 12275 for (unsigned i = 0; i < length; ++i) { 12276 CharacterType ch = characters[i]; 12277 if (ch == '\\' || ch == '\'') { 12278 quotedStringSize += 2; 12279 afterEscape = false; 12280 } else if (ch < 0x20 || ch == 0x7F) { 12281 quotedStringSize += 2 + (ch >= 0x10); 12282 afterEscape = true; 12283 } else { 12284 quotedStringSize += 1 + (afterEscape && (isASCIIHexDigit(ch) || ch == ' ')); 12285 afterEscape = false; 12286 } 12287 } 12288 12289 StringBuffer<CharacterType> buffer(quotedStringSize); 12290 unsigned index = 0; 12291 buffer[index++] = '\''; 12292 afterEscape = false; 12293 for (unsigned i = 0; i < length; ++i) { 12294 CharacterType ch = characters[i]; 12295 if (ch == '\\' || ch == '\'') { 12296 buffer[index++] = '\\'; 12297 buffer[index++] = ch; 12298 afterEscape = false; 12299 } else if (ch < 0x20 || ch == 0x7F) { // Control characters. 12300 buffer[index++] = '\\'; 12301 placeByteAsHexCompressIfPossible(ch, buffer, index, Lowercase); 12302 afterEscape = true; 12303 } else { 12304 // Space character may be required to separate backslash-escape sequence and normal characters. 12305 if (afterEscape && (isASCIIHexDigit(ch) || ch == ' ')) 12306 buffer[index++] = ' '; 12307 buffer[index++] = ch; 12308 afterEscape = false; 12309 } 12310 } 12311 buffer[index++] = '\''; 12312 12313 ASSERT(quotedStringSize == index); 12314 return String::adopt(buffer); 12315} 12316 12317// We use single quotes for now because markup.cpp uses double quotes. 12318String quoteCSSString(const String& string) 12319{ 12320 // This function expands each character to at most 3 characters ('\u0010' -> '\' '1' '0') as well as adds 12321 // 2 quote characters (before and after). Make sure the resulting size (3 * length + 2) will not overflow unsigned. 12322 12323 unsigned length = string.length(); 12324 12325 if (!length) 12326 return String("\'\'"); 12327 12328 if (length > std::numeric_limits<unsigned>::max() / 3 - 2) 12329 return emptyString(); 12330 12331 if (string.is8Bit()) 12332 return quoteCSSStringInternal(string.characters8(), length); 12333 return quoteCSSStringInternal(string.characters16(), length); 12334} 12335 12336String quoteCSSStringIfNeeded(const String& string) 12337{ 12338 return isCSSTokenizerIdentifier(string) ? string : quoteCSSString(string); 12339} 12340 12341String quoteCSSURLIfNeeded(const String& string) 12342{ 12343 return isCSSTokenizerURL(string) ? string : quoteCSSString(string); 12344} 12345 12346bool isValidNthToken(const CSSParserString& token) 12347{ 12348 // The tokenizer checks for the construct of an+b. 12349 // However, since the {ident} rule precedes the {nth} rule, some of those 12350 // tokens are identified as string literal. Furthermore we need to accept 12351 // "odd" and "even" which does not match to an+b. 12352 return equalIgnoringCase(token, "odd") || equalIgnoringCase(token, "even") 12353 || equalIgnoringCase(token, "n") || equalIgnoringCase(token, "-n"); 12354} 12355 12356} 12357