1/*
2 * Copyright (C) 2013 Google Inc. 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 are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "core/animation/Animation.h"
33
34#include "bindings/core/v8/Dictionary.h"
35#include "bindings/core/v8/ExceptionState.h"
36#include "core/animation/ActiveAnimations.h"
37#include "core/animation/AnimationPlayer.h"
38#include "core/animation/AnimationTimeline.h"
39#include "core/animation/CompositorAnimations.h"
40#include "core/animation/Interpolation.h"
41#include "core/animation/KeyframeEffectModel.h"
42#include "core/dom/Element.h"
43#include "core/frame/UseCounter.h"
44#include "core/rendering/RenderLayer.h"
45
46namespace blink {
47
48PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* target, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtrWillBeRawPtr<EventDelegate> eventDelegate)
49{
50    return adoptRefWillBeNoop(new Animation(target, effect, timing, priority, eventDelegate));
51}
52
53PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Dictionary& timingInputDictionary)
54{
55    ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
56    return create(element, effect, TimingInput::convert(timingInputDictionary));
57}
58PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, PassRefPtrWillBeRawPtr<AnimationEffect> effect, double duration)
59{
60    ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
61    return create(element, effect, TimingInput::convert(duration));
62}
63PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, PassRefPtrWillBeRawPtr<AnimationEffect> effect)
64{
65    ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
66    return create(element, effect, Timing());
67}
68PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, const Dictionary& timingInputDictionary, ExceptionState& exceptionState)
69{
70    ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
71    if (element)
72        UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectObjectTiming);
73    return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), TimingInput::convert(timingInputDictionary));
74}
75PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, double duration, ExceptionState& exceptionState)
76{
77    ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
78    if (element)
79        UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectDoubleTiming);
80    return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), TimingInput::convert(duration));
81}
82PassRefPtrWillBeRawPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState)
83{
84    ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
85    if (element)
86        UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectNoTiming);
87    return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), Timing());
88}
89
90Animation::Animation(Element* target, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtrWillBeRawPtr<EventDelegate> eventDelegate)
91    : AnimationNode(timing, eventDelegate)
92    , m_target(target)
93    , m_effect(effect)
94    , m_sampledEffect(nullptr)
95    , m_priority(priority)
96{
97#if !ENABLE(OILPAN)
98    if (m_target)
99        m_target->ensureActiveAnimations().addAnimation(this);
100#endif
101}
102
103Animation::~Animation()
104{
105#if !ENABLE(OILPAN)
106    if (m_target)
107        m_target->activeAnimations()->notifyAnimationDestroyed(this);
108#endif
109}
110
111void Animation::attach(AnimationPlayer* player)
112{
113    if (m_target) {
114        m_target->ensureActiveAnimations().players().add(player);
115        m_target->setNeedsAnimationStyleRecalc();
116    }
117    AnimationNode::attach(player);
118}
119
120void Animation::detach()
121{
122    if (m_target)
123        m_target->activeAnimations()->players().remove(player());
124    if (m_sampledEffect)
125        clearEffects();
126    AnimationNode::detach();
127}
128
129void Animation::specifiedTimingChanged()
130{
131    if (player()) {
132        // FIXME: Needs to consider groups when added.
133        ASSERT(player()->source() == this);
134        player()->setCompositorPending(true);
135    }
136}
137
138static AnimationStack& ensureAnimationStack(Element* element)
139{
140    return element->ensureActiveAnimations().defaultStack();
141}
142
143void Animation::applyEffects()
144{
145    ASSERT(isInEffect());
146    ASSERT(player());
147    if (!m_target || !m_effect)
148        return;
149
150    double iteration = currentIteration();
151    ASSERT(iteration >= 0);
152    // FIXME: Handle iteration values which overflow int.
153    OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > interpolations = m_effect->sample(static_cast<int>(iteration), timeFraction(), iterationDuration());
154    if (m_sampledEffect) {
155        m_sampledEffect->setInterpolations(interpolations.release());
156    } else if (!interpolations->isEmpty()) {
157        OwnPtrWillBeRawPtr<SampledEffect> sampledEffect = SampledEffect::create(this, interpolations.release());
158        m_sampledEffect = sampledEffect.get();
159        ensureAnimationStack(m_target).add(sampledEffect.release());
160    } else {
161        return;
162    }
163
164    m_target->setNeedsAnimationStyleRecalc();
165}
166
167void Animation::clearEffects()
168{
169    ASSERT(player());
170    ASSERT(m_sampledEffect);
171
172    m_sampledEffect->clear();
173    m_sampledEffect = nullptr;
174    cancelAnimationOnCompositor();
175    m_target->setNeedsAnimationStyleRecalc();
176    invalidate();
177}
178
179void Animation::updateChildrenAndEffects() const
180{
181    if (!m_effect)
182        return;
183    if (isInEffect())
184        const_cast<Animation*>(this)->applyEffects();
185    else if (m_sampledEffect)
186        const_cast<Animation*>(this)->clearEffects();
187}
188
189double Animation::calculateTimeToEffectChange(bool forwards, double localTime, double timeToNextIteration) const
190{
191    const double start = startTimeInternal() + specifiedTiming().startDelay;
192    const double end = start + activeDurationInternal();
193
194    switch (phase()) {
195    case PhaseNone:
196        return std::numeric_limits<double>::infinity();
197    case PhaseBefore:
198        ASSERT(start >= localTime);
199        return forwards
200            ? start - localTime
201            : std::numeric_limits<double>::infinity();
202    case PhaseActive:
203        if (forwards && hasActiveAnimationsOnCompositor()) {
204            // Need service to apply fill / fire events.
205            const double timeToEnd = end - localTime;
206            if (hasEvents()) {
207                return std::min(timeToEnd, timeToNextIteration);
208            } else {
209                return timeToEnd;
210            }
211        }
212        return 0;
213    case PhaseAfter:
214        ASSERT(localTime >= end);
215        // If this Animation is still in effect then it will need to update
216        // when its parent goes out of effect. We have no way of knowing when
217        // that will be, however, so the parent will need to supply it.
218        return forwards
219            ? std::numeric_limits<double>::infinity()
220            : localTime - end;
221    default:
222        ASSERT_NOT_REACHED();
223        return std::numeric_limits<double>::infinity();
224    }
225}
226
227void Animation::notifySampledEffectRemovedFromAnimationStack()
228{
229    ASSERT(m_sampledEffect);
230    m_sampledEffect = nullptr;
231}
232
233#if !ENABLE(OILPAN)
234void Animation::notifyElementDestroyed()
235{
236    // If our player is kept alive just by the sampledEffect, we might get our
237    // destructor called when we call SampledEffect::clear(), so we need to
238    // clear m_sampledEffect first.
239    m_target = nullptr;
240    clearEventDelegate();
241    SampledEffect* sampledEffect = m_sampledEffect;
242    m_sampledEffect = nullptr;
243    if (sampledEffect)
244        sampledEffect->clear();
245}
246#endif
247
248bool Animation::isCandidateForAnimationOnCompositor(double playerPlaybackRate) const
249{
250    if (!effect() || !m_target)
251        return false;
252    return CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(specifiedTiming(), *effect(), playerPlaybackRate);
253}
254
255bool Animation::maybeStartAnimationOnCompositor(double startTime, double currentTime)
256{
257    return maybeStartAnimationOnCompositor(startTime, currentTime, 1);
258}
259
260bool Animation::maybeStartAnimationOnCompositor(double startTime, double currentTime, double playerPlaybackRate)
261{
262    ASSERT(!hasActiveAnimationsOnCompositor());
263    if (!isCandidateForAnimationOnCompositor(playerPlaybackRate))
264        return false;
265    if (!CompositorAnimations::instance()->canStartAnimationOnCompositor(*m_target))
266        return false;
267    if (!CompositorAnimations::instance()->startAnimationOnCompositor(*m_target, startTime, currentTime, specifiedTiming(), *effect(), m_compositorAnimationIds, playerPlaybackRate))
268        return false;
269    ASSERT(!m_compositorAnimationIds.isEmpty());
270    return true;
271}
272
273bool Animation::hasActiveAnimationsOnCompositor() const
274{
275    return !m_compositorAnimationIds.isEmpty();
276}
277
278bool Animation::hasActiveAnimationsOnCompositor(CSSPropertyID property) const
279{
280    return hasActiveAnimationsOnCompositor() && affects(property);
281}
282
283bool Animation::affects(CSSPropertyID property) const
284{
285    return m_effect && m_effect->affects(property);
286}
287
288void Animation::cancelAnimationOnCompositor()
289{
290    // FIXME: cancelAnimationOnCompositor is called from withins style recalc.
291    // This queries compositingState, which is not necessarily up to date.
292    // https://code.google.com/p/chromium/issues/detail?id=339847
293    DisableCompositingQueryAsserts disabler;
294    if (!hasActiveAnimationsOnCompositor())
295        return;
296    if (!m_target || !m_target->renderer())
297        return;
298    for (size_t i = 0; i < m_compositorAnimationIds.size(); ++i)
299        CompositorAnimations::instance()->cancelAnimationOnCompositor(*m_target, m_compositorAnimationIds[i]);
300    m_compositorAnimationIds.clear();
301    player()->setCompositorPending(true);
302}
303
304void Animation::pauseAnimationForTestingOnCompositor(double pauseTime)
305{
306    ASSERT(hasActiveAnimationsOnCompositor());
307    if (!m_target || !m_target->renderer())
308        return;
309    for (size_t i = 0; i < m_compositorAnimationIds.size(); ++i)
310        CompositorAnimations::instance()->pauseAnimationForTestingOnCompositor(*m_target, m_compositorAnimationIds[i], pauseTime);
311}
312
313void Animation::trace(Visitor* visitor)
314{
315    visitor->trace(m_target);
316    visitor->trace(m_effect);
317    visitor->trace(m_sampledEffect);
318    AnimationNode::trace(visitor);
319}
320
321} // namespace blink
322