AnimationController.cpp [plain text]
#include "config.h"
#include "AnimationController.h"
#include "CSSPropertyNames.h"
#include "Document.h"
#include "FloatConversion.h"
#include "Frame.h"
#include "RenderObject.h"
#include "RenderStyle.h"
#include "SystemTime.h"
#include "Timer.h"
#include "EventNames.h"
#include "CString.h"
namespace WebCore {
static void setChanged(Node* node)
{
ASSERT(!node || (node->document() && !node->document()->inPageCache()));
node->setChanged(AnimationStyleChange);
}
static const double cAnimationTimerDelay = 0.025;
struct CurveData {
CurveData(double p1x, double p1y, double p2x, double p2y)
{
cx = 3.0 * p1x;
bx = 3.0 * (p2x - p1x) - cx;
ax = 1.0 - cx -bx;
cy = 3.0 * p1y;
by = 3.0 * (p2y - p1y) - cy;
ay = 1.0 - cy - by;
}
double sampleCurveX(double t)
{
return ((ax * t + bx) * t + cx) * t;
}
double sampleCurveY(double t)
{
return ((ay * t + by) * t + cy) * t;
}
double sampleCurveDerivativeX(double t)
{
return (3.0 * ax * t + 2.0 * bx) * t + cx;
}
double solveCurveX(double x, double epsilon)
{
double t0;
double t1;
double t2;
double x2;
double d2;
int i;
for (t2 = x, i = 0; i < 8; i++) {
x2 = sampleCurveX(t2) - x;
if (fabs (x2) < epsilon)
return t2;
d2 = sampleCurveDerivativeX(t2);
if (fabs(d2) < 1e-6)
break;
t2 = t2 - x2 / d2;
}
t0 = 0.0;
t1 = 1.0;
t2 = x;
if (t2 < t0)
return t0;
if (t2 > t1)
return t1;
while (t0 < t1) {
x2 = sampleCurveX(t2);
if (fabs(x2 - x) < epsilon)
return t2;
if (x > x2)
t0 = t2;
else
t1 = t2;
t2 = (t1 - t0) * .5 + t0;
}
return t2;
}
private:
double ax;
double bx;
double cx;
double ay;
double by;
double cy;
};
static inline double solveEpsilon(double duration) { return 1. / (200. * duration); }
static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration)
{
CurveData c(p1x, p1y, p2x, p2y);
t = c.solveCurveX(t, solveEpsilon(duration));
t = c.sampleCurveY(t);
return t;
}
class CompositeAnimation;
class AnimationBase;
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 AnimationEventDispatcher : public AnimationTimerBase {
public:
AnimationEventDispatcher(AnimationBase* anim)
: AnimationTimerBase(anim)
, m_property(-1)
, m_reset(false)
, m_elapsedTime(-1)
{
}
virtual ~AnimationEventDispatcher()
{
}
void startTimer(HTMLElement* element, const AtomicString& name, int property, bool reset,
const AtomicString& eventType, double elapsedTime)
{
m_element = element;
m_name = name;
m_property = property;
m_reset = reset;
m_eventType = eventType;
m_elapsedTime = elapsedTime;
AnimationTimerBase::startTimer();
}
virtual void timerFired(Timer<AnimationTimerBase>*);
private:
RefPtr<HTMLElement> m_element;
AtomicString m_name;
int m_property;
bool m_reset;
AtomicString m_eventType;
double m_elapsedTime;
};
class AnimationTimerCB : public AnimationTimerBase {
public:
AnimationTimerCB(AnimationBase* anim)
: AnimationTimerBase(anim)
, m_elapsedTime(0)
{ }
virtual ~AnimationTimerCB() { }
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;
};
#pragma mark -
class ImplicitAnimation;
class KeyframeAnimation;
class AnimationControllerPrivate;
class CompositeAnimation : public Noncopyable {
public:
CompositeAnimation(bool suspended, AnimationControllerPrivate* animationController)
: m_suspended(suspended)
, m_animationController(animationController)
, m_numStyleIsSetupWaiters(0)
{ }
~CompositeAnimation()
{
deleteAllValues(m_transitions);
deleteAllValues(m_keyframeAnimations);
}
RenderStyle* animate(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle);
void setAnimating(bool inAnimating);
bool animating();
bool hasAnimationForProperty(int prop) const { return m_transitions.contains(prop); }
void resetTransitions(RenderObject*);
void resetAnimations(RenderObject*);
void cleanupFinishedAnimations(RenderObject*);
void setAnimationStartTime(double t);
void setTransitionStartTime(int property, double t);
void suspendAnimations();
void resumeAnimations();
bool suspended() const { return m_suspended; }
void overrideImplicitAnimations(int property);
void resumeOverriddenImplicitAnimations(int property);
void styleIsSetup();
bool isAnimatingProperty(int property) const;
void setWaitingForStyleIsSetup(bool waiting);
protected:
void updateTransitions(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle);
void updateKeyframeAnimations(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle);
int findKeyframeAnimation(const AtomicString& name);
private:
typedef HashMap<int, ImplicitAnimation*> CSSPropertyTransitionsMap;
CSSPropertyTransitionsMap m_transitions;
Vector<KeyframeAnimation*> m_keyframeAnimations;
bool m_suspended;
AnimationControllerPrivate* m_animationController;
uint32_t m_numStyleIsSetupWaiters;
};
#pragma mark -
class AnimationBase : public Noncopyable {
public:
AnimationBase(const Transition* transition, RenderObject* renderer, CompositeAnimation* compAnim);
virtual ~AnimationBase()
{
if (m_animState == STATE_START_WAIT_STYLE_SETUP)
m_compAnim->setWaitingForStyleIsSetup(false);
}
RenderObject* renderer() const { return m_object; }
double startTime() const { return m_startTime; }
double duration() const { return m_animation->transitionDuration(); }
void cancelTimers()
{
m_animationTimerCB.cancelTimer();
m_animationEventDispatcher.cancelTimer();
}
enum AnimState {
STATE_NEW, STATE_START_WAIT_TIMER, STATE_START_WAIT_STYLE_SETUP, STATE_START_WAIT_RESPONSE, STATE_LOOPING, STATE_ENDING, STATE_PAUSED_WAIT_TIMER, STATE_PAUSED_WAIT_RESPONSE, STATE_PAUSED_RUN, STATE_DONE };
enum AnimStateInput {
STATE_INPUT_MAKE_NEW, STATE_INPUT_START_ANIMATION, STATE_INPUT_RESTART_ANIMATION, STATE_INPUT_START_TIMER_FIRED, STATE_INPUT_STYLE_SETUP, STATE_INPUT_START_TIME_SET, STATE_INPUT_LOOP_TIMER_FIRED, STATE_INPUT_END_TIMER_FIRED, STATE_INPUT_PAUSE_OVERRIDE, STATE_INPUT_RESUME_OVERRIDE, STATE_INPUT_PLAY_STATE_RUNNING, STATE_INPUT_PLAY_STATE_PAUSED, STATE_INPUT_END_ANIMATION };
void updateStateMachine(AnimStateInput input, double param);
void onAnimationStartResponse(double startTime);
void updatePlayState(bool running);
bool playStatePlaying() const { return m_animation && m_animation->animationPlayState() == AnimPlayStatePlaying; }
bool waitingToStart() const { return m_animState == STATE_NEW || m_animState == STATE_START_WAIT_TIMER; }
bool preactive() const { return m_animState == STATE_NEW || m_animState == STATE_START_WAIT_TIMER ||
m_animState == STATE_START_WAIT_STYLE_SETUP || m_animState == STATE_START_WAIT_RESPONSE; }
bool postactive() const { return m_animState == STATE_DONE; }
bool active() const { return !postactive() && !preactive(); }
bool running() const { return m_pauseTime < 0; }
bool isnew() const { return m_animState == STATE_NEW; }
bool waitingForStartTime() const { return m_animState == STATE_START_WAIT_RESPONSE; }
bool waitingForStyleSetup() const { return m_animState == STATE_START_WAIT_STYLE_SETUP; }
bool waitingForEndEvent() const { return m_waitingForEndEvent; }
void setAnimating(bool inAnimating = true) { m_animating = inAnimating; }
bool animating() const { return m_animating; }
double progress(double scale, double offset) const;
virtual void animate(CompositeAnimation*, RenderObject*, const RenderStyle* currentStyle,
const RenderStyle* targetStyle, RenderStyle*& ioAnimatedStyle) { }
virtual void reset(RenderObject* renderer, const RenderStyle* from = 0, const RenderStyle* to = 0) { }
virtual bool shouldFireEvents() const { return false; }
void animationTimerCBFired(const AtomicString& eventType, double elapsedTime);
void animationEventDispatcherFired(HTMLElement* element, const AtomicString& name, int property, bool reset,
const AtomicString& eventType, double elapsedTime);
bool animationsMatch(const Transition* anim) const { return m_animation->transitionsMatch(anim, false); }
void setAnimation(const Transition* anim) { m_animation = const_cast<Transition*>(anim); }
virtual bool overridden() const { return false; }
virtual bool affectsProperty(int property) const { return false; }
bool isAnimatingProperty(int property) const
{
return (!waitingToStart() && !postactive()) && affectsProperty(property);
}
protected:
HTMLElement* elementForEventDispatch()
{
if (m_object->node() && m_object->node()->isHTMLElement())
return static_cast<HTMLElement*>(m_object->node());
return 0;
}
virtual void overrideAnimations() { }
virtual void resumeOverriddenAnimations() { }
CompositeAnimation* compositeAnimation() { return m_compAnim; }
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();
protected:
AnimState m_animState;
int m_iteration;
bool m_animating; bool m_waitedForResponse;
double m_startTime;
double m_pauseTime;
RenderObject* m_object;
AnimationTimerCB m_animationTimerCB;
AnimationEventDispatcher m_animationEventDispatcher;
RefPtr<Transition> m_animation;
CompositeAnimation* m_compAnim;
bool m_waitingForEndEvent;
};
#pragma mark -
class ImplicitAnimation : public AnimationBase {
public:
ImplicitAnimation(const Transition* transition, RenderObject* renderer, CompositeAnimation* compAnim)
: AnimationBase(transition, renderer, compAnim)
, m_property(transition->transitionProperty())
, m_overridden(false)
, m_fromStyle(0)
, m_toStyle(0)
{
}
virtual ~ImplicitAnimation()
{
ASSERT(!m_fromStyle && !m_toStyle);
if (waitingForEndEvent() && m_object)
m_object->transitionFinished(m_property);
if (!postactive())
updateStateMachine(STATE_INPUT_END_ANIMATION, -1);
}
int transitionProperty() const { return m_property; }
virtual void onAnimationEnd(double inElapsedTime);
virtual bool startAnimation(double beginTime);
virtual void endAnimation(bool reset);
virtual void animate(CompositeAnimation*, RenderObject*, const RenderStyle* currentStyle,
const RenderStyle* targetStyle, RenderStyle*& ioAnimatedStyle);
virtual void reset(RenderObject* renderer, const RenderStyle* from = 0, const RenderStyle* to = 0);
void setOverridden(bool b);
virtual bool overridden() const { return m_overridden; }
virtual bool shouldFireEvents() const { return true; }
virtual bool affectsProperty(int property) const;
bool hasStyle() const { return m_fromStyle && m_toStyle; }
protected:
bool shouldSendEventForListener(Document::ListenerType inListenerType)
{
return m_object->document()->hasListenerType(inListenerType);
}
bool sendTransitionEvent(const AtomicString& inEventType, double inElapsedTime);
private:
int m_property;
bool m_overridden;
RenderStyle* m_fromStyle;
RenderStyle* m_toStyle;
};
#pragma mark -
class KeyframeAnimation : public AnimationBase {
public:
KeyframeAnimation(const Transition* transition, RenderObject* renderer, int index, CompositeAnimation* compAnim)
: AnimationBase(transition, renderer, compAnim)
, m_keyframes(transition->keyframeList())
, m_name(transition->animationName())
, m_index(index)
{
}
virtual ~KeyframeAnimation()
{
if (waitingForEndEvent() && m_object)
m_object->animationFinished(m_name, 0, true);
if (!postactive())
updateStateMachine(STATE_INPUT_END_ANIMATION, -1);
}
virtual void animate(CompositeAnimation*, RenderObject*, const RenderStyle* currentStyle,
const RenderStyle* targetStyle, RenderStyle*& ioAnimatedStyle);
void setName(const String& s) { m_name = s; }
const AtomicString& name() const { return m_name; }
virtual bool shouldFireEvents() const { return true; }
protected:
virtual void onAnimationStart(double inElapsedTime);
virtual void onAnimationIteration(double inElapsedTime);
virtual void onAnimationEnd(double inElapsedTime);
virtual bool startAnimation(double beginTime);
virtual void endAnimation(bool reset);
virtual void overrideAnimations();
virtual void resumeOverriddenAnimations();
bool shouldSendEventForListener(Document::ListenerType inListenerType)
{
return m_object->document()->hasListenerType(inListenerType);
}
bool sendAnimationEvent(const AtomicString& inEventType, double inElapsedTime);
virtual bool affectsProperty(int property) const;
private:
RefPtr<KeyframeList> m_keyframes;
AtomicString m_name;
int m_index;
};
#pragma mark -
void AnimationTimerCB::timerFired(Timer<AnimationTimerBase>*)
{
m_anim->animationTimerCBFired(m_eventType, m_elapsedTime);
}
void AnimationEventDispatcher::timerFired(Timer<AnimationTimerBase>*)
{
m_anim->animationEventDispatcherFired(m_element.get(), m_name, m_property, m_reset, m_eventType, m_elapsedTime);
}
#pragma mark -
AnimationBase::AnimationBase(const Transition* transition, RenderObject* renderer, CompositeAnimation* compAnim)
: m_animState(STATE_NEW)
, m_iteration(0)
, m_animating(false)
, m_waitedForResponse(false)
, m_startTime(0)
, m_pauseTime(-1)
, m_object(renderer)
, m_animationTimerCB(const_cast<AnimationBase*>(this))
, m_animationEventDispatcher(const_cast<AnimationBase*>(this))
, m_animation(const_cast<Transition*>(transition))
, m_compAnim(compAnim)
, m_waitingForEndEvent(false)
{
}
#ifdef DEBUG_STATE_MACHINE
#define DSM_PRINT_FORCE(input, next) \
fprintf(stderr, "*** got %s --> %s at %f for animation %p\n", input, next, currentTime(), this)
#define DSM_PRINT(state, input, next) \
fprintf(stderr, "*** got %s in %s --> %s at %f for animation %p\n", input, state, next, currentTime(), this)
#define DSM_PRINT_PARAM(state, input, name, param, next) \
fprintf(stderr, "*** got %s in %s --> %s (%s=%f) at %f for animation %p\n", input, state, next, name, param, currentTime(), this)
#else
#define DSM_PRINT_FORCE(input, next)
#define DSM_PRINT(state, input, next)
#define DSM_PRINT_PARAM(state, input, name, param, next)
#endif
void AnimationBase::updateStateMachine(AnimStateInput input, double param)
{
if (input == STATE_INPUT_MAKE_NEW) {
DSM_PRINT_FORCE("STATE_INPUT_MAKE_NEW", "STATE_NEW");
if (m_animState == STATE_START_WAIT_STYLE_SETUP)
m_compAnim->setWaitingForStyleIsSetup(false);
m_animState = STATE_NEW;
m_startTime = 0;
m_pauseTime = -1;
m_waitedForResponse = false;
endAnimation(false);
return;
}
else if (input == STATE_INPUT_RESTART_ANIMATION) {
DSM_PRINT_FORCE("STATE_INPUT_RESTART_ANIMATION", "STATE_NEW");
cancelTimers();
if (m_animState == STATE_START_WAIT_STYLE_SETUP)
m_compAnim->setWaitingForStyleIsSetup(false);
m_animState = STATE_NEW;
m_startTime = 0;
m_pauseTime = -1;
endAnimation(false);
if (running())
updateStateMachine(STATE_INPUT_START_ANIMATION, -1);
return;
}
else if (input == STATE_INPUT_END_ANIMATION) {
DSM_PRINT_FORCE("STATE_INPUT_END_ANIMATION", "STATE_DONE");
cancelTimers();
if (m_animState == STATE_START_WAIT_STYLE_SETUP)
m_compAnim->setWaitingForStyleIsSetup(false);
m_animState = STATE_DONE;
endAnimation(true);
return;
}
else if (input == STATE_INPUT_PAUSE_OVERRIDE) {
#ifdef DEBUG_STATE_MACHINE
fprintf(stderr, "*** got STATE_INPUT_PAUSE_OVERRIDE at %f for animation %p\n", currentTime(), this);
#endif
if (m_animState == STATE_START_WAIT_RESPONSE) {
endAnimation(false);
updateStateMachine(STATE_INPUT_START_TIME_SET, currentTime());
}
return;
}
else if (input == STATE_INPUT_RESUME_OVERRIDE) {
#ifdef DEBUG_STATE_MACHINE
fprintf(stderr, "*** got STATE_INPUT_RESUME_OVERRIDE at %f for animation %p\n", currentTime(), this);
#endif
if (m_animState == STATE_LOOPING || m_animState == STATE_ENDING) {
startAnimation(m_startTime);
}
return;
}
switch(m_animState) {
case STATE_NEW:
ASSERT(input == STATE_INPUT_START_ANIMATION || input == STATE_INPUT_PLAY_STATE_RUNNING || input == STATE_INPUT_PLAY_STATE_PAUSED);
if (input == STATE_INPUT_START_ANIMATION || input == STATE_INPUT_PLAY_STATE_RUNNING) {
DSM_PRINT_PARAM("STATE_NEW",
(input == STATE_INPUT_START_ANIMATION) ? "STATE_INPUT_START_ANIMATION": "STATE_INPUT_PLAY_STATE_RUNNING",
"delay", m_animation->animationDelay(), "STATE_START_WAIT_TIMER");
m_waitedForResponse = false;
m_animState = STATE_START_WAIT_TIMER;
m_animationTimerCB.startTimer(m_animation->animationDelay(),
EventNames::webkitAnimationStartEvent, m_animation->animationDelay());
}
else {
DSM_PRINT("STATE_NEW", "STATE_INPUT_PLAY_STATE_PAUSED", "STATE_NEW");
}
break;
case STATE_START_WAIT_TIMER:
ASSERT(input == STATE_INPUT_START_TIMER_FIRED || input == STATE_INPUT_PLAY_STATE_PAUSED);
if (input == STATE_INPUT_START_TIMER_FIRED) {
ASSERT(param >= 0);
DSM_PRINT_PARAM("STATE_START_WAIT_TIMER", "STATE_INPUT_START_TIMER_FIRED", "elapsedTime", param,
"STATE_START_WAIT_STYLE_SETUP");
m_animState = STATE_START_WAIT_STYLE_SETUP;
m_compAnim->setWaitingForStyleIsSetup(true);
setChanged(m_object->element());
m_object->animationController()->startUpdateRenderingDispatcher();
}
else {
ASSERT(running());
DSM_PRINT("STATE_START_WAIT_TIMER", "STATE_INPUT_PLAY_STATE_PAUSED", "STATE_PAUSED_WAIT_TIMER");
m_pauseTime = currentTime();
cancelTimers();
m_animState = STATE_PAUSED_WAIT_TIMER;
}
break;
case STATE_START_WAIT_STYLE_SETUP:
ASSERT(input == STATE_INPUT_STYLE_SETUP || input == STATE_INPUT_PLAY_STATE_PAUSED);
m_compAnim->setWaitingForStyleIsSetup(false);
if (input == STATE_INPUT_STYLE_SETUP) {
DSM_PRINT("STATE_START_WAIT_STYLE_SETUP", "STATE_INPUT_STYLE_SETUP", "STATE_START_WAIT_RESPONSE");
m_animState = STATE_START_WAIT_RESPONSE;
overrideAnimations();
onAnimationStart(0.0f);
if (overridden() || !startAnimation(0)) {
#ifdef DEBUG_STATE_MACHINE
fprintf(stderr, "*** startAnimation returned false, setting start time immediately\n");
#endif
m_animState = STATE_START_WAIT_RESPONSE;
updateStateMachine(STATE_INPUT_START_TIME_SET, currentTime());
}
else
m_waitedForResponse = true;
}
else {
ASSERT(running());
DSM_PRINT("STATE_START_WAIT_TIMER", "STATE_INPUT_PLAY_STATE_PAUSED", "STATE_START_WAIT_RESPONSE");
m_pauseTime = 0;
m_animState = STATE_START_WAIT_RESPONSE;
}
break;
case STATE_START_WAIT_RESPONSE:
ASSERT(input == STATE_INPUT_START_TIME_SET || input == STATE_INPUT_PLAY_STATE_PAUSED);
if (input == STATE_INPUT_START_TIME_SET) {
ASSERT(param >= 0);
if (m_startTime <= 0)
m_startTime = param;
primeEventTimers();
DSM_PRINT_PARAM("STATE_START_WAIT_RESPONSE", "STATE_INPUT_START_TIME_SET", "startTime", param,
(m_animState==STATE_LOOPING)?"STATE_LOOPING":"STATE_ENDING");
setChanged(m_object->element());
m_object->animationController()->startUpdateRenderingDispatcher();
}
else {
DSM_PRINT("STATE_START_WAIT_RESPONSE", "STATE_INPUT_PLAY_STATE_PAUSED", "STATE_PAUSED_WAIT_RESPONSE");
m_pauseTime = 0;
endAnimation(false);
m_animState = STATE_PAUSED_WAIT_RESPONSE;
}
break;
case STATE_LOOPING:
ASSERT(input == STATE_INPUT_LOOP_TIMER_FIRED || input == STATE_INPUT_PLAY_STATE_PAUSED);
if (input == STATE_INPUT_LOOP_TIMER_FIRED) {
ASSERT(param >= 0);
onAnimationIteration(param);
primeEventTimers();
DSM_PRINT_PARAM("STATE_LOOPING", "STATE_INPUT_LOOP_TIMER_FIRED", "elapsedTime", param,
(m_animState==STATE_LOOPING)?"STATE_LOOPING":"STATE_ENDING");
}
else {
DSM_PRINT("STATE_LOOPING", "STATE_INPUT_PLAY_STATE_PAUSED", "STATE_PAUSED_RUN");
m_pauseTime = currentTime();
cancelTimers();
endAnimation(false);
m_animState = STATE_PAUSED_RUN;
}
break;
case STATE_ENDING:
ASSERT(input == STATE_INPUT_END_TIMER_FIRED || input == STATE_INPUT_PLAY_STATE_PAUSED);
if (input == STATE_INPUT_END_TIMER_FIRED) {
ASSERT(param >= 0);
DSM_PRINT_PARAM("STATE_ENDING", "STATE_INPUT_END_TIMER_FIRED", "elapsedTime", param, "STATE_DONE");
onAnimationEnd(param);
resumeOverriddenAnimations();
setChanged(m_object->element());
m_animState = STATE_DONE;
m_object->animationController()->startUpdateRenderingDispatcher();
}
else {
DSM_PRINT("STATE_ENDING", "STATE_INPUT_PLAY_STATE_PAUSED", "STATE_PAUSED_RUN");
m_pauseTime = currentTime();
cancelTimers();
endAnimation(false);
m_animState = STATE_PAUSED_RUN;
}
break;
case STATE_PAUSED_WAIT_TIMER:
ASSERT(input == STATE_INPUT_PLAY_STATE_RUNNING);
ASSERT(!running());
DSM_PRINT("STATE_PAUSED_WAIT_TIMER", "STATE_INPUT_PLAY_STATE_RUNNING", "STATE_NEW");
m_startTime += currentTime() - m_pauseTime;
m_pauseTime = -1;
m_animState = STATE_NEW;
updateStateMachine(STATE_INPUT_START_ANIMATION, 0);
break;
case STATE_PAUSED_WAIT_RESPONSE:
case STATE_PAUSED_RUN:
ASSERT(input == STATE_INPUT_PLAY_STATE_RUNNING);
ASSERT(!running());
DSM_PRINT("STATE_PAUSED_WAIT_RESPONSE", "STATE_INPUT_PLAY_STATE_RUNNING", "STATE_START_WAIT_RESPONSE");
if (m_animState == STATE_PAUSED_RUN)
m_startTime += currentTime() - m_pauseTime;
else
m_startTime = 0;
m_pauseTime = -1;
m_animState = STATE_START_WAIT_RESPONSE;
if (overridden() || !startAnimation(m_startTime)) {
updateStateMachine(STATE_INPUT_START_TIME_SET, currentTime());
}
else
m_waitedForResponse = true;
break;
case STATE_DONE:
DSM_PRINT_FORCE("STATE_DONE", "STATE_DONE");
break;
}
}
void AnimationBase::animationTimerCBFired(const AtomicString& eventType, double elapsedTime)
{
ASSERT(m_object->document() && !m_object->document()->inPageCache());
if (eventType == EventNames::webkitAnimationStartEvent)
updateStateMachine(STATE_INPUT_START_TIMER_FIRED, elapsedTime);
else if (eventType == EventNames::webkitAnimationIterationEvent)
updateStateMachine(STATE_INPUT_LOOP_TIMER_FIRED, elapsedTime);
else if (eventType == EventNames::webkitAnimationEndEvent) {
updateStateMachine(STATE_INPUT_END_TIMER_FIRED, elapsedTime);
}
}
void AnimationBase::animationEventDispatcherFired(HTMLElement* element, const AtomicString& name, int property,
bool reset, const AtomicString& eventType, double elapsedTime)
{
m_waitingForEndEvent = false;
AtomicString animName(name);
AtomicString animEventType(eventType);
RefPtr<HTMLElement> elementRetainer(element);
ASSERT(!element || (element->document() && !element->document()->inPageCache()));
if (!element)
return;
if (eventType == EventNames::webkitTransitionEndEvent) {
element->dispatchWebKitTransitionEvent(eventType, name, elapsedTime);
}
else {
element->dispatchWebKitAnimationEvent(eventType, name, elapsedTime);
}
if (animEventType == EventNames::webkitTransitionEndEvent) {
if (element->renderer())
element->renderer()->transitionFinished(property);
}
else if (animEventType == EventNames::webkitAnimationEndEvent) {
if (element->renderer()) {
element->renderer()->animationFinished(animName, 0, reset);
setChanged(element->renderer()->element());
}
}
}
void AnimationBase::updatePlayState(bool run)
{
if (running() != run || isnew()) {
if (m_object->document()->loadComplete())
updateStateMachine(run ? STATE_INPUT_PLAY_STATE_RUNNING : STATE_INPUT_PLAY_STATE_PAUSED, -1);
}
}
double AnimationBase::progress(double scale, double offset) const
{
if (preactive())
return 0;
double elapsedTime = running() ? (currentTime() - m_startTime) : (m_pauseTime - m_startTime);
if (running() && elapsedTime < 0)
return 0;
double dur = m_animation->transitionDuration();
if (m_animation->transitionIterationCount() > 0)
dur *= m_animation->transitionIterationCount();
if (postactive() || !m_animation->transitionDuration() || (m_animation->transitionIterationCount() > 0 && elapsedTime >= dur))
return 1.0;
double t = elapsedTime / m_animation->transitionDuration();
int i = (int) t;
t -= i;
if (m_animation->animationDirection() && (i & 1))
t = 1 - t;
if (scale != 1 || offset != 0)
t = (t - offset) * scale;
if (m_animation->transitionTimingFunction().type() == LinearTimingFunction)
return t;
double tt = solveCubicBezierFunction(m_animation->transitionTimingFunction().x1(),
m_animation->transitionTimingFunction().y1(),
m_animation->transitionTimingFunction().x2(),
m_animation->transitionTimingFunction().y2(),
t, m_animation->transitionDuration());
return tt;
}
void AnimationBase::primeEventTimers()
{
double ct = currentTime();
const double elapsedDuration = ct - m_startTime;
ASSERT(elapsedDuration >= 0);
double totalDuration = -1;
if (m_animation->transitionIterationCount() > 0)
totalDuration = m_animation->transitionDuration() * m_animation->transitionIterationCount();
double durationLeft = 0;
double nextIterationTime = totalDuration;
if (totalDuration < 0 || elapsedDuration < totalDuration) {
durationLeft = m_animation->transitionDuration() - fmod(elapsedDuration, m_animation->transitionDuration());
nextIterationTime = elapsedDuration + durationLeft;
}
if (totalDuration < 0 || nextIterationTime < totalDuration) {
ASSERT(nextIterationTime > 0);
m_animState = STATE_LOOPING;
m_animationTimerCB.startTimer(durationLeft, EventNames::webkitAnimationIterationEvent, nextIterationTime);
}
else {
m_animState = STATE_ENDING;
m_animationTimerCB.startTimer(durationLeft, EventNames::webkitAnimationEndEvent, nextIterationTime);
}
}
#pragma mark -
static inline int blendFunc(int from, int to, double progress)
{
return int(from + (to - from) * progress);
}
static inline double blendFunc(double from, double to, double progress)
{
return from + (to - from) * progress;
}
static inline float blendFunc(float from, float to, double progress)
{
return narrowPrecisionToFloat(from + (to - from) * progress);
}
static inline Color blendFunc(const Color& from, const Color& to, double progress)
{
return Color(blendFunc(from.red(), to.red(), progress),
blendFunc(from.green(), to.green(), progress),
blendFunc(from.blue(), to.blue(), progress),
blendFunc(from.alpha(), to.alpha(), progress));
}
static inline Length blendFunc(const Length& from, const Length& to, double progress)
{
return to.blend(from, progress);
}
static inline IntSize blendFunc(const IntSize& from, const IntSize& to, double progress)
{
return IntSize(blendFunc(from.width(), to.width(), progress),
blendFunc(from.height(), to.height(), progress));
}
static inline ShadowData* blendFunc(const ShadowData* from, const ShadowData* to, double progress)
{
ASSERT(from && to);
return new ShadowData(blendFunc(from->x, to->x, progress), blendFunc(from->y, to->y, progress),
blendFunc(from->blur, to->blur, progress), blendFunc(from->color, to->color, progress));
}
static inline TransformOperations blendFunc(const TransformOperations& from, const TransformOperations& to, double progress)
{
unsigned fromSize = from.size();
unsigned toSize = to.size();
unsigned size = max(fromSize, toSize);
TransformOperations result;
for (unsigned i = 0; i < size; i++) {
TransformOperation* fromOp = i < fromSize ? from[i].get() : 0;
TransformOperation* toOp = i < toSize ? to[i].get() : 0;
TransformOperation* blendedOp = toOp ? toOp->blend(fromOp, progress) : fromOp->blend(0, progress, true);
if (blendedOp)
result.append(blendedOp);
else {
if (progress > 0.5)
result.append(toOp ? toOp : new IdentityTransformOperation());
else
result.append( fromOp ? fromOp : new IdentityTransformOperation());
}
}
return result;
}
static inline EVisibility blendFunc(EVisibility from, EVisibility to, double progress)
{
double fromVal = from == VISIBLE ? 1. : 0.;
double toVal = to == VISIBLE ? 1. : 0.;
if (fromVal == toVal)
return to;
double result = blendFunc(fromVal, toVal, progress);
return result > 0. ? VISIBLE : (to != VISIBLE ? to : from);
}
#pragma mark -
void CompositeAnimation::updateTransitions(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
{
const Transition* anim = targetStyle->transitions();
if (m_transitions.isEmpty() && !anim)
return;
int numAnims = 0;
bool transitionsChanged = false;
for ( ; anim; anim = anim->next()) {
++numAnims;
ImplicitAnimation* implAnim = (anim->transitionProperty() == CSS_PROP_INVALID) ? 0 :
m_transitions.get(anim->transitionProperty());
if (!implAnim || !implAnim->animationsMatch(anim)) {
transitionsChanged = true;
break;
}
}
if (!transitionsChanged && m_transitions.size() != numAnims)
transitionsChanged = true;
if (!transitionsChanged)
return;
resetTransitions(renderer);
if (!targetStyle->transitions())
return;
for (anim = targetStyle->transitions(); anim; anim = anim->next()) {
int property = anim->transitionProperty();
double duration = anim->transitionDuration();
int iterationCount = anim->transitionIterationCount();
double delay = anim->animationDelay();
if (property && (duration || delay) && iterationCount && !m_transitions.contains(property)) {
ImplicitAnimation* animation = new ImplicitAnimation(const_cast<Transition*>(anim), renderer, this);
m_transitions.set(property, animation);
}
}
}
void CompositeAnimation::updateKeyframeAnimations(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
{
const Transition* anim = targetStyle->animations();
if ((m_keyframeAnimations.isEmpty() && !anim) ||
(currentStyle && currentStyle->animations() && *(currentStyle->animations()) == *anim))
return;
size_t numAnims = 0;
bool animsChanged = false;
for ( ; anim; anim = anim->next()) {
if (!anim->isValidAnimation()) {
animsChanged = true;
} else {
KeyframeAnimation* kfAnim = (numAnims < m_keyframeAnimations.size()) ? m_keyframeAnimations[numAnims] : 0;
if (!kfAnim || !kfAnim->animationsMatch(anim))
animsChanged = true;
else {
if (anim) {
kfAnim->updatePlayState(anim->animationPlayState() == AnimPlayStatePlaying);
kfAnim->setAnimation(anim);
}
}
}
++numAnims;
}
if (!animsChanged && m_keyframeAnimations.size() != numAnims)
animsChanged = true;
if (!animsChanged)
return;
resetAnimations(renderer);
if (!targetStyle->animations())
return;
int index = 0;
for (anim = targetStyle->animations(); anim; anim = anim->next(), ++index) {
if (!anim->isValidAnimation())
continue;
if ((anim->transitionDuration() || anim->animationDelay()) && anim->transitionIterationCount() &&
anim->keyframeList().get() && !anim->keyframeList()->isEmpty()) {
m_keyframeAnimations.append(new KeyframeAnimation(const_cast<Transition*>(anim), renderer, index, this));
}
}
}
int CompositeAnimation::findKeyframeAnimation(const AtomicString& name)
{
for (int i = 0; i < (int) m_keyframeAnimations.size(); ++i)
if (m_keyframeAnimations[i]->name() == name)
return i;
return -1;
}
RenderStyle* CompositeAnimation::animate(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
{
RenderStyle* resultStyle = 0;
updateTransitions(renderer, currentStyle, targetStyle);
if (currentStyle) {
CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
ImplicitAnimation* anim = it->second;
if (anim)
anim->animate(this, renderer, currentStyle, targetStyle, resultStyle);
}
}
updateKeyframeAnimations(renderer, currentStyle, targetStyle);
Vector<KeyframeAnimation*>::const_iterator kfend = m_keyframeAnimations.end();
for (Vector<KeyframeAnimation*>::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
KeyframeAnimation* anim = *it;
if (anim)
anim->animate(this, renderer, currentStyle, targetStyle, resultStyle);
}
return resultStyle ? resultStyle : targetStyle;
}
void CompositeAnimation::setAnimating(bool inAnimating)
{
CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
ImplicitAnimation* transition = it->second;
transition->setAnimating(inAnimating);
}
Vector<KeyframeAnimation*>::const_iterator kfend = m_keyframeAnimations.end();
for (Vector<KeyframeAnimation*>::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
KeyframeAnimation* anim = *it;
anim->setAnimating(inAnimating);
}
}
bool CompositeAnimation::animating()
{
CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
ImplicitAnimation* transition = it->second;
if (transition && transition->animating() && transition->running())
return true;
}
Vector<KeyframeAnimation*>::const_iterator kfend = m_keyframeAnimations.end();
for (Vector<KeyframeAnimation*>::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
KeyframeAnimation* anim = *it;
if (anim && anim->running() && anim->animating() && anim->active())
return true;
}
return false;
}
void CompositeAnimation::resetTransitions(RenderObject* renderer)
{
CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
ImplicitAnimation* transition = it->second;
transition->reset(renderer);
delete transition;
}
m_transitions.clear();
}
void CompositeAnimation::resetAnimations(RenderObject* renderer)
{
Vector<KeyframeAnimation*>::const_iterator kfend = m_keyframeAnimations.end();
for (Vector<KeyframeAnimation*>::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
KeyframeAnimation* anim = *it;
delete anim;
}
m_keyframeAnimations.clear();
}
void CompositeAnimation::cleanupFinishedAnimations(RenderObject* renderer)
{
if (suspended())
return;
CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
ImplicitAnimation* anim = it->second;
if (!anim)
continue;
if (anim->postactive() && !anim->waitingForEndEvent()) {
anim->updateStateMachine(anim->STATE_INPUT_MAKE_NEW, -1);
anim->reset(renderer);
}
}
Vector<int> finishedAnimations;
for (int i = 0; i < (int) m_keyframeAnimations.size(); ++i) {
KeyframeAnimation* anim = m_keyframeAnimations[i];
if (!anim)
continue;
if (anim->postactive() && !anim->waitingForEndEvent())
finishedAnimations.append(i);
}
for (Vector<int>::iterator it = finishedAnimations.begin(); it != finishedAnimations.end(); ++it) {
m_keyframeAnimations[*it]->reset(renderer);
delete m_keyframeAnimations[*it];
m_keyframeAnimations.remove(*it);
}
}
void CompositeAnimation::setAnimationStartTime(double t)
{
Vector<KeyframeAnimation*>::const_iterator kfend = m_keyframeAnimations.end();
for (Vector<KeyframeAnimation*>::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
KeyframeAnimation* anim = *it;
if (anim && anim->waitingForStartTime())
anim->updateStateMachine(AnimationBase::STATE_INPUT_START_TIME_SET, t);
}
}
void CompositeAnimation::setTransitionStartTime(int property, double t)
{
CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
ImplicitAnimation* anim = it->second;
if (anim && anim->waitingForStartTime() &&
(anim->transitionProperty() == property || anim->transitionProperty() == cAnimateAll))
anim->updateStateMachine(AnimationBase::STATE_INPUT_START_TIME_SET, t);
}
}
void CompositeAnimation::suspendAnimations()
{
if (m_suspended)
return;
m_suspended = true;
Vector<KeyframeAnimation*>::const_iterator kfend = m_keyframeAnimations.end();
for (Vector<KeyframeAnimation*>::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
KeyframeAnimation* anim = *it;
if (anim) {
anim->updatePlayState(false);
anim->renderer()->suspendAnimations();
}
}
CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
ImplicitAnimation* anim = it->second;
if (anim && anim->hasStyle()) {
anim->updatePlayState(false);
anim->renderer()->suspendAnimations();
}
}
}
void CompositeAnimation::resumeAnimations()
{
if (!m_suspended)
return;
m_suspended = false;
Vector<KeyframeAnimation*>::const_iterator kfend = m_keyframeAnimations.end();
for (Vector<KeyframeAnimation*>::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
KeyframeAnimation* anim = *it;
if (anim && anim->playStatePlaying()) {
anim->updatePlayState(true);
anim->renderer()->resumeAnimations();
}
}
CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
ImplicitAnimation* anim = it->second;
if (anim && anim->hasStyle()) {
anim->updatePlayState(true);
anim->renderer()->resumeAnimations();
}
}
}
void CompositeAnimation::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;
if (anim && (anim->transitionProperty() == property || anim->transitionProperty() == cAnimateAll))
anim->setOverridden(true);
}
}
void CompositeAnimation::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;
if (anim && (anim->transitionProperty() == property || anim->transitionProperty() == cAnimateAll))
anim->setOverridden(false);
}
}
void CompositeAnimation::styleIsSetup()
{
if (m_numStyleIsSetupWaiters == 0)
return;
Vector<KeyframeAnimation*>::const_iterator kfend = m_keyframeAnimations.end();
for (Vector<KeyframeAnimation*>::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
KeyframeAnimation* anim = *it;
if (anim && anim->waitingForStyleSetup())
anim->updateStateMachine(AnimationBase::STATE_INPUT_STYLE_SETUP, -1);
}
CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
ImplicitAnimation* anim = it->second;
if (anim && anim->waitingForStyleSetup())
anim->updateStateMachine(AnimationBase::STATE_INPUT_STYLE_SETUP, -1);
}
}
bool CompositeAnimation::isAnimatingProperty(int property) const
{
Vector<KeyframeAnimation*>::const_iterator kfend = m_keyframeAnimations.end();
for (Vector<KeyframeAnimation*>::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
KeyframeAnimation* anim = *it;
if (anim && anim->isAnimatingProperty(property))
return true;
}
CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
ImplicitAnimation* anim = it->second;
if (anim && anim->isAnimatingProperty(property))
return true;
}
return false;
}
#pragma mark -
class PropertyWrapperBase {
public:
PropertyWrapperBase(int prop)
: m_prop(prop)
{ }
virtual ~PropertyWrapperBase() { }
virtual bool equals(const RenderStyle* a, const RenderStyle* b) const=0;
virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const=0;
int property() const { return m_prop; }
virtual bool hardwareAnimated() const { return false; }
private:
int m_prop;
};
template <typename T>
class PropertyWrapperGetter : public PropertyWrapperBase {
public:
PropertyWrapperGetter(int prop, T (RenderStyle::*getter)() const)
: PropertyWrapperBase(prop)
, m_getter(getter)
{ }
virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
{
return (a->*m_getter)() == (b->*m_getter)();
}
protected:
T (RenderStyle::*m_getter)() const;
};
template <typename T>
class PropertyWrapper : public PropertyWrapperGetter<T> {
public:
PropertyWrapper(int prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T))
: PropertyWrapperGetter<T>(prop, getter)
, m_setter(setter)
{ }
virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const
{
(dst->*m_setter)(blendFunc((a->*PropertyWrapperGetter<T>::m_getter)(), (b->*PropertyWrapperGetter<T>::m_getter)(), prog));
}
protected:
void (RenderStyle::*m_setter)(T);
};
class PropertyWrapperHWOpacity : public PropertyWrapper<float> {
public:
PropertyWrapperHWOpacity()
: PropertyWrapper<float>(CSS_PROP_OPACITY, &RenderStyle::opacity, &RenderStyle::setOpacity)
{ }
virtual bool hardwareAnimated() const { return true; }
virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const
{
float fromOpacity = a->opacity();
dst->setOpacity(blendFunc((fromOpacity == 1) ? 0.999999f : fromOpacity, b->opacity(), prog));
}
};
class PropertyWrapperHWTransform : public PropertyWrapper<const TransformOperations&> {
public:
PropertyWrapperHWTransform()
: PropertyWrapper<const TransformOperations&>(CSS_PROP__WEBKIT_TRANSFORM, &RenderStyle::transform, &RenderStyle::setTransform)
{ }
virtual bool hardwareAnimated() const { return true; }
virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const
{
const TransformOperations& t = a->transform();
dst->setTransform(blendFunc(t.isEmpty() ? TransformOperations(true) : t, b->transform(), prog));
}
};
class PropertyWrapperShadow : public PropertyWrapperGetter<ShadowData*> {
public:
PropertyWrapperShadow(int prop, ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(ShadowData*, bool))
: PropertyWrapperGetter<ShadowData*>(prop, getter)
, m_setter(setter)
{ }
virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
{
ShadowData* shadowa = (a->*m_getter)();
ShadowData* shadowb = (b->*m_getter)();
if (!shadowa && shadowb || shadowa && !shadowb)
return false;
if (shadowa && shadowb && (*shadowa != *shadowb))
return false;
return true;
}
virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const
{
ShadowData* shadowa = (a->*m_getter)();
ShadowData* shadowb = (b->*m_getter)();
ShadowData defaultShadowData;
if (!shadowa)
shadowa = &defaultShadowData;
if (!shadowb)
shadowb = &defaultShadowData;
(dst->*m_setter)(blendFunc(shadowa, shadowb, prog), false);
}
private:
void (RenderStyle::*m_setter)(ShadowData*, bool);
};
class PropertyWrapperMaybeInvalidColor : public PropertyWrapperBase {
public:
PropertyWrapperMaybeInvalidColor(int prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
: PropertyWrapperBase(prop)
, m_getter(getter)
, m_setter(setter)
{ }
virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
{
Color fromColor = (a->*m_getter)();
Color toColor = (b->*m_getter)();
if (!fromColor.isValid())
fromColor = a->color();
if (!toColor.isValid())
toColor = b->color();
return fromColor == toColor;
}
virtual void blend(RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog) const
{
Color fromColor = (a->*m_getter)();
Color toColor = (b->*m_getter)();
if (!fromColor.isValid())
fromColor = a->color();
if (!toColor.isValid())
toColor = b->color();
(dst->*m_setter)(blendFunc(fromColor, toColor, prog));
}
private:
const Color& (RenderStyle::*m_getter)() const;
void (RenderStyle::*m_setter)(const Color&);
};
class AnimationControllerPrivate {
public:
AnimationControllerPrivate(Frame*);
~AnimationControllerPrivate();
CompositeAnimation* accessCompositeAnimation(RenderObject*);
bool clear(RenderObject*);
void swAnimationTimerFired(Timer<AnimationControllerPrivate>*);
void updateSWAnimationTimer();
void updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*);
void startUpdateRenderingDispatcher();
bool hasAnimations() const { return !m_compositeAnimations.isEmpty(); }
void suspendAnimations(Document* document);
void resumeAnimations(Document* document);
void cleanupFinishedAnimations();
void styleIsSetup();
bool isAnimatingPropertyOnRenderer(RenderObject* obj, int property) const;
static bool propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b);
static bool blendProperties(int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog);
void setWaitingForStyleIsSetup(bool waiting) { if (waiting) m_numStyleIsSetupWaiters++; else m_numStyleIsSetupWaiters--; }
private:
static void ensurePropertyMap();
typedef HashMap<RenderObject*, CompositeAnimation*> RenderObjectAnimationMap;
RenderObjectAnimationMap m_compositeAnimations;
Timer<AnimationControllerPrivate> m_swAnimationTimer;
Timer<AnimationControllerPrivate> m_updateRenderingDispatcher;
Frame* m_frame;
uint32_t m_numStyleIsSetupWaiters;
static Vector<PropertyWrapperBase*>* g_propertyWrappers;
static int g_propertyWrapperMap[numCSSProperties];
};
#pragma mark -
Vector<PropertyWrapperBase*>* AnimationControllerPrivate::g_propertyWrappers = 0;
int AnimationControllerPrivate::g_propertyWrapperMap[];
AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame)
: m_swAnimationTimer(this, &AnimationControllerPrivate::swAnimationTimerFired)
, m_updateRenderingDispatcher(this, &AnimationControllerPrivate::updateRenderingDispatcherFired)
, m_frame(frame)
, m_numStyleIsSetupWaiters(0)
{
ensurePropertyMap();
}
AnimationControllerPrivate::~AnimationControllerPrivate()
{
deleteAllValues(m_compositeAnimations);
}
void AnimationControllerPrivate::ensurePropertyMap()
{
if (g_propertyWrappers == 0) {
g_propertyWrappers = new Vector<PropertyWrapperBase*>();
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_LEFT, &RenderStyle::left, &RenderStyle::setLeft));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_RIGHT, &RenderStyle::right, &RenderStyle::setRight));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_TOP, &RenderStyle::top, &RenderStyle::setTop));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_BOTTOM, &RenderStyle::bottom, &RenderStyle::setBottom));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_WIDTH, &RenderStyle::width, &RenderStyle::setWidth));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_HEIGHT, &RenderStyle::height, &RenderStyle::setHeight));
g_propertyWrappers->append(new PropertyWrapper<unsigned short>(CSS_PROP_BORDER_LEFT_WIDTH, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth));
g_propertyWrappers->append(new PropertyWrapper<unsigned short>(CSS_PROP_BORDER_RIGHT_WIDTH, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth));
g_propertyWrappers->append(new PropertyWrapper<unsigned short>(CSS_PROP_BORDER_TOP_WIDTH, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth));
g_propertyWrappers->append(new PropertyWrapper<unsigned short>(CSS_PROP_BORDER_BOTTOM_WIDTH, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_MARGIN_LEFT, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_MARGIN_RIGHT, &RenderStyle::marginRight, &RenderStyle::setMarginRight));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_MARGIN_TOP, &RenderStyle::marginTop, &RenderStyle::setMarginTop));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_MARGIN_BOTTOM, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_PADDING_LEFT, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_PADDING_RIGHT, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_PADDING_TOP, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_PADDING_BOTTOM, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom));
g_propertyWrappers->append(new PropertyWrapper<const Color&>(CSS_PROP_COLOR, &RenderStyle::color, &RenderStyle::setColor));
g_propertyWrappers->append(new PropertyWrapper<const Color&>(CSS_PROP_BACKGROUND_COLOR, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor));
g_propertyWrappers->append(new PropertyWrapper<unsigned short>(CSS_PROP__WEBKIT_COLUMN_RULE_WIDTH, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth));
g_propertyWrappers->append(new PropertyWrapper<float>(CSS_PROP__WEBKIT_COLUMN_GAP, &RenderStyle::columnGap, &RenderStyle::setColumnGap));
g_propertyWrappers->append(new PropertyWrapper<unsigned short>(CSS_PROP__WEBKIT_COLUMN_COUNT, &RenderStyle::columnCount, &RenderStyle::setColumnCount));
g_propertyWrappers->append(new PropertyWrapper<float>(CSS_PROP__WEBKIT_COLUMN_WIDTH, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth));
g_propertyWrappers->append(new PropertyWrapper<short>(CSS_PROP__WEBKIT_BORDER_HORIZONTAL_SPACING, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing));
g_propertyWrappers->append(new PropertyWrapper<short>(CSS_PROP__WEBKIT_BORDER_VERTICAL_SPACING, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing));
g_propertyWrappers->append(new PropertyWrapper<int>(CSS_PROP_Z_INDEX, &RenderStyle::zIndex, &RenderStyle::setZIndex));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP_LINE_HEIGHT, &RenderStyle::lineHeight, &RenderStyle::setLineHeight));
g_propertyWrappers->append(new PropertyWrapper<int>(CSS_PROP_OUTLINE_OFFSET, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset));
g_propertyWrappers->append(new PropertyWrapper<unsigned short>(CSS_PROP_OUTLINE_WIDTH, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth));
g_propertyWrappers->append(new PropertyWrapper<float>(CSS_PROP_LETTER_SPACING, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing));
g_propertyWrappers->append(new PropertyWrapper<int>(CSS_PROP_WORD_SPACING, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP__WEBKIT_TRANSFORM_ORIGIN_X, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX));
g_propertyWrappers->append(new PropertyWrapper<Length>(CSS_PROP__WEBKIT_TRANSFORM_ORIGIN_Y, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY));
g_propertyWrappers->append(new PropertyWrapper<float>(CSS_PROP__WEBKIT_TRANSFORM_ORIGIN_Z, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ));
g_propertyWrappers->append(new PropertyWrapper<const IntSize&>(CSS_PROP__WEBKIT_BORDER_TOP_LEFT_RADIUS, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius));
g_propertyWrappers->append(new PropertyWrapper<const IntSize&>(CSS_PROP__WEBKIT_BORDER_TOP_RIGHT_RADIUS, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius));
g_propertyWrappers->append(new PropertyWrapper<const IntSize&>(CSS_PROP__WEBKIT_BORDER_BOTTOM_LEFT_RADIUS, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius));
g_propertyWrappers->append(new PropertyWrapper<const IntSize&>(CSS_PROP__WEBKIT_BORDER_BOTTOM_RIGHT_RADIUS, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius));
g_propertyWrappers->append(new PropertyWrapper<EVisibility>(CSS_PROP_VISIBILITY, &RenderStyle::visibility, &RenderStyle::setVisibility));
#if ENABLE(HW_COMP)
g_propertyWrappers->append(new PropertyWrapperHWOpacity());
g_propertyWrappers->append(new PropertyWrapperHWTransform());
#else
g_propertyWrappers->append(new PropertyWrapper<float>(CSS_PROP_OPACITY, &RenderStyle::opacity, &RenderStyle::setOpacity));
g_propertyWrappers->append(new PropertyWrapper<const TransformOperations&>(CSS_PROP__WEBKIT_TRANSFORM, &RenderStyle::transform, &RenderStyle::setTransform));
#endif
g_propertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSS_PROP__WEBKIT_COLUMN_RULE_COLOR, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor));
g_propertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSS_PROP__WEBKIT_TEXT_STROKE_COLOR, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor));
g_propertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSS_PROP__WEBKIT_TEXT_FILL_COLOR, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor));
g_propertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSS_PROP_BORDER_LEFT_COLOR, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor));
g_propertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSS_PROP_BORDER_RIGHT_COLOR, &RenderStyle::borderRightColor, &RenderStyle::setBorderRightColor));
g_propertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSS_PROP_BORDER_TOP_COLOR, &RenderStyle::borderTopColor, &RenderStyle::setBorderTopColor));
g_propertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSS_PROP_BORDER_BOTTOM_COLOR, &RenderStyle::borderBottomColor, &RenderStyle::setBorderBottomColor));
g_propertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSS_PROP_OUTLINE_COLOR, &RenderStyle::outlineColor, &RenderStyle::setOutlineColor));
g_propertyWrappers->append(new PropertyWrapperShadow(CSS_PROP__WEBKIT_BOX_SHADOW, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow));
g_propertyWrappers->append(new PropertyWrapperShadow(CSS_PROP_TEXT_SHADOW, &RenderStyle::textShadow, &RenderStyle::setTextShadow));
for (unsigned int i = 0; i < (unsigned int) numCSSProperties; ++i)
g_propertyWrapperMap[i] = -1;
size_t n = g_propertyWrappers->size();
for (unsigned int i = 0; i < n; ++i)
g_propertyWrapperMap[(*g_propertyWrappers)[i]->property()] = i;
}
}
bool AnimationControllerPrivate::propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b)
{
if (prop == cAnimateAll) {
size_t n = g_propertyWrappers->size();
for (unsigned int i = 0; i < n; ++i) {
if (!(*g_propertyWrappers)[i]->equals(a, b))
return false;
}
}
else if (prop >= 0 && prop < numCSSProperties) {
int i = g_propertyWrapperMap[prop];
return (i >= 0) ? (*g_propertyWrappers)[i]->equals(a, b) : true;
}
return true;
}
bool AnimationControllerPrivate::blendProperties(int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double prog)
{
if (prop == cAnimateAll) {
bool needSWAnim = false;
size_t n = g_propertyWrappers->size();
for (unsigned int i = 0; i < n; ++i) {
PropertyWrapperBase* wrapper = (*g_propertyWrappers)[i];
if (!wrapper->equals(a, b)) {
wrapper->blend(dst, a, b, prog);
needSWAnim |= !wrapper->hardwareAnimated();
}
}
return needSWAnim;
}
if (prop >= 0 && prop < numCSSProperties) {
int i = g_propertyWrapperMap[prop];
if (i >= 0) {
PropertyWrapperBase* wrapper = (*g_propertyWrappers)[i];
wrapper->blend(dst, a, b, prog);
return !wrapper->hardwareAnimated();
}
}
return false;
}
CompositeAnimation* AnimationControllerPrivate::accessCompositeAnimation(RenderObject* renderer)
{
CompositeAnimation* animation = m_compositeAnimations.get(renderer);
if (!animation) {
animation = new CompositeAnimation(!renderer->document()->loadComplete(), this);
m_compositeAnimations.set(renderer, animation);
}
return animation;
}
bool AnimationControllerPrivate::clear(RenderObject* renderer)
{
CompositeAnimation* animation = m_compositeAnimations.take(renderer);
if (!animation)
return false;
animation->resetTransitions(renderer);
bool wasSuspended = animation->suspended();
delete animation;
return !wasSuspended;
}
void AnimationControllerPrivate::cleanupFinishedAnimations()
{
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;
compAnim->cleanupFinishedAnimations(renderer);
}
}
void AnimationControllerPrivate::styleIsSetup()
{
if (m_numStyleIsSetupWaiters == 0)
return;
RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin();
it != animationsEnd; ++it) {
it->second->styleIsSetup();
}
}
void AnimationControllerPrivate::updateSWAnimationTimer()
{
bool animating = 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->suspended() && compAnim->animating()) {
animating = true;
break;
}
}
if (animating) {
if (!m_swAnimationTimer.isActive())
m_swAnimationTimer.startRepeating(cAnimationTimerDelay);
} else if (m_swAnimationTimer.isActive())
m_swAnimationTimer.stop();
}
void AnimationControllerPrivate::updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*)
{
if (m_frame && m_frame->document())
m_frame->document()->updateRendering();
}
void AnimationControllerPrivate::startUpdateRenderingDispatcher()
{
if (!m_updateRenderingDispatcher.isActive())
m_updateRenderingDispatcher.startOneShot(0);
}
void AnimationControllerPrivate::swAnimationTimerFired(Timer<AnimationControllerPrivate>* timer)
{
bool animating = false;
RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin();
it != animationsEnd; ++it) {
RenderObject* renderer = it->first;
CompositeAnimation* compAnim = it->second;
if (!compAnim->suspended() && compAnim->animating()) {
animating = true;
compAnim->setAnimating(false);
setChanged(renderer->element());
}
}
m_frame->document()->updateRendering();
updateSWAnimationTimer();
}
bool AnimationControllerPrivate::isAnimatingPropertyOnRenderer(RenderObject* obj, int property) const
{
CompositeAnimation* animation = m_compositeAnimations.get(obj);
if (!animation) return false;
return animation->isAnimatingProperty(property);
}
void AnimationControllerPrivate::suspendAnimations(Document* document)
{
RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin();
it != animationsEnd; ++it) {
RenderObject* renderer = it->first;
CompositeAnimation* compAnim = it->second;
if (renderer->document() == document)
compAnim->suspendAnimations();
}
updateSWAnimationTimer();
}
void AnimationControllerPrivate::resumeAnimations(Document* document)
{
RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin();
it != animationsEnd; ++it) {
RenderObject* renderer = it->first;
CompositeAnimation* compAnim = it->second;
if (renderer->document() == document)
compAnim->resumeAnimations();
}
updateSWAnimationTimer();
}
#pragma mark -
void ImplicitAnimation::animate(CompositeAnimation* animation, RenderObject* renderer, const RenderStyle* currentStyle,
const RenderStyle* targetStyle, RenderStyle*& ioAnimatedStyle)
{
if (!running() || !m_object->document()->loadComplete())
return;
if (isnew() && AnimationControllerPrivate::propertiesEqual(m_property, currentStyle, targetStyle))
return;
if (postactive()) {
updateStateMachine(STATE_INPUT_MAKE_NEW, -1);
reset(renderer);
if (!ioAnimatedStyle)
ioAnimatedStyle = const_cast<RenderStyle*>(targetStyle);
return;
}
if (isnew() || !m_toStyle || !AnimationControllerPrivate::propertiesEqual(m_property, m_toStyle, targetStyle)) {
if (!isnew() && m_fromStyle && (m_property == CSS_PROP_OPACITY || m_property == cAnimateAll)) {
double prog = progress(1, 0);
AnimationControllerPrivate::blendProperties(CSS_PROP_OPACITY, const_cast<RenderStyle*>(currentStyle), m_fromStyle, m_toStyle, prog);
}
reset(renderer, currentStyle, targetStyle);
}
if (!ioAnimatedStyle)
ioAnimatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle);
double prog = progress(1, 0);
bool needsSWAnim = AnimationControllerPrivate::blendProperties(m_property, ioAnimatedStyle, m_fromStyle, m_toStyle, prog);
if (needsSWAnim)
setAnimating();
}
bool ImplicitAnimation::startAnimation(double beginTime)
{
return m_object ? m_object->startTransition(beginTime, m_property, m_fromStyle, m_toStyle) : false;
}
void ImplicitAnimation::endAnimation(bool reset)
{
if (m_object)
m_object->transitionFinished(m_property);
}
void ImplicitAnimation::onAnimationEnd(double inElapsedTime)
{
if (!sendTransitionEvent(EventNames::webkitTransitionEndEvent, inElapsedTime)) {
endAnimation(true);
}
}
bool ImplicitAnimation::sendTransitionEvent(const AtomicString& inEventType, double inElapsedTime)
{
if (inEventType == EventNames::webkitTransitionEndEvent) {
Document::ListenerType listenerType = Document::TRANSITIONEND_LISTENER;
if (shouldSendEventForListener(listenerType)) {
HTMLElement* element = elementForEventDispatch();
String propertyName;
if (m_property == cAnimateAll) {
propertyName = String("");
}
else {
propertyName = String(getPropertyName((CSSPropertyID)m_property));
}
if (element) {
m_waitingForEndEvent = true;
m_animationEventDispatcher.startTimer(element, propertyName, m_property, true, inEventType, inElapsedTime);
return true; }
}
}
return false; }
void ImplicitAnimation::reset(RenderObject* renderer, const RenderStyle* from , const RenderStyle* to )
{
ASSERT((!m_toStyle && !to) || m_toStyle != to);
ASSERT((!m_fromStyle && !from) || m_fromStyle != from);
if (m_fromStyle)
m_fromStyle->deref(renderer->renderArena());
if (m_toStyle)
m_toStyle->deref(renderer->renderArena());
m_fromStyle = const_cast<RenderStyle*>(from); if (m_fromStyle)
m_fromStyle->ref();
m_toStyle = const_cast<RenderStyle*>(to); if (m_toStyle)
m_toStyle->ref();
if (from && to)
updateStateMachine(STATE_INPUT_RESTART_ANIMATION, -1);
}
void ImplicitAnimation::setOverridden(bool b)
{
if (b != m_overridden) {
m_overridden = b;
updateStateMachine(m_overridden ? STATE_INPUT_PAUSE_OVERRIDE : STATE_INPUT_RESUME_OVERRIDE, -1);
}
}
bool ImplicitAnimation::affectsProperty(int property) const
{
return m_property == property ||
(m_property == cAnimateAll && !AnimationControllerPrivate::propertiesEqual(property, m_fromStyle, m_toStyle));
}
#pragma mark -
void KeyframeAnimation::animate(CompositeAnimation* animation, RenderObject* renderer, const RenderStyle* currentStyle,
const RenderStyle* targetStyle, RenderStyle*& ioAnimatedStyle)
{
if (isnew() && m_animation->animationPlayState() == AnimPlayStatePlaying && m_object->document()->loadComplete())
updateStateMachine(STATE_INPUT_START_ANIMATION, -1);
if (postactive()) {
if (!ioAnimatedStyle)
ioAnimatedStyle = const_cast<RenderStyle*>(targetStyle);
return;
}
if (waitingToStart() && m_animation->animationDelay() > 0)
return;
double elapsedTime = (m_startTime > 0) ? ((running() ? currentTime() : m_pauseTime) - m_startTime) : 0;
if (elapsedTime < 0)
elapsedTime = 0;
double t = m_animation->transitionDuration() ? (elapsedTime / m_animation->transitionDuration()) : 1;
int i = (int) t;
t -= i;
if (m_animation->animationDirection() && (i & 1))
t = 1 - t;
RenderStyle* fromStyle = 0;
RenderStyle* toStyle = 0;
double scale = 1;
double offset = 0;
if (m_keyframes.get()) {
Vector<KeyframeValue>::const_iterator end = m_keyframes->endKeyframes();
for (Vector<KeyframeValue>::const_iterator it = m_keyframes->beginKeyframes(); it != end; ++it) {
if (t < it->key) {
if (!fromStyle)
break;
scale = 1 / (it->key - offset);
toStyle = const_cast<RenderStyle*>(&(it->style));
break;
}
offset = it->key;
fromStyle = const_cast<RenderStyle*>(&(it->style));
}
}
if (!fromStyle || !toStyle) {
updateStateMachine(STATE_INPUT_END_ANIMATION, -1);
return;
}
if (!ioAnimatedStyle)
ioAnimatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle);
double prog = progress(scale, offset);
HashSet<int>::const_iterator end = m_keyframes->endProperties();
for (HashSet<int>::const_iterator it = m_keyframes->beginProperties(); it != end; ++it) {
bool needsSWAnim = AnimationControllerPrivate::blendProperties(*it, ioAnimatedStyle, fromStyle, toStyle, prog);
if (needsSWAnim)
setAnimating();
}
}
bool KeyframeAnimation::startAnimation(double beginTime)
{
return m_object ? m_object->startAnimation(beginTime, m_index) : false;
}
void KeyframeAnimation::endAnimation(bool reset)
{
if (m_object) {
m_object->animationFinished(m_name, 0, reset);
setChanged(m_object->element());
}
}
void KeyframeAnimation::onAnimationStart(double inElapsedTime)
{
sendAnimationEvent(EventNames::webkitAnimationStartEvent, inElapsedTime);
}
void KeyframeAnimation::onAnimationIteration(double inElapsedTime)
{
sendAnimationEvent(EventNames::webkitAnimationIterationEvent, inElapsedTime);
}
void KeyframeAnimation::onAnimationEnd(double inElapsedTime)
{
if (!sendAnimationEvent(EventNames::webkitAnimationEndEvent, inElapsedTime)) {
endAnimation(true);
}
}
bool KeyframeAnimation::sendAnimationEvent(const AtomicString& inEventType, double inElapsedTime)
{
Document::ListenerType listenerType;
if (inEventType == EventNames::webkitAnimationIterationEvent)
listenerType = Document::ANIMATIONITERATION_LISTENER;
else if (inEventType == EventNames::webkitAnimationEndEvent)
listenerType = Document::ANIMATIONEND_LISTENER;
else
listenerType = Document::ANIMATIONSTART_LISTENER;
if (shouldSendEventForListener(listenerType)) {
HTMLElement* element = elementForEventDispatch();
if (element) {
m_waitingForEndEvent = true;
m_animationEventDispatcher.startTimer(element, m_name, -1, true, inEventType, inElapsedTime);
return true; }
}
return false; }
void KeyframeAnimation::overrideAnimations()
{
HashSet<int>::const_iterator end = m_keyframes->endProperties();
for (HashSet<int>::const_iterator it = m_keyframes->beginProperties(); it != end; ++it)
compositeAnimation()->overrideImplicitAnimations(*it);
}
void KeyframeAnimation::resumeOverriddenAnimations()
{
HashSet<int>::const_iterator end = m_keyframes->endProperties();
for (HashSet<int>::const_iterator it = m_keyframes->beginProperties(); it != end; ++it)
compositeAnimation()->resumeOverriddenImplicitAnimations(*it);
}
bool KeyframeAnimation::affectsProperty(int property) const
{
HashSet<int>::const_iterator end = m_keyframes->endProperties();
for (HashSet<int>::const_iterator it = m_keyframes->beginProperties(); it != end; ++it) {
if ((*it) == property)
return true;
}
return false;
}
#pragma mark -
AnimationController::AnimationController(Frame* frame)
: m_data(new AnimationControllerPrivate(frame))
{
}
AnimationController::~AnimationController()
{
delete m_data;
}
void AnimationController::cancelAnimations(RenderObject* renderer)
{
if (!m_data->hasAnimations())
return;
if (m_data->clear(renderer))
setChanged(renderer->element());
}
RenderStyle* AnimationController::updateAnimations(RenderObject* renderer, RenderStyle* newStyle)
{
if (!renderer->document() || renderer->document()->inPageCache())
return newStyle;
RenderStyle* oldStyle = renderer->style();
if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) &&
(!newStyle->animations() && !newStyle->transitions()))
return newStyle;
RenderStyle* blendedStyle = newStyle;
ASSERT(renderer->element());
CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer);
blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle);
m_data->updateSWAnimationTimer();
if (blendedStyle != newStyle) {
if (blendedStyle->hasAutoZIndex() && ((blendedStyle->opacity() < 1.0f || blendedStyle->hasTransformOrPerspective())))
blendedStyle->setZIndex(0);
}
return blendedStyle;
}
void AnimationController::setAnimationStartTime(RenderObject* obj, double t)
{
CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(obj);
rendererAnimations->setAnimationStartTime(t);
}
void AnimationController::setTransitionStartTime(RenderObject* obj, int property, double t)
{
CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(obj);
rendererAnimations->setTransitionStartTime(property, t);
}
bool AnimationController::isAnimatingPropertyOnRenderer(RenderObject* obj, int property) const
{
return m_data->isAnimatingPropertyOnRenderer(obj, property);
}
void AnimationController::suspendAnimations(Document* document)
{
#ifdef DEBUG_STATE_MACHINE
fprintf(stderr, "AnimationController %p suspendAnimations for document %p\n", this, document);
#endif
m_data->suspendAnimations(document);
}
void AnimationController::resumeAnimations(Document* document)
{
#ifdef DEBUG_STATE_MACHINE
fprintf(stderr, "AnimationController %p resumeAnimations for document %p\n", this, document);
#endif
m_data->resumeAnimations(document);
}
void AnimationController::startUpdateRenderingDispatcher()
{
m_data->startUpdateRenderingDispatcher();
}
void AnimationController::styleIsSetup()
{
m_data->styleIsSetup();
}
void CompositeAnimation::setWaitingForStyleIsSetup(bool waiting)
{
if (waiting)
m_numStyleIsSetupWaiters++;
else
m_numStyleIsSetupWaiters--;
m_animationController->setWaitingForStyleIsSetup(waiting);
}
}