/* | |
* 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 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 "CString.h" | |
#include "CSSTimingFunctionValue.h" | |
#include "CSSBorderImageValue.h" | |
#include "CSSCanvasValue.h" | |
#include "CSSCharsetRule.h" | |
#include "CSSCursorImageValue.h" | |
#include "CSSHelper.h" | |
#include "CSSImageValue.h" | |
#include "CSSFontFaceRule.h" | |
#include "CSSFontFaceSrcValue.h" | |
#include "CSSGradientValue.h" | |
#include "CSSImportRule.h" | |
#include "CSSInheritedValue.h" | |
#include "CSSInitialValue.h" | |
#include "CSSMediaRule.h" | |
#include "CSSMutableStyleDeclaration.h" | |
#include "CSSPrimitiveValue.h" | |
#include "CSSProperty.h" | |
#include "CSSPropertyNames.h" | |
#include "CSSQuirkPrimitiveValue.h" | |
#include "CSSReflectValue.h" | |
#include "CSSRuleList.h" | |
#include "CSSSelector.h" | |
#include "CSSStyleRule.h" | |
#include "CSSStyleSheet.h" | |
#include "CSSUnicodeRangeValue.h" | |
#include "CSSValueKeywords.h" | |
#include "CSSValueList.h" | |
#include "CSSVariableDependentValue.h" | |
#include "CSSVariablesDeclaration.h" | |
#include "CSSVariablesRule.h" | |
#include "Counter.h" | |
#include "Document.h" | |
#include "FloatConversion.h" | |
#include "FontFamilyValue.h" | |
#include "FontValue.h" | |
#include "MediaList.h" | |
#include "MediaQueryExp.h" | |
#include "Pair.h" | |
#include "Rect.h" | |
#include "ShadowValue.h" | |
#include "WebKitCSSKeyframeRule.h" | |
#include "WebKitCSSKeyframesRule.h" | |
#include "WebKitCSSTransformValue.h" | |
#include <wtf/dtoa.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; | |
#include "CSSPropertyNames.cpp" | |
#include "CSSValueKeywords.c" | |
namespace WebCore { | |
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_mediaQuery(0) | |
, m_valueList(0) | |
, m_parsedProperties(static_cast<CSSProperty**>(fastMalloc(32 * sizeof(CSSProperty*)))) | |
, m_numParsedProperties(0) | |
, m_maxParsedProperties(32) | |
, m_inParseShorthand(0) | |
, m_currentShorthand(0) | |
, m_implicitShorthand(false) | |
, m_hasFontFaceOnlyValues(false) | |
, m_hadSyntacticallyValidCSSRule(false) | |
, m_defaultNamespace(starAtom) | |
, m_data(0) | |
, yy_start(1) | |
, m_line(0) | |
, m_lastSelectorLine(0) | |
, m_allowImportRules(true) | |
, m_allowVariablesRules(true) | |
, m_allowNamespaceDeclarations(true) | |
, m_floatingMediaQuery(0) | |
, m_floatingMediaQueryExp(0) | |
, m_floatingMediaQueryExpList(0) | |
{ | |
#if YYDEBUG > 0 | |
cssyydebug = 1; | |
#endif | |
} | |
CSSParser::~CSSParser() | |
{ | |
clearProperties(); | |
fastFree(m_parsedProperties); | |
clearVariables(); | |
delete m_valueList; | |
fastFree(m_data); | |
if (m_floatingMediaQueryExpList) { | |
deleteAllValues(*m_floatingMediaQueryExpList); | |
delete m_floatingMediaQueryExpList; | |
} | |
delete m_floatingMediaQueryExp; | |
delete m_floatingMediaQuery; | |
fastDeleteAllValues(m_floatingSelectors); | |
deleteAllValues(m_floatingValueLists); | |
deleteAllValues(m_floatingFunctions); | |
deleteAllValues(m_reusableSelectorVector); | |
} | |
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; | |
} | |
void CSSParser::parseSheet(CSSStyleSheet* sheet, const String& string) | |
{ | |
m_styleSheet = sheet; | |
m_defaultNamespace = starAtom; // Reset the default namespace. | |
setupParser("", string, ""); | |
cssyyparse(this); | |
m_rule = 0; | |
} | |
PassRefPtr<CSSRule> CSSParser::parseRule(CSSStyleSheet* sheet, const String& string) | |
{ | |
m_styleSheet = sheet; | |
m_allowNamespaceDeclarations = false; | |
setupParser("@-webkit-rule{", string, "} "); | |
cssyyparse(this); | |
return m_rule.release(); | |
} | |
PassRefPtr<CSSRule> CSSParser::parseKeyframeRule(CSSStyleSheet *sheet, const String &string) | |
{ | |
m_styleSheet = sheet; | |
setupParser("@-webkit-keyframe-rule{ ", string, "} "); | |
cssyyparse(this); | |
return m_keyframe.release(); | |
} | |
bool CSSParser::parseValue(CSSMutableStyleDeclaration* declaration, int id, const String& string, bool important) | |
{ | |
ASSERT(!declaration->stylesheet() || declaration->stylesheet()->isCSSStyleSheet()); | |
m_styleSheet = static_cast<CSSStyleSheet*>(declaration->stylesheet()); | |
setupParser("@-webkit-value{", string, "} "); | |
m_id = id; | |
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(); | |
} | |
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) | |
{ | |
CSSParser parser(true); | |
// First try creating a color specified by name or the "#" syntax. | |
if (!parser.parseColor(string, color, strict)) { | |
RefPtr<CSSMutableStyleDeclaration> dummyStyleDeclaration = CSSMutableStyleDeclaration::create(); | |
// Now try to create a color from the rgb() or rgba() syntax. | |
if (parser.parseColor(dummyStyleDeclaration.get(), string)) { | |
CSSValue* value = parser.m_parsedProperties[0]->value(); | |
if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) { | |
CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value); | |
color = primitiveValue->getRGBA32Value(); | |
} | |
} else | |
return false; | |
} | |
return true; | |
} | |
bool CSSParser::parseColor(CSSMutableStyleDeclaration* declaration, const String& string) | |
{ | |
ASSERT(!declaration->stylesheet() || declaration->stylesheet()->isCSSStyleSheet()); | |
m_styleSheet = static_cast<CSSStyleSheet*>(declaration->stylesheet()); | |
setupParser("@-webkit-decls{color:", string, "} "); | |
cssyyparse(this); | |
m_rule = 0; | |
return (m_numParsedProperties && m_parsedProperties[0]->m_id == CSSPropertyColor); | |
} | |
void CSSParser::parseSelector(const String& string, Document* doc, CSSSelectorList& selectorList) | |
{ | |
RefPtr<CSSStyleSheet> dummyStyleSheet = CSSStyleSheet::create(doc); | |
m_styleSheet = dummyStyleSheet.get(); | |
m_selectorListForParseSelector = &selectorList; | |
setupParser("@-webkit-selector{", string, "}"); | |
cssyyparse(this); | |
m_selectorListForParseSelector = 0; | |
} | |
bool CSSParser::parseDeclaration(CSSMutableStyleDeclaration* declaration, const String& string) | |
{ | |
ASSERT(!declaration->stylesheet() || declaration->stylesheet()->isCSSStyleSheet()); | |
m_styleSheet = static_cast<CSSStyleSheet*>(declaration->stylesheet()); | |
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(); | |
} | |
return ok; | |
} | |
bool CSSParser::parseMediaQuery(MediaList* queries, const String& string) | |
{ | |
if (string.isEmpty()) | |
return true; | |
m_mediaQuery = 0; | |
// 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); | |
m_mediaQuery = 0; | |
} | |
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.release(); | |
} | |
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_hasFontFaceOnlyValues = false; | |
} | |
Document* CSSParser::document() const | |
{ | |
StyleBase* root = m_styleSheet; | |
Document* doc = 0; | |
while (root && root->parent()) | |
root = root->parent(); | |
if (root && root->isCSSStyleSheet()) | |
doc = static_cast<CSSStyleSheet*>(root)->doc(); | |
return doc; | |
} | |
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; | |
} | |
// If we have any variables, then we don't parse the list of values yet. We add them to the declaration | |
// as unresolved, and allow them to be parsed later. The parse is considered "successful" for now, even though | |
// it might ultimately fail once the variable has been resolved. | |
if (!inShorthand() && checkForVariables(m_valueList)) { | |
addUnresolvedProperty(propId, 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 | portrait | landscape | inherit | |
case CSSPropertyQuotes: // [<string> <string>]+ | none | inherit | |
if (id) | |
validPrimitive = true; | |
break; | |
case CSSPropertyUnicodeBidi: // normal | embed | bidi-override | inherit | |
if (id == CSSValueNormal || | |
id == CSSValueEmbed || | |
id == CSSValueBidiOverride) | |
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 | start | end | <string> | inherit | |
if ((id >= CSSValueWebkitAuto && id <= CSSValueWebkitCenter) || 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 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 (propId == CSSPropertyOutlineColor && (id == CSSValueInvert || id == CSSValueWebkitFocusRingColor)) { | |
validPrimitive = true; | |
break; | |
} | |
/* nobreak */ | |
case CSSPropertyBackgroundColor: // <color> | inherit | |
case CSSPropertyBorderTopColor: // <color> | inherit | |
case CSSPropertyBorderRightColor: // <color> | inherit | |
case CSSPropertyBorderBottomColor: // <color> | inherit | |
case CSSPropertyBorderLeftColor: // <color> | inherit | |
case CSSPropertyColor: // <color> | inherit | |
case CSSPropertyTextLineThroughColor: // CSS3 text decoration colors | |
case CSSPropertyTextUnderlineColor: | |
case CSSPropertyTextOverlineColor: | |
case CSSPropertyWebkitColumnRuleColor: | |
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-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; | |
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(CSSPrimitiveValue::createIdentifier(CSSValuePointer)); | |
else if (value && ((value->id >= CSSValueAuto && value->id <= CSSValueWebkitGrabbing) || value->id == CSSValueCopy || value->id == CSSValueNone)) | |
list->append(CSSPrimitiveValue::createIdentifier(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 (value->unit == CSSParserValue::Function && equalIgnoringCase(value->function->name, "-webkit-gradient(")) { | |
if (parseGradient(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 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 CSSPropertyTextIndent: // <length> | <percentage> | inherit | |
case CSSPropertyPaddingTop: //// <padding-width> | inherit | |
case CSSPropertyPaddingRight: // Which is defined as | |
case CSSPropertyPaddingBottom: // <length> | <percentage> | |
case CSSPropertyPaddingLeft: //// | |
case CSSPropertyWebkitPaddingStart: | |
validPrimitive = (!id && validUnit(value, FLength | FPercent, m_strict)); | |
break; | |
case CSSPropertyMaxHeight: // <length> | <percentage> | none | inherit | |
case CSSPropertyMaxWidth: // <length> | <percentage> | none | inherit | |
if (id == CSSValueNone || id == CSSValueIntrinsic || id == CSSValueMinIntrinsic) { | |
validPrimitive = true; | |
break; | |
} | |
/* nobreak */ | |
case CSSPropertyMinHeight: // <length> | <percentage> | inherit | |
case CSSPropertyMinWidth: // <length> | <percentage> | inherit | |
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 | |
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: | |
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(CSSPrimitiveValue::createIdentifier(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 CSSPropertyWebkitBinding: | |
#if ENABLE(XBL) | |
if (id == CSSValueNone) | |
validPrimitive = true; | |
else { | |
RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); | |
CSSParserValue* val; | |
RefPtr<CSSValue> parsedValue; | |
while ((val = m_valueList->current())) { | |
if (val->unit == CSSPrimitiveValue::CSS_URI && m_styleSheet) { | |
// FIXME: The completeURL call should be done when using the CSSPrimitiveValue, | |
// not when creating it. | |
parsedValue = CSSPrimitiveValue::create(m_styleSheet->completeURL(val->string), CSSPrimitiveValue::CSS_URI); | |
} | |
if (!parsedValue) | |
break; | |
// FIXME: We can't use release() here since we might hit this path twice | |
// but that logic seems wrong to me to begin with, we convert all non-uri values | |
// into the last seen URI value!? | |
// -webkit-binding: url(foo.xml), 1, 2; (if that were valid) is treated as: | |
// -webkit-binding: url(foo.xml), url(foo.xml), url(foo.xml); !? | |
values->append(parsedValue.get()); | |
m_valueList->next(); | |
} | |
if (!values->length()) | |
return false; | |
addProperty(propId, values.release(), important); | |
m_valueList->next(); | |
return true; | |
} | |
#endif | |
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, m_strict); | |
if (!validPrimitive) | |
return false; | |
RefPtr<CSSPrimitiveValue> parsedValue1 = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); | |
RefPtr<CSSPrimitiveValue> parsedValue2; | |
if (num == 2) { | |
value = m_valueList->next(); | |
validPrimitive = validUnit(value, FLength, m_strict); | |
if (!validPrimitive) | |
return false; | |
parsedValue2 = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); | |
} else | |
parsedValue2 = parsedValue1; | |
RefPtr<Pair> pair = Pair::create(parsedValue1.release(), parsedValue2.release()); | |
RefPtr<CSSPrimitiveValue> val = CSSPrimitiveValue::create(pair.release()); | |
addProperty(propId, val.release(), important); | |
return true; | |
} | |
case CSSPropertyBorderRadius: | |
case CSSPropertyWebkitBorderRadius: | |
return parseBorderRadius(propId, important); | |
case CSSPropertyOutlineOffset: | |
validPrimitive = validUnit(value, FLength, m_strict); | |
break; | |
case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3 | |
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 CSSPropertyWebkitBoxSizing: | |
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 = CSSPrimitiveValue::create(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] = { CSSPropertyWebkitMarginTopCollapse, | |
CSSPropertyWebkitMarginBottomCollapse }; | |
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 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 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 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, | |
CSSPropertyBackgroundColor }; | |
return parseFillShorthand(propId, properties, 6, important); | |
} | |
case CSSPropertyWebkitMask: { | |
const int properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskRepeat, | |
CSSPropertyWebkitMaskAttachment, CSSPropertyWebkitMaskPosition, | |
CSSPropertyWebkitMaskOrigin }; | |
return parseFillShorthand(propId, properties, 5, 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 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 CSSPropertyFontStretch: | |
case CSSPropertyPage: | |
case CSSPropertyTextLineThrough: | |
case CSSPropertyTextOverline: | |
case CSSPropertyTextUnderline: | |
case CSSPropertyWebkitVariableDeclarationBlock: | |
return false; | |
#if ENABLE(SVG) | |
default: | |
return parseSVGValue(propId, important); | |
#endif | |
} | |
if (validPrimitive) { | |
if (id != 0) | |
parsedValue = CSSPrimitiveValue::createIdentifier(id); | |
else if (value->unit == CSSPrimitiveValue::CSS_STRING) | |
parsedValue = CSSPrimitiveValue::create(value->string, (CSSPrimitiveValue::UnitTypes) value->unit); | |
else if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) | |
parsedValue = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); | |
else if (value->unit >= CSSPrimitiveValue::CSS_TURN && value->unit <= CSSPrimitiveValue::CSS_REMS) | |
parsedValue = CSSPrimitiveValue::create(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; | |
} | |
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) | |
{ | |
if (parserValue->id == CSSValueBorderBox || parserValue->id == CSSValuePaddingBox || parserValue->id == CSSValueWebkitText) { | |
cssValue = CSSPrimitiveValue::createIdentifier(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; | |
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)) | |
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 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 | |
addProperty(properties[i], values[i].release(), important); | |
// Add in clip values when we hit the corresponding origin property. | |
if (properties[i] == CSSPropertyBackgroundOrigin) | |
addProperty(CSSPropertyBackgroundClip, clipValue.release(), important); | |
else if (properties[i] == CSSPropertyWebkitMaskOrigin) | |
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 = sizeof(properties) / sizeof(properties[0]); | |
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 = sizeof(properties) / sizeof(properties[0]); | |
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; | |
} | |
// [ <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; | |
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 (equalIgnoringCase(val->function->name, "-webkit-gradient(")) { | |
if (!parseGradient(parsedValue)) | |
return false; | |
} else if (equalIgnoringCase(val->function->name, "-webkit-canvas(")) { | |
if (!parseCanvas(parsedValue)) | |
return false; | |
} else | |
return false; | |
} else if (val->unit == CSSPrimitiveValue::CSS_IDENT) { | |
// open-quote | |
// close-quote | |
// no-open-quote | |
// no-close-quote | |
// FIXME: These are not yet implemented (http://bugs.webkit.org/show_bug.cgi?id=6503). | |
} else if (val->unit == CSSPrimitiveValue::CSS_STRING) { | |
parsedValue = CSSPrimitiveValue::create(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 CSSPrimitiveValue::create(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 CSSPrimitiveValue::createIdentifier(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 (m_valueList->current()->unit == CSSParserValue::Function) { | |
if (equalIgnoringCase(m_valueList->current()->function->name, "-webkit-gradient(")) | |
return parseGradient(value); | |
if (equalIgnoringCase(m_valueList->current()->function->name, "-webkit-canvas(")) | |
return parseCanvas(value); | |
} | |
return false; | |
} | |
PassRefPtr<CSSValue> CSSParser::parseFillPositionXY(bool& xFound, bool& yFound) | |
{ | |
int id = m_valueList->current()->id; | |
if (id == CSSValueLeft || id == CSSValueTop || id == CSSValueRight || id == CSSValueBottom || id == CSSValueCenter) { | |
int percent = 0; | |
if (id == CSSValueLeft || id == CSSValueRight) { | |
if (xFound) | |
return 0; | |
xFound = true; | |
if (id == CSSValueRight) | |
percent = 100; | |
} | |
else if (id == CSSValueTop || id == CSSValueBottom) { | |
if (yFound) | |
return 0; | |
yFound = true; | |
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; | |
return CSSPrimitiveValue::create(percent, CSSPrimitiveValue::CSS_PERCENTAGE); | |
} | |
if (validUnit(m_valueList->current(), FPercent | FLength, m_strict)) | |
return CSSPrimitiveValue::create(m_valueList->current()->fValue, | |
(CSSPrimitiveValue::UnitTypes)m_valueList->current()->unit); | |
return 0; | |
} | |
void CSSParser::parseFillPosition(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2) | |
{ | |
CSSParserValue* value = m_valueList->current(); | |
// Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length. | |
bool value1IsX = false, value1IsY = false; | |
value1 = parseFillPositionXY(value1IsX, value1IsY); | |
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 = 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; | |
bool value2IsX = false, value2IsY = false; | |
if (value) { | |
value2 = parseFillPositionXY(value2IsX, value2IsY); | |
if (value2) | |
m_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 = CSSPrimitiveValue::create(50, CSSPrimitiveValue::CSS_PERCENTAGE); | |
if (value1IsY || value2IsX) | |
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 = CSSPrimitiveValue::createIdentifier(CSSValueRepeat); | |
value2 = CSSPrimitiveValue::createIdentifier(CSSValueNoRepeat); | |
m_valueList->next(); | |
return; | |
} | |
if (id == CSSValueRepeatY) { | |
m_implicitShorthand = true; | |
value1 = CSSPrimitiveValue::createIdentifier(CSSValueNoRepeat); | |
value2 = CSSPrimitiveValue::createIdentifier(CSSValueRepeat); | |
m_valueList->next(); | |
return; | |
} | |
if (id == CSSValueRepeat || id == CSSValueNoRepeat || id == CSSValueRound || id == CSSValueSpace) | |
value1 = CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::createIdentifier(id); | |
m_valueList->next(); | |
} else { | |
// If only one value was specified, value2 is the same as value1. | |
m_implicitShorthand = true; | |
value2 = CSSPrimitiveValue::createIdentifier(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 CSSPrimitiveValue::createIdentifier(value->id); | |
RefPtr<CSSPrimitiveValue> parsedValue1; | |
if (value->id == CSSValueAuto) | |
parsedValue1 = CSSPrimitiveValue::create(0, CSSPrimitiveValue::CSS_UNKNOWN); | |
else { | |
if (!validUnit(value, FLength | FPercent, m_strict)) | |
return 0; | |
parsedValue1 = CSSPrimitiveValue::create(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 = CSSPrimitiveValue::create(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 = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit); | |
} | |
} | |
if (!parsedValue2) { | |
if (property == CSSPropertyWebkitBackgroundSize || property == CSSPropertyWebkitMaskSize) | |
parsedValue2 = parsedValue1; | |
else | |
parsedValue2 = CSSPrimitiveValue::create(0, CSSPrimitiveValue::CSS_UNKNOWN); | |
} | |
return CSSPrimitiveValue::create(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 = CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::createIdentifier(val->id); | |
m_valueList->next(); | |
} | |
break; | |
case CSSPropertyBackgroundClip: | |
if (parseBackgroundClip(val, currValue)) | |
m_valueList->next(); | |
break; | |
case CSSPropertyBackgroundOrigin: | |
if (val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox) { | |
currValue = CSSPrimitiveValue::createIdentifier(val->id); | |
m_valueList->next(); | |
} | |
break; | |
case CSSPropertyBackgroundPosition: | |
case CSSPropertyWebkitMaskPosition: | |
parseFillPosition(currValue, currValue2); | |
// parseFillPosition advances the m_valueList pointer | |
break; | |
case CSSPropertyBackgroundPositionX: | |
case CSSPropertyWebkitMaskPositionX: { | |
bool xFound = false, yFound = true; | |
currValue = parseFillPositionXY(xFound, yFound); | |
if (currValue) | |
m_valueList->next(); | |
break; | |
} | |
case CSSPropertyBackgroundPositionY: | |
case CSSPropertyWebkitMaskPositionY: { | |
bool xFound = true, yFound = false; | |
currValue = parseFillPositionXY(xFound, yFound); | |
if (currValue) | |
m_valueList->next(); | |
break; | |
} | |
case CSSPropertyWebkitBackgroundComposite: | |
case CSSPropertyWebkitMaskComposite: | |
if ((val->id >= CSSValueClear && val->id <= CSSValuePlusLighter) || val->id == CSSValueHighlight) { | |
currValue = CSSPrimitiveValue::createIdentifier(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 CSSPrimitiveValue::create(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 CSSPrimitiveValue::createIdentifier(value->id); | |
return 0; | |
} | |
PassRefPtr<CSSValue> CSSParser::parseAnimationDuration() | |
{ | |
CSSParserValue* value = m_valueList->current(); | |
if (validUnit(value, FTime | FNonNeg, m_strict)) | |
return CSSPrimitiveValue::create(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 CSSPrimitiveValue::createIdentifier(value->id); | |
return 0; | |
} | |
PassRefPtr<CSSValue> CSSParser::parseAnimationIterationCount() | |
{ | |
CSSParserValue* value = m_valueList->current(); | |
if (value->id == CSSValueInfinite) | |
return CSSPrimitiveValue::createIdentifier(value->id); | |
if (validUnit(value, FInteger | FNonNeg, m_strict)) | |
return CSSPrimitiveValue::create(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 CSSPrimitiveValue::createIdentifier(CSSValueNone); | |
} else { | |
return CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_STRING); | |
} | |
} | |
return 0; | |
} | |
PassRefPtr<CSSValue> CSSParser::parseAnimationPlayState() | |
{ | |
CSSParserValue* value = m_valueList->current(); | |
if (value->id == CSSValueRunning || value->id == CSSValuePaused) | |
return CSSPrimitiveValue::createIdentifier(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 CSSPrimitiveValue::createIdentifier(result); | |
if (equalIgnoringCase(value->string, "all")) | |
return CSSPrimitiveValue::createIdentifier(CSSValueAll); | |
if (equalIgnoringCase(value->string, "none")) | |
return CSSPrimitiveValue::createIdentifier(CSSValueNone); | |
return 0; | |
} | |
void CSSParser::parseTransformOriginShorthand(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3) | |
{ | |
parseFillPosition(value1, value2); | |
// now get z | |
if (m_valueList->current() && validUnit(m_valueList->current(), FLength, m_strict)) | |
value3 = CSSPrimitiveValue::create(m_valueList->current()->fValue, | |
(CSSPrimitiveValue::UnitTypes)m_valueList->current()->unit); | |
if (value3) | |
m_valueList->next(); | |
} | |
bool CSSParser::parseTimingFunctionValue(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) | |
return CSSPrimitiveValue::createIdentifier(value->id); | |
// We must be a function. | |
if (value->unit != CSSParserValue::Function) | |
return 0; | |
// The only timing function we accept for now is a cubic bezier function. 4 points must be specified. | |
CSSParserValueList* args = value->function->args; | |
if (!equalIgnoringCase(value->function->name, "cubic-bezier(") || !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 (!parseTimingFunctionValue(args, x1)) | |
return 0; | |
if (!parseTimingFunctionValue(args, y1)) | |
return 0; | |
if (!parseTimingFunctionValue(args, x2)) | |
return 0; | |
if (!parseTimingFunctionValue(args, y2)) | |
return 0; | |
return CSSTimingFunctionValue::create(x1, y1, x2, y2); | |
} | |
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, CSSPrimitiveValue::createIdentifier(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; | |
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 = CSSPrimitiveValue::createIdentifier(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 ? | |
CSSPrimitiveValue::createIdentifier(CSSValueAuto) : | |
CSSPrimitiveValue::create(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, CSSPrimitiveValue::create(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 = CSSPrimitiveValue::create(i->string, CSSPrimitiveValue::CSS_STRING); | |
RefPtr<CSSPrimitiveValue> separator; | |
if (!counters) | |
separator = CSSPrimitiveValue::create(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 = CSSPrimitiveValue::create(i->string, (CSSPrimitiveValue::UnitTypes) i->unit); | |
} | |
RefPtr<CSSPrimitiveValue> listStyle; | |
i = args->next(); | |
if (!i) // Make the list style default decimal | |
listStyle = CSSPrimitiveValue::create(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 = CSSPrimitiveValue::create(ls, (CSSPrimitiveValue::UnitTypes) i->unit); | |
} | |
return CSSPrimitiveValue::create(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; | |
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 ? | |
CSSPrimitiveValue::createIdentifier(CSSValueAuto) : | |
CSSPrimitiveValue::create(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, CSSPrimitiveValue::create(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 = CSSPrimitiveValue::createIdentifier(id); | |
} else if (id == CSSValueSmallCaps) { | |
if (font->variant) | |
return false; | |
font->variant = CSSPrimitiveValue::createIdentifier(id); | |
} else if (id >= CSSValueBold && id <= CSSValueLighter) { | |
if (font->weight) | |
return false; | |
font->weight = CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::createIdentifier(CSSValueNormal); | |
if (!font->variant) | |
font->variant = CSSPrimitiveValue::createIdentifier(CSSValueNormal); | |
if (!font->weight) | |
font->weight = CSSPrimitiveValue::createIdentifier(CSSValueNormal); | |
// now a font size _must_ come | |
// <absolute-size> | <relative-size> | <length> | <percentage> | inherit | |
if (value->id >= CSSValueXxSmall && value->id <= CSSValueLarger) | |
font->size = CSSPrimitiveValue::createIdentifier(value->id); | |
else if (validUnit(value, FLength | FPercent | FNonNeg, m_strict)) | |
font->size = CSSPrimitiveValue::create(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 = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); | |
else | |
return false; | |
value = m_valueList->next(); | |
if (!value) | |
return false; | |
} | |
if (!font->lineHeight) | |
font->lineHeight = CSSPrimitiveValue::createIdentifier(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(CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::createIdentifier(val->id); | |
} | |
} else if (validUnit(val, FInteger | FNonNeg, false)) { | |
int weight = static_cast<int>(val->fValue); | |
if (!(weight % 100) && weight >= 100 && weight <= 900) | |
parsedValue = CSSPrimitiveValue::createIdentifier(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; | |
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; | |
if (args && args->size() == 1) { | |
if (equalIgnoringCase(val->function->name, "local(") && !expectComma) { | |
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(); | |
CSSParserValue* currentValue; | |
bool failed = false; | |
while ((currentValue = m_valueList->current())) { | |
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; | |
} | |
values->append(CSSUnicodeRangeValue::create(from, to)); | |
m_valueList->next(); | |
} | |
if (failed || !values->length()) | |
return false; | |
addProperty(CSSPropertyUnicodeRange, values.release(), m_important); | |
return true; | |
} | |
static inline bool isCSSWhitespace(UChar c) | |
{ | |
return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f'; | |
} | |
static inline bool parseInt(const UChar*& string, const UChar* end, UChar terminator, int& value) | |
{ | |
const UChar* current = string; | |
int localValue = 0; | |
bool negative = false; | |
while (current != end && isCSSWhitespace(*current)) | |
current++; | |
if (current != end && *current == '-') { | |
negative = true; | |
current++; | |
} | |
if (current == end || !isASCIIDigit(*current)) | |
return false; | |
while (current != end && isASCIIDigit(*current)) | |
localValue = localValue * 10 + *current++ - '0'; | |
while (current != end && isCSSWhitespace(*current)) | |
current++; | |
if (current == end || *current++ != terminator) | |
return false; | |
// Clamp negative values at zero. | |
value = negative ? 0 : localValue; | |
string = current; | |
return true; | |
} | |
bool CSSParser::parseColor(const String &name, RGBA32& rgb, bool strict) | |
{ | |
if (!strict && Color::parseHexColor(name, rgb)) | |
return true; | |
// Try rgb() syntax. | |
if (name.startsWith("rgb(")) { | |
const UChar* current = name.characters() + 4; | |
const UChar* end = name.characters() + name.length(); | |
int red; | |
int green; | |
int blue; | |
if (!parseInt(current, end, ',', red)) | |
return false; | |
if (!parseInt(current, end, ',', green)) | |
return false; | |
if (!parseInt(current, end, ')', 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; | |
} | |
bool CSSParser::parseColorParameters(CSSParserValue* value, int* colorArray, bool parseAlpha) | |
{ | |
CSSParserValueList* args = value->function->args; | |
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] = static_cast<int>(v->fValue * (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 256.0 / 100.0 : 1.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, unitType, true)) | |
return false; | |
colorArray[i] = static_cast<int>(v->fValue * (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE ? 256.0 / 100.0 : 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; | |
// 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; | |
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 CSSPrimitiveValue::createColor(c); | |
} | |
bool CSSParser::parseColorFromValue(CSSParserValue* value, RGBA32& c, bool svg) | |
{ | |
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 (!svg) { | |
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; | |
} 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) | |
: property(prop) | |
, allowX(true) | |
, allowY(false) | |
, allowBlur(false) | |
, allowSpread(false) | |
, allowColor(true) | |
, allowStyle(prop == CSSPropertyWebkitBoxShadow) | |
, 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; | |
} | |
void commitLength(CSSParserValue* v) | |
{ | |
RefPtr<CSSPrimitiveValue> val = CSSPrimitiveValue::create(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; | |
allowBreak = true; | |
} else if (allowBlur) { | |
blur = val.release(); | |
allowBlur = false; | |
allowSpread = property == CSSPropertyWebkitBoxShadow; | |
} 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; | |
} | |
} | |
void commitStyle(CSSParserValue* v) | |
{ | |
style = CSSPrimitiveValue::createIdentifier(v->id); | |
allowStyle = false; | |
if (allowX) | |
allowBreak = false; | |
else { | |
allowBlur = false; | |
allowSpread = false; | |
allowColor = false; | |
} | |
} | |
CSSPropertyID property; | |
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; | |
bool allowBreak; | |
}; | |
bool CSSParser::parseShadow(int propId, bool important) | |
{ | |
ShadowParseContext context(static_cast<CSSPropertyID>(propId)); | |
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 = CSSPrimitiveValue::createIdentifier(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 = CSSPrimitiveValue::create(0, CSSPrimitiveValue::CSS_PX); | |
else { | |
if (!validUnit(val, FLength | FPercent, m_strict)) | |
return false; | |
offset = CSSPrimitiveValue::create(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() | |
: 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 = CSSPrimitiveValue::create(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 = CSSPrimitiveValue::create(m_top->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_top->primitiveType()); | |
m_bottom = CSSPrimitiveValue::create(m_top->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_top->primitiveType()); | |
m_left = CSSPrimitiveValue::create(m_top->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_top->primitiveType()); | |
} | |
if (!m_bottom) { | |
m_bottom = CSSPrimitiveValue::create(m_top->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_top->primitiveType()); | |
m_left = CSSPrimitiveValue::create(m_right->getDoubleValue(), (CSSPrimitiveValue::UnitTypes)m_right->primitiveType()); | |
} | |
if (!m_left) | |
m_left = CSSPrimitiveValue::create(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); | |
} | |
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; | |
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 (val->unit == CSSParserValue::Function) { | |
RefPtr<CSSValue> value; | |
if ((equalIgnoringCase(val->function->name, "-webkit-gradient(") && parseGradient(value)) || | |
(equalIgnoringCase(val->function->name, "-webkit-canvas(") && parseCanvas(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 = CSSPrimitiveValue::create(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; | |
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, m_strict)) | |
return false; | |
RefPtr<CSSPrimitiveValue> radius = CSSPrimitiveValue::create(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]); | |
addProperty(CSSPropertyBorderTopLeftRadius, CSSPrimitiveValue::create(Pair::create(radii[0][0].release(), radii[1][0].release())), important); | |
addProperty(CSSPropertyBorderTopRightRadius, CSSPrimitiveValue::create(Pair::create(radii[0][1].release(), radii[1][1].release())), important); | |
addProperty(CSSPropertyBorderBottomRightRadius, CSSPrimitiveValue::create(Pair::create(radii[0][2].release(), radii[1][2].release())), important); | |
addProperty(CSSPropertyBorderBottomLeftRadius, CSSPrimitiveValue::create(Pair::create(radii[0][3].release(), radii[1][3].release())), important); | |
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 = CSSPrimitiveValue::create(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 = (int)val->fValue; | |
m_valueList->next(); | |
} | |
list->append(CSSPrimitiveValue::create(Pair::create(counterName.release(), | |
CSSPrimitiveValue::create(i, CSSPrimitiveValue::CSS_NUMBER)))); | |
state = ID; | |
continue; | |
} | |
} | |
break; | |
} | |
if (list->length() > 0) { | |
addProperty(propId, list.release(), important); | |
return true; | |
} | |
return false; | |
} | |
static PassRefPtr<CSSPrimitiveValue> parseGradientPoint(CSSParserValue* a, bool horizontal) | |
{ | |
RefPtr<CSSPrimitiveValue> result; | |
if (a->unit == CSSPrimitiveValue::CSS_IDENT) { | |
if ((equalIgnoringCase(a->string, "left") && horizontal) || | |
(equalIgnoringCase(a->string, "top") && !horizontal)) | |
result = CSSPrimitiveValue::create(0., CSSPrimitiveValue::CSS_PERCENTAGE); | |
else if ((equalIgnoringCase(a->string, "right") && horizontal) || | |
(equalIgnoringCase(a->string, "bottom") && !horizontal)) | |
result = CSSPrimitiveValue::create(100., CSSPrimitiveValue::CSS_PERCENTAGE); | |
else if (equalIgnoringCase(a->string, "center")) | |
result = CSSPrimitiveValue::create(50., CSSPrimitiveValue::CSS_PERCENTAGE); | |
} else if (a->unit == CSSPrimitiveValue::CSS_NUMBER || a->unit == CSSPrimitiveValue::CSS_PERCENTAGE) | |
result = CSSPrimitiveValue::create(a->fValue, (CSSPrimitiveValue::UnitTypes)a->unit); | |
return result; | |
} | |
static bool parseGradientColorStop(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; | |
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_stop = 0.f; | |
else | |
stop.m_stop = 1.f; | |
int id = args->current()->id; | |
if (id == CSSValueWebkitText || (id >= CSSValueAqua && id <= CSSValueWindowtext) || id == CSSValueMenu) | |
stop.m_color = CSSPrimitiveValue::createIdentifier(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_stop = (float)stopArg->fValue / 100.f; | |
else if (stopArg->unit == CSSPrimitiveValue::CSS_NUMBER) | |
stop.m_stop = (float)stopArg->fValue; | |
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 = CSSPrimitiveValue::createIdentifier(id); | |
else | |
stop.m_color = p->parseColor(stopArg); | |
if (!stop.m_color) | |
return false; | |
} | |
return true; | |
} | |
bool CSSParser::parseGradient(RefPtr<CSSValue>& gradient) | |
{ | |
RefPtr<CSSGradientValue> result = CSSGradientValue::create(); | |
// Walk the arguments. | |
CSSParserValueList* args = m_valueList->current()->function->args; | |
if (!args || args->size() == 0) | |
return false; | |
// The first argument is the gradient type. It is an identifier. | |
CSSParserValue* a = args->current(); | |
if (!a || a->unit != CSSPrimitiveValue::CSS_IDENT) | |
return false; | |
if (equalIgnoringCase(a->string, "linear")) | |
result->setType(CSSLinearGradient); | |
else if (equalIgnoringCase(a->string, "radial")) | |
result->setType(CSSRadialGradient); | |
else | |
return false; | |
// 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 = parseGradientPoint(a, true); | |
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 = parseGradientPoint(a, false); | |
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 (result->type() == CSSRadialGradient) { | |
a = args->next(); | |
if (!a || a->unit != CSSPrimitiveValue::CSS_NUMBER) | |
return false; | |
result->setFirstRadius(CSSPrimitiveValue::create(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 = parseGradientPoint(a, true); | |
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 = parseGradientPoint(a, false); | |
if (!point) | |
return false; | |
result->setSecondY(point.release()); | |
// For radial gradients only, we now expect the second radius. | |
if (result->type() == 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; | |
result->setSecondRadius(CSSPrimitiveValue::create(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 (!parseGradientColorStop(this, a, stop)) | |
return false; | |
result->addStop(stop); | |
// Advance | |
a = args->next(); | |
} | |
gradient = result.release(); | |
return true; | |
} | |
bool CSSParser::parseCanvas(RefPtr<CSSValue>& canvas) | |
{ | |
RefPtr<CSSCanvasValue> result = CSSCanvasValue::create(); | |
// Walk the arguments. | |
CSSParserValueList* args = m_valueList->current()->function->args; | |
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; | |
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(); | |
// 4th param of rotate3d() is an angle rather than a bare number, validate it as such | |
if (info.type() == WebKitCSSTransformValue::Rotate3DTransformOperation && argNumber == 3) { | |
if (!validUnit(a, FAngle, true)) | |
return 0; | |
} else if (!validUnit(a, unit, true)) | |
return 0; | |
// Add the value to the current transform operation. | |
transformValue->append(CSSPrimitiveValue::create(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: | |
parseTransformOriginShorthand(value, value2, value3); | |
// parseTransformOriginShorthand advances the m_valueList pointer | |
break; | |
case CSSPropertyWebkitTransformOriginX: { | |
bool xFound = false, yFound = true; | |
value = parseFillPositionXY(xFound, yFound); | |
if (value) | |
m_valueList->next(); | |
break; | |
} | |
case CSSPropertyWebkitTransformOriginY: { | |
bool xFound = true, yFound = false; | |
value = parseFillPositionXY(xFound, yFound); | |
if (value) | |
m_valueList->next(); | |
break; | |
} | |
case CSSPropertyWebkitTransformOriginZ: { | |
if (validUnit(m_valueList->current(), FLength, m_strict)) | |
value = CSSPrimitiveValue::create(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(value, value2); | |
break; | |
case CSSPropertyWebkitPerspectiveOriginX: { | |
bool xFound = false, yFound = true; | |
value = parseFillPositionXY(xFound, yFound); | |
if (value) | |
m_valueList->next(); | |
break; | |
} | |
case CSSPropertyWebkitPerspectiveOriginY: { | |
bool xFound = true, yFound = false; | |
value = parseFillPositionXY(xFound, yFound); | |
if (value) | |
m_valueList->next(); | |
break; | |
} | |
} | |
return value; | |
} | |
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 NOTFUNCTION: | |
case VARCALL: | |
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 KHERZ: | |
case REMS: | |
length--; | |
case MSECS: | |
case HERZ: | |
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; | |
// FIXME: Add CSS Variables if we ever decide to turn it back on. | |
} | |
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 && isCSSWhitespace(*start)) { | |
++start; | |
--l; | |
} | |
while (l && isCSSWhitespace(start[l - 1])) | |
--l; | |
if (l && (*start == '"' || *start == '\'')) { | |
ASSERT(l >= 2 && start[l - 1] == *start); | |
++start; | |
l -= 2; | |
} | |
break; | |
case VARCALL: | |
// "-webkit-var("{w}{ident}{w}")" | |
// strip "-webkit-var(" and ")" | |
start += 12; | |
l -= 13; | |
// strip {w} | |
while (l && isCSSWhitespace(*start)) { | |
++start; | |
--l; | |
} | |
while (l && isCSSWhitespace(start[l - 1])) | |
--l; | |
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 (isCSSWhitespace(*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_line; | |
} | |
} | |
CSSSelector* CSSParser::createFloatingSelector() | |
{ | |
CSSSelector* selector = fastNew<CSSSelector>(); | |
m_floatingSelectors.add(selector); | |
return selector; | |
} | |
CSSSelector* CSSParser::sinkFloatingSelector(CSSSelector* selector) | |
{ | |
if (selector) { | |
ASSERT(m_floatingSelectors.contains(selector)); | |
m_floatingSelectors.remove(selector); | |
} | |
return selector; | |
} | |
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) | |
{ | |
delete m_floatingMediaQueryExp; | |
m_floatingMediaQueryExp = new MediaQueryExp(mediaFeature, values); | |
return m_floatingMediaQueryExp; | |
} | |
MediaQueryExp* CSSParser::sinkFloatingMediaQueryExp(MediaQueryExp* e) | |
{ | |
ASSERT(e == m_floatingMediaQueryExp); | |
m_floatingMediaQueryExp = 0; | |
return e; | |
} | |
Vector<MediaQueryExp*>* CSSParser::createFloatingMediaQueryExpList() | |
{ | |
if (m_floatingMediaQueryExpList) { | |
deleteAllValues(*m_floatingMediaQueryExpList); | |
delete m_floatingMediaQueryExpList; | |
} | |
m_floatingMediaQueryExpList = new Vector<MediaQueryExp*>; | |
return m_floatingMediaQueryExpList; | |
} | |
Vector<MediaQueryExp*>* CSSParser::sinkFloatingMediaQueryExpList(Vector<MediaQueryExp*>* l) | |
{ | |
ASSERT(l == m_floatingMediaQueryExpList); | |
m_floatingMediaQueryExpList = 0; | |
return l; | |
} | |
MediaQuery* CSSParser::createFloatingMediaQuery(MediaQuery::Restrictor r, const String& mediaType, Vector<MediaQueryExp*>* exprs) | |
{ | |
delete m_floatingMediaQuery; | |
m_floatingMediaQuery = new MediaQuery(r, mediaType, exprs); | |
return m_floatingMediaQuery; | |
} | |
MediaQuery* CSSParser::createFloatingMediaQuery(Vector<MediaQueryExp*>* exprs) | |
{ | |
return createFloatingMediaQuery(MediaQuery::None, "all", exprs); | |
} | |
MediaQuery* CSSParser::sinkFloatingMediaQuery(MediaQuery* mq) | |
{ | |
ASSERT(mq == m_floatingMediaQuery); | |
m_floatingMediaQuery = 0; | |
return mq; | |
} | |
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 = m_allowVariablesRules = 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 = m_allowVariablesRules = false; | |
RefPtr<WebKitCSSKeyframesRule> rule = WebKitCSSKeyframesRule::create(m_styleSheet); | |
WebKitCSSKeyframesRule* rulePtr = rule.get(); | |
m_parsedStyleObjects.append(rule.release()); | |
return rulePtr; | |
} | |
CSSRule* CSSParser::createStyleRule(Vector<CSSSelector*>* selectors) | |
{ | |
m_allowImportRules = m_allowNamespaceDeclarations = m_allowVariablesRules = false; | |
CSSStyleRule* result = 0; | |
if (selectors) { | |
RefPtr<CSSStyleRule> rule = CSSStyleRule::create(m_styleSheet, m_lastSelectorLine); | |
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()); | |
} | |
clearProperties(); | |
return result; | |
} | |
CSSRule* CSSParser::createFontFaceRule() | |
{ | |
m_allowImportRules = m_allowNamespaceDeclarations = m_allowVariablesRules = false; | |
RefPtr<CSSFontFaceRule> rule = CSSFontFaceRule::create(m_styleSheet); | |
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()); | |
} | |
} | |
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_allowVariablesRules = false; | |
m_styleSheet->addNamespace(this, prefix, uri); | |
} | |
#if !ENABLE(CSS_VARIABLES) | |
CSSRule* CSSParser::createVariablesRule(MediaList*, bool) | |
{ | |
return 0; | |
} | |
bool CSSParser::addVariable(const CSSParserString&, CSSParserValueList*) | |
{ | |
return false; | |
} | |
bool CSSParser::addVariableDeclarationBlock(const CSSParserString&) | |
{ | |
return false; | |
} | |
#else | |
CSSRule* CSSParser::createVariablesRule(MediaList* mediaList, bool variablesKeyword) | |
{ | |
if (!m_allowVariablesRules) | |
return 0; | |
m_allowImportRules = false; | |
RefPtr<CSSVariablesRule> rule = CSSVariablesRule::create(m_styleSheet, mediaList, variablesKeyword); | |
rule->setDeclaration(CSSVariablesDeclaration::create(rule.get(), m_variableNames, m_variableValues)); | |
clearVariables(); | |
CSSRule* result = rule.get(); | |
m_parsedStyleObjects.append(rule.release()); | |
return result; | |
} | |
bool CSSParser::addVariable(const CSSParserString& name, CSSParserValueList* valueList) | |
{ | |
if (checkForVariables(valueList)) { | |
delete valueList; | |
return false; | |
} | |
m_variableNames.append(String(name)); | |
m_variableValues.append(CSSValueList::createFromParserValueList(valueList)); | |
return true; | |
} | |
bool CSSParser::addVariableDeclarationBlock(const CSSParserString&) | |
{ | |
// FIXME: Disabling declarations as variable values for now since they no longer have a common base class with CSSValues. | |
#if 0 | |
m_variableNames.append(String(name)); | |
m_variableValues.append(CSSMutableStyleDeclaration::create(0, m_parsedProperties, m_numParsedProperties)); | |
clearProperties(); | |
#endif | |
return true; | |
} | |
#endif | |
void CSSParser::clearVariables() | |
{ | |
m_variableNames.clear(); | |
m_variableValues.clear(); | |
} | |
bool CSSParser::parseVariable(CSSVariablesDeclaration* declaration, const String& variableName, const String& variableValue) | |
{ | |
m_styleSheet = static_cast<CSSStyleSheet*>(declaration->stylesheet()); | |
String nameValuePair = variableName + ": "; | |
nameValuePair += variableValue; | |
setupParser("@-webkit-variables-decls{", nameValuePair, "} "); | |
cssyyparse(this); | |
m_rule = 0; | |
bool ok = false; | |
if (m_variableNames.size()) { | |
ok = true; | |
declaration->addParsedVariable(variableName, m_variableValues[0]); | |
} | |
clearVariables(); | |
return ok; | |
} | |
void CSSParser::parsePropertyWithResolvedVariables(int propId, bool isImportant, CSSMutableStyleDeclaration* declaration, CSSParserValueList* list) | |
{ | |
m_valueList = list; | |
m_styleSheet = static_cast<CSSStyleSheet*>(declaration->stylesheet()); | |
if (parseValue(propId, isImportant)) | |
declaration->addParsedProperties(m_parsedProperties, m_numParsedProperties); | |
clearProperties(); | |
m_valueList = 0; | |
} | |
bool CSSParser::checkForVariables(CSSParserValueList* valueList) | |
{ | |
if (!valueList || !valueList->containsVariables()) | |
return false; | |
bool hasVariables = false; | |
for (unsigned i = 0; i < valueList->size(); ++i) { | |
if (valueList->valueAt(i)->isVariable()) { | |
hasVariables = true; | |
break; | |
} | |
if (valueList->valueAt(i)->unit == CSSParserValue::Function && checkForVariables(valueList->valueAt(i)->function->args)) { | |
hasVariables = true; | |
break; | |
} | |
} | |
return hasVariables; | |
} | |
void CSSParser::addUnresolvedProperty(int propId, bool important) | |
{ | |
RefPtr<CSSVariableDependentValue> val = CSSVariableDependentValue::create(CSSValueList::createFromParserValueList(m_valueList)); | |
addProperty(propId, val.release(), important); | |
} | |
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); | |
} | |
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-opacity") == 0) { | |
// 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); | |
} 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 props* hashTableEntry = findProp(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 css_value* hashTableEntry = findValue(buffer, length); | |
return hashTableEntry ? hashTableEntry->id : 0; | |
} | |
#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" | |
} |