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