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