| /* |
| * 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 <webkit@nickshanks.com> |
| * Copyright (C) 2008 Eric Seidel <eric@webkit.org> |
| * 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 <limits.h> |
| #include <wtf/HexNumber.h> |
| #include <wtf/dtoa.h> |
| #include <wtf/text/StringBuffer.h> |
| |
| #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<CSSProperty**>(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<UChar*>(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<CSSRule> 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<CSSRule> 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<UChar*>(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<CSSStyleSheet*>(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<CSSStyleSheet*>(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<CSSStyleSheet*>(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<CSSMutableStyleDeclaration> 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<CSSPrimitiveValue*>(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<CSSStyleSheet*>(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<UChar*>(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<CSSStyleSheet> 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<CSSStyleSourceData>* 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<CSSStyleSheet*>(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<CSSPropertySourceData>::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<CSSValue> value, bool important) |
| { |
| OwnPtr<CSSProperty> 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<CSSProperty**>(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<unsigned>(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<CSSStyleSheet*>(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<CSSValue> parsedValue; |
| |
| switch (static_cast<CSSPropertyID>(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: // <length>{1,2} | auto | [ <page-size> || [ portrait | landscape] ] |
| return parseSize(propId, important); |
| |
| case CSSPropertyQuotes: // [<string> <string>]+ | 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: // [ <string> | <uri> | <counter> | 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: // <shape> | 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 | <string> | inherit |
| if ((id >= CSSValueWebkitAuto && id <= CSSValueWebkitMatchParent) || id == CSSValueStart || id == CSSValueEnd |
| || value->unit == CSSPrimitiveValue::CSS_STRING) |
| validPrimitive = true; |
| break; |
| |
| case CSSPropertyOutlineStyle: // (<border-style> except hidden) | auto | inherit |
| if (id == CSSValueAuto || id == CSSValueNone || (id >= CSSValueInset && id <= CSSValueDouble)) |
| validPrimitive = true; |
| break; |
| |
| case CSSPropertyBorderTopStyle: //// <border-style> | 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: // <color> | 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: // <color> | inherit |
| case CSSPropertyBorderTopColor: // <color> | inherit |
| case CSSPropertyBorderRightColor: |
| case CSSPropertyBorderBottomColor: |
| case CSSPropertyBorderLeftColor: |
| case CSSPropertyWebkitBorderStartColor: |
| case CSSPropertyWebkitBorderEndColor: |
| case CSSPropertyWebkitBorderBeforeColor: |
| case CSSPropertyWebkitBorderAfterColor: |
| case CSSPropertyColor: // <color> | 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: { |
| // [<uri>,]* [ 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<CSSValueList> list; |
| while (value && value->unit == CSSPrimitiveValue::CSS_URI) { |
| if (!list) |
| list = CSSValueList::createCommaSeparated(); |
| String uri = value->string; |
| Vector<int> 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<CSSValue> val1; |
| RefPtr<CSSValue> val2; |
| int propId1, propId2; |
| bool result = false; |
| if (parseFillProperty(propId, propId1, propId2, val1, val2)) { |
| OwnPtr<ShorthandScope> 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: // <uri> | 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: // <border-width> | inherit |
| case CSSPropertyBorderTopWidth: //// <border-width> | inherit |
| case CSSPropertyBorderRightWidth: // Which is defined as |
| case CSSPropertyBorderBottomWidth: // thin | medium | thick | <length> |
| 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 | <length> | inherit |
| case CSSPropertyWordSpacing: // normal | <length> | 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: // <length> | <percentage> | inherit |
| validPrimitive = (!id && validUnit(value, FLength | FPercent, m_strict)); |
| break; |
| |
| case CSSPropertyPaddingTop: //// <padding-width> | inherit |
| case CSSPropertyPaddingRight: // Which is defined as |
| case CSSPropertyPaddingBottom: // <length> | <percentage> |
| case CSSPropertyPaddingLeft: //// |
| case CSSPropertyWebkitPaddingStart: |
| case CSSPropertyWebkitPaddingEnd: |
| case CSSPropertyWebkitPaddingBefore: |
| case CSSPropertyWebkitPaddingAfter: |
| validPrimitive = (!id && validUnit(value, FLength | FPercent | FNonNeg, m_strict)); |
| break; |
| |
| case CSSPropertyMaxHeight: // <length> | <percentage> | none | inherit |
| case CSSPropertyMaxWidth: // <length> | <percentage> | none | inherit |
| case CSSPropertyWebkitMaxLogicalWidth: |
| case CSSPropertyWebkitMaxLogicalHeight: |
| if (id == CSSValueNone || id == CSSValueIntrinsic || id == CSSValueMinIntrinsic) { |
| validPrimitive = true; |
| break; |
| } |
| /* nobreak */ |
| case CSSPropertyMinHeight: // <length> | <percentage> | inherit |
| case CSSPropertyMinWidth: // <length> | <percentage> | 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: |
| // <absolute-size> | <relative-size> | <length> | <percentage> | 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 | |
| // <percentage> | <length> | inherit |
| |
| if (id >= CSSValueBaseline && id <= CSSValueWebkitBaselineMiddle) |
| validPrimitive = true; |
| else |
| validPrimitive = (!id && validUnit(value, FLength | FPercent, m_strict)); |
| break; |
| |
| case CSSPropertyHeight: // <length> | <percentage> | auto | inherit |
| case CSSPropertyWidth: // <length> | <percentage> | 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: // <length> | <percentage> | auto | inherit |
| case CSSPropertyLeft: // <length> | <percentage> | auto | inherit |
| case CSSPropertyRight: // <length> | <percentage> | auto | inherit |
| case CSSPropertyTop: // <length> | <percentage> | auto | inherit |
| case CSSPropertyMarginTop: //// <margin-width> | inherit |
| case CSSPropertyMarginRight: // Which is defined as |
| case CSSPropertyMarginBottom: // <length> | <percentage> | 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 | <integer> | inherit |
| if (id == CSSValueAuto) { |
| validPrimitive = true; |
| break; |
| } |
| /* nobreak */ |
| case CSSPropertyOrphans: // <integer> | inherit |
| case CSSPropertyWidows: // <integer> | inherit |
| // ### not supported later on |
| validPrimitive = (!id && validUnit(value, FInteger, false)); |
| break; |
| |
| case CSSPropertyLineHeight: // normal | <number> | <length> | <percentage> | inherit |
| if (id == CSSValueNormal) |
| validPrimitive = true; |
| else |
| validPrimitive = (!id && validUnit(value, FNumber | FLength | FPercent | FNonNeg, m_strict)); |
| break; |
| case CSSPropertyCounterIncrement: // [ <identifier> <integer>? ]+ | none | inherit |
| if (id != CSSValueNone) |
| return parseCounter(propId, 1, important); |
| validPrimitive = true; |
| break; |
| case CSSPropertyCounterReset: // [ <identifier> <integer>? ]+ | none | inherit |
| if (id != CSSValueNone) |
| return parseCounter(propId, 0, important); |
| validPrimitive = true; |
| break; |
| case CSSPropertyFontFamily: |
| // [[ <family-name> | <generic-family> ],]* [<family-name> | <generic-family>] | inherit |
| { |
| parsedValue = parseFontFamily(); |
| break; |
| } |
| |
| case CSSPropertyTextDecoration: |
| case CSSPropertyWebkitTextDecorationsInEffect: |
| // none | [ underline || overline || line-through || blink ] | inherit |
| if (id == CSSValueNone) { |
| validPrimitive = true; |
| } else { |
| RefPtr<CSSValueList> 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 | <number> | <percentage> | 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<CSSValue> 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<CSSPrimitiveValue> parsedValue1 = primitiveValueCache()->createValue(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); |
| RefPtr<CSSPrimitiveValue> 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 = Pair::create(parsedValue1.release(), parsedValue2.release()); |
| RefPtr<CSSPrimitiveValue> 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<CSSValue> val = parseTransform(); |
| if (val) { |
| addProperty(propId, val, important); |
| return true; |
| } |
| return false; |
| } |
| break; |
| case CSSPropertyWebkitTransformOrigin: |
| case CSSPropertyWebkitTransformOriginX: |
| case CSSPropertyWebkitTransformOriginY: |
| case CSSPropertyWebkitTransformOriginZ: { |
| RefPtr<CSSValue> val1; |
| RefPtr<CSSValue> val2; |
| RefPtr<CSSValue> 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<CSSValue> 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<CSSValue> val1; |
| RefPtr<CSSValue> 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<CSSValue> 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 | <length> |
| 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 | <length> |
| 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: // <length> |
| 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: // <dashboard-region> | <dashboard-region> |
| 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' || <color> ] | inherit |
| { |
| const int properties[3] = { CSSPropertyBorderWidth, CSSPropertyBorderStyle, |
| CSSPropertyBorderColor }; |
| return parseShorthand(propId, properties, 3, important); |
| } |
| case CSSPropertyBorderTop: |
| // [ 'border-top-width' || 'border-style' || <color> ] | inherit |
| { |
| const int properties[3] = { CSSPropertyBorderTopWidth, CSSPropertyBorderTopStyle, |
| CSSPropertyBorderTopColor}; |
| return parseShorthand(propId, properties, 3, important); |
| } |
| case CSSPropertyBorderRight: |
| // [ 'border-right-width' || 'border-style' || <color> ] | inherit |
| { |
| const int properties[3] = { CSSPropertyBorderRightWidth, CSSPropertyBorderRightStyle, |
| CSSPropertyBorderRightColor }; |
| return parseShorthand(propId, properties, 3, important); |
| } |
| case CSSPropertyBorderBottom: |
| // [ 'border-bottom-width' || 'border-style' || <color> ] | inherit |
| { |
| const int properties[3] = { CSSPropertyBorderBottomWidth, CSSPropertyBorderBottomStyle, |
| CSSPropertyBorderBottomColor }; |
| return parseShorthand(propId, properties, 3, important); |
| } |
| case CSSPropertyBorderLeft: |
| // [ 'border-left-width' || 'border-style' || <color> ] | 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: |
| // <color>{1,4} | inherit |
| { |
| const int properties[4] = { CSSPropertyBorderTopColor, CSSPropertyBorderRightColor, |
| CSSPropertyBorderBottomColor, CSSPropertyBorderLeftColor }; |
| return parse4Values(propId, properties, important); |
| } |
| case CSSPropertyBorderWidth: |
| // <border-width>{1,4} | inherit |
| { |
| const int properties[4] = { CSSPropertyBorderTopWidth, CSSPropertyBorderRightWidth, |
| CSSPropertyBorderBottomWidth, CSSPropertyBorderLeftWidth }; |
| return parse4Values(propId, properties, important); |
| } |
| case CSSPropertyBorderStyle: |
| // <border-style>{1,4} | inherit |
| { |
| const int properties[4] = { CSSPropertyBorderTopStyle, CSSPropertyBorderRightStyle, |
| CSSPropertyBorderBottomStyle, CSSPropertyBorderLeftStyle }; |
| return parse4Values(propId, properties, important); |
| } |
| case CSSPropertyMargin: |
| // <margin-width>{1,4} | inherit |
| { |
| const int properties[4] = { CSSPropertyMarginTop, CSSPropertyMarginRight, |
| CSSPropertyMarginBottom, CSSPropertyMarginLeft }; |
| return parse4Values(propId, properties, important); |
| } |
| case CSSPropertyPadding: |
| // <padding-width>{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<CSSValue> CSSParser::parseWCSSInputProperty() |
| { |
| RefPtr<CSSValue> 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<CSSValue>& lval, PassRefPtr<CSSValue> rval) |
| { |
| if (lval) { |
| if (lval->isValueList()) |
| static_cast<CSSValueList*>(lval.get())->append(rval); |
| else { |
| PassRefPtr<CSSValue> oldlVal(lval.release()); |
| PassRefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); |
| list->append(oldlVal); |
| list->append(rval); |
| lval = list; |
| } |
| } |
| else |
| lval = rval; |
| } |
| |
| static bool parseBackgroundClip(CSSParserValue* parserValue, RefPtr<CSSValue>& 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<CSSValue> values[cMaxFillProperties]; |
| RefPtr<CSSValue> clipValue; |
| RefPtr<CSSValue> positionYValue; |
| RefPtr<CSSValue> 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<CSSValue> val1; |
| RefPtr<CSSValue> 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<CSSValue>& lval, PassRefPtr<CSSValue> rval) |
| { |
| if (lval) { |
| if (lval->isValueList()) |
| static_cast<CSSValueList*>(lval.get())->append(rval); |
| else { |
| PassRefPtr<CSSValue> oldVal(lval.release()); |
| PassRefPtr<CSSValueList> 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<CSSValue> 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<CSSValue> 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<CSSValue> 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<CSSValue> 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 | <identifier> |
| 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; |
| } |
| |
| // <length>{1,2} | auto | [ <page-size> || [ 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<CSSValueList> 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<CSSPrimitiveValue::UnitTypes>(value->unit))); |
| return Length; |
| } |
| return None; |
| default: |
| return None; |
| } |
| } |
| |
| // [ <string> <string> ]+ | inherit | none |
| // inherit and none are handled in parseValue. |
| bool CSSParser::parseQuotes(int propId, bool important) |
| { |
| RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); |
| while (CSSParserValue* val = m_valueList->current()) { |
| RefPtr<CSSValue> 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; |
| } |
| |
| // [ <string> | <uri> | <counter> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit |
| // in CSS 2.1 this got somewhat reduced: |
| // [ <string> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit |
| bool CSSParser::parseContent(int propId, bool important) |
| { |
| RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); |
| |
| while (CSSParserValue* val = m_valueList->current()) { |
| RefPtr<CSSValue> 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<CSSValue> 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<CSSValue> 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<CSSValue>& 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<CSSValue> 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<CSSValue> 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<CSSValue> 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<CSSValue>& value1, RefPtr<CSSValue>& 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<CSSValue>& value1, RefPtr<CSSValue>& 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<CSSPrimitiveValue*>(value1.get())->getIdent()); |
| } |
| } |
| |
| PassRefPtr<CSSValue> 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<CSSPrimitiveValue> 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<CSSPropertyID>(propId); |
| RefPtr<CSSPrimitiveValue> 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<CSSValue>& retValue1, RefPtr<CSSValue>& retValue2) |
| { |
| RefPtr<CSSValueList> values; |
| RefPtr<CSSValueList> values2; |
| CSSParserValue* val; |
| RefPtr<CSSValue> value; |
| RefPtr<CSSValue> 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<CSSValue> currValue; |
| RefPtr<CSSValue> 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<CSSValue> 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<CSSValue> CSSParser::parseAnimationDirection() |
| { |
| CSSParserValue* value = m_valueList->current(); |
| if (value->id == CSSValueNormal || value->id == CSSValueAlternate) |
| return primitiveValueCache()->createIdentifierValue(value->id); |
| return 0; |
| } |
| |
| PassRefPtr<CSSValue> 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<CSSValue> 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<CSSValue> 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<CSSValue> 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<CSSValue> CSSParser::parseAnimationPlayState() |
| { |
| CSSParserValue* value = m_valueList->current(); |
| if (value->id == CSSValueRunning || value->id == CSSValuePaused) |
| return primitiveValueCache()->createIdentifierValue(value->id); |
| return 0; |
| } |
| |
| PassRefPtr<CSSValue> 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<CSSValue>& value1, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& 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<CSSValue> 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<CSSValue>& result) |
| { |
| RefPtr<CSSValueList> values; |
| CSSParserValue* val; |
| RefPtr<CSSValue> value; |
| bool allowComma = false; |
| |
| result = 0; |
| |
| while ((val = m_valueList->current())) { |
| RefPtr<CSSValue> 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<DashboardRegion> firstRegion = DashboardRegion::create(); |
| DashboardRegion* region = 0; |
| |
| while (value) { |
| if (region == 0) { |
| region = firstRegion.get(); |
| } else { |
| RefPtr<DashboardRegion> 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<CSSPrimitiveValue> 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<CSSPrimitiveValue> 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<CSSValue> 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<CSSPrimitiveValue> identifier = primitiveValueCache()->createValue(i->string, CSSPrimitiveValue::CSS_STRING); |
| |
| RefPtr<CSSPrimitiveValue> 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<CSSPrimitiveValue> 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 = 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<CSSPrimitiveValue> 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<FontValue> 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 |
| // <absolute-size> | <relative-size> | <length> | <percentage> | 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<CSSValueList> CSSParser::parseFontFamily() |
| { |
| RefPtr<CSSValueList> 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<FontFamilyValue> 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<FontFamilyValue> 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<CSSValueList> values; |
| if (m_valueList->size() > 1) |
| values = CSSValueList::createCommaSeparated(); |
| CSSParserValue* val; |
| bool expectComma = false; |
| while ((val = m_valueList->current())) { |
| RefPtr<CSSPrimitiveValue> 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<CSSValueList> values; |
| if (m_valueList->size() > 1) |
| values = CSSValueList::createCommaSeparated(); |
| CSSParserValue* val; |
| bool expectComma = false; |
| while ((val = m_valueList->current())) { |
| RefPtr<CSSPrimitiveValue> 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<CSSValueList> values; |
| if (m_valueList->size() > 1) |
| values = CSSValueList::createCommaSeparated(); |
| CSSParserValue* val; |
| bool expectComma = false; |
| while ((val = m_valueList->current())) { |
| RefPtr<CSSPrimitiveValue> 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<int>(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<CSSValueList> values(CSSValueList::createCommaSeparated()); |
| CSSParserValue* val; |
| bool expectComma = false; |
| bool allowFormat = false; |
| bool failed = false; |
| RefPtr<CSSFontFaceSrcValue> uriValue; |
| while ((val = m_valueList->current())) { |
| RefPtr<CSSFontFaceSrcValue> 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<CSSValueList> 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 (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<int>(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<int>(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<int>(v->fValue * 256.0 / 100.0); |
| } |
| |
| if (v->fValue >= 255.0) |
| return 255; |
| |
| return static_cast<int>(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<int>(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(<number>, <percent>, <percent>) |
| // and with alpha, the format is |
| // hsla(<number>, <percent>, <percent>, <number>) |
| // The first value, HUE, is in an angle with a value between 0 and 360 |
| bool CSSParser::parseHSLParameters(CSSParserValue* value, double* colorArray, bool parseAlpha) |
| { |
| CSSParserValueList* args = value->function->args.get(); |
| CSSParserValue* v = args->current(); |
| // Get the first value |
| if (!validUnit(v, FNumber, true)) |
| return false; |
| // normalize the Hue value and change it to be between 0 and 1.0 |
| colorArray[0] = (((static_cast<int>(v->fValue) % 360) + 360) % 360) / 360.0; |
| 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, FPercent, true)) |
| return false; |
| colorArray[i] = max(0.0, min(100.0, v->fValue)) / 100.0; // needs to be value between 0 and 1.0 |
| } |
| if (parseAlpha) { |
| v = args->next(); |
| if (v->unit != CSSParserValue::Operator && v->iValue != ',') |
| return false; |
| v = args->next(); |
| if (!validUnit(v, FNumber, true)) |
| return false; |
| colorArray[3] = max(0.0, min(1.0, v->fValue)); |
| } |
| return true; |
| } |
| |
| PassRefPtr<CSSPrimitiveValue> CSSParser::parseColor(CSSParserValue* value) |
| { |
| RGBA32 c = Color::transparent; |
| if (!parseColorFromValue(value ? value : m_valueList->current(), c)) |
| return 0; |
| return primitiveValueCache()->createColorValue(c); |
| } |
| |
| bool CSSParser::parseColorFromValue(CSSParserValue* value, RGBA32& c) |
| { |
| if (!m_strict && value->unit == CSSPrimitiveValue::CSS_NUMBER && |
| value->fValue >= 0. && value->fValue < 1000000.) { |
| String str = String::format("%06d", (int)(value->fValue+.5)); |
| if (!CSSParser::parseColor(str, c, m_strict)) |
| return false; |
| } else if (value->unit == CSSPrimitiveValue::CSS_PARSER_HEXCOLOR || |
| value->unit == CSSPrimitiveValue::CSS_IDENT || |
| (!m_strict && value->unit == CSSPrimitiveValue::CSS_DIMENSION)) { |
| if (!CSSParser::parseColor(value->string, c, m_strict && value->unit == CSSPrimitiveValue::CSS_IDENT)) |
| return false; |
| } else if (value->unit == CSSParserValue::Function && |
| value->function->args != 0 && |
| value->function->args->size() == 5 /* rgb + two commas */ && |
| equalIgnoringCase(value->function->name, "rgb(")) { |
| int colorValues[3]; |
| if (!parseColorParameters(value, colorValues, false)) |
| return false; |
| c = makeRGB(colorValues[0], colorValues[1], colorValues[2]); |
| } else { |
| if (value->unit == CSSParserValue::Function && |
| value->function->args != 0 && |
| value->function->args->size() == 7 /* rgba + three commas */ && |
| equalIgnoringCase(value->function->name, "rgba(")) { |
| int colorValues[4]; |
| if (!parseColorParameters(value, colorValues, true)) |
| return false; |
| c = makeRGBA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); |
| } else if (value->unit == CSSParserValue::Function && |
| value->function->args != 0 && |
| value->function->args->size() == 5 /* hsl + two commas */ && |
| equalIgnoringCase(value->function->name, "hsl(")) { |
| double colorValues[3]; |
| if (!parseHSLParameters(value, colorValues, false)) |
| return false; |
| c = makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], 1.0); |
| } else if (value->unit == CSSParserValue::Function && |
| value->function->args != 0 && |
| value->function->args->size() == 7 /* hsla + three commas */ && |
| equalIgnoringCase(value->function->name, "hsla(")) { |
| double colorValues[4]; |
| if (!parseHSLParameters(value, colorValues, true)) |
| return false; |
| c = makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); |
| } else |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // This class tracks parsing state for shadow values. If it goes out of scope (e.g., due to an early return) |
| // without the allowBreak bit being set, then it will clean up all of the objects and destroy them. |
| struct ShadowParseContext { |
| ShadowParseContext(CSSPropertyID prop, CSSPrimitiveValueCache* primitiveValueCache) |
| : property(prop) |
| , m_primitiveValueCache(primitiveValueCache) |
| , allowX(true) |
| , allowY(false) |
| , allowBlur(false) |
| , allowSpread(false) |
| , allowColor(true) |
| , allowStyle(prop == CSSPropertyWebkitBoxShadow || prop == CSSPropertyBoxShadow) |
| , allowBreak(true) |
| { |
| } |
| |
| bool allowLength() { return allowX || allowY || allowBlur || allowSpread; } |
| |
| void commitValue() |
| { |
| // Handle the ,, case gracefully by doing nothing. |
| if (x || y || blur || spread || color || style) { |
| if (!values) |
| values = CSSValueList::createCommaSeparated(); |
| |
| // Construct the current shadow value and add it to the list. |
| values->append(ShadowValue::create(x.release(), y.release(), blur.release(), spread.release(), style.release(), color.release())); |
| } |
| |
| // Now reset for the next shadow value. |
| x = 0; |
| y = 0; |
| blur = 0; |
| spread = 0; |
| style = 0; |
| color = 0; |
| |
| allowX = true; |
| allowColor = true; |
| allowBreak = true; |
| allowY = false; |
| allowBlur = false; |
| allowSpread = false; |
| allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; |
| } |
| |
| void commitLength(CSSParserValue* v) |
| { |
| RefPtr<CSSPrimitiveValue> val = m_primitiveValueCache->createValue(v->fValue, (CSSPrimitiveValue::UnitTypes)v->unit); |
| |
| if (allowX) { |
| x = val.release(); |
| allowX = false; |
| allowY = true; |
| allowColor = false; |
| allowStyle = false; |
| allowBreak = false; |
| } else if (allowY) { |
| y = val.release(); |
| allowY = false; |
| allowBlur = true; |
| allowColor = true; |
| allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; |
| allowBreak = true; |
| } else if (allowBlur) { |
| blur = val.release(); |
| allowBlur = false; |
| allowSpread = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; |
| } else if (allowSpread) { |
| spread = val.release(); |
| allowSpread = false; |
| } |
| } |
| |
| void commitColor(PassRefPtr<CSSPrimitiveValue> val) |
| { |
| color = val; |
| allowColor = false; |
| if (allowX) { |
| allowStyle = false; |
| allowBreak = false; |
| } else { |
| allowBlur = false; |
| allowSpread = false; |
| allowStyle = property == CSSPropertyWebkitBoxShadow || property == CSSPropertyBoxShadow; |
| } |
| } |
| |
| void commitStyle(CSSParserValue* v) |
| { |
| style = m_primitiveValueCache->createIdentifierValue(v->id); |
| allowStyle = false; |
| if (allowX) |
| allowBreak = false; |
| else { |
| allowBlur = false; |
| allowSpread = false; |
| allowColor = false; |
| } |
| } |
| |
| CSSPropertyID property; |
| CSSPrimitiveValueCache* m_primitiveValueCache; |
| |
| RefPtr<CSSValueList> values; |
| RefPtr<CSSPrimitiveValue> x; |
| RefPtr<CSSPrimitiveValue> y; |
| RefPtr<CSSPrimitiveValue> blur; |
| RefPtr<CSSPrimitiveValue> spread; |
| RefPtr<CSSPrimitiveValue> style; |
| RefPtr<CSSPrimitiveValue> color; |
| |
| bool allowX; |
| bool allowY; |
| bool allowBlur; |
| bool allowSpread; |
| bool allowColor; |
| bool allowStyle; // inset or not. |
| bool allowBreak; |
| }; |
| |
| bool CSSParser::parseShadow(int propId, bool important) |
| { |
| ShadowParseContext context(static_cast<CSSPropertyID>(propId), primitiveValueCache()); |
| CSSParserValue* val; |
| while ((val = m_valueList->current())) { |
| // Check for a comma break first. |
| if (val->unit == CSSParserValue::Operator) { |
| if (val->iValue != ',' || !context.allowBreak) |
| // Other operators aren't legal or we aren't done with the current shadow |
| // value. Treat as invalid. |
| return false; |
| #if ENABLE(SVG) |
| // -webkit-svg-shadow does not support multiple values. |
| if (static_cast<CSSPropertyID>(propId) == CSSPropertyWebkitSvgShadow) |
| return false; |
| #endif |
| // The value is good. Commit it. |
| context.commitValue(); |
| } else if (validUnit(val, FLength, true)) { |
| // We required a length and didn't get one. Invalid. |
| if (!context.allowLength()) |
| return false; |
| |
| // A length is allowed here. Construct the value and add it. |
| context.commitLength(val); |
| } else if (val->id == CSSValueInset) { |
| if (!context.allowStyle) |
| return false; |
| |
| context.commitStyle(val); |
| } else { |
| // The only other type of value that's ok is a color value. |
| RefPtr<CSSPrimitiveValue> parsedColor; |
| bool isColor = ((val->id >= CSSValueAqua && val->id <= CSSValueWindowtext) || val->id == CSSValueMenu || |
| (val->id >= CSSValueWebkitFocusRingColor && val->id <= CSSValueWebkitText && !m_strict)); |
| if (isColor) { |
| if (!context.allowColor) |
| return false; |
| parsedColor = primitiveValueCache()->createIdentifierValue(val->id); |
| } |
| |
| if (!parsedColor) |
| // It's not built-in. Try to parse it as a color. |
| parsedColor = parseColor(val); |
| |
| if (!parsedColor || !context.allowColor) |
| return false; // This value is not a color or length and is invalid or |
| // it is a color, but a color isn't allowed at this point. |
| |
| context.commitColor(parsedColor.release()); |
| } |
| |
| m_valueList->next(); |
| } |
| |
| if (context.allowBreak) { |
| context.commitValue(); |
| if (context.values->length()) { |
| addProperty(propId, context.values.release(), important); |
| m_valueList->next(); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool CSSParser::parseReflect(int propId, bool important) |
| { |
| // box-reflect: <direction> <offset> <mask> |
| |
| // Direction comes first. |
| CSSParserValue* val = m_valueList->current(); |
| CSSReflectionDirection direction; |
| switch (val->id) { |
| case CSSValueAbove: |
| direction = ReflectionAbove; |
| break; |
| case CSSValueBelow: |
| direction = ReflectionBelow; |
| break; |
| case CSSValueLeft: |
| direction = ReflectionLeft; |
| break; |
| case CSSValueRight: |
| direction = ReflectionRight; |
| break; |
| default: |
| return false; |
| } |
| |
| // The offset comes next. |
| val = m_valueList->next(); |
| RefPtr<CSSPrimitiveValue> offset; |
| if (!val) |
| offset = primitiveValueCache()->createValue(0, CSSPrimitiveValue::CSS_PX); |
| else { |
| if (!validUnit(val, FLength | FPercent, m_strict)) |
| return false; |
| offset = primitiveValueCache()->createValue(val->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(val->unit)); |
| } |
| |
| // Now for the mask. |
| RefPtr<CSSValue> mask; |
| val = m_valueList->next(); |
| if (val) { |
| if (!parseBorderImage(propId, important, mask)) |
| return false; |
| } |
| |
| RefPtr<CSSReflectValue> reflectValue = CSSReflectValue::create(direction, offset.release(), mask.release()); |
| addProperty(propId, reflectValue.release(), important); |
| m_valueList->next(); |
| return true; |
| } |
| |
| struct BorderImageParseContext { |
| BorderImageParseContext(CSSPrimitiveValueCache* primitiveValueCache) |
| : m_primitiveValueCache(primitiveValueCache) |
| , m_allowBreak(false) |
| , m_allowNumber(false) |
| , m_allowSlash(false) |
| , m_allowWidth(false) |
| , m_allowRule(false) |
| , m_borderTop(0) |
| , m_borderRight(0) |
| , m_borderBottom(0) |
| , m_borderLeft(0) |
| , m_horizontalRule(0) |
| , m_verticalRule(0) |
| {} |
| |
| bool allowBreak() const { return m_allowBreak; } |
| bool allowNumber() const { return m_allowNumber; } |
| bool allowSlash() const { return m_allowSlash; } |
| bool allowWidth() const { return m_allowWidth; } |
| bool allowRule() const { return m_allowRule; } |
| |
| void commitImage(PassRefPtr<CSSValue> image) { m_image = image; m_allowNumber = true; } |
| void commitNumber(CSSParserValue* v) |
| { |
| PassRefPtr<CSSPrimitiveValue> val = m_primitiveValueCache->createValue(v->fValue, (CSSPrimitiveValue::UnitTypes)v->unit); |
| if (!m_top) |
| m_top = val; |
| else if (!m_right) |
| m_right = val; |
| else if (!m_bottom) |
| m_bottom = val; |
| else { |
| ASSERT(!m_left); |
| m_left = val; |
| } |
| |
| m_allowBreak = m_allowSlash = m_allowRule = true; |
| m_allowNumber = !m_left; |
| } |
| void commitSlash() { m_allowBreak = m_allowSlash = m_allowNumber = false; m_allowWidth = true; } |
| void commitWidth(CSSParserValue* val) |
| { |
| if (!m_borderTop) |
| m_borderTop = val; |
| else if (!m_borderRight) |
| m_borderRight = val; |
| else if (!m_borderBottom) |
| m_borderBottom = val; |
| else { |
| ASSERT(!m_borderLeft); |
| m_borderLeft = val; |
| } |
| |
| m_allowBreak = m_allowRule = true; |
| m_allowWidth = !m_borderLeft; |
| } |
| void commitRule(int keyword) |
| { |
| if (!m_horizontalRule) |
| m_horizontalRule = keyword; |
| else if (!m_verticalRule) |
| m_verticalRule = keyword; |
| m_allowRule = !m_verticalRule; |
| } |
| PassRefPtr<CSSValue> commitBorderImage(CSSParser* p, bool important) |
| { |
| // We need to clone and repeat values for any omissions. |
| if (!m_right) { |
| m_right = m_primitiveValueCache->createValue(m_top->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_top->primitiveType()); |
| m_bottom = m_primitiveValueCache->createValue(m_top->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_top->primitiveType()); |
| m_left = m_primitiveValueCache->createValue(m_top->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_top->primitiveType()); |
| } |
| if (!m_bottom) { |
| m_bottom = m_primitiveValueCache->createValue(m_top->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_top->primitiveType()); |
| m_left = m_primitiveValueCache->createValue(m_right->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_right->primitiveType()); |
| } |
| if (!m_left) |
| m_left = m_primitiveValueCache->createValue(m_right->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_right->primitiveType()); |
| |
| // Now build a rect value to hold all four of our primitive values. |
| RefPtr<Rect> rect = Rect::create(); |
| rect->setTop(m_top); |
| rect->setRight(m_right); |
| rect->setBottom(m_bottom); |
| rect->setLeft(m_left); |
| |
| // Fill in STRETCH as the default if it wasn't specified. |
| if (!m_horizontalRule) |
| m_horizontalRule = CSSValueStretch; |
| |
| // The vertical rule should match the horizontal rule if unspecified. |
| if (!m_verticalRule) |
| m_verticalRule = m_horizontalRule; |
| |
| // Now we have to deal with the border widths. The best way to deal with these is to actually put these values into a value |
| // list and then make our parsing machinery do the parsing. |
| if (m_borderTop) { |
| CSSParserValueList newList; |
| newList.addValue(*m_borderTop); |
| if (m_borderRight) |
| newList.addValue(*m_borderRight); |
| if (m_borderBottom) |
| newList.addValue(*m_borderBottom); |
| if (m_borderLeft) |
| newList.addValue(*m_borderLeft); |
| CSSParserValueList* oldList = p->m_valueList; |
| p->m_valueList = &newList; |
| p->parseValue(CSSPropertyBorderWidth, important); |
| p->m_valueList = oldList; |
| } |
| |
| // Make our new border image value now. |
| return CSSBorderImageValue::create(m_image, rect.release(), m_horizontalRule, m_verticalRule); |
| } |
| |
| CSSPrimitiveValueCache* m_primitiveValueCache; |
| |
| bool m_allowBreak; |
| bool m_allowNumber; |
| bool m_allowSlash; |
| bool m_allowWidth; |
| bool m_allowRule; |
| |
| RefPtr<CSSValue> m_image; |
| |
| RefPtr<CSSPrimitiveValue> m_top; |
| RefPtr<CSSPrimitiveValue> m_right; |
| RefPtr<CSSPrimitiveValue> m_bottom; |
| RefPtr<CSSPrimitiveValue> m_left; |
| |
| CSSParserValue* m_borderTop; |
| CSSParserValue* m_borderRight; |
| CSSParserValue* m_borderBottom; |
| CSSParserValue* m_borderLeft; |
| |
| int m_horizontalRule; |
| int m_verticalRule; |
| }; |
| |
| bool CSSParser::parseBorderImage(int propId, bool important, RefPtr<CSSValue>& result) |
| { |
| // Look for an image initially. If the first value is not a URI, then we're done. |
| BorderImageParseContext context(primitiveValueCache()); |
| CSSParserValue* val = m_valueList->current(); |
| if (val->unit == CSSPrimitiveValue::CSS_URI && m_styleSheet) { |
| // FIXME: The completeURL call should be done when using the CSSImageValue, |
| // not when creating it. |
| context.commitImage(CSSImageValue::create(m_styleSheet->completeURL(val->string))); |
| } else if (isGeneratedImageValue(val)) { |
| RefPtr<CSSValue> value; |
| if (parseGeneratedImage(value)) |
| context.commitImage(value); |
| else |
| return false; |
| } else |
| return false; |
| |
| while ((val = m_valueList->next())) { |
| if (context.allowNumber() && validUnit(val, FInteger | FNonNeg | FPercent, true)) { |
| context.commitNumber(val); |
| } else if (propId == CSSPropertyWebkitBorderImage && context.allowSlash() && val->unit == CSSParserValue::Operator && val->iValue == '/') { |
| context.commitSlash(); |
| } else if (context.allowWidth() && |
| (val->id == CSSValueThin || val->id == CSSValueMedium || val->id == CSSValueThick || validUnit(val, FLength, m_strict))) { |
| context.commitWidth(val); |
| } else if (context.allowRule() && |
| (val->id == CSSValueStretch || val->id == CSSValueRound || val->id == CSSValueRepeat)) { |
| context.commitRule(val->id); |
| } else { |
| // Something invalid was encountered. |
| return false; |
| } |
| } |
| |
| if (context.allowNumber() && propId != CSSPropertyWebkitBorderImage) { |
| // Allow the slices to be omitted for images that don't fit to a border. We just set the slices to be 0. |
| context.m_top = primitiveValueCache()->createValue(0, CSSPrimitiveValue::CSS_NUMBER); |
| context.m_allowBreak = true; |
| } |
| |
| if (context.allowBreak()) { |
| // Need to fully commit as a single value. |
| result = context.commitBorderImage(this, important); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static void completeBorderRadii(RefPtr<CSSPrimitiveValue> radii[4]) |
| { |
| if (radii[3]) |
| return; |
| if (!radii[2]) { |
| if (!radii[1]) |
| radii[1] = radii[0]; |
| radii[2] = radii[0]; |
| } |
| radii[3] = radii[1]; |
| } |
| |
| bool CSSParser::parseBorderRadius(int propId, bool important) |
| { |
| unsigned num = m_valueList->size(); |
| if (num > 9) |
| return false; |
| |
| ShorthandScope scope(this, propId); |
| RefPtr<CSSPrimitiveValue> radii[2][4]; |
| |
| unsigned indexAfterSlash = 0; |
| for (unsigned i = 0; i < num; ++i) { |
| CSSParserValue* value = m_valueList->valueAt(i); |
| if (value->unit == CSSParserValue::Operator) { |
| if (value->iValue != '/') |
| return false; |
| |
| if (!i || indexAfterSlash || i + 1 == num || num > i + 5) |
| return false; |
| |
| indexAfterSlash = i + 1; |
| completeBorderRadii(radii[0]); |
| continue; |
| } |
| |
| if (i - indexAfterSlash >= 4) |
| return false; |
| |
| if (!validUnit(value, FLength | FPercent, m_strict)) |
| return false; |
| |
| RefPtr<CSSPrimitiveValue> radius = primitiveValueCache()->createValue(value->fValue, static_cast<CSSPrimitiveValue::UnitTypes>(value->unit)); |
| |
| if (!indexAfterSlash) { |
| radii[0][i] = radius; |
| |
| // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2; |
| if (num == 2 && propId == CSSPropertyWebkitBorderRadius) { |
| indexAfterSlash = 1; |
| completeBorderRadii(radii[0]); |
| } |
| } else |
| radii[1][i - indexAfterSlash] = radius.release(); |
| } |
| |
| if (!indexAfterSlash) { |
| completeBorderRadii(radii[0]); |
| for (unsigned i = 0; i < 4; ++i) |
| radii[1][i] = radii[0][i]; |
| } else |
| completeBorderRadii(radii[1]); |
| |
| m_implicitShorthand = true; |
| addProperty(CSSPropertyBorderTopLeftRadius, primitiveValueCache()->createValue(Pair::create(radii[0][0].release(), radii[1][0].release())), important); |
| addProperty(CSSPropertyBorderTopRightRadius, primitiveValueCache()->createValue(Pair::create(radii[0][1].release(), radii[1][1].release())), important); |
| addProperty(CSSPropertyBorderBottomRightRadius, primitiveValueCache()->createValue(Pair::create(radii[0][2].release(), radii[1][2].release())), important); |
| addProperty(CSSPropertyBorderBottomLeftRadius, primitiveValueCache()->createValue(Pair::create(radii[0][3].release(), radii[1][3].release())), important); |
| m_implicitShorthand = false; |
| return true; |
| } |
| |
| bool CSSParser::parseCounter(int propId, int defaultValue, bool important) |
| { |
| enum { ID, VAL } state = ID; |
| |
| RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); |
| RefPtr<CSSPrimitiveValue> counterName; |
| |
| while (true) { |
| CSSParserValue* val = m_valueList->current(); |
| switch (state) { |
| case ID: |
| if (val && val->unit == CSSPrimitiveValue::CSS_IDENT) { |
| counterName = primitiveValueCache()->createValue(val->string, CSSPrimitiveValue::CSS_STRING); |
| state = VAL; |
| m_valueList->next(); |
| continue; |
| } |
| break; |
| case VAL: { |
| int i = defaultValue; |
| if (val && val->unit == CSSPrimitiveValue::CSS_NUMBER) { |
| i = clampToInteger(val->fValue); |
| m_valueList->next(); |
| } |
| |
| list->append(primitiveValueCache()->createValue(Pair::create(counterName.release(), |
| primitiveValueCache()->createValue(i, CSSPrimitiveValue::CSS_NUMBER)))); |
| state = ID; |
| continue; |
| } |
| } |
| break; |
| } |
| |
| if (list->length() > 0) { |
| addProperty(propId, list.release(), important); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // This should go away once we drop support for -webkit-gradient |
| static PassRefPtr<CSSPrimitiveValue> parseDeprecatedGradientPoint(CSSParserValue* a, bool horizontal, CSSPrimitiveValueCache* primitiveValueCache) |
| { |
| RefPtr<CSSPrimitiveValue> result; |
| if (a->unit == CSSPrimitiveValue::CSS_IDENT) { |
| if ((equalIgnoringCase(a->string, "left") && horizontal) || |
| (equalIgnoringCase(a->string, "top") && !horizontal)) |
| result = primitiveValueCache->createValue(0., CSSPrimitiveValue::CSS_PERCENTAGE); |
| else if ((equalIgnoringCase(a->string, "right") && horizontal) || |
| (equalIgnoringCase(a->string, "bottom") && !horizontal)) |
| result = primitiveValueCache->createValue(100., CSSPrimitiveValue::CSS_PERCENTAGE); |
| else if (equalIgnoringCase(a->string, "center")) |
| result = primitiveValueCache->createValue(50., CSSPrimitiveValue::CSS_PERCENTAGE); |
| } else if (a->unit == CSSPrimitiveValue::CSS_NUMBER || a->unit == CSSPrimitiveValue::CSS_PERCENTAGE) |
| result = primitiveValueCache->createValue(a->fValue, (CSSPrimitiveValue::UnitTypes)a->unit); |
| return result; |
| } |
| |
| static bool parseDeprecatedGradientColorStop(CSSParser* p, CSSParserValue* a, CSSGradientColorStop& stop) |
| { |
| if (a->unit != CSSParserValue::Function) |
| return false; |
| |
| if (!equalIgnoringCase(a->function->name, "from(") && |
| !equalIgnoringCase(a->function->name, "to(") && |
| !equalIgnoringCase(a->function->name, "color-stop(")) |
| return false; |
| |
| CSSParserValueList* args = a->function->args.get(); |
| if (!args) |
| return false; |
| |
| if (equalIgnoringCase(a->function->name, "from(") || |
| equalIgnoringCase(a->function->name, "to(")) { |
| // The "from" and "to" stops expect 1 argument. |
| if (args->size() != 1) |
| return false; |
| |
| if (equalIgnoringCase(a->function->name, "from(")) |
| stop.m_position = p->primitiveValueCache()->createValue(0, CSSPrimitiveValue::CSS_NUMBER); |
| else |
| stop.m_position = p->primitiveValueCache()->createValue(1, CSSPrimitiveValue::CSS_NUMBER); |
| |
| int id = args->current()->id; |
| if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu) |
| stop.m_color = p->primitiveValueCache()->createIdentifierValue(id); |
| else |
| stop.m_color = p->parseColor(args->current()); |
| if (!stop.m_color) |
| return false; |
| } |
| |
| // The "color-stop" function expects 3 arguments. |
| if (equalIgnoringCase(a->function->name, "color-stop(")) { |
| if (args->size() != 3) |
| return false; |
| |
| CSSParserValue* stopArg = args->current(); |
| if (stopArg->unit == CSSPrimitiveValue::CSS_PERCENTAGE) |
| stop.m_position = p->primitiveValueCache()->createValue(stopArg->fValue / 100, CSSPrimitiveValue::CSS_NUMBER); |
| else if (stopArg->unit == CSSPrimitiveValue::CSS_NUMBER) |
| stop.m_position = p->primitiveValueCache()->createValue(stopArg->fValue, CSSPrimitiveValue::CSS_NUMBER); |
| else |
| return false; |
| |
| stopArg = args->next(); |
| if (stopArg->unit != CSSParserValue::Operator || stopArg->iValue != ',') |
| return false; |
| |
| stopArg = args->next(); |
| int id = stopArg->id; |
| if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu) |
| stop.m_color = p->primitiveValueCache()->createIdentifierValue(id); |
| else |
| stop.m_color = p->parseColor(stopArg); |
| if (!stop.m_color) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool CSSParser::parseDeprecatedGradient(RefPtr<CSSValue>& gradient) |
| { |
| // Walk the arguments. |
| CSSParserValueList* args = m_valueList->current()->function->args.get(); |
| if (!args || args->size() == 0) |
| return false; |
| |
| // The first argument is the gradient type. It is an identifier. |
| CSSGradientType gradientType; |
| CSSParserValue* a = args->current(); |
| if (!a || a->unit != CSSPrimitiveValue::CSS_IDENT) |
| return false; |
| if (equalIgnoringCase(a->string, "linear")) |
| gradientType = CSSLinearGradient; |
| else if (equalIgnoringCase(a->string, "radial")) |
| gradientType = CSSRadialGradient; |
| else |
| return false; |
| |
| RefPtr<CSSGradientValue> result; |
| switch (gradientType) { |
| case CSSLinearGradient: |
| result = CSSLinearGradientValue::create(NonRepeating, true); |
| break; |
| case CSSRadialGradient: |
| result = CSSRadialGradientValue::create(NonRepeating, true); |
| break; |
| } |
| |
| // Comma. |
| a = args->next(); |
| if (!a || a->unit != CSSParserValue::Operator || a->iValue != ',') |
| return false; |
| |
| // Next comes the starting point for the gradient as an x y pair. There is no |
| // comma between the x and the y values. |
| // First X. It can be left, right, number or percent. |
| a = args->next(); |
| if (!a) |
| return false; |
| RefPtr<CSSPrimitiveValue> point = parseDeprecatedGradientPoint(a, true, primitiveValueCache()); |
| if (!point) |
| return false; |
| result->setFirstX(point.release()); |
| |
| // First Y. It can be top, bottom, number or percent. |
| a = args->next(); |
| if (!a) |
| return false; |
| point = parseDeprecatedGradientPoint(a, false, primitiveValueCache()); |
| if (!point) |
| return false; |
| result->setFirstY(point.release()); |
| |
| // Comma after the first point. |
| a = args->next(); |
| if (!a || a->unit != CSSParserValue::Operator || a->iValue != ',') |
| return false; |
| |
| // For radial gradients only, we now expect a numeric radius. |
| if (gradientType == CSSRadialGradient) { |
| a = args->next(); |
| if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER) |
| return false; |
| static_cast<CSSRadialGradientValue*>(result.get())->setFirstRadius(primitiveValueCache()->createValue(a->fValue, CSSPrimitiveValue::CSS_NUMBER)); |
| |
| // Comma after the first radius. |
| a = args->next(); |
| if (!a || a->unit != CSSParserValue::Operator || a->iValue != ',') |
| return false; |
| } |
| |
| // Next is the ending point for the gradient as an x, y pair. |
| // Second X. It can be left, right, number or percent. |
| a = args->next(); |
| if (!a) |
| return false; |
| point = parseDeprecatedGradientPoint(a, true, primitiveValueCache()); |
| if (!point) |
| return false; |
| result->setSecondX(point.release()); |
| |
| // Second Y. It can be top, bottom, number or percent. |
| a = args->next(); |
| if (!a) |
| return false; |
| point = parseDeprecatedGradientPoint(a, false, primitiveValueCache()); |
| if (!point) |
| return false; |
| result->setSecondY(point.release()); |
| |
| // For radial gradients only, we now expect the second radius. |
| if (gradientType == CSSRadialGradient) { |
| // Comma after the second point. |
| a = args->next(); |
| if (!a || a->unit != CSSParserValue::Operator || a->iValue != ',') |
| return false; |
| |
| a = args->next(); |
| if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER) |
| return false; |
| static_cast<CSSRadialGradientValue*>(result.get())->setSecondRadius(primitiveValueCache()->createValue(a->fValue, CSSPrimitiveValue::CSS_NUMBER)); |
| } |
| |
| // We now will accept any number of stops (0 or more). |
| a = args->next(); |
| while (a) { |
| // Look for the comma before the next stop. |
| if (a->unit != CSSParserValue::Operator || a->iValue != ',') |
| return false; |
| |
| // Now examine the stop itself. |
| a = args->next(); |
| if (!a) |
| return false; |
| |
| // The function name needs to be one of "from", "to", or "color-stop." |
| CSSGradientColorStop stop; |
| if (!parseDeprecatedGradientColorStop(this, a, stop)) |
| return false; |
| result->addStop(stop); |
| |
| // Advance |
| a = args->next(); |
| } |
| |
| gradient = result.release(); |
| return true; |
| } |
| |
| static PassRefPtr<CSSPrimitiveValue> valueFromSideKeyword(CSSParserValue* a, bool& isHorizontal, CSSPrimitiveValueCache* primitiveValueCache) |
| { |
| if (a->unit != CSSPrimitiveValue::CSS_IDENT) |
| return 0; |
| |
| switch (a->id) { |
| case CSSValueLeft: |
| case CSSValueRight: |
| isHorizontal = true; |
| break; |
| case CSSValueTop: |
| case CSSValueBottom: |
| isHorizontal = false; |
| break; |
| default: |
| return 0; |
| } |
| return primitiveValueCache->createIdentifierValue(a->id); |
| } |
| |
| static PassRefPtr<CSSPrimitiveValue> parseGradientColorOrKeyword(CSSParser* p, CSSParserValue* value) |
| { |
| int id = value->id; |
| if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu) |
| return p->primitiveValueCache()->createIdentifierValue(id); |
| |
| return p->parseColor(value); |
| } |
| |
| bool CSSParser::parseLinearGradient(RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) |
| { |
| RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating); |
| |
| // Walk the arguments. |
| CSSParserValueList* args = m_valueList->current()->function->args.get(); |
| if (!args || !args->size()) |
| return false; |
| |
| CSSParserValue* a = args->current(); |
| if (!a) |
| return false; |
| |
| bool expectComma = false; |
| // Look for angle. |
| if (validUnit(a, FAngle, true)) { |
| result->setAngle(primitiveValueCache()->createValue(a->fValue, (CSSPrimitiveValue::UnitTypes)a->unit)); |
| |
| a = args->next(); |
| expectComma = true; |
| } else { |
| // Look one or two optional keywords that indicate a side or corner. |
| RefPtr<CSSPrimitiveValue> startX, startY; |
| |
| RefPtr<CSSPrimitiveValue> location; |
| bool isHorizontal = false; |
| if ((location = valueFromSideKeyword(a, isHorizontal, primitiveValueCache()))) { |
| if (isHorizontal) |
| startX = location; |
| else |
| startY = location; |
| |
| a = args->next(); |
| if (a) { |
| if ((location = valueFromSideKeyword(a, isHorizontal, primitiveValueCache()))) { |
| if (isHorizontal) { |
| if (startX) |
| return false; |
| startX = location; |
| } else { |
| if (startY) |
| return false; |
| startY = location; |
| } |
| |
| a = args->next(); |
| } |
| } |
| |
| expectComma = true; |
| } |
| |
| if (!startX && !startY) |
| startY = primitiveValueCache()->createIdentifierValue(CSSValueTop); |
| |
| result->setFirstX(startX.release()); |
| result->setFirstY(startY.release()); |
| } |
| |
| if (!parseGradientColorStops(args, result.get(), expectComma)) |
| return false; |
| |
| Vector<CSSGradientColorStop>& stops = result->stops(); |
| if (stops.isEmpty()) |
| return false; |
| |
| gradient = result.release(); |
| return true; |
| } |
| |
| bool CSSParser::parseRadialGradient(RefPtr<CSSValue>& gradient, CSSGradientRepeat repeating) |
| { |
| RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating); |
| |
| // Walk the arguments. |
| CSSParserValueList* args = m_valueList->current()->function->args.get(); |
| if (!args || !args->size()) |
| return false; |
| |
| CSSParserValue* a = args->current(); |
| if (!a) |
| return false; |
| |
| bool expectComma = false; |
| |
| // Optional background-position |
| RefPtr<CSSValue> centerX; |
| RefPtr<CSSValue> centerY; |
| // parseFillPosition advances the args next pointer. |
| parseFillPosition(args, centerX, centerY); |
| a = args->current(); |
| |
| if (centerX || centerY) { |
| // Comma |
| if (a->unit != CSSParserValue::Operator || a->iValue != ',') |
| return false; |
| |
| a = args->next(); |
| if (!a) |
| return false; |
| } |
| |
| ASSERT(!centerX || centerX->isPrimitiveValue()); |
| ASSERT(!centerY || centerY->isPrimitiveValue()); |
| |
| result->setFirstX(static_cast<CSSPrimitiveValue*>(centerX.get())); |
| result->setSecondX(static_cast<CSSPrimitiveValue*>(centerX.get())); |
| // CSS3 radial gradients always share the same start and end point. |
| result->setFirstY(static_cast<CSSPrimitiveValue*>(centerY.get())); |
| result->setSecondY(static_cast<CSSPrimitiveValue*>(centerY.get())); |
| |
| RefPtr<CSSPrimitiveValue> shapeValue; |
| RefPtr<CSSPrimitiveValue> sizeValue; |
| |
| // Optional shape and/or size in any order. |
| for (int i = 0; i < 2; ++i) { |
| if (a->unit != CSSPrimitiveValue::CSS_IDENT) |
| break; |
| |
| bool foundValue = false; |
| switch (a->id) { |
| case CSSValueCircle: |
| case CSSValueEllipse: |
| shapeValue = primitiveValueCache()->createIdentifierValue(a->id); |
| foundValue = true; |
| break; |
| case CSSValueClosestSide: |
| case CSSValueClosestCorner: |
| case CSSValueFarthestSide: |
| case CSSValueFarthestCorner: |
| case CSSValueContain: |
| case CSSValueCover: |
| sizeValue = primitiveValueCache()->createIdentifierValue(a->id); |
| foundValue = true; |
| break; |
| } |
| |
| if (foundValue) { |
| a = args->next(); |
| if (!a) |
| return false; |
| |
| expectComma = true; |
| } |
| } |
| |
| result->setShape(shapeValue); |
| result->setSizingBehavior(sizeValue); |
| |
| // Or, two lengths or percentages |
| RefPtr<CSSPrimitiveValue> horizontalSize; |
| RefPtr<CSSPrimitiveValue> verticalSize; |
| |
| if (!shapeValue && !sizeValue) { |
| if (validUnit(a, FLength | FPercent, m_strict)) { |
| horizontalSize = primitiveValueCache()->createValue(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit); |
| a = args->next(); |
| if (!a) |
| return false; |
| |
| expectComma = true; |
| } |
| |
| if (validUnit(a, FLength | FPercent, m_strict)) { |
| verticalSize = primitiveValueCache()->createValue(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit); |
| |
| a = args->next(); |
| if (!a) |
| return false; |
| expectComma = true; |
| } |
| } |
| |
| // Must have neither or both. |
| if (!horizontalSize != !verticalSize) |
| return false; |
| |
| result->setEndHorizontalSize(horizontalSize); |
| result->setEndVerticalSize(verticalSize); |
| |
| if (!parseGradientColorStops(args, result.get(), expectComma)) |
| return false; |
| |
| gradient = result.release(); |
| return true; |
| } |
| |
| bool CSSParser::parseGradientColorStops(CSSParserValueList* valueList, CSSGradientValue* gradient, bool expectComma) |
| { |
| CSSParserValue* a = valueList->current(); |
| |
| // Now look for color stops. |
| while (a) { |
| // Look for the comma before the next stop. |
| if (expectComma) { |
| if (a->unit != CSSParserValue::Operator || a->iValue != ',') |
| return false; |
| |
| a = valueList->next(); |
| if (!a) |
| return false; |
| } |
| |
| // <color-stop> = <color> [ <percentage> | <length> ]? |
| CSSGradientColorStop stop; |
| stop.m_color = parseGradientColorOrKeyword(this, a); |
| if (!stop.m_color) |
| return false; |
| |
| a = valueList->next(); |
| if (a) { |
| if (validUnit(a, FLength | FPercent, m_strict)) { |
| stop.m_position = primitiveValueCache()->createValue(a->fValue, (CSSPrimitiveValue::UnitTypes)a->unit); |
| a = valueList->next(); |
| } |
| } |
| |
| gradient->addStop(stop); |
| expectComma = true; |
| } |
| |
| // Must have 2 or more stops to be valid. |
| return gradient->stops().size() > 1; |
| } |
| |
| bool CSSParser::isGeneratedImageValue(CSSParserValue* val) const |
| { |
| if (val->unit != CSSParserValue::Function) |
| return false; |
| |
| return equalIgnoringCase(val->function->name, "-webkit-gradient(") |
| || equalIgnoringCase(val->function->name, "-webkit-linear-gradient(") |
| || equalIgnoringCase(val->function->name, "-webkit-repeating-linear-gradient(") |
| || equalIgnoringCase(val->function->name, "-webkit-radial-gradient(") |
| || equalIgnoringCase(val->function->name, "-webkit-repeating-radial-gradient(") |
| || equalIgnoringCase(val->function->name, "-webkit-canvas("); |
| } |
| |
| bool CSSParser::parseGeneratedImage(RefPtr<CSSValue>& value) |
| { |
| CSSParserValue* val = m_valueList->current(); |
| |
| if (val->unit != CSSParserValue::Function) |
| return false; |
| |
| if (equalIgnoringCase(val->function->name, "-webkit-gradient(")) |
| return parseDeprecatedGradient(value); |
| |
| if (equalIgnoringCase(val->function->name, "-webkit-linear-gradient(")) |
| return parseLinearGradient(value, NonRepeating); |
| |
| if (equalIgnoringCase(val->function->name, "-webkit-repeating-linear-gradient(")) |
| return parseLinearGradient(value, Repeating); |
| |
| if (equalIgnoringCase(val->function->name, "-webkit-radial-gradient(")) |
| return parseRadialGradient(value, NonRepeating); |
| |
| if (equalIgnoringCase(val->function->name, "-webkit-repeating-radial-gradient(")) |
| return parseRadialGradient(value, Repeating); |
| |
| if (equalIgnoringCase(val->function->name, "-webkit-canvas(")) |
| return parseCanvas(value); |
| |
| return false; |
| } |
| |
| bool CSSParser::parseCanvas(RefPtr<CSSValue>& canvas) |
| { |
| RefPtr<CSSCanvasValue> result = CSSCanvasValue::create(); |
| |
| // Walk the arguments. |
| CSSParserValueList* args = m_valueList->current()->function->args.get(); |
| if (!args || args->size() != 1) |
| return false; |
| |
| // The first argument is the canvas name. It is an identifier. |
| CSSParserValue* a = args->current(); |
| if (!a || a->unit != CSSPrimitiveValue::CSS_IDENT) |
| return false; |
| result->setName(a->string); |
| canvas = result; |
| return true; |
| } |
| |
| class TransformOperationInfo { |
| public: |
| TransformOperationInfo(const CSSParserString& name) |
| : m_type(WebKitCSSTransformValue::UnknownTransformOperation) |
| , m_argCount(1) |
| , m_allowSingleArgument(false) |
| , m_unit(CSSParser::FUnknown) |
| { |
| if (equalIgnoringCase(name, "scale(") || equalIgnoringCase(name, "scalex(") || equalIgnoringCase(name, "scaley(") || equalIgnoringCase(name, "scalez(")) { |
| m_unit = CSSParser::FNumber; |
| if (equalIgnoringCase(name, "scale(")) |
| m_type = WebKitCSSTransformValue::ScaleTransformOperation; |
| else if (equalIgnoringCase(name, "scalex(")) |
| m_type = WebKitCSSTransformValue::ScaleXTransformOperation; |
| else if (equalIgnoringCase(name, "scaley(")) |
| m_type = WebKitCSSTransformValue::ScaleYTransformOperation; |
| else |
| m_type = WebKitCSSTransformValue::ScaleZTransformOperation; |
| } else if (equalIgnoringCase(name, "scale3d(")) { |
| m_type = WebKitCSSTransformValue::Scale3DTransformOperation; |
| m_argCount = 5; |
| m_unit = CSSParser::FNumber; |
| } else if (equalIgnoringCase(name, "rotate(")) { |
| m_type = WebKitCSSTransformValue::RotateTransformOperation; |
| m_unit = CSSParser::FAngle; |
| } else if (equalIgnoringCase(name, "rotatex(") || |
| equalIgnoringCase(name, "rotatey(") || |
| equalIgnoringCase(name, "rotatez(")) { |
| m_unit = CSSParser::FAngle; |
| if (equalIgnoringCase(name, "rotatex(")) |
| m_type = WebKitCSSTransformValue::RotateXTransformOperation; |
| else if (equalIgnoringCase(name, "rotatey(")) |
| m_type = WebKitCSSTransformValue::RotateYTransformOperation; |
| else |
| m_type = WebKitCSSTransformValue::RotateZTransformOperation; |
| } else if (equalIgnoringCase(name, "rotate3d(")) { |
| m_type = WebKitCSSTransformValue::Rotate3DTransformOperation; |
| m_argCount = 7; |
| m_unit = CSSParser::FNumber; |
| } else if (equalIgnoringCase(name, "skew(") || equalIgnoringCase(name, "skewx(") || equalIgnoringCase(name, "skewy(")) { |
| m_unit = CSSParser::FAngle; |
| if (equalIgnoringCase(name, "skew(")) |
| m_type = WebKitCSSTransformValue::SkewTransformOperation; |
| else if (equalIgnoringCase(name, "skewx(")) |
| m_type = WebKitCSSTransformValue::SkewXTransformOperation; |
| else |
| m_type = WebKitCSSTransformValue::SkewYTransformOperation; |
| } else if (equalIgnoringCase(name, "translate(") || equalIgnoringCase(name, "translatex(") || equalIgnoringCase(name, "translatey(") || equalIgnoringCase(name, "translatez(")) { |
| m_unit = CSSParser::FLength | CSSParser::FPercent; |
| if (equalIgnoringCase(name, "translate(")) |
| m_type = WebKitCSSTransformValue::TranslateTransformOperation; |
| else if (equalIgnoringCase(name, "translatex(")) |
| m_type = WebKitCSSTransformValue::TranslateXTransformOperation; |
| else if (equalIgnoringCase(name, "translatey(")) |
| m_type = WebKitCSSTransformValue::TranslateYTransformOperation; |
| else |
| m_type = WebKitCSSTransformValue::TranslateZTransformOperation; |
| } else if (equalIgnoringCase(name, "translate3d(")) { |
| m_type = WebKitCSSTransformValue::Translate3DTransformOperation; |
| m_argCount = 5; |
| m_unit = CSSParser::FLength | CSSParser::FPercent; |
| } else if (equalIgnoringCase(name, "matrix(")) { |
| m_type = WebKitCSSTransformValue::MatrixTransformOperation; |
| m_argCount = 11; |
| m_unit = CSSParser::FNumber; |
| } else if (equalIgnoringCase(name, "matrix3d(")) { |
| m_type = WebKitCSSTransformValue::Matrix3DTransformOperation; |
| m_argCount = 31; |
| m_unit = CSSParser::FNumber; |
| } else if (equalIgnoringCase(name, "perspective(")) { |
| m_type = WebKitCSSTransformValue::PerspectiveTransformOperation; |
| m_unit = CSSParser::FNumber; |
| } |
| |
| if (equalIgnoringCase(name, "scale(") || equalIgnoringCase(name, "skew(") || equalIgnoringCase(name, "translate(")) { |
| m_allowSingleArgument = true; |
| m_argCount = 3; |
| } |
| } |
| |
| WebKitCSSTransformValue::TransformOperationType type() const { return m_type; } |
| unsigned argCount() const { return m_argCount; } |
| CSSParser::Units unit() const { return m_unit; } |
| |
| bool unknown() const { return m_type == WebKitCSSTransformValue::UnknownTransformOperation; } |
| bool hasCorrectArgCount(unsigned argCount) { return m_argCount == argCount || (m_allowSingleArgument && argCount == 1); } |
| |
| private: |
| WebKitCSSTransformValue::TransformOperationType m_type; |
| unsigned m_argCount; |
| bool m_allowSingleArgument; |
| CSSParser::Units m_unit; |
| }; |
| |
| PassRefPtr<CSSValueList> CSSParser::parseTransform() |
| { |
| if (!m_valueList) |
| return 0; |
| |
| // The transform is a list of functional primitives that specify transform operations. |
| // We collect a list of WebKitCSSTransformValues, where each value specifies a single operation. |
| RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated(); |
| for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { |
| if (value->unit != CSSParserValue::Function || !value->function) |
| return 0; |
| |
| // Every primitive requires at least one argument. |
| CSSParserValueList* args = value->function->args.get(); |
| if (!args) |
| return 0; |
| |
| // See if the specified primitive is one we understand. |
| TransformOperationInfo info(value->function->name); |
| if (info.unknown()) |
| return 0; |
| |
| if (!info.hasCorrectArgCount(args->size())) |
| return 0; |
| |
| // Create the new WebKitCSSTransformValue for this operation and add it to our list. |
| RefPtr<WebKitCSSTransformValue> transformValue = WebKitCSSTransformValue::create(info.type()); |
| list->append(transformValue); |
| |
| // Snag our values. |
| CSSParserValue* a = args->current(); |
| unsigned argNumber = 0; |
| while (a) { |
| CSSParser::Units unit = info.unit(); |
| |
| if (info.type() == WebKitCSSTransformValue::Rotate3DTransformOperation && argNumber == 3) { |
| // 4th param of rotate3d() is an angle rather than a bare number, validate it as such |
| if (!validUnit(a, FAngle, true)) |
| return 0; |
| } else if (info.type() == WebKitCSSTransformValue::Translate3DTransformOperation && argNumber == 2) { |
| // 3rd param of translate3d() cannot be a percentage |
| if (!validUnit(a, FLength, true)) |
| return 0; |
| } else if (info.type() == WebKitCSSTransformValue::TranslateZTransformOperation && argNumber == 0) { |
| // 1st param of translateZ() cannot be a percentage |
| if (!validUnit(a, FLength, true)) |
| return 0; |
| } else if (info.type() == WebKitCSSTransformValue::PerspectiveTransformOperation && argNumber == 0) { |
| // 1st param of perspective() must be a non-negative number (deprecated) or length. |
| if (!validUnit(a, FNumber | FLength | FNonNeg, true)) |
| return 0; |
| } else if (!validUnit(a, unit, true)) |
| return 0; |
| |
| // Add the value to the current transform operation. |
| transformValue->append(primitiveValueCache()->createValue(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit)); |
| |
| a = args->next(); |
| if (!a) |
| break; |
| if (a->unit != CSSParserValue::Operator || a->iValue != ',') |
| return 0; |
| a = args->next(); |
| |
| argNumber++; |
| } |
| } |
| |
| return list.release(); |
| } |
| |
| bool CSSParser::parseTransformOrigin(int propId, int& propId1, int& propId2, int& propId3, RefPtr<CSSValue>& value, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3) |
| { |
| propId1 = propId; |
| propId2 = propId; |
| propId3 = propId; |
| if (propId == CSSPropertyWebkitTransformOrigin) { |
| propId1 = CSSPropertyWebkitTransformOriginX; |
| propId2 = CSSPropertyWebkitTransformOriginY; |
| propId3 = CSSPropertyWebkitTransformOriginZ; |
| } |
| |
| switch (propId) { |
| case CSSPropertyWebkitTransformOrigin: |
| if (!parseTransformOriginShorthand(value, value2, value3)) |
| return false; |
| // parseTransformOriginShorthand advances the m_valueList pointer |
| break; |
| case CSSPropertyWebkitTransformOriginX: { |
| value = parseFillPositionX(m_valueList); |
| if (value) |
| m_valueList->next(); |
| break; |
| } |
| case CSSPropertyWebkitTransformOriginY: { |
| value = parseFillPositionY(m_valueList); |
| if (value) |
| m_valueList->next(); |
| break; |
| } |
| case CSSPropertyWebkitTransformOriginZ: { |
| if (validUnit(m_valueList->current(), FLength, m_strict)) |
| value = primitiveValueCache()->createValue(m_valueList->current()->fValue, (CSSPrimitiveValue::UnitTypes)m_valueList->current()->unit); |
| if (value) |
| m_valueList->next(); |
| break; |
| } |
| } |
| |
| return value; |
| } |
| |
| bool CSSParser::parsePerspectiveOrigin(int propId, int& propId1, int& propId2, RefPtr<CSSValue>& value, RefPtr<CSSValue>& value2) |
| { |
| propId1 = propId; |
| propId2 = propId; |
| if (propId == CSSPropertyWebkitPerspectiveOrigin) { |
| propId1 = CSSPropertyWebkitPerspectiveOriginX; |
| propId2 = CSSPropertyWebkitPerspectiveOriginY; |
| } |
| |
| switch (propId) { |
| case CSSPropertyWebkitPerspectiveOrigin: |
| parseFillPosition(m_valueList, value, value2); |
| break; |
| case CSSPropertyWebkitPerspectiveOriginX: { |
| value = parseFillPositionX(m_valueList); |
| if (value) |
| m_valueList->next(); |
| break; |
| } |
| case CSSPropertyWebkitPerspectiveOriginY: { |
| value = parseFillPositionY(m_valueList); |
| if (value) |
| m_valueList->next(); |
| break; |
| } |
| } |
| |
| return value; |
| } |
| |
| bool CSSParser::parseTextEmphasisStyle(bool important) |
| { |
| unsigned valueListSize = m_valueList->size(); |
| |
| RefPtr<CSSPrimitiveValue> fill; |
| RefPtr<CSSPrimitiveValue> shape; |
| |
| for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { |
| if (value->unit == CSSPrimitiveValue::CSS_STRING) { |
| if (fill || shape || (valueListSize != 1 && !inShorthand())) |
| return false; |
| addProperty(CSSPropertyWebkitTextEmphasisStyle, primitiveValueCache()->createValue(value->string, CSSPrimitiveValue::CSS_STRING), important); |
| m_valueList->next(); |
| return true; |
| } |
| |
| if (value->id == CSSValueNone) { |
| if (fill || shape || (valueListSize != 1 && !inShorthand())) |
| return false; |
| addProperty(CSSPropertyWebkitTextEmphasisStyle, primitiveValueCache()->createIdentifierValue(CSSValueNone), important); |
| m_valueList->next(); |
| return true; |
| } |
| |
| if (value->id == CSSValueOpen || value->id == CSSValueFilled) { |
| if (fill) |
| return false; |
| fill = primitiveValueCache()->createIdentifierValue(value->id); |
| } else if (value->id == CSSValueDot || value->id == CSSValueCircle || value->id == CSSValueDoubleCircle || value->id == CSSValueTriangle || value->id == CSSValueSesame) { |
| if (shape) |
| return false; |
| shape = primitiveValueCache()->createIdentifierValue(value->id); |
| } else if (!inShorthand()) |
| return false; |
| else |
| break; |
| } |
| |
| if (fill && shape) { |
| RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated(); |
| parsedValues->append(fill.release()); |
| parsedValues->append(shape.release()); |
| addProperty(CSSPropertyWebkitTextEmphasisStyle, parsedValues.release(), important); |
| return true; |
| } |
| if (fill) { |
| addProperty(CSSPropertyWebkitTextEmphasisStyle, fill.release(), important); |
| return true; |
| } |
| if (shape) { |
| addProperty(CSSPropertyWebkitTextEmphasisStyle, shape.release(), important); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool CSSParser::parseLineBoxContain(bool important) |
| { |
| LineBoxContain lineBoxContain = LineBoxContainNone; |
| |
| for (CSSParserValue* value = m_valueList->current(); value; value = m_valueList->next()) { |
| if (value->id == CSSValueBlock) { |
| if (lineBoxContain & LineBoxContainBlock) |
| return false; |
| lineBoxContain |= LineBoxContainBlock; |
| } else if (value->id == CSSValueInline) { |
| if (lineBoxContain & LineBoxContainInline) |
| return false; |
| lineBoxContain |= LineBoxContainInline; |
| } else if (value->id == CSSValueFont) { |
| if (lineBoxContain & LineBoxContainFont) |
| return false; |
| lineBoxContain |= LineBoxContainFont; |
| } else if (value->id == CSSValueGlyphs) { |
| if (lineBoxContain & LineBoxContainGlyphs) |
| return false; |
| lineBoxContain |= LineBoxContainGlyphs; |
| } else if (value->id == CSSValueReplaced) { |
| if (lineBoxContain & LineBoxContainReplaced) |
| return false; |
| lineBoxContain |= LineBoxContainReplaced; |
| } else if (value->id == CSSValueInlineBox) { |
| if (lineBoxContain & LineBoxContainInlineBox) |
| return false; |
| lineBoxContain |= LineBoxContainInlineBox; |
| } else |
| return false; |
| } |
| |
| if (!lineBoxContain) |
| return false; |
| |
| addProperty(CSSPropertyWebkitLineBoxContain, CSSLineBoxContainValue::create(lineBoxContain), important); |
| return true; |
| } |
| |
| static inline int yyerror(const char*) { return 1; } |
| |
| #define END_TOKEN 0 |
| |
| #include "CSSGrammar.h" |
| |
| int CSSParser::lex(void* yylvalWithoutType) |
| { |
| YYSTYPE* yylval = static_cast<YYSTYPE*>(yylvalWithoutType); |
| int length; |
| |
| lex(); |
| |
| UChar* t = text(&length); |
| |
| switch (token()) { |
| case WHITESPACE: |
| case SGML_CD: |
| case INCLUDES: |
| case DASHMATCH: |
| break; |
| |
| case URI: |
| case STRING: |
| case IDENT: |
| case NTH: |
| case HEX: |
| case IDSEL: |
| case DIMEN: |
| case UNICODERANGE: |
| case FUNCTION: |
| case ANYFUNCTION: |
| case NOTFUNCTION: |
| yylval->string.characters = t; |
| yylval->string.length = length; |
| break; |
| |
| case IMPORT_SYM: |
| case PAGE_SYM: |
| case MEDIA_SYM: |
| case FONT_FACE_SYM: |
| case CHARSET_SYM: |
| case NAMESPACE_SYM: |
| case WEBKIT_KEYFRAMES_SYM: |
| |
| case IMPORTANT_SYM: |
| break; |
| |
| case QEMS: |
| length--; |
| case GRADS: |
| case TURNS: |
| length--; |
| case DEGS: |
| case RADS: |
| case KHERTZ: |
| case REMS: |
| length--; |
| case MSECS: |
| case HERTZ: |
| case EMS: |
| case EXS: |
| case PXS: |
| case CMS: |
| case MMS: |
| case INS: |
| case PTS: |
| case PCS: |
| length--; |
| case SECS: |
| case PERCENTAGE: |
| length--; |
| case FLOATTOKEN: |
| case INTEGER: |
| yylval->number = charactersToDouble(t, length); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return token(); |
| } |
| |
| void CSSParser::recheckAtKeyword(const UChar* str, int len) |
| { |
| String ruleName(str, len); |
| if (equalIgnoringCase(ruleName, "@import")) |
| yyTok = IMPORT_SYM; |
| else if (equalIgnoringCase(ruleName, "@page")) |
| yyTok = PAGE_SYM; |
| else if (equalIgnoringCase(ruleName, "@media")) |
| yyTok = MEDIA_SYM; |
| else if (equalIgnoringCase(ruleName, "@font-face")) |
| yyTok = FONT_FACE_SYM; |
| else if (equalIgnoringCase(ruleName, "@charset")) |
| yyTok = CHARSET_SYM; |
| else if (equalIgnoringCase(ruleName, "@namespace")) |
| yyTok = NAMESPACE_SYM; |
| else if (equalIgnoringCase(ruleName, "@-webkit-keyframes")) |
| yyTok = WEBKIT_KEYFRAMES_SYM; |
| else if (equalIgnoringCase(ruleName, "@-webkit-mediaquery")) |
| yyTok = WEBKIT_MEDIAQUERY_SYM; |
| } |
| |
| UChar* CSSParser::text(int *length) |
| { |
| UChar* start = yytext; |
| int l = yyleng; |
| switch (yyTok) { |
| case STRING: |
| l--; |
| /* nobreak */ |
| case HEX: |
| case IDSEL: |
| start++; |
| l--; |
| break; |
| case URI: |
| // "url("{w}{string}{w}")" |
| // "url("{w}{url}{w}")" |
| // strip "url(" and ")" |
| start += 4; |
| l -= 5; |
| // strip {w} |
| while (l && isHTMLSpace(*start)) { |
| ++start; |
| --l; |
| } |
| while (l && isHTMLSpace(start[l - 1])) |
| --l; |
| if (l && (*start == '"' || *start == '\'')) { |
| ASSERT(l >= 2 && start[l - 1] == *start); |
| ++start; |
| l -= 2; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| // process escapes |
| UChar* out = start; |
| UChar* escape = 0; |
| |
| bool sawEscape = false; |
| |
| for (int i = 0; i < l; i++) { |
| UChar* current = start + i; |
| if (escape == current - 1) { |
| if (isASCIIHexDigit(*current)) |
| continue; |
| if (yyTok == STRING && |
| (*current == '\n' || *current == '\r' || *current == '\f')) { |
| // ### handle \r\n case |
| if (*current != '\r') |
| escape = 0; |
| continue; |
| } |
| // in all other cases copy the char to output |
| // ### |
| *out++ = *current; |
| escape = 0; |
| continue; |
| } |
| if (escape == current - 2 && yyTok == STRING && |
| *(current-1) == '\r' && *current == '\n') { |
| escape = 0; |
| continue; |
| } |
| if (escape > current - 7 && isASCIIHexDigit(*current)) |
| continue; |
| if (escape) { |
| // add escaped char |
| unsigned uc = 0; |
| escape++; |
| while (escape < current) { |
| uc *= 16; |
| uc += toASCIIHexValue(*escape); |
| escape++; |
| } |
| // can't handle chars outside ucs2 |
| if (uc > 0xffff) |
| uc = 0xfffd; |
| *out++ = uc; |
| escape = 0; |
| if (isHTMLSpace(*current)) |
| continue; |
| } |
| if (!escape && *current == '\\') { |
| escape = current; |
| sawEscape = true; |
| continue; |
| } |
| *out++ = *current; |
| } |
| if (escape) { |
| // add escaped char |
| unsigned uc = 0; |
| escape++; |
| while (escape < start+l) { |
| uc *= 16; |
| uc += toASCIIHexValue(*escape); |
| escape++; |
| } |
| // can't handle chars outside ucs2 |
| if (uc > 0xffff) |
| uc = 0xfffd; |
| *out++ = uc; |
| } |
| |
| *length = out - start; |
| |
| // If we have an unrecognized @-keyword, and if we handled any escapes at all, then |
| // we should attempt to adjust yyTok to the correct type. |
| if (yyTok == ATKEYWORD && sawEscape) |
| recheckAtKeyword(start, *length); |
| |
| return start; |
| } |
| |
| void CSSParser::countLines() |
| { |
| for (UChar* current = yytext; current < yytext + yyleng; ++current) { |
| if (*current == '\n') |
| ++m_lineNumber; |
| } |
| } |
| |
| CSSParserSelector* CSSParser::createFloatingSelector() |
| { |
| CSSParserSelector* selector = new CSSParserSelector; |
| m_floatingSelectors.add(selector); |
| return selector; |
| } |
| |
| PassOwnPtr<CSSParserSelector> CSSParser::sinkFloatingSelector(CSSParserSelector* selector) |
| { |
| if (selector) { |
| ASSERT(m_floatingSelectors.contains(selector)); |
| m_floatingSelectors.remove(selector); |
| } |
| return adoptPtr(selector); |
| } |
| |
| Vector<OwnPtr<CSSParserSelector> >* CSSParser::createFloatingSelectorVector() |
| { |
| Vector<OwnPtr<CSSParserSelector> >* selectorVector = new Vector<OwnPtr<CSSParserSelector> >; |
| m_floatingSelectorVectors.add(selectorVector); |
| return selectorVector; |
| } |
| |
| PassOwnPtr<Vector<OwnPtr<CSSParserSelector> > > CSSParser::sinkFloatingSelectorVector(Vector<OwnPtr<CSSParserSelector> >* selectorVector) |
| { |
| if (selectorVector) { |
| ASSERT(m_floatingSelectorVectors.contains(selectorVector)); |
| m_floatingSelectorVectors.remove(selectorVector); |
| } |
| return adoptPtr(selectorVector); |
| } |
| |
| CSSParserValueList* CSSParser::createFloatingValueList() |
| { |
| CSSParserValueList* list = new CSSParserValueList; |
| m_floatingValueLists.add(list); |
| return list; |
| } |
| |
| CSSParserValueList* CSSParser::sinkFloatingValueList(CSSParserValueList* list) |
| { |
| if (list) { |
| ASSERT(m_floatingValueLists.contains(list)); |
| m_floatingValueLists.remove(list); |
| } |
| return list; |
| } |
| |
| CSSParserFunction* CSSParser::createFloatingFunction() |
| { |
| CSSParserFunction* function = new CSSParserFunction; |
| m_floatingFunctions.add(function); |
| return function; |
| } |
| |
| CSSParserFunction* CSSParser::sinkFloatingFunction(CSSParserFunction* function) |
| { |
| if (function) { |
| ASSERT(m_floatingFunctions.contains(function)); |
| m_floatingFunctions.remove(function); |
| } |
| return function; |
| } |
| |
| CSSParserValue& CSSParser::sinkFloatingValue(CSSParserValue& value) |
| { |
| if (value.unit == CSSParserValue::Function) { |
| ASSERT(m_floatingFunctions.contains(value.function)); |
| m_floatingFunctions.remove(value.function); |
| } |
| return value; |
| } |
| |
| MediaQueryExp* CSSParser::createFloatingMediaQueryExp(const AtomicString& mediaFeature, CSSParserValueList* values) |
| { |
| m_floatingMediaQueryExp = MediaQueryExp::create(mediaFeature, values); |
| return m_floatingMediaQueryExp.get(); |
| } |
| |
| PassOwnPtr<MediaQueryExp> CSSParser::sinkFloatingMediaQueryExp(MediaQueryExp* expression) |
| { |
| ASSERT_UNUSED(expression, expression == m_floatingMediaQueryExp); |
| return m_floatingMediaQueryExp.release(); |
| } |
| |
| Vector<OwnPtr<MediaQueryExp> >* CSSParser::createFloatingMediaQueryExpList() |
| { |
| m_floatingMediaQueryExpList = adoptPtr(new Vector<OwnPtr<MediaQueryExp> >); |
| return m_floatingMediaQueryExpList.get(); |
| } |
| |
| PassOwnPtr<Vector<OwnPtr<MediaQueryExp> > > CSSParser::sinkFloatingMediaQueryExpList(Vector<OwnPtr<MediaQueryExp> >* list) |
| { |
| ASSERT_UNUSED(list, list == m_floatingMediaQueryExpList); |
| return m_floatingMediaQueryExpList.release(); |
| } |
| |
| MediaQuery* CSSParser::createFloatingMediaQuery(MediaQuery::Restrictor restrictor, const String& mediaType, PassOwnPtr<Vector<OwnPtr<MediaQueryExp> > > expressions) |
| { |
| m_floatingMediaQuery = adoptPtr(new MediaQuery(restrictor, mediaType, expressions)); |
| return m_floatingMediaQuery.get(); |
| } |
| |
| MediaQuery* CSSParser::createFloatingMediaQuery(PassOwnPtr<Vector<OwnPtr<MediaQueryExp> > > expressions) |
| { |
| return createFloatingMediaQuery(MediaQuery::None, "all", expressions); |
| } |
| |
| PassOwnPtr<MediaQuery> CSSParser::sinkFloatingMediaQuery(MediaQuery* query) |
| { |
| ASSERT_UNUSED(query, query == m_floatingMediaQuery); |
| return m_floatingMediaQuery.release(); |
| } |
| |
| MediaList* CSSParser::createMediaList() |
| { |
| RefPtr<MediaList> list = MediaList::create(); |
| MediaList* result = list.get(); |
| m_parsedStyleObjects.append(list.release()); |
| return result; |
| } |
| |
| CSSRule* CSSParser::createCharsetRule(const CSSParserString& charset) |
| { |
| if (!m_styleSheet) |
| return 0; |
| RefPtr<CSSCharsetRule> rule = CSSCharsetRule::create(m_styleSheet, charset); |
| CSSCharsetRule* result = rule.get(); |
| m_parsedStyleObjects.append(rule.release()); |
| return result; |
| } |
| |
| CSSRule* CSSParser::createImportRule(const CSSParserString& url, MediaList* media) |
| { |
| if (!media || !m_styleSheet || !m_allowImportRules) |
| return 0; |
| RefPtr<CSSImportRule> rule = CSSImportRule::create(m_styleSheet, url, media); |
| CSSImportRule* result = rule.get(); |
| m_parsedStyleObjects.append(rule.release()); |
| return result; |
| } |
| |
| CSSRule* CSSParser::createMediaRule(MediaList* media, CSSRuleList* rules) |
| { |
| if (!media || !rules || !m_styleSheet) |
| return 0; |
| m_allowImportRules = m_allowNamespaceDeclarations = false; |
| RefPtr<CSSMediaRule> rule = CSSMediaRule::create(m_styleSheet, media, rules); |
| CSSMediaRule* result = rule.get(); |
| m_parsedStyleObjects.append(rule.release()); |
| return result; |
| } |
| |
| CSSRuleList* CSSParser::createRuleList() |
| { |
| RefPtr<CSSRuleList> list = CSSRuleList::create(); |
| CSSRuleList* listPtr = list.get(); |
| |
| m_parsedRuleLists.append(list.release()); |
| return listPtr; |
| } |
| |
| WebKitCSSKeyframesRule* CSSParser::createKeyframesRule() |
| { |
| m_allowImportRules = m_allowNamespaceDeclarations = false; |
| RefPtr<WebKitCSSKeyframesRule> rule = WebKitCSSKeyframesRule::create(m_styleSheet); |
| WebKitCSSKeyframesRule* rulePtr = rule.get(); |
| m_parsedStyleObjects.append(rule.release()); |
| return rulePtr; |
| } |
| |
| CSSRule* CSSParser::createStyleRule(Vector<OwnPtr<CSSParserSelector> >* selectors) |
| { |
| CSSStyleRule* result = 0; |
| markRuleBodyEnd(); |
| if (selectors) { |
| m_allowImportRules = m_allowNamespaceDeclarations = false; |
| RefPtr<CSSStyleRule> rule = CSSStyleRule::create(m_styleSheet, m_lastSelectorLineNumber); |
| rule->adoptSelectorVector(*selectors); |
| if (m_hasFontFaceOnlyValues) |
| deleteFontFaceOnlyValues(); |
| rule->setDeclaration(CSSMutableStyleDeclaration::create(rule.get(), m_parsedProperties, m_numParsedProperties)); |
| result = rule.get(); |
| m_parsedStyleObjects.append(rule.release()); |
| if (m_ruleRangeMap) { |
| ASSERT(m_currentRuleData); |
| m_currentRuleData->styleSourceData->styleBodyRange = m_ruleBodyRange; |
| m_currentRuleData->selectorListRange = m_selectorListRange; |
| m_ruleRangeMap->set(result, m_currentRuleData.release()); |
| m_currentRuleData = CSSRuleSourceData::create(); |
| m_currentRuleData->styleSourceData = CSSStyleSourceData::create(); |
| m_inStyleRuleOrDeclaration = false; |
| } |
| } |
| resetSelectorListMarks(); |
| resetRuleBodyMarks(); |
| clearProperties(); |
| return result; |
| } |
| |
| CSSRule* CSSParser::createFontFaceRule() |
| { |
| m_allowImportRules = m_allowNamespaceDeclarations = false; |
| for (unsigned i = 0; i < m_numParsedProperties; ++i) { |
| CSSProperty* property = m_parsedProperties[i]; |
| int id = property->id(); |
| if ((id == CSSPropertyFontWeight || id == CSSPropertyFontStyle || id == CSSPropertyFontVariant) && property->value()->isPrimitiveValue()) { |
| RefPtr<CSSValue> value = property->m_value.release(); |
| property->m_value = CSSValueList::createCommaSeparated(); |
| static_cast<CSSValueList*>(property->m_value.get())->append(value.release()); |
| } else if (id == CSSPropertyFontFamily && static_cast<CSSValueList*>(property->m_value.get())->length() != 1) { |
| // Unlike font-family property, font-family descriptor in @font-face rule can take only one family name. |
| // See http://dev.w3.org/csswg/css3-fonts/#font-family-desc |
| clearProperties(); |
| return 0; |
| } |
| } |
| RefPtr<CSSFontFaceRule> rule = CSSFontFaceRule::create(m_styleSheet); |
| rule->setDeclaration(CSSMutableStyleDeclaration::create(rule.get(), m_parsedProperties, m_numParsedProperties)); |
| clearProperties(); |
| CSSFontFaceRule* result = rule.get(); |
| m_parsedStyleObjects.append(rule.release()); |
| return result; |
| } |
| |
| void CSSParser::addNamespace(const AtomicString& prefix, const AtomicString& uri) |
| { |
| if (!m_styleSheet || !m_allowNamespaceDeclarations) |
| return; |
| m_allowImportRules = false; |
| m_styleSheet->addNamespace(this, prefix, uri); |
| } |
| |
| void CSSParser::updateSpecifiersWithElementName(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector* specifiers) |
| { |
| AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleSheet ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace; |
| QualifiedName tag = QualifiedName(namespacePrefix, elementName, determinedNamespace); |
| if (!specifiers->isUnknownPseudoElement()) { |
| specifiers->setTag(tag); |
| return; |
| } |
| |
| specifiers->setRelation(CSSSelector::ShadowDescendant); |
| if (CSSParserSelector* history = specifiers->tagHistory()) { |
| history->setTag(tag); |
| return; |
| } |
| |
| // No need to create an extra element name selector if we are matching any element |
| // in any namespace. |
| if (elementName == starAtom && m_defaultNamespace == starAtom) |
| return; |
| |
| CSSParserSelector* elementNameSelector = new CSSParserSelector; |
| elementNameSelector->setTag(tag); |
| specifiers->setTagHistory(elementNameSelector); |
| } |
| |
| |
| CSSRule* CSSParser::createPageRule(PassOwnPtr<CSSParserSelector> pageSelector) |
| { |
| // FIXME: Margin at-rules are ignored. |
| m_allowImportRules = m_allowNamespaceDeclarations = false; |
| CSSPageRule* pageRule = 0; |
| if (pageSelector) { |
| RefPtr<CSSPageRule> rule = CSSPageRule::create(m_styleSheet, m_lastSelectorLineNumber); |
| Vector<OwnPtr<CSSParserSelector> > selectorVector; |
| selectorVector.append(pageSelector); |
| rule->adoptSelectorVector(selectorVector); |
| rule->setDeclaration(CSSMutableStyleDeclaration::create(rule.get(), m_parsedProperties, m_numParsedProperties)); |
| pageRule = rule.get(); |
| m_parsedStyleObjects.append(rule.release()); |
| } |
| clearProperties(); |
| return pageRule; |
| } |
| |
| CSSRule* CSSParser::createMarginAtRule(CSSSelector::MarginBoxType /* marginBox */) |
| { |
| // FIXME: Implement margin at-rule here, using: |
| // - marginBox: margin box |
| // - m_parsedProperties: properties at [m_numParsedPropertiesBeforeMarginBox, m_numParsedProperties) are for this at-rule. |
| // Don't forget to also update the action for page symbol in CSSGrammar.y such that margin at-rule data is cleared if page_selector is invalid. |
| |
| endDeclarationsForMarginBox(); |
| return 0; // until this method is implemented. |
| } |
| |
| void CSSParser::startDeclarationsForMarginBox() |
| { |
| m_numParsedPropertiesBeforeMarginBox = m_numParsedProperties; |
| } |
| |
| void CSSParser::endDeclarationsForMarginBox() |
| { |
| ASSERT(m_numParsedPropertiesBeforeMarginBox != INVALID_NUM_PARSED_PROPERTIES); |
| rollbackLastProperties(m_numParsedProperties - m_numParsedPropertiesBeforeMarginBox); |
| m_numParsedPropertiesBeforeMarginBox = INVALID_NUM_PARSED_PROPERTIES; |
| } |
| |
| void CSSParser::deleteFontFaceOnlyValues() |
| { |
| ASSERT(m_hasFontFaceOnlyValues); |
| int deletedProperties = 0; |
| |
| for (unsigned i = 0; i < m_numParsedProperties; ++i) { |
| CSSProperty* property = m_parsedProperties[i]; |
| int id = property->id(); |
| if ((id == CSSPropertyFontWeight || id == CSSPropertyFontStyle || id == CSSPropertyFontVariant) && property->value()->isValueList()) { |
| delete property; |
| deletedProperties++; |
| } else if (deletedProperties) |
| m_parsedProperties[i - deletedProperties] = m_parsedProperties[i]; |
| } |
| |
| m_numParsedProperties -= deletedProperties; |
| } |
| |
| WebKitCSSKeyframeRule* CSSParser::createKeyframeRule(CSSParserValueList* keys) |
| { |
| // Create a key string from the passed keys |
| String keyString; |
| for (unsigned i = 0; i < keys->size(); ++i) { |
| float key = (float) keys->valueAt(i)->fValue; |
| if (i != 0) |
| keyString += ","; |
| keyString += String::number(key); |
| keyString += "%"; |
| } |
| |
| RefPtr<WebKitCSSKeyframeRule> keyframe = WebKitCSSKeyframeRule::create(m_styleSheet); |
| keyframe->setKeyText(keyString); |
| keyframe->setDeclaration(CSSMutableStyleDeclaration::create(0, m_parsedProperties, m_numParsedProperties)); |
| |
| clearProperties(); |
| |
| WebKitCSSKeyframeRule* keyframePtr = keyframe.get(); |
| m_parsedStyleObjects.append(keyframe.release()); |
| return keyframePtr; |
| } |
| |
| void CSSParser::invalidBlockHit() |
| { |
| if (m_styleSheet && !m_hadSyntacticallyValidCSSRule) |
| m_styleSheet->setHasSyntacticallyValidCSSHeader(false); |
| } |
| |
| void CSSParser::updateLastSelectorLineAndPosition() |
| { |
| m_lastSelectorLineNumber = m_lineNumber; |
| markRuleBodyStart(); |
| } |
| |
| void CSSParser::markSelectorListStart() |
| { |
| m_selectorListRange.start = yytext - m_data; |
| } |
| |
| void CSSParser::markSelectorListEnd() |
| { |
| if (!m_currentRuleData) |
| return; |
| UChar* listEnd = yytext; |
| while (listEnd > m_data + 1) { |
| if (isHTMLSpace(*(listEnd - 1))) |
| --listEnd; |
| else |
| break; |
| } |
| m_selectorListRange.end = listEnd - m_data; |
| } |
| |
| void CSSParser::markRuleBodyStart() |
| { |
| unsigned offset = yytext - m_data; |
| if (*yytext == '{') |
| ++offset; // Skip the rule body opening brace. |
| if (offset > m_ruleBodyRange.start) |
| m_ruleBodyRange.start = offset; |
| m_inStyleRuleOrDeclaration = true; |
| } |
| |
| void CSSParser::markRuleBodyEnd() |
| { |
| unsigned offset = yytext - m_data; |
| if (offset > m_ruleBodyRange.end) |
| m_ruleBodyRange.end = offset; |
| } |
| |
| void CSSParser::markPropertyStart() |
| { |
| if (!m_inStyleRuleOrDeclaration) |
| return; |
| m_propertyRange.start = yytext - m_data; |
| } |
| |
| void CSSParser::markPropertyEnd(bool isImportantFound, bool isPropertyParsed) |
| { |
| if (!m_inStyleRuleOrDeclaration) |
| return; |
| unsigned offset = yytext - m_data; |
| if (*yytext == ';') // Include semicolon into the property text. |
| ++offset; |
| m_propertyRange.end = offset; |
| if (m_propertyRange.start != UINT_MAX && m_currentRuleData) { |
| // This stuff is only executed when the style data retrieval is requested by client. |
| const unsigned start = m_propertyRange.start; |
| const unsigned end = m_propertyRange.end; |
| ASSERT(start < end); |
| String propertyString = String(m_data + start, end - start).stripWhiteSpace(); |
| if (propertyString.endsWith(";", true)) |
| propertyString = propertyString.left(propertyString.length() - 1); |
| Vector<String> propertyComponents; |
| size_t colonIndex = propertyString.find(":"); |
| ASSERT(colonIndex != notFound); |
| |
| String name = propertyString.left(colonIndex).stripWhiteSpace(); |
| String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace(); |
| // The property range is relative to the declaration start offset. |
| m_currentRuleData->styleSourceData->propertyData.append( |
| CSSPropertySourceData(name, value, isImportantFound, isPropertyParsed, SourceRange(start - m_ruleBodyRange.start, end - m_ruleBodyRange.start))); |
| } |
| resetPropertyMarks(); |
| } |
| |
| static int cssPropertyID(const UChar* propertyName, unsigned length) |
| { |
| if (!length) |
| return 0; |
| if (length > maxCSSPropertyNameLength) |
| return 0; |
| |
| char buffer[maxCSSPropertyNameLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character |
| |
| for (unsigned i = 0; i != length; ++i) { |
| UChar c = propertyName[i]; |
| if (c == 0 || c >= 0x7F) |
| return 0; // illegal character |
| buffer[i] = toASCIILower(c); |
| } |
| buffer[length] = '\0'; |
| |
| const char* name = buffer; |
| if (buffer[0] == '-') { |
| // If the prefix is -apple- or -khtml-, change it to -webkit-. |
| // This makes the string one character longer. |
| if (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-")) { |
| memmove(buffer + 7, buffer + 6, length + 1 - 6); |
| memcpy(buffer, "-webkit", 7); |
| ++length; |
| } |
| |
| if (hasPrefix(buffer, length, "-webkit")) { |
| if (!strcmp(buffer, "-webkit-box-sizing")) { |
| // -webkit-box-sizing worked in Safari 4 and earlier. |
| const char* const boxSizing = "box-sizing"; |
| name = boxSizing; |
| length = strlen(boxSizing); |
| } else if (!strcmp(buffer, "-webkit-opacity")) { |
| // Honor -webkit-opacity as a synonym for opacity. |
| // This was the only syntax that worked in Safari 1.1, and may be in use on some websites and widgets. |
| const char* const opacity = "opacity"; |
| name = opacity; |
| length = strlen(opacity); |
| #if PLATFORM(IOS) |
| } else if (!strcmp(buffer, "-webkit-hyphenate-locale")) { |
| // Worked in iOS 4.2. |
| const char* const webkitLocale = "-webkit-locale"; |
| name = webkitLocale; |
| length = strlen(webkitLocale); |
| #endif |
| } else if (hasPrefix(buffer + 7, length - 7, "-border-")) { |
| // -webkit-border-*-*-radius worked in Safari 4 and earlier. -webkit-border-radius syntax |
| // differs from border-radius, so it is remains as a distinct property. |
| if (!strcmp(buffer + 15, "top-left-radius") |
| || !strcmp(buffer + 15, "top-right-radius") |
| || !strcmp(buffer + 15, "bottom-right-radius") |
| || !strcmp(buffer + 15, "bottom-left-radius")) { |
| name = buffer + 8; |
| length -= 8; |
| } |
| } |
| } |
| } |
| |
| const Property* hashTableEntry = findProperty(name, length); |
| return hashTableEntry ? hashTableEntry->id : 0; |
| } |
| |
| int cssPropertyID(const String& string) |
| { |
| return cssPropertyID(string.characters(), string.length()); |
| } |
| |
| int cssPropertyID(const CSSParserString& string) |
| { |
| return cssPropertyID(string.characters, string.length); |
| } |
| |
| int cssValueKeywordID(const CSSParserString& string) |
| { |
| unsigned length = string.length; |
| if (!length) |
| return 0; |
| if (length > maxCSSValueKeywordLength) |
| return 0; |
| |
| char buffer[maxCSSValueKeywordLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character |
| |
| for (unsigned i = 0; i != length; ++i) { |
| UChar c = string.characters[i]; |
| if (c == 0 || c >= 0x7F) |
| return 0; // illegal character |
| buffer[i] = WTF::toASCIILower(c); |
| } |
| buffer[length] = '\0'; |
| |
| if (buffer[0] == '-') { |
| // If the prefix is -apple- or -khtml-, change it to -webkit-. |
| // This makes the string one character longer. |
| if (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-")) { |
| memmove(buffer + 7, buffer + 6, length + 1 - 6); |
| memcpy(buffer, "-webkit", 7); |
| ++length; |
| } |
| } |
| |
| const Value* hashTableEntry = findValue(buffer, length); |
| return hashTableEntry ? hashTableEntry->id : 0; |
| } |
| |
| // "ident" from the CSS tokenizer, minus backslash-escape sequences |
| static bool isCSSTokenizerIdentifier(const String& string) |
| { |
| const UChar* p = string.characters(); |
| const UChar* end = p + string.length(); |
| |
| // -? |
| if (p != end && p[0] == '-') |
| ++p; |
| |
| // {nmstart} |
| if (p == end || !(p[0] == '_' || p[0] >= 128 || isASCIIAlpha(p[0]))) |
| return false; |
| ++p; |
| |
| // {nmchar}* |
| for (; p != end; ++p) { |
| if (!(p[0] == '_' || p[0] == '-' || p[0] >= 128 || isASCIIAlphanumeric(p[0]))) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // "url" from the CSS tokenizer, minus backslash-escape sequences |
| static bool isCSSTokenizerURL(const String& string) |
| { |
| const UChar* p = string.characters(); |
| const UChar* end = p + string.length(); |
| |
| for (; p != end; ++p) { |
| UChar c = p[0]; |
| switch (c) { |
| case '!': |
| case '#': |
| case '$': |
| case '%': |
| case '&': |
| break; |
| default: |
| if (c < '*') |
| return false; |
| if (c <= '~') |
| break; |
| if (c < 128) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // We use single quotes for now because markup.cpp uses double quotes. |
| String quoteCSSString(const String& string) |
| { |
| // For efficiency, we first pre-calculate the length of the quoted string, then we build the actual one. |
| // Please see below for the actual logic. |
| unsigned quotedStringSize = 2; // Two quotes surrounding the entire string. |
| bool afterEscape = false; |
| for (unsigned i = 0; i < string.length(); ++i) { |
| UChar ch = string[i]; |
| if (ch == '\\' || ch == '\'') { |
| quotedStringSize += 2; |
| afterEscape = false; |
| } else if (ch < 0x20 || ch == 0x7F) { |
| quotedStringSize += 2 + (ch >= 0x10); |
| afterEscape = true; |
| } else { |
| quotedStringSize += 1 + (afterEscape && (isASCIIHexDigit(ch) || ch == ' ')); |
| afterEscape = false; |
| } |
| } |
| |
| StringBuffer buffer(quotedStringSize); |
| unsigned index = 0; |
| buffer[index++] = '\''; |
| afterEscape = false; |
| for (unsigned i = 0; i < string.length(); ++i) { |
| UChar ch = string[i]; |
| if (ch == '\\' || ch == '\'') { |
| buffer[index++] = '\\'; |
| buffer[index++] = ch; |
| afterEscape = false; |
| } else if (ch < 0x20 || ch == 0x7F) { // Control characters. |
| buffer[index++] = '\\'; |
| placeByteAsHexCompressIfPossible(ch, buffer, index, Lowercase); |
| afterEscape = true; |
| } else { |
| // Space character may be required to separate backslash-escape sequence and normal characters. |
| if (afterEscape && (isASCIIHexDigit(ch) || ch == ' ')) |
| buffer[index++] = ' '; |
| buffer[index++] = ch; |
| afterEscape = false; |
| } |
| } |
| buffer[index++] = '\''; |
| |
| ASSERT(quotedStringSize == index); |
| return String::adopt(buffer); |
| } |
| |
| String quoteCSSStringIfNeeded(const String& string) |
| { |
| return isCSSTokenizerIdentifier(string) ? string : quoteCSSString(string); |
| } |
| |
| String quoteCSSURLIfNeeded(const String& string) |
| { |
| return isCSSTokenizerURL(string) ? string : quoteCSSString(string); |
| } |
| |
| bool isValidNthToken(const CSSParserString& token) |
| { |
| // The tokenizer checks for the construct of an+b. |
| // nth can also accept "odd" or "even" but should not accept any other token. |
| return equalIgnoringCase(token, "odd") || equalIgnoringCase(token, "even"); |
| } |
| |
| #define YY_DECL int CSSParser::lex() |
| #define yyconst const |
| typedef int yy_state_type; |
| typedef unsigned YY_CHAR; |
| // The following line makes sure we treat non-Latin-1 Unicode characters correctly. |
| #define YY_SC_TO_UI(c) (c > 0xff ? 0xff : c) |
| #define YY_DO_BEFORE_ACTION \ |
| yytext = yy_bp; \ |
| yyleng = (int) (yy_cp - yy_bp); \ |
| yy_hold_char = *yy_cp; \ |
| *yy_cp = 0; \ |
| yy_c_buf_p = yy_cp; |
| #define YY_BREAK break; |
| #define ECHO |
| #define YY_RULE_SETUP |
| #define INITIAL 0 |
| #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) |
| #define yyterminate() yyTok = END_TOKEN; return yyTok |
| #define YY_FATAL_ERROR(a) |
| // The following line is needed to build the tokenizer with a condition stack. |
| // The macro is used in the tokenizer grammar with lines containing |
| // BEGIN(mediaqueries) and BEGIN(initial). yy_start acts as index to |
| // tokenizer transition table, and 'mediaqueries' and 'initial' are |
| // offset multipliers that specify which transitions are active |
| // in the tokenizer during in each condition (tokenizer state). |
| #define BEGIN yy_start = 1 + 2 * |
| |
| #include "tokenizer.cpp" |
| |
| } |