Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / frame / animation / AnimationBase.cpp
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 "core/frame/animation/AnimationBase.h"
31
32 #include "core/frame/animation/AnimationControllerPrivate.h"
33 #include "core/frame/animation/CompositeAnimation.h"
34 #include "core/rendering/RenderBox.h"
35 #include "platform/animation/AnimationUtilities.h"
36 #include "platform/animation/TimingFunction.h"
37 #include <algorithm>
38
39 using namespace std;
40
41 namespace WebCore {
42
43 AnimationBase::AnimationBase(const CSSAnimationData* transition, RenderObject& renderer, CompositeAnimation* compAnim)
44     : m_animState(AnimationStateNew)
45     , m_isAccelerated(false)
46     , m_transformFunctionListValid(false)
47     , m_filterFunctionListsMatch(false)
48     , m_startTime(0)
49     , m_pauseTime(-1)
50     , m_requestedStartTime(0)
51     , m_totalDuration(-1)
52     , m_nextIterationDuration(-1)
53     , m_object(&renderer)
54     , m_animation(const_cast<CSSAnimationData*>(transition))
55     , m_compAnim(compAnim)
56 {
57     // Compute the total duration
58     if (m_animation->iterationCount() > 0)
59         m_totalDuration = m_animation->duration() * m_animation->iterationCount();
60 }
61
62 void AnimationBase::setNeedsStyleRecalc(Node* node)
63 {
64     if (node)
65         node->setNeedsStyleRecalc(LocalStyleChange);
66 }
67
68 double AnimationBase::duration() const
69 {
70     return m_animation->duration();
71 }
72
73 bool AnimationBase::playStatePlaying() const
74 {
75     return m_animation->playState() == AnimPlayStatePlaying;
76 }
77
78 void AnimationBase::updateStateMachine(AnimStateInput input, double param)
79 {
80     if (!m_compAnim)
81         return;
82
83     // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state.
84     if (input == AnimationStateInputMakeNew) {
85         if (m_animState == AnimationStateStartWaitStyleAvailable)
86             m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
87         m_animState = AnimationStateNew;
88         m_startTime = 0;
89         m_pauseTime = -1;
90         m_requestedStartTime = 0;
91         m_nextIterationDuration = -1;
92         endAnimation();
93         return;
94     }
95
96     if (input == AnimationStateInputRestartAnimation) {
97         if (m_animState == AnimationStateStartWaitStyleAvailable)
98             m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
99         m_animState = AnimationStateNew;
100         m_startTime = 0;
101         m_pauseTime = -1;
102         m_requestedStartTime = 0;
103         m_nextIterationDuration = -1;
104         endAnimation();
105
106         if (!paused())
107             updateStateMachine(AnimationStateInputStartAnimation, -1);
108         return;
109     }
110
111     if (input == AnimationStateInputEndAnimation) {
112         if (m_animState == AnimationStateStartWaitStyleAvailable)
113             m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
114         m_animState = AnimationStateDone;
115         endAnimation();
116         return;
117     }
118
119     if (input == AnimationStateInputPauseOverride) {
120         if (m_animState == AnimationStateStartWaitResponse) {
121             // If we are in AnimationStateStartWaitResponse, the animation will get canceled before
122             // we get a response, so move to the next state.
123             endAnimation();
124             updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
125         }
126         return;
127     }
128
129     if (input == AnimationStateInputResumeOverride) {
130         if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) {
131             // Start the animation
132             startAnimation(beginAnimationUpdateTime() - m_startTime);
133         }
134         return;
135     }
136
137     // Execute state machine
138     switch (m_animState) {
139         case AnimationStateNew:
140             ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning || input == AnimationStateInputPlayStatePaused);
141             if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning) {
142                 m_requestedStartTime = beginAnimationUpdateTime();
143                 m_animState = AnimationStateStartWaitTimer;
144             }
145             break;
146         case AnimationStateStartWaitTimer:
147             ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused);
148
149             if (input == AnimationStateInputStartTimerFired) {
150                 ASSERT(param >= 0);
151                 // Start timer has fired, tell the animation to start and wait for it to respond with start time
152                 m_animState = AnimationStateStartWaitStyleAvailable;
153                 m_compAnim->animationController()->addToAnimationsWaitingForStyle(this);
154
155                 // Trigger a render so we can start the animation
156                 if (m_object)
157                     m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
158             } else {
159                 ASSERT(!paused());
160                 // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait
161                 m_pauseTime = beginAnimationUpdateTime();
162                 m_animState = AnimationStatePausedWaitTimer;
163             }
164             break;
165         case AnimationStateStartWaitStyleAvailable:
166             ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused);
167
168             if (input == AnimationStateInputStyleAvailable) {
169                 // Start timer has fired, tell the animation to start and wait for it to respond with start time
170                 m_animState = AnimationStateStartWaitResponse;
171
172                 overrideAnimations();
173
174                 // Start the animation
175                 if (overridden()) {
176                     m_animState = AnimationStateStartWaitResponse;
177                     updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
178                 } else {
179                     double timeOffset = 0;
180                     // If the value for 'animation-delay' is negative then the animation appears to have started in the past.
181                     if (m_animation->delay() < 0)
182                         timeOffset = -m_animation->delay();
183                     startAnimation(timeOffset);
184                     m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, isAccelerated());
185                 }
186             } else {
187                 // We're waiting for the style to be available and we got a pause. Pause and wait
188                 m_pauseTime = beginAnimationUpdateTime();
189                 m_animState = AnimationStatePausedWaitStyleAvailable;
190             }
191             break;
192         case AnimationStateStartWaitResponse:
193             ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused);
194
195             if (input == AnimationStateInputStartTimeSet) {
196                 ASSERT(param >= 0);
197                 // We have a start time, set it, unless the startTime is already set
198                 if (m_startTime <= 0) {
199                     m_startTime = param;
200                     // If the value for 'animation-delay' is negative then the animation appears to have started in the past.
201                     if (m_animation->delay() < 0)
202                         m_startTime += m_animation->delay();
203                 }
204
205                 // Now that we know the start time, fire the start event.
206                 onAnimationStart(0); // The elapsedTime is 0.
207
208                 // Decide whether to go into looping or ending state
209                 goIntoEndingOrLoopingState();
210
211                 // Dispatch updateStyleIfNeeded so we can start the animation
212                 if (m_object)
213                     m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
214             } else {
215                 // We are pausing while waiting for a start response. Cancel the animation and wait. When
216                 // we unpause, we will act as though the start timer just fired
217                 m_pauseTime = beginAnimationUpdateTime();
218                 pauseAnimation(beginAnimationUpdateTime() - m_startTime);
219                 m_animState = AnimationStatePausedWaitResponse;
220             }
221             break;
222         case AnimationStateLooping:
223             ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused);
224
225             if (input == AnimationStateInputLoopTimerFired) {
226                 ASSERT(param >= 0);
227                 // Loop timer fired, loop again or end.
228                 onAnimationIteration(param);
229
230                 // Decide whether to go into looping or ending state
231                 goIntoEndingOrLoopingState();
232             } else {
233                 // We are pausing while running. Cancel the animation and wait
234                 m_pauseTime = beginAnimationUpdateTime();
235                 pauseAnimation(beginAnimationUpdateTime() - m_startTime);
236                 m_animState = AnimationStatePausedRun;
237             }
238             break;
239         case AnimationStateEnding:
240 #if !LOG_DISABLED
241             if (input != AnimationStateInputEndTimerFired && input != AnimationStateInputPlayStatePaused)
242                 WTF_LOG_ERROR("State is AnimationStateEnding, but input is not AnimationStateInputEndTimerFired or AnimationStateInputPlayStatePaused. It is %d.", input);
243 #endif
244             if (input == AnimationStateInputEndTimerFired) {
245
246                 ASSERT(param >= 0);
247                 // End timer fired, finish up
248                 onAnimationEnd(param);
249
250                 m_animState = AnimationStateDone;
251
252                 if (m_object) {
253                     if (m_animation->fillsForwards())
254                         m_animState = AnimationStateFillingForwards;
255                     else
256                         resumeOverriddenAnimations();
257
258                     // Fire off another style change so we can set the final value
259                     m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
260                 }
261             } else {
262                 // We are pausing while running. Cancel the animation and wait
263                 m_pauseTime = beginAnimationUpdateTime();
264                 pauseAnimation(beginAnimationUpdateTime() - m_startTime);
265                 m_animState = AnimationStatePausedRun;
266             }
267             // |this| may be deleted here
268             break;
269         case AnimationStatePausedWaitTimer:
270             ASSERT(input == AnimationStateInputPlayStateRunning);
271             ASSERT(paused());
272             // Update the times
273             m_startTime += beginAnimationUpdateTime() - m_pauseTime;
274             m_pauseTime = -1;
275
276             // we were waiting for the start timer to fire, go back and wait again
277             m_animState = AnimationStateNew;
278             updateStateMachine(AnimationStateInputStartAnimation, 0);
279             break;
280         case AnimationStatePausedWaitResponse:
281         case AnimationStatePausedWaitStyleAvailable:
282         case AnimationStatePausedRun:
283             // We treat these two cases the same. The only difference is that, when we are in
284             // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation.
285             // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice
286             // that we have already set the startTime and will ignore it.
287             ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable);
288             ASSERT(paused());
289
290             if (input == AnimationStateInputPlayStateRunning) {
291                 // Update the times
292                 if (m_animState == AnimationStatePausedRun)
293                     m_startTime += beginAnimationUpdateTime() - m_pauseTime;
294                 else
295                     m_startTime = 0;
296                 m_pauseTime = -1;
297
298                 if (m_animState == AnimationStatePausedWaitStyleAvailable)
299                     m_animState = AnimationStateStartWaitStyleAvailable;
300                 else {
301                     // We were either running or waiting for a begin time response from the animation.
302                     // Either way we need to restart the animation (possibly with an offset if we
303                     // had already been running) and wait for it to start.
304                     m_animState = AnimationStateStartWaitResponse;
305
306                     // Start the animation
307                     if (overridden()) {
308                         updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
309                     } else {
310                         startAnimation(beginAnimationUpdateTime() - m_startTime);
311                         m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, isAccelerated());
312                     }
313                 }
314                 break;
315             }
316
317             if (input == AnimationStateInputStartTimeSet) {
318                 ASSERT(m_animState == AnimationStatePausedWaitResponse);
319
320                 // We are paused but we got the callback that notifies us that an accelerated animation started.
321                 // We ignore the start time and just move into the paused-run state.
322                 m_animState = AnimationStatePausedRun;
323                 ASSERT(m_startTime == 0);
324                 m_startTime = param;
325                 m_pauseTime += m_startTime;
326                 break;
327             }
328
329             ASSERT(m_animState == AnimationStatePausedWaitStyleAvailable);
330             // We are paused but we got the callback that notifies us that style has been updated.
331             // We move to the AnimationStatePausedWaitResponse state
332             m_animState = AnimationStatePausedWaitResponse;
333             overrideAnimations();
334             break;
335         case AnimationStateFillingForwards:
336         case AnimationStateDone:
337             // We're done. Stay in this state until we are deleted
338             break;
339     }
340 }
341
342 void AnimationBase::fireAnimationEventsIfNeeded()
343 {
344     if (!m_compAnim)
345         return;
346
347     // If we are waiting for the delay time to expire and it has, go to the next state
348     if (m_animState != AnimationStateStartWaitTimer && m_animState != AnimationStateLooping && m_animState != AnimationStateEnding)
349         return;
350
351     // We have to make sure to keep a ref to the this pointer, because it could get destroyed
352     // during an animation callback that might get called. Since the owner is a CompositeAnimation
353     // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase
354     // can still access the resources of its CompositeAnimation as needed.
355     RefPtr<AnimationBase> protector(this);
356     RefPtr<CompositeAnimation> compProtector(m_compAnim);
357
358     // Check for start timeout
359     if (m_animState == AnimationStateStartWaitTimer) {
360         if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay())
361             updateStateMachine(AnimationStateInputStartTimerFired, 0);
362         return;
363     }
364
365     double elapsedDuration = getElapsedTime();
366
367     // Check for end timeout
368     if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) {
369         // We may still be in AnimationStateLooping if we've managed to skip a
370         // whole iteration, in which case we should jump to the end state.
371         m_animState = AnimationStateEnding;
372
373         // Fire an end event
374         updateStateMachine(AnimationStateInputEndTimerFired, m_totalDuration);
375     } else {
376         // Check for iteration timeout
377         if (m_nextIterationDuration < 0) {
378             // Hasn't been set yet, set it
379             double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
380             m_nextIterationDuration = elapsedDuration + durationLeft;
381         }
382
383         if (elapsedDuration >= m_nextIterationDuration) {
384             // Set to the next iteration
385             double previous = m_nextIterationDuration;
386             double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
387             m_nextIterationDuration = elapsedDuration + durationLeft;
388
389             // Send the event
390             updateStateMachine(AnimationStateInputLoopTimerFired, previous);
391         }
392     }
393 }
394
395 void AnimationBase::updatePlayState(EAnimPlayState playState)
396 {
397     if (!m_compAnim)
398         return;
399
400     // Set the state machine to the desired state.
401     bool pause = playState == AnimPlayStatePaused;
402
403     if (pause == paused() && !isNew())
404         return;
405
406     updateStateMachine(pause ?  AnimationStateInputPlayStatePaused : AnimationStateInputPlayStateRunning, -1);
407 }
408
409 double AnimationBase::timeToNextService()
410 {
411     // Returns the time at which next service is required. -1 means no service is required. 0 means
412     // service is required now, and > 0 means service is required that many seconds in the future.
413     if (paused() || isNew() || m_animState == AnimationStateFillingForwards)
414         return -1;
415
416     if (m_animState == AnimationStateStartWaitTimer) {
417         double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime);
418         return max(timeFromNow, 0.0);
419     }
420
421     fireAnimationEventsIfNeeded();
422
423     // In all other cases, we need service right away.
424     return 0;
425 }
426
427 // Compute the fractional time, taking into account direction.
428 // There is no need to worry about iterations, we assume that we would have
429 // short circuited above if we were done.
430
431 double AnimationBase::fractionalTime(double scale, double elapsedTime, double offset) const
432 {
433     double fractionalTime = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1;
434     // FIXME: startTime can be before the current animation "frame" time. This is to sync with the frame time
435     // concept in AnimationTimeController. So we need to somehow sync the two. Until then, the possible
436     // error is small and will probably not be noticeable. Until we fix this, remove the assert.
437     // https://bugs.webkit.org/show_bug.cgi?id=52037
438     // ASSERT(fractionalTime >= 0);
439     if (fractionalTime < 0)
440         fractionalTime = 0;
441
442     int integralTime = static_cast<int>(fractionalTime);
443     const int integralIterationCount = static_cast<int>(m_animation->iterationCount());
444     const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount;
445     if (m_animation->iterationCount() != CSSAnimationData::IterationCountInfinite && !iterationCountHasFractional)
446         integralTime = min(integralTime, integralIterationCount - 1);
447
448     fractionalTime -= integralTime;
449
450     // Thie method can be called with an elapsedTime which very slightly
451     // exceeds the end of the animation. In this case, clamp the
452     // fractionalTime.
453     if (fractionalTime > 1)
454         fractionalTime = 1;
455     ASSERT(fractionalTime >= 0 && fractionalTime <= 1);
456
457     if (((m_animation->direction() == CSSAnimationData::AnimationDirectionAlternate) && (integralTime & 1))
458         || ((m_animation->direction() == CSSAnimationData::AnimationDirectionAlternateReverse) && !(integralTime & 1))
459         || m_animation->direction() == CSSAnimationData::AnimationDirectionReverse)
460         fractionalTime = 1 - fractionalTime;
461
462     fractionalTime -= offset;
463     // Note that if fractionalTime == 0 here, scale may be infinity, but in
464     // this case we don't need to apply scale anyway.
465     if (scale != 1.0 && fractionalTime) {
466         ASSERT(scale >= 0 && !std::isinf(scale));
467         fractionalTime *= scale;
468     }
469
470     ASSERT(fractionalTime >= 0 && fractionalTime <= 1);
471     return fractionalTime;
472 }
473
474 double AnimationBase::progress(double scale, double offset, const TimingFunction* timingFunction) const
475 {
476     if (preActive())
477         return 0;
478
479     double dur = m_animation->duration();
480     if (m_animation->iterationCount() > 0)
481         dur *= m_animation->iterationCount();
482
483     if (postActive() || !m_animation->duration())
484         return 1.0;
485
486     double elapsedTime = getElapsedTime();
487     if (m_animation->iterationCount() > 0 && elapsedTime >= dur) {
488         const int integralIterationCount = static_cast<int>(m_animation->iterationCount());
489         const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount;
490         return (integralIterationCount % 2 || iterationCountHasFractional) ? 1.0 : 0.0;
491     }
492
493     const double fractionalTime = this->fractionalTime(scale, elapsedTime, offset);
494
495     if (!timingFunction)
496         timingFunction = m_animation->timingFunction();
497
498     return timingFunction->evaluate(fractionalTime, accuracyForDuration(m_animation->duration()));
499 }
500
501 void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const
502 {
503     if (postActive()) {
504         time = -1;
505         isLooping = false;
506         return;
507     }
508
509     // Decide when the end or loop event needs to fire
510     const double elapsedDuration = getElapsedTime();
511     double durationLeft = 0;
512     double nextIterationTime = m_totalDuration;
513
514     if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) {
515         durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0;
516         nextIterationTime = elapsedDuration + durationLeft;
517     }
518
519     if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) {
520         // We are not at the end yet
521         ASSERT(m_totalDuration < 0 || nextIterationTime > 0);
522         isLooping = true;
523     } else {
524         // We are at the end
525         isLooping = false;
526     }
527
528     time = durationLeft;
529 }
530
531 void AnimationBase::goIntoEndingOrLoopingState()
532 {
533     double t;
534     bool isLooping;
535     getTimeToNextEvent(t, isLooping);
536     m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding;
537 }
538
539 void AnimationBase::freezeAtTime(double t)
540 {
541     if (!m_compAnim)
542         return;
543
544     if (!m_startTime) {
545         // If we haven't started yet, make it as if we started.
546         m_animState = AnimationStateStartWaitResponse;
547         onAnimationStartResponse(beginAnimationUpdateTime());
548     }
549
550     ASSERT(m_startTime);        // if m_startTime is zero, we haven't started yet, so we'll get a bad pause time.
551     if (t <= m_animation->delay())
552         m_pauseTime = m_startTime;
553     else
554         m_pauseTime = m_startTime + t - m_animation->delay();
555
556     // It is possible that m_isAccelerated is true and m_object->compositingState() is NotComposited, because of style change.
557     // So, both conditions need to be checked.
558     if (m_object && m_object->compositingState() == PaintsIntoOwnBacking && isAccelerated())
559         pauseAnimation(t);
560 }
561
562 double AnimationBase::beginAnimationUpdateTime() const
563 {
564     if (!m_compAnim)
565         return 0;
566
567     return m_compAnim->animationController()->beginAnimationUpdateTime();
568 }
569
570 double AnimationBase::getElapsedTime() const
571 {
572     ASSERT(!postActive());
573     if (paused())
574         return m_pauseTime - m_startTime;
575     if (m_startTime <= 0)
576         return 0;
577
578     double elapsedTime = beginAnimationUpdateTime() - m_startTime;
579     // It's possible for the start time to be ahead of the last update time
580     // if the compositor has just sent notification for the start of an
581     // accelerated animation.
582     return max(elapsedTime, 0.0);
583 }
584
585 } // namespace WebCore