1/*
2 * Copyright (C) 2007 Apple 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
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "CompositeAnimation.h"
31
32#include "AnimationControllerPrivate.h"
33#include "CSSPropertyNames.h"
34#include "ImplicitAnimation.h"
35#include "KeyframeAnimation.h"
36#include "RenderObject.h"
37#include "RenderStyle.h"
38
39namespace WebCore {
40
41CompositeAnimation::~CompositeAnimation()
42{
43    // Toss the refs to all animations
44    m_transitions.clear();
45    m_keyframeAnimations.clear();
46}
47
48void CompositeAnimation::clearRenderer()
49{
50    if (!m_transitions.isEmpty()) {
51        // Clear the renderers from all running animations, in case we are in the middle of
52        // an animation callback (see https://bugs.webkit.org/show_bug.cgi?id=22052)
53        CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
54        for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
55            ImplicitAnimation* transition = it->second.get();
56            transition->clearRenderer();
57        }
58    }
59    if (!m_keyframeAnimations.isEmpty()) {
60        m_keyframeAnimations.checkConsistency();
61        AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
62        for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
63            KeyframeAnimation* anim = it->second.get();
64            anim->clearRenderer();
65        }
66    }
67}
68
69void CompositeAnimation::updateTransitions(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
70{
71    // If currentStyle is null or there are no old or new transitions, just skip it
72    if (!currentStyle || (!targetStyle->transitions() && m_transitions.isEmpty()))
73        return;
74
75    // Mark all existing transitions as no longer active. We will mark the still active ones
76    // in the next loop and then toss the ones that didn't get marked.
77    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
78    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it)
79        it->second->setActive(false);
80
81    RefPtr<RenderStyle> modifiedCurrentStyle;
82
83    // Check to see if we need to update the active transitions
84    if (targetStyle->transitions()) {
85        for (size_t i = 0; i < targetStyle->transitions()->size(); ++i) {
86            const Animation* anim = targetStyle->transitions()->animation(i);
87            bool isActiveTransition = anim->duration() || anim->delay() > 0;
88
89            int prop = anim->property();
90
91            if (prop == cAnimateNone)
92                continue;
93
94            bool all = prop == cAnimateAll;
95
96            // Handle both the 'all' and single property cases. For the single prop case, we make only one pass
97            // through the loop.
98            for (int propertyIndex = 0; propertyIndex < AnimationBase::getNumProperties(); ++propertyIndex) {
99                if (all) {
100                    // Get the next property which is not a shorthand.
101                    bool isShorthand;
102                    prop = AnimationBase::getPropertyAtIndex(propertyIndex, isShorthand);
103                    if (isShorthand)
104                        continue;
105                }
106
107                // ImplicitAnimations are always hashed by actual properties, never cAnimateAll
108                ASSERT(prop >= firstCSSProperty && prop < (firstCSSProperty + numCSSProperties));
109
110                // If there is a running animation for this property, the transition is overridden
111                // and we have to use the unanimatedStyle from the animation. We do the test
112                // against the unanimated style here, but we "override" the transition later.
113                RefPtr<KeyframeAnimation> keyframeAnim = getAnimationForProperty(prop);
114                RenderStyle* fromStyle = keyframeAnim ? keyframeAnim->unanimatedStyle() : currentStyle;
115
116                // See if there is a current transition for this prop
117                ImplicitAnimation* implAnim = m_transitions.get(prop).get();
118                bool equal = true;
119
120                if (implAnim) {
121                    // If we are post active don't bother setting the active flag. This will cause
122                    // this animation to get removed at the end of this function.
123                    if (!implAnim->postActive())
124                        implAnim->setActive(true);
125
126                    // This might be a transition that is just finishing. That would be the case
127                    // if it were postActive. But we still need to check for equality because
128                    // it could be just finishing AND changing to a new goal state.
129                    //
130                    // This implAnim might also not be an already running transition. It might be
131                    // newly added to the list in a previous iteration. This would happen if
132                    // you have both an explicit transition-property and 'all' in the same
133                    // list. In this case, the latter one overrides the earlier one, so we
134                    // behave as though this is a running animation being replaced.
135                    if (!implAnim->isTargetPropertyEqual(prop, targetStyle)) {
136    #if USE(ACCELERATED_COMPOSITING)
137                        // For accelerated animations we need to return a new RenderStyle with the _current_ value
138                        // of the property, so that restarted transitions use the correct starting point.
139                        if (AnimationBase::animationOfPropertyIsAccelerated(prop) && !implAnim->isFallbackAnimating()) {
140                            if (!modifiedCurrentStyle)
141                                modifiedCurrentStyle = RenderStyle::clone(currentStyle);
142
143                            implAnim->blendPropertyValueInStyle(prop, modifiedCurrentStyle.get());
144                        }
145    #endif
146                        m_transitions.remove(prop);
147                        equal = false;
148                    }
149                } else {
150                    // We need to start a transition if it is active and the properties don't match
151                    equal = !isActiveTransition || AnimationBase::propertiesEqual(prop, fromStyle, targetStyle);
152                }
153
154                // We can be in this loop with an inactive transition (!isActiveTransition). We need
155                // to do that to check to see if we are canceling a transition. But we don't want to
156                // start one of the inactive transitions. So short circuit that here. (See
157                // <https://bugs.webkit.org/show_bug.cgi?id=24787>
158                if (!equal && isActiveTransition) {
159                    // Add the new transition
160                    m_transitions.set(prop, ImplicitAnimation::create(const_cast<Animation*>(anim), prop, renderer, this, modifiedCurrentStyle ? modifiedCurrentStyle.get() : fromStyle));
161                }
162
163                // We only need one pass for the single prop case
164                if (!all)
165                    break;
166            }
167        }
168    }
169
170    // Make a list of transitions to be removed
171    Vector<int> toBeRemoved;
172    end = m_transitions.end();
173    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
174        ImplicitAnimation* anim = it->second.get();
175        if (!anim->active())
176            toBeRemoved.append(anim->animatingProperty());
177    }
178
179    // Now remove the transitions from the list
180    for (size_t j = 0; j < toBeRemoved.size(); ++j)
181        m_transitions.remove(toBeRemoved[j]);
182}
183
184void CompositeAnimation::updateKeyframeAnimations(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
185{
186    // Nothing to do if we don't have any animations, and didn't have any before
187    if (m_keyframeAnimations.isEmpty() && !targetStyle->hasAnimations())
188        return;
189
190    m_keyframeAnimations.checkConsistency();
191
192    AnimationNameMap::const_iterator kfend = m_keyframeAnimations.end();
193
194    if (currentStyle && currentStyle->hasAnimations() && targetStyle->hasAnimations() && *(currentStyle->animations()) == *(targetStyle->animations())) {
195        // The current and target animations are the same so we just need to toss any
196        // animation which is finished (postActive).
197        for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
198            if (it->second->postActive())
199                it->second->setIndex(-1);
200        }
201    } else {
202        // Mark all existing animations as no longer active.
203        for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it)
204            it->second->setIndex(-1);
205
206        // Toss the animation order map.
207        m_keyframeAnimationOrderMap.clear();
208
209        // Now mark any still active animations as active and add any new animations.
210        if (targetStyle->animations()) {
211            int numAnims = targetStyle->animations()->size();
212            for (int i = 0; i < numAnims; ++i) {
213                const Animation* anim = targetStyle->animations()->animation(i);
214                AtomicString animationName(anim->name());
215
216                if (!anim->isValidAnimation())
217                    continue;
218
219                // See if there is a current animation for this name.
220                RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(animationName.impl());
221
222                if (keyframeAnim) {
223                    // If this animation is postActive, skip it so it gets removed at the end of this function.
224                    if (keyframeAnim->postActive())
225                        continue;
226
227                    // This one is still active.
228
229                    // Animations match, but play states may differ. Update if needed.
230                    keyframeAnim->updatePlayState(anim->playState() == AnimPlayStatePlaying);
231
232                    // Set the saved animation to this new one, just in case the play state has changed.
233                    keyframeAnim->setAnimation(anim);
234                    keyframeAnim->setIndex(i);
235                } else if ((anim->duration() || anim->delay()) && anim->iterationCount()) {
236                    keyframeAnim = KeyframeAnimation::create(const_cast<Animation*>(anim), renderer, i, this, currentStyle ? currentStyle : targetStyle);
237                    m_keyframeAnimations.set(keyframeAnim->name().impl(), keyframeAnim);
238                }
239
240                // Add this to the animation order map.
241                if (keyframeAnim)
242                    m_keyframeAnimationOrderMap.append(keyframeAnim->name().impl());
243            }
244        }
245    }
246
247    // Make a list of animations to be removed.
248    Vector<AtomicStringImpl*> animsToBeRemoved;
249    kfend = m_keyframeAnimations.end();
250    for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
251        KeyframeAnimation* keyframeAnim = it->second.get();
252        if (keyframeAnim->index() < 0)
253            animsToBeRemoved.append(keyframeAnim->name().impl());
254    }
255
256    // Now remove the animations from the list.
257    for (size_t j = 0; j < animsToBeRemoved.size(); ++j)
258        m_keyframeAnimations.remove(animsToBeRemoved[j]);
259}
260
261PassRefPtr<RenderStyle> CompositeAnimation::animate(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
262{
263    RefPtr<RenderStyle> resultStyle;
264
265    // We don't do any transitions if we don't have a currentStyle (on startup).
266    updateTransitions(renderer, currentStyle, targetStyle);
267    updateKeyframeAnimations(renderer, currentStyle, targetStyle);
268    m_keyframeAnimations.checkConsistency();
269
270    if (currentStyle) {
271        // Now that we have transition objects ready, let them know about the new goal state.  We want them
272        // to fill in a RenderStyle*& only if needed.
273        if (!m_transitions.isEmpty()) {
274            CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
275            for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
276                if (ImplicitAnimation* anim = it->second.get())
277                    anim->animate(this, renderer, currentStyle, targetStyle, resultStyle);
278            }
279        }
280    }
281
282    // Now that we have animation objects ready, let them know about the new goal state.  We want them
283    // to fill in a RenderStyle*& only if needed.
284    for (Vector<AtomicStringImpl*>::const_iterator it = m_keyframeAnimationOrderMap.begin(); it != m_keyframeAnimationOrderMap.end(); ++it) {
285        RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(*it);
286        if (keyframeAnim)
287            keyframeAnim->animate(this, renderer, currentStyle, targetStyle, resultStyle);
288    }
289
290    return resultStyle ? resultStyle.release() : targetStyle;
291}
292
293PassRefPtr<RenderStyle> CompositeAnimation::getAnimatedStyle() const
294{
295    RefPtr<RenderStyle> resultStyle;
296    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
297    for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
298        if (ImplicitAnimation* implicitAnimation = it->second.get())
299            implicitAnimation->getAnimatedStyle(resultStyle);
300    }
301
302    m_keyframeAnimations.checkConsistency();
303
304    for (Vector<AtomicStringImpl*>::const_iterator it = m_keyframeAnimationOrderMap.begin(); it != m_keyframeAnimationOrderMap.end(); ++it) {
305        RefPtr<KeyframeAnimation> keyframeAnimation = m_keyframeAnimations.get(*it);
306        if (keyframeAnimation)
307            keyframeAnimation->getAnimatedStyle(resultStyle);
308    }
309
310    return resultStyle;
311}
312
313// "animating" means that something is running that requires the timer to keep firing
314void CompositeAnimation::setAnimating(bool animating)
315{
316    if (!m_transitions.isEmpty()) {
317        CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
318        for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
319            ImplicitAnimation* transition = it->second.get();
320            transition->setAnimating(animating);
321        }
322    }
323    if (!m_keyframeAnimations.isEmpty()) {
324        m_keyframeAnimations.checkConsistency();
325        AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
326        for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
327            KeyframeAnimation* anim = it->second.get();
328            anim->setAnimating(animating);
329        }
330    }
331}
332
333double CompositeAnimation::timeToNextService() const
334{
335    // Returns the time at which next service is required. -1 means no service is required. 0 means
336    // service is required now, and > 0 means service is required that many seconds in the future.
337    double minT = -1;
338
339    if (!m_transitions.isEmpty()) {
340        CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
341        for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
342            ImplicitAnimation* transition = it->second.get();
343            double t = transition ? transition->timeToNextService() : -1;
344            if (t < minT || minT == -1)
345                minT = t;
346            if (minT == 0)
347                return 0;
348        }
349    }
350    if (!m_keyframeAnimations.isEmpty()) {
351        m_keyframeAnimations.checkConsistency();
352        AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
353        for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
354            KeyframeAnimation* animation = it->second.get();
355            double t = animation ? animation->timeToNextService() : -1;
356            if (t < minT || minT == -1)
357                minT = t;
358            if (minT == 0)
359                return 0;
360        }
361    }
362
363    return minT;
364}
365
366PassRefPtr<KeyframeAnimation> CompositeAnimation::getAnimationForProperty(int property) const
367{
368    RefPtr<KeyframeAnimation> retval;
369
370    // We want to send back the last animation with the property if there are multiples.
371    // So we need to iterate through all animations
372    if (!m_keyframeAnimations.isEmpty()) {
373        m_keyframeAnimations.checkConsistency();
374        AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
375        for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
376            RefPtr<KeyframeAnimation> anim = it->second;
377            if (anim->hasAnimationForProperty(property))
378                retval = anim;
379        }
380    }
381
382    return retval;
383}
384
385void CompositeAnimation::suspendAnimations()
386{
387    if (m_isSuspended)
388        return;
389
390    m_isSuspended = true;
391
392    if (!m_keyframeAnimations.isEmpty()) {
393        m_keyframeAnimations.checkConsistency();
394        AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
395        for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
396            if (KeyframeAnimation* anim = it->second.get())
397                anim->updatePlayState(false);
398        }
399    }
400    if (!m_transitions.isEmpty()) {
401        CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
402        for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
403            ImplicitAnimation* anim = it->second.get();
404            if (anim && anim->hasStyle())
405                anim->updatePlayState(false);
406        }
407    }
408}
409
410void CompositeAnimation::resumeAnimations()
411{
412    if (!m_isSuspended)
413        return;
414
415    m_isSuspended = false;
416
417    if (!m_keyframeAnimations.isEmpty()) {
418        m_keyframeAnimations.checkConsistency();
419        AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
420        for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
421            KeyframeAnimation* anim = it->second.get();
422            if (anim && anim->playStatePlaying())
423                anim->updatePlayState(true);
424        }
425    }
426
427    if (!m_transitions.isEmpty()) {
428        CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
429        for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
430            ImplicitAnimation* anim = it->second.get();
431            if (anim && anim->hasStyle())
432                anim->updatePlayState(true);
433        }
434    }
435}
436
437void CompositeAnimation::overrideImplicitAnimations(int property)
438{
439    CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
440    if (!m_transitions.isEmpty()) {
441        for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
442            ImplicitAnimation* anim = it->second.get();
443            if (anim && anim->animatingProperty() == property)
444                anim->setOverridden(true);
445        }
446    }
447}
448
449void CompositeAnimation::resumeOverriddenImplicitAnimations(int property)
450{
451    if (!m_transitions.isEmpty()) {
452        CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
453        for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
454            ImplicitAnimation* anim = it->second.get();
455            if (anim && anim->animatingProperty() == property)
456                anim->setOverridden(false);
457        }
458    }
459}
460
461bool CompositeAnimation::isAnimatingProperty(int property, bool isRunningNow) const
462{
463    if (!m_keyframeAnimations.isEmpty()) {
464        m_keyframeAnimations.checkConsistency();
465        AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
466        for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
467            KeyframeAnimation* anim = it->second.get();
468            if (anim && anim->isAnimatingProperty(property, isRunningNow))
469                return true;
470        }
471    }
472
473    if (!m_transitions.isEmpty()) {
474        CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
475        for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
476            ImplicitAnimation* anim = it->second.get();
477            if (anim && anim->isAnimatingProperty(property, isRunningNow))
478                return true;
479        }
480    }
481    return false;
482}
483
484bool CompositeAnimation::pauseAnimationAtTime(const AtomicString& name, double t)
485{
486    if (!name)
487        return false;
488
489    m_keyframeAnimations.checkConsistency();
490
491    RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(name.impl());
492    if (!keyframeAnim || !keyframeAnim->running())
493        return false;
494
495    int count = keyframeAnim->m_animation->iterationCount();
496    if ((t >= 0.0) && (!count || (t <= count * keyframeAnim->duration()))) {
497        keyframeAnim->freezeAtTime(t);
498        return true;
499    }
500
501    return false;
502}
503
504bool CompositeAnimation::pauseTransitionAtTime(int property, double t)
505{
506    if ((property < firstCSSProperty) || (property >= firstCSSProperty + numCSSProperties))
507        return false;
508
509    ImplicitAnimation* implAnim = m_transitions.get(property).get();
510    if (!implAnim || !implAnim->running())
511        return false;
512
513    if ((t >= 0.0) && (t <= implAnim->duration())) {
514        implAnim->freezeAtTime(t);
515        return true;
516    }
517
518    return false;
519}
520
521unsigned CompositeAnimation::numberOfActiveAnimations() const
522{
523    unsigned count = 0;
524
525    if (!m_keyframeAnimations.isEmpty()) {
526        m_keyframeAnimations.checkConsistency();
527        AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
528        for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
529            KeyframeAnimation* anim = it->second.get();
530            if (anim->running())
531                ++count;
532        }
533    }
534
535    if (!m_transitions.isEmpty()) {
536        CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
537        for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
538            ImplicitAnimation* anim = it->second.get();
539            if (anim->running())
540                ++count;
541        }
542    }
543
544    return count;
545}
546
547} // namespace WebCore
548