1/* 2 Copyright (C) 2008 Eric Seidel <eric@webkit.org> 3 Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> 4 2004, 2005, 2007, 2010 Rob Buis <buis@kde.org> 5 Copyright (C) 2005, 2006 Apple Inc. 6 7 This library is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Library General Public 9 License as published by the Free Software Foundation; either 10 version 2 of the License, or (at your option) any later version. 11 12 This library is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Library General Public License for more details. 16 17 You should have received a copy of the GNU Library General Public License 18 along with this library; see the file COPYING.LIB. If not, write to 19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 Boston, MA 02110-1301, USA. 21*/ 22 23#include "config.h" 24 25#include "CSSInheritedValue.h" 26#include "CSSInitialValue.h" 27#include "CSSParser.h" 28#include "CSSPropertyNames.h" 29#include "CSSValueKeywords.h" 30#include "CSSValueList.h" 31#include "RenderTheme.h" 32#include "SVGPaint.h" 33 34namespace WebCore { 35 36bool CSSParser::parseSVGValue(CSSPropertyID propId, bool important) 37{ 38 CSSParserValue* value = m_valueList->current(); 39 if (!value) 40 return false; 41 42 CSSValueID id = value->id; 43 44 bool valid_primitive = false; 45 RefPtr<CSSValue> parsedValue; 46 47 switch (propId) { 48 /* The comment to the right defines all valid value of these 49 * properties as defined in SVG 1.1, Appendix N. Property index */ 50 case CSSPropertyAlignmentBaseline: 51 // auto | baseline | before-edge | text-before-edge | middle | 52 // central | after-edge | text-after-edge | ideographic | alphabetic | 53 // hanging | mathematical | inherit 54 if (id == CSSValueAuto || id == CSSValueBaseline || id == CSSValueMiddle || 55 (id >= CSSValueBeforeEdge && id <= CSSValueMathematical)) 56 valid_primitive = true; 57 break; 58 59 case CSSPropertyBaselineShift: 60 // baseline | super | sub | <percentage> | <length> | inherit 61 if (id == CSSValueBaseline || id == CSSValueSub || 62 id >= CSSValueSuper) 63 valid_primitive = true; 64 else 65 valid_primitive = validUnit(value, FLength | FPercent, SVGAttributeMode); 66 break; 67 68 case CSSPropertyDominantBaseline: 69 // auto | use-script | no-change | reset-size | ideographic | 70 // alphabetic | hanging | mathematical | central | middle | 71 // text-after-edge | text-before-edge | inherit 72 if (id == CSSValueAuto || id == CSSValueMiddle || 73 (id >= CSSValueUseScript && id <= CSSValueResetSize) || 74 (id >= CSSValueCentral && id <= CSSValueMathematical)) 75 valid_primitive = true; 76 break; 77 78 case CSSPropertyEnableBackground: 79 // accumulate | new [x] [y] [width] [height] | inherit 80 if (id == CSSValueAccumulate) // TODO : new 81 valid_primitive = true; 82 break; 83 84 case CSSPropertyMarkerStart: 85 case CSSPropertyMarkerMid: 86 case CSSPropertyMarkerEnd: 87 case CSSPropertyMask: 88 if (id == CSSValueNone) 89 valid_primitive = true; 90 else if (value->unit == CSSPrimitiveValue::CSS_URI) { 91 parsedValue = CSSPrimitiveValue::create(value->string, CSSPrimitiveValue::CSS_URI); 92 if (parsedValue) 93 m_valueList->next(); 94 } 95 break; 96 97 case CSSPropertyClipRule: // nonzero | evenodd | inherit 98 case CSSPropertyFillRule: 99 if (id == CSSValueNonzero || id == CSSValueEvenodd) 100 valid_primitive = true; 101 break; 102 103 case CSSPropertyStrokeMiterlimit: // <miterlimit> | inherit 104 valid_primitive = validUnit(value, FNumber | FNonNeg, SVGAttributeMode); 105 break; 106 107 case CSSPropertyStrokeLinejoin: // miter | round | bevel | inherit 108 if (id == CSSValueMiter || id == CSSValueRound || id == CSSValueBevel) 109 valid_primitive = true; 110 break; 111 112 case CSSPropertyStrokeLinecap: // butt | round | square | inherit 113 if (id == CSSValueButt || id == CSSValueRound || id == CSSValueSquare) 114 valid_primitive = true; 115 break; 116 117 case CSSPropertyStrokeOpacity: // <opacity-value> | inherit 118 case CSSPropertyFillOpacity: 119 case CSSPropertyStopOpacity: 120 case CSSPropertyFloodOpacity: 121 valid_primitive = (!id && validUnit(value, FNumber | FPercent, SVGAttributeMode)); 122 break; 123 124 case CSSPropertyShapeRendering: 125 // auto | optimizeSpeed | crispEdges | geometricPrecision | inherit 126 if (id == CSSValueAuto || id == CSSValueOptimizespeed || 127 id == CSSValueCrispedges || id == CSSValueGeometricprecision) 128 valid_primitive = true; 129 break; 130 131 case CSSPropertyColorRendering: // auto | optimizeSpeed | optimizeQuality | inherit 132 if (id == CSSValueAuto || id == CSSValueOptimizespeed || 133 id == CSSValueOptimizequality) 134 valid_primitive = true; 135 break; 136 137 case CSSPropertyBufferedRendering: // auto | dynamic | static 138 if (id == CSSValueAuto || id == CSSValueDynamic || id == CSSValueStatic) 139 valid_primitive = true; 140 break; 141 142 case CSSPropertyColorProfile: // auto | sRGB | <name> | <uri> inherit 143 if (id == CSSValueAuto || id == CSSValueSrgb) 144 valid_primitive = true; 145 break; 146 147 case CSSPropertyColorInterpolation: // auto | sRGB | linearRGB | inherit 148 case CSSPropertyColorInterpolationFilters: 149 if (id == CSSValueAuto || id == CSSValueSrgb || id == CSSValueLinearrgb) 150 valid_primitive = true; 151 break; 152 153 /* Start of supported CSS properties with validation. This is needed for parseShortHand to work 154 * correctly and allows optimization in applyRule(..) 155 */ 156 157 case CSSPropertyTextAnchor: // start | middle | end | inherit 158 if (id == CSSValueStart || id == CSSValueMiddle || id == CSSValueEnd) 159 valid_primitive = true; 160 break; 161 162 case CSSPropertyGlyphOrientationVertical: // auto | <angle> | inherit 163 if (id == CSSValueAuto) { 164 valid_primitive = true; 165 break; 166 } 167 FALLTHROUGH; 168 169 case CSSPropertyGlyphOrientationHorizontal: // <angle> (restricted to _deg_ per SVG 1.1 spec) | inherit 170 if (value->unit == CSSPrimitiveValue::CSS_DEG || value->unit == CSSPrimitiveValue::CSS_NUMBER) { 171 parsedValue = CSSPrimitiveValue::create(value->fValue, CSSPrimitiveValue::CSS_DEG); 172 173 if (parsedValue) 174 m_valueList->next(); 175 } 176 break; 177 case CSSPropertyPaintOrder: 178 if (id == CSSValueNormal) 179 valid_primitive = true; 180 else 181 parsedValue = parsePaintOrder(); 182 break; 183 case CSSPropertyFill: // <paint> | inherit 184 case CSSPropertyStroke: // <paint> | inherit 185 { 186 if (id == CSSValueNone) 187 parsedValue = SVGPaint::createNone(); 188 else if (id == CSSValueCurrentcolor) 189 parsedValue = SVGPaint::createCurrentColor(); 190 else if ((id >= CSSValueActiveborder && id <= CSSValueWindowtext) || id == CSSValueMenu) 191 parsedValue = SVGPaint::createColor(RenderTheme::defaultTheme()->systemColor(id)); 192 else if (value->unit == CSSPrimitiveValue::CSS_URI) { 193 RGBA32 c = Color::transparent; 194 if (m_valueList->next()) { 195 if (parseColorFromValue(m_valueList->current(), c)) 196 parsedValue = SVGPaint::createURIAndColor(value->string, c); 197 else if (m_valueList->current()->id == CSSValueNone) 198 parsedValue = SVGPaint::createURIAndNone(value->string); 199 } 200 if (!parsedValue) 201 parsedValue = SVGPaint::createURI(value->string); 202 } else 203 parsedValue = parseSVGPaint(); 204 205 if (parsedValue) 206 m_valueList->next(); 207 } 208 break; 209 210 case CSSPropertyStopColor: // TODO : icccolor 211 case CSSPropertyFloodColor: 212 case CSSPropertyLightingColor: 213 if ((id >= CSSValueAqua && id <= CSSValueWindowtext) || 214 (id >= CSSValueAliceblue && id <= CSSValueYellowgreen)) 215 parsedValue = SVGColor::createFromString(value->string); 216 else if (id == CSSValueCurrentcolor) 217 parsedValue = SVGColor::createCurrentColor(); 218 else // TODO : svgcolor (iccColor) 219 parsedValue = parseSVGColor(); 220 221 if (parsedValue) 222 m_valueList->next(); 223 224 break; 225 226 case CSSPropertyVectorEffect: // none | non-scaling-stroke | inherit 227 if (id == CSSValueNone || id == CSSValueNonScalingStroke) 228 valid_primitive = true; 229 break; 230 231 case CSSPropertyWritingMode: 232 // lr-tb | rl_tb | tb-rl | lr | rl | tb | inherit 233 if (id == CSSValueLrTb || id == CSSValueRlTb || id == CSSValueTbRl || id == CSSValueLr || id == CSSValueRl || id == CSSValueTb) 234 valid_primitive = true; 235 break; 236 237 case CSSPropertyStrokeWidth: // <length> | inherit 238 case CSSPropertyStrokeDashoffset: 239 valid_primitive = validUnit(value, FLength | FPercent, SVGAttributeMode); 240 break; 241 case CSSPropertyStrokeDasharray: // none | <dasharray> | inherit 242 if (id == CSSValueNone) 243 valid_primitive = true; 244 else 245 parsedValue = parseSVGStrokeDasharray(); 246 247 break; 248 249 case CSSPropertyKerning: // auto | normal | <length> | inherit 250 if (id == CSSValueAuto || id == CSSValueNormal) 251 valid_primitive = true; 252 else 253 valid_primitive = validUnit(value, FLength, SVGAttributeMode); 254 break; 255 256 case CSSPropertyClipPath: // <uri> | none | inherit 257 case CSSPropertyFilter: 258 if (id == CSSValueNone) 259 valid_primitive = true; 260 else if (value->unit == CSSPrimitiveValue::CSS_URI) { 261 parsedValue = CSSPrimitiveValue::create(value->string, (CSSPrimitiveValue::UnitTypes) value->unit); 262 if (parsedValue) 263 m_valueList->next(); 264 } 265 break; 266 case CSSPropertyWebkitSvgShadow: 267 if (id == CSSValueNone) 268 valid_primitive = true; 269 else { 270 RefPtr<CSSValueList> shadowValueList = parseShadow(m_valueList.get(), propId); 271 if (shadowValueList) { 272 addProperty(propId, shadowValueList.release(), important); 273 m_valueList->next(); 274 return true; 275 } 276 return false; 277 } 278 break; 279 280 case CSSPropertyMaskType: // luminance | alpha | inherit 281 if (id == CSSValueLuminance || id == CSSValueAlpha) 282 valid_primitive = true; 283 break; 284 285 /* shorthand properties */ 286 case CSSPropertyMarker: 287 { 288 ShorthandScope scope(this, propId); 289 m_implicitShorthand = true; 290 if (!parseValue(CSSPropertyMarkerStart, important)) 291 return false; 292 if (m_valueList->current()) { 293 rollbackLastProperties(1); 294 return false; 295 } 296 CSSValue* value = m_parsedProperties.last().value(); 297 addProperty(CSSPropertyMarkerMid, value, important); 298 addProperty(CSSPropertyMarkerEnd, value, important); 299 m_implicitShorthand = false; 300 return true; 301 } 302 default: 303 // If you crash here, it's because you added a css property and are not handling it 304 // in either this switch statement or the one in CSSParser::parseValue 305 ASSERT_WITH_MESSAGE(0, "unimplemented propertyID: %d", propId); 306 return false; 307 } 308 309 if (valid_primitive) { 310 if (id != 0) 311 parsedValue = CSSPrimitiveValue::createIdentifier(id); 312 else if (value->unit == CSSPrimitiveValue::CSS_STRING) 313 parsedValue = CSSPrimitiveValue::create(value->string, (CSSPrimitiveValue::UnitTypes) value->unit); 314 else if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) 315 parsedValue = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); 316 else if (value->unit >= CSSParserValue::Q_EMS) 317 parsedValue = CSSPrimitiveValue::createAllowingMarginQuirk(value->fValue, CSSPrimitiveValue::CSS_EMS); 318 if (isCalculation(value)) { 319 // FIXME calc() http://webkit.org/b/16662 : actually create a CSSPrimitiveValue here, ie 320 // parsedValue = CSSPrimitiveValue::create(m_parsedCalculation.release()); 321 m_parsedCalculation.release(); 322 parsedValue = 0; 323 } 324 m_valueList->next(); 325 } 326 if (!parsedValue || (m_valueList->current() && !inShorthand())) 327 return false; 328 329 addProperty(propId, parsedValue.release(), important); 330 return true; 331} 332 333PassRefPtr<CSSValue> CSSParser::parseSVGStrokeDasharray() 334{ 335 RefPtr<CSSValueList> ret = CSSValueList::createCommaSeparated(); 336 CSSParserValue* value = m_valueList->current(); 337 bool valid_primitive = true; 338 while (value) { 339 valid_primitive = validUnit(value, FLength | FPercent | FNonNeg, SVGAttributeMode); 340 if (!valid_primitive) 341 break; 342 if (value->id != 0) 343 ret->append(CSSPrimitiveValue::createIdentifier(value->id)); 344 else if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ) 345 ret->append(CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit)); 346 value = m_valueList->next(); 347 if (value && value->unit == CSSParserValue::Operator && value->iValue == ',') 348 value = m_valueList->next(); 349 } 350 if (!valid_primitive) 351 return 0; 352 return ret.release(); 353} 354 355PassRefPtr<CSSValue> CSSParser::parseSVGPaint() 356{ 357 RGBA32 c = Color::transparent; 358 if (!parseColorFromValue(m_valueList->current(), c)) 359 return SVGPaint::createUnknown(); 360 return SVGPaint::createColor(Color(c)); 361} 362 363PassRefPtr<CSSValue> CSSParser::parseSVGColor() 364{ 365 RGBA32 c = Color::transparent; 366 if (!parseColorFromValue(m_valueList->current(), c)) 367 return 0; 368 return SVGColor::createFromColor(Color(c)); 369} 370 371PassRefPtr<CSSValue> CSSParser::parsePaintOrder() 372{ 373 CSSParserValue* value = m_valueList->current(); 374 375 Vector<CSSValueID> paintTypeList; 376 RefPtr<CSSPrimitiveValue> fill; 377 RefPtr<CSSPrimitiveValue> stroke; 378 RefPtr<CSSPrimitiveValue> markers; 379 while (value) { 380 if (value->id == CSSValueFill && !fill) 381 fill = CSSPrimitiveValue::createIdentifier(value->id); 382 else if (value->id == CSSValueStroke && !stroke) 383 stroke = CSSPrimitiveValue::createIdentifier(value->id); 384 else if (value->id == CSSValueMarkers && !markers) 385 markers = CSSPrimitiveValue::createIdentifier(value->id); 386 else 387 return nullptr; 388 paintTypeList.append(value->id); 389 value = m_valueList->next(); 390 } 391 392 // After parsing we serialize the paint-order list. Since it is not possible to 393 // pop a last list items from CSSValueList without bigger cost, we create the 394 // list after parsing. 395 CSSValueID firstPaintOrderType = paintTypeList.at(0); 396 RefPtr<CSSValueList> paintOrderList = CSSValueList::createSpaceSeparated(); 397 switch (firstPaintOrderType) { 398 case CSSValueFill: 399 FALLTHROUGH; 400 case CSSValueStroke: 401 paintOrderList->append(firstPaintOrderType == CSSValueFill ? fill.release() : stroke.release()); 402 if (paintTypeList.size() > 1) { 403 if (paintTypeList.at(1) == CSSValueMarkers) 404 paintOrderList->append(markers.release()); 405 } 406 break; 407 case CSSValueMarkers: 408 paintOrderList->append(markers.release()); 409 if (paintTypeList.size() > 1) { 410 if (paintTypeList.at(1) == CSSValueStroke) 411 paintOrderList->append(stroke.release()); 412 } 413 break; 414 default: 415 ASSERT_NOT_REACHED(); 416 } 417 return paintOrderList.release(); 418} 419 420} 421