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 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 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 "CSSFontFaceRule.h" 37#include "CSSFontFaceSrcValue.h" 38#include "CSSFunctionValue.h" 39#include "CSSGradientValue.h" 40#include "CSSImageValue.h" 41#include "CSSInheritedValue.h" 42#include "CSSInitialValue.h" 43#include "CSSLineBoxContainValue.h" 44#include "CSSMediaRule.h" 45#include "CSSPageRule.h" 46#include "CSSPrimitiveValue.h" 47#include "CSSPropertySourceData.h" 48#include "CSSReflectValue.h" 49#include "CSSSelector.h" 50#include "CSSStyleSheet.h" 51#include "CSSTimingFunctionValue.h" 52#include "CSSUnicodeRangeValue.h" 53#include "CSSValueKeywords.h" 54#include "CSSValueList.h" 55#include "CSSValuePool.h" 56#if ENABLE(CSS_VARIABLES) 57#include "CSSVariableValue.h" 58#endif 59#include "Counter.h" 60#include "Document.h" 61#include "FloatConversion.h" 62#include "FontFeatureValue.h" 63#include "FontValue.h" 64#include "HTMLParserIdioms.h" 65#include "HashTools.h" 66#include "HistogramSupport.h" 67#include "MediaList.h" 68#include "MediaQueryExp.h" 69#include "Page.h" 70#include "PageConsole.h" 71#include "Pair.h" 72#include "Rect.h" 73#include "RenderTheme.h" 74#include "RuntimeEnabledFeatures.h" 75#include "SVGParserUtilities.h" 76#include "Settings.h" 77#include "ShadowValue.h" 78#include "StylePropertySet.h" 79#include "StylePropertyShorthand.h" 80#include "StyleRule.h" 81#include "StyleRuleImport.h" 82#include "StyleSheetContents.h" 83#include "TextEncoding.h" 84#include "WebKitCSSKeyframeRule.h" 85#include "WebKitCSSKeyframesRule.h" 86#include "WebKitCSSRegionRule.h" 87#include "WebKitCSSTransformValue.h" 88#include <limits.h> 89#include <wtf/BitArray.h> 90#include <wtf/HexNumber.h> 91#include <wtf/dtoa.h> 92#include <wtf/text/StringBuffer.h> 93#include <wtf/text/StringBuilder.h> 94#include <wtf/text/StringImpl.h> 95 96#if ENABLE(CSS_IMAGE_SET) 97#include "CSSImageSetValue.h" 98#endif 99 100#if ENABLE(CSS_FILTERS) 101#include "WebKitCSSFilterValue.h" 102#if ENABLE(SVG) 103#include "WebKitCSSSVGDocumentValue.h" 104#endif 105#endif 106 107#if ENABLE(CSS_SHADERS) 108#include "WebKitCSSArrayFunctionValue.h" 109#include "WebKitCSSMatFunctionValue.h" 110#include "WebKitCSSMixFunctionValue.h" 111#include "WebKitCSSShaderValue.h" 112#endif 113 114#if ENABLE(DASHBOARD_SUPPORT) 115#include "DashboardRegion.h" 116#endif 117 118#define YYDEBUG 0 119 120#if YYDEBUG > 0 121extern int cssyydebug; 122#endif 123 124extern int cssyyparse(WebCore::CSSParser*); 125 126using namespace std; 127using namespace WTF; 128 129namespace { 130 131enum PropertyType { 132 PropertyExplicit, 133 PropertyImplicit 134}; 135 136class ImplicitScope { 137 WTF_MAKE_NONCOPYABLE(ImplicitScope); 138public: 139 ImplicitScope(WebCore::CSSParser* parser, PropertyType propertyType) 140 : m_parser(parser) 141 { 142 m_parser->m_implicitShorthand = propertyType == PropertyImplicit; 143 } 144 145 ~ImplicitScope() 146 { 147 m_parser->m_implicitShorthand = false; 148 } 149 150private: 151 WebCore::CSSParser* m_parser; 152}; 153 154} // namespace 155 156namespace WebCore { 157 158static const unsigned INVALID_NUM_PARSED_PROPERTIES = UINT_MAX; 159static const double MAX_SCALE = 1000000; 160 161template <unsigned N> 162static bool equal(const CSSParserString& a, const char (&b)[N]) 163{ 164 unsigned length = N - 1; // Ignore the trailing null character 165 if (a.length() != length) 166 return false; 167 168 return a.is8Bit() ? WTF::equal(a.characters8(), reinterpret_cast<const LChar*>(b), length) : WTF::equal(a.characters16(), reinterpret_cast<const LChar*>(b), length); 169} 170 171template <unsigned N> 172static bool equalIgnoringCase(const CSSParserString& a, const char (&b)[N]) 173{ 174 unsigned length = N - 1; // Ignore the trailing null character 175 if (a.length() != length) 176 return false; 177 178 return a.is8Bit() ? WTF::equalIgnoringCase(b, a.characters8(), length) : WTF::equalIgnoringCase(b, a.characters16(), length); 179} 180 181template <unsigned N> 182static bool equalIgnoringCase(CSSParserValue* value, const char (&b)[N]) 183{ 184 ASSERT(value->unit == CSSPrimitiveValue::CSS_IDENT || value->unit == CSSPrimitiveValue::CSS_STRING); 185 return equalIgnoringCase(value->string, b); 186} 187 188static bool hasPrefix(const char* string, unsigned length, const char* prefix) 189{ 190 for (unsigned i = 0; i < length; ++i) { 191 if (!prefix[i]) 192 return true; 193 if (string[i] != prefix[i]) 194 return false; 195 } 196 return false; 197} 198 199static PassRefPtr<CSSPrimitiveValue> createPrimitiveValuePair(PassRefPtr<CSSPrimitiveValue> first, PassRefPtr<CSSPrimitiveValue> second) 200{ 201 return cssValuePool().createValue(Pair::create(first, second)); 202} 203 204class AnimationParseContext { 205public: 206 AnimationParseContext() 207 : m_animationPropertyKeywordAllowed(true) 208 , m_firstAnimationCommitted(false) 209 , m_hasSeenAnimationPropertyKeyword(false) 210 { 211 } 212 213 void commitFirstAnimation() 214 { 215 m_firstAnimationCommitted = true; 216 } 217 218 bool hasCommittedFirstAnimation() const 219 { 220 return m_firstAnimationCommitted; 221 } 222 223 void commitAnimationPropertyKeyword() 224 { 225 m_animationPropertyKeywordAllowed = false; 226 } 227 228 bool animationPropertyKeywordAllowed() const 229 { 230 return m_animationPropertyKeywordAllowed; 231 } 232 233 bool hasSeenAnimationPropertyKeyword() const 234 { 235 return m_hasSeenAnimationPropertyKeyword; 236 } 237 238 void sawAnimationPropertyKeyword() 239 { 240 m_hasSeenAnimationPropertyKeyword = true; 241 } 242 243private: 244 bool m_animationPropertyKeywordAllowed; 245 bool m_firstAnimationCommitted; 246 bool m_hasSeenAnimationPropertyKeyword; 247}; 248 249const CSSParserContext& strictCSSParserContext() 250{ 251 DEFINE_STATIC_LOCAL(CSSParserContext, strictContext, (CSSStrictMode)); 252 return strictContext; 253} 254 255CSSParserContext::CSSParserContext(CSSParserMode mode, const KURL& baseURL) 256 : baseURL(baseURL) 257 , mode(mode) 258 , isHTMLDocument(false) 259 , isCSSCustomFilterEnabled(false) 260 , isCSSStickyPositionEnabled(false) 261 , isCSSRegionsEnabled(false) 262 , isCSSCompositingEnabled(false) 263 , isCSSGridLayoutEnabled(false) 264#if ENABLE(CSS_VARIABLES) 265 , isCSSVariablesEnabled(false) 266#endif 267 , needsSiteSpecificQuirks(false) 268 , enforcesCSSMIMETypeInNoQuirksMode(true) 269 , useLegacyBackgroundSizeShorthandBehavior(false) 270{ 271} 272 273CSSParserContext::CSSParserContext(Document* document, const KURL& baseURL, const String& charset) 274 : baseURL(baseURL.isNull() ? document->baseURL() : baseURL) 275 , charset(charset) 276 , mode(document->inQuirksMode() ? CSSQuirksMode : CSSStrictMode) 277 , isHTMLDocument(document->isHTMLDocument()) 278 , isCSSCustomFilterEnabled(document->settings() ? document->settings()->isCSSCustomFilterEnabled() : false) 279 , isCSSStickyPositionEnabled(document->cssStickyPositionEnabled()) 280 , isCSSRegionsEnabled(document->cssRegionsEnabled()) 281 , isCSSCompositingEnabled(document->cssCompositingEnabled()) 282 , isCSSGridLayoutEnabled(document->cssGridLayoutEnabled()) 283#if ENABLE(CSS_VARIABLES) 284 , isCSSVariablesEnabled(document->settings() ? document->settings()->cssVariablesEnabled() : false) 285#endif 286 , needsSiteSpecificQuirks(document->settings() ? document->settings()->needsSiteSpecificQuirks() : false) 287 , enforcesCSSMIMETypeInNoQuirksMode(!document->settings() || document->settings()->enforceCSSMIMETypeInNoQuirksMode()) 288 , useLegacyBackgroundSizeShorthandBehavior(document->settings() ? document->settings()->useLegacyBackgroundSizeShorthandBehavior() : false) 289{ 290} 291 292bool operator==(const CSSParserContext& a, const CSSParserContext& b) 293{ 294 return a.baseURL == b.baseURL 295 && a.charset == b.charset 296 && a.mode == b.mode 297 && a.isHTMLDocument == b.isHTMLDocument 298 && a.isCSSCustomFilterEnabled == b.isCSSCustomFilterEnabled 299 && a.isCSSStickyPositionEnabled == b.isCSSStickyPositionEnabled 300 && a.isCSSRegionsEnabled == b.isCSSRegionsEnabled 301 && a.isCSSCompositingEnabled == b.isCSSCompositingEnabled 302 && a.isCSSGridLayoutEnabled == b.isCSSGridLayoutEnabled 303#if ENABLE(CSS_VARIABLES) 304 && a.isCSSVariablesEnabled == b.isCSSVariablesEnabled 305#endif 306 && a.needsSiteSpecificQuirks == b.needsSiteSpecificQuirks 307 && a.enforcesCSSMIMETypeInNoQuirksMode == b.enforcesCSSMIMETypeInNoQuirksMode 308 && a.useLegacyBackgroundSizeShorthandBehavior == b.useLegacyBackgroundSizeShorthandBehavior; 309} 310 311CSSParser::CSSParser(const CSSParserContext& context) 312 : m_context(context) 313 , m_important(false) 314 , m_id(CSSPropertyInvalid) 315 , m_styleSheet(0) 316#if ENABLE(CSS3_CONDITIONAL_RULES) 317 , m_supportsCondition(false) 318#endif 319 , m_selectorListForParseSelector(0) 320 , m_numParsedPropertiesBeforeMarginBox(INVALID_NUM_PARSED_PROPERTIES) 321 , m_inParseShorthand(0) 322 , m_currentShorthand(CSSPropertyInvalid) 323 , m_implicitShorthand(false) 324 , m_hasFontFaceOnlyValues(false) 325 , m_hadSyntacticallyValidCSSRule(false) 326 , m_logErrors(false) 327 , m_ignoreErrorsInDeclaration(false) 328#if ENABLE(CSS_SHADERS) 329 , m_inFilterRule(false) 330#endif 331 , m_defaultNamespace(starAtom) 332 , m_parsedTextPrefixLength(0) 333 , m_propertyRange(UINT_MAX, UINT_MAX) 334 , m_ruleSourceDataResult(0) 335 , m_parsingMode(NormalMode) 336 , m_is8BitSource(false) 337 , m_currentCharacter8(0) 338 , m_currentCharacter16(0) 339 , m_length(0) 340 , m_token(0) 341 , m_lineNumber(0) 342 , m_tokenStartLineNumber(0) 343 , m_lastSelectorLineNumber(0) 344 , m_allowImportRules(true) 345 , m_allowNamespaceDeclarations(true) 346#if ENABLE(CSS_DEVICE_ADAPTATION) 347 , m_inViewport(false) 348#endif 349{ 350#if YYDEBUG > 0 351 cssyydebug = 1; 352#endif 353 m_tokenStart.ptr8 = 0; 354} 355 356CSSParser::~CSSParser() 357{ 358 clearProperties(); 359 360 deleteAllValues(m_floatingSelectors); 361 deleteAllValues(m_floatingSelectorVectors); 362 deleteAllValues(m_floatingValueLists); 363 deleteAllValues(m_floatingFunctions); 364} 365 366template <typename CharacterType> 367ALWAYS_INLINE static void makeLower(const CharacterType* input, CharacterType* output, unsigned length) 368{ 369 // FIXME: If we need Unicode lowercasing here, then we probably want the real kind 370 // that can potentially change the length of the string rather than the character 371 // by character kind. If we don't need Unicode lowercasing, it would be good to 372 // simplify this function. 373 374 if (charactersAreAllASCII(input, length)) { 375 // Fast case for all-ASCII. 376 for (unsigned i = 0; i < length; i++) 377 output[i] = toASCIILower(input[i]); 378 } else { 379 for (unsigned i = 0; i < length; i++) 380 output[i] = Unicode::toLower(input[i]); 381 } 382} 383 384void CSSParserString::lower() 385{ 386 if (is8Bit()) { 387 makeLower(characters8(), characters8(), length()); 388 return; 389 } 390 391 makeLower(characters16(), characters16(), length()); 392} 393 394#if ENABLE(CSS_VARIABLES) 395AtomicString CSSParserString::substring(unsigned position, unsigned length) const 396{ 397 ASSERT(m_length >= position + length); 398 399 if (is8Bit()) 400 return AtomicString(characters8() + position, length); 401 return AtomicString(characters16() + position, length); 402} 403#endif 404 405void CSSParser::setupParser(const char* prefix, unsigned prefixLength, const String& string, const char* suffix, unsigned suffixLength) 406{ 407 m_parsedTextPrefixLength = prefixLength; 408 unsigned stringLength = string.length(); 409 unsigned length = stringLength + m_parsedTextPrefixLength + suffixLength + 1; 410 m_length = length; 411 412 if (!stringLength || string.is8Bit()) { 413 m_dataStart8 = adoptArrayPtr(new LChar[length]); 414 for (unsigned i = 0; i < m_parsedTextPrefixLength; i++) 415 m_dataStart8[i] = prefix[i]; 416 417 if (stringLength) 418 memcpy(m_dataStart8.get() + m_parsedTextPrefixLength, string.characters8(), stringLength * sizeof(LChar)); 419 420 unsigned start = m_parsedTextPrefixLength + stringLength; 421 unsigned end = start + suffixLength; 422 for (unsigned i = start; i < end; i++) 423 m_dataStart8[i] = suffix[i - start]; 424 425 m_dataStart8[length - 1] = 0; 426 427 m_is8BitSource = true; 428 m_currentCharacter8 = m_dataStart8.get(); 429 m_currentCharacter16 = 0; 430 setTokenStart<LChar>(m_currentCharacter8); 431 m_lexFunc = &CSSParser::realLex<LChar>; 432 return; 433 } 434 435 m_dataStart16 = adoptArrayPtr(new UChar[length]); 436 for (unsigned i = 0; i < m_parsedTextPrefixLength; i++) 437 m_dataStart16[i] = prefix[i]; 438 439 memcpy(m_dataStart16.get() + m_parsedTextPrefixLength, string.characters(), stringLength * sizeof(UChar)); 440 441 unsigned start = m_parsedTextPrefixLength + stringLength; 442 unsigned end = start + suffixLength; 443 for (unsigned i = start; i < end; i++) 444 m_dataStart16[i] = suffix[i - start]; 445 446 m_dataStart16[length - 1] = 0; 447 448 m_is8BitSource = false; 449 m_currentCharacter8 = 0; 450 m_currentCharacter16 = m_dataStart16.get(); 451 setTokenStart<UChar>(m_currentCharacter16); 452 m_lexFunc = &CSSParser::realLex<UChar>; 453} 454 455void CSSParser::parseSheet(StyleSheetContents* sheet, const String& string, int startLineNumber, RuleSourceDataList* ruleSourceDataResult, bool logErrors) 456{ 457 setStyleSheet(sheet); 458 m_defaultNamespace = starAtom; // Reset the default namespace. 459 if (ruleSourceDataResult) 460 m_currentRuleDataStack = adoptPtr(new RuleSourceDataList()); 461 m_ruleSourceDataResult = ruleSourceDataResult; 462 463 m_logErrors = logErrors && sheet->singleOwnerDocument() && !sheet->baseURL().isEmpty() && sheet->singleOwnerDocument()->page(); 464 m_ignoreErrorsInDeclaration = false; 465 m_lineNumber = startLineNumber; 466 setupParser("", string, ""); 467 cssyyparse(this); 468 sheet->shrinkToFit(); 469 m_currentRuleDataStack.clear(); 470 m_ruleSourceDataResult = 0; 471 m_rule = 0; 472 m_ignoreErrorsInDeclaration = false; 473 m_logErrors = false; 474} 475 476PassRefPtr<StyleRuleBase> CSSParser::parseRule(StyleSheetContents* sheet, const String& string) 477{ 478 setStyleSheet(sheet); 479 m_allowNamespaceDeclarations = false; 480 setupParser("@-webkit-rule{", string, "} "); 481 cssyyparse(this); 482 return m_rule.release(); 483} 484 485PassRefPtr<StyleKeyframe> CSSParser::parseKeyframeRule(StyleSheetContents* sheet, const String& string) 486{ 487 setStyleSheet(sheet); 488 setupParser("@-webkit-keyframe-rule{ ", string, "} "); 489 cssyyparse(this); 490 return m_keyframe.release(); 491} 492 493#if ENABLE(CSS3_CONDITIONAL_RULES) 494bool CSSParser::parseSupportsCondition(const String& string) 495{ 496 m_supportsCondition = false; 497 setupParser("@-webkit-supports-condition{ ", string, "} "); 498 cssyyparse(this); 499 return m_supportsCondition; 500} 501#endif 502 503static inline bool isColorPropertyID(CSSPropertyID propertyId) 504{ 505 switch (propertyId) { 506 case CSSPropertyColor: 507 case CSSPropertyBackgroundColor: 508 case CSSPropertyBorderBottomColor: 509 case CSSPropertyBorderLeftColor: 510 case CSSPropertyBorderRightColor: 511 case CSSPropertyBorderTopColor: 512 case CSSPropertyOutlineColor: 513 case CSSPropertyTextLineThroughColor: 514 case CSSPropertyTextOverlineColor: 515 case CSSPropertyTextUnderlineColor: 516 case CSSPropertyWebkitBorderAfterColor: 517 case CSSPropertyWebkitBorderBeforeColor: 518 case CSSPropertyWebkitBorderEndColor: 519 case CSSPropertyWebkitBorderStartColor: 520 case CSSPropertyWebkitColumnRuleColor: 521#if ENABLE(CSS3_TEXT) 522 case CSSPropertyWebkitTextDecorationColor: 523#endif // CSS3_TEXT 524 case CSSPropertyWebkitTextEmphasisColor: 525 case CSSPropertyWebkitTextFillColor: 526 case CSSPropertyWebkitTextStrokeColor: 527 return true; 528 default: 529 return false; 530 } 531} 532 533static bool parseColorValue(MutableStylePropertySet* declaration, CSSPropertyID propertyId, const String& string, bool important, CSSParserMode cssParserMode) 534{ 535 ASSERT(!string.isEmpty()); 536 bool strict = isStrictParserMode(cssParserMode); 537 if (!isColorPropertyID(propertyId)) 538 return false; 539 CSSParserString cssString; 540 cssString.init(string); 541 int valueID = cssValueKeywordID(cssString); 542 bool validPrimitive = false; 543 if (valueID == CSSValueWebkitText) 544 validPrimitive = true; 545 else if (valueID == CSSValueCurrentcolor) 546 validPrimitive = true; 547 else if ((valueID >= CSSValueAqua && valueID <= CSSValueWindowtext) || valueID == CSSValueMenu 548 || (valueID >= CSSValueWebkitFocusRingColor && valueID < CSSValueWebkitText && !strict)) { 549 validPrimitive = true; 550 } 551 552 if (validPrimitive) { 553 RefPtr<CSSValue> value = cssValuePool().createIdentifierValue(valueID); 554 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 555 return true; 556 } 557 RGBA32 color; 558 if (!CSSParser::fastParseColor(color, string, strict && string[0] != '#')) 559 return false; 560 RefPtr<CSSValue> value = cssValuePool().createColorValue(color); 561 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 562 return true; 563} 564 565static inline bool isSimpleLengthPropertyID(CSSPropertyID propertyId, bool& acceptsNegativeNumbers) 566{ 567 switch (propertyId) { 568 case CSSPropertyFontSize: 569 case CSSPropertyHeight: 570 case CSSPropertyWidth: 571 case CSSPropertyMinHeight: 572 case CSSPropertyMinWidth: 573 case CSSPropertyPaddingBottom: 574 case CSSPropertyPaddingLeft: 575 case CSSPropertyPaddingRight: 576 case CSSPropertyPaddingTop: 577 case CSSPropertyWebkitLogicalWidth: 578 case CSSPropertyWebkitLogicalHeight: 579 case CSSPropertyWebkitMinLogicalWidth: 580 case CSSPropertyWebkitMinLogicalHeight: 581 case CSSPropertyWebkitPaddingAfter: 582 case CSSPropertyWebkitPaddingBefore: 583 case CSSPropertyWebkitPaddingEnd: 584 case CSSPropertyWebkitPaddingStart: 585 acceptsNegativeNumbers = false; 586 return true; 587#if ENABLE(CSS_SHAPES) 588 case CSSPropertyWebkitShapeMargin: 589 case CSSPropertyWebkitShapePadding: 590 acceptsNegativeNumbers = false; 591 return RuntimeEnabledFeatures::cssShapesEnabled(); 592#endif 593 case CSSPropertyBottom: 594 case CSSPropertyLeft: 595 case CSSPropertyMarginBottom: 596 case CSSPropertyMarginLeft: 597 case CSSPropertyMarginRight: 598 case CSSPropertyMarginTop: 599 case CSSPropertyRight: 600 case CSSPropertyTop: 601 case CSSPropertyWebkitMarginAfter: 602 case CSSPropertyWebkitMarginBefore: 603 case CSSPropertyWebkitMarginEnd: 604 case CSSPropertyWebkitMarginStart: 605 acceptsNegativeNumbers = true; 606 return true; 607 default: 608 return false; 609 } 610} 611 612template <typename CharacterType> 613static inline bool parseSimpleLength(const CharacterType* characters, unsigned& length, CSSPrimitiveValue::UnitTypes& unit, double& number) 614{ 615 if (length > 2 && (characters[length - 2] | 0x20) == 'p' && (characters[length - 1] | 0x20) == 'x') { 616 length -= 2; 617 unit = CSSPrimitiveValue::CSS_PX; 618 } else if (length > 1 && characters[length - 1] == '%') { 619 length -= 1; 620 unit = CSSPrimitiveValue::CSS_PERCENTAGE; 621 } 622 623 // We rely on charactersToDouble for validation as well. The function 624 // will set "ok" to "false" if the entire passed-in character range does 625 // not represent a double. 626 bool ok; 627 number = charactersToDouble(characters, length, &ok); 628 return ok; 629} 630 631static bool parseSimpleLengthValue(MutableStylePropertySet* declaration, CSSPropertyID propertyId, const String& string, bool important, CSSParserMode cssParserMode) 632{ 633 ASSERT(!string.isEmpty()); 634 bool acceptsNegativeNumbers; 635 if (!isSimpleLengthPropertyID(propertyId, acceptsNegativeNumbers)) 636 return false; 637 638 unsigned length = string.length(); 639 double number; 640 CSSPrimitiveValue::UnitTypes unit = CSSPrimitiveValue::CSS_NUMBER; 641 642 if (string.is8Bit()) { 643 if (!parseSimpleLength(string.characters8(), length, unit, number)) 644 return false; 645 } else { 646 if (!parseSimpleLength(string.characters16(), length, unit, number)) 647 return false; 648 } 649 650 if (unit == CSSPrimitiveValue::CSS_NUMBER) { 651 if (number && isStrictParserMode(cssParserMode)) 652 return false; 653 unit = CSSPrimitiveValue::CSS_PX; 654 } 655 if (number < 0 && !acceptsNegativeNumbers) 656 return false; 657 658 RefPtr<CSSValue> value = cssValuePool().createValue(number, unit); 659 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 660 return true; 661} 662 663static inline bool isValidKeywordPropertyAndValue(CSSPropertyID propertyId, int valueID, const CSSParserContext& parserContext) 664{ 665 if (!valueID) 666 return false; 667 668 switch (propertyId) { 669 case CSSPropertyBorderCollapse: // collapse | separate | inherit 670 if (valueID == CSSValueCollapse || valueID == CSSValueSeparate) 671 return true; 672 break; 673 case CSSPropertyBorderTopStyle: // <border-style> | inherit 674 case CSSPropertyBorderRightStyle: // Defined as: none | hidden | dotted | dashed | 675 case CSSPropertyBorderBottomStyle: // solid | double | groove | ridge | inset | outset 676 case CSSPropertyBorderLeftStyle: 677 case CSSPropertyWebkitBorderAfterStyle: 678 case CSSPropertyWebkitBorderBeforeStyle: 679 case CSSPropertyWebkitBorderEndStyle: 680 case CSSPropertyWebkitBorderStartStyle: 681 case CSSPropertyWebkitColumnRuleStyle: 682 if (valueID >= CSSValueNone && valueID <= CSSValueDouble) 683 return true; 684 break; 685 case CSSPropertyBoxSizing: 686 if (valueID == CSSValueBorderBox || valueID == CSSValueContentBox) 687 return true; 688 break; 689 case CSSPropertyCaptionSide: // top | bottom | left | right | inherit 690 if (valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueTop || valueID == CSSValueBottom) 691 return true; 692 break; 693 case CSSPropertyClear: // none | left | right | both | inherit 694 if (valueID == CSSValueNone || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueBoth) 695 return true; 696 break; 697 case CSSPropertyDirection: // ltr | rtl | inherit 698 if (valueID == CSSValueLtr || valueID == CSSValueRtl) 699 return true; 700 break; 701 case CSSPropertyDisplay: 702 // inline | block | list-item | run-in | inline-block | table | 703 // inline-table | table-row-group | table-header-group | table-footer-group | table-row | 704 // table-column-group | table-column | table-cell | table-caption | -webkit-box | -webkit-inline-box | none | inherit 705 // -webkit-flex | -webkit-inline-flex | -webkit-grid | -webkit-inline-grid 706 if ((valueID >= CSSValueInline && valueID <= CSSValueWebkitInlineFlex) || valueID == CSSValueNone) 707 return true; 708 if (parserContext.isCSSGridLayoutEnabled && (valueID == CSSValueWebkitGrid || valueID == CSSValueWebkitInlineGrid)) 709 return true; 710 break; 711 712 case CSSPropertyEmptyCells: // show | hide | inherit 713 if (valueID == CSSValueShow || valueID == CSSValueHide) 714 return true; 715 break; 716 case CSSPropertyFloat: // left | right | none | center (for buggy CSS, maps to none) 717 if (valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueNone || valueID == CSSValueCenter) 718 return true; 719 break; 720 case CSSPropertyFontStyle: // normal | italic | oblique | inherit 721 if (valueID == CSSValueNormal || valueID == CSSValueItalic || valueID == CSSValueOblique) 722 return true; 723 break; 724 case CSSPropertyImageRendering: // auto | optimizeSpeed | optimizeQuality | -webkit-crisp-edges | -webkit-optimize-contrast 725 if (valueID == CSSValueAuto || valueID == CSSValueOptimizespeed || valueID == CSSValueOptimizequality 726 || valueID == CSSValueWebkitCrispEdges || valueID == CSSValueWebkitOptimizeContrast) 727 return true; 728 break; 729 case CSSPropertyListStylePosition: // inside | outside | inherit 730 if (valueID == CSSValueInside || valueID == CSSValueOutside) 731 return true; 732 break; 733 case CSSPropertyListStyleType: 734 // See section CSS_PROP_LIST_STYLE_TYPE of file CSSValueKeywords.in 735 // for the list of supported list-style-types. 736 if ((valueID >= CSSValueDisc && valueID <= CSSValueKatakanaIroha) || valueID == CSSValueNone) 737 return true; 738 break; 739 case CSSPropertyOutlineStyle: // (<border-style> except hidden) | auto | inherit 740 if (valueID == CSSValueAuto || valueID == CSSValueNone || (valueID >= CSSValueInset && valueID <= CSSValueDouble)) 741 return true; 742 break; 743 case CSSPropertyOverflowWrap: // normal | break-word 744 case CSSPropertyWordWrap: 745 if (valueID == CSSValueNormal || valueID == CSSValueBreakWord) 746 return true; 747 break; 748 case CSSPropertyOverflowX: // visible | hidden | scroll | auto | marquee | overlay | inherit 749 if (valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay || valueID == CSSValueWebkitMarquee) 750 return true; 751 break; 752 case CSSPropertyOverflowY: // visible | hidden | scroll | auto | marquee | overlay | inherit | -webkit-paged-x | -webkit-paged-y 753 if (valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay || valueID == CSSValueWebkitMarquee || valueID == CSSValueWebkitPagedX || valueID == CSSValueWebkitPagedY) 754 return true; 755 break; 756 case CSSPropertyPageBreakAfter: // auto | always | avoid | left | right | inherit 757 case CSSPropertyPageBreakBefore: 758 case CSSPropertyWebkitColumnBreakAfter: 759 case CSSPropertyWebkitColumnBreakBefore: 760 if (valueID == CSSValueAuto || valueID == CSSValueAlways || valueID == CSSValueAvoid || valueID == CSSValueLeft || valueID == CSSValueRight) 761 return true; 762 break; 763 case CSSPropertyPageBreakInside: // avoid | auto | inherit 764 case CSSPropertyWebkitColumnBreakInside: 765 if (valueID == CSSValueAuto || valueID == CSSValueAvoid) 766 return true; 767 break; 768 case CSSPropertyPointerEvents: 769 // none | visiblePainted | visibleFill | visibleStroke | visible | 770 // painted | fill | stroke | auto | all | inherit 771 if (valueID == CSSValueVisible || valueID == CSSValueNone || valueID == CSSValueAll || valueID == CSSValueAuto || (valueID >= CSSValueVisiblepainted && valueID <= CSSValueStroke)) 772 return true; 773 break; 774 case CSSPropertyPosition: // static | relative | absolute | fixed | sticky | inherit 775 if (valueID == CSSValueStatic || valueID == CSSValueRelative || valueID == CSSValueAbsolute || valueID == CSSValueFixed 776#if ENABLE(CSS_STICKY_POSITION) 777 || (parserContext.isCSSStickyPositionEnabled && valueID == CSSValueWebkitSticky) 778#endif 779 ) 780 return true; 781 break; 782 case CSSPropertyResize: // none | both | horizontal | vertical | auto 783 if (valueID == CSSValueNone || valueID == CSSValueBoth || valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueAuto) 784 return true; 785 break; 786 case CSSPropertySpeak: // none | normal | spell-out | digits | literal-punctuation | no-punctuation | inherit 787 if (valueID == CSSValueNone || valueID == CSSValueNormal || valueID == CSSValueSpellOut || valueID == CSSValueDigits || valueID == CSSValueLiteralPunctuation || valueID == CSSValueNoPunctuation) 788 return true; 789 break; 790 case CSSPropertyTableLayout: // auto | fixed | inherit 791 if (valueID == CSSValueAuto || valueID == CSSValueFixed) 792 return true; 793 break; 794 case CSSPropertyTextLineThroughMode: 795 case CSSPropertyTextOverlineMode: 796 case CSSPropertyTextUnderlineMode: 797 if (valueID == CSSValueContinuous || valueID == CSSValueSkipWhiteSpace) 798 return true; 799 break; 800 case CSSPropertyTextLineThroughStyle: 801 case CSSPropertyTextOverlineStyle: 802 case CSSPropertyTextUnderlineStyle: 803 if (valueID == CSSValueNone || valueID == CSSValueSolid || valueID == CSSValueDouble || valueID == CSSValueDashed || valueID == CSSValueDotDash || valueID == CSSValueDotDotDash || valueID == CSSValueWave) 804 return true; 805 break; 806 case CSSPropertyTextOverflow: // clip | ellipsis 807 if (valueID == CSSValueClip || valueID == CSSValueEllipsis) 808 return true; 809 break; 810 case CSSPropertyTextRendering: // auto | optimizeSpeed | optimizeLegibility | geometricPrecision 811 if (valueID == CSSValueAuto || valueID == CSSValueOptimizespeed || valueID == CSSValueOptimizelegibility || valueID == CSSValueGeometricprecision) 812 return true; 813 break; 814 case CSSPropertyTextTransform: // capitalize | uppercase | lowercase | none | inherit 815 if ((valueID >= CSSValueCapitalize && valueID <= CSSValueLowercase) || valueID == CSSValueNone) 816 return true; 817 break; 818 case CSSPropertyVisibility: // visible | hidden | collapse | inherit 819 if (valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueCollapse) 820 return true; 821 break; 822 case CSSPropertyWebkitAppearance: 823 if ((valueID >= CSSValueCheckbox && valueID <= CSSValueTextarea) || valueID == CSSValueNone) 824 return true; 825 break; 826 case CSSPropertyWebkitBackfaceVisibility: 827 if (valueID == CSSValueVisible || valueID == CSSValueHidden) 828 return true; 829 break; 830#if ENABLE(CSS_COMPOSITING) 831 case CSSPropertyWebkitBlendMode: 832 if (parserContext.isCSSCompositingEnabled && (valueID == CSSValueNormal || valueID == CSSValueMultiply || valueID == CSSValueScreen 833 || valueID == CSSValueOverlay || valueID == CSSValueDarken || valueID == CSSValueLighten || valueID == CSSValueColorDodge 834 || valueID == CSSValueColorBurn || valueID == CSSValueHardLight || valueID == CSSValueSoftLight || valueID == CSSValueDifference 835 || valueID == CSSValueExclusion || valueID == CSSValueHue || valueID == CSSValueSaturation || valueID == CSSValueColor 836 || valueID == CSSValueLuminosity)) 837 return true; 838 break; 839#endif 840 case CSSPropertyWebkitBorderFit: 841 if (valueID == CSSValueBorder || valueID == CSSValueLines) 842 return true; 843 break; 844 case CSSPropertyWebkitBoxAlign: 845 if (valueID == CSSValueStretch || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline) 846 return true; 847 break; 848#if ENABLE(CSS_BOX_DECORATION_BREAK) 849 case CSSPropertyWebkitBoxDecorationBreak: 850 if (valueID == CSSValueClone || valueID == CSSValueSlice) 851 return true; 852 break; 853#endif 854 case CSSPropertyWebkitBoxDirection: 855 if (valueID == CSSValueNormal || valueID == CSSValueReverse) 856 return true; 857 break; 858 case CSSPropertyWebkitBoxLines: 859 if (valueID == CSSValueSingle || valueID == CSSValueMultiple) 860 return true; 861 break; 862 case CSSPropertyWebkitBoxOrient: 863 if (valueID == CSSValueHorizontal || valueID == CSSValueVertical || valueID == CSSValueInlineAxis || valueID == CSSValueBlockAxis) 864 return true; 865 break; 866 case CSSPropertyWebkitBoxPack: 867 if (valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueCenter || valueID == CSSValueJustify) 868 return true; 869 break; 870 case CSSPropertyWebkitColorCorrection: 871 if (valueID == CSSValueSrgb || valueID == CSSValueDefault) 872 return true; 873 break; 874 case CSSPropertyWebkitAlignContent: 875 if (valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueSpaceBetween || valueID == CSSValueSpaceAround || valueID == CSSValueStretch) 876 return true; 877 break; 878 case CSSPropertyWebkitAlignItems: 879 if (valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch) 880 return true; 881 break; 882 case CSSPropertyWebkitAlignSelf: 883 if (valueID == CSSValueAuto || valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueBaseline || valueID == CSSValueStretch) 884 return true; 885 break; 886 case CSSPropertyWebkitFlexDirection: 887 if (valueID == CSSValueRow || valueID == CSSValueRowReverse || valueID == CSSValueColumn || valueID == CSSValueColumnReverse) 888 return true; 889 break; 890 case CSSPropertyWebkitFlexWrap: 891 if (valueID == CSSValueNowrap || valueID == CSSValueWrap || valueID == CSSValueWrapReverse) 892 return true; 893 break; 894 case CSSPropertyWebkitJustifyContent: 895 if (valueID == CSSValueFlexStart || valueID == CSSValueFlexEnd || valueID == CSSValueCenter || valueID == CSSValueSpaceBetween || valueID == CSSValueSpaceAround) 896 return true; 897 break; 898 case CSSPropertyWebkitFontKerning: 899 if (valueID == CSSValueAuto || valueID == CSSValueNormal || valueID == CSSValueNone) 900 return true; 901 break; 902 case CSSPropertyWebkitFontSmoothing: 903 if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueAntialiased || valueID == CSSValueSubpixelAntialiased) 904 return true; 905 break; 906 case CSSPropertyWebkitHyphens: 907 if (valueID == CSSValueNone || valueID == CSSValueManual || valueID == CSSValueAuto) 908 return true; 909 break; 910 case CSSPropertyWebkitGridAutoFlow: 911 if (valueID == CSSValueNone || valueID == CSSValueRow || valueID == CSSValueColumn) 912 return true; 913 break; 914 case CSSPropertyWebkitLineAlign: 915 if (valueID == CSSValueNone || valueID == CSSValueEdges) 916 return true; 917 break; 918 case CSSPropertyWebkitLineBreak: // auto | loose | normal | strict | after-white-space 919 if (valueID == CSSValueAuto || valueID == CSSValueLoose || valueID == CSSValueNormal || valueID == CSSValueStrict || valueID == CSSValueAfterWhiteSpace) 920 return true; 921 break; 922 case CSSPropertyWebkitLineSnap: 923 if (valueID == CSSValueNone || valueID == CSSValueBaseline || valueID == CSSValueContain) 924 return true; 925 break; 926 case CSSPropertyWebkitMarginAfterCollapse: 927 case CSSPropertyWebkitMarginBeforeCollapse: 928 case CSSPropertyWebkitMarginBottomCollapse: 929 case CSSPropertyWebkitMarginTopCollapse: 930 if (valueID == CSSValueCollapse || valueID == CSSValueSeparate || valueID == CSSValueDiscard) 931 return true; 932 break; 933 case CSSPropertyWebkitMarqueeDirection: 934 if (valueID == CSSValueForwards || valueID == CSSValueBackwards || valueID == CSSValueAhead || valueID == CSSValueReverse || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueDown 935 || valueID == CSSValueUp || valueID == CSSValueAuto) 936 return true; 937 break; 938 case CSSPropertyWebkitMarqueeStyle: 939 if (valueID == CSSValueNone || valueID == CSSValueSlide || valueID == CSSValueScroll || valueID == CSSValueAlternate) 940 return true; 941 break; 942 case CSSPropertyWebkitNbspMode: // normal | space 943 if (valueID == CSSValueNormal || valueID == CSSValueSpace) 944 return true; 945 break; 946#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) 947 case CSSPropertyWebkitOverflowScrolling: 948 if (valueID == CSSValueAuto || valueID == CSSValueTouch) 949 return true; 950 break; 951#endif 952 case CSSPropertyWebkitPrintColorAdjust: 953 if (valueID == CSSValueExact || valueID == CSSValueEconomy) 954 return true; 955 break; 956#if ENABLE(CSS_REGIONS) 957 case CSSPropertyWebkitRegionBreakAfter: 958 case CSSPropertyWebkitRegionBreakBefore: 959 if (parserContext.isCSSRegionsEnabled && (valueID == CSSValueAuto || valueID == CSSValueAlways || valueID == CSSValueAvoid || valueID == CSSValueLeft || valueID == CSSValueRight)) 960 return true; 961 break; 962 case CSSPropertyWebkitRegionBreakInside: 963 if (parserContext.isCSSRegionsEnabled && (valueID == CSSValueAuto || valueID == CSSValueAvoid)) 964 return true; 965 break; 966 case CSSPropertyWebkitRegionFragment: 967 if (parserContext.isCSSRegionsEnabled && (valueID == CSSValueAuto || valueID == CSSValueBreak)) 968 return true; 969 break; 970#endif 971 case CSSPropertyWebkitRtlOrdering: 972 if (valueID == CSSValueLogical || valueID == CSSValueVisual) 973 return true; 974 break; 975 976 case CSSPropertyWebkitRubyPosition: 977 if (valueID == CSSValueBefore || valueID == CSSValueAfter) 978 return true; 979 break; 980 981#if ENABLE(CSS3_TEXT) 982 case CSSPropertyWebkitTextAlignLast: 983 // auto | start | end | left | right | center | justify 984 if ((valueID >= CSSValueLeft && valueID <= CSSValueJustify) || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueAuto) 985 return true; 986 break; 987#endif // CSS3_TEXT 988 case CSSPropertyWebkitTextCombine: 989 if (valueID == CSSValueNone || valueID == CSSValueHorizontal) 990 return true; 991 break; 992 case CSSPropertyWebkitTextEmphasisPosition: 993 if (valueID == CSSValueOver || valueID == CSSValueUnder) 994 return true; 995 break; 996#if ENABLE(CSS3_TEXT) 997 case CSSPropertyWebkitTextJustify: 998 // auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida 999 if ((valueID >= CSSValueInterWord && valueID <= CSSValueKashida) || valueID == CSSValueAuto || valueID == CSSValueNone) 1000 return true; 1001 break; 1002#endif // CSS3_TEXT 1003 case CSSPropertyWebkitTextSecurity: 1004 // disc | circle | square | none | inherit 1005 if (valueID == CSSValueDisc || valueID == CSSValueCircle || valueID == CSSValueSquare || valueID == CSSValueNone) 1006 return true; 1007 break; 1008 case CSSPropertyWebkitTransformStyle: 1009 if (valueID == CSSValueFlat || valueID == CSSValuePreserve3d) 1010 return true; 1011 break; 1012 case CSSPropertyWebkitUserDrag: // auto | none | element 1013 if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueElement) 1014 return true; 1015 break; 1016 case CSSPropertyWebkitUserModify: // read-only | read-write 1017 if (valueID == CSSValueReadOnly || valueID == CSSValueReadWrite || valueID == CSSValueReadWritePlaintextOnly) 1018 return true; 1019 break; 1020 case CSSPropertyWebkitUserSelect: // auto | none | text | all 1021 if (valueID == CSSValueAuto || valueID == CSSValueNone || valueID == CSSValueText || valueID == CSSValueAll) 1022 return true; 1023 break; 1024#if ENABLE(CSS_EXCLUSIONS) 1025 case CSSPropertyWebkitWrapFlow: 1026 if (!RuntimeEnabledFeatures::cssExclusionsEnabled()) 1027 return false; 1028 if (valueID == CSSValueAuto || valueID == CSSValueBoth || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueMaximum || valueID == CSSValueClear) 1029 return true; 1030 break; 1031 case CSSPropertyWebkitWrapThrough: 1032 if (!RuntimeEnabledFeatures::cssExclusionsEnabled()) 1033 return false; 1034 if (valueID == CSSValueWrap || valueID == CSSValueNone) 1035 return true; 1036 break; 1037#endif 1038 case CSSPropertyWebkitWritingMode: 1039 if (valueID >= CSSValueHorizontalTb && valueID <= CSSValueHorizontalBt) 1040 return true; 1041 break; 1042 case CSSPropertyWhiteSpace: // normal | pre | nowrap | inherit 1043 if (valueID == CSSValueNormal || valueID == CSSValuePre || valueID == CSSValuePreWrap || valueID == CSSValuePreLine || valueID == CSSValueNowrap) 1044 return true; 1045 break; 1046 case CSSPropertyWordBreak: // normal | break-all | break-word (this is a custom extension) 1047 if (valueID == CSSValueNormal || valueID == CSSValueBreakAll || valueID == CSSValueBreakWord) 1048 return true; 1049 break; 1050 default: 1051 ASSERT_NOT_REACHED(); 1052 return false; 1053 } 1054 return false; 1055} 1056 1057static inline bool isKeywordPropertyID(CSSPropertyID propertyId) 1058{ 1059 switch (propertyId) { 1060 case CSSPropertyBorderBottomStyle: 1061 case CSSPropertyBorderCollapse: 1062 case CSSPropertyBorderLeftStyle: 1063 case CSSPropertyBorderRightStyle: 1064 case CSSPropertyBorderTopStyle: 1065 case CSSPropertyBoxSizing: 1066 case CSSPropertyCaptionSide: 1067 case CSSPropertyClear: 1068 case CSSPropertyDirection: 1069 case CSSPropertyDisplay: 1070 case CSSPropertyEmptyCells: 1071 case CSSPropertyFloat: 1072 case CSSPropertyFontStyle: 1073 case CSSPropertyImageRendering: 1074 case CSSPropertyListStylePosition: 1075 case CSSPropertyListStyleType: 1076 case CSSPropertyOutlineStyle: 1077 case CSSPropertyOverflowWrap: 1078 case CSSPropertyOverflowX: 1079 case CSSPropertyOverflowY: 1080 case CSSPropertyPageBreakAfter: 1081 case CSSPropertyPageBreakBefore: 1082 case CSSPropertyPageBreakInside: 1083 case CSSPropertyPointerEvents: 1084 case CSSPropertyPosition: 1085 case CSSPropertyResize: 1086 case CSSPropertySpeak: 1087 case CSSPropertyTableLayout: 1088 case CSSPropertyTextLineThroughMode: 1089 case CSSPropertyTextLineThroughStyle: 1090 case CSSPropertyTextOverflow: 1091 case CSSPropertyTextOverlineMode: 1092 case CSSPropertyTextOverlineStyle: 1093 case CSSPropertyTextRendering: 1094 case CSSPropertyTextTransform: 1095 case CSSPropertyTextUnderlineMode: 1096 case CSSPropertyTextUnderlineStyle: 1097 case CSSPropertyVisibility: 1098 case CSSPropertyWebkitAppearance: 1099#if ENABLE(CSS_COMPOSITING) 1100 case CSSPropertyWebkitBlendMode: 1101#endif 1102 case CSSPropertyWebkitBackfaceVisibility: 1103 case CSSPropertyWebkitBorderAfterStyle: 1104 case CSSPropertyWebkitBorderBeforeStyle: 1105 case CSSPropertyWebkitBorderEndStyle: 1106 case CSSPropertyWebkitBorderFit: 1107 case CSSPropertyWebkitBorderStartStyle: 1108 case CSSPropertyWebkitBoxAlign: 1109#if ENABLE(CSS_BOX_DECORATION_BREAK) 1110 case CSSPropertyWebkitBoxDecorationBreak: 1111#endif 1112 case CSSPropertyWebkitBoxDirection: 1113 case CSSPropertyWebkitBoxLines: 1114 case CSSPropertyWebkitBoxOrient: 1115 case CSSPropertyWebkitBoxPack: 1116 case CSSPropertyWebkitColorCorrection: 1117 case CSSPropertyWebkitColumnBreakAfter: 1118 case CSSPropertyWebkitColumnBreakBefore: 1119 case CSSPropertyWebkitColumnBreakInside: 1120 case CSSPropertyWebkitColumnRuleStyle: 1121 case CSSPropertyWebkitAlignContent: 1122 case CSSPropertyWebkitAlignItems: 1123 case CSSPropertyWebkitAlignSelf: 1124 case CSSPropertyWebkitFlexDirection: 1125 case CSSPropertyWebkitFlexWrap: 1126 case CSSPropertyWebkitJustifyContent: 1127 case CSSPropertyWebkitFontKerning: 1128 case CSSPropertyWebkitFontSmoothing: 1129 case CSSPropertyWebkitHyphens: 1130 case CSSPropertyWebkitGridAutoFlow: 1131 case CSSPropertyWebkitLineAlign: 1132 case CSSPropertyWebkitLineBreak: 1133 case CSSPropertyWebkitLineSnap: 1134 case CSSPropertyWebkitMarginAfterCollapse: 1135 case CSSPropertyWebkitMarginBeforeCollapse: 1136 case CSSPropertyWebkitMarginBottomCollapse: 1137 case CSSPropertyWebkitMarginTopCollapse: 1138 case CSSPropertyWebkitMarqueeDirection: 1139 case CSSPropertyWebkitMarqueeStyle: 1140 case CSSPropertyWebkitNbspMode: 1141#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) 1142 case CSSPropertyWebkitOverflowScrolling: 1143#endif 1144 case CSSPropertyWebkitPrintColorAdjust: 1145#if ENABLE(CSS_REGIONS) 1146 case CSSPropertyWebkitRegionBreakAfter: 1147 case CSSPropertyWebkitRegionBreakBefore: 1148 case CSSPropertyWebkitRegionBreakInside: 1149 case CSSPropertyWebkitRegionFragment: 1150#endif 1151 case CSSPropertyWebkitRtlOrdering: 1152 case CSSPropertyWebkitRubyPosition: 1153#if ENABLE(CSS3_TEXT) 1154 case CSSPropertyWebkitTextAlignLast: 1155#endif // CSS3_TEXT 1156 case CSSPropertyWebkitTextCombine: 1157 case CSSPropertyWebkitTextEmphasisPosition: 1158#if ENABLE(CSS3_TEXT) 1159 case CSSPropertyWebkitTextJustify: 1160#endif // CSS3_TEXT 1161 case CSSPropertyWebkitTextSecurity: 1162 case CSSPropertyWebkitTransformStyle: 1163 case CSSPropertyWebkitUserDrag: 1164 case CSSPropertyWebkitUserModify: 1165 case CSSPropertyWebkitUserSelect: 1166#if ENABLE(CSS_EXCLUSIONS) 1167 case CSSPropertyWebkitWrapFlow: 1168 case CSSPropertyWebkitWrapThrough: 1169#endif 1170 case CSSPropertyWebkitWritingMode: 1171 case CSSPropertyWhiteSpace: 1172 case CSSPropertyWordBreak: 1173 case CSSPropertyWordWrap: 1174 return true; 1175 default: 1176 return false; 1177 } 1178} 1179 1180static bool parseKeywordValue(MutableStylePropertySet* declaration, CSSPropertyID propertyId, const String& string, bool important, const CSSParserContext& parserContext) 1181{ 1182 ASSERT(!string.isEmpty()); 1183 1184 if (!isKeywordPropertyID(propertyId)) { 1185 // All properties accept the values of "initial" and "inherit". 1186 String lowerCaseString = string.lower(); 1187 if (lowerCaseString != "initial" && lowerCaseString != "inherit") 1188 return false; 1189 1190 // Parse initial/inherit shorthands using the CSSParser. 1191 if (shorthandForProperty(propertyId).length()) 1192 return false; 1193 } 1194 1195 CSSParserString cssString; 1196 cssString.init(string); 1197 int valueID = cssValueKeywordID(cssString); 1198 1199 if (!valueID) 1200 return false; 1201 1202 RefPtr<CSSValue> value; 1203 if (valueID == CSSValueInherit) 1204 value = cssValuePool().createInheritedValue(); 1205 else if (valueID == CSSValueInitial) 1206 value = cssValuePool().createExplicitInitialValue(); 1207 else if (isValidKeywordPropertyAndValue(propertyId, valueID, parserContext)) 1208 value = cssValuePool().createIdentifierValue(valueID); 1209 else 1210 return false; 1211 1212 declaration->addParsedProperty(CSSProperty(propertyId, value.release(), important)); 1213 return true; 1214} 1215 1216template <typename CharacterType> 1217static bool parseTransformArguments(WebKitCSSTransformValue* transformValue, CharacterType* characters, unsigned length, unsigned start, unsigned expectedCount) 1218{ 1219 while (expectedCount) { 1220 size_t end = WTF::find(characters, length, expectedCount == 1 ? ')' : ',', start); 1221 if (end == notFound || (expectedCount == 1 && end != length - 1)) 1222 return false; 1223 unsigned argumentLength = end - start; 1224 CSSPrimitiveValue::UnitTypes unit = CSSPrimitiveValue::CSS_NUMBER; 1225 double number; 1226 if (!parseSimpleLength(characters + start, argumentLength, unit, number)) 1227 return false; 1228 if (unit != CSSPrimitiveValue::CSS_PX && (number || unit != CSSPrimitiveValue::CSS_NUMBER)) 1229 return false; 1230 transformValue->append(cssValuePool().createValue(number, unit)); 1231 start = end + 1; 1232 --expectedCount; 1233 } 1234 return true; 1235} 1236 1237static bool parseTranslateTransformValue(MutableStylePropertySet* properties, CSSPropertyID propertyID, const String& string, bool important) 1238{ 1239 if (propertyID != CSSPropertyWebkitTransform) 1240 return false; 1241 static const unsigned shortestValidTransformStringLength = 12; 1242 static const unsigned likelyMultipartTransformStringLengthCutoff = 32; 1243 if (string.length() < shortestValidTransformStringLength || string.length() > likelyMultipartTransformStringLengthCutoff) 1244 return false; 1245 if (!string.startsWith("translate", false)) 1246 return false; 1247 UChar c9 = toASCIILower(string[9]); 1248 UChar c10 = toASCIILower(string[10]); 1249 1250 WebKitCSSTransformValue::TransformOperationType transformType; 1251 unsigned expectedArgumentCount = 1; 1252 unsigned argumentStart = 11; 1253 if (c9 == 'x' && c10 == '(') 1254 transformType = WebKitCSSTransformValue::TranslateXTransformOperation; 1255 else if (c9 == 'y' && c10 == '(') 1256 transformType = WebKitCSSTransformValue::TranslateYTransformOperation; 1257 else if (c9 == 'z' && c10 == '(') 1258 transformType = WebKitCSSTransformValue::TranslateZTransformOperation; 1259 else if (c9 == '(') { 1260 transformType = WebKitCSSTransformValue::TranslateTransformOperation; 1261 expectedArgumentCount = 2; 1262 argumentStart = 10; 1263 } else if (c9 == '3' && c10 == 'd' && string[11] == '(') { 1264 transformType = WebKitCSSTransformValue::Translate3DTransformOperation; 1265 expectedArgumentCount = 3; 1266 argumentStart = 12; 1267 } else 1268 return false; 1269 1270 RefPtr<WebKitCSSTransformValue> transformValue = WebKitCSSTransformValue::create(transformType); 1271 bool success; 1272 if (string.is8Bit()) 1273 success = parseTransformArguments(transformValue.get(), string.characters8(), string.length(), argumentStart, expectedArgumentCount); 1274 else 1275 success = parseTransformArguments(transformValue.get(), string.characters16(), string.length(), argumentStart, expectedArgumentCount); 1276 if (!success) 1277 return false; 1278 RefPtr<CSSValueList> result = CSSValueList::createSpaceSeparated(); 1279 result->append(transformValue.release()); 1280 properties->addParsedProperty(CSSProperty(CSSPropertyWebkitTransform, result.release(), important)); 1281 return true; 1282} 1283 1284PassRefPtr<CSSValueList> CSSParser::parseFontFaceValue(const AtomicString& string) 1285{ 1286 if (string.isEmpty()) 1287 return 0; 1288 RefPtr<MutableStylePropertySet> dummyStyle = MutableStylePropertySet::create(); 1289 if (!parseValue(dummyStyle.get(), CSSPropertyFontFamily, string, false, CSSQuirksMode, 0)) 1290 return 0; 1291 1292 RefPtr<CSSValue> fontFamily = dummyStyle->getPropertyCSSValue(CSSPropertyFontFamily); 1293 if (!fontFamily->isValueList()) 1294 return 0; // FIXME: "initial" and "inherit" should be parsed as font names in the face attribute. 1295 return static_pointer_cast<CSSValueList>(fontFamily.release()); 1296} 1297 1298#if ENABLE(CSS_VARIABLES) 1299bool CSSParser::parseValue(MutableStylePropertySet* declaration, CSSPropertyID propertyID, const String& string, bool important, Document* document) 1300{ 1301 ASSERT(!string.isEmpty()); 1302 1303 CSSParserContext context(document); 1304 1305 if (parseSimpleLengthValue(declaration, propertyID, string, important, context.mode)) 1306 return true; 1307 if (parseColorValue(declaration, propertyID, string, important, context.mode)) 1308 return true; 1309 if (parseKeywordValue(declaration, propertyID, string, important, context)) 1310 return true; 1311 1312 CSSParser parser(context); 1313 return parser.parseValue(declaration, propertyID, string, important, static_cast<StyleSheetContents*>(0)); 1314} 1315#endif 1316 1317bool CSSParser::parseValue(MutableStylePropertySet* declaration, CSSPropertyID propertyID, const String& string, bool important, CSSParserMode cssParserMode, StyleSheetContents* contextStyleSheet) 1318{ 1319 ASSERT(!string.isEmpty()); 1320 if (parseSimpleLengthValue(declaration, propertyID, string, important, cssParserMode)) 1321 return true; 1322 if (parseColorValue(declaration, propertyID, string, important, cssParserMode)) 1323 return true; 1324 1325 CSSParserContext context(cssParserMode); 1326 if (contextStyleSheet) { 1327 context = contextStyleSheet->parserContext(); 1328 context.mode = cssParserMode; 1329 } 1330 1331 if (parseKeywordValue(declaration, propertyID, string, important, context)) 1332 return true; 1333 if (parseTranslateTransformValue(declaration, propertyID, string, important)) 1334 return true; 1335 1336 CSSParser parser(context); 1337 return parser.parseValue(declaration, propertyID, string, important, contextStyleSheet); 1338} 1339 1340bool CSSParser::parseValue(MutableStylePropertySet* declaration, CSSPropertyID propertyID, const String& string, bool important, StyleSheetContents* contextStyleSheet) 1341{ 1342 setStyleSheet(contextStyleSheet); 1343 1344 setupParser("@-webkit-value{", string, "} "); 1345 1346 m_id = propertyID; 1347 m_important = important; 1348 1349 cssyyparse(this); 1350 1351 m_rule = 0; 1352 1353 bool ok = false; 1354 if (m_hasFontFaceOnlyValues) 1355 deleteFontFaceOnlyValues(); 1356 if (!m_parsedProperties.isEmpty()) { 1357 ok = true; 1358 declaration->addParsedProperties(m_parsedProperties); 1359 clearProperties(); 1360 } 1361 1362 return ok; 1363} 1364 1365// The color will only be changed when string contains a valid CSS color, so callers 1366// can set it to a default color and ignore the boolean result. 1367bool CSSParser::parseColor(RGBA32& color, const String& string, bool strict) 1368{ 1369 // First try creating a color specified by name, rgba(), rgb() or "#" syntax. 1370 if (fastParseColor(color, string, strict)) 1371 return true; 1372 1373 CSSParser parser(CSSStrictMode); 1374 1375 // In case the fast-path parser didn't understand the color, try the full parser. 1376 if (!parser.parseColor(string)) 1377 return false; 1378 1379 CSSValue* value = parser.m_parsedProperties.first().value(); 1380 if (!value->isPrimitiveValue()) 1381 return false; 1382 1383 CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value); 1384 if (!primitiveValue->isRGBColor()) 1385 return false; 1386 1387 color = primitiveValue->getRGBA32Value(); 1388 return true; 1389} 1390 1391bool CSSParser::parseColor(const String& string) 1392{ 1393 setupParser("@-webkit-decls{color:", string, "} "); 1394 cssyyparse(this); 1395 m_rule = 0; 1396 1397 return !m_parsedProperties.isEmpty() && m_parsedProperties.first().id() == CSSPropertyColor; 1398} 1399 1400bool CSSParser::parseSystemColor(RGBA32& color, const String& string, Document* document) 1401{ 1402 if (!document || !document->page()) 1403 return false; 1404 1405 CSSParserString cssColor; 1406 cssColor.init(string); 1407 int id = cssValueKeywordID(cssColor); 1408 if (id <= 0) 1409 return false; 1410 1411 color = document->page()->theme()->systemColor(id).rgb(); 1412 return true; 1413} 1414 1415void CSSParser::parseSelector(const String& string, CSSSelectorList& selectorList) 1416{ 1417 m_selectorListForParseSelector = &selectorList; 1418 1419 setupParser("@-webkit-selector{", string, "}"); 1420 1421 cssyyparse(this); 1422 1423 m_selectorListForParseSelector = 0; 1424} 1425 1426PassRefPtr<ImmutableStylePropertySet> CSSParser::parseInlineStyleDeclaration(const String& string, Element* element) 1427{ 1428 CSSParserContext context = element->document()->elementSheet()->contents()->parserContext(); 1429 context.mode = strictToCSSParserMode(element->isHTMLElement() && !element->document()->inQuirksMode()); 1430 return CSSParser(context).parseDeclaration(string, element->document()->elementSheet()->contents()); 1431} 1432 1433PassRefPtr<ImmutableStylePropertySet> CSSParser::parseDeclaration(const String& string, StyleSheetContents* contextStyleSheet) 1434{ 1435 setStyleSheet(contextStyleSheet); 1436 1437 setupParser("@-webkit-decls{", string, "} "); 1438 cssyyparse(this); 1439 m_rule = 0; 1440 1441 if (m_hasFontFaceOnlyValues) 1442 deleteFontFaceOnlyValues(); 1443 1444 RefPtr<ImmutableStylePropertySet> style = createStylePropertySet(); 1445 clearProperties(); 1446 return style.release(); 1447} 1448 1449 1450bool CSSParser::parseDeclaration(MutableStylePropertySet* declaration, const String& string, PassRefPtr<CSSRuleSourceData> prpRuleSourceData, StyleSheetContents* contextStyleSheet) 1451{ 1452 // Length of the "@-webkit-decls{" prefix. 1453 static const unsigned prefixLength = 15; 1454 1455 setStyleSheet(contextStyleSheet); 1456 1457 RefPtr<CSSRuleSourceData> ruleSourceData = prpRuleSourceData; 1458 if (ruleSourceData) { 1459 m_currentRuleDataStack = adoptPtr(new RuleSourceDataList()); 1460 m_currentRuleDataStack->append(ruleSourceData); 1461 } 1462 1463 setupParser("@-webkit-decls{", string, "} "); 1464 cssyyparse(this); 1465 m_rule = 0; 1466 1467 bool ok = false; 1468 if (m_hasFontFaceOnlyValues) 1469 deleteFontFaceOnlyValues(); 1470 if (!m_parsedProperties.isEmpty()) { 1471 ok = true; 1472 declaration->addParsedProperties(m_parsedProperties); 1473 clearProperties(); 1474 } 1475 1476 if (ruleSourceData) { 1477 ASSERT(m_currentRuleDataStack->size() == 1); 1478 ruleSourceData->ruleBodyRange.start = 0; 1479 ruleSourceData->ruleBodyRange.end = string.length(); 1480 for (size_t i = 0, size = ruleSourceData->styleSourceData->propertyData.size(); i < size; ++i) { 1481 CSSPropertySourceData& propertyData = ruleSourceData->styleSourceData->propertyData.at(i); 1482 propertyData.range.start -= prefixLength; 1483 propertyData.range.end -= prefixLength; 1484 } 1485 1486 fixUnparsedPropertyRanges(ruleSourceData.get()); 1487 m_currentRuleDataStack.clear(); 1488 } 1489 1490 return ok; 1491} 1492 1493PassOwnPtr<MediaQuery> CSSParser::parseMediaQuery(const String& string) 1494{ 1495 if (string.isEmpty()) 1496 return nullptr; 1497 1498 ASSERT(!m_mediaQuery); 1499 1500 // can't use { because tokenizer state switches from mediaquery to initial state when it sees { token. 1501 // instead insert one " " (which is WHITESPACE in CSSGrammar.y) 1502 setupParser("@-webkit-mediaquery ", string, "} "); 1503 cssyyparse(this); 1504 1505 return m_mediaQuery.release(); 1506} 1507 1508#if ENABLE(CSS_VARIABLES) 1509static inline void filterProperties(bool important, const CSSParser::ParsedPropertyVector& input, Vector<CSSProperty, 256>& output, size_t& unusedEntries, BitArray<numCSSProperties>& seenProperties, HashSet<AtomicString>& seenVariables) 1510#else 1511static inline void filterProperties(bool important, const CSSParser::ParsedPropertyVector& input, Vector<CSSProperty, 256>& output, size_t& unusedEntries, BitArray<numCSSProperties>& seenProperties) 1512#endif 1513{ 1514 // Add properties in reverse order so that highest priority definitions are reached first. Duplicate definitions can then be ignored when found. 1515 for (int i = input.size() - 1; i >= 0; --i) { 1516 const CSSProperty& property = input[i]; 1517 if (property.isImportant() != important) 1518 continue; 1519#if ENABLE(CSS_VARIABLES) 1520 if (property.id() == CSSPropertyVariable) { 1521 const AtomicString& name = static_cast<CSSVariableValue*>(property.value())->name(); 1522 if (seenVariables.contains(name)) 1523 continue; 1524 seenVariables.add(name); 1525 output[--unusedEntries] = property; 1526 continue; 1527 } 1528#endif 1529 const unsigned propertyIDIndex = property.id() - firstCSSProperty; 1530 if (seenProperties.get(propertyIDIndex)) 1531 continue; 1532 seenProperties.set(propertyIDIndex); 1533 output[--unusedEntries] = property; 1534 } 1535} 1536 1537PassRefPtr<ImmutableStylePropertySet> CSSParser::createStylePropertySet() 1538{ 1539 BitArray<numCSSProperties> seenProperties; 1540 size_t unusedEntries = m_parsedProperties.size(); 1541 Vector<CSSProperty, 256> results(unusedEntries); 1542 1543 // Important properties have higher priority, so add them first. Duplicate definitions can then be ignored when found. 1544#if ENABLE(CSS_VARIABLES) 1545 HashSet<AtomicString> seenVariables; 1546 filterProperties(true, m_parsedProperties, results, unusedEntries, seenProperties, seenVariables); 1547 filterProperties(false, m_parsedProperties, results, unusedEntries, seenProperties, seenVariables); 1548#else 1549 filterProperties(true, m_parsedProperties, results, unusedEntries, seenProperties); 1550 filterProperties(false, m_parsedProperties, results, unusedEntries, seenProperties); 1551#endif 1552 if (unusedEntries) 1553 results.remove(0, unusedEntries); 1554 1555 return ImmutableStylePropertySet::create(results.data(), results.size(), m_context.mode); 1556} 1557 1558void CSSParser::addPropertyWithPrefixingVariant(CSSPropertyID propId, PassRefPtr<CSSValue> value, bool important, bool implicit) 1559{ 1560 RefPtr<CSSValue> val = value.get(); 1561 addProperty(propId, value, important, implicit); 1562 1563 CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(propId); 1564 if (prefixingVariant == propId) 1565 return; 1566 addProperty(prefixingVariant, val.release(), important, implicit); 1567} 1568 1569void CSSParser::addProperty(CSSPropertyID propId, PassRefPtr<CSSValue> value, bool important, bool implicit) 1570{ 1571 m_parsedProperties.append(CSSProperty(propId, value, important, m_currentShorthand, m_implicitShorthand || implicit)); 1572} 1573 1574void CSSParser::rollbackLastProperties(int num) 1575{ 1576 ASSERT(num >= 0); 1577 ASSERT(m_parsedProperties.size() >= static_cast<unsigned>(num)); 1578 m_parsedProperties.shrink(m_parsedProperties.size() - num); 1579} 1580 1581void CSSParser::clearProperties() 1582{ 1583 m_parsedProperties.clear(); 1584 m_numParsedPropertiesBeforeMarginBox = INVALID_NUM_PARSED_PROPERTIES; 1585 m_hasFontFaceOnlyValues = false; 1586} 1587 1588KURL CSSParser::completeURL(const CSSParserContext& context, const String& url) 1589{ 1590 if (url.isNull()) 1591 return KURL(); 1592 if (context.charset.isEmpty()) 1593 return KURL(context.baseURL, url); 1594 return KURL(context.baseURL, url, context.charset); 1595} 1596 1597KURL CSSParser::completeURL(const String& url) const 1598{ 1599 return completeURL(m_context, url); 1600} 1601 1602bool CSSParser::validCalculationUnit(CSSParserValue* value, Units unitflags, ReleaseParsedCalcValueCondition releaseCalc) 1603{ 1604 bool mustBeNonNegative = unitflags & FNonNeg; 1605 1606 if (!parseCalculation(value, mustBeNonNegative ? CalculationRangeNonNegative : CalculationRangeAll)) 1607 return false; 1608 1609 bool b = false; 1610 switch (m_parsedCalculation->category()) { 1611 case CalcLength: 1612 b = (unitflags & FLength); 1613 break; 1614 case CalcPercent: 1615 b = (unitflags & FPercent); 1616 if (b && mustBeNonNegative && m_parsedCalculation->isNegative()) 1617 b = false; 1618 break; 1619 case CalcNumber: 1620 b = (unitflags & FNumber); 1621 if (!b && (unitflags & FInteger) && m_parsedCalculation->isInt()) 1622 b = true; 1623 if (b && mustBeNonNegative && m_parsedCalculation->isNegative()) 1624 b = false; 1625 break; 1626 case CalcPercentLength: 1627 b = (unitflags & FPercent) && (unitflags & FLength); 1628 break; 1629 case CalcPercentNumber: 1630 b = (unitflags & FPercent) && (unitflags & FNumber); 1631 break; 1632#if ENABLE(CSS_VARIABLES) 1633 case CalcVariable: 1634 b = true; 1635 break; 1636#endif 1637 case CalcOther: 1638 break; 1639 } 1640 if (!b || releaseCalc == ReleaseParsedCalcValue) 1641 m_parsedCalculation.release(); 1642 return b; 1643} 1644 1645inline bool CSSParser::shouldAcceptUnitLessValues(CSSParserValue* value, Units unitflags, CSSParserMode cssParserMode) 1646{ 1647 // Qirks mode and svg presentation attributes accept unit less values. 1648 return (unitflags & (FLength | FAngle | FTime)) && (!value->fValue || cssParserMode == CSSQuirksMode || cssParserMode == SVGAttributeMode); 1649} 1650 1651bool CSSParser::validUnit(CSSParserValue* value, Units unitflags, CSSParserMode cssParserMode, ReleaseParsedCalcValueCondition releaseCalc) 1652{ 1653 if (isCalculation(value)) 1654 return validCalculationUnit(value, unitflags, releaseCalc); 1655 1656 bool b = false; 1657 switch (value->unit) { 1658#if ENABLE(CSS_VARIABLES) 1659 case CSSPrimitiveValue::CSS_VARIABLE_NAME: 1660 // Variables are checked at the point they are dereferenced because unit type is not available here. 1661 b = true; 1662 break; 1663#endif 1664 case CSSPrimitiveValue::CSS_NUMBER: 1665 b = (unitflags & FNumber); 1666 if (!b && shouldAcceptUnitLessValues(value, unitflags, cssParserMode)) { 1667 value->unit = (unitflags & FLength) ? CSSPrimitiveValue::CSS_PX : 1668 ((unitflags & FAngle) ? CSSPrimitiveValue::CSS_DEG : CSSPrimitiveValue::CSS_MS); 1669 b = true; 1670 } 1671 if (!b && (unitflags & FInteger) && value->isInt) 1672 b = true; 1673 if (!b && (unitflags & FPositiveInteger) && value->isInt && value->fValue > 0) 1674 b = true; 1675 break; 1676 case CSSPrimitiveValue::CSS_PERCENTAGE: 1677 b = (unitflags & FPercent); 1678 break; 1679 case CSSParserValue::Q_EMS: 1680 case CSSPrimitiveValue::CSS_EMS: 1681 case CSSPrimitiveValue::CSS_REMS: 1682 case CSSPrimitiveValue::CSS_CHS: 1683 case CSSPrimitiveValue::CSS_EXS: 1684 case CSSPrimitiveValue::CSS_PX: 1685 case CSSPrimitiveValue::CSS_CM: 1686 case CSSPrimitiveValue::CSS_MM: 1687 case CSSPrimitiveValue::CSS_IN: 1688 case CSSPrimitiveValue::CSS_PT: 1689 case CSSPrimitiveValue::CSS_PC: 1690 case CSSPrimitiveValue::CSS_VW: 1691 case CSSPrimitiveValue::CSS_VH: 1692 case CSSPrimitiveValue::CSS_VMIN: 1693 case CSSPrimitiveValue::CSS_VMAX: 1694 b = (unitflags & FLength); 1695 break; 1696 case CSSPrimitiveValue::CSS_MS: 1697 case CSSPrimitiveValue::CSS_S: 1698 b = (unitflags & FTime); 1699 break; 1700 case CSSPrimitiveValue::CSS_DEG: 1701 case CSSPrimitiveValue::CSS_RAD: 1702 case CSSPrimitiveValue::CSS_GRAD: 1703 case CSSPrimitiveValue::CSS_TURN: 1704 b = (unitflags & FAngle); 1705 break; 1706#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY) 1707 case CSSPrimitiveValue::CSS_DPPX: 1708 case CSSPrimitiveValue::CSS_DPI: 1709 case CSSPrimitiveValue::CSS_DPCM: 1710 b = (unitflags & FResolution); 1711 break; 1712#endif 1713 case CSSPrimitiveValue::CSS_HZ: 1714 case CSSPrimitiveValue::CSS_KHZ: 1715 case CSSPrimitiveValue::CSS_DIMENSION: 1716 default: 1717 break; 1718 } 1719 if (b && unitflags & FNonNeg && value->fValue < 0) 1720 b = false; 1721 return b; 1722} 1723 1724inline PassRefPtr<CSSPrimitiveValue> CSSParser::createPrimitiveNumericValue(CSSParserValue* value) 1725{ 1726#if ENABLE(CSS_VARIABLES) 1727 if (value->unit == CSSPrimitiveValue::CSS_VARIABLE_NAME) 1728 return createPrimitiveVariableNameValue(value); 1729#endif 1730 1731 if (m_parsedCalculation) { 1732 ASSERT(isCalculation(value)); 1733 return CSSPrimitiveValue::create(m_parsedCalculation.release()); 1734 } 1735 1736#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY) 1737 ASSERT((value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) 1738 || (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_CHS) 1739 || (value->unit >= CSSPrimitiveValue::CSS_VW && value->unit <= CSSPrimitiveValue::CSS_VMAX) 1740 || (value->unit >= CSSPrimitiveValue::CSS_DPPX && value->unit <= CSSPrimitiveValue::CSS_DPCM)); 1741#else 1742 ASSERT((value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) 1743 || (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_CHS) 1744 || (value->unit >= CSSPrimitiveValue::CSS_VW && value->unit <= CSSPrimitiveValue::CSS_VMAX)); 1745#endif 1746 return cssValuePool().createValue(value->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(value->unit)); 1747} 1748 1749inline PassRefPtr<CSSPrimitiveValue> CSSParser::createPrimitiveStringValue(CSSParserValue* value) 1750{ 1751 ASSERT(value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT); 1752 return cssValuePool().createValue(value->string, CSSPrimitiveValue::CSS_STRING); 1753} 1754 1755#if ENABLE(CSS_VARIABLES) 1756inline PassRefPtr<CSSPrimitiveValue> CSSParser::createPrimitiveVariableNameValue(CSSParserValue* value) 1757{ 1758 ASSERT(value->unit == CSSPrimitiveValue::CSS_VARIABLE_NAME); 1759 return CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_VARIABLE_NAME); 1760} 1761#endif 1762 1763static inline bool isComma(CSSParserValue* value) 1764{ 1765 return value && value->unit == CSSParserValue::Operator && value->iValue == ','; 1766} 1767 1768static inline bool isForwardSlashOperator(CSSParserValue* value) 1769{ 1770 ASSERT(value); 1771 return value->unit == CSSParserValue::Operator && value->iValue == '/'; 1772} 1773 1774bool CSSParser::validWidth(CSSParserValue* value) 1775{ 1776 int id = value->id; 1777 if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic || id == CSSValueWebkitMinContent || id == CSSValueWebkitMaxContent || id == CSSValueWebkitFillAvailable || id == CSSValueWebkitFitContent) 1778 return true; 1779 return !id && validUnit(value, FLength | FPercent | FNonNeg); 1780} 1781 1782// FIXME: Combine this with validWidth when we support fit-content, et al, for heights. 1783bool CSSParser::validHeight(CSSParserValue* value) 1784{ 1785 int id = value->id; 1786 if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic) 1787 return true; 1788 return !id && validUnit(value, FLength | FPercent | FNonNeg); 1789} 1790 1791inline PassRefPtr<CSSPrimitiveValue> CSSParser::parseValidPrimitive(int identifier, CSSParserValue* value) 1792{ 1793 if (identifier) 1794 return cssValuePool().createIdentifierValue(identifier); 1795 if (value->unit == CSSPrimitiveValue::CSS_STRING) 1796 return createPrimitiveStringValue(value); 1797 if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) 1798 return createPrimitiveNumericValue(value); 1799 if (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_CHS) 1800 return createPrimitiveNumericValue(value); 1801 if (value->unit >= CSSPrimitiveValue::CSS_VW && value->unit <= CSSPrimitiveValue::CSS_VMAX) 1802 return createPrimitiveNumericValue(value); 1803#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY) 1804 if (value->unit >= CSSPrimitiveValue::CSS_DPPX && value->unit <= CSSPrimitiveValue::CSS_DPCM) 1805 return createPrimitiveNumericValue(value); 1806#endif 1807#if ENABLE(CSS_VARIABLES) 1808 if (value->unit == CSSPrimitiveValue::CSS_VARIABLE_NAME) 1809 return createPrimitiveVariableNameValue(value); 1810#endif 1811 if (value->unit >= CSSParserValue::Q_EMS) 1812 return CSSPrimitiveValue::createAllowingMarginQuirk(value->fValue, CSSPrimitiveValue::CSS_EMS); 1813 if (isCalculation(value)) 1814 return CSSPrimitiveValue::create(m_parsedCalculation.release()); 1815 1816 return 0; 1817} 1818 1819void CSSParser::addExpandedPropertyForValue(CSSPropertyID propId, PassRefPtr<CSSValue> prpValue, bool important) 1820{ 1821 const StylePropertyShorthand& shorthand = shorthandForProperty(propId); 1822 unsigned shorthandLength = shorthand.length(); 1823 if (!shorthandLength) { 1824 addProperty(propId, prpValue, important); 1825 return; 1826 } 1827 1828 RefPtr<CSSValue> value = prpValue; 1829 ShorthandScope scope(this, propId); 1830 const CSSPropertyID* longhands = shorthand.properties(); 1831 for (unsigned i = 0; i < shorthandLength; ++i) 1832 addProperty(longhands[i], value, important); 1833} 1834 1835bool CSSParser::parseValue(CSSPropertyID propId, bool important) 1836{ 1837 if (!m_valueList) 1838 return false; 1839 1840 CSSParserValue* value = m_valueList->current(); 1841 1842 if (!value) 1843 return false; 1844 1845 // Note: m_parsedCalculation is used to pass the calc value to validUnit and then cleared at the end of this function. 1846 // FIXME: This is to avoid having to pass parsedCalc to all validUnit callers. 1847 ASSERT(!m_parsedCalculation); 1848 1849 int id = value->id; 1850 1851 int num = inShorthand() ? 1 : m_valueList->size(); 1852 1853 if (id == CSSValueInherit) { 1854 if (num != 1) 1855 return false; 1856 addExpandedPropertyForValue(propId, cssValuePool().createInheritedValue(), important); 1857 return true; 1858 } 1859 else if (id == CSSValueInitial) { 1860 if (num != 1) 1861 return false; 1862 addExpandedPropertyForValue(propId, cssValuePool().createExplicitInitialValue(), important); 1863 return true; 1864 } 1865 1866#if ENABLE(CSS_VARIABLES) 1867 if (!id && value->unit == CSSPrimitiveValue::CSS_VARIABLE_NAME && num == 1) { 1868 addProperty(propId, createPrimitiveVariableNameValue(value), important); 1869 m_valueList->next(); 1870 return true; 1871 } 1872 ASSERT(propId != CSSPropertyVariable); 1873#endif 1874 1875 if (isKeywordPropertyID(propId)) { 1876 if (!isValidKeywordPropertyAndValue(propId, id, m_context)) 1877 return false; 1878 if (m_valueList->next() && !inShorthand()) 1879 return false; 1880 addProperty(propId, cssValuePool().createIdentifierValue(id), important); 1881 return true; 1882 } 1883 1884#if ENABLE(CSS_DEVICE_ADAPTATION) 1885 if (inViewport()) 1886 return parseViewportProperty(propId, important); 1887#endif 1888 1889 bool validPrimitive = false; 1890 RefPtr<CSSValue> parsedValue; 1891 1892 switch (propId) { 1893 case CSSPropertySize: // <length>{1,2} | auto | [ <page-size> || [ portrait | landscape] ] 1894 return parseSize(propId, important); 1895 1896 case CSSPropertyQuotes: // [<string> <string>]+ | none | inherit 1897 if (id) 1898 validPrimitive = true; 1899 else 1900 return parseQuotes(propId, important); 1901 break; 1902 case CSSPropertyUnicodeBidi: // normal | embed | bidi-override | isolate | isolate-override | plaintext | inherit 1903 if (id == CSSValueNormal 1904 || id == CSSValueEmbed 1905 || id == CSSValueBidiOverride 1906 || id == CSSValueWebkitIsolate 1907 || id == CSSValueWebkitIsolateOverride 1908 || id == CSSValueWebkitPlaintext) 1909 validPrimitive = true; 1910 break; 1911 1912 case CSSPropertyContent: // [ <string> | <uri> | <counter> | attr(X) | open-quote | 1913 // close-quote | no-open-quote | no-close-quote ]+ | inherit 1914 return parseContent(propId, important); 1915 1916 case CSSPropertyClip: // <shape> | auto | inherit 1917 if (id == CSSValueAuto) 1918 validPrimitive = true; 1919 else if (value->unit == CSSParserValue::Function) 1920 return parseClipShape(propId, important); 1921 break; 1922 1923 /* Start of supported CSS properties with validation. This is needed for parseShorthand to work 1924 * correctly and allows optimization in WebCore::applyRule(..) 1925 */ 1926 case CSSPropertyOverflow: { 1927 ShorthandScope scope(this, propId); 1928 if (num != 1 || !parseValue(CSSPropertyOverflowY, important)) 1929 return false; 1930 1931 RefPtr<CSSValue> overflowXValue; 1932 1933 // FIXME: -webkit-paged-x or -webkit-paged-y only apply to overflow-y. If this value has been 1934 // set using the shorthand, then for now overflow-x will default to auto, but once we implement 1935 // pagination controls, it should default to hidden. If the overflow-y value is anything but 1936 // paged-x or paged-y, then overflow-x and overflow-y should have the same value. 1937 if (id == CSSValueWebkitPagedX || id == CSSValueWebkitPagedY) 1938 overflowXValue = cssValuePool().createIdentifierValue(CSSValueAuto); 1939 else 1940 overflowXValue = m_parsedProperties.last().value(); 1941 addProperty(CSSPropertyOverflowX, overflowXValue.release(), important); 1942 return true; 1943 } 1944 1945 case CSSPropertyTextAlign: 1946 // left | right | center | justify | -webkit-left | -webkit-right | -webkit-center | -webkit-match-parent 1947 // | start | end | inherit | -webkit-auto (converted to start) 1948 // NOTE: <string> is not supported. 1949 if ((id >= CSSValueWebkitAuto && id <= CSSValueWebkitMatchParent) || id == CSSValueStart || id == CSSValueEnd) 1950 validPrimitive = true; 1951 break; 1952 1953 case CSSPropertyFontWeight: { // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit 1954 if (m_valueList->size() != 1) 1955 return false; 1956 return parseFontWeight(important); 1957 } 1958 case CSSPropertyBorderSpacing: { 1959 if (num == 1) { 1960 ShorthandScope scope(this, CSSPropertyBorderSpacing); 1961 if (!parseValue(CSSPropertyWebkitBorderHorizontalSpacing, important)) 1962 return false; 1963 CSSValue* value = m_parsedProperties.last().value(); 1964 addProperty(CSSPropertyWebkitBorderVerticalSpacing, value, important); 1965 return true; 1966 } 1967 else if (num == 2) { 1968 ShorthandScope scope(this, CSSPropertyBorderSpacing); 1969 if (!parseValue(CSSPropertyWebkitBorderHorizontalSpacing, important) || !parseValue(CSSPropertyWebkitBorderVerticalSpacing, important)) 1970 return false; 1971 return true; 1972 } 1973 return false; 1974 } 1975 case CSSPropertyWebkitBorderHorizontalSpacing: 1976 case CSSPropertyWebkitBorderVerticalSpacing: 1977 validPrimitive = validUnit(value, FLength | FNonNeg); 1978 break; 1979 case CSSPropertyOutlineColor: // <color> | invert | inherit 1980 // Outline color has "invert" as additional keyword. 1981 // Also, we want to allow the special focus color even in strict parsing mode. 1982 if (id == CSSValueInvert || id == CSSValueWebkitFocusRingColor) { 1983 validPrimitive = true; 1984 break; 1985 } 1986 /* nobreak */ 1987 case CSSPropertyBackgroundColor: // <color> | inherit 1988 case CSSPropertyBorderTopColor: // <color> | inherit 1989 case CSSPropertyBorderRightColor: 1990 case CSSPropertyBorderBottomColor: 1991 case CSSPropertyBorderLeftColor: 1992 case CSSPropertyWebkitBorderStartColor: 1993 case CSSPropertyWebkitBorderEndColor: 1994 case CSSPropertyWebkitBorderBeforeColor: 1995 case CSSPropertyWebkitBorderAfterColor: 1996 case CSSPropertyColor: // <color> | inherit 1997 case CSSPropertyTextLineThroughColor: // CSS3 text decoration colors 1998 case CSSPropertyTextUnderlineColor: 1999 case CSSPropertyTextOverlineColor: 2000 case CSSPropertyWebkitColumnRuleColor: 2001#if ENABLE(CSS3_TEXT) 2002 case CSSPropertyWebkitTextDecorationColor: 2003#endif // CSS3_TEXT 2004 case CSSPropertyWebkitTextEmphasisColor: 2005 case CSSPropertyWebkitTextFillColor: 2006 case CSSPropertyWebkitTextStrokeColor: 2007 if (id == CSSValueWebkitText) 2008 validPrimitive = true; // Always allow this, even when strict parsing is on, 2009 // since we use this in our UA sheets. 2010 else if (id == CSSValueCurrentcolor) 2011 validPrimitive = true; 2012 else if ((id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || 2013 (id >= CSSValueWebkitFocusRingColor && id < CSSValueWebkitText && inQuirksMode())) { 2014 validPrimitive = true; 2015 } else { 2016 parsedValue = parseColor(); 2017 if (parsedValue) 2018 m_valueList->next(); 2019 } 2020 break; 2021 2022 case CSSPropertyCursor: { 2023 // Grammar defined by CSS3 UI and modified by CSS4 images: 2024 // [ [<image> [<x> <y>]?,]* 2025 // [ auto | crosshair | default | pointer | progress | move | e-resize | ne-resize | 2026 // nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize | ew-resize | 2027 // ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | text | wait | help | 2028 // vertical-text | cell | context-menu | alias | copy | no-drop | not-allowed | -webkit-zoom-in 2029 // -webkit-zoom-out | all-scroll | -webkit-grab | -webkit-grabbing ] ] | inherit 2030 RefPtr<CSSValueList> list; 2031 while (value) { 2032 RefPtr<CSSValue> image = 0; 2033 if (value->unit == CSSPrimitiveValue::CSS_URI) { 2034 String uri = value->string; 2035 if (!uri.isNull()) 2036 image = CSSImageValue::create(completeURL(uri)); 2037#if ENABLE(CSS_IMAGE_SET) && ENABLE(MOUSE_CURSOR_SCALE) 2038 } else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "-webkit-image-set(")) { 2039 image = parseImageSet(); 2040 if (!image) 2041 break; 2042#endif 2043 } else 2044 break; 2045 2046 Vector<int> coords; 2047 value = m_valueList->next(); 2048 while (value && value->unit == CSSPrimitiveValue::CSS_NUMBER) { 2049 coords.append(int(value->fValue)); 2050 value = m_valueList->next(); 2051 } 2052 bool hasHotSpot = false; 2053 IntPoint hotSpot(-1, -1); 2054 int nrcoords = coords.size(); 2055 if (nrcoords > 0 && nrcoords != 2) 2056 return false; 2057 if (nrcoords == 2) { 2058 hasHotSpot = true; 2059 hotSpot = IntPoint(coords[0], coords[1]); 2060 } 2061 2062 if (!list) 2063 list = CSSValueList::createCommaSeparated(); 2064 2065 if (image) 2066 list->append(CSSCursorImageValue::create(image, hasHotSpot, hotSpot)); 2067 2068 if ((inStrictMode() && !value) || (value && !(value->unit == CSSParserValue::Operator && value->iValue == ','))) 2069 return false; 2070 value = m_valueList->next(); // comma 2071 } 2072 if (list) { 2073 if (!value) { // no value after url list (MSIE 5 compatibility) 2074 if (list->length() != 1) 2075 return false; 2076 } else if (inQuirksMode() && value->id == CSSValueHand) // MSIE 5 compatibility :/ 2077 list->append(cssValuePool().createIdentifierValue(CSSValuePointer)); 2078 else if ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitGrabbing) || value->id == CSSValueCopy || value->id == CSSValueNone) 2079 list->append(cssValuePool().createIdentifierValue(value->id)); 2080 m_valueList->next(); 2081 parsedValue = list.release(); 2082 break; 2083 } else if (value) { 2084 id = value->id; 2085 if (inQuirksMode() && value->id == CSSValueHand) { // MSIE 5 compatibility :/ 2086 id = CSSValuePointer; 2087 validPrimitive = true; 2088 } else if ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitGrabbing) || value->id == CSSValueCopy || value->id == CSSValueNone) 2089 validPrimitive = true; 2090 } else { 2091 ASSERT_NOT_REACHED(); 2092 return false; 2093 } 2094 break; 2095 } 2096 2097#if ENABLE(CURSOR_VISIBILITY) 2098 case CSSPropertyWebkitCursorVisibility: 2099 if (id == CSSValueAuto || id == CSSValueAutoHide) 2100 validPrimitive = true; 2101 break; 2102#endif 2103 2104 case CSSPropertyBackgroundAttachment: 2105 case CSSPropertyBackgroundClip: 2106 case CSSPropertyWebkitBackgroundClip: 2107 case CSSPropertyWebkitBackgroundComposite: 2108 case CSSPropertyBackgroundImage: 2109 case CSSPropertyBackgroundOrigin: 2110 case CSSPropertyWebkitBackgroundOrigin: 2111 case CSSPropertyBackgroundPosition: 2112 case CSSPropertyBackgroundPositionX: 2113 case CSSPropertyBackgroundPositionY: 2114 case CSSPropertyBackgroundSize: 2115 case CSSPropertyWebkitBackgroundSize: 2116 case CSSPropertyBackgroundRepeat: 2117 case CSSPropertyBackgroundRepeatX: 2118 case CSSPropertyBackgroundRepeatY: 2119 case CSSPropertyWebkitMaskClip: 2120 case CSSPropertyWebkitMaskComposite: 2121 case CSSPropertyWebkitMaskImage: 2122 case CSSPropertyWebkitMaskOrigin: 2123 case CSSPropertyWebkitMaskPosition: 2124 case CSSPropertyWebkitMaskPositionX: 2125 case CSSPropertyWebkitMaskPositionY: 2126 case CSSPropertyWebkitMaskSize: 2127 case CSSPropertyWebkitMaskRepeat: 2128 case CSSPropertyWebkitMaskRepeatX: 2129 case CSSPropertyWebkitMaskRepeatY: 2130#if ENABLE(CSS_COMPOSITING) 2131 case CSSPropertyWebkitBackgroundBlendMode: 2132#endif 2133 { 2134 RefPtr<CSSValue> val1; 2135 RefPtr<CSSValue> val2; 2136 CSSPropertyID propId1, propId2; 2137 bool result = false; 2138 if (parseFillProperty(propId, propId1, propId2, val1, val2)) { 2139 OwnPtr<ShorthandScope> shorthandScope; 2140 if (propId == CSSPropertyBackgroundPosition || 2141 propId == CSSPropertyBackgroundRepeat || 2142 propId == CSSPropertyWebkitMaskPosition || 2143 propId == CSSPropertyWebkitMaskRepeat) { 2144 shorthandScope = adoptPtr(new ShorthandScope(this, propId)); 2145 } 2146 addProperty(propId1, val1.release(), important); 2147 if (val2) 2148 addProperty(propId2, val2.release(), important); 2149 result = true; 2150 } 2151 m_implicitShorthand = false; 2152 return result; 2153 } 2154 case CSSPropertyListStyleImage: // <uri> | none | inherit 2155 case CSSPropertyBorderImageSource: 2156 case CSSPropertyWebkitMaskBoxImageSource: 2157 if (id == CSSValueNone) { 2158 parsedValue = cssValuePool().createIdentifierValue(CSSValueNone); 2159 m_valueList->next(); 2160 } else if (value->unit == CSSPrimitiveValue::CSS_URI) { 2161 parsedValue = CSSImageValue::create(completeURL(value->string)); 2162 m_valueList->next(); 2163 } else if (isGeneratedImageValue(value)) { 2164 if (parseGeneratedImage(m_valueList.get(), parsedValue)) 2165 m_valueList->next(); 2166 else 2167 return false; 2168 } 2169#if ENABLE(CSS_IMAGE_SET) 2170 else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "-webkit-image-set(")) { 2171 parsedValue = parseImageSet(); 2172 if (!parsedValue) 2173 return false; 2174 m_valueList->next(); 2175 } 2176#endif 2177 break; 2178 2179 case CSSPropertyWebkitTextStrokeWidth: 2180 case CSSPropertyOutlineWidth: // <border-width> | inherit 2181 case CSSPropertyBorderTopWidth: //// <border-width> | inherit 2182 case CSSPropertyBorderRightWidth: // Which is defined as 2183 case CSSPropertyBorderBottomWidth: // thin | medium | thick | <length> 2184 case CSSPropertyBorderLeftWidth: 2185 case CSSPropertyWebkitBorderStartWidth: 2186 case CSSPropertyWebkitBorderEndWidth: 2187 case CSSPropertyWebkitBorderBeforeWidth: 2188 case CSSPropertyWebkitBorderAfterWidth: 2189 case CSSPropertyWebkitColumnRuleWidth: 2190 if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick) 2191 validPrimitive = true; 2192 else 2193 validPrimitive = validUnit(value, FLength | FNonNeg); 2194 break; 2195 2196 case CSSPropertyLetterSpacing: // normal | <length> | inherit 2197 case CSSPropertyWordSpacing: // normal | <length> | inherit 2198 if (id == CSSValueNormal) 2199 validPrimitive = true; 2200 else 2201 validPrimitive = validUnit(value, FLength); 2202 break; 2203 2204 case CSSPropertyTextIndent: 2205 parsedValue = parseTextIndent(); 2206 break; 2207 2208 case CSSPropertyPaddingTop: //// <padding-width> | inherit 2209 case CSSPropertyPaddingRight: // Which is defined as 2210 case CSSPropertyPaddingBottom: // <length> | <percentage> 2211 case CSSPropertyPaddingLeft: //// 2212 case CSSPropertyWebkitPaddingStart: 2213 case CSSPropertyWebkitPaddingEnd: 2214 case CSSPropertyWebkitPaddingBefore: 2215 case CSSPropertyWebkitPaddingAfter: 2216 validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg)); 2217 break; 2218 2219 case CSSPropertyMaxWidth: 2220 case CSSPropertyWebkitMaxLogicalWidth: 2221 validPrimitive = (id == CSSValueNone || validWidth(value)); 2222 break; 2223 2224 case CSSPropertyMinWidth: 2225 case CSSPropertyWebkitMinLogicalWidth: 2226 validPrimitive = validWidth(value); 2227 break; 2228 2229 case CSSPropertyWidth: 2230 case CSSPropertyWebkitLogicalWidth: 2231 validPrimitive = (id == CSSValueAuto || validWidth(value)); 2232 break; 2233 2234 case CSSPropertyMaxHeight: 2235 case CSSPropertyWebkitMaxLogicalHeight: 2236 validPrimitive = (id == CSSValueNone || validHeight(value)); 2237 break; 2238 2239 case CSSPropertyMinHeight: 2240 case CSSPropertyWebkitMinLogicalHeight: 2241 validPrimitive = validHeight(value); 2242 break; 2243 2244 case CSSPropertyHeight: 2245 case CSSPropertyWebkitLogicalHeight: 2246 validPrimitive = (id == CSSValueAuto || validHeight(value)); 2247 break; 2248 2249 case CSSPropertyFontSize: 2250 return parseFontSize(important); 2251 2252 case CSSPropertyFontVariant: // normal | small-caps | inherit 2253 return parseFontVariant(important); 2254 2255 case CSSPropertyVerticalAlign: 2256 // baseline | sub | super | top | text-top | middle | bottom | text-bottom | 2257 // <percentage> | <length> | inherit 2258 2259 if (id >= CSSValueBaseline && id <= CSSValueWebkitBaselineMiddle) 2260 validPrimitive = true; 2261 else 2262 validPrimitive = (!id && validUnit(value, FLength | FPercent)); 2263 break; 2264 2265 case CSSPropertyBottom: // <length> | <percentage> | auto | inherit 2266 case CSSPropertyLeft: // <length> | <percentage> | auto | inherit 2267 case CSSPropertyRight: // <length> | <percentage> | auto | inherit 2268 case CSSPropertyTop: // <length> | <percentage> | auto | inherit 2269 case CSSPropertyMarginTop: //// <margin-width> | inherit 2270 case CSSPropertyMarginRight: // Which is defined as 2271 case CSSPropertyMarginBottom: // <length> | <percentage> | auto | inherit 2272 case CSSPropertyMarginLeft: //// 2273 case CSSPropertyWebkitMarginStart: 2274 case CSSPropertyWebkitMarginEnd: 2275 case CSSPropertyWebkitMarginBefore: 2276 case CSSPropertyWebkitMarginAfter: 2277 if (id == CSSValueAuto) 2278 validPrimitive = true; 2279 else 2280 validPrimitive = (!id && validUnit(value, FLength | FPercent)); 2281 break; 2282 2283 case CSSPropertyZIndex: // auto | <integer> | inherit 2284 if (id == CSSValueAuto) { 2285 validPrimitive = true; 2286 break; 2287 } 2288 /* nobreak */ 2289 case CSSPropertyOrphans: // <integer> | inherit | auto (We've added support for auto for backwards compatibility) 2290 case CSSPropertyWidows: // <integer> | inherit | auto (Ditto) 2291 if (id == CSSValueAuto) 2292 validPrimitive = true; 2293 else 2294 validPrimitive = (!id && validUnit(value, FInteger, CSSQuirksMode)); 2295 break; 2296 2297 case CSSPropertyLineHeight: 2298 return parseLineHeight(important); 2299 case CSSPropertyCounterIncrement: // [ <identifier> <integer>? ]+ | none | inherit 2300 if (id != CSSValueNone) 2301 return parseCounter(propId, 1, important); 2302 validPrimitive = true; 2303 break; 2304 case CSSPropertyCounterReset: // [ <identifier> <integer>? ]+ | none | inherit 2305 if (id != CSSValueNone) 2306 return parseCounter(propId, 0, important); 2307 validPrimitive = true; 2308 break; 2309 case CSSPropertyFontFamily: 2310 // [[ <family-name> | <generic-family> ],]* [<family-name> | <generic-family>] | inherit 2311 { 2312 parsedValue = parseFontFamily(); 2313 break; 2314 } 2315 2316 case CSSPropertyTextDecoration: 2317 case CSSPropertyWebkitTextDecorationsInEffect: 2318#if ENABLE(CSS3_TEXT) 2319 case CSSPropertyWebkitTextDecorationLine: 2320#endif // CSS3_TEXT 2321 // none | [ underline || overline || line-through || blink ] | inherit 2322 return parseTextDecoration(propId, important); 2323 2324#if ENABLE(CSS3_TEXT) 2325 case CSSPropertyWebkitTextDecorationStyle: 2326 // solid | double | dotted | dashed | wavy 2327 if (id == CSSValueSolid || id == CSSValueDouble || id == CSSValueDotted || id == CSSValueDashed || id == CSSValueWavy) 2328 validPrimitive = true; 2329 break; 2330 2331 case CSSPropertyWebkitTextUnderlinePosition: 2332 // auto | alphabetic | under 2333 return parseTextUnderlinePosition(important); 2334#endif // CSS3_TEXT 2335 2336 case CSSPropertyZoom: // normal | reset | document | <number> | <percentage> | inherit 2337 if (id == CSSValueNormal || id == CSSValueReset || id == CSSValueDocument) 2338 validPrimitive = true; 2339 else 2340 validPrimitive = (!id && validUnit(value, FNumber | FPercent | FNonNeg, CSSStrictMode)); 2341 break; 2342 2343 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. 2344#if ENABLE(CSS_SHADERS) 2345 if (m_inFilterRule) 2346 return parseFilterRuleSrc(); 2347#endif 2348 return parseFontFaceSrc(); 2349 2350#if ENABLE(CSS_SHADERS) 2351 case CSSPropertyMix: 2352 // The mix property is just supported inside of an @filter rule. 2353 if (!m_inFilterRule) 2354 return false; 2355 return parseFilterRuleMix(); 2356 case CSSPropertyParameters: 2357 // The parameters property is just supported inside of an @filter rule. 2358 if (!m_inFilterRule) 2359 return false; 2360 return parseFilterRuleParameters(); 2361#endif 2362 2363 case CSSPropertyUnicodeRange: 2364 return parseFontFaceUnicodeRange(); 2365 2366 /* CSS3 properties */ 2367 2368 case CSSPropertyBorderImage: { 2369 RefPtr<CSSValue> result; 2370 return parseBorderImage(propId, result, important); 2371 } 2372 case CSSPropertyWebkitBorderImage: 2373 case CSSPropertyWebkitMaskBoxImage: { 2374 RefPtr<CSSValue> result; 2375 if (parseBorderImage(propId, result)) { 2376 addProperty(propId, result, important); 2377 return true; 2378 } 2379 break; 2380 } 2381 case CSSPropertyBorderImageOutset: 2382 case CSSPropertyWebkitMaskBoxImageOutset: { 2383 RefPtr<CSSPrimitiveValue> result; 2384 if (parseBorderImageOutset(result)) { 2385 addProperty(propId, result, important); 2386 return true; 2387 } 2388 break; 2389 } 2390 case CSSPropertyBorderImageRepeat: 2391 case CSSPropertyWebkitMaskBoxImageRepeat: { 2392 RefPtr<CSSValue> result; 2393 if (parseBorderImageRepeat(result)) { 2394 addProperty(propId, result, important); 2395 return true; 2396 } 2397 break; 2398 } 2399 case CSSPropertyBorderImageSlice: 2400 case CSSPropertyWebkitMaskBoxImageSlice: { 2401 RefPtr<CSSBorderImageSliceValue> result; 2402 if (parseBorderImageSlice(propId, result)) { 2403 addProperty(propId, result, important); 2404 return true; 2405 } 2406 break; 2407 } 2408 case CSSPropertyBorderImageWidth: 2409 case CSSPropertyWebkitMaskBoxImageWidth: { 2410 RefPtr<CSSPrimitiveValue> result; 2411 if (parseBorderImageWidth(result)) { 2412 addProperty(propId, result, important); 2413 return true; 2414 } 2415 break; 2416 } 2417 case CSSPropertyBorderTopRightRadius: 2418 case CSSPropertyBorderTopLeftRadius: 2419 case CSSPropertyBorderBottomLeftRadius: 2420 case CSSPropertyBorderBottomRightRadius: { 2421 if (num != 1 && num != 2) 2422 return false; 2423 validPrimitive = validUnit(value, FLength | FPercent | FNonNeg); 2424 if (!validPrimitive) 2425 return false; 2426 RefPtr<CSSPrimitiveValue> parsedValue1 = createPrimitiveNumericValue(value); 2427 RefPtr<CSSPrimitiveValue> parsedValue2; 2428 if (num == 2) { 2429 value = m_valueList->next(); 2430 validPrimitive = validUnit(value, FLength | FPercent | FNonNeg); 2431 if (!validPrimitive) 2432 return false; 2433 parsedValue2 = createPrimitiveNumericValue(value); 2434 } else 2435 parsedValue2 = parsedValue1; 2436 2437 addProperty(propId, createPrimitiveValuePair(parsedValue1.release(), parsedValue2.release()), important); 2438 return true; 2439 } 2440 case CSSPropertyTabSize: 2441 validPrimitive = validUnit(value, FInteger | FNonNeg); 2442 break; 2443 case CSSPropertyWebkitAspectRatio: 2444 return parseAspectRatio(important); 2445 case CSSPropertyBorderRadius: 2446 case CSSPropertyWebkitBorderRadius: 2447 return parseBorderRadius(propId, important); 2448 case CSSPropertyOutlineOffset: 2449 validPrimitive = validUnit(value, FLength | FPercent); 2450 break; 2451 case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3 2452 case CSSPropertyBoxShadow: 2453 case CSSPropertyWebkitBoxShadow: 2454 if (id == CSSValueNone) 2455 validPrimitive = true; 2456 else { 2457 RefPtr<CSSValueList> shadowValueList = parseShadow(m_valueList.get(), propId); 2458 if (shadowValueList) { 2459 addProperty(propId, shadowValueList.release(), important); 2460 m_valueList->next(); 2461 return true; 2462 } 2463 return false; 2464 } 2465 break; 2466 case CSSPropertyWebkitBoxReflect: 2467 if (id == CSSValueNone) 2468 validPrimitive = true; 2469 else 2470 return parseReflect(propId, important); 2471 break; 2472 case CSSPropertyOpacity: 2473 validPrimitive = validUnit(value, FNumber); 2474 break; 2475 case CSSPropertyWebkitBoxFlex: 2476 validPrimitive = validUnit(value, FNumber); 2477 break; 2478 case CSSPropertyWebkitBoxFlexGroup: 2479 validPrimitive = validUnit(value, FInteger | FNonNeg, CSSStrictMode); 2480 break; 2481 case CSSPropertyWebkitBoxOrdinalGroup: 2482 validPrimitive = validUnit(value, FInteger | FNonNeg, CSSStrictMode) && value->fValue; 2483 break; 2484#if ENABLE(CSS_FILTERS) 2485 case CSSPropertyWebkitFilter: 2486 if (id == CSSValueNone) 2487 validPrimitive = true; 2488 else { 2489 RefPtr<CSSValue> val = parseFilter(); 2490 if (val) { 2491 addProperty(propId, val, important); 2492 return true; 2493 } 2494 return false; 2495 } 2496 break; 2497#endif 2498#if ENABLE(CSS_COMPOSITING) 2499 case CSSPropertyWebkitBlendMode: 2500 if (cssCompositingEnabled()) 2501 validPrimitive = true; 2502 break; 2503#endif 2504 case CSSPropertyWebkitFlex: { 2505 ShorthandScope scope(this, propId); 2506 if (id == CSSValueNone) { 2507 addProperty(CSSPropertyWebkitFlexGrow, cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER), important); 2508 addProperty(CSSPropertyWebkitFlexShrink, cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER), important); 2509 addProperty(CSSPropertyWebkitFlexBasis, cssValuePool().createIdentifierValue(CSSValueAuto), important); 2510 return true; 2511 } 2512 return parseFlex(m_valueList.get(), important); 2513 } 2514 case CSSPropertyWebkitFlexBasis: 2515 // FIXME: Support intrinsic dimensions too. 2516 if (id == CSSValueAuto) 2517 validPrimitive = true; 2518 else 2519 validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg)); 2520 break; 2521 case CSSPropertyWebkitFlexGrow: 2522 case CSSPropertyWebkitFlexShrink: 2523 validPrimitive = validUnit(value, FNumber | FNonNeg); 2524 break; 2525 case CSSPropertyWebkitOrder: 2526 if (validUnit(value, FInteger, CSSStrictMode)) { 2527 // 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. 2528 parsedValue = cssValuePool().createValue(max(static_cast<double>(std::numeric_limits<int>::min() + 2), value->fValue), 2529 static_cast<CSSPrimitiveValue::UnitTypes>(value->unit)); 2530 m_valueList->next(); 2531 } 2532 break; 2533 case CSSPropertyWebkitMarquee: 2534 return parseShorthand(propId, webkitMarqueeShorthand(), important); 2535 case CSSPropertyWebkitMarqueeIncrement: 2536 if (id == CSSValueSmall || id == CSSValueLarge || id == CSSValueMedium) 2537 validPrimitive = true; 2538 else 2539 validPrimitive = validUnit(value, FLength | FPercent); 2540 break; 2541 case CSSPropertyWebkitMarqueeRepetition: 2542 if (id == CSSValueInfinite) 2543 validPrimitive = true; 2544 else 2545 validPrimitive = validUnit(value, FInteger | FNonNeg); 2546 break; 2547 case CSSPropertyWebkitMarqueeSpeed: 2548 if (id == CSSValueNormal || id == CSSValueSlow || id == CSSValueFast) 2549 validPrimitive = true; 2550 else 2551 validPrimitive = validUnit(value, FTime | FInteger | FNonNeg); 2552 break; 2553#if ENABLE(CSS_REGIONS) 2554 case CSSPropertyWebkitFlowInto: 2555 if (!cssRegionsEnabled()) 2556 return false; 2557 return parseFlowThread(propId, important); 2558 case CSSPropertyWebkitFlowFrom: 2559 if (!cssRegionsEnabled()) 2560 return false; 2561 return parseRegionThread(propId, important); 2562#endif 2563 case CSSPropertyWebkitTransform: 2564 if (id == CSSValueNone) 2565 validPrimitive = true; 2566 else { 2567 RefPtr<CSSValue> transformValue = parseTransform(); 2568 if (transformValue) { 2569 addProperty(propId, transformValue.release(), important); 2570 return true; 2571 } 2572 return false; 2573 } 2574 break; 2575 case CSSPropertyWebkitTransformOrigin: 2576 case CSSPropertyWebkitTransformOriginX: 2577 case CSSPropertyWebkitTransformOriginY: 2578 case CSSPropertyWebkitTransformOriginZ: { 2579 RefPtr<CSSValue> val1; 2580 RefPtr<CSSValue> val2; 2581 RefPtr<CSSValue> val3; 2582 CSSPropertyID propId1, propId2, propId3; 2583 if (parseTransformOrigin(propId, propId1, propId2, propId3, val1, val2, val3)) { 2584 addProperty(propId1, val1.release(), important); 2585 if (val2) 2586 addProperty(propId2, val2.release(), important); 2587 if (val3) 2588 addProperty(propId3, val3.release(), important); 2589 return true; 2590 } 2591 return false; 2592 } 2593 case CSSPropertyWebkitPerspective: 2594 if (id == CSSValueNone) 2595 validPrimitive = true; 2596 else { 2597 // Accepting valueless numbers is a quirk of the -webkit prefixed version of the property. 2598 if (validUnit(value, FNumber | FLength | FNonNeg)) { 2599 RefPtr<CSSValue> val = createPrimitiveNumericValue(value); 2600 if (val) { 2601 addProperty(propId, val.release(), important); 2602 return true; 2603 } 2604 return false; 2605 } 2606 } 2607 break; 2608 case CSSPropertyWebkitPerspectiveOrigin: 2609 case CSSPropertyWebkitPerspectiveOriginX: 2610 case CSSPropertyWebkitPerspectiveOriginY: { 2611 RefPtr<CSSValue> val1; 2612 RefPtr<CSSValue> val2; 2613 CSSPropertyID propId1, propId2; 2614 if (parsePerspectiveOrigin(propId, propId1, propId2, val1, val2)) { 2615 addProperty(propId1, val1.release(), important); 2616 if (val2) 2617 addProperty(propId2, val2.release(), important); 2618 return true; 2619 } 2620 return false; 2621 } 2622 case CSSPropertyWebkitAnimationDelay: 2623 case CSSPropertyWebkitAnimationDirection: 2624 case CSSPropertyWebkitAnimationDuration: 2625 case CSSPropertyWebkitAnimationFillMode: 2626 case CSSPropertyWebkitAnimationName: 2627 case CSSPropertyWebkitAnimationPlayState: 2628 case CSSPropertyWebkitAnimationIterationCount: 2629 case CSSPropertyWebkitAnimationTimingFunction: 2630 case CSSPropertyTransitionDelay: 2631 case CSSPropertyTransitionDuration: 2632 case CSSPropertyTransitionTimingFunction: 2633 case CSSPropertyTransitionProperty: 2634 case CSSPropertyWebkitTransitionDelay: 2635 case CSSPropertyWebkitTransitionDuration: 2636 case CSSPropertyWebkitTransitionTimingFunction: 2637 case CSSPropertyWebkitTransitionProperty: { 2638 RefPtr<CSSValue> val; 2639 AnimationParseContext context; 2640 if (parseAnimationProperty(propId, val, context)) { 2641 addPropertyWithPrefixingVariant(propId, val.release(), important); 2642 return true; 2643 } 2644 return false; 2645 } 2646 2647 case CSSPropertyWebkitGridAutoColumns: 2648 case CSSPropertyWebkitGridAutoRows: 2649 if (!cssGridLayoutEnabled()) 2650 return false; 2651 parsedValue = parseGridTrackSize(); 2652 break; 2653 2654 case CSSPropertyWebkitGridColumns: 2655 case CSSPropertyWebkitGridRows: 2656 if (!cssGridLayoutEnabled()) 2657 return false; 2658 return parseGridTrackList(propId, important); 2659 2660 case CSSPropertyWebkitGridStart: 2661 case CSSPropertyWebkitGridEnd: 2662 case CSSPropertyWebkitGridBefore: 2663 case CSSPropertyWebkitGridAfter: 2664 if (!cssGridLayoutEnabled()) 2665 return false; 2666 2667 validPrimitive = id == CSSValueAuto || (validUnit(value, FInteger) && value->fValue); 2668 break; 2669 2670 case CSSPropertyWebkitGridColumn: 2671 case CSSPropertyWebkitGridRow: { 2672 if (!cssGridLayoutEnabled()) 2673 return false; 2674 2675 return parseGridItemPositionShorthand(propId, important); 2676 } 2677 2678 case CSSPropertyWebkitMarginCollapse: { 2679 if (num == 1) { 2680 ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse); 2681 if (!parseValue(webkitMarginCollapseShorthand().properties()[0], important)) 2682 return false; 2683 CSSValue* value = m_parsedProperties.last().value(); 2684 addProperty(webkitMarginCollapseShorthand().properties()[1], value, important); 2685 return true; 2686 } 2687 else if (num == 2) { 2688 ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse); 2689 if (!parseValue(webkitMarginCollapseShorthand().properties()[0], important) || !parseValue(webkitMarginCollapseShorthand().properties()[1], important)) 2690 return false; 2691 return true; 2692 } 2693 return false; 2694 } 2695 case CSSPropertyTextLineThroughWidth: 2696 case CSSPropertyTextOverlineWidth: 2697 case CSSPropertyTextUnderlineWidth: 2698 if (id == CSSValueAuto || id == CSSValueNormal || id == CSSValueThin || 2699 id == CSSValueMedium || id == CSSValueThick) 2700 validPrimitive = true; 2701 else 2702 validPrimitive = !id && validUnit(value, FNumber | FLength | FPercent); 2703 break; 2704 case CSSPropertyWebkitColumnCount: 2705 if (id == CSSValueAuto) 2706 validPrimitive = true; 2707 else 2708 validPrimitive = !id && validUnit(value, FPositiveInteger, CSSQuirksMode); 2709 break; 2710 case CSSPropertyWebkitColumnGap: // normal | <length> 2711 if (id == CSSValueNormal) 2712 validPrimitive = true; 2713 else 2714 validPrimitive = validUnit(value, FLength | FNonNeg); 2715 break; 2716 case CSSPropertyWebkitColumnAxis: 2717 if (id == CSSValueHorizontal || id == CSSValueVertical || id == CSSValueAuto) 2718 validPrimitive = true; 2719 break; 2720 case CSSPropertyWebkitColumnProgression: 2721 if (id == CSSValueNormal || id == CSSValueReverse) 2722 validPrimitive = true; 2723 break; 2724 case CSSPropertyWebkitColumnSpan: // none | all | 1 (will be dropped in the unprefixed property) 2725 if (id == CSSValueAll || id == CSSValueNone) 2726 validPrimitive = true; 2727 else 2728 validPrimitive = validUnit(value, FNumber | FNonNeg) && value->fValue == 1; 2729 break; 2730 case CSSPropertyWebkitColumnWidth: // auto | <length> 2731 if (id == CSSValueAuto) 2732 validPrimitive = true; 2733 else // Always parse this property in strict mode, since it would be ambiguous otherwise when used in the 'columns' shorthand property. 2734 validPrimitive = validUnit(value, FLength | FNonNeg, CSSStrictMode) && value->fValue; 2735 break; 2736 // End of CSS3 properties 2737 2738 // Apple specific properties. These will never be standardized and are purely to 2739 // support custom WebKit-based Apple applications. 2740 case CSSPropertyWebkitLineClamp: 2741 // When specifying number of lines, don't allow 0 as a valid value 2742 // When specifying either type of unit, require non-negative integers 2743 validPrimitive = (!id && (value->unit == CSSPrimitiveValue::CSS_PERCENTAGE || value->fValue) && validUnit(value, FInteger | FPercent | FNonNeg, CSSQuirksMode)); 2744 break; 2745 2746 case CSSPropertyWebkitFontSizeDelta: // <length> 2747 validPrimitive = validUnit(value, FLength); 2748 break; 2749 2750 case CSSPropertyWebkitHighlight: 2751 if (id == CSSValueNone || value->unit == CSSPrimitiveValue::CSS_STRING) 2752 validPrimitive = true; 2753 break; 2754 2755 case CSSPropertyWebkitHyphenateCharacter: 2756 if (id == CSSValueAuto || value->unit == CSSPrimitiveValue::CSS_STRING) 2757 validPrimitive = true; 2758 break; 2759 2760 case CSSPropertyWebkitHyphenateLimitBefore: 2761 case CSSPropertyWebkitHyphenateLimitAfter: 2762 if (id == CSSValueAuto || validUnit(value, FInteger | FNonNeg, CSSStrictMode)) 2763 validPrimitive = true; 2764 break; 2765 2766 case CSSPropertyWebkitHyphenateLimitLines: 2767 if (id == CSSValueNoLimit || validUnit(value, FInteger | FNonNeg, CSSStrictMode)) 2768 validPrimitive = true; 2769 break; 2770 2771 case CSSPropertyWebkitLineGrid: 2772 if (id == CSSValueNone) 2773 validPrimitive = true; 2774 else if (value->unit == CSSPrimitiveValue::CSS_IDENT) { 2775 String lineGridValue = String(value->string); 2776 if (!lineGridValue.isEmpty()) { 2777 addProperty(propId, cssValuePool().createValue(lineGridValue, CSSPrimitiveValue::CSS_STRING), important); 2778 return true; 2779 } 2780 } 2781 break; 2782 case CSSPropertyWebkitLocale: 2783 if (id == CSSValueAuto || value->unit == CSSPrimitiveValue::CSS_STRING) 2784 validPrimitive = true; 2785 break; 2786 2787#if ENABLE(DASHBOARD_SUPPORT) 2788 case CSSPropertyWebkitDashboardRegion: // <dashboard-region> | <dashboard-region> 2789 if (value->unit == CSSParserValue::Function || id == CSSValueNone) 2790 return parseDashboardRegions(propId, important); 2791 break; 2792#endif 2793 // End Apple-specific properties 2794 2795#if ENABLE(DRAGGABLE_REGION) 2796 case CSSPropertyWebkitAppRegion: 2797 if (id >= CSSValueDrag && id <= CSSValueNoDrag) 2798 validPrimitive = true; 2799 break; 2800#endif 2801 2802#if ENABLE(TOUCH_EVENTS) 2803 case CSSPropertyWebkitTapHighlightColor: 2804 if ((id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu 2805 || (id >= CSSValueWebkitFocusRingColor && id < CSSValueWebkitText && inQuirksMode())) { 2806 validPrimitive = true; 2807 } else { 2808 parsedValue = parseColor(); 2809 if (parsedValue) 2810 m_valueList->next(); 2811 } 2812 break; 2813#endif 2814 2815 /* shorthand properties */ 2816 case CSSPropertyBackground: { 2817 // Position must come before color in this array because a plain old "0" is a legal color 2818 // in quirks mode but it's usually the X coordinate of a position. 2819 const CSSPropertyID properties[] = { CSSPropertyBackgroundImage, CSSPropertyBackgroundRepeat, 2820 CSSPropertyBackgroundAttachment, CSSPropertyBackgroundPosition, CSSPropertyBackgroundOrigin, 2821 CSSPropertyBackgroundClip, CSSPropertyBackgroundColor, CSSPropertyBackgroundSize }; 2822 return parseFillShorthand(propId, properties, WTF_ARRAY_LENGTH(properties), important); 2823 } 2824 case CSSPropertyWebkitMask: { 2825 const CSSPropertyID properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskRepeat, 2826 CSSPropertyWebkitMaskPosition, CSSPropertyWebkitMaskOrigin, CSSPropertyWebkitMaskClip, CSSPropertyWebkitMaskSize }; 2827 return parseFillShorthand(propId, properties, WTF_ARRAY_LENGTH(properties), important); 2828 } 2829 case CSSPropertyBorder: 2830 // [ 'border-width' || 'border-style' || <color> ] | inherit 2831 { 2832 if (parseShorthand(propId, borderAbridgedShorthand(), important)) { 2833 // The CSS3 Borders and Backgrounds specification says that border also resets border-image. It's as 2834 // though a value of none was specified for the image. 2835 addExpandedPropertyForValue(CSSPropertyBorderImage, cssValuePool().createImplicitInitialValue(), important); 2836 return true; 2837 } 2838 return false; 2839 } 2840 case CSSPropertyBorderTop: 2841 // [ 'border-top-width' || 'border-style' || <color> ] | inherit 2842 return parseShorthand(propId, borderTopShorthand(), important); 2843 case CSSPropertyBorderRight: 2844 // [ 'border-right-width' || 'border-style' || <color> ] | inherit 2845 return parseShorthand(propId, borderRightShorthand(), important); 2846 case CSSPropertyBorderBottom: 2847 // [ 'border-bottom-width' || 'border-style' || <color> ] | inherit 2848 return parseShorthand(propId, borderBottomShorthand(), important); 2849 case CSSPropertyBorderLeft: 2850 // [ 'border-left-width' || 'border-style' || <color> ] | inherit 2851 return parseShorthand(propId, borderLeftShorthand(), important); 2852 case CSSPropertyWebkitBorderStart: 2853 return parseShorthand(propId, webkitBorderStartShorthand(), important); 2854 case CSSPropertyWebkitBorderEnd: 2855 return parseShorthand(propId, webkitBorderEndShorthand(), important); 2856 case CSSPropertyWebkitBorderBefore: 2857 return parseShorthand(propId, webkitBorderBeforeShorthand(), important); 2858 case CSSPropertyWebkitBorderAfter: 2859 return parseShorthand(propId, webkitBorderAfterShorthand(), important); 2860 case CSSPropertyOutline: 2861 // [ 'outline-color' || 'outline-style' || 'outline-width' ] | inherit 2862 return parseShorthand(propId, outlineShorthand(), important); 2863 case CSSPropertyBorderColor: 2864 // <color>{1,4} | inherit 2865 return parse4Values(propId, borderColorShorthand().properties(), important); 2866 case CSSPropertyBorderWidth: 2867 // <border-width>{1,4} | inherit 2868 return parse4Values(propId, borderWidthShorthand().properties(), important); 2869 case CSSPropertyBorderStyle: 2870 // <border-style>{1,4} | inherit 2871 return parse4Values(propId, borderStyleShorthand().properties(), important); 2872 case CSSPropertyMargin: 2873 // <margin-width>{1,4} | inherit 2874 return parse4Values(propId, marginShorthand().properties(), important); 2875 case CSSPropertyPadding: 2876 // <padding-width>{1,4} | inherit 2877 return parse4Values(propId, paddingShorthand().properties(), important); 2878 case CSSPropertyWebkitFlexFlow: 2879 return parseShorthand(propId, webkitFlexFlowShorthand(), important); 2880 case CSSPropertyFont: 2881 // [ [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 2882 // 'font-family' ] | caption | icon | menu | message-box | small-caption | status-bar | inherit 2883 if (id >= CSSValueCaption && id <= CSSValueStatusBar) 2884 validPrimitive = true; 2885 else 2886 return parseFont(important); 2887 break; 2888 case CSSPropertyListStyle: 2889 return parseShorthand(propId, listStyleShorthand(), important); 2890 case CSSPropertyWebkitColumns: 2891 return parseShorthand(propId, webkitColumnsShorthand(), important); 2892 case CSSPropertyWebkitColumnRule: 2893 return parseShorthand(propId, webkitColumnRuleShorthand(), important); 2894 case CSSPropertyWebkitTextStroke: 2895 return parseShorthand(propId, webkitTextStrokeShorthand(), important); 2896 case CSSPropertyWebkitAnimation: 2897 return parseAnimationShorthand(important); 2898 case CSSPropertyTransition: 2899 case CSSPropertyWebkitTransition: 2900 return parseTransitionShorthand(propId, important); 2901 case CSSPropertyInvalid: 2902 return false; 2903 case CSSPropertyPage: 2904 return parsePage(propId, important); 2905#if ENABLE(CSS_SHADERS) 2906 case CSSPropertyGeometry: 2907 return m_inFilterRule ? parseGeometry(propId, value, important) : false; 2908#endif 2909 case CSSPropertyFontStretch: 2910 case CSSPropertyTextLineThrough: 2911 case CSSPropertyTextOverline: 2912 case CSSPropertyTextUnderline: 2913 return false; 2914 // CSS Text Layout Module Level 3: Vertical writing support 2915 case CSSPropertyWebkitTextEmphasis: 2916 return parseShorthand(propId, webkitTextEmphasisShorthand(), important); 2917 2918 case CSSPropertyWebkitTextEmphasisStyle: 2919 return parseTextEmphasisStyle(important); 2920 2921 case CSSPropertyWebkitTextOrientation: 2922 // FIXME: For now just support sideways, sideways-right, upright and vertical-right. 2923 if (id == CSSValueSideways || id == CSSValueSidewaysRight || id == CSSValueVerticalRight || id == CSSValueUpright) 2924 validPrimitive = true; 2925 break; 2926 2927 case CSSPropertyWebkitLineBoxContain: 2928 if (id == CSSValueNone) 2929 validPrimitive = true; 2930 else 2931 return parseLineBoxContain(important); 2932 break; 2933 case CSSPropertyWebkitFontFeatureSettings: 2934 if (id == CSSValueNormal) 2935 validPrimitive = true; 2936 else 2937 return parseFontFeatureSettings(important); 2938 break; 2939 2940 case CSSPropertyWebkitFontVariantLigatures: 2941 if (id == CSSValueNormal) 2942 validPrimitive = true; 2943 else 2944 return parseFontVariantLigatures(important); 2945 break; 2946 case CSSPropertyWebkitClipPath: 2947 if (id == CSSValueNone) 2948 validPrimitive = true; 2949 else if (value->unit == CSSParserValue::Function) 2950 return parseBasicShape(propId, important); 2951#if ENABLE(SVG) 2952 else if (value->unit == CSSPrimitiveValue::CSS_URI) { 2953 parsedValue = CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_URI); 2954 addProperty(propId, parsedValue.release(), important); 2955 return true; 2956 } 2957#endif 2958 break; 2959#if ENABLE(CSS_SHAPES) 2960 case CSSPropertyWebkitShapeInside: 2961 case CSSPropertyWebkitShapeOutside: 2962 if (!RuntimeEnabledFeatures::cssShapesEnabled()) 2963 return false; 2964 if (id == CSSValueAuto) 2965 validPrimitive = true; 2966 else if (propId == CSSPropertyWebkitShapeInside && id == CSSValueOutsideShape) 2967 validPrimitive = true; 2968 else if (value->unit == CSSParserValue::Function) 2969 return parseBasicShape(propId, important); 2970 else if (value->unit == CSSPrimitiveValue::CSS_URI) { 2971 parsedValue = CSSImageValue::create(completeURL(value->string)); 2972 m_valueList->next(); 2973 } 2974 break; 2975 case CSSPropertyWebkitShapeMargin: 2976 case CSSPropertyWebkitShapePadding: 2977 validPrimitive = (RuntimeEnabledFeatures::cssShapesEnabled() && !id && validUnit(value, FLength | FNonNeg)); 2978 break; 2979#endif 2980#if ENABLE(CSS_IMAGE_ORIENTATION) 2981 case CSSPropertyImageOrientation: 2982 validPrimitive = !id && validUnit(value, FAngle); 2983 break; 2984#endif 2985#if ENABLE(CSS_IMAGE_RESOLUTION) 2986 case CSSPropertyImageResolution: 2987 parsedValue = parseImageResolution(); 2988 break; 2989#endif 2990 case CSSPropertyBorderBottomStyle: 2991 case CSSPropertyBorderCollapse: 2992 case CSSPropertyBorderLeftStyle: 2993 case CSSPropertyBorderRightStyle: 2994 case CSSPropertyBorderTopStyle: 2995 case CSSPropertyBoxSizing: 2996 case CSSPropertyCaptionSide: 2997 case CSSPropertyClear: 2998 case CSSPropertyDirection: 2999 case CSSPropertyDisplay: 3000 case CSSPropertyEmptyCells: 3001 case CSSPropertyFloat: 3002 case CSSPropertyFontStyle: 3003 case CSSPropertyImageRendering: 3004 case CSSPropertyListStylePosition: 3005 case CSSPropertyListStyleType: 3006 case CSSPropertyOutlineStyle: 3007 case CSSPropertyOverflowWrap: 3008 case CSSPropertyOverflowX: 3009 case CSSPropertyOverflowY: 3010 case CSSPropertyPageBreakAfter: 3011 case CSSPropertyPageBreakBefore: 3012 case CSSPropertyPageBreakInside: 3013 case CSSPropertyPointerEvents: 3014 case CSSPropertyPosition: 3015 case CSSPropertyResize: 3016 case CSSPropertySpeak: 3017 case CSSPropertyTableLayout: 3018 case CSSPropertyTextLineThroughMode: 3019 case CSSPropertyTextLineThroughStyle: 3020 case CSSPropertyTextOverflow: 3021 case CSSPropertyTextOverlineMode: 3022 case CSSPropertyTextOverlineStyle: 3023 case CSSPropertyTextRendering: 3024 case CSSPropertyTextTransform: 3025 case CSSPropertyTextUnderlineMode: 3026 case CSSPropertyTextUnderlineStyle: 3027#if ENABLE(CSS_VARIABLES) 3028 case CSSPropertyVariable: 3029#endif 3030 case CSSPropertyVisibility: 3031 case CSSPropertyWebkitAppearance: 3032 case CSSPropertyWebkitBackfaceVisibility: 3033 case CSSPropertyWebkitBorderAfterStyle: 3034 case CSSPropertyWebkitBorderBeforeStyle: 3035 case CSSPropertyWebkitBorderEndStyle: 3036 case CSSPropertyWebkitBorderFit: 3037 case CSSPropertyWebkitBorderStartStyle: 3038 case CSSPropertyWebkitBoxAlign: 3039#if ENABLE(CSS_BOX_DECORATION_BREAK) 3040 case CSSPropertyWebkitBoxDecorationBreak: 3041#endif 3042 case CSSPropertyWebkitBoxDirection: 3043 case CSSPropertyWebkitBoxLines: 3044 case CSSPropertyWebkitBoxOrient: 3045 case CSSPropertyWebkitBoxPack: 3046 case CSSPropertyWebkitColorCorrection: 3047 case CSSPropertyWebkitColumnBreakAfter: 3048 case CSSPropertyWebkitColumnBreakBefore: 3049 case CSSPropertyWebkitColumnBreakInside: 3050 case CSSPropertyWebkitColumnRuleStyle: 3051 case CSSPropertyWebkitAlignContent: 3052 case CSSPropertyWebkitAlignItems: 3053 case CSSPropertyWebkitAlignSelf: 3054 case CSSPropertyWebkitFlexDirection: 3055 case CSSPropertyWebkitFlexWrap: 3056 case CSSPropertyWebkitJustifyContent: 3057 case CSSPropertyWebkitFontKerning: 3058 case CSSPropertyWebkitFontSmoothing: 3059 case CSSPropertyWebkitHyphens: 3060 case CSSPropertyWebkitGridAutoFlow: 3061 case CSSPropertyWebkitLineAlign: 3062 case CSSPropertyWebkitLineBreak: 3063 case CSSPropertyWebkitLineSnap: 3064 case CSSPropertyWebkitMarginAfterCollapse: 3065 case CSSPropertyWebkitMarginBeforeCollapse: 3066 case CSSPropertyWebkitMarginBottomCollapse: 3067 case CSSPropertyWebkitMarginTopCollapse: 3068 case CSSPropertyWebkitMarqueeDirection: 3069 case CSSPropertyWebkitMarqueeStyle: 3070 case CSSPropertyWebkitNbspMode: 3071#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) 3072 case CSSPropertyWebkitOverflowScrolling: 3073#endif 3074 case CSSPropertyWebkitPrintColorAdjust: 3075#if ENABLE(CSS_REGIONS) 3076 case CSSPropertyWebkitRegionBreakAfter: 3077 case CSSPropertyWebkitRegionBreakBefore: 3078 case CSSPropertyWebkitRegionBreakInside: 3079 case CSSPropertyWebkitRegionFragment: 3080#endif 3081 case CSSPropertyWebkitRtlOrdering: 3082 case CSSPropertyWebkitRubyPosition: 3083#if ENABLE(CSS3_TEXT) 3084 case CSSPropertyWebkitTextAlignLast: 3085#endif // CSS3_TEXT 3086 case CSSPropertyWebkitTextCombine: 3087 case CSSPropertyWebkitTextEmphasisPosition: 3088#if ENABLE(CSS3_TEXT) 3089 case CSSPropertyWebkitTextJustify: 3090#endif // CSS3_TEXT 3091 case CSSPropertyWebkitTextSecurity: 3092 case CSSPropertyWebkitTransformStyle: 3093 case CSSPropertyWebkitUserDrag: 3094 case CSSPropertyWebkitUserModify: 3095 case CSSPropertyWebkitUserSelect: 3096#if ENABLE(CSS_EXCLUSIONS) 3097 case CSSPropertyWebkitWrapFlow: 3098 case CSSPropertyWebkitWrapThrough: 3099#endif 3100 case CSSPropertyWebkitWritingMode: 3101 case CSSPropertyWhiteSpace: 3102 case CSSPropertyWordBreak: 3103 case CSSPropertyWordWrap: 3104 // These properties should be handled before in isValidKeywordPropertyAndValue(). 3105 ASSERT_NOT_REACHED(); 3106 return false; 3107#if ENABLE(CSS_DEVICE_ADAPTATION) 3108 // Properties bellow are validated inside parseViewportProperty, because we 3109 // check for parser state inViewportScope. We need to invalidate if someone 3110 // adds them outside a @viewport rule. 3111 case CSSPropertyMaxZoom: 3112 case CSSPropertyMinZoom: 3113 case CSSPropertyOrientation: 3114 case CSSPropertyUserZoom: 3115 validPrimitive = false; 3116 break; 3117#endif 3118#if ENABLE(SVG) 3119 default: 3120 return parseSVGValue(propId, important); 3121#endif 3122 } 3123 3124 if (validPrimitive) { 3125 parsedValue = parseValidPrimitive(id, value); 3126 m_valueList->next(); 3127 } 3128 ASSERT(!m_parsedCalculation); 3129 if (parsedValue) { 3130 if (!m_valueList->current() || inShorthand()) { 3131 addProperty(propId, parsedValue.release(), important); 3132 return true; 3133 } 3134 } 3135 return false; 3136} 3137 3138void CSSParser::addFillValue(RefPtr<CSSValue>& lval, PassRefPtr<CSSValue> rval) 3139{ 3140 if (lval) { 3141 if (lval->isBaseValueList()) 3142 static_cast<CSSValueList*>(lval.get())->append(rval); 3143 else { 3144 PassRefPtr<CSSValue> oldlVal(lval.release()); 3145 PassRefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 3146 list->append(oldlVal); 3147 list->append(rval); 3148 lval = list; 3149 } 3150 } 3151 else 3152 lval = rval; 3153} 3154 3155static bool parseBackgroundClip(CSSParserValue* parserValue, RefPtr<CSSValue>& cssValue) 3156{ 3157 if (parserValue->id == CSSValueBorderBox || parserValue->id == CSSValuePaddingBox 3158 || parserValue->id == CSSValueContentBox || parserValue->id == CSSValueWebkitText) { 3159 cssValue = cssValuePool().createIdentifierValue(parserValue->id); 3160 return true; 3161 } 3162 return false; 3163} 3164 3165bool CSSParser::useLegacyBackgroundSizeShorthandBehavior() const 3166{ 3167 return m_context.useLegacyBackgroundSizeShorthandBehavior; 3168} 3169 3170const int cMaxFillProperties = 9; 3171 3172bool CSSParser::parseFillShorthand(CSSPropertyID propId, const CSSPropertyID* properties, int numProperties, bool important) 3173{ 3174 ASSERT(numProperties <= cMaxFillProperties); 3175 if (numProperties > cMaxFillProperties) 3176 return false; 3177 3178 ShorthandScope scope(this, propId); 3179 3180 bool parsedProperty[cMaxFillProperties] = { false }; 3181 RefPtr<CSSValue> values[cMaxFillProperties]; 3182 RefPtr<CSSValue> clipValue; 3183 RefPtr<CSSValue> positionYValue; 3184 RefPtr<CSSValue> repeatYValue; 3185 bool foundClip = false; 3186 int i; 3187 bool foundPositionCSSProperty = false; 3188 3189 while (m_valueList->current()) { 3190 CSSParserValue* val = m_valueList->current(); 3191 if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 3192 // We hit the end. Fill in all remaining values with the initial value. 3193 m_valueList->next(); 3194 for (i = 0; i < numProperties; ++i) { 3195 if (properties[i] == CSSPropertyBackgroundColor && parsedProperty[i]) 3196 // Color is not allowed except as the last item in a list for backgrounds. 3197 // Reject the entire property. 3198 return false; 3199 3200 if (!parsedProperty[i] && properties[i] != CSSPropertyBackgroundColor) { 3201 addFillValue(values[i], cssValuePool().createImplicitInitialValue()); 3202 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 3203 addFillValue(positionYValue, cssValuePool().createImplicitInitialValue()); 3204 if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) 3205 addFillValue(repeatYValue, cssValuePool().createImplicitInitialValue()); 3206 if ((properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) && !parsedProperty[i]) { 3207 // If background-origin wasn't present, then reset background-clip also. 3208 addFillValue(clipValue, cssValuePool().createImplicitInitialValue()); 3209 } 3210 } 3211 parsedProperty[i] = false; 3212 } 3213 if (!m_valueList->current()) 3214 break; 3215 } 3216 3217 bool sizeCSSPropertyExpected = false; 3218 if (isForwardSlashOperator(val) && foundPositionCSSProperty) { 3219 sizeCSSPropertyExpected = true; 3220 m_valueList->next(); 3221 } 3222 3223 foundPositionCSSProperty = false; 3224 bool found = false; 3225 for (i = 0; !found && i < numProperties; ++i) { 3226 3227 if (sizeCSSPropertyExpected && (properties[i] != CSSPropertyBackgroundSize && properties[i] != CSSPropertyWebkitMaskSize)) 3228 continue; 3229 if (!sizeCSSPropertyExpected && (properties[i] == CSSPropertyBackgroundSize || properties[i] == CSSPropertyWebkitMaskSize)) 3230 continue; 3231 3232 if (!parsedProperty[i]) { 3233 RefPtr<CSSValue> val1; 3234 RefPtr<CSSValue> val2; 3235 CSSPropertyID propId1, propId2; 3236 CSSParserValue* parserValue = m_valueList->current(); 3237 if (parseFillProperty(properties[i], propId1, propId2, val1, val2)) { 3238 parsedProperty[i] = found = true; 3239 addFillValue(values[i], val1.release()); 3240 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 3241 addFillValue(positionYValue, val2.release()); 3242 if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) 3243 addFillValue(repeatYValue, val2.release()); 3244 if (properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) { 3245 // Reparse the value as a clip, and see if we succeed. 3246 if (parseBackgroundClip(parserValue, val1)) 3247 addFillValue(clipValue, val1.release()); // The property parsed successfully. 3248 else 3249 addFillValue(clipValue, cssValuePool().createImplicitInitialValue()); // Some value was used for origin that is not supported by clip. Just reset clip instead. 3250 } 3251 if (properties[i] == CSSPropertyBackgroundClip || properties[i] == CSSPropertyWebkitMaskClip) { 3252 // Update clipValue 3253 addFillValue(clipValue, val1.release()); 3254 foundClip = true; 3255 } 3256 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 3257 foundPositionCSSProperty = true; 3258 } 3259 } 3260 } 3261 3262 // if we didn't find at least one match, this is an 3263 // invalid shorthand and we have to ignore it 3264 if (!found) 3265 return false; 3266 } 3267 3268 // Now add all of the properties we found. 3269 for (i = 0; i < numProperties; i++) { 3270 // Fill in any remaining properties with the initial value. 3271 if (!parsedProperty[i]) { 3272 addFillValue(values[i], cssValuePool().createImplicitInitialValue()); 3273 if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) 3274 addFillValue(positionYValue, cssValuePool().createImplicitInitialValue()); 3275 if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) 3276 addFillValue(repeatYValue, cssValuePool().createImplicitInitialValue()); 3277 if (properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) { 3278 // If background-origin wasn't present, then reset background-clip also. 3279 addFillValue(clipValue, cssValuePool().createImplicitInitialValue()); 3280 } 3281 } 3282 if (properties[i] == CSSPropertyBackgroundPosition) { 3283 addProperty(CSSPropertyBackgroundPositionX, values[i].release(), important); 3284 // it's OK to call positionYValue.release() since we only see CSSPropertyBackgroundPosition once 3285 addProperty(CSSPropertyBackgroundPositionY, positionYValue.release(), important); 3286 } else if (properties[i] == CSSPropertyWebkitMaskPosition) { 3287 addProperty(CSSPropertyWebkitMaskPositionX, values[i].release(), important); 3288 // it's OK to call positionYValue.release() since we only see CSSPropertyWebkitMaskPosition once 3289 addProperty(CSSPropertyWebkitMaskPositionY, positionYValue.release(), important); 3290 } else if (properties[i] == CSSPropertyBackgroundRepeat) { 3291 addProperty(CSSPropertyBackgroundRepeatX, values[i].release(), important); 3292 // it's OK to call repeatYValue.release() since we only see CSSPropertyBackgroundPosition once 3293 addProperty(CSSPropertyBackgroundRepeatY, repeatYValue.release(), important); 3294 } else if (properties[i] == CSSPropertyWebkitMaskRepeat) { 3295 addProperty(CSSPropertyWebkitMaskRepeatX, values[i].release(), important); 3296 // it's OK to call repeatYValue.release() since we only see CSSPropertyBackgroundPosition once 3297 addProperty(CSSPropertyWebkitMaskRepeatY, repeatYValue.release(), important); 3298 } else if ((properties[i] == CSSPropertyBackgroundClip || properties[i] == CSSPropertyWebkitMaskClip) && !foundClip) 3299 // Value is already set while updating origin 3300 continue; 3301 else if (properties[i] == CSSPropertyBackgroundSize && !parsedProperty[i] && useLegacyBackgroundSizeShorthandBehavior()) 3302 continue; 3303 else 3304 addProperty(properties[i], values[i].release(), important); 3305 3306 // Add in clip values when we hit the corresponding origin property. 3307 if (properties[i] == CSSPropertyBackgroundOrigin && !foundClip) 3308 addProperty(CSSPropertyBackgroundClip, clipValue.release(), important); 3309 else if (properties[i] == CSSPropertyWebkitMaskOrigin && !foundClip) 3310 addProperty(CSSPropertyWebkitMaskClip, clipValue.release(), important); 3311 } 3312 3313 return true; 3314} 3315 3316#if ENABLE(CSS_VARIABLES) 3317bool CSSParser::cssVariablesEnabled() const 3318{ 3319 return m_context.isCSSVariablesEnabled; 3320} 3321 3322void CSSParser::storeVariableDeclaration(const CSSParserString& name, PassOwnPtr<CSSParserValueList> value, bool important) 3323{ 3324 // When CSSGrammar.y encounters an invalid declaration it passes null for the CSSParserValueList, just bail. 3325 if (!value) 3326 return; 3327 3328 static const unsigned prefixLength = sizeof("-webkit-var-") - 1; 3329 3330 ASSERT(name.length() > prefixLength); 3331 AtomicString variableName = name.substring(prefixLength, name.length() - prefixLength); 3332 3333 StringBuilder builder; 3334 for (unsigned i = 0, size = value->size(); i < size; i++) { 3335 if (i) 3336 builder.append(' '); 3337 RefPtr<CSSValue> cssValue = value->valueAt(i)->createCSSValue(); 3338 if (!cssValue) 3339 return; 3340 builder.append(cssValue->cssText()); 3341 } 3342 addProperty(CSSPropertyVariable, CSSVariableValue::create(variableName, builder.toString().lower()), important, false); 3343} 3344#endif 3345 3346void CSSParser::addAnimationValue(RefPtr<CSSValue>& lval, PassRefPtr<CSSValue> rval) 3347{ 3348 if (lval) { 3349 if (lval->isValueList()) 3350 static_cast<CSSValueList*>(lval.get())->append(rval); 3351 else { 3352 PassRefPtr<CSSValue> oldVal(lval.release()); 3353 PassRefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 3354 list->append(oldVal); 3355 list->append(rval); 3356 lval = list; 3357 } 3358 } 3359 else 3360 lval = rval; 3361} 3362 3363bool CSSParser::parseAnimationShorthand(bool important) 3364{ 3365 const StylePropertyShorthand& animationProperties = webkitAnimationShorthandForParsing(); 3366 const unsigned numProperties = 7; 3367 3368 // The list of properties in the shorthand should be the same 3369 // length as the list with animation name in last position, even though they are 3370 // in a different order. 3371 ASSERT(numProperties == webkitAnimationShorthandForParsing().length()); 3372 ASSERT(numProperties == webkitAnimationShorthand().length()); 3373 3374 ShorthandScope scope(this, CSSPropertyWebkitAnimation); 3375 3376 bool parsedProperty[numProperties] = { false }; 3377 AnimationParseContext context; 3378 RefPtr<CSSValue> values[numProperties]; 3379 3380 unsigned i; 3381 while (m_valueList->current()) { 3382 CSSParserValue* val = m_valueList->current(); 3383 if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 3384 // We hit the end. Fill in all remaining values with the initial value. 3385 m_valueList->next(); 3386 for (i = 0; i < numProperties; ++i) { 3387 if (!parsedProperty[i]) 3388 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3389 parsedProperty[i] = false; 3390 } 3391 if (!m_valueList->current()) 3392 break; 3393 context.commitFirstAnimation(); 3394 } 3395 3396 bool found = false; 3397 for (i = 0; i < numProperties; ++i) { 3398 if (!parsedProperty[i]) { 3399 RefPtr<CSSValue> val; 3400 if (parseAnimationProperty(animationProperties.properties()[i], val, context)) { 3401 parsedProperty[i] = found = true; 3402 addAnimationValue(values[i], val.release()); 3403 break; 3404 } 3405 } 3406 3407 // There are more values to process but 'none' or 'all' were already defined as the animation property, the declaration becomes invalid. 3408 if (!context.animationPropertyKeywordAllowed() && context.hasCommittedFirstAnimation()) 3409 return false; 3410 } 3411 3412 // if we didn't find at least one match, this is an 3413 // invalid shorthand and we have to ignore it 3414 if (!found) 3415 return false; 3416 } 3417 3418 for (i = 0; i < numProperties; ++i) { 3419 // If we didn't find the property, set an intial value. 3420 if (!parsedProperty[i]) 3421 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3422 3423 addProperty(animationProperties.properties()[i], values[i].release(), important); 3424 } 3425 3426 return true; 3427} 3428 3429bool CSSParser::parseTransitionShorthand(CSSPropertyID propId, bool important) 3430{ 3431 const unsigned numProperties = 4; 3432 const StylePropertyShorthand& shorthand = shorthandForProperty(propId); 3433 ASSERT(numProperties == shorthand.length()); 3434 3435 ShorthandScope scope(this, propId); 3436 3437 bool parsedProperty[numProperties] = { false }; 3438 AnimationParseContext context; 3439 RefPtr<CSSValue> values[numProperties]; 3440 3441 unsigned i; 3442 while (m_valueList->current()) { 3443 CSSParserValue* val = m_valueList->current(); 3444 if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 3445 // We hit the end. Fill in all remaining values with the initial value. 3446 m_valueList->next(); 3447 for (i = 0; i < numProperties; ++i) { 3448 if (!parsedProperty[i]) 3449 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3450 parsedProperty[i] = false; 3451 } 3452 if (!m_valueList->current()) 3453 break; 3454 context.commitFirstAnimation(); 3455 } 3456 3457 bool found = false; 3458 for (i = 0; !found && i < numProperties; ++i) { 3459 if (!parsedProperty[i]) { 3460 RefPtr<CSSValue> val; 3461 if (parseAnimationProperty(shorthand.properties()[i], val, context)) { 3462 parsedProperty[i] = found = true; 3463 addAnimationValue(values[i], val.release()); 3464 } 3465 3466 // There are more values to process but 'none' or 'all' were already defined as the animation property, the declaration becomes invalid. 3467 if (!context.animationPropertyKeywordAllowed() && context.hasCommittedFirstAnimation()) 3468 return false; 3469 } 3470 } 3471 3472 // if we didn't find at least one match, this is an 3473 // invalid shorthand and we have to ignore it 3474 if (!found) 3475 return false; 3476 } 3477 3478 // Fill in any remaining properties with the initial value. 3479 for (i = 0; i < numProperties; ++i) { 3480 if (!parsedProperty[i]) 3481 addAnimationValue(values[i], cssValuePool().createImplicitInitialValue()); 3482 } 3483 3484 // Now add all of the properties we found. 3485 for (i = 0; i < numProperties; i++) 3486 addPropertyWithPrefixingVariant(shorthand.properties()[i], values[i].release(), important); 3487 3488 return true; 3489} 3490 3491bool CSSParser::parseShorthand(CSSPropertyID propId, const StylePropertyShorthand& shorthand, bool important) 3492{ 3493 // We try to match as many properties as possible 3494 // We set up an array of booleans to mark which property has been found, 3495 // and we try to search for properties until it makes no longer any sense. 3496 ShorthandScope scope(this, propId); 3497 3498 bool found = false; 3499 unsigned propertiesParsed = 0; 3500 bool propertyFound[6]= { false, false, false, false, false, false }; // 6 is enough size. 3501 3502 while (m_valueList->current()) { 3503 found = false; 3504 for (unsigned propIndex = 0; !found && propIndex < shorthand.length(); ++propIndex) { 3505 if (!propertyFound[propIndex] && parseValue(shorthand.properties()[propIndex], important)) { 3506 propertyFound[propIndex] = found = true; 3507 propertiesParsed++; 3508 } 3509 } 3510 3511 // if we didn't find at least one match, this is an 3512 // invalid shorthand and we have to ignore it 3513 if (!found) 3514 return false; 3515 } 3516 3517 if (propertiesParsed == shorthand.length()) 3518 return true; 3519 3520 // Fill in any remaining properties with the initial value. 3521 ImplicitScope implicitScope(this, PropertyImplicit); 3522 const StylePropertyShorthand* const* const propertiesForInitialization = shorthand.propertiesForInitialization(); 3523 for (unsigned i = 0; i < shorthand.length(); ++i) { 3524 if (propertyFound[i]) 3525 continue; 3526 3527 if (propertiesForInitialization) { 3528 const StylePropertyShorthand& initProperties = *(propertiesForInitialization[i]); 3529 for (unsigned propIndex = 0; propIndex < initProperties.length(); ++propIndex) 3530 addProperty(initProperties.properties()[propIndex], cssValuePool().createImplicitInitialValue(), important); 3531 } else 3532 addProperty(shorthand.properties()[i], cssValuePool().createImplicitInitialValue(), important); 3533 } 3534 3535 return true; 3536} 3537 3538bool CSSParser::parse4Values(CSSPropertyID propId, const CSSPropertyID *properties, bool important) 3539{ 3540 /* From the CSS 2 specs, 8.3 3541 * If there is only one value, it applies to all sides. If there are two values, the top and 3542 * bottom margins are set to the first value and the right and left margins are set to the second. 3543 * If there are three values, the top is set to the first value, the left and right are set to the 3544 * second, and the bottom is set to the third. If there are four values, they apply to the top, 3545 * right, bottom, and left, respectively. 3546 */ 3547 3548 int num = inShorthand() ? 1 : m_valueList->size(); 3549 3550 ShorthandScope scope(this, propId); 3551 3552 // the order is top, right, bottom, left 3553 switch (num) { 3554 case 1: { 3555 if (!parseValue(properties[0], important)) 3556 return false; 3557 CSSValue* value = m_parsedProperties.last().value(); 3558 ImplicitScope implicitScope(this, PropertyImplicit); 3559 addProperty(properties[1], value, important); 3560 addProperty(properties[2], value, important); 3561 addProperty(properties[3], value, important); 3562 break; 3563 } 3564 case 2: { 3565 if (!parseValue(properties[0], important) || !parseValue(properties[1], important)) 3566 return false; 3567 CSSValue* value = m_parsedProperties[m_parsedProperties.size() - 2].value(); 3568 ImplicitScope implicitScope(this, PropertyImplicit); 3569 addProperty(properties[2], value, important); 3570 value = m_parsedProperties[m_parsedProperties.size() - 2].value(); 3571 addProperty(properties[3], value, important); 3572 break; 3573 } 3574 case 3: { 3575 if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || !parseValue(properties[2], important)) 3576 return false; 3577 CSSValue* value = m_parsedProperties[m_parsedProperties.size() - 2].value(); 3578 ImplicitScope implicitScope(this, PropertyImplicit); 3579 addProperty(properties[3], value, important); 3580 break; 3581 } 3582 case 4: { 3583 if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || 3584 !parseValue(properties[2], important) || !parseValue(properties[3], important)) 3585 return false; 3586 break; 3587 } 3588 default: { 3589 return false; 3590 } 3591 } 3592 3593 return true; 3594} 3595 3596// auto | <identifier> 3597bool CSSParser::parsePage(CSSPropertyID propId, bool important) 3598{ 3599 ASSERT(propId == CSSPropertyPage); 3600 3601 if (m_valueList->size() != 1) 3602 return false; 3603 3604 CSSParserValue* value = m_valueList->current(); 3605 if (!value) 3606 return false; 3607 3608 if (value->id == CSSValueAuto) { 3609 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 3610 return true; 3611 } else if (value->id == 0 && value->unit == CSSPrimitiveValue::CSS_IDENT) { 3612 addProperty(propId, createPrimitiveStringValue(value), important); 3613 return true; 3614 } 3615 return false; 3616} 3617 3618// <length>{1,2} | auto | [ <page-size> || [ portrait | landscape] ] 3619bool CSSParser::parseSize(CSSPropertyID propId, bool important) 3620{ 3621 ASSERT(propId == CSSPropertySize); 3622 3623 if (m_valueList->size() > 2) 3624 return false; 3625 3626 CSSParserValue* value = m_valueList->current(); 3627 if (!value) 3628 return false; 3629 3630 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated(); 3631 3632 // First parameter. 3633 SizeParameterType paramType = parseSizeParameter(parsedValues.get(), value, None); 3634 if (paramType == None) 3635 return false; 3636 3637 // Second parameter, if any. 3638 value = m_valueList->next(); 3639 if (value) { 3640 paramType = parseSizeParameter(parsedValues.get(), value, paramType); 3641 if (paramType == None) 3642 return false; 3643 } 3644 3645 addProperty(propId, parsedValues.release(), important); 3646 return true; 3647} 3648 3649CSSParser::SizeParameterType CSSParser::parseSizeParameter(CSSValueList* parsedValues, CSSParserValue* value, SizeParameterType prevParamType) 3650{ 3651 switch (value->id) { 3652 case CSSValueAuto: 3653 if (prevParamType == None) { 3654 parsedValues->append(cssValuePool().createIdentifierValue(value->id)); 3655 return Auto; 3656 } 3657 return None; 3658 case CSSValueLandscape: 3659 case CSSValuePortrait: 3660 if (prevParamType == None || prevParamType == PageSize) { 3661 parsedValues->append(cssValuePool().createIdentifierValue(value->id)); 3662 return Orientation; 3663 } 3664 return None; 3665 case CSSValueA3: 3666 case CSSValueA4: 3667 case CSSValueA5: 3668 case CSSValueB4: 3669 case CSSValueB5: 3670 case CSSValueLedger: 3671 case CSSValueLegal: 3672 case CSSValueLetter: 3673 if (prevParamType == None || prevParamType == Orientation) { 3674 // Normalize to Page Size then Orientation order by prepending. 3675 // This is not specified by the CSS3 Paged Media specification, but for simpler processing later (StyleResolver::applyPageSizeProperty). 3676 parsedValues->prepend(cssValuePool().createIdentifierValue(value->id)); 3677 return PageSize; 3678 } 3679 return None; 3680 case 0: 3681 if (validUnit(value, FLength | FNonNeg) && (prevParamType == None || prevParamType == Length)) { 3682 parsedValues->append(createPrimitiveNumericValue(value)); 3683 return Length; 3684 } 3685 return None; 3686 default: 3687 return None; 3688 } 3689} 3690 3691// [ <string> <string> ]+ | inherit | none 3692// inherit and none are handled in parseValue. 3693bool CSSParser::parseQuotes(CSSPropertyID propId, bool important) 3694{ 3695 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); 3696 while (CSSParserValue* val = m_valueList->current()) { 3697 RefPtr<CSSValue> parsedValue; 3698 if (val->unit == CSSPrimitiveValue::CSS_STRING) 3699 parsedValue = CSSPrimitiveValue::create(val->string, CSSPrimitiveValue::CSS_STRING); 3700 else 3701 break; 3702 values->append(parsedValue.release()); 3703 m_valueList->next(); 3704 } 3705 if (values->length()) { 3706 addProperty(propId, values.release(), important); 3707 m_valueList->next(); 3708 return true; 3709 } 3710 return false; 3711} 3712 3713// [ <string> | <uri> | <counter> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit 3714// in CSS 2.1 this got somewhat reduced: 3715// [ <string> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit 3716bool CSSParser::parseContent(CSSPropertyID propId, bool important) 3717{ 3718 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); 3719 3720 while (CSSParserValue* val = m_valueList->current()) { 3721 RefPtr<CSSValue> parsedValue; 3722 if (val->unit == CSSPrimitiveValue::CSS_URI) { 3723 // url 3724 parsedValue = CSSImageValue::create(completeURL(val->string)); 3725 } else if (val->unit == CSSParserValue::Function) { 3726 // attr(X) | counter(X [,Y]) | counters(X, Y, [,Z]) | -webkit-gradient(...) 3727 CSSParserValueList* args = val->function->args.get(); 3728 if (!args) 3729 return false; 3730 if (equalIgnoringCase(val->function->name, "attr(")) { 3731 parsedValue = parseAttr(args); 3732 if (!parsedValue) 3733 return false; 3734 } else if (equalIgnoringCase(val->function->name, "counter(")) { 3735 parsedValue = parseCounterContent(args, false); 3736 if (!parsedValue) 3737 return false; 3738 } else if (equalIgnoringCase(val->function->name, "counters(")) { 3739 parsedValue = parseCounterContent(args, true); 3740 if (!parsedValue) 3741 return false; 3742#if ENABLE(CSS_IMAGE_SET) 3743 } else if (equalIgnoringCase(val->function->name, "-webkit-image-set(")) { 3744 parsedValue = parseImageSet(); 3745 if (!parsedValue) 3746 return false; 3747#endif 3748 } else if (isGeneratedImageValue(val)) { 3749 if (!parseGeneratedImage(m_valueList.get(), parsedValue)) 3750 return false; 3751 } else 3752 return false; 3753 } else if (val->unit == CSSPrimitiveValue::CSS_IDENT) { 3754 // open-quote 3755 // close-quote 3756 // no-open-quote 3757 // no-close-quote 3758 // inherit 3759 // FIXME: These are not yet implemented (http://bugs.webkit.org/show_bug.cgi?id=6503). 3760 // none 3761 // normal 3762 switch (val->id) { 3763 case CSSValueOpenQuote: 3764 case CSSValueCloseQuote: 3765 case CSSValueNoOpenQuote: 3766 case CSSValueNoCloseQuote: 3767 case CSSValueNone: 3768 case CSSValueNormal: 3769 parsedValue = cssValuePool().createIdentifierValue(val->id); 3770 } 3771 } else if (val->unit == CSSPrimitiveValue::CSS_STRING) { 3772 parsedValue = createPrimitiveStringValue(val); 3773 } 3774 if (!parsedValue) 3775 break; 3776 values->append(parsedValue.release()); 3777 m_valueList->next(); 3778 } 3779 3780 if (values->length()) { 3781 addProperty(propId, values.release(), important); 3782 m_valueList->next(); 3783 return true; 3784 } 3785 3786 return false; 3787} 3788 3789PassRefPtr<CSSValue> CSSParser::parseAttr(CSSParserValueList* args) 3790{ 3791 if (args->size() != 1) 3792 return 0; 3793 3794 CSSParserValue* a = args->current(); 3795 3796 if (a->unit != CSSPrimitiveValue::CSS_IDENT) 3797 return 0; 3798 3799 String attrName = a->string; 3800 // CSS allows identifiers with "-" at the start, like "-webkit-mask-image". 3801 // But HTML attribute names can't have those characters, and we should not 3802 // even parse them inside attr(). 3803 if (attrName[0] == '-') 3804 return 0; 3805 3806 if (m_context.isHTMLDocument) 3807 attrName = attrName.lower(); 3808 3809 return cssValuePool().createValue(attrName, CSSPrimitiveValue::CSS_ATTR); 3810} 3811 3812PassRefPtr<CSSValue> CSSParser::parseBackgroundColor() 3813{ 3814 int id = m_valueList->current()->id; 3815 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || id == CSSValueCurrentcolor || 3816 (id >= CSSValueGrey && id < CSSValueWebkitText && inQuirksMode())) 3817 return cssValuePool().createIdentifierValue(id); 3818 return parseColor(); 3819} 3820 3821bool CSSParser::parseFillImage(CSSParserValueList* valueList, RefPtr<CSSValue>& value) 3822{ 3823 if (valueList->current()->id == CSSValueNone) { 3824 value = cssValuePool().createIdentifierValue(CSSValueNone); 3825 return true; 3826 } 3827 if (valueList->current()->unit == CSSPrimitiveValue::CSS_URI) { 3828 value = CSSImageValue::create(completeURL(valueList->current()->string)); 3829 return true; 3830 } 3831 3832 if (isGeneratedImageValue(valueList->current())) 3833 return parseGeneratedImage(valueList, value); 3834 3835#if ENABLE(CSS_IMAGE_SET) 3836 if (valueList->current()->unit == CSSParserValue::Function && equalIgnoringCase(valueList->current()->function->name, "-webkit-image-set(")) { 3837 value = parseImageSet(); 3838 if (value) 3839 return true; 3840 } 3841#endif 3842 3843 return false; 3844} 3845 3846PassRefPtr<CSSValue> CSSParser::parseFillPositionX(CSSParserValueList* valueList) 3847{ 3848 int id = valueList->current()->id; 3849 if (id == CSSValueLeft || id == CSSValueRight || id == CSSValueCenter) { 3850 int percent = 0; 3851 if (id == CSSValueRight) 3852 percent = 100; 3853 else if (id == CSSValueCenter) 3854 percent = 50; 3855 return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); 3856 } 3857 if (validUnit(valueList->current(), FPercent | FLength)) 3858 return createPrimitiveNumericValue(valueList->current()); 3859 return 0; 3860} 3861 3862PassRefPtr<CSSValue> CSSParser::parseFillPositionY(CSSParserValueList* valueList) 3863{ 3864 int id = valueList->current()->id; 3865 if (id == CSSValueTop || id == CSSValueBottom || id == CSSValueCenter) { 3866 int percent = 0; 3867 if (id == CSSValueBottom) 3868 percent = 100; 3869 else if (id == CSSValueCenter) 3870 percent = 50; 3871 return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); 3872 } 3873 if (validUnit(valueList->current(), FPercent | FLength)) 3874 return createPrimitiveNumericValue(valueList->current()); 3875 return 0; 3876} 3877 3878PassRefPtr<CSSPrimitiveValue> CSSParser::parseFillPositionComponent(CSSParserValueList* valueList, unsigned& cumulativeFlags, FillPositionFlag& individualFlag, FillPositionParsingMode parsingMode) 3879{ 3880 int id = valueList->current()->id; 3881 if (id == CSSValueLeft || id == CSSValueTop || id == CSSValueRight || id == CSSValueBottom || id == CSSValueCenter) { 3882 int percent = 0; 3883 if (id == CSSValueLeft || id == CSSValueRight) { 3884 if (cumulativeFlags & XFillPosition) 3885 return 0; 3886 cumulativeFlags |= XFillPosition; 3887 individualFlag = XFillPosition; 3888 if (id == CSSValueRight) 3889 percent = 100; 3890 } 3891 else if (id == CSSValueTop || id == CSSValueBottom) { 3892 if (cumulativeFlags & YFillPosition) 3893 return 0; 3894 cumulativeFlags |= YFillPosition; 3895 individualFlag = YFillPosition; 3896 if (id == CSSValueBottom) 3897 percent = 100; 3898 } else if (id == CSSValueCenter) { 3899 // Center is ambiguous, so we're not sure which position we've found yet, an x or a y. 3900 percent = 50; 3901 cumulativeFlags |= AmbiguousFillPosition; 3902 individualFlag = AmbiguousFillPosition; 3903 } 3904 3905 if (parsingMode == ResolveValuesAsKeyword) 3906 return cssValuePool().createIdentifierValue(id); 3907 3908 return cssValuePool().createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); 3909 } 3910 if (validUnit(valueList->current(), FPercent | FLength)) { 3911 if (!cumulativeFlags) { 3912 cumulativeFlags |= XFillPosition; 3913 individualFlag = XFillPosition; 3914 } else if (cumulativeFlags & (XFillPosition | AmbiguousFillPosition)) { 3915 cumulativeFlags |= YFillPosition; 3916 individualFlag = YFillPosition; 3917 } else { 3918 if (m_parsedCalculation) 3919 m_parsedCalculation.release(); 3920 return 0; 3921 } 3922 return createPrimitiveNumericValue(valueList->current()); 3923 } 3924 return 0; 3925} 3926 3927static bool isValueConflictingWithCurrentEdge(int value1, int value2) 3928{ 3929 if ((value1 == CSSValueLeft || value1 == CSSValueRight) && (value2 == CSSValueLeft || value2 == CSSValueRight)) 3930 return true; 3931 3932 if ((value1 == CSSValueTop || value1 == CSSValueBottom) && (value2 == CSSValueTop || value2 == CSSValueBottom)) 3933 return true; 3934 3935 return false; 3936} 3937 3938static bool isFillPositionKeyword(int value) 3939{ 3940 return value == CSSValueLeft || value == CSSValueTop || value == CSSValueBottom || value == CSSValueRight || value == CSSValueCenter; 3941} 3942 3943void CSSParser::parse4ValuesFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, PassRefPtr<CSSPrimitiveValue> parsedValue1, PassRefPtr<CSSPrimitiveValue> parsedValue2) 3944{ 3945 // [ left | right ] [ <percentage] | <length> ] && [ top | bottom ] [ <percentage> | <length> ] 3946 // In the case of 4 values <position> requires the second value to be a length or a percentage. 3947 if (isFillPositionKeyword(parsedValue2->getIdent())) 3948 return; 3949 3950 unsigned cumulativeFlags = 0; 3951 FillPositionFlag value3Flag = InvalidFillPosition; 3952 RefPtr<CSSPrimitiveValue> value3 = parseFillPositionComponent(valueList, cumulativeFlags, value3Flag, ResolveValuesAsKeyword); 3953 if (!value3) 3954 return; 3955 3956 int ident1 = parsedValue1->getIdent(); 3957 int ident3 = value3->getIdent(); 3958 3959 if (ident1 == CSSValueCenter) 3960 return; 3961 3962 if (!isFillPositionKeyword(ident3) || ident3 == CSSValueCenter) 3963 return; 3964 3965 // We need to check if the values are not conflicting, e.g. they are not on the same edge. It is 3966 // needed as the second call to parseFillPositionComponent was on purpose not checking it. In the 3967 // case of two values top 20px is invalid but in the case of 4 values it becomes valid. 3968 if (isValueConflictingWithCurrentEdge(ident1, ident3)) 3969 return; 3970 3971 valueList->next(); 3972 3973 cumulativeFlags = 0; 3974 FillPositionFlag value4Flag = InvalidFillPosition; 3975 RefPtr<CSSPrimitiveValue> value4 = parseFillPositionComponent(valueList, cumulativeFlags, value4Flag, ResolveValuesAsKeyword); 3976 if (!value4) 3977 return; 3978 3979 // 4th value must be a length or a percentage. 3980 if (isFillPositionKeyword(value4->getIdent())) 3981 return; 3982 3983 value1 = createPrimitiveValuePair(parsedValue1, parsedValue2); 3984 value2 = createPrimitiveValuePair(value3, value4); 3985 3986 if (ident1 == CSSValueTop || ident1 == CSSValueBottom) 3987 value1.swap(value2); 3988 3989 valueList->next(); 3990} 3991void CSSParser::parse3ValuesFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, PassRefPtr<CSSPrimitiveValue> parsedValue1, PassRefPtr<CSSPrimitiveValue> parsedValue2) 3992{ 3993 unsigned cumulativeFlags = 0; 3994 FillPositionFlag value3Flag = InvalidFillPosition; 3995 RefPtr<CSSPrimitiveValue> value3 = parseFillPositionComponent(valueList, cumulativeFlags, value3Flag, ResolveValuesAsKeyword); 3996 3997 // value3 is not an expected value, we return. 3998 if (!value3) 3999 return; 4000 4001 valueList->next(); 4002 4003 bool swapNeeded = false; 4004 int ident1 = parsedValue1->getIdent(); 4005 int ident2 = parsedValue2->getIdent(); 4006 int ident3 = value3->getIdent(); 4007 4008 int firstPositionKeyword; 4009 int secondPositionKeyword; 4010 4011 if (ident1 == CSSValueCenter) { 4012 // <position> requires the first 'center' to be followed by a keyword. 4013 if (!isFillPositionKeyword(ident2)) 4014 return; 4015 4016 // If 'center' is the first keyword then the last one needs to be a length. 4017 if (isFillPositionKeyword(ident3)) 4018 return; 4019 4020 firstPositionKeyword = CSSValueLeft; 4021 if (ident2 == CSSValueLeft || ident2 == CSSValueRight) { 4022 firstPositionKeyword = CSSValueTop; 4023 swapNeeded = true; 4024 } 4025 value1 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(firstPositionKeyword), cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE)); 4026 value2 = createPrimitiveValuePair(parsedValue2, value3); 4027 } else if (ident3 == CSSValueCenter) { 4028 if (isFillPositionKeyword(ident2)) 4029 return; 4030 4031 secondPositionKeyword = CSSValueTop; 4032 if (ident1 == CSSValueTop || ident1 == CSSValueBottom) { 4033 secondPositionKeyword = CSSValueLeft; 4034 swapNeeded = true; 4035 } 4036 value1 = createPrimitiveValuePair(parsedValue1, parsedValue2); 4037 value2 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(secondPositionKeyword), cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE)); 4038 } else { 4039 RefPtr<CSSPrimitiveValue> firstPositionValue; 4040 RefPtr<CSSPrimitiveValue> secondPositionValue; 4041 4042 if (isFillPositionKeyword(ident2)) { 4043 // To match CSS grammar, we should only accept: [ center | left | right | bottom | top ] [ left | right | top | bottom ] [ <percentage> | <length> ]. 4044 ASSERT(ident2 != CSSValueCenter); 4045 4046 if (isFillPositionKeyword(ident3)) 4047 return; 4048 4049 secondPositionValue = value3; 4050 secondPositionKeyword = ident2; 4051 firstPositionValue = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE); 4052 } else { 4053 // Per CSS, we should only accept: [ right | left | top | bottom ] [ <percentage> | <length> ] [ center | left | right | bottom | top ]. 4054 if (!isFillPositionKeyword(ident3)) 4055 return; 4056 4057 firstPositionValue = parsedValue2; 4058 secondPositionKeyword = ident3; 4059 secondPositionValue = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PERCENTAGE); 4060 } 4061 4062 if (isValueConflictingWithCurrentEdge(ident1, secondPositionKeyword)) 4063 return; 4064 4065 value1 = createPrimitiveValuePair(parsedValue1, firstPositionValue); 4066 value2 = createPrimitiveValuePair(cssValuePool().createIdentifierValue(secondPositionKeyword), secondPositionValue); 4067 } 4068 4069 if (ident1 == CSSValueTop || ident1 == CSSValueBottom || swapNeeded) 4070 value1.swap(value2); 4071 4072#ifndef NDEBUG 4073 CSSPrimitiveValue* first = static_cast<CSSPrimitiveValue*>(value1.get()); 4074 CSSPrimitiveValue* second = static_cast<CSSPrimitiveValue*>(value2.get()); 4075 ident1 = first->getPairValue()->first()->getIdent(); 4076 ident2 = second->getPairValue()->first()->getIdent(); 4077 ASSERT(ident1 == CSSValueLeft || ident1 == CSSValueRight); 4078 ASSERT(ident2 == CSSValueBottom || ident2 == CSSValueTop); 4079#endif 4080} 4081 4082inline bool CSSParser::isPotentialPositionValue(CSSParserValue* value) 4083{ 4084 return isFillPositionKeyword(value->id) || validUnit(value, FPercent | FLength, ReleaseParsedCalcValue); 4085} 4086 4087void CSSParser::parseFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2) 4088{ 4089 unsigned numberOfValues = 0; 4090 for (unsigned i = valueList->currentIndex(); i < valueList->size(); ++i, ++numberOfValues) { 4091 CSSParserValue* current = valueList->valueAt(i); 4092 if (isComma(current) || !current || isForwardSlashOperator(current) || !isPotentialPositionValue(current)) 4093 break; 4094 } 4095 4096 if (numberOfValues > 4) 4097 return; 4098 4099 // If we are parsing two values, we can safely call the CSS 2.1 parsing function and return. 4100 if (numberOfValues <= 2) { 4101 parse2ValuesFillPosition(valueList, value1, value2); 4102 return; 4103 } 4104 4105 ASSERT(numberOfValues > 2 && numberOfValues <= 4); 4106 4107 CSSParserValue* value = valueList->current(); 4108 4109 // <position> requires the first value to be a background keyword. 4110 if (!isFillPositionKeyword(value->id)) 4111 return; 4112 4113 // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length. 4114 unsigned cumulativeFlags = 0; 4115 FillPositionFlag value1Flag = InvalidFillPosition; 4116 FillPositionFlag value2Flag = InvalidFillPosition; 4117 value1 = parseFillPositionComponent(valueList, cumulativeFlags, value1Flag, ResolveValuesAsKeyword); 4118 if (!value1) 4119 return; 4120 4121 value = valueList->next(); 4122 4123 // In case we are parsing more than two values, relax the check inside of parseFillPositionComponent. top 20px is 4124 // a valid start for <position>. 4125 cumulativeFlags = AmbiguousFillPosition; 4126 value2 = parseFillPositionComponent(valueList, cumulativeFlags, value2Flag, ResolveValuesAsKeyword); 4127 if (value2) 4128 valueList->next(); 4129 else { 4130 value1.clear(); 4131 return; 4132 } 4133 4134 RefPtr<CSSPrimitiveValue> parsedValue1 = static_cast<CSSPrimitiveValue*>(value1.get()); 4135 RefPtr<CSSPrimitiveValue> parsedValue2 = static_cast<CSSPrimitiveValue*>(value2.get()); 4136 4137 value1.clear(); 4138 value2.clear(); 4139 4140 // Per CSS3 syntax, <position> can't have 'center' as its second keyword as we have more arguments to follow. 4141 if (parsedValue2->getIdent() == CSSValueCenter) 4142 return; 4143 4144 if (numberOfValues == 3) 4145 parse3ValuesFillPosition(valueList, value1, value2, parsedValue1.release(), parsedValue2.release()); 4146 else 4147 parse4ValuesFillPosition(valueList, value1, value2, parsedValue1.release(), parsedValue2.release()); 4148} 4149 4150void CSSParser::parse2ValuesFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2) 4151{ 4152 CSSParserValue* value = valueList->current(); 4153 4154 // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length. 4155 unsigned cumulativeFlags = 0; 4156 FillPositionFlag value1Flag = InvalidFillPosition; 4157 FillPositionFlag value2Flag = InvalidFillPosition; 4158 value1 = parseFillPositionComponent(valueList, cumulativeFlags, value1Flag); 4159 if (!value1) 4160 return; 4161 4162 // It only takes one value for background-position to be correctly parsed if it was specified in a shorthand (since we 4163 // can assume that any other values belong to the rest of the shorthand). If we're not parsing a shorthand, though, the 4164 // value was explicitly specified for our property. 4165 value = valueList->next(); 4166 4167 // First check for the comma. If so, we are finished parsing this value or value pair. 4168 if (isComma(value)) 4169 value = 0; 4170 4171 if (value) { 4172 value2 = parseFillPositionComponent(valueList, cumulativeFlags, value2Flag); 4173 if (value2) 4174 valueList->next(); 4175 else { 4176 if (!inShorthand()) { 4177 value1.clear(); 4178 return; 4179 } 4180 } 4181 } 4182 4183 if (!value2) 4184 // Only one value was specified. If that value was not a keyword, then it sets the x position, and the y position 4185 // is simply 50%. This is our default. 4186 // For keywords, the keyword was either an x-keyword (left/right), a y-keyword (top/bottom), or an ambiguous keyword (center). 4187 // For left/right/center, the default of 50% in the y is still correct. 4188 value2 = cssValuePool().createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE); 4189 4190 if (value1Flag == YFillPosition || value2Flag == XFillPosition) 4191 value1.swap(value2); 4192} 4193 4194void CSSParser::parseFillRepeat(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2) 4195{ 4196 int id = m_valueList->current()->id; 4197 if (id == CSSValueRepeatX) { 4198 m_implicitShorthand = true; 4199 value1 = cssValuePool().createIdentifierValue(CSSValueRepeat); 4200 value2 = cssValuePool().createIdentifierValue(CSSValueNoRepeat); 4201 m_valueList->next(); 4202 return; 4203 } 4204 if (id == CSSValueRepeatY) { 4205 m_implicitShorthand = true; 4206 value1 = cssValuePool().createIdentifierValue(CSSValueNoRepeat); 4207 value2 = cssValuePool().createIdentifierValue(CSSValueRepeat); 4208 m_valueList->next(); 4209 return; 4210 } 4211 if (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace) 4212 value1 = cssValuePool().createIdentifierValue(id); 4213 else { 4214 value1 = 0; 4215 return; 4216 } 4217 4218 CSSParserValue* value = m_valueList->next(); 4219 4220 // Parse the second value if one is available 4221 if (value && !isComma(value)) { 4222 id = value->id; 4223 if (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace) { 4224 value2 = cssValuePool().createIdentifierValue(id); 4225 m_valueList->next(); 4226 return; 4227 } 4228 } 4229 4230 // If only one value was specified, value2 is the same as value1. 4231 m_implicitShorthand = true; 4232 value2 = cssValuePool().createIdentifierValue(static_cast<CSSPrimitiveValue*>(value1.get())->getIdent()); 4233} 4234 4235PassRefPtr<CSSValue> CSSParser::parseFillSize(CSSPropertyID propId, bool& allowComma) 4236{ 4237 allowComma = true; 4238 CSSParserValue* value = m_valueList->current(); 4239 4240 if (value->id == CSSValueContain || value->id == CSSValueCover) 4241 return cssValuePool().createIdentifierValue(value->id); 4242 4243 RefPtr<CSSPrimitiveValue> parsedValue1; 4244 4245 if (value->id == CSSValueAuto) 4246 parsedValue1 = cssValuePool().createIdentifierValue(CSSValueAuto); 4247 else { 4248 if (!validUnit(value, FLength | FPercent)) 4249 return 0; 4250 parsedValue1 = createPrimitiveNumericValue(value); 4251 } 4252 4253 RefPtr<CSSPrimitiveValue> parsedValue2; 4254 if ((value = m_valueList->next())) { 4255 if (value->unit == CSSParserValue::Operator && value->iValue == ',') 4256 allowComma = false; 4257 else if (value->id != CSSValueAuto) { 4258 if (!validUnit(value, FLength | FPercent)) { 4259 if (!inShorthand()) 4260 return 0; 4261 // We need to rewind the value list, so that when it is advanced we'll end up back at this value. 4262 m_valueList->previous(); 4263 } else 4264 parsedValue2 = createPrimitiveNumericValue(value); 4265 } 4266 } else if (!parsedValue2 && propId == CSSPropertyWebkitBackgroundSize) { 4267 // For backwards compatibility we set the second value to the first if it is omitted. 4268 // We only need to do this for -webkit-background-size. It should be safe to let masks match 4269 // the real property. 4270 parsedValue2 = parsedValue1; 4271 } 4272 4273 if (!parsedValue2) 4274 return parsedValue1; 4275 return createPrimitiveValuePair(parsedValue1.release(), parsedValue2.release()); 4276} 4277 4278bool CSSParser::parseFillProperty(CSSPropertyID propId, CSSPropertyID& propId1, CSSPropertyID& propId2, 4279 RefPtr<CSSValue>& retValue1, RefPtr<CSSValue>& retValue2) 4280{ 4281 RefPtr<CSSValueList> values; 4282 RefPtr<CSSValueList> values2; 4283 CSSParserValue* val; 4284 RefPtr<CSSValue> value; 4285 RefPtr<CSSValue> value2; 4286 4287 bool allowComma = false; 4288 4289 retValue1 = retValue2 = 0; 4290 propId1 = propId; 4291 propId2 = propId; 4292 if (propId == CSSPropertyBackgroundPosition) { 4293 propId1 = CSSPropertyBackgroundPositionX; 4294 propId2 = CSSPropertyBackgroundPositionY; 4295 } else if (propId == CSSPropertyWebkitMaskPosition) { 4296 propId1 = CSSPropertyWebkitMaskPositionX; 4297 propId2 = CSSPropertyWebkitMaskPositionY; 4298 } else if (propId == CSSPropertyBackgroundRepeat) { 4299 propId1 = CSSPropertyBackgroundRepeatX; 4300 propId2 = CSSPropertyBackgroundRepeatY; 4301 } else if (propId == CSSPropertyWebkitMaskRepeat) { 4302 propId1 = CSSPropertyWebkitMaskRepeatX; 4303 propId2 = CSSPropertyWebkitMaskRepeatY; 4304 } 4305 4306 while ((val = m_valueList->current())) { 4307 RefPtr<CSSValue> currValue; 4308 RefPtr<CSSValue> currValue2; 4309 4310 if (allowComma) { 4311 if (!isComma(val)) 4312 return false; 4313 m_valueList->next(); 4314 allowComma = false; 4315 } else { 4316 allowComma = true; 4317 switch (propId) { 4318 case CSSPropertyBackgroundColor: 4319 currValue = parseBackgroundColor(); 4320 if (currValue) 4321 m_valueList->next(); 4322 break; 4323 case CSSPropertyBackgroundAttachment: 4324 if (val->id == CSSValueScroll || val->id == CSSValueFixed || val->id == CSSValueLocal) { 4325 currValue = cssValuePool().createIdentifierValue(val->id); 4326 m_valueList->next(); 4327 } 4328 break; 4329 case CSSPropertyBackgroundImage: 4330 case CSSPropertyWebkitMaskImage: 4331 if (parseFillImage(m_valueList.get(), currValue)) 4332 m_valueList->next(); 4333 break; 4334 case CSSPropertyWebkitBackgroundClip: 4335 case CSSPropertyWebkitBackgroundOrigin: 4336 case CSSPropertyWebkitMaskClip: 4337 case CSSPropertyWebkitMaskOrigin: 4338 // The first three values here are deprecated and do not apply to the version of the property that has 4339 // the -webkit- prefix removed. 4340 if (val->id == CSSValueBorder || val->id == CSSValuePadding || val->id == CSSValueContent || 4341 val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox || 4342 ((propId == CSSPropertyWebkitBackgroundClip || propId == CSSPropertyWebkitMaskClip) && 4343 (val->id == CSSValueText || val->id == CSSValueWebkitText))) { 4344 currValue = cssValuePool().createIdentifierValue(val->id); 4345 m_valueList->next(); 4346 } 4347 break; 4348 case CSSPropertyBackgroundClip: 4349 if (parseBackgroundClip(val, currValue)) 4350 m_valueList->next(); 4351 break; 4352 case CSSPropertyBackgroundOrigin: 4353 if (val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox) { 4354 currValue = cssValuePool().createIdentifierValue(val->id); 4355 m_valueList->next(); 4356 } 4357 break; 4358 case CSSPropertyBackgroundPosition: 4359 case CSSPropertyWebkitMaskPosition: 4360 parseFillPosition(m_valueList.get(), currValue, currValue2); 4361 // parseFillPosition advances the m_valueList pointer. 4362 break; 4363 case CSSPropertyBackgroundPositionX: 4364 case CSSPropertyWebkitMaskPositionX: { 4365 currValue = parseFillPositionX(m_valueList.get()); 4366 if (currValue) 4367 m_valueList->next(); 4368 break; 4369 } 4370 case CSSPropertyBackgroundPositionY: 4371 case CSSPropertyWebkitMaskPositionY: { 4372 currValue = parseFillPositionY(m_valueList.get()); 4373 if (currValue) 4374 m_valueList->next(); 4375 break; 4376 } 4377 case CSSPropertyWebkitBackgroundComposite: 4378 case CSSPropertyWebkitMaskComposite: 4379 if (val->id >= CSSValueClear && val->id <= CSSValuePlusLighter) { 4380 currValue = cssValuePool().createIdentifierValue(val->id); 4381 m_valueList->next(); 4382 } 4383 break; 4384#if ENABLE(CSS_COMPOSITING) 4385 case CSSPropertyWebkitBackgroundBlendMode: 4386 if (cssCompositingEnabled() && (val->id == CSSValueNormal || val->id == CSSValueMultiply 4387 || val->id == CSSValueScreen || val->id == CSSValueOverlay || val->id == CSSValueDarken 4388 || val->id == CSSValueLighten || val->id == CSSValueColorDodge || val->id == CSSValueColorBurn 4389 || val->id == CSSValueHardLight || val->id == CSSValueSoftLight || val->id == CSSValueDifference 4390 || val->id == CSSValueExclusion || val->id == CSSValueHue || val->id == CSSValueSaturation 4391 || val->id == CSSValueColor || val->id == CSSValueLuminosity)) { 4392 currValue = cssValuePool().createIdentifierValue(val->id); 4393 m_valueList->next(); 4394 } 4395 break; 4396#endif 4397 case CSSPropertyBackgroundRepeat: 4398 case CSSPropertyWebkitMaskRepeat: 4399 parseFillRepeat(currValue, currValue2); 4400 // parseFillRepeat advances the m_valueList pointer 4401 break; 4402 case CSSPropertyBackgroundSize: 4403 case CSSPropertyWebkitBackgroundSize: 4404 case CSSPropertyWebkitMaskSize: { 4405 currValue = parseFillSize(propId, allowComma); 4406 if (currValue) 4407 m_valueList->next(); 4408 break; 4409 } 4410 default: 4411 break; 4412 } 4413 if (!currValue) 4414 return false; 4415 4416 if (value && !values) { 4417 values = CSSValueList::createCommaSeparated(); 4418 values->append(value.release()); 4419 } 4420 4421 if (value2 && !values2) { 4422 values2 = CSSValueList::createCommaSeparated(); 4423 values2->append(value2.release()); 4424 } 4425 4426 if (values) 4427 values->append(currValue.release()); 4428 else 4429 value = currValue.release(); 4430 if (currValue2) { 4431 if (values2) 4432 values2->append(currValue2.release()); 4433 else 4434 value2 = currValue2.release(); 4435 } 4436 } 4437 4438 // When parsing any fill shorthand property, we let it handle building up the lists for all 4439 // properties. 4440 if (inShorthand()) 4441 break; 4442 } 4443 4444 if (values && values->length()) { 4445 retValue1 = values.release(); 4446 if (values2 && values2->length()) 4447 retValue2 = values2.release(); 4448 return true; 4449 } 4450 if (value) { 4451 retValue1 = value.release(); 4452 retValue2 = value2.release(); 4453 return true; 4454 } 4455 return false; 4456} 4457 4458PassRefPtr<CSSValue> CSSParser::parseAnimationDelay() 4459{ 4460 CSSParserValue* value = m_valueList->current(); 4461 if (validUnit(value, FTime)) 4462 return createPrimitiveNumericValue(value); 4463 return 0; 4464} 4465 4466PassRefPtr<CSSValue> CSSParser::parseAnimationDirection() 4467{ 4468 CSSParserValue* value = m_valueList->current(); 4469 if (value->id == CSSValueNormal || value->id == CSSValueAlternate || value->id == CSSValueReverse || value->id == CSSValueAlternateReverse) 4470 return cssValuePool().createIdentifierValue(value->id); 4471 return 0; 4472} 4473 4474PassRefPtr<CSSValue> CSSParser::parseAnimationDuration() 4475{ 4476 CSSParserValue* value = m_valueList->current(); 4477 if (validUnit(value, FTime | FNonNeg)) 4478 return createPrimitiveNumericValue(value); 4479 return 0; 4480} 4481 4482PassRefPtr<CSSValue> CSSParser::parseAnimationFillMode() 4483{ 4484 CSSParserValue* value = m_valueList->current(); 4485 if (value->id == CSSValueNone || value->id == CSSValueForwards || value->id == CSSValueBackwards || value->id == CSSValueBoth) 4486 return cssValuePool().createIdentifierValue(value->id); 4487 return 0; 4488} 4489 4490PassRefPtr<CSSValue> CSSParser::parseAnimationIterationCount() 4491{ 4492 CSSParserValue* value = m_valueList->current(); 4493 if (value->id == CSSValueInfinite) 4494 return cssValuePool().createIdentifierValue(value->id); 4495 if (validUnit(value, FNumber | FNonNeg)) 4496 return createPrimitiveNumericValue(value); 4497 return 0; 4498} 4499 4500PassRefPtr<CSSValue> CSSParser::parseAnimationName() 4501{ 4502 CSSParserValue* value = m_valueList->current(); 4503 if (value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT) { 4504 if (value->id == CSSValueNone || (value->unit == CSSPrimitiveValue::CSS_STRING && equalIgnoringCase(value, "none"))) { 4505 return cssValuePool().createIdentifierValue(CSSValueNone); 4506 } else { 4507 return createPrimitiveStringValue(value); 4508 } 4509 } 4510 return 0; 4511} 4512 4513PassRefPtr<CSSValue> CSSParser::parseAnimationPlayState() 4514{ 4515 CSSParserValue* value = m_valueList->current(); 4516 if (value->id == CSSValueRunning || value->id == CSSValuePaused) 4517 return cssValuePool().createIdentifierValue(value->id); 4518 return 0; 4519} 4520 4521PassRefPtr<CSSValue> CSSParser::parseAnimationProperty(AnimationParseContext& context) 4522{ 4523 CSSParserValue* value = m_valueList->current(); 4524 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 4525 return 0; 4526 int result = cssPropertyID(value->string); 4527 if (result) 4528 return cssValuePool().createIdentifierValue(result); 4529 if (equalIgnoringCase(value, "all")) { 4530 context.sawAnimationPropertyKeyword(); 4531 return cssValuePool().createIdentifierValue(CSSValueAll); 4532 } 4533 if (equalIgnoringCase(value, "none")) { 4534 context.commitAnimationPropertyKeyword(); 4535 context.sawAnimationPropertyKeyword(); 4536 return cssValuePool().createIdentifierValue(CSSValueNone); 4537 } 4538 return 0; 4539} 4540 4541bool CSSParser::parseTransformOriginShorthand(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3) 4542{ 4543 parse2ValuesFillPosition(m_valueList.get(), value1, value2); 4544 4545 // now get z 4546 if (m_valueList->current()) { 4547 if (validUnit(m_valueList->current(), FLength)) { 4548 value3 = createPrimitiveNumericValue(m_valueList->current()); 4549 m_valueList->next(); 4550 return true; 4551 } 4552 return false; 4553 } 4554 value3 = cssValuePool().createImplicitInitialValue(); 4555 return true; 4556} 4557 4558bool CSSParser::parseCubicBezierTimingFunctionValue(CSSParserValueList*& args, double& result) 4559{ 4560 CSSParserValue* v = args->current(); 4561 if (!validUnit(v, FNumber)) 4562 return false; 4563 result = v->fValue; 4564 v = args->next(); 4565 if (!v) 4566 // The last number in the function has no comma after it, so we're done. 4567 return true; 4568 if (!isComma(v)) 4569 return false; 4570 args->next(); 4571 return true; 4572} 4573 4574PassRefPtr<CSSValue> CSSParser::parseAnimationTimingFunction() 4575{ 4576 CSSParserValue* value = m_valueList->current(); 4577 if (value->id == CSSValueEase || value->id == CSSValueLinear || value->id == CSSValueEaseIn || value->id == CSSValueEaseOut 4578 || value->id == CSSValueEaseInOut || value->id == CSSValueStepStart || value->id == CSSValueStepEnd) 4579 return cssValuePool().createIdentifierValue(value->id); 4580 4581 // We must be a function. 4582 if (value->unit != CSSParserValue::Function) 4583 return 0; 4584 4585 CSSParserValueList* args = value->function->args.get(); 4586 4587 if (equalIgnoringCase(value->function->name, "steps(")) { 4588 // For steps, 1 or 2 params must be specified (comma-separated) 4589 if (!args || (args->size() != 1 && args->size() != 3)) 4590 return 0; 4591 4592 // There are two values. 4593 int numSteps; 4594 bool stepAtStart = false; 4595 4596 CSSParserValue* v = args->current(); 4597 if (!validUnit(v, FInteger)) 4598 return 0; 4599 numSteps = clampToInteger(v->fValue); 4600 if (numSteps < 1) 4601 return 0; 4602 v = args->next(); 4603 4604 if (v) { 4605 // There is a comma so we need to parse the second value 4606 if (!isComma(v)) 4607 return 0; 4608 v = args->next(); 4609 if (v->id != CSSValueStart && v->id != CSSValueEnd) 4610 return 0; 4611 stepAtStart = v->id == CSSValueStart; 4612 } 4613 4614 return CSSStepsTimingFunctionValue::create(numSteps, stepAtStart); 4615 } 4616 4617 if (equalIgnoringCase(value->function->name, "cubic-bezier(")) { 4618 // For cubic bezier, 4 values must be specified. 4619 if (!args || args->size() != 7) 4620 return 0; 4621 4622 // There are two points specified. The x values must be between 0 and 1 but the y values can exceed this range. 4623 double x1, y1, x2, y2; 4624 4625 if (!parseCubicBezierTimingFunctionValue(args, x1)) 4626 return 0; 4627 if (x1 < 0 || x1 > 1) 4628 return 0; 4629 if (!parseCubicBezierTimingFunctionValue(args, y1)) 4630 return 0; 4631 if (!parseCubicBezierTimingFunctionValue(args, x2)) 4632 return 0; 4633 if (x2 < 0 || x2 > 1) 4634 return 0; 4635 if (!parseCubicBezierTimingFunctionValue(args, y2)) 4636 return 0; 4637 4638 return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2); 4639 } 4640 4641 return 0; 4642} 4643 4644bool CSSParser::parseAnimationProperty(CSSPropertyID propId, RefPtr<CSSValue>& result, AnimationParseContext& context) 4645{ 4646 RefPtr<CSSValueList> values; 4647 CSSParserValue* val; 4648 RefPtr<CSSValue> value; 4649 bool allowComma = false; 4650 4651 result = 0; 4652 4653 while ((val = m_valueList->current())) { 4654 RefPtr<CSSValue> currValue; 4655 if (allowComma) { 4656 if (!isComma(val)) 4657 return false; 4658 m_valueList->next(); 4659 allowComma = false; 4660 } 4661 else { 4662 switch (propId) { 4663 case CSSPropertyWebkitAnimationDelay: 4664 case CSSPropertyTransitionDelay: 4665 case CSSPropertyWebkitTransitionDelay: 4666 currValue = parseAnimationDelay(); 4667 if (currValue) 4668 m_valueList->next(); 4669 break; 4670 case CSSPropertyWebkitAnimationDirection: 4671 currValue = parseAnimationDirection(); 4672 if (currValue) 4673 m_valueList->next(); 4674 break; 4675 case CSSPropertyWebkitAnimationDuration: 4676 case CSSPropertyTransitionDuration: 4677 case CSSPropertyWebkitTransitionDuration: 4678 currValue = parseAnimationDuration(); 4679 if (currValue) 4680 m_valueList->next(); 4681 break; 4682 case CSSPropertyWebkitAnimationFillMode: 4683 currValue = parseAnimationFillMode(); 4684 if (currValue) 4685 m_valueList->next(); 4686 break; 4687 case CSSPropertyWebkitAnimationIterationCount: 4688 currValue = parseAnimationIterationCount(); 4689 if (currValue) 4690 m_valueList->next(); 4691 break; 4692 case CSSPropertyWebkitAnimationName: 4693 currValue = parseAnimationName(); 4694 if (currValue) 4695 m_valueList->next(); 4696 break; 4697 case CSSPropertyWebkitAnimationPlayState: 4698 currValue = parseAnimationPlayState(); 4699 if (currValue) 4700 m_valueList->next(); 4701 break; 4702 case CSSPropertyTransitionProperty: 4703 case CSSPropertyWebkitTransitionProperty: 4704 currValue = parseAnimationProperty(context); 4705 if (value && !context.animationPropertyKeywordAllowed()) 4706 return false; 4707 if (currValue) 4708 m_valueList->next(); 4709 break; 4710 case CSSPropertyWebkitAnimationTimingFunction: 4711 case CSSPropertyTransitionTimingFunction: 4712 case CSSPropertyWebkitTransitionTimingFunction: 4713 currValue = parseAnimationTimingFunction(); 4714 if (currValue) 4715 m_valueList->next(); 4716 break; 4717 default: 4718 ASSERT_NOT_REACHED(); 4719 return false; 4720 } 4721 4722 if (!currValue) 4723 return false; 4724 4725 if (value && !values) { 4726 values = CSSValueList::createCommaSeparated(); 4727 values->append(value.release()); 4728 } 4729 4730 if (values) 4731 values->append(currValue.release()); 4732 else 4733 value = currValue.release(); 4734 4735 allowComma = true; 4736 } 4737 4738 // When parsing the 'transition' shorthand property, we let it handle building up the lists for all 4739 // properties. 4740 if (inShorthand()) 4741 break; 4742 } 4743 4744 if (values && values->length()) { 4745 result = values.release(); 4746 return true; 4747 } 4748 if (value) { 4749 result = value.release(); 4750 return true; 4751 } 4752 return false; 4753} 4754 4755bool CSSParser::parseGridItemPositionShorthand(CSSPropertyID shorthandId, bool important) 4756{ 4757 ShorthandScope scope(this, shorthandId); 4758 const StylePropertyShorthand& shorthand = shorthandForProperty(shorthandId); 4759 ASSERT(shorthand.length() == 2); 4760 if (!parseValue(shorthand.properties()[0], important)) 4761 return false; 4762 4763 if (!m_valueList->current()) { 4764 // Only one value was specified, the opposite value should be set to 'auto'. 4765 // FIXME: If the first property was <ident>, the opposite value should be the same <ident>. 4766 addProperty(shorthand.properties()[1], cssValuePool().createIdentifierValue(CSSValueAuto), important); 4767 return true; 4768 } 4769 4770 if (!isForwardSlashOperator(m_valueList->current())) 4771 return false; 4772 4773 if (!m_valueList->next()) 4774 return false; 4775 4776 return parseValue(shorthand.properties()[1], important); 4777} 4778 4779bool CSSParser::parseGridTrackList(CSSPropertyID propId, bool important) 4780{ 4781 CSSParserValue* value = m_valueList->current(); 4782 if (value->id == CSSValueNone) { 4783 if (m_valueList->next()) 4784 return false; 4785 4786 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 4787 return true; 4788 } 4789 4790 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated(); 4791 while (m_valueList->current()) { 4792 RefPtr<CSSPrimitiveValue> primitiveValue = parseGridTrackSize(); 4793 if (!primitiveValue) 4794 return false; 4795 4796 values->append(primitiveValue.release()); 4797 } 4798 addProperty(propId, values.release(), important); 4799 return true; 4800} 4801 4802PassRefPtr<CSSPrimitiveValue> CSSParser::parseGridTrackSize() 4803{ 4804 CSSParserValue* currentValue = m_valueList->current(); 4805 m_valueList->next(); 4806 4807 if (currentValue->id == CSSValueAuto) 4808 return cssValuePool().createIdentifierValue(CSSValueAuto); 4809 4810 if (currentValue->unit == CSSParserValue::Function && equalIgnoringCase(currentValue->function->name, "minmax(")) { 4811 // The spec defines the following grammar: minmax( <track-breadth> , <track-breadth> ) 4812 CSSParserValueList* arguments = currentValue->function->args.get(); 4813 if (!arguments || arguments->size() != 3 || !isComma(arguments->valueAt(1))) 4814 return 0; 4815 4816 RefPtr<CSSPrimitiveValue> minTrackBreadth = parseGridBreadth(arguments->valueAt(0)); 4817 if (!minTrackBreadth) 4818 return 0; 4819 4820 RefPtr<CSSPrimitiveValue> maxTrackBreadth = parseGridBreadth(arguments->valueAt(2)); 4821 if (!maxTrackBreadth) 4822 return 0; 4823 4824 return createPrimitiveValuePair(minTrackBreadth, maxTrackBreadth); 4825 } 4826 4827 if (PassRefPtr<CSSPrimitiveValue> trackBreadth = parseGridBreadth(currentValue)) 4828 return trackBreadth; 4829 4830 return 0; 4831} 4832 4833PassRefPtr<CSSPrimitiveValue> CSSParser::parseGridBreadth(CSSParserValue* currentValue) 4834{ 4835 if (currentValue->id == CSSValueWebkitMinContent || currentValue->id == CSSValueWebkitMaxContent) 4836 return cssValuePool().createIdentifierValue(currentValue->id); 4837 4838 if (!validUnit(currentValue, FLength | FPercent)) 4839 return 0; 4840 4841 return createPrimitiveNumericValue(currentValue); 4842} 4843 4844#if ENABLE(DASHBOARD_SUPPORT) 4845 4846#define DASHBOARD_REGION_NUM_PARAMETERS 6 4847#define DASHBOARD_REGION_SHORT_NUM_PARAMETERS 2 4848 4849static CSSParserValue* skipCommaInDashboardRegion(CSSParserValueList *args) 4850{ 4851 if (args->size() == (DASHBOARD_REGION_NUM_PARAMETERS*2-1) || 4852 args->size() == (DASHBOARD_REGION_SHORT_NUM_PARAMETERS*2-1)) { 4853 CSSParserValue* current = args->current(); 4854 if (current->unit == CSSParserValue::Operator && current->iValue == ',') 4855 return args->next(); 4856 } 4857 return args->current(); 4858} 4859 4860bool CSSParser::parseDashboardRegions(CSSPropertyID propId, bool important) 4861{ 4862 bool valid = true; 4863 4864 CSSParserValue* value = m_valueList->current(); 4865 4866 if (value->id == CSSValueNone) { 4867 if (m_valueList->next()) 4868 return false; 4869 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 4870 return valid; 4871 } 4872 4873 RefPtr<DashboardRegion> firstRegion = DashboardRegion::create(); 4874 DashboardRegion* region = 0; 4875 4876 while (value) { 4877 if (region == 0) { 4878 region = firstRegion.get(); 4879 } else { 4880 RefPtr<DashboardRegion> nextRegion = DashboardRegion::create(); 4881 region->m_next = nextRegion; 4882 region = nextRegion.get(); 4883 } 4884 4885 if (value->unit != CSSParserValue::Function) { 4886 valid = false; 4887 break; 4888 } 4889 4890 // Commas count as values, so allow (function name is dashboard-region for DASHBOARD_SUPPORT feature): 4891 // dashboard-region(label, type, t, r, b, l) or dashboard-region(label type t r b l) 4892 // dashboard-region(label, type, t, r, b, l) or dashboard-region(label type t r b l) 4893 // also allow 4894 // dashboard-region(label, type) or dashboard-region(label type) 4895 // dashboard-region(label, type) or dashboard-region(label type) 4896 CSSParserValueList* args = value->function->args.get(); 4897 if (!equalIgnoringCase(value->function->name, "dashboard-region(") || !args) { 4898 valid = false; 4899 break; 4900 } 4901 4902 int numArgs = args->size(); 4903 if ((numArgs != DASHBOARD_REGION_NUM_PARAMETERS && numArgs != (DASHBOARD_REGION_NUM_PARAMETERS*2-1)) && 4904 (numArgs != DASHBOARD_REGION_SHORT_NUM_PARAMETERS && numArgs != (DASHBOARD_REGION_SHORT_NUM_PARAMETERS*2-1))) { 4905 valid = false; 4906 break; 4907 } 4908 4909 // First arg is a label. 4910 CSSParserValue* arg = args->current(); 4911 if (arg->unit != CSSPrimitiveValue::CSS_IDENT) { 4912 valid = false; 4913 break; 4914 } 4915 4916 region->m_label = arg->string; 4917 4918 // Second arg is a type. 4919 arg = args->next(); 4920 arg = skipCommaInDashboardRegion(args); 4921 if (arg->unit != CSSPrimitiveValue::CSS_IDENT) { 4922 valid = false; 4923 break; 4924 } 4925 4926 if (equalIgnoringCase(arg, "circle")) 4927 region->m_isCircle = true; 4928 else if (equalIgnoringCase(arg, "rectangle")) 4929 region->m_isRectangle = true; 4930 else { 4931 valid = false; 4932 break; 4933 } 4934 4935 region->m_geometryType = arg->string; 4936 4937 if (numArgs == DASHBOARD_REGION_SHORT_NUM_PARAMETERS || numArgs == (DASHBOARD_REGION_SHORT_NUM_PARAMETERS*2-1)) { 4938 // This originally used CSSValueInvalid by accident. It might be more logical to use something else. 4939 RefPtr<CSSPrimitiveValue> amount = cssValuePool().createIdentifierValue(CSSValueInvalid); 4940 4941 region->setTop(amount); 4942 region->setRight(amount); 4943 region->setBottom(amount); 4944 region->setLeft(amount); 4945 } else { 4946 // Next four arguments must be offset numbers 4947 int i; 4948 for (i = 0; i < 4; i++) { 4949 arg = args->next(); 4950 arg = skipCommaInDashboardRegion(args); 4951 4952 valid = arg->id == CSSValueAuto || validUnit(arg, FLength); 4953 if (!valid) 4954 break; 4955 4956 RefPtr<CSSPrimitiveValue> amount = arg->id == CSSValueAuto ? 4957 cssValuePool().createIdentifierValue(CSSValueAuto) : 4958 createPrimitiveNumericValue(arg); 4959 4960 if (i == 0) 4961 region->setTop(amount); 4962 else if (i == 1) 4963 region->setRight(amount); 4964 else if (i == 2) 4965 region->setBottom(amount); 4966 else 4967 region->setLeft(amount); 4968 } 4969 } 4970 4971 if (args->next()) 4972 return false; 4973 4974 value = m_valueList->next(); 4975 } 4976 4977 if (valid) 4978 addProperty(propId, cssValuePool().createValue(firstRegion.release()), important); 4979 4980 return valid; 4981} 4982 4983#endif /* ENABLE(DASHBOARD_SUPPORT) */ 4984 4985PassRefPtr<CSSValue> CSSParser::parseCounterContent(CSSParserValueList* args, bool counters) 4986{ 4987 unsigned numArgs = args->size(); 4988 if (counters && numArgs != 3 && numArgs != 5) 4989 return 0; 4990 if (!counters && numArgs != 1 && numArgs != 3) 4991 return 0; 4992 4993 CSSParserValue* i = args->current(); 4994 if (i->unit != CSSPrimitiveValue::CSS_IDENT) 4995 return 0; 4996 RefPtr<CSSPrimitiveValue> identifier = createPrimitiveStringValue(i); 4997 4998 RefPtr<CSSPrimitiveValue> separator; 4999 if (!counters) 5000 separator = cssValuePool().createValue(String(), CSSPrimitiveValue::CSS_STRING); 5001 else { 5002 i = args->next(); 5003 if (i->unit != CSSParserValue::Operator || i->iValue != ',') 5004 return 0; 5005 5006 i = args->next(); 5007 if (i->unit != CSSPrimitiveValue::CSS_STRING) 5008 return 0; 5009 5010 separator = createPrimitiveStringValue(i); 5011 } 5012 5013 RefPtr<CSSPrimitiveValue> listStyle; 5014 i = args->next(); 5015 if (!i) // Make the list style default decimal 5016 listStyle = cssValuePool().createIdentifierValue(CSSValueDecimal); 5017 else { 5018 if (i->unit != CSSParserValue::Operator || i->iValue != ',') 5019 return 0; 5020 5021 i = args->next(); 5022 if (i->unit != CSSPrimitiveValue::CSS_IDENT) 5023 return 0; 5024 5025 int listStyleID = 0; 5026 if (i->id == CSSValueNone || (i->id >= CSSValueDisc && i->id <= CSSValueKatakanaIroha)) 5027 listStyleID = i->id; 5028 else 5029 return 0; 5030 5031 listStyle = cssValuePool().createIdentifierValue(listStyleID); 5032 } 5033 5034 return cssValuePool().createValue(Counter::create(identifier.release(), listStyle.release(), separator.release())); 5035} 5036 5037bool CSSParser::parseClipShape(CSSPropertyID propId, bool important) 5038{ 5039 CSSParserValue* value = m_valueList->current(); 5040 CSSParserValueList* args = value->function->args.get(); 5041 5042 if (!equalIgnoringCase(value->function->name, "rect(") || !args) 5043 return false; 5044 5045 // rect(t, r, b, l) || rect(t r b l) 5046 if (args->size() != 4 && args->size() != 7) 5047 return false; 5048 RefPtr<Rect> rect = Rect::create(); 5049 bool valid = true; 5050 int i = 0; 5051 CSSParserValue* a = args->current(); 5052 while (a) { 5053 valid = a->id == CSSValueAuto || validUnit(a, FLength); 5054 if (!valid) 5055 break; 5056 RefPtr<CSSPrimitiveValue> length = a->id == CSSValueAuto ? 5057 cssValuePool().createIdentifierValue(CSSValueAuto) : 5058 createPrimitiveNumericValue(a); 5059 if (i == 0) 5060 rect->setTop(length); 5061 else if (i == 1) 5062 rect->setRight(length); 5063 else if (i == 2) 5064 rect->setBottom(length); 5065 else 5066 rect->setLeft(length); 5067 a = args->next(); 5068 if (a && args->size() == 7) { 5069 if (a->unit == CSSParserValue::Operator && a->iValue == ',') { 5070 a = args->next(); 5071 } else { 5072 valid = false; 5073 break; 5074 } 5075 } 5076 i++; 5077 } 5078 if (valid) { 5079 addProperty(propId, cssValuePool().createValue(rect.release()), important); 5080 m_valueList->next(); 5081 return true; 5082 } 5083 return false; 5084} 5085 5086PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeRectangle(CSSParserValueList* args) 5087{ 5088 ASSERT(args); 5089 5090 // rect(x, y, width, height, [[rx], ry]) 5091 if (args->size() != 7 && args->size() != 9 && args->size() != 11) 5092 return 0; 5093 5094 RefPtr<CSSBasicShapeRectangle> shape = CSSBasicShapeRectangle::create(); 5095 5096 unsigned argumentNumber = 0; 5097 CSSParserValue* argument = args->current(); 5098 while (argument) { 5099 Units unitFlags = FLength | FPercent; 5100 if (argumentNumber > 1) { 5101 // Arguments width, height, rx, and ry cannot be negative. 5102 unitFlags = unitFlags | FNonNeg; 5103 } 5104 if (!validUnit(argument, unitFlags)) 5105 return 0; 5106 5107 RefPtr<CSSPrimitiveValue> length = createPrimitiveNumericValue(argument); 5108 ASSERT(argumentNumber < 6); 5109 switch (argumentNumber) { 5110 case 0: 5111 shape->setX(length); 5112 break; 5113 case 1: 5114 shape->setY(length); 5115 break; 5116 case 2: 5117 shape->setWidth(length); 5118 break; 5119 case 3: 5120 shape->setHeight(length); 5121 break; 5122 case 4: 5123 shape->setRadiusX(length); 5124 break; 5125 case 5: 5126 shape->setRadiusY(length); 5127 break; 5128 } 5129 argument = args->next(); 5130 if (argument) { 5131 if (!isComma(argument)) 5132 return 0; 5133 5134 argument = args->next(); 5135 } 5136 argumentNumber++; 5137 } 5138 5139 if (argumentNumber < 4) 5140 return 0; 5141 return shape; 5142} 5143 5144PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeInsetRectangle(CSSParserValueList* args) 5145{ 5146 ASSERT(args); 5147 5148 // inset-rectangle(top, right, bottom, left, [[rx], ry]) 5149 if (args->size() != 7 && args->size() != 9 && args->size() != 11) 5150 return 0; 5151 5152 RefPtr<CSSBasicShapeInsetRectangle> shape = CSSBasicShapeInsetRectangle::create(); 5153 5154 unsigned argumentNumber = 0; 5155 CSSParserValue* argument = args->current(); 5156 while (argument) { 5157 Units unitFlags = FLength | FPercent | FNonNeg; 5158 if (!validUnit(argument, unitFlags)) 5159 return 0; 5160 5161 RefPtr<CSSPrimitiveValue> length = createPrimitiveNumericValue(argument); 5162 ASSERT(argumentNumber < 6); 5163 switch (argumentNumber) { 5164 case 0: 5165 shape->setTop(length); 5166 break; 5167 case 1: 5168 shape->setRight(length); 5169 break; 5170 case 2: 5171 shape->setBottom(length); 5172 break; 5173 case 3: 5174 shape->setLeft(length); 5175 break; 5176 case 4: 5177 shape->setRadiusX(length); 5178 break; 5179 case 5: 5180 shape->setRadiusY(length); 5181 break; 5182 } 5183 argument = args->next(); 5184 if (argument) { 5185 if (!isComma(argument)) 5186 return 0; 5187 5188 argument = args->next(); 5189 } 5190 argumentNumber++; 5191 } 5192 5193 if (argumentNumber < 4) 5194 return 0; 5195 return shape; 5196} 5197 5198PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeCircle(CSSParserValueList* args) 5199{ 5200 ASSERT(args); 5201 5202 // circle(centerX, centerY, radius) 5203 if (args->size() != 5) 5204 return 0; 5205 5206 RefPtr<CSSBasicShapeCircle> shape = CSSBasicShapeCircle::create(); 5207 5208 unsigned argumentNumber = 0; 5209 CSSParserValue* argument = args->current(); 5210 while (argument) { 5211 Units unitFlags = FLength | FPercent; 5212 if (argumentNumber == 2) { 5213 // Argument radius cannot be negative. 5214 unitFlags = unitFlags | FNonNeg; 5215 } 5216 5217 if (!validUnit(argument, unitFlags)) 5218 return 0; 5219 5220 RefPtr<CSSPrimitiveValue> length = createPrimitiveNumericValue(argument); 5221 ASSERT(argumentNumber < 3); 5222 switch (argumentNumber) { 5223 case 0: 5224 shape->setCenterX(length); 5225 break; 5226 case 1: 5227 shape->setCenterY(length); 5228 break; 5229 case 2: 5230 shape->setRadius(length); 5231 break; 5232 } 5233 5234 argument = args->next(); 5235 if (argument) { 5236 if (!isComma(argument)) 5237 return 0; 5238 argument = args->next(); 5239 } 5240 argumentNumber++; 5241 } 5242 5243 if (argumentNumber < 3) 5244 return 0; 5245 return shape; 5246} 5247 5248PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapeEllipse(CSSParserValueList* args) 5249{ 5250 ASSERT(args); 5251 5252 // ellipse(centerX, centerY, radiusX, radiusY) 5253 if (args->size() != 7) 5254 return 0; 5255 5256 RefPtr<CSSBasicShapeEllipse> shape = CSSBasicShapeEllipse::create(); 5257 unsigned argumentNumber = 0; 5258 CSSParserValue* argument = args->current(); 5259 while (argument) { 5260 Units unitFlags = FLength | FPercent; 5261 if (argumentNumber > 1) { 5262 // Arguments radiusX and radiusY cannot be negative. 5263 unitFlags = unitFlags | FNonNeg; 5264 } 5265 if (!validUnit(argument, unitFlags)) 5266 return 0; 5267 5268 RefPtr<CSSPrimitiveValue> length = createPrimitiveNumericValue(argument); 5269 ASSERT(argumentNumber < 4); 5270 switch (argumentNumber) { 5271 case 0: 5272 shape->setCenterX(length); 5273 break; 5274 case 1: 5275 shape->setCenterY(length); 5276 break; 5277 case 2: 5278 shape->setRadiusX(length); 5279 break; 5280 case 3: 5281 shape->setRadiusY(length); 5282 break; 5283 } 5284 5285 argument = args->next(); 5286 if (argument) { 5287 if (!isComma(argument)) 5288 return 0; 5289 argument = args->next(); 5290 } 5291 argumentNumber++; 5292 } 5293 5294 if (argumentNumber < 4) 5295 return 0; 5296 return shape; 5297} 5298 5299PassRefPtr<CSSBasicShape> CSSParser::parseBasicShapePolygon(CSSParserValueList* args) 5300{ 5301 ASSERT(args); 5302 5303 unsigned size = args->size(); 5304 if (!size) 5305 return 0; 5306 5307 RefPtr<CSSBasicShapePolygon> shape = CSSBasicShapePolygon::create(); 5308 5309 CSSParserValue* argument = args->current(); 5310 if (argument->id == CSSValueEvenodd || argument->id == CSSValueNonzero) { 5311 shape->setWindRule(argument->id == CSSValueEvenodd ? RULE_EVENODD : RULE_NONZERO); 5312 5313 if (!isComma(args->next())) 5314 return 0; 5315 5316 argument = args->next(); 5317 size -= 2; 5318 } 5319 5320 // <length> <length>, ... <length> <length> -> each pair has 3 elements except the last one 5321 if (!size || (size % 3) - 2) 5322 return 0; 5323 5324 CSSParserValue* argumentX = argument; 5325 while (argumentX) { 5326 if (!validUnit(argumentX, FLength | FPercent)) 5327 return 0; 5328 5329 CSSParserValue* argumentY = args->next(); 5330 if (!argumentY || !validUnit(argumentY, FLength | FPercent)) 5331 return 0; 5332 5333 RefPtr<CSSPrimitiveValue> xLength = createPrimitiveNumericValue(argumentX); 5334 RefPtr<CSSPrimitiveValue> yLength = createPrimitiveNumericValue(argumentY); 5335 5336 shape->appendPoint(xLength.release(), yLength.release()); 5337 5338 CSSParserValue* commaOrNull = args->next(); 5339 if (!commaOrNull) 5340 argumentX = 0; 5341 else if (!isComma(commaOrNull)) 5342 return 0; 5343 else 5344 argumentX = args->next(); 5345 } 5346 5347 return shape; 5348} 5349 5350bool CSSParser::parseBasicShape(CSSPropertyID propId, bool important) 5351{ 5352 CSSParserValue* value = m_valueList->current(); 5353 ASSERT(value->unit == CSSParserValue::Function); 5354 CSSParserValueList* args = value->function->args.get(); 5355 5356 if (!args) 5357 return false; 5358 5359 RefPtr<CSSBasicShape> shape; 5360 if (equalIgnoringCase(value->function->name, "rectangle(")) 5361 shape = parseBasicShapeRectangle(args); 5362 else if (equalIgnoringCase(value->function->name, "circle(")) 5363 shape = parseBasicShapeCircle(args); 5364 else if (equalIgnoringCase(value->function->name, "ellipse(")) 5365 shape = parseBasicShapeEllipse(args); 5366 else if (equalIgnoringCase(value->function->name, "polygon(")) 5367 shape = parseBasicShapePolygon(args); 5368 else if (equalIgnoringCase(value->function->name, "inset-rectangle(")) 5369 shape = parseBasicShapeInsetRectangle(args); 5370 5371 if (!shape) 5372 return false; 5373 5374 addProperty(propId, cssValuePool().createValue(shape.release()), important); 5375 m_valueList->next(); 5376 return true; 5377} 5378 5379// [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 'font-family' 5380bool CSSParser::parseFont(bool important) 5381{ 5382 // Let's check if there is an inherit or initial somewhere in the shorthand. 5383 for (unsigned i = 0; i < m_valueList->size(); ++i) { 5384 if (m_valueList->valueAt(i)->id == CSSValueInherit || m_valueList->valueAt(i)->id == CSSValueInitial) 5385 return false; 5386 } 5387 5388 ShorthandScope scope(this, CSSPropertyFont); 5389 // Optional font-style, font-variant and font-weight. 5390 bool fontStyleParsed = false; 5391 bool fontVariantParsed = false; 5392 bool fontWeightParsed = false; 5393 CSSParserValue* value; 5394 while ((value = m_valueList->current())) { 5395 if (!fontStyleParsed && isValidKeywordPropertyAndValue(CSSPropertyFontStyle, value->id, m_context)) { 5396 addProperty(CSSPropertyFontStyle, cssValuePool().createIdentifierValue(value->id), important); 5397 fontStyleParsed = true; 5398 } else if (!fontVariantParsed && (value->id == CSSValueNormal || value->id == CSSValueSmallCaps)) { 5399 // Font variant in the shorthand is particular, it only accepts normal or small-caps. 5400 addProperty(CSSPropertyFontVariant, cssValuePool().createIdentifierValue(value->id), important); 5401 fontVariantParsed = true; 5402 } else if (!fontWeightParsed && parseFontWeight(important)) 5403 fontWeightParsed = true; 5404 else 5405 break; 5406 m_valueList->next(); 5407 } 5408 5409 if (!value) 5410 return false; 5411 5412 if (!fontStyleParsed) 5413 addProperty(CSSPropertyFontStyle, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 5414 if (!fontVariantParsed) 5415 addProperty(CSSPropertyFontVariant, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 5416 if (!fontWeightParsed) 5417 addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 5418 5419 // Now a font size _must_ come. 5420 // <absolute-size> | <relative-size> | <length> | <percentage> | inherit 5421 if (!parseFontSize(important)) 5422 return false; 5423 5424 value = m_valueList->current(); 5425 if (!value) 5426 return false; 5427 5428 if (isForwardSlashOperator(value)) { 5429 // The line-height property. 5430 value = m_valueList->next(); 5431 if (!value) 5432 return false; 5433 if (!parseLineHeight(important)) 5434 return false; 5435 } else 5436 addProperty(CSSPropertyLineHeight, cssValuePool().createIdentifierValue(CSSValueNormal), important, true); 5437 5438 // Font family must come now. 5439 RefPtr<CSSValue> parsedFamilyValue = parseFontFamily(); 5440 if (!parsedFamilyValue) 5441 return false; 5442 5443 addProperty(CSSPropertyFontFamily, parsedFamilyValue.release(), important); 5444 5445 // FIXME: http://www.w3.org/TR/2011/WD-css3-fonts-20110324/#font-prop requires that 5446 // "font-stretch", "font-size-adjust", and "font-kerning" be reset to their initial values 5447 // but we don't seem to support them at the moment. They should also be added here once implemented. 5448 if (m_valueList->current()) 5449 return false; 5450 5451 return true; 5452} 5453 5454class FontFamilyValueBuilder { 5455public: 5456 FontFamilyValueBuilder(CSSValueList* list) 5457 : m_list(list) 5458 { 5459 } 5460 5461 void add(const CSSParserString& string) 5462 { 5463 if (!m_builder.isEmpty()) 5464 m_builder.append(' '); 5465 5466 if (string.is8Bit()) { 5467 m_builder.append(string.characters8(), string.length()); 5468 return; 5469 } 5470 5471 m_builder.append(string.characters16(), string.length()); 5472 } 5473 5474 void commit() 5475 { 5476 if (m_builder.isEmpty()) 5477 return; 5478 m_list->append(cssValuePool().createFontFamilyValue(m_builder.toString())); 5479 m_builder.clear(); 5480 } 5481 5482private: 5483 StringBuilder m_builder; 5484 CSSValueList* m_list; 5485}; 5486 5487PassRefPtr<CSSValueList> CSSParser::parseFontFamily() 5488{ 5489 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 5490 CSSParserValue* value = m_valueList->current(); 5491 5492 FontFamilyValueBuilder familyBuilder(list.get()); 5493 bool inFamily = false; 5494 5495 while (value) { 5496 CSSParserValue* nextValue = m_valueList->next(); 5497 bool nextValBreaksFont = !nextValue || 5498 (nextValue->unit == CSSParserValue::Operator && nextValue->iValue == ','); 5499 bool nextValIsFontName = nextValue && 5500 ((nextValue->id >= CSSValueSerif && nextValue->id <= CSSValueWebkitBody) || 5501 (nextValue->unit == CSSPrimitiveValue::CSS_STRING || nextValue->unit == CSSPrimitiveValue::CSS_IDENT)); 5502 5503 bool valueIsKeyword = value->id == CSSValueInitial || value->id == CSSValueInherit || value->id == CSSValueDefault; 5504 if (valueIsKeyword && !inFamily) { 5505 if (nextValBreaksFont) 5506 value = m_valueList->next(); 5507 else if (nextValIsFontName) 5508 value = nextValue; 5509 continue; 5510 } 5511 5512 if (value->id >= CSSValueSerif && value->id <= CSSValueWebkitBody) { 5513 if (inFamily) 5514 familyBuilder.add(value->string); 5515 else if (nextValBreaksFont || !nextValIsFontName) 5516 list->append(cssValuePool().createIdentifierValue(value->id)); 5517 else { 5518 familyBuilder.commit(); 5519 familyBuilder.add(value->string); 5520 inFamily = true; 5521 } 5522 } else if (value->unit == CSSPrimitiveValue::CSS_STRING) { 5523 // Strings never share in a family name. 5524 inFamily = false; 5525 familyBuilder.commit(); 5526 list->append(cssValuePool().createFontFamilyValue(value->string)); 5527 } else if (value->unit == CSSPrimitiveValue::CSS_IDENT) { 5528 if (inFamily) 5529 familyBuilder.add(value->string); 5530 else if (nextValBreaksFont || !nextValIsFontName) 5531 list->append(cssValuePool().createFontFamilyValue(value->string)); 5532 else { 5533 familyBuilder.commit(); 5534 familyBuilder.add(value->string); 5535 inFamily = true; 5536 } 5537 } else { 5538 break; 5539 } 5540 5541 if (!nextValue) 5542 break; 5543 5544 if (nextValBreaksFont) { 5545 value = m_valueList->next(); 5546 familyBuilder.commit(); 5547 inFamily = false; 5548 } 5549 else if (nextValIsFontName) 5550 value = nextValue; 5551 else 5552 break; 5553 } 5554 familyBuilder.commit(); 5555 5556 if (!list->length()) 5557 list = 0; 5558 return list.release(); 5559} 5560 5561bool CSSParser::parseLineHeight(bool important) 5562{ 5563 CSSParserValue* value = m_valueList->current(); 5564 int id = value->id; 5565 bool validPrimitive = false; 5566 // normal | <number> | <length> | <percentage> | inherit 5567 if (id == CSSValueNormal) 5568 validPrimitive = true; 5569 else 5570 validPrimitive = (!id && validUnit(value, FNumber | FLength | FPercent | FNonNeg)); 5571 if (validPrimitive && (!m_valueList->next() || inShorthand())) 5572 addProperty(CSSPropertyLineHeight, parseValidPrimitive(id, value), important); 5573 return validPrimitive; 5574} 5575 5576bool CSSParser::parseFontSize(bool important) 5577{ 5578 CSSParserValue* value = m_valueList->current(); 5579 int id = value->id; 5580 bool validPrimitive = false; 5581 // <absolute-size> | <relative-size> | <length> | <percentage> | inherit 5582 if (id >= CSSValueXxSmall && id <= CSSValueLarger) 5583 validPrimitive = true; 5584 else 5585 validPrimitive = validUnit(value, FLength | FPercent | FNonNeg); 5586 if (validPrimitive && (!m_valueList->next() || inShorthand())) 5587 addProperty(CSSPropertyFontSize, parseValidPrimitive(id, value), important); 5588 return validPrimitive; 5589} 5590 5591bool CSSParser::parseFontVariant(bool important) 5592{ 5593 RefPtr<CSSValueList> values; 5594 if (m_valueList->size() > 1) 5595 values = CSSValueList::createCommaSeparated(); 5596 CSSParserValue* val; 5597 bool expectComma = false; 5598 while ((val = m_valueList->current())) { 5599 RefPtr<CSSPrimitiveValue> parsedValue; 5600 if (!expectComma) { 5601 expectComma = true; 5602 if (val->id == CSSValueNormal || val->id == CSSValueSmallCaps) 5603 parsedValue = cssValuePool().createIdentifierValue(val->id); 5604 else if (val->id == CSSValueAll && !values) { 5605 // 'all' is only allowed in @font-face and with no other values. Make a value list to 5606 // indicate that we are in the @font-face case. 5607 values = CSSValueList::createCommaSeparated(); 5608 parsedValue = cssValuePool().createIdentifierValue(val->id); 5609 } 5610 } else if (val->unit == CSSParserValue::Operator && val->iValue == ',') { 5611 expectComma = false; 5612 m_valueList->next(); 5613 continue; 5614 } 5615 5616 if (!parsedValue) 5617 return false; 5618 5619 m_valueList->next(); 5620 5621 if (values) 5622 values->append(parsedValue.release()); 5623 else { 5624 addProperty(CSSPropertyFontVariant, parsedValue.release(), important); 5625 return true; 5626 } 5627 } 5628 5629 if (values && values->length()) { 5630 m_hasFontFaceOnlyValues = true; 5631 addProperty(CSSPropertyFontVariant, values.release(), important); 5632 return true; 5633 } 5634 5635 return false; 5636} 5637 5638bool CSSParser::parseFontWeight(bool important) 5639{ 5640 CSSParserValue* value = m_valueList->current(); 5641 if ((value->id >= CSSValueNormal) && (value->id <= CSSValue900)) { 5642 addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(value->id), important); 5643 return true; 5644 } 5645 if (validUnit(value, FInteger | FNonNeg, CSSQuirksMode)) { 5646 int weight = static_cast<int>(value->fValue); 5647 if (!(weight % 100) && weight >= 100 && weight <= 900) { 5648 addProperty(CSSPropertyFontWeight, cssValuePool().createIdentifierValue(CSSValue100 + weight / 100 - 1), important); 5649 return true; 5650 } 5651 } 5652 return false; 5653} 5654 5655bool CSSParser::parseFontFaceSrcURI(CSSValueList* valueList) 5656{ 5657 RefPtr<CSSFontFaceSrcValue> uriValue(CSSFontFaceSrcValue::create(completeURL(m_valueList->current()->string))); 5658 5659 CSSParserValue* value = m_valueList->next(); 5660 if (!value) { 5661 valueList->append(uriValue.release()); 5662 return true; 5663 } 5664 if (value->unit == CSSParserValue::Operator && value->iValue == ',') { 5665 m_valueList->next(); 5666 valueList->append(uriValue.release()); 5667 return true; 5668 } 5669 5670 if (value->unit != CSSParserValue::Function || !equalIgnoringCase(value->function->name, "format(")) 5671 return false; 5672 5673 // FIXME: http://www.w3.org/TR/2011/WD-css3-fonts-20111004/ says that format() contains a comma-separated list of strings, 5674 // but CSSFontFaceSrcValue stores only one format. Allowing one format for now. 5675 CSSParserValueList* args = value->function->args.get(); 5676 if (!args || args->size() != 1 || (args->current()->unit != CSSPrimitiveValue::CSS_STRING && args->current()->unit != CSSPrimitiveValue::CSS_IDENT)) 5677 return false; 5678 uriValue->setFormat(args->current()->string); 5679 valueList->append(uriValue.release()); 5680 value = m_valueList->next(); 5681 if (value && value->unit == CSSParserValue::Operator && value->iValue == ',') 5682 m_valueList->next(); 5683 return true; 5684} 5685 5686bool CSSParser::parseFontFaceSrcLocal(CSSValueList* valueList) 5687{ 5688 CSSParserValueList* args = m_valueList->current()->function->args.get(); 5689 if (!args || !args->size()) 5690 return false; 5691 5692 if (args->size() == 1 && args->current()->unit == CSSPrimitiveValue::CSS_STRING) 5693 valueList->append(CSSFontFaceSrcValue::createLocal(args->current()->string)); 5694 else if (args->current()->unit == CSSPrimitiveValue::CSS_IDENT) { 5695 StringBuilder builder; 5696 for (CSSParserValue* localValue = args->current(); localValue; localValue = args->next()) { 5697 if (localValue->unit != CSSPrimitiveValue::CSS_IDENT) 5698 return false; 5699 if (!builder.isEmpty()) 5700 builder.append(' '); 5701 builder.append(localValue->string); 5702 } 5703 valueList->append(CSSFontFaceSrcValue::createLocal(builder.toString())); 5704 } else 5705 return false; 5706 5707 if (CSSParserValue* value = m_valueList->next()) { 5708 if (value->unit == CSSParserValue::Operator && value->iValue == ',') 5709 m_valueList->next(); 5710 } 5711 return true; 5712} 5713 5714bool CSSParser::parseFontFaceSrc() 5715{ 5716 RefPtr<CSSValueList> values(CSSValueList::createCommaSeparated()); 5717 5718 while (CSSParserValue* value = m_valueList->current()) { 5719 if (value->unit == CSSPrimitiveValue::CSS_URI) { 5720 if (!parseFontFaceSrcURI(values.get())) 5721 return false; 5722 } else if (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "local(")) { 5723 if (!parseFontFaceSrcLocal(values.get())) 5724 return false; 5725 } else 5726 return false; 5727 } 5728 if (!values->length()) 5729 return false; 5730 5731 addProperty(CSSPropertySrc, values.release(), m_important); 5732 m_valueList->next(); 5733 return true; 5734} 5735 5736bool CSSParser::parseFontFaceUnicodeRange() 5737{ 5738 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); 5739 bool failed = false; 5740 bool operatorExpected = false; 5741 for (; m_valueList->current(); m_valueList->next(), operatorExpected = !operatorExpected) { 5742 if (operatorExpected) { 5743 if (m_valueList->current()->unit == CSSParserValue::Operator && m_valueList->current()->iValue == ',') 5744 continue; 5745 failed = true; 5746 break; 5747 } 5748 if (m_valueList->current()->unit != CSSPrimitiveValue::CSS_UNICODE_RANGE) { 5749 failed = true; 5750 break; 5751 } 5752 5753 String rangeString = m_valueList->current()->string; 5754 UChar32 from = 0; 5755 UChar32 to = 0; 5756 unsigned length = rangeString.length(); 5757 5758 if (length < 3) { 5759 failed = true; 5760 break; 5761 } 5762 5763 unsigned i = 2; 5764 while (i < length) { 5765 UChar c = rangeString[i]; 5766 if (c == '-' || c == '?') 5767 break; 5768 from *= 16; 5769 if (c >= '0' && c <= '9') 5770 from += c - '0'; 5771 else if (c >= 'A' && c <= 'F') 5772 from += 10 + c - 'A'; 5773 else if (c >= 'a' && c <= 'f') 5774 from += 10 + c - 'a'; 5775 else { 5776 failed = true; 5777 break; 5778 } 5779 i++; 5780 } 5781 if (failed) 5782 break; 5783 5784 if (i == length) 5785 to = from; 5786 else if (rangeString[i] == '?') { 5787 unsigned span = 1; 5788 while (i < length && rangeString[i] == '?') { 5789 span *= 16; 5790 from *= 16; 5791 i++; 5792 } 5793 if (i < length) 5794 failed = true; 5795 to = from + span - 1; 5796 } else { 5797 if (length < i + 2) { 5798 failed = true; 5799 break; 5800 } 5801 i++; 5802 while (i < length) { 5803 UChar c = rangeString[i]; 5804 to *= 16; 5805 if (c >= '0' && c <= '9') 5806 to += c - '0'; 5807 else if (c >= 'A' && c <= 'F') 5808 to += 10 + c - 'A'; 5809 else if (c >= 'a' && c <= 'f') 5810 to += 10 + c - 'a'; 5811 else { 5812 failed = true; 5813 break; 5814 } 5815 i++; 5816 } 5817 if (failed) 5818 break; 5819 } 5820 if (from <= to) 5821 values->append(CSSUnicodeRangeValue::create(from, to)); 5822 } 5823 if (failed || !values->length()) 5824 return false; 5825 addProperty(CSSPropertyUnicodeRange, values.release(), m_important); 5826 return true; 5827} 5828 5829// Returns the number of characters which form a valid double 5830// and are terminated by the given terminator character 5831template <typename CharacterType> 5832static int checkForValidDouble(const CharacterType* string, const CharacterType* end, const char terminator) 5833{ 5834 int length = end - string; 5835 if (length < 1) 5836 return 0; 5837 5838 bool decimalMarkSeen = false; 5839 int processedLength = 0; 5840 5841 for (int i = 0; i < length; ++i) { 5842 if (string[i] == terminator) { 5843 processedLength = i; 5844 break; 5845 } 5846 if (!isASCIIDigit(string[i])) { 5847 if (!decimalMarkSeen && string[i] == '.') 5848 decimalMarkSeen = true; 5849 else 5850 return 0; 5851 } 5852 } 5853 5854 if (decimalMarkSeen && processedLength == 1) 5855 return 0; 5856 5857 return processedLength; 5858} 5859 5860// Returns the number of characters consumed for parsing a valid double 5861// terminated by the given terminator character 5862template <typename CharacterType> 5863static int parseDouble(const CharacterType* string, const CharacterType* end, const char terminator, double& value) 5864{ 5865 int length = checkForValidDouble(string, end, terminator); 5866 if (!length) 5867 return 0; 5868 5869 int position = 0; 5870 double localValue = 0; 5871 5872 // The consumed characters here are guaranteed to be 5873 // ASCII digits with or without a decimal mark 5874 for (; position < length; ++position) { 5875 if (string[position] == '.') 5876 break; 5877 localValue = localValue * 10 + string[position] - '0'; 5878 } 5879 5880 if (++position == length) { 5881 value = localValue; 5882 return length; 5883 } 5884 5885 double fraction = 0; 5886 double scale = 1; 5887 5888 while (position < length && scale < MAX_SCALE) { 5889 fraction = fraction * 10 + string[position++] - '0'; 5890 scale *= 10; 5891 } 5892 5893 value = localValue + fraction / scale; 5894 return length; 5895} 5896 5897template <typename CharacterType> 5898static bool parseColorIntOrPercentage(const CharacterType*& string, const CharacterType* end, const char terminator, CSSPrimitiveValue::UnitTypes& expect, int& value) 5899{ 5900 const CharacterType* current = string; 5901 double localValue = 0; 5902 bool negative = false; 5903 while (current != end && isHTMLSpace(*current)) 5904 current++; 5905 if (current != end && *current == '-') { 5906 negative = true; 5907 current++; 5908 } 5909 if (current == end || !isASCIIDigit(*current)) 5910 return false; 5911 while (current != end && isASCIIDigit(*current)) { 5912 double newValue = localValue * 10 + *current++ - '0'; 5913 if (newValue >= 255) { 5914 // Clamp values at 255. 5915 localValue = 255; 5916 while (current != end && isASCIIDigit(*current)) 5917 ++current; 5918 break; 5919 } 5920 localValue = newValue; 5921 } 5922 5923 if (current == end) 5924 return false; 5925 5926 if (expect == CSSPrimitiveValue::CSS_NUMBER && (*current == '.' || *current == '%')) 5927 return false; 5928 5929 if (*current == '.') { 5930 // We already parsed the integral part, try to parse 5931 // the fraction part of the percentage value. 5932 double percentage = 0; 5933 int numCharactersParsed = parseDouble(current, end, '%', percentage); 5934 if (!numCharactersParsed) 5935 return false; 5936 current += numCharactersParsed; 5937 if (*current != '%') 5938 return false; 5939 localValue += percentage; 5940 } 5941 5942 if (expect == CSSPrimitiveValue::CSS_PERCENTAGE && *current != '%') 5943 return false; 5944 5945 if (*current == '%') { 5946 expect = CSSPrimitiveValue::CSS_PERCENTAGE; 5947 localValue = localValue / 100.0 * 256.0; 5948 // Clamp values at 255 for percentages over 100% 5949 if (localValue > 255) 5950 localValue = 255; 5951 current++; 5952 } else 5953 expect = CSSPrimitiveValue::CSS_NUMBER; 5954 5955 while (current != end && isHTMLSpace(*current)) 5956 current++; 5957 if (current == end || *current++ != terminator) 5958 return false; 5959 // Clamp negative values at zero. 5960 value = negative ? 0 : static_cast<int>(localValue); 5961 string = current; 5962 return true; 5963} 5964 5965template <typename CharacterType> 5966static inline bool isTenthAlpha(const CharacterType* string, const int length) 5967{ 5968 // "0.X" 5969 if (length == 3 && string[0] == '0' && string[1] == '.' && isASCIIDigit(string[2])) 5970 return true; 5971 5972 // ".X" 5973 if (length == 2 && string[0] == '.' && isASCIIDigit(string[1])) 5974 return true; 5975 5976 return false; 5977} 5978 5979template <typename CharacterType> 5980static inline bool parseAlphaValue(const CharacterType*& string, const CharacterType* end, const char terminator, int& value) 5981{ 5982 while (string != end && isHTMLSpace(*string)) 5983 string++; 5984 5985 bool negative = false; 5986 5987 if (string != end && *string == '-') { 5988 negative = true; 5989 string++; 5990 } 5991 5992 value = 0; 5993 5994 int length = end - string; 5995 if (length < 2) 5996 return false; 5997 5998 if (string[length - 1] != terminator || !isASCIIDigit(string[length - 2])) 5999 return false; 6000 6001 if (string[0] != '0' && string[0] != '1' && string[0] != '.') { 6002 if (checkForValidDouble(string, end, terminator)) { 6003 value = negative ? 0 : 255; 6004 string = end; 6005 return true; 6006 } 6007 return false; 6008 } 6009 6010 if (length == 2 && string[0] != '.') { 6011 value = !negative && string[0] == '1' ? 255 : 0; 6012 string = end; 6013 return true; 6014 } 6015 6016 if (isTenthAlpha(string, length - 1)) { 6017 static const int tenthAlphaValues[] = { 0, 25, 51, 76, 102, 127, 153, 179, 204, 230 }; 6018 value = negative ? 0 : tenthAlphaValues[string[length - 2] - '0']; 6019 string = end; 6020 return true; 6021 } 6022 6023 double alpha = 0; 6024 if (!parseDouble(string, end, terminator, alpha)) 6025 return false; 6026 value = negative ? 0 : static_cast<int>(alpha * nextafter(256.0, 0.0)); 6027 string = end; 6028 return true; 6029} 6030 6031template <typename CharacterType> 6032static inline bool mightBeRGBA(const CharacterType* characters, unsigned length) 6033{ 6034 if (length < 5) 6035 return false; 6036 return characters[4] == '(' 6037 && isASCIIAlphaCaselessEqual(characters[0], 'r') 6038 && isASCIIAlphaCaselessEqual(characters[1], 'g') 6039 && isASCIIAlphaCaselessEqual(characters[2], 'b') 6040 && isASCIIAlphaCaselessEqual(characters[3], 'a'); 6041} 6042 6043template <typename CharacterType> 6044static inline bool mightBeRGB(const CharacterType* characters, unsigned length) 6045{ 6046 if (length < 4) 6047 return false; 6048 return characters[3] == '(' 6049 && isASCIIAlphaCaselessEqual(characters[0], 'r') 6050 && isASCIIAlphaCaselessEqual(characters[1], 'g') 6051 && isASCIIAlphaCaselessEqual(characters[2], 'b'); 6052} 6053 6054template <typename CharacterType> 6055static inline bool fastParseColorInternal(RGBA32& rgb, const CharacterType* characters, unsigned length , bool strict) 6056{ 6057 CSSPrimitiveValue::UnitTypes expect = CSSPrimitiveValue::CSS_UNKNOWN; 6058 6059 if (!strict && length >= 3) { 6060 if (characters[0] == '#') { 6061 if (Color::parseHexColor(characters + 1, length - 1, rgb)) 6062 return true; 6063 } else { 6064 if (Color::parseHexColor(characters, length, rgb)) 6065 return true; 6066 } 6067 } 6068 6069 // Try rgba() syntax. 6070 if (mightBeRGBA(characters, length)) { 6071 const CharacterType* current = characters + 5; 6072 const CharacterType* end = characters + length; 6073 int red; 6074 int green; 6075 int blue; 6076 int alpha; 6077 6078 if (!parseColorIntOrPercentage(current, end, ',', expect, red)) 6079 return false; 6080 if (!parseColorIntOrPercentage(current, end, ',', expect, green)) 6081 return false; 6082 if (!parseColorIntOrPercentage(current, end, ',', expect, blue)) 6083 return false; 6084 if (!parseAlphaValue(current, end, ')', alpha)) 6085 return false; 6086 if (current != end) 6087 return false; 6088 rgb = makeRGBA(red, green, blue, alpha); 6089 return true; 6090 } 6091 6092 // Try rgb() syntax. 6093 if (mightBeRGB(characters, length)) { 6094 const CharacterType* current = characters + 4; 6095 const CharacterType* end = characters + length; 6096 int red; 6097 int green; 6098 int blue; 6099 if (!parseColorIntOrPercentage(current, end, ',', expect, red)) 6100 return false; 6101 if (!parseColorIntOrPercentage(current, end, ',', expect, green)) 6102 return false; 6103 if (!parseColorIntOrPercentage(current, end, ')', expect, blue)) 6104 return false; 6105 if (current != end) 6106 return false; 6107 rgb = makeRGB(red, green, blue); 6108 return true; 6109 } 6110 6111 return false; 6112} 6113 6114template<typename StringType> 6115bool CSSParser::fastParseColor(RGBA32& rgb, const StringType& name, bool strict) 6116{ 6117 unsigned length = name.length(); 6118 bool parseResult; 6119 6120 if (!length) 6121 return false; 6122 6123 if (name.is8Bit()) 6124 parseResult = fastParseColorInternal(rgb, name.characters8(), length, strict); 6125 else 6126 parseResult = fastParseColorInternal(rgb, name.characters16(), length, strict); 6127 6128 if (parseResult) 6129 return true; 6130 6131 // Try named colors. 6132 Color tc; 6133 tc.setNamedColor(name); 6134 if (tc.isValid()) { 6135 rgb = tc.rgb(); 6136 return true; 6137 } 6138 return false; 6139} 6140 6141inline double CSSParser::parsedDouble(CSSParserValue *v, ReleaseParsedCalcValueCondition releaseCalc) 6142{ 6143 const double result = m_parsedCalculation ? m_parsedCalculation->doubleValue() : v->fValue; 6144 if (releaseCalc == ReleaseParsedCalcValue) 6145 m_parsedCalculation.release(); 6146 return result; 6147} 6148 6149bool CSSParser::isCalculation(CSSParserValue* value) 6150{ 6151 return (value->unit == CSSParserValue::Function) 6152 && (equalIgnoringCase(value->function->name, "calc(") 6153 || equalIgnoringCase(value->function->name, "-webkit-calc(") 6154 || equalIgnoringCase(value->function->name, "-webkit-min(") 6155 || equalIgnoringCase(value->function->name, "-webkit-max(")); 6156} 6157 6158inline int CSSParser::colorIntFromValue(CSSParserValue* v) 6159{ 6160 bool isPercent; 6161 6162 if (m_parsedCalculation) 6163 isPercent = m_parsedCalculation->category() == CalcPercent; 6164 else 6165 isPercent = v->unit == CSSPrimitiveValue::CSS_PERCENTAGE; 6166 6167 const double value = parsedDouble(v, ReleaseParsedCalcValue); 6168 6169 if (value <= 0.0) 6170 return 0; 6171 6172 if (isPercent) { 6173 if (value >= 100.0) 6174 return 255; 6175 return static_cast<int>(value * 256.0 / 100.0); 6176 } 6177 6178 if (value >= 255.0) 6179 return 255; 6180 6181 return static_cast<int>(value); 6182} 6183 6184bool CSSParser::parseColorParameters(CSSParserValue* value, int* colorArray, bool parseAlpha) 6185{ 6186 CSSParserValueList* args = value->function->args.get(); 6187 CSSParserValue* v = args->current(); 6188 Units unitType = FUnknown; 6189 // Get the first value and its type 6190 if (validUnit(v, FInteger, CSSStrictMode)) 6191 unitType = FInteger; 6192 else if (validUnit(v, FPercent, CSSStrictMode)) 6193 unitType = FPercent; 6194 else 6195 return false; 6196 6197 colorArray[0] = colorIntFromValue(v); 6198 for (int i = 1; i < 3; i++) { 6199 v = args->next(); 6200 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6201 return false; 6202 v = args->next(); 6203 if (!validUnit(v, unitType, CSSStrictMode)) 6204 return false; 6205 colorArray[i] = colorIntFromValue(v); 6206 } 6207 if (parseAlpha) { 6208 v = args->next(); 6209 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6210 return false; 6211 v = args->next(); 6212 if (!validUnit(v, FNumber, CSSStrictMode)) 6213 return false; 6214 const double value = parsedDouble(v, ReleaseParsedCalcValue); 6215 // Convert the floating pointer number of alpha to an integer in the range [0, 256), 6216 // with an equal distribution across all 256 values. 6217 colorArray[3] = static_cast<int>(max(0.0, min(1.0, value)) * nextafter(256.0, 0.0)); 6218 } 6219 return true; 6220} 6221 6222// The CSS3 specification defines the format of a HSL color as 6223// hsl(<number>, <percent>, <percent>) 6224// and with alpha, the format is 6225// hsla(<number>, <percent>, <percent>, <number>) 6226// The first value, HUE, is in an angle with a value between 0 and 360 6227bool CSSParser::parseHSLParameters(CSSParserValue* value, double* colorArray, bool parseAlpha) 6228{ 6229 CSSParserValueList* args = value->function->args.get(); 6230 CSSParserValue* v = args->current(); 6231 // Get the first value 6232 if (!validUnit(v, FNumber, CSSStrictMode)) 6233 return false; 6234 // normalize the Hue value and change it to be between 0 and 1.0 6235 colorArray[0] = (((static_cast<int>(parsedDouble(v, ReleaseParsedCalcValue)) % 360) + 360) % 360) / 360.0; 6236 for (int i = 1; i < 3; i++) { 6237 v = args->next(); 6238 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6239 return false; 6240 v = args->next(); 6241 if (!validUnit(v, FPercent, CSSStrictMode)) 6242 return false; 6243 colorArray[i] = max(0.0, min(100.0, parsedDouble(v, ReleaseParsedCalcValue))) / 100.0; // needs to be value between 0 and 1.0 6244 } 6245 if (parseAlpha) { 6246 v = args->next(); 6247 if (v->unit != CSSParserValue::Operator && v->iValue != ',') 6248 return false; 6249 v = args->next(); 6250 if (!validUnit(v, FNumber, CSSStrictMode)) 6251 return false; 6252 colorArray[3] = max(0.0, min(1.0, parsedDouble(v, ReleaseParsedCalcValue))); 6253 } 6254 return true; 6255} 6256 6257PassRefPtr<CSSPrimitiveValue> CSSParser::parseColor(CSSParserValue* value) 6258{ 6259 RGBA32 c = Color::transparent; 6260 if (!parseColorFromValue(value ? value : m_valueList->current(), c)) 6261 return 0; 6262 return cssValuePool().createColorValue(c); 6263} 6264 6265bool CSSParser::parseColorFromValue(CSSParserValue* value, RGBA32& c) 6266{ 6267 if (inQuirksMode() && value->unit == CSSPrimitiveValue::CSS_NUMBER 6268 && value->fValue >= 0. && value->fValue < 1000000.) { 6269 String str = String::format("%06d", static_cast<int>((value->fValue+.5))); 6270 // FIXME: This should be strict parsing for SVG as well. 6271 if (!fastParseColor(c, str, inStrictMode())) 6272 return false; 6273 } else if (value->unit == CSSPrimitiveValue::CSS_PARSER_HEXCOLOR || 6274 value->unit == CSSPrimitiveValue::CSS_IDENT || 6275 (inQuirksMode() && value->unit == CSSPrimitiveValue::CSS_DIMENSION)) { 6276 if (!fastParseColor(c, value->string, inStrictMode() && value->unit == CSSPrimitiveValue::CSS_IDENT)) 6277 return false; 6278 } else if (value->unit == CSSParserValue::Function && 6279 value->function->args != 0 && 6280 value->function->args->size() == 5 /* rgb + two commas */ && 6281 equalIgnoringCase(value->function->name, "rgb(")) { 6282 int colorValues[3]; 6283 if (!parseColorParameters(value, colorValues, false)) 6284 return false; 6285 c = makeRGB(colorValues[0], colorValues[1], colorValues[2]); 6286 } else { 6287 if (value->unit == CSSParserValue::Function && 6288 value->function->args != 0 && 6289 value->function->args->size() == 7 /* rgba + three commas */ && 6290 equalIgnoringCase(value->function->name, "rgba(")) { 6291 int colorValues[4]; 6292 if (!parseColorParameters(value, colorValues, true)) 6293 return false; 6294 c = makeRGBA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); 6295 } else if (value->unit == CSSParserValue::Function && 6296 value->function->args != 0 && 6297 value->function->args->size() == 5 /* hsl + two commas */ && 6298 equalIgnoringCase(value->function->name, "hsl(")) { 6299 double colorValues[3]; 6300 if (!parseHSLParameters(value, colorValues, false)) 6301 return false; 6302 c = makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], 1.0); 6303 } else if (value->unit == CSSParserValue::Function && 6304 value->function->args != 0 && 6305 value->function->args->size() == 7 /* hsla + three commas */ && 6306 equalIgnoringCase(value->function->name, "hsla(")) { 6307 double colorValues[4]; 6308 if (!parseHSLParameters(value, colorValues, true)) 6309 return false; 6310 c = makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); 6311 } else 6312 return false; 6313 } 6314 6315 return true; 6316} 6317 6318// This class tracks parsing state for shadow values. If it goes out of scope (e.g., due to an early return) 6319// without the allowBreak bit being set, then it will clean up all of the objects and destroy them. 6320struct ShadowParseContext { 6321 ShadowParseContext(CSSPropertyID prop, CSSParser* parser) 6322 : property(prop) 6323 , m_parser(parser) 6324 , allowX(true) 6325 , allowY(false) 6326 , allowBlur(false) 6327 , allowSpread(false) 6328 , allowColor(true) 6329 , allowStyle(prop == CSSPropertyWebkitBoxShadow || prop == CSSPropertyBoxShadow) 6330 , allowBreak(true) 6331 { 6332 } 6333 6334 bool allowLength() { return allowX || allowY || allowBlur || allowSpread; } 6335 6336 void commitValue() 6337 { 6338 // Handle the ,, case gracefully by doing nothing. 6339 if (x || y || blur || spread || color || style) { 6340 if (!values) 6341 values = CSSValueList::createCommaSeparated(); 6342 6343 // Construct the current shadow value and add it to the list. 6344 values->append(ShadowValue::create(x.release(), y.release(), blur.release(), spread.release(), style.release(), color.release())); 6345 } 6346 6347 // Now reset for the next shadow value. 6348 x = 0; 6349 y = 0; 6350 blur = 0; 6351 spread = 0; 6352 style = 0; 6353 color = 0; 6354 6355 allowX = true; 6356 allowColor = true; 6357 allowBreak = true; 6358 allowY = false; 6359 allowBlur = false; 6360 allowSpread = false; 6361 allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 6362 } 6363 6364 void commitLength(CSSParserValue* v) 6365 { 6366 RefPtr<CSSPrimitiveValue> val = m_parser->createPrimitiveNumericValue(v); 6367 6368 if (allowX) { 6369 x = val.release(); 6370 allowX = false; 6371 allowY = true; 6372 allowColor = false; 6373 allowStyle = false; 6374 allowBreak = false; 6375 } else if (allowY) { 6376 y = val.release(); 6377 allowY = false; 6378 allowBlur = true; 6379 allowColor = true; 6380 allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 6381 allowBreak = true; 6382 } else if (allowBlur) { 6383 blur = val.release(); 6384 allowBlur = false; 6385 allowSpread = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 6386 } else if (allowSpread) { 6387 spread = val.release(); 6388 allowSpread = false; 6389 } 6390 } 6391 6392 void commitColor(PassRefPtr<CSSPrimitiveValue> val) 6393 { 6394 color = val; 6395 allowColor = false; 6396 if (allowX) { 6397 allowStyle = false; 6398 allowBreak = false; 6399 } else { 6400 allowBlur = false; 6401 allowSpread = false; 6402 allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; 6403 } 6404 } 6405 6406 void commitStyle(CSSParserValue* v) 6407 { 6408 style = cssValuePool().createIdentifierValue(v->id); 6409 allowStyle = false; 6410 if (allowX) 6411 allowBreak = false; 6412 else { 6413 allowBlur = false; 6414 allowSpread = false; 6415 allowColor = false; 6416 } 6417 } 6418 6419 CSSPropertyID property; 6420 CSSParser* m_parser; 6421 6422 RefPtr<CSSValueList> values; 6423 RefPtr<CSSPrimitiveValue> x; 6424 RefPtr<CSSPrimitiveValue> y; 6425 RefPtr<CSSPrimitiveValue> blur; 6426 RefPtr<CSSPrimitiveValue> spread; 6427 RefPtr<CSSPrimitiveValue> style; 6428 RefPtr<CSSPrimitiveValue> color; 6429 6430 bool allowX; 6431 bool allowY; 6432 bool allowBlur; 6433 bool allowSpread; 6434 bool allowColor; 6435 bool allowStyle; // inset or not. 6436 bool allowBreak; 6437}; 6438 6439PassRefPtr<CSSValueList> CSSParser::parseShadow(CSSParserValueList* valueList, CSSPropertyID propId) 6440{ 6441 ShadowParseContext context(propId, this); 6442 CSSParserValue* val; 6443 while ((val = valueList->current())) { 6444 // Check for a comma break first. 6445 if (val->unit == CSSParserValue::Operator) { 6446 if (val->iValue != ',' || !context.allowBreak) 6447 // Other operators aren't legal or we aren't done with the current shadow 6448 // value. Treat as invalid. 6449 return 0; 6450#if ENABLE(SVG) 6451 // -webkit-svg-shadow does not support multiple values. 6452 if (propId == CSSPropertyWebkitSvgShadow) 6453 return 0; 6454#endif 6455 // The value is good. Commit it. 6456 context.commitValue(); 6457 } else if (validUnit(val, FLength, CSSStrictMode)) { 6458 // We required a length and didn't get one. Invalid. 6459 if (!context.allowLength()) 6460 return 0; 6461 6462 // Blur radius must be non-negative. 6463 if (context.allowBlur && !validUnit(val, FLength | FNonNeg, CSSStrictMode)) 6464 return 0; 6465 6466 // A length is allowed here. Construct the value and add it. 6467 context.commitLength(val); 6468 } else if (val->id == CSSValueInset) { 6469 if (!context.allowStyle) 6470 return 0; 6471 6472 context.commitStyle(val); 6473 } else { 6474 // The only other type of value that's ok is a color value. 6475 RefPtr<CSSPrimitiveValue> parsedColor; 6476 bool isColor = ((val->id >= CSSValueAqua && val->id <= CSSValueWindowtext) || val->id == CSSValueMenu 6477 || (val->id >= CSSValueWebkitFocusRingColor && val->id <= CSSValueWebkitText && inQuirksMode()) 6478 || val->id == CSSValueCurrentcolor); 6479 if (isColor) { 6480 if (!context.allowColor) 6481 return 0; 6482 parsedColor = cssValuePool().createIdentifierValue(val->id); 6483 } 6484 6485 if (!parsedColor) 6486 // It's not built-in. Try to parse it as a color. 6487 parsedColor = parseColor(val); 6488 6489 if (!parsedColor || !context.allowColor) 6490 return 0; // This value is not a color or length and is invalid or 6491 // it is a color, but a color isn't allowed at this point. 6492 6493 context.commitColor(parsedColor.release()); 6494 } 6495 6496 valueList->next(); 6497 } 6498 6499 if (context.allowBreak) { 6500 context.commitValue(); 6501 if (context.values && context.values->length()) 6502 return context.values.release(); 6503 } 6504 6505 return 0; 6506} 6507 6508bool CSSParser::parseReflect(CSSPropertyID propId, bool important) 6509{ 6510 // box-reflect: <direction> <offset> <mask> 6511 6512 // Direction comes first. 6513 CSSParserValue* val = m_valueList->current(); 6514 RefPtr<CSSPrimitiveValue> direction; 6515#if ENABLE(CSS_VARIABLES) 6516 if (val->unit == CSSPrimitiveValue::CSS_VARIABLE_NAME) 6517 direction = createPrimitiveVariableNameValue(val); 6518 else 6519#endif 6520 switch (val->id) { 6521 case CSSValueAbove: 6522 case CSSValueBelow: 6523 case CSSValueLeft: 6524 case CSSValueRight: 6525 direction = cssValuePool().createIdentifierValue(val->id); 6526 break; 6527 default: 6528 return false; 6529 } 6530 6531 // The offset comes next. 6532 val = m_valueList->next(); 6533 RefPtr<CSSPrimitiveValue> offset; 6534 if (!val) 6535 offset = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX); 6536 else { 6537 if (!validUnit(val, FLength | FPercent)) 6538 return false; 6539 offset = createPrimitiveNumericValue(val); 6540 } 6541 6542 // Now for the mask. 6543 RefPtr<CSSValue> mask; 6544 val = m_valueList->next(); 6545 if (val) { 6546 if (!parseBorderImage(propId, mask)) 6547 return false; 6548 } 6549 6550 RefPtr<CSSReflectValue> reflectValue = CSSReflectValue::create(direction.release(), offset.release(), mask.release()); 6551 addProperty(propId, reflectValue.release(), important); 6552 m_valueList->next(); 6553 return true; 6554} 6555 6556bool CSSParser::parseFlex(CSSParserValueList* args, bool important) 6557{ 6558 if (!args || !args->size() || args->size() > 3) 6559 return false; 6560 static const double unsetValue = -1; 6561 double flexGrow = unsetValue; 6562 double flexShrink = unsetValue; 6563 RefPtr<CSSPrimitiveValue> flexBasis; 6564 6565 while (CSSParserValue* arg = args->current()) { 6566 if (validUnit(arg, FNumber | FNonNeg)) { 6567 if (flexGrow == unsetValue) 6568 flexGrow = arg->fValue; 6569 else if (flexShrink == unsetValue) 6570 flexShrink = arg->fValue; 6571 else if (!arg->fValue) { 6572 // flex only allows a basis of 0 (sans units) if flex-grow and flex-shrink values have already been set. 6573 flexBasis = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX); 6574 } else { 6575 // We only allow 3 numbers without units if the last value is 0. E.g., flex:1 1 1 is invalid. 6576 return false; 6577 } 6578 } else if (!flexBasis && (arg->id == CSSValueAuto || validUnit(arg, FLength | FPercent | FNonNeg))) 6579 flexBasis = parseValidPrimitive(arg->id, arg); 6580 else { 6581 // Not a valid arg for flex. 6582 return false; 6583 } 6584 args->next(); 6585 } 6586 6587 if (flexGrow == unsetValue) 6588 flexGrow = 1; 6589 if (flexShrink == unsetValue) 6590 flexShrink = 1; 6591 if (!flexBasis) 6592 flexBasis = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_PX); 6593 6594 addProperty(CSSPropertyWebkitFlexGrow, cssValuePool().createValue(clampToFloat(flexGrow), CSSPrimitiveValue::CSS_NUMBER), important); 6595 addProperty(CSSPropertyWebkitFlexShrink, cssValuePool().createValue(clampToFloat(flexShrink), CSSPrimitiveValue::CSS_NUMBER), important); 6596 addProperty(CSSPropertyWebkitFlexBasis, flexBasis, important); 6597 return true; 6598} 6599 6600struct BorderImageParseContext { 6601 BorderImageParseContext() 6602 : m_canAdvance(false) 6603 , m_allowCommit(true) 6604 , m_allowImage(true) 6605 , m_allowImageSlice(true) 6606 , m_allowRepeat(true) 6607 , m_allowForwardSlashOperator(false) 6608 , m_requireWidth(false) 6609 , m_requireOutset(false) 6610 {} 6611 6612 bool canAdvance() const { return m_canAdvance; } 6613 void setCanAdvance(bool canAdvance) { m_canAdvance = canAdvance; } 6614 6615 bool allowCommit() const { return m_allowCommit; } 6616 bool allowImage() const { return m_allowImage; } 6617 bool allowImageSlice() const { return m_allowImageSlice; } 6618 bool allowRepeat() const { return m_allowRepeat; } 6619 bool allowForwardSlashOperator() const { return m_allowForwardSlashOperator; } 6620 6621 bool requireWidth() const { return m_requireWidth; } 6622 bool requireOutset() const { return m_requireOutset; } 6623 6624 void commitImage(PassRefPtr<CSSValue> image) 6625 { 6626 m_image = image; 6627 m_canAdvance = true; 6628 m_allowCommit = true; 6629 m_allowImage = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false; 6630 m_allowImageSlice = !m_imageSlice; 6631 m_allowRepeat = !m_repeat; 6632 } 6633 void commitImageSlice(PassRefPtr<CSSBorderImageSliceValue> slice) 6634 { 6635 m_imageSlice = slice; 6636 m_canAdvance = true; 6637 m_allowCommit = m_allowForwardSlashOperator = true; 6638 m_allowImageSlice = m_requireWidth = m_requireOutset = false; 6639 m_allowImage = !m_image; 6640 m_allowRepeat = !m_repeat; 6641 } 6642 void commitForwardSlashOperator() 6643 { 6644 m_canAdvance = true; 6645 m_allowCommit = m_allowImage = m_allowImageSlice = m_allowRepeat = m_allowForwardSlashOperator = false; 6646 if (!m_borderSlice) { 6647 m_requireWidth = true; 6648 m_requireOutset = false; 6649 } else { 6650 m_requireOutset = true; 6651 m_requireWidth = false; 6652 } 6653 } 6654 void commitBorderWidth(PassRefPtr<CSSPrimitiveValue> slice) 6655 { 6656 m_borderSlice = slice; 6657 m_canAdvance = true; 6658 m_allowCommit = m_allowForwardSlashOperator = true; 6659 m_allowImageSlice = m_requireWidth = m_requireOutset = false; 6660 m_allowImage = !m_image; 6661 m_allowRepeat = !m_repeat; 6662 } 6663 void commitBorderOutset(PassRefPtr<CSSPrimitiveValue> outset) 6664 { 6665 m_outset = outset; 6666 m_canAdvance = true; 6667 m_allowCommit = true; 6668 m_allowImageSlice = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false; 6669 m_allowImage = !m_image; 6670 m_allowRepeat = !m_repeat; 6671 } 6672 void commitRepeat(PassRefPtr<CSSValue> repeat) 6673 { 6674 m_repeat = repeat; 6675 m_canAdvance = true; 6676 m_allowCommit = true; 6677 m_allowRepeat = m_allowForwardSlashOperator = m_requireWidth = m_requireOutset = false; 6678 m_allowImageSlice = !m_imageSlice; 6679 m_allowImage = !m_image; 6680 } 6681 6682 PassRefPtr<CSSValue> commitWebKitBorderImage() 6683 { 6684 return createBorderImageValue(m_image, m_imageSlice, m_borderSlice, m_outset, m_repeat); 6685 } 6686 6687 void commitBorderImage(CSSParser* parser, bool important) 6688 { 6689 commitBorderImageProperty(CSSPropertyBorderImageSource, parser, m_image, important); 6690 commitBorderImageProperty(CSSPropertyBorderImageSlice, parser, m_imageSlice, important); 6691 commitBorderImageProperty(CSSPropertyBorderImageWidth, parser, m_borderSlice, important); 6692 commitBorderImageProperty(CSSPropertyBorderImageOutset, parser, m_outset, important); 6693 commitBorderImageProperty(CSSPropertyBorderImageRepeat, parser, m_repeat, important); 6694 } 6695 6696 void commitBorderImageProperty(CSSPropertyID propId, CSSParser* parser, PassRefPtr<CSSValue> value, bool important) 6697 { 6698 if (value) 6699 parser->addProperty(propId, value, important); 6700 else 6701 parser->addProperty(propId, cssValuePool().createImplicitInitialValue(), important, true); 6702 } 6703 6704 bool m_canAdvance; 6705 6706 bool m_allowCommit; 6707 bool m_allowImage; 6708 bool m_allowImageSlice; 6709 bool m_allowRepeat; 6710 bool m_allowForwardSlashOperator; 6711 6712 bool m_requireWidth; 6713 bool m_requireOutset; 6714 6715 RefPtr<CSSValue> m_image; 6716 RefPtr<CSSBorderImageSliceValue> m_imageSlice; 6717 RefPtr<CSSPrimitiveValue> m_borderSlice; 6718 RefPtr<CSSPrimitiveValue> m_outset; 6719 6720 RefPtr<CSSValue> m_repeat; 6721}; 6722 6723bool CSSParser::parseBorderImage(CSSPropertyID propId, RefPtr<CSSValue>& result, bool important) 6724{ 6725 ShorthandScope scope(this, propId); 6726 BorderImageParseContext context; 6727 while (CSSParserValue* val = m_valueList->current()) { 6728 context.setCanAdvance(false); 6729 6730 if (!context.canAdvance() && context.allowForwardSlashOperator() && isForwardSlashOperator(val)) 6731 context.commitForwardSlashOperator(); 6732 6733 if (!context.canAdvance() && context.allowImage()) { 6734 if (val->unit == CSSPrimitiveValue::CSS_URI) 6735 context.commitImage(CSSImageValue::create(completeURL(val->string))); 6736 else if (isGeneratedImageValue(val)) { 6737 RefPtr<CSSValue> value; 6738 if (parseGeneratedImage(m_valueList.get(), value)) 6739 context.commitImage(value.release()); 6740 else 6741 return false; 6742#if ENABLE(CSS_IMAGE_SET) 6743 } else if (val->unit == CSSParserValue::Function && equalIgnoringCase(val->function->name, "-webkit-image-set(")) { 6744 RefPtr<CSSValue> value = parseImageSet(); 6745 if (value) 6746 context.commitImage(value.release()); 6747 else 6748 return false; 6749#endif 6750 } else if (val->id == CSSValueNone) 6751 context.commitImage(cssValuePool().createIdentifierValue(CSSValueNone)); 6752 } 6753 6754 if (!context.canAdvance() && context.allowImageSlice()) { 6755 RefPtr<CSSBorderImageSliceValue> imageSlice; 6756 if (parseBorderImageSlice(propId, imageSlice)) 6757 context.commitImageSlice(imageSlice.release()); 6758 } 6759 6760 if (!context.canAdvance() && context.allowRepeat()) { 6761 RefPtr<CSSValue> repeat; 6762 if (parseBorderImageRepeat(repeat)) 6763 context.commitRepeat(repeat.release()); 6764 } 6765 6766 if (!context.canAdvance() && context.requireWidth()) { 6767 RefPtr<CSSPrimitiveValue> borderSlice; 6768 if (parseBorderImageWidth(borderSlice)) 6769 context.commitBorderWidth(borderSlice.release()); 6770 } 6771 6772 if (!context.canAdvance() && context.requireOutset()) { 6773 RefPtr<CSSPrimitiveValue> borderOutset; 6774 if (parseBorderImageOutset(borderOutset)) 6775 context.commitBorderOutset(borderOutset.release()); 6776 } 6777 6778 if (!context.canAdvance()) 6779 return false; 6780 6781 m_valueList->next(); 6782 } 6783 6784 if (context.allowCommit()) { 6785 if (propId == CSSPropertyBorderImage) 6786 context.commitBorderImage(this, important); 6787 else 6788 // Need to fully commit as a single value. 6789 result = context.commitWebKitBorderImage(); 6790 return true; 6791 } 6792 6793 return false; 6794} 6795 6796static bool isBorderImageRepeatKeyword(int id) 6797{ 6798 return id == CSSValueStretch || id == CSSValueRepeat || id == CSSValueSpace || id == CSSValueRound; 6799} 6800 6801bool CSSParser::parseBorderImageRepeat(RefPtr<CSSValue>& result) 6802{ 6803 RefPtr<CSSPrimitiveValue> firstValue; 6804 RefPtr<CSSPrimitiveValue> secondValue; 6805 CSSParserValue* val = m_valueList->current(); 6806 if (!val) 6807 return false; 6808 if (isBorderImageRepeatKeyword(val->id)) 6809 firstValue = cssValuePool().createIdentifierValue(val->id); 6810 else 6811 return false; 6812 6813 val = m_valueList->next(); 6814 if (val) { 6815 if (isBorderImageRepeatKeyword(val->id)) 6816 secondValue = cssValuePool().createIdentifierValue(val->id); 6817 else if (!inShorthand()) { 6818 // If we're not parsing a shorthand then we are invalid. 6819 return false; 6820 } else { 6821 // We need to rewind the value list, so that when its advanced we'll 6822 // end up back at this value. 6823 m_valueList->previous(); 6824 secondValue = firstValue; 6825 } 6826 } else 6827 secondValue = firstValue; 6828 6829 result = createPrimitiveValuePair(firstValue, secondValue); 6830 return true; 6831} 6832 6833class BorderImageSliceParseContext { 6834public: 6835 BorderImageSliceParseContext(CSSParser* parser) 6836 : m_parser(parser) 6837 , m_allowNumber(true) 6838 , m_allowFill(true) 6839 , m_allowFinalCommit(false) 6840 , m_fill(false) 6841 { } 6842 6843 bool allowNumber() const { return m_allowNumber; } 6844 bool allowFill() const { return m_allowFill; } 6845 bool allowFinalCommit() const { return m_allowFinalCommit; } 6846 CSSPrimitiveValue* top() const { return m_top.get(); } 6847 6848 void commitNumber(CSSParserValue* v) 6849 { 6850 RefPtr<CSSPrimitiveValue> val = m_parser->createPrimitiveNumericValue(v); 6851 if (!m_top) 6852 m_top = val; 6853 else if (!m_right) 6854 m_right = val; 6855 else if (!m_bottom) 6856 m_bottom = val; 6857 else { 6858 ASSERT(!m_left); 6859 m_left = val; 6860 } 6861 6862 m_allowNumber = !m_left; 6863 m_allowFinalCommit = true; 6864 } 6865 6866 void commitFill() { m_fill = true; m_allowFill = false; m_allowNumber = !m_top; } 6867 6868 PassRefPtr<CSSBorderImageSliceValue> commitBorderImageSlice() 6869 { 6870 // We need to clone and repeat values for any omissions. 6871 ASSERT(m_top); 6872 if (!m_right) { 6873 m_right = m_top; 6874 m_bottom = m_top; 6875 m_left = m_top; 6876 } 6877 if (!m_bottom) { 6878 m_bottom = m_top; 6879 m_left = m_right; 6880 } 6881 if (!m_left) 6882 m_left = m_right; 6883 6884 // Now build a rect value to hold all four of our primitive values. 6885 RefPtr<Quad> quad = Quad::create(); 6886 quad->setTop(m_top); 6887 quad->setRight(m_right); 6888 quad->setBottom(m_bottom); 6889 quad->setLeft(m_left); 6890 6891 // Make our new border image value now. 6892 return CSSBorderImageSliceValue::create(cssValuePool().createValue(quad.release()), m_fill); 6893 } 6894 6895private: 6896 CSSParser* m_parser; 6897 6898 bool m_allowNumber; 6899 bool m_allowFill; 6900 bool m_allowFinalCommit; 6901 6902 RefPtr<CSSPrimitiveValue> m_top; 6903 RefPtr<CSSPrimitiveValue> m_right; 6904 RefPtr<CSSPrimitiveValue> m_bottom; 6905 RefPtr<CSSPrimitiveValue> m_left; 6906 6907 bool m_fill; 6908}; 6909 6910bool CSSParser::parseBorderImageSlice(CSSPropertyID propId, RefPtr<CSSBorderImageSliceValue>& result) 6911{ 6912 BorderImageSliceParseContext context(this); 6913 CSSParserValue* val; 6914 while ((val = m_valueList->current())) { 6915 // FIXME calc() http://webkit.org/b/16662 : calc is parsed but values are not created yet. 6916 if (context.allowNumber() && !isCalculation(val) && validUnit(val, FInteger | FNonNeg | FPercent, CSSStrictMode)) { 6917 context.commitNumber(val); 6918 } else if (context.allowFill() && val->id == CSSValueFill) 6919 context.commitFill(); 6920 else if (!inShorthand()) { 6921 // If we're not parsing a shorthand then we are invalid. 6922 return false; 6923 } else { 6924 if (context.allowFinalCommit()) { 6925 // We're going to successfully parse, but we don't want to consume this token. 6926 m_valueList->previous(); 6927 } 6928 break; 6929 } 6930 m_valueList->next(); 6931 } 6932 6933 if (context.allowFinalCommit()) { 6934 // FIXME: For backwards compatibility, -webkit-border-image, -webkit-mask-box-image and -webkit-box-reflect have to do a fill by default. 6935 // FIXME: What do we do with -webkit-box-reflect and -webkit-mask-box-image? Probably just have to leave them filling... 6936 if (propId == CSSPropertyWebkitBorderImage || propId == CSSPropertyWebkitMaskBoxImage || propId == CSSPropertyWebkitBoxReflect) 6937 context.commitFill(); 6938 6939 // Need to fully commit as a single value. 6940 result = context.commitBorderImageSlice(); 6941 return true; 6942 } 6943 6944 return false; 6945} 6946 6947class BorderImageQuadParseContext { 6948public: 6949 BorderImageQuadParseContext(CSSParser* parser) 6950 : m_parser(parser) 6951 , m_allowNumber(true) 6952 , m_allowFinalCommit(false) 6953 { } 6954 6955 bool allowNumber() const { return m_allowNumber; } 6956 bool allowFinalCommit() const { return m_allowFinalCommit; } 6957 CSSPrimitiveValue* top() const { return m_top.get(); } 6958 6959 void commitNumber(CSSParserValue* v) 6960 { 6961 RefPtr<CSSPrimitiveValue> val; 6962 if (v->id == CSSValueAuto) 6963 val = cssValuePool().createIdentifierValue(v->id); 6964 else 6965 val = m_parser->createPrimitiveNumericValue(v); 6966 6967 if (!m_top) 6968 m_top = val; 6969 else if (!m_right) 6970 m_right = val; 6971 else if (!m_bottom) 6972 m_bottom = val; 6973 else { 6974 ASSERT(!m_left); 6975 m_left = val; 6976 } 6977 6978 m_allowNumber = !m_left; 6979 m_allowFinalCommit = true; 6980 } 6981 6982 void setAllowFinalCommit() { m_allowFinalCommit = true; } 6983 void setTop(PassRefPtr<CSSPrimitiveValue> val) { m_top = val; } 6984 6985 PassRefPtr<CSSPrimitiveValue> commitBorderImageQuad() 6986 { 6987 // We need to clone and repeat values for any omissions. 6988 ASSERT(m_top); 6989 if (!m_right) { 6990 m_right = m_top; 6991 m_bottom = m_top; 6992 m_left = m_top; 6993 } 6994 if (!m_bottom) { 6995 m_bottom = m_top; 6996 m_left = m_right; 6997 } 6998 if (!m_left) 6999 m_left = m_right; 7000 7001 // Now build a quad value to hold all four of our primitive values. 7002 RefPtr<Quad> quad = Quad::create(); 7003 quad->setTop(m_top); 7004 quad->setRight(m_right); 7005 quad->setBottom(m_bottom); 7006 quad->setLeft(m_left); 7007 7008 // Make our new value now. 7009 return cssValuePool().createValue(quad.release()); 7010 } 7011 7012private: 7013 CSSParser* m_parser; 7014 7015 bool m_allowNumber; 7016 bool m_allowFinalCommit; 7017 7018 RefPtr<CSSPrimitiveValue> m_top; 7019 RefPtr<CSSPrimitiveValue> m_right; 7020 RefPtr<CSSPrimitiveValue> m_bottom; 7021 RefPtr<CSSPrimitiveValue> m_left; 7022}; 7023 7024bool CSSParser::parseBorderImageQuad(Units validUnits, RefPtr<CSSPrimitiveValue>& result) 7025{ 7026 BorderImageQuadParseContext context(this); 7027 CSSParserValue* val; 7028 while ((val = m_valueList->current())) { 7029 if (context.allowNumber() && (validUnit(val, validUnits, CSSStrictMode) || val->id == CSSValueAuto)) { 7030 context.commitNumber(val); 7031 } else if (!inShorthand()) { 7032 // If we're not parsing a shorthand then we are invalid. 7033 return false; 7034 } else { 7035 if (context.allowFinalCommit()) 7036 m_valueList->previous(); // The shorthand loop will advance back to this point. 7037 break; 7038 } 7039 m_valueList->next(); 7040 } 7041 7042 if (context.allowFinalCommit()) { 7043 // Need to fully commit as a single value. 7044 result = context.commitBorderImageQuad(); 7045 return true; 7046 } 7047 return false; 7048} 7049 7050bool CSSParser::parseBorderImageWidth(RefPtr<CSSPrimitiveValue>& result) 7051{ 7052 return parseBorderImageQuad(FLength | FInteger | FNonNeg | FPercent, result); 7053} 7054 7055bool CSSParser::parseBorderImageOutset(RefPtr<CSSPrimitiveValue>& result) 7056{ 7057 return parseBorderImageQuad(FLength | FInteger | FNonNeg, result); 7058} 7059 7060static void completeBorderRadii(RefPtr<CSSPrimitiveValue> radii[4]) 7061{ 7062 if (radii[3]) 7063 return; 7064 if (!radii[2]) { 7065 if (!radii[1]) 7066 radii[1] = radii[0]; 7067 radii[2] = radii[0]; 7068 } 7069 radii[3] = radii[1]; 7070} 7071 7072bool CSSParser::parseBorderRadius(CSSPropertyID propId, bool important) 7073{ 7074 unsigned num = m_valueList->size(); 7075 if (num > 9) 7076 return false; 7077 7078 ShorthandScope scope(this, propId); 7079 RefPtr<CSSPrimitiveValue> radii[2][4]; 7080 7081 unsigned indexAfterSlash = 0; 7082 for (unsigned i = 0; i < num; ++i) { 7083 CSSParserValue* value = m_valueList->valueAt(i); 7084 if (value->unit == CSSParserValue::Operator) { 7085 if (value->iValue != '/') 7086 return false; 7087 7088 if (!i || indexAfterSlash || i + 1 == num || num > i + 5) 7089 return false; 7090 7091 indexAfterSlash = i + 1; 7092 completeBorderRadii(radii[0]); 7093 continue; 7094 } 7095 7096 if (i - indexAfterSlash >= 4) 7097 return false; 7098 7099 if (!validUnit(value, FLength | FPercent | FNonNeg)) 7100 return false; 7101 7102 RefPtr<CSSPrimitiveValue> radius = createPrimitiveNumericValue(value); 7103 7104 if (!indexAfterSlash) { 7105 radii[0][i] = radius; 7106 7107 // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2; 7108 if (num == 2 && propId == CSSPropertyWebkitBorderRadius) { 7109 indexAfterSlash = 1; 7110 completeBorderRadii(radii[0]); 7111 } 7112 } else 7113 radii[1][i - indexAfterSlash] = radius.release(); 7114 } 7115 7116 if (!indexAfterSlash) { 7117 completeBorderRadii(radii[0]); 7118 for (unsigned i = 0; i < 4; ++i) 7119 radii[1][i] = radii[0][i]; 7120 } else 7121 completeBorderRadii(radii[1]); 7122 7123 ImplicitScope implicitScope(this, PropertyImplicit); 7124 addProperty(CSSPropertyBorderTopLeftRadius, createPrimitiveValuePair(radii[0][0].release(), radii[1][0].release()), important); 7125 addProperty(CSSPropertyBorderTopRightRadius, createPrimitiveValuePair(radii[0][1].release(), radii[1][1].release()), important); 7126 addProperty(CSSPropertyBorderBottomRightRadius, createPrimitiveValuePair(radii[0][2].release(), radii[1][2].release()), important); 7127 addProperty(CSSPropertyBorderBottomLeftRadius, createPrimitiveValuePair(radii[0][3].release(), radii[1][3].release()), important); 7128 return true; 7129} 7130 7131bool CSSParser::parseAspectRatio(bool important) 7132{ 7133 unsigned num = m_valueList->size(); 7134 if (num == 1 && m_valueList->valueAt(0)->id == CSSValueNone) { 7135 addProperty(CSSPropertyWebkitAspectRatio, cssValuePool().createIdentifierValue(CSSValueNone), important); 7136 return true; 7137 } 7138 7139 if (num != 3) 7140 return false; 7141 7142 CSSParserValue* lvalue = m_valueList->valueAt(0); 7143 CSSParserValue* op = m_valueList->valueAt(1); 7144 CSSParserValue* rvalue = m_valueList->valueAt(2); 7145 7146 if (!isForwardSlashOperator(op)) 7147 return false; 7148 7149 if (!validUnit(lvalue, FNumber | FNonNeg) || !validUnit(rvalue, FNumber | FNonNeg)) 7150 return false; 7151 7152 if (!lvalue->fValue || !rvalue->fValue) 7153 return false; 7154 7155 addProperty(CSSPropertyWebkitAspectRatio, CSSAspectRatioValue::create(narrowPrecisionToFloat(lvalue->fValue), narrowPrecisionToFloat(rvalue->fValue)), important); 7156 7157 return true; 7158} 7159 7160bool CSSParser::parseCounter(CSSPropertyID propId, int defaultValue, bool important) 7161{ 7162 enum { ID, VAL } state = ID; 7163 7164 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 7165 RefPtr<CSSPrimitiveValue> counterName; 7166 7167 while (true) { 7168 CSSParserValue* val = m_valueList->current(); 7169 switch (state) { 7170 case ID: 7171 if (val && val->unit == CSSPrimitiveValue::CSS_IDENT) { 7172 counterName = createPrimitiveStringValue(val); 7173 state = VAL; 7174 m_valueList->next(); 7175 continue; 7176 } 7177 break; 7178 case VAL: { 7179 int i = defaultValue; 7180 if (val && val->unit == CSSPrimitiveValue::CSS_NUMBER) { 7181 i = clampToInteger(val->fValue); 7182 m_valueList->next(); 7183 } 7184 7185 list->append(createPrimitiveValuePair(counterName.release(), 7186 cssValuePool().createValue(i, CSSPrimitiveValue::CSS_NUMBER))); 7187 state = ID; 7188 continue; 7189 } 7190 } 7191 break; 7192 } 7193 7194 if (list->length() > 0) { 7195 addProperty(propId, list.release(), important); 7196 return true; 7197 } 7198 7199 return false; 7200} 7201 7202// This should go away once we drop support for -webkit-gradient 7203static PassRefPtr<CSSPrimitiveValue> parseDeprecatedGradientPoint(CSSParserValue* a, bool horizontal) 7204{ 7205 RefPtr<CSSPrimitiveValue> result; 7206 if (a->unit == CSSPrimitiveValue::CSS_IDENT) { 7207 if ((equalIgnoringCase(a, "left") && horizontal) 7208 || (equalIgnoringCase(a, "top") && !horizontal)) 7209 result = cssValuePool().createValue(0., CSSPrimitiveValue::CSS_PERCENTAGE); 7210 else if ((equalIgnoringCase(a, "right") && horizontal) 7211 || (equalIgnoringCase(a, "bottom") && !horizontal)) 7212 result = cssValuePool().createValue(100., CSSPrimitiveValue::CSS_PERCENTAGE); 7213 else if (equalIgnoringCase(a, "center")) 7214 result = cssValuePool().createValue(50., CSSPrimitiveValue::CSS_PERCENTAGE); 7215 } else if (a->unit == CSSPrimitiveValue::CSS_NUMBER || a->unit == CSSPrimitiveValue::CSS_PERCENTAGE) 7216 result = cssValuePool().createValue(a->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(a->unit)); 7217 return result; 7218} 7219 7220static bool parseDeprecatedGradientColorStop(CSSParser* p, CSSParserValue* a, CSSGradientColorStop& stop) 7221{ 7222 if (a->unit != CSSParserValue::Function) 7223 return false; 7224 7225 if (!equalIgnoringCase(a->function->name, "from(") && 7226 !equalIgnoringCase(a->function->name, "to(") && 7227 !equalIgnoringCase(a->function->name, "color-stop(")) 7228 return false; 7229 7230 CSSParserValueList* args = a->function->args.get(); 7231 if (!args) 7232 return false; 7233 7234 if (equalIgnoringCase(a->function->name, "from(") 7235 || equalIgnoringCase(a->function->name, "to(")) { 7236 // The "from" and "to" stops expect 1 argument. 7237 if (args->size() != 1) 7238 return false; 7239 7240 if (equalIgnoringCase(a->function->name, "from(")) 7241 stop.m_position = cssValuePool().createValue(0, CSSPrimitiveValue::CSS_NUMBER); 7242 else 7243 stop.m_position = cssValuePool().createValue(1, CSSPrimitiveValue::CSS_NUMBER); 7244 7245 int id = args->current()->id; 7246 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu) 7247 stop.m_color = cssValuePool().createIdentifierValue(id); 7248 else 7249 stop.m_color = p->parseColor(args->current()); 7250 if (!stop.m_color) 7251 return false; 7252 } 7253 7254 // The "color-stop" function expects 3 arguments. 7255 if (equalIgnoringCase(a->function->name, "color-stop(")) { 7256 if (args->size() != 3) 7257 return false; 7258 7259 CSSParserValue* stopArg = args->current(); 7260 if (stopArg->unit == CSSPrimitiveValue::CSS_PERCENTAGE) 7261 stop.m_position = cssValuePool().createValue(stopArg->fValue / 100, CSSPrimitiveValue::CSS_NUMBER); 7262 else if (stopArg->unit == CSSPrimitiveValue::CSS_NUMBER) 7263 stop.m_position = cssValuePool().createValue(stopArg->fValue, CSSPrimitiveValue::CSS_NUMBER); 7264 else 7265 return false; 7266 7267 stopArg = args->next(); 7268 if (stopArg->unit != CSSParserValue::Operator || stopArg->iValue != ',') 7269 return false; 7270 7271 stopArg = args->next(); 7272 int id = stopArg->id; 7273 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu) 7274 stop.m_color = cssValuePool().createIdentifierValue(id); 7275 else 7276 stop.m_color = p->parseColor(stopArg); 7277 if (!stop.m_color) 7278 return false; 7279 } 7280 7281 return true; 7282} 7283 7284bool CSSParser::parseDeprecatedGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient) 7285{ 7286 // Walk the arguments. 7287 CSSParserValueList* args = valueList->current()->function->args.get(); 7288 if (!args || args->size() == 0) 7289 return false; 7290 7291 // The first argument is the gradient type. It is an identifier. 7292 CSSGradientType gradientType; 7293 CSSParserValue* a = args->current(); 7294 if (!a || a->unit != CSSPrimitiveValue::CSS_IDENT) 7295 return false; 7296 if (equalIgnoringCase(a, "linear")) 7297 gradientType = CSSDeprecatedLinearGradient; 7298 else if (equalIgnoringCase(a, "radial")) 7299 gradientType = CSSDeprecatedRadialGradient; 7300 else 7301 return false; 7302 7303 RefPtr<CSSGradientValue> result; 7304 switch (gradientType) { 7305 case CSSDeprecatedLinearGradient: 7306 result = CSSLinearGradientValue::create(NonRepeating, gradientType); 7307 break; 7308 case CSSDeprecatedRadialGradient: 7309 result = CSSRadialGradientValue::create(NonRepeating, gradientType); 7310 break; 7311 default: 7312 // The rest of the gradient types shouldn't appear here. 7313 ASSERT_NOT_REACHED(); 7314 } 7315 7316 // Comma. 7317 a = args->next(); 7318 if (!isComma(a)) 7319 return false; 7320 7321 // Next comes the starting point for the gradient as an x y pair. There is no 7322 // comma between the x and the y values. 7323 // First X. It can be left, right, number or percent. 7324 a = args->next(); 7325 if (!a) 7326 return false; 7327 RefPtr<CSSPrimitiveValue> point = parseDeprecatedGradientPoint(a, true); 7328 if (!point) 7329 return false; 7330 result->setFirstX(point.release()); 7331 7332 // First Y. It can be top, bottom, number or percent. 7333 a = args->next(); 7334 if (!a) 7335 return false; 7336 point = parseDeprecatedGradientPoint(a, false); 7337 if (!point) 7338 return false; 7339 result->setFirstY(point.release()); 7340 7341 // Comma after the first point. 7342 a = args->next(); 7343 if (!isComma(a)) 7344 return false; 7345 7346 // For radial gradients only, we now expect a numeric radius. 7347 if (gradientType == CSSDeprecatedRadialGradient) { 7348 a = args->next(); 7349 if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER) 7350 return false; 7351 static_cast<CSSRadialGradientValue*>(result.get())->setFirstRadius(createPrimitiveNumericValue(a)); 7352 7353 // Comma after the first radius. 7354 a = args->next(); 7355 if (!isComma(a)) 7356 return false; 7357 } 7358 7359 // Next is the ending point for the gradient as an x, y pair. 7360 // Second X. It can be left, right, number or percent. 7361 a = args->next(); 7362 if (!a) 7363 return false; 7364 point = parseDeprecatedGradientPoint(a, true); 7365 if (!point) 7366 return false; 7367 result->setSecondX(point.release()); 7368 7369 // Second Y. It can be top, bottom, number or percent. 7370 a = args->next(); 7371 if (!a) 7372 return false; 7373 point = parseDeprecatedGradientPoint(a, false); 7374 if (!point) 7375 return false; 7376 result->setSecondY(point.release()); 7377 7378 // For radial gradients only, we now expect the second radius. 7379 if (gradientType == CSSDeprecatedRadialGradient) { 7380 // Comma after the second point. 7381 a = args->next(); 7382 if (!isComma(a)) 7383 return false; 7384 7385 a = args->next(); 7386 if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER) 7387 return false; 7388 static_cast<CSSRadialGradientValue*>(result.get())->setSecondRadius(createPrimitiveNumericValue(a)); 7389 } 7390 7391 // We now will accept any number of stops (0 or more). 7392 a = args->next(); 7393 while (a) { 7394 // Look for the comma before the next stop. 7395 if (!isComma(a)) 7396 return false; 7397 7398 // Now examine the stop itself. 7399 a = args->next(); 7400 if (!a) 7401 return false; 7402 7403 // The function name needs to be one of "from", "to", or "color-stop." 7404 CSSGradientColorStop stop; 7405 if (!parseDeprecatedGradientColorStop(this, a, stop)) 7406 return false; 7407 result->addStop(stop); 7408 7409 // Advance 7410 a = args->next(); 7411 } 7412 7413 gradient = result.release(); 7414 return true; 7415} 7416 7417static PassRefPtr<CSSPrimitiveValue> valueFromSideKeyword(CSSParserValue* a, bool& isHorizontal) 7418{ 7419 if (a->unit != CSSPrimitiveValue::CSS_IDENT) 7420 return 0; 7421 7422 switch (a->id) { 7423 case CSSValueLeft: 7424 case CSSValueRight: 7425 isHorizontal = true; 7426 break; 7427 case CSSValueTop: 7428 case CSSValueBottom: 7429 isHorizontal = false; 7430 break; 7431 default: 7432 return 0; 7433 } 7434 return cssValuePool().createIdentifierValue(a->id); 7435} 7436 7437static PassRefPtr<CSSPrimitiveValue> parseGradientColorOrKeyword(CSSParser* p, CSSParserValue* value) 7438{ 7439 int id = value->id; 7440 if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || id == CSSValueCurrentcolor) 7441 return cssValuePool().createIdentifierValue(id); 7442 7443 return p->parseColor(value); 7444} 7445 7446bool CSSParser::parseDeprecatedLinearGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 7447{ 7448 RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, CSSPrefixedLinearGradient); 7449 7450 // Walk the arguments. 7451 CSSParserValueList* args = valueList->current()->function->args.get(); 7452 if (!args || !args->size()) 7453 return false; 7454 7455 CSSParserValue* a = args->current(); 7456 if (!a) 7457 return false; 7458 7459 bool expectComma = false; 7460 // Look for angle. 7461 if (validUnit(a, FAngle, CSSStrictMode)) { 7462 result->setAngle(createPrimitiveNumericValue(a)); 7463 7464 args->next(); 7465 expectComma = true; 7466 } else { 7467 // Look one or two optional keywords that indicate a side or corner. 7468 RefPtr<CSSPrimitiveValue> startX, startY; 7469 7470 RefPtr<CSSPrimitiveValue> location; 7471 bool isHorizontal = false; 7472 if ((location = valueFromSideKeyword(a, isHorizontal))) { 7473 if (isHorizontal) 7474 startX = location; 7475 else 7476 startY = location; 7477 7478 if ((a = args->next())) { 7479 if ((location = valueFromSideKeyword(a, isHorizontal))) { 7480 if (isHorizontal) { 7481 if (startX) 7482 return false; 7483 startX = location; 7484 } else { 7485 if (startY) 7486 return false; 7487 startY = location; 7488 } 7489 7490 args->next(); 7491 } 7492 } 7493 7494 expectComma = true; 7495 } 7496 7497 if (!startX && !startY) 7498 startY = cssValuePool().createIdentifierValue(CSSValueTop); 7499 7500 result->setFirstX(startX.release()); 7501 result->setFirstY(startY.release()); 7502 } 7503 7504 if (!parseGradientColorStops(args, result.get(), expectComma)) 7505 return false; 7506 7507 if (!result->stopCount()) 7508 return false; 7509 7510 gradient = result.release(); 7511 return true; 7512} 7513 7514bool CSSParser::parseDeprecatedRadialGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 7515{ 7516 RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSPrefixedRadialGradient); 7517 7518 // Walk the arguments. 7519 CSSParserValueList* args = valueList->current()->function->args.get(); 7520 if (!args || !args->size()) 7521 return false; 7522 7523 CSSParserValue* a = args->current(); 7524 if (!a) 7525 return false; 7526 7527 bool expectComma = false; 7528 7529 // Optional background-position 7530 RefPtr<CSSValue> centerX; 7531 RefPtr<CSSValue> centerY; 7532 // parse2ValuesFillPosition advances the args next pointer. 7533 parse2ValuesFillPosition(args, centerX, centerY); 7534 a = args->current(); 7535 if (!a) 7536 return false; 7537 7538 if (centerX || centerY) { 7539 // Comma 7540 if (!isComma(a)) 7541 return false; 7542 7543 a = args->next(); 7544 if (!a) 7545 return false; 7546 } 7547 7548 ASSERT(!centerX || centerX->isPrimitiveValue()); 7549 ASSERT(!centerY || centerY->isPrimitiveValue()); 7550 7551 result->setFirstX(static_cast<CSSPrimitiveValue*>(centerX.get())); 7552 result->setSecondX(static_cast<CSSPrimitiveValue*>(centerX.get())); 7553 // CSS3 radial gradients always share the same start and end point. 7554 result->setFirstY(static_cast<CSSPrimitiveValue*>(centerY.get())); 7555 result->setSecondY(static_cast<CSSPrimitiveValue*>(centerY.get())); 7556 7557 RefPtr<CSSPrimitiveValue> shapeValue; 7558 RefPtr<CSSPrimitiveValue> sizeValue; 7559 7560 // Optional shape and/or size in any order. 7561 for (int i = 0; i < 2; ++i) { 7562 if (a->unit != CSSPrimitiveValue::CSS_IDENT) 7563 break; 7564 7565 bool foundValue = false; 7566 switch (a->id) { 7567 case CSSValueCircle: 7568 case CSSValueEllipse: 7569 shapeValue = cssValuePool().createIdentifierValue(a->id); 7570 foundValue = true; 7571 break; 7572 case CSSValueClosestSide: 7573 case CSSValueClosestCorner: 7574 case CSSValueFarthestSide: 7575 case CSSValueFarthestCorner: 7576 case CSSValueContain: 7577 case CSSValueCover: 7578 sizeValue = cssValuePool().createIdentifierValue(a->id); 7579 foundValue = true; 7580 break; 7581 } 7582 7583 if (foundValue) { 7584 a = args->next(); 7585 if (!a) 7586 return false; 7587 7588 expectComma = true; 7589 } 7590 } 7591 7592 result->setShape(shapeValue); 7593 result->setSizingBehavior(sizeValue); 7594 7595 // Or, two lengths or percentages 7596 RefPtr<CSSPrimitiveValue> horizontalSize; 7597 RefPtr<CSSPrimitiveValue> verticalSize; 7598 7599 if (!shapeValue && !sizeValue) { 7600 if (validUnit(a, FLength | FPercent)) { 7601 horizontalSize = createPrimitiveNumericValue(a); 7602 a = args->next(); 7603 if (!a) 7604 return false; 7605 7606 expectComma = true; 7607 } 7608 7609 if (validUnit(a, FLength | FPercent)) { 7610 verticalSize = createPrimitiveNumericValue(a); 7611 7612 a = args->next(); 7613 if (!a) 7614 return false; 7615 expectComma = true; 7616 } 7617 } 7618 7619 // Must have neither or both. 7620 if (!horizontalSize != !verticalSize) 7621 return false; 7622 7623 result->setEndHorizontalSize(horizontalSize); 7624 result->setEndVerticalSize(verticalSize); 7625 7626 if (!parseGradientColorStops(args, result.get(), expectComma)) 7627 return false; 7628 7629 gradient = result.release(); 7630 return true; 7631} 7632 7633bool CSSParser::parseLinearGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 7634{ 7635 RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, CSSLinearGradient); 7636 7637 CSSParserValueList* args = valueList->current()->function->args.get(); 7638 if (!args || !args->size()) 7639 return false; 7640 7641 CSSParserValue* a = args->current(); 7642 if (!a) 7643 return false; 7644 7645 bool expectComma = false; 7646 // Look for angle. 7647 if (validUnit(a, FAngle, CSSStrictMode)) { 7648 result->setAngle(createPrimitiveNumericValue(a)); 7649 7650 args->next(); 7651 expectComma = true; 7652 } else if (a->unit == CSSPrimitiveValue::CSS_IDENT && equalIgnoringCase(a, "to")) { 7653 // to [ [left | right] || [top | bottom] ] 7654 a = args->next(); 7655 if (!a) 7656 return false; 7657 7658 RefPtr<CSSPrimitiveValue> endX, endY; 7659 RefPtr<CSSPrimitiveValue> location; 7660 bool isHorizontal = false; 7661 7662 location = valueFromSideKeyword(a, isHorizontal); 7663 if (!location) 7664 return false; 7665 7666 if (isHorizontal) 7667 endX = location; 7668 else 7669 endY = location; 7670 7671 a = args->next(); 7672 if (!a) 7673 return false; 7674 7675 location = valueFromSideKeyword(a, isHorizontal); 7676 if (location) { 7677 if (isHorizontal) { 7678 if (endX) 7679 return false; 7680 endX = location; 7681 } else { 7682 if (endY) 7683 return false; 7684 endY = location; 7685 } 7686 7687 args->next(); 7688 } 7689 7690 expectComma = true; 7691 result->setFirstX(endX.release()); 7692 result->setFirstY(endY.release()); 7693 } 7694 7695 if (!parseGradientColorStops(args, result.get(), expectComma)) 7696 return false; 7697 7698 if (!result->stopCount()) 7699 return false; 7700 7701 gradient = result.release(); 7702 return true; 7703} 7704 7705bool CSSParser::parseRadialGradient(CSSParserValueList* valueList, RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) 7706{ 7707 RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient); 7708 7709 CSSParserValueList* args = valueList->current()->function->args.get(); 7710 if (!args || !args->size()) 7711 return false; 7712 7713 CSSParserValue* a = args->current(); 7714 if (!a) 7715 return false; 7716 7717 bool expectComma = false; 7718 7719 RefPtr<CSSPrimitiveValue> shapeValue; 7720 RefPtr<CSSPrimitiveValue> sizeValue; 7721 RefPtr<CSSPrimitiveValue> horizontalSize; 7722 RefPtr<CSSPrimitiveValue> verticalSize; 7723 7724 // First part of grammar, the size/shape clause: 7725 // [ circle || <length> ] | 7726 // [ ellipse || [ <length> | <percentage> ]{2} ] | 7727 // [ [ circle | ellipse] || <size-keyword> ] 7728 for (int i = 0; i < 3; ++i) { 7729 if (a->unit == CSSPrimitiveValue::CSS_IDENT) { 7730 bool badIdent = false; 7731 switch (a->id) { 7732 case CSSValueCircle: 7733 case CSSValueEllipse: 7734 if (shapeValue) 7735 return false; 7736 shapeValue = cssValuePool().createIdentifierValue(a->id); 7737 break; 7738 case CSSValueClosestSide: 7739 case CSSValueClosestCorner: 7740 case CSSValueFarthestSide: 7741 case CSSValueFarthestCorner: 7742 if (sizeValue || horizontalSize) 7743 return false; 7744 sizeValue = cssValuePool().createIdentifierValue(a->id); 7745 break; 7746 default: 7747 badIdent = true; 7748 } 7749 7750 if (badIdent) 7751 break; 7752 7753 a = args->next(); 7754 if (!a) 7755 return false; 7756 } else if (validUnit(a, FLength | FPercent)) { 7757 7758 if (sizeValue || horizontalSize) 7759 return false; 7760 horizontalSize = createPrimitiveNumericValue(a); 7761 7762 a = args->next(); 7763 if (!a) 7764 return false; 7765 7766 if (validUnit(a, FLength | FPercent)) { 7767 verticalSize = createPrimitiveNumericValue(a); 7768 ++i; 7769 a = args->next(); 7770 if (!a) 7771 return false; 7772 } 7773 } else 7774 break; 7775 } 7776 7777 // You can specify size as a keyword or a length/percentage, not both. 7778 if (sizeValue && horizontalSize) 7779 return false; 7780 // Circles must have 0 or 1 lengths. 7781 if (shapeValue && shapeValue->getIdent() == CSSValueCircle && verticalSize) 7782 return false; 7783 // Ellipses must have 0 or 2 length/percentages. 7784 if (shapeValue && shapeValue->getIdent() == CSSValueEllipse && horizontalSize && !verticalSize) 7785 return false; 7786 // If there's only one size, it must be a length. 7787 if (!verticalSize && horizontalSize && horizontalSize->isPercentage()) 7788 return false; 7789 7790 result->setShape(shapeValue); 7791 result->setSizingBehavior(sizeValue); 7792 result->setEndHorizontalSize(horizontalSize); 7793 result->setEndVerticalSize(verticalSize); 7794 7795 // Second part of grammar, the center-position clause: 7796 // at <position> 7797 RefPtr<CSSValue> centerX; 7798 RefPtr<CSSValue> centerY; 7799 if (a->unit == CSSPrimitiveValue::CSS_IDENT && equalIgnoringCase(a, "at")) { 7800 a = args->next(); 7801 if (!a) 7802 return false; 7803 7804 parseFillPosition(args, centerX, centerY); 7805 ASSERT(centerX->isPrimitiveValue()); 7806 ASSERT(centerY->isPrimitiveValue()); 7807 if (!(centerX && centerY)) 7808 return false; 7809 7810 a = args->current(); 7811 if (!a) 7812 return false; 7813 result->setFirstX(static_cast<CSSPrimitiveValue*>(centerX.get())); 7814 result->setFirstY(static_cast<CSSPrimitiveValue*>(centerY.get())); 7815 // Right now, CSS radial gradients have the same start and end centers. 7816 result->setSecondX(static_cast<CSSPrimitiveValue*>(centerX.get())); 7817 result->setSecondY(static_cast<CSSPrimitiveValue*>(centerY.get())); 7818 } 7819 7820 if (shapeValue || sizeValue || horizontalSize || centerX || centerY) 7821 expectComma = true; 7822 7823 if (!parseGradientColorStops(args, result.get(), expectComma)) 7824 return false; 7825 7826 gradient = result.release(); 7827 return true; 7828} 7829 7830bool CSSParser::parseGradientColorStops(CSSParserValueList* valueList, CSSGradientValue* gradient, bool expectComma) 7831{ 7832 CSSParserValue* a = valueList->current(); 7833 7834 // Now look for color stops. 7835 while (a) { 7836 // Look for the comma before the next stop. 7837 if (expectComma) { 7838 if (!isComma(a)) 7839 return false; 7840 7841 a = valueList->next(); 7842 if (!a) 7843 return false; 7844 } 7845 7846 // <color-stop> = <color> [ <percentage> | <length> ]? 7847 CSSGradientColorStop stop; 7848 stop.m_color = parseGradientColorOrKeyword(this, a); 7849 if (!stop.m_color) 7850 return false; 7851 7852 a = valueList->next(); 7853 if (a) { 7854 if (validUnit(a, FLength | FPercent)) { 7855 stop.m_position = createPrimitiveNumericValue(a); 7856 a = valueList->next(); 7857 } 7858 } 7859 7860 gradient->addStop(stop); 7861 expectComma = true; 7862 } 7863 7864 // Must have 2 or more stops to be valid. 7865 return gradient->stopCount() >= 2; 7866} 7867 7868bool CSSParser::isGeneratedImageValue(CSSParserValue* val) const 7869{ 7870 if (val->unit != CSSParserValue::Function) 7871 return false; 7872 7873 return equalIgnoringCase(val->function->name, "-webkit-gradient(") 7874 || equalIgnoringCase(val->function->name, "-webkit-linear-gradient(") 7875 || equalIgnoringCase(val->function->name, "linear-gradient(") 7876 || equalIgnoringCase(val->function->name, "-webkit-repeating-linear-gradient(") 7877 || equalIgnoringCase(val->function->name, "repeating-linear-gradient(") 7878 || equalIgnoringCase(val->function->name, "-webkit-radial-gradient(") 7879 || equalIgnoringCase(val->function->name, "radial-gradient(") 7880 || equalIgnoringCase(val->function->name, "-webkit-repeating-radial-gradient(") 7881 || equalIgnoringCase(val->function->name, "repeating-radial-gradient(") 7882 || equalIgnoringCase(val->function->name, "-webkit-canvas(") 7883 || equalIgnoringCase(val->function->name, "-webkit-cross-fade("); 7884} 7885 7886bool CSSParser::parseGeneratedImage(CSSParserValueList* valueList, RefPtr<CSSValue>& value) 7887{ 7888 CSSParserValue* val = valueList->current(); 7889 7890 if (val->unit != CSSParserValue::Function) 7891 return false; 7892 7893 if (equalIgnoringCase(val->function->name, "-webkit-gradient(")) 7894 return parseDeprecatedGradient(valueList, value); 7895 7896 if (equalIgnoringCase(val->function->name, "-webkit-linear-gradient(")) 7897 return parseDeprecatedLinearGradient(valueList, value, NonRepeating); 7898 7899 if (equalIgnoringCase(val->function->name, "linear-gradient(")) 7900 return parseLinearGradient(valueList, value, NonRepeating); 7901 7902 if (equalIgnoringCase(val->function->name, "-webkit-repeating-linear-gradient(")) 7903 return parseDeprecatedLinearGradient(valueList, value, Repeating); 7904 7905 if (equalIgnoringCase(val->function->name, "repeating-linear-gradient(")) 7906 return parseLinearGradient(valueList, value, Repeating); 7907 7908 if (equalIgnoringCase(val->function->name, "-webkit-radial-gradient(")) 7909 return parseDeprecatedRadialGradient(valueList, value, NonRepeating); 7910 7911 if (equalIgnoringCase(val->function->name, "radial-gradient(")) 7912 return parseRadialGradient(valueList, value, NonRepeating); 7913 7914 if (equalIgnoringCase(val->function->name, "-webkit-repeating-radial-gradient(")) 7915 return parseDeprecatedRadialGradient(valueList, value, Repeating); 7916 7917 if (equalIgnoringCase(val->function->name, "repeating-radial-gradient(")) 7918 return parseRadialGradient(valueList, value, Repeating); 7919 7920 if (equalIgnoringCase(val->function->name, "-webkit-canvas(")) 7921 return parseCanvas(valueList, value); 7922 7923 if (equalIgnoringCase(val->function->name, "-webkit-cross-fade(")) 7924 return parseCrossfade(valueList, value); 7925 7926 return false; 7927} 7928 7929bool CSSParser::parseCrossfade(CSSParserValueList* valueList, RefPtr<CSSValue>& crossfade) 7930{ 7931 RefPtr<CSSCrossfadeValue> result; 7932 7933 // Walk the arguments. 7934 CSSParserValueList* args = valueList->current()->function->args.get(); 7935 if (!args || args->size() != 5) 7936 return false; 7937 CSSParserValue* a = args->current(); 7938 RefPtr<CSSValue> fromImageValue; 7939 RefPtr<CSSValue> toImageValue; 7940 7941 // The first argument is the "from" image. It is a fill image. 7942 if (!a || !parseFillImage(args, fromImageValue)) 7943 return false; 7944 a = args->next(); 7945 7946 // Skip a comma 7947 if (!isComma(a)) 7948 return false; 7949 a = args->next(); 7950 7951 // The second argument is the "to" image. It is a fill image. 7952 if (!a || !parseFillImage(args, toImageValue)) 7953 return false; 7954 a = args->next(); 7955 7956 // Skip a comma 7957 if (!isComma(a)) 7958 return false; 7959 a = args->next(); 7960 7961 // The third argument is the crossfade value. It is a percentage or a fractional number. 7962 RefPtr<CSSPrimitiveValue> percentage; 7963 if (!a) 7964 return false; 7965 7966 if (a->unit == CSSPrimitiveValue::CSS_PERCENTAGE) 7967 percentage = cssValuePool().createValue(clampTo<double>(a->fValue / 100, 0, 1), CSSPrimitiveValue::CSS_NUMBER); 7968 else if (a->unit == CSSPrimitiveValue::CSS_NUMBER) 7969 percentage = cssValuePool().createValue(clampTo<double>(a->fValue, 0, 1), CSSPrimitiveValue::CSS_NUMBER); 7970 else 7971 return false; 7972 7973 result = CSSCrossfadeValue::create(fromImageValue, toImageValue); 7974 result->setPercentage(percentage); 7975 7976 crossfade = result; 7977 7978 return true; 7979} 7980 7981bool CSSParser::parseCanvas(CSSParserValueList* valueList, RefPtr<CSSValue>& canvas) 7982{ 7983 // Walk the arguments. 7984 CSSParserValueList* args = valueList->current()->function->args.get(); 7985 if (!args || args->size() != 1) 7986 return false; 7987 7988 // The first argument is the canvas name. It is an identifier. 7989 CSSParserValue* value = args->current(); 7990 if (!value || value->unit != CSSPrimitiveValue::CSS_IDENT) 7991 return false; 7992 7993 canvas = CSSCanvasValue::create(value->string); 7994 return true; 7995} 7996 7997#if ENABLE(CSS_IMAGE_RESOLUTION) 7998PassRefPtr<CSSValue> CSSParser::parseImageResolution() 7999{ 8000 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 8001 bool haveResolution = false; 8002 bool haveFromImage = false; 8003 bool haveSnap = false; 8004 8005 CSSParserValue* value = m_valueList->current(); 8006 while (value) { 8007 if (!haveFromImage && value->id == CSSValueFromImage) { 8008 list->append(cssValuePool().createIdentifierValue(value->id)); 8009 haveFromImage = true; 8010 } else if (!haveSnap && value->id == CSSValueSnap) { 8011 list->append(cssValuePool().createIdentifierValue(value->id)); 8012 haveSnap = true; 8013 } else if (!haveResolution && validUnit(value, FResolution | FNonNeg) && value->fValue > 0) { 8014 list->append(createPrimitiveNumericValue(value)); 8015 haveResolution = true; 8016 } else 8017 return 0; 8018 value = m_valueList->next(); 8019 } 8020 if (!list->length()) 8021 return 0; 8022 if (!haveFromImage && !haveResolution) 8023 return 0; 8024 return list.release(); 8025} 8026#endif 8027 8028#if ENABLE(CSS_IMAGE_SET) 8029PassRefPtr<CSSValue> CSSParser::parseImageSet() 8030{ 8031 CSSParserValue* value = m_valueList->current(); 8032 ASSERT(value->unit == CSSParserValue::Function); 8033 8034 CSSParserValueList* functionArgs = value->function->args.get(); 8035 if (!functionArgs || !functionArgs->size() || !functionArgs->current()) 8036 return 0; 8037 8038 RefPtr<CSSImageSetValue> imageSet = CSSImageSetValue::create(); 8039 CSSParserValue* arg = functionArgs->current(); 8040 while (arg) { 8041 if (arg->unit != CSSPrimitiveValue::CSS_URI) 8042 return 0; 8043 8044 imageSet->append(CSSImageValue::create(completeURL(arg->string))); 8045 arg = functionArgs->next(); 8046 if (!arg || arg->unit != CSSPrimitiveValue::CSS_DIMENSION) 8047 return 0; 8048 8049 double imageScaleFactor = 0; 8050 const String& string = arg->string; 8051 unsigned length = string.length(); 8052 if (!length) 8053 return 0; 8054 if (string.is8Bit()) { 8055 const LChar* start = string.characters8(); 8056 parseDouble(start, start + length, 'x', imageScaleFactor); 8057 } else { 8058 const UChar* start = string.characters(); 8059 parseDouble(start, start + length, 'x', imageScaleFactor); 8060 } 8061 if (imageScaleFactor <= 0) 8062 return 0; 8063 imageSet->append(cssValuePool().createValue(imageScaleFactor, CSSPrimitiveValue::CSS_NUMBER)); 8064 8065 // If there are no more arguments, we're done. 8066 arg = functionArgs->next(); 8067 if (!arg) 8068 break; 8069 8070 // If there are more arguments, they should be after a comma. 8071 if (!isComma(arg)) 8072 return 0; 8073 8074 // Skip the comma and move on to the next argument. 8075 arg = functionArgs->next(); 8076 } 8077 8078 return imageSet.release(); 8079} 8080#endif 8081 8082class TransformOperationInfo { 8083public: 8084 TransformOperationInfo(const CSSParserString& name) 8085 : m_type(WebKitCSSTransformValue::UnknownTransformOperation) 8086 , m_argCount(1) 8087 , m_allowSingleArgument(false) 8088 , m_unit(CSSParser::FUnknown) 8089 { 8090 const UChar* characters; 8091 unsigned nameLength = name.length(); 8092 8093 const unsigned longestNameLength = 12; 8094 UChar characterBuffer[longestNameLength]; 8095 if (name.is8Bit()) { 8096 unsigned length = std::min(longestNameLength, nameLength); 8097 const LChar* characters8 = name.characters8(); 8098 for (unsigned i = 0; i < length; ++i) 8099 characterBuffer[i] = characters8[i]; 8100 characters = characterBuffer; 8101 } else 8102 characters = name.characters16(); 8103 8104 switch (nameLength) { 8105 case 5: 8106 // Valid name: skew(. 8107 if (((characters[0] == 's') || (characters[0] == 'S')) 8108 & ((characters[1] == 'k') || (characters[1] == 'K')) 8109 & ((characters[2] == 'e') || (characters[2] == 'E')) 8110 & ((characters[3] == 'w') || (characters[3] == 'W')) 8111 & (characters[4] == '(')) { 8112 m_unit = CSSParser::FAngle; 8113 m_type = WebKitCSSTransformValue::SkewTransformOperation; 8114 m_allowSingleArgument = true; 8115 m_argCount = 3; 8116 } 8117 break; 8118 case 6: 8119 // Valid names: skewx(, skewy(, scale(. 8120 if ((characters[1] == 'c') || (characters[1] == 'C')) { 8121 if (((characters[0] == 's') || (characters[0] == 'S')) 8122 & ((characters[2] == 'a') || (characters[2] == 'A')) 8123 & ((characters[3] == 'l') || (characters[3] == 'L')) 8124 & ((characters[4] == 'e') || (characters[4] == 'E')) 8125 & (characters[5] == '(')) { 8126 m_unit = CSSParser::FNumber; 8127 m_type = WebKitCSSTransformValue::ScaleTransformOperation; 8128 m_allowSingleArgument = true; 8129 m_argCount = 3; 8130 } 8131 } else if (((characters[0] == 's') || (characters[0] == 'S')) 8132 & ((characters[1] == 'k') || (characters[1] == 'K')) 8133 & ((characters[2] == 'e') || (characters[2] == 'E')) 8134 & ((characters[3] == 'w') || (characters[3] == 'W')) 8135 & (characters[5] == '(')) { 8136 if ((characters[4] == 'x') || (characters[4] == 'X')) { 8137 m_unit = CSSParser::FAngle; 8138 m_type = WebKitCSSTransformValue::SkewXTransformOperation; 8139 } else if ((characters[4] == 'y') || (characters[4] == 'Y')) { 8140 m_unit = CSSParser::FAngle; 8141 m_type = WebKitCSSTransformValue::SkewYTransformOperation; 8142 } 8143 } 8144 break; 8145 case 7: 8146 // Valid names: matrix(, rotate(, scalex(, scaley(, scalez(. 8147 if ((characters[0] == 'm') || (characters[0] == 'M')) { 8148 if (((characters[1] == 'a') || (characters[1] == 'A')) 8149 & ((characters[2] == 't') || (characters[2] == 'T')) 8150 & ((characters[3] == 'r') || (characters[3] == 'R')) 8151 & ((characters[4] == 'i') || (characters[4] == 'I')) 8152 & ((characters[5] == 'x') || (characters[5] == 'X')) 8153 & (characters[6] == '(')) { 8154 m_unit = CSSParser::FNumber; 8155 m_type = WebKitCSSTransformValue::MatrixTransformOperation; 8156 m_argCount = 11; 8157 } 8158 } else if ((characters[0] == 'r') || (characters[0] == 'R')) { 8159 if (((characters[1] == 'o') || (characters[1] == 'O')) 8160 & ((characters[2] == 't') || (characters[2] == 'T')) 8161 & ((characters[3] == 'a') || (characters[3] == 'A')) 8162 & ((characters[4] == 't') || (characters[4] == 'T')) 8163 & ((characters[5] == 'e') || (characters[5] == 'E')) 8164 & (characters[6] == '(')) { 8165 m_unit = CSSParser::FAngle; 8166 m_type = WebKitCSSTransformValue::RotateTransformOperation; 8167 } 8168 } else if (((characters[0] == 's') || (characters[0] == 'S')) 8169 & ((characters[1] == 'c') || (characters[1] == 'C')) 8170 & ((characters[2] == 'a') || (characters[2] == 'A')) 8171 & ((characters[3] == 'l') || (characters[3] == 'L')) 8172 & ((characters[4] == 'e') || (characters[4] == 'E')) 8173 & (characters[6] == '(')) { 8174 if ((characters[5] == 'x') || (characters[5] == 'X')) { 8175 m_unit = CSSParser::FNumber; 8176 m_type = WebKitCSSTransformValue::ScaleXTransformOperation; 8177 } else if ((characters[5] == 'y') || (characters[5] == 'Y')) { 8178 m_unit = CSSParser::FNumber; 8179 m_type = WebKitCSSTransformValue::ScaleYTransformOperation; 8180 } else if ((characters[5] == 'z') || (characters[5] == 'Z')) { 8181 m_unit = CSSParser::FNumber; 8182 m_type = WebKitCSSTransformValue::ScaleZTransformOperation; 8183 } 8184 } 8185 break; 8186 case 8: 8187 // Valid names: rotatex(, rotatey(, rotatez(, scale3d(. 8188 if ((characters[0] == 's') || (characters[0] == 'S')) { 8189 if (((characters[1] == 'c') || (characters[1] == 'C')) 8190 & ((characters[2] == 'a') || (characters[2] == 'A')) 8191 & ((characters[3] == 'l') || (characters[3] == 'L')) 8192 & ((characters[4] == 'e') || (characters[4] == 'E')) 8193 & (characters[5] == '3') 8194 & ((characters[6] == 'd') || (characters[6] == 'D')) 8195 & (characters[7] == '(')) { 8196 m_unit = CSSParser::FNumber; 8197 m_type = WebKitCSSTransformValue::Scale3DTransformOperation; 8198 m_argCount = 5; 8199 } 8200 } else if (((characters[0] == 'r') || (characters[0] == 'R')) 8201 & ((characters[1] == 'o') || (characters[1] == 'O')) 8202 & ((characters[2] == 't') || (characters[2] == 'T')) 8203 & ((characters[3] == 'a') || (characters[3] == 'A')) 8204 & ((characters[4] == 't') || (characters[4] == 'T')) 8205 & ((characters[5] == 'e') || (characters[5] == 'E')) 8206 & (characters[7] == '(')) { 8207 if ((characters[6] == 'x') || (characters[6] == 'X')) { 8208 m_unit = CSSParser::FAngle; 8209 m_type = WebKitCSSTransformValue::RotateXTransformOperation; 8210 } else if ((characters[6] == 'y') || (characters[6] == 'Y')) { 8211 m_unit = CSSParser::FAngle; 8212 m_type = WebKitCSSTransformValue::RotateYTransformOperation; 8213 } else if ((characters[6] == 'z') || (characters[6] == 'Z')) { 8214 m_unit = CSSParser::FAngle; 8215 m_type = WebKitCSSTransformValue::RotateZTransformOperation; 8216 } 8217 } 8218 break; 8219 case 9: 8220 // Valid names: matrix3d(, rotate3d(. 8221 if ((characters[0] == 'm') || (characters[0] == 'M')) { 8222 if (((characters[1] == 'a') || (characters[1] == 'A')) 8223 & ((characters[2] == 't') || (characters[2] == 'T')) 8224 & ((characters[3] == 'r') || (characters[3] == 'R')) 8225 & ((characters[4] == 'i') || (characters[4] == 'I')) 8226 & ((characters[5] == 'x') || (characters[5] == 'X')) 8227 & (characters[6] == '3') 8228 & ((characters[7] == 'd') || (characters[7] == 'D')) 8229 & (characters[8] == '(')) { 8230 m_unit = CSSParser::FNumber; 8231 m_type = WebKitCSSTransformValue::Matrix3DTransformOperation; 8232 m_argCount = 31; 8233 } 8234 } else if (((characters[0] == 'r') || (characters[0] == 'R')) 8235 & ((characters[1] == 'o') || (characters[1] == 'O')) 8236 & ((characters[2] == 't') || (characters[2] == 'T')) 8237 & ((characters[3] == 'a') || (characters[3] == 'A')) 8238 & ((characters[4] == 't') || (characters[4] == 'T')) 8239 & ((characters[5] == 'e') || (characters[5] == 'E')) 8240 & (characters[6] == '3') 8241 & ((characters[7] == 'd') || (characters[7] == 'D')) 8242 & (characters[8] == '(')) { 8243 m_unit = CSSParser::FNumber; 8244 m_type = WebKitCSSTransformValue::Rotate3DTransformOperation; 8245 m_argCount = 7; 8246 } 8247 break; 8248 case 10: 8249 // Valid name: translate(. 8250 if (((characters[0] == 't') || (characters[0] == 'T')) 8251 & ((characters[1] == 'r') || (characters[1] == 'R')) 8252 & ((characters[2] == 'a') || (characters[2] == 'A')) 8253 & ((characters[3] == 'n') || (characters[3] == 'N')) 8254 & ((characters[4] == 's') || (characters[4] == 'S')) 8255 & ((characters[5] == 'l') || (characters[5] == 'L')) 8256 & ((characters[6] == 'a') || (characters[6] == 'A')) 8257 & ((characters[7] == 't') || (characters[7] == 'T')) 8258 & ((characters[8] == 'e') || (characters[8] == 'E')) 8259 & (characters[9] == '(')) { 8260 m_unit = CSSParser::FLength | CSSParser::FPercent; 8261 m_type = WebKitCSSTransformValue::TranslateTransformOperation; 8262 m_allowSingleArgument = true; 8263 m_argCount = 3; 8264 } 8265 break; 8266 case 11: 8267 // Valid names: translatex(, translatey(, translatez(. 8268 if (((characters[0] == 't') || (characters[0] == 'T')) 8269 & ((characters[1] == 'r') || (characters[1] == 'R')) 8270 & ((characters[2] == 'a') || (characters[2] == 'A')) 8271 & ((characters[3] == 'n') || (characters[3] == 'N')) 8272 & ((characters[4] == 's') || (characters[4] == 'S')) 8273 & ((characters[5] == 'l') || (characters[5] == 'L')) 8274 & ((characters[6] == 'a') || (characters[6] == 'A')) 8275 & ((characters[7] == 't') || (characters[7] == 'T')) 8276 & ((characters[8] == 'e') || (characters[8] == 'E')) 8277 & (characters[10] == '(')) { 8278 if ((characters[9] == 'x') || (characters[9] == 'X')) { 8279 m_unit = CSSParser::FLength | CSSParser::FPercent; 8280 m_type = WebKitCSSTransformValue::TranslateXTransformOperation; 8281 } else if ((characters[9] == 'y') || (characters[9] == 'Y')) { 8282 m_unit = CSSParser::FLength | CSSParser::FPercent; 8283 m_type = WebKitCSSTransformValue::TranslateYTransformOperation; 8284 } else if ((characters[9] == 'z') || (characters[9] == 'Z')) { 8285 m_unit = CSSParser::FLength | CSSParser::FPercent; 8286 m_type = WebKitCSSTransformValue::TranslateZTransformOperation; 8287 } 8288 } 8289 break; 8290 case 12: 8291 // Valid names: perspective(, translate3d(. 8292 if ((characters[0] == 'p') || (characters[0] == 'P')) { 8293 if (((characters[1] == 'e') || (characters[1] == 'E')) 8294 & ((characters[2] == 'r') || (characters[2] == 'R')) 8295 & ((characters[3] == 's') || (characters[3] == 'S')) 8296 & ((characters[4] == 'p') || (characters[4] == 'P')) 8297 & ((characters[5] == 'e') || (characters[5] == 'E')) 8298 & ((characters[6] == 'c') || (characters[6] == 'C')) 8299 & ((characters[7] == 't') || (characters[7] == 'T')) 8300 & ((characters[8] == 'i') || (characters[8] == 'I')) 8301 & ((characters[9] == 'v') || (characters[9] == 'V')) 8302 & ((characters[10] == 'e') || (characters[10] == 'E')) 8303 & (characters[11] == '(')) { 8304 m_unit = CSSParser::FNumber; 8305 m_type = WebKitCSSTransformValue::PerspectiveTransformOperation; 8306 } 8307 } else if (((characters[0] == 't') || (characters[0] == 'T')) 8308 & ((characters[1] == 'r') || (characters[1] == 'R')) 8309 & ((characters[2] == 'a') || (characters[2] == 'A')) 8310 & ((characters[3] == 'n') || (characters[3] == 'N')) 8311 & ((characters[4] == 's') || (characters[4] == 'S')) 8312 & ((characters[5] == 'l') || (characters[5] == 'L')) 8313 & ((characters[6] == 'a') || (characters[6] == 'A')) 8314 & ((characters[7] == 't') || (characters[7] == 'T')) 8315 & ((characters[8] == 'e') || (characters[8] == 'E')) 8316 & (characters[9] == '3') 8317 & ((characters[10] == 'd') || (characters[10] == 'D')) 8318 & (characters[11] == '(')) { 8319 m_unit = CSSParser::FLength | CSSParser::FPercent; 8320 m_type = WebKitCSSTransformValue::Translate3DTransformOperation; 8321 m_argCount = 5; 8322 } 8323 break; 8324 } // end switch () 8325 } 8326 8327 WebKitCSSTransformValue::TransformOperationType type() const { return m_type; } 8328 unsigned argCount() const { return m_argCount; } 8329 CSSParser::Units unit() const { return m_unit; } 8330 8331 bool unknown() const { return m_type == WebKitCSSTransformValue::UnknownTransformOperation; } 8332 bool hasCorrectArgCount(unsigned argCount) { return m_argCount == argCount || (m_allowSingleArgument && argCount == 1); } 8333 8334private: 8335 WebKitCSSTransformValue::TransformOperationType m_type; 8336 unsigned m_argCount; 8337 bool m_allowSingleArgument; 8338 CSSParser::Units m_unit; 8339}; 8340 8341PassRefPtr<CSSValueList> CSSParser::parseTransform() 8342{ 8343 if (!m_valueList) 8344 return 0; 8345 8346 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 8347 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 8348 RefPtr<CSSValue> parsedTransformValue = parseTransformValue(value); 8349 if (!parsedTransformValue) 8350 return 0; 8351 8352 list->append(parsedTransformValue.release()); 8353 } 8354 8355 return list.release(); 8356} 8357 8358PassRefPtr<CSSValue> CSSParser::parseTransformValue(CSSParserValue *value) 8359{ 8360 if (value->unit != CSSParserValue::Function || !value->function) 8361 return 0; 8362 8363 // Every primitive requires at least one argument. 8364 CSSParserValueList* args = value->function->args.get(); 8365 if (!args) 8366 return 0; 8367 8368 // See if the specified primitive is one we understand. 8369 TransformOperationInfo info(value->function->name); 8370 if (info.unknown()) 8371 return 0; 8372 8373 if (!info.hasCorrectArgCount(args->size())) 8374 return 0; 8375 8376 // The transform is a list of functional primitives that specify transform operations. 8377 // We collect a list of WebKitCSSTransformValues, where each value specifies a single operation. 8378 8379 // Create the new WebKitCSSTransformValue for this operation and add it to our list. 8380 RefPtr<WebKitCSSTransformValue> transformValue = WebKitCSSTransformValue::create(info.type()); 8381 8382 // Snag our values. 8383 CSSParserValue* a = args->current(); 8384 unsigned argNumber = 0; 8385 while (a) { 8386 CSSParser::Units unit = info.unit(); 8387 8388 if (info.type() == WebKitCSSTransformValue::Rotate3DTransformOperation && argNumber == 3) { 8389 // 4th param of rotate3d() is an angle rather than a bare number, validate it as such 8390 if (!validUnit(a, FAngle, CSSStrictMode)) 8391 return 0; 8392 } else if (info.type() == WebKitCSSTransformValue::Translate3DTransformOperation && argNumber == 2) { 8393 // 3rd param of translate3d() cannot be a percentage 8394 if (!validUnit(a, FLength, CSSStrictMode)) 8395 return 0; 8396 } else if (info.type() == WebKitCSSTransformValue::TranslateZTransformOperation && !argNumber) { 8397 // 1st param of translateZ() cannot be a percentage 8398 if (!validUnit(a, FLength, CSSStrictMode)) 8399 return 0; 8400 } else if (info.type() == WebKitCSSTransformValue::PerspectiveTransformOperation && !argNumber) { 8401 // 1st param of perspective() must be a non-negative number (deprecated) or length. 8402 if (!validUnit(a, FNumber | FLength | FNonNeg, CSSStrictMode)) 8403 return 0; 8404 } else if (!validUnit(a, unit, CSSStrictMode)) 8405 return 0; 8406 8407 // Add the value to the current transform operation. 8408 transformValue->append(createPrimitiveNumericValue(a)); 8409 8410 a = args->next(); 8411 if (!a) 8412 break; 8413 if (a->unit != CSSParserValue::Operator || a->iValue != ',') 8414 return 0; 8415 a = args->next(); 8416 8417 argNumber++; 8418 } 8419 8420 return transformValue.release(); 8421} 8422 8423bool CSSParser::isBlendMode(int ident) 8424{ 8425 return (ident >= CSSValueMultiply && ident <= CSSValueLuminosity) 8426 || ident == CSSValueNormal 8427 || ident == CSSValueOverlay; 8428} 8429 8430bool CSSParser::isCompositeOperator(int ident) 8431{ 8432 // FIXME: Add CSSValueDestination and CSSValueLighter when the Compositing spec updates. 8433 return ident >= CSSValueClear && ident <= CSSValueXor; 8434} 8435 8436#if ENABLE(CSS_FILTERS) 8437 8438static void filterInfoForName(const CSSParserString& name, WebKitCSSFilterValue::FilterOperationType& filterType, unsigned& maximumArgumentCount) 8439{ 8440 if (equalIgnoringCase(name, "grayscale(")) 8441 filterType = WebKitCSSFilterValue::GrayscaleFilterOperation; 8442 else if (equalIgnoringCase(name, "sepia(")) 8443 filterType = WebKitCSSFilterValue::SepiaFilterOperation; 8444 else if (equalIgnoringCase(name, "saturate(")) 8445 filterType = WebKitCSSFilterValue::SaturateFilterOperation; 8446 else if (equalIgnoringCase(name, "hue-rotate(")) 8447 filterType = WebKitCSSFilterValue::HueRotateFilterOperation; 8448 else if (equalIgnoringCase(name, "invert(")) 8449 filterType = WebKitCSSFilterValue::InvertFilterOperation; 8450 else if (equalIgnoringCase(name, "opacity(")) 8451 filterType = WebKitCSSFilterValue::OpacityFilterOperation; 8452 else if (equalIgnoringCase(name, "brightness(")) 8453 filterType = WebKitCSSFilterValue::BrightnessFilterOperation; 8454 else if (equalIgnoringCase(name, "contrast(")) 8455 filterType = WebKitCSSFilterValue::ContrastFilterOperation; 8456 else if (equalIgnoringCase(name, "blur(")) 8457 filterType = WebKitCSSFilterValue::BlurFilterOperation; 8458 else if (equalIgnoringCase(name, "drop-shadow(")) { 8459 filterType = WebKitCSSFilterValue::DropShadowFilterOperation; 8460 maximumArgumentCount = 4; // x-offset, y-offset, blur-radius, color -- spread and inset style not allowed. 8461 } 8462#if ENABLE(CSS_SHADERS) 8463 else if (equalIgnoringCase(name, "custom(")) 8464 filterType = WebKitCSSFilterValue::CustomFilterOperation; 8465#endif 8466} 8467 8468#if ENABLE(CSS_SHADERS) 8469static bool acceptCommaOperator(CSSParserValueList* argsList) 8470{ 8471 if (CSSParserValue* arg = argsList->current()) { 8472 if (!isComma(arg)) 8473 return false; 8474 argsList->next(); 8475 } 8476 return true; 8477} 8478 8479PassRefPtr<WebKitCSSArrayFunctionValue> CSSParser::parseCustomFilterArrayFunction(CSSParserValue* value) 8480{ 8481 ASSERT(value->unit == CSSParserValue::Function && value->function); 8482 8483 if (!equalIgnoringCase(value->function->name, "array(")) 8484 return 0; 8485 8486 CSSParserValueList* arrayArgsParserValueList = value->function->args.get(); 8487 if (!arrayArgsParserValueList || !arrayArgsParserValueList->size()) 8488 return 0; 8489 8490 // array() values are comma separated. 8491 RefPtr<WebKitCSSArrayFunctionValue> arrayFunction = WebKitCSSArrayFunctionValue::create(); 8492 while (true) { 8493 // We parse pairs <Value, Comma> at each step. 8494 CSSParserValue* currentParserValue = arrayArgsParserValueList->current(); 8495 if (!currentParserValue || !validUnit(currentParserValue, FNumber, CSSStrictMode)) 8496 return 0; 8497 8498 RefPtr<CSSValue> arrayValue = cssValuePool().createValue(currentParserValue->fValue, CSSPrimitiveValue::CSS_NUMBER); 8499 arrayFunction->append(arrayValue.release()); 8500 8501 CSSParserValue* nextParserValue = arrayArgsParserValueList->next(); 8502 if (!nextParserValue) 8503 break; 8504 8505 if (!isComma(nextParserValue)) 8506 return 0; 8507 8508 arrayArgsParserValueList->next(); 8509 } 8510 8511 return arrayFunction; 8512} 8513 8514PassRefPtr<WebKitCSSMatFunctionValue> CSSParser::parseMatValue(CSSParserValue* value) 8515{ 8516 if (value->unit != CSSParserValue::Function || !value->function) 8517 return 0; 8518 8519 unsigned numberOfValues = 0; 8520 if (equalIgnoringCase(value->function->name, "mat2(")) 8521 numberOfValues = 4; 8522 else if (equalIgnoringCase(value->function->name, "mat3(")) 8523 numberOfValues = 9; 8524 else if (equalIgnoringCase(value->function->name, "mat4(")) 8525 numberOfValues = 16; 8526 else 8527 return 0; 8528 8529 CSSParserValueList* args = value->function->args.get(); 8530 if (!args || args->size() != (numberOfValues * 2 - 1)) 8531 return 0; 8532 8533 RefPtr<WebKitCSSMatFunctionValue> matValueList = WebKitCSSMatFunctionValue::create(); 8534 CSSParserValue* arg = args->current(); 8535 while (arg) { 8536 if (!validUnit(arg, FNumber, CSSStrictMode)) 8537 return 0; 8538 matValueList->append(cssValuePool().createValue(arg->fValue, CSSPrimitiveValue::CSS_NUMBER)); 8539 arg = args->next(); 8540 8541 if (!arg) 8542 break; 8543 8544 if (!isComma(arg)) 8545 return 0; 8546 8547 arg = args->next(); 8548 } 8549 8550 if (!matValueList || matValueList->length() != numberOfValues) 8551 return 0; 8552 8553 return matValueList.release(); 8554} 8555 8556PassRefPtr<WebKitCSSMixFunctionValue> CSSParser::parseMixFunction(CSSParserValue* value) 8557{ 8558 ASSERT(value->unit == CSSParserValue::Function && value->function); 8559 8560 if (!equalIgnoringCase(value->function->name, "mix(")) 8561 return 0; 8562 8563 CSSParserValueList* argsList = value->function->args.get(); 8564 if (!argsList) 8565 return 0; 8566 8567 unsigned numArgs = argsList->size(); 8568 if (numArgs < 1 || numArgs > 3) 8569 return 0; 8570 8571 RefPtr<WebKitCSSMixFunctionValue> mixFunction = WebKitCSSMixFunctionValue::create(); 8572 8573 bool hasBlendMode = false; 8574 bool hasAlphaCompositing = false; 8575 CSSParserValue* arg; 8576 while ((arg = argsList->current())) { 8577 RefPtr<CSSValue> value; 8578 8579 unsigned argNumber = argsList->currentIndex(); 8580 if (!argNumber) { 8581 if (arg->unit == CSSPrimitiveValue::CSS_URI) { 8582 KURL shaderURL = completeURL(arg->string); 8583 value = WebKitCSSShaderValue::create(shaderURL.string()); 8584 } 8585 } else if (argNumber == 1 || argNumber == 2) { 8586 if (!hasBlendMode && isBlendMode(arg->id)) { 8587 hasBlendMode = true; 8588 value = cssValuePool().createIdentifierValue(arg->id); 8589 } else if (!hasAlphaCompositing && isCompositeOperator(arg->id)) { 8590 hasAlphaCompositing = true; 8591 value = cssValuePool().createIdentifierValue(arg->id); 8592 } 8593 } 8594 8595 if (!value) 8596 return 0; 8597 8598 mixFunction->append(value.release()); 8599 8600 arg = argsList->next(); 8601 } 8602 8603 return mixFunction; 8604} 8605 8606PassRefPtr<CSSValueList> CSSParser::parseCustomFilterParameters(CSSParserValueList* argsList) 8607{ 8608 // 8609 // params: [<param-def>[,<param-def>*]] 8610 // param-def: <param-name>wsp<param-value> 8611 // param-name: <ident> 8612 // param-value: true|false[wsp+true|false]{0-3} | 8613 // <number>[wsp+<number>]{0-3} | 8614 // <array> | 8615 // <transform> | 8616 // <texture(<uri>)> 8617 // array: 'array('<number>[wsp<number>]*')' 8618 // css-3d-transform: <transform-function>;[<transform-function>]* 8619 // transform: <css-3d-transform> | <mat> 8620 // mat: 'mat2('<number>(,<number>){3}')' | 8621 // 'mat3('<number>(,<number>){8}')' | 8622 // 'mat4('<number>(,<number>){15}')' ) 8623 // 8624 8625 RefPtr<CSSValueList> paramList = CSSValueList::createCommaSeparated(); 8626 8627 while (CSSParserValue* arg = argsList->current()) { 8628 if (arg->unit != CSSPrimitiveValue::CSS_IDENT) 8629 return 0; 8630 8631 RefPtr<CSSValueList> parameter = CSSValueList::createSpaceSeparated(); 8632 parameter->append(createPrimitiveStringValue(arg)); 8633 8634 arg = argsList->next(); 8635 if (!arg) 8636 return 0; 8637 8638 RefPtr<CSSValue> parameterValue; 8639 8640 if (arg->unit == CSSParserValue::Function && arg->function) { 8641 // FIXME: Implement parsing for the other parameter types. 8642 // textures: https://bugs.webkit.org/show_bug.cgi?id=71442 8643 if (equalIgnoringCase(arg->function->name, "array(")) { 8644 parameterValue = parseCustomFilterArrayFunction(arg); 8645 // This parsing step only consumes function arguments, 8646 // argsList is therefore moved forward explicitely. 8647 argsList->next(); 8648 } else { 8649 // Parse mat2, mat3 and mat4 functions. 8650 parameterValue = parseMatValue(arg); 8651 if (!parameterValue && arg) 8652 parameterValue = parseCustomFilterTransform(argsList); 8653 else if (parameterValue) 8654 argsList->next(); 8655 } 8656 } else if (validUnit(arg, FNumber, CSSStrictMode)) { 8657 RefPtr<CSSValueList> paramValueList = CSSValueList::createSpaceSeparated(); 8658 while (arg) { 8659 // If we hit a comma, it means that we finished this parameter's values. 8660 if (isComma(arg)) 8661 break; 8662 if (!validUnit(arg, FNumber, CSSStrictMode)) 8663 return 0; 8664 paramValueList->append(cssValuePool().createValue(arg->fValue, CSSPrimitiveValue::CSS_NUMBER)); 8665 arg = argsList->next(); 8666 } 8667 if (!paramValueList->length() || paramValueList->length() > 4) 8668 return 0; 8669 parameterValue = paramValueList.release(); 8670 } 8671 if (!parameterValue && arg) { 8672 // All parameter values need to be CSSValueLists. 8673 RefPtr<CSSValueList> paramValueList = CSSValueList::createSpaceSeparated(); 8674 RefPtr<CSSPrimitiveValue> colorValue = parseColor(arg); 8675 if (!colorValue) 8676 return 0; 8677 paramValueList->append(colorValue.release()); 8678 parameterValue = paramValueList.release(); 8679 arg = argsList->next(); 8680 } 8681 8682 if (!parameterValue || !acceptCommaOperator(argsList)) 8683 return 0; 8684 8685 parameter->append(parameterValue.release()); 8686 paramList->append(parameter.release()); 8687 } 8688 8689 return paramList; 8690} 8691 8692PassRefPtr<WebKitCSSFilterValue> CSSParser::parseCustomFilterFunctionWithAtRuleReferenceSyntax(CSSParserValue* value) 8693{ 8694 // 8695 // Custom filter function "at-rule reference" syntax: 8696 // 8697 // custom(<filter-name>wsp[,wsp<params>]) 8698 // 8699 // filter-name: <filter-name> 8700 // params: See the comment in CSSParser::parseCustomFilterParameters. 8701 // 8702 8703 ASSERT(value->function); 8704 8705 CSSParserValueList* argsList = value->function->args.get(); 8706 if (!argsList || !argsList->size()) 8707 return 0; 8708 8709 // 1. Parse the filter name. 8710 CSSParserValue* arg = argsList->current(); 8711 if (arg->unit != CSSPrimitiveValue::CSS_IDENT) 8712 return 0; 8713 8714 RefPtr<WebKitCSSFilterValue> filterValue = WebKitCSSFilterValue::create(WebKitCSSFilterValue::CustomFilterOperation); 8715 8716 RefPtr<CSSValue> filterName = createPrimitiveStringValue(arg); 8717 filterValue->append(filterName); 8718 argsList->next(); 8719 8720 if (!acceptCommaOperator(argsList)) 8721 return 0; 8722 8723 // 2. Parse the parameters. 8724 RefPtr<CSSValueList> paramList = parseCustomFilterParameters(argsList); 8725 if (!paramList) 8726 return 0; 8727 8728 if (paramList->length()) 8729 filterValue->append(paramList.release()); 8730 8731 return filterValue; 8732} 8733 8734// FIXME: The custom filters "inline" syntax is deprecated. We will remove it eventually. 8735PassRefPtr<WebKitCSSFilterValue> CSSParser::parseCustomFilterFunctionWithInlineSyntax(CSSParserValue* value) 8736{ 8737 // 8738 // Custom filter function "inline" syntax: 8739 // 8740 // custom(<vertex-shader>[wsp<fragment-shader>][,<vertex-mesh>][,<params>]) 8741 // 8742 // vertexShader: <uri> | none 8743 // fragmentShader: <uri> | none | mix(<uri> [ <blend-mode> || <alpha-compositing> ]?) 8744 // 8745 // blend-mode: normal | multiply | screen | overlay | darken | lighten | color-dodge | 8746 // color-burn | hard-light | soft-light | difference | exclusion | hue | 8747 // saturation | color | luminosity 8748 // alpha-compositing: clear | src | dst | src-over | dst-over | src-in | dst-in | 8749 // src-out | dst-out | src-atop | dst-atop | xor | plus 8750 // 8751 // vertexMesh: +<integer>{1,2}[wsp<box>][wsp'detached'] 8752 // box: filter-box | border-box | padding-box | content-box 8753 // 8754 // params: See the comment in CSSParser::parseCustomFilterParameters. 8755 // 8756 8757 ASSERT(value->function); 8758 8759 CSSParserValueList* argsList = value->function->args.get(); 8760 if (!argsList) 8761 return 0; 8762 8763 RefPtr<WebKitCSSFilterValue> filterValue = WebKitCSSFilterValue::create(WebKitCSSFilterValue::CustomFilterOperation); 8764 8765 // 1. Parse the shader URLs: <vertex-shader>[wsp<fragment-shader>] 8766 RefPtr<CSSValueList> shadersList = CSSValueList::createSpaceSeparated(); 8767 bool hadAtLeastOneCustomShader = false; 8768 CSSParserValue* arg; 8769 while ((arg = argsList->current())) { 8770 RefPtr<CSSValue> value; 8771 if (arg->id == CSSValueNone) 8772 value = cssValuePool().createIdentifierValue(CSSValueNone); 8773 else if (arg->unit == CSSPrimitiveValue::CSS_URI) { 8774 KURL shaderURL = completeURL(arg->string); 8775 value = WebKitCSSShaderValue::create(shaderURL.string()); 8776 hadAtLeastOneCustomShader = true; 8777 } else if (argsList->currentIndex() == 1 && arg->unit == CSSParserValue::Function) { 8778 if (!(value = parseMixFunction(arg))) 8779 return 0; 8780 hadAtLeastOneCustomShader = true; 8781 } 8782 8783 if (!value) 8784 break; 8785 shadersList->append(value.release()); 8786 argsList->next(); 8787 } 8788 8789 if (!shadersList->length() || !hadAtLeastOneCustomShader || shadersList->length() > 2 || !acceptCommaOperator(argsList)) 8790 return 0; 8791 8792 filterValue->append(shadersList.release()); 8793 8794 // 2. Parse the mesh size <vertex-mesh> 8795 RefPtr<CSSValueList> meshSizeList = CSSValueList::createSpaceSeparated(); 8796 8797 while ((arg = argsList->current())) { 8798 if (!validUnit(arg, FInteger | FNonNeg, CSSStrictMode)) 8799 break; 8800 int integerValue = clampToInteger(arg->fValue); 8801 // According to the specification we can only accept positive non-zero values. 8802 if (integerValue < 1) 8803 return 0; 8804 meshSizeList->append(cssValuePool().createValue(integerValue, CSSPrimitiveValue::CSS_NUMBER)); 8805 argsList->next(); 8806 } 8807 8808 if (meshSizeList->length() > 2) 8809 return 0; 8810 8811 // FIXME: For legacy content, we accept the mesh box types. We don't do anything else with them. 8812 // Eventually, we'll remove them completely. 8813 // https://bugs.webkit.org/show_bug.cgi?id=103778 8814 if ((arg = argsList->current()) && (arg->id == CSSValueBorderBox || arg->id == CSSValuePaddingBox 8815 || arg->id == CSSValueContentBox || arg->id == CSSValueFilterBox)) 8816 argsList->next(); 8817 8818 if ((arg = argsList->current()) && arg->id == CSSValueDetached) { 8819 meshSizeList->append(cssValuePool().createIdentifierValue(arg->id)); 8820 argsList->next(); 8821 } 8822 8823 if (meshSizeList->length()) { 8824 if (!acceptCommaOperator(argsList)) 8825 return 0; 8826 filterValue->append(meshSizeList.release()); 8827 } 8828 8829 // 3. Parse the parameters. 8830 RefPtr<CSSValueList> paramList = parseCustomFilterParameters(argsList); 8831 if (!paramList) 8832 return 0; 8833 8834 if (paramList->length()) 8835 filterValue->append(paramList.release()); 8836 8837 return filterValue; 8838} 8839 8840PassRefPtr<WebKitCSSFilterValue> CSSParser::parseCustomFilterFunction(CSSParserValue* value) 8841{ 8842 ASSERT(value->function); 8843 8844 // Look ahead to determine which syntax the custom function is using. 8845 // Both the at-rule reference syntax and the inline syntax require at least one argument. 8846 CSSParserValueList* argsList = value->function->args.get(); 8847 if (!argsList || !argsList->size()) 8848 return 0; 8849 8850 // The at-rule reference syntax expects a single ident or an ident followed by a comma. 8851 // e.g. custom(my-filter) or custom(my-filter, ...) 8852 // In contrast, when the inline syntax starts with an ident like "none", it expects a uri or a mix function next. 8853 // e.g. custom(none url(...)) or custom(none mix(...) 8854 bool isAtRuleReferenceSyntax = argsList->valueAt(0)->unit == CSSPrimitiveValue::CSS_IDENT 8855 && (argsList->size() == 1 || isComma(argsList->valueAt(1))); 8856 return isAtRuleReferenceSyntax ? parseCustomFilterFunctionWithAtRuleReferenceSyntax(value) : parseCustomFilterFunctionWithInlineSyntax(value); 8857} 8858 8859PassRefPtr<CSSValueList> CSSParser::parseCustomFilterTransform(CSSParserValueList* valueList) 8860{ 8861 if (!valueList) 8862 return 0; 8863 8864 // CSS Shaders' custom() transforms are space separated and comma terminated. 8865 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 8866 for (CSSParserValue* value = valueList->current(); value; value = valueList->next()) { 8867 if (isComma(value)) 8868 break; 8869 8870 RefPtr<CSSValue> parsedTransformValue = parseTransformValue(value); 8871 if (!parsedTransformValue) 8872 return 0; 8873 8874 list->append(parsedTransformValue.release()); 8875 } 8876 8877 return list.release(); 8878} 8879 8880PassRefPtr<WebKitCSSShaderValue> CSSParser::parseFilterRuleSrcUriAndFormat(CSSParserValueList* valueList) 8881{ 8882 CSSParserValue* value = valueList->current(); 8883 ASSERT(value && value->unit == CSSPrimitiveValue::CSS_URI); 8884 RefPtr<WebKitCSSShaderValue> shaderValue = WebKitCSSShaderValue::create(completeURL(value->string)); 8885 8886 value = valueList->next(); 8887 if (value && value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "format(")) { 8888 CSSParserValueList* args = value->function->args.get(); 8889 if (!args || args->size() != 1) 8890 return 0; 8891 8892 CSSParserValue* arg = args->current(); 8893 if (arg->unit != CSSPrimitiveValue::CSS_STRING) 8894 return 0; 8895 8896 shaderValue->setFormat(arg->string); 8897 valueList->next(); 8898 } 8899 8900 return shaderValue.release(); 8901} 8902 8903bool CSSParser::parseFilterRuleSrc() 8904{ 8905 RefPtr<CSSValueList> srcList = CSSValueList::createCommaSeparated(); 8906 8907 CSSParserValue* value = m_valueList->current(); 8908 while (value) { 8909 if (value->unit != CSSPrimitiveValue::CSS_URI) 8910 return false; 8911 8912 RefPtr<WebKitCSSShaderValue> shaderValue = parseFilterRuleSrcUriAndFormat(m_valueList.get()); 8913 if (!shaderValue) 8914 return false; 8915 srcList->append(shaderValue.release()); 8916 8917 if (!acceptCommaOperator(m_valueList.get())) 8918 return false; 8919 8920 value = m_valueList->current(); 8921 } 8922 8923 if (!srcList->length()) 8924 return false; 8925 8926 addProperty(CSSPropertySrc, srcList.release(), m_important); 8927 return true; 8928} 8929 8930bool CSSParser::parseFilterRuleMix() 8931{ 8932 if (m_valueList->size() > 2) 8933 return false; 8934 8935 RefPtr<CSSValueList> mixList = CSSValueList::createSpaceSeparated(); 8936 8937 bool hasBlendMode = false; 8938 bool hasAlphaCompositing = false; 8939 CSSParserValue* value = m_valueList->current(); 8940 while (value) { 8941 RefPtr<CSSValue> mixValue; 8942 if (!hasBlendMode && isBlendMode(value->id)) { 8943 hasBlendMode = true; 8944 mixValue = cssValuePool().createIdentifierValue(value->id); 8945 } else if (!hasAlphaCompositing && isCompositeOperator(value->id)) { 8946 hasAlphaCompositing = true; 8947 mixValue = cssValuePool().createIdentifierValue(value->id); 8948 } 8949 8950 if (!mixValue) 8951 return false; 8952 8953 mixList->append(mixValue.release()); 8954 value = m_valueList->next(); 8955 } 8956 8957 addProperty(CSSPropertyMix, mixList.release(), m_important); 8958 return true; 8959} 8960 8961bool CSSParser::parseGeometry(CSSPropertyID propId, CSSParserValue* value, bool important) 8962{ 8963 ASSERT(propId == CSSPropertyGeometry); 8964 8965 // <geometry-shape> = grid(<integer>{1,2} || [ detached | attached ]?) 8966 if (value->unit != CSSParserValue::Function || !equalIgnoringCase(value->function->name, "grid(")) 8967 return false; 8968 8969 ASSERT(value->function->args); 8970 8971 // grid() function should have from 1 to 3 arguments. 8972 unsigned size = value->function->args->size(); 8973 if (!size || size > 3) 8974 return false; 8975 8976 CSSParserValueList* gridParserValueList = value->function->args.get(); 8977 CSSParserValue* gridParserValue = gridParserValueList->current(); 8978 RefPtr<CSSValueList> geometryList = CSSValueList::createSpaceSeparated(); 8979 8980 bool hasDimensions = false; 8981 bool hasConnectivity = false; 8982 8983 while (gridParserValue) { 8984 if (hasDimensions && hasConnectivity) { 8985 geometryList.release(); 8986 return false; 8987 } 8988 8989 if (gridParserValue->id == CSSValueAttached || gridParserValue->id == CSSValueDetached) { 8990 hasConnectivity = true; 8991 geometryList->append(cssValuePool().createIdentifierValue(gridParserValue->id)); 8992 gridParserValue = gridParserValueList->next(); 8993 } else if (!hasDimensions && parseGridDimensions(gridParserValueList, geometryList)) { 8994 hasDimensions = true; 8995 gridParserValue = gridParserValueList->current(); 8996 } else { 8997 geometryList.release(); 8998 return false; 8999 } 9000 } 9001 9002 addProperty(propId, geometryList.release(), important); 9003 return hasDimensions; 9004} 9005 9006bool CSSParser::parseGridDimensions(CSSParserValueList* args, RefPtr<CSSValueList>& gridValueList) 9007{ 9008 ASSERT(args); 9009 9010 // There must be at least one valid numeric value. 9011 CSSParserValue* arg = args->current(); 9012 if (!arg || !validUnit(arg, FPositiveInteger)) 9013 return false; 9014 9015 // A valid numeric value is parsed and then we move on. 9016 gridValueList->append(createPrimitiveNumericValue(arg)); 9017 arg = args->next(); 9018 9019 // If the next argument is not numeric, we are done parsing the grid dimensions. 9020 if (!arg || !validUnit(arg, FPositiveInteger)) 9021 return true; 9022 9023 // Commit the second numeric value we found. 9024 gridValueList->append(createPrimitiveNumericValue(arg)); 9025 args->next(); 9026 return true; 9027} 9028 9029bool CSSParser::parseFilterRuleParameters() 9030{ 9031 RefPtr<CSSValueList> paramsList = parseCustomFilterParameters(m_valueList.get()); 9032 if (!paramsList) 9033 return false; 9034 9035 addProperty(CSSPropertyParameters, paramsList.release(), m_important); 9036 return true; 9037} 9038 9039StyleRuleBase* CSSParser::createFilterRule(const CSSParserString& filterName) 9040{ 9041 RefPtr<StyleRuleFilter> rule = StyleRuleFilter::create(filterName); 9042 rule->setProperties(createStylePropertySet()); 9043 clearProperties(); 9044 StyleRuleFilter* result = rule.get(); 9045 m_parsedRules.append(rule.release()); 9046 processAndAddNewRuleToSourceTreeIfNeeded(); 9047 return result; 9048} 9049 9050#endif // ENABLE(CSS_SHADERS) 9051 9052PassRefPtr<WebKitCSSFilterValue> CSSParser::parseBuiltinFilterArguments(CSSParserValueList* args, WebKitCSSFilterValue::FilterOperationType filterType) 9053{ 9054 RefPtr<WebKitCSSFilterValue> filterValue = WebKitCSSFilterValue::create(filterType); 9055 ASSERT(args); 9056 9057 switch (filterType) { 9058 case WebKitCSSFilterValue::GrayscaleFilterOperation: 9059 case WebKitCSSFilterValue::SepiaFilterOperation: 9060 case WebKitCSSFilterValue::SaturateFilterOperation: 9061 case WebKitCSSFilterValue::InvertFilterOperation: 9062 case WebKitCSSFilterValue::OpacityFilterOperation: 9063 case WebKitCSSFilterValue::ContrastFilterOperation: { 9064 // One optional argument, 0-1 or 0%-100%, if missing use 100%. 9065 if (args->size() > 1) 9066 return 0; 9067 9068 if (args->size()) { 9069 CSSParserValue* value = args->current(); 9070 if (!validUnit(value, FNumber | FPercent | FNonNeg, CSSStrictMode)) 9071 return 0; 9072 9073 double amount = value->fValue; 9074 9075 // Saturate and Contrast allow values over 100%. 9076 if (filterType != WebKitCSSFilterValue::SaturateFilterOperation 9077 && filterType != WebKitCSSFilterValue::ContrastFilterOperation) { 9078 double maxAllowed = value->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 100.0 : 1.0; 9079 if (amount > maxAllowed) 9080 return 0; 9081 } 9082 9083 filterValue->append(cssValuePool().createValue(amount, static_cast<CSSPrimitiveValue::UnitTypes>(value->unit))); 9084 } 9085 break; 9086 } 9087 case WebKitCSSFilterValue::BrightnessFilterOperation: { 9088 // One optional argument, if missing use 100%. 9089 if (args->size() > 1) 9090 return 0; 9091 9092 if (args->size()) { 9093 CSSParserValue* value = args->current(); 9094 if (!validUnit(value, FNumber | FPercent, CSSStrictMode)) 9095 return 0; 9096 9097 filterValue->append(cssValuePool().createValue(value->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(value->unit))); 9098 } 9099 break; 9100 } 9101 case WebKitCSSFilterValue::HueRotateFilterOperation: { 9102 // hue-rotate() takes one optional angle. 9103 if (args->size() > 1) 9104 return 0; 9105 9106 if (args->size()) { 9107 CSSParserValue* argument = args->current(); 9108 if (!validUnit(argument, FAngle, CSSStrictMode)) 9109 return 0; 9110 9111 filterValue->append(createPrimitiveNumericValue(argument)); 9112 } 9113 break; 9114 } 9115 case WebKitCSSFilterValue::BlurFilterOperation: { 9116 // Blur takes a single length. Zero parameters are allowed. 9117 if (args->size() > 1) 9118 return 0; 9119 9120 if (args->size()) { 9121 CSSParserValue* argument = args->current(); 9122 if (!validUnit(argument, FLength | FNonNeg, CSSStrictMode)) 9123 return 0; 9124 9125 filterValue->append(createPrimitiveNumericValue(argument)); 9126 } 9127 break; 9128 } 9129 case WebKitCSSFilterValue::DropShadowFilterOperation: { 9130 // drop-shadow() takes a single shadow. 9131 RefPtr<CSSValueList> shadowValueList = parseShadow(args, CSSPropertyWebkitFilter); 9132 if (!shadowValueList || shadowValueList->length() != 1) 9133 return 0; 9134 9135 filterValue->append((shadowValueList.release())->itemWithoutBoundsCheck(0)); 9136 break; 9137 } 9138 default: 9139 ASSERT_NOT_REACHED(); 9140 } 9141 return filterValue.release(); 9142} 9143 9144PassRefPtr<CSSValueList> CSSParser::parseFilter() 9145{ 9146 if (!m_valueList) 9147 return 0; 9148 9149 // The filter is a list of functional primitives that specify individual operations. 9150 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9151 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9152 if (value->unit != CSSPrimitiveValue::CSS_URI && (value->unit != CSSParserValue::Function || !value->function)) 9153 return 0; 9154 9155 WebKitCSSFilterValue::FilterOperationType filterType = WebKitCSSFilterValue::UnknownFilterOperation; 9156 9157 // See if the specified primitive is one we understand. 9158 if (value->unit == CSSPrimitiveValue::CSS_URI) { 9159#if ENABLE(SVG) 9160 RefPtr<WebKitCSSFilterValue> referenceFilterValue = WebKitCSSFilterValue::create(WebKitCSSFilterValue::ReferenceFilterOperation); 9161 list->append(referenceFilterValue); 9162 referenceFilterValue->append(WebKitCSSSVGDocumentValue::create(value->string)); 9163#endif 9164 } else { 9165 const CSSParserString name = value->function->name; 9166 unsigned maximumArgumentCount = 1; 9167 9168 filterInfoForName(name, filterType, maximumArgumentCount); 9169 9170 if (filterType == WebKitCSSFilterValue::UnknownFilterOperation) 9171 return 0; 9172 9173#if ENABLE(CSS_SHADERS) 9174 if (filterType == WebKitCSSFilterValue::CustomFilterOperation) { 9175 // Make sure parsing fails if custom filters are disabled. 9176 if (!m_context.isCSSCustomFilterEnabled) 9177 return 0; 9178 9179 RefPtr<WebKitCSSFilterValue> filterValue = parseCustomFilterFunction(value); 9180 if (!filterValue) 9181 return 0; 9182 list->append(filterValue.release()); 9183 continue; 9184 } 9185#endif 9186 CSSParserValueList* args = value->function->args.get(); 9187 if (!args) 9188 return 0; 9189 9190 RefPtr<WebKitCSSFilterValue> filterValue = parseBuiltinFilterArguments(args, filterType); 9191 if (!filterValue) 9192 return 0; 9193 9194 list->append(filterValue); 9195 } 9196 } 9197 9198 return list.release(); 9199} 9200#endif 9201 9202#if ENABLE(CSS_REGIONS) 9203static bool validFlowName(const String& flowName) 9204{ 9205 return !(equalIgnoringCase(flowName, "auto") 9206 || equalIgnoringCase(flowName, "default") 9207 || equalIgnoringCase(flowName, "inherit") 9208 || equalIgnoringCase(flowName, "initial") 9209 || equalIgnoringCase(flowName, "none")); 9210} 9211#endif 9212 9213bool CSSParser::cssRegionsEnabled() const 9214{ 9215 return m_context.isCSSRegionsEnabled; 9216} 9217 9218bool CSSParser::cssCompositingEnabled() const 9219{ 9220 return m_context.isCSSCompositingEnabled; 9221} 9222 9223bool CSSParser::cssGridLayoutEnabled() const 9224{ 9225 return m_context.isCSSGridLayoutEnabled; 9226} 9227 9228#if ENABLE(CSS_REGIONS) 9229bool CSSParser::parseFlowThread(const String& flowName) 9230{ 9231 setupParser("@-webkit-decls{-webkit-flow-into:", flowName, "}"); 9232 cssyyparse(this); 9233 9234 m_rule = 0; 9235 9236 return ((m_parsedProperties.size() == 1) && (m_parsedProperties.first().id() == CSSPropertyWebkitFlowInto)); 9237} 9238 9239// none | <ident> 9240bool CSSParser::parseFlowThread(CSSPropertyID propId, bool important) 9241{ 9242 ASSERT(propId == CSSPropertyWebkitFlowInto); 9243 ASSERT(cssRegionsEnabled()); 9244 9245 if (m_valueList->size() != 1) 9246 return false; 9247 9248 CSSParserValue* value = m_valueList->current(); 9249 if (!value) 9250 return false; 9251 9252 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 9253 return false; 9254 9255 if (value->id == CSSValueNone) { 9256 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 9257 return true; 9258 } 9259 9260 String inputProperty = String(value->string); 9261 if (!inputProperty.isEmpty()) { 9262 if (!validFlowName(inputProperty)) 9263 return false; 9264 addProperty(propId, cssValuePool().createValue(inputProperty, CSSPrimitiveValue::CSS_STRING), important); 9265 } else 9266 addProperty(propId, cssValuePool().createIdentifierValue(CSSValueNone), important); 9267 9268 return true; 9269} 9270 9271// -webkit-flow-from: none | <ident> 9272bool CSSParser::parseRegionThread(CSSPropertyID propId, bool important) 9273{ 9274 ASSERT(propId == CSSPropertyWebkitFlowFrom); 9275 ASSERT(cssRegionsEnabled()); 9276 9277 if (m_valueList->size() != 1) 9278 return false; 9279 9280 CSSParserValue* value = m_valueList->current(); 9281 if (!value) 9282 return false; 9283 9284 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 9285 return false; 9286 9287 if (value->id == CSSValueNone) 9288 addProperty(propId, cssValuePool().createIdentifierValue(value->id), important); 9289 else { 9290 String inputProperty = String(value->string); 9291 if (!inputProperty.isEmpty()) { 9292 if (!validFlowName(inputProperty)) 9293 return false; 9294 addProperty(propId, cssValuePool().createValue(inputProperty, CSSPrimitiveValue::CSS_STRING), important); 9295 } else 9296 addProperty(propId, cssValuePool().createIdentifierValue(CSSValueNone), important); 9297 } 9298 9299 return true; 9300} 9301#endif 9302 9303bool CSSParser::parseTransformOrigin(CSSPropertyID propId, CSSPropertyID& propId1, CSSPropertyID& propId2, CSSPropertyID& propId3, RefPtr<CSSValue>& value, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3) 9304{ 9305 propId1 = propId; 9306 propId2 = propId; 9307 propId3 = propId; 9308 if (propId == CSSPropertyWebkitTransformOrigin) { 9309 propId1 = CSSPropertyWebkitTransformOriginX; 9310 propId2 = CSSPropertyWebkitTransformOriginY; 9311 propId3 = CSSPropertyWebkitTransformOriginZ; 9312 } 9313 9314 switch (propId) { 9315 case CSSPropertyWebkitTransformOrigin: 9316 if (!parseTransformOriginShorthand(value, value2, value3)) 9317 return false; 9318 // parseTransformOriginShorthand advances the m_valueList pointer 9319 break; 9320 case CSSPropertyWebkitTransformOriginX: { 9321 value = parseFillPositionX(m_valueList.get()); 9322 if (value) 9323 m_valueList->next(); 9324 break; 9325 } 9326 case CSSPropertyWebkitTransformOriginY: { 9327 value = parseFillPositionY(m_valueList.get()); 9328 if (value) 9329 m_valueList->next(); 9330 break; 9331 } 9332 case CSSPropertyWebkitTransformOriginZ: { 9333 if (validUnit(m_valueList->current(), FLength)) 9334 value = createPrimitiveNumericValue(m_valueList->current()); 9335 if (value) 9336 m_valueList->next(); 9337 break; 9338 } 9339 default: 9340 ASSERT_NOT_REACHED(); 9341 return false; 9342 } 9343 9344 return value; 9345} 9346 9347bool CSSParser::parsePerspectiveOrigin(CSSPropertyID propId, CSSPropertyID& propId1, CSSPropertyID& propId2, RefPtr<CSSValue>& value, RefPtr<CSSValue>& value2) 9348{ 9349 propId1 = propId; 9350 propId2 = propId; 9351 if (propId == CSSPropertyWebkitPerspectiveOrigin) { 9352 propId1 = CSSPropertyWebkitPerspectiveOriginX; 9353 propId2 = CSSPropertyWebkitPerspectiveOriginY; 9354 } 9355 9356 switch (propId) { 9357 case CSSPropertyWebkitPerspectiveOrigin: 9358 if (m_valueList->size() > 2) 9359 return false; 9360 parse2ValuesFillPosition(m_valueList.get(), value, value2); 9361 break; 9362 case CSSPropertyWebkitPerspectiveOriginX: { 9363 value = parseFillPositionX(m_valueList.get()); 9364 if (value) 9365 m_valueList->next(); 9366 break; 9367 } 9368 case CSSPropertyWebkitPerspectiveOriginY: { 9369 value = parseFillPositionY(m_valueList.get()); 9370 if (value) 9371 m_valueList->next(); 9372 break; 9373 } 9374 default: 9375 ASSERT_NOT_REACHED(); 9376 return false; 9377 } 9378 9379 return value; 9380} 9381 9382void CSSParser::addTextDecorationProperty(CSSPropertyID propId, PassRefPtr<CSSValue> value, bool important) 9383{ 9384#if ENABLE(CSS3_TEXT) 9385 // The text-decoration-line property takes priority over text-decoration, unless the latter has important priority set. 9386 if (propId == CSSPropertyTextDecoration && !important && m_currentShorthand == CSSPropertyInvalid) { 9387 for (unsigned i = 0; i < m_parsedProperties.size(); ++i) { 9388 if (m_parsedProperties[i].id() == CSSPropertyWebkitTextDecorationLine) 9389 return; 9390 } 9391 } 9392#endif // CSS3_TEXT 9393 addProperty(propId, value, important); 9394} 9395 9396bool CSSParser::parseTextDecoration(CSSPropertyID propId, bool important) 9397{ 9398 CSSParserValue* value = m_valueList->current(); 9399 if (value->id == CSSValueNone) { 9400 addTextDecorationProperty(propId, cssValuePool().createIdentifierValue(CSSValueNone), important); 9401 m_valueList->next(); 9402 return true; 9403 } 9404 9405 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9406 bool isValid = true; 9407 while (isValid && value) { 9408 switch (value->id) { 9409 case CSSValueBlink: 9410 case CSSValueLineThrough: 9411 case CSSValueOverline: 9412 case CSSValueUnderline: 9413 list->append(cssValuePool().createIdentifierValue(value->id)); 9414 break; 9415 default: 9416 isValid = false; 9417 break; 9418 } 9419 if (isValid) 9420 value = m_valueList->next(); 9421 } 9422 9423 if (list->length() && isValid) { 9424 addTextDecorationProperty(propId, list.release(), important); 9425 return true; 9426 } 9427 9428 return false; 9429} 9430 9431#if ENABLE(CSS3_TEXT) 9432bool CSSParser::parseTextUnderlinePosition(bool important) 9433{ 9434 // The text-underline-position property has sintax "auto | alphabetic | [ under || [ left | right ] ]". 9435 // However, values 'left' and 'right' are not implemented yet, so we will parse sintax 9436 // "auto | alphabetic | under" for now. 9437 CSSParserValue* value = m_valueList->current(); 9438 switch (value->id) { 9439 case CSSValueAuto: 9440 case CSSValueAlphabetic: 9441 case CSSValueUnder: 9442 if (m_valueList->next()) 9443 return false; 9444 9445 addProperty(CSSPropertyWebkitTextUnderlinePosition, cssValuePool().createIdentifierValue(value->id), important); 9446 return true; 9447 } 9448 return false; 9449} 9450#endif // CSS3_TEXT 9451 9452bool CSSParser::parseTextEmphasisStyle(bool important) 9453{ 9454 unsigned valueListSize = m_valueList->size(); 9455 9456 RefPtr<CSSPrimitiveValue> fill; 9457 RefPtr<CSSPrimitiveValue> shape; 9458 9459 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9460 if (value->unit == CSSPrimitiveValue::CSS_STRING) { 9461 if (fill || shape || (valueListSize != 1 && !inShorthand())) 9462 return false; 9463 addProperty(CSSPropertyWebkitTextEmphasisStyle, createPrimitiveStringValue(value), important); 9464 m_valueList->next(); 9465 return true; 9466 } 9467 9468 if (value->id == CSSValueNone) { 9469 if (fill || shape || (valueListSize != 1 && !inShorthand())) 9470 return false; 9471 addProperty(CSSPropertyWebkitTextEmphasisStyle, cssValuePool().createIdentifierValue(CSSValueNone), important); 9472 m_valueList->next(); 9473 return true; 9474 } 9475 9476 if (value->id == CSSValueOpen || value->id == CSSValueFilled) { 9477 if (fill) 9478 return false; 9479 fill = cssValuePool().createIdentifierValue(value->id); 9480 } else if (value->id == CSSValueDot || value->id == CSSValueCircle || value->id == CSSValueDoubleCircle || value->id == CSSValueTriangle || value->id == CSSValueSesame) { 9481 if (shape) 9482 return false; 9483 shape = cssValuePool().createIdentifierValue(value->id); 9484 } else if (!inShorthand()) 9485 return false; 9486 else 9487 break; 9488 } 9489 9490 if (fill && shape) { 9491 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated(); 9492 parsedValues->append(fill.release()); 9493 parsedValues->append(shape.release()); 9494 addProperty(CSSPropertyWebkitTextEmphasisStyle, parsedValues.release(), important); 9495 return true; 9496 } 9497 if (fill) { 9498 addProperty(CSSPropertyWebkitTextEmphasisStyle, fill.release(), important); 9499 return true; 9500 } 9501 if (shape) { 9502 addProperty(CSSPropertyWebkitTextEmphasisStyle, shape.release(), important); 9503 return true; 9504 } 9505 9506 return false; 9507} 9508 9509PassRefPtr<CSSValue> CSSParser::parseTextIndent() 9510{ 9511 // <length> | <percentage> | inherit when CSS3_TEXT is disabled. 9512 // [ <length> | <percentage> ] && [ -webkit-hanging || -webkit-each-line ]? | inherit when CSS3_TEXT is enabled. 9513 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); 9514 bool hasLengthOrPercentage = false; 9515#if ENABLE(CSS3_TEXT) 9516 bool hasEachLine = false; 9517 bool hasHanging = false; 9518#endif 9519 9520 CSSParserValue* value = m_valueList->current(); 9521 while (value) { 9522 if (!hasLengthOrPercentage && validUnit(value, FLength | FPercent)) { 9523 list->append(createPrimitiveNumericValue(value)); 9524 hasLengthOrPercentage = true; 9525 } 9526#if ENABLE(CSS3_TEXT) 9527 else if (!hasEachLine && value->id == CSSValueWebkitEachLine) { 9528 list->append(cssValuePool().createIdentifierValue(CSSValueWebkitEachLine)); 9529 hasEachLine = true; 9530 } else if (!hasHanging && value->id == CSSValueWebkitHanging) { 9531 list->append(cssValuePool().createIdentifierValue(CSSValueWebkitHanging)); 9532 hasHanging = true; 9533 } 9534#endif 9535 else 9536 return 0; 9537 9538 value = m_valueList->next(); 9539 } 9540 9541 if (!hasLengthOrPercentage) 9542 return 0; 9543 9544 return list.release(); 9545} 9546 9547bool CSSParser::parseLineBoxContain(bool important) 9548{ 9549 LineBoxContain lineBoxContain = LineBoxContainNone; 9550 9551 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9552 if (value->id == CSSValueBlock) { 9553 if (lineBoxContain & LineBoxContainBlock) 9554 return false; 9555 lineBoxContain |= LineBoxContainBlock; 9556 } else if (value->id == CSSValueInline) { 9557 if (lineBoxContain & LineBoxContainInline) 9558 return false; 9559 lineBoxContain |= LineBoxContainInline; 9560 } else if (value->id == CSSValueFont) { 9561 if (lineBoxContain & LineBoxContainFont) 9562 return false; 9563 lineBoxContain |= LineBoxContainFont; 9564 } else if (value->id == CSSValueGlyphs) { 9565 if (lineBoxContain & LineBoxContainGlyphs) 9566 return false; 9567 lineBoxContain |= LineBoxContainGlyphs; 9568 } else if (value->id == CSSValueReplaced) { 9569 if (lineBoxContain & LineBoxContainReplaced) 9570 return false; 9571 lineBoxContain |= LineBoxContainReplaced; 9572 } else if (value->id == CSSValueInlineBox) { 9573 if (lineBoxContain & LineBoxContainInlineBox) 9574 return false; 9575 lineBoxContain |= LineBoxContainInlineBox; 9576 } else 9577 return false; 9578 } 9579 9580 if (!lineBoxContain) 9581 return false; 9582 9583 addProperty(CSSPropertyWebkitLineBoxContain, CSSLineBoxContainValue::create(lineBoxContain), important); 9584 return true; 9585} 9586 9587bool CSSParser::parseFontFeatureTag(CSSValueList* settings) 9588{ 9589 // Feature tag name consists of 4-letter characters. 9590 static const unsigned tagNameLength = 4; 9591 9592 CSSParserValue* value = m_valueList->current(); 9593 // Feature tag name comes first 9594 if (value->unit != CSSPrimitiveValue::CSS_STRING) 9595 return false; 9596 if (value->string.length() != tagNameLength) 9597 return false; 9598 for (unsigned i = 0; i < tagNameLength; ++i) { 9599 // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification. 9600 UChar character = value->string[i]; 9601 if (character < 0x20 || character > 0x7E) 9602 return false; 9603 } 9604 9605 String tag = value->string; 9606 int tagValue = 1; 9607 // Feature tag values could follow: <integer> | on | off 9608 value = m_valueList->next(); 9609 if (value) { 9610 if (value->unit == CSSPrimitiveValue::CSS_NUMBER && value->isInt && value->fValue >= 0) { 9611 tagValue = clampToInteger(value->fValue); 9612 if (tagValue < 0) 9613 return false; 9614 m_valueList->next(); 9615 } else if (value->id == CSSValueOn || value->id == CSSValueOff) { 9616 tagValue = value->id == CSSValueOn; 9617 m_valueList->next(); 9618 } 9619 } 9620 settings->append(FontFeatureValue::create(tag, tagValue)); 9621 return true; 9622} 9623 9624bool CSSParser::parseFontFeatureSettings(bool important) 9625{ 9626 if (m_valueList->size() == 1 && m_valueList->current()->id == CSSValueNormal) { 9627 RefPtr<CSSPrimitiveValue> normalValue = cssValuePool().createIdentifierValue(CSSValueNormal); 9628 m_valueList->next(); 9629 addProperty(CSSPropertyWebkitFontFeatureSettings, normalValue.release(), important); 9630 return true; 9631 } 9632 9633 RefPtr<CSSValueList> settings = CSSValueList::createCommaSeparated(); 9634 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9635 if (!parseFontFeatureTag(settings.get())) 9636 return false; 9637 9638 // If the list isn't parsed fully, the current value should be comma. 9639 value = m_valueList->current(); 9640 if (value && !isComma(value)) 9641 return false; 9642 } 9643 if (settings->length()) { 9644 addProperty(CSSPropertyWebkitFontFeatureSettings, settings.release(), important); 9645 return true; 9646 } 9647 return false; 9648} 9649 9650bool CSSParser::parseFontVariantLigatures(bool important) 9651{ 9652 RefPtr<CSSValueList> ligatureValues = CSSValueList::createSpaceSeparated(); 9653 bool sawCommonLigaturesValue = false; 9654 bool sawDiscretionaryLigaturesValue = false; 9655 bool sawHistoricalLigaturesValue = false; 9656 9657 for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { 9658 if (value->unit != CSSPrimitiveValue::CSS_IDENT) 9659 return false; 9660 9661 switch (value->id) { 9662 case CSSValueNoCommonLigatures: 9663 case CSSValueCommonLigatures: 9664 if (sawCommonLigaturesValue) 9665 return false; 9666 sawCommonLigaturesValue = true; 9667 ligatureValues->append(cssValuePool().createIdentifierValue(value->id)); 9668 break; 9669 case CSSValueNoDiscretionaryLigatures: 9670 case CSSValueDiscretionaryLigatures: 9671 if (sawDiscretionaryLigaturesValue) 9672 return false; 9673 sawDiscretionaryLigaturesValue = true; 9674 ligatureValues->append(cssValuePool().createIdentifierValue(value->id)); 9675 break; 9676 case CSSValueNoHistoricalLigatures: 9677 case CSSValueHistoricalLigatures: 9678 if (sawHistoricalLigaturesValue) 9679 return false; 9680 sawHistoricalLigaturesValue = true; 9681 ligatureValues->append(cssValuePool().createIdentifierValue(value->id)); 9682 break; 9683 default: 9684 return false; 9685 } 9686 } 9687 9688 if (!ligatureValues->length()) 9689 return false; 9690 9691 addProperty(CSSPropertyWebkitFontVariantLigatures, ligatureValues.release(), important); 9692 return true; 9693} 9694 9695bool CSSParser::parseCalculation(CSSParserValue* value, CalculationPermittedValueRange range) 9696{ 9697 ASSERT(isCalculation(value)); 9698 9699 CSSParserValueList* args = value->function->args.get(); 9700 if (!args || !args->size()) 9701 return false; 9702 9703 ASSERT(!m_parsedCalculation); 9704 m_parsedCalculation = CSSCalcValue::create(value->function->name, args, range); 9705 9706 if (!m_parsedCalculation) 9707 return false; 9708 9709 return true; 9710} 9711 9712#define END_TOKEN 0 9713 9714#include "CSSGrammar.h" 9715 9716enum CharacterType { 9717 // Types for the main switch. 9718 9719 // The first 4 types must be grouped together, as they 9720 // represent the allowed chars in an identifier. 9721 CharacterCaselessU, 9722 CharacterIdentifierStart, 9723 CharacterNumber, 9724 CharacterDash, 9725 9726 CharacterOther, 9727 CharacterNull, 9728 CharacterWhiteSpace, 9729 CharacterEndMediaQuery, 9730 CharacterEndNthChild, 9731 CharacterQuote, 9732 CharacterExclamationMark, 9733 CharacterHashmark, 9734 CharacterDollar, 9735 CharacterAsterisk, 9736 CharacterPlus, 9737 CharacterDot, 9738 CharacterSlash, 9739 CharacterLess, 9740 CharacterAt, 9741 CharacterBackSlash, 9742 CharacterXor, 9743 CharacterVerticalBar, 9744 CharacterTilde, 9745}; 9746 9747// 128 ASCII codes 9748static const CharacterType typesOfASCIICharacters[128] = { 9749/* 0 - Null */ CharacterNull, 9750/* 1 - Start of Heading */ CharacterOther, 9751/* 2 - Start of Text */ CharacterOther, 9752/* 3 - End of Text */ CharacterOther, 9753/* 4 - End of Transm. */ CharacterOther, 9754/* 5 - Enquiry */ CharacterOther, 9755/* 6 - Acknowledgment */ CharacterOther, 9756/* 7 - Bell */ CharacterOther, 9757/* 8 - Back Space */ CharacterOther, 9758/* 9 - Horizontal Tab */ CharacterWhiteSpace, 9759/* 10 - Line Feed */ CharacterWhiteSpace, 9760/* 11 - Vertical Tab */ CharacterOther, 9761/* 12 - Form Feed */ CharacterWhiteSpace, 9762/* 13 - Carriage Return */ CharacterWhiteSpace, 9763/* 14 - Shift Out */ CharacterOther, 9764/* 15 - Shift In */ CharacterOther, 9765/* 16 - Data Line Escape */ CharacterOther, 9766/* 17 - Device Control 1 */ CharacterOther, 9767/* 18 - Device Control 2 */ CharacterOther, 9768/* 19 - Device Control 3 */ CharacterOther, 9769/* 20 - Device Control 4 */ CharacterOther, 9770/* 21 - Negative Ack. */ CharacterOther, 9771/* 22 - Synchronous Idle */ CharacterOther, 9772/* 23 - End of Transmit */ CharacterOther, 9773/* 24 - Cancel */ CharacterOther, 9774/* 25 - End of Medium */ CharacterOther, 9775/* 26 - Substitute */ CharacterOther, 9776/* 27 - Escape */ CharacterOther, 9777/* 28 - File Separator */ CharacterOther, 9778/* 29 - Group Separator */ CharacterOther, 9779/* 30 - Record Separator */ CharacterOther, 9780/* 31 - Unit Separator */ CharacterOther, 9781/* 32 - Space */ CharacterWhiteSpace, 9782/* 33 - ! */ CharacterExclamationMark, 9783/* 34 - " */ CharacterQuote, 9784/* 35 - # */ CharacterHashmark, 9785/* 36 - $ */ CharacterDollar, 9786/* 37 - % */ CharacterOther, 9787/* 38 - & */ CharacterOther, 9788/* 39 - ' */ CharacterQuote, 9789/* 40 - ( */ CharacterOther, 9790/* 41 - ) */ CharacterEndNthChild, 9791/* 42 - * */ CharacterAsterisk, 9792/* 43 - + */ CharacterPlus, 9793/* 44 - , */ CharacterOther, 9794/* 45 - - */ CharacterDash, 9795/* 46 - . */ CharacterDot, 9796/* 47 - / */ CharacterSlash, 9797/* 48 - 0 */ CharacterNumber, 9798/* 49 - 1 */ CharacterNumber, 9799/* 50 - 2 */ CharacterNumber, 9800/* 51 - 3 */ CharacterNumber, 9801/* 52 - 4 */ CharacterNumber, 9802/* 53 - 5 */ CharacterNumber, 9803/* 54 - 6 */ CharacterNumber, 9804/* 55 - 7 */ CharacterNumber, 9805/* 56 - 8 */ CharacterNumber, 9806/* 57 - 9 */ CharacterNumber, 9807/* 58 - : */ CharacterOther, 9808/* 59 - ; */ CharacterEndMediaQuery, 9809/* 60 - < */ CharacterLess, 9810/* 61 - = */ CharacterOther, 9811/* 62 - > */ CharacterOther, 9812/* 63 - ? */ CharacterOther, 9813/* 64 - @ */ CharacterAt, 9814/* 65 - A */ CharacterIdentifierStart, 9815/* 66 - B */ CharacterIdentifierStart, 9816/* 67 - C */ CharacterIdentifierStart, 9817/* 68 - D */ CharacterIdentifierStart, 9818/* 69 - E */ CharacterIdentifierStart, 9819/* 70 - F */ CharacterIdentifierStart, 9820/* 71 - G */ CharacterIdentifierStart, 9821/* 72 - H */ CharacterIdentifierStart, 9822/* 73 - I */ CharacterIdentifierStart, 9823/* 74 - J */ CharacterIdentifierStart, 9824/* 75 - K */ CharacterIdentifierStart, 9825/* 76 - L */ CharacterIdentifierStart, 9826/* 77 - M */ CharacterIdentifierStart, 9827/* 78 - N */ CharacterIdentifierStart, 9828/* 79 - O */ CharacterIdentifierStart, 9829/* 80 - P */ CharacterIdentifierStart, 9830/* 81 - Q */ CharacterIdentifierStart, 9831/* 82 - R */ CharacterIdentifierStart, 9832/* 83 - S */ CharacterIdentifierStart, 9833/* 84 - T */ CharacterIdentifierStart, 9834/* 85 - U */ CharacterCaselessU, 9835/* 86 - V */ CharacterIdentifierStart, 9836/* 87 - W */ CharacterIdentifierStart, 9837/* 88 - X */ CharacterIdentifierStart, 9838/* 89 - Y */ CharacterIdentifierStart, 9839/* 90 - Z */ CharacterIdentifierStart, 9840/* 91 - [ */ CharacterOther, 9841/* 92 - \ */ CharacterBackSlash, 9842/* 93 - ] */ CharacterOther, 9843/* 94 - ^ */ CharacterXor, 9844/* 95 - _ */ CharacterIdentifierStart, 9845/* 96 - ` */ CharacterOther, 9846/* 97 - a */ CharacterIdentifierStart, 9847/* 98 - b */ CharacterIdentifierStart, 9848/* 99 - c */ CharacterIdentifierStart, 9849/* 100 - d */ CharacterIdentifierStart, 9850/* 101 - e */ CharacterIdentifierStart, 9851/* 102 - f */ CharacterIdentifierStart, 9852/* 103 - g */ CharacterIdentifierStart, 9853/* 104 - h */ CharacterIdentifierStart, 9854/* 105 - i */ CharacterIdentifierStart, 9855/* 106 - j */ CharacterIdentifierStart, 9856/* 107 - k */ CharacterIdentifierStart, 9857/* 108 - l */ CharacterIdentifierStart, 9858/* 109 - m */ CharacterIdentifierStart, 9859/* 110 - n */ CharacterIdentifierStart, 9860/* 111 - o */ CharacterIdentifierStart, 9861/* 112 - p */ CharacterIdentifierStart, 9862/* 113 - q */ CharacterIdentifierStart, 9863/* 114 - r */ CharacterIdentifierStart, 9864/* 115 - s */ CharacterIdentifierStart, 9865/* 116 - t */ CharacterIdentifierStart, 9866/* 117 - u */ CharacterCaselessU, 9867/* 118 - v */ CharacterIdentifierStart, 9868/* 119 - w */ CharacterIdentifierStart, 9869/* 120 - x */ CharacterIdentifierStart, 9870/* 121 - y */ CharacterIdentifierStart, 9871/* 122 - z */ CharacterIdentifierStart, 9872/* 123 - { */ CharacterEndMediaQuery, 9873/* 124 - | */ CharacterVerticalBar, 9874/* 125 - } */ CharacterOther, 9875/* 126 - ~ */ CharacterTilde, 9876/* 127 - Delete */ CharacterOther, 9877}; 9878 9879// Utility functions for the CSS tokenizer. 9880 9881template <typename CharacterType> 9882static inline bool isCSSLetter(CharacterType character) 9883{ 9884 return character >= 128 || typesOfASCIICharacters[character] <= CharacterDash; 9885} 9886 9887template <typename CharacterType> 9888static inline bool isCSSEscape(CharacterType character) 9889{ 9890 return character >= ' ' && character != 127; 9891} 9892 9893template <typename CharacterType> 9894static inline bool isURILetter(CharacterType character) 9895{ 9896 return (character >= '*' && character != 127) || (character >= '#' && character <= '&') || character == '!'; 9897} 9898 9899template <typename CharacterType> 9900static inline bool isIdentifierStartAfterDash(CharacterType* currentCharacter) 9901{ 9902 return isASCIIAlpha(currentCharacter[0]) || currentCharacter[0] == '_' || currentCharacter[0] >= 128 9903 || (currentCharacter[0] == '\\' && isCSSEscape(currentCharacter[1])); 9904} 9905 9906template <typename CharacterType> 9907static inline bool isEqualToCSSIdentifier(CharacterType* cssString, const char* constantString) 9908{ 9909 // Compare an character memory data with a zero terminated string. 9910 do { 9911 // The input must be part of an identifier if constantChar or constString 9912 // contains '-'. Otherwise toASCIILowerUnchecked('\r') would be equal to '-'. 9913 ASSERT((*constantString >= 'a' && *constantString <= 'z') || *constantString == '-'); 9914 ASSERT(*constantString != '-' || isCSSLetter(*cssString)); 9915 if (toASCIILowerUnchecked(*cssString++) != (*constantString++)) 9916 return false; 9917 } while (*constantString); 9918 return true; 9919} 9920 9921template <typename CharacterType> 9922static inline bool isEqualToCSSCaseSensitiveIdentifier(CharacterType* string, const char* constantString) 9923{ 9924 do { 9925 if (*string++ != *constantString++) 9926 return false; 9927 } while (*constantString); 9928 return true; 9929} 9930 9931template <typename CharacterType> 9932static CharacterType* checkAndSkipEscape(CharacterType* currentCharacter) 9933{ 9934 // Returns with 0, if escape check is failed. Otherwise 9935 // it returns with the following character. 9936 ASSERT(*currentCharacter == '\\'); 9937 9938 ++currentCharacter; 9939 if (!isCSSEscape(*currentCharacter)) 9940 return 0; 9941 9942 if (isASCIIHexDigit(*currentCharacter)) { 9943 int length = 6; 9944 9945 do { 9946 ++currentCharacter; 9947 } while (isASCIIHexDigit(*currentCharacter) && --length); 9948 9949 // Optional space after the escape sequence. 9950 if (isHTMLSpace(*currentCharacter)) 9951 ++currentCharacter; 9952 return currentCharacter; 9953 } 9954 return currentCharacter + 1; 9955} 9956 9957template <typename CharacterType> 9958static inline CharacterType* skipWhiteSpace(CharacterType* currentCharacter) 9959{ 9960 while (isHTMLSpace(*currentCharacter)) 9961 ++currentCharacter; 9962 return currentCharacter; 9963} 9964 9965// Main CSS tokenizer functions. 9966 9967template <> 9968LChar* CSSParserString::characters<LChar>() const { return characters8(); } 9969 9970template <> 9971UChar* CSSParserString::characters<UChar>() const { return characters16(); } 9972 9973template <> 9974inline LChar*& CSSParser::currentCharacter<LChar>() 9975{ 9976 return m_currentCharacter8; 9977} 9978 9979template <> 9980inline UChar*& CSSParser::currentCharacter<UChar>() 9981{ 9982 return m_currentCharacter16; 9983} 9984 9985UChar*& CSSParser::currentCharacter16() 9986{ 9987 if (!m_currentCharacter16) { 9988 m_dataStart16 = adoptArrayPtr(new UChar[m_length]); 9989 m_currentCharacter16 = m_dataStart16.get(); 9990 } 9991 9992 return m_currentCharacter16; 9993} 9994 9995template <> 9996inline LChar* CSSParser::tokenStart<LChar>() 9997{ 9998 return m_tokenStart.ptr8; 9999} 10000 10001template <> 10002inline UChar* CSSParser::tokenStart<UChar>() 10003{ 10004 return m_tokenStart.ptr16; 10005} 10006 10007CSSParser::Location CSSParser::currentLocation() 10008{ 10009 Location location; 10010 location.lineNumber = m_tokenStartLineNumber; 10011 if (is8BitSource()) 10012 location.token.init(tokenStart<LChar>(), currentCharacter<LChar>() - tokenStart<LChar>()); 10013 else 10014 location.token.init(tokenStart<UChar>(), currentCharacter<UChar>() - tokenStart<UChar>()); 10015 return location; 10016} 10017 10018template <typename CharacterType> 10019inline bool CSSParser::isIdentifierStart() 10020{ 10021 // Check whether an identifier is started. 10022 return isIdentifierStartAfterDash((*currentCharacter<CharacterType>() != '-') ? currentCharacter<CharacterType>() : currentCharacter<CharacterType>() + 1); 10023} 10024 10025template <typename CharacterType> 10026static inline CharacterType* checkAndSkipString(CharacterType* currentCharacter, int quote) 10027{ 10028 // Returns with 0, if string check is failed. Otherwise 10029 // it returns with the following character. This is necessary 10030 // since we cannot revert escape sequences, thus strings 10031 // must be validated before parsing. 10032 while (true) { 10033 if (UNLIKELY(*currentCharacter == quote)) { 10034 // String parsing is successful. 10035 return currentCharacter + 1; 10036 } 10037 if (UNLIKELY(!*currentCharacter)) { 10038 // String parsing is successful up to end of input. 10039 return currentCharacter; 10040 } 10041 if (UNLIKELY(*currentCharacter <= '\r' && (*currentCharacter == '\n' || (*currentCharacter | 0x1) == '\r'))) { 10042 // String parsing is failed for character '\n', '\f' or '\r'. 10043 return 0; 10044 } 10045 10046 if (LIKELY(currentCharacter[0] != '\\')) 10047 ++currentCharacter; 10048 else if (currentCharacter[1] == '\n' || currentCharacter[1] == '\f') 10049 currentCharacter += 2; 10050 else if (currentCharacter[1] == '\r') 10051 currentCharacter += currentCharacter[2] == '\n' ? 3 : 2; 10052 else { 10053 currentCharacter = checkAndSkipEscape(currentCharacter); 10054 if (!currentCharacter) 10055 return 0; 10056 } 10057 } 10058} 10059 10060template <typename CharacterType> 10061unsigned CSSParser::parseEscape(CharacterType*& src) 10062{ 10063 ASSERT(*src == '\\' && isCSSEscape(src[1])); 10064 10065 unsigned unicode = 0; 10066 10067 ++src; 10068 if (isASCIIHexDigit(*src)) { 10069 10070 int length = 6; 10071 10072 do { 10073 unicode = (unicode << 4) + toASCIIHexValue(*src++); 10074 } while (--length && isASCIIHexDigit(*src)); 10075 10076 // Characters above 0x10ffff are not handled. 10077 if (unicode > 0x10ffff) 10078 unicode = 0xfffd; 10079 10080 // Optional space after the escape sequence. 10081 if (isHTMLSpace(*src)) 10082 ++src; 10083 10084 return unicode; 10085 } 10086 10087 return *currentCharacter<CharacterType>()++; 10088} 10089 10090template <> 10091inline void CSSParser::UnicodeToChars<LChar>(LChar*& result, unsigned unicode) 10092{ 10093 ASSERT(unicode <= 0xff); 10094 *result = unicode; 10095 10096 ++result; 10097} 10098 10099template <> 10100inline void CSSParser::UnicodeToChars<UChar>(UChar*& result, unsigned unicode) 10101{ 10102 // Replace unicode with a surrogate pairs when it is bigger than 0xffff 10103 if (U16_LENGTH(unicode) == 2) { 10104 *result++ = U16_LEAD(unicode); 10105 *result = U16_TRAIL(unicode); 10106 } else 10107 *result = unicode; 10108 10109 ++result; 10110} 10111 10112template <typename SrcCharacterType, typename DestCharacterType> 10113inline bool CSSParser::parseIdentifierInternal(SrcCharacterType*& src, DestCharacterType*& result, bool& hasEscape) 10114{ 10115 hasEscape = false; 10116 do { 10117 if (LIKELY(*src != '\\')) 10118 *result++ = *src++; 10119 else { 10120 hasEscape = true; 10121 SrcCharacterType* savedEscapeStart = src; 10122 unsigned unicode = parseEscape<SrcCharacterType>(src); 10123 if (unicode > 0xff && sizeof(DestCharacterType) == 1) { 10124 src = savedEscapeStart; 10125 return false; 10126 } 10127 UnicodeToChars(result, unicode); 10128 } 10129 } while (isCSSLetter(src[0]) || (src[0] == '\\' && isCSSEscape(src[1]))); 10130 10131 return true; 10132} 10133 10134template <typename CharacterType> 10135inline void CSSParser::parseIdentifier(CharacterType*& result, CSSParserString& resultString, bool& hasEscape) 10136{ 10137 // If a valid identifier start is found, we can safely 10138 // parse the identifier until the next invalid character. 10139 ASSERT(isIdentifierStart<CharacterType>()); 10140 10141 CharacterType* start = currentCharacter<CharacterType>(); 10142 if (UNLIKELY(!parseIdentifierInternal(currentCharacter<CharacterType>(), result, hasEscape))) { 10143 // Found an escape we couldn't handle with 8 bits, copy what has been recognized and continue 10144 ASSERT(is8BitSource()); 10145 UChar*& result16 = currentCharacter16(); 10146 UChar* start16 = result16; 10147 int i = 0; 10148 for (; i < result - start; i++) 10149 result16[i] = start[i]; 10150 10151 result16 += i; 10152 10153 parseIdentifierInternal(currentCharacter<CharacterType>(), result16, hasEscape); 10154 10155 resultString.init(start16, result16 - start16); 10156 10157 return; 10158 } 10159 10160 resultString.init(start, result - start); 10161} 10162 10163template <typename SrcCharacterType, typename DestCharacterType> 10164inline bool CSSParser::parseStringInternal(SrcCharacterType*& src, DestCharacterType*& result, UChar quote) 10165{ 10166 while (true) { 10167 if (UNLIKELY(*src == quote)) { 10168 // String parsing is done. 10169 ++src; 10170 return true; 10171 } 10172 if (UNLIKELY(!*src)) { 10173 // String parsing is done, but don't advance pointer if at the end of input. 10174 return true; 10175 } 10176 ASSERT(*src > '\r' || (*src < '\n' && *src) || *src == '\v'); 10177 10178 if (LIKELY(src[0] != '\\')) 10179 *result++ = *src++; 10180 else if (src[1] == '\n' || src[1] == '\f') 10181 src += 2; 10182 else if (src[1] == '\r') 10183 src += src[2] == '\n' ? 3 : 2; 10184 else { 10185 SrcCharacterType* savedEscapeStart = src; 10186 unsigned unicode = parseEscape<SrcCharacterType>(src); 10187 if (unicode > 0xff && sizeof(DestCharacterType) == 1) { 10188 src = savedEscapeStart; 10189 return false; 10190 } 10191 UnicodeToChars(result, unicode); 10192 } 10193 } 10194 10195 return true; 10196} 10197 10198template <typename CharacterType> 10199inline void CSSParser::parseString(CharacterType*& result, CSSParserString& resultString, UChar quote) 10200{ 10201 CharacterType* start = currentCharacter<CharacterType>(); 10202 10203 if (UNLIKELY(!parseStringInternal(currentCharacter<CharacterType>(), result, quote))) { 10204 // Found an escape we couldn't handle with 8 bits, copy what has been recognized and continue 10205 ASSERT(is8BitSource()); 10206 UChar*& result16 = currentCharacter16(); 10207 UChar* start16 = result16; 10208 int i = 0; 10209 for (; i < result - start; i++) 10210 result16[i] = start[i]; 10211 10212 result16 += i; 10213 10214 parseStringInternal(currentCharacter<CharacterType>(), result16, quote); 10215 10216 resultString.init(start16, result16 - start16); 10217 return; 10218 } 10219 10220 resultString.init(start, result - start); 10221} 10222 10223template <typename CharacterType> 10224inline bool CSSParser::findURI(CharacterType*& start, CharacterType*& end, UChar& quote) 10225{ 10226 start = skipWhiteSpace(currentCharacter<CharacterType>()); 10227 10228 if (*start == '"' || *start == '\'') { 10229 quote = *start++; 10230 end = checkAndSkipString(start, quote); 10231 if (!end) 10232 return false; 10233 } else { 10234 quote = 0; 10235 end = start; 10236 while (isURILetter(*end)) { 10237 if (LIKELY(*end != '\\')) 10238 ++end; 10239 else { 10240 end = checkAndSkipEscape(end); 10241 if (!end) 10242 return false; 10243 } 10244 } 10245 } 10246 10247 end = skipWhiteSpace(end); 10248 if (*end != ')') 10249 return false; 10250 10251 return true; 10252} 10253 10254template <typename SrcCharacterType, typename DestCharacterType> 10255inline bool CSSParser::parseURIInternal(SrcCharacterType*& src, DestCharacterType*& dest, UChar quote) 10256{ 10257 if (quote) { 10258 ASSERT(quote == '"' || quote == '\''); 10259 return parseStringInternal(src, dest, quote); 10260 } 10261 10262 while (isURILetter(*src)) { 10263 if (LIKELY(*src != '\\')) 10264 *dest++ = *src++; 10265 else { 10266 unsigned unicode = parseEscape<SrcCharacterType>(src); 10267 if (unicode > 0xff && sizeof(SrcCharacterType) == 1) 10268 return false; 10269 UnicodeToChars(dest, unicode); 10270 } 10271 } 10272 10273 return true; 10274} 10275 10276template <typename CharacterType> 10277inline void CSSParser::parseURI(CSSParserString& string) 10278{ 10279 CharacterType* uriStart; 10280 CharacterType* uriEnd; 10281 UChar quote; 10282 if (!findURI(uriStart, uriEnd, quote)) 10283 return; 10284 10285 CharacterType* dest = currentCharacter<CharacterType>() = uriStart; 10286 if (LIKELY(parseURIInternal(currentCharacter<CharacterType>(), dest, quote))) 10287 string.init(uriStart, dest - uriStart); 10288 else { 10289 // An escape sequence was encountered that can't be stored in 8 bits. 10290 // Reset the current character to the start of the URI and re-parse with 10291 // a 16-bit destination. 10292 ASSERT(is8BitSource()); 10293 UChar* uriStart16 = currentCharacter16(); 10294 currentCharacter<CharacterType>() = uriStart; 10295 bool result = parseURIInternal(currentCharacter<CharacterType>(), currentCharacter16(), quote); 10296 ASSERT_UNUSED(result, result); 10297 string.init(uriStart16, currentCharacter16() - uriStart16); 10298 } 10299 10300 currentCharacter<CharacterType>() = uriEnd + 1; 10301 m_token = URI; 10302} 10303 10304template <typename CharacterType> 10305inline bool CSSParser::parseUnicodeRange() 10306{ 10307 CharacterType* character = currentCharacter<CharacterType>() + 1; 10308 int length = 6; 10309 ASSERT(*currentCharacter<CharacterType>() == '+'); 10310 10311 while (isASCIIHexDigit(*character) && length) { 10312 ++character; 10313 --length; 10314 } 10315 10316 if (length && *character == '?') { 10317 // At most 5 hex digit followed by a question mark. 10318 do { 10319 ++character; 10320 --length; 10321 } while (*character == '?' && length); 10322 currentCharacter<CharacterType>() = character; 10323 return true; 10324 } 10325 10326 if (length < 6) { 10327 // At least one hex digit. 10328 if (character[0] == '-' && isASCIIHexDigit(character[1])) { 10329 // Followed by a dash and a hex digit. 10330 ++character; 10331 length = 6; 10332 do { 10333 ++character; 10334 } while (--length && isASCIIHexDigit(*character)); 10335 } 10336 currentCharacter<CharacterType>() = character; 10337 return true; 10338 } 10339 return false; 10340} 10341 10342template <typename CharacterType> 10343bool CSSParser::parseNthChild() 10344{ 10345 CharacterType* character = currentCharacter<CharacterType>(); 10346 10347 while (isASCIIDigit(*character)) 10348 ++character; 10349 if (isASCIIAlphaCaselessEqual(*character, 'n')) { 10350 currentCharacter<CharacterType>() = character + 1; 10351 return true; 10352 } 10353 return false; 10354} 10355 10356template <typename CharacterType> 10357bool CSSParser::parseNthChildExtra() 10358{ 10359 CharacterType* character = skipWhiteSpace(currentCharacter<CharacterType>()); 10360 if (*character != '+' && *character != '-') 10361 return false; 10362 10363 character = skipWhiteSpace(character + 1); 10364 if (!isASCIIDigit(*character)) 10365 return false; 10366 10367 do { 10368 ++character; 10369 } while (isASCIIDigit(*character)); 10370 10371 currentCharacter<CharacterType>() = character; 10372 return true; 10373} 10374 10375template <typename CharacterType> 10376inline bool CSSParser::detectFunctionTypeToken(int length) 10377{ 10378 ASSERT(length > 0); 10379 CharacterType* name = tokenStart<CharacterType>(); 10380 10381 switch (length) { 10382 case 3: 10383 if (isASCIIAlphaCaselessEqual(name[0], 'n') && isASCIIAlphaCaselessEqual(name[1], 'o') && isASCIIAlphaCaselessEqual(name[2], 't')) { 10384 m_token = NOTFUNCTION; 10385 return true; 10386 } 10387 if (isASCIIAlphaCaselessEqual(name[0], 'u') && isASCIIAlphaCaselessEqual(name[1], 'r') && isASCIIAlphaCaselessEqual(name[2], 'l')) { 10388 m_token = URI; 10389 return true; 10390 } 10391#if ENABLE(VIDEO_TRACK) 10392 if (isASCIIAlphaCaselessEqual(name[0], 'c') && isASCIIAlphaCaselessEqual(name[1], 'u') && isASCIIAlphaCaselessEqual(name[2], 'e')) { 10393 m_token = CUEFUNCTION; 10394 return true; 10395 } 10396#endif 10397 return false; 10398 10399 case 4: 10400 if (isEqualToCSSIdentifier(name, "calc")) { 10401 m_token = CALCFUNCTION; 10402 return true; 10403 } 10404 return false; 10405 10406 case 9: 10407 if (isEqualToCSSIdentifier(name, "nth-child")) { 10408 m_parsingMode = NthChildMode; 10409 return true; 10410 } 10411 return false; 10412 10413 case 11: 10414 if (isEqualToCSSIdentifier(name, "nth-of-type")) { 10415 m_parsingMode = NthChildMode; 10416 return true; 10417 } 10418 return false; 10419 10420 case 14: 10421 if (isEqualToCSSIdentifier(name, "nth-last-child")) { 10422 m_parsingMode = NthChildMode; 10423 return true; 10424 } 10425 return false; 10426 10427 case 16: 10428 if (isEqualToCSSIdentifier(name, "nth-last-of-type")) { 10429 m_parsingMode = NthChildMode; 10430 return true; 10431 } 10432 return false; 10433 } 10434 10435 return false; 10436} 10437 10438template <typename CharacterType> 10439inline void CSSParser::detectMediaQueryToken(int length) 10440{ 10441 ASSERT(m_parsingMode == MediaQueryMode); 10442 CharacterType* name = tokenStart<CharacterType>(); 10443 10444 if (length == 3) { 10445 if (isASCIIAlphaCaselessEqual(name[0], 'a') && isASCIIAlphaCaselessEqual(name[1], 'n') && isASCIIAlphaCaselessEqual(name[2], 'd')) 10446 m_token = MEDIA_AND; 10447 else if (isASCIIAlphaCaselessEqual(name[0], 'n') && isASCIIAlphaCaselessEqual(name[1], 'o') && isASCIIAlphaCaselessEqual(name[2], 't')) 10448 m_token = MEDIA_NOT; 10449 } else if (length == 4) { 10450 if (isASCIIAlphaCaselessEqual(name[0], 'o') && isASCIIAlphaCaselessEqual(name[1], 'n') 10451 && isASCIIAlphaCaselessEqual(name[2], 'l') && isASCIIAlphaCaselessEqual(name[3], 'y')) 10452 m_token = MEDIA_ONLY; 10453 } 10454} 10455 10456template <typename CharacterType> 10457inline void CSSParser::detectNumberToken(CharacterType* type, int length) 10458{ 10459 ASSERT(length > 0); 10460 10461 switch (toASCIILowerUnchecked(type[0])) { 10462 case 'c': 10463 if (length == 2 && isASCIIAlphaCaselessEqual(type[1], 'm')) 10464 m_token = CMS; 10465 else if (length == 2 && isASCIIAlphaCaselessEqual(type[1], 'h')) 10466 m_token = CHS; 10467 return; 10468 10469 case 'd': 10470 if (length == 3 && isASCIIAlphaCaselessEqual(type[1], 'e') && isASCIIAlphaCaselessEqual(type[2], 'g')) 10471 m_token = DEGS; 10472#if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY) 10473 else if (length > 2 && isASCIIAlphaCaselessEqual(type[1], 'p')) { 10474 if (length == 4) { 10475 // There is a discussion about the name of this unit on www-style. 10476 // Keep this compile time guard in place until that is resolved. 10477 // http://lists.w3.org/Archives/Public/www-style/2012May/0915.html 10478 if (isASCIIAlphaCaselessEqual(type[2], 'p') && isASCIIAlphaCaselessEqual(type[3], 'x')) 10479 m_token = DPPX; 10480 else if (isASCIIAlphaCaselessEqual(type[2], 'c') && isASCIIAlphaCaselessEqual(type[3], 'm')) 10481 m_token = DPCM; 10482 } else if (length == 3 && isASCIIAlphaCaselessEqual(type[2], 'i')) 10483 m_token = DPI; 10484 } 10485#endif 10486 return; 10487 10488 case 'e': 10489 if (length == 2) { 10490 if (isASCIIAlphaCaselessEqual(type[1], 'm')) 10491 m_token = EMS; 10492 else if (isASCIIAlphaCaselessEqual(type[1], 'x')) 10493 m_token = EXS; 10494 } 10495 return; 10496 10497 case 'g': 10498 if (length == 4 && isASCIIAlphaCaselessEqual(type[1], 'r') 10499 && isASCIIAlphaCaselessEqual(type[2], 'a') && isASCIIAlphaCaselessEqual(type[3], 'd')) 10500 m_token = GRADS; 10501 return; 10502 10503 case 'h': 10504 if (length == 2 && isASCIIAlphaCaselessEqual(type[1], 'z')) 10505 m_token = HERTZ; 10506 return; 10507 10508 case 'i': 10509 if (length == 2 && isASCIIAlphaCaselessEqual(type[1], 'n')) 10510 m_token = INS; 10511 return; 10512 10513 case 'k': 10514 if (length == 3 && isASCIIAlphaCaselessEqual(type[1], 'h') && isASCIIAlphaCaselessEqual(type[2], 'z')) 10515 m_token = KHERTZ; 10516 return; 10517 10518 case 'm': 10519 if (length == 2) { 10520 if (isASCIIAlphaCaselessEqual(type[1], 'm')) 10521 m_token = MMS; 10522 else if (isASCIIAlphaCaselessEqual(type[1], 's')) 10523 m_token = MSECS; 10524 } 10525 return; 10526 10527 case 'p': 10528 if (length == 2) { 10529 if (isASCIIAlphaCaselessEqual(type[1], 'x')) 10530 m_token = PXS; 10531 else if (isASCIIAlphaCaselessEqual(type[1], 't')) 10532 m_token = PTS; 10533 else if (isASCIIAlphaCaselessEqual(type[1], 'c')) 10534 m_token = PCS; 10535 } 10536 return; 10537 10538 case 'r': 10539 if (length == 3) { 10540 if (isASCIIAlphaCaselessEqual(type[1], 'a') && isASCIIAlphaCaselessEqual(type[2], 'd')) 10541 m_token = RADS; 10542 else if (isASCIIAlphaCaselessEqual(type[1], 'e') && isASCIIAlphaCaselessEqual(type[2], 'm')) 10543 m_token = REMS; 10544 } 10545 return; 10546 10547 case 's': 10548 if (length == 1) 10549 m_token = SECS; 10550 return; 10551 10552 case 't': 10553 if (length == 4 && isASCIIAlphaCaselessEqual(type[1], 'u') 10554 && isASCIIAlphaCaselessEqual(type[2], 'r') && isASCIIAlphaCaselessEqual(type[3], 'n')) 10555 m_token = TURNS; 10556 return; 10557 case 'v': 10558 if (length == 2) { 10559 if (isASCIIAlphaCaselessEqual(type[1], 'w')) 10560 m_token = VW; 10561 else if (isASCIIAlphaCaselessEqual(type[1], 'h')) 10562 m_token = VH; 10563 } else if (length == 4 && isASCIIAlphaCaselessEqual(type[1], 'm')) { 10564 if (isASCIIAlphaCaselessEqual(type[2], 'i') && isASCIIAlphaCaselessEqual(type[3], 'n')) 10565 m_token = VMIN; 10566 else if (isASCIIAlphaCaselessEqual(type[2], 'a') && isASCIIAlphaCaselessEqual(type[3], 'x')) 10567 m_token = VMAX; 10568 } 10569 return; 10570 10571 default: 10572 if (type[0] == '_' && length == 5 && type[1] == '_' && isASCIIAlphaCaselessEqual(type[2], 'q') 10573 && isASCIIAlphaCaselessEqual(type[3], 'e') && isASCIIAlphaCaselessEqual(type[4], 'm')) 10574 m_token = QEMS; 10575 return; 10576 } 10577} 10578 10579template <typename CharacterType> 10580inline void CSSParser::detectDashToken(int length) 10581{ 10582 CharacterType* name = tokenStart<CharacterType>(); 10583 10584 if (length == 11) { 10585 if (isASCIIAlphaCaselessEqual(name[10], 'y') && isEqualToCSSIdentifier(name + 1, "webkit-an")) 10586 m_token = ANYFUNCTION; 10587 else if (isASCIIAlphaCaselessEqual(name[10], 'n') && isEqualToCSSIdentifier(name + 1, "webkit-mi")) 10588 m_token = MINFUNCTION; 10589 else if (isASCIIAlphaCaselessEqual(name[10], 'x') && isEqualToCSSIdentifier(name + 1, "webkit-ma")) 10590 m_token = MAXFUNCTION; 10591#if ENABLE(CSS_VARIABLES) 10592 else if (cssVariablesEnabled() && isASCIIAlphaCaselessEqual(name[10], 'r') && isEqualToCSSIdentifier(name + 1, "webkit-va")) 10593 m_token = VARFUNCTION; 10594#endif 10595 } else if (length == 12 && isEqualToCSSIdentifier(name + 1, "webkit-calc")) 10596 m_token = CALCFUNCTION; 10597#if ENABLE(SHADOW_DOM) 10598 else if (length == 19 && isEqualToCSSIdentifier(name + 1, "webkit-distributed")) 10599 m_token = DISTRIBUTEDFUNCTION; 10600#endif 10601} 10602 10603template <typename CharacterType> 10604inline void CSSParser::detectAtToken(int length, bool hasEscape) 10605{ 10606 CharacterType* name = tokenStart<CharacterType>(); 10607 ASSERT(name[0] == '@' && length >= 2); 10608 10609 // charset, font-face, import, media, namespace, page, supports, 10610 // -webkit-keyframes, and -webkit-mediaquery are not affected by hasEscape. 10611 switch (toASCIILowerUnchecked(name[1])) { 10612 case 'b': 10613 if (hasEscape) 10614 return; 10615 10616 switch (length) { 10617 case 12: 10618 if (isEqualToCSSIdentifier(name + 2, "ottom-left")) 10619 m_token = BOTTOMLEFT_SYM; 10620 return; 10621 10622 case 13: 10623 if (isEqualToCSSIdentifier(name + 2, "ottom-right")) 10624 m_token = BOTTOMRIGHT_SYM; 10625 return; 10626 10627 case 14: 10628 if (isEqualToCSSIdentifier(name + 2, "ottom-center")) 10629 m_token = BOTTOMCENTER_SYM; 10630 return; 10631 10632 case 19: 10633 if (isEqualToCSSIdentifier(name + 2, "ottom-left-corner")) 10634 m_token = BOTTOMLEFTCORNER_SYM; 10635 return; 10636 10637 case 20: 10638 if (isEqualToCSSIdentifier(name + 2, "ottom-right-corner")) 10639 m_token = BOTTOMRIGHTCORNER_SYM; 10640 return; 10641 } 10642 return; 10643 10644 case 'c': 10645 if (length == 8 && isEqualToCSSIdentifier(name + 2, "harset")) 10646 m_token = CHARSET_SYM; 10647 return; 10648 10649 case 'f': 10650 if (length == 10 && isEqualToCSSIdentifier(name + 2, "ont-face")) 10651 m_token = FONT_FACE_SYM; 10652 return; 10653 10654#if ENABLE(SHADOW_DOM) 10655 case 'h': 10656 if (length == 5 && isEqualToCSSIdentifier(name + 2, "ost")) 10657 m_token = HOST_SYM; 10658 return; 10659#endif 10660 10661 case 'i': 10662 if (length == 7 && isEqualToCSSIdentifier(name + 2, "mport")) { 10663 m_parsingMode = MediaQueryMode; 10664 m_token = IMPORT_SYM; 10665 } 10666 return; 10667 10668 case 'l': 10669 if (hasEscape) 10670 return; 10671 10672 if (length == 9) { 10673 if (isEqualToCSSIdentifier(name + 2, "eft-top")) 10674 m_token = LEFTTOP_SYM; 10675 } else if (length == 12) { 10676 // Checking the last character first could further reduce the possibile cases. 10677 if (isASCIIAlphaCaselessEqual(name[11], 'e') && isEqualToCSSIdentifier(name + 2, "eft-middl")) 10678 m_token = LEFTMIDDLE_SYM; 10679 else if (isASCIIAlphaCaselessEqual(name[11], 'm') && isEqualToCSSIdentifier(name + 2, "eft-botto")) 10680 m_token = LEFTBOTTOM_SYM; 10681 } 10682 return; 10683 10684 case 'm': 10685 if (length == 6 && isEqualToCSSIdentifier(name + 2, "edia")) { 10686 m_parsingMode = MediaQueryMode; 10687 m_token = MEDIA_SYM; 10688 } 10689 return; 10690 10691 case 'n': 10692 if (length == 10 && isEqualToCSSIdentifier(name + 2, "amespace")) 10693 m_token = NAMESPACE_SYM; 10694 return; 10695 10696 case 'p': 10697 if (length == 5 && isEqualToCSSIdentifier(name + 2, "age")) 10698 m_token = PAGE_SYM; 10699 return; 10700 10701 case 'r': 10702 if (hasEscape) 10703 return; 10704 10705 if (length == 10) { 10706 if (isEqualToCSSIdentifier(name + 2, "ight-top")) 10707 m_token = RIGHTTOP_SYM; 10708 } else if (length == 13) { 10709 // Checking the last character first could further reduce the possibile cases. 10710 if (isASCIIAlphaCaselessEqual(name[12], 'e') && isEqualToCSSIdentifier(name + 2, "ight-middl")) 10711 m_token = RIGHTMIDDLE_SYM; 10712 else if (isASCIIAlphaCaselessEqual(name[12], 'm') && isEqualToCSSIdentifier(name + 2, "ight-botto")) 10713 m_token = RIGHTBOTTOM_SYM; 10714 } 10715 return; 10716 10717#if ENABLE(CSS3_CONDITIONAL_RULES) 10718 case 's': 10719 if (length == 9 && isEqualToCSSIdentifier(name + 2, "upports")) { 10720 m_parsingMode = SupportsMode; 10721 m_token = SUPPORTS_SYM; 10722 } 10723 return; 10724#endif 10725 10726 case 't': 10727 if (hasEscape) 10728 return; 10729 10730 switch (length) { 10731 case 9: 10732 if (isEqualToCSSIdentifier(name + 2, "op-left")) 10733 m_token = TOPLEFT_SYM; 10734 return; 10735 10736 case 10: 10737 if (isEqualToCSSIdentifier(name + 2, "op-right")) 10738 m_token = TOPRIGHT_SYM; 10739 return; 10740 10741 case 11: 10742 if (isEqualToCSSIdentifier(name + 2, "op-center")) 10743 m_token = TOPCENTER_SYM; 10744 return; 10745 10746 case 16: 10747 if (isEqualToCSSIdentifier(name + 2, "op-left-corner")) 10748 m_token = TOPLEFTCORNER_SYM; 10749 return; 10750 10751 case 17: 10752 if (isEqualToCSSIdentifier(name + 2, "op-right-corner")) 10753 m_token = TOPRIGHTCORNER_SYM; 10754 return; 10755 } 10756 return; 10757 10758 case '-': 10759 switch (length) { 10760 case 13: 10761 if (!hasEscape && isEqualToCSSIdentifier(name + 2, "webkit-rule")) 10762 m_token = WEBKIT_RULE_SYM; 10763 return; 10764 10765 case 14: 10766 if (hasEscape) 10767 return; 10768 10769 // Checking the last character first could further reduce the possibile cases. 10770 if (isASCIIAlphaCaselessEqual(name[13], 's') && isEqualToCSSIdentifier(name + 2, "webkit-decl")) 10771 m_token = WEBKIT_DECLS_SYM; 10772 else if (isASCIIAlphaCaselessEqual(name[13], 'e') && isEqualToCSSIdentifier(name + 2, "webkit-valu")) 10773 m_token = WEBKIT_VALUE_SYM; 10774 return; 10775 10776 case 15: 10777 if (hasEscape) 10778 return; 10779 10780#if ENABLE(CSS_REGIONS) 10781 if (isASCIIAlphaCaselessEqual(name[14], 'n') && isEqualToCSSIdentifier(name + 2, "webkit-regio")) { 10782 m_token = WEBKIT_REGION_RULE_SYM; 10783 return; 10784 } 10785#endif 10786#if ENABLE(CSS_SHADERS) 10787 if (isASCIIAlphaCaselessEqual(name[14], 'r') && isEqualToCSSIdentifier(name + 2, "webkit-filte")) { 10788 m_token = WEBKIT_FILTER_RULE_SYM; 10789 return; 10790 } 10791#endif 10792 return; 10793 10794 case 17: 10795 if (hasEscape) 10796 return; 10797 10798 if (isASCIIAlphaCaselessEqual(name[16], 'r') && isEqualToCSSIdentifier(name + 2, "webkit-selecto")) 10799 m_token = WEBKIT_SELECTOR_SYM; 10800#if ENABLE(CSS_DEVICE_ADAPTATION) 10801 else if (isASCIIAlphaCaselessEqual(name[16], 't') && isEqualToCSSIdentifier(name + 2, "webkit-viewpor")) 10802 m_token = WEBKIT_VIEWPORT_RULE_SYM; 10803#endif 10804 return; 10805 10806 case 18: 10807 if (isEqualToCSSIdentifier(name + 2, "webkit-keyframes")) 10808 m_token = WEBKIT_KEYFRAMES_SYM; 10809 return; 10810 10811 case 19: 10812 if (isEqualToCSSIdentifier(name + 2, "webkit-mediaquery")) { 10813 m_parsingMode = MediaQueryMode; 10814 m_token = WEBKIT_MEDIAQUERY_SYM; 10815 } 10816 return; 10817 10818 case 22: 10819 if (!hasEscape && isEqualToCSSIdentifier(name + 2, "webkit-keyframe-rule")) 10820 m_token = WEBKIT_KEYFRAME_RULE_SYM; 10821 return; 10822 10823 case 27: 10824#if ENABLE(CSS3_CONDITIONAL_RULES) 10825 if (isEqualToCSSIdentifier(name + 2, "webkit-supports-condition")) { 10826 m_parsingMode = SupportsMode; 10827 m_token = WEBKIT_SUPPORTS_CONDITION_SYM; 10828 } 10829#endif 10830 return; 10831 } 10832 } 10833} 10834 10835#if ENABLE(CSS3_CONDITIONAL_RULES) 10836template <typename CharacterType> 10837inline void CSSParser::detectSupportsToken(int length) 10838{ 10839 ASSERT(m_parsingMode == SupportsMode); 10840 CharacterType* name = tokenStart<CharacterType>(); 10841 10842 if (length == 2) { 10843 if (isASCIIAlphaCaselessEqual(name[0], 'o') && isASCIIAlphaCaselessEqual(name[1], 'r')) 10844 m_token = SUPPORTS_OR; 10845 } else if (length == 3) { 10846 if (isASCIIAlphaCaselessEqual(name[0], 'a') && isASCIIAlphaCaselessEqual(name[1], 'n') && isASCIIAlphaCaselessEqual(name[2], 'd')) 10847 m_token = SUPPORTS_AND; 10848 else if (isASCIIAlphaCaselessEqual(name[0], 'n') && isASCIIAlphaCaselessEqual(name[1], 'o') && isASCIIAlphaCaselessEqual(name[2], 't')) 10849 m_token = SUPPORTS_NOT; 10850 } 10851} 10852#endif 10853 10854template <typename SrcCharacterType> 10855int CSSParser::realLex(void* yylvalWithoutType) 10856{ 10857 YYSTYPE* yylval = static_cast<YYSTYPE*>(yylvalWithoutType); 10858 // Write pointer for the next character. 10859 SrcCharacterType* result; 10860 CSSParserString resultString; 10861 bool hasEscape; 10862 10863 // The input buffer is terminated by a \0 character, so 10864 // it is safe to read one character ahead of a known non-null. 10865#ifndef NDEBUG 10866 // In debug we check with an ASSERT that the length is > 0 for string types. 10867 yylval->string.clear(); 10868#endif 10869 10870restartAfterComment: 10871 result = currentCharacter<SrcCharacterType>(); 10872 setTokenStart(result); 10873 m_tokenStartLineNumber = m_lineNumber; 10874 m_token = *currentCharacter<SrcCharacterType>(); 10875 ++currentCharacter<SrcCharacterType>(); 10876 10877 switch ((m_token <= 127) ? typesOfASCIICharacters[m_token] : CharacterIdentifierStart) { 10878 case CharacterCaselessU: 10879 if (UNLIKELY(*currentCharacter<SrcCharacterType>() == '+')) 10880 if (parseUnicodeRange<SrcCharacterType>()) { 10881 m_token = UNICODERANGE; 10882 yylval->string.init(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 10883 break; 10884 } 10885 // Fall through to CharacterIdentifierStart. 10886 10887 case CharacterIdentifierStart: 10888 --currentCharacter<SrcCharacterType>(); 10889 parseIdentifier(result, yylval->string, hasEscape); 10890 m_token = IDENT; 10891 10892 if (UNLIKELY(*currentCharacter<SrcCharacterType>() == '(')) { 10893#if ENABLE(CSS3_CONDITIONAL_RULES) 10894 if (m_parsingMode == SupportsMode && !hasEscape) { 10895 detectSupportsToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 10896 if (m_token != IDENT) 10897 break; 10898 } 10899#endif 10900 m_token = FUNCTION; 10901 bool shouldSkipParenthesis = true; 10902 if (!hasEscape) { 10903 bool detected = detectFunctionTypeToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 10904 if (!detected && m_parsingMode == MediaQueryMode) { 10905 // ... and(max-width: 480px) ... looks like a function, but in fact it is not, 10906 // so run more detection code in the MediaQueryMode. 10907 detectMediaQueryToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 10908 shouldSkipParenthesis = false; 10909 } 10910 } 10911 10912 if (LIKELY(shouldSkipParenthesis)) { 10913 ++currentCharacter<SrcCharacterType>(); 10914 ++result; 10915 ++yylval->string.m_length; 10916 } 10917 10918 if (token() == URI) { 10919 m_token = FUNCTION; 10920 // Check whether it is really an URI. 10921 if (yylval->string.is8Bit()) 10922 parseURI<LChar>(yylval->string); 10923 else 10924 parseURI<UChar>(yylval->string); 10925 } 10926 } else if (UNLIKELY(m_parsingMode != NormalMode) && !hasEscape) { 10927 if (m_parsingMode == MediaQueryMode) 10928 detectMediaQueryToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 10929#if ENABLE(CSS3_CONDITIONAL_RULES) 10930 else if (m_parsingMode == SupportsMode) 10931 detectSupportsToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 10932#endif 10933 else if (m_parsingMode == NthChildMode && isASCIIAlphaCaselessEqual(tokenStart<SrcCharacterType>()[0], 'n')) { 10934 if (result - tokenStart<SrcCharacterType>() == 1) { 10935 // String "n" is IDENT but "n+1" is NTH. 10936 if (parseNthChildExtra<SrcCharacterType>()) { 10937 m_token = NTH; 10938 yylval->string.m_length = currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>(); 10939 } 10940 } else if (result - tokenStart<SrcCharacterType>() >= 2 && tokenStart<SrcCharacterType>()[1] == '-') { 10941 // String "n-" is IDENT but "n-1" is NTH. 10942 // Set currentCharacter to '-' to continue parsing. 10943 SrcCharacterType* nextCharacter = result; 10944 currentCharacter<SrcCharacterType>() = tokenStart<SrcCharacterType>() + 1; 10945 if (parseNthChildExtra<SrcCharacterType>()) { 10946 m_token = NTH; 10947 yylval->string.setLength(currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 10948 } else { 10949 // Revert the change to currentCharacter if unsuccessful. 10950 currentCharacter<SrcCharacterType>() = nextCharacter; 10951 } 10952 } 10953 } 10954 } 10955 break; 10956 10957 case CharacterDot: 10958 if (!isASCIIDigit(currentCharacter<SrcCharacterType>()[0])) 10959 break; 10960 // Fall through to CharacterNumber. 10961 10962 case CharacterNumber: { 10963 bool dotSeen = (m_token == '.'); 10964 10965 while (true) { 10966 if (!isASCIIDigit(currentCharacter<SrcCharacterType>()[0])) { 10967 // Only one dot is allowed for a number, 10968 // and it must be followed by a digit. 10969 if (currentCharacter<SrcCharacterType>()[0] != '.' || dotSeen || !isASCIIDigit(currentCharacter<SrcCharacterType>()[1])) 10970 break; 10971 dotSeen = true; 10972 } 10973 ++currentCharacter<SrcCharacterType>(); 10974 } 10975 10976 if (UNLIKELY(m_parsingMode == NthChildMode) && !dotSeen && isASCIIAlphaCaselessEqual(*currentCharacter<SrcCharacterType>(), 'n')) { 10977 // "[0-9]+n" is always an NthChild. 10978 ++currentCharacter<SrcCharacterType>(); 10979 parseNthChildExtra<SrcCharacterType>(); 10980 m_token = NTH; 10981 yylval->string.init(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 10982 break; 10983 } 10984 10985#if ENABLE(SVG) 10986 // Use SVG parser for numbers on SVG presentation attributes. 10987 if (m_context.mode == SVGAttributeMode) { 10988 // We need to take care of units like 'em' or 'ex'. 10989 SrcCharacterType* character = currentCharacter<SrcCharacterType>(); 10990 if (isASCIIAlphaCaselessEqual(*character, 'e')) { 10991 ASSERT(character - tokenStart<SrcCharacterType>() > 0); 10992 ++character; 10993 if (*character == '-' || *character == '+' || isASCIIDigit(*character)) { 10994 ++character; 10995 while (isASCIIDigit(*character)) 10996 ++character; 10997 // Use FLOATTOKEN if the string contains exponents. 10998 dotSeen = true; 10999 currentCharacter<SrcCharacterType>() = character; 11000 } 11001 } 11002 if (!parseSVGNumber(tokenStart<SrcCharacterType>(), character - tokenStart<SrcCharacterType>(), yylval->number)) 11003 break; 11004 } else 11005#endif 11006 yylval->number = charactersToDouble(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11007 11008 // Type of the function. 11009 if (isIdentifierStart<SrcCharacterType>()) { 11010 SrcCharacterType* type = currentCharacter<SrcCharacterType>(); 11011 result = currentCharacter<SrcCharacterType>(); 11012 11013 parseIdentifier(result, resultString, hasEscape); 11014 11015 m_token = DIMEN; 11016 if (!hasEscape) 11017 detectNumberToken(type, currentCharacter<SrcCharacterType>() - type); 11018 11019 if (m_token == DIMEN) { 11020 // The decoded number is overwritten, but this is intentional. 11021 yylval->string.init(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11022 } 11023 } else if (*currentCharacter<SrcCharacterType>() == '%') { 11024 // Although the CSS grammar says {num}% we follow 11025 // webkit at the moment which uses {num}%+. 11026 do { 11027 ++currentCharacter<SrcCharacterType>(); 11028 } while (*currentCharacter<SrcCharacterType>() == '%'); 11029 m_token = PERCENTAGE; 11030 } else 11031 m_token = dotSeen ? FLOATTOKEN : INTEGER; 11032 break; 11033 } 11034 11035 case CharacterDash: 11036 if (isIdentifierStartAfterDash(currentCharacter<SrcCharacterType>())) { 11037 --currentCharacter<SrcCharacterType>(); 11038 parseIdentifier(result, resultString, hasEscape); 11039 m_token = IDENT; 11040 11041#if ENABLE(CSS_VARIABLES) 11042 if (cssVariablesEnabled() && isEqualToCSSCaseSensitiveIdentifier(tokenStart<SrcCharacterType>() + 1, "webkit-var") && tokenStart<SrcCharacterType>()[11] == '-' && isIdentifierStartAfterDash(tokenStart<SrcCharacterType>() + 12)) 11043 m_token = VAR_DEFINITION; 11044 else 11045#endif 11046 if (*currentCharacter<SrcCharacterType>() == '(') { 11047 m_token = FUNCTION; 11048 if (!hasEscape) 11049 detectDashToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>()); 11050 ++currentCharacter<SrcCharacterType>(); 11051 ++result; 11052 } else if (UNLIKELY(m_parsingMode == NthChildMode) && !hasEscape && isASCIIAlphaCaselessEqual(tokenStart<SrcCharacterType>()[1], 'n')) { 11053 if (result - tokenStart<SrcCharacterType>() == 2) { 11054 // String "-n" is IDENT but "-n+1" is NTH. 11055 if (parseNthChildExtra<SrcCharacterType>()) { 11056 m_token = NTH; 11057 result = currentCharacter<SrcCharacterType>(); 11058 } 11059 } else if (result - tokenStart<SrcCharacterType>() >= 3 && tokenStart<SrcCharacterType>()[2] == '-') { 11060 // String "-n-" is IDENT but "-n-1" is NTH. 11061 // Set currentCharacter to second '-' of '-n-' to continue parsing. 11062 SrcCharacterType* nextCharacter = result; 11063 currentCharacter<SrcCharacterType>() = tokenStart<SrcCharacterType>() + 2; 11064 if (parseNthChildExtra<SrcCharacterType>()) { 11065 m_token = NTH; 11066 result = currentCharacter<SrcCharacterType>(); 11067 } else { 11068 // Revert the change to currentCharacter if unsuccessful. 11069 currentCharacter<SrcCharacterType>() = nextCharacter; 11070 } 11071 } 11072 } 11073 resultString.setLength(result - tokenStart<SrcCharacterType>()); 11074 yylval->string = resultString; 11075 } else if (currentCharacter<SrcCharacterType>()[0] == '-' && currentCharacter<SrcCharacterType>()[1] == '>') { 11076 currentCharacter<SrcCharacterType>() += 2; 11077 m_token = SGML_CD; 11078 } else if (UNLIKELY(m_parsingMode == NthChildMode)) { 11079 // "-[0-9]+n" is always an NthChild. 11080 if (parseNthChild<SrcCharacterType>()) { 11081 parseNthChildExtra<SrcCharacterType>(); 11082 m_token = NTH; 11083 yylval->string.init(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11084 } 11085 } 11086 break; 11087 11088 case CharacterOther: 11089 // m_token is simply the current character. 11090 break; 11091 11092 case CharacterNull: 11093 // Do not advance pointer at the end of input. 11094 --currentCharacter<SrcCharacterType>(); 11095 break; 11096 11097 case CharacterWhiteSpace: 11098 m_token = WHITESPACE; 11099 // Might start with a '\n'. 11100 --currentCharacter<SrcCharacterType>(); 11101 do { 11102 if (*currentCharacter<SrcCharacterType>() == '\n') 11103 ++m_lineNumber; 11104 ++currentCharacter<SrcCharacterType>(); 11105 } while (*currentCharacter<SrcCharacterType>() <= ' ' && (typesOfASCIICharacters[*currentCharacter<SrcCharacterType>()] == CharacterWhiteSpace)); 11106 break; 11107 11108 case CharacterEndMediaQuery: 11109 if (m_parsingMode == MediaQueryMode) 11110 m_parsingMode = NormalMode; 11111 break; 11112 11113 case CharacterEndNthChild: 11114 if (m_parsingMode == NthChildMode) 11115 m_parsingMode = NormalMode; 11116 break; 11117 11118 case CharacterQuote: 11119 if (checkAndSkipString(currentCharacter<SrcCharacterType>(), m_token)) { 11120 ++result; 11121 parseString<SrcCharacterType>(result, yylval->string, m_token); 11122 m_token = STRING; 11123 } 11124 break; 11125 11126 case CharacterExclamationMark: { 11127 SrcCharacterType* start = skipWhiteSpace(currentCharacter<SrcCharacterType>()); 11128 if (isEqualToCSSIdentifier(start, "important")) { 11129 m_token = IMPORTANT_SYM; 11130 currentCharacter<SrcCharacterType>() = start + 9; 11131 } 11132 break; 11133 } 11134 11135 case CharacterHashmark: { 11136 SrcCharacterType* start = currentCharacter<SrcCharacterType>(); 11137 result = currentCharacter<SrcCharacterType>(); 11138 11139 if (isASCIIDigit(*currentCharacter<SrcCharacterType>())) { 11140 // This must be a valid hex number token. 11141 do { 11142 ++currentCharacter<SrcCharacterType>(); 11143 } while (isASCIIHexDigit(*currentCharacter<SrcCharacterType>())); 11144 m_token = HEX; 11145 yylval->string.init(start, currentCharacter<SrcCharacterType>() - start); 11146 } else if (isIdentifierStart<SrcCharacterType>()) { 11147 m_token = IDSEL; 11148 parseIdentifier(result, yylval->string, hasEscape); 11149 if (!hasEscape) { 11150 // Check whether the identifier is also a valid hex number. 11151 SrcCharacterType* current = start; 11152 m_token = HEX; 11153 do { 11154 if (!isASCIIHexDigit(*current)) { 11155 m_token = IDSEL; 11156 break; 11157 } 11158 ++current; 11159 } while (current < result); 11160 } 11161 } 11162 break; 11163 } 11164 11165 case CharacterSlash: 11166 // Ignore comments. They are not even considered as white spaces. 11167 if (*currentCharacter<SrcCharacterType>() == '*') { 11168 ++currentCharacter<SrcCharacterType>(); 11169 while (currentCharacter<SrcCharacterType>()[0] != '*' || currentCharacter<SrcCharacterType>()[1] != '/') { 11170 if (*currentCharacter<SrcCharacterType>() == '\n') 11171 ++m_lineNumber; 11172 if (*currentCharacter<SrcCharacterType>() == '\0') { 11173 // Unterminated comments are simply ignored. 11174 currentCharacter<SrcCharacterType>() -= 2; 11175 break; 11176 } 11177 ++currentCharacter<SrcCharacterType>(); 11178 } 11179 currentCharacter<SrcCharacterType>() += 2; 11180 goto restartAfterComment; 11181 } 11182 break; 11183 11184 case CharacterDollar: 11185 if (*currentCharacter<SrcCharacterType>() == '=') { 11186 ++currentCharacter<SrcCharacterType>(); 11187 m_token = ENDSWITH; 11188 } 11189 break; 11190 11191 case CharacterAsterisk: 11192 if (*currentCharacter<SrcCharacterType>() == '=') { 11193 ++currentCharacter<SrcCharacterType>(); 11194 m_token = CONTAINS; 11195 } 11196 break; 11197 11198 case CharacterPlus: 11199 if (UNLIKELY(m_parsingMode == NthChildMode)) { 11200 // Simplest case. "+[0-9]*n" is always NthChild. 11201 if (parseNthChild<SrcCharacterType>()) { 11202 parseNthChildExtra<SrcCharacterType>(); 11203 m_token = NTH; 11204 yylval->string.init(tokenStart<SrcCharacterType>(), currentCharacter<SrcCharacterType>() - tokenStart<SrcCharacterType>()); 11205 } 11206 } 11207 break; 11208 11209 case CharacterLess: 11210 if (currentCharacter<SrcCharacterType>()[0] == '!' && currentCharacter<SrcCharacterType>()[1] == '-' && currentCharacter<SrcCharacterType>()[2] == '-') { 11211 currentCharacter<SrcCharacterType>() += 3; 11212 m_token = SGML_CD; 11213 } 11214 break; 11215 11216 case CharacterAt: 11217 if (isIdentifierStart<SrcCharacterType>()) { 11218 m_token = ATKEYWORD; 11219 ++result; 11220 parseIdentifier(result, resultString, hasEscape); 11221 detectAtToken<SrcCharacterType>(result - tokenStart<SrcCharacterType>(), hasEscape); 11222 } 11223 break; 11224 11225 case CharacterBackSlash: 11226 if (isCSSEscape(*currentCharacter<SrcCharacterType>())) { 11227 --currentCharacter<SrcCharacterType>(); 11228 parseIdentifier(result, yylval->string, hasEscape); 11229 m_token = IDENT; 11230 } 11231 break; 11232 11233 case CharacterXor: 11234 if (*currentCharacter<SrcCharacterType>() == '=') { 11235 ++currentCharacter<SrcCharacterType>(); 11236 m_token = BEGINSWITH; 11237 } 11238 break; 11239 11240 case CharacterVerticalBar: 11241 if (*currentCharacter<SrcCharacterType>() == '=') { 11242 ++currentCharacter<SrcCharacterType>(); 11243 m_token = DASHMATCH; 11244 } 11245 break; 11246 11247 case CharacterTilde: 11248 if (*currentCharacter<SrcCharacterType>() == '=') { 11249 ++currentCharacter<SrcCharacterType>(); 11250 m_token = INCLUDES; 11251 } 11252 break; 11253 11254 default: 11255 ASSERT_NOT_REACHED(); 11256 break; 11257 } 11258 11259 return token(); 11260} 11261 11262CSSParserSelector* CSSParser::createFloatingSelectorWithTagName(const QualifiedName& tagQName) 11263{ 11264 CSSParserSelector* selector = new CSSParserSelector(tagQName); 11265 m_floatingSelectors.add(selector); 11266 return selector; 11267} 11268 11269CSSParserSelector* CSSParser::createFloatingSelector() 11270{ 11271 CSSParserSelector* selector = new CSSParserSelector; 11272 m_floatingSelectors.add(selector); 11273 return selector; 11274} 11275 11276PassOwnPtr<CSSParserSelector> CSSParser::sinkFloatingSelector(CSSParserSelector* selector) 11277{ 11278 if (selector) { 11279 ASSERT(m_floatingSelectors.contains(selector)); 11280 m_floatingSelectors.remove(selector); 11281 } 11282 return adoptPtr(selector); 11283} 11284 11285Vector<OwnPtr<CSSParserSelector> >* CSSParser::createFloatingSelectorVector() 11286{ 11287 Vector<OwnPtr<CSSParserSelector> >* selectorVector = new Vector<OwnPtr<CSSParserSelector> >; 11288 m_floatingSelectorVectors.add(selectorVector); 11289 return selectorVector; 11290} 11291 11292PassOwnPtr<Vector<OwnPtr<CSSParserSelector> > > CSSParser::sinkFloatingSelectorVector(Vector<OwnPtr<CSSParserSelector> >* selectorVector) 11293{ 11294 if (selectorVector) { 11295 ASSERT(m_floatingSelectorVectors.contains(selectorVector)); 11296 m_floatingSelectorVectors.remove(selectorVector); 11297 } 11298 return adoptPtr(selectorVector); 11299} 11300 11301CSSParserValueList* CSSParser::createFloatingValueList() 11302{ 11303 CSSParserValueList* list = new CSSParserValueList; 11304 m_floatingValueLists.add(list); 11305 return list; 11306} 11307 11308PassOwnPtr<CSSParserValueList> CSSParser::sinkFloatingValueList(CSSParserValueList* list) 11309{ 11310 if (list) { 11311 ASSERT(m_floatingValueLists.contains(list)); 11312 m_floatingValueLists.remove(list); 11313 } 11314 return adoptPtr(list); 11315} 11316 11317CSSParserFunction* CSSParser::createFloatingFunction() 11318{ 11319 CSSParserFunction* function = new CSSParserFunction; 11320 m_floatingFunctions.add(function); 11321 return function; 11322} 11323 11324PassOwnPtr<CSSParserFunction> CSSParser::sinkFloatingFunction(CSSParserFunction* function) 11325{ 11326 if (function) { 11327 ASSERT(m_floatingFunctions.contains(function)); 11328 m_floatingFunctions.remove(function); 11329 } 11330 return adoptPtr(function); 11331} 11332 11333CSSParserValue& CSSParser::sinkFloatingValue(CSSParserValue& value) 11334{ 11335 if (value.unit == CSSParserValue::Function) { 11336 ASSERT(m_floatingFunctions.contains(value.function)); 11337 m_floatingFunctions.remove(value.function); 11338 } 11339 return value; 11340} 11341 11342MediaQueryExp* CSSParser::createFloatingMediaQueryExp(const AtomicString& mediaFeature, CSSParserValueList* values) 11343{ 11344 m_floatingMediaQueryExp = MediaQueryExp::create(mediaFeature, values); 11345 return m_floatingMediaQueryExp.get(); 11346} 11347 11348PassOwnPtr<MediaQueryExp> CSSParser::sinkFloatingMediaQueryExp(MediaQueryExp* expression) 11349{ 11350 ASSERT_UNUSED(expression, expression == m_floatingMediaQueryExp); 11351 return m_floatingMediaQueryExp.release(); 11352} 11353 11354Vector<OwnPtr<MediaQueryExp> >* CSSParser::createFloatingMediaQueryExpList() 11355{ 11356 m_floatingMediaQueryExpList = adoptPtr(new Vector<OwnPtr<MediaQueryExp> >); 11357 return m_floatingMediaQueryExpList.get(); 11358} 11359 11360PassOwnPtr<Vector<OwnPtr<MediaQueryExp> > > CSSParser::sinkFloatingMediaQueryExpList(Vector<OwnPtr<MediaQueryExp> >* list) 11361{ 11362 ASSERT_UNUSED(list, list == m_floatingMediaQueryExpList); 11363 return m_floatingMediaQueryExpList.release(); 11364} 11365 11366MediaQuery* CSSParser::createFloatingMediaQuery(MediaQuery::Restrictor restrictor, const String& mediaType, PassOwnPtr<Vector<OwnPtr<MediaQueryExp> > > expressions) 11367{ 11368 m_floatingMediaQuery = adoptPtr(new MediaQuery(restrictor, mediaType, expressions)); 11369 return m_floatingMediaQuery.get(); 11370} 11371 11372MediaQuery* CSSParser::createFloatingMediaQuery(PassOwnPtr<Vector<OwnPtr<MediaQueryExp> > > expressions) 11373{ 11374 return createFloatingMediaQuery(MediaQuery::None, "all", expressions); 11375} 11376 11377PassOwnPtr<MediaQuery> CSSParser::sinkFloatingMediaQuery(MediaQuery* query) 11378{ 11379 ASSERT_UNUSED(query, query == m_floatingMediaQuery); 11380 return m_floatingMediaQuery.release(); 11381} 11382 11383Vector<RefPtr<StyleKeyframe> >* CSSParser::createFloatingKeyframeVector() 11384{ 11385 m_floatingKeyframeVector = adoptPtr(new Vector<RefPtr<StyleKeyframe> >()); 11386 return m_floatingKeyframeVector.get(); 11387} 11388 11389PassOwnPtr<Vector<RefPtr<StyleKeyframe> > > CSSParser::sinkFloatingKeyframeVector(Vector<RefPtr<StyleKeyframe> >* keyframeVector) 11390{ 11391 ASSERT_UNUSED(keyframeVector, m_floatingKeyframeVector == keyframeVector); 11392 return m_floatingKeyframeVector.release(); 11393} 11394 11395MediaQuerySet* CSSParser::createMediaQuerySet() 11396{ 11397 RefPtr<MediaQuerySet> queries = MediaQuerySet::create(); 11398 MediaQuerySet* result = queries.get(); 11399 m_parsedMediaQuerySets.append(queries.release()); 11400 return result; 11401} 11402 11403StyleRuleBase* CSSParser::createImportRule(const CSSParserString& url, MediaQuerySet* media) 11404{ 11405 if (!media || !m_allowImportRules) { 11406 popRuleData(); 11407 return 0; 11408 } 11409 RefPtr<StyleRuleImport> rule = StyleRuleImport::create(url, media); 11410 StyleRuleImport* result = rule.get(); 11411 m_parsedRules.append(rule.release()); 11412 processAndAddNewRuleToSourceTreeIfNeeded(); 11413 return result; 11414} 11415 11416StyleRuleBase* CSSParser::createMediaRule(MediaQuerySet* media, RuleList* rules) 11417{ 11418 m_allowImportRules = m_allowNamespaceDeclarations = false; 11419 RefPtr<StyleRuleMedia> rule; 11420 if (rules) 11421 rule = StyleRuleMedia::create(media ? media : MediaQuerySet::create(), *rules); 11422 else { 11423 RuleList emptyRules; 11424 rule = StyleRuleMedia::create(media ? media : MediaQuerySet::create(), emptyRules); 11425 } 11426 StyleRuleMedia* result = rule.get(); 11427 m_parsedRules.append(rule.release()); 11428 processAndAddNewRuleToSourceTreeIfNeeded(); 11429 return result; 11430} 11431 11432#if ENABLE(CSS3_CONDITIONAL_RULES) 11433StyleRuleBase* CSSParser::createSupportsRule(bool conditionIsSupported, RuleList* rules) 11434{ 11435 m_allowImportRules = m_allowNamespaceDeclarations = false; 11436 11437 RefPtr<CSSRuleSourceData> data = popSupportsRuleData(); 11438 RefPtr<StyleRuleSupports> rule; 11439 String conditionText; 11440 unsigned conditionOffset = data->ruleHeaderRange.start + 9; 11441 unsigned conditionLength = data->ruleHeaderRange.length() - 9; 11442 11443 if (is8BitSource()) 11444 conditionText = String(m_dataStart8.get() + conditionOffset, conditionLength).stripWhiteSpace(); 11445 else 11446 conditionText = String(m_dataStart16.get() + conditionOffset, conditionLength).stripWhiteSpace(); 11447 11448 if (rules) 11449 rule = StyleRuleSupports::create(conditionText, conditionIsSupported, *rules); 11450 else { 11451 RuleList emptyRules; 11452 rule = StyleRuleSupports::create(conditionText, conditionIsSupported, emptyRules); 11453 } 11454 11455 StyleRuleSupports* result = rule.get(); 11456 m_parsedRules.append(rule.release()); 11457 processAndAddNewRuleToSourceTreeIfNeeded(); 11458 11459 return result; 11460} 11461 11462void CSSParser::markSupportsRuleHeaderStart() 11463{ 11464 if (!m_supportsRuleDataStack) 11465 m_supportsRuleDataStack = adoptPtr(new RuleSourceDataList()); 11466 11467 RefPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(CSSRuleSourceData::SUPPORTS_RULE); 11468 data->ruleHeaderRange.start = tokenStartOffset(); 11469 m_supportsRuleDataStack->append(data); 11470} 11471 11472void CSSParser::markSupportsRuleHeaderEnd() 11473{ 11474 ASSERT(m_supportsRuleDataStack && !m_supportsRuleDataStack->isEmpty()); 11475 11476 if (is8BitSource()) 11477 m_supportsRuleDataStack->last()->ruleHeaderRange.end = tokenStart<LChar>() - m_dataStart8.get(); 11478 else 11479 m_supportsRuleDataStack->last()->ruleHeaderRange.end = tokenStart<UChar>() - m_dataStart16.get(); 11480} 11481 11482PassRefPtr<CSSRuleSourceData> CSSParser::popSupportsRuleData() 11483{ 11484 ASSERT(m_supportsRuleDataStack && !m_supportsRuleDataStack->isEmpty()); 11485 RefPtr<CSSRuleSourceData> data = m_supportsRuleDataStack->last(); 11486 m_supportsRuleDataStack->removeLast(); 11487 return data.release(); 11488} 11489 11490#endif 11491 11492CSSParser::RuleList* CSSParser::createRuleList() 11493{ 11494 OwnPtr<RuleList> list = adoptPtr(new RuleList); 11495 RuleList* listPtr = list.get(); 11496 11497 m_parsedRuleLists.append(list.release()); 11498 return listPtr; 11499} 11500 11501void CSSParser::processAndAddNewRuleToSourceTreeIfNeeded() 11502{ 11503 if (!isExtractingSourceData()) 11504 return; 11505 markRuleBodyEnd(); 11506 RefPtr<CSSRuleSourceData> rule = popRuleData(); 11507 fixUnparsedPropertyRanges(rule.get()); 11508 addNewRuleToSourceTree(rule.release()); 11509} 11510 11511void CSSParser::addNewRuleToSourceTree(PassRefPtr<CSSRuleSourceData> rule) 11512{ 11513 // Precondition: (isExtractingSourceData()). 11514 if (!m_ruleSourceDataResult) 11515 return; 11516 if (m_currentRuleDataStack->isEmpty()) 11517 m_ruleSourceDataResult->append(rule); 11518 else 11519 m_currentRuleDataStack->last()->childRules.append(rule); 11520} 11521 11522PassRefPtr<CSSRuleSourceData> CSSParser::popRuleData() 11523{ 11524 if (!m_ruleSourceDataResult) 11525 return 0; 11526 11527 ASSERT(!m_currentRuleDataStack->isEmpty()); 11528 m_currentRuleData.clear(); 11529 RefPtr<CSSRuleSourceData> data = m_currentRuleDataStack->last(); 11530 m_currentRuleDataStack->removeLast(); 11531 return data.release(); 11532} 11533 11534void CSSParser::syntaxError(const Location& location, SyntaxErrorType error) 11535{ 11536 if (!isLoggingErrors()) 11537 return; 11538 StringBuilder builder; 11539 switch (error) { 11540 case PropertyDeclarationError: 11541 builder.appendLiteral("Invalid CSS property declaration at: "); 11542 break; 11543 11544 default: 11545 builder.appendLiteral("Unexpected CSS token: "); 11546 } 11547 11548 if (location.token.is8Bit()) 11549 builder.append(location.token.characters8(), location.token.length()); 11550 else 11551 builder.append(location.token.characters16(), location.token.length()); 11552 11553 logError(builder.toString(), location.lineNumber); 11554 11555 m_ignoreErrorsInDeclaration = true; 11556} 11557 11558bool CSSParser::isLoggingErrors() 11559{ 11560 return m_logErrors && !m_ignoreErrorsInDeclaration; 11561} 11562 11563void CSSParser::logError(const String& message, int lineNumber) 11564{ 11565 // FIXME: <http://webkit.org/b/114313> CSS Parser ConsoleMessage errors should include column numbers 11566 PageConsole* console = m_styleSheet->singleOwnerDocument()->page()->console(); 11567 console->addMessage(CSSMessageSource, WarningMessageLevel, message, m_styleSheet->baseURL().string(), lineNumber + 1, 0); 11568} 11569 11570StyleRuleKeyframes* CSSParser::createKeyframesRule(const String& name, PassOwnPtr<Vector<RefPtr<StyleKeyframe> > > popKeyframes) 11571{ 11572 OwnPtr<Vector<RefPtr<StyleKeyframe> > > keyframes = popKeyframes; 11573 m_allowImportRules = m_allowNamespaceDeclarations = false; 11574 RefPtr<StyleRuleKeyframes> rule = StyleRuleKeyframes::create(); 11575 for (size_t i = 0; i < keyframes->size(); ++i) 11576 rule->parserAppendKeyframe(keyframes->at(i)); 11577 rule->setName(name); 11578 StyleRuleKeyframes* rulePtr = rule.get(); 11579 m_parsedRules.append(rule.release()); 11580 processAndAddNewRuleToSourceTreeIfNeeded(); 11581 return rulePtr; 11582} 11583 11584StyleRuleBase* CSSParser::createStyleRule(Vector<OwnPtr<CSSParserSelector> >* selectors) 11585{ 11586 StyleRule* result = 0; 11587 if (selectors) { 11588 m_allowImportRules = m_allowNamespaceDeclarations = false; 11589 RefPtr<StyleRule> rule = StyleRule::create(m_lastSelectorLineNumber); 11590 rule->parserAdoptSelectorVector(*selectors); 11591 if (m_hasFontFaceOnlyValues) 11592 deleteFontFaceOnlyValues(); 11593 rule->setProperties(createStylePropertySet()); 11594 result = rule.get(); 11595 m_parsedRules.append(rule.release()); 11596 processAndAddNewRuleToSourceTreeIfNeeded(); 11597 } else 11598 popRuleData(); 11599 clearProperties(); 11600 return result; 11601} 11602 11603StyleRuleBase* CSSParser::createFontFaceRule() 11604{ 11605 m_allowImportRules = m_allowNamespaceDeclarations = false; 11606 for (unsigned i = 0; i < m_parsedProperties.size(); ++i) { 11607 CSSProperty& property = m_parsedProperties[i]; 11608 if (property.id() == CSSPropertyFontVariant && property.value()->isPrimitiveValue()) 11609 property.wrapValueInCommaSeparatedList(); 11610 else if (property.id() == CSSPropertyFontFamily && (!property.value()->isValueList() || static_cast<CSSValueList*>(property.value())->length() != 1)) { 11611 // Unlike font-family property, font-family descriptor in @font-face rule 11612 // has to be a value list with exactly one family name. It cannot have a 11613 // have 'initial' value and cannot 'inherit' from parent. 11614 // See http://dev.w3.org/csswg/css3-fonts/#font-family-desc 11615 clearProperties(); 11616 popRuleData(); 11617 return 0; 11618 } 11619 } 11620 RefPtr<StyleRuleFontFace> rule = StyleRuleFontFace::create(); 11621 rule->setProperties(createStylePropertySet()); 11622 clearProperties(); 11623 StyleRuleFontFace* result = rule.get(); 11624 m_parsedRules.append(rule.release()); 11625 processAndAddNewRuleToSourceTreeIfNeeded(); 11626 return result; 11627} 11628 11629#if ENABLE(SHADOW_DOM) 11630StyleRuleBase* CSSParser::createHostRule(RuleList* rules) 11631{ 11632 m_allowImportRules = m_allowNamespaceDeclarations = false; 11633 RefPtr<StyleRuleHost> rule; 11634 if (rules) 11635 rule = StyleRuleHost::create(*rules); 11636 else { 11637 RuleList emptyRules; 11638 rule = StyleRuleHost::create(emptyRules); 11639 } 11640 StyleRuleHost* result = rule.get(); 11641 m_parsedRules.append(rule.release()); 11642 processAndAddNewRuleToSourceTreeIfNeeded(); 11643 return result; 11644} 11645#endif 11646 11647void CSSParser::addNamespace(const AtomicString& prefix, const AtomicString& uri) 11648{ 11649 if (!m_styleSheet || !m_allowNamespaceDeclarations) 11650 return; 11651 m_allowImportRules = false; 11652 m_styleSheet->parserAddNamespace(prefix, uri); 11653 if (prefix.isEmpty() && !uri.isNull()) 11654 m_defaultNamespace = uri; 11655} 11656 11657QualifiedName CSSParser::determineNameInNamespace(const AtomicString& prefix, const AtomicString& localName) 11658{ 11659 if (!m_styleSheet) 11660 return QualifiedName(prefix, localName, m_defaultNamespace); 11661 return QualifiedName(prefix, localName, m_styleSheet->determineNamespace(prefix)); 11662} 11663 11664CSSParserSelector* CSSParser::rewriteSpecifiersWithNamespaceIfNeeded(CSSParserSelector* specifiers) 11665{ 11666 if (m_defaultNamespace != starAtom || specifiers->isCustomPseudoElement()) 11667 return rewriteSpecifiersWithElementName(nullAtom, starAtom, specifiers, /*tagIsForNamespaceRule*/true); 11668 return specifiers; 11669} 11670 11671CSSParserSelector* CSSParser::rewriteSpecifiersWithElementName(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector* specifiers, bool tagIsForNamespaceRule) 11672{ 11673 AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleSheet ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace; 11674 QualifiedName tag(namespacePrefix, elementName, determinedNamespace); 11675 11676 if (!specifiers->isCustomPseudoElement()) { 11677 if (tag == anyQName()) 11678 return specifiers; 11679#if ENABLE(VIDEO_TRACK) 11680 if (!(specifiers->pseudoType() == CSSSelector::PseudoCue)) 11681#endif 11682 specifiers->prependTagSelector(tag, tagIsForNamespaceRule); 11683 return specifiers; 11684 } 11685 11686 CSSParserSelector* lastShadowDescendant = specifiers; 11687 CSSParserSelector* history = specifiers; 11688 while (history->tagHistory()) { 11689 history = history->tagHistory(); 11690 if (history->isCustomPseudoElement() || history->hasShadowDescendant()) 11691 lastShadowDescendant = history; 11692 } 11693 11694 if (lastShadowDescendant->tagHistory()) { 11695 if (tag != anyQName()) 11696 lastShadowDescendant->tagHistory()->prependTagSelector(tag, tagIsForNamespaceRule); 11697 return specifiers; 11698 } 11699 11700 // For shadow-ID pseudo-elements to be correctly matched, the ShadowDescendant combinator has to be used. 11701 // We therefore create a new Selector with that combinator here in any case, even if matching any (host) element in any namespace (i.e. '*'). 11702 OwnPtr<CSSParserSelector> elementNameSelector = adoptPtr(new CSSParserSelector(tag)); 11703 lastShadowDescendant->setTagHistory(elementNameSelector.release()); 11704 lastShadowDescendant->setRelation(CSSSelector::ShadowDescendant); 11705 return specifiers; 11706} 11707 11708CSSParserSelector* CSSParser::rewriteSpecifiers(CSSParserSelector* specifiers, CSSParserSelector* newSpecifier) 11709{ 11710#if ENABLE(VIDEO_TRACK) 11711 if (newSpecifier->isCustomPseudoElement() || newSpecifier->pseudoType() == CSSSelector::PseudoCue) { 11712#else 11713 if (newSpecifier->isCustomPseudoElement()) { 11714#endif 11715 // Unknown pseudo element always goes at the top of selector chain. 11716 newSpecifier->appendTagHistory(CSSSelector::ShadowDescendant, sinkFloatingSelector(specifiers)); 11717 return newSpecifier; 11718 } 11719 if (specifiers->isCustomPseudoElement()) { 11720 // Specifiers for unknown pseudo element go right behind it in the chain. 11721 specifiers->insertTagHistory(CSSSelector::SubSelector, sinkFloatingSelector(newSpecifier), CSSSelector::ShadowDescendant); 11722 return specifiers; 11723 } 11724 specifiers->appendTagHistory(CSSSelector::SubSelector, sinkFloatingSelector(newSpecifier)); 11725 return specifiers; 11726} 11727 11728StyleRuleBase* CSSParser::createPageRule(PassOwnPtr<CSSParserSelector> pageSelector) 11729{ 11730 // FIXME: Margin at-rules are ignored. 11731 m_allowImportRules = m_allowNamespaceDeclarations = false; 11732 StyleRulePage* pageRule = 0; 11733 if (pageSelector) { 11734 RefPtr<StyleRulePage> rule = StyleRulePage::create(); 11735 Vector<OwnPtr<CSSParserSelector> > selectorVector; 11736 selectorVector.append(pageSelector); 11737 rule->parserAdoptSelectorVector(selectorVector); 11738 rule->setProperties(createStylePropertySet()); 11739 pageRule = rule.get(); 11740 m_parsedRules.append(rule.release()); 11741 processAndAddNewRuleToSourceTreeIfNeeded(); 11742 } else 11743 popRuleData(); 11744 clearProperties(); 11745 return pageRule; 11746} 11747 11748void CSSParser::setReusableRegionSelectorVector(Vector<OwnPtr<CSSParserSelector> >* selectors) 11749{ 11750 if (selectors) 11751 m_reusableRegionSelectorVector.swap(*selectors); 11752} 11753 11754StyleRuleBase* CSSParser::createRegionRule(Vector<OwnPtr<CSSParserSelector> >* regionSelector, RuleList* rules) 11755{ 11756 if (!cssRegionsEnabled() || !regionSelector || !rules) { 11757 popRuleData(); 11758 return 0; 11759 } 11760 11761 m_allowImportRules = m_allowNamespaceDeclarations = false; 11762 11763 RefPtr<StyleRuleRegion> regionRule = StyleRuleRegion::create(regionSelector, *rules); 11764 11765 StyleRuleRegion* result = regionRule.get(); 11766 m_parsedRules.append(regionRule.release()); 11767 if (isExtractingSourceData()) 11768 addNewRuleToSourceTree(CSSRuleSourceData::createUnknown()); 11769 11770 return result; 11771} 11772 11773StyleRuleBase* CSSParser::createMarginAtRule(CSSSelector::MarginBoxType /* marginBox */) 11774{ 11775 // FIXME: Implement margin at-rule here, using: 11776 // - marginBox: margin box 11777 // - m_parsedProperties: properties at [m_numParsedPropertiesBeforeMarginBox, m_parsedProperties.size()] are for this at-rule. 11778 // 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. 11779 11780 endDeclarationsForMarginBox(); 11781 return 0; // until this method is implemented. 11782} 11783 11784void CSSParser::startDeclarationsForMarginBox() 11785{ 11786 m_numParsedPropertiesBeforeMarginBox = m_parsedProperties.size(); 11787} 11788 11789void CSSParser::endDeclarationsForMarginBox() 11790{ 11791 rollbackLastProperties(m_parsedProperties.size() - m_numParsedPropertiesBeforeMarginBox); 11792 m_numParsedPropertiesBeforeMarginBox = INVALID_NUM_PARSED_PROPERTIES; 11793} 11794 11795void CSSParser::deleteFontFaceOnlyValues() 11796{ 11797 ASSERT(m_hasFontFaceOnlyValues); 11798 for (unsigned i = 0; i < m_parsedProperties.size();) { 11799 CSSProperty& property = m_parsedProperties[i]; 11800 if (property.id() == CSSPropertyFontVariant && property.value()->isValueList()) { 11801 m_parsedProperties.remove(i); 11802 continue; 11803 } 11804 ++i; 11805 } 11806} 11807 11808StyleKeyframe* CSSParser::createKeyframe(CSSParserValueList* keys) 11809{ 11810 // Create a key string from the passed keys 11811 StringBuilder keyString; 11812 for (unsigned i = 0; i < keys->size(); ++i) { 11813 // Just as per the comment below, we ignore keyframes with 11814 // invalid key values (plain numbers or unknown identifiers) 11815 // marked as CSSPrimitiveValue::CSS_UNKNOWN during parsing. 11816 if (keys->valueAt(i)->unit == CSSPrimitiveValue::CSS_UNKNOWN) { 11817 clearProperties(); 11818 return 0; 11819 } 11820 11821 ASSERT(keys->valueAt(i)->unit == CSSPrimitiveValue::CSS_NUMBER); 11822 float key = static_cast<float>(keys->valueAt(i)->fValue); 11823 if (key < 0 || key > 100) { 11824 // As per http://www.w3.org/TR/css3-animations/#keyframes, 11825 // "If a keyframe selector specifies negative percentage values 11826 // or values higher than 100%, then the keyframe will be ignored." 11827 clearProperties(); 11828 return 0; 11829 } 11830 if (i != 0) 11831 keyString.append(','); 11832 keyString.append(String::number(key)); 11833 keyString.append('%'); 11834 } 11835 11836 RefPtr<StyleKeyframe> keyframe = StyleKeyframe::create(); 11837 keyframe->setKeyText(keyString.toString()); 11838 keyframe->setProperties(createStylePropertySet()); 11839 11840 clearProperties(); 11841 11842 StyleKeyframe* keyframePtr = keyframe.get(); 11843 m_parsedKeyframes.append(keyframe.release()); 11844 return keyframePtr; 11845} 11846 11847void CSSParser::invalidBlockHit() 11848{ 11849 if (m_styleSheet && !m_hadSyntacticallyValidCSSRule) 11850 m_styleSheet->setHasSyntacticallyValidCSSHeader(false); 11851} 11852 11853void CSSParser::updateLastSelectorLineAndPosition() 11854{ 11855 m_lastSelectorLineNumber = m_lineNumber; 11856} 11857 11858void CSSParser::updateLastMediaLine(MediaQuerySet* media) 11859{ 11860 media->setLastLine(m_lineNumber); 11861} 11862 11863template <typename CharacterType> 11864static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData) 11865{ 11866 Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData; 11867 unsigned size = propertyData.size(); 11868 if (!size) 11869 return; 11870 11871 unsigned styleStart = ruleData->ruleBodyRange.start; 11872 CSSPropertySourceData* nextData = &(propertyData.at(0)); 11873 for (unsigned i = 0; i < size; ++i) { 11874 CSSPropertySourceData* currentData = nextData; 11875 nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0; 11876 11877 if (currentData->parsedOk) 11878 continue; 11879 if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';') 11880 continue; 11881 11882 unsigned propertyEndInStyleSheet; 11883 if (!nextData) 11884 propertyEndInStyleSheet = ruleData->ruleBodyRange.end - 1; 11885 else 11886 propertyEndInStyleSheet = styleStart + nextData->range.start - 1; 11887 11888 while (isHTMLSpace(characters[propertyEndInStyleSheet])) 11889 --propertyEndInStyleSheet; 11890 11891 // propertyEndInStyleSheet points at the last property text character. 11892 unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character. 11893 if (currentData->range.end != newPropertyEnd) { 11894 currentData->range.end = newPropertyEnd; 11895 unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length(); 11896 while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':') 11897 ++valueStartInStyleSheet; 11898 if (valueStartInStyleSheet < propertyEndInStyleSheet) 11899 ++valueStartInStyleSheet; // Shift past the ':'. 11900 while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace(characters[valueStartInStyleSheet])) 11901 ++valueStartInStyleSheet; 11902 // Need to exclude the trailing ';' from the property value. 11903 currentData->value = String(characters + valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1)); 11904 } 11905 } 11906} 11907 11908void CSSParser::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData) 11909{ 11910 if (!ruleData->styleSourceData) 11911 return; 11912 11913 if (is8BitSource()) { 11914 fixUnparsedProperties<LChar>(m_dataStart8.get() + m_parsedTextPrefixLength, ruleData); 11915 return; 11916 } 11917 11918 fixUnparsedProperties<UChar>(m_dataStart16.get() + m_parsedTextPrefixLength, ruleData); 11919} 11920 11921void CSSParser::markRuleHeaderStart(CSSRuleSourceData::Type ruleType) 11922{ 11923 if (!isExtractingSourceData()) 11924 return; 11925 11926 // Pop off data for a previous invalid rule. 11927 if (m_currentRuleData) 11928 m_currentRuleDataStack->removeLast(); 11929 11930 RefPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(ruleType); 11931 data->ruleHeaderRange.start = tokenStartOffset(); 11932 m_currentRuleData = data; 11933 m_currentRuleDataStack->append(data.release()); 11934} 11935 11936template <typename CharacterType> 11937inline void CSSParser::setRuleHeaderEnd(const CharacterType* dataStart) 11938{ 11939 CharacterType* listEnd = tokenStart<CharacterType>(); 11940 while (listEnd > dataStart + 1) { 11941 if (isHTMLSpace(*(listEnd - 1))) 11942 --listEnd; 11943 else 11944 break; 11945 } 11946 11947 m_currentRuleDataStack->last()->ruleHeaderRange.end = listEnd - dataStart; 11948} 11949 11950void CSSParser::markRuleHeaderEnd() 11951{ 11952 if (!isExtractingSourceData()) 11953 return; 11954 ASSERT(!m_currentRuleDataStack->isEmpty()); 11955 11956 if (is8BitSource()) 11957 setRuleHeaderEnd<LChar>(m_dataStart8.get()); 11958 else 11959 setRuleHeaderEnd<UChar>(m_dataStart16.get()); 11960} 11961 11962void CSSParser::markSelectorStart() 11963{ 11964 if (!isExtractingSourceData()) 11965 return; 11966 ASSERT(!m_selectorRange.end); 11967 11968 m_selectorRange.start = tokenStartOffset(); 11969} 11970 11971void CSSParser::markSelectorEnd() 11972{ 11973 if (!isExtractingSourceData()) 11974 return; 11975 ASSERT(!m_selectorRange.end); 11976 ASSERT(m_currentRuleDataStack->size()); 11977 11978 m_selectorRange.end = tokenStartOffset(); 11979 m_currentRuleDataStack->last()->selectorRanges.append(m_selectorRange); 11980 m_selectorRange.start = 0; 11981 m_selectorRange.end = 0; 11982} 11983 11984void CSSParser::markRuleBodyStart() 11985{ 11986 if (!isExtractingSourceData()) 11987 return; 11988 m_currentRuleData.clear(); 11989 unsigned offset = tokenStartOffset(); 11990 if (tokenStartChar() == '{') 11991 ++offset; // Skip the rule body opening brace. 11992 ASSERT(!m_currentRuleDataStack->isEmpty()); 11993 m_currentRuleDataStack->last()->ruleBodyRange.start = offset; 11994} 11995 11996void CSSParser::markRuleBodyEnd() 11997{ 11998 // Precondition: (!isExtractingSourceData()) 11999 unsigned offset = tokenStartOffset(); 12000 ASSERT(!m_currentRuleDataStack->isEmpty()); 12001 m_currentRuleDataStack->last()->ruleBodyRange.end = offset; 12002} 12003 12004void CSSParser::markPropertyStart() 12005{ 12006 m_ignoreErrorsInDeclaration = false; 12007 if (!isExtractingSourceData()) 12008 return; 12009 if (m_currentRuleDataStack->isEmpty() || !m_currentRuleDataStack->last()->styleSourceData) 12010 return; 12011 12012 m_propertyRange.start = tokenStartOffset(); 12013} 12014 12015void CSSParser::markPropertyEnd(bool isImportantFound, bool isPropertyParsed) 12016{ 12017 if (!isExtractingSourceData()) 12018 return; 12019 if (m_currentRuleDataStack->isEmpty() || !m_currentRuleDataStack->last()->styleSourceData) 12020 return; 12021 12022 unsigned offset = tokenStartOffset(); 12023 if (tokenStartChar() == ';') // Include semicolon into the property text. 12024 ++offset; 12025 m_propertyRange.end = offset; 12026 if (m_propertyRange.start != UINT_MAX && !m_currentRuleDataStack->isEmpty()) { 12027 // This stuff is only executed when the style data retrieval is requested by client. 12028 const unsigned start = m_propertyRange.start; 12029 const unsigned end = m_propertyRange.end; 12030 ASSERT(start < end); 12031 String propertyString; 12032 if (is8BitSource()) 12033 propertyString = String(m_dataStart8.get() + start, end - start).stripWhiteSpace(); 12034 else 12035 propertyString = String(m_dataStart16.get() + start, end - start).stripWhiteSpace(); 12036 if (propertyString.endsWith(';')) 12037 propertyString = propertyString.left(propertyString.length() - 1); 12038 size_t colonIndex = propertyString.find(':'); 12039 ASSERT(colonIndex != notFound); 12040 12041 String name = propertyString.left(colonIndex).stripWhiteSpace(); 12042 String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace(); 12043 // The property range is relative to the declaration start offset. 12044 SourceRange& topRuleBodyRange = m_currentRuleDataStack->last()->ruleBodyRange; 12045 m_currentRuleDataStack->last()->styleSourceData->propertyData.append( 12046 CSSPropertySourceData(name, value, isImportantFound, isPropertyParsed, SourceRange(start - topRuleBodyRange.start, end - topRuleBodyRange.start))); 12047 } 12048 resetPropertyRange(); 12049} 12050 12051#if ENABLE(CSS_DEVICE_ADAPTATION) 12052StyleRuleBase* CSSParser::createViewportRule() 12053{ 12054 m_allowImportRules = m_allowNamespaceDeclarations = false; 12055 12056 RefPtr<StyleRuleViewport> rule = StyleRuleViewport::create(); 12057 12058 rule->setProperties(createStylePropertySet()); 12059 clearProperties(); 12060 12061 StyleRuleViewport* result = rule.get(); 12062 m_parsedRules.append(rule.release()); 12063 processAndAddNewRuleToSourceTreeIfNeeded(); 12064 12065 return result; 12066} 12067 12068bool CSSParser::parseViewportProperty(CSSPropertyID propId, bool important) 12069{ 12070 CSSParserValue* value = m_valueList->current(); 12071 if (!value) 12072 return false; 12073 12074 int id = value->id; 12075 bool validPrimitive = false; 12076 12077 switch (propId) { 12078 case CSSPropertyMinWidth: // auto | device-width | device-height | <length> | <percentage> 12079 case CSSPropertyMaxWidth: 12080 case CSSPropertyMinHeight: 12081 case CSSPropertyMaxHeight: 12082 if (id == CSSValueAuto || id == CSSValueDeviceWidth || id == CSSValueDeviceHeight) 12083 validPrimitive = true; 12084 else 12085 validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg)); 12086 break; 12087 case CSSPropertyWidth: // shorthand 12088 return parseViewportShorthand(propId, CSSPropertyMinWidth, CSSPropertyMaxWidth, important); 12089 case CSSPropertyHeight: 12090 return parseViewportShorthand(propId, CSSPropertyMinHeight, CSSPropertyMaxHeight, important); 12091 case CSSPropertyMinZoom: // auto | <number> | <percentage> 12092 case CSSPropertyMaxZoom: 12093 case CSSPropertyZoom: 12094 if (id == CSSValueAuto) 12095 validPrimitive = true; 12096 else 12097 validPrimitive = (!id && validUnit(value, FNumber | FPercent | FNonNeg)); 12098 break; 12099 case CSSPropertyUserZoom: // zoom | fixed 12100 if (id == CSSValueZoom || id == CSSValueFixed) 12101 validPrimitive = true; 12102 break; 12103 case CSSPropertyOrientation: // auto | portrait | landscape 12104 if (id == CSSValueAuto || id == CSSValuePortrait || id == CSSValueLandscape) 12105 validPrimitive = true; 12106 default: 12107 break; 12108 } 12109 12110 RefPtr<CSSValue> parsedValue; 12111 if (validPrimitive) { 12112 parsedValue = parseValidPrimitive(id, value); 12113 m_valueList->next(); 12114 } 12115 12116 if (parsedValue) { 12117 if (!m_valueList->current() || inShorthand()) { 12118 addProperty(propId, parsedValue.release(), important); 12119 return true; 12120 } 12121 } 12122 12123 return false; 12124} 12125 12126bool CSSParser::parseViewportShorthand(CSSPropertyID propId, CSSPropertyID first, CSSPropertyID second, bool important) 12127{ 12128 unsigned numValues = m_valueList->size(); 12129 12130 if (numValues > 2) 12131 return false; 12132 12133 ShorthandScope scope(this, propId); 12134 12135 if (!parseViewportProperty(first, important)) 12136 return false; 12137 12138 // If just one value is supplied, the second value 12139 // is implicitly initialized with the first value. 12140 if (numValues == 1) 12141 m_valueList->previous(); 12142 12143 return parseViewportProperty(second, important); 12144} 12145#endif 12146 12147template <typename CharacterType> 12148static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length) 12149{ 12150 char buffer[maxCSSPropertyNameLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character 12151 12152 for (unsigned i = 0; i != length; ++i) { 12153 CharacterType c = propertyName[i]; 12154 if (c == 0 || c >= 0x7F) 12155 return CSSPropertyInvalid; // illegal character 12156 buffer[i] = toASCIILower(c); 12157 } 12158 buffer[length] = '\0'; 12159 12160 const char* name = buffer; 12161 if (buffer[0] == '-') { 12162#if ENABLE(LEGACY_CSS_VENDOR_PREFIXES) 12163 // If the prefix is -apple- or -khtml-, change it to -webkit-. 12164 // This makes the string one character longer. 12165 if (RuntimeEnabledFeatures::legacyCSSVendorPrefixesEnabled() 12166 && (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-"))) { 12167 memmove(buffer + 7, buffer + 6, length + 1 - 6); 12168 memcpy(buffer, "-webkit", 7); 12169 ++length; 12170 } 12171#endif 12172#if PLATFORM(IOS) 12173 cssPropertyNameIOSAliasing(buffer, name, length); 12174#endif 12175 } 12176 12177 const Property* hashTableEntry = findProperty(name, length); 12178 const CSSPropertyID propertyID = hashTableEntry ? static_cast<CSSPropertyID>(hashTableEntry->id) : CSSPropertyInvalid; 12179 12180 static const int cssPropertyHistogramSize = numCSSProperties; 12181 if (hasPrefix(buffer, length, "-webkit-") && propertyID != CSSPropertyInvalid) { 12182 int histogramValue = propertyID - firstCSSProperty; 12183 ASSERT(0 <= histogramValue && histogramValue < cssPropertyHistogramSize); 12184 HistogramSupport::histogramEnumeration("CSS.PrefixUsage", histogramValue, cssPropertyHistogramSize); 12185 } 12186 12187 return propertyID; 12188} 12189 12190CSSPropertyID cssPropertyID(const String& string) 12191{ 12192 unsigned length = string.length(); 12193 12194 if (!length) 12195 return CSSPropertyInvalid; 12196 if (length > maxCSSPropertyNameLength) 12197 return CSSPropertyInvalid; 12198 12199 return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters(), length); 12200} 12201 12202CSSPropertyID cssPropertyID(const CSSParserString& string) 12203{ 12204 unsigned length = string.length(); 12205 12206 if (!length) 12207 return CSSPropertyInvalid; 12208 if (length > maxCSSPropertyNameLength) 12209 return CSSPropertyInvalid; 12210 12211 return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length); 12212} 12213 12214#if PLATFORM(IOS) 12215void cssPropertyNameIOSAliasing(const char* propertyName, const char*& propertyNameAlias, unsigned& newLength) 12216{ 12217 if (!strcmp(propertyName, "-webkit-hyphenate-locale")) { 12218 // Worked in iOS 4.2. 12219 static const char* const webkitLocale = "-webkit-locale"; 12220 propertyNameAlias = webkitLocale; 12221 newLength = strlen(webkitLocale); 12222 } 12223} 12224#endif 12225 12226template <typename CharacterType> 12227static int cssValueKeywordID(const CharacterType* valueKeyword, unsigned length) 12228{ 12229 char buffer[maxCSSValueKeywordLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character 12230 12231 for (unsigned i = 0; i != length; ++i) { 12232 CharacterType c = valueKeyword[i]; 12233 if (c == 0 || c >= 0x7F) 12234 return 0; // illegal character 12235 buffer[i] = WTF::toASCIILower(c); 12236 } 12237 buffer[length] = '\0'; 12238 12239 if (buffer[0] == '-') { 12240 // If the prefix is -apple- or -khtml-, change it to -webkit-. 12241 // This makes the string one character longer. 12242 if (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-")) { 12243 memmove(buffer + 7, buffer + 6, length + 1 - 6); 12244 memcpy(buffer, "-webkit", 7); 12245 ++length; 12246 } 12247 } 12248 12249 const Value* hashTableEntry = findValue(buffer, length); 12250 return hashTableEntry ? hashTableEntry->id : 0; 12251} 12252 12253int cssValueKeywordID(const CSSParserString& string) 12254{ 12255 unsigned length = string.length(); 12256 if (!length) 12257 return 0; 12258 if (length > maxCSSValueKeywordLength) 12259 return 0; 12260 12261 return string.is8Bit() ? cssValueKeywordID(string.characters8(), length) : cssValueKeywordID(string.characters16(), length); 12262} 12263 12264template <typename CharacterType> 12265static inline bool isCSSTokenizerIdentifier(const CharacterType* characters, unsigned length) 12266{ 12267 const CharacterType* end = characters + length; 12268 12269 // -? 12270 if (characters != end && characters[0] == '-') 12271 ++characters; 12272 12273 // {nmstart} 12274 if (characters == end || !(characters[0] == '_' || characters[0] >= 128 || isASCIIAlpha(characters[0]))) 12275 return false; 12276 ++characters; 12277 12278 // {nmchar}* 12279 for (; characters != end; ++characters) { 12280 if (!(characters[0] == '_' || characters[0] == '-' || characters[0] >= 128 || isASCIIAlphanumeric(characters[0]))) 12281 return false; 12282 } 12283 12284 return true; 12285} 12286 12287// "ident" from the CSS tokenizer, minus backslash-escape sequences 12288static bool isCSSTokenizerIdentifier(const String& string) 12289{ 12290 unsigned length = string.length(); 12291 12292 if (!length) 12293 return false; 12294 12295 if (string.is8Bit()) 12296 return isCSSTokenizerIdentifier(string.characters8(), length); 12297 return isCSSTokenizerIdentifier(string.characters(), length); 12298} 12299 12300template <typename CharacterType> 12301static inline bool isCSSTokenizerURL(const CharacterType* characters, unsigned length) 12302{ 12303 const CharacterType* end = characters + length; 12304 12305 for (; characters != end; ++characters) { 12306 CharacterType c = characters[0]; 12307 switch (c) { 12308 case '!': 12309 case '#': 12310 case '$': 12311 case '%': 12312 case '&': 12313 break; 12314 default: 12315 if (c < '*') 12316 return false; 12317 if (c <= '~') 12318 break; 12319 if (c < 128) 12320 return false; 12321 } 12322 } 12323 12324 return true; 12325} 12326 12327// "url" from the CSS tokenizer, minus backslash-escape sequences 12328static bool isCSSTokenizerURL(const String& string) 12329{ 12330 unsigned length = string.length(); 12331 12332 if (!length) 12333 return true; 12334 12335 if (string.is8Bit()) 12336 return isCSSTokenizerURL(string.characters8(), length); 12337 return isCSSTokenizerURL(string.characters(), length); 12338} 12339 12340 12341template <typename CharacterType> 12342static inline String quoteCSSStringInternal(const CharacterType* characters, unsigned length) 12343{ 12344 // For efficiency, we first pre-calculate the length of the quoted string, then we build the actual one. 12345 // Please see below for the actual logic. 12346 unsigned quotedStringSize = 2; // Two quotes surrounding the entire string. 12347 bool afterEscape = false; 12348 for (unsigned i = 0; i < length; ++i) { 12349 CharacterType ch = characters[i]; 12350 if (ch == '\\' || ch == '\'') { 12351 quotedStringSize += 2; 12352 afterEscape = false; 12353 } else if (ch < 0x20 || ch == 0x7F) { 12354 quotedStringSize += 2 + (ch >= 0x10); 12355 afterEscape = true; 12356 } else { 12357 quotedStringSize += 1 + (afterEscape && (isASCIIHexDigit(ch) || ch == ' ')); 12358 afterEscape = false; 12359 } 12360 } 12361 12362 StringBuffer<CharacterType> buffer(quotedStringSize); 12363 unsigned index = 0; 12364 buffer[index++] = '\''; 12365 afterEscape = false; 12366 for (unsigned i = 0; i < length; ++i) { 12367 CharacterType ch = characters[i]; 12368 if (ch == '\\' || ch == '\'') { 12369 buffer[index++] = '\\'; 12370 buffer[index++] = ch; 12371 afterEscape = false; 12372 } else if (ch < 0x20 || ch == 0x7F) { // Control characters. 12373 buffer[index++] = '\\'; 12374 placeByteAsHexCompressIfPossible(ch, buffer, index, Lowercase); 12375 afterEscape = true; 12376 } else { 12377 // Space character may be required to separate backslash-escape sequence and normal characters. 12378 if (afterEscape && (isASCIIHexDigit(ch) || ch == ' ')) 12379 buffer[index++] = ' '; 12380 buffer[index++] = ch; 12381 afterEscape = false; 12382 } 12383 } 12384 buffer[index++] = '\''; 12385 12386 ASSERT(quotedStringSize == index); 12387 return String::adopt(buffer); 12388} 12389 12390// We use single quotes for now because markup.cpp uses double quotes. 12391String quoteCSSString(const String& string) 12392{ 12393 // This function expands each character to at most 3 characters ('\u0010' -> '\' '1' '0') as well as adds 12394 // 2 quote characters (before and after). Make sure the resulting size (3 * length + 2) will not overflow unsigned. 12395 12396 unsigned length = string.length(); 12397 12398 if (!length) 12399 return String("\'\'"); 12400 12401 if (length > std::numeric_limits<unsigned>::max() / 3 - 2) 12402 return emptyString(); 12403 12404 if (string.is8Bit()) 12405 return quoteCSSStringInternal(string.characters8(), length); 12406 return quoteCSSStringInternal(string.characters16(), length); 12407} 12408 12409String quoteCSSStringIfNeeded(const String& string) 12410{ 12411 return isCSSTokenizerIdentifier(string) ? string : quoteCSSString(string); 12412} 12413 12414String quoteCSSURLIfNeeded(const String& string) 12415{ 12416 return isCSSTokenizerURL(string) ? string : quoteCSSString(string); 12417} 12418 12419bool isValidNthToken(const CSSParserString& token) 12420{ 12421 // The tokenizer checks for the construct of an+b. 12422 // However, since the {ident} rule precedes the {nth} rule, some of those 12423 // tokens are identified as string literal. Furthermore we need to accept 12424 // "odd" and "even" which does not match to an+b. 12425 return equalIgnoringCase(token, "odd") || equalIgnoringCase(token, "even") 12426 || equalIgnoringCase(token, "n") || equalIgnoringCase(token, "-n"); 12427} 12428 12429} 12430