1/*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
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
13 * distribution.
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.
17 *
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.
29 */
30
31#include "config.h"
32#include "core/animation/AnimationPlayer.h"
33
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"
41
42namespace blink {
43
44namespace {
45
46static unsigned nextSequenceNumber()
47{
48    static unsigned next = 0;
49    return ++next;
50}
51
52}
53
54PassRefPtrWillBeRawPtr<AnimationPlayer> AnimationPlayer::create(ExecutionContext* executionContext, AnimationTimeline& timeline, AnimationNode* content)
55{
56    RefPtrWillBeRawPtr<AnimationPlayer> player = adoptRefWillBeNoop(new AnimationPlayer(executionContext, timeline, content));
57    player->uncancel();
58    timeline.document()->compositorPendingAnimations().add(player.get());
59    player->suspendIfNeeded();
60    return player.release();
61}
62
63AnimationPlayer::AnimationPlayer(ExecutionContext* executionContext, AnimationTimeline& timeline, AnimationNode* content)
64    : ActiveDOMObject(executionContext)
65    , m_playbackRate(1)
66    , m_startTime(nullValue())
67    , m_holdTime(0)
68    , m_sequenceNumber(nextSequenceNumber())
69    , m_content(content)
70    , m_timeline(&timeline)
71    , m_paused(false)
72    , m_held(true)
73    , m_isPausedForTesting(false)
74    , m_outdated(true)
75    , m_finished(true)
76    , m_compositorState(nullptr)
77    , m_compositorPending(true)
78    , m_currentTimePending(false)
79    , m_idle(true)
80{
81    if (m_content) {
82        if (m_content->player()) {
83            m_content->player()->cancel();
84            m_content->player()->setSource(0);
85        }
86        m_content->attach(this);
87    }
88}
89
90AnimationPlayer::~AnimationPlayer()
91{
92#if !ENABLE(OILPAN)
93    if (m_content)
94        m_content->detach();
95    if (m_timeline)
96        m_timeline->playerDestroyed(this);
97#endif
98}
99
100double AnimationPlayer::sourceEnd() const
101{
102    return m_content ? m_content->endTimeInternal() : 0;
103}
104
105bool AnimationPlayer::limited(double currentTime) const
106{
107    return (m_playbackRate < 0 && currentTime <= 0) || (m_playbackRate > 0 && currentTime >= sourceEnd());
108}
109
110void AnimationPlayer::setCurrentTimeInternal(double newCurrentTime, TimingUpdateReason reason)
111{
112    ASSERT(std::isfinite(newCurrentTime));
113
114    bool oldHeld = m_held;
115    bool outdated = false;
116    bool isLimited = limited(newCurrentTime);
117    m_held = m_paused || !m_playbackRate || isLimited || std::isnan(m_startTime);
118    if (m_held) {
119        if (!oldHeld || m_holdTime != newCurrentTime)
120            outdated = true;
121        m_holdTime = newCurrentTime;
122        if (m_paused || !m_playbackRate) {
123            m_startTime = nullValue();
124        } else if (isLimited && std::isnan(m_startTime) && reason == TimingUpdateForAnimationFrame) {
125            m_startTime = calculateStartTime(newCurrentTime);
126        }
127    } else {
128        m_holdTime = nullValue();
129        m_startTime = calculateStartTime(newCurrentTime);
130        setFinished(false);
131        outdated = true;
132    }
133
134    if (outdated) {
135        setOutdated();
136    }
137}
138
139// Update timing to reflect updated animation clock due to tick
140void AnimationPlayer::updateCurrentTimingState(TimingUpdateReason reason)
141{
142    if (m_held) {
143        setCurrentTimeInternal(m_holdTime, reason);
144        return;
145    }
146    if (!limited(calculateCurrentTime()))
147        return;
148    m_held = true;
149    m_holdTime = m_playbackRate < 0 ? 0 : sourceEnd();
150}
151
152double AnimationPlayer::startTime(bool& isNull) const
153{
154    double result = startTime();
155    isNull = std::isnan(result);
156    return result;
157}
158
159double AnimationPlayer::startTime() const
160{
161    UseCounter::count(executionContext(), UseCounter::AnimationPlayerGetStartTime);
162    return m_startTime * 1000;
163}
164
165double AnimationPlayer::currentTime(bool& isNull)
166{
167    double result = currentTime();
168    isNull = std::isnan(result);
169    return result;
170}
171
172double AnimationPlayer::currentTime()
173{
174    UseCounter::count(executionContext(), UseCounter::AnimationPlayerGetCurrentTime);
175    if (m_currentTimePending || m_idle)
176        return std::numeric_limits<double>::quiet_NaN();
177    return currentTimeInternal() * 1000;
178}
179
180double AnimationPlayer::currentTimeInternal()
181{
182    updateCurrentTimingState(TimingUpdateOnDemand);
183    if (m_held)
184        return m_holdTime;
185    return calculateCurrentTime();
186}
187
188void AnimationPlayer::preCommit(bool startOnCompositor)
189{
190    if (m_compositorState && m_compositorState->pendingAction == Start) {
191        // Still waiting for a start time.
192        return;
193    }
194
195    bool softChange = m_compositorState && (paused() || m_compositorState->playbackRate != m_playbackRate);
196    bool hardChange = m_compositorState && (m_compositorState->sourceChanged || (m_compositorState->startTime != m_startTime && !std::isnan(m_compositorState->startTime) && !std::isnan(m_startTime)));
197
198    // FIXME: softChange && !hardChange should generate a Pause/ThenStart,
199    // not a Cancel, but we can't communicate these to the compositor yet.
200
201    bool changed = softChange || hardChange;
202    bool shouldCancel = (!playing() && m_compositorState) || changed;
203    bool shouldStart = playing() && (!m_compositorState || changed);
204
205    if (shouldCancel) {
206        cancelAnimationOnCompositor();
207        m_compositorState = nullptr;
208
209    }
210
211    if (!shouldStart) {
212        m_currentTimePending = false;
213    }
214
215    if (shouldStart && startOnCompositor && maybeStartAnimationOnCompositor()) {
216        m_compositorState = adoptPtr(new CompositorState(*this));
217    }
218}
219
220void AnimationPlayer::postCommit(double timelineTime)
221{
222    m_compositorPending = false;
223
224    if (!m_compositorState || m_compositorState->pendingAction == None)
225        return;
226
227    switch (m_compositorState->pendingAction) {
228    case Start:
229        if (!std::isnan(m_compositorState->startTime)) {
230            ASSERT(m_startTime == m_compositorState->startTime);
231            m_compositorState->pendingAction = None;
232        }
233        break;
234    case Pause:
235    case PauseThenStart:
236        ASSERT(std::isnan(m_startTime));
237        m_compositorState->pendingAction = None;
238        setCurrentTimeInternal((timelineTime - m_compositorState->startTime) * m_playbackRate, TimingUpdateForAnimationFrame);
239        m_currentTimePending = false;
240        break;
241    default:
242        ASSERT_NOT_REACHED();
243    }
244}
245
246void AnimationPlayer::notifyCompositorStartTime(double timelineTime)
247{
248    if (m_compositorState) {
249        ASSERT(m_compositorState->pendingAction == Start);
250        ASSERT(std::isnan(m_compositorState->startTime));
251
252        double initialCompositorHoldTime = m_compositorState->holdTime;
253        m_compositorState->pendingAction = None;
254        m_compositorState->startTime = timelineTime + currentTimeInternal() / -m_playbackRate;
255
256        if (paused() || m_compositorState->playbackRate != m_playbackRate || m_compositorState->sourceChanged) {
257            // Paused state, playback rate, or source changed while starting.
258            setCompositorPending();
259        }
260
261        if (m_startTime == timelineTime) {
262            // The start time was set to the incoming compositor start time.
263            // Unlikely, but possible.
264            // FIXME: Depending on what changed above this might still be pending.
265            // Maybe...
266            m_currentTimePending = false;
267            return;
268        }
269
270        if (!std::isnan(m_startTime) || currentTimeInternal() != initialCompositorHoldTime) {
271            // A new start time or current time was set while starting.
272            setCompositorPending();
273            return;
274        }
275    }
276
277    if (playing()) {
278        ASSERT(std::isnan(m_startTime));
279        ASSERT(m_held);
280
281        if (m_playbackRate == 0) {
282            setStartTimeInternal(timelineTime);
283        } else {
284            setStartTimeInternal(timelineTime + currentTimeInternal() / -m_playbackRate);
285        }
286
287        // FIXME: This avoids marking this player as outdated needlessly when a start time
288        // is notified, but we should refactor how outdating works to avoid this.
289        m_outdated = false;
290
291        m_currentTimePending = false;
292    }
293}
294
295double AnimationPlayer::calculateStartTime(double currentTime) const
296{
297    return m_timeline->effectiveTime() - currentTime / m_playbackRate;
298}
299
300double AnimationPlayer::calculateCurrentTime() const
301{
302    ASSERT(!m_held);
303    if (isNull(m_startTime) || !m_timeline)
304        return 0;
305    return (m_timeline->effectiveTime() - m_startTime) * m_playbackRate;
306}
307
308void AnimationPlayer::setCurrentTime(double newCurrentTime)
309{
310    UseCounter::count(executionContext(), UseCounter::AnimationPlayerSetCurrentTime);
311    if (!std::isfinite(newCurrentTime))
312        return;
313
314    setCompositorPending();
315    m_currentTimePending = false;
316    setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand);
317}
318
319void AnimationPlayer::setStartTime(double startTime)
320{
321    UseCounter::count(executionContext(), UseCounter::AnimationPlayerSetStartTime);
322    if (m_paused || m_idle)
323        return;
324    if (!std::isfinite(startTime))
325        return;
326    if (startTime == m_startTime)
327        return;
328
329    setCompositorPending();
330    m_currentTimePending = false;
331    setStartTimeInternal(startTime / 1000);
332}
333
334void AnimationPlayer::setStartTimeInternal(double newStartTime)
335{
336    ASSERT(!m_paused);
337    ASSERT(std::isfinite(newStartTime));
338    ASSERT(newStartTime != m_startTime);
339
340    bool hadStartTime = hasStartTime();
341    double previousCurrentTime = currentTimeInternal();
342    m_startTime = newStartTime;
343    if (m_held && m_playbackRate) {
344        // If held, the start time would still be derrived from the hold time.
345        // Force a new, limited, current time.
346        m_held = false;
347        double currentTime = calculateCurrentTime();
348        if (m_playbackRate > 0 && currentTime > sourceEnd()) {
349            currentTime = sourceEnd();
350        } else if (m_playbackRate < 0 && currentTime < 0) {
351            currentTime = 0;
352        }
353        setCurrentTimeInternal(currentTime, TimingUpdateOnDemand);
354    }
355    double newCurrentTime = currentTimeInternal();
356
357    if (previousCurrentTime != newCurrentTime) {
358        setOutdated();
359    } else if (!hadStartTime && m_timeline) {
360        // Even though this player is not outdated, time to effect change is
361        // infinity until start time is set.
362        m_timeline->wake();
363    }
364}
365
366void AnimationPlayer::setSource(AnimationNode* newSource)
367{
368    if (m_content == newSource)
369        return;
370
371    setCompositorPending(true);
372
373    double storedCurrentTime = currentTimeInternal();
374    if (m_content)
375        m_content->detach();
376    m_content = newSource;
377    if (newSource) {
378        // FIXME: This logic needs to be updated once groups are implemented
379        if (newSource->player()) {
380            newSource->player()->cancel();
381            newSource->player()->setSource(0);
382        }
383        newSource->attach(this);
384        setOutdated();
385    }
386    setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand);
387}
388
389String AnimationPlayer::playState()
390{
391    switch (playStateInternal()) {
392    case Idle:
393        return "idle";
394    case Pending:
395        return "pending";
396    case Running:
397        return "running";
398    case Paused:
399        return "paused";
400    case Finished:
401        return "finished";
402    default:
403        ASSERT_NOT_REACHED();
404        return "";
405    }
406}
407
408AnimationPlayer::AnimationPlayState AnimationPlayer::playStateInternal()
409{
410    if (m_idle)
411        return Idle;
412    if (m_currentTimePending || (isNull(m_startTime) && !m_paused && m_playbackRate != 0))
413        return Pending;
414    if (m_paused)
415        return Paused;
416    if (finished())
417        return Finished;
418    return Running;
419}
420
421void AnimationPlayer::pause()
422{
423    if (m_paused)
424        return;
425    if (playing()) {
426        setCompositorPending();
427        m_currentTimePending = true;
428    }
429    m_paused = true;
430    setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand);
431}
432
433void AnimationPlayer::unpause()
434{
435    if (!m_paused)
436        return;
437    setCompositorPending();
438    m_currentTimePending = true;
439    unpauseInternal();
440}
441
442void AnimationPlayer::unpauseInternal()
443{
444    if (!m_paused)
445        return;
446    m_paused = false;
447    setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand);
448}
449
450void AnimationPlayer::play()
451{
452    if (!playing())
453        m_startTime = nullValue();
454
455    setCompositorPending();
456    uncancel();
457    unpauseInternal();
458    if (!m_content)
459        return;
460    double currentTime = this->currentTimeInternal();
461    if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd()))
462        setCurrentTimeInternal(0, TimingUpdateOnDemand);
463    else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd()))
464        setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand);
465    setFinished(false);
466}
467
468void AnimationPlayer::reverse()
469{
470    if (!m_playbackRate) {
471        return;
472    }
473
474    uncancel();
475    setPlaybackRateInternal(-m_playbackRate);
476    play();
477}
478
479void AnimationPlayer::finish(ExceptionState& exceptionState)
480{
481    if (!m_playbackRate || m_idle) {
482        return;
483    }
484    if (m_playbackRate > 0 && sourceEnd() == std::numeric_limits<double>::infinity()) {
485        exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has source content whose end time is infinity.");
486        return;
487    }
488    if (playing()) {
489        setCompositorPending();
490    }
491
492    uncancel();
493
494    double newCurrentTime = m_playbackRate < 0 ? 0 : sourceEnd();
495    setCurrentTimeInternal(newCurrentTime, TimingUpdateOnDemand);
496    if (!paused()) {
497        m_startTime = calculateStartTime(newCurrentTime);
498    }
499
500    m_currentTimePending = false;
501    ASSERT(finished());
502}
503
504const AtomicString& AnimationPlayer::interfaceName() const
505{
506    return EventTargetNames::AnimationPlayer;
507}
508
509ExecutionContext* AnimationPlayer::executionContext() const
510{
511    return ActiveDOMObject::executionContext();
512}
513
514bool AnimationPlayer::hasPendingActivity() const
515{
516    return m_pendingFinishedEvent || (!m_finished && hasEventListeners(EventTypeNames::finish));
517}
518
519void AnimationPlayer::stop()
520{
521    setFinished(true);
522    m_pendingFinishedEvent = nullptr;
523}
524
525bool AnimationPlayer::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
526{
527    if (m_pendingFinishedEvent == event)
528        m_pendingFinishedEvent = nullptr;
529    return EventTargetWithInlineData::dispatchEvent(event);
530}
531
532double AnimationPlayer::playbackRate() const
533{
534    UseCounter::count(executionContext(), UseCounter::AnimationPlayerGetPlaybackRate);
535    return m_playbackRate;
536}
537
538void AnimationPlayer::setPlaybackRate(double playbackRate)
539{
540    UseCounter::count(executionContext(), UseCounter::AnimationPlayerSetPlaybackRate);
541    if (!std::isfinite(playbackRate))
542        return;
543    if (playbackRate == m_playbackRate)
544        return;
545
546    setPlaybackRateInternal(playbackRate);
547}
548
549void AnimationPlayer::setPlaybackRateInternal(double playbackRate)
550{
551    ASSERT(std::isfinite(playbackRate));
552    ASSERT(playbackRate != m_playbackRate);
553
554    setCompositorPending();
555    if (!finished() && !paused() && hasStartTime())
556        m_currentTimePending = true;
557
558    double storedCurrentTime = currentTimeInternal();
559    if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && playbackRate <= 0))
560        setFinished(false);
561
562    m_playbackRate = playbackRate;
563    m_startTime = std::numeric_limits<double>::quiet_NaN();
564    setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand);
565}
566
567void AnimationPlayer::setOutdated()
568{
569    m_outdated = true;
570    if (m_timeline)
571        m_timeline->setOutdatedAnimationPlayer(this);
572}
573
574bool AnimationPlayer::canStartAnimationOnCompositor()
575{
576    if (m_playbackRate == 0 || (std::isinf(sourceEnd()) && m_playbackRate < 0))
577        return false;
578
579    return m_timeline && m_content && m_content->isAnimation() && playing();
580}
581
582bool AnimationPlayer::maybeStartAnimationOnCompositor()
583{
584    if (!canStartAnimationOnCompositor())
585        return false;
586
587    bool reversed = m_playbackRate < 0;
588
589    double startTime = timeline()->zeroTime() + startTimeInternal();
590    if (reversed) {
591        startTime -= sourceEnd() / fabs(m_playbackRate);
592    }
593
594    double timeOffset = 0;
595    if (std::isnan(startTime)) {
596        timeOffset = reversed ? sourceEnd() - currentTimeInternal() : currentTimeInternal();
597        timeOffset = timeOffset / fabs(m_playbackRate);
598    }
599    return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(startTime, timeOffset, m_playbackRate);
600}
601
602void AnimationPlayer::setCompositorPending(bool sourceChanged)
603{
604    // FIXME: Animation could notify this directly?
605    if (!hasActiveAnimationsOnCompositor()) {
606        m_compositorState.release();
607    }
608    if (!m_compositorPending) {
609        m_compositorPending = true;
610        if (sourceChanged && m_compositorState)
611            m_compositorState->sourceChanged = true;
612        timeline()->document()->compositorPendingAnimations().add(this);
613    }
614}
615
616bool AnimationPlayer::hasActiveAnimationsOnCompositor()
617{
618    if (!m_content || !m_content->isAnimation())
619        return false;
620
621    return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor();
622}
623
624void AnimationPlayer::cancelAnimationOnCompositor()
625{
626    if (hasActiveAnimationsOnCompositor())
627        toAnimation(m_content.get())->cancelAnimationOnCompositor();
628}
629
630bool AnimationPlayer::update(TimingUpdateReason reason)
631{
632    if (!m_timeline)
633        return false;
634
635    updateCurrentTimingState(reason);
636    m_outdated = false;
637
638    if (m_content) {
639        double inheritedTime = m_idle || isNull(m_timeline->currentTimeInternal()) ? nullValue() : currentTimeInternal();
640        // Special case for end-exclusivity when playing backwards.
641        if (inheritedTime == 0 && m_playbackRate < 0)
642            inheritedTime = -1;
643        m_content->updateInheritedTime(inheritedTime, reason);
644    }
645
646    if ((m_idle || finished()) && !m_finished) {
647        if (reason == TimingUpdateForAnimationFrame && (m_idle || hasStartTime())) {
648            const AtomicString& eventType = EventTypeNames::finish;
649            if (executionContext() && hasEventListeners(eventType)) {
650                double eventCurrentTime = currentTimeInternal() * 1000;
651                m_pendingFinishedEvent = AnimationPlayerEvent::create(eventType, eventCurrentTime, timeline()->currentTime());
652                m_pendingFinishedEvent->setTarget(this);
653                m_pendingFinishedEvent->setCurrentTarget(this);
654                m_timeline->document()->enqueueAnimationFrameEvent(m_pendingFinishedEvent);
655            }
656            setFinished(true);
657        }
658    }
659    ASSERT(!m_outdated);
660    return !m_finished;
661}
662
663double AnimationPlayer::timeToEffectChange()
664{
665    ASSERT(!m_outdated);
666    if (m_held || !hasStartTime())
667        return std::numeric_limits<double>::infinity();
668    if (!m_content)
669        return -currentTimeInternal() / m_playbackRate;
670    if (m_playbackRate > 0)
671        return m_content->timeToForwardsEffectChange() / m_playbackRate;
672    return m_content->timeToReverseEffectChange() / -m_playbackRate;
673}
674
675void AnimationPlayer::setFinished(bool finished)
676{
677    if (m_finished && !finished) {
678        if (m_content) {
679            TRACE_EVENT_ASYNC_BEGIN1("blink", "Animation", this, "Name", TRACE_STR_COPY(m_content->name().utf8().data()));
680        } else {
681            TRACE_EVENT_ASYNC_BEGIN0("blink", "Animation", this);
682        }
683    }
684    if (!m_finished && finished) {
685        TRACE_EVENT_ASYNC_END0("blink", "Animation", this);
686    }
687    m_finished = finished;
688}
689
690void AnimationPlayer::cancel()
691{
692    if (m_idle)
693        return;
694
695    m_holdTime = currentTimeInternal();
696    m_held = true;
697    m_idle = true;
698    m_startTime = nullValue();
699    m_currentTimePending = false;
700    setCompositorPending();
701}
702
703void AnimationPlayer::uncancel()
704{
705    if (!m_idle)
706        return;
707
708    m_idle = false;
709    m_held = true;
710    m_holdTime = 0;
711    setFinished(false);
712}
713
714
715#if !ENABLE(OILPAN)
716bool AnimationPlayer::canFree() const
717{
718    ASSERT(m_content);
719    return hasOneRef() && m_content->isAnimation() && m_content->hasOneRef();
720}
721#endif
722
723bool AnimationPlayer::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
724{
725    if (eventType == EventTypeNames::finish)
726        UseCounter::count(executionContext(), UseCounter::AnimationPlayerFinishEvent);
727    return EventTargetWithInlineData::addEventListener(eventType, listener, useCapture);
728}
729
730void AnimationPlayer::pauseForTesting(double pauseTime)
731{
732    RELEASE_ASSERT(!paused());
733    setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand);
734    if (hasActiveAnimationsOnCompositor())
735        toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTimeInternal());
736    m_isPausedForTesting = true;
737    pause();
738}
739
740void AnimationPlayer::trace(Visitor* visitor)
741{
742    visitor->trace(m_content);
743    visitor->trace(m_timeline);
744    visitor->trace(m_pendingFinishedEvent);
745    EventTargetWithInlineData::trace(visitor);
746}
747
748} // namespace
749