diff options
Diffstat (limited to 'src/3rdparty/webkit/WebCore/page/animation/AnimationController.cpp')
-rw-r--r-- | src/3rdparty/webkit/WebCore/page/animation/AnimationController.cpp | 379 |
1 files changed, 239 insertions, 140 deletions
diff --git a/src/3rdparty/webkit/WebCore/page/animation/AnimationController.cpp b/src/3rdparty/webkit/WebCore/page/animation/AnimationController.cpp index 3b6cfcc..58a1f5b 100644 --- a/src/3rdparty/webkit/WebCore/page/animation/AnimationController.cpp +++ b/src/3rdparty/webkit/WebCore/page/animation/AnimationController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,81 +28,32 @@ #include "config.h" #include "AnimationController.h" -#include "CompositeAnimation.h" + +#include "AnimationBase.h" +#include "AnimationControllerPrivate.h" #include "CSSParser.h" +#include "CompositeAnimation.h" #include "EventNames.h" #include "Frame.h" -#include "SystemTime.h" -#include "Timer.h" +#include "RenderView.h" +#include <wtf/CurrentTime.h> +#include <wtf/UnusedParam.h> namespace WebCore { static const double cAnimationTimerDelay = 0.025; static const double cBeginAnimationUpdateTimeNotSet = -1; -class AnimationControllerPrivate { -public: - AnimationControllerPrivate(Frame*); - ~AnimationControllerPrivate(); - - PassRefPtr<CompositeAnimation> accessCompositeAnimation(RenderObject*); - bool clear(RenderObject*); - - void animationTimerFired(Timer<AnimationControllerPrivate>*); - void updateAnimationTimer(bool callSetChanged = false); - - void updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*); - void startUpdateRenderingDispatcher(); - void addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime); - - bool hasAnimations() const { return !m_compositeAnimations.isEmpty(); } - - void suspendAnimations(Document*); - void resumeAnimations(Document*); - - void styleAvailable(); - - bool isAnimatingPropertyOnRenderer(RenderObject*, int property, bool isRunningNow) const; - - bool pauseAnimationAtTime(RenderObject*, const String& name, double t); - bool pauseTransitionAtTime(RenderObject*, const String& property, double t); - unsigned numberOfActiveAnimations() const; - - double beginAnimationUpdateTime() - { - if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) - m_beginAnimationUpdateTime = currentTime(); - return m_beginAnimationUpdateTime; - } - - void setBeginAnimationUpdateTime(double t) { m_beginAnimationUpdateTime = t; } - -private: - typedef HashMap<RenderObject*, RefPtr<CompositeAnimation> > RenderObjectAnimationMap; - - RenderObjectAnimationMap m_compositeAnimations; - Timer<AnimationControllerPrivate> m_animationTimer; - Timer<AnimationControllerPrivate> m_updateRenderingDispatcher; - Frame* m_frame; - - class EventToDispatch { - public: - RefPtr<Element> element; - AtomicString eventType; - String name; - double elapsedTime; - }; - - Vector<EventToDispatch> m_eventsToDispatch; - - double m_beginAnimationUpdateTime; -}; - AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame) : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired) - , m_updateRenderingDispatcher(this, &AnimationControllerPrivate::updateRenderingDispatcherFired) + , m_updateStyleIfNeededDispatcher(this, &AnimationControllerPrivate::updateStyleIfNeededDispatcherFired) , m_frame(frame) , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet) + , m_styleAvailableWaiters(0) + , m_lastStyleAvailableWaiter(0) + , m_responseWaiters(0) + , m_lastResponseWaiter(0) + , m_waitingForAResponse(false) { } @@ -114,7 +65,7 @@ PassRefPtr<CompositeAnimation> AnimationControllerPrivate::accessCompositeAnimat { RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); if (!animation) { - animation = CompositeAnimation::create(m_frame->animation()); + animation = CompositeAnimation::create(this); m_compositeAnimations.set(renderer, animation); } return animation; @@ -123,7 +74,7 @@ PassRefPtr<CompositeAnimation> AnimationControllerPrivate::accessCompositeAnimat bool AnimationControllerPrivate::clear(RenderObject* renderer) { // Return false if we didn't do anything OR we are suspended (so we don't try to - // do a setChanged() when suspended). + // do a setNeedsStyleRecalc() when suspended). PassRefPtr<CompositeAnimation> animation = m_compositeAnimations.take(renderer); if (!animation) return false; @@ -131,23 +82,6 @@ bool AnimationControllerPrivate::clear(RenderObject* renderer) return animation->isSuspended(); } -void AnimationControllerPrivate::styleAvailable() -{ - // styleAvailable() can call event handlers which would ultimately delete a CompositeAnimation - // from the m_compositeAnimations table. So we can't iterate it directly. We will instead build - // a list of CompositeAnimations which need the styleAvailable() call iterate over that. - Vector<RefPtr<CompositeAnimation> > list; - - RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); - for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) - if (it->second->isWaitingForStyleAvailable()) - list.append(it->second); - - Vector<RefPtr<CompositeAnimation> >::const_iterator listEnd = list.end(); - for (Vector<RefPtr<CompositeAnimation> >::const_iterator it = list.begin(); it != listEnd; ++it) - (*it)->styleAvailable(); -} - void AnimationControllerPrivate::updateAnimationTimer(bool callSetChanged/* = false*/) { double needsService = -1; @@ -155,16 +89,16 @@ void AnimationControllerPrivate::updateAnimationTimer(bool callSetChanged/* = fa RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { - RefPtr<CompositeAnimation> compAnim = it->second; - if (!compAnim->isSuspended()) { - double t = compAnim->willNeedService(); + CompositeAnimation* compAnim = it->second.get(); + if (!compAnim->isSuspended() && compAnim->hasAnimations()) { + double t = compAnim->timeToNextService(); if (t != -1 && (t < needsService || needsService == -1)) needsService = t; if (needsService == 0) { if (callSetChanged) { - Node* node = it->first->element(); + Node* node = it->first->node(); ASSERT(!node || (node->document() && !node->document()->inPageCache())); - node->setChanged(AnimationStyleChange); + node->setNeedsStyleRecalc(AnimationStyleChange); calledSetChanged = true; } else @@ -174,7 +108,7 @@ void AnimationControllerPrivate::updateAnimationTimer(bool callSetChanged/* = fa } if (calledSetChanged) - m_frame->document()->updateRendering(); + m_frame->document()->updateStyleIfNeeded(); // If we want service immediately, we start a repeating timer to reduce the overhead of starting if (needsService == 0) { @@ -196,11 +130,11 @@ void AnimationControllerPrivate::updateAnimationTimer(bool callSetChanged/* = fa m_animationTimer.startOneShot(needsService); } -void AnimationControllerPrivate::updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*) +void AnimationControllerPrivate::updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate>*) { // fire all the events - Vector<EventToDispatch>::const_iterator end = m_eventsToDispatch.end(); - for (Vector<EventToDispatch>::const_iterator it = m_eventsToDispatch.begin(); it != end; ++it) { + Vector<EventToDispatch>::const_iterator eventsToDispatchEnd = m_eventsToDispatch.end(); + for (Vector<EventToDispatch>::const_iterator it = m_eventsToDispatch.begin(); it != eventsToDispatchEnd; ++it) { if (it->eventType == eventNames().webkitTransitionEndEvent) it->element->dispatchWebKitTransitionEvent(it->eventType,it->name, it->elapsedTime); else @@ -209,14 +143,35 @@ void AnimationControllerPrivate::updateRenderingDispatcherFired(Timer<AnimationC m_eventsToDispatch.clear(); - if (m_frame && m_frame->document()) - m_frame->document()->updateRendering(); + // call setChanged on all the elements + Vector<RefPtr<Node> >::const_iterator nodeChangesToDispatchEnd = m_nodeChangesToDispatch.end(); + for (Vector<RefPtr<Node> >::const_iterator it = m_nodeChangesToDispatch.begin(); it != nodeChangesToDispatchEnd; ++it) + (*it)->setNeedsStyleRecalc(AnimationStyleChange); + + m_nodeChangesToDispatch.clear(); + + if (m_frame) + m_frame->document()->updateStyleIfNeeded(); + + // We can now safely remove any animations or transitions that are finished. + // We can't remove them any earlier because we might get a false restart of + // a transition. This can happen because we have not yet set the final property + // value until we call the rendering dispatcher. So this can make the current + // style slightly different from the desired final style (because our last + // animation step was, say 0.9999 or something). And we need to remove them + // here because if there are no more animations running we'll never get back + // into the animation code to clean them up. + RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); + for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { + CompositeAnimation* compAnim = it->second.get(); + compAnim->cleanupFinishedAnimations(); // will not modify m_compositeAnimations, so OK to call while iterating + } } -void AnimationControllerPrivate::startUpdateRenderingDispatcher() +void AnimationControllerPrivate::startUpdateStyleIfNeededDispatcher() { - if (!m_updateRenderingDispatcher.isActive()) - m_updateRenderingDispatcher.startOneShot(0); + if (!m_updateStyleIfNeededDispatcher.isActive()) + m_updateStyleIfNeededDispatcher.startOneShot(0); } void AnimationControllerPrivate::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime) @@ -228,21 +183,31 @@ void AnimationControllerPrivate::addEventToDispatch(PassRefPtr<Element> element, event.name = name; event.elapsedTime = elapsedTime; - startUpdateRenderingDispatcher(); + startUpdateStyleIfNeededDispatcher(); +} + +void AnimationControllerPrivate::addNodeChangeToDispatch(PassRefPtr<Node> node) +{ + ASSERT(!node || (node->document() && !node->document()->inPageCache())); + if (!node) + return; + + m_nodeChangesToDispatch.append(node); + startUpdateStyleIfNeededDispatcher(); } void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>*) { // Make sure animationUpdateTime is updated, so that it is current even if no - // styleChange has happened (e.g. hardware animations) + // styleChange has happened (e.g. accelerated animations) setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate - // updateRendering. It will then call back to us with new information. + // updateStyleIfNeeded. It will then call back to us with new information. updateAnimationTimer(true); } -bool AnimationControllerPrivate::isAnimatingPropertyOnRenderer(RenderObject* renderer, int property, bool isRunningNow) const +bool AnimationControllerPrivate::isAnimatingPropertyOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const { RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); if (!animation) @@ -253,12 +218,15 @@ bool AnimationControllerPrivate::isAnimatingPropertyOnRenderer(RenderObject* ren void AnimationControllerPrivate::suspendAnimations(Document* document) { + setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); + RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { RenderObject* renderer = it->first; - RefPtr<CompositeAnimation> compAnim = it->second; - if (renderer->document() == document) + if (renderer->document() == document) { + CompositeAnimation* compAnim = it->second.get(); compAnim->suspendAnimations(); + } } updateAnimationTimer(); @@ -266,12 +234,15 @@ void AnimationControllerPrivate::suspendAnimations(Document* document) void AnimationControllerPrivate::resumeAnimations(Document* document) { + setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); + RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { RenderObject* renderer = it->first; - RefPtr<CompositeAnimation> compAnim = it->second; - if (renderer->document() == document) + if (renderer->document() == document) { + CompositeAnimation* compAnim = it->second.get(); compAnim->resumeAnimations(); + } } updateAnimationTimer(); @@ -287,7 +258,8 @@ bool AnimationControllerPrivate::pauseAnimationAtTime(RenderObject* renderer, co return false; if (compAnim->pauseAnimationAtTime(name, t)) { - renderer->node()->setChanged(AnimationStyleChange); + renderer->node()->setNeedsStyleRecalc(AnimationStyleChange); + startUpdateStyleIfNeededDispatcher(); return true; } @@ -304,13 +276,40 @@ bool AnimationControllerPrivate::pauseTransitionAtTime(RenderObject* renderer, c return false; if (compAnim->pauseTransitionAtTime(cssPropertyID(property), t)) { - renderer->node()->setChanged(AnimationStyleChange); + renderer->node()->setNeedsStyleRecalc(AnimationStyleChange); + startUpdateStyleIfNeededDispatcher(); return true; } return false; } +double AnimationControllerPrivate::beginAnimationUpdateTime() +{ + if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) + m_beginAnimationUpdateTime = currentTime(); + return m_beginAnimationUpdateTime; +} + +PassRefPtr<RenderStyle> AnimationControllerPrivate::getAnimatedStyleForRenderer(RenderObject* renderer) +{ + if (!renderer) + return 0; + + RefPtr<CompositeAnimation> rendererAnimations = m_compositeAnimations.get(renderer); + if (!rendererAnimations) + return renderer->style(); + + // Make sure animationUpdateTime is updated, so that it is current even if no + // styleChange has happened (e.g. accelerated animations). + setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); + RefPtr<RenderStyle> animatingStyle = rendererAnimations->getAnimatedStyle(); + if (!animatingStyle) + animatingStyle = renderer->style(); + + return animatingStyle.release(); +} + unsigned AnimationControllerPrivate::numberOfActiveAnimations() const { unsigned count = 0; @@ -324,9 +323,119 @@ unsigned AnimationControllerPrivate::numberOfActiveAnimations() const return count; } +void AnimationControllerPrivate::addToStyleAvailableWaitList(AnimationBase* animation) +{ + ASSERT(!animation->next()); + + if (m_styleAvailableWaiters) + m_lastStyleAvailableWaiter->setNext(animation); + else + m_styleAvailableWaiters = animation; + + m_lastStyleAvailableWaiter = animation; + animation->setNext(0); +} + +void AnimationControllerPrivate::removeFromStyleAvailableWaitList(AnimationBase* animationToRemove) +{ + AnimationBase* prevAnimation = 0; + for (AnimationBase* animation = m_styleAvailableWaiters; animation; animation = animation->next()) { + if (animation == animationToRemove) { + if (prevAnimation) + prevAnimation->setNext(animation->next()); + else + m_styleAvailableWaiters = animation->next(); + + if (m_lastStyleAvailableWaiter == animation) + m_lastStyleAvailableWaiter = prevAnimation; + + animationToRemove->setNext(0); + } + } +} + +void AnimationControllerPrivate::styleAvailable() +{ + // Go through list of waiters and send them on their way + for (AnimationBase* animation = m_styleAvailableWaiters; animation; ) { + AnimationBase* nextAnimation = animation->next(); + animation->setNext(0); + animation->styleAvailable(); + animation = nextAnimation; + } + + m_styleAvailableWaiters = 0; + m_lastStyleAvailableWaiter = 0; +} + +void AnimationControllerPrivate::addToStartTimeResponseWaitList(AnimationBase* animation, bool willGetResponse) +{ + // If willGetResponse is true, it means this animation is actually waiting for a response + // (which will come in as a call to notifyAnimationStarted()). + // In that case we don't need to add it to this list. We just set a waitingForAResponse flag + // which says we are waiting for the response. If willGetResponse is false, this animation + // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for + // another animation to which it will sync. + // + // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is + // true. If so, we just return and will do our work when the first notifyXXXStarted() call + // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will + // do our work right away. In both cases we call the onAnimationStartResponse() method + // on each animation. In the first case we send in the time we got from notifyXXXStarted(). + // In the second case, we just pass in the beginAnimationUpdateTime(). + // + // This will synchronize all software and accelerated animations started in the same + // updateStyleIfNeeded cycle. + // + ASSERT(!animation->next()); + + if (willGetResponse) + m_waitingForAResponse = true; + + if (m_responseWaiters) + m_lastResponseWaiter->setNext(animation); + else + m_responseWaiters = animation; + + m_lastResponseWaiter = animation; + animation->setNext(0); +} + +void AnimationControllerPrivate::removeFromStartTimeResponseWaitList(AnimationBase* animationToRemove) +{ + AnimationBase* prevAnimation = 0; + for (AnimationBase* animation = m_responseWaiters; animation; animation = animation->next()) { + if (animation == animationToRemove) { + if (prevAnimation) + prevAnimation->setNext(animation->next()); + else + m_responseWaiters = animation->next(); + + if (m_lastResponseWaiter == animation) + m_lastResponseWaiter = prevAnimation; + + animationToRemove->setNext(0); + } + prevAnimation = animation; + } +} + +void AnimationControllerPrivate::startTimeResponse(double t) +{ + // Go through list of waiters and send them on their way + for (AnimationBase* animation = m_responseWaiters; animation; ) { + AnimationBase* nextAnimation = animation->next(); + animation->setNext(0); + animation->onAnimationStartResponse(t); + animation = nextAnimation; + } + + m_responseWaiters = 0; + m_lastResponseWaiter = 0; +} + AnimationController::AnimationController(Frame* frame) : m_data(new AnimationControllerPrivate(frame)) - , m_numStyleAvailableWaiters(0) { } @@ -341,9 +450,9 @@ void AnimationController::cancelAnimations(RenderObject* renderer) return; if (m_data->clear(renderer)) { - Node* node = renderer->element(); + Node* node = renderer->node(); ASSERT(!node || (node->document() && !node->document()->inPageCache())); - node->setChanged(AnimationStyleChange); + node->setNeedsStyleRecalc(AnimationStyleChange); } } @@ -358,11 +467,15 @@ PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* rend if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle->animations() && !newStyle->transitions())) return newStyle; + // Don't run transitions when printing. + if (renderer->view()->printing()) + return newStyle; + // Fetch our current set of implicit animations from a hashtable. We then compare them // against the animations in the style and make sure we're in sync. If destination values // have changed, we reset the animation. We then do a blend to get new values and we return // a new style. - ASSERT(renderer->element()); // FIXME: We do not animate generated content yet. + ASSERT(renderer->node()); // FIXME: We do not animate generated content yet. RefPtr<CompositeAnimation> rendererAnimations = m_data->accessCompositeAnimation(renderer); RefPtr<RenderStyle> blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle); @@ -379,16 +492,14 @@ PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* rend return blendedStyle.release(); } -void AnimationController::setAnimationStartTime(RenderObject* renderer, double t) +PassRefPtr<RenderStyle> AnimationController::getAnimatedStyleForRenderer(RenderObject* renderer) { - RefPtr<CompositeAnimation> rendererAnimations = m_data->accessCompositeAnimation(renderer); - rendererAnimations->setAnimationStartTime(t); + return m_data->getAnimatedStyleForRenderer(renderer); } -void AnimationController::setTransitionStartTime(RenderObject* renderer, int property, double t) +void AnimationController::notifyAnimationStarted(RenderObject*, double startTime) { - RefPtr<CompositeAnimation> rendererAnimations = m_data->accessCompositeAnimation(renderer); - rendererAnimations->setTransitionStartTime(property, t); + m_data->receivedStartTimeResponse(startTime); } bool AnimationController::pauseAnimationAtTime(RenderObject* renderer, const String& name, double t) @@ -406,7 +517,7 @@ bool AnimationController::pauseTransitionAtTime(RenderObject* renderer, const St return m_data->pauseTransitionAtTime(renderer, property, t); } -bool AnimationController::isAnimatingPropertyOnRenderer(RenderObject* renderer, int property, bool isRunningNow) const +bool AnimationController::isAnimatingPropertyOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const { return m_data->isAnimatingPropertyOnRenderer(renderer, property, isRunningNow); } @@ -421,29 +532,6 @@ void AnimationController::resumeAnimations(Document* document) m_data->resumeAnimations(document); } -void AnimationController::startUpdateRenderingDispatcher() -{ - m_data->startUpdateRenderingDispatcher(); -} - -void AnimationController::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime) -{ - m_data->addEventToDispatch(element, eventType, name, elapsedTime); -} - -void AnimationController::styleAvailable() -{ - if (!m_numStyleAvailableWaiters) - return; - - m_data->styleAvailable(); -} - -double AnimationController::beginAnimationUpdateTime() -{ - return m_data->beginAnimationUpdateTime(); -} - void AnimationController::beginAnimationUpdate() { m_data->setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); @@ -451,6 +539,17 @@ void AnimationController::beginAnimationUpdate() void AnimationController::endAnimationUpdate() { + m_data->endAnimationUpdate(); +} + +bool AnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property) +{ +#if USE(ACCELERATED_COMPOSITING) + return AnimationBase::animationOfPropertyIsAccelerated(property); +#else + UNUSED_PARAM(property); + return false; +#endif } } // namespace WebCore |