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