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 WebCore { 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(CSSPropertyID, 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(CSSPropertyID, 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(CSSPropertyID, 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(CSSPropertyID, 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(CSSPropertyID, 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(CSSPropertyID property, FillLayer* layer, CSSValue* value) 139 { 140 if (value->isInitialValue()) { 141 layer->setImage(FillLayer::initialFillImage(layer->type())); 142 return; 143 } 144 145 layer->setImage(styleImage(property, value)); 146 } 147 148 void CSSToStyleMap::mapFillRepeatX(CSSPropertyID, FillLayer* layer, CSSValue* value) const 149 { 150 if (value->isInitialValue()) { 151 layer->setRepeatX(FillLayer::initialFillRepeatX(layer->type())); 152 return; 153 } 154 155 if (!value->isPrimitiveValue()) 156 return; 157 158 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 159 layer->setRepeatX(*primitiveValue); 160 } 161 162 void CSSToStyleMap::mapFillRepeatY(CSSPropertyID, FillLayer* layer, CSSValue* value) const 163 { 164 if (value->isInitialValue()) { 165 layer->setRepeatY(FillLayer::initialFillRepeatY(layer->type())); 166 return; 167 } 168 169 if (!value->isPrimitiveValue()) 170 return; 171 172 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 173 layer->setRepeatY(*primitiveValue); 174 } 175 176 void CSSToStyleMap::mapFillSize(CSSPropertyID, FillLayer* layer, CSSValue* value) const 177 { 178 if (value->isInitialValue()) { 179 layer->setSizeType(FillLayer::initialFillSizeType(layer->type())); 180 layer->setSizeLength(FillLayer::initialFillSizeLength(layer->type())); 181 return; 182 } 183 184 if (!value->isPrimitiveValue()) 185 return; 186 187 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 188 if (primitiveValue->getValueID() == CSSValueContain) 189 layer->setSizeType(Contain); 190 else if (primitiveValue->getValueID() == CSSValueCover) 191 layer->setSizeType(Cover); 192 else 193 layer->setSizeType(SizeLength); 194 195 LengthSize b = FillLayer::initialFillSizeLength(layer->type()); 196 197 if (primitiveValue->getValueID() == CSSValueContain || primitiveValue->getValueID() == CSSValueCover) { 198 layer->setSizeLength(b); 199 return; 200 } 201 202 Length firstLength; 203 Length secondLength; 204 205 if (Pair* pair = primitiveValue->getPairValue()) { 206 firstLength = pair->first()->convertToLength<AnyConversion>(cssToLengthConversionData()); 207 secondLength = pair->second()->convertToLength<AnyConversion>(cssToLengthConversionData()); 208 } else { 209 firstLength = primitiveValue->convertToLength<AnyConversion>(cssToLengthConversionData()); 210 secondLength = Length(); 211 } 212 213 b.setWidth(firstLength); 214 b.setHeight(secondLength); 215 layer->setSizeLength(b); 216 } 217 218 void CSSToStyleMap::mapFillXPosition(CSSPropertyID propertyID, FillLayer* layer, CSSValue* value) const 219 { 220 if (value->isInitialValue()) { 221 layer->setXPosition(FillLayer::initialFillXPosition(layer->type())); 222 return; 223 } 224 225 if (!value->isPrimitiveValue()) 226 return; 227 228 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 229 Pair* pair = primitiveValue->getPairValue(); 230 if (pair) { 231 ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionX || propertyID == CSSPropertyWebkitMaskPositionX); 232 primitiveValue = pair->second(); 233 } 234 235 Length length = primitiveValue->convertToLength<FixedConversion | PercentConversion>(cssToLengthConversionData()); 236 237 layer->setXPosition(length); 238 if (pair) 239 layer->setBackgroundXOrigin(*(pair->first())); 240 } 241 242 void CSSToStyleMap::mapFillYPosition(CSSPropertyID propertyID, FillLayer* layer, CSSValue* value) const 243 { 244 if (value->isInitialValue()) { 245 layer->setYPosition(FillLayer::initialFillYPosition(layer->type())); 246 return; 247 } 248 249 if (!value->isPrimitiveValue()) 250 return; 251 252 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 253 Pair* pair = primitiveValue->getPairValue(); 254 if (pair) { 255 ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionY || propertyID == CSSPropertyWebkitMaskPositionY); 256 primitiveValue = pair->second(); 257 } 258 259 Length length = primitiveValue->convertToLength<FixedConversion | PercentConversion>(cssToLengthConversionData()); 260 261 layer->setYPosition(length); 262 if (pair) 263 layer->setBackgroundYOrigin(*(pair->first())); 264 } 265 266 void CSSToStyleMap::mapFillMaskSourceType(CSSPropertyID, FillLayer* layer, CSSValue* value) 267 { 268 EMaskSourceType type = FillLayer::initialFillMaskSourceType(layer->type()); 269 if (value->isInitialValue()) { 270 layer->setMaskSourceType(type); 271 return; 272 } 273 274 if (!value->isPrimitiveValue()) 275 return; 276 277 switch (toCSSPrimitiveValue(value)->getValueID()) { 278 case CSSValueAlpha: 279 type = MaskAlpha; 280 break; 281 case CSSValueLuminance: 282 type = MaskLuminance; 283 break; 284 case CSSValueAuto: 285 break; 286 default: 287 ASSERT_NOT_REACHED(); 288 } 289 290 layer->setMaskSourceType(type); 291 } 292 293 double CSSToStyleMap::mapAnimationDelay(CSSValue* value) 294 { 295 if (value->isInitialValue()) 296 return CSSTimingData::initialDelay(); 297 return toCSSPrimitiveValue(value)->computeTime<double, CSSPrimitiveValue::Seconds>(); 298 } 299 300 Timing::PlaybackDirection CSSToStyleMap::mapAnimationDirection(CSSValue* value) 301 { 302 if (value->isInitialValue()) 303 return CSSAnimationData::initialDirection(); 304 305 switch (toCSSPrimitiveValue(value)->getValueID()) { 306 case CSSValueNormal: 307 return Timing::PlaybackDirectionNormal; 308 case CSSValueAlternate: 309 return Timing::PlaybackDirectionAlternate; 310 case CSSValueReverse: 311 return Timing::PlaybackDirectionReverse; 312 case CSSValueAlternateReverse: 313 return Timing::PlaybackDirectionAlternateReverse; 314 default: 315 ASSERT_NOT_REACHED(); 316 return CSSAnimationData::initialDirection(); 317 } 318 } 319 320 double CSSToStyleMap::mapAnimationDuration(CSSValue* value) 321 { 322 if (value->isInitialValue()) 323 return CSSTimingData::initialDuration(); 324 return toCSSPrimitiveValue(value)->computeTime<double, CSSPrimitiveValue::Seconds>(); 325 } 326 327 Timing::FillMode CSSToStyleMap::mapAnimationFillMode(CSSValue* value) 328 { 329 if (value->isInitialValue()) 330 return CSSAnimationData::initialFillMode(); 331 332 switch (toCSSPrimitiveValue(value)->getValueID()) { 333 case CSSValueNone: 334 return Timing::FillModeNone; 335 case CSSValueForwards: 336 return Timing::FillModeForwards; 337 case CSSValueBackwards: 338 return Timing::FillModeBackwards; 339 case CSSValueBoth: 340 return Timing::FillModeBoth; 341 default: 342 ASSERT_NOT_REACHED(); 343 return CSSAnimationData::initialFillMode(); 344 } 345 } 346 347 double CSSToStyleMap::mapAnimationIterationCount(CSSValue* value) 348 { 349 if (value->isInitialValue()) 350 return CSSAnimationData::initialIterationCount(); 351 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 352 if (primitiveValue->getValueID() == CSSValueInfinite) 353 return std::numeric_limits<double>::infinity(); 354 return primitiveValue->getFloatValue(); 355 } 356 357 AtomicString CSSToStyleMap::mapAnimationName(CSSValue* value) 358 { 359 if (value->isInitialValue()) 360 return CSSAnimationData::initialName(); 361 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 362 if (primitiveValue->getValueID() == CSSValueNone) 363 return CSSAnimationData::initialName(); 364 return AtomicString(primitiveValue->getStringValue()); 365 } 366 367 EAnimPlayState CSSToStyleMap::mapAnimationPlayState(CSSValue* value) 368 { 369 if (value->isInitialValue()) 370 return CSSAnimationData::initialPlayState(); 371 if (toCSSPrimitiveValue(value)->getValueID() == CSSValuePaused) 372 return AnimPlayStatePaused; 373 ASSERT(toCSSPrimitiveValue(value)->getValueID() == CSSValueRunning); 374 return AnimPlayStatePlaying; 375 } 376 377 CSSTransitionData::TransitionProperty CSSToStyleMap::mapAnimationProperty(CSSValue* value) 378 { 379 if (value->isInitialValue()) 380 return CSSTransitionData::initialProperty(); 381 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 382 if (primitiveValue->isString()) 383 return CSSTransitionData::TransitionProperty(primitiveValue->getStringValue()); 384 if (primitiveValue->getValueID() == CSSValueAll) 385 return CSSTransitionData::TransitionProperty(CSSTransitionData::TransitionAll); 386 if (primitiveValue->getValueID() == CSSValueNone) 387 return CSSTransitionData::TransitionProperty(CSSTransitionData::TransitionNone); 388 return CSSTransitionData::TransitionProperty(primitiveValue->getPropertyID()); 389 } 390 391 PassRefPtr<TimingFunction> CSSToStyleMap::mapAnimationTimingFunction(CSSValue* value, bool allowStepMiddle) 392 { 393 // FIXME: We should probably only call into this function with a valid 394 // single timing function value which isn't initial or inherit. We can 395 // currently get into here with initial since the parser expands unset 396 // properties in shorthands to initial. 397 398 if (value->isPrimitiveValue()) { 399 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 400 switch (primitiveValue->getValueID()) { 401 case CSSValueLinear: 402 return LinearTimingFunction::shared(); 403 case CSSValueEase: 404 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease); 405 case CSSValueEaseIn: 406 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); 407 case CSSValueEaseOut: 408 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); 409 case CSSValueEaseInOut: 410 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); 411 case CSSValueStepStart: 412 return StepsTimingFunction::preset(StepsTimingFunction::Start); 413 case CSSValueStepMiddle: 414 if (allowStepMiddle) 415 return StepsTimingFunction::preset(StepsTimingFunction::Middle); 416 return CSSTimingData::initialTimingFunction(); 417 case CSSValueStepEnd: 418 return StepsTimingFunction::preset(StepsTimingFunction::End); 419 default: 420 ASSERT_NOT_REACHED(); 421 return CSSTimingData::initialTimingFunction(); 422 } 423 } 424 425 if (value->isCubicBezierTimingFunctionValue()) { 426 CSSCubicBezierTimingFunctionValue* cubicTimingFunction = toCSSCubicBezierTimingFunctionValue(value); 427 return CubicBezierTimingFunction::create(cubicTimingFunction->x1(), cubicTimingFunction->y1(), cubicTimingFunction->x2(), cubicTimingFunction->y2()); 428 } 429 430 if (value->isInitialValue()) 431 return CSSTimingData::initialTimingFunction(); 432 433 CSSStepsTimingFunctionValue* stepsTimingFunction = toCSSStepsTimingFunctionValue(value); 434 if (stepsTimingFunction->stepAtPosition() == StepsTimingFunction::StepAtMiddle && !allowStepMiddle) 435 return CSSTimingData::initialTimingFunction(); 436 return StepsTimingFunction::create(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtPosition()); 437 } 438 439 void CSSToStyleMap::mapNinePieceImage(RenderStyle* mutableStyle, CSSPropertyID property, CSSValue* value, NinePieceImage& image) 440 { 441 // If we're not a value list, then we are "none" and don't need to alter the empty image at all. 442 if (!value || !value->isValueList()) 443 return; 444 445 // Retrieve the border image value. 446 CSSValueList* borderImage = toCSSValueList(value); 447 448 // Set the image (this kicks off the load). 449 CSSPropertyID imageProperty; 450 if (property == CSSPropertyWebkitBorderImage) 451 imageProperty = CSSPropertyBorderImageSource; 452 else if (property == CSSPropertyWebkitMaskBoxImage) 453 imageProperty = CSSPropertyWebkitMaskBoxImageSource; 454 else 455 imageProperty = property; 456 457 for (unsigned i = 0 ; i < borderImage->length() ; ++i) { 458 CSSValue* current = borderImage->item(i); 459 460 if (current->isImageValue() || current->isImageGeneratorValue() || current->isImageSetValue()) 461 image.setImage(styleImage(imageProperty, current)); 462 else if (current->isBorderImageSliceValue()) 463 mapNinePieceImageSlice(current, image); 464 else if (current->isValueList()) { 465 CSSValueList* slashList = toCSSValueList(current); 466 // Map in the image slices. 467 if (slashList->item(0) && slashList->item(0)->isBorderImageSliceValue()) 468 mapNinePieceImageSlice(slashList->item(0), image); 469 470 // Map in the border slices. 471 if (slashList->item(1)) 472 image.setBorderSlices(mapNinePieceImageQuad(slashList->item(1))); 473 474 // Map in the outset. 475 if (slashList->item(2)) 476 image.setOutset(mapNinePieceImageQuad(slashList->item(2))); 477 } else if (current->isPrimitiveValue()) { 478 // Set the appropriate rules for stretch/round/repeat of the slices. 479 mapNinePieceImageRepeat(current, image); 480 } 481 } 482 483 if (property == CSSPropertyWebkitBorderImage) { 484 // We have to preserve the legacy behavior of -webkit-border-image and make the border slices 485 // also set the border widths. We don't need to worry about percentages, since we don't even support 486 // those on real borders yet. 487 if (image.borderSlices().top().isLength() && image.borderSlices().top().length().isFixed()) 488 mutableStyle->setBorderTopWidth(image.borderSlices().top().length().value()); 489 if (image.borderSlices().right().isLength() && image.borderSlices().right().length().isFixed()) 490 mutableStyle->setBorderRightWidth(image.borderSlices().right().length().value()); 491 if (image.borderSlices().bottom().isLength() && image.borderSlices().bottom().length().isFixed()) 492 mutableStyle->setBorderBottomWidth(image.borderSlices().bottom().length().value()); 493 if (image.borderSlices().left().isLength() && image.borderSlices().left().length().isFixed()) 494 mutableStyle->setBorderLeftWidth(image.borderSlices().left().length().value()); 495 } 496 } 497 498 void CSSToStyleMap::mapNinePieceImageSlice(CSSValue* value, NinePieceImage& image) const 499 { 500 if (!value || !value->isBorderImageSliceValue()) 501 return; 502 503 // Retrieve the border image value. 504 CSSBorderImageSliceValue* borderImageSlice = toCSSBorderImageSliceValue(value); 505 506 // Set up a length box to represent our image slices. 507 LengthBox box; 508 Quad* slices = borderImageSlice->slices(); 509 if (slices->top()->isPercentage()) 510 box.m_top = Length(slices->top()->getDoubleValue(), Percent); 511 else 512 box.m_top = Length(slices->top()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed); 513 if (slices->bottom()->isPercentage()) 514 box.m_bottom = Length(slices->bottom()->getDoubleValue(), Percent); 515 else 516 box.m_bottom = Length((int)slices->bottom()->getFloatValue(CSSPrimitiveValue::CSS_NUMBER), Fixed); 517 if (slices->left()->isPercentage()) 518 box.m_left = Length(slices->left()->getDoubleValue(), Percent); 519 else 520 box.m_left = Length(slices->left()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed); 521 if (slices->right()->isPercentage()) 522 box.m_right = Length(slices->right()->getDoubleValue(), Percent); 523 else 524 box.m_right = Length(slices->right()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed); 525 image.setImageSlices(box); 526 527 // Set our fill mode. 528 image.setFill(borderImageSlice->m_fill); 529 } 530 531 static BorderImageLength toBorderImageLength(CSSPrimitiveValue& value, const CSSToLengthConversionData& conversionData) 532 { 533 if (value.isNumber()) 534 return value.getDoubleValue(); 535 if (value.isPercentage()) 536 return Length(value.getDoubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent); 537 if (value.getValueID() != CSSValueAuto) 538 return value.computeLength<Length>(conversionData); 539 return Length(Auto); 540 } 541 542 BorderImageLengthBox CSSToStyleMap::mapNinePieceImageQuad(CSSValue* value) const 543 { 544 if (!value || !value->isPrimitiveValue()) 545 return BorderImageLengthBox(Length(Auto)); 546 547 Quad* slices = toCSSPrimitiveValue(value)->getQuadValue(); 548 549 // Set up a border image length box to represent our image slices. 550 return BorderImageLengthBox( 551 toBorderImageLength(*slices->top(), cssToLengthConversionData()), 552 toBorderImageLength(*slices->right(), cssToLengthConversionData()), 553 toBorderImageLength(*slices->bottom(), cssToLengthConversionData()), 554 toBorderImageLength(*slices->left(), cssToLengthConversionData())); 555 } 556 557 void CSSToStyleMap::mapNinePieceImageRepeat(CSSValue* value, NinePieceImage& image) const 558 { 559 if (!value || !value->isPrimitiveValue()) 560 return; 561 562 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 563 Pair* pair = primitiveValue->getPairValue(); 564 if (!pair || !pair->first() || !pair->second()) 565 return; 566 567 CSSValueID firstIdentifier = pair->first()->getValueID(); 568 CSSValueID secondIdentifier = pair->second()->getValueID(); 569 570 ENinePieceImageRule horizontalRule; 571 switch (firstIdentifier) { 572 case CSSValueStretch: 573 horizontalRule = StretchImageRule; 574 break; 575 case CSSValueRound: 576 horizontalRule = RoundImageRule; 577 break; 578 case CSSValueSpace: 579 horizontalRule = SpaceImageRule; 580 break; 581 default: // CSSValueRepeat 582 horizontalRule = RepeatImageRule; 583 break; 584 } 585 image.setHorizontalRule(horizontalRule); 586 587 ENinePieceImageRule verticalRule; 588 switch (secondIdentifier) { 589 case CSSValueStretch: 590 verticalRule = StretchImageRule; 591 break; 592 case CSSValueRound: 593 verticalRule = RoundImageRule; 594 break; 595 case CSSValueSpace: 596 verticalRule = SpaceImageRule; 597 break; 598 default: // CSSValueRepeat 599 verticalRule = RepeatImageRule; 600 break; 601 } 602 image.setVerticalRule(verticalRule); 603 } 604 605 }; 606