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