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