2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "core/animation/AnimationPlayer.h"
34 #include "core/animation/Animation.h"
35 #include "core/animation/AnimationTimeline.h"
36 #include "core/dom/Document.h"
37 #include "core/events/AnimationPlayerEvent.h"
38 #include "core/frame/UseCounter.h"
39 #include "platform/TraceEvent.h"
40 #include "wtf/MathExtras.h"
46 static unsigned nextSequenceNumber()
48 static unsigned next = 0;
54 PassRefPtrWillBeRawPtr<AnimationPlayer> AnimationPlayer::create(ExecutionContext* executionContext, AnimationTimeline& timeline, AnimationNode* content)
56 RefPtrWillBeRawPtr<AnimationPlayer> player = adoptRefWillBeNoop(new AnimationPlayer(executionContext, timeline, content));
58 timeline.document()->compositorPendingAnimations().add(player.get());
59 player->suspendIfNeeded();
60 return player.release();
63 AnimationPlayer::AnimationPlayer(ExecutionContext* executionContext, AnimationTimeline& timeline, AnimationNode* content)
64 : ActiveDOMObject(executionContext)
67 , m_startTime(nullValue())
69 , m_sequenceNumber(nextSequenceNumber())
71 , m_timeline(&timeline)
74 , m_isPausedForTesting(false)
77 , m_compositorState(nullptr)
78 , m_compositorPending(true)
79 , m_currentTimePending(false)
82 if (m_content->player()) {
83 m_content->player()->cancel();
84 m_content->player()->setSource(0);
86 m_content->attach(this);
90 AnimationPlayer::~AnimationPlayer()
96 m_timeline->playerDestroyed(this);
100 double AnimationPlayer::sourceEnd() const
102 return m_content ? m_content->endTimeInternal() : 0;
105 bool AnimationPlayer::limited(double currentTime) const
107 return (m_playbackRate < 0 && currentTime <= 0) || (m_playbackRate > 0 && currentTime >= sourceEnd());
110 void AnimationPlayer::setCurrentTime(double newCurrentTime)
112 UseCounter::count(executionContext(), UseCounter::AnimationPlayerSetCurrentTime);
113 if (!std::isfinite(newCurrentTime))
116 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
118 m_currentTimePending = false;
119 setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand);
122 void AnimationPlayer::setCurrentTimeInternal(double newCurrentTime, TimingUpdateReason reason)
124 ASSERT(std::isfinite(newCurrentTime));
126 bool oldHeld = m_held;
127 bool outdated = false;
128 bool isLimited = limited(newCurrentTime);
129 m_held = m_paused || !m_playbackRate || isLimited || std::isnan(m_startTime);
131 if (!oldHeld || m_holdTime != newCurrentTime)
133 m_holdTime = newCurrentTime;
134 if (m_paused || !m_playbackRate) {
135 m_startTime = nullValue();
136 } else if (isLimited && std::isnan(m_startTime) && reason == TimingUpdateForAnimationFrame) {
137 m_startTime = calculateStartTime(newCurrentTime);
140 m_holdTime = nullValue();
141 m_startTime = calculateStartTime(newCurrentTime);
151 // Update timing to reflect updated animation clock due to tick
152 void AnimationPlayer::updateCurrentTimingState(TimingUpdateReason reason)
155 setCurrentTimeInternal(m_holdTime, reason);
158 if (!limited(calculateCurrentTime()))
161 m_holdTime = m_playbackRate < 0 ? 0 : sourceEnd();
164 double AnimationPlayer::startTime(bool& isNull) const
166 double result = startTime();
167 isNull = std::isnan(result);
171 double AnimationPlayer::startTime() const
173 UseCounter::count(executionContext(), UseCounter::AnimationPlayerGetStartTime);
174 return m_startTime * 1000;
177 double AnimationPlayer::currentTime(bool& isNull)
179 double result = currentTime();
180 isNull = std::isnan(result);
184 double AnimationPlayer::currentTime()
186 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
188 UseCounter::count(executionContext(), UseCounter::AnimationPlayerGetCurrentTime);
189 if (m_currentTimePending || playStateInternal() == Idle)
190 return std::numeric_limits<double>::quiet_NaN();
192 return currentTimeInternal() * 1000;
195 double AnimationPlayer::currentTimeInternal() const
197 double result = m_held ? m_holdTime : calculateCurrentTime();
199 const_cast<AnimationPlayer*>(this)->updateCurrentTimingState(TimingUpdateOnDemand);
200 ASSERT(result == (m_held ? m_holdTime : calculateCurrentTime()));
205 void AnimationPlayer::preCommit(bool startOnCompositor)
207 if (m_compositorState && m_compositorState->pendingAction == Start) {
208 // Still waiting for a start time.
212 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, DoNotSetCompositorPending);
214 bool softChange = m_compositorState && (paused() || m_compositorState->playbackRate != m_playbackRate);
215 bool hardChange = m_compositorState && (m_compositorState->sourceChanged || (m_compositorState->startTime != m_startTime && !std::isnan(m_compositorState->startTime) && !std::isnan(m_startTime)));
217 // FIXME: softChange && !hardChange should generate a Pause/ThenStart,
218 // not a Cancel, but we can't communicate these to the compositor yet.
220 bool changed = softChange || hardChange;
221 bool shouldCancel = (!playing() && m_compositorState) || changed;
222 bool shouldStart = playing() && (!m_compositorState || changed);
225 cancelAnimationOnCompositor();
226 m_compositorState = nullptr;
230 m_currentTimePending = false;
233 if (shouldStart && startOnCompositor && maybeStartAnimationOnCompositor()) {
234 m_compositorState = adoptPtr(new CompositorState(*this));
238 void AnimationPlayer::postCommit(double timelineTime)
240 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, DoNotSetCompositorPending);
242 m_compositorPending = false;
244 if (!m_compositorState || m_compositorState->pendingAction == None)
247 switch (m_compositorState->pendingAction) {
249 if (!std::isnan(m_compositorState->startTime)) {
250 ASSERT(m_startTime == m_compositorState->startTime);
251 m_compositorState->pendingAction = None;
256 ASSERT(std::isnan(m_startTime));
257 m_compositorState->pendingAction = None;
258 setCurrentTimeInternal((timelineTime - m_compositorState->startTime) * m_playbackRate, TimingUpdateForAnimationFrame);
259 m_currentTimePending = false;
262 ASSERT_NOT_REACHED();
266 void AnimationPlayer::notifyCompositorStartTime(double timelineTime)
268 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
270 if (m_compositorState) {
271 ASSERT(m_compositorState->pendingAction == Start);
272 ASSERT(std::isnan(m_compositorState->startTime));
274 double initialCompositorHoldTime = m_compositorState->holdTime;
275 m_compositorState->pendingAction = None;
276 m_compositorState->startTime = timelineTime + currentTimeInternal() / -m_playbackRate;
278 if (m_startTime == timelineTime) {
279 // The start time was set to the incoming compositor start time.
280 // Unlikely, but possible.
281 // FIXME: Depending on what changed above this might still be pending.
283 m_currentTimePending = false;
287 if (!std::isnan(m_startTime) || currentTimeInternal() != initialCompositorHoldTime) {
288 // A new start time or current time was set while starting.
289 setCompositorPending(true);
294 notifyStartTime(timelineTime);
297 void AnimationPlayer::notifyStartTime(double timelineTime)
300 ASSERT(std::isnan(m_startTime));
303 if (m_playbackRate == 0) {
304 setStartTimeInternal(timelineTime);
306 setStartTimeInternal(timelineTime + currentTimeInternal() / -m_playbackRate);
309 // FIXME: This avoids marking this player as outdated needlessly when a start time
310 // is notified, but we should refactor how outdating works to avoid this.
313 m_currentTimePending = false;
317 double AnimationPlayer::calculateStartTime(double currentTime) const
319 return m_timeline->effectiveTime() - currentTime / m_playbackRate;
322 double AnimationPlayer::calculateCurrentTime() const
325 if (isNull(m_startTime) || !m_timeline)
327 return (m_timeline->effectiveTime() - m_startTime) * m_playbackRate;
330 void AnimationPlayer::setStartTime(double startTime)
332 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
334 UseCounter::count(executionContext(), UseCounter::AnimationPlayerSetStartTime);
335 if (m_paused || playStateInternal() == Idle)
337 if (!std::isfinite(startTime))
339 if (startTime == m_startTime)
342 m_currentTimePending = false;
343 setStartTimeInternal(startTime / 1000);
346 void AnimationPlayer::setStartTimeInternal(double newStartTime)
349 ASSERT(std::isfinite(newStartTime));
350 ASSERT(newStartTime != m_startTime);
352 bool hadStartTime = hasStartTime();
353 double previousCurrentTime = currentTimeInternal();
354 m_startTime = newStartTime;
355 if (m_held && m_playbackRate) {
356 // If held, the start time would still be derrived from the hold time.
357 // Force a new, limited, current time.
359 double currentTime = calculateCurrentTime();
360 if (m_playbackRate > 0 && currentTime > sourceEnd()) {
361 currentTime = sourceEnd();
362 } else if (m_playbackRate < 0 && currentTime < 0) {
365 setCurrentTimeInternal(currentTime, TimingUpdateOnDemand);
367 updateCurrentTimingState(TimingUpdateOnDemand);
368 double newCurrentTime = currentTimeInternal();
370 if (previousCurrentTime != newCurrentTime) {
372 } else if (!hadStartTime && m_timeline) {
373 // Even though this player is not outdated, time to effect change is
374 // infinity until start time is set.
379 void AnimationPlayer::setSource(AnimationNode* newSource)
381 if (m_content == newSource)
384 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, SetCompositorPendingWithSourceChanged);
386 double storedCurrentTime = currentTimeInternal();
389 m_content = newSource;
391 // FIXME: This logic needs to be updated once groups are implemented
392 if (newSource->player()) {
393 newSource->player()->cancel();
394 newSource->player()->setSource(0);
396 newSource->attach(this);
399 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand);
402 const char* AnimationPlayer::playStateString(AnimationPlayState playState)
416 ASSERT_NOT_REACHED();
421 AnimationPlayer::AnimationPlayState AnimationPlayer::playStateInternal() const
426 AnimationPlayer::AnimationPlayState AnimationPlayer::calculatePlayState()
428 if (m_playState == Idle)
430 if (m_currentTimePending || (isNull(m_startTime) && !m_paused && m_playbackRate != 0))
439 void AnimationPlayer::pause()
444 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
447 m_currentTimePending = true;
450 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand);
453 void AnimationPlayer::unpause()
458 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
460 m_currentTimePending = true;
464 void AnimationPlayer::unpauseInternal()
469 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand);
472 void AnimationPlayer::play()
474 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
477 m_startTime = nullValue();
483 double currentTime = this->currentTimeInternal();
484 if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd()))
485 setCurrentTimeInternal(0, TimingUpdateOnDemand);
486 else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd()))
487 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand);
491 void AnimationPlayer::reverse()
493 if (!m_playbackRate) {
498 setPlaybackRateInternal(-m_playbackRate);
502 void AnimationPlayer::finish(ExceptionState& exceptionState)
504 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
506 if (!m_playbackRate || playStateInternal() == Idle) {
509 if (m_playbackRate > 0 && sourceEnd() == std::numeric_limits<double>::infinity()) {
510 exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has source content whose end time is infinity.");
516 double newCurrentTime = m_playbackRate < 0 ? 0 : sourceEnd();
517 setCurrentTimeInternal(newCurrentTime, TimingUpdateOnDemand);
519 m_startTime = calculateStartTime(newCurrentTime);
522 m_currentTimePending = false;
526 const AtomicString& AnimationPlayer::interfaceName() const
528 return EventTargetNames::AnimationPlayer;
531 ExecutionContext* AnimationPlayer::executionContext() const
533 return ActiveDOMObject::executionContext();
536 bool AnimationPlayer::hasPendingActivity() const
538 return m_pendingFinishedEvent || (!m_finished && hasEventListeners(EventTypeNames::finish));
541 void AnimationPlayer::stop()
543 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
546 m_pendingFinishedEvent = nullptr;
549 bool AnimationPlayer::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
551 if (m_pendingFinishedEvent == event)
552 m_pendingFinishedEvent = nullptr;
553 return EventTargetWithInlineData::dispatchEvent(event);
556 double AnimationPlayer::playbackRate() const
558 UseCounter::count(executionContext(), UseCounter::AnimationPlayerGetPlaybackRate);
559 return m_playbackRate;
562 void AnimationPlayer::setPlaybackRate(double playbackRate)
564 UseCounter::count(executionContext(), UseCounter::AnimationPlayerSetPlaybackRate);
565 if (!std::isfinite(playbackRate))
567 if (playbackRate == m_playbackRate)
570 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
572 setPlaybackRateInternal(playbackRate);
575 void AnimationPlayer::setPlaybackRateInternal(double playbackRate)
577 ASSERT(std::isfinite(playbackRate));
578 ASSERT(playbackRate != m_playbackRate);
580 if (!finished() && !paused() && hasStartTime())
581 m_currentTimePending = true;
583 double storedCurrentTime = currentTimeInternal();
584 if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && playbackRate <= 0))
587 m_playbackRate = playbackRate;
588 m_startTime = std::numeric_limits<double>::quiet_NaN();
589 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand);
592 void AnimationPlayer::setOutdated()
596 m_timeline->setOutdatedAnimationPlayer(this);
599 bool AnimationPlayer::canStartAnimationOnCompositor()
601 if (m_playbackRate == 0 || (std::isinf(sourceEnd()) && m_playbackRate < 0))
604 return m_timeline && m_content && m_content->isAnimation() && playing();
607 bool AnimationPlayer::maybeStartAnimationOnCompositor()
609 if (!canStartAnimationOnCompositor())
612 bool reversed = m_playbackRate < 0;
614 double startTime = timeline()->zeroTime() + startTimeInternal();
616 startTime -= sourceEnd() / fabs(m_playbackRate);
619 double timeOffset = 0;
620 if (std::isnan(startTime)) {
621 timeOffset = reversed ? sourceEnd() - currentTimeInternal() : currentTimeInternal();
622 timeOffset = timeOffset / fabs(m_playbackRate);
624 return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(startTime, timeOffset, m_playbackRate);
627 void AnimationPlayer::setCompositorPending(bool sourceChanged)
629 // FIXME: Animation could notify this directly?
630 if (!hasActiveAnimationsOnCompositor()) {
631 m_compositorState.release();
633 if (sourceChanged && m_compositorState) {
634 m_compositorState->sourceChanged = true;
636 if (m_compositorPending || m_isPausedForTesting) {
640 if (sourceChanged || !m_compositorState
641 || !playing() || m_compositorState->playbackRate != m_playbackRate
642 || m_compositorState->startTime != m_startTime) {
643 m_compositorPending = true;
644 timeline()->document()->compositorPendingAnimations().add(this);
648 bool AnimationPlayer::hasActiveAnimationsOnCompositor()
650 if (!m_content || !m_content->isAnimation())
653 return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor();
656 void AnimationPlayer::cancelAnimationOnCompositor()
658 if (hasActiveAnimationsOnCompositor())
659 toAnimation(m_content.get())->cancelAnimationOnCompositor();
662 bool AnimationPlayer::update(TimingUpdateReason reason)
667 PlayStateUpdateScope updateScope(*this, reason, DoNotSetCompositorPending);
670 bool idle = playStateInternal() == Idle;
673 double inheritedTime = idle || isNull(m_timeline->currentTimeInternal()) ? nullValue() : currentTimeInternal();
674 // Special case for end-exclusivity when playing backwards.
675 if (inheritedTime == 0 && m_playbackRate < 0)
677 m_content->updateInheritedTime(inheritedTime, reason);
680 if ((idle || finished()) && !m_finished) {
681 if (reason == TimingUpdateForAnimationFrame && (idle || hasStartTime())) {
682 const AtomicString& eventType = EventTypeNames::finish;
683 if (executionContext() && hasEventListeners(eventType)) {
684 double eventCurrentTime = currentTimeInternal() * 1000;
685 m_pendingFinishedEvent = AnimationPlayerEvent::create(eventType, eventCurrentTime, timeline()->currentTime());
686 m_pendingFinishedEvent->setTarget(this);
687 m_pendingFinishedEvent->setCurrentTarget(this);
688 m_timeline->document()->enqueueAnimationFrameEvent(m_pendingFinishedEvent);
697 double AnimationPlayer::timeToEffectChange()
700 if (m_held || !hasStartTime())
701 return std::numeric_limits<double>::infinity();
703 return -currentTimeInternal() / m_playbackRate;
704 if (m_playbackRate > 0)
705 return m_content->timeToForwardsEffectChange() / m_playbackRate;
706 return m_content->timeToReverseEffectChange() / -m_playbackRate;
709 void AnimationPlayer::cancel()
711 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
713 if (playStateInternal() == Idle)
716 m_holdTime = currentTimeInternal();
720 m_startTime = nullValue();
721 m_currentTimePending = false;
724 void AnimationPlayer::uncancel()
726 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand);
728 if (playStateInternal() != Idle)
731 // We may not go into the pending state, but setting it to something other
732 // than Idle here will force an update.
733 ASSERT(isNull(m_startTime));
734 m_playState = Pending;
740 AnimationPlayer::PlayStateUpdateScope::PlayStateUpdateScope(AnimationPlayer& player, TimingUpdateReason reason, CompositorPendingChange compositorPendingChange)
742 , m_initial(player.playStateInternal())
743 , m_compositorPendingChange(compositorPendingChange)
745 m_player.updateCurrentTimingState(reason);
748 AnimationPlayer::PlayStateUpdateScope::~PlayStateUpdateScope()
750 AnimationPlayState oldPlayState = m_initial;
751 AnimationPlayState newPlayState = m_player.calculatePlayState();
752 if (oldPlayState != newPlayState) {
753 bool wasActive = oldPlayState == Pending || oldPlayState == Running;
754 bool isActive = newPlayState == Pending || newPlayState == Running;
755 if (!wasActive && isActive) {
756 if (m_player.m_content) {
757 TRACE_EVENT_ASYNC_BEGIN1("blink", "Animation", &m_player, "Name", TRACE_STR_COPY(m_player.m_content->name().utf8().data()));
759 TRACE_EVENT_ASYNC_BEGIN0("blink", "Animation", &m_player);
761 } else if (wasActive && !isActive) {
762 if (oldPlayState != Idle && oldPlayState != Finished) {
763 TRACE_EVENT_ASYNC_END0("blink", "Animation", &m_player);
767 TRACE_EVENT_ASYNC_STEP_INTO0("blink", "Animation", &m_player, playStateString(newPlayState));
771 m_player.m_playState = newPlayState;
774 // Verify that current time is up to date.
775 m_player.currentTimeInternal();
778 switch (m_compositorPendingChange) {
779 case SetCompositorPending:
780 m_player.setCompositorPending();
782 case SetCompositorPendingWithSourceChanged:
783 m_player.setCompositorPending(true);
785 case DoNotSetCompositorPending:
788 ASSERT_NOT_REACHED();
795 bool AnimationPlayer::canFree() const
798 return hasOneRef() && m_content->isAnimation() && m_content->hasOneRef();
802 bool AnimationPlayer::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
804 if (eventType == EventTypeNames::finish)
805 UseCounter::count(executionContext(), UseCounter::AnimationPlayerFinishEvent);
806 return EventTargetWithInlineData::addEventListener(eventType, listener, useCapture);
809 void AnimationPlayer::pauseForTesting(double pauseTime)
811 RELEASE_ASSERT(!paused());
812 setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand);
813 if (hasActiveAnimationsOnCompositor())
814 toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTimeInternal());
815 m_isPausedForTesting = true;
819 void AnimationPlayer::trace(Visitor* visitor)
821 visitor->trace(m_content);
822 visitor->trace(m_timeline);
823 visitor->trace(m_pendingFinishedEvent);
824 EventTargetWithInlineData::trace(visitor);