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