| OLD | NEW |
| (Empty) |
| 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/dom/ExceptionCode.h" | |
| 38 #include "core/events/AnimationPlayerEvent.h" | |
| 39 #include "core/frame/UseCounter.h" | |
| 40 #include "core/inspector/InspectorInstrumentation.h" | |
| 41 #include "core/inspector/InspectorTraceEvents.h" | |
| 42 #include "platform/RuntimeEnabledFeatures.h" | |
| 43 #include "platform/TraceEvent.h" | |
| 44 #include "public/platform/Platform.h" | |
| 45 #include "public/platform/WebCompositorAnimationPlayer.h" | |
| 46 #include "public/platform/WebCompositorSupport.h" | |
| 47 #include "wtf/MathExtras.h" | |
| 48 | |
| 49 namespace blink { | |
| 50 | |
| 51 namespace { | |
| 52 | |
| 53 static unsigned nextSequenceNumber() | |
| 54 { | |
| 55 static unsigned next = 0; | |
| 56 return ++next; | |
| 57 } | |
| 58 | |
| 59 } | |
| 60 | |
| 61 PassRefPtrWillBeRawPtr<AnimationPlayer> AnimationPlayer::create(AnimationNode* s
ource, AnimationTimeline* timeline) | |
| 62 { | |
| 63 if (!timeline) { | |
| 64 // FIXME: Support creating players without a timeline. | |
| 65 return nullptr; | |
| 66 } | |
| 67 | |
| 68 RefPtrWillBeRawPtr<AnimationPlayer> player = adoptRefWillBeNoop(new Animatio
nPlayer(timeline->document()->contextDocument().get(), *timeline, source)); | |
| 69 player->suspendIfNeeded(); | |
| 70 | |
| 71 if (timeline) { | |
| 72 timeline->playerAttached(*player); | |
| 73 player->attachCompositorTimeline(); | |
| 74 } | |
| 75 | |
| 76 return player.release(); | |
| 77 } | |
| 78 | |
| 79 AnimationPlayer::AnimationPlayer(ExecutionContext* executionContext, AnimationTi
meline& timeline, AnimationNode* content) | |
| 80 : ActiveDOMObject(executionContext) | |
| 81 , m_playState(Idle) | |
| 82 , m_playbackRate(1) | |
| 83 , m_startTime(nullValue()) | |
| 84 , m_holdTime(0) | |
| 85 , m_sequenceNumber(nextSequenceNumber()) | |
| 86 , m_content(content) | |
| 87 , m_timeline(&timeline) | |
| 88 , m_paused(false) | |
| 89 , m_held(true) | |
| 90 , m_isPausedForTesting(false) | |
| 91 , m_outdated(false) | |
| 92 , m_finished(true) | |
| 93 , m_compositorState(nullptr) | |
| 94 , m_compositorPending(false) | |
| 95 , m_compositorGroup(0) | |
| 96 , m_currentTimePending(false) | |
| 97 , m_stateIsBeingUpdated(false) | |
| 98 { | |
| 99 if (m_content) { | |
| 100 if (m_content->player()) { | |
| 101 m_content->player()->cancel(); | |
| 102 m_content->player()->setSource(0); | |
| 103 } | |
| 104 m_content->attach(this); | |
| 105 } | |
| 106 } | |
| 107 | |
| 108 AnimationPlayer::~AnimationPlayer() | |
| 109 { | |
| 110 #if !ENABLE(OILPAN) | |
| 111 if (m_content) | |
| 112 m_content->detach(); | |
| 113 if (m_timeline) | |
| 114 m_timeline->playerDestroyed(this); | |
| 115 #endif | |
| 116 | |
| 117 destroyCompositorPlayer(); | |
| 118 } | |
| 119 | |
| 120 #if !ENABLE(OILPAN) | |
| 121 void AnimationPlayer::detachFromTimeline() | |
| 122 { | |
| 123 dispose(); | |
| 124 m_timeline = nullptr; | |
| 125 } | |
| 126 #endif | |
| 127 | |
| 128 double AnimationPlayer::sourceEnd() const | |
| 129 { | |
| 130 return m_content ? m_content->endTimeInternal() : 0; | |
| 131 } | |
| 132 | |
| 133 bool AnimationPlayer::limited(double currentTime) const | |
| 134 { | |
| 135 return (m_playbackRate < 0 && currentTime <= 0) || (m_playbackRate > 0 && cu
rrentTime >= sourceEnd()); | |
| 136 } | |
| 137 | |
| 138 void AnimationPlayer::setCurrentTime(double newCurrentTime) | |
| 139 { | |
| 140 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
| 141 | |
| 142 m_currentTimePending = false; | |
| 143 setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand); | |
| 144 | |
| 145 if (calculatePlayState() == Finished) | |
| 146 m_startTime = calculateStartTime(newCurrentTime); | |
| 147 } | |
| 148 | |
| 149 void AnimationPlayer::setCurrentTimeInternal(double newCurrentTime, TimingUpdate
Reason reason) | |
| 150 { | |
| 151 ASSERT(std::isfinite(newCurrentTime)); | |
| 152 | |
| 153 bool oldHeld = m_held; | |
| 154 bool outdated = false; | |
| 155 bool isLimited = limited(newCurrentTime); | |
| 156 m_held = m_paused || !m_playbackRate || isLimited || std::isnan(m_startTime)
; | |
| 157 if (m_held) { | |
| 158 if (!oldHeld || m_holdTime != newCurrentTime) | |
| 159 outdated = true; | |
| 160 m_holdTime = newCurrentTime; | |
| 161 if (m_paused || !m_playbackRate) { | |
| 162 m_startTime = nullValue(); | |
| 163 } else if (isLimited && std::isnan(m_startTime) && reason == TimingUpdat
eForAnimationFrame) { | |
| 164 m_startTime = calculateStartTime(newCurrentTime); | |
| 165 } | |
| 166 } else { | |
| 167 m_holdTime = nullValue(); | |
| 168 m_startTime = calculateStartTime(newCurrentTime); | |
| 169 m_finished = false; | |
| 170 outdated = true; | |
| 171 } | |
| 172 | |
| 173 if (outdated) { | |
| 174 setOutdated(); | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 // Update timing to reflect updated animation clock due to tick | |
| 179 void AnimationPlayer::updateCurrentTimingState(TimingUpdateReason reason) | |
| 180 { | |
| 181 if (m_held) { | |
| 182 double newCurrentTime = m_holdTime; | |
| 183 if (playStateInternal() == Finished && !isNull(m_startTime) && m_timelin
e) { | |
| 184 // Add hystersis due to floating point error accumulation | |
| 185 if (!limited(calculateCurrentTime() + 0.001 * m_playbackRate)) { | |
| 186 // The current time became unlimited, eg. due to a backwards | |
| 187 // seek of the timeline. | |
| 188 newCurrentTime = calculateCurrentTime(); | |
| 189 } else if (!limited(m_holdTime)) { | |
| 190 // The hold time became unlimited, eg. due to the source content | |
| 191 // becoming longer. | |
| 192 newCurrentTime = clampTo<double>(calculateCurrentTime(), 0, sour
ceEnd()); | |
| 193 } | |
| 194 } | |
| 195 setCurrentTimeInternal(newCurrentTime, reason); | |
| 196 } else if (limited(calculateCurrentTime())) { | |
| 197 m_held = true; | |
| 198 m_holdTime = m_playbackRate < 0 ? 0 : sourceEnd(); | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 double AnimationPlayer::startTime(bool& isNull) const | |
| 203 { | |
| 204 double result = startTime(); | |
| 205 isNull = std::isnan(result); | |
| 206 return result; | |
| 207 } | |
| 208 | |
| 209 double AnimationPlayer::startTime() const | |
| 210 { | |
| 211 return m_startTime * 1000; | |
| 212 } | |
| 213 | |
| 214 double AnimationPlayer::currentTime(bool& isNull) | |
| 215 { | |
| 216 double result = currentTime(); | |
| 217 isNull = std::isnan(result); | |
| 218 return result; | |
| 219 } | |
| 220 | |
| 221 double AnimationPlayer::currentTime() | |
| 222 { | |
| 223 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
| 224 | |
| 225 if (m_currentTimePending || playStateInternal() == Idle) | |
| 226 return std::numeric_limits<double>::quiet_NaN(); | |
| 227 | |
| 228 return currentTimeInternal() * 1000; | |
| 229 } | |
| 230 | |
| 231 double AnimationPlayer::currentTimeInternal() const | |
| 232 { | |
| 233 double result = m_held ? m_holdTime : calculateCurrentTime(); | |
| 234 #if ENABLE(ASSERT) | |
| 235 const_cast<AnimationPlayer*>(this)->updateCurrentTimingState(TimingUpdateOnD
emand); | |
| 236 ASSERT(result == (m_held ? m_holdTime : calculateCurrentTime())); | |
| 237 #endif | |
| 238 return result; | |
| 239 } | |
| 240 | |
| 241 double AnimationPlayer::unlimitedCurrentTimeInternal() const | |
| 242 { | |
| 243 #if ENABLE(ASSERT) | |
| 244 currentTimeInternal(); | |
| 245 #endif | |
| 246 return playStateInternal() == Paused || isNull(m_startTime) | |
| 247 ? currentTimeInternal() | |
| 248 : calculateCurrentTime(); | |
| 249 } | |
| 250 | |
| 251 void AnimationPlayer::preCommit(int compositorGroup, bool startOnCompositor) | |
| 252 { | |
| 253 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, DoNotSetCompos
itorPending); | |
| 254 | |
| 255 bool softChange = m_compositorState && (paused() || m_compositorState->playb
ackRate != m_playbackRate); | |
| 256 bool hardChange = m_compositorState && (m_compositorState->sourceChanged ||
m_compositorState->startTime != m_startTime); | |
| 257 | |
| 258 // FIXME: softChange && !hardChange should generate a Pause/ThenStart, | |
| 259 // not a Cancel, but we can't communicate these to the compositor yet. | |
| 260 | |
| 261 bool changed = softChange || hardChange; | |
| 262 bool shouldCancel = (!playing() && m_compositorState) || changed; | |
| 263 bool shouldStart = playing() && (!m_compositorState || changed); | |
| 264 | |
| 265 if (shouldCancel) { | |
| 266 cancelAnimationOnCompositor(); | |
| 267 m_compositorState = nullptr; | |
| 268 } | |
| 269 | |
| 270 if (m_compositorState && m_compositorState->pendingAction == Start) { | |
| 271 // Still waiting for a start time. | |
| 272 return; | |
| 273 } | |
| 274 | |
| 275 ASSERT(!m_compositorState || !std::isnan(m_compositorState->startTime)); | |
| 276 | |
| 277 if (!shouldStart) { | |
| 278 m_currentTimePending = false; | |
| 279 } | |
| 280 | |
| 281 if (shouldStart) { | |
| 282 m_compositorGroup = compositorGroup; | |
| 283 if (startOnCompositor) { | |
| 284 if (isCandidateForAnimationOnCompositor()) | |
| 285 createCompositorPlayer(); | |
| 286 | |
| 287 if (maybeStartAnimationOnCompositor()) | |
| 288 m_compositorState = adoptPtr(new CompositorState(*this)); | |
| 289 else | |
| 290 cancelIncompatibleAnimationsOnCompositor(); | |
| 291 } | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 void AnimationPlayer::postCommit(double timelineTime) | |
| 296 { | |
| 297 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, DoNotSetCompos
itorPending); | |
| 298 | |
| 299 m_compositorPending = false; | |
| 300 | |
| 301 if (!m_compositorState || m_compositorState->pendingAction == None) | |
| 302 return; | |
| 303 | |
| 304 switch (m_compositorState->pendingAction) { | |
| 305 case Start: | |
| 306 if (!std::isnan(m_compositorState->startTime)) { | |
| 307 ASSERT(m_startTime == m_compositorState->startTime); | |
| 308 m_compositorState->pendingAction = None; | |
| 309 } | |
| 310 break; | |
| 311 case Pause: | |
| 312 case PauseThenStart: | |
| 313 ASSERT(std::isnan(m_startTime)); | |
| 314 m_compositorState->pendingAction = None; | |
| 315 setCurrentTimeInternal((timelineTime - m_compositorState->startTime) * m
_playbackRate, TimingUpdateForAnimationFrame); | |
| 316 m_currentTimePending = false; | |
| 317 break; | |
| 318 default: | |
| 319 ASSERT_NOT_REACHED(); | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 void AnimationPlayer::notifyCompositorStartTime(double timelineTime) | |
| 324 { | |
| 325 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, DoNotSetCompos
itorPending); | |
| 326 | |
| 327 if (m_compositorState) { | |
| 328 ASSERT(m_compositorState->pendingAction == Start); | |
| 329 ASSERT(std::isnan(m_compositorState->startTime)); | |
| 330 | |
| 331 double initialCompositorHoldTime = m_compositorState->holdTime; | |
| 332 m_compositorState->pendingAction = None; | |
| 333 m_compositorState->startTime = timelineTime + currentTimeInternal() / -m
_playbackRate; | |
| 334 | |
| 335 if (m_startTime == timelineTime) { | |
| 336 // The start time was set to the incoming compositor start time. | |
| 337 // Unlikely, but possible. | |
| 338 // FIXME: Depending on what changed above this might still be pendin
g. | |
| 339 // Maybe... | |
| 340 m_currentTimePending = false; | |
| 341 return; | |
| 342 } | |
| 343 | |
| 344 if (!std::isnan(m_startTime) || currentTimeInternal() != initialComposit
orHoldTime) { | |
| 345 // A new start time or current time was set while starting. | |
| 346 setCompositorPending(true); | |
| 347 return; | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 notifyStartTime(timelineTime); | |
| 352 } | |
| 353 | |
| 354 void AnimationPlayer::notifyStartTime(double timelineTime) | |
| 355 { | |
| 356 if (playing()) { | |
| 357 ASSERT(std::isnan(m_startTime)); | |
| 358 ASSERT(m_held); | |
| 359 | |
| 360 if (m_playbackRate == 0) { | |
| 361 setStartTimeInternal(timelineTime); | |
| 362 } else { | |
| 363 setStartTimeInternal(timelineTime + currentTimeInternal() / -m_playb
ackRate); | |
| 364 } | |
| 365 | |
| 366 // FIXME: This avoids marking this player as outdated needlessly when a
start time | |
| 367 // is notified, but we should refactor how outdating works to avoid this
. | |
| 368 m_outdated = false; | |
| 369 | |
| 370 m_currentTimePending = false; | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 bool AnimationPlayer::affects(const Element& element, CSSPropertyID property) co
nst | |
| 375 { | |
| 376 if (!m_content || !m_content->isAnimation()) | |
| 377 return false; | |
| 378 | |
| 379 const Animation* animation = toAnimation(m_content.get()); | |
| 380 return (animation->target() == &element) && animation->affects(PropertyHandl
e(property)); | |
| 381 } | |
| 382 | |
| 383 double AnimationPlayer::calculateStartTime(double currentTime) const | |
| 384 { | |
| 385 return m_timeline->effectiveTime() - currentTime / m_playbackRate; | |
| 386 } | |
| 387 | |
| 388 double AnimationPlayer::calculateCurrentTime() const | |
| 389 { | |
| 390 if (isNull(m_startTime) || !m_timeline) | |
| 391 return 0; | |
| 392 return (m_timeline->effectiveTime() - m_startTime) * m_playbackRate; | |
| 393 } | |
| 394 | |
| 395 void AnimationPlayer::setStartTime(double startTime) | |
| 396 { | |
| 397 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
| 398 | |
| 399 if (m_paused || playStateInternal() == Idle) | |
| 400 return; | |
| 401 if (startTime == m_startTime) | |
| 402 return; | |
| 403 | |
| 404 m_currentTimePending = false; | |
| 405 setStartTimeInternal(startTime / 1000); | |
| 406 } | |
| 407 | |
| 408 void AnimationPlayer::setStartTimeInternal(double newStartTime) | |
| 409 { | |
| 410 ASSERT(!m_paused); | |
| 411 ASSERT(std::isfinite(newStartTime)); | |
| 412 ASSERT(newStartTime != m_startTime); | |
| 413 | |
| 414 bool hadStartTime = hasStartTime(); | |
| 415 double previousCurrentTime = currentTimeInternal(); | |
| 416 m_startTime = newStartTime; | |
| 417 if (m_held && m_playbackRate) { | |
| 418 // If held, the start time would still be derrived from the hold time. | |
| 419 // Force a new, limited, current time. | |
| 420 m_held = false; | |
| 421 double currentTime = calculateCurrentTime(); | |
| 422 if (m_playbackRate > 0 && currentTime > sourceEnd()) { | |
| 423 currentTime = sourceEnd(); | |
| 424 } else if (m_playbackRate < 0 && currentTime < 0) { | |
| 425 currentTime = 0; | |
| 426 } | |
| 427 setCurrentTimeInternal(currentTime, TimingUpdateOnDemand); | |
| 428 } | |
| 429 updateCurrentTimingState(TimingUpdateOnDemand); | |
| 430 double newCurrentTime = currentTimeInternal(); | |
| 431 | |
| 432 if (previousCurrentTime != newCurrentTime) { | |
| 433 setOutdated(); | |
| 434 } else if (!hadStartTime && m_timeline) { | |
| 435 // Even though this player is not outdated, time to effect change is | |
| 436 // infinity until start time is set. | |
| 437 m_timeline->wake(); | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 void AnimationPlayer::setSource(AnimationNode* newSource) | |
| 442 { | |
| 443 if (m_content == newSource) | |
| 444 return; | |
| 445 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, SetCompositorP
endingWithSourceChanged); | |
| 446 | |
| 447 double storedCurrentTime = currentTimeInternal(); | |
| 448 if (m_content) | |
| 449 m_content->detach(); | |
| 450 m_content = newSource; | |
| 451 if (newSource) { | |
| 452 // FIXME: This logic needs to be updated once groups are implemented | |
| 453 if (newSource->player()) { | |
| 454 newSource->player()->cancel(); | |
| 455 newSource->player()->setSource(0); | |
| 456 } | |
| 457 newSource->attach(this); | |
| 458 setOutdated(); | |
| 459 } | |
| 460 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); | |
| 461 } | |
| 462 | |
| 463 const char* AnimationPlayer::playStateString(AnimationPlayState playState) | |
| 464 { | |
| 465 switch (playState) { | |
| 466 case Idle: | |
| 467 return "idle"; | |
| 468 case Pending: | |
| 469 return "pending"; | |
| 470 case Running: | |
| 471 return "running"; | |
| 472 case Paused: | |
| 473 return "paused"; | |
| 474 case Finished: | |
| 475 return "finished"; | |
| 476 default: | |
| 477 ASSERT_NOT_REACHED(); | |
| 478 return ""; | |
| 479 } | |
| 480 } | |
| 481 | |
| 482 AnimationPlayer::AnimationPlayState AnimationPlayer::playStateInternal() const | |
| 483 { | |
| 484 return m_playState; | |
| 485 } | |
| 486 | |
| 487 AnimationPlayer::AnimationPlayState AnimationPlayer::calculatePlayState() | |
| 488 { | |
| 489 if (m_playState == Idle) | |
| 490 return Idle; | |
| 491 if (m_currentTimePending || (isNull(m_startTime) && !m_paused && m_playbackR
ate != 0)) | |
| 492 return Pending; | |
| 493 if (m_paused) | |
| 494 return Paused; | |
| 495 if (limited()) | |
| 496 return Finished; | |
| 497 return Running; | |
| 498 } | |
| 499 | |
| 500 void AnimationPlayer::pause() | |
| 501 { | |
| 502 if (m_paused) | |
| 503 return; | |
| 504 | |
| 505 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
| 506 | |
| 507 if (playing()) { | |
| 508 m_currentTimePending = true; | |
| 509 } | |
| 510 m_paused = true; | |
| 511 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); | |
| 512 } | |
| 513 | |
| 514 void AnimationPlayer::unpause() | |
| 515 { | |
| 516 if (!m_paused) | |
| 517 return; | |
| 518 | |
| 519 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
| 520 | |
| 521 m_currentTimePending = true; | |
| 522 unpauseInternal(); | |
| 523 } | |
| 524 | |
| 525 void AnimationPlayer::unpauseInternal() | |
| 526 { | |
| 527 if (!m_paused) | |
| 528 return; | |
| 529 m_paused = false; | |
| 530 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); | |
| 531 } | |
| 532 | |
| 533 void AnimationPlayer::play() | |
| 534 { | |
| 535 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
| 536 | |
| 537 if (!playing()) | |
| 538 m_startTime = nullValue(); | |
| 539 | |
| 540 if (playStateInternal() == Idle) { | |
| 541 // We may not go into the pending state, but setting it to something oth
er | |
| 542 // than Idle here will force an update. | |
| 543 ASSERT(isNull(m_startTime)); | |
| 544 m_playState = Pending; | |
| 545 m_held = true; | |
| 546 m_holdTime = 0; | |
| 547 } | |
| 548 | |
| 549 m_finished = false; | |
| 550 unpauseInternal(); | |
| 551 if (!m_content) | |
| 552 return; | |
| 553 double currentTime = this->currentTimeInternal(); | |
| 554 if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd())) { | |
| 555 m_startTime = nullValue(); | |
| 556 setCurrentTimeInternal(0, TimingUpdateOnDemand); | |
| 557 } else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEn
d())) { | |
| 558 m_startTime = nullValue(); | |
| 559 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); | |
| 560 } | |
| 561 } | |
| 562 | |
| 563 void AnimationPlayer::reverse() | |
| 564 { | |
| 565 if (!m_playbackRate) { | |
| 566 return; | |
| 567 } | |
| 568 | |
| 569 setPlaybackRateInternal(-m_playbackRate); | |
| 570 play(); | |
| 571 } | |
| 572 | |
| 573 void AnimationPlayer::finish(ExceptionState& exceptionState) | |
| 574 { | |
| 575 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
| 576 | |
| 577 if (!m_playbackRate || playStateInternal() == Idle) { | |
| 578 return; | |
| 579 } | |
| 580 if (m_playbackRate > 0 && sourceEnd() == std::numeric_limits<double>::infini
ty()) { | |
| 581 exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has
source content whose end time is infinity."); | |
| 582 return; | |
| 583 } | |
| 584 | |
| 585 double newCurrentTime = m_playbackRate < 0 ? 0 : sourceEnd(); | |
| 586 setCurrentTimeInternal(newCurrentTime, TimingUpdateOnDemand); | |
| 587 if (!paused()) { | |
| 588 m_startTime = calculateStartTime(newCurrentTime); | |
| 589 } | |
| 590 | |
| 591 m_currentTimePending = false; | |
| 592 ASSERT(playStateInternal() != Idle); | |
| 593 ASSERT(limited()); | |
| 594 } | |
| 595 | |
| 596 ScriptPromise AnimationPlayer::finished(ScriptState* scriptState) | |
| 597 { | |
| 598 if (!m_finishedPromise) { | |
| 599 m_finishedPromise = new AnimationPlayerPromise(scriptState->executionCon
text(), this, AnimationPlayerPromise::Finished); | |
| 600 if (playStateInternal() == Finished) | |
| 601 m_finishedPromise->resolve(this); | |
| 602 } | |
| 603 return m_finishedPromise->promise(scriptState->world()); | |
| 604 } | |
| 605 | |
| 606 ScriptPromise AnimationPlayer::ready(ScriptState* scriptState) | |
| 607 { | |
| 608 if (!m_readyPromise) { | |
| 609 m_readyPromise = new AnimationPlayerPromise(scriptState->executionContex
t(), this, AnimationPlayerPromise::Ready); | |
| 610 if (playStateInternal() != Pending) | |
| 611 m_readyPromise->resolve(this); | |
| 612 } | |
| 613 return m_readyPromise->promise(scriptState->world()); | |
| 614 } | |
| 615 | |
| 616 const AtomicString& AnimationPlayer::interfaceName() const | |
| 617 { | |
| 618 return EventTargetNames::AnimationPlayer; | |
| 619 } | |
| 620 | |
| 621 ExecutionContext* AnimationPlayer::executionContext() const | |
| 622 { | |
| 623 return ActiveDOMObject::executionContext(); | |
| 624 } | |
| 625 | |
| 626 bool AnimationPlayer::hasPendingActivity() const | |
| 627 { | |
| 628 return m_pendingFinishedEvent || (!m_finished && hasEventListeners(EventType
Names::finish)); | |
| 629 } | |
| 630 | |
| 631 void AnimationPlayer::stop() | |
| 632 { | |
| 633 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
| 634 | |
| 635 m_finished = true; | |
| 636 m_pendingFinishedEvent = nullptr; | |
| 637 } | |
| 638 | |
| 639 bool AnimationPlayer::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event) | |
| 640 { | |
| 641 if (m_pendingFinishedEvent == event) | |
| 642 m_pendingFinishedEvent = nullptr; | |
| 643 return EventTargetWithInlineData::dispatchEvent(event); | |
| 644 } | |
| 645 | |
| 646 double AnimationPlayer::playbackRate() const | |
| 647 { | |
| 648 return m_playbackRate; | |
| 649 } | |
| 650 | |
| 651 void AnimationPlayer::setPlaybackRate(double playbackRate) | |
| 652 { | |
| 653 if (playbackRate == m_playbackRate) | |
| 654 return; | |
| 655 | |
| 656 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
| 657 | |
| 658 setPlaybackRateInternal(playbackRate); | |
| 659 } | |
| 660 | |
| 661 void AnimationPlayer::setPlaybackRateInternal(double playbackRate) | |
| 662 { | |
| 663 ASSERT(std::isfinite(playbackRate)); | |
| 664 ASSERT(playbackRate != m_playbackRate); | |
| 665 | |
| 666 if (!limited() && !paused() && hasStartTime()) | |
| 667 m_currentTimePending = true; | |
| 668 | |
| 669 double storedCurrentTime = currentTimeInternal(); | |
| 670 if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && play
backRate <= 0)) | |
| 671 m_finished = false; | |
| 672 | |
| 673 m_playbackRate = playbackRate; | |
| 674 m_startTime = std::numeric_limits<double>::quiet_NaN(); | |
| 675 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); | |
| 676 } | |
| 677 | |
| 678 void AnimationPlayer::setOutdated() | |
| 679 { | |
| 680 m_outdated = true; | |
| 681 if (m_timeline) | |
| 682 m_timeline->setOutdatedAnimationPlayer(this); | |
| 683 } | |
| 684 | |
| 685 bool AnimationPlayer::canStartAnimationOnCompositor() const | |
| 686 { | |
| 687 // FIXME: Timeline playback rates should be compositable | |
| 688 if (m_playbackRate == 0 || (std::isinf(sourceEnd()) && m_playbackRate < 0) |
| (timeline() && timeline()->playbackRate() != 1)) | |
| 689 return false; | |
| 690 | |
| 691 return m_timeline && m_content && m_content->isAnimation() && playing(); | |
| 692 } | |
| 693 | |
| 694 bool AnimationPlayer::isCandidateForAnimationOnCompositor() const | |
| 695 { | |
| 696 if (!canStartAnimationOnCompositor()) | |
| 697 return false; | |
| 698 | |
| 699 return toAnimation(m_content.get())->isCandidateForAnimationOnCompositor(m_p
laybackRate); | |
| 700 } | |
| 701 | |
| 702 bool AnimationPlayer::maybeStartAnimationOnCompositor() | |
| 703 { | |
| 704 if (!canStartAnimationOnCompositor()) | |
| 705 return false; | |
| 706 | |
| 707 bool reversed = m_playbackRate < 0; | |
| 708 | |
| 709 double startTime = timeline()->zeroTime() + startTimeInternal(); | |
| 710 if (reversed) { | |
| 711 startTime -= sourceEnd() / fabs(m_playbackRate); | |
| 712 } | |
| 713 | |
| 714 double timeOffset = 0; | |
| 715 if (std::isnan(startTime)) { | |
| 716 timeOffset = reversed ? sourceEnd() - currentTimeInternal() : currentTim
eInternal(); | |
| 717 timeOffset = timeOffset / fabs(m_playbackRate); | |
| 718 } | |
| 719 ASSERT(m_compositorGroup != 0); | |
| 720 return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(m_compo
sitorGroup, startTime, timeOffset, m_playbackRate); | |
| 721 } | |
| 722 | |
| 723 void AnimationPlayer::setCompositorPending(bool sourceChanged) | |
| 724 { | |
| 725 // FIXME: Animation could notify this directly? | |
| 726 if (!hasActiveAnimationsOnCompositor()) { | |
| 727 destroyCompositorPlayer(); | |
| 728 m_compositorState.release(); | |
| 729 } | |
| 730 if (sourceChanged && m_compositorState) { | |
| 731 m_compositorState->sourceChanged = true; | |
| 732 } | |
| 733 if (m_compositorPending || m_isPausedForTesting) { | |
| 734 return; | |
| 735 } | |
| 736 | |
| 737 if (sourceChanged || !m_compositorState | |
| 738 || !playing() || m_compositorState->playbackRate != m_playbackRate | |
| 739 || m_compositorState->startTime != m_startTime) { | |
| 740 m_compositorPending = true; | |
| 741 ASSERT(timeline()); | |
| 742 ASSERT(timeline()->document()); | |
| 743 timeline()->document()->compositorPendingAnimations().add(this); | |
| 744 } | |
| 745 } | |
| 746 | |
| 747 void AnimationPlayer::cancelAnimationOnCompositor() | |
| 748 { | |
| 749 if (hasActiveAnimationsOnCompositor()) | |
| 750 toAnimation(m_content.get())->cancelAnimationOnCompositor(); | |
| 751 | |
| 752 destroyCompositorPlayer(); | |
| 753 } | |
| 754 | |
| 755 void AnimationPlayer::restartAnimationOnCompositor() | |
| 756 { | |
| 757 if (hasActiveAnimationsOnCompositor()) | |
| 758 toAnimation(m_content.get())->restartAnimationOnCompositor(); | |
| 759 } | |
| 760 | |
| 761 void AnimationPlayer::cancelIncompatibleAnimationsOnCompositor() | |
| 762 { | |
| 763 if (m_content && m_content->isAnimation()) | |
| 764 toAnimation(m_content.get())->cancelIncompatibleAnimationsOnCompositor()
; | |
| 765 } | |
| 766 | |
| 767 bool AnimationPlayer::hasActiveAnimationsOnCompositor() | |
| 768 { | |
| 769 if (!m_content || !m_content->isAnimation()) | |
| 770 return false; | |
| 771 | |
| 772 return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor(); | |
| 773 } | |
| 774 | |
| 775 bool AnimationPlayer::update(TimingUpdateReason reason) | |
| 776 { | |
| 777 if (!m_timeline) | |
| 778 return false; | |
| 779 | |
| 780 PlayStateUpdateScope updateScope(*this, reason, DoNotSetCompositorPending); | |
| 781 | |
| 782 m_outdated = false; | |
| 783 bool idle = playStateInternal() == Idle; | |
| 784 | |
| 785 if (m_content) { | |
| 786 double inheritedTime = idle || isNull(m_timeline->currentTimeInternal())
? nullValue() : currentTimeInternal(); | |
| 787 // Special case for end-exclusivity when playing backwards. | |
| 788 if (inheritedTime == 0 && m_playbackRate < 0) | |
| 789 inheritedTime = -1; | |
| 790 m_content->updateInheritedTime(inheritedTime, reason); | |
| 791 } | |
| 792 | |
| 793 if ((idle || limited()) && !m_finished) { | |
| 794 if (reason == TimingUpdateForAnimationFrame && (idle || hasStartTime()))
{ | |
| 795 const AtomicString& eventType = EventTypeNames::finish; | |
| 796 if (executionContext() && hasEventListeners(eventType)) { | |
| 797 double eventCurrentTime = currentTimeInternal() * 1000; | |
| 798 m_pendingFinishedEvent = AnimationPlayerEvent::create(eventType,
eventCurrentTime, timeline()->currentTime()); | |
| 799 m_pendingFinishedEvent->setTarget(this); | |
| 800 m_pendingFinishedEvent->setCurrentTarget(this); | |
| 801 m_timeline->document()->enqueueAnimationFrameEvent(m_pendingFini
shedEvent); | |
| 802 } | |
| 803 m_finished = true; | |
| 804 } | |
| 805 } | |
| 806 ASSERT(!m_outdated); | |
| 807 return !m_finished; | |
| 808 } | |
| 809 | |
| 810 double AnimationPlayer::timeToEffectChange() | |
| 811 { | |
| 812 ASSERT(!m_outdated); | |
| 813 if (m_held || !hasStartTime()) | |
| 814 return std::numeric_limits<double>::infinity(); | |
| 815 if (!m_content) | |
| 816 return -currentTimeInternal() / m_playbackRate; | |
| 817 double result = m_playbackRate > 0 | |
| 818 ? m_content->timeToForwardsEffectChange() / m_playbackRate | |
| 819 : m_content->timeToReverseEffectChange() / -m_playbackRate; | |
| 820 return !hasActiveAnimationsOnCompositor() && m_content->phase() == Animation
Node::PhaseActive | |
| 821 ? 0 | |
| 822 : result; | |
| 823 } | |
| 824 | |
| 825 void AnimationPlayer::cancel() | |
| 826 { | |
| 827 PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); | |
| 828 | |
| 829 if (playStateInternal() == Idle) | |
| 830 return; | |
| 831 | |
| 832 m_holdTime = currentTimeInternal(); | |
| 833 m_held = true; | |
| 834 // TODO | |
| 835 m_playState = Idle; | |
| 836 m_startTime = nullValue(); | |
| 837 m_currentTimePending = false; | |
| 838 | |
| 839 InspectorInstrumentation::didCancelAnimationPlayer(timeline()->document(), t
his); | |
| 840 } | |
| 841 | |
| 842 void AnimationPlayer::beginUpdatingState() | |
| 843 { | |
| 844 // Nested calls are not allowed! | |
| 845 ASSERT(!m_stateIsBeingUpdated); | |
| 846 m_stateIsBeingUpdated = true; | |
| 847 } | |
| 848 | |
| 849 void AnimationPlayer::endUpdatingState() | |
| 850 { | |
| 851 ASSERT(m_stateIsBeingUpdated); | |
| 852 m_stateIsBeingUpdated = false; | |
| 853 } | |
| 854 | |
| 855 void AnimationPlayer::createCompositorPlayer() | |
| 856 { | |
| 857 if (RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled() && !m_comp
ositorPlayer && Platform::current()->compositorSupport()) { | |
| 858 m_compositorPlayer = adoptPtr(Platform::current()->compositorSupport()->
createAnimationPlayer()); | |
| 859 ASSERT(m_compositorPlayer); | |
| 860 m_compositorPlayer->setAnimationDelegate(this); | |
| 861 attachCompositorTimeline(); | |
| 862 } | |
| 863 | |
| 864 attachCompositedLayers(); | |
| 865 } | |
| 866 | |
| 867 void AnimationPlayer::destroyCompositorPlayer() | |
| 868 { | |
| 869 detachCompositedLayers(); | |
| 870 | |
| 871 if (m_compositorPlayer) { | |
| 872 detachCompositorTimeline(); | |
| 873 m_compositorPlayer->setAnimationDelegate(nullptr); | |
| 874 } | |
| 875 m_compositorPlayer.clear(); | |
| 876 } | |
| 877 | |
| 878 void AnimationPlayer::attachCompositorTimeline() | |
| 879 { | |
| 880 if (m_compositorPlayer) { | |
| 881 WebCompositorAnimationTimeline* timeline = m_timeline ? m_timeline->comp
ositorTimeline() : nullptr; | |
| 882 if (timeline) | |
| 883 timeline->playerAttached(*this); | |
| 884 } | |
| 885 } | |
| 886 | |
| 887 void AnimationPlayer::detachCompositorTimeline() | |
| 888 { | |
| 889 if (m_compositorPlayer) { | |
| 890 WebCompositorAnimationTimeline* timeline = m_timeline ? m_timeline->comp
ositorTimeline() : nullptr; | |
| 891 if (timeline) | |
| 892 timeline->playerDestroyed(*this); | |
| 893 } | |
| 894 } | |
| 895 | |
| 896 void AnimationPlayer::attachCompositedLayers() | |
| 897 { | |
| 898 if (!RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled() || !m_com
positorPlayer) | |
| 899 return; | |
| 900 | |
| 901 ASSERT(m_content); | |
| 902 ASSERT(m_content->isAnimation()); | |
| 903 | |
| 904 if (toAnimation(m_content.get())->canAttachCompositedLayers()) | |
| 905 toAnimation(m_content.get())->attachCompositedLayers(); | |
| 906 } | |
| 907 | |
| 908 void AnimationPlayer::detachCompositedLayers() | |
| 909 { | |
| 910 if (m_compositorPlayer && m_compositorPlayer->isLayerAttached()) | |
| 911 m_compositorPlayer->detachLayer(); | |
| 912 } | |
| 913 | |
| 914 void AnimationPlayer::notifyAnimationStarted(double monotonicTime, int group) | |
| 915 { | |
| 916 ASSERT(RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled()); | |
| 917 timeline()->document()->compositorPendingAnimations().notifyCompositorAnimat
ionStarted(monotonicTime, group); | |
| 918 } | |
| 919 | |
| 920 AnimationPlayer::PlayStateUpdateScope::PlayStateUpdateScope(AnimationPlayer& pla
yer, TimingUpdateReason reason, CompositorPendingChange compositorPendingChange) | |
| 921 : m_player(player) | |
| 922 , m_initialPlayState(m_player->playStateInternal()) | |
| 923 , m_compositorPendingChange(compositorPendingChange) | |
| 924 { | |
| 925 m_player->beginUpdatingState(); | |
| 926 m_player->updateCurrentTimingState(reason); | |
| 927 } | |
| 928 | |
| 929 AnimationPlayer::PlayStateUpdateScope::~PlayStateUpdateScope() | |
| 930 { | |
| 931 AnimationPlayState oldPlayState = m_initialPlayState; | |
| 932 AnimationPlayState newPlayState = m_player->calculatePlayState(); | |
| 933 | |
| 934 m_player->m_playState = newPlayState; | |
| 935 if (oldPlayState != newPlayState) { | |
| 936 bool wasActive = oldPlayState == Pending || oldPlayState == Running; | |
| 937 bool isActive = newPlayState == Pending || newPlayState == Running; | |
| 938 if (!wasActive && isActive) | |
| 939 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("blink.animations," TRACE_DISABLED
_BY_DEFAULT("devtools.timeline"), "Animation", m_player, "data", InspectorAnimat
ionEvent::data(*m_player)); | |
| 940 else if (wasActive && !isActive) | |
| 941 TRACE_EVENT_NESTABLE_ASYNC_END1("blink.animations," TRACE_DISABLED_B
Y_DEFAULT("devtools.timeline"), "Animation", m_player, "endData", InspectorAnima
tionStateEvent::data(*m_player)); | |
| 942 else | |
| 943 TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("blink.animations," TRACE_DISABL
ED_BY_DEFAULT("devtools.timeline"), "Animation", m_player, "data", InspectorAnim
ationStateEvent::data(*m_player)); | |
| 944 } | |
| 945 | |
| 946 // Ordering is important, the ready promise should resolve/reject before | |
| 947 // the finished promise. | |
| 948 if (m_player->m_readyPromise && newPlayState != oldPlayState) { | |
| 949 if (newPlayState == Idle) { | |
| 950 if (m_player->m_readyPromise->state() == AnimationPlayerPromise::Pen
ding) { | |
| 951 m_player->m_readyPromise->reject(DOMException::create(AbortError
)); | |
| 952 } | |
| 953 m_player->m_readyPromise->reset(); | |
| 954 m_player->m_readyPromise->resolve(m_player); | |
| 955 } else if (oldPlayState == Pending) { | |
| 956 m_player->m_readyPromise->resolve(m_player); | |
| 957 } else if (newPlayState == Pending) { | |
| 958 ASSERT(m_player->m_readyPromise->state() != AnimationPlayerPromise::
Pending); | |
| 959 m_player->m_readyPromise->reset(); | |
| 960 } | |
| 961 } | |
| 962 | |
| 963 if (m_player->m_finishedPromise && newPlayState != oldPlayState) { | |
| 964 if (newPlayState == Idle) { | |
| 965 if (m_player->m_finishedPromise->state() == AnimationPlayerPromise::
Pending) { | |
| 966 m_player->m_finishedPromise->reject(DOMException::create(AbortEr
ror)); | |
| 967 } | |
| 968 m_player->m_finishedPromise->reset(); | |
| 969 } else if (newPlayState == Finished) { | |
| 970 m_player->m_finishedPromise->resolve(m_player); | |
| 971 } else if (oldPlayState == Finished) { | |
| 972 m_player->m_finishedPromise->reset(); | |
| 973 } | |
| 974 } | |
| 975 | |
| 976 if (oldPlayState != newPlayState && (oldPlayState == Idle || newPlayState ==
Idle)) { | |
| 977 m_player->setOutdated(); | |
| 978 } | |
| 979 | |
| 980 #if ENABLE(ASSERT) | |
| 981 // Verify that current time is up to date. | |
| 982 m_player->currentTimeInternal(); | |
| 983 #endif | |
| 984 | |
| 985 switch (m_compositorPendingChange) { | |
| 986 case SetCompositorPending: | |
| 987 m_player->setCompositorPending(); | |
| 988 break; | |
| 989 case SetCompositorPendingWithSourceChanged: | |
| 990 m_player->setCompositorPending(true); | |
| 991 break; | |
| 992 case DoNotSetCompositorPending: | |
| 993 break; | |
| 994 default: | |
| 995 ASSERT_NOT_REACHED(); | |
| 996 break; | |
| 997 } | |
| 998 m_player->endUpdatingState(); | |
| 999 | |
| 1000 if (oldPlayState != newPlayState && newPlayState == Running) | |
| 1001 InspectorInstrumentation::didCreateAnimationPlayer(m_player->timeline()-
>document(), m_player); | |
| 1002 } | |
| 1003 | |
| 1004 bool AnimationPlayer::addEventListener(const AtomicString& eventType, PassRefPtr
<EventListener> listener, bool useCapture) | |
| 1005 { | |
| 1006 if (eventType == EventTypeNames::finish) | |
| 1007 UseCounter::count(executionContext(), UseCounter::AnimationPlayerFinishE
vent); | |
| 1008 return EventTargetWithInlineData::addEventListener(eventType, listener, useC
apture); | |
| 1009 } | |
| 1010 | |
| 1011 void AnimationPlayer::pauseForTesting(double pauseTime) | |
| 1012 { | |
| 1013 RELEASE_ASSERT(!paused()); | |
| 1014 setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand); | |
| 1015 if (hasActiveAnimationsOnCompositor()) | |
| 1016 toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(curre
ntTimeInternal()); | |
| 1017 m_isPausedForTesting = true; | |
| 1018 pause(); | |
| 1019 } | |
| 1020 | |
| 1021 DEFINE_TRACE(AnimationPlayer) | |
| 1022 { | |
| 1023 visitor->trace(m_content); | |
| 1024 visitor->trace(m_timeline); | |
| 1025 visitor->trace(m_pendingFinishedEvent); | |
| 1026 visitor->trace(m_finishedPromise); | |
| 1027 visitor->trace(m_readyPromise); | |
| 1028 EventTargetWithInlineData::trace(visitor); | |
| 1029 ActiveDOMObject::trace(visitor); | |
| 1030 } | |
| 1031 | |
| 1032 } // namespace | |
| OLD | NEW |