1/* 2 * Copyright (C) 2007, 2008, 2009 Apple 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 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30#include "AnimationBase.h" 31 32#include "AnimationControllerPrivate.h" 33#include "CSSPrimitiveValue.h" 34#include "CSSPropertyAnimation.h" 35#include "CompositeAnimation.h" 36#include "Document.h" 37#include "EventNames.h" 38#include "FloatConversion.h" 39#include "Logging.h" 40#include "RenderBox.h" 41#include "RenderStyle.h" 42#include "UnitBezier.h" 43#include <algorithm> 44#include <wtf/CurrentTime.h> 45 46using namespace std; 47 48namespace WebCore { 49 50// The epsilon value we pass to UnitBezier::solve given that the animation is going to run over |dur| seconds. The longer the 51// animation, the more precision we need in the timing function result to avoid ugly discontinuities. 52static inline double solveEpsilon(double duration) 53{ 54 return 1.0 / (200.0 * duration); 55} 56 57static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration) 58{ 59 // Convert from input time to parametric value in curve, then from 60 // that to output time. 61 UnitBezier bezier(p1x, p1y, p2x, p2y); 62 return bezier.solve(t, solveEpsilon(duration)); 63} 64 65static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t) 66{ 67 if (stepAtStart) 68 return min(1.0, (floor(numSteps * t) + 1) / numSteps); 69 return floor(numSteps * t) / numSteps; 70} 71 72AnimationBase::AnimationBase(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim) 73 : m_animState(AnimationStateNew) 74 , m_isAccelerated(false) 75 , m_transformFunctionListValid(false) 76#if ENABLE(CSS_FILTERS) 77 , m_filterFunctionListsMatch(false) 78#endif 79 , m_startTime(0) 80 , m_pauseTime(-1) 81 , m_requestedStartTime(0) 82 , m_totalDuration(-1) 83 , m_nextIterationDuration(-1) 84 , m_object(renderer) 85 , m_animation(const_cast<Animation*>(transition)) 86 , m_compAnim(compAnim) 87{ 88 // Compute the total duration 89 if (m_animation->iterationCount() > 0) 90 m_totalDuration = m_animation->duration() * m_animation->iterationCount(); 91} 92 93void AnimationBase::setNeedsStyleRecalc(Node* node) 94{ 95 ASSERT(!node || (node->document() && !node->document()->inPageCache())); 96 if (node) 97 node->setNeedsStyleRecalc(SyntheticStyleChange); 98} 99 100double AnimationBase::duration() const 101{ 102 return m_animation->duration(); 103} 104 105bool AnimationBase::playStatePlaying() const 106{ 107 return m_animation->playState() == AnimPlayStatePlaying; 108} 109 110bool AnimationBase::animationsMatch(const Animation* anim) const 111{ 112 return m_animation->animationsMatch(anim); 113} 114 115#if !LOG_DISABLED 116static const char* nameForState(AnimationBase::AnimState state) 117{ 118 switch (state) { 119 case AnimationBase::AnimationStateNew: return "New"; 120 case AnimationBase::AnimationStateStartWaitTimer: return "StartWaitTimer"; 121 case AnimationBase::AnimationStateStartWaitStyleAvailable: return "StartWaitStyleAvailable"; 122 case AnimationBase::AnimationStateStartWaitResponse: return "StartWaitResponse"; 123 case AnimationBase::AnimationStateLooping: return "Looping"; 124 case AnimationBase::AnimationStateEnding: return "Ending"; 125 case AnimationBase::AnimationStatePausedNew: return "PausedNew"; 126 case AnimationBase::AnimationStatePausedWaitTimer: return "PausedWaitTimer"; 127 case AnimationBase::AnimationStatePausedWaitStyleAvailable: return "PausedWaitStyleAvailable"; 128 case AnimationBase::AnimationStatePausedWaitResponse: return "PausedWaitResponse"; 129 case AnimationBase::AnimationStatePausedRun: return "PausedRun"; 130 case AnimationBase::AnimationStateDone: return "Done"; 131 case AnimationBase::AnimationStateFillingForwards: return "FillingForwards"; 132 } 133 return ""; 134} 135#endif 136 137void AnimationBase::updateStateMachine(AnimStateInput input, double param) 138{ 139 if (!m_compAnim) 140 return; 141 142 // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state. 143 if (input == AnimationStateInputMakeNew) { 144 if (m_animState == AnimationStateStartWaitStyleAvailable) 145 m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); 146 LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState)); 147 m_animState = AnimationStateNew; 148 m_startTime = 0; 149 m_pauseTime = -1; 150 m_requestedStartTime = 0; 151 m_nextIterationDuration = -1; 152 endAnimation(); 153 return; 154 } 155 156 if (input == AnimationStateInputRestartAnimation) { 157 if (m_animState == AnimationStateStartWaitStyleAvailable) 158 m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); 159 LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState)); 160 m_animState = AnimationStateNew; 161 m_startTime = 0; 162 m_pauseTime = -1; 163 m_requestedStartTime = 0; 164 m_nextIterationDuration = -1; 165 endAnimation(); 166 167 if (!paused()) 168 updateStateMachine(AnimationStateInputStartAnimation, -1); 169 return; 170 } 171 172 if (input == AnimationStateInputEndAnimation) { 173 if (m_animState == AnimationStateStartWaitStyleAvailable) 174 m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); 175 LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animState)); 176 m_animState = AnimationStateDone; 177 endAnimation(); 178 return; 179 } 180 181 if (input == AnimationStateInputPauseOverride) { 182 if (m_animState == AnimationStateStartWaitResponse) { 183 // If we are in AnimationStateStartWaitResponse, the animation will get canceled before 184 // we get a response, so move to the next state. 185 endAnimation(); 186 updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); 187 } 188 return; 189 } 190 191 if (input == AnimationStateInputResumeOverride) { 192 if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) { 193 // Start the animation 194 startAnimation(beginAnimationUpdateTime() - m_startTime); 195 } 196 return; 197 } 198 199 // Execute state machine 200 switch (m_animState) { 201 case AnimationStateNew: 202 ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning || input == AnimationStateInputPlayStatePaused); 203 if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning) { 204 m_requestedStartTime = beginAnimationUpdateTime(); 205 LOG(Animations, "%p AnimationState %s -> StartWaitTimer", this, nameForState(m_animState)); 206 m_animState = AnimationStateStartWaitTimer; 207 } else { 208 // We are pausing before we even started. 209 LOG(Animations, "%p AnimationState %s -> AnimationStatePausedNew", this, nameForState(m_animState)); 210 m_animState = AnimationStatePausedNew; 211 } 212 break; 213 case AnimationStateStartWaitTimer: 214 ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused); 215 216 if (input == AnimationStateInputStartTimerFired) { 217 ASSERT(param >= 0); 218 // Start timer has fired, tell the animation to start and wait for it to respond with start time 219 LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animState)); 220 m_animState = AnimationStateStartWaitStyleAvailable; 221 m_compAnim->animationController()->addToAnimationsWaitingForStyle(this); 222 223 // Trigger a render so we can start the animation 224 if (m_object) 225 m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node()); 226 } else { 227 ASSERT(!paused()); 228 // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait 229 m_pauseTime = beginAnimationUpdateTime(); 230 LOG(Animations, "%p AnimationState %s -> PausedWaitTimer", this, nameForState(m_animState)); 231 m_animState = AnimationStatePausedWaitTimer; 232 } 233 break; 234 case AnimationStateStartWaitStyleAvailable: 235 ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused); 236 237 if (input == AnimationStateInputStyleAvailable) { 238 // Start timer has fired, tell the animation to start and wait for it to respond with start time 239 LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); 240 m_animState = AnimationStateStartWaitResponse; 241 242 overrideAnimations(); 243 244 // Start the animation 245 if (overridden()) { 246 // We won't try to start accelerated animations if we are overridden and 247 // just move on to the next state. 248 LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); 249 m_animState = AnimationStateStartWaitResponse; 250 m_isAccelerated = false; 251 updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); 252 } else { 253 double timeOffset = 0; 254 // If the value for 'animation-delay' is negative then the animation appears to have started in the past. 255 if (m_animation->delay() < 0) 256 timeOffset = -m_animation->delay(); 257 bool started = startAnimation(timeOffset); 258 259 m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started); 260 m_isAccelerated = started; 261 } 262 } else { 263 // We're waiting for the style to be available and we got a pause. Pause and wait 264 m_pauseTime = beginAnimationUpdateTime(); 265 LOG(Animations, "%p AnimationState %s -> PausedWaitStyleAvailable", this, nameForState(m_animState)); 266 m_animState = AnimationStatePausedWaitStyleAvailable; 267 } 268 break; 269 case AnimationStateStartWaitResponse: 270 ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused); 271 272 if (input == AnimationStateInputStartTimeSet) { 273 ASSERT(param >= 0); 274 // We have a start time, set it, unless the startTime is already set 275 if (m_startTime <= 0) { 276 m_startTime = param; 277 // If the value for 'animation-delay' is negative then the animation appears to have started in the past. 278 if (m_animation->delay() < 0) 279 m_startTime += m_animation->delay(); 280 } 281 282 // Now that we know the start time, fire the start event. 283 onAnimationStart(0); // The elapsedTime is 0. 284 285 // Decide whether to go into looping or ending state 286 goIntoEndingOrLoopingState(); 287 288 // Dispatch updateStyleIfNeeded so we can start the animation 289 if (m_object) 290 m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node()); 291 } else { 292 // We are pausing while waiting for a start response. Cancel the animation and wait. When 293 // we unpause, we will act as though the start timer just fired 294 m_pauseTime = beginAnimationUpdateTime(); 295 pauseAnimation(beginAnimationUpdateTime() - m_startTime); 296 LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animState)); 297 m_animState = AnimationStatePausedWaitResponse; 298 } 299 break; 300 case AnimationStateLooping: 301 ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused); 302 303 if (input == AnimationStateInputLoopTimerFired) { 304 ASSERT(param >= 0); 305 // Loop timer fired, loop again or end. 306 onAnimationIteration(param); 307 308 // Decide whether to go into looping or ending state 309 goIntoEndingOrLoopingState(); 310 } else { 311 // We are pausing while running. Cancel the animation and wait 312 m_pauseTime = beginAnimationUpdateTime(); 313 pauseAnimation(beginAnimationUpdateTime() - m_startTime); 314 LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState)); 315 m_animState = AnimationStatePausedRun; 316 } 317 break; 318 case AnimationStateEnding: 319#if !LOG_DISABLED 320 if (input != AnimationStateInputEndTimerFired && input != AnimationStateInputPlayStatePaused) 321 LOG_ERROR("State is AnimationStateEnding, but input is not AnimationStateInputEndTimerFired or AnimationStateInputPlayStatePaused. It is %d.", input); 322#endif 323 if (input == AnimationStateInputEndTimerFired) { 324 325 ASSERT(param >= 0); 326 // End timer fired, finish up 327 onAnimationEnd(param); 328 329 LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animState)); 330 m_animState = AnimationStateDone; 331 332 if (m_object) { 333 if (m_animation->fillsForwards()) { 334 LOG(Animations, "%p AnimationState %s -> FillingForwards", this, nameForState(m_animState)); 335 m_animState = AnimationStateFillingForwards; 336 } else 337 resumeOverriddenAnimations(); 338 339 // Fire off another style change so we can set the final value 340 m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node()); 341 } 342 } else { 343 // We are pausing while running. Cancel the animation and wait 344 m_pauseTime = beginAnimationUpdateTime(); 345 pauseAnimation(beginAnimationUpdateTime() - m_startTime); 346 LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState)); 347 m_animState = AnimationStatePausedRun; 348 } 349 // |this| may be deleted here 350 break; 351 case AnimationStatePausedWaitTimer: 352 ASSERT(input == AnimationStateInputPlayStateRunning); 353 ASSERT(paused()); 354 // Update the times 355 m_startTime += beginAnimationUpdateTime() - m_pauseTime; 356 m_pauseTime = -1; 357 358 // we were waiting for the start timer to fire, go back and wait again 359 LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState)); 360 m_animState = AnimationStateNew; 361 updateStateMachine(AnimationStateInputStartAnimation, 0); 362 break; 363 case AnimationStatePausedNew: 364 case AnimationStatePausedWaitResponse: 365 case AnimationStatePausedWaitStyleAvailable: 366 case AnimationStatePausedRun: 367 // We treat these two cases the same. The only difference is that, when we are in 368 // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation. 369 // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice 370 // that we have already set the startTime and will ignore it. 371 ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable || input == AnimationStateInputStartAnimation); 372 ASSERT(paused()); 373 374 if (input == AnimationStateInputPlayStateRunning) { 375 if (m_animState == AnimationStatePausedNew) { 376 // We were paused before we even started, and now we're supposed 377 // to start, so jump back to the New state and reset. 378 LOG(Animations, "%p AnimationState %s -> AnimationStateNew", this, nameForState(m_animState)); 379 m_animState = AnimationStateNew; 380 updateStateMachine(input, param); 381 break; 382 } 383 384 // Update the times 385 if (m_animState == AnimationStatePausedRun) 386 m_startTime += beginAnimationUpdateTime() - m_pauseTime; 387 else 388 m_startTime = 0; 389 m_pauseTime = -1; 390 391 if (m_animState == AnimationStatePausedWaitStyleAvailable) { 392 LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animState)); 393 m_animState = AnimationStateStartWaitStyleAvailable; 394 } else { 395 // We were either running or waiting for a begin time response from the animation. 396 // Either way we need to restart the animation (possibly with an offset if we 397 // had already been running) and wait for it to start. 398 LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); 399 m_animState = AnimationStateStartWaitResponse; 400 401 // Start the animation 402 if (overridden()) { 403 // We won't try to start accelerated animations if we are overridden and 404 // just move on to the next state. 405 updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); 406 m_isAccelerated = true; 407 } else { 408 bool started = startAnimation(beginAnimationUpdateTime() - m_startTime); 409 m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started); 410 m_isAccelerated = started; 411 } 412 } 413 break; 414 } 415 416 if (input == AnimationStateInputStartTimeSet) { 417 ASSERT(m_animState == AnimationStatePausedWaitResponse); 418 419 // We are paused but we got the callback that notifies us that an accelerated animation started. 420 // We ignore the start time and just move into the paused-run state. 421 LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState)); 422 m_animState = AnimationStatePausedRun; 423 ASSERT(m_startTime == 0); 424 m_startTime = param; 425 m_pauseTime += m_startTime; 426 break; 427 } 428 429 ASSERT(m_animState == AnimationStatePausedWaitStyleAvailable); 430 // We are paused but we got the callback that notifies us that style has been updated. 431 // We move to the AnimationStatePausedWaitResponse state 432 LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animState)); 433 m_animState = AnimationStatePausedWaitResponse; 434 overrideAnimations(); 435 break; 436 case AnimationStateFillingForwards: 437 case AnimationStateDone: 438 // We're done. Stay in this state until we are deleted 439 break; 440 } 441} 442 443void AnimationBase::fireAnimationEventsIfNeeded() 444{ 445 if (!m_compAnim) 446 return; 447 448 // If we are waiting for the delay time to expire and it has, go to the next state 449 if (m_animState != AnimationStateStartWaitTimer && m_animState != AnimationStateLooping && m_animState != AnimationStateEnding) 450 return; 451 452 // We have to make sure to keep a ref to the this pointer, because it could get destroyed 453 // during an animation callback that might get called. Since the owner is a CompositeAnimation 454 // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase 455 // can still access the resources of its CompositeAnimation as needed. 456 RefPtr<AnimationBase> protector(this); 457 RefPtr<CompositeAnimation> compProtector(m_compAnim); 458 459 // Check for start timeout 460 if (m_animState == AnimationStateStartWaitTimer) { 461 if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay()) 462 updateStateMachine(AnimationStateInputStartTimerFired, 0); 463 return; 464 } 465 466 double elapsedDuration = beginAnimationUpdateTime() - m_startTime; 467 // FIXME: we need to ensure that elapsedDuration is never < 0. If it is, this suggests that 468 // we had a recalcStyle() outside of beginAnimationUpdate()/endAnimationUpdate(). 469 // Also check in getTimeToNextEvent(). 470 elapsedDuration = max(elapsedDuration, 0.0); 471 472 // Check for end timeout 473 if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) { 474 // We may still be in AnimationStateLooping if we've managed to skip a 475 // whole iteration, in which case we should jump to the end state. 476 LOG(Animations, "%p AnimationState %s -> Ending", this, nameForState(m_animState)); 477 m_animState = AnimationStateEnding; 478 479 // Fire an end event 480 updateStateMachine(AnimationStateInputEndTimerFired, m_totalDuration); 481 } else { 482 // Check for iteration timeout 483 if (m_nextIterationDuration < 0) { 484 // Hasn't been set yet, set it 485 double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); 486 m_nextIterationDuration = elapsedDuration + durationLeft; 487 } 488 489 if (elapsedDuration >= m_nextIterationDuration) { 490 // Set to the next iteration 491 double previous = m_nextIterationDuration; 492 double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); 493 m_nextIterationDuration = elapsedDuration + durationLeft; 494 495 // Send the event 496 updateStateMachine(AnimationStateInputLoopTimerFired, previous); 497 } 498 } 499} 500 501void AnimationBase::updatePlayState(EAnimPlayState playState) 502{ 503 if (!m_compAnim) 504 return; 505 506 // When we get here, we can have one of 4 desired states: running, paused, suspended, paused & suspended. 507 // The state machine can be in one of two states: running, paused. 508 // Set the state machine to the desired state. 509 bool pause = playState == AnimPlayStatePaused || m_compAnim->isSuspended(); 510 511 if (pause == paused() && !isNew()) 512 return; 513 514 updateStateMachine(pause ? AnimationStateInputPlayStatePaused : AnimationStateInputPlayStateRunning, -1); 515} 516 517double AnimationBase::timeToNextService() 518{ 519 // Returns the time at which next service is required. -1 means no service is required. 0 means 520 // service is required now, and > 0 means service is required that many seconds in the future. 521 if (paused() || isNew() || m_animState == AnimationStateFillingForwards) 522 return -1; 523 524 if (m_animState == AnimationStateStartWaitTimer) { 525 double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime); 526 return max(timeFromNow, 0.0); 527 } 528 529 fireAnimationEventsIfNeeded(); 530 531 // In all other cases, we need service right away. 532 return 0; 533} 534 535// Compute the fractional time, taking into account direction. 536// There is no need to worry about iterations, we assume that we would have 537// short circuited above if we were done. 538 539double AnimationBase::fractionalTime(double scale, double elapsedTime, double offset) const 540{ 541 double fractionalTime = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1; 542 // FIXME: startTime can be before the current animation "frame" time. This is to sync with the frame time 543 // concept in AnimationTimeController. So we need to somehow sync the two. Until then, the possible 544 // error is small and will probably not be noticeable. Until we fix this, remove the assert. 545 // https://bugs.webkit.org/show_bug.cgi?id=52037 546 // ASSERT(fractionalTime >= 0); 547 if (fractionalTime < 0) 548 fractionalTime = 0; 549 550 int integralTime = static_cast<int>(fractionalTime); 551 const int integralIterationCount = static_cast<int>(m_animation->iterationCount()); 552 const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount; 553 if (m_animation->iterationCount() != Animation::IterationCountInfinite && !iterationCountHasFractional) 554 integralTime = min(integralTime, integralIterationCount - 1); 555 556 fractionalTime -= integralTime; 557 558 if (((m_animation->direction() == Animation::AnimationDirectionAlternate) && (integralTime & 1)) 559 || ((m_animation->direction() == Animation::AnimationDirectionAlternateReverse) && !(integralTime & 1)) 560 || m_animation->direction() == Animation::AnimationDirectionReverse) 561 fractionalTime = 1 - fractionalTime; 562 563 if (scale != 1 || offset) 564 fractionalTime = (fractionalTime - offset) * scale; 565 566 return fractionalTime; 567} 568 569double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const 570{ 571 if (preActive()) 572 return 0; 573 574 double elapsedTime = getElapsedTime(); 575 576 double dur = m_animation->duration(); 577 if (m_animation->iterationCount() > 0) 578 dur *= m_animation->iterationCount(); 579 580 if (postActive() || !m_animation->duration()) 581 return 1.0; 582 583 if (m_animation->iterationCount() > 0 && elapsedTime >= dur) { 584 const int integralIterationCount = static_cast<int>(m_animation->iterationCount()); 585 const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount; 586 return (integralIterationCount % 2 || iterationCountHasFractional) ? 1.0 : 0.0; 587 } 588 589 const double fractionalTime = this->fractionalTime(scale, elapsedTime, offset); 590 591 if (!tf) 592 tf = m_animation->timingFunction().get(); 593 594 if (tf->isCubicBezierTimingFunction()) { 595 const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(tf); 596 return solveCubicBezierFunction(ctf->x1(), 597 ctf->y1(), 598 ctf->x2(), 599 ctf->y2(), 600 fractionalTime, m_animation->duration()); 601 } 602 603 if (tf->isStepsTimingFunction()) { 604 const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(tf); 605 return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), fractionalTime); 606 } 607 608 return fractionalTime; 609} 610 611void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const 612{ 613 // Decide when the end or loop event needs to fire 614 const double elapsedDuration = max(beginAnimationUpdateTime() - m_startTime, 0.0); 615 double durationLeft = 0; 616 double nextIterationTime = m_totalDuration; 617 618 if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) { 619 durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0; 620 nextIterationTime = elapsedDuration + durationLeft; 621 } 622 623 if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) { 624 // We are not at the end yet 625 ASSERT(nextIterationTime > 0); 626 isLooping = true; 627 } else { 628 // We are at the end 629 isLooping = false; 630 } 631 632 time = durationLeft; 633} 634 635void AnimationBase::goIntoEndingOrLoopingState() 636{ 637 double t; 638 bool isLooping; 639 getTimeToNextEvent(t, isLooping); 640 LOG(Animations, "%p AnimationState %s -> %s", this, nameForState(m_animState), isLooping ? "Looping" : "Ending"); 641 m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding; 642} 643 644void AnimationBase::freezeAtTime(double t) 645{ 646 if (!m_compAnim) 647 return; 648 649 if (!m_startTime) { 650 // If we haven't started yet, make it as if we started. 651 LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); 652 m_animState = AnimationStateStartWaitResponse; 653 onAnimationStartResponse(currentTime()); 654 } 655 656 ASSERT(m_startTime); // if m_startTime is zero, we haven't started yet, so we'll get a bad pause time. 657 if (t <= m_animation->delay()) 658 m_pauseTime = m_startTime; 659 else 660 m_pauseTime = m_startTime + t - m_animation->delay(); 661 662#if USE(ACCELERATED_COMPOSITING) 663 if (m_object && m_object->isComposited()) 664 toRenderBoxModelObject(m_object)->suspendAnimations(m_pauseTime); 665#endif 666} 667 668double AnimationBase::beginAnimationUpdateTime() const 669{ 670 if (!m_compAnim) 671 return 0; 672 673 return m_compAnim->animationController()->beginAnimationUpdateTime(); 674} 675 676double AnimationBase::getElapsedTime() const 677{ 678 if (paused()) 679 return m_pauseTime - m_startTime; 680 if (m_startTime <= 0) 681 return 0; 682 if (postActive()) 683 return 1; 684 685 return beginAnimationUpdateTime() - m_startTime; 686} 687 688void AnimationBase::setElapsedTime(double time) 689{ 690 // FIXME: implement this method 691 UNUSED_PARAM(time); 692} 693 694void AnimationBase::play() 695{ 696 // FIXME: implement this method 697} 698 699void AnimationBase::pause() 700{ 701 // FIXME: implement this method 702} 703 704} // namespace WebCore 705