namespace WebCore {
-PassRefPtr<Animation> Animation::create(PassRefPtr<Element> target, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate)
+PassRefPtr<Animation> Animation::create(Element* target, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate)
{
return adoptRef(new Animation(target, effect, timing, priority, eventDelegate));
}
PassRefPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, const Dictionary& timingInputDictionary, ExceptionState& exceptionState)
{
ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
- UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectObjectTiming);
+ if (element)
+ UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectObjectTiming);
return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), TimingInput::convert(timingInputDictionary));
}
PassRefPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, double duration, ExceptionState& exceptionState)
{
ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
- UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectDoubleTiming);
+ if (element)
+ UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectDoubleTiming);
return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), TimingInput::convert(duration));
}
PassRefPtr<Animation> Animation::create(Element* element, const Vector<Dictionary>& keyframeDictionaryVector, ExceptionState& exceptionState)
{
ASSERT(RuntimeEnabledFeatures::webAnimationsAPIEnabled());
- UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectNoTiming);
+ if (element)
+ UseCounter::count(element->document(), UseCounter::AnimationConstructorKeyframeListEffectNoTiming);
return create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), Timing());
}
-Animation::Animation(PassRefPtr<Element> target, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate)
+Animation::Animation(Element* target, PassRefPtrWillBeRawPtr<AnimationEffect> effect, const Timing& timing, Priority priority, PassOwnPtr<EventDelegate> eventDelegate)
: TimedItem(timing, eventDelegate)
, m_target(target)
, m_effect(effect)
- , m_activeInAnimationStack(false)
+ , m_sampledEffect(0)
, m_priority(priority)
{
+ if (m_target)
+ m_target->ensureActiveAnimations().addAnimation(this);
}
-void Animation::didAttach()
+Animation::~Animation()
{
if (m_target)
- m_target->ensureActiveAnimations().players().add(player());
+ m_target->activeAnimations()->notifyAnimationDestroyed(this);
+}
+
+void Animation::didAttach()
+{
+ if (m_target) {
+ m_target->ensureActiveAnimations().addPlayer(player());
+ m_target->setNeedsAnimationStyleRecalc();
+ }
}
void Animation::willDetach()
{
if (m_target)
- m_target->activeAnimations()->players().remove(player());
- if (m_activeInAnimationStack)
+ m_target->activeAnimations()->removePlayer(player());
+ if (m_sampledEffect)
clearEffects();
}
+void Animation::specifiedTimingChanged()
+{
+ cancelAnimationOnCompositor();
+ if (player()) {
+ // FIXME: Needs to consider groups when added.
+ ASSERT(player()->source() == this);
+ player()->schedulePendingAnimationOnCompositor();
+ }
+}
+
static AnimationStack& ensureAnimationStack(Element* element)
{
return element->ensureActiveAnimations().defaultStack();
}
-void Animation::applyEffects(bool previouslyInEffect)
+void Animation::applyEffects()
{
ASSERT(isInEffect());
+ ASSERT(player());
if (!m_target || !m_effect)
return;
- if (player() && !previouslyInEffect) {
- ensureAnimationStack(m_target.get()).add(this);
- m_activeInAnimationStack = true;
- }
-
double iteration = currentIteration();
ASSERT(iteration >= 0);
// FIXME: Handle iteration values which overflow int.
- m_activeInterpolations = m_effect->sample(static_cast<int>(iteration), timeFraction(), duration());
- if (player())
- m_target->setNeedsAnimationStyleRecalc();
+ OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation> > > interpolations = m_effect->sample(static_cast<int>(iteration), timeFraction(), iterationDuration());
+ if (m_sampledEffect) {
+ m_sampledEffect->setInterpolations(interpolations.release());
+ } else if (!interpolations->isEmpty()) {
+ OwnPtr<SampledEffect> sampledEffect = SampledEffect::create(this, interpolations.release());
+ m_sampledEffect = sampledEffect.get();
+ ensureAnimationStack(m_target).add(sampledEffect.release());
+ } else {
+ return;
+ }
+
+ m_target->setNeedsAnimationStyleRecalc();
}
void Animation::clearEffects()
{
ASSERT(player());
- ASSERT(m_activeInAnimationStack);
- ensureAnimationStack(m_target.get()).remove(this);
-
- {
- // FIXME: clearEffects is called from withins style recalc.
- // This queries compositingState, which is not necessarily up to date.
- // https://code.google.com/p/chromium/issues/detail?id=339847
- DisableCompositingQueryAsserts disabler;
- cancelAnimationOnCompositor();
- }
+ ASSERT(m_sampledEffect);
- m_activeInAnimationStack = false;
- m_activeInterpolations.clear();
+ m_sampledEffect->clear();
+ m_sampledEffect = 0;
+ cancelAnimationOnCompositor();
m_target->setNeedsAnimationStyleRecalc();
invalidate();
}
if (!m_effect)
return;
if (isInEffect())
- const_cast<Animation*>(this)->applyEffects(m_activeInAnimationStack);
- else if (m_activeInAnimationStack)
+ const_cast<Animation*>(this)->applyEffects();
+ else if (m_sampledEffect)
const_cast<Animation*>(this)->clearEffects();
}
double Animation::calculateTimeToEffectChange(bool forwards, double localTime, double timeToNextIteration) const
{
- const double start = startTime() + specifiedTiming().startDelay;
- const double end = start + activeDuration();
+ const double start = startTimeInternal() + specifiedTiming().startDelay;
+ const double end = start + activeDurationInternal();
switch (phase()) {
case PhaseBefore:
if (forwards && hasActiveAnimationsOnCompositor()) {
ASSERT(specifiedTiming().playbackRate == 1);
// Need service to apply fill / fire events.
- return std::min(end - localTime, timeToNextIteration);
+ const double timeToEnd = end - localTime;
+ if (hasEvents()) {
+ return std::min(timeToEnd, timeToNextIteration);
+ } else {
+ return timeToEnd;
+ }
}
return 0;
case PhaseAfter:
}
}
+void Animation::notifySampledEffectRemovedFromAnimationStack()
+{
+ ASSERT(m_sampledEffect);
+ m_sampledEffect = 0;
+}
+
+void Animation::notifyElementDestroyed()
+{
+ // If our player is kept alive just by the sampledEffect, we might get our
+ // destructor called when we call SampledEffect::clear(), so we need to
+ // clear m_sampledEffect first.
+ m_target = 0;
+ clearEventDelegate();
+ SampledEffect* sampledEffect = m_sampledEffect;
+ m_sampledEffect = 0;
+ if (sampledEffect)
+ sampledEffect->clear();
+}
+
bool Animation::isCandidateForAnimationOnCompositor() const
{
if (!effect() || !m_target)
return CompositorAnimations::instance()->isCandidateForAnimationOnCompositor(specifiedTiming(), *effect());
}
-bool Animation::maybeStartAnimationOnCompositor()
+bool Animation::maybeStartAnimationOnCompositor(double startTime)
{
ASSERT(!hasActiveAnimationsOnCompositor());
if (!isCandidateForAnimationOnCompositor())
return false;
- if (!CompositorAnimations::instance()->canStartAnimationOnCompositor(*m_target.get()))
+ if (!CompositorAnimations::instance()->canStartAnimationOnCompositor(*m_target))
return false;
- if (!CompositorAnimations::instance()->startAnimationOnCompositor(*m_target.get(), specifiedTiming(), *effect(), m_compositorAnimationIds))
+ if (!CompositorAnimations::instance()->startAnimationOnCompositor(*m_target, startTime, specifiedTiming(), *effect(), m_compositorAnimationIds))
return false;
ASSERT(!m_compositorAnimationIds.isEmpty());
return true;
void Animation::cancelAnimationOnCompositor()
{
+ // FIXME: cancelAnimationOnCompositor is called from withins style recalc.
+ // This queries compositingState, which is not necessarily up to date.
+ // https://code.google.com/p/chromium/issues/detail?id=339847
+ DisableCompositingQueryAsserts disabler;
if (!hasActiveAnimationsOnCompositor())
return;
if (!m_target || !m_target->renderer())
return;
for (size_t i = 0; i < m_compositorAnimationIds.size(); ++i)
- CompositorAnimations::instance()->cancelAnimationOnCompositor(*m_target.get(), m_compositorAnimationIds[i]);
+ CompositorAnimations::instance()->cancelAnimationOnCompositor(*m_target, m_compositorAnimationIds[i]);
m_compositorAnimationIds.clear();
}
if (!m_target || !m_target->renderer())
return;
for (size_t i = 0; i < m_compositorAnimationIds.size(); ++i)
- CompositorAnimations::instance()->pauseAnimationForTestingOnCompositor(*m_target.get(), m_compositorAnimationIds[i], pauseTime);
+ CompositorAnimations::instance()->pauseAnimationForTestingOnCompositor(*m_target, m_compositorAnimationIds[i], pauseTime);
}
} // namespace WebCore