1 /* 2 * Copyright (C) 2007, 2008, 2009 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/AnimationController.h" 31 32 #include "core/dom/AnimationEvent.h" 33 #include "core/dom/EventNames.h" 34 #include "core/dom/PseudoElement.h" 35 #include "core/dom/TransitionEvent.h" 36 #include "core/page/Frame.h" 37 #include "core/page/FrameView.h" 38 #include "core/page/Page.h" 39 #include "core/page/animation/AnimationBase.h" 40 #include "core/page/animation/AnimationControllerPrivate.h" 41 #include "core/page/animation/CSSPropertyAnimation.h" 42 #include "core/page/animation/CompositeAnimation.h" 43 #include "core/rendering/RenderView.h" 44 #include "wtf/CurrentTime.h" 45 46 namespace WebCore { 47 48 static const double cBeginAnimationUpdateTimeNotSet = -1; 49 50 AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame) 51 : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired) 52 , m_updateStyleIfNeededDispatcher(this, &AnimationControllerPrivate::updateStyleIfNeededDispatcherFired) 53 , m_frame(frame) 54 , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet) 55 , m_animationsWaitingForStyle() 56 , m_animationsWaitingForStartTimeResponse() 57 , m_waitingForAsyncStartNotification(false) 58 { 59 } 60 61 AnimationControllerPrivate::~AnimationControllerPrivate() 62 { 63 } 64 65 PassRefPtr<CompositeAnimation> AnimationControllerPrivate::accessCompositeAnimation(RenderObject* renderer) 66 { 67 RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); 68 if (!animation) { 69 animation = CompositeAnimation::create(this); 70 m_compositeAnimations.set(renderer, animation); 71 } 72 return animation; 73 } 74 75 bool AnimationControllerPrivate::clear(RenderObject* renderer) 76 { 77 // Return false if we didn't do anything OR we are suspended (so we don't try to 78 // do a setNeedsStyleRecalc() when suspended). 79 PassRefPtr<CompositeAnimation> animation = m_compositeAnimations.take(renderer); 80 if (!animation) 81 return false; 82 animation->clearRenderer(); 83 return animation->suspended(); 84 } 85 86 void AnimationControllerPrivate::updateAnimations(double& timeToNextService, double& timeToNextEvent, SetNeedsStyleRecalc callSetNeedsStyleRecalc/* = DoNotCallSetNeedsStyleRecalc*/) 87 { 88 double minTimeToNextService = -1; 89 double minTimeToNextEvent = -1; 90 bool updateStyleNeeded = false; 91 92 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 93 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 94 CompositeAnimation* compAnim = it->value.get(); 95 if (!compAnim->suspended() && compAnim->hasAnimations()) { 96 double t = compAnim->timeToNextService(); 97 if (t != -1 && (t < minTimeToNextService || minTimeToNextService == -1)) 98 minTimeToNextService = t; 99 double nextEvent = compAnim->timeToNextEvent(); 100 if (nextEvent != -1 && (nextEvent < minTimeToNextEvent || minTimeToNextEvent == -1)) 101 minTimeToNextEvent = nextEvent; 102 if (callSetNeedsStyleRecalc == CallSetNeedsStyleRecalc) { 103 if (!t) { 104 Node* node = it->key->node(); 105 node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeFromRenderer); 106 updateStyleNeeded = true; 107 } 108 } else if (!minTimeToNextService && !minTimeToNextEvent) { 109 // Found the minimum values and do not need to mark for style recalc. 110 break; 111 } 112 } 113 } 114 115 if (updateStyleNeeded) 116 m_frame->document()->updateStyleIfNeeded(); 117 118 timeToNextService = minTimeToNextService; 119 timeToNextEvent = minTimeToNextEvent; 120 } 121 122 void AnimationControllerPrivate::scheduleServiceForRenderer(RenderObject* renderer) 123 { 124 double timeToNextService = -1; 125 double timeToNextEvent = -1; 126 127 RefPtr<CompositeAnimation> compAnim = m_compositeAnimations.get(renderer); 128 if (!compAnim->suspended() && compAnim->hasAnimations()) { 129 timeToNextService = compAnim->timeToNextService(); 130 timeToNextEvent = compAnim->timeToNextEvent(); 131 } 132 133 if (timeToNextService >= 0) 134 scheduleService(timeToNextService, timeToNextEvent); 135 } 136 137 void AnimationControllerPrivate::scheduleService() 138 { 139 double timeToNextService = -1; 140 double timeToNextEvent = -1; 141 updateAnimations(timeToNextService, timeToNextEvent, DoNotCallSetNeedsStyleRecalc); 142 scheduleService(timeToNextService, timeToNextEvent); 143 } 144 145 void AnimationControllerPrivate::scheduleService(double timeToNextService, double timeToNextEvent) 146 { 147 if (!m_frame->page()) 148 return; 149 150 bool visible = m_frame->page()->visibilityState() == WebCore::PageVisibilityStateVisible; 151 152 if (!visible) 153 timeToNextService = timeToNextEvent; 154 155 if (visible && !timeToNextService) { 156 m_frame->document()->view()->scheduleAnimation(); 157 if (m_animationTimer.isActive()) 158 m_animationTimer.stop(); 159 return; 160 } 161 162 if (timeToNextService < 0) { 163 if (m_animationTimer.isActive()) 164 m_animationTimer.stop(); 165 return; 166 } 167 168 if (m_animationTimer.isActive() && m_animationTimer.nextFireInterval() <= timeToNextService) 169 return; 170 171 m_animationTimer.startOneShot(timeToNextService); 172 } 173 174 void AnimationControllerPrivate::updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate>*) 175 { 176 fireEventsAndUpdateStyle(); 177 } 178 179 void AnimationControllerPrivate::fireEventsAndUpdateStyle() 180 { 181 // Protect the frame from getting destroyed in the event handler 182 RefPtr<Frame> protector = m_frame; 183 184 bool updateStyle = !m_eventsToDispatch.isEmpty() || !m_nodeChangesToDispatch.isEmpty(); 185 186 // fire all the events 187 Vector<EventToDispatch> eventsToDispatch = m_eventsToDispatch; 188 m_eventsToDispatch.clear(); 189 Vector<EventToDispatch>::const_iterator eventsToDispatchEnd = eventsToDispatch.end(); 190 for (Vector<EventToDispatch>::const_iterator it = eventsToDispatch.begin(); it != eventsToDispatchEnd; ++it) { 191 Element* element = it->element.get(); 192 if (it->eventType == eventNames().transitionendEvent) 193 element->dispatchEvent(TransitionEvent::create(it->eventType, it->name, it->elapsedTime, PseudoElement::pseudoElementNameForEvents(element->pseudoId()))); 194 else 195 element->dispatchEvent(AnimationEvent::create(it->eventType, it->name, it->elapsedTime)); 196 } 197 198 // call setChanged on all the elements 199 Vector<RefPtr<Node> >::const_iterator nodeChangesToDispatchEnd = m_nodeChangesToDispatch.end(); 200 for (Vector<RefPtr<Node> >::const_iterator it = m_nodeChangesToDispatch.begin(); it != nodeChangesToDispatchEnd; ++it) 201 (*it)->setNeedsStyleRecalc(LocalStyleChange, StyleChangeFromRenderer); 202 203 m_nodeChangesToDispatch.clear(); 204 205 if (updateStyle && m_frame) 206 m_frame->document()->updateStyleIfNeeded(); 207 } 208 209 void AnimationControllerPrivate::startUpdateStyleIfNeededDispatcher() 210 { 211 if (!m_updateStyleIfNeededDispatcher.isActive()) 212 m_updateStyleIfNeededDispatcher.startOneShot(0); 213 } 214 215 void AnimationControllerPrivate::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime) 216 { 217 m_eventsToDispatch.grow(m_eventsToDispatch.size()+1); 218 EventToDispatch& event = m_eventsToDispatch[m_eventsToDispatch.size()-1]; 219 event.element = element; 220 event.eventType = eventType; 221 event.name = name; 222 event.elapsedTime = elapsedTime; 223 224 startUpdateStyleIfNeededDispatcher(); 225 } 226 227 void AnimationControllerPrivate::addNodeChangeToDispatch(PassRefPtr<Node> node) 228 { 229 if (!node) 230 return; 231 232 m_nodeChangesToDispatch.append(node); 233 startUpdateStyleIfNeededDispatcher(); 234 } 235 236 void AnimationControllerPrivate::serviceAnimations() 237 { 238 double timeToNextService = -1; 239 double timeToNextEvent = -1; 240 updateAnimations(timeToNextService, timeToNextEvent, CallSetNeedsStyleRecalc); 241 scheduleService(timeToNextService, timeToNextEvent); 242 243 // Fire events right away, to avoid a flash of unanimated style after an animation completes, and before 244 // the 'end' event fires. 245 fireEventsAndUpdateStyle(); 246 } 247 248 void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>*) 249 { 250 // Make sure animationUpdateTime is updated, so that it is current even if no 251 // styleChange has happened (e.g. accelerated animations) 252 setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 253 serviceAnimations(); 254 } 255 256 bool AnimationControllerPrivate::isRunningAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 257 { 258 RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); 259 if (!animation) 260 return false; 261 262 return animation->isAnimatingProperty(property, false, isRunningNow); 263 } 264 265 bool AnimationControllerPrivate::isRunningAcceleratableAnimationOnRenderer(RenderObject *renderer) const 266 { 267 RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); 268 if (!animation) 269 return false; 270 271 bool acceleratedOnly = false; 272 bool isRunningNow = true; 273 return animation->isAnimatingProperty(CSSPropertyOpacity, acceleratedOnly, isRunningNow) 274 || animation->isAnimatingProperty(CSSPropertyWebkitTransform, acceleratedOnly, isRunningNow) 275 || animation->isAnimatingProperty(CSSPropertyWebkitFilter, acceleratedOnly, isRunningNow); 276 } 277 278 bool AnimationControllerPrivate::isRunningAcceleratedAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 279 { 280 RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); 281 if (!animation) 282 return false; 283 284 return animation->isAnimatingProperty(property, true, isRunningNow); 285 } 286 287 void AnimationControllerPrivate::suspendAnimations() 288 { 289 suspendAnimationsForDocument(m_frame->document()); 290 291 // Traverse subframes 292 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) 293 child->animation()->suspendAnimations(); 294 } 295 296 void AnimationControllerPrivate::resumeAnimations() 297 { 298 resumeAnimationsForDocument(m_frame->document()); 299 300 // Traverse subframes 301 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) 302 child->animation()->resumeAnimations(); 303 } 304 305 void AnimationControllerPrivate::suspendAnimationsForDocument(Document* document) 306 { 307 setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 308 309 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 310 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 311 RenderObject* renderer = it->key; 312 if (renderer->document() == document) { 313 CompositeAnimation* compAnim = it->value.get(); 314 compAnim->suspendAnimations(); 315 } 316 } 317 318 scheduleService(); 319 } 320 321 void AnimationControllerPrivate::resumeAnimationsForDocument(Document* document) 322 { 323 setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 324 325 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 326 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 327 RenderObject* renderer = it->key; 328 if (renderer->document() == document) { 329 CompositeAnimation* compAnim = it->value.get(); 330 compAnim->resumeAnimations(); 331 } 332 } 333 334 scheduleService(); 335 } 336 337 void AnimationControllerPrivate::pauseAnimationsForTesting(double t) 338 { 339 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 340 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 341 it->value->pauseAnimationsForTesting(t); 342 it->key->node()->setNeedsStyleRecalc(LocalStyleChange, StyleChangeFromRenderer); 343 } 344 } 345 346 double AnimationControllerPrivate::beginAnimationUpdateTime() 347 { 348 if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) 349 m_beginAnimationUpdateTime = currentTime(); 350 return m_beginAnimationUpdateTime; 351 } 352 353 void AnimationControllerPrivate::endAnimationUpdate() 354 { 355 styleAvailable(); 356 if (!m_waitingForAsyncStartNotification) 357 startTimeResponse(beginAnimationUpdateTime()); 358 } 359 360 void AnimationControllerPrivate::receivedStartTimeResponse(double time) 361 { 362 m_waitingForAsyncStartNotification = false; 363 startTimeResponse(time); 364 } 365 366 PassRefPtr<RenderStyle> AnimationControllerPrivate::getAnimatedStyleForRenderer(RenderObject* renderer) 367 { 368 if (!renderer) 369 return 0; 370 371 RefPtr<CompositeAnimation> rendererAnimations = m_compositeAnimations.get(renderer); 372 if (!rendererAnimations) 373 return renderer->style(); 374 375 RefPtr<RenderStyle> animatingStyle = rendererAnimations->getAnimatedStyle(); 376 if (!animatingStyle) 377 animatingStyle = renderer->style(); 378 379 return animatingStyle.release(); 380 } 381 382 unsigned AnimationControllerPrivate::numberOfActiveAnimations(Document* document) const 383 { 384 unsigned count = 0; 385 386 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 387 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 388 RenderObject* renderer = it->key; 389 CompositeAnimation* compAnim = it->value.get(); 390 if (renderer->document() == document) 391 count += compAnim->numberOfActiveAnimations(); 392 } 393 394 return count; 395 } 396 397 void AnimationControllerPrivate::addToAnimationsWaitingForStyle(AnimationBase* animation) 398 { 399 // Make sure this animation is not in the start time waiters 400 m_animationsWaitingForStartTimeResponse.remove(animation); 401 402 m_animationsWaitingForStyle.add(animation); 403 } 404 405 void AnimationControllerPrivate::removeFromAnimationsWaitingForStyle(AnimationBase* animationToRemove) 406 { 407 m_animationsWaitingForStyle.remove(animationToRemove); 408 } 409 410 void AnimationControllerPrivate::styleAvailable() 411 { 412 // Go through list of waiters and send them on their way 413 WaitingAnimationsSet::const_iterator it = m_animationsWaitingForStyle.begin(); 414 WaitingAnimationsSet::const_iterator end = m_animationsWaitingForStyle.end(); 415 for (; it != end; ++it) 416 (*it)->styleAvailable(); 417 418 m_animationsWaitingForStyle.clear(); 419 } 420 421 void AnimationControllerPrivate::addToAnimationsWaitingForStartTimeResponse(AnimationBase* animation, bool willGetResponse) 422 { 423 // If willGetResponse is true, it means this animation is actually waiting for a response 424 // (which will come in as a call to notifyAnimationStarted()). 425 // In that case we don't need to add it to this list. We just set a waitingForAResponse flag 426 // which says we are waiting for the response. If willGetResponse is false, this animation 427 // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for 428 // another animation to which it will sync. 429 // 430 // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is 431 // true. If so, we just return and will do our work when the first notifyXXXStarted() call 432 // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will 433 // do our work right away. In both cases we call the onAnimationStartResponse() method 434 // on each animation. In the first case we send in the time we got from notifyXXXStarted(). 435 // In the second case, we just pass in the beginAnimationUpdateTime(). 436 // 437 // This will synchronize all software and accelerated animations started in the same 438 // updateStyleIfNeeded cycle. 439 // 440 441 if (willGetResponse) 442 m_waitingForAsyncStartNotification = true; 443 444 m_animationsWaitingForStartTimeResponse.add(animation); 445 } 446 447 void AnimationControllerPrivate::removeFromAnimationsWaitingForStartTimeResponse(AnimationBase* animationToRemove) 448 { 449 m_animationsWaitingForStartTimeResponse.remove(animationToRemove); 450 451 if (m_animationsWaitingForStartTimeResponse.isEmpty()) 452 m_waitingForAsyncStartNotification = false; 453 } 454 455 void AnimationControllerPrivate::startTimeResponse(double time) 456 { 457 // Go through list of waiters and send them on their way 458 459 WaitingAnimationsSet::const_iterator it = m_animationsWaitingForStartTimeResponse.begin(); 460 WaitingAnimationsSet::const_iterator end = m_animationsWaitingForStartTimeResponse.end(); 461 for (; it != end; ++it) 462 (*it)->onAnimationStartResponse(time); 463 464 m_animationsWaitingForStartTimeResponse.clear(); 465 m_waitingForAsyncStartNotification = false; 466 } 467 468 void AnimationControllerPrivate::animationWillBeRemoved(AnimationBase* animation) 469 { 470 removeFromAnimationsWaitingForStyle(animation); 471 removeFromAnimationsWaitingForStartTimeResponse(animation); 472 } 473 474 AnimationController::AnimationController(Frame* frame) 475 : m_data(adoptPtr(new AnimationControllerPrivate(frame))) 476 , m_beginAnimationUpdateCount(0) 477 { 478 } 479 480 AnimationController::~AnimationController() 481 { 482 } 483 484 void AnimationController::cancelAnimations(RenderObject* renderer) 485 { 486 if (!m_data->hasAnimations()) 487 return; 488 489 if (m_data->clear(renderer)) { 490 if (Node* node = renderer->node()) 491 node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeFromRenderer); 492 } 493 } 494 495 PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* renderer, RenderStyle* newStyle) 496 { 497 if (!renderer->document()) 498 return newStyle; 499 500 RenderStyle* oldStyle = renderer->style(); 501 502 if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle->animations() && !newStyle->transitions())) 503 return newStyle; 504 505 // Don't run transitions when printing. 506 if (renderer->view()->printing()) 507 return newStyle; 508 509 // Fetch our current set of implicit animations from a hashtable. We then compare them 510 // against the animations in the style and make sure we're in sync. If destination values 511 // have changed, we reset the animation. We then do a blend to get new values and we return 512 // a new style. 513 514 // We don't support anonymous pseudo elements like :first-line or :first-letter. 515 ASSERT(renderer->node()); 516 517 RefPtr<CompositeAnimation> rendererAnimations = m_data->accessCompositeAnimation(renderer); 518 RefPtr<RenderStyle> blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle); 519 520 if (renderer->parent() || newStyle->animations() || (oldStyle && oldStyle->animations())) { 521 m_data->scheduleServiceForRenderer(renderer); 522 } 523 524 if (blendedStyle != newStyle) { 525 // If the animations/transitions change opacity or transform, we need to update 526 // the style to impose the stacking rules. Note that this is also 527 // done in StyleResolver::adjustRenderStyle(). 528 if (blendedStyle->hasAutoZIndex() && (blendedStyle->opacity() < 1.0f || blendedStyle->hasTransform())) 529 blendedStyle->setZIndex(0); 530 } 531 return blendedStyle.release(); 532 } 533 534 PassRefPtr<RenderStyle> AnimationController::getAnimatedStyleForRenderer(RenderObject* renderer) 535 { 536 return m_data->getAnimatedStyleForRenderer(renderer); 537 } 538 539 void AnimationController::notifyAnimationStarted(RenderObject*, double startTime) 540 { 541 m_data->receivedStartTimeResponse(startTime); 542 } 543 544 void AnimationController::pauseAnimationsForTesting(double t) 545 { 546 m_data->pauseAnimationsForTesting(t); 547 } 548 549 unsigned AnimationController::numberOfActiveAnimations(Document* document) const 550 { 551 return m_data->numberOfActiveAnimations(document); 552 } 553 554 bool AnimationController::isRunningAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 555 { 556 return m_data->isRunningAnimationOnRenderer(renderer, property, isRunningNow); 557 } 558 559 bool AnimationController::isRunningAcceleratableAnimationOnRenderer(RenderObject* renderer) const 560 { 561 return m_data->isRunningAcceleratableAnimationOnRenderer(renderer); 562 } 563 564 bool AnimationController::isRunningAcceleratedAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 565 { 566 return m_data->isRunningAcceleratedAnimationOnRenderer(renderer, property, isRunningNow); 567 } 568 569 void AnimationController::suspendAnimations() 570 { 571 m_data->suspendAnimations(); 572 } 573 574 void AnimationController::resumeAnimations() 575 { 576 m_data->resumeAnimations(); 577 } 578 579 void AnimationController::serviceAnimations() 580 { 581 m_data->serviceAnimations(); 582 } 583 584 void AnimationController::suspendAnimationsForDocument(Document* document) 585 { 586 m_data->suspendAnimationsForDocument(document); 587 } 588 589 void AnimationController::resumeAnimationsForDocument(Document* document) 590 { 591 m_data->resumeAnimationsForDocument(document); 592 } 593 594 void AnimationController::beginAnimationUpdate() 595 { 596 if (!m_beginAnimationUpdateCount) 597 m_data->setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 598 ++m_beginAnimationUpdateCount; 599 } 600 601 void AnimationController::endAnimationUpdate() 602 { 603 ASSERT(m_beginAnimationUpdateCount > 0); 604 --m_beginAnimationUpdateCount; 605 if (!m_beginAnimationUpdateCount) 606 m_data->endAnimationUpdate(); 607 } 608 609 bool AnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property) 610 { 611 return CSSPropertyAnimation::animationOfPropertyIsAccelerated(property); 612 } 613 614 } // namespace WebCore 615