summaryrefslogtreecommitdiffstats
path: root/WebCore/page/animation/AnimationController.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/page/animation/AnimationController.cpp')
-rw-r--r--WebCore/page/animation/AnimationController.cpp298
1 files changed, 298 insertions, 0 deletions
diff --git a/WebCore/page/animation/AnimationController.cpp b/WebCore/page/animation/AnimationController.cpp
new file mode 100644
index 0000000..d449afe
--- /dev/null
+++ b/WebCore/page/animation/AnimationController.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AnimationController.h"
+#include "CompositeAnimation.h"
+#include "Frame.h"
+#include "Timer.h"
+
+namespace WebCore {
+
+static const double cAnimationTimerDelay = 0.025;
+
+class AnimationControllerPrivate {
+public:
+ AnimationControllerPrivate(Frame*);
+ ~AnimationControllerPrivate();
+
+ CompositeAnimation* accessCompositeAnimation(RenderObject*);
+ bool clear(RenderObject*);
+
+ void animationTimerFired(Timer<AnimationControllerPrivate>*);
+ void updateAnimationTimer();
+
+ void updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*);
+ void startUpdateRenderingDispatcher();
+
+ bool hasAnimations() const { return !m_compositeAnimations.isEmpty(); }
+
+ void suspendAnimations(Document*);
+ void resumeAnimations(Document*);
+
+ void styleAvailable();
+
+ bool isAnimatingPropertyOnRenderer(RenderObject*, int property, bool isRunningNow) const;
+
+private:
+ typedef HashMap<RenderObject*, CompositeAnimation*> RenderObjectAnimationMap;
+
+ RenderObjectAnimationMap m_compositeAnimations;
+ Timer<AnimationControllerPrivate> m_animationTimer;
+ Timer<AnimationControllerPrivate> m_updateRenderingDispatcher;
+ Frame* m_frame;
+};
+
+AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame)
+ : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired)
+ , m_updateRenderingDispatcher(this, &AnimationControllerPrivate::updateRenderingDispatcherFired)
+ , m_frame(frame)
+{
+}
+
+AnimationControllerPrivate::~AnimationControllerPrivate()
+{
+ deleteAllValues(m_compositeAnimations);
+}
+
+CompositeAnimation* AnimationControllerPrivate::accessCompositeAnimation(RenderObject* renderer)
+{
+ CompositeAnimation* animation = m_compositeAnimations.get(renderer);
+ if (!animation) {
+ animation = new CompositeAnimation(m_frame->animation());
+ m_compositeAnimations.set(renderer, animation);
+ }
+ return animation;
+}
+
+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).
+ CompositeAnimation* animation = m_compositeAnimations.take(renderer);
+ if (!animation)
+ return false;
+ animation->resetTransitions(renderer);
+ bool wasSuspended = animation->isSuspended();
+ delete animation;
+ return !wasSuspended;
+}
+
+void AnimationControllerPrivate::styleAvailable()
+{
+ RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
+ for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it)
+ it->second->styleAvailable();
+}
+
+void AnimationControllerPrivate::updateAnimationTimer()
+{
+ bool isAnimating = false;
+
+ RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
+ for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
+ CompositeAnimation* compAnim = it->second;
+ if (!compAnim->isSuspended() && compAnim->isAnimating()) {
+ isAnimating = true;
+ break;
+ }
+ }
+
+ if (isAnimating) {
+ if (!m_animationTimer.isActive())
+ m_animationTimer.startRepeating(cAnimationTimerDelay);
+ } else if (m_animationTimer.isActive())
+ m_animationTimer.stop();
+}
+
+void AnimationControllerPrivate::updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*)
+{
+ if (m_frame && m_frame->document())
+ m_frame->document()->updateRendering();
+}
+
+void AnimationControllerPrivate::startUpdateRenderingDispatcher()
+{
+ if (!m_updateRenderingDispatcher.isActive())
+ m_updateRenderingDispatcher.startOneShot(0);
+}
+
+void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>* timer)
+{
+ // 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.
+ bool isAnimating = false;
+ RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
+ for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
+ CompositeAnimation* compAnim = it->second;
+ if (!compAnim->isSuspended() && compAnim->isAnimating()) {
+ isAnimating = true;
+ compAnim->setAnimating(false);
+
+ Node* node = it->first->element();
+ ASSERT(!node || (node->document() && !node->document()->inPageCache()));
+ node->setChanged(AnimationStyleChange);
+ }
+ }
+
+ m_frame->document()->updateRendering();
+
+ updateAnimationTimer();
+}
+
+bool AnimationControllerPrivate::isAnimatingPropertyOnRenderer(RenderObject* renderer, int property, bool isRunningNow) const
+{
+ CompositeAnimation* animation = m_compositeAnimations.get(renderer);
+ if (!animation)
+ return false;
+
+ return animation->isAnimatingProperty(property, isRunningNow);
+}
+
+void AnimationControllerPrivate::suspendAnimations(Document* document)
+{
+ RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
+ for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
+ RenderObject* renderer = it->first;
+ CompositeAnimation* compAnim = it->second;
+ if (renderer->document() == document)
+ compAnim->suspendAnimations();
+ }
+
+ updateAnimationTimer();
+}
+
+void AnimationControllerPrivate::resumeAnimations(Document* document)
+{
+ RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
+ for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
+ RenderObject* renderer = it->first;
+ CompositeAnimation* compAnim = it->second;
+ if (renderer->document() == document)
+ compAnim->resumeAnimations();
+ }
+
+ updateAnimationTimer();
+}
+
+AnimationController::AnimationController(Frame* frame)
+ : m_data(new AnimationControllerPrivate(frame))
+ , m_numStyleAvailableWaiters(0)
+{
+}
+
+AnimationController::~AnimationController()
+{
+ delete m_data;
+}
+
+void AnimationController::cancelAnimations(RenderObject* renderer)
+{
+ if (!m_data->hasAnimations())
+ return;
+
+ if (m_data->clear(renderer)) {
+ Node* node = renderer->element();
+ ASSERT(!node || (node->document() && !node->document()->inPageCache()));
+ node->setChanged(AnimationStyleChange);
+ }
+}
+
+PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* renderer, RenderStyle* newStyle)
+{
+ // Don't do anything if we're in the cache
+ if (!renderer->document() || renderer->document()->inPageCache())
+ return newStyle;
+
+ RenderStyle* oldStyle = renderer->style();
+
+ if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle->animations() && !newStyle->transitions()))
+ 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.
+
+ CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer);
+ RefPtr<RenderStyle> blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle);
+
+ m_data->updateAnimationTimer();
+
+ if (blendedStyle != newStyle) {
+ // If the animations/transitions change opacity or transform, we neeed to update
+ // the style to impose the stacking rules. Note that this is also
+ // done in CSSStyleSelector::adjustRenderStyle().
+ if (blendedStyle->hasAutoZIndex() && (blendedStyle->opacity() < 1.0f || blendedStyle->hasTransform()))
+ blendedStyle->setZIndex(0);
+ }
+ return blendedStyle.release();
+}
+
+void AnimationController::setAnimationStartTime(RenderObject* renderer, double t)
+{
+ CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer);
+ rendererAnimations->setAnimationStartTime(t);
+}
+
+void AnimationController::setTransitionStartTime(RenderObject* renderer, int property, double t)
+{
+ CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer);
+ rendererAnimations->setTransitionStartTime(property, t);
+}
+
+bool AnimationController::isAnimatingPropertyOnRenderer(RenderObject* renderer, int property, bool isRunningNow) const
+{
+ return m_data->isAnimatingPropertyOnRenderer(renderer, property, isRunningNow);
+}
+
+void AnimationController::suspendAnimations(Document* document)
+{
+ m_data->suspendAnimations(document);
+}
+
+void AnimationController::resumeAnimations(Document* document)
+{
+ m_data->resumeAnimations(document);
+}
+
+void AnimationController::startUpdateRenderingDispatcher()
+{
+ m_data->startUpdateRenderingDispatcher();
+}
+
+void AnimationController::styleAvailable()
+{
+ if (!m_numStyleAvailableWaiters)
+ return;
+
+ m_data->styleAvailable();
+}
+
+} // namespace WebCore