#include "core/animation/Animation.h"
#include "core/animation/DocumentTimeline.h"
+#include "core/events/AnimationPlayerEvent.h"
+#include "core/frame/UseCounter.h"
namespace WebCore {
, m_paused(false)
, m_held(false)
, m_isPausedForTesting(false)
- , m_outdated(false)
+ , m_outdated(true)
+ , m_finished(false)
{
if (m_content) {
if (m_content->player())
double AnimationPlayer::sourceEnd() const
{
- return m_content ? m_content->endTime() : 0;
+ return m_content ? m_content->endTimeInternal() : 0;
}
bool AnimationPlayer::limited(double currentTime) const
} else {
m_holdTime = nullValue();
m_storedTimeLag = currentTimeWithoutLag() - newCurrentTime;
+ m_finished = false;
setOutdated();
}
}
double AnimationPlayer::currentTime()
{
+ return currentTimeInternal() * 1000;
+}
+
+double AnimationPlayer::currentTimeInternal()
+{
updateCurrentTimingState();
if (m_held)
return m_holdTime;
void AnimationPlayer::setCurrentTime(double newCurrentTime)
{
+ setCurrentTimeInternal(newCurrentTime / 1000);
+}
+
+void AnimationPlayer::setCurrentTimeInternal(double newCurrentTime)
+{
if (!std::isfinite(newCurrentTime))
return;
updateTimingState(newCurrentTime);
+ cancelAnimationOnCompositor();
+ schedulePendingAnimationOnCompositor();
}
-void AnimationPlayer::setStartTime(double newStartTime)
+void AnimationPlayer::setStartTimeInternal(double newStartTime, bool isUpdateFromCompositor)
{
+ ASSERT(!isUpdateFromCompositor || !hasStartTime());
+
if (!std::isfinite(newStartTime))
return;
+ if (newStartTime == m_startTime)
+ return;
updateCurrentTimingState(); // Update the value of held
+ bool hadStartTime = hasStartTime();
+ double previousCurrentTime = currentTimeInternal();
m_startTime = newStartTime;
m_sortInfo.m_startTime = newStartTime;
- if (m_held)
- return;
updateCurrentTimingState();
- setOutdated();
+ if (previousCurrentTime != currentTimeInternal()) {
+ setOutdated();
+ } else if (!hadStartTime && m_timeline) {
+ // Even though this player is not outdated, time to effect change is
+ // infinity until start time is set.
+ m_timeline->wake();
+ }
+ if (!isUpdateFromCompositor) {
+ cancelAnimationOnCompositor();
+ schedulePendingAnimationOnCompositor();
+ }
}
void AnimationPlayer::setSource(TimedItem* newSource)
{
if (m_content == newSource)
return;
- double storedCurrentTime = currentTime();
+ cancelAnimationOnCompositor();
+ double storedCurrentTime = currentTimeInternal();
if (m_content)
m_content->detach();
m_content = newSource;
newSource->attach(this);
}
updateTimingState(storedCurrentTime);
+ schedulePendingAnimationOnCompositor();
}
void AnimationPlayer::pause()
if (m_paused)
return;
m_paused = true;
- updateTimingState(currentTime());
- // FIXME: resume compositor animation rather than pull back to main-thread
+ updateTimingState(currentTimeInternal());
cancelAnimationOnCompositor();
}
if (!m_paused)
return;
m_paused = false;
- updateTimingState(currentTime());
+ updateTimingState(currentTimeInternal());
+ schedulePendingAnimationOnCompositor();
}
void AnimationPlayer::play()
{
+ cancelAnimationOnCompositor();
+ // Note, unpause schedules pending animation on compositor if necessary.
unpause();
if (!m_content)
return;
- double currentTime = this->currentTime();
+ double currentTime = this->currentTimeInternal();
if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd()))
- setCurrentTime(0);
+ setCurrentTimeInternal(0);
else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd()))
- setCurrentTime(sourceEnd());
+ setCurrentTimeInternal(sourceEnd());
+ m_finished = false;
}
void AnimationPlayer::reverse()
if (!m_playbackRate)
return;
if (m_content) {
- if (m_playbackRate > 0 && currentTime() > sourceEnd())
- setCurrentTime(sourceEnd());
- else if (m_playbackRate < 0 && currentTime() < 0)
- setCurrentTime(0);
+ if (m_playbackRate > 0 && currentTimeInternal() > sourceEnd())
+ setCurrentTimeInternal(sourceEnd());
+ else if (m_playbackRate < 0 && currentTimeInternal() < 0)
+ setCurrentTimeInternal(0);
}
setPlaybackRate(-m_playbackRate);
+ cancelAnimationOnCompositor();
+ // Note, unpause schedules pending animation on compositor if necessary.
unpause();
}
if (!m_playbackRate)
return;
if (m_playbackRate < 0) {
- setCurrentTime(0);
+ setCurrentTimeInternal(0);
} else {
if (sourceEnd() == std::numeric_limits<double>::infinity()) {
exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has source content whose end time is infinity.");
return;
}
- setCurrentTime(sourceEnd());
+ setCurrentTimeInternal(sourceEnd());
}
ASSERT(finished());
+ cancelAnimationOnCompositor();
+}
+
+const AtomicString& AnimationPlayer::interfaceName() const
+{
+ return EventTargetNames::AnimationPlayer;
+}
+
+ExecutionContext* AnimationPlayer::executionContext() const
+{
+ if (m_timeline) {
+ if (Document* document = m_timeline->document())
+ return document->contextDocument().get();
+ }
+ return 0;
}
void AnimationPlayer::setPlaybackRate(double playbackRate)
{
if (!std::isfinite(playbackRate))
return;
- double storedCurrentTime = currentTime();
+ double storedCurrentTime = currentTimeInternal();
+ if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && playbackRate <= 0))
+ m_finished = false;
m_playbackRate = playbackRate;
updateTimingState(storedCurrentTime);
+ cancelAnimationOnCompositor();
+ schedulePendingAnimationOnCompositor();
}
void AnimationPlayer::setOutdated()
m_timeline->setOutdatedAnimationPlayer(this);
}
+bool AnimationPlayer::canStartAnimationOnCompositor()
+{
+ // FIXME: Need compositor support for playback rate != 1.
+ if (playbackRate() != 1)
+ return false;
+
+ return m_timeline && m_content && m_content->isAnimation() && !m_held;
+}
+
bool AnimationPlayer::maybeStartAnimationOnCompositor()
{
- // FIXME: Support starting compositor animations that have a fixed
- // start time.
- ASSERT(!hasStartTime());
- if (!m_content || !m_content->isAnimation() || paused())
+ if (!canStartAnimationOnCompositor())
return false;
- return toAnimation(m_content.get())->maybeStartAnimationOnCompositor();
+ return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(timeline()->zeroTime() + startTimeInternal() + timeLagInternal());
+}
+
+void AnimationPlayer::schedulePendingAnimationOnCompositor()
+{
+ ASSERT(!hasActiveAnimationsOnCompositor());
+
+ if (canStartAnimationOnCompositor())
+ timeline()->document()->compositorPendingAnimations().add(this);
}
bool AnimationPlayer::hasActiveAnimationsOnCompositor()
{
if (!m_content || !m_content->isAnimation())
return false;
+
return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor();
}
toAnimation(m_content.get())->cancelAnimationOnCompositor();
}
-bool AnimationPlayer::update()
+bool AnimationPlayer::update(TimingUpdateReason reason)
{
m_outdated = false;
- if (!m_timeline || !m_content)
+ if (!m_timeline)
return false;
- double inheritedTime = isNull(m_timeline->currentTime()) ? nullValue() : currentTime();
- m_content->updateInheritedTime(inheritedTime);
+ if (m_content) {
+ double inheritedTime = isNull(m_timeline->currentTimeInternal()) ? nullValue() : currentTimeInternal();
+ m_content->updateInheritedTime(inheritedTime, reason);
+ }
+ if (finished() && !m_finished) {
+ if (reason == TimingUpdateForAnimationFrame && hasStartTime()) {
+ const AtomicString& eventType = EventTypeNames::finish;
+ if (executionContext() && hasEventListeners(eventType)) {
+ RefPtrWillBeRawPtr<AnimationPlayerEvent> event = AnimationPlayerEvent::create(eventType, currentTime(), timeline()->currentTime());
+ event->setTarget(this);
+ event->setCurrentTarget(this);
+ m_timeline->document()->enqueueAnimationFrameEvent(event.release());
+ }
+ m_finished = true;
+ }
+ }
ASSERT(!m_outdated);
- return m_content->isCurrent() || m_content->isInEffect();
+ return !m_finished || !finished();
}
double AnimationPlayer::timeToEffectChange()
{
ASSERT(!m_outdated);
- if (!m_content || !m_playbackRate)
+ if (m_held || !hasStartTime())
return std::numeric_limits<double>::infinity();
+ if (!m_content)
+ return -currentTimeInternal() / m_playbackRate;
if (m_playbackRate > 0)
return m_content->timeToForwardsEffectChange() / m_playbackRate;
- return m_content->timeToReverseEffectChange() / std::abs(m_playbackRate);
+ return m_content->timeToReverseEffectChange() / -m_playbackRate;
}
void AnimationPlayer::cancel()
{
- if (!m_content)
- return;
-
- ASSERT(m_content->player() == this);
- m_content->detach();
- m_content = nullptr;
+ setSource(0);
}
bool AnimationPlayer::SortInfo::operator<(const SortInfo& other) const
return m_sequenceNumber < other.m_sequenceNumber;
}
+bool AnimationPlayer::canFree() const
+{
+ ASSERT(m_content);
+ return hasOneRef() && m_content->isAnimation() && m_content->hasOneRef();
+}
+
+bool AnimationPlayer::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
+{
+ if (eventType == EventTypeNames::finish)
+ UseCounter::count(executionContext(), UseCounter::AnimationPlayerFinishEvent);
+ return EventTargetWithInlineData::addEventListener(eventType, listener, useCapture);
+}
+
void AnimationPlayer::pauseForTesting(double pauseTime)
{
RELEASE_ASSERT(!paused());
updateTimingState(pauseTime);
if (!m_isPausedForTesting && hasActiveAnimationsOnCompositor())
- toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTime());
+ toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTimeInternal());
m_isPausedForTesting = true;
pause();
}