Home | History | Annotate | Download | only in css
      1 /*
      2     Copyright (C) 2008 Eric Seidel <eric (at) webkit.org>
      3     Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      4                   2004, 2005, 2007, 2010 Rob Buis <buis (at) 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 
     33 using namespace std;
     34 
     35 namespace WebCore {
     36 
     37 static bool isSystemColor(int id)
     38 {
     39     return (id >= CSSValueActiveborder && id <= CSSValueWindowtext) || id == CSSValueMenu;
     40 }
     41 
     42 bool 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 
    331 PassRefPtr<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 
    353 PassRefPtr<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 
    361 PassRefPtr<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 ]
    370 PassRefPtr<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