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