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