HELLO·Android
系统源代码
IT资讯
技术文章
我的收藏
注册
登录
-
我收藏的文章
创建代码块
我的代码块
我的账号
Ice Cream Sandwich
|
4.0.2_r1
下载
查看原文件
收藏
根目录
external
webkit
Source
WebCore
css
CSSParser.cpp
/* * Copyright (C) 2003 Lars Knoll (knoll@kde.org) * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * Copyright (C) 2007 Nicholas Shanks
* Copyright (C) 2008 Eric Seidel
* Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "CSSParser.h" #include "CSSBorderImageValue.h" #include "CSSCanvasValue.h" #include "CSSCharsetRule.h" #include "CSSCursorImageValue.h" #include "CSSFontFaceRule.h" #include "CSSFontFaceSrcValue.h" #include "CSSGradientValue.h" #include "CSSImageValue.h" #include "CSSImportRule.h" #include "CSSInheritedValue.h" #include "CSSInitialValue.h" #include "CSSLineBoxContainValue.h" #include "CSSMediaRule.h" #include "CSSMutableStyleDeclaration.h" #include "CSSPageRule.h" #include "CSSPrimitiveValue.h" #include "CSSPrimitiveValueCache.h" #include "CSSProperty.h" #include "CSSPropertyNames.h" #include "CSSPropertySourceData.h" #include "CSSQuirkPrimitiveValue.h" #include "CSSReflectValue.h" #include "CSSRuleList.h" #include "CSSSelector.h" #include "CSSStyleRule.h" #include "CSSStyleSheet.h" #include "CSSTimingFunctionValue.h" #include "CSSUnicodeRangeValue.h" #include "CSSValueKeywords.h" #include "CSSValueList.h" #include "Counter.h" #include "Document.h" #include "FloatConversion.h" #include "FontFamilyValue.h" #include "FontValue.h" #include "HTMLParserIdioms.h" #include "HashTools.h" #include "MediaList.h" #include "MediaQueryExp.h" #include "Page.h" #include "Pair.h" #include "Rect.h" #include "RenderTheme.h" #include "ShadowValue.h" #include "WebKitCSSKeyframeRule.h" #include "WebKitCSSKeyframesRule.h" #include "WebKitCSSTransformValue.h" #include
#include
#include
#include
#if ENABLE(DASHBOARD_SUPPORT) #include "DashboardRegion.h" #endif #define YYDEBUG 0 #if YYDEBUG > 0 extern int cssyydebug; #endif extern int cssyyparse(void* parser); using namespace std; using namespace WTF; #ifdef ANDROID_INSTRUMENT #include "TimeCounter.h" #endif namespace WebCore { static const unsigned INVALID_NUM_PARSED_PROPERTIES = UINT_MAX; static const double MAX_SCALE = 1000000; static bool equal(const CSSParserString& a, const char* b) { for (int i = 0; i < a.length; ++i) { if (!b[i]) return false; if (a.characters[i] != b[i]) return false; } return !b[a.length]; } static bool equalIgnoringCase(const CSSParserString& a, const char* b) { for (int i = 0; i < a.length; ++i) { if (!b[i]) return false; ASSERT(!isASCIIUpper(b[i])); if (toASCIILower(a.characters[i]) != b[i]) return false; } return !b[a.length]; } static bool hasPrefix(const char* string, unsigned length, const char* prefix) { for (unsigned i = 0; i < length; ++i) { if (!prefix[i]) return true; if (string[i] != prefix[i]) return false; } return false; } CSSParser::CSSParser(bool strictParsing) : m_strict(strictParsing) , m_important(false) , m_id(0) , m_styleSheet(0) , m_valueList(0) , m_parsedProperties(static_cast
(fastMalloc(32 * sizeof(CSSProperty*)))) , m_numParsedProperties(0) , m_maxParsedProperties(32) , m_numParsedPropertiesBeforeMarginBox(INVALID_NUM_PARSED_PROPERTIES) , m_inParseShorthand(0) , m_currentShorthand(0) , m_implicitShorthand(false) , m_hasFontFaceOnlyValues(false) , m_hadSyntacticallyValidCSSRule(false) , m_defaultNamespace(starAtom) , m_inStyleRuleOrDeclaration(false) , m_selectorListRange(0, 0) , m_ruleBodyRange(0, 0) , m_propertyRange(UINT_MAX, UINT_MAX) , m_ruleRangeMap(0) , m_currentRuleData(0) , m_data(0) , yy_start(1) , m_lineNumber(0) , m_lastSelectorLineNumber(0) , m_allowImportRules(true) , m_allowNamespaceDeclarations(true) { #if YYDEBUG > 0 cssyydebug = 1; #endif CSSPropertySourceData::init(); } CSSParser::~CSSParser() { clearProperties(); fastFree(m_parsedProperties); delete m_valueList; fastFree(m_data); fastDeleteAllValues(m_floatingSelectors); deleteAllValues(m_floatingSelectorVectors); deleteAllValues(m_floatingValueLists); deleteAllValues(m_floatingFunctions); } void CSSParserString::lower() { // FIXME: If we need Unicode lowercasing here, then we probably want the real kind // that can potentially change the length of the string rather than the character // by character kind. If we don't need Unicode lowercasing, it would be good to // simplify this function. if (charactersAreAllASCII(characters, length)) { // Fast case for all-ASCII. for (int i = 0; i < length; i++) characters[i] = toASCIILower(characters[i]); } else { for (int i = 0; i < length; i++) characters[i] = Unicode::toLower(characters[i]); } } void CSSParser::setupParser(const char* prefix, const String& string, const char* suffix) { int length = string.length() + strlen(prefix) + strlen(suffix) + 2; fastFree(m_data); m_data = static_cast
(fastMalloc(length * sizeof(UChar))); for (unsigned i = 0; i < strlen(prefix); i++) m_data[i] = prefix[i]; memcpy(m_data + strlen(prefix), string.characters(), string.length() * sizeof(UChar)); unsigned start = strlen(prefix) + string.length(); unsigned end = start + strlen(suffix); for (unsigned i = start; i < end; i++) m_data[i] = suffix[i - start]; m_data[length - 1] = 0; m_data[length - 2] = 0; yy_hold_char = 0; yyleng = 0; yytext = yy_c_buf_p = m_data; yy_hold_char = *yy_c_buf_p; resetRuleBodyMarks(); } void CSSParser::parseSheet(CSSStyleSheet* sheet, const String& string, int startLineNumber, StyleRuleRangeMap* ruleRangeMap) { #ifdef ANDROID_INSTRUMENT android::TimeCounter::start(android::TimeCounter::CSSParseTimeCounter); #endif setStyleSheet(sheet); m_defaultNamespace = starAtom; // Reset the default namespace. m_ruleRangeMap = ruleRangeMap; if (ruleRangeMap) { m_currentRuleData = CSSRuleSourceData::create(); m_currentRuleData->styleSourceData = CSSStyleSourceData::create(); } m_lineNumber = startLineNumber; setupParser("", string, ""); cssyyparse(this); m_ruleRangeMap = 0; m_currentRuleData = 0; m_rule = 0; #ifdef ANDROID_INSTRUMENT android::TimeCounter::record(android::TimeCounter::CSSParseTimeCounter, __FUNCTION__); #endif } PassRefPtr
CSSParser::parseRule(CSSStyleSheet* sheet, const String& string) { #ifdef ANDROID_INSTRUMENT android::TimeCounter::start(android::TimeCounter::CSSParseTimeCounter); #endif setStyleSheet(sheet); m_allowNamespaceDeclarations = false; setupParser("@-webkit-rule{", string, "} "); cssyyparse(this); #ifdef ANDROID_INSTRUMENT android::TimeCounter::record(android::TimeCounter::CSSParseTimeCounter, __FUNCTION__); #endif return m_rule.release(); } PassRefPtr
CSSParser::parseKeyframeRule(CSSStyleSheet *sheet, const String &string) { #ifdef ANDROID_INSTRUMENT android::TimeCounter::start(android::TimeCounter::CSSParseTimeCounter); #endif setStyleSheet(sheet); setupParser("@-webkit-keyframe-rule{ ", string, "} "); cssyyparse(this); #ifdef ANDROID_INSTRUMENT android::TimeCounter::record(android::TimeCounter::CSSParseTimeCounter, __FUNCTION__); #endif return m_keyframe.release(); } static inline bool isColorPropertyID(int propertyId) { switch (propertyId) { case CSSPropertyColor: case CSSPropertyBackgroundColor: case CSSPropertyBorderBottomColor: case CSSPropertyBorderLeftColor: case CSSPropertyBorderRightColor: case CSSPropertyBorderTopColor: case CSSPropertyOutlineColor: case CSSPropertyTextLineThroughColor: case CSSPropertyTextOverlineColor: case CSSPropertyTextUnderlineColor: case CSSPropertyWebkitBorderAfterColor: case CSSPropertyWebkitBorderBeforeColor: case CSSPropertyWebkitBorderEndColor: case CSSPropertyWebkitBorderStartColor: case CSSPropertyWebkitColumnRuleColor: case CSSPropertyWebkitTextEmphasisColor: case CSSPropertyWebkitTextFillColor: case CSSPropertyWebkitTextStrokeColor: return true; default: return false; } } static bool parseColorValue(CSSMutableStyleDeclaration* declaration, int propertyId, const String& string, bool important, bool strict) { if (!string.length()) return false; if (!isColorPropertyID(propertyId)) return false; CSSParserString cssString; cssString.characters = const_cast
(string.characters()); cssString.length = string.length(); int valueID = cssValueKeywordID(cssString); bool validPrimitive = false; if (valueID == CSSValueWebkitText) validPrimitive = true; else if (valueID == CSSValueCurrentcolor) validPrimitive = true; else if ((valueID >= CSSValueAqua && valueID <= CSSValueWindowtext) || valueID == CSSValueMenu || (valueID >= CSSValueWebkitFocusRingColor && valueID < CSSValueWebkitText && !strict)) { validPrimitive = true; } CSSStyleSheet* stylesheet = static_cast
(declaration->stylesheet()); if (!stylesheet || !stylesheet->document()) return false; if (validPrimitive) { CSSProperty property(propertyId, stylesheet->document()->cssPrimitiveValueCache()->createIdentifierValue(valueID), important); declaration->addParsedProperty(property); return true; } RGBA32 color; if (!CSSParser::parseColor(string, color, strict && string[0] != '#')) return false; CSSProperty property(propertyId, stylesheet->document()->cssPrimitiveValueCache()->createColorValue(color), important); declaration->addParsedProperty(property); return true; } static inline bool isSimpleLengthPropertyID(int propertyId, bool& acceptsNegativeNumbers) { switch (propertyId) { case CSSPropertyFontSize: case CSSPropertyHeight: case CSSPropertyWidth: case CSSPropertyMinHeight: case CSSPropertyMinWidth: case CSSPropertyPaddingBottom: case CSSPropertyPaddingLeft: case CSSPropertyPaddingRight: case CSSPropertyPaddingTop: case CSSPropertyWebkitLogicalWidth: case CSSPropertyWebkitLogicalHeight: case CSSPropertyWebkitMinLogicalWidth: case CSSPropertyWebkitMinLogicalHeight: case CSSPropertyWebkitPaddingAfter: case CSSPropertyWebkitPaddingBefore: case CSSPropertyWebkitPaddingEnd: case CSSPropertyWebkitPaddingStart: acceptsNegativeNumbers = false; return true; case CSSPropertyBottom: case CSSPropertyLeft: case CSSPropertyMarginBottom: case CSSPropertyMarginLeft: case CSSPropertyMarginRight: case CSSPropertyMarginTop: case CSSPropertyRight: case CSSPropertyTextIndent: case CSSPropertyTop: case CSSPropertyWebkitMarginAfter: case CSSPropertyWebkitMarginBefore: case CSSPropertyWebkitMarginEnd: case CSSPropertyWebkitMarginStart: acceptsNegativeNumbers = true; return true; default: return false; } } static bool parseSimpleLengthValue(CSSMutableStyleDeclaration* declaration, int propertyId, const String& string, bool important, bool strict) { const UChar* characters = string.characters(); unsigned length = string.length(); if (!characters || !length) return false; bool acceptsNegativeNumbers; if (!isSimpleLengthPropertyID(propertyId, acceptsNegativeNumbers)) return false; CSSPrimitiveValue::UnitTypes unit = CSSPrimitiveValue::CSS_NUMBER; if (length > 2 && characters[length - 2] == 'p' && characters[length - 1] == 'x') { length -= 2; unit = CSSPrimitiveValue::CSS_PX; } else if (length > 1 && characters[length - 1] == '%') { length -= 1; unit = CSSPrimitiveValue::CSS_PERCENTAGE; } // We rely on charactersToDouble for validation as well. The function // will set "ok" to "false" if the entire passed-in character range does // not represent a double. bool ok; double number = charactersToDouble(characters, length, &ok); if (!ok) return false; if (unit == CSSPrimitiveValue::CSS_NUMBER) { if (number && strict) return false; unit = CSSPrimitiveValue::CSS_PX; } if (number < 0 && !acceptsNegativeNumbers) return false; CSSStyleSheet* stylesheet = static_cast
(declaration->stylesheet()); if (!stylesheet || !stylesheet->document()) return false; CSSProperty property(propertyId, stylesheet->document()->cssPrimitiveValueCache()->createValue(number, unit), important); declaration->addParsedProperty(property); return true; } bool CSSParser::parseValue(CSSMutableStyleDeclaration* declaration, int propertyId, const String& string, bool important, bool strict) { if (parseSimpleLengthValue(declaration, propertyId, string, important, strict)) return true; if (parseColorValue(declaration, propertyId, string, important, strict)) return true; CSSParser parser(strict); return parser.parseValue(declaration, propertyId, string, important); } bool CSSParser::parseValue(CSSMutableStyleDeclaration* declaration, int propertyId, const String& string, bool important) { #ifdef ANDROID_INSTRUMENT android::TimeCounter::start(android::TimeCounter::CSSParseTimeCounter); #endif ASSERT(!declaration->stylesheet() || declaration->stylesheet()->isCSSStyleSheet()); setStyleSheet(static_cast
(declaration->stylesheet())); setupParser("@-webkit-value{", string, "} "); m_id = propertyId; m_important = important; cssyyparse(this); m_rule = 0; bool ok = false; if (m_hasFontFaceOnlyValues) deleteFontFaceOnlyValues(); if (m_numParsedProperties) { ok = true; declaration->addParsedProperties(m_parsedProperties, m_numParsedProperties); clearProperties(); } #ifdef ANDROID_INSTRUMENT android::TimeCounter::record(android::TimeCounter::CSSParseTimeCounter, __FUNCTION__); #endif return ok; } // color will only be changed when string contains a valid css color, making it // possible to set up a default color. bool CSSParser::parseColor(RGBA32& color, const String& string, bool strict) { // First try creating a color specified by name, rgba(), rgb() or "#" syntax. if (parseColor(string, color, strict)) return true; CSSParser parser(true); RefPtr
dummyStyleDeclaration = CSSMutableStyleDeclaration::create(); // Now try to create a color from rgba() syntax. if (!parser.parseColor(dummyStyleDeclaration.get(), string)) return false; CSSValue* value = parser.m_parsedProperties[0]->value(); if (value->cssValueType() != CSSValue::CSS_PRIMITIVE_VALUE) return false; CSSPrimitiveValue* primitiveValue = static_cast
(value); if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_RGBCOLOR) return false; color = primitiveValue->getRGBA32Value(); return true; } bool CSSParser::parseColor(CSSMutableStyleDeclaration* declaration, const String& string) { #ifdef ANDROID_INSTRUMENT android::TimeCounter::start(android::TimeCounter::CSSParseTimeCounter); #endif ASSERT(!declaration->stylesheet() || declaration->stylesheet()->isCSSStyleSheet()); setStyleSheet(static_cast
(declaration->stylesheet())); setupParser("@-webkit-decls{color:", string, "} "); cssyyparse(this); m_rule = 0; #ifdef ANDROID_INSTRUMENT android::TimeCounter::record(android::TimeCounter::CSSParseTimeCounter, __FUNCTION__); #endif return (m_numParsedProperties && m_parsedProperties[0]->m_id == CSSPropertyColor); } bool CSSParser::parseSystemColor(RGBA32& color, const String& string, Document* document) { if (!document || !document->page()) return false; CSSParserString cssColor; cssColor.characters = const_cast
(string.characters()); cssColor.length = string.length(); int id = cssValueKeywordID(cssColor); if (id <= 0) return false; color = document->page()->theme()->systemColor(id).rgb(); return true; } void CSSParser::parseSelector(const String& string, Document* doc, CSSSelectorList& selectorList) { #ifdef ANDROID_INSTRUMENT android::TimeCounter::start(android::TimeCounter::CSSParseTimeCounter); #endif RefPtr
dummyStyleSheet = CSSStyleSheet::create(doc); setStyleSheet(dummyStyleSheet.get()); m_selectorListForParseSelector = &selectorList; setupParser("@-webkit-selector{", string, "}"); cssyyparse(this); m_selectorListForParseSelector = 0; // The style sheet will be deleted right away, so it won't outlive the document. ASSERT(dummyStyleSheet->hasOneRef()); #ifdef ANDROID_INSTRUMENT android::TimeCounter::record(android::TimeCounter::CSSParseTimeCounter, __FUNCTION__); #endif } bool CSSParser::parseDeclaration(CSSMutableStyleDeclaration* declaration, const String& string, RefPtr
* styleSourceData) { #ifdef ANDROID_INSTRUMENT android::TimeCounter::start(android::TimeCounter::CSSParseTimeCounter); #endif // Length of the "@-webkit-decls{" prefix. static const unsigned prefixLength = 15; ASSERT(!declaration->stylesheet() || declaration->stylesheet()->isCSSStyleSheet()); setStyleSheet(static_cast
(declaration->stylesheet())); if (styleSourceData) { m_currentRuleData = CSSRuleSourceData::create(); m_currentRuleData->styleSourceData = CSSStyleSourceData::create(); m_inStyleRuleOrDeclaration = true; } setupParser("@-webkit-decls{", string, "} "); cssyyparse(this); m_rule = 0; bool ok = false; if (m_hasFontFaceOnlyValues) deleteFontFaceOnlyValues(); if (m_numParsedProperties) { ok = true; declaration->addParsedProperties(m_parsedProperties, m_numParsedProperties); clearProperties(); } if (m_currentRuleData) { m_currentRuleData->styleSourceData->styleBodyRange.start = 0; m_currentRuleData->styleSourceData->styleBodyRange.end = string.length(); for (Vector
::iterator it = m_currentRuleData->styleSourceData->propertyData.begin(), endIt = m_currentRuleData->styleSourceData->propertyData.end(); it != endIt; ++it) { (*it).range.start -= prefixLength; (*it).range.end -= prefixLength; } } if (styleSourceData) { *styleSourceData = m_currentRuleData->styleSourceData.release(); m_currentRuleData = 0; m_inStyleRuleOrDeclaration = false; } #ifdef ANDROID_INSTRUMENT android::TimeCounter::record(android::TimeCounter::CSSParseTimeCounter, __FUNCTION__); #endif return ok; } bool CSSParser::parseMediaQuery(MediaList* queries, const String& string) { if (string.isEmpty()) return true; #ifdef ANDROID_INSTRUMENT android::TimeCounter::start(android::TimeCounter::CSSParseTimeCounter); #endif ASSERT(!m_mediaQuery); // can't use { because tokenizer state switches from mediaquery to initial state when it sees { token. // instead insert one " " (which is WHITESPACE in CSSGrammar.y) setupParser("@-webkit-mediaquery ", string, "} "); cssyyparse(this); bool ok = false; if (m_mediaQuery) { ok = true; queries->appendMediaQuery(m_mediaQuery.release()); } #ifdef ANDROID_INSTRUMENT android::TimeCounter::record(android::TimeCounter::CSSParseTimeCounter, __FUNCTION__); #endif return ok; } void CSSParser::addProperty(int propId, PassRefPtr
value, bool important) { OwnPtr
prop(new CSSProperty(propId, value, important, m_currentShorthand, m_implicitShorthand)); if (m_numParsedProperties >= m_maxParsedProperties) { m_maxParsedProperties += 32; if (m_maxParsedProperties > UINT_MAX / sizeof(CSSProperty*)) return; m_parsedProperties = static_cast
(fastRealloc(m_parsedProperties, m_maxParsedProperties * sizeof(CSSProperty*))); } m_parsedProperties[m_numParsedProperties++] = prop.leakPtr(); } void CSSParser::rollbackLastProperties(int num) { ASSERT(num >= 0); ASSERT(m_numParsedProperties >= static_cast
(num)); for (int i = 0; i < num; ++i) delete m_parsedProperties[--m_numParsedProperties]; } void CSSParser::clearProperties() { for (unsigned i = 0; i < m_numParsedProperties; i++) delete m_parsedProperties[i]; m_numParsedProperties = 0; m_numParsedPropertiesBeforeMarginBox = INVALID_NUM_PARSED_PROPERTIES; m_hasFontFaceOnlyValues = false; } void CSSParser::setStyleSheet(CSSStyleSheet* styleSheet) { m_styleSheet = styleSheet; m_primitiveValueCache = document() ? document()->cssPrimitiveValueCache() : CSSPrimitiveValueCache::create(); } Document* CSSParser::document() const { StyleBase* root = m_styleSheet; while (root && root->parent()) root = root->parent(); if (!root) return 0; if (!root->isCSSStyleSheet()) return 0; return static_cast
(root)->document(); } bool CSSParser::validUnit(CSSParserValue* value, Units unitflags, bool strict) { bool b = false; switch (value->unit) { case CSSPrimitiveValue::CSS_NUMBER: b = (unitflags & FNumber); if (!b && ((unitflags & (FLength | FAngle | FTime)) && (value->fValue == 0 || !strict))) { value->unit = (unitflags & FLength) ? CSSPrimitiveValue::CSS_PX : ((unitflags & FAngle) ? CSSPrimitiveValue::CSS_DEG : CSSPrimitiveValue::CSS_MS); b = true; } if (!b && (unitflags & FInteger) && value->isInt) b = true; break; case CSSPrimitiveValue::CSS_PERCENTAGE: b = (unitflags & FPercent); break; case CSSParserValue::Q_EMS: case CSSPrimitiveValue::CSS_EMS: case CSSPrimitiveValue::CSS_REMS: case CSSPrimitiveValue::CSS_EXS: case CSSPrimitiveValue::CSS_PX: case CSSPrimitiveValue::CSS_CM: case CSSPrimitiveValue::CSS_MM: case CSSPrimitiveValue::CSS_IN: case CSSPrimitiveValue::CSS_PT: case CSSPrimitiveValue::CSS_PC: b = (unitflags & FLength); break; case CSSPrimitiveValue::CSS_MS: case CSSPrimitiveValue::CSS_S: b = (unitflags & FTime); break; case CSSPrimitiveValue::CSS_DEG: case CSSPrimitiveValue::CSS_RAD: case CSSPrimitiveValue::CSS_GRAD: case CSSPrimitiveValue::CSS_TURN: b = (unitflags & FAngle); break; case CSSPrimitiveValue::CSS_HZ: case CSSPrimitiveValue::CSS_KHZ: case CSSPrimitiveValue::CSS_DIMENSION: default: break; } if (b && unitflags & FNonNeg && value->fValue < 0) b = false; return b; } static int unitFromString(CSSParserValue* value) { if (value->unit != CSSPrimitiveValue::CSS_IDENT || value->id) return 0; if (equal(value->string, "em")) return CSSPrimitiveValue::CSS_EMS; if (equal(value->string, "rem")) return CSSPrimitiveValue::CSS_REMS; if (equal(value->string, "ex")) return CSSPrimitiveValue::CSS_EXS; if (equal(value->string, "px")) return CSSPrimitiveValue::CSS_PX; if (equal(value->string, "cm")) return CSSPrimitiveValue::CSS_CM; if (equal(value->string, "mm")) return CSSPrimitiveValue::CSS_MM; if (equal(value->string, "in")) return CSSPrimitiveValue::CSS_IN; if (equal(value->string, "pt")) return CSSPrimitiveValue::CSS_PT; if (equal(value->string, "pc")) return CSSPrimitiveValue::CSS_PC; if (equal(value->string, "deg")) return CSSPrimitiveValue::CSS_DEG; if (equal(value->string, "rad")) return CSSPrimitiveValue::CSS_RAD; if (equal(value->string, "grad")) return CSSPrimitiveValue::CSS_GRAD; if (equal(value->string, "turn")) return CSSPrimitiveValue::CSS_TURN; if (equal(value->string, "ms")) return CSSPrimitiveValue::CSS_MS; if (equal(value->string, "s")) return CSSPrimitiveValue::CSS_S; if (equal(value->string, "Hz")) return CSSPrimitiveValue::CSS_HZ; if (equal(value->string, "kHz")) return CSSPrimitiveValue::CSS_KHZ; return 0; } void CSSParser::checkForOrphanedUnits() { if (m_strict || inShorthand()) return; // The purpose of this code is to implement the WinIE quirk that allows unit types to be separated from their numeric values // by whitespace, so e.g., width: 20 px instead of width:20px. This is invalid CSS, so we don't do this in strict mode. CSSParserValue* numericVal = 0; unsigned size = m_valueList->size(); for (unsigned i = 0; i < size; i++) { CSSParserValue* value = m_valueList->valueAt(i); if (numericVal) { // Change the unit type of the numeric val to match. int unit = unitFromString(value); if (unit) { numericVal->unit = unit; numericVal = 0; // Now delete the bogus unit value. m_valueList->deleteValueAt(i); i--; // We're safe even though |i| is unsigned, since we only hit this code if we had a previous numeric value (so |i| is always > 0 here). size--; continue; } } numericVal = (value->unit == CSSPrimitiveValue::CSS_NUMBER) ? value : 0; } } bool CSSParser::parseValue(int propId, bool important) { if (!m_valueList) return false; CSSParserValue* value = m_valueList->current(); if (!value) return false; int id = value->id; // In quirks mode, we will look for units that have been incorrectly separated from the number they belong to // by a space. We go ahead and associate the unit with the number even though it is invalid CSS. checkForOrphanedUnits(); int num = inShorthand() ? 1 : m_valueList->size(); if (id == CSSValueInherit) { if (num != 1) return false; addProperty(propId, CSSInheritedValue::create(), important); return true; } else if (id == CSSValueInitial) { if (num != 1) return false; addProperty(propId, CSSInitialValue::createExplicit(), important); return true; } bool validPrimitive = false; RefPtr
parsedValue; switch (static_cast
(propId)) { /* The comment to the left defines all valid value of this properties as defined * in CSS 2, Appendix F. Property index */ /* All the CSS properties are not supported by the renderer at the moment. * Note that all the CSS2 Aural properties are only checked, if CSS_AURAL is defined * (see parseAuralValues). As we don't support them at all this seems reasonable. */ case CSSPropertySize: //
{1,2} | auto | [
|| [ portrait | landscape] ] return parseSize(propId, important); case CSSPropertyQuotes: // [
]+ | none | inherit if (id) validPrimitive = true; else return parseQuotes(propId, important); break; case CSSPropertyUnicodeBidi: // normal | embed | bidi-override | isolate | inherit if (id == CSSValueNormal || id == CSSValueEmbed || id == CSSValueBidiOverride || id == CSSValueWebkitIsolate) validPrimitive = true; break; case CSSPropertyPosition: // static | relative | absolute | fixed | inherit if (id == CSSValueStatic || id == CSSValueRelative || id == CSSValueAbsolute || id == CSSValueFixed) validPrimitive = true; break; case CSSPropertyPageBreakAfter: // auto | always | avoid | left | right | inherit case CSSPropertyPageBreakBefore: case CSSPropertyWebkitColumnBreakAfter: case CSSPropertyWebkitColumnBreakBefore: if (id == CSSValueAuto || id == CSSValueAlways || id == CSSValueAvoid || id == CSSValueLeft || id == CSSValueRight) validPrimitive = true; break; case CSSPropertyPageBreakInside: // avoid | auto | inherit case CSSPropertyWebkitColumnBreakInside: if (id == CSSValueAuto || id == CSSValueAvoid) validPrimitive = true; break; case CSSPropertyEmptyCells: // show | hide | inherit if (id == CSSValueShow || id == CSSValueHide) validPrimitive = true; break; case CSSPropertyContent: // [
|
|
| attr(X) | open-quote | // close-quote | no-open-quote | no-close-quote ]+ | inherit return parseContent(propId, important); case CSSPropertyWhiteSpace: // normal | pre | nowrap | inherit if (id == CSSValueNormal || id == CSSValuePre || id == CSSValuePreWrap || id == CSSValuePreLine || id == CSSValueNowrap) validPrimitive = true; break; case CSSPropertyClip: //
| auto | inherit if (id == CSSValueAuto) validPrimitive = true; else if (value->unit == CSSParserValue::Function) return parseShape(propId, important); break; /* Start of supported CSS properties with validation. This is needed for parseShorthand to work * correctly and allows optimization in WebCore::applyRule(..) */ case CSSPropertyCaptionSide: // top | bottom | left | right | inherit if (id == CSSValueLeft || id == CSSValueRight || id == CSSValueTop || id == CSSValueBottom) validPrimitive = true; break; case CSSPropertyBorderCollapse: // collapse | separate | inherit if (id == CSSValueCollapse || id == CSSValueSeparate) validPrimitive = true; break; case CSSPropertyVisibility: // visible | hidden | collapse | inherit if (id == CSSValueVisible || id == CSSValueHidden || id == CSSValueCollapse) validPrimitive = true; break; case CSSPropertyOverflow: { ShorthandScope scope(this, propId); if (num != 1 || !parseValue(CSSPropertyOverflowX, important)) return false; CSSValue* value = m_parsedProperties[m_numParsedProperties - 1]->value(); addProperty(CSSPropertyOverflowY, value, important); return true; } case CSSPropertyOverflowX: case CSSPropertyOverflowY: // visible | hidden | scroll | auto | marquee | overlay | inherit if (id == CSSValueVisible || id == CSSValueHidden || id == CSSValueScroll || id == CSSValueAuto || id == CSSValueOverlay || id == CSSValueWebkitMarquee) validPrimitive = true; break; case CSSPropertyListStylePosition: // inside | outside | inherit if (id == CSSValueInside || id == CSSValueOutside) validPrimitive = true; break; case CSSPropertyListStyleType: // See section CSS_PROP_LIST_STYLE_TYPE of file CSSValueKeywords.in // for the list of supported list-style-types. if ((id >= CSSValueDisc && id <= CSSValueKatakanaIroha) || id == CSSValueNone) validPrimitive = true; break; case CSSPropertyDisplay: // inline | block | list-item | run-in | inline-block | table | // inline-table | table-row-group | table-header-group | table-footer-group | table-row | // table-column-group | table-column | table-cell | table-caption | box | inline-box | none | inherit #if ENABLE(WCSS) if ((id >= CSSValueInline && id <= CSSValueWapMarquee) || id == CSSValueNone) #else if ((id >= CSSValueInline && id <= CSSValueWebkitInlineBox) || id == CSSValueNone) #endif validPrimitive = true; break; case CSSPropertyDirection: // ltr | rtl | inherit if (id == CSSValueLtr || id == CSSValueRtl) validPrimitive = true; break; case CSSPropertyTextTransform: // capitalize | uppercase | lowercase | none | inherit if ((id >= CSSValueCapitalize && id <= CSSValueLowercase) || id == CSSValueNone) validPrimitive = true; break; case CSSPropertyFloat: // left | right | none | inherit + center for buggy CSS if (id == CSSValueLeft || id == CSSValueRight || id == CSSValueNone || id == CSSValueCenter) validPrimitive = true; break; case CSSPropertyClear: // none | left | right | both | inherit if (id == CSSValueNone || id == CSSValueLeft || id == CSSValueRight|| id == CSSValueBoth) validPrimitive = true; break; case CSSPropertyTextAlign: // left | right | center | justify | webkit_left | webkit_right | webkit_center | webkit_match_parent | // start | end |
| inherit if ((id >= CSSValueWebkitAuto && id <= CSSValueWebkitMatchParent) || id == CSSValueStart || id == CSSValueEnd || value->unit == CSSPrimitiveValue::CSS_STRING) validPrimitive = true; break; case CSSPropertyOutlineStyle: // (
except hidden) | auto | inherit if (id == CSSValueAuto || id == CSSValueNone || (id >= CSSValueInset && id <= CSSValueDouble)) validPrimitive = true; break; case CSSPropertyBorderTopStyle: ////
| inherit case CSSPropertyBorderRightStyle: // Defined as: none | hidden | dotted | dashed | case CSSPropertyBorderBottomStyle: // solid | double | groove | ridge | inset | outset case CSSPropertyBorderLeftStyle: case CSSPropertyWebkitBorderStartStyle: case CSSPropertyWebkitBorderEndStyle: case CSSPropertyWebkitBorderBeforeStyle: case CSSPropertyWebkitBorderAfterStyle: case CSSPropertyWebkitColumnRuleStyle: if (id >= CSSValueNone && id <= CSSValueDouble) validPrimitive = true; break; case CSSPropertyFontWeight: // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit return parseFontWeight(important); case CSSPropertyBorderSpacing: { const int properties[2] = { CSSPropertyWebkitBorderHorizontalSpacing, CSSPropertyWebkitBorderVerticalSpacing }; if (num == 1) { ShorthandScope scope(this, CSSPropertyBorderSpacing); if (!parseValue(properties[0], important)) return false; CSSValue* value = m_parsedProperties[m_numParsedProperties-1]->value(); addProperty(properties[1], value, important); return true; } else if (num == 2) { ShorthandScope scope(this, CSSPropertyBorderSpacing); if (!parseValue(properties[0], important) || !parseValue(properties[1], important)) return false; return true; } return false; } case CSSPropertyWebkitBorderHorizontalSpacing: case CSSPropertyWebkitBorderVerticalSpacing: validPrimitive = validUnit(value, FLength | FNonNeg, m_strict); break; case CSSPropertyOutlineColor: //
| invert | inherit // Outline color has "invert" as additional keyword. // Also, we want to allow the special focus color even in strict parsing mode. if (id == CSSValueInvert || id == CSSValueWebkitFocusRingColor) { validPrimitive = true; break; } /* nobreak */ case CSSPropertyBackgroundColor: //
| inherit case CSSPropertyBorderTopColor: //
| inherit case CSSPropertyBorderRightColor: case CSSPropertyBorderBottomColor: case CSSPropertyBorderLeftColor: case CSSPropertyWebkitBorderStartColor: case CSSPropertyWebkitBorderEndColor: case CSSPropertyWebkitBorderBeforeColor: case CSSPropertyWebkitBorderAfterColor: case CSSPropertyColor: //
| inherit case CSSPropertyTextLineThroughColor: // CSS3 text decoration colors case CSSPropertyTextUnderlineColor: case CSSPropertyTextOverlineColor: case CSSPropertyWebkitColumnRuleColor: case CSSPropertyWebkitTextEmphasisColor: case CSSPropertyWebkitTextFillColor: case CSSPropertyWebkitTextStrokeColor: if (id == CSSValueWebkitText) validPrimitive = true; // Always allow this, even when strict parsing is on, // since we use this in our UA sheets. else if (id == CSSValueCurrentcolor) validPrimitive = true; else if ((id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || (id >= CSSValueWebkitFocusRingColor && id < CSSValueWebkitText && !m_strict)) { validPrimitive = true; } else { parsedValue = parseColor(); if (parsedValue) m_valueList->next(); } break; case CSSPropertyCursor: { // [
,]* [ auto | crosshair | default | pointer | progress | move | e-resize | ne-resize | // nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize | ew-resize | // ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | text | wait | help | // vertical-text | cell | context-menu | alias | copy | no-drop | not-allowed | -webkit-zoom-in // -webkit-zoom-out | all-scroll | -webkit-grab | -webkit-grabbing ] ] | inherit RefPtr
list; while (value && value->unit == CSSPrimitiveValue::CSS_URI) { if (!list) list = CSSValueList::createCommaSeparated(); String uri = value->string; Vector
coords; value = m_valueList->next(); while (value && value->unit == CSSPrimitiveValue::CSS_NUMBER) { coords.append(int(value->fValue)); value = m_valueList->next(); } IntPoint hotSpot(-1, -1); int nrcoords = coords.size(); if (nrcoords > 0 && nrcoords != 2) return false; if (nrcoords == 2) hotSpot = IntPoint(coords[0], coords[1]); if (!uri.isNull() && m_styleSheet) { // FIXME: The completeURL call should be done when using the CSSCursorImageValue, // not when creating it. list->append(CSSCursorImageValue::create(m_styleSheet->completeURL(uri), hotSpot)); } if ((m_strict && !value) || (value && !(value->unit == CSSParserValue::Operator && value->iValue == ','))) return false; value = m_valueList->next(); // comma } if (list) { if (!value) { // no value after url list (MSIE 5 compatibility) if (list->length() != 1) return false; } else if (!m_strict && value->id == CSSValueHand) // MSIE 5 compatibility :/ list->append(primitiveValueCache()->createIdentifierValue(CSSValuePointer)); else if (value && ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitGrabbing) || value->id == CSSValueCopy || value->id == CSSValueNone)) list->append(primitiveValueCache()->createIdentifierValue(value->id)); m_valueList->next(); parsedValue = list.release(); break; } id = value->id; if (!m_strict && value->id == CSSValueHand) { // MSIE 5 compatibility :/ id = CSSValuePointer; validPrimitive = true; } else if ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitGrabbing) || value->id == CSSValueCopy || value->id == CSSValueNone) validPrimitive = true; break; } case CSSPropertyBackgroundAttachment: case CSSPropertyBackgroundClip: case CSSPropertyWebkitBackgroundClip: case CSSPropertyWebkitBackgroundComposite: case CSSPropertyBackgroundImage: case CSSPropertyBackgroundOrigin: case CSSPropertyWebkitBackgroundOrigin: case CSSPropertyBackgroundPosition: case CSSPropertyBackgroundPositionX: case CSSPropertyBackgroundPositionY: case CSSPropertyBackgroundSize: case CSSPropertyWebkitBackgroundSize: case CSSPropertyBackgroundRepeat: case CSSPropertyBackgroundRepeatX: case CSSPropertyBackgroundRepeatY: case CSSPropertyWebkitMaskAttachment: case CSSPropertyWebkitMaskClip: case CSSPropertyWebkitMaskComposite: case CSSPropertyWebkitMaskImage: case CSSPropertyWebkitMaskOrigin: case CSSPropertyWebkitMaskPosition: case CSSPropertyWebkitMaskPositionX: case CSSPropertyWebkitMaskPositionY: case CSSPropertyWebkitMaskSize: case CSSPropertyWebkitMaskRepeat: case CSSPropertyWebkitMaskRepeatX: case CSSPropertyWebkitMaskRepeatY: { RefPtr
val1; RefPtr
val2; int propId1, propId2; bool result = false; if (parseFillProperty(propId, propId1, propId2, val1, val2)) { OwnPtr
shorthandScope; if (propId == CSSPropertyBackgroundPosition || propId == CSSPropertyBackgroundRepeat || propId == CSSPropertyWebkitMaskPosition || propId == CSSPropertyWebkitMaskRepeat) { shorthandScope.set(new ShorthandScope(this, propId)); } addProperty(propId1, val1.release(), important); if (val2) addProperty(propId2, val2.release(), important); result = true; } m_implicitShorthand = false; return result; } case CSSPropertyListStyleImage: //
| none | inherit if (id == CSSValueNone) { parsedValue = CSSImageValue::create(); m_valueList->next(); } else if (value->unit == CSSPrimitiveValue::CSS_URI) { if (m_styleSheet) { // FIXME: The completeURL call should be done when using the CSSImageValue, // not when creating it. parsedValue = CSSImageValue::create(m_styleSheet->completeURL(value->string)); m_valueList->next(); } } else if (isGeneratedImageValue(value)) { if (parseGeneratedImage(parsedValue)) m_valueList->next(); else return false; } break; case CSSPropertyWebkitTextStrokeWidth: case CSSPropertyOutlineWidth: //
| inherit case CSSPropertyBorderTopWidth: ////
| inherit case CSSPropertyBorderRightWidth: // Which is defined as case CSSPropertyBorderBottomWidth: // thin | medium | thick |
case CSSPropertyBorderLeftWidth: case CSSPropertyWebkitBorderStartWidth: case CSSPropertyWebkitBorderEndWidth: case CSSPropertyWebkitBorderBeforeWidth: case CSSPropertyWebkitBorderAfterWidth: case CSSPropertyWebkitColumnRuleWidth: if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick) validPrimitive = true; else validPrimitive = validUnit(value, FLength | FNonNeg, m_strict); break; case CSSPropertyLetterSpacing: // normal |
| inherit case CSSPropertyWordSpacing: // normal |
| inherit if (id == CSSValueNormal) validPrimitive = true; else validPrimitive = validUnit(value, FLength, m_strict); break; case CSSPropertyWordBreak: // normal | break-all | break-word (this is a custom extension) if (id == CSSValueNormal || id == CSSValueBreakAll || id == CSSValueBreakWord) validPrimitive = true; break; case CSSPropertyWordWrap: // normal | break-word if (id == CSSValueNormal || id == CSSValueBreakWord) validPrimitive = true; break; case CSSPropertySpeak: // none | normal | spell-out | digits | literal-punctuation | no-punctuation | inherit if (id == CSSValueNone || id == CSSValueNormal || id == CSSValueSpellOut || id == CSSValueDigits || id == CSSValueLiteralPunctuation || id == CSSValueNoPunctuation) validPrimitive = true; break; case CSSPropertyTextIndent: //
|
| inherit validPrimitive = (!id && validUnit(value, FLength | FPercent, m_strict)); break; case CSSPropertyPaddingTop: ////
| inherit case CSSPropertyPaddingRight: // Which is defined as case CSSPropertyPaddingBottom: //
|
case CSSPropertyPaddingLeft: //// case CSSPropertyWebkitPaddingStart: case CSSPropertyWebkitPaddingEnd: case CSSPropertyWebkitPaddingBefore: case CSSPropertyWebkitPaddingAfter: validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg, m_strict)); break; case CSSPropertyMaxHeight: //
|
| none | inherit case CSSPropertyMaxWidth: //
|
| none | inherit case CSSPropertyWebkitMaxLogicalWidth: case CSSPropertyWebkitMaxLogicalHeight: if (id == CSSValueNone || id == CSSValueIntrinsic || id == CSSValueMinIntrinsic) { validPrimitive = true; break; } /* nobreak */ case CSSPropertyMinHeight: //
|
| inherit case CSSPropertyMinWidth: //
|
| inherit case CSSPropertyWebkitMinLogicalWidth: case CSSPropertyWebkitMinLogicalHeight: if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic) validPrimitive = true; else validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg, m_strict)); break; case CSSPropertyFontSize: //
|
|
|
| inherit if (id >= CSSValueXxSmall && id <= CSSValueLarger) validPrimitive = true; else validPrimitive = (validUnit(value, FLength | FPercent | FNonNeg, m_strict)); break; case CSSPropertyFontStyle: // normal | italic | oblique | inherit return parseFontStyle(important); case CSSPropertyFontVariant: // normal | small-caps | inherit return parseFontVariant(important); case CSSPropertyVerticalAlign: // baseline | sub | super | top | text-top | middle | bottom | text-bottom | //
|
| inherit if (id >= CSSValueBaseline && id <= CSSValueWebkitBaselineMiddle) validPrimitive = true; else validPrimitive = (!id && validUnit(value, FLength | FPercent, m_strict)); break; case CSSPropertyHeight: //
|
| auto | inherit case CSSPropertyWidth: //
|
| auto | inherit case CSSPropertyWebkitLogicalWidth: case CSSPropertyWebkitLogicalHeight: if (id == CSSValueAuto || id == CSSValueIntrinsic || id == CSSValueMinIntrinsic) validPrimitive = true; else // ### handle multilength case where we allow relative units validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg, m_strict)); break; case CSSPropertyBottom: //
|
| auto | inherit case CSSPropertyLeft: //
|
| auto | inherit case CSSPropertyRight: //
|
| auto | inherit case CSSPropertyTop: //
|
| auto | inherit case CSSPropertyMarginTop: ////
| inherit case CSSPropertyMarginRight: // Which is defined as case CSSPropertyMarginBottom: //
|
| auto | inherit case CSSPropertyMarginLeft: //// case CSSPropertyWebkitMarginStart: case CSSPropertyWebkitMarginEnd: case CSSPropertyWebkitMarginBefore: case CSSPropertyWebkitMarginAfter: if (id == CSSValueAuto) validPrimitive = true; else validPrimitive = (!id && validUnit(value, FLength | FPercent, m_strict)); break; case CSSPropertyZIndex: // auto |
| inherit if (id == CSSValueAuto) { validPrimitive = true; break; } /* nobreak */ case CSSPropertyOrphans: //
| inherit case CSSPropertyWidows: //
| inherit // ### not supported later on validPrimitive = (!id && validUnit(value, FInteger, false)); break; case CSSPropertyLineHeight: // normal |
|
|
| inherit if (id == CSSValueNormal) validPrimitive = true; else validPrimitive = (!id && validUnit(value, FNumber | FLength | FPercent | FNonNeg, m_strict)); break; case CSSPropertyCounterIncrement: // [
? ]+ | none | inherit if (id != CSSValueNone) return parseCounter(propId, 1, important); validPrimitive = true; break; case CSSPropertyCounterReset: // [
? ]+ | none | inherit if (id != CSSValueNone) return parseCounter(propId, 0, important); validPrimitive = true; break; case CSSPropertyFontFamily: // [[
|
],]* [
|
] | inherit { parsedValue = parseFontFamily(); break; } case CSSPropertyTextDecoration: case CSSPropertyWebkitTextDecorationsInEffect: // none | [ underline || overline || line-through || blink ] | inherit if (id == CSSValueNone) { validPrimitive = true; } else { RefPtr
list = CSSValueList::createSpaceSeparated(); bool isValid = true; while (isValid && value) { switch (value->id) { case CSSValueBlink: break; case CSSValueUnderline: case CSSValueOverline: case CSSValueLineThrough: list->append(primitiveValueCache()->createIdentifierValue(value->id)); break; default: isValid = false; } value = m_valueList->next(); } if (list->length() && isValid) { parsedValue = list.release(); m_valueList->next(); } } break; case CSSPropertyZoom: // normal | reset | document |
|
| inherit if (id == CSSValueNormal || id == CSSValueReset || id == CSSValueDocument) validPrimitive = true; else validPrimitive = (!id && validUnit(value, FNumber | FPercent | FNonNeg, true)); break; case CSSPropertyTableLayout: // auto | fixed | inherit if (id == CSSValueAuto || id == CSSValueFixed) validPrimitive = true; break; case CSSPropertySrc: // Only used within @font-face, so cannot use inherit | initial or be !important. This is a list of urls or local references. return parseFontFaceSrc(); case CSSPropertyUnicodeRange: return parseFontFaceUnicodeRange(); /* CSS3 properties */ case CSSPropertyWebkitAppearance: if ((id >= CSSValueCheckbox && id <= CSSValueTextarea) || id == CSSValueNone) validPrimitive = true; break; case CSSPropertyWebkitBorderImage: case CSSPropertyWebkitMaskBoxImage: if (id == CSSValueNone) validPrimitive = true; else { RefPtr
result; if (parseBorderImage(propId, important, result)) { addProperty(propId, result, important); return true; } } break; case CSSPropertyBorderTopRightRadius: case CSSPropertyBorderTopLeftRadius: case CSSPropertyBorderBottomLeftRadius: case CSSPropertyBorderBottomRightRadius: { if (num != 1 && num != 2) return false; validPrimitive = validUnit(value, FLength | FPercent, m_strict); if (!validPrimitive) return false; RefPtr
parsedValue1 = primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); RefPtr
parsedValue2; if (num == 2) { value = m_valueList->next(); validPrimitive = validUnit(value, FLength | FPercent, m_strict); if (!validPrimitive) return false; parsedValue2 = primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); } else parsedValue2 = parsedValue1; RefPtr
pair = Pair::create(parsedValue1.release(), parsedValue2.release()); RefPtr
val = primitiveValueCache()->createValue(pair.release()); addProperty(propId, val.release(), important); return true; } case CSSPropertyBorderRadius: case CSSPropertyWebkitBorderRadius: return parseBorderRadius(propId, important); case CSSPropertyOutlineOffset: validPrimitive = validUnit(value, FLength | FPercent, m_strict); break; case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3 case CSSPropertyBoxShadow: case CSSPropertyWebkitBoxShadow: if (id == CSSValueNone) validPrimitive = true; else return parseShadow(propId, important); break; case CSSPropertyWebkitBoxReflect: if (id == CSSValueNone) validPrimitive = true; else return parseReflect(propId, important); break; case CSSPropertyOpacity: validPrimitive = validUnit(value, FNumber, m_strict); break; case CSSPropertyWebkitBoxAlign: if (id == CSSValueStretch || id == CSSValueStart || id == CSSValueEnd || id == CSSValueCenter || id == CSSValueBaseline) validPrimitive = true; break; case CSSPropertyWebkitBoxDirection: if (id == CSSValueNormal || id == CSSValueReverse) validPrimitive = true; break; case CSSPropertyWebkitBoxLines: if (id == CSSValueSingle || id == CSSValueMultiple) validPrimitive = true; break; case CSSPropertyWebkitBoxOrient: if (id == CSSValueHorizontal || id == CSSValueVertical || id == CSSValueInlineAxis || id == CSSValueBlockAxis) validPrimitive = true; break; case CSSPropertyWebkitBoxPack: if (id == CSSValueStart || id == CSSValueEnd || id == CSSValueCenter || id == CSSValueJustify) validPrimitive = true; break; case CSSPropertyWebkitBoxFlex: validPrimitive = validUnit(value, FNumber, m_strict); break; case CSSPropertyWebkitBoxFlexGroup: case CSSPropertyWebkitBoxOrdinalGroup: validPrimitive = validUnit(value, FInteger | FNonNeg, true); break; case CSSPropertyBoxSizing: validPrimitive = id == CSSValueBorderBox || id == CSSValueContentBox; break; case CSSPropertyWebkitColorCorrection: validPrimitive = id == CSSValueSrgb || id == CSSValueDefault; break; case CSSPropertyWebkitMarquee: { const int properties[5] = { CSSPropertyWebkitMarqueeDirection, CSSPropertyWebkitMarqueeIncrement, CSSPropertyWebkitMarqueeRepetition, CSSPropertyWebkitMarqueeStyle, CSSPropertyWebkitMarqueeSpeed }; return parseShorthand(propId, properties, 5, important); } case CSSPropertyWebkitMarqueeDirection: if (id == CSSValueForwards || id == CSSValueBackwards || id == CSSValueAhead || id == CSSValueReverse || id == CSSValueLeft || id == CSSValueRight || id == CSSValueDown || id == CSSValueUp || id == CSSValueAuto) validPrimitive = true; break; case CSSPropertyWebkitMarqueeIncrement: if (id == CSSValueSmall || id == CSSValueLarge || id == CSSValueMedium) validPrimitive = true; else validPrimitive = validUnit(value, FLength | FPercent, m_strict); break; case CSSPropertyWebkitMarqueeStyle: if (id == CSSValueNone || id == CSSValueSlide || id == CSSValueScroll || id == CSSValueAlternate) validPrimitive = true; break; case CSSPropertyWebkitMarqueeRepetition: if (id == CSSValueInfinite) validPrimitive = true; else validPrimitive = validUnit(value, FInteger | FNonNeg, m_strict); break; case CSSPropertyWebkitMarqueeSpeed: if (id == CSSValueNormal || id == CSSValueSlow || id == CSSValueFast) validPrimitive = true; else validPrimitive = validUnit(value, FTime | FInteger | FNonNeg, m_strict); break; #if ENABLE(WCSS) case CSSPropertyWapMarqueeDir: if (id == CSSValueLtr || id == CSSValueRtl) validPrimitive = true; break; case CSSPropertyWapMarqueeStyle: if (id == CSSValueNone || id == CSSValueSlide || id == CSSValueScroll || id == CSSValueAlternate) validPrimitive = true; break; case CSSPropertyWapMarqueeLoop: if (id == CSSValueInfinite) validPrimitive = true; else validPrimitive = validUnit(value, FInteger | FNonNeg, m_strict); break; case CSSPropertyWapMarqueeSpeed: if (id == CSSValueNormal || id == CSSValueSlow || id == CSSValueFast) validPrimitive = true; else validPrimitive = validUnit(value, FTime | FInteger | FNonNeg, m_strict); break; #endif case CSSPropertyWebkitUserDrag: // auto | none | element if (id == CSSValueAuto || id == CSSValueNone || id == CSSValueElement) validPrimitive = true; break; case CSSPropertyWebkitUserModify: // read-only | read-write if (id == CSSValueReadOnly || id == CSSValueReadWrite || id == CSSValueReadWritePlaintextOnly) validPrimitive = true; break; case CSSPropertyWebkitUserSelect: // auto | none | text if (id == CSSValueAuto || id == CSSValueNone || id == CSSValueText) validPrimitive = true; break; case CSSPropertyTextOverflow: // clip | ellipsis if (id == CSSValueClip || id == CSSValueEllipsis) validPrimitive = true; break; case CSSPropertyWebkitTransform: if (id == CSSValueNone) validPrimitive = true; else { PassRefPtr
val = parseTransform(); if (val) { addProperty(propId, val, important); return true; } return false; } break; case CSSPropertyWebkitTransformOrigin: case CSSPropertyWebkitTransformOriginX: case CSSPropertyWebkitTransformOriginY: case CSSPropertyWebkitTransformOriginZ: { RefPtr
val1; RefPtr
val2; RefPtr
val3; int propId1, propId2, propId3; if (parseTransformOrigin(propId, propId1, propId2, propId3, val1, val2, val3)) { addProperty(propId1, val1.release(), important); if (val2) addProperty(propId2, val2.release(), important); if (val3) addProperty(propId3, val3.release(), important); return true; } return false; } case CSSPropertyWebkitTransformStyle: if (value->id == CSSValueFlat || value->id == CSSValuePreserve3d) validPrimitive = true; break; case CSSPropertyWebkitBackfaceVisibility: if (value->id == CSSValueVisible || value->id == CSSValueHidden) validPrimitive = true; break; case CSSPropertyWebkitPerspective: if (id == CSSValueNone) validPrimitive = true; else { // Accepting valueless numbers is a quirk of the -webkit prefixed version of the property. if (validUnit(value, FNumber | FLength | FNonNeg, m_strict)) { RefPtr
val = primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); if (val) { addProperty(propId, val.release(), important); return true; } return false; } } break; case CSSPropertyWebkitPerspectiveOrigin: case CSSPropertyWebkitPerspectiveOriginX: case CSSPropertyWebkitPerspectiveOriginY: { RefPtr
val1; RefPtr
val2; int propId1, propId2; if (parsePerspectiveOrigin(propId, propId1, propId2, val1, val2)) { addProperty(propId1, val1.release(), important); if (val2) addProperty(propId2, val2.release(), important); return true; } return false; } case CSSPropertyWebkitAnimationDelay: case CSSPropertyWebkitAnimationDirection: case CSSPropertyWebkitAnimationDuration: case CSSPropertyWebkitAnimationFillMode: case CSSPropertyWebkitAnimationName: case CSSPropertyWebkitAnimationPlayState: case CSSPropertyWebkitAnimationIterationCount: case CSSPropertyWebkitAnimationTimingFunction: case CSSPropertyWebkitTransitionDelay: case CSSPropertyWebkitTransitionDuration: case CSSPropertyWebkitTransitionTimingFunction: case CSSPropertyWebkitTransitionProperty: { RefPtr
val; if (parseAnimationProperty(propId, val)) { addProperty(propId, val.release(), important); return true; } return false; } case CSSPropertyWebkitMarginCollapse: { const int properties[2] = { CSSPropertyWebkitMarginBeforeCollapse, CSSPropertyWebkitMarginAfterCollapse }; if (num == 1) { ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse); if (!parseValue(properties[0], important)) return false; CSSValue* value = m_parsedProperties[m_numParsedProperties-1]->value(); addProperty(properties[1], value, important); return true; } else if (num == 2) { ShorthandScope scope(this, CSSPropertyWebkitMarginCollapse); if (!parseValue(properties[0], important) || !parseValue(properties[1], important)) return false; return true; } return false; } case CSSPropertyWebkitMarginBeforeCollapse: case CSSPropertyWebkitMarginAfterCollapse: case CSSPropertyWebkitMarginTopCollapse: case CSSPropertyWebkitMarginBottomCollapse: if (id == CSSValueCollapse || id == CSSValueSeparate || id == CSSValueDiscard) validPrimitive = true; break; case CSSPropertyTextLineThroughMode: case CSSPropertyTextOverlineMode: case CSSPropertyTextUnderlineMode: if (id == CSSValueContinuous || id == CSSValueSkipWhiteSpace) validPrimitive = true; break; case CSSPropertyTextLineThroughStyle: case CSSPropertyTextOverlineStyle: case CSSPropertyTextUnderlineStyle: if (id == CSSValueNone || id == CSSValueSolid || id == CSSValueDouble || id == CSSValueDashed || id == CSSValueDotDash || id == CSSValueDotDotDash || id == CSSValueWave) validPrimitive = true; break; case CSSPropertyTextRendering: // auto | optimizeSpeed | optimizeLegibility | geometricPrecision if (id == CSSValueAuto || id == CSSValueOptimizespeed || id == CSSValueOptimizelegibility || id == CSSValueGeometricprecision) validPrimitive = true; break; case CSSPropertyTextLineThroughWidth: case CSSPropertyTextOverlineWidth: case CSSPropertyTextUnderlineWidth: if (id == CSSValueAuto || id == CSSValueNormal || id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick) validPrimitive = true; else validPrimitive = !id && validUnit(value, FNumber | FLength | FPercent, m_strict); break; case CSSPropertyResize: // none | both | horizontal | vertical | auto if (id == CSSValueNone || id == CSSValueBoth || id == CSSValueHorizontal || id == CSSValueVertical || id == CSSValueAuto) validPrimitive = true; break; case CSSPropertyWebkitColumnCount: if (id == CSSValueAuto) validPrimitive = true; else validPrimitive = !id && validUnit(value, FInteger | FNonNeg, false); break; case CSSPropertyWebkitColumnGap: // normal |
if (id == CSSValueNormal) validPrimitive = true; else validPrimitive = validUnit(value, FLength | FNonNeg, m_strict); break; case CSSPropertyWebkitColumnSpan: // all | 1 if (id == CSSValueAll) validPrimitive = true; else validPrimitive = validUnit(value, FNumber | FNonNeg, m_strict) && value->fValue == 1; break; case CSSPropertyWebkitColumnWidth: // auto |
if (id == CSSValueAuto) validPrimitive = true; else // Always parse this property in strict mode, since it would be ambiguous otherwise when used in the 'columns' shorthand property. validPrimitive = validUnit(value, FLength, true); break; case CSSPropertyPointerEvents: // none | visiblePainted | visibleFill | visibleStroke | visible | // painted | fill | stroke | auto | all | inherit if (id == CSSValueVisible || id == CSSValueNone || id == CSSValueAll || id == CSSValueAuto || (id >= CSSValueVisiblepainted && id <= CSSValueStroke)) validPrimitive = true; break; // End of CSS3 properties // Apple specific properties. These will never be standardized and are purely to // support custom WebKit-based Apple applications. case CSSPropertyWebkitLineClamp: // When specifying number of lines, don't allow 0 as a valid value // When specifying either type of unit, require non-negative integers validPrimitive = (!id && (value->unit == CSSPrimitiveValue::CSS_PERCENTAGE || value->fValue) && validUnit(value, FInteger | FPercent | FNonNeg, false)); break; case CSSPropertyWebkitTextSizeAdjust: if (id == CSSValueAuto || id == CSSValueNone) validPrimitive = true; break; case CSSPropertyWebkitRtlOrdering: if (id == CSSValueLogical || id == CSSValueVisual) validPrimitive = true; break; case CSSPropertyWebkitFontSizeDelta: //
validPrimitive = validUnit(value, FLength, m_strict); break; case CSSPropertyWebkitNbspMode: // normal | space if (id == CSSValueNormal || id == CSSValueSpace) validPrimitive = true; break; case CSSPropertyWebkitLineBreak: // normal | after-white-space if (id == CSSValueNormal || id == CSSValueAfterWhiteSpace) validPrimitive = true; break; case CSSPropertyWebkitMatchNearestMailBlockquoteColor: // normal | match if (id == CSSValueNormal || id == CSSValueMatch) validPrimitive = true; break; case CSSPropertyWebkitHighlight: if (id == CSSValueNone || value->unit == CSSPrimitiveValue::CSS_STRING) validPrimitive = true; break; case CSSPropertyWebkitHyphens: if (id == CSSValueNone || id == CSSValueManual || id == CSSValueAuto) validPrimitive = true; break; case CSSPropertyWebkitHyphenateCharacter: if (id == CSSValueAuto || value->unit == CSSPrimitiveValue::CSS_STRING) validPrimitive = true; break; case CSSPropertyWebkitHyphenateLimitBefore: case CSSPropertyWebkitHyphenateLimitAfter: if (id == CSSValueAuto || validUnit(value, FInteger | FNonNeg, true)) validPrimitive = true; break; case CSSPropertyWebkitLocale: if (id == CSSValueAuto || value->unit == CSSPrimitiveValue::CSS_STRING) validPrimitive = true; break; case CSSPropertyWebkitBorderFit: if (id == CSSValueBorder || id == CSSValueLines) validPrimitive = true; break; case CSSPropertyWebkitTextSecurity: // disc | circle | square | none | inherit if (id == CSSValueDisc || id == CSSValueCircle || id == CSSValueSquare|| id == CSSValueNone) validPrimitive = true; break; case CSSPropertyWebkitFontSmoothing: if (id == CSSValueAuto || id == CSSValueNone || id == CSSValueAntialiased || id == CSSValueSubpixelAntialiased) validPrimitive = true; break; #if ENABLE(DASHBOARD_SUPPORT) case CSSPropertyWebkitDashboardRegion: //
|
if (value->unit == CSSParserValue::Function || id == CSSValueNone) return parseDashboardRegions(propId, important); break; #endif // End Apple-specific properties /* shorthand properties */ case CSSPropertyBackground: { // Position must come before color in this array because a plain old "0" is a legal color // in quirks mode but it's usually the X coordinate of a position. // FIXME: Add CSSPropertyBackgroundSize to the shorthand. const int properties[] = { CSSPropertyBackgroundImage, CSSPropertyBackgroundRepeat, CSSPropertyBackgroundAttachment, CSSPropertyBackgroundPosition, CSSPropertyBackgroundOrigin, CSSPropertyBackgroundClip, CSSPropertyBackgroundColor }; return parseFillShorthand(propId, properties, 7, important); } case CSSPropertyWebkitMask: { const int properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskRepeat, CSSPropertyWebkitMaskAttachment, CSSPropertyWebkitMaskPosition, CSSPropertyWebkitMaskOrigin, CSSPropertyWebkitMaskClip }; return parseFillShorthand(propId, properties, 6, important); } case CSSPropertyBorder: // [ 'border-width' || 'border-style' ||
] | inherit { const int properties[3] = { CSSPropertyBorderWidth, CSSPropertyBorderStyle, CSSPropertyBorderColor }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyBorderTop: // [ 'border-top-width' || 'border-style' ||
] | inherit { const int properties[3] = { CSSPropertyBorderTopWidth, CSSPropertyBorderTopStyle, CSSPropertyBorderTopColor}; return parseShorthand(propId, properties, 3, important); } case CSSPropertyBorderRight: // [ 'border-right-width' || 'border-style' ||
] | inherit { const int properties[3] = { CSSPropertyBorderRightWidth, CSSPropertyBorderRightStyle, CSSPropertyBorderRightColor }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyBorderBottom: // [ 'border-bottom-width' || 'border-style' ||
] | inherit { const int properties[3] = { CSSPropertyBorderBottomWidth, CSSPropertyBorderBottomStyle, CSSPropertyBorderBottomColor }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyBorderLeft: // [ 'border-left-width' || 'border-style' ||
] | inherit { const int properties[3] = { CSSPropertyBorderLeftWidth, CSSPropertyBorderLeftStyle, CSSPropertyBorderLeftColor }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyWebkitBorderStart: { const int properties[3] = { CSSPropertyWebkitBorderStartWidth, CSSPropertyWebkitBorderStartStyle, CSSPropertyWebkitBorderStartColor }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyWebkitBorderEnd: { const int properties[3] = { CSSPropertyWebkitBorderEndWidth, CSSPropertyWebkitBorderEndStyle, CSSPropertyWebkitBorderEndColor }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyWebkitBorderBefore: { const int properties[3] = { CSSPropertyWebkitBorderBeforeWidth, CSSPropertyWebkitBorderBeforeStyle, CSSPropertyWebkitBorderBeforeColor }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyWebkitBorderAfter: { const int properties[3] = { CSSPropertyWebkitBorderAfterWidth, CSSPropertyWebkitBorderAfterStyle, CSSPropertyWebkitBorderAfterColor }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyOutline: // [ 'outline-color' || 'outline-style' || 'outline-width' ] | inherit { const int properties[3] = { CSSPropertyOutlineWidth, CSSPropertyOutlineStyle, CSSPropertyOutlineColor }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyBorderColor: //
{1,4} | inherit { const int properties[4] = { CSSPropertyBorderTopColor, CSSPropertyBorderRightColor, CSSPropertyBorderBottomColor, CSSPropertyBorderLeftColor }; return parse4Values(propId, properties, important); } case CSSPropertyBorderWidth: //
{1,4} | inherit { const int properties[4] = { CSSPropertyBorderTopWidth, CSSPropertyBorderRightWidth, CSSPropertyBorderBottomWidth, CSSPropertyBorderLeftWidth }; return parse4Values(propId, properties, important); } case CSSPropertyBorderStyle: //
{1,4} | inherit { const int properties[4] = { CSSPropertyBorderTopStyle, CSSPropertyBorderRightStyle, CSSPropertyBorderBottomStyle, CSSPropertyBorderLeftStyle }; return parse4Values(propId, properties, important); } case CSSPropertyMargin: //
{1,4} | inherit { const int properties[4] = { CSSPropertyMarginTop, CSSPropertyMarginRight, CSSPropertyMarginBottom, CSSPropertyMarginLeft }; return parse4Values(propId, properties, important); } case CSSPropertyPadding: //
{1,4} | inherit { const int properties[4] = { CSSPropertyPaddingTop, CSSPropertyPaddingRight, CSSPropertyPaddingBottom, CSSPropertyPaddingLeft }; return parse4Values(propId, properties, important); } case CSSPropertyFont: // [ [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? // 'font-family' ] | caption | icon | menu | message-box | small-caption | status-bar | inherit if (id >= CSSValueCaption && id <= CSSValueStatusBar) validPrimitive = true; else return parseFont(important); break; case CSSPropertyListStyle: { const int properties[3] = { CSSPropertyListStyleType, CSSPropertyListStylePosition, CSSPropertyListStyleImage }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyWebkitColumns: { const int properties[2] = { CSSPropertyWebkitColumnWidth, CSSPropertyWebkitColumnCount }; return parseShorthand(propId, properties, 2, important); } case CSSPropertyWebkitColumnRule: { const int properties[3] = { CSSPropertyWebkitColumnRuleWidth, CSSPropertyWebkitColumnRuleStyle, CSSPropertyWebkitColumnRuleColor }; return parseShorthand(propId, properties, 3, important); } case CSSPropertyWebkitTextStroke: { const int properties[2] = { CSSPropertyWebkitTextStrokeWidth, CSSPropertyWebkitTextStrokeColor }; return parseShorthand(propId, properties, 2, important); } case CSSPropertyWebkitAnimation: return parseAnimationShorthand(important); case CSSPropertyWebkitTransition: return parseTransitionShorthand(important); case CSSPropertyInvalid: return false; case CSSPropertyPage: return parsePage(propId, important); case CSSPropertyFontStretch: case CSSPropertyTextLineThrough: case CSSPropertyTextOverline: case CSSPropertyTextUnderline: return false; #if ENABLE(WCSS) case CSSPropertyWapInputFormat: validPrimitive = true; break; case CSSPropertyWapInputRequired: parsedValue = parseWCSSInputProperty(); break; #endif // CSS Text Layout Module Level 3: Vertical writing support case CSSPropertyWebkitWritingMode: if (id >= CSSValueHorizontalTb && id <= CSSValueHorizontalBt) validPrimitive = true; break; case CSSPropertyWebkitTextCombine: if (id == CSSValueNone || id == CSSValueHorizontal) validPrimitive = true; break; case CSSPropertyWebkitTextEmphasis: { const int properties[] = { CSSPropertyWebkitTextEmphasisStyle, CSSPropertyWebkitTextEmphasisColor }; return parseShorthand(propId, properties, WTF_ARRAY_LENGTH(properties), important); } case CSSPropertyWebkitTextEmphasisPosition: if (id == CSSValueOver || id == CSSValueUnder) validPrimitive = true; break; case CSSPropertyWebkitTextEmphasisStyle: return parseTextEmphasisStyle(important); case CSSPropertyWebkitTextOrientation: // FIXME: For now just support upright and vertical-right. if (id == CSSValueVerticalRight || id == CSSValueUpright) validPrimitive = true; break; case CSSPropertyWebkitLineBoxContain: if (id == CSSValueNone) validPrimitive = true; else return parseLineBoxContain(important); break; #ifdef ANDROID_CSS_RING case CSSPropertyWebkitRing: { const int properties[9] = { CSSPropertyWebkitRingFillColor, CSSPropertyWebkitRingInnerWidth, CSSPropertyWebkitRingOuterWidth, CSSPropertyWebkitRingOutset, CSSPropertyWebkitRingPressedInnerColor, CSSPropertyWebkitRingPressedOuterColor, CSSPropertyWebkitRingRadius, CSSPropertyWebkitRingSelectedInnerColor, CSSPropertyWebkitRingSelectedOuterColor }; return parseShorthand(propId, properties, 9, important); } case CSSPropertyWebkitRingFillColor: case CSSPropertyWebkitRingPressedInnerColor: case CSSPropertyWebkitRingPressedOuterColor: case CSSPropertyWebkitRingSelectedInnerColor: case CSSPropertyWebkitRingSelectedOuterColor: parsedValue = parseColor(); if (parsedValue) m_valueList->next(); break; case CSSPropertyWebkitRingInnerWidth: case CSSPropertyWebkitRingOuterWidth: case CSSPropertyWebkitRingOutset: case CSSPropertyWebkitRingRadius: validPrimitive = validUnit(value, FLength | FNonNeg, m_strict); break; #endif #ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR case CSSPropertyWebkitTapHighlightColor: parsedValue = parseColor(); if (parsedValue) m_valueList->next(); break; #endif #if ENABLE(SVG) default: return parseSVGValue(propId, important); #endif } if (validPrimitive) { if (id != 0) parsedValue = primitiveValueCache()->createIdentifierValue(id); else if (value->unit == CSSPrimitiveValue::CSS_STRING) parsedValue = primitiveValueCache()->createValue(value->string, (CSSPrimitiveValue::UnitTypes) value->unit); else if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) parsedValue = primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); else if (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_REMS) parsedValue = primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); else if (value->unit >= CSSParserValue::Q_EMS) parsedValue = CSSQuirkPrimitiveValue::create(value->fValue, CSSPrimitiveValue::CSS_EMS); m_valueList->next(); } if (parsedValue) { if (!m_valueList->current() || inShorthand()) { addProperty(propId, parsedValue.release(), important); return true; } } return false; } #if ENABLE(WCSS) PassRefPtr
CSSParser::parseWCSSInputProperty() { RefPtr
parsedValue = 0; CSSParserValue* value = m_valueList->current(); String inputProperty; if (value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT) inputProperty = String(value->string); if (!inputProperty.isEmpty()) parsedValue = primitiveValueCache()->createValue(inputProperty, CSSPrimitiveValue::CSS_STRING); while (m_valueList->next()) { // pass all other values, if any. If we don't do this, // the parser will think that it's not done and won't process this property } return parsedValue; } #endif void CSSParser::addFillValue(RefPtr
& lval, PassRefPtr
rval) { if (lval) { if (lval->isValueList()) static_cast
(lval.get())->append(rval); else { PassRefPtr
oldlVal(lval.release()); PassRefPtr
list = CSSValueList::createCommaSeparated(); list->append(oldlVal); list->append(rval); lval = list; } } else lval = rval; } static bool parseBackgroundClip(CSSParserValue* parserValue, RefPtr
& cssValue, CSSPrimitiveValueCache* primitiveValueCache) { if (parserValue->id == CSSValueBorderBox || parserValue->id == CSSValuePaddingBox || parserValue->id == CSSValueContentBox || parserValue->id == CSSValueWebkitText) { cssValue = primitiveValueCache->createIdentifierValue(parserValue->id); return true; } return false; } const int cMaxFillProperties = 9; bool CSSParser::parseFillShorthand(int propId, const int* properties, int numProperties, bool important) { ASSERT(numProperties <= cMaxFillProperties); if (numProperties > cMaxFillProperties) return false; ShorthandScope scope(this, propId); bool parsedProperty[cMaxFillProperties] = { false }; RefPtr
values[cMaxFillProperties]; RefPtr
clipValue; RefPtr
positionYValue; RefPtr
repeatYValue; bool foundClip = false; int i; while (m_valueList->current()) { CSSParserValue* val = m_valueList->current(); if (val->unit == CSSParserValue::Operator && val->iValue == ',') { // We hit the end. Fill in all remaining values with the initial value. m_valueList->next(); for (i = 0; i < numProperties; ++i) { if (properties[i] == CSSPropertyBackgroundColor && parsedProperty[i]) // Color is not allowed except as the last item in a list for backgrounds. // Reject the entire property. return false; if (!parsedProperty[i] && properties[i] != CSSPropertyBackgroundColor) { addFillValue(values[i], CSSInitialValue::createImplicit()); if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) addFillValue(positionYValue, CSSInitialValue::createImplicit()); if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) addFillValue(repeatYValue, CSSInitialValue::createImplicit()); if ((properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) && !parsedProperty[i]) { // If background-origin wasn't present, then reset background-clip also. addFillValue(clipValue, CSSInitialValue::createImplicit()); } } parsedProperty[i] = false; } if (!m_valueList->current()) break; } bool found = false; for (i = 0; !found && i < numProperties; ++i) { if (!parsedProperty[i]) { RefPtr
val1; RefPtr
val2; int propId1, propId2; CSSParserValue* parserValue = m_valueList->current(); if (parseFillProperty(properties[i], propId1, propId2, val1, val2)) { parsedProperty[i] = found = true; addFillValue(values[i], val1.release()); if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) addFillValue(positionYValue, val2.release()); if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) addFillValue(repeatYValue, val2.release()); if (properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) { // Reparse the value as a clip, and see if we succeed. if (parseBackgroundClip(parserValue, val1, primitiveValueCache())) addFillValue(clipValue, val1.release()); // The property parsed successfully. else addFillValue(clipValue, CSSInitialValue::createImplicit()); // Some value was used for origin that is not supported by clip. Just reset clip instead. } if (properties[i] == CSSPropertyBackgroundClip || properties[i] == CSSPropertyWebkitMaskClip) { // Update clipValue addFillValue(clipValue, val1.release()); foundClip = true; } } } } // if we didn't find at least one match, this is an // invalid shorthand and we have to ignore it if (!found) return false; } // Fill in any remaining properties with the initial value. for (i = 0; i < numProperties; ++i) { if (!parsedProperty[i]) { addFillValue(values[i], CSSInitialValue::createImplicit()); if (properties[i] == CSSPropertyBackgroundPosition || properties[i] == CSSPropertyWebkitMaskPosition) addFillValue(positionYValue, CSSInitialValue::createImplicit()); if (properties[i] == CSSPropertyBackgroundRepeat || properties[i] == CSSPropertyWebkitMaskRepeat) addFillValue(repeatYValue, CSSInitialValue::createImplicit()); if ((properties[i] == CSSPropertyBackgroundOrigin || properties[i] == CSSPropertyWebkitMaskOrigin) && !parsedProperty[i]) { // If background-origin wasn't present, then reset background-clip also. addFillValue(clipValue, CSSInitialValue::createImplicit()); } } } // Now add all of the properties we found. for (i = 0; i < numProperties; i++) { if (properties[i] == CSSPropertyBackgroundPosition) { addProperty(CSSPropertyBackgroundPositionX, values[i].release(), important); // it's OK to call positionYValue.release() since we only see CSSPropertyBackgroundPosition once addProperty(CSSPropertyBackgroundPositionY, positionYValue.release(), important); } else if (properties[i] == CSSPropertyWebkitMaskPosition) { addProperty(CSSPropertyWebkitMaskPositionX, values[i].release(), important); // it's OK to call positionYValue.release() since we only see CSSPropertyWebkitMaskPosition once addProperty(CSSPropertyWebkitMaskPositionY, positionYValue.release(), important); } else if (properties[i] == CSSPropertyBackgroundRepeat) { addProperty(CSSPropertyBackgroundRepeatX, values[i].release(), important); // it's OK to call repeatYValue.release() since we only see CSSPropertyBackgroundPosition once addProperty(CSSPropertyBackgroundRepeatY, repeatYValue.release(), important); } else if (properties[i] == CSSPropertyWebkitMaskRepeat) { addProperty(CSSPropertyWebkitMaskRepeatX, values[i].release(), important); // it's OK to call repeatYValue.release() since we only see CSSPropertyBackgroundPosition once addProperty(CSSPropertyWebkitMaskRepeatY, repeatYValue.release(), important); } else if ((properties[i] == CSSPropertyBackgroundClip || properties[i] == CSSPropertyWebkitMaskClip) && !foundClip) // Value is already set while updating origin continue; else addProperty(properties[i], values[i].release(), important); // Add in clip values when we hit the corresponding origin property. if (properties[i] == CSSPropertyBackgroundOrigin && !foundClip) addProperty(CSSPropertyBackgroundClip, clipValue.release(), important); else if (properties[i] == CSSPropertyWebkitMaskOrigin && !foundClip) addProperty(CSSPropertyWebkitMaskClip, clipValue.release(), important); } return true; } void CSSParser::addAnimationValue(RefPtr
& lval, PassRefPtr
rval) { if (lval) { if (lval->isValueList()) static_cast
(lval.get())->append(rval); else { PassRefPtr
oldVal(lval.release()); PassRefPtr
list = CSSValueList::createCommaSeparated(); list->append(oldVal); list->append(rval); lval = list; } } else lval = rval; } bool CSSParser::parseAnimationShorthand(bool important) { const int properties[] = { CSSPropertyWebkitAnimationName, CSSPropertyWebkitAnimationDuration, CSSPropertyWebkitAnimationTimingFunction, CSSPropertyWebkitAnimationDelay, CSSPropertyWebkitAnimationIterationCount, CSSPropertyWebkitAnimationDirection, CSSPropertyWebkitAnimationFillMode }; const int numProperties = WTF_ARRAY_LENGTH(properties); ShorthandScope scope(this, CSSPropertyWebkitAnimation); bool parsedProperty[numProperties] = { false }; // compiler will repeat false as necessary RefPtr
values[numProperties]; int i; while (m_valueList->current()) { CSSParserValue* val = m_valueList->current(); if (val->unit == CSSParserValue::Operator && val->iValue == ',') { // We hit the end. Fill in all remaining values with the initial value. m_valueList->next(); for (i = 0; i < numProperties; ++i) { if (!parsedProperty[i]) addAnimationValue(values[i], CSSInitialValue::createImplicit()); parsedProperty[i] = false; } if (!m_valueList->current()) break; } bool found = false; for (i = 0; !found && i < numProperties; ++i) { if (!parsedProperty[i]) { RefPtr
val; if (parseAnimationProperty(properties[i], val)) { parsedProperty[i] = found = true; addAnimationValue(values[i], val.release()); } } } // if we didn't find at least one match, this is an // invalid shorthand and we have to ignore it if (!found) return false; } // Fill in any remaining properties with the initial value. for (i = 0; i < numProperties; ++i) { if (!parsedProperty[i]) addAnimationValue(values[i], CSSInitialValue::createImplicit()); } // Now add all of the properties we found. for (i = 0; i < numProperties; i++) addProperty(properties[i], values[i].release(), important); return true; } bool CSSParser::parseTransitionShorthand(bool important) { const int properties[] = { CSSPropertyWebkitTransitionProperty, CSSPropertyWebkitTransitionDuration, CSSPropertyWebkitTransitionTimingFunction, CSSPropertyWebkitTransitionDelay }; const int numProperties = WTF_ARRAY_LENGTH(properties); ShorthandScope scope(this, CSSPropertyWebkitTransition); bool parsedProperty[numProperties] = { false }; // compiler will repeat false as necessary RefPtr
values[numProperties]; int i; while (m_valueList->current()) { CSSParserValue* val = m_valueList->current(); if (val->unit == CSSParserValue::Operator && val->iValue == ',') { // We hit the end. Fill in all remaining values with the initial value. m_valueList->next(); for (i = 0; i < numProperties; ++i) { if (!parsedProperty[i]) addAnimationValue(values[i], CSSInitialValue::createImplicit()); parsedProperty[i] = false; } if (!m_valueList->current()) break; } bool found = false; for (i = 0; !found && i < numProperties; ++i) { if (!parsedProperty[i]) { RefPtr
val; if (parseAnimationProperty(properties[i], val)) { parsedProperty[i] = found = true; addAnimationValue(values[i], val.release()); } } } // if we didn't find at least one match, this is an // invalid shorthand and we have to ignore it if (!found) return false; } // Fill in any remaining properties with the initial value. for (i = 0; i < numProperties; ++i) { if (!parsedProperty[i]) addAnimationValue(values[i], CSSInitialValue::createImplicit()); } // Now add all of the properties we found. for (i = 0; i < numProperties; i++) addProperty(properties[i], values[i].release(), important); return true; } bool CSSParser::parseShorthand(int propId, const int *properties, int numProperties, bool important) { // We try to match as many properties as possible // We set up an array of booleans to mark which property has been found, // and we try to search for properties until it makes no longer any sense. ShorthandScope scope(this, propId); bool found = false; bool fnd[6]; // Trust me ;) for (int i = 0; i < numProperties; i++) fnd[i] = false; while (m_valueList->current()) { found = false; for (int propIndex = 0; !found && propIndex < numProperties; ++propIndex) { if (!fnd[propIndex]) { if (parseValue(properties[propIndex], important)) fnd[propIndex] = found = true; } } // if we didn't find at least one match, this is an // invalid shorthand and we have to ignore it if (!found) return false; } // Fill in any remaining properties with the initial value. m_implicitShorthand = true; for (int i = 0; i < numProperties; ++i) { if (!fnd[i]) addProperty(properties[i], CSSInitialValue::createImplicit(), important); } m_implicitShorthand = false; return true; } bool CSSParser::parse4Values(int propId, const int *properties, bool important) { /* From the CSS 2 specs, 8.3 * If there is only one value, it applies to all sides. If there are two values, the top and * bottom margins are set to the first value and the right and left margins are set to the second. * If there are three values, the top is set to the first value, the left and right are set to the * second, and the bottom is set to the third. If there are four values, they apply to the top, * right, bottom, and left, respectively. */ int num = inShorthand() ? 1 : m_valueList->size(); ShorthandScope scope(this, propId); // the order is top, right, bottom, left switch (num) { case 1: { if (!parseValue(properties[0], important)) return false; CSSValue *value = m_parsedProperties[m_numParsedProperties-1]->value(); m_implicitShorthand = true; addProperty(properties[1], value, important); addProperty(properties[2], value, important); addProperty(properties[3], value, important); m_implicitShorthand = false; break; } case 2: { if (!parseValue(properties[0], important) || !parseValue(properties[1], important)) return false; CSSValue *value = m_parsedProperties[m_numParsedProperties-2]->value(); m_implicitShorthand = true; addProperty(properties[2], value, important); value = m_parsedProperties[m_numParsedProperties-2]->value(); addProperty(properties[3], value, important); m_implicitShorthand = false; break; } case 3: { if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || !parseValue(properties[2], important)) return false; CSSValue *value = m_parsedProperties[m_numParsedProperties-2]->value(); m_implicitShorthand = true; addProperty(properties[3], value, important); m_implicitShorthand = false; break; } case 4: { if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || !parseValue(properties[2], important) || !parseValue(properties[3], important)) return false; break; } default: { return false; } } return true; } // auto |
bool CSSParser::parsePage(int propId, bool important) { ASSERT(propId == CSSPropertyPage); if (m_valueList->size() != 1) return false; CSSParserValue* value = m_valueList->current(); if (!value) return false; if (value->id == CSSValueAuto) { addProperty(propId, primitiveValueCache()->createIdentifierValue(value->id), important); return true; } else if (value->id == 0 && value->unit == CSSPrimitiveValue::CSS_IDENT) { addProperty(propId, primitiveValueCache()->createValue(value->string, CSSPrimitiveValue::CSS_STRING), important); return true; } return false; } //
{1,2} | auto | [
|| [ portrait | landscape] ] bool CSSParser::parseSize(int propId, bool important) { ASSERT(propId == CSSPropertySize); if (m_valueList->size() > 2) return false; CSSParserValue* value = m_valueList->current(); if (!value) return false; RefPtr
parsedValues = CSSValueList::createSpaceSeparated(); // First parameter. SizeParameterType paramType = parseSizeParameter(parsedValues.get(), value, None); if (paramType == None) return false; // Second parameter, if any. value = m_valueList->next(); if (value) { paramType = parseSizeParameter(parsedValues.get(), value, paramType); if (paramType == None) return false; } addProperty(propId, parsedValues.release(), important); return true; } CSSParser::SizeParameterType CSSParser::parseSizeParameter(CSSValueList* parsedValues, CSSParserValue* value, SizeParameterType prevParamType) { switch (value->id) { case CSSValueAuto: if (prevParamType == None) { parsedValues->append(primitiveValueCache()->createIdentifierValue(value->id)); return Auto; } return None; case CSSValueLandscape: case CSSValuePortrait: if (prevParamType == None || prevParamType == PageSize) { parsedValues->append(primitiveValueCache()->createIdentifierValue(value->id)); return Orientation; } return None; case CSSValueA3: case CSSValueA4: case CSSValueA5: case CSSValueB4: case CSSValueB5: case CSSValueLedger: case CSSValueLegal: case CSSValueLetter: if (prevParamType == None || prevParamType == Orientation) { // Normalize to Page Size then Orientation order by prepending. // This is not specified by the CSS3 Paged Media specification, but for simpler processing later (CSSStyleSelector::applyPageSizeProperty). parsedValues->prepend(primitiveValueCache()->createIdentifierValue(value->id)); return PageSize; } return None; case 0: if (validUnit(value, FLength | FNonNeg, m_strict) && (prevParamType == None || prevParamType == Length)) { parsedValues->append(primitiveValueCache()->createValue(value->fValue, static_cast
(value->unit))); return Length; } return None; default: return None; } } // [
]+ | inherit | none // inherit and none are handled in parseValue. bool CSSParser::parseQuotes(int propId, bool important) { RefPtr
values = CSSValueList::createCommaSeparated(); while (CSSParserValue* val = m_valueList->current()) { RefPtr
parsedValue; if (val->unit == CSSPrimitiveValue::CSS_STRING) parsedValue = CSSPrimitiveValue::create(val->string, CSSPrimitiveValue::CSS_STRING); else break; values->append(parsedValue.release()); m_valueList->next(); } if (values->length()) { addProperty(propId, values.release(), important); m_valueList->next(); return true; } return false; } // [
|
|
| attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit // in CSS 2.1 this got somewhat reduced: // [
| attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit bool CSSParser::parseContent(int propId, bool important) { RefPtr
values = CSSValueList::createCommaSeparated(); while (CSSParserValue* val = m_valueList->current()) { RefPtr
parsedValue; if (val->unit == CSSPrimitiveValue::CSS_URI && m_styleSheet) { // url // FIXME: The completeURL call should be done when using the CSSImageValue, // not when creating it. parsedValue = CSSImageValue::create(m_styleSheet->completeURL(val->string)); } else if (val->unit == CSSParserValue::Function) { // attr(X) | counter(X [,Y]) | counters(X, Y, [,Z]) | -webkit-gradient(...) CSSParserValueList* args = val->function->args.get(); if (!args) return false; if (equalIgnoringCase(val->function->name, "attr(")) { parsedValue = parseAttr(args); if (!parsedValue) return false; } else if (equalIgnoringCase(val->function->name, "counter(")) { parsedValue = parseCounterContent(args, false); if (!parsedValue) return false; } else if (equalIgnoringCase(val->function->name, "counters(")) { parsedValue = parseCounterContent(args, true); if (!parsedValue) return false; } else if (isGeneratedImageValue(val)) { if (!parseGeneratedImage(parsedValue)) return false; } else return false; } else if (val->unit == CSSPrimitiveValue::CSS_IDENT) { // open-quote // close-quote // no-open-quote // no-close-quote // inherit // FIXME: These are not yet implemented (http://bugs.webkit.org/show_bug.cgi?id=6503). // none // normal switch (val->id) { case CSSValueOpenQuote: case CSSValueCloseQuote: case CSSValueNoOpenQuote: case CSSValueNoCloseQuote: case CSSValueNone: case CSSValueNormal: parsedValue = primitiveValueCache()->createIdentifierValue(val->id); } } else if (val->unit == CSSPrimitiveValue::CSS_STRING) { parsedValue = primitiveValueCache()->createValue(val->string, CSSPrimitiveValue::CSS_STRING); } if (!parsedValue) break; values->append(parsedValue.release()); m_valueList->next(); } if (values->length()) { addProperty(propId, values.release(), important); m_valueList->next(); return true; } return false; } PassRefPtr
CSSParser::parseAttr(CSSParserValueList* args) { if (args->size() != 1) return 0; CSSParserValue* a = args->current(); if (a->unit != CSSPrimitiveValue::CSS_IDENT) return 0; String attrName = a->string; // CSS allows identifiers with "-" at the start, like "-webkit-mask-image". // But HTML attribute names can't have those characters, and we should not // even parse them inside attr(). if (attrName[0] == '-') return 0; if (document() && document()->isHTMLDocument()) attrName = attrName.lower(); return primitiveValueCache()->createValue(attrName, CSSPrimitiveValue::CSS_ATTR); } PassRefPtr
CSSParser::parseBackgroundColor() { int id = m_valueList->current()->id; if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu || id == CSSValueCurrentcolor || (id >= CSSValueGrey && id < CSSValueWebkitText && !m_strict)) return primitiveValueCache()->createIdentifierValue(id); return parseColor(); } bool CSSParser::parseFillImage(RefPtr
& value) { if (m_valueList->current()->id == CSSValueNone) { value = CSSImageValue::create(); return true; } if (m_valueList->current()->unit == CSSPrimitiveValue::CSS_URI) { // FIXME: The completeURL call should be done when using the CSSImageValue, // not when creating it. if (m_styleSheet) value = CSSImageValue::create(m_styleSheet->completeURL(m_valueList->current()->string)); return true; } if (isGeneratedImageValue(m_valueList->current())) return parseGeneratedImage(value); return false; } PassRefPtr
CSSParser::parseFillPositionX(CSSParserValueList* valueList) { int id = valueList->current()->id; if (id == CSSValueLeft || id == CSSValueRight || id == CSSValueCenter) { int percent = 0; if (id == CSSValueRight) percent = 100; else if (id == CSSValueCenter) percent = 50; return primitiveValueCache()->createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); } if (validUnit(valueList->current(), FPercent | FLength, m_strict)) return primitiveValueCache()->createValue(valueList->current()->fValue, (CSSPrimitiveValue::UnitTypes)valueList->current()->unit); return 0; } PassRefPtr
CSSParser::parseFillPositionY(CSSParserValueList* valueList) { int id = valueList->current()->id; if (id == CSSValueTop || id == CSSValueBottom || id == CSSValueCenter) { int percent = 0; if (id == CSSValueBottom) percent = 100; else if (id == CSSValueCenter) percent = 50; return primitiveValueCache()->createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); } if (validUnit(valueList->current(), FPercent | FLength, m_strict)) return primitiveValueCache()->createValue(valueList->current()->fValue, (CSSPrimitiveValue::UnitTypes)valueList->current()->unit); return 0; } PassRefPtr
CSSParser::parseFillPositionComponent(CSSParserValueList* valueList, unsigned& cumulativeFlags, FillPositionFlag& individualFlag) { int id = valueList->current()->id; if (id == CSSValueLeft || id == CSSValueTop || id == CSSValueRight || id == CSSValueBottom || id == CSSValueCenter) { int percent = 0; if (id == CSSValueLeft || id == CSSValueRight) { if (cumulativeFlags & XFillPosition) return 0; cumulativeFlags |= XFillPosition; individualFlag = XFillPosition; if (id == CSSValueRight) percent = 100; } else if (id == CSSValueTop || id == CSSValueBottom) { if (cumulativeFlags & YFillPosition) return 0; cumulativeFlags |= YFillPosition; individualFlag = YFillPosition; if (id == CSSValueBottom) percent = 100; } else if (id == CSSValueCenter) { // Center is ambiguous, so we're not sure which position we've found yet, an x or a y. percent = 50; cumulativeFlags |= AmbiguousFillPosition; individualFlag = AmbiguousFillPosition; } return primitiveValueCache()->createValue(percent, CSSPrimitiveValue::CSS_PERCENTAGE); } if (validUnit(valueList->current(), FPercent | FLength, m_strict)) { if (!cumulativeFlags) { cumulativeFlags |= XFillPosition; individualFlag = XFillPosition; } else if (cumulativeFlags & (XFillPosition | AmbiguousFillPosition)) { cumulativeFlags |= YFillPosition; individualFlag = YFillPosition; } else return 0; return primitiveValueCache()->createValue(valueList->current()->fValue, (CSSPrimitiveValue::UnitTypes)valueList->current()->unit); } return 0; } void CSSParser::parseFillPosition(CSSParserValueList* valueList, RefPtr
& value1, RefPtr
& value2) { CSSParserValue* value = valueList->current(); // Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length. unsigned cumulativeFlags = 0; FillPositionFlag value1Flag = InvalidFillPosition; FillPositionFlag value2Flag = InvalidFillPosition; value1 = parseFillPositionComponent(valueList, cumulativeFlags, value1Flag); if (!value1) return; // It only takes one value for background-position to be correctly parsed if it was specified in a shorthand (since we // can assume that any other values belong to the rest of the shorthand). If we're not parsing a shorthand, though, the // value was explicitly specified for our property. value = valueList->next(); // First check for the comma. If so, we are finished parsing this value or value pair. if (value && value->unit == CSSParserValue::Operator && value->iValue == ',') value = 0; if (value) { value2 = parseFillPositionComponent(valueList, cumulativeFlags, value2Flag); if (value2) valueList->next(); else { if (!inShorthand()) { value1.clear(); return; } } } if (!value2) // Only one value was specified. If that value was not a keyword, then it sets the x position, and the y position // is simply 50%. This is our default. // For keywords, the keyword was either an x-keyword (left/right), a y-keyword (top/bottom), or an ambiguous keyword (center). // For left/right/center, the default of 50% in the y is still correct. value2 = primitiveValueCache()->createValue(50, CSSPrimitiveValue::CSS_PERCENTAGE); if (value1Flag == YFillPosition || value2Flag == XFillPosition) value1.swap(value2); } void CSSParser::parseFillRepeat(RefPtr
& value1, RefPtr
& value2) { CSSParserValue* value = m_valueList->current(); int id = m_valueList->current()->id; if (id == CSSValueRepeatX) { m_implicitShorthand = true; value1 = primitiveValueCache()->createIdentifierValue(CSSValueRepeat); value2 = primitiveValueCache()->createIdentifierValue(CSSValueNoRepeat); m_valueList->next(); return; } if (id == CSSValueRepeatY) { m_implicitShorthand = true; value1 = primitiveValueCache()->createIdentifierValue(CSSValueNoRepeat); value2 = primitiveValueCache()->createIdentifierValue(CSSValueRepeat); m_valueList->next(); return; } if (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace) value1 = primitiveValueCache()->createIdentifierValue(id); else { value1 = 0; return; } value = m_valueList->next(); // First check for the comma. If so, we are finished parsing this value or value pair. if (value && value->unit == CSSParserValue::Operator && value->iValue == ',') value = 0; if (value) id = m_valueList->current()->id; if (value && (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace)) { value2 = primitiveValueCache()->createIdentifierValue(id); m_valueList->next(); } else { // If only one value was specified, value2 is the same as value1. m_implicitShorthand = true; value2 = primitiveValueCache()->createIdentifierValue(static_cast
(value1.get())->getIdent()); } } PassRefPtr
CSSParser::parseFillSize(int propId, bool& allowComma) { allowComma = true; CSSParserValue* value = m_valueList->current(); if (value->id == CSSValueContain || value->id == CSSValueCover) return primitiveValueCache()->createIdentifierValue(value->id); RefPtr
parsedValue1; if (value->id == CSSValueAuto) parsedValue1 = primitiveValueCache()->createValue(0, CSSPrimitiveValue::CSS_UNKNOWN); else { if (!validUnit(value, FLength | FPercent, m_strict)) return 0; parsedValue1 = primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); } CSSPropertyID property = static_cast
(propId); RefPtr
parsedValue2; if ((value = m_valueList->next())) { if (value->id == CSSValueAuto) parsedValue2 = primitiveValueCache()->createValue(0, CSSPrimitiveValue::CSS_UNKNOWN); else if (value->unit == CSSParserValue::Operator && value->iValue == ',') allowComma = false; else { if (!validUnit(value, FLength | FPercent, m_strict)) return 0; parsedValue2 = primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); } } if (!parsedValue2) { if (property == CSSPropertyWebkitBackgroundSize || property == CSSPropertyWebkitMaskSize) parsedValue2 = parsedValue1; else parsedValue2 = primitiveValueCache()->createValue(0, CSSPrimitiveValue::CSS_UNKNOWN); } return primitiveValueCache()->createValue(Pair::create(parsedValue1.release(), parsedValue2.release())); } bool CSSParser::parseFillProperty(int propId, int& propId1, int& propId2, RefPtr
& retValue1, RefPtr
& retValue2) { RefPtr
values; RefPtr
values2; CSSParserValue* val; RefPtr
value; RefPtr
value2; bool allowComma = false; retValue1 = retValue2 = 0; propId1 = propId; propId2 = propId; if (propId == CSSPropertyBackgroundPosition) { propId1 = CSSPropertyBackgroundPositionX; propId2 = CSSPropertyBackgroundPositionY; } else if (propId == CSSPropertyWebkitMaskPosition) { propId1 = CSSPropertyWebkitMaskPositionX; propId2 = CSSPropertyWebkitMaskPositionY; } else if (propId == CSSPropertyBackgroundRepeat) { propId1 = CSSPropertyBackgroundRepeatX; propId2 = CSSPropertyBackgroundRepeatY; } else if (propId == CSSPropertyWebkitMaskRepeat) { propId1 = CSSPropertyWebkitMaskRepeatX; propId2 = CSSPropertyWebkitMaskRepeatY; } while ((val = m_valueList->current())) { RefPtr
currValue; RefPtr
currValue2; if (allowComma) { if (val->unit != CSSParserValue::Operator || val->iValue != ',') return false; m_valueList->next(); allowComma = false; } else { allowComma = true; switch (propId) { case CSSPropertyBackgroundColor: currValue = parseBackgroundColor(); if (currValue) m_valueList->next(); break; case CSSPropertyBackgroundAttachment: case CSSPropertyWebkitMaskAttachment: if (val->id == CSSValueScroll || val->id == CSSValueFixed || val->id == CSSValueLocal) { currValue = primitiveValueCache()->createIdentifierValue(val->id); m_valueList->next(); } break; case CSSPropertyBackgroundImage: case CSSPropertyWebkitMaskImage: if (parseFillImage(currValue)) m_valueList->next(); break; case CSSPropertyWebkitBackgroundClip: case CSSPropertyWebkitBackgroundOrigin: case CSSPropertyWebkitMaskClip: case CSSPropertyWebkitMaskOrigin: // The first three values here are deprecated and do not apply to the version of the property that has // the -webkit- prefix removed. if (val->id == CSSValueBorder || val->id == CSSValuePadding || val->id == CSSValueContent || val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox || ((propId == CSSPropertyWebkitBackgroundClip || propId == CSSPropertyWebkitMaskClip) && (val->id == CSSValueText || val->id == CSSValueWebkitText))) { currValue = primitiveValueCache()->createIdentifierValue(val->id); m_valueList->next(); } break; case CSSPropertyBackgroundClip: if (parseBackgroundClip(val, currValue, primitiveValueCache())) m_valueList->next(); break; case CSSPropertyBackgroundOrigin: if (val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox) { currValue = primitiveValueCache()->createIdentifierValue(val->id); m_valueList->next(); } break; case CSSPropertyBackgroundPosition: case CSSPropertyWebkitMaskPosition: parseFillPosition(m_valueList, currValue, currValue2); // parseFillPosition advances the m_valueList pointer break; case CSSPropertyBackgroundPositionX: case CSSPropertyWebkitMaskPositionX: { currValue = parseFillPositionX(m_valueList); if (currValue) m_valueList->next(); break; } case CSSPropertyBackgroundPositionY: case CSSPropertyWebkitMaskPositionY: { currValue = parseFillPositionY(m_valueList); if (currValue) m_valueList->next(); break; } case CSSPropertyWebkitBackgroundComposite: case CSSPropertyWebkitMaskComposite: if ((val->id >= CSSValueClear && val->id <= CSSValuePlusLighter) || val->id == CSSValueHighlight) { currValue = primitiveValueCache()->createIdentifierValue(val->id); m_valueList->next(); } break; case CSSPropertyBackgroundRepeat: case CSSPropertyWebkitMaskRepeat: parseFillRepeat(currValue, currValue2); // parseFillRepeat advances the m_valueList pointer break; case CSSPropertyBackgroundSize: case CSSPropertyWebkitBackgroundSize: case CSSPropertyWebkitMaskSize: { currValue = parseFillSize(propId, allowComma); if (currValue) m_valueList->next(); break; } } if (!currValue) return false; if (value && !values) { values = CSSValueList::createCommaSeparated(); values->append(value.release()); } if (value2 && !values2) { values2 = CSSValueList::createCommaSeparated(); values2->append(value2.release()); } if (values) values->append(currValue.release()); else value = currValue.release(); if (currValue2) { if (values2) values2->append(currValue2.release()); else value2 = currValue2.release(); } } // When parsing any fill shorthand property, we let it handle building up the lists for all // properties. if (inShorthand()) break; } if (values && values->length()) { retValue1 = values.release(); if (values2 && values2->length()) retValue2 = values2.release(); return true; } if (value) { retValue1 = value.release(); retValue2 = value2.release(); return true; } return false; } PassRefPtr
CSSParser::parseAnimationDelay() { CSSParserValue* value = m_valueList->current(); if (validUnit(value, FTime, m_strict)) return primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); return 0; } PassRefPtr
CSSParser::parseAnimationDirection() { CSSParserValue* value = m_valueList->current(); if (value->id == CSSValueNormal || value->id == CSSValueAlternate) return primitiveValueCache()->createIdentifierValue(value->id); return 0; } PassRefPtr
CSSParser::parseAnimationDuration() { CSSParserValue* value = m_valueList->current(); if (validUnit(value, FTime | FNonNeg, m_strict)) return primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); return 0; } PassRefPtr
CSSParser::parseAnimationFillMode() { CSSParserValue* value = m_valueList->current(); if (value->id == CSSValueNone || value->id == CSSValueForwards || value->id == CSSValueBackwards || value->id == CSSValueBoth) return primitiveValueCache()->createIdentifierValue(value->id); return 0; } PassRefPtr
CSSParser::parseAnimationIterationCount() { CSSParserValue* value = m_valueList->current(); if (value->id == CSSValueInfinite) return primitiveValueCache()->createIdentifierValue(value->id); if (validUnit(value, FInteger | FNonNeg, m_strict)) return primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); return 0; } PassRefPtr
CSSParser::parseAnimationName() { CSSParserValue* value = m_valueList->current(); if (value->unit == CSSPrimitiveValue::CSS_STRING || value->unit == CSSPrimitiveValue::CSS_IDENT) { if (value->id == CSSValueNone || (value->unit == CSSPrimitiveValue::CSS_STRING && equalIgnoringCase(value->string, "none"))) { return primitiveValueCache()->createIdentifierValue(CSSValueNone); } else { return primitiveValueCache()->createValue(value->string, CSSPrimitiveValue::CSS_STRING); } } return 0; } PassRefPtr
CSSParser::parseAnimationPlayState() { CSSParserValue* value = m_valueList->current(); if (value->id == CSSValueRunning || value->id == CSSValuePaused) return primitiveValueCache()->createIdentifierValue(value->id); return 0; } PassRefPtr
CSSParser::parseAnimationProperty() { CSSParserValue* value = m_valueList->current(); if (value->unit != CSSPrimitiveValue::CSS_IDENT) return 0; int result = cssPropertyID(value->string); if (result) return primitiveValueCache()->createIdentifierValue(result); if (equalIgnoringCase(value->string, "all")) return primitiveValueCache()->createIdentifierValue(CSSValueAll); if (equalIgnoringCase(value->string, "none")) return primitiveValueCache()->createIdentifierValue(CSSValueNone); return 0; } bool CSSParser::parseTransformOriginShorthand(RefPtr
& value1, RefPtr
& value2, RefPtr
& value3) { parseFillPosition(m_valueList, value1, value2); // now get z if (m_valueList->current()) { if (validUnit(m_valueList->current(), FLength, m_strict)) { value3 = primitiveValueCache()->createValue(m_valueList->current()->fValue, (CSSPrimitiveValue::UnitTypes)m_valueList->current()->unit); m_valueList->next(); return true; } return false; } return true; } bool CSSParser::parseCubicBezierTimingFunctionValue(CSSParserValueList*& args, double& result) { CSSParserValue* v = args->current(); if (!validUnit(v, FNumber, m_strict)) return false; result = v->fValue; if (result < 0 || result > 1.0) return false; v = args->next(); if (!v) // The last number in the function has no comma after it, so we're done. return true; if (v->unit != CSSParserValue::Operator && v->iValue != ',') return false; v = args->next(); return true; } PassRefPtr
CSSParser::parseAnimationTimingFunction() { CSSParserValue* value = m_valueList->current(); if (value->id == CSSValueEase || value->id == CSSValueLinear || value->id == CSSValueEaseIn || value->id == CSSValueEaseOut || value->id == CSSValueEaseInOut || value->id == CSSValueStepStart || value->id == CSSValueStepEnd) return primitiveValueCache()->createIdentifierValue(value->id); // We must be a function. if (value->unit != CSSParserValue::Function) return 0; CSSParserValueList* args = value->function->args.get(); if (equalIgnoringCase(value->function->name, "steps(")) { // For steps, 1 or 2 params must be specified (comma-separated) if (!args || (args->size() != 1 && args->size() != 3)) return 0; // There are two values. int numSteps; bool stepAtStart = false; CSSParserValue* v = args->current(); if (!validUnit(v, FInteger, m_strict)) return 0; numSteps = (int) min(v->fValue, (double)INT_MAX); if (numSteps < 1) return 0; v = args->next(); if (v) { // There is a comma so we need to parse the second value if (v->unit != CSSParserValue::Operator && v->iValue != ',') return 0; v = args->next(); if (v->id != CSSValueStart && v->id != CSSValueEnd) return 0; stepAtStart = v->id == CSSValueStart; } return CSSStepsTimingFunctionValue::create(numSteps, stepAtStart); } if (equalIgnoringCase(value->function->name, "cubic-bezier(")) { // For cubic bezier, 4 values must be specified. if (!args || args->size() != 7) return 0; // There are two points specified. The values must be between 0 and 1. double x1, y1, x2, y2; if (!parseCubicBezierTimingFunctionValue(args, x1)) return 0; if (!parseCubicBezierTimingFunctionValue(args, y1)) return 0; if (!parseCubicBezierTimingFunctionValue(args, x2)) return 0; if (!parseCubicBezierTimingFunctionValue(args, y2)) return 0; return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2); } return 0; } bool CSSParser::parseAnimationProperty(int propId, RefPtr
& result) { RefPtr
values; CSSParserValue* val; RefPtr
value; bool allowComma = false; result = 0; while ((val = m_valueList->current())) { RefPtr
currValue; if (allowComma) { if (val->unit != CSSParserValue::Operator || val->iValue != ',') return false; m_valueList->next(); allowComma = false; } else { switch (propId) { case CSSPropertyWebkitAnimationDelay: case CSSPropertyWebkitTransitionDelay: currValue = parseAnimationDelay(); if (currValue) m_valueList->next(); break; case CSSPropertyWebkitAnimationDirection: currValue = parseAnimationDirection(); if (currValue) m_valueList->next(); break; case CSSPropertyWebkitAnimationDuration: case CSSPropertyWebkitTransitionDuration: currValue = parseAnimationDuration(); if (currValue) m_valueList->next(); break; case CSSPropertyWebkitAnimationFillMode: currValue = parseAnimationFillMode(); if (currValue) m_valueList->next(); break; case CSSPropertyWebkitAnimationIterationCount: currValue = parseAnimationIterationCount(); if (currValue) m_valueList->next(); break; case CSSPropertyWebkitAnimationName: currValue = parseAnimationName(); if (currValue) m_valueList->next(); break; case CSSPropertyWebkitAnimationPlayState: currValue = parseAnimationPlayState(); if (currValue) m_valueList->next(); break; case CSSPropertyWebkitTransitionProperty: currValue = parseAnimationProperty(); if (currValue) m_valueList->next(); break; case CSSPropertyWebkitAnimationTimingFunction: case CSSPropertyWebkitTransitionTimingFunction: currValue = parseAnimationTimingFunction(); if (currValue) m_valueList->next(); break; } if (!currValue) return false; if (value && !values) { values = CSSValueList::createCommaSeparated(); values->append(value.release()); } if (values) values->append(currValue.release()); else value = currValue.release(); allowComma = true; } // When parsing the 'transition' shorthand property, we let it handle building up the lists for all // properties. if (inShorthand()) break; } if (values && values->length()) { result = values.release(); return true; } if (value) { result = value.release(); return true; } return false; } #if ENABLE(DASHBOARD_SUPPORT) #define DASHBOARD_REGION_NUM_PARAMETERS 6 #define DASHBOARD_REGION_SHORT_NUM_PARAMETERS 2 static CSSParserValue* skipCommaInDashboardRegion(CSSParserValueList *args) { if (args->size() == (DASHBOARD_REGION_NUM_PARAMETERS*2-1) || args->size() == (DASHBOARD_REGION_SHORT_NUM_PARAMETERS*2-1)) { CSSParserValue* current = args->current(); if (current->unit == CSSParserValue::Operator && current->iValue == ',') return args->next(); } return args->current(); } bool CSSParser::parseDashboardRegions(int propId, bool important) { bool valid = true; CSSParserValue* value = m_valueList->current(); if (value->id == CSSValueNone) { if (m_valueList->next()) return false; addProperty(propId, primitiveValueCache()->createIdentifierValue(value->id), important); return valid; } RefPtr
firstRegion = DashboardRegion::create(); DashboardRegion* region = 0; while (value) { if (region == 0) { region = firstRegion.get(); } else { RefPtr
nextRegion = DashboardRegion::create(); region->m_next = nextRegion; region = nextRegion.get(); } if (value->unit != CSSParserValue::Function) { valid = false; break; } // Commas count as values, so allow: // dashboard-region(label, type, t, r, b, l) or dashboard-region(label type t r b l) // dashboard-region(label, type, t, r, b, l) or dashboard-region(label type t r b l) // also allow // dashboard-region(label, type) or dashboard-region(label type) // dashboard-region(label, type) or dashboard-region(label type) CSSParserValueList* args = value->function->args.get(); if (!equalIgnoringCase(value->function->name, "dashboard-region(") || !args) { valid = false; break; } int numArgs = args->size(); if ((numArgs != DASHBOARD_REGION_NUM_PARAMETERS && numArgs != (DASHBOARD_REGION_NUM_PARAMETERS*2-1)) && (numArgs != DASHBOARD_REGION_SHORT_NUM_PARAMETERS && numArgs != (DASHBOARD_REGION_SHORT_NUM_PARAMETERS*2-1))) { valid = false; break; } // First arg is a label. CSSParserValue* arg = args->current(); if (arg->unit != CSSPrimitiveValue::CSS_IDENT) { valid = false; break; } region->m_label = arg->string; // Second arg is a type. arg = args->next(); arg = skipCommaInDashboardRegion(args); if (arg->unit != CSSPrimitiveValue::CSS_IDENT) { valid = false; break; } if (equalIgnoringCase(arg->string, "circle")) region->m_isCircle = true; else if (equalIgnoringCase(arg->string, "rectangle")) region->m_isRectangle = true; else { valid = false; break; } region->m_geometryType = arg->string; if (numArgs == DASHBOARD_REGION_SHORT_NUM_PARAMETERS || numArgs == (DASHBOARD_REGION_SHORT_NUM_PARAMETERS*2-1)) { // This originally used CSSValueInvalid by accident. It might be more logical to use something else. RefPtr
amount = primitiveValueCache()->createIdentifierValue(CSSValueInvalid); region->setTop(amount); region->setRight(amount); region->setBottom(amount); region->setLeft(amount); } else { // Next four arguments must be offset numbers int i; for (i = 0; i < 4; i++) { arg = args->next(); arg = skipCommaInDashboardRegion(args); valid = arg->id == CSSValueAuto || validUnit(arg, FLength, m_strict); if (!valid) break; RefPtr
amount = arg->id == CSSValueAuto ? primitiveValueCache()->createIdentifierValue(CSSValueAuto) : primitiveValueCache()->createValue(arg->fValue, (CSSPrimitiveValue::UnitTypes) arg->unit); if (i == 0) region->setTop(amount); else if (i == 1) region->setRight(amount); else if (i == 2) region->setBottom(amount); else region->setLeft(amount); } } if (args->next()) return false; value = m_valueList->next(); } if (valid) addProperty(propId, primitiveValueCache()->createValue(firstRegion.release()), important); return valid; } #endif /* ENABLE(DASHBOARD_SUPPORT) */ PassRefPtr
CSSParser::parseCounterContent(CSSParserValueList* args, bool counters) { unsigned numArgs = args->size(); if (counters && numArgs != 3 && numArgs != 5) return 0; if (!counters && numArgs != 1 && numArgs != 3) return 0; CSSParserValue* i = args->current(); if (i->unit != CSSPrimitiveValue::CSS_IDENT) return 0; RefPtr
identifier = primitiveValueCache()->createValue(i->string, CSSPrimitiveValue::CSS_STRING); RefPtr
separator; if (!counters) separator = primitiveValueCache()->createValue(String(), CSSPrimitiveValue::CSS_STRING); else { i = args->next(); if (i->unit != CSSParserValue::Operator || i->iValue != ',') return 0; i = args->next(); if (i->unit != CSSPrimitiveValue::CSS_STRING) return 0; separator = primitiveValueCache()->createValue(i->string, (CSSPrimitiveValue::UnitTypes) i->unit); } RefPtr
listStyle; i = args->next(); if (!i) // Make the list style default decimal listStyle = primitiveValueCache()->createValue(CSSValueDecimal - CSSValueDisc, CSSPrimitiveValue::CSS_NUMBER); else { if (i->unit != CSSParserValue::Operator || i->iValue != ',') return 0; i = args->next(); if (i->unit != CSSPrimitiveValue::CSS_IDENT) return 0; short ls = 0; if (i->id == CSSValueNone) ls = CSSValueKatakanaIroha - CSSValueDisc + 1; else if (i->id >= CSSValueDisc && i->id <= CSSValueKatakanaIroha) ls = i->id - CSSValueDisc; else return 0; listStyle = primitiveValueCache()->createValue(ls, (CSSPrimitiveValue::UnitTypes) i->unit); } return primitiveValueCache()->createValue(Counter::create(identifier.release(), listStyle.release(), separator.release())); } bool CSSParser::parseShape(int propId, bool important) { CSSParserValue* value = m_valueList->current(); CSSParserValueList* args = value->function->args.get(); if (!equalIgnoringCase(value->function->name, "rect(") || !args) return false; // rect(t, r, b, l) || rect(t r b l) if (args->size() != 4 && args->size() != 7) return false; RefPtr
rect = Rect::create(); bool valid = true; int i = 0; CSSParserValue* a = args->current(); while (a) { valid = a->id == CSSValueAuto || validUnit(a, FLength, m_strict); if (!valid) break; RefPtr
length = a->id == CSSValueAuto ? primitiveValueCache()->createIdentifierValue(CSSValueAuto) : primitiveValueCache()->createValue(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit); if (i == 0) rect->setTop(length); else if (i == 1) rect->setRight(length); else if (i == 2) rect->setBottom(length); else rect->setLeft(length); a = args->next(); if (a && args->size() == 7) { if (a->unit == CSSParserValue::Operator && a->iValue == ',') { a = args->next(); } else { valid = false; break; } } i++; } if (valid) { addProperty(propId, primitiveValueCache()->createValue(rect.release()), important); m_valueList->next(); return true; } return false; } // [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 'font-family' bool CSSParser::parseFont(bool important) { bool valid = true; CSSParserValue *value = m_valueList->current(); RefPtr
font = FontValue::create(); // optional font-style, font-variant and font-weight while (value) { int id = value->id; if (id) { if (id == CSSValueNormal) { // do nothing, it's the inital value for all three } else if (id == CSSValueItalic || id == CSSValueOblique) { if (font->style) return false; font->style = primitiveValueCache()->createIdentifierValue(id); } else if (id == CSSValueSmallCaps) { if (font->variant) return false; font->variant = primitiveValueCache()->createIdentifierValue(id); } else if (id >= CSSValueBold && id <= CSSValueLighter) { if (font->weight) return false; font->weight = primitiveValueCache()->createIdentifierValue(id); } else { valid = false; } } else if (!font->weight && validUnit(value, FInteger | FNonNeg, true)) { int weight = (int)value->fValue; int val = 0; if (weight == 100) val = CSSValue100; else if (weight == 200) val = CSSValue200; else if (weight == 300) val = CSSValue300; else if (weight == 400) val = CSSValue400; else if (weight == 500) val = CSSValue500; else if (weight == 600) val = CSSValue600; else if (weight == 700) val = CSSValue700; else if (weight == 800) val = CSSValue800; else if (weight == 900) val = CSSValue900; if (val) font->weight = primitiveValueCache()->createIdentifierValue(val); else valid = false; } else { valid = false; } if (!valid) break; value = m_valueList->next(); } if (!value) return false; // set undefined values to default if (!font->style) font->style = primitiveValueCache()->createIdentifierValue(CSSValueNormal); if (!font->variant) font->variant = primitiveValueCache()->createIdentifierValue(CSSValueNormal); if (!font->weight) font->weight = primitiveValueCache()->createIdentifierValue(CSSValueNormal); // now a font size _must_ come //
|
|
|
| inherit if (value->id >= CSSValueXxSmall && value->id <= CSSValueLarger) font->size = primitiveValueCache()->createIdentifierValue(value->id); else if (validUnit(value, FLength | FPercent | FNonNeg, m_strict)) font->size = primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); value = m_valueList->next(); if (!font->size || !value) return false; if (value->unit == CSSParserValue::Operator && value->iValue == '/') { // line-height value = m_valueList->next(); if (!value) return false; if (value->id == CSSValueNormal) { // default value, nothing to do } else if (validUnit(value, FNumber | FLength | FPercent | FNonNeg, m_strict)) font->lineHeight = primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); else return false; value = m_valueList->next(); if (!value) return false; } if (!font->lineHeight) font->lineHeight = primitiveValueCache()->createIdentifierValue(CSSValueNormal); // font family must come now font->family = parseFontFamily(); if (m_valueList->current() || !font->family) return false; addProperty(CSSPropertyFont, font.release(), important); return true; } PassRefPtr
CSSParser::parseFontFamily() { RefPtr
list = CSSValueList::createCommaSeparated(); CSSParserValue* value = m_valueList->current(); FontFamilyValue* currFamily = 0; while (value) { CSSParserValue* nextValue = m_valueList->next(); bool nextValBreaksFont = !nextValue || (nextValue->unit == CSSParserValue::Operator && nextValue->iValue == ','); bool nextValIsFontName = nextValue && ((nextValue->id >= CSSValueSerif && nextValue->id <= CSSValueWebkitBody) || (nextValue->unit == CSSPrimitiveValue::CSS_STRING || nextValue->unit == CSSPrimitiveValue::CSS_IDENT)); if (value->id >= CSSValueSerif && value->id <= CSSValueWebkitBody) { if (currFamily) currFamily->appendSpaceSeparated(value->string.characters, value->string.length); else if (nextValBreaksFont || !nextValIsFontName) list->append(primitiveValueCache()->createIdentifierValue(value->id)); else { RefPtr
newFamily = FontFamilyValue::create(value->string); currFamily = newFamily.get(); list->append(newFamily.release()); } } else if (value->unit == CSSPrimitiveValue::CSS_STRING) { // Strings never share in a family name. currFamily = 0; list->append(FontFamilyValue::create(value->string)); } else if (value->unit == CSSPrimitiveValue::CSS_IDENT) { if (currFamily) currFamily->appendSpaceSeparated(value->string.characters, value->string.length); else if (nextValBreaksFont || !nextValIsFontName) list->append(FontFamilyValue::create(value->string)); else { RefPtr
newFamily = FontFamilyValue::create(value->string); currFamily = newFamily.get(); list->append(newFamily.release()); } } else { break; } if (!nextValue) break; if (nextValBreaksFont) { value = m_valueList->next(); currFamily = 0; } else if (nextValIsFontName) value = nextValue; else break; } if (!list->length()) list = 0; return list.release(); } bool CSSParser::parseFontStyle(bool important) { RefPtr
values; if (m_valueList->size() > 1) values = CSSValueList::createCommaSeparated(); CSSParserValue* val; bool expectComma = false; while ((val = m_valueList->current())) { RefPtr
parsedValue; if (!expectComma) { expectComma = true; if (val->id == CSSValueNormal || val->id == CSSValueItalic || val->id == CSSValueOblique) parsedValue = primitiveValueCache()->createIdentifierValue(val->id); else if (val->id == CSSValueAll && !values) { // 'all' is only allowed in @font-face and with no other values. Make a value list to // indicate that we are in the @font-face case. values = CSSValueList::createCommaSeparated(); parsedValue = primitiveValueCache()->createIdentifierValue(val->id); } } else if (val->unit == CSSParserValue::Operator && val->iValue == ',') { expectComma = false; m_valueList->next(); continue; } if (!parsedValue) return false; m_valueList->next(); if (values) values->append(parsedValue.release()); else { addProperty(CSSPropertyFontStyle, parsedValue.release(), important); return true; } } if (values && values->length()) { m_hasFontFaceOnlyValues = true; addProperty(CSSPropertyFontStyle, values.release(), important); return true; } return false; } bool CSSParser::parseFontVariant(bool important) { RefPtr
values; if (m_valueList->size() > 1) values = CSSValueList::createCommaSeparated(); CSSParserValue* val; bool expectComma = false; while ((val = m_valueList->current())) { RefPtr
parsedValue; if (!expectComma) { expectComma = true; if (val->id == CSSValueNormal || val->id == CSSValueSmallCaps) parsedValue = primitiveValueCache()->createIdentifierValue(val->id); else if (val->id == CSSValueAll && !values) { // 'all' is only allowed in @font-face and with no other values. Make a value list to // indicate that we are in the @font-face case. values = CSSValueList::createCommaSeparated(); parsedValue = primitiveValueCache()->createIdentifierValue(val->id); } } else if (val->unit == CSSParserValue::Operator && val->iValue == ',') { expectComma = false; m_valueList->next(); continue; } if (!parsedValue) return false; m_valueList->next(); if (values) values->append(parsedValue.release()); else { addProperty(CSSPropertyFontVariant, parsedValue.release(), important); return true; } } if (values && values->length()) { m_hasFontFaceOnlyValues = true; addProperty(CSSPropertyFontVariant, values.release(), important); return true; } return false; } bool CSSParser::parseFontWeight(bool important) { RefPtr
values; if (m_valueList->size() > 1) values = CSSValueList::createCommaSeparated(); CSSParserValue* val; bool expectComma = false; while ((val = m_valueList->current())) { RefPtr
parsedValue; if (!expectComma) { expectComma = true; if (val->unit == CSSPrimitiveValue::CSS_IDENT) { if (val->id >= CSSValueNormal && val->id <= CSSValue900) parsedValue = primitiveValueCache()->createIdentifierValue(val->id); else if (val->id == CSSValueAll && !values) { // 'all' is only allowed in @font-face and with no other values. Make a value list to // indicate that we are in the @font-face case. values = CSSValueList::createCommaSeparated(); parsedValue = primitiveValueCache()->createIdentifierValue(val->id); } } else if (validUnit(val, FInteger | FNonNeg, false)) { int weight = static_cast
(val->fValue); if (!(weight % 100) && weight >= 100 && weight <= 900) parsedValue = primitiveValueCache()->createIdentifierValue(CSSValue100 + weight / 100 - 1); } } else if (val->unit == CSSParserValue::Operator && val->iValue == ',') { expectComma = false; m_valueList->next(); continue; } if (!parsedValue) return false; m_valueList->next(); if (values) values->append(parsedValue.release()); else { addProperty(CSSPropertyFontWeight, parsedValue.release(), important); return true; } } if (values && values->length()) { m_hasFontFaceOnlyValues = true; addProperty(CSSPropertyFontWeight, values.release(), important); return true; } return false; } static bool isValidFormatFunction(CSSParserValue* val) { CSSParserValueList* args = val->function->args.get(); return equalIgnoringCase(val->function->name, "format(") && (args->current()->unit == CSSPrimitiveValue::CSS_STRING || args->current()->unit == CSSPrimitiveValue::CSS_IDENT); } bool CSSParser::parseFontFaceSrc() { RefPtr
values(CSSValueList::createCommaSeparated()); CSSParserValue* val; bool expectComma = false; bool allowFormat = false; bool failed = false; RefPtr
uriValue; while ((val = m_valueList->current())) { RefPtr
parsedValue; if (val->unit == CSSPrimitiveValue::CSS_URI && !expectComma && m_styleSheet) { // FIXME: The completeURL call should be done when using the CSSFontFaceSrcValue, // not when creating it. parsedValue = CSSFontFaceSrcValue::create(m_styleSheet->completeURL(val->string)); uriValue = parsedValue; allowFormat = true; expectComma = true; } else if (val->unit == CSSParserValue::Function) { // There are two allowed functions: local() and format(). CSSParserValueList* args = val->function->args.get(); if (args && args->size() == 1) { if (equalIgnoringCase(val->function->name, "local(") && !expectComma && (args->current()->unit == CSSPrimitiveValue::CSS_STRING || args->current()->unit == CSSPrimitiveValue::CSS_IDENT)) { expectComma = true; allowFormat = false; CSSParserValue* a = args->current(); uriValue.clear(); parsedValue = CSSFontFaceSrcValue::createLocal(a->string); } else if (allowFormat && uriValue && isValidFormatFunction(val)) { expectComma = true; allowFormat = false; uriValue->setFormat(args->current()->string); uriValue.clear(); m_valueList->next(); continue; } } } else if (val->unit == CSSParserValue::Operator && val->iValue == ',' && expectComma) { expectComma = false; allowFormat = false; uriValue.clear(); m_valueList->next(); continue; } if (parsedValue) values->append(parsedValue.release()); else { failed = true; break; } m_valueList->next(); } if (values->length() && !failed) { addProperty(CSSPropertySrc, values.release(), m_important); m_valueList->next(); return true; } return false; } bool CSSParser::parseFontFaceUnicodeRange() { RefPtr
values = CSSValueList::createCommaSeparated(); bool failed = false; bool operatorExpected = false; for (; m_valueList->current(); m_valueList->next(), operatorExpected = !operatorExpected) { if (operatorExpected) { if (m_valueList->current()->unit == CSSParserValue::Operator && m_valueList->current()->iValue == ',') continue; failed = true; break; } if (m_valueList->current()->unit != CSSPrimitiveValue::CSS_UNICODE_RANGE) { failed = true; break; } String rangeString = m_valueList->current()->string; UChar32 from = 0; UChar32 to = 0; unsigned length = rangeString.length(); if (length < 3) { failed = true; break; } unsigned i = 2; while (i < length) { UChar c = rangeString[i]; if (c == '-' || c == '?') break; from *= 16; if (c >= '0' && c <= '9') from += c - '0'; else if (c >= 'A' && c <= 'F') from += 10 + c - 'A'; else if (c >= 'a' && c <= 'f') from += 10 + c - 'a'; else { failed = true; break; } i++; } if (failed) break; if (i == length) to = from; else if (rangeString[i] == '?') { unsigned span = 1; while (i < length && rangeString[i] == '?') { span *= 16; from *= 16; i++; } if (i < length) failed = true; to = from + span - 1; } else { if (length < i + 2) { failed = true; break; } i++; while (i < length) { UChar c = rangeString[i]; to *= 16; if (c >= '0' && c <= '9') to += c - '0'; else if (c >= 'A' && c <= 'F') to += 10 + c - 'A'; else if (c >= 'a' && c <= 'f') to += 10 + c - 'a'; else { failed = true; break; } i++; } if (failed) break; } if (from <= to) values->append(CSSUnicodeRangeValue::create(from, to)); } if (failed || !values->length()) return false; addProperty(CSSPropertyUnicodeRange, values.release(), m_important); return true; } // Returns the number of characters which form a valid double // and are terminated by the given terminator character static int checkForValidDouble(const UChar* string, const UChar* end, const char terminator) { int length = end - string; if (length < 1) return 0; bool decimalMarkSeen = false; int processedLength = 0; for (int i = 0; i < length; ++i) { if (string[i] == terminator) { processedLength = i; break; } if (!isASCIIDigit(string[i])) { if (!decimalMarkSeen && string[i] == '.') decimalMarkSeen = true; else return 0; } } if (decimalMarkSeen && processedLength == 1) return 0; return processedLength; } // Returns the number of characters consumed for parsing a valid double // terminated by the given terminator character static int parseDouble(const UChar* string, const UChar* end, const char terminator, double& value) { int length = checkForValidDouble(string, end, terminator); if (!length) return 0; int position = 0; double localValue = 0; // The consumed characters here are guaranteed to be // ASCII digits with or without a decimal mark for (; position < length; ++position) { if (string[position] == '.') break; localValue = localValue * 10 + string[position] - '0'; } if (++position == length) { value = localValue; return length; } double fraction = 0; double scale = 1; while (position < length && scale < MAX_SCALE) { fraction = fraction * 10 + string[position++] - '0'; scale *= 10; } value = localValue + fraction / scale; return length; } static bool parseColorIntOrPercentage(const UChar*& string, const UChar* end, const char terminator, CSSPrimitiveValue::UnitTypes& expect, int& value) { const UChar* current = string; double localValue = 0; bool negative = false; while (current != end && isHTMLSpace(*current)) current++; if (current != end && *current == '-') { negative = true; current++; } if (current == end || !isASCIIDigit(*current)) return false; while (current != end && isASCIIDigit(*current)) { double newValue = localValue * 10 + *current++ - '0'; if (newValue >= 255) { // Clamp values at 255. localValue = 255; while (current != end && isASCIIDigit(*current)) ++current; break; } localValue = newValue; } if (current == end) return false; if (expect == CSSPrimitiveValue::CSS_NUMBER && (*current == '.' || *current == '%')) return false; if (*current == '.') { // We already parsed the integral part, try to parse // the fraction part of the percentage value. double percentage = 0; int numCharactersParsed = parseDouble(current, end, '%', percentage); if (!numCharactersParsed) return false; current += numCharactersParsed; if (*current != '%') return false; localValue += percentage; } if (expect == CSSPrimitiveValue::CSS_PERCENTAGE && *current != '%') return false; if (*current == '%') { expect = CSSPrimitiveValue::CSS_PERCENTAGE; localValue = localValue / 100.0 * 256.0; // Clamp values at 255 for percentages over 100% if (localValue > 255) localValue = 255; current++; } else expect = CSSPrimitiveValue::CSS_NUMBER; while (current != end && isHTMLSpace(*current)) current++; if (current == end || *current++ != terminator) return false; // Clamp negative values at zero. value = negative ? 0 : static_cast
(localValue); string = current; return true; } static inline bool isTenthAlpha(const UChar* string, const int length) { // "0.X" if (length == 3 && string[0] == '0' && string[1] == '.' && isASCIIDigit(string[2])) return true; // ".X" if (length == 2 && string[0] == '.' && isASCIIDigit(string[1])) return true; return false; } static inline bool parseAlphaValue(const UChar*& string, const UChar* end, const char terminator, int& value) { while (string != end && isHTMLSpace(*string)) string++; bool negative = false; if (string != end && *string == '-') { negative = true; string++; } value = 0; int length = end - string; if (length < 2) return false; if (string[length - 1] != terminator) return false; if (string[0] != '0' && string[0] != '1' && string[0] != '.') { if (checkForValidDouble(string, end, terminator)) { value = negative ? 0 : 255; string = end; return true; } return false; } if (length == 2 && string[0] != '.') { value = !negative && string[0] == '1' ? 255 : 0; string = end; return true; } if (isTenthAlpha(string, length - 1)) { static const int tenthAlphaValues[] = { 0, 25, 51, 76, 102, 127, 153, 179, 204, 230 }; value = negative ? 0 : tenthAlphaValues[string[length - 2] - '0']; string = end; return true; } double alpha = 0; if (!parseDouble(string, end, terminator, alpha)) return false; value = negative ? 0 : static_cast
(alpha * nextafter(256.0, 0.0)); string = end; return true; } static inline bool mightBeRGBA(const UChar* characters, unsigned length) { if (length < 5) return false; return characters[4] == '(' && (characters[0] | 0x20) == 'r' && (characters[1] | 0x20) == 'g' && (characters[2] | 0x20) == 'b' && (characters[3] | 0x20) == 'a'; } static inline bool mightBeRGB(const UChar* characters, unsigned length) { if (length < 4) return false; return characters[3] == '(' && (characters[0] | 0x20) == 'r' && (characters[1] | 0x20) == 'g' && (characters[2] | 0x20) == 'b'; } bool CSSParser::parseColor(const String &name, RGBA32& rgb, bool strict) { const UChar* characters = name.characters(); unsigned length = name.length(); CSSPrimitiveValue::UnitTypes expect = CSSPrimitiveValue::CSS_UNKNOWN; if (!strict && length >= 3) { if (name[0] == '#') { if (Color::parseHexColor(characters + 1, length - 1, rgb)) return true; } else { if (Color::parseHexColor(characters, length, rgb)) return true; } } // Try rgba() syntax. if (mightBeRGBA(characters, length)) { const UChar* current = characters + 5; const UChar* end = characters + length; int red; int green; int blue; int alpha; if (!parseColorIntOrPercentage(current, end, ',', expect, red)) return false; if (!parseColorIntOrPercentage(current, end, ',', expect, green)) return false; if (!parseColorIntOrPercentage(current, end, ',', expect, blue)) return false; if (!parseAlphaValue(current, end, ')', alpha)) return false; if (current != end) return false; rgb = makeRGBA(red, green, blue, alpha); return true; } // Try rgb() syntax. if (mightBeRGB(characters, length)) { const UChar* current = characters + 4; const UChar* end = characters + length; int red; int green; int blue; if (!parseColorIntOrPercentage(current, end, ',', expect, red)) return false; if (!parseColorIntOrPercentage(current, end, ',', expect, green)) return false; if (!parseColorIntOrPercentage(current, end, ')', expect, blue)) return false; if (current != end) return false; rgb = makeRGB(red, green, blue); return true; } // Try named colors. Color tc; tc.setNamedColor(name); if (tc.isValid()) { rgb = tc.rgb(); return true; } return false; } static inline int colorIntFromValue(CSSParserValue* v) { if (v->fValue <= 0.0) return 0; if (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE) { if (v->fValue >= 100.0) return 255; return static_cast
(v->fValue * 256.0 / 100.0); } if (v->fValue >= 255.0) return 255; return static_cast
(v->fValue); } bool CSSParser::parseColorParameters(CSSParserValue* value, int* colorArray, bool parseAlpha) { CSSParserValueList* args = value->function->args.get(); CSSParserValue* v = args->current(); Units unitType = FUnknown; // Get the first value and its type if (validUnit(v, FInteger, true)) unitType = FInteger; else if (validUnit(v, FPercent, true)) unitType = FPercent; else return false; colorArray[0] = colorIntFromValue(v); for (int i = 1; i < 3; i++) { v = args->next(); if (v->unit != CSSParserValue::Operator && v->iValue != ',') return false; v = args->next(); if (!validUnit(v, unitType, true)) return false; colorArray[i] = colorIntFromValue(v); } if (parseAlpha) { v = args->next(); if (v->unit != CSSParserValue::Operator && v->iValue != ',') return false; v = args->next(); if (!validUnit(v, FNumber, true)) return false; // Convert the floating pointer number of alpha to an integer in the range [0, 256), // with an equal distribution across all 256 values. colorArray[3] = static_cast
(max(0.0, min(1.0, v->fValue)) * nextafter(256.0, 0.0)); } return true; } // The CSS3 specification defines the format of a HSL color as // hsl(
,
,
) // and with alpha, the format is // hsla(
,
,
,