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 
~CompositeAnimation()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 
clearRenderer()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 
updateTransitions(RenderObject * renderer,RenderStyle * currentStyle,RenderStyle * targetStyle)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 
updateKeyframeAnimations(RenderObject * renderer,RenderStyle * currentStyle,RenderStyle * targetStyle)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 
animate(RenderObject * renderer,RenderStyle * currentStyle,RenderStyle * targetStyle)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 
getAnimatedStyle() const309 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
setAnimating(bool animating)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 
timeToNextService() const349 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 
getAnimationForProperty(int property) const382 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 
suspendAnimations()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 
resumeAnimations()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 
overrideImplicitAnimations(int property)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 
resumeOverriddenImplicitAnimations(int property)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 
isAnimatingProperty(int property,bool acceleratedOnly,bool isRunningNow) const477 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 
pauseAnimationAtTime(const AtomicString & name,double t)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 
pauseTransitionAtTime(int property,double t)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 
numberOfActiveAnimations() const550 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 
animations() const576 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