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"
45 static unsigned nextSequenceNumber()
47 static unsigned next = 0;
53 PassRefPtrWillBeRawPtr<AnimationPlayer> AnimationPlayer::create(ExecutionContext* executionContext, AnimationTimeline& timeline, AnimationNode* content)
55 RefPtrWillBeRawPtr<AnimationPlayer> player = adoptRefWillBeNoop(new AnimationPlayer(executionContext, timeline, content));
57 timeline.document()->compositorPendingAnimations().add(player.get());
58 player->suspendIfNeeded();
59 return player.release();
62 AnimationPlayer::AnimationPlayer(ExecutionContext* executionContext, AnimationTimeline& timeline, AnimationNode* content)
63 : ActiveDOMObject(executionContext)
65 , m_startTime(nullValue())
67 , m_sequenceNumber(nextSequenceNumber())
69 , m_timeline(&timeline)
72 , m_isPausedForTesting(false)
75 , m_compositorState(nullptr)
76 , m_compositorPending(true)
77 , m_currentTimePending(false)
81 if (m_content->player()) {
82 m_content->player()->cancel();
83 m_content->player()->setSource(0);
85 m_content->attach(this);
89 AnimationPlayer::~AnimationPlayer()
95 m_timeline->playerDestroyed(this);
99 double AnimationPlayer::sourceEnd() const
101 return m_content ? m_content->endTimeInternal() : 0;
104 bool AnimationPlayer::limited(double currentTime) const
106 return (m_playbackRate < 0 && currentTime <= 0) || (m_playbackRate > 0 && currentTime >= sourceEnd());
109 void AnimationPlayer::setCurrentTimeInternal(double newCurrentTime, TimingUpdateReason reason)
111 ASSERT(std::isfinite(newCurrentTime));
113 bool oldHeld = m_held;
114 bool outdated = false;
115 bool isLimited = limited(newCurrentTime);
116 m_held = m_paused || !m_playbackRate || isLimited || std::isnan(m_startTime);
118 if (!oldHeld || m_holdTime != newCurrentTime)
120 m_holdTime = newCurrentTime;
121 if (m_paused || !m_playbackRate) {
122 m_startTime = nullValue();
123 } else if (isLimited && std::isnan(m_startTime) && reason == TimingUpdateForAnimationFrame) {
124 m_startTime = calculateStartTime(newCurrentTime);
127 m_holdTime = nullValue();
128 m_startTime = calculateStartTime(newCurrentTime);
138 // Update timing to reflect updated animation clock due to tick
139 void AnimationPlayer::updateCurrentTimingState(TimingUpdateReason reason)
142 setCurrentTimeInternal(m_holdTime, reason);
145 if (!limited(calculateCurrentTime()))
148 m_holdTime = m_playbackRate < 0 ? 0 : sourceEnd();
151 double AnimationPlayer::startTime(bool& isNull) const
153 double result = startTime();
154 isNull = std::isnan(result);
158 double AnimationPlayer::startTime() const
160 UseCounter::count(executionContext(), UseCounter::AnimationPlayerGetStartTime);
161 return m_startTime * 1000;
164 double AnimationPlayer::currentTime(bool& isNull)
166 double result = currentTime();
167 isNull = std::isnan(result);
171 double AnimationPlayer::currentTime()
173 UseCounter::count(executionContext(), UseCounter::AnimationPlayerGetCurrentTime);
174 if (m_currentTimePending || m_idle)
175 return std::numeric_limits<double>::quiet_NaN();
176 return currentTimeInternal() * 1000;
179 double AnimationPlayer::currentTimeInternal()
181 updateCurrentTimingState(TimingUpdateOnDemand);
184 return calculateCurrentTime();
187 void AnimationPlayer::preCommit(bool startOnCompositor)
189 if (m_compositorState && m_compositorState->pendingAction == Start) {
190 // Still waiting for a start time.
194 bool softChange = m_compositorState && (paused() || m_compositorState->playbackRate != m_playbackRate);
195 bool hardChange = m_compositorState && (m_compositorState->sourceChanged || (m_compositorState->startTime != m_startTime && !std::isnan(m_compositorState->startTime) && !std::isnan(m_startTime)));
197 // FIXME: softChange && !hardChange should generate a Pause/ThenStart,
198 // not a Cancel, but we can't communicate these to the compositor yet.
200 bool changed = softChange || hardChange;
201 bool shouldCancel = (!playing() && m_compositorState) || changed;
202 bool shouldStart = playing() && (!m_compositorState || changed);
205 cancelAnimationOnCompositor();
206 m_compositorState = nullptr;
211 m_currentTimePending = false;
214 if (shouldStart && startOnCompositor && maybeStartAnimationOnCompositor()) {
215 m_compositorState = adoptPtr(new CompositorState(*this));
219 void AnimationPlayer::postCommit(double timelineTime)
221 m_compositorPending = false;
223 if (!m_compositorState || m_compositorState->pendingAction == None)
226 switch (m_compositorState->pendingAction) {
228 if (!std::isnan(m_compositorState->startTime)) {
229 ASSERT(m_startTime == m_compositorState->startTime);
230 m_compositorState->pendingAction = None;
235 ASSERT(std::isnan(m_startTime));
236 m_compositorState->pendingAction = None;
237 setCurrentTimeInternal((timelineTime - m_compositorState->startTime) * m_playbackRate, TimingUpdateForAnimationFrame);
238 m_currentTimePending = false;
241 ASSERT_NOT_REACHED();
245 void AnimationPlayer::notifyCompositorStartTime(double timelineTime)
247 if (m_compositorState) {
248 ASSERT(m_compositorState->pendingAction == Start);
249 ASSERT(std::isnan(m_compositorState->startTime));
251 double initialCompositorHoldTime = m_compositorState->holdTime;
252 m_compositorState->pendingAction = None;
253 m_compositorState->startTime = timelineTime;
255 if (paused() || m_compositorState->playbackRate != m_playbackRate || m_compositorState->sourceChanged) {
256 // Paused state, playback rate, or source changed while starting.
257 setCompositorPending();
260 if (m_startTime == timelineTime) {
261 // The start time was set to the incoming compositor start time.
262 // Unlikely, but possible.
263 // FIXME: Depending on what changed above this might still be pending.
265 m_currentTimePending = false;
269 if (!std::isnan(m_startTime) || currentTimeInternal() != initialCompositorHoldTime) {
270 // A new start time or current time was set while starting.
271 setCompositorPending();
277 ASSERT(std::isnan(m_startTime));
280 if (m_playbackRate == 0) {
281 setStartTimeInternal(timelineTime);
283 setStartTimeInternal(timelineTime + currentTimeInternal() / -m_playbackRate);
286 // FIXME: This avoids marking this player as outdated needlessly when a start time
287 // is notified, but we should refactor how outdating works to avoid this.
290 m_currentTimePending = false;
294 double AnimationPlayer::calculateStartTime(double currentTime) const
296 return m_timeline->effectiveTime() - currentTime / m_playbackRate;
299 double AnimationPlayer::calculateCurrentTime() const
302 if (isNull(m_startTime) || !m_timeline)
304 return (m_timeline->effectiveTime() - m_startTime) * m_playbackRate;
307 void AnimationPlayer::setCurrentTime(double newCurrentTime)
309 UseCounter::count(executionContext(), UseCounter::AnimationPlayerSetCurrentTime);
310 if (!std::isfinite(newCurrentTime))
313 setCompositorPending();
314 m_currentTimePending = false;
315 setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand);
318 void AnimationPlayer::setStartTime(double startTime)
320 UseCounter::count(executionContext(), UseCounter::AnimationPlayerSetStartTime);
321 if (m_paused || m_idle)
323 if (!std::isfinite(startTime))
325 if (startTime == m_startTime)
328 setCompositorPending();
329 m_currentTimePending = false;
330 setStartTimeInternal(startTime / 1000);
333 void AnimationPlayer::setStartTimeInternal(double newStartTime)
336 ASSERT(std::isfinite(newStartTime));
337 ASSERT(newStartTime != m_startTime);
339 bool hadStartTime = hasStartTime();
340 double previousCurrentTime = currentTimeInternal();
341 m_startTime = newStartTime;
342 if (m_held && m_playbackRate) {
343 // If held, the start time would still be derrived from the hold time.
344 // Force a new, limited, current time.
346 double currentTime = calculateCurrentTime();
347 if (m_playbackRate > 0 && currentTime > sourceEnd()) {
348 currentTime = sourceEnd();
349 } else if (m_playbackRate < 0 && currentTime < 0) {
352 setCurrentTimeInternal(currentTime, TimingUpdateOnDemand);
354 double newCurrentTime = currentTimeInternal();
356 if (previousCurrentTime != newCurrentTime) {
358 } else if (!hadStartTime && m_timeline) {
359 // Even though this player is not outdated, time to effect change is
360 // infinity until start time is set.
365 void AnimationPlayer::setSource(AnimationNode* newSource)
367 if (m_content == newSource)
370 setCompositorPending(true);
372 double storedCurrentTime = currentTimeInternal();
375 m_content = newSource;
377 // FIXME: This logic needs to be updated once groups are implemented
378 if (newSource->player()) {
379 newSource->player()->cancel();
380 newSource->player()->setSource(0);
382 newSource->attach(this);
385 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand);
388 String AnimationPlayer::playState()
390 switch (playStateInternal()) {
402 ASSERT_NOT_REACHED();
407 AnimationPlayer::AnimationPlayState AnimationPlayer::playStateInternal()
411 if (m_currentTimePending || (isNull(m_startTime) && !m_paused && m_playbackRate != 0))
420 void AnimationPlayer::pause()
425 setCompositorPending();
426 m_currentTimePending = true;
429 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand);
432 void AnimationPlayer::unpause()
436 setCompositorPending();
437 m_currentTimePending = true;
441 void AnimationPlayer::unpauseInternal()
446 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand);
449 void AnimationPlayer::play()
452 m_startTime = nullValue();
454 setCompositorPending();
459 double currentTime = this->currentTimeInternal();
460 if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd()))
461 setCurrentTimeInternal(0, TimingUpdateOnDemand);
462 else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd()))
463 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand);
467 void AnimationPlayer::reverse()
469 if (!m_playbackRate) {
474 setPlaybackRateInternal(-m_playbackRate);
478 void AnimationPlayer::finish(ExceptionState& exceptionState)
480 if (!m_playbackRate || m_idle) {
483 if (m_playbackRate > 0 && sourceEnd() == std::numeric_limits<double>::infinity()) {
484 exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has source content whose end time is infinity.");
488 setCompositorPending();
493 double newCurrentTime = m_playbackRate < 0 ? 0 : sourceEnd();
494 setCurrentTimeInternal(newCurrentTime, TimingUpdateOnDemand);
496 m_startTime = calculateStartTime(newCurrentTime);
499 m_currentTimePending = false;
503 const AtomicString& AnimationPlayer::interfaceName() const
505 return EventTargetNames::AnimationPlayer;
508 ExecutionContext* AnimationPlayer::executionContext() const
510 return ActiveDOMObject::executionContext();
513 bool AnimationPlayer::hasPendingActivity() const
515 return m_pendingFinishedEvent || (!m_finished && hasEventListeners(EventTypeNames::finish));
518 void AnimationPlayer::stop()
521 m_pendingFinishedEvent = nullptr;
524 bool AnimationPlayer::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
526 if (m_pendingFinishedEvent == event)
527 m_pendingFinishedEvent = nullptr;
528 return EventTargetWithInlineData::dispatchEvent(event);
531 double AnimationPlayer::playbackRate() const
533 UseCounter::count(executionContext(), UseCounter::AnimationPlayerGetPlaybackRate);
534 return m_playbackRate;
537 void AnimationPlayer::setPlaybackRate(double playbackRate)
539 UseCounter::count(executionContext(), UseCounter::AnimationPlayerSetPlaybackRate);
540 if (!std::isfinite(playbackRate))
542 if (playbackRate == m_playbackRate)
545 setPlaybackRateInternal(playbackRate);
548 void AnimationPlayer::setPlaybackRateInternal(double playbackRate)
550 ASSERT(std::isfinite(playbackRate));
551 ASSERT(playbackRate != m_playbackRate);
553 setCompositorPending();
554 if (!finished() && !paused() && hasStartTime())
555 m_currentTimePending = true;
557 double storedCurrentTime = currentTimeInternal();
558 if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && playbackRate <= 0))
561 m_playbackRate = playbackRate;
562 m_startTime = std::numeric_limits<double>::quiet_NaN();
563 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand);
566 void AnimationPlayer::setOutdated()
570 m_timeline->setOutdatedAnimationPlayer(this);
573 bool AnimationPlayer::canStartAnimationOnCompositor()
575 if (m_playbackRate == 0)
578 return m_timeline && m_content && m_content->isAnimation() && playing();
581 bool AnimationPlayer::maybeStartAnimationOnCompositor()
583 if (!canStartAnimationOnCompositor())
586 double startTime = timeline()->zeroTime() + startTimeInternal();
587 double timeOffset = 0;
588 if (std::isnan(startTime)) {
589 timeOffset = currentTimeInternal();
591 return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(startTime, timeOffset, m_playbackRate);
594 void AnimationPlayer::setCompositorPending(bool sourceChanged)
596 // FIXME: Animation could notify this directly?
597 if (!hasActiveAnimationsOnCompositor()) {
598 m_compositorState.release();
600 if (!m_compositorPending) {
601 m_compositorPending = true;
602 if (sourceChanged && m_compositorState)
603 m_compositorState->sourceChanged = true;
604 timeline()->document()->compositorPendingAnimations().add(this);
608 bool AnimationPlayer::hasActiveAnimationsOnCompositor()
610 if (!m_content || !m_content->isAnimation())
613 return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor();
616 void AnimationPlayer::cancelAnimationOnCompositor()
618 if (hasActiveAnimationsOnCompositor())
619 toAnimation(m_content.get())->cancelAnimationOnCompositor();
622 bool AnimationPlayer::update(TimingUpdateReason reason)
627 updateCurrentTimingState(reason);
631 double inheritedTime = m_idle || isNull(m_timeline->currentTimeInternal()) ? nullValue() : currentTimeInternal();
632 // Special case for end-exclusivity when playing backwards.
633 if (inheritedTime == 0 && m_playbackRate < 0)
635 m_content->updateInheritedTime(inheritedTime, reason);
638 if ((m_idle || finished()) && !m_finished) {
639 if (reason == TimingUpdateForAnimationFrame && (m_idle || hasStartTime())) {
640 const AtomicString& eventType = EventTypeNames::finish;
641 if (executionContext() && hasEventListeners(eventType)) {
642 double eventCurrentTime = currentTimeInternal() * 1000;
643 m_pendingFinishedEvent = AnimationPlayerEvent::create(eventType, eventCurrentTime, timeline()->currentTime());
644 m_pendingFinishedEvent->setTarget(this);
645 m_pendingFinishedEvent->setCurrentTarget(this);
646 m_timeline->document()->enqueueAnimationFrameEvent(m_pendingFinishedEvent);
655 double AnimationPlayer::timeToEffectChange()
658 if (m_held || !hasStartTime())
659 return std::numeric_limits<double>::infinity();
661 return -currentTimeInternal() / m_playbackRate;
662 if (m_playbackRate > 0)
663 return m_content->timeToForwardsEffectChange() / m_playbackRate;
664 return m_content->timeToReverseEffectChange() / -m_playbackRate;
667 void AnimationPlayer::setFinished(bool finished)
669 if (m_finished && !finished) {
671 TRACE_EVENT_ASYNC_BEGIN1("blink", "Animation", this, "Name", TRACE_STR_COPY(m_content->name().utf8().data()));
673 TRACE_EVENT_ASYNC_BEGIN0("blink", "Animation", this);
676 if (!m_finished && finished) {
677 TRACE_EVENT_ASYNC_END0("blink", "Animation", this);
679 m_finished = finished;
682 void AnimationPlayer::cancel()
687 m_holdTime = currentTimeInternal();
690 m_startTime = nullValue();
691 m_currentTimePending = false;
692 setCompositorPending();
695 void AnimationPlayer::uncancel()
708 bool AnimationPlayer::canFree() const
711 return hasOneRef() && m_content->isAnimation() && m_content->hasOneRef();
715 bool AnimationPlayer::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
717 if (eventType == EventTypeNames::finish)
718 UseCounter::count(executionContext(), UseCounter::AnimationPlayerFinishEvent);
719 return EventTargetWithInlineData::addEventListener(eventType, listener, useCapture);
722 void AnimationPlayer::pauseForTesting(double pauseTime)
724 RELEASE_ASSERT(!paused());
725 setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand);
726 if (hasActiveAnimationsOnCompositor())
727 toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTimeInternal());
728 m_isPausedForTesting = true;
732 void AnimationPlayer::trace(Visitor* visitor)
734 visitor->trace(m_content);
735 visitor->trace(m_timeline);
736 visitor->trace(m_pendingFinishedEvent);
737 EventTargetWithInlineData::trace(visitor);