dbf92c46ba659e1d8b7c61e9edf31b4106329a2d
[WebKit-https.git] / Source / WebCore / css / SVGCSSParser.cpp
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
34 namespace WebCore {
35
36 bool CSSParser::parseSVGValue(CSSPropertyID propId, bool important)
37 {
38     if (!m_valueList->current())
39         return false;
40     ValueWithCalculation valueWithCalculation(*m_valueList->current());
41
42     CSSValueID id = valueWithCalculation.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 = validateUnit(valueWithCalculation, 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 (valueWithCalculation.value().unit == CSSPrimitiveValue::CSS_URI) {
91             parsedValue = CSSPrimitiveValue::create(valueWithCalculation.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 = validateUnit(valueWithCalculation, 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 && validateUnit(valueWithCalculation, 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 (valueWithCalculation.value().unit == CSSPrimitiveValue::CSS_DEG || valueWithCalculation.value().unit == CSSPrimitiveValue::CSS_NUMBER) {
171             parsedValue = CSSPrimitiveValue::create(valueWithCalculation.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 (valueWithCalculation.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(valueWithCalculation.value().string, c);
197                     else if (m_valueList->current()->id == CSSValueNone)
198                         parsedValue = SVGPaint::createURIAndNone(valueWithCalculation.value().string);
199                 }
200                 if (!parsedValue)
201                     parsedValue = SVGPaint::createURI(valueWithCalculation.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(valueWithCalculation.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 = validateUnit(valueWithCalculation, 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 = validateUnit(valueWithCalculation, 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 (valueWithCalculation.value().unit == CSSPrimitiveValue::CSS_URI) {
261             parsedValue = CSSPrimitiveValue::create(valueWithCalculation.value().string, (CSSPrimitiveValue::UnitTypes) valueWithCalculation.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, 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     case CSSPropertyCx:
303     case CSSPropertyCy:
304     case CSSPropertyR:
305     case CSSPropertyRx:
306     case CSSPropertyRy:
307     case CSSPropertyX:
308     case CSSPropertyY:
309         valid_primitive = (!id && validateUnit(valueWithCalculation, FLength | FPercent));
310         break;
311     default:
312         // If you crash here, it's because you added a css property and are not handling it
313         // in either this switch statement or the one in CSSParser::parseValue
314         ASSERT_WITH_MESSAGE(0, "unimplemented propertyID: %d", propId);
315         return false;
316     }
317
318     if (valid_primitive) {
319         if (id != 0)
320             parsedValue = CSSPrimitiveValue::createIdentifier(id);
321         else if (valueWithCalculation.value().unit == CSSPrimitiveValue::CSS_STRING)
322             parsedValue = CSSPrimitiveValue::create(valueWithCalculation.value().string, (CSSPrimitiveValue::UnitTypes) valueWithCalculation.value().unit);
323         else if (valueWithCalculation.value().unit >= CSSPrimitiveValue::CSS_NUMBER && valueWithCalculation.value().unit <= CSSPrimitiveValue::CSS_KHZ)
324             parsedValue = CSSPrimitiveValue::create(valueWithCalculation.value().fValue, (CSSPrimitiveValue::UnitTypes) valueWithCalculation.value().unit);
325         else if (valueWithCalculation.value().unit >= CSSParserValue::Q_EMS)
326             parsedValue = CSSPrimitiveValue::createAllowingMarginQuirk(valueWithCalculation.value().fValue, CSSPrimitiveValue::CSS_EMS);
327         if (isCalculation(valueWithCalculation))
328             parsedValue = CSSPrimitiveValue::create(valueWithCalculation.calculation());
329         m_valueList->next();
330     }
331     if (!parsedValue || (m_valueList->current() && !inShorthand()))
332         return false;
333
334     addProperty(propId, parsedValue.release(), important);
335     return true;
336 }
337
338 PassRefPtr<CSSValue> CSSParser::parseSVGStrokeDasharray()
339 {
340     RefPtr<CSSValueList> ret = CSSValueList::createCommaSeparated();
341     CSSParserValue* value = m_valueList->current();
342     bool valid_primitive = true;
343     while (value) {
344         ValueWithCalculation valueWithCalculation(*value);
345         valid_primitive = validateUnit(valueWithCalculation, FLength | FPercent | FNonNeg, SVGAttributeMode);
346         if (!valid_primitive)
347             break;
348         // FIXME: This code doesn't handle calculated values.
349         if (value->id != 0)
350             ret->append(CSSPrimitiveValue::createIdentifier(value->id));
351         else if (value->unit >= CSSPrimitiveValue::CSS_NUMBER && value->unit <= CSSPrimitiveValue::CSS_KHZ)
352             ret->append(CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit));
353         value = m_valueList->next();
354         if (value && value->unit == CSSParserValue::Operator && value->iValue == ',')
355             value = m_valueList->next();
356     }
357     if (!valid_primitive)
358         return nullptr;
359     return ret.release();
360 }
361
362 PassRefPtr<CSSValue> CSSParser::parseSVGPaint()
363 {
364     RGBA32 c = Color::transparent;
365     if (!parseColorFromValue(*m_valueList->current(), c))
366         return SVGPaint::createUnknown();
367     return SVGPaint::createColor(Color(c));
368 }
369
370 PassRefPtr<CSSValue> CSSParser::parseSVGColor()
371 {
372     RGBA32 c = Color::transparent;
373     if (!parseColorFromValue(*m_valueList->current(), c))
374         return 0;
375     return SVGColor::createFromColor(Color(c));
376 }
377
378 PassRefPtr<CSSValue> CSSParser::parsePaintOrder()
379 {
380     CSSParserValue* value = m_valueList->current();
381
382     Vector<CSSValueID> paintTypeList;
383     RefPtr<CSSPrimitiveValue> fill;
384     RefPtr<CSSPrimitiveValue> stroke;
385     RefPtr<CSSPrimitiveValue> markers;
386     while (value) {
387         if (value->id == CSSValueFill && !fill)
388             fill = CSSPrimitiveValue::createIdentifier(value->id);
389         else if (value->id == CSSValueStroke && !stroke)
390             stroke = CSSPrimitiveValue::createIdentifier(value->id);
391         else if (value->id == CSSValueMarkers && !markers)
392             markers = CSSPrimitiveValue::createIdentifier(value->id);
393         else
394             return nullptr;
395         paintTypeList.append(value->id);
396         value = m_valueList->next();
397     }
398
399     // After parsing we serialize the paint-order list. Since it is not possible to
400     // pop a last list items from CSSValueList without bigger cost, we create the
401     // list after parsing. 
402     CSSValueID firstPaintOrderType = paintTypeList.at(0);
403     RefPtr<CSSValueList> paintOrderList = CSSValueList::createSpaceSeparated();
404     switch (firstPaintOrderType) {
405     case CSSValueFill:
406         FALLTHROUGH;
407     case CSSValueStroke:
408         paintOrderList->append(firstPaintOrderType == CSSValueFill ? fill.releaseNonNull() : stroke.releaseNonNull());
409         if (paintTypeList.size() > 1) {
410             if (paintTypeList.at(1) == CSSValueMarkers)
411                 paintOrderList->append(markers.releaseNonNull());
412         }
413         break;
414     case CSSValueMarkers:
415         paintOrderList->append(markers.releaseNonNull());
416         if (paintTypeList.size() > 1) {
417             if (paintTypeList.at(1) == CSSValueStroke)
418                 paintOrderList->append(stroke.releaseNonNull());
419         }
420         break;
421     default:
422         ASSERT_NOT_REACHED();
423     }
424     return paintOrderList.release();
425 }
426
427 }