| 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 "sky/engine/core/animation/AnimationPlayer.h" | |
| 32 | |
| 33 #include "sky/engine/core/animation/Animation.h" | |
| 34 #include "sky/engine/core/animation/AnimationTimeline.h" | |
| 35 #include "sky/engine/core/dom/Document.h" | |
| 36 #include "sky/engine/core/events/AnimationPlayerEvent.h" | |
| 37 | |
| 38 namespace blink { | |
| 39 | |
| 40 namespace { | |
| 41 | |
| 42 static unsigned nextSequenceNumber() | |
| 43 { | |
| 44 static unsigned next = 0; | |
| 45 return ++next; | |
| 46 } | |
| 47 | |
| 48 } | |
| 49 | |
| 50 PassRefPtr<AnimationPlayer> AnimationPlayer::create(ExecutionContext* executionC
ontext, AnimationTimeline& timeline, AnimationNode* content) | |
| 51 { | |
| 52 RefPtr<AnimationPlayer> player = adoptRef(new AnimationPlayer(executionConte
xt, timeline, content)); | |
| 53 timeline.document()->pendingAnimations().add(player.get()); | |
| 54 player->suspendIfNeeded(); | |
| 55 return player.release(); | |
| 56 } | |
| 57 | |
| 58 AnimationPlayer::AnimationPlayer(ExecutionContext* executionContext, AnimationTi
meline& timeline, AnimationNode* content) | |
| 59 : ActiveDOMObject(executionContext) | |
| 60 , m_playbackRate(1) | |
| 61 , m_startTime(nullValue()) | |
| 62 , m_holdTime(0) | |
| 63 , m_sequenceNumber(nextSequenceNumber()) | |
| 64 , m_content(content) | |
| 65 , m_timeline(&timeline) | |
| 66 , m_paused(false) | |
| 67 , m_held(true) | |
| 68 , m_isPausedForTesting(false) | |
| 69 , m_outdated(true) | |
| 70 , m_finished(false) | |
| 71 , m_pending(true) | |
| 72 , m_currentTimePending(false) | |
| 73 { | |
| 74 if (m_content) { | |
| 75 if (m_content->player()) | |
| 76 m_content->player()->cancel(); | |
| 77 m_content->attach(this); | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 AnimationPlayer::~AnimationPlayer() | |
| 82 { | |
| 83 if (m_content) | |
| 84 m_content->detach(); | |
| 85 if (m_timeline) | |
| 86 m_timeline->playerDestroyed(this); | |
| 87 } | |
| 88 | |
| 89 double AnimationPlayer::sourceEnd() const | |
| 90 { | |
| 91 return m_content ? m_content->endTimeInternal() : 0; | |
| 92 } | |
| 93 | |
| 94 bool AnimationPlayer::limited(double currentTime) const | |
| 95 { | |
| 96 return (m_playbackRate < 0 && currentTime <= 0) || (m_playbackRate > 0 && cu
rrentTime >= sourceEnd()); | |
| 97 } | |
| 98 | |
| 99 void AnimationPlayer::setCurrentTimeInternal(double newCurrentTime, TimingUpdate
Reason reason) | |
| 100 { | |
| 101 ASSERT(std::isfinite(newCurrentTime)); | |
| 102 | |
| 103 bool oldHeld = m_held; | |
| 104 bool outdated = false; | |
| 105 bool isLimited = limited(newCurrentTime); | |
| 106 m_held = m_paused || !m_playbackRate || isLimited || std::isnan(m_startTime)
; | |
| 107 if (m_held) { | |
| 108 if (!oldHeld || m_holdTime != newCurrentTime) | |
| 109 outdated = true; | |
| 110 m_holdTime = newCurrentTime; | |
| 111 if (m_paused || !m_playbackRate) { | |
| 112 m_startTime = nullValue(); | |
| 113 } else if (isLimited && std::isnan(m_startTime) && reason == TimingUpdat
eForAnimationFrame) { | |
| 114 m_startTime = calculateStartTime(newCurrentTime); | |
| 115 } | |
| 116 } else { | |
| 117 m_holdTime = nullValue(); | |
| 118 m_startTime = calculateStartTime(newCurrentTime); | |
| 119 m_finished = false; | |
| 120 outdated = true; | |
| 121 } | |
| 122 | |
| 123 if (outdated) { | |
| 124 setOutdated(); | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 // Update timing to reflect updated animation clock due to tick | |
| 129 void AnimationPlayer::updateCurrentTimingState(TimingUpdateReason reason) | |
| 130 { | |
| 131 if (m_held) { | |
| 132 setCurrentTimeInternal(m_holdTime, reason); | |
| 133 return; | |
| 134 } | |
| 135 if (!limited(calculateCurrentTime())) | |
| 136 return; | |
| 137 m_held = true; | |
| 138 m_holdTime = m_playbackRate < 0 ? 0 : sourceEnd(); | |
| 139 } | |
| 140 | |
| 141 double AnimationPlayer::startTime() const | |
| 142 { | |
| 143 return m_startTime * 1000; | |
| 144 } | |
| 145 | |
| 146 double AnimationPlayer::currentTime() | |
| 147 { | |
| 148 if (m_currentTimePending) | |
| 149 return std::numeric_limits<double>::quiet_NaN(); | |
| 150 return currentTimeInternal() * 1000; | |
| 151 } | |
| 152 | |
| 153 double AnimationPlayer::currentTimeInternal() | |
| 154 { | |
| 155 updateCurrentTimingState(TimingUpdateOnDemand); | |
| 156 if (m_held) | |
| 157 return m_holdTime; | |
| 158 return calculateCurrentTime(); | |
| 159 } | |
| 160 | |
| 161 void AnimationPlayer::preCommit() | |
| 162 { | |
| 163 if (!playing()) { | |
| 164 m_currentTimePending = false; | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 void AnimationPlayer::postCommit(double timelineTime) | |
| 169 { | |
| 170 m_pending = false; | |
| 171 } | |
| 172 | |
| 173 void AnimationPlayer::notifyCompositorStartTime(double timelineTime) | |
| 174 { | |
| 175 if (playing()) { | |
| 176 ASSERT(std::isnan(m_startTime)); | |
| 177 ASSERT(m_held); | |
| 178 | |
| 179 if (m_playbackRate == 0) { | |
| 180 setStartTimeInternal(timelineTime); | |
| 181 } else { | |
| 182 setStartTimeInternal(timelineTime + currentTimeInternal() / -m_playb
ackRate); | |
| 183 } | |
| 184 | |
| 185 // FIXME: This avoids marking this player as outdated needlessly when a
start time | |
| 186 // is notified, but we should refactor how outdating works to avoid this
. | |
| 187 m_outdated = false; | |
| 188 | |
| 189 m_currentTimePending = false; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 double AnimationPlayer::calculateStartTime(double currentTime) const | |
| 194 { | |
| 195 return m_timeline->effectiveTime() - currentTime / m_playbackRate; | |
| 196 } | |
| 197 | |
| 198 double AnimationPlayer::calculateCurrentTime() const | |
| 199 { | |
| 200 ASSERT(!m_held); | |
| 201 if (isNull(m_startTime) || !m_timeline) | |
| 202 return 0; | |
| 203 return (m_timeline->effectiveTime() - m_startTime) * m_playbackRate; | |
| 204 } | |
| 205 | |
| 206 void AnimationPlayer::setCurrentTime(double newCurrentTime) | |
| 207 { | |
| 208 if (!std::isfinite(newCurrentTime)) | |
| 209 return; | |
| 210 | |
| 211 setPending(); | |
| 212 | |
| 213 // Setting current time while pending forces a start time. | |
| 214 if (m_currentTimePending) { | |
| 215 m_startTime = 0; | |
| 216 m_currentTimePending = false; | |
| 217 } | |
| 218 | |
| 219 setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand); | |
| 220 } | |
| 221 | |
| 222 void AnimationPlayer::setStartTime(double startTime) | |
| 223 { | |
| 224 if (m_paused) // FIXME: Should this throw an exception? | |
| 225 return; | |
| 226 if (!std::isfinite(startTime)) | |
| 227 return; | |
| 228 if (startTime == m_startTime) | |
| 229 return; | |
| 230 | |
| 231 setPending(); | |
| 232 m_currentTimePending = false; | |
| 233 setStartTimeInternal(startTime / 1000); | |
| 234 } | |
| 235 | |
| 236 void AnimationPlayer::setStartTimeInternal(double newStartTime) | |
| 237 { | |
| 238 ASSERT(!m_paused); | |
| 239 ASSERT(std::isfinite(newStartTime)); | |
| 240 ASSERT(newStartTime != m_startTime); | |
| 241 | |
| 242 bool hadStartTime = hasStartTime(); | |
| 243 double previousCurrentTime = currentTimeInternal(); | |
| 244 m_startTime = newStartTime; | |
| 245 if (m_held && m_playbackRate) { | |
| 246 // If held, the start time would still be derrived from the hold time. | |
| 247 // Force a new, limited, current time. | |
| 248 m_held = false; | |
| 249 double currentTime = calculateCurrentTime(); | |
| 250 if (m_playbackRate > 0 && currentTime > sourceEnd()) { | |
| 251 currentTime = sourceEnd(); | |
| 252 } else if (m_playbackRate < 0 && currentTime < 0) { | |
| 253 currentTime = 0; | |
| 254 } | |
| 255 setCurrentTimeInternal(currentTime, TimingUpdateOnDemand); | |
| 256 } | |
| 257 double newCurrentTime = currentTimeInternal(); | |
| 258 | |
| 259 if (previousCurrentTime != newCurrentTime) { | |
| 260 setOutdated(); | |
| 261 } else if (!hadStartTime && m_timeline) { | |
| 262 // Even though this player is not outdated, time to effect change is | |
| 263 // infinity until start time is set. | |
| 264 m_timeline->wake(); | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 void AnimationPlayer::setSource(AnimationNode* newSource) | |
| 269 { | |
| 270 if (m_content == newSource) | |
| 271 return; | |
| 272 | |
| 273 setPending(); | |
| 274 | |
| 275 double storedCurrentTime = currentTimeInternal(); | |
| 276 if (m_content) | |
| 277 m_content->detach(); | |
| 278 m_content = newSource; | |
| 279 if (newSource) { | |
| 280 // FIXME: This logic needs to be updated once groups are implemented | |
| 281 if (newSource->player()) | |
| 282 newSource->player()->cancel(); | |
| 283 newSource->attach(this); | |
| 284 setOutdated(); | |
| 285 } | |
| 286 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); | |
| 287 } | |
| 288 | |
| 289 String AnimationPlayer::playState() | |
| 290 { | |
| 291 switch (playStateInternal()) { | |
| 292 case Idle: | |
| 293 return "idle"; | |
| 294 case Pending: | |
| 295 return "pending"; | |
| 296 case Running: | |
| 297 return "running"; | |
| 298 case Paused: | |
| 299 return "paused"; | |
| 300 case Finished: | |
| 301 return "finished"; | |
| 302 default: | |
| 303 ASSERT_NOT_REACHED(); | |
| 304 return ""; | |
| 305 } | |
| 306 } | |
| 307 | |
| 308 AnimationPlayer::AnimationPlayState AnimationPlayer::playStateInternal() | |
| 309 { | |
| 310 // FIXME(shanestephens): Add clause for in-idle-state here. | |
| 311 if (m_currentTimePending || (isNull(m_startTime) && !m_paused && m_playbackR
ate != 0)) | |
| 312 return Pending; | |
| 313 // FIXME(shanestephens): Add idle handling here. | |
| 314 if (m_paused) | |
| 315 return Paused; | |
| 316 if (finished()) | |
| 317 return Finished; | |
| 318 return Running; | |
| 319 } | |
| 320 | |
| 321 void AnimationPlayer::pause() | |
| 322 { | |
| 323 if (m_paused) | |
| 324 return; | |
| 325 if (playing()) { | |
| 326 setPending(); | |
| 327 m_currentTimePending = true; | |
| 328 } | |
| 329 m_paused = true; | |
| 330 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); | |
| 331 } | |
| 332 | |
| 333 void AnimationPlayer::unpause() | |
| 334 { | |
| 335 if (!m_paused) | |
| 336 return; | |
| 337 setPending(); | |
| 338 m_currentTimePending = true; | |
| 339 unpauseInternal(); | |
| 340 } | |
| 341 | |
| 342 void AnimationPlayer::unpauseInternal() | |
| 343 { | |
| 344 if (!m_paused) | |
| 345 return; | |
| 346 m_paused = false; | |
| 347 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); | |
| 348 } | |
| 349 | |
| 350 void AnimationPlayer::play() | |
| 351 { | |
| 352 if (!playing()) | |
| 353 m_startTime = nullValue(); | |
| 354 | |
| 355 setPending(); | |
| 356 unpauseInternal(); | |
| 357 if (!m_content) | |
| 358 return; | |
| 359 double currentTime = this->currentTimeInternal(); | |
| 360 if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd())) | |
| 361 setCurrentTimeInternal(0, TimingUpdateOnDemand); | |
| 362 else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd(
))) | |
| 363 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); | |
| 364 m_finished = false; | |
| 365 } | |
| 366 | |
| 367 void AnimationPlayer::reverse() | |
| 368 { | |
| 369 if (!m_playbackRate) { | |
| 370 return; | |
| 371 } | |
| 372 if (m_content) { | |
| 373 if (m_playbackRate > 0 && currentTimeInternal() > sourceEnd()) { | |
| 374 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); | |
| 375 ASSERT(finished()); | |
| 376 } else if (m_playbackRate < 0 && currentTimeInternal() < 0) { | |
| 377 setCurrentTimeInternal(0, TimingUpdateOnDemand); | |
| 378 ASSERT(finished()); | |
| 379 } | |
| 380 } | |
| 381 setPlaybackRate(-m_playbackRate); | |
| 382 unpauseInternal(); | |
| 383 } | |
| 384 | |
| 385 void AnimationPlayer::finish(ExceptionState& exceptionState) | |
| 386 { | |
| 387 if (!m_playbackRate) { | |
| 388 return; | |
| 389 } | |
| 390 if (m_playbackRate > 0 && sourceEnd() == std::numeric_limits<double>::infini
ty()) { | |
| 391 exceptionState.ThrowDOMException(InvalidStateError, "AnimationPlayer has
source content whose end time is infinity."); | |
| 392 return; | |
| 393 } | |
| 394 if (playing()) { | |
| 395 setPending(); | |
| 396 } | |
| 397 if (m_playbackRate < 0) { | |
| 398 setCurrentTimeInternal(0, TimingUpdateOnDemand); | |
| 399 } else { | |
| 400 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); | |
| 401 } | |
| 402 ASSERT(finished()); | |
| 403 } | |
| 404 | |
| 405 const AtomicString& AnimationPlayer::interfaceName() const | |
| 406 { | |
| 407 return EventTargetNames::AnimationPlayer; | |
| 408 } | |
| 409 | |
| 410 ExecutionContext* AnimationPlayer::executionContext() const | |
| 411 { | |
| 412 return ActiveDOMObject::executionContext(); | |
| 413 } | |
| 414 | |
| 415 bool AnimationPlayer::hasPendingActivity() const | |
| 416 { | |
| 417 return m_pendingFinishedEvent || (!m_finished && hasEventListeners(EventType
Names::finish)); | |
| 418 } | |
| 419 | |
| 420 void AnimationPlayer::stop() | |
| 421 { | |
| 422 m_finished = true; | |
| 423 m_pendingFinishedEvent = nullptr; | |
| 424 } | |
| 425 | |
| 426 bool AnimationPlayer::dispatchEvent(PassRefPtr<Event> event) | |
| 427 { | |
| 428 if (m_pendingFinishedEvent == event) | |
| 429 m_pendingFinishedEvent = nullptr; | |
| 430 return EventTargetWithInlineData::dispatchEvent(event); | |
| 431 } | |
| 432 | |
| 433 void AnimationPlayer::setPlaybackRate(double playbackRate) | |
| 434 { | |
| 435 if (!std::isfinite(playbackRate)) | |
| 436 return; | |
| 437 if (playbackRate == m_playbackRate) | |
| 438 return; | |
| 439 | |
| 440 setPending(); | |
| 441 if (!finished() && !paused()) | |
| 442 m_currentTimePending = true; | |
| 443 | |
| 444 double storedCurrentTime = currentTimeInternal(); | |
| 445 if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && play
backRate <= 0)) | |
| 446 m_finished = false; | |
| 447 | |
| 448 m_playbackRate = playbackRate; | |
| 449 m_startTime = std::numeric_limits<double>::quiet_NaN(); | |
| 450 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); | |
| 451 } | |
| 452 | |
| 453 void AnimationPlayer::setOutdated() | |
| 454 { | |
| 455 m_outdated = true; | |
| 456 if (m_timeline) | |
| 457 m_timeline->setOutdatedAnimationPlayer(this); | |
| 458 } | |
| 459 | |
| 460 void AnimationPlayer::setPending() | |
| 461 { | |
| 462 if (!m_pending) { | |
| 463 m_pending = true; | |
| 464 timeline()->document()->pendingAnimations().add(this); | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 bool AnimationPlayer::update(TimingUpdateReason reason) | |
| 469 { | |
| 470 if (!m_timeline) | |
| 471 return false; | |
| 472 | |
| 473 updateCurrentTimingState(reason); | |
| 474 m_outdated = false; | |
| 475 | |
| 476 if (m_content) { | |
| 477 double inheritedTime = isNull(m_timeline->currentTimeInternal()) ? nullV
alue() : currentTimeInternal(); | |
| 478 m_content->updateInheritedTime(inheritedTime, reason); | |
| 479 } | |
| 480 | |
| 481 if (finished() && !m_finished) { | |
| 482 if (reason == TimingUpdateForAnimationFrame && hasStartTime()) { | |
| 483 const AtomicString& eventType = EventTypeNames::finish; | |
| 484 if (executionContext() && hasEventListeners(eventType)) { | |
| 485 m_pendingFinishedEvent = AnimationPlayerEvent::create(eventType,
currentTime(), timeline()->currentTime()); | |
| 486 m_pendingFinishedEvent->setTarget(this); | |
| 487 m_pendingFinishedEvent->setCurrentTarget(this); | |
| 488 m_timeline->document()->enqueueAnimationFrameEvent(m_pendingFini
shedEvent); | |
| 489 } | |
| 490 m_finished = true; | |
| 491 } | |
| 492 } | |
| 493 ASSERT(!m_outdated); | |
| 494 return !m_finished || !finished(); | |
| 495 } | |
| 496 | |
| 497 double AnimationPlayer::timeToEffectChange() | |
| 498 { | |
| 499 ASSERT(!m_outdated); | |
| 500 if (m_held || !hasStartTime()) | |
| 501 return std::numeric_limits<double>::infinity(); | |
| 502 if (!m_content) | |
| 503 return -currentTimeInternal() / m_playbackRate; | |
| 504 if (m_playbackRate > 0) | |
| 505 return m_content->timeToForwardsEffectChange() / m_playbackRate; | |
| 506 return m_content->timeToReverseEffectChange() / -m_playbackRate; | |
| 507 } | |
| 508 | |
| 509 void AnimationPlayer::cancel() | |
| 510 { | |
| 511 setSource(0); | |
| 512 } | |
| 513 | |
| 514 bool AnimationPlayer::canFree() const | |
| 515 { | |
| 516 ASSERT(m_content); | |
| 517 return hasOneRef() && m_content->isAnimation() && m_content->hasOneRef(); | |
| 518 } | |
| 519 | |
| 520 bool AnimationPlayer::addEventListener(const AtomicString& eventType, PassRefPtr
<EventListener> listener, bool useCapture) | |
| 521 { | |
| 522 return EventTargetWithInlineData::addEventListener(eventType, listener, useC
apture); | |
| 523 } | |
| 524 | |
| 525 void AnimationPlayer::pauseForTesting(double pauseTime) | |
| 526 { | |
| 527 RELEASE_ASSERT(!paused()); | |
| 528 setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand); | |
| 529 m_isPausedForTesting = true; | |
| 530 pause(); | |
| 531 } | |
| 532 | |
| 533 } // namespace | |
| OLD | NEW |