diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 14:34:32 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 14:34:32 -0800 |
commit | 635860845790a19bf50bbc51ba8fb66a96dde068 (patch) | |
tree | ef6ad9ff73a5b57f65249d4232a202fa77e6a140 /WebCore/page/animation | |
parent | 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (diff) | |
download | external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.zip external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.tar.gz external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.tar.bz2 |
auto import from //depot/cupcake/@136594
Diffstat (limited to 'WebCore/page/animation')
-rw-r--r-- | WebCore/page/animation/AnimationBase.cpp | 439 | ||||
-rw-r--r-- | WebCore/page/animation/AnimationBase.h | 109 | ||||
-rw-r--r-- | WebCore/page/animation/AnimationController.cpp | 328 | ||||
-rw-r--r-- | WebCore/page/animation/AnimationController.h | 27 | ||||
-rw-r--r-- | WebCore/page/animation/CompositeAnimation.cpp | 466 | ||||
-rw-r--r-- | WebCore/page/animation/CompositeAnimation.h | 34 | ||||
-rw-r--r-- | WebCore/page/animation/ImplicitAnimation.cpp | 31 | ||||
-rw-r--r-- | WebCore/page/animation/ImplicitAnimation.h | 2 | ||||
-rw-r--r-- | WebCore/page/animation/KeyframeAnimation.cpp | 19 | ||||
-rw-r--r-- | WebCore/page/animation/KeyframeAnimation.h | 3 |
10 files changed, 971 insertions, 487 deletions
diff --git a/WebCore/page/animation/AnimationBase.cpp b/WebCore/page/animation/AnimationBase.cpp index fc28469..3e43f66 100644 --- a/WebCore/page/animation/AnimationBase.cpp +++ b/WebCore/page/animation/AnimationBase.cpp @@ -30,6 +30,8 @@ #include "AnimationBase.h" #include "AnimationController.h" +#include "CSSMutableStyleDeclaration.h" +#include "CSSPropertyLonghand.h" #include "CSSPropertyNames.h" #include "CString.h" #include "CompositeAnimation.h" @@ -43,13 +45,10 @@ #include "MatrixTransformOperation.h" #include "RenderObject.h" #include "RenderStyle.h" -#include "SystemTime.h" #include "UnitBezier.h" namespace WebCore { -static const double cAnimationTimerDelay = 0.025; - // The epsilon value we pass to UnitBezier::solve given that the animation is going to run over |dur| seconds. The longer the // animation, the more precision we need in the timing function result to avoid ugly discontinuities. static inline double solveEpsilon(double duration) @@ -65,35 +64,34 @@ static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x return bezier.solve(t, solveEpsilon(duration)); } -void AnimationTimerCallback::timerFired(Timer<AnimationTimerBase>*) -{ - m_anim->animationTimerCallbackFired(m_eventType, m_elapsedTime); -} - -static inline int blendFunc(const AnimationBase* anim, int from, int to, double progress) +static inline int blendFunc(const AnimationBase*, int from, int to, double progress) { return int(from + (to - from) * progress); } -static inline double blendFunc(const AnimationBase* anim, double from, double to, double progress) +static inline double blendFunc(const AnimationBase*, double from, double to, double progress) { return from + (to - from) * progress; } -static inline float blendFunc(const AnimationBase* anim, float from, float to, double progress) +static inline float blendFunc(const AnimationBase*, float from, float to, double progress) { return narrowPrecisionToFloat(from + (to - from) * progress); } static inline Color blendFunc(const AnimationBase* anim, const Color& from, const Color& to, double progress) -{ +{ + // We need to preserve the state of the valid flag at the end of the animation + if (progress == 1 && !to.isValid()) + return Color(); + return Color(blendFunc(anim, from.red(), to.red(), progress), blendFunc(anim, from.green(), to.green(), progress), blendFunc(anim, from.blue(), to.blue(), progress), blendFunc(anim, from.alpha(), to.alpha(), progress)); } -static inline Length blendFunc(const AnimationBase* anim, const Length& from, const Length& to, double progress) +static inline Length blendFunc(const AnimationBase*, const Length& from, const Length& to, double progress) { return to.blend(from, progress); } @@ -136,9 +134,9 @@ static inline TransformOperations blendFunc(const AnimationBase* anim, const Tra } } else { // Convert the TransformOperations into matrices - IntSize size = anim->renderer()->borderBox().size(); - AffineTransform fromT; - AffineTransform toT; + IntSize size = anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : IntSize(); + TransformationMatrix fromT; + TransformationMatrix toT; from.apply(size, fromT); to.apply(size, toT); @@ -162,6 +160,11 @@ static inline EVisibility blendFunc(const AnimationBase* anim, EVisibility from, return result > 0. ? VISIBLE : (to != VISIBLE ? to : from); } +class PropertyWrapperBase; + +static void addShorthandProperties(); +static PropertyWrapperBase* wrapperForProperty(int propertyID); + class PropertyWrapperBase { public: PropertyWrapperBase(int prop) @@ -170,6 +173,8 @@ public: } virtual ~PropertyWrapperBase() { } + + virtual bool isShorthandWrapper() const { return false; } virtual bool equals(const RenderStyle* a, const RenderStyle* b) const = 0; virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const = 0; @@ -296,9 +301,48 @@ private: void (RenderStyle::*m_setter)(const Color&); }; +class ShorthandPropertyWrapper : public PropertyWrapperBase { +public: + ShorthandPropertyWrapper(int property, const CSSPropertyLonghand& longhand) + : PropertyWrapperBase(property) + { + for (unsigned i = 0; i < longhand.length(); ++i) { + PropertyWrapperBase* wrapper = wrapperForProperty(longhand.properties()[i]); + if (wrapper) + m_propertyWrappers.append(wrapper); + } + } + + virtual bool isShorthandWrapper() const { return true; } + + virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + { + Vector<PropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end(); + for (Vector<PropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) { + if (!(*it)->equals(a, b)) + return false; + } + return true; + } + + virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + { + Vector<PropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end(); + for (Vector<PropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) + (*it)->blend(anim, dst, a, b, progress); + } + +private: + Vector<PropertyWrapperBase*> m_propertyWrappers; +}; + + static Vector<PropertyWrapperBase*>* gPropertyWrappers = 0; static int gPropertyWrapperMap[numCSSProperties]; +static const int cInvalidPropertyWrapperIndex = -1; + + static void ensurePropertyMap() { // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last AnimationController is destroyed? @@ -368,37 +412,122 @@ static void ensurePropertyMap() gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyStrokeOpacity, &RenderStyle::strokeOpacity, &RenderStyle::setStrokeOpacity)); #endif + // TODO: + // + // CSSPropertyBackground, CSSPropertyBackgroundPosition + // CSSPropertyMinWidth, CSSPropertyMaxWidth, CSSPropertyMinHeight, CSSPropertyMaxHeight + // CSSPropertyTextIndent + // CSSPropertyVerticalAlign + // CSSPropertyWebkitBackgroundOrigin + // CSSPropertyWebkitBackgroundSize + // CSSPropertyWebkitMaskPosition + // CSSPropertyWebkitMaskOrigin + // CSSPropertyWebkitMaskSize + // + // Compound properties that have components that should be animatable: + // + // CSSPropertyWebkitColumns + // CSSPropertyWebkitMask + // CSSPropertyWebkitBoxReflect + // Make sure unused slots have a value - for (unsigned int i = 0; i < (unsigned int) numCSSProperties; ++i) - gPropertyWrapperMap[i] = CSSPropertyInvalid; + for (unsigned int i = 0; i < static_cast<unsigned int>(numCSSProperties); ++i) + gPropertyWrapperMap[i] = cInvalidPropertyWrapperIndex; + // First we put the non-shorthand property wrappers into the map, so the shorthand-building + // code can find them. size_t n = gPropertyWrappers->size(); for (unsigned int i = 0; i < n; ++i) { ASSERT((*gPropertyWrappers)[i]->property() - firstCSSProperty < numCSSProperties); gPropertyWrapperMap[(*gPropertyWrappers)[i]->property() - firstCSSProperty] = i; } + + // Now add the shorthand wrappers. + addShorthandProperties(); } } +static void addPropertyWrapper(int propertyID, PropertyWrapperBase* wrapper) +{ + int propIndex = propertyID - firstCSSProperty; + + ASSERT(gPropertyWrapperMap[propIndex] == cInvalidPropertyWrapperIndex); + + unsigned wrapperIndex = gPropertyWrappers->size(); + gPropertyWrappers->append(wrapper); + gPropertyWrapperMap[propIndex] = wrapperIndex; +} + +static void addShorthandProperties() +{ + static const int animatableShorthandProperties[] = { + CSSPropertyBackground, // for background-color + CSSPropertyBorderTop, CSSPropertyBorderRight, CSSPropertyBorderBottom, CSSPropertyBorderLeft, + CSSPropertyBorderColor, + CSSPropertyBorderWidth, + CSSPropertyBorder, + CSSPropertyBorderSpacing, + CSSPropertyMargin, + CSSPropertyOutline, + CSSPropertyPadding, + CSSPropertyWebkitTextStroke, + CSSPropertyWebkitColumnRule, + CSSPropertyWebkitBorderRadius, + CSSPropertyWebkitTransformOrigin + }; + + for (unsigned i = 0; i < sizeof(animatableShorthandProperties) / sizeof(animatableShorthandProperties[0]); ++i) { + int propertyID = animatableShorthandProperties[i]; + CSSPropertyLonghand longhand = longhandForProperty(propertyID); + if (longhand.length() > 0) + addPropertyWrapper(propertyID, new ShorthandPropertyWrapper(propertyID, longhand)); + } + + // 'font' is not in the shorthand map. + static const int animatableFontProperties[] = { + CSSPropertyFontSize, + CSSPropertyFontWeight + }; + + CSSPropertyLonghand fontLonghand(animatableFontProperties, sizeof(animatableFontProperties) / sizeof(animatableFontProperties[0])); + addPropertyWrapper(CSSPropertyFont, new ShorthandPropertyWrapper(CSSPropertyFont, fontLonghand)); +} + +static PropertyWrapperBase* wrapperForProperty(int propertyID) +{ + int propIndex = propertyID - firstCSSProperty; + if (propIndex >= 0 && propIndex < numCSSProperties) { + int wrapperIndex = gPropertyWrapperMap[propIndex]; + if (wrapperIndex >= 0) + return (*gPropertyWrappers)[wrapperIndex]; + } + return 0; +} + AnimationBase::AnimationBase(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim) : m_animState(AnimationStateNew) - , m_iteration(0) , m_isAnimating(false) , m_waitedForResponse(false) , m_startTime(0) , m_pauseTime(-1) + , m_requestedStartTime(0) , m_object(renderer) - , m_animationTimerCallback(const_cast<AnimationBase*>(this)) , m_animation(const_cast<Animation*>(transition)) , m_compAnim(compAnim) , m_transformFunctionListValid(false) + , m_nextIterationDuration(-1) + , m_next(0) { + // Compute the total duration + m_totalDuration = -1; + if (m_animation->iterationCount() > 0) + m_totalDuration = m_animation->duration() * m_animation->iterationCount(); } AnimationBase::~AnimationBase() { if (m_animState == AnimationStateStartWaitStyleAvailable) - m_compAnim->setWaitingForStyleAvailable(false); + m_compAnim->removeFromStyleAvailableWaitList(this); } bool AnimationBase::propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b) @@ -407,27 +536,28 @@ bool AnimationBase::propertiesEqual(int prop, const RenderStyle* a, const Render if (prop == cAnimateAll) { size_t n = gPropertyWrappers->size(); for (unsigned int i = 0; i < n; ++i) { - if (!(*gPropertyWrappers)[i]->equals(a, b)) + PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i]; + // No point comparing shorthand wrappers for 'all'. + if (!wrapper->isShorthandWrapper() && !wrapper->equals(a, b)) return false; } } else { - int propIndex = prop - firstCSSProperty; - - if (propIndex >= 0 && propIndex < numCSSProperties) { - int i = gPropertyWrapperMap[propIndex]; - return i >= 0 ? (*gPropertyWrappers)[i]->equals(a, b) : true; - } + PropertyWrapperBase* wrapper = wrapperForProperty(prop); + if (wrapper) + return wrapper->equals(a, b); } return true; } -int AnimationBase::getPropertyAtIndex(int i) +int AnimationBase::getPropertyAtIndex(int i, bool& isShorthand) { ensurePropertyMap(); if (i < 0 || i >= static_cast<int>(gPropertyWrappers->size())) return CSSPropertyInvalid; - return (*gPropertyWrappers)[i]->property(); + PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i]; + isShorthand = wrapper->isShorthandWrapper(); + return wrapper->property(); } int AnimationBase::getNumProperties() @@ -440,31 +570,12 @@ int AnimationBase::getNumProperties() bool AnimationBase::blendProperties(const AnimationBase* anim, int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) { ASSERT(prop != cAnimateAll); - // FIXME: Why can this happen? - - ensurePropertyMap(); - if (prop == cAnimateAll) { - bool needsTimer = false; - size_t n = gPropertyWrappers->size(); - for (unsigned int i = 0; i < n; ++i) { - PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i]; - if (!wrapper->equals(a, b)) { - wrapper->blend(anim, dst, a, b, progress); - needsTimer = true; - } - } - return needsTimer; - } - - int propIndex = prop - firstCSSProperty; - if (propIndex >= 0 && propIndex < numCSSProperties) { - int i = gPropertyWrapperMap[propIndex]; - if (i >= 0) { - PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i]; - wrapper->blend(anim, dst, a, b, progress); - return true; - } + ensurePropertyMap(); + PropertyWrapperBase* wrapper = wrapperForProperty(prop); + if (wrapper) { + wrapper->blend(anim, dst, a, b, progress); + return true; } return false; @@ -473,7 +584,8 @@ bool AnimationBase::blendProperties(const AnimationBase* anim, int prop, RenderS void AnimationBase::setChanged(Node* node) { ASSERT(!node || (node->document() && !node->document()->inPageCache())); - node->setChanged(AnimationStyleChange); + if (node) + node->setChanged(AnimationStyleChange); } double AnimationBase::duration() const @@ -483,7 +595,7 @@ double AnimationBase::duration() const bool AnimationBase::playStatePlaying() const { - return m_animation && m_animation->playState() == AnimPlayStatePlaying; + return m_animation->playState() == AnimPlayStatePlaying; } bool AnimationBase::animationsMatch(const Animation* anim) const @@ -496,22 +608,25 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state. if (input == AnimationStateInputMakeNew) { if (m_animState == AnimationStateStartWaitStyleAvailable) - m_compAnim->setWaitingForStyleAvailable(false); + m_compAnim->removeFromStyleAvailableWaitList(this); m_animState = AnimationStateNew; m_startTime = 0; m_pauseTime = -1; + m_requestedStartTime = 0; + m_nextIterationDuration = -1; m_waitedForResponse = false; endAnimation(false); return; } if (input == AnimationStateInputRestartAnimation) { - cancelTimers(); if (m_animState == AnimationStateStartWaitStyleAvailable) - m_compAnim->setWaitingForStyleAvailable(false); + m_compAnim->removeFromStyleAvailableWaitList(this); m_animState = AnimationStateNew; m_startTime = 0; m_pauseTime = -1; + m_requestedStartTime = 0; + m_nextIterationDuration = -1; endAnimation(false); if (!paused()) @@ -520,9 +635,8 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) } if (input == AnimationStateInputEndAnimation) { - cancelTimers(); if (m_animState == AnimationStateStartWaitStyleAvailable) - m_compAnim->setWaitingForStyleAvailable(false); + m_compAnim->removeFromStyleAvailableWaitList(this); m_animState = AnimationStateDone; endAnimation(true); return; @@ -533,7 +647,7 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) // If we are in AnimationStateStartWaitResponse, the animation will get canceled before // we get a response, so move to the next state. endAnimation(false); - updateStateMachine(AnimationStateInputStartTimeSet, currentTime()); + updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); } return; } @@ -551,10 +665,9 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) case AnimationStateNew: ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunnning || input == AnimationStateInputPlayStatePaused); if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunnning) { - // Set the start timer to the initial delay (0 if no delay) m_waitedForResponse = false; + m_requestedStartTime = beginAnimationUpdateTime(); m_animState = AnimationStateStartWaitTimer; - m_animationTimerCallback.startTimer(m_animation->delay(), eventNames().webkitAnimationStartEvent, m_animation->delay()); } break; case AnimationStateStartWaitTimer: @@ -564,48 +677,36 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) ASSERT(param >= 0); // Start timer has fired, tell the animation to start and wait for it to respond with start time m_animState = AnimationStateStartWaitStyleAvailable; - m_compAnim->setWaitingForStyleAvailable(true); + m_compAnim->addToStyleAvailableWaitList(this); // Trigger a render so we can start the animation - setChanged(m_object->element()); - m_object->animation()->startUpdateRenderingDispatcher(); + if (m_object) + m_object->animation()->addNodeChangeToDispatch(m_object->element()); } else { ASSERT(!paused()); // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait - m_pauseTime = currentTime(); - cancelTimers(); + m_pauseTime = beginAnimationUpdateTime(); m_animState = AnimationStatePausedWaitTimer; } break; case AnimationStateStartWaitStyleAvailable: ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused); - m_compAnim->setWaitingForStyleAvailable(false); - - if (input == AnimationStateInputStyleAvailable) { - // Start timer has fired, tell the animation to start and wait for it to respond with start time - m_animState = AnimationStateStartWaitResponse; + // Start timer has fired, tell the animation to start and wait for it to respond with start time + m_animState = AnimationStateStartWaitResponse; - overrideAnimations(); + overrideAnimations(); - // Send start event, if needed - onAnimationStart(0); // The elapsedTime is always 0 here + // Send start event, if needed + onAnimationStart(0); // The elapsedTime is always 0 here - // Start the animation - if (overridden() || !startAnimation(0)) { - // We're not going to get a startTime callback, so fire the start time here - m_animState = AnimationStateStartWaitResponse; - updateStateMachine(AnimationStateInputStartTimeSet, currentTime()); - } else - m_waitedForResponse = true; - } else { - ASSERT(!paused()); - // We're waiting for the a notification that the style has been setup. If we're asked to wait - // at this point, the style must have been processed, so we can deal with this like we would - // for WAIT_RESPONSE, except that we don't need to do an endAnimation(). - m_pauseTime = 0; + // Start the animation + if (overridden() || !startAnimation(0)) { + // We're not going to get a startTime callback, so fire the start time here m_animState = AnimationStateStartWaitResponse; - } + updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); + } else + m_waitedForResponse = true; break; case AnimationStateStartWaitResponse: ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused); @@ -616,16 +717,16 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) if (m_startTime <= 0) m_startTime = param; - // Decide when the end or loop event needs to fire - primeEventTimers(); + // Decide whether to go into looping or ending state + goIntoEndingOrLoopingState(); - // Trigger a render so we can start the animation - setChanged(m_object->element()); - m_object->animation()->startUpdateRenderingDispatcher(); + // Dispatch updateRendering so we can start the animation + if (m_object) + m_object->animation()->addNodeChangeToDispatch(m_object->element()); } else { // We are pausing while waiting for a start response. Cancel the animation and wait. When // we unpause, we will act as though the start timer just fired - m_pauseTime = 0; + m_pauseTime = -1; endAnimation(false); m_animState = AnimationStatePausedWaitResponse; } @@ -637,11 +738,12 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) ASSERT(param >= 0); // Loop timer fired, loop again or end. onAnimationIteration(param); - primeEventTimers(); + + // Decide whether to go into looping or ending state + goIntoEndingOrLoopingState(); } else { // We are pausing while running. Cancel the animation and wait - m_pauseTime = currentTime(); - cancelTimers(); + m_pauseTime = beginAnimationUpdateTime(); endAnimation(false); m_animState = AnimationStatePausedRun; } @@ -654,17 +756,17 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) // End timer fired, finish up onAnimationEnd(param); - resumeOverriddenAnimations(); - - // Fire off another style change so we can set the final value - setChanged(m_object->element()); m_animState = AnimationStateDone; - m_object->animation()->startUpdateRenderingDispatcher(); - // |this| may be deleted here when we've been called from timerFired() + + if (m_object) { + resumeOverriddenAnimations(); + + // Fire off another style change so we can set the final value + m_object->animation()->addNodeChangeToDispatch(m_object->element()); + } } else { // We are pausing while running. Cancel the animation and wait - m_pauseTime = currentTime(); - cancelTimers(); + m_pauseTime = beginAnimationUpdateTime(); endAnimation(false); m_animState = AnimationStatePausedRun; } @@ -674,7 +776,7 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) ASSERT(input == AnimationStateInputPlayStateRunnning); ASSERT(paused()); // Update the times - m_startTime += currentTime() - m_pauseTime; + m_startTime += beginAnimationUpdateTime() - m_pauseTime; m_pauseTime = -1; // we were waiting for the start timer to fire, go back and wait again @@ -691,7 +793,7 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) ASSERT(paused()); // Update the times if (m_animState == AnimationStatePausedRun) - m_startTime += currentTime() - m_pauseTime; + m_startTime += beginAnimationUpdateTime() - m_pauseTime; else m_startTime = 0; m_pauseTime = -1; @@ -702,7 +804,7 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) // Start the animation if (overridden() || !startAnimation(m_startTime)) { // We're not going to get a startTime callback, so fire the start time here - updateStateMachine(AnimationStateInputStartTimeSet, currentTime()); + updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); } else m_waitedForResponse = true; break; @@ -710,21 +812,53 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) // We're done. Stay in this state until we are deleted break; } - // |this| may be deleted here if we came out of AnimationStateEnding when we've been called from timerFired() } -void AnimationBase::animationTimerCallbackFired(const AtomicString& eventType, double elapsedTime) +void AnimationBase::fireAnimationEventsIfNeeded() { - ASSERT(m_object->document() && !m_object->document()->inPageCache()); + // If we are waiting for the delay time to expire and it has, go to the next state + if (m_animState != AnimationStateStartWaitTimer && m_animState != AnimationStateLooping && m_animState != AnimationStateEnding) + return; - // FIXME: use an enum - if (eventType == eventNames().webkitAnimationStartEvent) - updateStateMachine(AnimationStateInputStartTimerFired, elapsedTime); - else if (eventType == eventNames().webkitAnimationIterationEvent) - updateStateMachine(AnimationStateInputLoopTimerFired, elapsedTime); - else if (eventType == eventNames().webkitAnimationEndEvent) { - updateStateMachine(AnimationStateInputEndTimerFired, elapsedTime); - // |this| may be deleted here + // We have to make sure to keep a ref to the this pointer, because it could get destroyed + // during an animation callback that might get called. Since the owner is a CompositeAnimation + // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase + // can still access the resources of its CompositeAnimation as needed. + RefPtr<AnimationBase> protector(this); + RefPtr<CompositeAnimation> compProtector(m_compAnim); + + // Check for start timeout + if (m_animState == AnimationStateStartWaitTimer) { + if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay()) + updateStateMachine(AnimationStateInputStartTimerFired, 0); + return; + } + + double elapsedDuration = beginAnimationUpdateTime() - m_startTime; + ASSERT(elapsedDuration >= 0); + + // Check for end timeout + if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) { + // Fire an end event + updateStateMachine(AnimationStateInputEndTimerFired, m_totalDuration); + } + else { + // Check for iteration timeout + if (m_nextIterationDuration < 0) { + // Hasn't been set yet, set it + double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); + m_nextIterationDuration = elapsedDuration + durationLeft; + } + + if (elapsedDuration >= m_nextIterationDuration) { + // Set to the next iteration + double previous = m_nextIterationDuration; + double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); + m_nextIterationDuration = elapsedDuration + durationLeft; + + // Send the event + updateStateMachine(AnimationStateInputLoopTimerFired, previous); + } } } @@ -734,14 +868,28 @@ void AnimationBase::updatePlayState(bool run) updateStateMachine(run ? AnimationStateInputPlayStateRunnning : AnimationStateInputPlayStatePaused, -1); } +double AnimationBase::willNeedService() const +{ + // Returns the time at which next service is required. -1 means no service is required. 0 means + // service is required now, and > 0 means service is required that many seconds in the future. + if (paused() || isNew()) + return -1; + + if (m_animState == AnimationStateStartWaitTimer) { + double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime); + return (float) ((timeFromNow > 0) ? timeFromNow : 0); + } + + // In all other cases, we need service right away. + return 0; +} + double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const { if (preActive()) return 0; - double elapsedTime = running() ? (currentTime() - m_startTime) : (m_pauseTime - m_startTime); - if (running() && elapsedTime < 0) - return 0; + double elapsedTime = getElapsedTime(); double dur = m_animation->duration(); if (m_animation->iterationCount() > 0) @@ -778,38 +926,53 @@ double AnimationBase::progress(double scale, double offset, const TimingFunction return result; } -void AnimationBase::primeEventTimers() +void AnimationBase::goIntoEndingOrLoopingState() { // Decide when the end or loop event needs to fire - double ct = currentTime(); - const double elapsedDuration = ct - m_startTime; - ASSERT(elapsedDuration >= 0); - double totalDuration = -1; if (m_animation->iterationCount() > 0) totalDuration = m_animation->duration() * m_animation->iterationCount(); + const double elapsedDuration = beginAnimationUpdateTime() - m_startTime; + ASSERT(elapsedDuration >= 0); double durationLeft = 0; double nextIterationTime = totalDuration; + if (totalDuration < 0 || elapsedDuration < totalDuration) { durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); nextIterationTime = elapsedDuration + durationLeft; } - // At this point, we may have 0 durationLeft, if we've gotten the event late and we are already - // past totalDuration. In this case we still fire an end timer before processing the end. - // This defers the call to sendAnimationEvents to avoid re-entrant calls that destroy - // the RenderObject, and therefore |this| before we're done with it. if (totalDuration < 0 || nextIterationTime < totalDuration) { - // We are not at the end yet, send a loop event + // We are not at the end yet ASSERT(nextIterationTime > 0); m_animState = AnimationStateLooping; - m_animationTimerCallback.startTimer(durationLeft, eventNames().webkitAnimationIterationEvent, nextIterationTime); } else { - // We are at the end, send an end event + // We are at the end m_animState = AnimationStateEnding; - m_animationTimerCallback.startTimer(durationLeft, eventNames().webkitAnimationEndEvent, nextIterationTime); } } +void AnimationBase::pauseAtTime(double t) +{ + updatePlayState(false); + m_pauseTime = m_startTime + t - m_animation->delay(); +} + +double AnimationBase::beginAnimationUpdateTime() const +{ + return m_compAnim->animationController()->beginAnimationUpdateTime(); +} + +double AnimationBase::getElapsedTime() const +{ + if (paused()) + return m_pauseTime - m_startTime; + if (m_startTime <= 0) + return 0; + if (postActive()) + return 1; + return beginAnimationUpdateTime() - m_startTime; +} + } // namespace WebCore diff --git a/WebCore/page/animation/AnimationBase.h b/WebCore/page/animation/AnimationBase.h index 925c0d5..ce16f93 100644 --- a/WebCore/page/animation/AnimationBase.h +++ b/WebCore/page/animation/AnimationBase.h @@ -30,7 +30,6 @@ #define AnimationBase_h #include "AtomicString.h" -#include "Timer.h" #include <wtf/HashMap.h> namespace WebCore { @@ -45,60 +44,6 @@ class RenderObject; class RenderStyle; class TimingFunction; -class AnimationTimerBase { -public: - AnimationTimerBase(AnimationBase* anim) - : m_timer(this, &AnimationTimerBase::timerFired) - , m_anim(anim) - { - m_timer.startOneShot(0); - } - - virtual ~AnimationTimerBase() { } - - void startTimer(double timeout = 0) - { - m_timer.startOneShot(timeout); - } - - void cancelTimer() - { - m_timer.stop(); - } - - virtual void timerFired(Timer<AnimationTimerBase>*) = 0; - -private: - Timer<AnimationTimerBase> m_timer; - -protected: - AnimationBase* m_anim; -}; - -class AnimationTimerCallback : public AnimationTimerBase { -public: - AnimationTimerCallback(AnimationBase* anim) - : AnimationTimerBase(anim) - , m_elapsedTime(0) - { - } - - virtual ~AnimationTimerCallback() { } - - virtual void timerFired(Timer<AnimationTimerBase>*); - - void startTimer(double timeout, const AtomicString& eventType, double elapsedTime) - { - m_eventType = eventType; - m_elapsedTime = elapsedTime; - AnimationTimerBase::startTimer(timeout); - } - -private: - AtomicString m_eventType; - double m_elapsedTime; -}; - class AnimationBase : public RefCounted<AnimationBase> { friend class CompositeAnimationPrivate; @@ -107,14 +52,10 @@ public: virtual ~AnimationBase(); RenderObject* renderer() const { return m_object; } - double startTime() const { return m_startTime; } + void clearRenderer() { m_object = 0; } + double duration() const; - void cancelTimers() - { - m_animationTimerCallback.cancelTimer(); - } - // Animations and Transitions go through the states below. When entering the STARTED state // the animation is started. This may or may not require deferred response from the animator. // If so, we stay in this state until that response is received (and it returns the start time). @@ -176,16 +117,16 @@ public: // "animating" means that something is running that requires a timer to keep firing // (e.g. a software animation) void setAnimating(bool inAnimating = true) { m_isAnimating = inAnimating; } - bool isAnimating() const { return m_isAnimating; } + double willNeedService() const; double progress(double scale, double offset, const TimingFunction*) const; - virtual void animate(CompositeAnimation*, RenderObject*, const RenderStyle* currentStyle, - const RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) { } + virtual void animate(CompositeAnimation*, RenderObject*, const RenderStyle* /*currentStyle*/, + const RenderStyle* /*targetStyle*/, RefPtr<RenderStyle>& /*animatedStyle*/) { } virtual bool shouldFireEvents() const { return false; } - void animationTimerCallbackFired(const AtomicString& eventType, double elapsedTime); + void fireAnimationEventsIfNeeded(); bool animationsMatch(const Animation*) const; @@ -198,7 +139,7 @@ public: virtual bool overridden() const { return false; } // Does this animation/transition involve the given property? - virtual bool affectsProperty(int property) const { return false; } + virtual bool affectsProperty(int /*property*/) const { return false; } bool isAnimatingProperty(int property, bool isRunningNow) const { if (isRunningNow) @@ -209,6 +150,21 @@ public: bool isTransformFunctionListValid() const { return m_transformFunctionListValid; } + void pauseAtTime(double t); + + double beginAnimationUpdateTime() const; + + double getElapsedTime() const; + + AnimationBase* next() const { return m_next; } + void setNext(AnimationBase* animation) { m_next = animation; } + + void styleAvailable() + { + ASSERT(waitingForStyleAvailable()); + updateStateMachine(AnimationBase::AnimationStateInputStyleAvailable, -1); + } + protected: virtual void overrideAnimations() { } virtual void resumeOverriddenAnimations() { } @@ -216,16 +172,16 @@ protected: CompositeAnimation* compositeAnimation() { return m_compAnim; } // These are called when the corresponding timer fires so subclasses can do any extra work - virtual void onAnimationStart(double elapsedTime) { } - virtual void onAnimationIteration(double elapsedTime) { } - virtual void onAnimationEnd(double elapsedTime) { } - virtual bool startAnimation(double beginTime) { return false; } - virtual void endAnimation(bool reset) { } + virtual void onAnimationStart(double /*elapsedTime*/) { } + virtual void onAnimationIteration(double /*elapsedTime*/) { } + virtual void onAnimationEnd(double /*elapsedTime*/) { } + virtual bool startAnimation(double /*beginTime*/) { return false; } + virtual void endAnimation(bool /*reset*/) { } - void primeEventTimers(); + void goIntoEndingOrLoopingState(); static bool propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b); - static int getPropertyAtIndex(int); + static int getPropertyAtIndex(int, bool& isShorthand); static int getNumProperties(); // Return true if we need to start software animation timers @@ -233,20 +189,21 @@ protected: static void setChanged(Node*); -protected: AnimState m_animState; - int m_iteration; bool m_isAnimating; // transition/animation requires continual timer firing bool m_waitedForResponse; double m_startTime; double m_pauseTime; + double m_requestedStartTime; RenderObject* m_object; - AnimationTimerCallback m_animationTimerCallback; RefPtr<Animation> m_animation; CompositeAnimation* m_compAnim; bool m_transformFunctionListValid; + double m_totalDuration, m_nextIterationDuration; + + AnimationBase* m_next; }; } // namespace WebCore diff --git a/WebCore/page/animation/AnimationController.cpp b/WebCore/page/animation/AnimationController.cpp index d449afe..f85e6c1 100644 --- a/WebCore/page/animation/AnimationController.cpp +++ b/WebCore/page/animation/AnimationController.cpp @@ -28,27 +28,34 @@ #include "config.h" #include "AnimationController.h" +#include "AnimationBase.h" #include "CompositeAnimation.h" +#include "CSSParser.h" +#include "EventNames.h" #include "Frame.h" #include "Timer.h" +#include <wtf/CurrentTime.h> namespace WebCore { static const double cAnimationTimerDelay = 0.025; +static const double cBeginAnimationUpdateTimeNotSet = -1; class AnimationControllerPrivate { public: AnimationControllerPrivate(Frame*); ~AnimationControllerPrivate(); - CompositeAnimation* accessCompositeAnimation(RenderObject*); + PassRefPtr<CompositeAnimation> accessCompositeAnimation(RenderObject*); bool clear(RenderObject*); void animationTimerFired(Timer<AnimationControllerPrivate>*); - void updateAnimationTimer(); + void updateAnimationTimer(bool callSetChanged = false); void updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*); void startUpdateRenderingDispatcher(); + void addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime); + void addNodeChangeToDispatch(PassRefPtr<Node>); bool hasAnimations() const { return !m_compositeAnimations.isEmpty(); } @@ -59,32 +66,65 @@ public: 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; } + + void addToStyleAvailableWaitList(AnimationBase*); + void removeFromStyleAvailableWaitList(AnimationBase*); + private: - typedef HashMap<RenderObject*, CompositeAnimation*> RenderObjectAnimationMap; + 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; + Vector<RefPtr<Node> > m_nodeChangesToDispatch; + + double m_beginAnimationUpdateTime; + AnimationBase* m_styleAvailableWaiters; + AnimationBase* m_lastStyleAvailableWaiter; }; AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame) : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired) , m_updateRenderingDispatcher(this, &AnimationControllerPrivate::updateRenderingDispatcherFired) , m_frame(frame) + , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet) + , m_styleAvailableWaiters(0) + , m_lastStyleAvailableWaiter(0) { } AnimationControllerPrivate::~AnimationControllerPrivate() { - deleteAllValues(m_compositeAnimations); } -CompositeAnimation* AnimationControllerPrivate::accessCompositeAnimation(RenderObject* renderer) +PassRefPtr<CompositeAnimation> AnimationControllerPrivate::accessCompositeAnimation(RenderObject* renderer) { - CompositeAnimation* animation = m_compositeAnimations.get(renderer); + RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); if (!animation) { - animation = new CompositeAnimation(m_frame->animation()); + animation = CompositeAnimation::create(m_frame->animation()); m_compositeAnimations.set(renderer, animation); } return animation; @@ -94,44 +134,81 @@ 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); + PassRefPtr<CompositeAnimation> animation = m_compositeAnimations.take(renderer); if (!animation) return false; - animation->resetTransitions(renderer); - bool wasSuspended = animation->isSuspended(); - delete animation; - return !wasSuspended; + animation->clearRenderer(); + return animation->isSuspended(); } -void AnimationControllerPrivate::styleAvailable() +void AnimationControllerPrivate::updateAnimationTimer(bool callSetChanged/* = false*/) { - 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; + double needsService = -1; + bool calledSetChanged = 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; + RefPtr<CompositeAnimation> compAnim = it->second; + if (!compAnim->isSuspended() && compAnim->hasAnimations()) { + double t = compAnim->willNeedService(); + if (t != -1 && (t < needsService || needsService == -1)) + needsService = t; + if (needsService == 0) { + if (callSetChanged) { + Node* node = it->first->element(); + ASSERT(!node || (node->document() && !node->document()->inPageCache())); + node->setChanged(AnimationStyleChange); + calledSetChanged = true; + } + else + break; + } } } - if (isAnimating) { - if (!m_animationTimer.isActive()) + if (calledSetChanged) + m_frame->document()->updateRendering(); + + // If we want service immediately, we start a repeating timer to reduce the overhead of starting + if (needsService == 0) { + if (!m_animationTimer.isActive() || m_animationTimer.repeatInterval() == 0) m_animationTimer.startRepeating(cAnimationTimerDelay); - } else if (m_animationTimer.isActive()) + return; + } + + // If we don't need service, we want to make sure the timer is no longer running + if (needsService < 0) { + if (m_animationTimer.isActive()) + m_animationTimer.stop(); + return; + } + + // Otherwise, we want to start a one-shot timer so we get here again + if (m_animationTimer.isActive()) m_animationTimer.stop(); + m_animationTimer.startOneShot(needsService); } void AnimationControllerPrivate::updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*) { + // fire all the events + 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 + it->element->dispatchWebKitAnimationEvent(it->eventType,it->name, it->elapsedTime); + } + + m_eventsToDispatch.clear(); + + // 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)->setChanged(AnimationStyleChange); + + m_nodeChangesToDispatch.clear(); + if (m_frame && m_frame->document()) m_frame->document()->updateRendering(); } @@ -142,32 +219,38 @@ void AnimationControllerPrivate::startUpdateRenderingDispatcher() m_updateRenderingDispatcher.startOneShot(0); } -void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>* timer) +void AnimationControllerPrivate::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime) { - // 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_eventsToDispatch.grow(m_eventsToDispatch.size()+1); + EventToDispatch& event = m_eventsToDispatch[m_eventsToDispatch.size()-1]; + event.element = element; + event.eventType = eventType; + event.name = name; + event.elapsedTime = elapsedTime; + + startUpdateRenderingDispatcher(); +} + +void AnimationControllerPrivate::addNodeChangeToDispatch(PassRefPtr<Node> node) +{ + m_nodeChangesToDispatch.append(node); + startUpdateRenderingDispatcher(); +} - m_frame->document()->updateRendering(); +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) + setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); - updateAnimationTimer(); + // 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. + updateAnimationTimer(true); } bool AnimationControllerPrivate::isAnimatingPropertyOnRenderer(RenderObject* renderer, int property, bool isRunningNow) const { - CompositeAnimation* animation = m_compositeAnimations.get(renderer); + RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); if (!animation) return false; @@ -179,7 +262,7 @@ 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; + RefPtr<CompositeAnimation> compAnim = it->second; if (renderer->document() == document) compAnim->suspendAnimations(); } @@ -192,7 +275,7 @@ 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; + RefPtr<CompositeAnimation> compAnim = it->second; if (renderer->document() == document) compAnim->resumeAnimations(); } @@ -200,9 +283,96 @@ void AnimationControllerPrivate::resumeAnimations(Document* document) updateAnimationTimer(); } +bool AnimationControllerPrivate::pauseAnimationAtTime(RenderObject* renderer, const String& name, double t) +{ + if (!renderer) + return false; + + RefPtr<CompositeAnimation> compAnim = accessCompositeAnimation(renderer); + if (!compAnim) + return false; + + if (compAnim->pauseAnimationAtTime(name, t)) { + renderer->node()->setChanged(AnimationStyleChange); + return true; + } + + return false; +} + +bool AnimationControllerPrivate::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t) +{ + if (!renderer) + return false; + + RefPtr<CompositeAnimation> compAnim = accessCompositeAnimation(renderer); + if (!compAnim) + return false; + + if (compAnim->pauseTransitionAtTime(cssPropertyID(property), t)) { + renderer->node()->setChanged(AnimationStyleChange); + return true; + } + + return false; +} + +unsigned AnimationControllerPrivate::numberOfActiveAnimations() const +{ + unsigned count = 0; + + RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); + for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { + CompositeAnimation* compAnim = it->second.get(); + count += compAnim->numberOfActiveAnimations(); + } + + 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; animation = animation->next()) + animation->styleAvailable(); + + m_styleAvailableWaiters = 0; + m_lastStyleAvailableWaiter = 0; +} + AnimationController::AnimationController(Frame* frame) : m_data(new AnimationControllerPrivate(frame)) - , m_numStyleAvailableWaiters(0) { } @@ -224,7 +394,7 @@ void AnimationController::cancelAnimations(RenderObject* renderer) } 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; @@ -240,7 +410,7 @@ PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* rend // a new style. ASSERT(renderer->element()); // FIXME: We do not animate generated content yet. - CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer); + RefPtr<CompositeAnimation> rendererAnimations = m_data->accessCompositeAnimation(renderer); RefPtr<RenderStyle> blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle); m_data->updateAnimationTimer(); @@ -257,16 +427,31 @@ PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* rend void AnimationController::setAnimationStartTime(RenderObject* renderer, double t) { - CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer); + RefPtr<CompositeAnimation> rendererAnimations = m_data->accessCompositeAnimation(renderer); rendererAnimations->setAnimationStartTime(t); } void AnimationController::setTransitionStartTime(RenderObject* renderer, int property, double t) { - CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer); + RefPtr<CompositeAnimation> rendererAnimations = m_data->accessCompositeAnimation(renderer); rendererAnimations->setTransitionStartTime(property, t); } +bool AnimationController::pauseAnimationAtTime(RenderObject* renderer, const String& name, double t) +{ + return m_data->pauseAnimationAtTime(renderer, name, t); +} + +unsigned AnimationController::numberOfActiveAnimations() const +{ + return m_data->numberOfActiveAnimations(); +} + +bool AnimationController::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t) +{ + return m_data->pauseTransitionAtTime(renderer, property, t); +} + bool AnimationController::isAnimatingPropertyOnRenderer(RenderObject* renderer, int property, bool isRunningNow) const { return m_data->isAnimatingPropertyOnRenderer(renderer, property, isRunningNow); @@ -287,12 +472,41 @@ void AnimationController::startUpdateRenderingDispatcher() m_data->startUpdateRenderingDispatcher(); } -void AnimationController::styleAvailable() +void AnimationController::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime) { - if (!m_numStyleAvailableWaiters) - return; + m_data->addEventToDispatch(element, eventType, name, elapsedTime); +} + +void AnimationController::addNodeChangeToDispatch(PassRefPtr<Node> node) +{ + ASSERT(!node || (node->document() && !node->document()->inPageCache())); + if (node) + m_data->addNodeChangeToDispatch(node); +} +double AnimationController::beginAnimationUpdateTime() +{ + return m_data->beginAnimationUpdateTime(); +} + +void AnimationController::beginAnimationUpdate() +{ + m_data->setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); +} + +void AnimationController::endAnimationUpdate() +{ m_data->styleAvailable(); } +void AnimationController::addToStyleAvailableWaitList(AnimationBase* animation) +{ + m_data->addToStyleAvailableWaitList(animation); +} + +void AnimationController::removeFromStyleAvailableWaitList(AnimationBase* animation) +{ + m_data->removeFromStyleAvailableWaitList(animation); +} + } // namespace WebCore diff --git a/WebCore/page/animation/AnimationController.h b/WebCore/page/animation/AnimationController.h index bc13a2a..13ea1bd 100644 --- a/WebCore/page/animation/AnimationController.h +++ b/WebCore/page/animation/AnimationController.h @@ -33,11 +33,16 @@ namespace WebCore { +class AnimationBase; class AnimationControllerPrivate; +class AtomicString; class Document; +class Element; class Frame; +class Node; class RenderObject; class RenderStyle; +class String; class AnimationController { public: @@ -50,27 +55,29 @@ public: void setAnimationStartTime(RenderObject*, double t); void setTransitionStartTime(RenderObject*, int property, double t); + bool pauseAnimationAtTime(RenderObject*, const String& name, double t); // To be used only for testing + bool pauseTransitionAtTime(RenderObject*, const String& property, double t); // To be used only for testing + unsigned numberOfActiveAnimations() const; // To be used only for testing + bool isAnimatingPropertyOnRenderer(RenderObject*, int property, bool isRunningNow) const; void suspendAnimations(Document*); void resumeAnimations(Document*); - void updateAnimationTimer(); void startUpdateRenderingDispatcher(); + void addEventToDispatch(PassRefPtr<Element>, const AtomicString& eventType, const String& name, double elapsedTime); + void addNodeChangeToDispatch(PassRefPtr<Node>); - void styleAvailable(); + void addToStyleAvailableWaitList(AnimationBase*); + void removeFromStyleAvailableWaitList(AnimationBase*); - void setWaitingForStyleAvailable(bool waiting) - { - if (waiting) - m_numStyleAvailableWaiters++; - else - m_numStyleAvailableWaiters--; - } + double beginAnimationUpdateTime(); + + void beginAnimationUpdate(); + void endAnimationUpdate(); private: AnimationControllerPrivate* m_data; - unsigned m_numStyleAvailableWaiters; }; } // namespace WebCore diff --git a/WebCore/page/animation/CompositeAnimation.cpp b/WebCore/page/animation/CompositeAnimation.cpp index 2ae68d9..bf61b78 100644 --- a/WebCore/page/animation/CompositeAnimation.cpp +++ b/WebCore/page/animation/CompositeAnimation.cpp @@ -50,15 +50,16 @@ public: ~CompositeAnimationPrivate(); + void clearRenderer(); + PassRefPtr<RenderStyle> animate(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle); + AnimationController* animationController() { return m_animationController; } + void setAnimating(bool); - bool isAnimating() const; + double willNeedService() const; - const KeyframeAnimation* getAnimationForProperty(int property) const; - - void resetTransitions(RenderObject*); - void resetAnimations(RenderObject*); + PassRefPtr<KeyframeAnimation> getAnimationForProperty(int property); void cleanupFinishedAnimations(RenderObject*); @@ -72,11 +73,16 @@ public: void overrideImplicitAnimations(int property); void resumeOverriddenImplicitAnimations(int property); - void styleAvailable(); + bool hasAnimations() const { return !m_transitions.isEmpty() || !m_keyframeAnimations.isEmpty(); } bool isAnimatingProperty(int property, bool isRunningNow) const; - void setWaitingForStyleAvailable(bool); + void addToStyleAvailableWaitList(AnimationBase*); + void removeFromStyleAvailableWaitList(AnimationBase*); + + bool pauseAnimationAtTime(const AtomicString& name, double t); + bool pauseTransitionAtTime(int property, double t); + unsigned numberOfActiveAnimations() const; protected: void updateTransitions(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle); @@ -96,9 +102,30 @@ private: CompositeAnimationPrivate::~CompositeAnimationPrivate() { + // Toss the refs to all animations m_transitions.clear(); m_keyframeAnimations.clear(); } + +void CompositeAnimationPrivate::clearRenderer() +{ + if (!m_transitions.isEmpty()) { + // Clear the renderers from all running animations, in case we are in the middle of + // an animation callback (see https://bugs.webkit.org/show_bug.cgi?id=22052) + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* transition = it->second.get(); + transition->clearRenderer(); + } + } + if (!m_keyframeAnimations.isEmpty()) { + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + anim->clearRenderer(); + } + } +} void CompositeAnimationPrivate::updateTransitions(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) { @@ -122,17 +149,20 @@ void CompositeAnimationPrivate::updateTransitions(RenderObject* renderer, Render // through the loop. for (int propertyIndex = 0; propertyIndex < AnimationBase::getNumProperties(); ++propertyIndex) { if (all) { - // Get the next property - prop = AnimationBase::getPropertyAtIndex(propertyIndex); + // Get the next property which is not a shorthand. + bool isShorthand; + prop = AnimationBase::getPropertyAtIndex(propertyIndex, isShorthand); + if (isShorthand) + continue; } // ImplicitAnimations are always hashed by actual properties, never cAnimateAll - ASSERT(prop > firstCSSProperty && prop < (firstCSSProperty + numCSSProperties)); + ASSERT(prop >= firstCSSProperty && prop < (firstCSSProperty + numCSSProperties)); // If there is a running animation for this property, the transition is overridden // and we have to use the unanimatedStyle from the animation. We do the test // against the unanimated style here, but we "override" the transition later. - const KeyframeAnimation* keyframeAnim = getAnimationForProperty(prop); + RefPtr<KeyframeAnimation> keyframeAnim = getAnimationForProperty(prop); RenderStyle* fromStyle = keyframeAnim ? keyframeAnim->unanimatedStyle() : currentStyle; // See if there is a current transition for this prop @@ -140,14 +170,12 @@ void CompositeAnimationPrivate::updateTransitions(RenderObject* renderer, Render bool equal = true; if (implAnim) { - // This implAnim might not be an already running transition. It might be - // newly added to the list in a previous iteration. This would happen if - // you have both an explicit transition-property and 'all' in the same - // list. In this case, the latter one overrides the earlier one, so we - // behave as though this is a running animation being replaced. - if (!isActiveTransition) - m_transitions.remove(prop); - else if (!implAnim->isTargetPropertyEqual(prop, targetStyle)) { + // This implAnim might not be an already running transition. It might be + // newly added to the list in a previous iteration. This would happen if + // you have both an explicit transition-property and 'all' in the same + // list. In this case, the latter one overrides the earlier one, so we + // behave as though this is a running animation being replaced. + if (!implAnim->isTargetPropertyEqual(prop, targetStyle)) { m_transitions.remove(prop); equal = false; } @@ -239,10 +267,12 @@ PassRefPtr<RenderStyle> CompositeAnimationPrivate::animate(RenderObject* rendere if (currentStyle) { // Now that we have transition objects ready, let them know about the new goal state. We want them // to fill in a RenderStyle*& only if needed. - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - if (ImplicitAnimation* anim = it->second.get()) - anim->animate(m_compositeAnimation, renderer, currentStyle, targetStyle, resultStyle); + if (!m_transitions.isEmpty()) { + CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { + if (ImplicitAnimation* anim = it->second.get()) + anim->animate(m_compositeAnimation, renderer, currentStyle, targetStyle, resultStyle); + } } } @@ -269,123 +299,139 @@ PassRefPtr<RenderStyle> CompositeAnimationPrivate::animate(RenderObject* rendere // "animating" means that something is running that requires the timer to keep firing void CompositeAnimationPrivate::setAnimating(bool animating) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* transition = it->second.get(); - transition->setAnimating(animating); + if (!m_transitions.isEmpty()) { + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* transition = it->second.get(); + transition->setAnimating(animating); + } } - - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->second.get(); - anim->setAnimating(animating); + if (!m_keyframeAnimations.isEmpty()) { + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + anim->setAnimating(animating); + } } } -bool CompositeAnimationPrivate::isAnimating() const +double CompositeAnimationPrivate::willNeedService() const { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* transition = it->second.get(); - if (transition && transition->isAnimating() && transition->running()) - return true; + // Returns the time at which next service is required. -1 means no service is required. 0 means + // service is required now, and > 0 means service is required that many seconds in the future. + double minT = -1; + + if (!m_transitions.isEmpty()) { + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* transition = it->second.get(); + double t = transition ? transition->willNeedService() : -1; + if (t < minT || minT == -1) + minT = t; + if (minT == 0) + return 0; + } } - - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->second.get(); - if (anim && !anim->paused() && anim->isAnimating() && anim->active()) - return true; + if (!m_keyframeAnimations.isEmpty()) { + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* animation = it->second.get(); + double t = animation ? animation->willNeedService() : -1; + if (t < minT || minT == -1) + minT = t; + if (minT == 0) + return 0; + } } - return false; + return minT; } -const KeyframeAnimation* CompositeAnimationPrivate::getAnimationForProperty(int property) const +PassRefPtr<KeyframeAnimation> CompositeAnimationPrivate::getAnimationForProperty(int property) { - const KeyframeAnimation* retval = 0; + RefPtr<KeyframeAnimation> retval; // We want to send back the last animation with the property if there are multiples. // So we need to iterate through all animations - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - const KeyframeAnimation* anim = it->second.get(); - if (anim->hasAnimationForProperty(property)) - retval = anim; + if (!m_keyframeAnimations.isEmpty()) { + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + RefPtr<KeyframeAnimation> anim = it->second; + if (anim->hasAnimationForProperty(property)) + retval = anim; + } } return retval; } -void CompositeAnimationPrivate::resetTransitions(RenderObject* renderer) -{ - m_transitions.clear(); -} - -void CompositeAnimationPrivate::resetAnimations(RenderObject*) -{ - m_keyframeAnimations.clear(); -} - -void CompositeAnimationPrivate::cleanupFinishedAnimations(RenderObject* renderer) +void CompositeAnimationPrivate::cleanupFinishedAnimations(RenderObject*) { if (isSuspended()) return; // Make a list of transitions to be deleted Vector<int> finishedTransitions; - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + if (!m_transitions.isEmpty()) { + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->second.get(); - if (!anim) - continue; - if (anim->postActive()) - finishedTransitions.append(anim->animatingProperty()); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (!anim) + continue; + if (anim->postActive()) + finishedTransitions.append(anim->animatingProperty()); + } + + // Delete them + size_t finishedTransitionCount = finishedTransitions.size(); + for (size_t i = 0; i < finishedTransitionCount; ++i) + m_transitions.remove(finishedTransitions[i]); } - // Delete them - size_t finishedTransitionCount = finishedTransitions.size(); - for (size_t i = 0; i < finishedTransitionCount; ++i) - m_transitions.remove(finishedTransitions[i]); - // Make a list of animations to be deleted Vector<AtomicStringImpl*> finishedAnimations; - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + if (!m_keyframeAnimations.isEmpty()) { + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->second.get(); - if (!anim) - continue; - if (anim->postActive()) - finishedAnimations.append(anim->name().impl()); - } + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + if (!anim) + continue; + if (anim->postActive()) + finishedAnimations.append(anim->name().impl()); + } - // Delete them - size_t finishedAnimationCount = finishedAnimations.size(); - for (size_t i = 0; i < finishedAnimationCount; ++i) - m_keyframeAnimations.remove(finishedAnimations[i]); + // Delete them + size_t finishedAnimationCount = finishedAnimations.size(); + for (size_t i = 0; i < finishedAnimationCount; ++i) + m_keyframeAnimations.remove(finishedAnimations[i]); + } } void CompositeAnimationPrivate::setAnimationStartTime(double t) { // Set start time on all animations waiting for it - AnimationNameMap::const_iterator end = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != end; ++it) { - KeyframeAnimation* anim = it->second.get(); - if (anim && anim->waitingForStartTime()) - anim->updateStateMachine(AnimationBase::AnimationStateInputStartTimeSet, t); + if (!m_keyframeAnimations.isEmpty()) { + AnimationNameMap::const_iterator end = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != end; ++it) { + KeyframeAnimation* anim = it->second.get(); + if (anim && anim->waitingForStartTime()) + anim->updateStateMachine(AnimationBase::AnimationStateInputStartTimeSet, t); + } } } void CompositeAnimationPrivate::setTransitionStartTime(int property, double t) { // Set the start time for given property transition - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - ImplicitAnimation* anim = it->second.get(); - if (anim && anim->waitingForStartTime() && anim->animatingProperty() == property) - anim->updateStateMachine(AnimationBase::AnimationStateInputStartTimeSet, t); + if (!m_transitions.isEmpty()) { + CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->waitingForStartTime() && anim->animatingProperty() == property) + anim->updateStateMachine(AnimationBase::AnimationStateInputStartTimeSet, t); + } } } @@ -396,17 +442,20 @@ void CompositeAnimationPrivate::suspendAnimations() m_isSuspended = true; - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - if (KeyframeAnimation* anim = it->second.get()) - anim->updatePlayState(false); + if (!m_keyframeAnimations.isEmpty()) { + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + if (KeyframeAnimation* anim = it->second.get()) + anim->updatePlayState(false); + } } - - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->second.get(); - if (anim && anim->hasStyle()) - anim->updatePlayState(false); + if (!m_transitions.isEmpty()) { + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->hasStyle()) + anim->updatePlayState(false); + } } } @@ -417,98 +466,139 @@ void CompositeAnimationPrivate::resumeAnimations() m_isSuspended = false; - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->second.get(); - if (anim && anim->playStatePlaying()) - anim->updatePlayState(true); + if (!m_keyframeAnimations.isEmpty()) { + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + if (anim && anim->playStatePlaying()) + anim->updatePlayState(true); + } } - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->second.get(); - if (anim && anim->hasStyle()) - anim->updatePlayState(true); + if (!m_transitions.isEmpty()) { + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->hasStyle()) + anim->updatePlayState(true); + } } } void CompositeAnimationPrivate::overrideImplicitAnimations(int property) { CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - ImplicitAnimation* anim = it->second.get(); - if (anim && anim->animatingProperty() == property) - anim->setOverridden(true); + if (!m_transitions.isEmpty()) { + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->animatingProperty() == property) + anim->setOverridden(true); + } } } void CompositeAnimationPrivate::resumeOverriddenImplicitAnimations(int property) { - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - ImplicitAnimation* anim = it->second.get(); - if (anim && anim->animatingProperty() == property) - anim->setOverridden(false); + if (!m_transitions.isEmpty()) { + CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->animatingProperty() == property) + anim->setOverridden(false); + } + } +} + +bool CompositeAnimationPrivate::isAnimatingProperty(int property, bool isRunningNow) const +{ + if (!m_keyframeAnimations.isEmpty()) { + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + if (anim && anim->isAnimatingProperty(property, isRunningNow)) + return true; + } + } + + if (!m_transitions.isEmpty()) { + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->isAnimatingProperty(property, isRunningNow)) + return true; + } } + return false; } -static inline bool compareAnimationIndices(RefPtr<KeyframeAnimation> a, const RefPtr<KeyframeAnimation> b) +void CompositeAnimationPrivate::addToStyleAvailableWaitList(AnimationBase* animation) { - return a->index() < b->index(); + m_animationController->addToStyleAvailableWaitList(animation); } -void CompositeAnimationPrivate::styleAvailable() +void CompositeAnimationPrivate::removeFromStyleAvailableWaitList(AnimationBase* animation) { - if (m_numStyleAvailableWaiters == 0) - return; + m_animationController->removeFromStyleAvailableWaitList(animation); +} - // We have to go through animations in the order in which they appear in - // the style, because order matters for additivity. - Vector<RefPtr<KeyframeAnimation> > animations(m_keyframeAnimations.size()); - copyValuesToVector(m_keyframeAnimations, animations); +bool CompositeAnimationPrivate::pauseAnimationAtTime(const AtomicString& name, double t) +{ + if (!name) + return false; - if (animations.size() > 1) - std::stable_sort(animations.begin(), animations.end(), compareAnimationIndices); + RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(name.impl()); + if (!keyframeAnim || !keyframeAnim->running()) + return false; - for (size_t i = 0; i < animations.size(); ++i) { - KeyframeAnimation* anim = animations[i].get(); - if (anim && anim->waitingForStyleAvailable()) - anim->updateStateMachine(AnimationBase::AnimationStateInputStyleAvailable, -1); + int count = keyframeAnim->m_animation->iterationCount(); + if ((t >= 0.0) && (!count || (t <= count * keyframeAnim->duration()))) { + keyframeAnim->pauseAtTime(t); + return true; } - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - ImplicitAnimation* anim = it->second.get(); - if (anim && anim->waitingForStyleAvailable()) - anim->updateStateMachine(AnimationBase::AnimationStateInputStyleAvailable, -1); - } + return false; } -bool CompositeAnimationPrivate::isAnimatingProperty(int property, bool isRunningNow) const +bool CompositeAnimationPrivate::pauseTransitionAtTime(int property, double t) { - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->second.get(); - if (anim && anim->isAnimatingProperty(property, isRunningNow)) - return true; - } + if ((property < firstCSSProperty) || (property >= firstCSSProperty + numCSSProperties)) + return false; + + ImplicitAnimation* implAnim = m_transitions.get(property).get(); + if (!implAnim || !implAnim->running()) + return false; - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->second.get(); - if (anim && anim->isAnimatingProperty(property, isRunningNow)) - return true; + if ((t >= 0.0) && (t <= implAnim->duration())) { + implAnim->pauseAtTime(t); + return true; } + return false; } -void CompositeAnimationPrivate::setWaitingForStyleAvailable(bool waiting) +unsigned CompositeAnimationPrivate::numberOfActiveAnimations() const { - if (waiting) - m_numStyleAvailableWaiters++; - else - m_numStyleAvailableWaiters--; - m_animationController->setWaitingForStyleAvailable(waiting); + unsigned count = 0; + + if (!m_keyframeAnimations.isEmpty()) { + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + if (anim->running()) + ++count; + } + } + + if (!m_transitions.isEmpty()) { + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim->running()) + ++count; + } + } + + return count; } CompositeAnimation::CompositeAnimation(AnimationController* animationController) @@ -521,24 +611,34 @@ CompositeAnimation::~CompositeAnimation() delete m_data; } +AnimationController* CompositeAnimation::animationController() +{ + return m_data->animationController(); +} + +void CompositeAnimation::clearRenderer() +{ + m_data->clearRenderer(); +} + PassRefPtr<RenderStyle> CompositeAnimation::animate(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) { return m_data->animate(renderer, currentStyle, targetStyle); } -bool CompositeAnimation::isAnimating() const +double CompositeAnimation::willNeedService() const { - return m_data->isAnimating(); + return m_data->willNeedService(); } -void CompositeAnimation::setWaitingForStyleAvailable(bool b) +void CompositeAnimation::addToStyleAvailableWaitList(AnimationBase* animation) { - m_data->setWaitingForStyleAvailable(b); + m_data->addToStyleAvailableWaitList(animation); } -void CompositeAnimation::resetTransitions(RenderObject* renderer) +void CompositeAnimation::removeFromStyleAvailableWaitList(AnimationBase* animation) { - m_data->resetTransitions(renderer); + m_data->removeFromStyleAvailableWaitList(animation); } void CompositeAnimation::suspendAnimations() @@ -556,9 +656,9 @@ bool CompositeAnimation::isSuspended() const return m_data->isSuspended(); } -void CompositeAnimation::styleAvailable() +bool CompositeAnimation::hasAnimations() const { - m_data->styleAvailable(); + return m_data->hasAnimations(); } void CompositeAnimation::setAnimating(bool b) @@ -571,6 +671,11 @@ bool CompositeAnimation::isAnimatingProperty(int property, bool isRunningNow) co return m_data->isAnimatingProperty(property, isRunningNow); } +PassRefPtr<KeyframeAnimation> CompositeAnimation::getAnimationForProperty(int property) +{ + return m_data->getAnimationForProperty(property); +} + void CompositeAnimation::setAnimationStartTime(double t) { m_data->setAnimationStartTime(t); @@ -591,4 +696,19 @@ void CompositeAnimation::resumeOverriddenImplicitAnimations(int property) m_data->resumeOverriddenImplicitAnimations(property); } +bool CompositeAnimation::pauseAnimationAtTime(const AtomicString& name, double t) +{ + return m_data->pauseAnimationAtTime(name, t); +} + +bool CompositeAnimation::pauseTransitionAtTime(int property, double t) +{ + return m_data->pauseTransitionAtTime(property, t); +} + +unsigned CompositeAnimation::numberOfActiveAnimations() const +{ + return m_data->numberOfActiveAnimations(); +} + } // namespace WebCore diff --git a/WebCore/page/animation/CompositeAnimation.h b/WebCore/page/animation/CompositeAnimation.h index 13f1179..3517b34 100644 --- a/WebCore/page/animation/CompositeAnimation.h +++ b/WebCore/page/animation/CompositeAnimation.h @@ -37,30 +37,41 @@ namespace WebCore { class CompositeAnimationPrivate; +class AnimationBase; class AnimationController; +class KeyframeAnimation; class RenderObject; class RenderStyle; // A CompositeAnimation represents a collection of animations that are running // on a single RenderObject, such as a number of properties transitioning at once. -class CompositeAnimation : public Noncopyable { +class CompositeAnimation : public RefCounted<CompositeAnimation> { public: - CompositeAnimation(AnimationController* animationController); + static PassRefPtr<CompositeAnimation> create(AnimationController* animationController) + { + return adoptRef(new CompositeAnimation(animationController)); + }; + ~CompositeAnimation(); + + void clearRenderer(); PassRefPtr<RenderStyle> animate(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle); - bool isAnimating() const; - - void setWaitingForStyleAvailable(bool); - void resetTransitions(RenderObject*); + double willNeedService() const; + + AnimationController* animationController(); void suspendAnimations(); void resumeAnimations(); bool isSuspended() const; + + bool hasAnimations() const; - void styleAvailable(); void setAnimating(bool); bool isAnimatingProperty(int property, bool isRunningNow) const; + + PassRefPtr<KeyframeAnimation> getAnimationForProperty(int property); + void setAnimationStartTime(double t); void setTransitionStartTime(int property, double t); @@ -68,7 +79,16 @@ public: void overrideImplicitAnimations(int property); void resumeOverriddenImplicitAnimations(int property); + bool pauseAnimationAtTime(const AtomicString& name, double t); + bool pauseTransitionAtTime(int property, double t); + unsigned numberOfActiveAnimations() const; + + void addToStyleAvailableWaitList(AnimationBase*); + void removeFromStyleAvailableWaitList(AnimationBase*); + private: + CompositeAnimation(AnimationController* animationController); + CompositeAnimationPrivate* m_data; }; diff --git a/WebCore/page/animation/ImplicitAnimation.cpp b/WebCore/page/animation/ImplicitAnimation.cpp index 4d470e4..f984909 100644 --- a/WebCore/page/animation/ImplicitAnimation.cpp +++ b/WebCore/page/animation/ImplicitAnimation.cpp @@ -27,9 +27,13 @@ */ #include "config.h" + +#include "AnimationController.h" +#include "CompositeAnimation.h" #include "CSSPropertyNames.h" #include "EventNames.h" #include "ImplicitAnimation.h" +#include "KeyframeAnimation.h" #include "RenderObject.h" namespace WebCore { @@ -56,12 +60,8 @@ bool ImplicitAnimation::shouldSendEventForListener(Document::ListenerType inList return m_object->document()->hasListenerType(inListenerType); } -void ImplicitAnimation::animate(CompositeAnimation* animation, RenderObject* renderer, RenderStyle* currentStyle, - RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) +void ImplicitAnimation::animate(CompositeAnimation*, RenderObject*, RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) { - if (paused()) - return; - // If we get this far and the animation is done, it means we are cleaning up a just finished animation. // So just return. Everything is already all cleaned up. if (postActive()) @@ -78,10 +78,22 @@ void ImplicitAnimation::animate(CompositeAnimation* animation, RenderObject* ren if (blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0))) setAnimating(); + + // Fire the start timeout if needed + fireAnimationEventsIfNeeded(); } void ImplicitAnimation::onAnimationEnd(double elapsedTime) { + // If we have a keyframe animation on this property, this transition is being overridden. The keyframe + // animation keeps an unanimated style in case a transition starts while the keyframe animation is + // running. But now that the transition has completed, we need to update this style with its new + // destination. If we didn't, the next time through we would think a transition had started + // (comparing the old unanimated style with the new final style of the transition). + RefPtr<KeyframeAnimation> keyframeAnim = m_compAnim->getAnimationForProperty(m_animatingProperty); + if (keyframeAnim) + keyframeAnim->setUnanimatedStyle(m_toStyle); + if (!sendTransitionEvent(eventNames().webkitTransitionEndEvent, elapsedTime)) { // We didn't dispatch an event, which would call endAnimation(), so we'll just call it here. endAnimation(true); @@ -107,14 +119,11 @@ bool ImplicitAnimation::sendTransitionEvent(const AtomicString& eventType, doubl if (!element) return false; - // Keep a reference to this ImplicitAnimation so it doesn't go away in the handler - RefPtr<ImplicitAnimation> retainer(this); - - // Call the event handler - element->dispatchWebKitTransitionEvent(eventType, propertyName, elapsedTime); + // Schedule event handling + m_object->animation()->addEventToDispatch(element, eventType, propertyName, elapsedTime); // Restore the original (unanimated) style - if (eventType == eventNames().webkitAnimationEndEvent && element->renderer()) + if (eventType == eventNames().webkitTransitionEndEvent && element->renderer()) setChanged(element.get()); return true; // Did dispatch an event diff --git a/WebCore/page/animation/ImplicitAnimation.h b/WebCore/page/animation/ImplicitAnimation.h index 7c9d50f..cf98bba 100644 --- a/WebCore/page/animation/ImplicitAnimation.h +++ b/WebCore/page/animation/ImplicitAnimation.h @@ -54,8 +54,6 @@ public: void setOverridden(bool); virtual bool overridden() const { return m_overridden; } - virtual bool shouldFireEvents() const { return true; } - virtual bool affectsProperty(int) const; bool hasStyle() const { return m_fromStyle && m_toStyle; } diff --git a/WebCore/page/animation/KeyframeAnimation.cpp b/WebCore/page/animation/KeyframeAnimation.cpp index 69fdd11..2efa578 100644 --- a/WebCore/page/animation/KeyframeAnimation.cpp +++ b/WebCore/page/animation/KeyframeAnimation.cpp @@ -29,12 +29,12 @@ #include "config.h" #include "KeyframeAnimation.h" +#include "AnimationController.h" #include "CSSPropertyNames.h" #include "CSSStyleSelector.h" #include "CompositeAnimation.h" #include "EventNames.h" #include "RenderObject.h" -#include "SystemTime.h" namespace WebCore { @@ -59,9 +59,11 @@ KeyframeAnimation::~KeyframeAnimation() updateStateMachine(AnimationStateInputEndAnimation, -1); } -void KeyframeAnimation::animate(CompositeAnimation* animation, RenderObject* renderer, const RenderStyle* currentStyle, - const RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) +void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const RenderStyle*, const RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) { + // Fire the start timeout if needed + fireAnimationEventsIfNeeded(); + // If we have not yet started, we will not have a valid start time, so just start the animation if needed. if (isNew() && m_animation->playState() == AnimPlayStatePlaying) updateStateMachine(AnimationStateInputStartAnimation, -1); @@ -84,9 +86,7 @@ void KeyframeAnimation::animate(CompositeAnimation* animation, RenderObject* ren // We should cache the last pair or something. // Find the first key - double elapsedTime = (m_startTime > 0) ? ((!paused() ? currentTime() : m_pauseTime) - m_startTime) : 0; - if (elapsedTime < 0) - elapsedTime = 0; + double elapsedTime = getElapsedTime(); double t = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1; int i = static_cast<int>(t); @@ -200,11 +200,8 @@ bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double if (!element) return false; - // Keep a reference to this ImplicitAnimation so it doesn't go away in the handler - RefPtr<KeyframeAnimation> retainer(this); - - // Call the event handler - element->dispatchWebKitAnimationEvent(eventType, m_keyframes.animationName(), elapsedTime); + // Schedule event handling + m_object->animation()->addEventToDispatch(element, eventType, m_keyframes.animationName(), elapsedTime); // Restore the original (unanimated) style if (eventType == eventNames().webkitAnimationEndEvent && element->renderer()) diff --git a/WebCore/page/animation/KeyframeAnimation.h b/WebCore/page/animation/KeyframeAnimation.h index 55b429a..5c3176c 100644 --- a/WebCore/page/animation/KeyframeAnimation.h +++ b/WebCore/page/animation/KeyframeAnimation.h @@ -51,10 +51,9 @@ public: int index() const { return m_index; } void setIndex(int i) { m_index = i; } - virtual bool shouldFireEvents() const { return true; } - bool hasAnimationForProperty(int property) const; + void setUnanimatedStyle(PassRefPtr<RenderStyle> style) { m_unanimatedStyle = style; } RenderStyle* unanimatedStyle() const { return m_unanimatedStyle.get(); } protected: |