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 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
isSystemColor(int id)37 static bool isSystemColor(int id)
38 {
39 return (id >= CSSValueActiveborder && id <= CSSValueWindowtext) || id == CSSValueMenu;
40 }
41
parseSVGValue(CSSPropertyID propId,bool important)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
parseSVGStrokeDasharray()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
parseSVGPaint()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
parseSVGColor()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 ]
parsePaintOrder() const370 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