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 40 namespace WebCore { 41 42 namespace { 43 44 static unsigned nextSequenceNumber() 45 { 46 static unsigned next = 0; 47 return ++next; 48 } 49 50 } 51 52 PassRefPtrWillBeRawPtr<AnimationPlayer> AnimationPlayer::create(ExecutionContext* executionContext, AnimationTimeline& timeline, AnimationNode* content) 53 { 54 RefPtrWillBeRawPtr<AnimationPlayer> player = adoptRefWillBeRefCountedGarbageCollected(new AnimationPlayer(executionContext, timeline, content)); 55 player->suspendIfNeeded(); 56 return player.release(); 57 } 58 59 AnimationPlayer::AnimationPlayer(ExecutionContext* executionContext, AnimationTimeline& timeline, AnimationNode* content) 60 : ActiveDOMObject(executionContext) 61 , m_playbackRate(1) 62 , m_startTime(nullValue()) 63 , m_holdTime(nullValue()) 64 , m_storedTimeLag(0) 65 , m_sortInfo(nextSequenceNumber(), timeline.effectiveTime()) 66 , m_content(content) 67 , m_timeline(&timeline) 68 , m_paused(false) 69 , m_held(false) 70 , m_isPausedForTesting(false) 71 , m_outdated(true) 72 , m_finished(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 !ENABLE(OILPAN) 84 if (m_content) 85 m_content->detach(); 86 if (m_timeline) 87 m_timeline->playerDestroyed(this); 88 #endif 89 } 90 91 double AnimationPlayer::sourceEnd() const 92 { 93 return m_content ? m_content->endTimeInternal() : 0; 94 } 95 96 bool AnimationPlayer::limited(double currentTime) const 97 { 98 return (m_playbackRate < 0 && currentTime <= 0) || (m_playbackRate > 0 && currentTime >= sourceEnd()); 99 } 100 101 double AnimationPlayer::currentTimeWithoutLag() const 102 { 103 if (isNull(m_startTime) || !m_timeline) 104 return 0; 105 return (m_timeline->effectiveTime() - m_startTime) * m_playbackRate; 106 } 107 108 double AnimationPlayer::currentTimeWithLag() const 109 { 110 ASSERT(!m_held); 111 double time = currentTimeWithoutLag(); 112 return std::isinf(time) ? time : time - m_storedTimeLag; 113 } 114 115 void AnimationPlayer::updateTimingState(double newCurrentTime) 116 { 117 ASSERT(!isNull(newCurrentTime)); 118 bool oldHeld = m_held; 119 m_held = m_paused || !m_playbackRate || limited(newCurrentTime); 120 if (m_held) { 121 if (!oldHeld || m_holdTime != newCurrentTime) 122 setOutdated(); 123 m_holdTime = newCurrentTime; 124 m_storedTimeLag = nullValue(); 125 } else { 126 m_holdTime = nullValue(); 127 m_storedTimeLag = currentTimeWithoutLag() - newCurrentTime; 128 m_finished = false; 129 setOutdated(); 130 } 131 } 132 133 void AnimationPlayer::updateCurrentTimingState() 134 { 135 if (m_held) { 136 updateTimingState(m_holdTime); 137 return; 138 } 139 if (!limited(currentTimeWithLag())) 140 return; 141 m_held = true; 142 m_holdTime = m_playbackRate < 0 ? 0 : sourceEnd(); 143 m_storedTimeLag = nullValue(); 144 } 145 146 double AnimationPlayer::currentTime() 147 { 148 return currentTimeInternal() * 1000; 149 } 150 151 double AnimationPlayer::currentTimeInternal() 152 { 153 updateCurrentTimingState(); 154 if (m_held) 155 return m_holdTime; 156 return currentTimeWithLag(); 157 } 158 159 void AnimationPlayer::setCurrentTime(double newCurrentTime) 160 { 161 setCurrentTimeInternal(newCurrentTime / 1000); 162 } 163 164 void AnimationPlayer::setCurrentTimeInternal(double newCurrentTime) 165 { 166 if (!std::isfinite(newCurrentTime)) 167 return; 168 updateTimingState(newCurrentTime); 169 cancelAnimationOnCompositor(); 170 schedulePendingAnimationOnCompositor(); 171 } 172 173 void AnimationPlayer::setStartTimeInternal(double newStartTime, bool isUpdateFromCompositor) 174 { 175 ASSERT(!isUpdateFromCompositor || !hasStartTime()); 176 177 if (!std::isfinite(newStartTime)) 178 return; 179 if (newStartTime == m_startTime) 180 return; 181 updateCurrentTimingState(); // Update the value of held 182 bool hadStartTime = hasStartTime(); 183 double previousCurrentTime = currentTimeInternal(); 184 m_startTime = newStartTime; 185 m_sortInfo.m_startTime = newStartTime; 186 updateCurrentTimingState(); 187 if (previousCurrentTime != currentTimeInternal()) { 188 setOutdated(); 189 } else if (!hadStartTime && m_timeline) { 190 // Even though this player is not outdated, time to effect change is 191 // infinity until start time is set. 192 m_timeline->wake(); 193 } 194 if (!isUpdateFromCompositor) { 195 cancelAnimationOnCompositor(); 196 schedulePendingAnimationOnCompositor(); 197 } 198 } 199 200 void AnimationPlayer::setSource(AnimationNode* newSource) 201 { 202 if (m_content == newSource) 203 return; 204 cancelAnimationOnCompositor(); 205 double storedCurrentTime = currentTimeInternal(); 206 if (m_content) 207 m_content->detach(); 208 m_content = newSource; 209 if (newSource) { 210 // FIXME: This logic needs to be updated once groups are implemented 211 if (newSource->player()) 212 newSource->player()->cancel(); 213 newSource->attach(this); 214 } 215 updateTimingState(storedCurrentTime); 216 schedulePendingAnimationOnCompositor(); 217 } 218 219 void AnimationPlayer::pause() 220 { 221 if (m_paused) 222 return; 223 m_paused = true; 224 updateTimingState(currentTimeInternal()); 225 cancelAnimationOnCompositor(); 226 } 227 228 void AnimationPlayer::unpause() 229 { 230 if (!m_paused) 231 return; 232 m_paused = false; 233 updateTimingState(currentTimeInternal()); 234 schedulePendingAnimationOnCompositor(); 235 } 236 237 void AnimationPlayer::play() 238 { 239 cancelAnimationOnCompositor(); 240 // Note, unpause schedules pending animation on compositor if necessary. 241 unpause(); 242 if (!m_content) 243 return; 244 double currentTime = this->currentTimeInternal(); 245 if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd())) 246 setCurrentTimeInternal(0); 247 else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd())) 248 setCurrentTimeInternal(sourceEnd()); 249 m_finished = false; 250 } 251 252 void AnimationPlayer::reverse() 253 { 254 if (!m_playbackRate) 255 return; 256 if (m_content) { 257 if (m_playbackRate > 0 && currentTimeInternal() > sourceEnd()) 258 setCurrentTimeInternal(sourceEnd()); 259 else if (m_playbackRate < 0 && currentTimeInternal() < 0) 260 setCurrentTimeInternal(0); 261 } 262 setPlaybackRate(-m_playbackRate); 263 cancelAnimationOnCompositor(); 264 // Note, unpause schedules pending animation on compositor if necessary. 265 unpause(); 266 } 267 268 void AnimationPlayer::finish(ExceptionState& exceptionState) 269 { 270 if (!m_playbackRate) 271 return; 272 if (m_playbackRate < 0) { 273 setCurrentTimeInternal(0); 274 } else { 275 if (sourceEnd() == std::numeric_limits<double>::infinity()) { 276 exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has source content whose end time is infinity."); 277 return; 278 } 279 setCurrentTimeInternal(sourceEnd()); 280 } 281 ASSERT(finished()); 282 cancelAnimationOnCompositor(); 283 } 284 285 const AtomicString& AnimationPlayer::interfaceName() const 286 { 287 return EventTargetNames::AnimationPlayer; 288 } 289 290 ExecutionContext* AnimationPlayer::executionContext() const 291 { 292 return ActiveDOMObject::executionContext(); 293 } 294 295 bool AnimationPlayer::hasPendingActivity() const 296 { 297 return m_pendingFinishedEvent || (!m_finished && hasEventListeners(EventTypeNames::finish)); 298 } 299 300 void AnimationPlayer::stop() 301 { 302 m_finished = true; 303 m_pendingFinishedEvent = nullptr; 304 } 305 306 bool AnimationPlayer::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event) 307 { 308 if (m_pendingFinishedEvent == event) 309 m_pendingFinishedEvent = nullptr; 310 return EventTargetWithInlineData::dispatchEvent(event); 311 } 312 313 void AnimationPlayer::setPlaybackRate(double playbackRate) 314 { 315 if (!std::isfinite(playbackRate)) 316 return; 317 double storedCurrentTime = currentTimeInternal(); 318 if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && playbackRate <= 0)) 319 m_finished = false; 320 m_playbackRate = playbackRate; 321 updateTimingState(storedCurrentTime); 322 cancelAnimationOnCompositor(); 323 schedulePendingAnimationOnCompositor(); 324 } 325 326 void AnimationPlayer::setOutdated() 327 { 328 m_outdated = true; 329 if (m_timeline) 330 m_timeline->setOutdatedAnimationPlayer(this); 331 } 332 333 bool AnimationPlayer::canStartAnimationOnCompositor() 334 { 335 // FIXME: Need compositor support for playback rate != 1. 336 if (playbackRate() != 1) 337 return false; 338 339 return m_timeline && m_content && m_content->isAnimation() && !m_held; 340 } 341 342 bool AnimationPlayer::maybeStartAnimationOnCompositor() 343 { 344 if (!canStartAnimationOnCompositor()) 345 return false; 346 347 return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(timeline()->zeroTime() + startTimeInternal() + timeLagInternal()); 348 } 349 350 void AnimationPlayer::schedulePendingAnimationOnCompositor() 351 { 352 ASSERT(!hasActiveAnimationsOnCompositor()); 353 354 if (canStartAnimationOnCompositor()) 355 timeline()->document()->compositorPendingAnimations().add(this); 356 } 357 358 bool AnimationPlayer::hasActiveAnimationsOnCompositor() 359 { 360 if (!m_content || !m_content->isAnimation()) 361 return false; 362 363 return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor(); 364 } 365 366 void AnimationPlayer::cancelAnimationOnCompositor() 367 { 368 if (hasActiveAnimationsOnCompositor()) 369 toAnimation(m_content.get())->cancelAnimationOnCompositor(); 370 } 371 372 bool AnimationPlayer::update(TimingUpdateReason reason) 373 { 374 m_outdated = false; 375 376 if (!m_timeline) 377 return false; 378 379 if (m_content) { 380 double inheritedTime = isNull(m_timeline->currentTimeInternal()) ? nullValue() : currentTimeInternal(); 381 m_content->updateInheritedTime(inheritedTime, reason); 382 } 383 384 if (finished() && !m_finished) { 385 if (reason == TimingUpdateForAnimationFrame && hasStartTime()) { 386 const AtomicString& eventType = EventTypeNames::finish; 387 if (executionContext() && hasEventListeners(eventType)) { 388 m_pendingFinishedEvent = AnimationPlayerEvent::create(eventType, currentTime(), timeline()->currentTime()); 389 m_pendingFinishedEvent->setTarget(this); 390 m_pendingFinishedEvent->setCurrentTarget(this); 391 m_timeline->document()->enqueueAnimationFrameEvent(m_pendingFinishedEvent); 392 } 393 m_finished = true; 394 } 395 } 396 ASSERT(!m_outdated); 397 return !m_finished || !finished(); 398 } 399 400 double AnimationPlayer::timeToEffectChange() 401 { 402 ASSERT(!m_outdated); 403 if (m_held || !hasStartTime()) 404 return std::numeric_limits<double>::infinity(); 405 if (!m_content) 406 return -currentTimeInternal() / m_playbackRate; 407 if (m_playbackRate > 0) 408 return m_content->timeToForwardsEffectChange() / m_playbackRate; 409 return m_content->timeToReverseEffectChange() / -m_playbackRate; 410 } 411 412 void AnimationPlayer::cancel() 413 { 414 setSource(0); 415 } 416 417 bool AnimationPlayer::SortInfo::operator<(const SortInfo& other) const 418 { 419 ASSERT(!std::isnan(m_startTime) && !std::isnan(other.m_startTime)); 420 if (m_startTime < other.m_startTime) 421 return true; 422 if (m_startTime > other.m_startTime) 423 return false; 424 return m_sequenceNumber < other.m_sequenceNumber; 425 } 426 427 #if !ENABLE(OILPAN) 428 bool AnimationPlayer::canFree() const 429 { 430 ASSERT(m_content); 431 return hasOneRef() && m_content->isAnimation() && m_content->hasOneRef(); 432 } 433 #endif 434 435 bool AnimationPlayer::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) 436 { 437 if (eventType == EventTypeNames::finish) 438 UseCounter::count(executionContext(), UseCounter::AnimationPlayerFinishEvent); 439 return EventTargetWithInlineData::addEventListener(eventType, listener, useCapture); 440 } 441 442 void AnimationPlayer::pauseForTesting(double pauseTime) 443 { 444 RELEASE_ASSERT(!paused()); 445 updateTimingState(pauseTime); 446 if (!m_isPausedForTesting && hasActiveAnimationsOnCompositor()) 447 toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTimeInternal()); 448 m_isPausedForTesting = true; 449 pause(); 450 } 451 452 void AnimationPlayer::trace(Visitor* visitor) 453 { 454 visitor->trace(m_content); 455 visitor->trace(m_timeline); 456 visitor->trace(m_pendingFinishedEvent); 457 EventTargetWithInlineData::trace(visitor); 458 } 459 460 } // namespace 461