Add experimental support for spring based CSS animations
[WebKit-https.git] / Source / WebCore / css / CSSToStyleMap.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4  * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@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@webkit.org>
7  * Copyright (C) 2007, 2008 Eric Seidel <eric@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 "CSSToStyleMap.h"
30
31 #include "Animation.h"
32 #include "CSSAnimationTriggerScrollValue.h"
33 #include "CSSBorderImageSliceValue.h"
34 #include "CSSImageGeneratorValue.h"
35 #include "CSSImageSetValue.h"
36 #include "CSSImageValue.h"
37 #include "CSSPrimitiveValue.h"
38 #include "CSSPrimitiveValueMappings.h"
39 #include "CSSTimingFunctionValue.h"
40 #include "CSSValueKeywords.h"
41 #include "FillLayer.h"
42 #include "Pair.h"
43 #include "Rect.h"
44 #include "RenderView.h"
45 #include "StyleResolver.h"
46
47 namespace WebCore {
48
49 CSSToStyleMap::CSSToStyleMap(StyleResolver* resolver)
50     : m_resolver(resolver)
51 {
52 }
53
54 RenderStyle* CSSToStyleMap::style() const
55 {
56     return m_resolver->style();
57 }
58
59 const RenderStyle* CSSToStyleMap::rootElementStyle() const
60 {
61     return m_resolver->rootElementStyle();
62 }
63
64 bool CSSToStyleMap::useSVGZoomRules() const
65 {
66     return m_resolver->useSVGZoomRules();
67 }
68
69 RefPtr<StyleImage> CSSToStyleMap::styleImage(CSSPropertyID propertyId, CSSValue& value)
70 {
71     return m_resolver->styleImage(propertyId, value);
72 }
73
74 void CSSToStyleMap::mapFillAttachment(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
75 {
76     if (value.treatAsInitialValue(propertyID)) {
77         layer.setAttachment(FillLayer::initialFillAttachment(layer.type()));
78         return;
79     }
80
81     if (!is<CSSPrimitiveValue>(value))
82         return;
83
84     switch (downcast<CSSPrimitiveValue>(value).getValueID()) {
85     case CSSValueFixed:
86         layer.setAttachment(FixedBackgroundAttachment);
87         break;
88     case CSSValueScroll:
89         layer.setAttachment(ScrollBackgroundAttachment);
90         break;
91     case CSSValueLocal:
92         layer.setAttachment(LocalBackgroundAttachment);
93         break;
94     default:
95         return;
96     }
97 }
98
99 void CSSToStyleMap::mapFillClip(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
100 {
101     if (value.treatAsInitialValue(propertyID)) {
102         layer.setClip(FillLayer::initialFillClip(layer.type()));
103         return;
104     }
105
106     if (!is<CSSPrimitiveValue>(value))
107         return;
108
109     layer.setClip(downcast<CSSPrimitiveValue>(value));
110 }
111
112 void CSSToStyleMap::mapFillComposite(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
113 {
114     if (value.treatAsInitialValue(propertyID)) {
115         layer.setComposite(FillLayer::initialFillComposite(layer.type()));
116         return;
117     }
118
119     if (!is<CSSPrimitiveValue>(value))
120         return;
121
122     layer.setComposite(downcast<CSSPrimitiveValue>(value));
123 }
124
125 void CSSToStyleMap::mapFillBlendMode(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
126 {
127     if (value.treatAsInitialValue(propertyID)) {
128         layer.setBlendMode(FillLayer::initialFillBlendMode(layer.type()));
129         return;
130     }
131
132     if (!is<CSSPrimitiveValue>(value))
133         return;
134
135     layer.setBlendMode(downcast<CSSPrimitiveValue>(value));
136 }
137
138 void CSSToStyleMap::mapFillOrigin(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
139 {
140     if (value.treatAsInitialValue(propertyID)) {
141         layer.setOrigin(FillLayer::initialFillOrigin(layer.type()));
142         return;
143     }
144
145     if (!is<CSSPrimitiveValue>(value))
146         return;
147
148     layer.setOrigin(downcast<CSSPrimitiveValue>(value));
149 }
150
151 void CSSToStyleMap::mapFillImage(CSSPropertyID propertyID, FillLayer& layer, CSSValue& value)
152 {
153     if (value.treatAsInitialValue(propertyID)) {
154         layer.setImage(FillLayer::initialFillImage(layer.type()));
155         return;
156     }
157
158     layer.setImage(styleImage(propertyID, value));
159 }
160
161 void CSSToStyleMap::mapFillRepeatX(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
162 {
163     if (value.treatAsInitialValue(propertyID)) {
164         layer.setRepeatX(FillLayer::initialFillRepeatX(layer.type()));
165         return;
166     }
167
168     if (!is<CSSPrimitiveValue>(value))
169         return;
170
171     layer.setRepeatX(downcast<CSSPrimitiveValue>(value));
172 }
173
174 void CSSToStyleMap::mapFillRepeatY(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
175 {
176     if (value.treatAsInitialValue(propertyID)) {
177         layer.setRepeatY(FillLayer::initialFillRepeatY(layer.type()));
178         return;
179     }
180
181     if (!is<CSSPrimitiveValue>(value))
182         return;
183
184     layer.setRepeatY(downcast<CSSPrimitiveValue>(value));
185 }
186
187 static inline bool convertToLengthSize(const CSSPrimitiveValue& primitiveValue, CSSToLengthConversionData conversionData, LengthSize& size)
188 {
189     if (auto* pair = primitiveValue.getPairValue()) {
190         size.setWidth(pair->first()->convertToLength<AnyConversion>(conversionData));
191         size.setHeight(pair->second()->convertToLength<AnyConversion>(conversionData));
192     } else
193         size.setWidth(primitiveValue.convertToLength<AnyConversion>(conversionData));
194
195     return !size.width().isUndefined() && !size.height().isUndefined();
196 }
197
198 void CSSToStyleMap::mapFillSize(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
199 {
200     if (value.treatAsInitialValue(propertyID)) {
201         layer.setSize(FillLayer::initialFillSize(layer.type()));
202         return;
203     }
204
205     if (!is<CSSPrimitiveValue>(value))
206         return;
207
208     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
209     FillSize fillSize;
210     switch (primitiveValue.getValueID()) {
211     case CSSValueContain:
212         fillSize.type = Contain;
213         break;
214     case CSSValueCover:
215         fillSize.type = Cover;
216         break;
217     default:
218         ASSERT(fillSize.type == SizeLength);
219         if (!convertToLengthSize(primitiveValue, m_resolver->state().cssToLengthConversionData(), fillSize.size))
220             return;
221         break;
222     }
223     layer.setSize(fillSize);
224 }
225
226 void CSSToStyleMap::mapFillXPosition(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
227 {
228     if (value.treatAsInitialValue(propertyID)) {
229         layer.setXPosition(FillLayer::initialFillXPosition(layer.type()));
230         return;
231     }
232
233     if (!is<CSSPrimitiveValue>(value))
234         return;
235
236     auto* primitiveValue = &downcast<CSSPrimitiveValue>(value);
237     Pair* pair = primitiveValue->getPairValue();
238     if (pair) {
239         ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionX || propertyID == CSSPropertyWebkitMaskPositionX);
240         primitiveValue = pair->second();
241     }
242
243     Length length;
244     if (primitiveValue->isLength())
245         length = primitiveValue->computeLength<Length>(m_resolver->state().cssToLengthConversionData());
246     else if (primitiveValue->isPercentage())
247         length = Length(primitiveValue->getDoubleValue(), Percent);
248     else if (primitiveValue->isCalculatedPercentageWithLength())
249         length = Length(primitiveValue->cssCalcValue()->createCalculationValue(m_resolver->state().cssToLengthConversionData()));
250     else
251         return;
252
253     layer.setXPosition(length);
254     if (pair)
255         layer.setBackgroundXOrigin(*pair->first());
256 }
257
258 void CSSToStyleMap::mapFillYPosition(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
259 {
260     if (value.treatAsInitialValue(propertyID)) {
261         layer.setYPosition(FillLayer::initialFillYPosition(layer.type()));
262         return;
263     }
264
265     if (!is<CSSPrimitiveValue>(value))
266         return;
267
268     auto* primitiveValue = &downcast<CSSPrimitiveValue>(value);
269     Pair* pair = primitiveValue->getPairValue();
270     if (pair) {
271         ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionY || propertyID == CSSPropertyWebkitMaskPositionY);
272         primitiveValue = pair->second();
273     }
274
275     Length length;
276     if (primitiveValue->isLength())
277         length = primitiveValue->computeLength<Length>(m_resolver->state().cssToLengthConversionData());
278     else if (primitiveValue->isPercentage())
279         length = Length(primitiveValue->getDoubleValue(), Percent);
280     else if (primitiveValue->isCalculatedPercentageWithLength())
281         length = Length(primitiveValue->cssCalcValue()->createCalculationValue(m_resolver->state().cssToLengthConversionData()));
282     else
283         return;
284
285     layer.setYPosition(length);
286     if (pair)
287         layer.setBackgroundYOrigin(*pair->first());
288 }
289
290 void CSSToStyleMap::mapFillMaskSourceType(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
291 {
292     EMaskSourceType type = FillLayer::initialFillMaskSourceType(layer.type());
293     if (value.treatAsInitialValue(propertyID)) {
294         layer.setMaskSourceType(type);
295         return;
296     }
297
298     if (!is<CSSPrimitiveValue>(value))
299         return;
300
301     switch (downcast<CSSPrimitiveValue>(value).getValueID()) {
302     case CSSValueAlpha:
303         type = EMaskSourceType::MaskAlpha;
304         break;
305     case CSSValueLuminance:
306         type = EMaskSourceType::MaskLuminance;
307         break;
308     case CSSValueAuto:
309         break;
310     default:
311         ASSERT_NOT_REACHED();
312     }
313
314     layer.setMaskSourceType(type);
315 }
316
317 void CSSToStyleMap::mapAnimationDelay(Animation& animation, const CSSValue& value)
318 {
319     if (value.treatAsInitialValue(CSSPropertyWebkitAnimationDelay)) {
320         animation.setDelay(Animation::initialDelay());
321         return;
322     }
323
324     if (!is<CSSPrimitiveValue>(value))
325         return;
326
327     animation.setDelay(downcast<CSSPrimitiveValue>(value).computeTime<double, CSSPrimitiveValue::Seconds>());
328 }
329
330 void CSSToStyleMap::mapAnimationDirection(Animation& layer, const CSSValue& value)
331 {
332     if (value.treatAsInitialValue(CSSPropertyWebkitAnimationDirection)) {
333         layer.setDirection(Animation::initialDirection());
334         return;
335     }
336
337     if (!is<CSSPrimitiveValue>(value))
338         return;
339
340     switch (downcast<CSSPrimitiveValue>(value).getValueID()) {
341     case CSSValueNormal:
342         layer.setDirection(Animation::AnimationDirectionNormal);
343         break;
344     case CSSValueAlternate:
345         layer.setDirection(Animation::AnimationDirectionAlternate);
346         break;
347     case CSSValueReverse:
348         layer.setDirection(Animation::AnimationDirectionReverse);
349         break;
350     case CSSValueAlternateReverse:
351         layer.setDirection(Animation::AnimationDirectionAlternateReverse);
352         break;
353     default:
354         break;
355     }
356 }
357
358 void CSSToStyleMap::mapAnimationDuration(Animation& animation, const CSSValue& value)
359 {
360     if (value.treatAsInitialValue(CSSPropertyWebkitAnimationDuration)) {
361         animation.setDuration(Animation::initialDuration());
362         return;
363     }
364
365     if (!is<CSSPrimitiveValue>(value))
366         return;
367
368     animation.setDuration(downcast<CSSPrimitiveValue>(value).computeTime<double, CSSPrimitiveValue::Seconds>());
369 }
370
371 void CSSToStyleMap::mapAnimationFillMode(Animation& layer, const CSSValue& value)
372 {
373     if (value.treatAsInitialValue(CSSPropertyWebkitAnimationFillMode)) {
374         layer.setFillMode(Animation::initialFillMode());
375         return;
376     }
377
378     if (!is<CSSPrimitiveValue>(value))
379         return;
380
381     switch (downcast<CSSPrimitiveValue>(value).getValueID()) {
382     case CSSValueNone:
383         layer.setFillMode(AnimationFillModeNone);
384         break;
385     case CSSValueForwards:
386         layer.setFillMode(AnimationFillModeForwards);
387         break;
388     case CSSValueBackwards:
389         layer.setFillMode(AnimationFillModeBackwards);
390         break;
391     case CSSValueBoth:
392         layer.setFillMode(AnimationFillModeBoth);
393         break;
394     default:
395         break;
396     }
397 }
398
399 void CSSToStyleMap::mapAnimationIterationCount(Animation& animation, const CSSValue& value)
400 {
401     if (value.treatAsInitialValue(CSSPropertyWebkitAnimationIterationCount)) {
402         animation.setIterationCount(Animation::initialIterationCount());
403         return;
404     }
405
406     if (!is<CSSPrimitiveValue>(value))
407         return;
408
409     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
410     if (primitiveValue.getValueID() == CSSValueInfinite)
411         animation.setIterationCount(Animation::IterationCountInfinite);
412     else
413         animation.setIterationCount(primitiveValue.getFloatValue());
414 }
415
416 void CSSToStyleMap::mapAnimationName(Animation& layer, const CSSValue& value)
417 {
418     if (value.treatAsInitialValue(CSSPropertyWebkitAnimationName)) {
419         layer.setName(Animation::initialName());
420         return;
421     }
422
423     if (!is<CSSPrimitiveValue>(value))
424         return;
425
426     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
427     if (primitiveValue.getValueID() == CSSValueNone)
428         layer.setIsNoneAnimation(true);
429     else
430         layer.setName(primitiveValue.getStringValue());
431 }
432
433 void CSSToStyleMap::mapAnimationPlayState(Animation& layer, const CSSValue& value)
434 {
435     if (value.treatAsInitialValue(CSSPropertyWebkitAnimationPlayState)) {
436         layer.setPlayState(Animation::initialPlayState());
437         return;
438     }
439
440     if (!is<CSSPrimitiveValue>(value))
441         return;
442
443     EAnimPlayState playState = (downcast<CSSPrimitiveValue>(value).getValueID() == CSSValuePaused) ? AnimPlayStatePaused : AnimPlayStatePlaying;
444     layer.setPlayState(playState);
445 }
446
447 void CSSToStyleMap::mapAnimationProperty(Animation& animation, const CSSValue& value)
448 {
449     if (value.treatAsInitialValue(CSSPropertyWebkitAnimation)) {
450         animation.setAnimationMode(Animation::AnimateAll);
451         animation.setProperty(CSSPropertyInvalid);
452         return;
453     }
454
455     if (!is<CSSPrimitiveValue>(value))
456         return;
457
458     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
459     if (primitiveValue.getValueID() == CSSValueAll) {
460         animation.setAnimationMode(Animation::AnimateAll);
461         animation.setProperty(CSSPropertyInvalid);
462     } else if (primitiveValue.getValueID() == CSSValueNone) {
463         animation.setAnimationMode(Animation::AnimateNone);
464         animation.setProperty(CSSPropertyInvalid);
465     } else {
466         animation.setAnimationMode(Animation::AnimateSingleProperty);
467         animation.setProperty(primitiveValue.getPropertyID());
468     }
469 }
470
471 void CSSToStyleMap::mapAnimationTimingFunction(Animation& animation, const CSSValue& value)
472 {
473     if (value.treatAsInitialValue(CSSPropertyWebkitAnimationTimingFunction)) {
474         animation.setTimingFunction(Animation::initialTimingFunction());
475         return;
476     }
477
478     if (is<CSSPrimitiveValue>(value)) {
479         switch (downcast<CSSPrimitiveValue>(value).getValueID()) {
480         case CSSValueLinear:
481             animation.setTimingFunction(LinearTimingFunction::create());
482             break;
483         case CSSValueEase:
484             animation.setTimingFunction(CubicBezierTimingFunction::create());
485             break;
486         case CSSValueEaseIn:
487             animation.setTimingFunction(CubicBezierTimingFunction::create(CubicBezierTimingFunction::EaseIn));
488             break;
489         case CSSValueEaseOut:
490             animation.setTimingFunction(CubicBezierTimingFunction::create(CubicBezierTimingFunction::EaseOut));
491             break;
492         case CSSValueEaseInOut:
493             animation.setTimingFunction(CubicBezierTimingFunction::create(CubicBezierTimingFunction::EaseInOut));
494             break;
495         case CSSValueStepStart:
496             animation.setTimingFunction(StepsTimingFunction::create(1, true));
497             break;
498         case CSSValueStepEnd:
499             animation.setTimingFunction(StepsTimingFunction::create(1, false));
500             break;
501         default:
502             break;
503         }
504         return;
505     }
506
507     if (is<CSSCubicBezierTimingFunctionValue>(value)) {
508         auto& cubicTimingFunction = downcast<CSSCubicBezierTimingFunctionValue>(value);
509         animation.setTimingFunction(CubicBezierTimingFunction::create(cubicTimingFunction.x1(), cubicTimingFunction.y1(), cubicTimingFunction.x2(), cubicTimingFunction.y2()));
510     } else if (is<CSSStepsTimingFunctionValue>(value)) {
511         auto& stepsTimingFunction = downcast<CSSStepsTimingFunctionValue>(value);
512         animation.setTimingFunction(StepsTimingFunction::create(stepsTimingFunction.numberOfSteps(), stepsTimingFunction.stepAtStart()));
513     } else if (is<CSSSpringTimingFunctionValue>(value)) {
514         auto& springTimingFunction = downcast<CSSSpringTimingFunctionValue>(value);
515         animation.setTimingFunction(SpringTimingFunction::create(springTimingFunction.mass(), springTimingFunction.stiffness(), springTimingFunction.damping(), springTimingFunction.initialVelocity()));
516     }
517 }
518
519 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
520 void CSSToStyleMap::mapAnimationTrigger(Animation& animation, const CSSValue& value)
521 {
522     if (value.treatAsInitialValue(CSSPropertyWebkitAnimationTrigger)) {
523         animation.setTrigger(Animation::initialTrigger());
524         return;
525     }
526
527     if (value.isPrimitiveValue()) {
528         auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
529         if (primitiveValue.getValueID() == CSSValueAuto)
530             animation.setTrigger(AutoAnimationTrigger::create());
531         return;
532     }
533
534     if (value.isAnimationTriggerScrollValue()) {
535         auto& scrollTrigger = downcast<CSSAnimationTriggerScrollValue>(value);
536
537         const CSSPrimitiveValue& startValue = downcast<CSSPrimitiveValue>(scrollTrigger.startValue());
538         Length startLength = startValue.computeLength<Length>(m_resolver->state().cssToLengthConversionData());
539
540         Length endLength;
541         if (scrollTrigger.hasEndValue()) {
542             const CSSPrimitiveValue* endValue = downcast<CSSPrimitiveValue>(scrollTrigger.endValue());
543             endLength = endValue->computeLength<Length>(m_resolver->state().cssToLengthConversionData());
544         }
545
546         animation.setTrigger(ScrollAnimationTrigger::create(startLength, endLength));
547     }
548 }
549 #endif
550
551 void CSSToStyleMap::mapNinePieceImage(CSSPropertyID property, CSSValue* value, NinePieceImage& image)
552 {
553     // If we're not a value list, then we are "none" and don't need to alter the empty image at all.
554     if (!is<CSSValueList>(value))
555         return;
556
557     // Retrieve the border image value.
558     CSSValueList& borderImage = downcast<CSSValueList>(*value);
559
560     // Set the image (this kicks off the load).
561     CSSPropertyID imageProperty;
562     if (property == CSSPropertyWebkitBorderImage)
563         imageProperty = CSSPropertyBorderImageSource;
564     else if (property == CSSPropertyWebkitMaskBoxImage)
565         imageProperty = CSSPropertyWebkitMaskBoxImageSource;
566     else
567         imageProperty = property;
568
569     for (auto& current : borderImage) {
570         if (is<CSSImageValue>(current.get()) || is<CSSImageGeneratorValue>(current.get())
571 #if ENABLE(CSS_IMAGE_SET)
572             || is<CSSImageSetValue>(current.get())
573 #endif
574             )
575             image.setImage(styleImage(imageProperty, current.get()));
576         else if (is<CSSBorderImageSliceValue>(current.get()))
577             mapNinePieceImageSlice(current, image);
578         else if (is<CSSValueList>(current.get())) {
579             CSSValueList& slashList = downcast<CSSValueList>(current.get());
580             // Map in the image slices.
581             if (is<CSSBorderImageSliceValue>(slashList.item(0)))
582                 mapNinePieceImageSlice(*slashList.item(0), image);
583
584             // Map in the border slices.
585             if (slashList.item(1))
586                 image.setBorderSlices(mapNinePieceImageQuad(*slashList.item(1)));
587
588             // Map in the outset.
589             if (slashList.item(2))
590                 image.setOutset(mapNinePieceImageQuad(*slashList.item(2)));
591         } else if (is<CSSPrimitiveValue>(current.get())) {
592             // Set the appropriate rules for stretch/round/repeat of the slices.
593             mapNinePieceImageRepeat(current, image);
594         }
595     }
596
597     if (property == CSSPropertyWebkitBorderImage) {
598         // We have to preserve the legacy behavior of -webkit-border-image and make the border slices
599         // also set the border widths. We don't need to worry about percentages, since we don't even support
600         // those on real borders yet.
601         if (image.borderSlices().top().isFixed())
602             style()->setBorderTopWidth(image.borderSlices().top().value());
603         if (image.borderSlices().right().isFixed())
604             style()->setBorderRightWidth(image.borderSlices().right().value());
605         if (image.borderSlices().bottom().isFixed())
606             style()->setBorderBottomWidth(image.borderSlices().bottom().value());
607         if (image.borderSlices().left().isFixed())
608             style()->setBorderLeftWidth(image.borderSlices().left().value());
609     }
610 }
611
612 void CSSToStyleMap::mapNinePieceImageSlice(CSSValue& value, NinePieceImage& image)
613 {
614     if (!is<CSSBorderImageSliceValue>(value))
615         return;
616
617     // Retrieve the border image value.
618     auto& borderImageSlice = downcast<CSSBorderImageSliceValue>(value);
619
620     // Set up a length box to represent our image slices.
621     LengthBox box;
622     Quad* slices = borderImageSlice.slices();
623     if (slices->top()->isPercentage())
624         box.top() = Length(slices->top()->getDoubleValue(), Percent);
625     else
626         box.top() = Length(slices->top()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
627     if (slices->bottom()->isPercentage())
628         box.bottom() = Length(slices->bottom()->getDoubleValue(), Percent);
629     else
630         box.bottom() = Length((int)slices->bottom()->getFloatValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
631     if (slices->left()->isPercentage())
632         box.left() = Length(slices->left()->getDoubleValue(), Percent);
633     else
634         box.left() = Length(slices->left()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
635     if (slices->right()->isPercentage())
636         box.right() = Length(slices->right()->getDoubleValue(), Percent);
637     else
638         box.right() = Length(slices->right()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
639     image.setImageSlices(box);
640
641     // Set our fill mode.
642     image.setFill(borderImageSlice.m_fill);
643 }
644
645 LengthBox CSSToStyleMap::mapNinePieceImageQuad(CSSValue& value)
646 {
647     if (!is<CSSPrimitiveValue>(value))
648         return LengthBox();
649
650     // Get our zoom value.
651     CSSToLengthConversionData conversionData = useSVGZoomRules() ? m_resolver->state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f) : m_resolver->state().cssToLengthConversionData();
652
653     // Retrieve the primitive value.
654     auto& borderWidths = downcast<CSSPrimitiveValue>(value);
655
656     // Set up a length box to represent our image slices.
657     LengthBox box; // Defaults to 'auto' so we don't have to handle that explicitly below.
658     Quad* slices = borderWidths.getQuadValue();
659     if (slices->top()->isNumber())
660         box.top() = Length(slices->top()->getIntValue(), Relative);
661     else if (slices->top()->isPercentage())
662         box.top() = Length(slices->top()->getDoubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
663     else if (slices->top()->getValueID() != CSSValueAuto)
664         box.top() = slices->top()->computeLength<Length>(conversionData);
665
666     if (slices->right()->isNumber())
667         box.right() = Length(slices->right()->getIntValue(), Relative);
668     else if (slices->right()->isPercentage())
669         box.right() = Length(slices->right()->getDoubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
670     else if (slices->right()->getValueID() != CSSValueAuto)
671         box.right() = slices->right()->computeLength<Length>(conversionData);
672
673     if (slices->bottom()->isNumber())
674         box.bottom() = Length(slices->bottom()->getIntValue(), Relative);
675     else if (slices->bottom()->isPercentage())
676         box.bottom() = Length(slices->bottom()->getDoubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
677     else if (slices->bottom()->getValueID() != CSSValueAuto)
678         box.bottom() = slices->bottom()->computeLength<Length>(conversionData);
679
680     if (slices->left()->isNumber())
681         box.left() = Length(slices->left()->getIntValue(), Relative);
682     else if (slices->left()->isPercentage())
683         box.left() = Length(slices->left()->getDoubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
684     else if (slices->left()->getValueID() != CSSValueAuto)
685         box.left() = slices->left()->computeLength<Length>(conversionData);
686
687     return box;
688 }
689
690 void CSSToStyleMap::mapNinePieceImageRepeat(CSSValue& value, NinePieceImage& image)
691 {
692     if (!is<CSSPrimitiveValue>(value))
693         return;
694
695     CSSPrimitiveValue& primitiveValue = downcast<CSSPrimitiveValue>(value);
696     Pair* pair = primitiveValue.getPairValue();
697     if (!pair || !pair->first() || !pair->second())
698         return;
699
700     CSSValueID firstIdentifier = pair->first()->getValueID();
701     CSSValueID secondIdentifier = pair->second()->getValueID();
702
703     ENinePieceImageRule horizontalRule;
704     switch (firstIdentifier) {
705     case CSSValueStretch:
706         horizontalRule = StretchImageRule;
707         break;
708     case CSSValueRound:
709         horizontalRule = RoundImageRule;
710         break;
711     case CSSValueSpace:
712         horizontalRule = SpaceImageRule;
713         break;
714     default: // CSSValueRepeat
715         horizontalRule = RepeatImageRule;
716         break;
717     }
718     image.setHorizontalRule(horizontalRule);
719
720     ENinePieceImageRule verticalRule;
721     switch (secondIdentifier) {
722     case CSSValueStretch:
723         verticalRule = StretchImageRule;
724         break;
725     case CSSValueRound:
726         verticalRule = RoundImageRule;
727         break;
728     case CSSValueSpace:
729         verticalRule = SpaceImageRule;
730         break;
731     default: // CSSValueRepeat
732         verticalRule = RepeatImageRule;
733         break;
734     }
735     image.setVerticalRule(verticalRule);
736 }
737
738 };