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
33using namespace std;
34
35namespace WebCore {
36
37static bool isSystemColor(int id)
38{
39    return (id >= CSSValueActiveborder && id <= CSSValueWindowtext) || id == CSSValueMenu;
40}
41
42bool 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
331PassRefPtr<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
353PassRefPtr<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
361PassRefPtr<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 ]
370PassRefPtr<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