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