summaryrefslogtreecommitdiffstats
path: root/WebCore/page/animation
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-05 14:34:32 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-05 14:34:32 -0800
commit635860845790a19bf50bbc51ba8fb66a96dde068 (patch)
treeef6ad9ff73a5b57f65249d4232a202fa77e6a140 /WebCore/page/animation
parent8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (diff)
downloadexternal_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.cpp439
-rw-r--r--WebCore/page/animation/AnimationBase.h109
-rw-r--r--WebCore/page/animation/AnimationController.cpp328
-rw-r--r--WebCore/page/animation/AnimationController.h27
-rw-r--r--WebCore/page/animation/CompositeAnimation.cpp466
-rw-r--r--WebCore/page/animation/CompositeAnimation.h34
-rw-r--r--WebCore/page/animation/ImplicitAnimation.cpp31
-rw-r--r--WebCore/page/animation/ImplicitAnimation.h2
-rw-r--r--WebCore/page/animation/KeyframeAnimation.cpp19
-rw-r--r--WebCore/page/animation/KeyframeAnimation.h3
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: