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 "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
43namespace blink {
44
45const CSSToLengthConversionData& CSSToStyleMap::cssToLengthConversionData() const
46{
47    return m_state.cssToLengthConversionData();
48}
49
50PassRefPtr<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
55void 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
81void 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
95void 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
109void 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
123void 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
138void 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
149void 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
163void 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
177void 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
219void 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
241void 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
263void 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
290double CSSToStyleMap::mapAnimationDelay(CSSValue* value)
291{
292    if (value->isInitialValue())
293        return CSSTimingData::initialDelay();
294    return toCSSPrimitiveValue(value)->computeSeconds();
295}
296
297Timing::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
317double CSSToStyleMap::mapAnimationDuration(CSSValue* value)
318{
319    if (value->isInitialValue())
320        return CSSTimingData::initialDuration();
321    return toCSSPrimitiveValue(value)->computeSeconds();
322}
323
324Timing::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
344double 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
354AtomicString 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
364EAnimPlayState 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
374CSSTransitionData::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
388PassRefPtr<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
436void 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
496void 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
529static 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
540BorderImageLengthBox 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
555void 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