[CSS Parser] Fix background-position parsing
[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(CSSValue& value)
70 {
71     return m_resolver->styleImage(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).valueID()) {
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(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.pairValue()) {
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.valueID()) {
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->pairValue();
238     if (pair) {
239         ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionX || propertyID == CSSPropertyWebkitMaskPositionX);
240         primitiveValue = pair->second();
241     } else if (primitiveValue->isValueID()) {
242         if (primitiveValue->valueID() == CSSValueCenter) {
243             layer.setBackgroundXOrigin(Edge::Left);
244             layer.setXPosition(Length(50, Percent));
245         } else {
246             layer.setBackgroundXOrigin(*primitiveValue);
247             layer.setXPosition(Length());
248         }
249         return;
250     }
251
252     Length length;
253     if (primitiveValue->isLength())
254         length = primitiveValue->computeLength<Length>(m_resolver->state().cssToLengthConversionData());
255     else if (primitiveValue->isPercentage())
256         length = Length(primitiveValue->doubleValue(), Percent);
257     else if (primitiveValue->isCalculatedPercentageWithLength())
258         length = Length(primitiveValue->cssCalcValue()->createCalculationValue(m_resolver->state().cssToLengthConversionData()));
259     else
260         return;
261
262     layer.setXPosition(length);
263     if (pair)
264         layer.setBackgroundXOrigin(*pair->first());
265 }
266
267 void CSSToStyleMap::mapFillYPosition(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
268 {
269     if (value.treatAsInitialValue(propertyID)) {
270         layer.setYPosition(FillLayer::initialFillYPosition(layer.type()));
271         return;
272     }
273
274     if (!is<CSSPrimitiveValue>(value))
275         return;
276
277     auto* primitiveValue = &downcast<CSSPrimitiveValue>(value);
278     Pair* pair = primitiveValue->pairValue();
279     if (pair) {
280         ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionY || propertyID == CSSPropertyWebkitMaskPositionY);
281         primitiveValue = pair->second();
282     } else if (primitiveValue->isValueID()) {
283         if (primitiveValue->valueID() == CSSValueCenter) {
284             layer.setBackgroundYOrigin(Edge::Top);
285             layer.setYPosition(Length(50, Percent));
286         } else {
287             layer.setBackgroundYOrigin(*primitiveValue);
288             layer.setYPosition(Length());
289         }
290         return;
291     }
292
293     Length length;
294     if (primitiveValue->isLength())
295         length = primitiveValue->computeLength<Length>(m_resolver->state().cssToLengthConversionData());
296     else if (primitiveValue->isPercentage())
297         length = Length(primitiveValue->doubleValue(), Percent);
298     else if (primitiveValue->isCalculatedPercentageWithLength())
299         length = Length(primitiveValue->cssCalcValue()->createCalculationValue(m_resolver->state().cssToLengthConversionData()));
300     else
301         return;
302
303     layer.setYPosition(length);
304     if (pair)
305         layer.setBackgroundYOrigin(*pair->first());
306 }
307
308 void CSSToStyleMap::mapFillMaskSourceType(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value)
309 {
310     EMaskSourceType type = FillLayer::initialFillMaskSourceType(layer.type());
311     if (value.treatAsInitialValue(propertyID)) {
312         layer.setMaskSourceType(type);
313         return;
314     }
315
316     if (!is<CSSPrimitiveValue>(value))
317         return;
318
319     switch (downcast<CSSPrimitiveValue>(value).valueID()) {
320     case CSSValueAlpha:
321         type = EMaskSourceType::MaskAlpha;
322         break;
323     case CSSValueLuminance:
324         type = EMaskSourceType::MaskLuminance;
325         break;
326     case CSSValueAuto:
327         break;
328     default:
329         ASSERT_NOT_REACHED();
330     }
331
332     layer.setMaskSourceType(type);
333 }
334
335 void CSSToStyleMap::mapAnimationDelay(Animation& animation, const CSSValue& value)
336 {
337     if (value.treatAsInitialValue(CSSPropertyAnimationDelay)) {
338         animation.setDelay(Animation::initialDelay());
339         return;
340     }
341
342     if (!is<CSSPrimitiveValue>(value))
343         return;
344
345     animation.setDelay(downcast<CSSPrimitiveValue>(value).computeTime<double, CSSPrimitiveValue::Seconds>());
346 }
347
348 void CSSToStyleMap::mapAnimationDirection(Animation& layer, const CSSValue& value)
349 {
350     if (value.treatAsInitialValue(CSSPropertyAnimationDirection)) {
351         layer.setDirection(Animation::initialDirection());
352         return;
353     }
354
355     if (!is<CSSPrimitiveValue>(value))
356         return;
357
358     switch (downcast<CSSPrimitiveValue>(value).valueID()) {
359     case CSSValueNormal:
360         layer.setDirection(Animation::AnimationDirectionNormal);
361         break;
362     case CSSValueAlternate:
363         layer.setDirection(Animation::AnimationDirectionAlternate);
364         break;
365     case CSSValueReverse:
366         layer.setDirection(Animation::AnimationDirectionReverse);
367         break;
368     case CSSValueAlternateReverse:
369         layer.setDirection(Animation::AnimationDirectionAlternateReverse);
370         break;
371     default:
372         break;
373     }
374 }
375
376 void CSSToStyleMap::mapAnimationDuration(Animation& animation, const CSSValue& value)
377 {
378     if (value.treatAsInitialValue(CSSPropertyAnimationDuration)) {
379         animation.setDuration(Animation::initialDuration());
380         return;
381     }
382
383     if (!is<CSSPrimitiveValue>(value))
384         return;
385
386     animation.setDuration(downcast<CSSPrimitiveValue>(value).computeTime<double, CSSPrimitiveValue::Seconds>());
387 }
388
389 void CSSToStyleMap::mapAnimationFillMode(Animation& layer, const CSSValue& value)
390 {
391     if (value.treatAsInitialValue(CSSPropertyAnimationFillMode)) {
392         layer.setFillMode(Animation::initialFillMode());
393         return;
394     }
395
396     if (!is<CSSPrimitiveValue>(value))
397         return;
398
399     switch (downcast<CSSPrimitiveValue>(value).valueID()) {
400     case CSSValueNone:
401         layer.setFillMode(AnimationFillModeNone);
402         break;
403     case CSSValueForwards:
404         layer.setFillMode(AnimationFillModeForwards);
405         break;
406     case CSSValueBackwards:
407         layer.setFillMode(AnimationFillModeBackwards);
408         break;
409     case CSSValueBoth:
410         layer.setFillMode(AnimationFillModeBoth);
411         break;
412     default:
413         break;
414     }
415 }
416
417 void CSSToStyleMap::mapAnimationIterationCount(Animation& animation, const CSSValue& value)
418 {
419     if (value.treatAsInitialValue(CSSPropertyAnimationIterationCount)) {
420         animation.setIterationCount(Animation::initialIterationCount());
421         return;
422     }
423
424     if (!is<CSSPrimitiveValue>(value))
425         return;
426
427     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
428     if (primitiveValue.valueID() == CSSValueInfinite)
429         animation.setIterationCount(Animation::IterationCountInfinite);
430     else
431         animation.setIterationCount(primitiveValue.floatValue());
432 }
433
434 void CSSToStyleMap::mapAnimationName(Animation& layer, const CSSValue& value)
435 {
436     if (value.treatAsInitialValue(CSSPropertyAnimationName)) {
437         layer.setName(Animation::initialName());
438         return;
439     }
440
441     if (!is<CSSPrimitiveValue>(value))
442         return;
443
444     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
445     if (primitiveValue.valueID() == CSSValueNone)
446         layer.setIsNoneAnimation(true);
447     else
448         layer.setName(primitiveValue.stringValue());
449 }
450
451 void CSSToStyleMap::mapAnimationPlayState(Animation& layer, const CSSValue& value)
452 {
453     if (value.treatAsInitialValue(CSSPropertyAnimationPlayState)) {
454         layer.setPlayState(Animation::initialPlayState());
455         return;
456     }
457
458     if (!is<CSSPrimitiveValue>(value))
459         return;
460
461     EAnimPlayState playState = (downcast<CSSPrimitiveValue>(value).valueID() == CSSValuePaused) ? AnimPlayStatePaused : AnimPlayStatePlaying;
462     layer.setPlayState(playState);
463 }
464
465 void CSSToStyleMap::mapAnimationProperty(Animation& animation, const CSSValue& value)
466 {
467     if (value.treatAsInitialValue(CSSPropertyAnimation)) {
468         animation.setAnimationMode(Animation::AnimateAll);
469         animation.setProperty(CSSPropertyInvalid);
470         return;
471     }
472
473     if (!is<CSSPrimitiveValue>(value))
474         return;
475
476     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
477     if (primitiveValue.valueID() == CSSValueAll) {
478         animation.setAnimationMode(Animation::AnimateAll);
479         animation.setProperty(CSSPropertyInvalid);
480     } else if (primitiveValue.valueID() == CSSValueNone) {
481         animation.setAnimationMode(Animation::AnimateNone);
482         animation.setProperty(CSSPropertyInvalid);
483     } else {
484         animation.setAnimationMode(Animation::AnimateSingleProperty);
485         animation.setProperty(primitiveValue.propertyID());
486     }
487 }
488
489 void CSSToStyleMap::mapAnimationTimingFunction(Animation& animation, const CSSValue& value)
490 {
491     if (value.treatAsInitialValue(CSSPropertyAnimationTimingFunction)) {
492         animation.setTimingFunction(Animation::initialTimingFunction());
493         return;
494     }
495
496     if (is<CSSPrimitiveValue>(value)) {
497         switch (downcast<CSSPrimitiveValue>(value).valueID()) {
498         case CSSValueLinear:
499             animation.setTimingFunction(LinearTimingFunction::create());
500             break;
501         case CSSValueEase:
502             animation.setTimingFunction(CubicBezierTimingFunction::create());
503             break;
504         case CSSValueEaseIn:
505             animation.setTimingFunction(CubicBezierTimingFunction::create(CubicBezierTimingFunction::EaseIn));
506             break;
507         case CSSValueEaseOut:
508             animation.setTimingFunction(CubicBezierTimingFunction::create(CubicBezierTimingFunction::EaseOut));
509             break;
510         case CSSValueEaseInOut:
511             animation.setTimingFunction(CubicBezierTimingFunction::create(CubicBezierTimingFunction::EaseInOut));
512             break;
513         case CSSValueStepStart:
514             animation.setTimingFunction(StepsTimingFunction::create(1, true));
515             break;
516         case CSSValueStepEnd:
517             animation.setTimingFunction(StepsTimingFunction::create(1, false));
518             break;
519         default:
520             break;
521         }
522         return;
523     }
524
525     if (is<CSSCubicBezierTimingFunctionValue>(value)) {
526         auto& cubicTimingFunction = downcast<CSSCubicBezierTimingFunctionValue>(value);
527         animation.setTimingFunction(CubicBezierTimingFunction::create(cubicTimingFunction.x1(), cubicTimingFunction.y1(), cubicTimingFunction.x2(), cubicTimingFunction.y2()));
528     } else if (is<CSSStepsTimingFunctionValue>(value)) {
529         auto& stepsTimingFunction = downcast<CSSStepsTimingFunctionValue>(value);
530         animation.setTimingFunction(StepsTimingFunction::create(stepsTimingFunction.numberOfSteps(), stepsTimingFunction.stepAtStart()));
531     } else if (is<CSSSpringTimingFunctionValue>(value)) {
532         auto& springTimingFunction = downcast<CSSSpringTimingFunctionValue>(value);
533         animation.setTimingFunction(SpringTimingFunction::create(springTimingFunction.mass(), springTimingFunction.stiffness(), springTimingFunction.damping(), springTimingFunction.initialVelocity()));
534     }
535 }
536
537 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
538 void CSSToStyleMap::mapAnimationTrigger(Animation& animation, const CSSValue& value)
539 {
540     if (value.treatAsInitialValue(CSSPropertyWebkitAnimationTrigger)) {
541         animation.setTrigger(Animation::initialTrigger());
542         return;
543     }
544
545     if (value.isPrimitiveValue()) {
546         auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
547         if (primitiveValue.valueID() == CSSValueAuto)
548             animation.setTrigger(AutoAnimationTrigger::create());
549         return;
550     }
551
552     if (value.isAnimationTriggerScrollValue()) {
553         auto& scrollTrigger = downcast<CSSAnimationTriggerScrollValue>(value);
554
555         const CSSPrimitiveValue& startValue = downcast<CSSPrimitiveValue>(scrollTrigger.startValue());
556         Length startLength = startValue.computeLength<Length>(m_resolver->state().cssToLengthConversionData());
557
558         Length endLength;
559         if (scrollTrigger.hasEndValue()) {
560             const CSSPrimitiveValue* endValue = downcast<CSSPrimitiveValue>(scrollTrigger.endValue());
561             endLength = endValue->computeLength<Length>(m_resolver->state().cssToLengthConversionData());
562         }
563
564         animation.setTrigger(ScrollAnimationTrigger::create(startLength, endLength));
565     }
566 }
567 #endif
568
569 void CSSToStyleMap::mapNinePieceImage(CSSPropertyID property, CSSValue* value, NinePieceImage& image)
570 {
571     // If we're not a value list, then we are "none" and don't need to alter the empty image at all.
572     if (!is<CSSValueList>(value))
573         return;
574
575     // Retrieve the border image value.
576     CSSValueList& borderImage = downcast<CSSValueList>(*value);
577
578     for (auto& current : borderImage) {
579         if (is<CSSImageValue>(current.get()) || is<CSSImageGeneratorValue>(current.get()) || is<CSSImageSetValue>(current.get()))
580             image.setImage(styleImage(current.get()));
581         else if (is<CSSBorderImageSliceValue>(current.get()))
582             mapNinePieceImageSlice(current, image);
583         else if (is<CSSValueList>(current.get())) {
584             CSSValueList& slashList = downcast<CSSValueList>(current.get());
585             // Map in the image slices.
586             if (is<CSSBorderImageSliceValue>(slashList.item(0)))
587                 mapNinePieceImageSlice(*slashList.item(0), image);
588
589             // Map in the border slices.
590             if (slashList.item(1))
591                 image.setBorderSlices(mapNinePieceImageQuad(*slashList.item(1)));
592
593             // Map in the outset.
594             if (slashList.item(2))
595                 image.setOutset(mapNinePieceImageQuad(*slashList.item(2)));
596         } else if (is<CSSPrimitiveValue>(current.get())) {
597             // Set the appropriate rules for stretch/round/repeat of the slices.
598             mapNinePieceImageRepeat(current, image);
599         }
600     }
601
602     if (property == CSSPropertyWebkitBorderImage) {
603         // We have to preserve the legacy behavior of -webkit-border-image and make the border slices
604         // also set the border widths. We don't need to worry about percentages, since we don't even support
605         // those on real borders yet.
606         if (image.borderSlices().top().isFixed())
607             style()->setBorderTopWidth(image.borderSlices().top().value());
608         if (image.borderSlices().right().isFixed())
609             style()->setBorderRightWidth(image.borderSlices().right().value());
610         if (image.borderSlices().bottom().isFixed())
611             style()->setBorderBottomWidth(image.borderSlices().bottom().value());
612         if (image.borderSlices().left().isFixed())
613             style()->setBorderLeftWidth(image.borderSlices().left().value());
614     }
615 }
616
617 void CSSToStyleMap::mapNinePieceImageSlice(CSSValue& value, NinePieceImage& image)
618 {
619     if (!is<CSSBorderImageSliceValue>(value))
620         return;
621
622     // Retrieve the border image value.
623     auto& borderImageSlice = downcast<CSSBorderImageSliceValue>(value);
624
625     // Set up a length box to represent our image slices.
626     LengthBox box;
627     Quad* slices = borderImageSlice.slices();
628     if (slices->top()->isPercentage())
629         box.top() = Length(slices->top()->doubleValue(), Percent);
630     else
631         box.top() = Length(slices->top()->intValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
632     if (slices->bottom()->isPercentage())
633         box.bottom() = Length(slices->bottom()->doubleValue(), Percent);
634     else
635         box.bottom() = Length((int)slices->bottom()->floatValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
636     if (slices->left()->isPercentage())
637         box.left() = Length(slices->left()->doubleValue(), Percent);
638     else
639         box.left() = Length(slices->left()->intValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
640     if (slices->right()->isPercentage())
641         box.right() = Length(slices->right()->doubleValue(), Percent);
642     else
643         box.right() = Length(slices->right()->intValue(CSSPrimitiveValue::CSS_NUMBER), Fixed);
644     image.setImageSlices(box);
645
646     // Set our fill mode.
647     image.setFill(borderImageSlice.m_fill);
648 }
649
650 LengthBox CSSToStyleMap::mapNinePieceImageQuad(CSSValue& value)
651 {
652     if (!is<CSSPrimitiveValue>(value))
653         return LengthBox();
654
655     // Get our zoom value.
656     CSSToLengthConversionData conversionData = useSVGZoomRules() ? m_resolver->state().cssToLengthConversionData().copyWithAdjustedZoom(1.0f) : m_resolver->state().cssToLengthConversionData();
657
658     // Retrieve the primitive value.
659     auto& borderWidths = downcast<CSSPrimitiveValue>(value);
660
661     // Set up a length box to represent our image slices.
662     LengthBox box; // Defaults to 'auto' so we don't have to handle that explicitly below.
663     Quad* slices = borderWidths.quadValue();
664     if (slices->top()->isNumber())
665         box.top() = Length(slices->top()->intValue(), Relative);
666     else if (slices->top()->isPercentage())
667         box.top() = Length(slices->top()->doubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
668     else if (slices->top()->valueID() != CSSValueAuto)
669         box.top() = slices->top()->computeLength<Length>(conversionData);
670
671     if (slices->right()->isNumber())
672         box.right() = Length(slices->right()->intValue(), Relative);
673     else if (slices->right()->isPercentage())
674         box.right() = Length(slices->right()->doubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
675     else if (slices->right()->valueID() != CSSValueAuto)
676         box.right() = slices->right()->computeLength<Length>(conversionData);
677
678     if (slices->bottom()->isNumber())
679         box.bottom() = Length(slices->bottom()->intValue(), Relative);
680     else if (slices->bottom()->isPercentage())
681         box.bottom() = Length(slices->bottom()->doubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
682     else if (slices->bottom()->valueID() != CSSValueAuto)
683         box.bottom() = slices->bottom()->computeLength<Length>(conversionData);
684
685     if (slices->left()->isNumber())
686         box.left() = Length(slices->left()->intValue(), Relative);
687     else if (slices->left()->isPercentage())
688         box.left() = Length(slices->left()->doubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent);
689     else if (slices->left()->valueID() != CSSValueAuto)
690         box.left() = slices->left()->computeLength<Length>(conversionData);
691
692     return box;
693 }
694
695 void CSSToStyleMap::mapNinePieceImageRepeat(CSSValue& value, NinePieceImage& image)
696 {
697     if (!is<CSSPrimitiveValue>(value))
698         return;
699
700     CSSPrimitiveValue& primitiveValue = downcast<CSSPrimitiveValue>(value);
701     Pair* pair = primitiveValue.pairValue();
702     if (!pair || !pair->first() || !pair->second())
703         return;
704
705     CSSValueID firstIdentifier = pair->first()->valueID();
706     CSSValueID secondIdentifier = pair->second()->valueID();
707
708     ENinePieceImageRule horizontalRule;
709     switch (firstIdentifier) {
710     case CSSValueStretch:
711         horizontalRule = StretchImageRule;
712         break;
713     case CSSValueRound:
714         horizontalRule = RoundImageRule;
715         break;
716     case CSSValueSpace:
717         horizontalRule = SpaceImageRule;
718         break;
719     default: // CSSValueRepeat
720         horizontalRule = RepeatImageRule;
721         break;
722     }
723     image.setHorizontalRule(horizontalRule);
724
725     ENinePieceImageRule verticalRule;
726     switch (secondIdentifier) {
727     case CSSValueStretch:
728         verticalRule = StretchImageRule;
729         break;
730     case CSSValueRound:
731         verticalRule = RoundImageRule;
732         break;
733     case CSSValueSpace:
734         verticalRule = SpaceImageRule;
735         break;
736     default: // CSSValueRepeat
737         verticalRule = RepeatImageRule;
738         break;
739     }
740     image.setVerticalRule(verticalRule);
741 }
742
743 };