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