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