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 "CSSPropertyNames.h"
     34 #include "ImplicitAnimation.h"
     35 #include "KeyframeAnimation.h"
     36 #include "RenderObject.h"
     37 #include "RenderStyle.h"
     38 
     39 namespace WebCore {
     40 
     41 CompositeAnimation::~CompositeAnimation()
     42 {
     43     // Toss the refs to all animations
     44     m_transitions.clear();
     45     m_keyframeAnimations.clear();
     46 }
     47 
     48 void 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 
     69 void 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 
    184 void 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 
    261 PassRefPtr<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 
    293 PassRefPtr<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
    314 void 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 
    333 double 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 
    366 PassRefPtr<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 
    385 void 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 
    410 void 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 
    437 void 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 
    449 void 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 
    461 bool 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 
    484 bool 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 
    504 bool 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 
    521 unsigned 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