1/*
2 * Copyright (C) 2013 Intel Corporation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#include "core/rendering/animation/WebAnimationProvider.h"
29
30#include "core/platform/animation/AnimationTranslationUtil.h"
31#include "core/platform/animation/CSSAnimationData.h"
32#include "core/rendering/style/KeyframeList.h"
33#include "core/rendering/style/RenderStyle.h"
34#include "public/platform/WebAnimation.h"
35#include "wtf/text/StringBuilder.h"
36
37using blink::WebAnimation;
38
39namespace WebCore {
40
41namespace {
42
43String animationNameForTransition(AnimatedPropertyID property)
44{
45    // | is not a valid identifier character in CSS, so this can never conflict with a keyframe identifier.
46    StringBuilder id;
47    id.appendLiteral("-|transition");
48    id.appendNumber(static_cast<int>(property));
49    id.append('-');
50    return id.toString();
51}
52
53AnimatedPropertyID cssToGraphicsLayerProperty(CSSPropertyID cssProperty)
54{
55    switch (cssProperty) {
56    case CSSPropertyWebkitTransform:
57        return AnimatedPropertyWebkitTransform;
58    case CSSPropertyOpacity:
59        return AnimatedPropertyOpacity;
60    case CSSPropertyBackgroundColor:
61        ASSERT_NOT_REACHED();
62        return AnimatedPropertyInvalid; // Chromium compositor cannot accelerate background color yet.
63    case CSSPropertyWebkitFilter:
64        return AnimatedPropertyWebkitFilter;
65    default:
66        // It's fine if we see other css properties here; they are just not accelerated.
67        break;
68    }
69    return AnimatedPropertyInvalid;
70}
71
72} // namespace
73
74WebAnimations::WebAnimations()
75{
76}
77
78WebAnimations::~WebAnimations()
79{
80}
81
82// Copy constructor is needed to use this struct as a return value. It actually moves the ownership, not copy.
83WebAnimations::WebAnimations(const WebAnimations& other)
84{
85    ASSERT(isEmpty());
86    m_transformAnimation.swap(const_cast<OwnPtr<WebAnimation>& >(other.m_transformAnimation));
87    m_opacityAnimation.swap(const_cast<OwnPtr<WebAnimation>& >(other.m_opacityAnimation));
88    m_filterAnimation.swap(const_cast<OwnPtr<WebAnimation>& >(other.m_filterAnimation));
89    ASSERT(other.isEmpty());
90}
91
92bool WebAnimations::isEmpty() const
93{
94    return !m_transformAnimation && !m_opacityAnimation && !m_filterAnimation;
95}
96
97WebAnimationProvider::WebAnimationProvider()
98{
99}
100
101WebAnimationProvider::~WebAnimationProvider()
102{
103}
104
105int WebAnimationProvider::getWebAnimationId(const String& animationName) const
106{
107    if (!m_animationIdMap.contains(animationName))
108        return 0;
109    return m_animationIdMap.get(animationName);
110}
111
112int WebAnimationProvider::getWebAnimationId(CSSPropertyID property) const
113{
114    AnimatedPropertyID animatedProperty = cssToGraphicsLayerProperty(property);
115    ASSERT(animatedProperty != AnimatedPropertyInvalid);
116    return getWebAnimationId(animationNameForTransition(animatedProperty));
117}
118
119WebAnimations WebAnimationProvider::startAnimation(double timeOffset, const CSSAnimationData* anim, const KeyframeList& keyframes, bool hasTransform, const IntSize& boxSize)
120{
121    ASSERT(hasTransform || boxSize.isEmpty());
122    bool hasOpacity = keyframes.containsProperty(CSSPropertyOpacity);
123    bool hasFilter = keyframes.containsProperty(CSSPropertyWebkitFilter);
124
125    if (!hasOpacity && !hasTransform && !hasFilter)
126        return WebAnimations();
127
128    KeyframeValueList transformVector(AnimatedPropertyWebkitTransform);
129    KeyframeValueList opacityVector(AnimatedPropertyOpacity);
130    KeyframeValueList filterVector(AnimatedPropertyWebkitFilter);
131
132    size_t numKeyframes = keyframes.size();
133    for (size_t i = 0; i < numKeyframes; ++i) {
134        const KeyframeValue& currentKeyframe = keyframes[i];
135        const RenderStyle* keyframeStyle = currentKeyframe.style();
136        double key = currentKeyframe.key();
137
138        if (!keyframeStyle)
139            continue;
140
141        // Get timing function.
142        RefPtr<TimingFunction> tf = KeyframeValue::timingFunction(*keyframeStyle);
143
144        bool isFirstOrLastKeyframe = !key || key == 1;
145        if ((hasTransform && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyWebkitTransform))
146            transformVector.insert(adoptPtr(new TransformAnimationValue(key, &(keyframeStyle->transform()), tf)));
147
148        if ((hasOpacity && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyOpacity))
149            opacityVector.insert(adoptPtr(new FloatAnimationValue(key, keyframeStyle->opacity(), tf)));
150
151        if ((hasFilter && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyWebkitFilter))
152            filterVector.insert(adoptPtr(new FilterAnimationValue(key, &(keyframeStyle->filter()), tf)));
153    }
154    WebAnimations resultAnimations;
155    if (hasTransform)
156        resultAnimations.m_transformAnimation = createWebAnimationAndStoreId(transformVector, boxSize, anim, keyframes.animationName(), timeOffset);
157    if (hasOpacity)
158        resultAnimations.m_opacityAnimation = createWebAnimationAndStoreId(opacityVector, IntSize(), anim, keyframes.animationName(), timeOffset);
159    if (hasFilter)
160        resultAnimations.m_filterAnimation = createWebAnimationAndStoreId(filterVector, IntSize(), anim, keyframes.animationName(), timeOffset);
161
162    return resultAnimations;
163}
164
165WebAnimations WebAnimationProvider::startTransition(double timeOffset, CSSPropertyID property, const RenderStyle* fromStyle, const RenderStyle* toStyle, bool hasTransform, bool hasFilter, const IntSize& boxSize, float fromOpacity, float toOpacity)
166{
167    ASSERT(property != CSSPropertyInvalid);
168    ASSERT(property == CSSPropertyOpacity || (!fromOpacity && !toOpacity));
169
170    WebAnimations resultAnimations;
171    if (property == CSSPropertyOpacity) {
172        const CSSAnimationData* opacityAnim = toStyle->transitionForProperty(CSSPropertyOpacity);
173        if (opacityAnim && !opacityAnim->isEmptyOrZeroDuration()) {
174            KeyframeValueList opacityVector(AnimatedPropertyOpacity);
175            opacityVector.insert(adoptPtr(new FloatAnimationValue(0, fromOpacity)));
176            opacityVector.insert(adoptPtr(new FloatAnimationValue(1, toOpacity)));
177            resultAnimations.m_opacityAnimation = createWebAnimationAndStoreId(opacityVector, IntSize(), opacityAnim, animationNameForTransition(AnimatedPropertyOpacity), timeOffset);
178        }
179    }
180    if (property == CSSPropertyWebkitTransform && hasTransform) {
181        const CSSAnimationData* transformAnim = toStyle->transitionForProperty(CSSPropertyWebkitTransform);
182        if (transformAnim && !transformAnim->isEmptyOrZeroDuration()) {
183            KeyframeValueList transformVector(AnimatedPropertyWebkitTransform);
184            transformVector.insert(adoptPtr(new TransformAnimationValue(0, &fromStyle->transform())));
185            transformVector.insert(adoptPtr(new TransformAnimationValue(1, &toStyle->transform())));
186            resultAnimations.m_transformAnimation = createWebAnimationAndStoreId(transformVector, boxSize, transformAnim, animationNameForTransition(AnimatedPropertyWebkitTransform), timeOffset);
187        }
188    }
189    if (property == CSSPropertyWebkitFilter && hasFilter) {
190        const CSSAnimationData* filterAnim = toStyle->transitionForProperty(CSSPropertyWebkitFilter);
191        if (filterAnim && !filterAnim->isEmptyOrZeroDuration()) {
192            KeyframeValueList filterVector(AnimatedPropertyWebkitFilter);
193            filterVector.insert(adoptPtr(new FilterAnimationValue(0, &fromStyle->filter())));
194            filterVector.insert(adoptPtr(new FilterAnimationValue(1, &toStyle->filter())));
195            resultAnimations.m_filterAnimation = createWebAnimationAndStoreId(filterVector, IntSize(), filterAnim, animationNameForTransition(AnimatedPropertyWebkitFilter), timeOffset);
196        }
197    }
198
199    return resultAnimations;
200}
201
202PassOwnPtr<WebAnimation> WebAnimationProvider::createWebAnimationAndStoreId(const KeyframeValueList& values, const IntSize& boxSize, const CSSAnimationData* animation, const String& animationName, double timeOffset)
203{
204    int animationId = getWebAnimationId(animationName);
205    OwnPtr<WebAnimation> webAnimation(createWebAnimation(values, animation, animationId, timeOffset, boxSize));
206    if (!webAnimation)
207        return PassOwnPtr<WebAnimation>();
208
209    if (!animationId)
210        m_animationIdMap.set(animationName, webAnimation->id());
211    return webAnimation.release();
212}
213
214} // namespace WebCore
215