Home | History | Annotate | Download | only in animation
      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 
     42 namespace WebCore {
     43 
     44 CompositeAnimation::~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 
     54 void 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 
     77 void 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 
    195 void 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 
    277 PassRefPtr<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 
    309 PassRefPtr<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
    330 void 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 
    349 double 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 
    382 PassRefPtr<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 
    401 void 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 
    426 void 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 
    453 void 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 
    465 void 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 
    477 bool 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 
    500 bool 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 
    520 bool 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 
    550 unsigned 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 
    576 PassRefPtr<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