Home | History | Annotate | Download | only in resolver
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 2004-2005 Allan Sandfeld Jensen (kde (at) carewolf.com)
      4  * Copyright (C) 2006, 2007 Nicholas Shanks (webkit (at) nickshanks.com)
      5  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
      6  * Copyright (C) 2007 Alexey Proskuryakov <ap (at) webkit.org>
      7  * Copyright (C) 2007, 2008 Eric Seidel <eric (at) webkit.org>
      8  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      9  * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
     10  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
     11  *
     12  * This library is free software; you can redistribute it and/or
     13  * modify it under the terms of the GNU Library General Public
     14  * License as published by the Free Software Foundation; either
     15  * version 2 of the License, or (at your option) any later version.
     16  *
     17  * This library is distributed in the hope that it will be useful,
     18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     20  * Library General Public License for more details.
     21  *
     22  * You should have received a copy of the GNU Library General Public License
     23  * along with this library; see the file COPYING.LIB.  If not, write to
     24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     25  * Boston, MA 02110-1301, USA.
     26  */
     27 
     28 #include "config.h"
     29 #include "core/css/resolver/CSSToStyleMap.h"
     30 
     31 #include "core/CSSValueKeywords.h"
     32 #include "core/animation/css/CSSAnimationData.h"
     33 #include "core/css/CSSBorderImageSliceValue.h"
     34 #include "core/css/CSSPrimitiveValue.h"
     35 #include "core/css/CSSPrimitiveValueMappings.h"
     36 #include "core/css/CSSTimingFunctionValue.h"
     37 #include "core/css/Pair.h"
     38 #include "core/css/Rect.h"
     39 #include "core/css/resolver/StyleResolverState.h"
     40 #include "core/rendering/style/BorderImageLengthBox.h"
     41 #include "core/rendering/style/FillLayer.h"
     42 
     43 namespace blink {
     44 
     45 const CSSToLengthConversionData& CSSToStyleMap::cssToLengthConversionData() const
     46 {
     47     return m_state.cssToLengthConversionData();
     48 }
     49 
     50 PassRefPtr<StyleImage> CSSToStyleMap::styleImage(CSSPropertyID propertyId, CSSValue* value)
     51 {
     52     return m_elementStyleResources.styleImage(m_state.document(), m_state.document().textLinkColors(), m_state.style()->color(), propertyId, value);
     53 }
     54 
     55 void CSSToStyleMap::mapFillAttachment(FillLayer* layer, CSSValue* value) const
     56 {
     57     if (value->isInitialValue()) {
     58         layer->setAttachment(FillLayer::initialFillAttachment(layer->type()));
     59         return;
     60     }
     61 
     62     if (!value->isPrimitiveValue())
     63         return;
     64 
     65     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
     66     switch (primitiveValue->getValueID()) {
     67     case CSSValueFixed:
     68         layer->setAttachment(FixedBackgroundAttachment);
     69         break;
     70     case CSSValueScroll:
     71         layer->setAttachment(ScrollBackgroundAttachment);
     72         break;
     73     case CSSValueLocal:
     74         layer->setAttachment(LocalBackgroundAttachment);
     75         break;
     76     default:
     77         return;
     78     }
     79 }
     80 
     81 void CSSToStyleMap::mapFillClip(FillLayer* layer, CSSValue* value) const
     82 {
     83     if (value->isInitialValue()) {
     84         layer->setClip(FillLayer::initialFillClip(layer->type()));
     85         return;
     86     }
     87 
     88     if (!value->isPrimitiveValue())
     89         return;
     90 
     91     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
     92     layer->setClip(*primitiveValue);
     93 }
     94 
     95 void CSSToStyleMap::mapFillComposite(FillLayer* layer, CSSValue* value) const
     96 {
     97     if (value->isInitialValue()) {
     98         layer->setComposite(FillLayer::initialFillComposite(layer->type()));
     99         return;
    100     }
    101 
    102     if (!value->isPrimitiveValue())
    103         return;
    104 
    105     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    106     layer->setComposite(*primitiveValue);
    107 }
    108 
    109 void CSSToStyleMap::mapFillBlendMode(FillLayer* layer, CSSValue* value) const
    110 {
    111     if (value->isInitialValue()) {
    112         layer->setBlendMode(FillLayer::initialFillBlendMode(layer->type()));
    113         return;
    114     }
    115 
    116     if (!value->isPrimitiveValue())
    117         return;
    118 
    119     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    120     layer->setBlendMode(*primitiveValue);
    121 }
    122 
    123 void CSSToStyleMap::mapFillOrigin(FillLayer* layer, CSSValue* value) const
    124 {
    125     if (value->isInitialValue()) {
    126         layer->setOrigin(FillLayer::initialFillOrigin(layer->type()));
    127         return;
    128     }
    129 
    130     if (!value->isPrimitiveValue())
    131         return;
    132 
    133     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    134     layer->setOrigin(*primitiveValue);
    135 }
    136 
    137 
    138 void CSSToStyleMap::mapFillImage(FillLayer* layer, CSSValue* value)
    139 {
    140     if (value->isInitialValue()) {
    141         layer->setImage(FillLayer::initialFillImage(layer->type()));
    142         return;
    143     }
    144 
    145     CSSPropertyID property = layer->type() == BackgroundFillLayer ? CSSPropertyBackgroundImage : CSSPropertyWebkitMaskImage;
    146     layer->setImage(styleImage(property, value));
    147 }
    148 
    149 void CSSToStyleMap::mapFillRepeatX(FillLayer* layer, CSSValue* value) const
    150 {
    151     if (value->isInitialValue()) {
    152         layer->setRepeatX(FillLayer::initialFillRepeatX(layer->type()));
    153         return;
    154     }
    155 
    156     if (!value->isPrimitiveValue())
    157         return;
    158 
    159     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    160     layer->setRepeatX(*primitiveValue);
    161 }
    162 
    163 void CSSToStyleMap::mapFillRepeatY(FillLayer* layer, CSSValue* value) const
    164 {
    165     if (value->isInitialValue()) {
    166         layer->setRepeatY(FillLayer::initialFillRepeatY(layer->type()));
    167         return;
    168     }
    169 
    170     if (!value->isPrimitiveValue())
    171         return;
    172 
    173     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    174     layer->setRepeatY(*primitiveValue);
    175 }
    176 
    177 void CSSToStyleMap::mapFillSize(FillLayer* layer, CSSValue* value) const
    178 {
    179     if (value->isInitialValue()) {
    180         layer->setSizeType(FillLayer::initialFillSizeType(layer->type()));
    181         layer->setSizeLength(FillLayer::initialFillSizeLength(layer->type()));
    182         return;
    183     }
    184 
    185     if (!value->isPrimitiveValue())
    186         return;
    187 
    188     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    189     if (primitiveValue->getValueID() == CSSValueContain)
    190         layer->setSizeType(Contain);
    191     else if (primitiveValue->getValueID() == CSSValueCover)
    192         layer->setSizeType(Cover);
    193     else
    194         layer->setSizeType(SizeLength);
    195 
    196     LengthSize b = FillLayer::initialFillSizeLength(layer->type());
    197 
    198     if (primitiveValue->getValueID() == CSSValueContain || primitiveValue->getValueID() == CSSValueCover) {
    199         layer->setSizeLength(b);
    200         return;
    201     }
    202 
    203     Length firstLength;
    204     Length secondLength;
    205 
    206     if (Pair* pair = primitiveValue->getPairValue()) {
    207         firstLength = pair->first()->convertToLength<AnyConversion>(cssToLengthConversionData());
    208         secondLength = pair->second()->convertToLength<AnyConversion>(cssToLengthConversionData());
    209     } else {
    210         firstLength = primitiveValue->convertToLength<AnyConversion>(cssToLengthConversionData());
    211         secondLength = Length();
    212     }
    213 
    214     b.setWidth(firstLength);
    215     b.setHeight(secondLength);
    216     layer->setSizeLength(b);
    217 }
    218 
    219 void CSSToStyleMap::mapFillXPosition(FillLayer* layer, CSSValue* value) const
    220 {
    221     if (value->isInitialValue()) {
    222         layer->setXPosition(FillLayer::initialFillXPosition(layer->type()));
    223         return;
    224     }
    225 
    226     if (!value->isPrimitiveValue())
    227         return;
    228 
    229     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    230     Pair* pair = primitiveValue->getPairValue();
    231     if (pair)
    232         primitiveValue = pair->second();
    233 
    234     Length length = primitiveValue->convertToLength<FixedConversion | PercentConversion>(cssToLengthConversionData());
    235 
    236     layer->setXPosition(length);
    237     if (pair)
    238         layer->setBackgroundXOrigin(*(pair->first()));
    239 }
    240 
    241 void CSSToStyleMap::mapFillYPosition(FillLayer* layer, CSSValue* value) const
    242 {
    243     if (value->isInitialValue()) {
    244         layer->setYPosition(FillLayer::initialFillYPosition(layer->type()));
    245         return;
    246     }
    247 
    248     if (!value->isPrimitiveValue())
    249         return;
    250 
    251     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    252     Pair* pair = primitiveValue->getPairValue();
    253     if (pair)
    254         primitiveValue = pair->second();
    255 
    256     Length length = primitiveValue->convertToLength<FixedConversion | PercentConversion>(cssToLengthConversionData());
    257 
    258     layer->setYPosition(length);
    259     if (pair)
    260         layer->setBackgroundYOrigin(*(pair->first()));
    261 }
    262 
    263 void CSSToStyleMap::mapFillMaskSourceType(FillLayer* layer, CSSValue* value) const
    264 {
    265     EMaskSourceType type = FillLayer::initialFillMaskSourceType(layer->type());
    266     if (value->isInitialValue()) {
    267         layer->setMaskSourceType(type);
    268         return;
    269     }
    270 
    271     if (!value->isPrimitiveValue())
    272         return;
    273 
    274     switch (toCSSPrimitiveValue(value)->getValueID()) {
    275     case CSSValueAlpha:
    276         type = MaskAlpha;
    277         break;
    278     case CSSValueLuminance:
    279         type = MaskLuminance;
    280         break;
    281     case CSSValueAuto:
    282         break;
    283     default:
    284         ASSERT_NOT_REACHED();
    285     }
    286 
    287     layer->setMaskSourceType(type);
    288 }
    289 
    290 double CSSToStyleMap::mapAnimationDelay(CSSValue* value)
    291 {
    292     if (value->isInitialValue())
    293         return CSSTimingData::initialDelay();
    294     return toCSSPrimitiveValue(value)->computeSeconds();
    295 }
    296 
    297 Timing::PlaybackDirection CSSToStyleMap::mapAnimationDirection(CSSValue* value)
    298 {
    299     if (value->isInitialValue())
    300         return CSSAnimationData::initialDirection();
    301 
    302     switch (toCSSPrimitiveValue(value)->getValueID()) {
    303     case CSSValueNormal:
    304         return Timing::PlaybackDirectionNormal;
    305     case CSSValueAlternate:
    306         return Timing::PlaybackDirectionAlternate;
    307     case CSSValueReverse:
    308         return Timing::PlaybackDirectionReverse;
    309     case CSSValueAlternateReverse:
    310         return Timing::PlaybackDirectionAlternateReverse;
    311     default:
    312         ASSERT_NOT_REACHED();
    313         return CSSAnimationData::initialDirection();
    314     }
    315 }
    316 
    317 double CSSToStyleMap::mapAnimationDuration(CSSValue* value)
    318 {
    319     if (value->isInitialValue())
    320         return CSSTimingData::initialDuration();
    321     return toCSSPrimitiveValue(value)->computeSeconds();
    322 }
    323 
    324 Timing::FillMode CSSToStyleMap::mapAnimationFillMode(CSSValue* value)
    325 {
    326     if (value->isInitialValue())
    327         return CSSAnimationData::initialFillMode();
    328 
    329     switch (toCSSPrimitiveValue(value)->getValueID()) {
    330     case CSSValueNone:
    331         return Timing::FillModeNone;
    332     case CSSValueForwards:
    333         return Timing::FillModeForwards;
    334     case CSSValueBackwards:
    335         return Timing::FillModeBackwards;
    336     case CSSValueBoth:
    337         return Timing::FillModeBoth;
    338     default:
    339         ASSERT_NOT_REACHED();
    340         return CSSAnimationData::initialFillMode();
    341     }
    342 }
    343 
    344 double CSSToStyleMap::mapAnimationIterationCount(CSSValue* value)
    345 {
    346     if (value->isInitialValue())
    347         return CSSAnimationData::initialIterationCount();
    348     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    349     if (primitiveValue->getValueID() == CSSValueInfinite)
    350         return std::numeric_limits<double>::infinity();
    351     return primitiveValue->getFloatValue();
    352 }
    353 
    354 AtomicString CSSToStyleMap::mapAnimationName(CSSValue* value)
    355 {
    356     if (value->isInitialValue())
    357         return CSSAnimationData::initialName();
    358     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    359     if (primitiveValue->getValueID() == CSSValueNone)
    360         return CSSAnimationData::initialName();
    361     return AtomicString(primitiveValue->getStringValue());
    362 }
    363 
    364 EAnimPlayState CSSToStyleMap::mapAnimationPlayState(CSSValue* value)
    365 {
    366     if (value->isInitialValue())
    367         return CSSAnimationData::initialPlayState();
    368     if (toCSSPrimitiveValue(value)->getValueID() == CSSValuePaused)
    369         return AnimPlayStatePaused;
    370     ASSERT(toCSSPrimitiveValue(value)->getValueID() == CSSValueRunning);
    371     return AnimPlayStatePlaying;
    372 }
    373 
    374 CSSTransitionData::TransitionProperty CSSToStyleMap::mapAnimationProperty(CSSValue* value)
    375 {
    376     if (value->isInitialValue())
    377         return CSSTransitionData::initialProperty();
    378     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    379     if (primitiveValue->isString())
    380         return CSSTransitionData::TransitionProperty(primitiveValue->getStringValue());
    381     if (primitiveValue->getValueID() == CSSValueAll)
    382         return CSSTransitionData::TransitionProperty(CSSTransitionData::TransitionAll);
    383     if (primitiveValue->getValueID() == CSSValueNone)
    384         return CSSTransitionData::TransitionProperty(CSSTransitionData::TransitionNone);
    385     return CSSTransitionData::TransitionProperty(primitiveValue->getPropertyID());
    386 }
    387 
    388 PassRefPtr<TimingFunction> CSSToStyleMap::mapAnimationTimingFunction(CSSValue* value, bool allowStepMiddle)
    389 {
    390     // FIXME: We should probably only call into this function with a valid
    391     // single timing function value which isn't initial or inherit. We can
    392     // currently get into here with initial since the parser expands unset
    393     // properties in shorthands to initial.
    394 
    395     if (value->isPrimitiveValue()) {
    396         CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    397         switch (primitiveValue->getValueID()) {
    398         case CSSValueLinear:
    399             return LinearTimingFunction::shared();
    400         case CSSValueEase:
    401             return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease);
    402         case CSSValueEaseIn:
    403             return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn);
    404         case CSSValueEaseOut:
    405             return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut);
    406         case CSSValueEaseInOut:
    407             return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut);
    408         case CSSValueStepStart:
    409             return StepsTimingFunction::preset(StepsTimingFunction::Start);
    410         case CSSValueStepMiddle:
    411             if (allowStepMiddle)
    412                 return StepsTimingFunction::preset(StepsTimingFunction::Middle);
    413             return CSSTimingData::initialTimingFunction();
    414         case CSSValueStepEnd:
    415             return StepsTimingFunction::preset(StepsTimingFunction::End);
    416         default:
    417             ASSERT_NOT_REACHED();
    418             return CSSTimingData::initialTimingFunction();
    419         }
    420     }
    421 
    422     if (value->isCubicBezierTimingFunctionValue()) {
    423         CSSCubicBezierTimingFunctionValue* cubicTimingFunction = toCSSCubicBezierTimingFunctionValue(value);
    424         return CubicBezierTimingFunction::create(cubicTimingFunction->x1(), cubicTimingFunction->y1(), cubicTimingFunction->x2(), cubicTimingFunction->y2());
    425     }
    426 
    427     if (value->isInitialValue())
    428         return CSSTimingData::initialTimingFunction();
    429 
    430     CSSStepsTimingFunctionValue* stepsTimingFunction = toCSSStepsTimingFunctionValue(value);
    431     if (stepsTimingFunction->stepAtPosition() == StepsTimingFunction::Middle && !allowStepMiddle)
    432         return CSSTimingData::initialTimingFunction();
    433     return StepsTimingFunction::create(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtPosition());
    434 }
    435 
    436 void CSSToStyleMap::mapNinePieceImage(RenderStyle* mutableStyle, CSSPropertyID property, CSSValue* value, NinePieceImage& image)
    437 {
    438     // If we're not a value list, then we are "none" and don't need to alter the empty image at all.
    439     if (!value || !value->isValueList())
    440         return;
    441 
    442     // Retrieve the border image value.
    443     CSSValueList* borderImage = toCSSValueList(value);
    444 
    445     // Set the image (this kicks off the load).
    446     CSSPropertyID imageProperty;
    447     if (property == CSSPropertyWebkitBorderImage)
    448         imageProperty = CSSPropertyBorderImageSource;
    449     else if (property == CSSPropertyWebkitMaskBoxImage)
    450         imageProperty = CSSPropertyWebkitMaskBoxImageSource;
    451     else
    452         imageProperty = property;
    453 
    454     for (unsigned i = 0 ; i < borderImage->length() ; ++i) {
    455         CSSValue* current = borderImage->item(i);
    456 
    457         if (current->isImageValue() || current->isImageGeneratorValue() || current->isImageSetValue())
    458             image.setImage(styleImage(imageProperty, current));
    459         else if (current->isBorderImageSliceValue())
    460             mapNinePieceImageSlice(current, image);
    461         else if (current->isValueList()) {
    462             CSSValueList* slashList = toCSSValueList(current);
    463             size_t length = slashList->length();
    464             // Map in the image slices.
    465             if (length && slashList->item(0)->isBorderImageSliceValue())
    466                 mapNinePieceImageSlice(slashList->item(0), image);
    467 
    468             // Map in the border slices.
    469             if (length > 1)
    470                 image.setBorderSlices(mapNinePieceImageQuad(slashList->item(1)));
    471 
    472             // Map in the outset.
    473             if (length > 2)
    474                 image.setOutset(mapNinePieceImageQuad(slashList->item(2)));
    475         } else if (current->isPrimitiveValue()) {
    476             // Set the appropriate rules for stretch/round/repeat of the slices.
    477             mapNinePieceImageRepeat(current, image);
    478         }
    479     }
    480 
    481     if (property == CSSPropertyWebkitBorderImage) {
    482         // We have to preserve the legacy behavior of -webkit-border-image and make the border slices
    483         // also set the border widths. We don't need to worry about percentages, since we don't even support
    484         // those on real borders yet.
    485         if (image.borderSlices().top().isLength() && image.borderSlices().top().length().isFixed())
    486             mutableStyle->setBorderTopWidth(image.borderSlices().top().length().value());
    487         if (image.borderSlices().right().isLength() && image.borderSlices().right().length().isFixed())
    488             mutableStyle->setBorderRightWidth(image.borderSlices().right().length().value());
    489         if (image.borderSlices().bottom().isLength() && image.borderSlices().bottom().length().isFixed())
    490             mutableStyle->setBorderBottomWidth(image.borderSlices().bottom().length().value());
    491         if (image.borderSlices().left().isLength() && image.borderSlices().left().length().isFixed())
    492             mutableStyle->setBorderLeftWidth(image.borderSlices().left().length().value());
    493     }
    494 }
    495 
    496 void CSSToStyleMap::mapNinePieceImageSlice(CSSValue* value, NinePieceImage& image) const
    497 {
    498     if (!value || !value->isBorderImageSliceValue())
    499         return;
    500 
    501     // Retrieve the border image value.
    502     CSSBorderImageSliceValue* borderImageSlice = toCSSBorderImageSliceValue(value);
    503 
    504     // Set up a length box to represent our image slices.
    505     LengthBox box;
    506     Quad* slices = borderImageSlice->slices();
    507     if (slices->top()->isPercentage())
    508         box.m_top = Length(slices->top()->getDoubleValue(), Percent);
    509     else
    510         box.m_top = Length(slices->top()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
    511     if (slices->bottom()->isPercentage())
    512         box.m_bottom = Length(slices->bottom()->getDoubleValue(), Percent);
    513     else
    514         box.m_bottom = Length((int)slices->bottom()->getFloatValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
    515     if (slices->left()->isPercentage())
    516         box.m_left = Length(slices->left()->getDoubleValue(), Percent);
    517     else
    518         box.m_left = Length(slices->left()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
    519     if (slices->right()->isPercentage())
    520         box.m_right = Length(slices->right()->getDoubleValue(), Percent);
    521     else
    522         box.m_right = Length(slices->right()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
    523     image.setImageSlices(box);
    524 
    525     // Set our fill mode.
    526     image.setFill(borderImageSlice->m_fill);
    527 }
    528 
    529 static BorderImageLength toBorderImageLength(CSSPrimitiveValue& value, const CSSToLengthConversionData& conversionData)
    530 {
    531     if (value.isNumber())
    532         return value.getDoubleValue();
    533     if (value.isPercentage())
    534         return Length(value.getDoubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
    535     if (value.getValueID() != CSSValueAuto)
    536         return value.computeLength<Length>(conversionData);
    537     return Length(Auto);
    538 }
    539 
    540 BorderImageLengthBox CSSToStyleMap::mapNinePieceImageQuad(CSSValue* value) const
    541 {
    542     if (!value || !value->isPrimitiveValue())
    543         return BorderImageLengthBox(Length(Auto));
    544 
    545     Quad* slices = toCSSPrimitiveValue(value)->getQuadValue();
    546 
    547     // Set up a border image length box to represent our image slices.
    548     return BorderImageLengthBox(
    549         toBorderImageLength(*slices->top(), cssToLengthConversionData()),
    550         toBorderImageLength(*slices->right(), cssToLengthConversionData()),
    551         toBorderImageLength(*slices->bottom(), cssToLengthConversionData()),
    552         toBorderImageLength(*slices->left(), cssToLengthConversionData()));
    553 }
    554 
    555 void CSSToStyleMap::mapNinePieceImageRepeat(CSSValue* value, NinePieceImage& image) const
    556 {
    557     if (!value || !value->isPrimitiveValue())
    558         return;
    559 
    560     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
    561     Pair* pair = primitiveValue->getPairValue();
    562     if (!pair || !pair->first() || !pair->second())
    563         return;
    564 
    565     CSSValueID firstIdentifier = pair->first()->getValueID();
    566     CSSValueID secondIdentifier = pair->second()->getValueID();
    567 
    568     ENinePieceImageRule horizontalRule;
    569     switch (firstIdentifier) {
    570     case CSSValueStretch:
    571         horizontalRule = StretchImageRule;
    572         break;
    573     case CSSValueRound:
    574         horizontalRule = RoundImageRule;
    575         break;
    576     case CSSValueSpace:
    577         horizontalRule = SpaceImageRule;
    578         break;
    579     default: // CSSValueRepeat
    580         horizontalRule = RepeatImageRule;
    581         break;
    582     }
    583     image.setHorizontalRule(horizontalRule);
    584 
    585     ENinePieceImageRule verticalRule;
    586     switch (secondIdentifier) {
    587     case CSSValueStretch:
    588         verticalRule = StretchImageRule;
    589         break;
    590     case CSSValueRound:
    591         verticalRule = RoundImageRule;
    592         break;
    593     case CSSValueSpace:
    594         verticalRule = SpaceImageRule;
    595         break;
    596     default: // CSSValueRepeat
    597         verticalRule = RepeatImageRule;
    598         break;
    599     }
    600     image.setVerticalRule(verticalRule);
    601 }
    602 
    603 };
    604