Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / frame / animation / CompositeAnimation.cpp
1 /*
2  * Copyright (C) 2007 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/CompositeAnimation.h"
31
32 #include "CSSPropertyNames.h"
33 #include "core/frame/animation/AnimationControllerPrivate.h"
34 #include "core/frame/animation/CSSPropertyAnimation.h"
35 #include "core/rendering/style/RenderStyle.h"
36
37 namespace WebCore {
38
39 CompositeAnimation::~CompositeAnimation()
40 {
41     // Toss the refs to all animations, but make sure we remove them from
42     // any waiting lists first.
43
44     clearRenderer();
45     m_transitions.clear();
46     m_keyframeAnimations.clear();
47 }
48
49 void CompositeAnimation::clearRenderer()
50 {
51     if (!m_transitions.isEmpty()) {
52         // Clear the renderers from all running animations, in case we are in the middle of
53         // an animation callback (see https://bugs.webkit.org/show_bug.cgi?id=22052)
54         CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
55         for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
56             ImplicitAnimation* transition = it->value.get();
57             animationController()->animationWillBeRemoved(transition);
58             transition->clear();
59         }
60     }
61     if (!m_keyframeAnimations.isEmpty()) {
62         AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
63         for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
64             KeyframeAnimation* anim = it->value.get();
65             animationController()->animationWillBeRemoved(anim);
66             anim->clear();
67         }
68     }
69 }
70
71 void CompositeAnimation::updateTransitions(RenderObject& renderer, RenderStyle* currentStyle, RenderStyle& targetStyle)
72 {
73     // If currentStyle is null or there are no old or new transitions, just skip it
74     if (!currentStyle || (!targetStyle.transitions() && m_transitions.isEmpty()))
75         return;
76
77     // Mark all existing transitions as no longer active. We will mark the still active ones
78     // in the next loop and then toss the ones that didn't get marked.
79     CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
80     for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it)
81         it->value->setActive(false);
82
83     RefPtr<RenderStyle> modifiedCurrentStyle;
84
85     // Check to see if we need to update the active transitions
86     if (targetStyle.transitions()) {
87         for (size_t i = 0; i < targetStyle.transitions()->size(); ++i) {
88             const CSSAnimationData* anim = targetStyle.transitions()->animation(i);
89             bool isActiveTransition = anim->duration() || anim->delay() > 0;
90
91             CSSAnimationData::AnimationMode mode = anim->animationMode();
92             if (mode == CSSAnimationData::AnimateNone)
93                 continue;
94
95             CSSPropertyID prop = anim->property();
96
97             bool all = mode == CSSAnimationData::AnimateAll;
98
99             // Handle both the 'all' and single property cases. For the single prop case, we make only one pass
100             // through the loop.
101             for (int propertyIndex = 0; propertyIndex < CSSPropertyAnimation::getNumProperties(); ++propertyIndex) {
102                 if (all) {
103                     // Get the next property which is not a shorthand.
104                     bool isShorthand;
105                     prop = CSSPropertyAnimation::getPropertyAtIndex(propertyIndex, isShorthand);
106                     if (isShorthand)
107                         continue;
108                 }
109
110                 // ImplicitAnimations are always hashed by actual properties, never animateAll.
111                 ASSERT(prop >= firstCSSProperty && prop < (firstCSSProperty + numCSSProperties));
112
113                 // If there is a running animation for this property, the transition is overridden
114                 // and we have to use the unanimatedStyle from the animation. We do the test
115                 // against the unanimated style here, but we "override" the transition later.
116                 RefPtr<KeyframeAnimation> keyframeAnim = getAnimationForProperty(prop);
117                 RenderStyle* fromStyle = keyframeAnim ? keyframeAnim->unanimatedStyle() : currentStyle;
118
119                 // See if there is a current transition for this prop
120                 ImplicitAnimation* implAnim = m_transitions.get(prop);
121                 bool equal = true;
122
123                 if (implAnim) {
124                     // If we are post active don't bother setting the active flag. This will cause
125                     // this animation to get removed at the end of this function.
126                     if (!implAnim->postActive())
127                         implAnim->setActive(true);
128
129                     // This might be a transition that is just finishing. That would be the case
130                     // if it were postActive. But we still need to check for equality because
131                     // it could be just finishing AND changing to a new goal state.
132                     //
133                     // This implAnim might also not be an already running transition. It might be
134                     // newly added to the list in a previous iteration. This would happen if
135                     // you have both an explicit transition-property and 'all' in the same
136                     // list. In this case, the latter one overrides the earlier one, so we
137                     // behave as though this is a running animation being replaced.
138                     if (!implAnim->isTargetPropertyEqual(prop, &targetStyle)) {
139                         // For accelerated animations we need to return a new RenderStyle with the _current_ value
140                         // of the property, so that restarted transitions use the correct starting point.
141                         if (CSSPropertyAnimation::animationOfPropertyIsAccelerated(prop) && implAnim->isAccelerated()) {
142                             if (!modifiedCurrentStyle)
143                                 modifiedCurrentStyle = RenderStyle::clone(currentStyle);
144
145                             implAnim->blendPropertyValueInStyle(prop, modifiedCurrentStyle.get());
146                         }
147                         animationController()->animationWillBeRemoved(implAnim);
148                         m_transitions.remove(prop);
149                         equal = false;
150                     }
151                 } else {
152                     // We need to start a transition if it is active and the properties don't match
153                     equal = !isActiveTransition || CSSPropertyAnimation::propertiesEqual(prop, fromStyle, &targetStyle);
154                 }
155
156                 // We can be in this loop with an inactive transition (!isActiveTransition). We need
157                 // to do that to check to see if we are canceling a transition. But we don't want to
158                 // start one of the inactive transitions. So short circuit that here. (See
159                 // <https://bugs.webkit.org/show_bug.cgi?id=24787>
160                 if (!equal && isActiveTransition) {
161                     // Add the new transition
162                     m_transitions.set(prop, ImplicitAnimation::create(const_cast<CSSAnimationData*>(anim), prop, renderer, this, modifiedCurrentStyle ? modifiedCurrentStyle.get() : fromStyle));
163                 }
164
165                 // We only need one pass for the single prop case
166                 if (!all)
167                     break;
168             }
169         }
170     }
171
172     // Make a list of transitions to be removed
173     Vector<int> toBeRemoved;
174     end = m_transitions.end();
175     for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
176         ImplicitAnimation* anim = it->value.get();
177         if (!anim->active()) {
178             animationController()->animationWillBeRemoved(anim);
179             toBeRemoved.append(anim->animatingProperty());
180         }
181     }
182
183     // Now remove the transitions from the list
184     for (size_t j = 0; j < toBeRemoved.size(); ++j)
185         m_transitions.remove(toBeRemoved[j]);
186 }
187
188 void CompositeAnimation::updateKeyframeAnimations(RenderObject& renderer, RenderStyle* currentStyle, RenderStyle& targetStyle)
189 {
190     // Nothing to do if we don't have any animations, and didn't have any before
191     if (m_keyframeAnimations.isEmpty() && !targetStyle.hasAnimations())
192         return;
193
194     AnimationNameMap::const_iterator kfend = m_keyframeAnimations.end();
195
196     if (currentStyle && currentStyle->hasAnimations() && targetStyle.hasAnimations() && *(currentStyle->animations()) == *(targetStyle.animations())) {
197         // The current and target animations are the same so we just need to toss any
198         // animation which is finished (postActive).
199         for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
200             if (it->value->postActive())
201                 it->value->setIndex(-1);
202         }
203     } else {
204         // Mark all existing animations as no longer active.
205         for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it)
206             it->value->setIndex(-1);
207
208         // Toss the animation order map.
209         m_keyframeAnimationOrderList.clear();
210
211         DEFINE_STATIC_LOCAL(const AtomicString, none, ("none", AtomicString::ConstructFromLiteral));
212
213         // Now mark any still active animations as active and add any new animations.
214         if (targetStyle.animations()) {
215             int numAnims = targetStyle.animations()->size();
216             for (int i = 0; i < numAnims; ++i) {
217                 const CSSAnimationData* anim = targetStyle.animations()->animation(i);
218                 if (!anim->isValidAnimation())
219                     continue;
220
221                 // See if there is a current animation for this name.
222                 AtomicString name(anim->name());
223                 RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(name);
224                 if (keyframeAnim) {
225                     // If this animation is postActive, skip it so it gets removed at the end of this function.
226                     if (keyframeAnim->postActive())
227                         continue;
228
229                     // This one is still active.
230
231                     // Animations match, but play states may differ. Update if needed.
232                     keyframeAnim->updatePlayState(anim->playState());
233
234                     // Set the saved animation to this new one, just in case the play state has changed.
235                     keyframeAnim->setAnimation(anim);
236                     keyframeAnim->setIndex(i);
237                 } else if ((anim->duration() || anim->delay()) && anim->iterationCount() && name != none) {
238                     keyframeAnim = KeyframeAnimation::create(const_cast<CSSAnimationData*>(anim), renderer, i, this, targetStyle);
239                     m_keyframeAnimations.set(name, keyframeAnim);
240                 }
241
242                 // Add this to the animation order map.
243                 if (keyframeAnim)
244                     m_keyframeAnimationOrderList.append(name);
245             }
246         }
247     }
248
249     // Make a list of animations to be removed.
250     Vector<AtomicString> animsToBeRemoved;
251     kfend = m_keyframeAnimations.end();
252     for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) {
253         KeyframeAnimation* keyframeAnim = it->value.get();
254         if (keyframeAnim->index() < 0) {
255             animsToBeRemoved.append(keyframeAnim->name());
256             animationController()->animationWillBeRemoved(keyframeAnim);
257             keyframeAnim->clear();
258         }
259     }
260
261     // Now remove the animations from the list, and keep stale keys out of the order list.
262     if (animsToBeRemoved.size()) {
263         for (size_t j = 0; j < animsToBeRemoved.size(); ++j) {
264             ASSERT(m_keyframeAnimations.contains(animsToBeRemoved[j]));
265             m_keyframeAnimations.remove(animsToBeRemoved[j]);
266         }
267         Vector<AtomicString> newOrderList;
268         for (size_t j = 0; j < m_keyframeAnimationOrderList.size(); ++j) {
269             AtomicString key = m_keyframeAnimationOrderList[j];
270             if (m_keyframeAnimations.contains(key))
271                 newOrderList.append(key);
272         }
273         m_keyframeAnimationOrderList.swap(newOrderList);
274     }
275 }
276
277 PassRefPtr<RenderStyle> CompositeAnimation::animate(RenderObject& renderer, RenderStyle* currentStyle, RenderStyle& targetStyle)
278 {
279     RefPtr<RenderStyle> resultStyle;
280
281     // We don't do any transitions if we don't have a currentStyle (on startup).
282     updateTransitions(renderer, currentStyle, targetStyle);
283     updateKeyframeAnimations(renderer, currentStyle, targetStyle);
284
285     if (currentStyle) {
286         // Now that we have transition objects ready, let them know about the new goal state.  We want them
287         // to fill in a RenderStyle*& only if needed.
288         if (!m_transitions.isEmpty()) {
289             CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
290             for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
291                 if (ImplicitAnimation* anim = it->value.get())
292                     anim->animate(this, &renderer, currentStyle, &targetStyle, resultStyle);
293             }
294         }
295     }
296
297     // Now that we have animation objects ready, let them know about the new goal state.  We want them
298     // to fill in a RenderStyle*& only if needed.
299     for (Vector<AtomicString>::const_iterator it = m_keyframeAnimationOrderList.begin(); it != m_keyframeAnimationOrderList.end(); ++it) {
300         RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(*it);
301         ASSERT(keyframeAnim);
302         keyframeAnim->animate(this, &renderer, currentStyle, &targetStyle, resultStyle);
303     }
304
305     return resultStyle ? resultStyle.release() : PassRefPtr<RenderStyle>(targetStyle);
306 }
307
308 PassRefPtr<RenderStyle> CompositeAnimation::getAnimatedStyle() const
309 {
310     RefPtr<RenderStyle> resultStyle;
311     CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
312     for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
313         if (ImplicitAnimation* implicitAnimation = it->value.get())
314             implicitAnimation->getAnimatedStyle(resultStyle);
315     }
316
317     for (Vector<AtomicString>::const_iterator it = m_keyframeAnimationOrderList.begin(); it != m_keyframeAnimationOrderList.end(); ++it) {
318         RefPtr<KeyframeAnimation> keyframeAnimation = m_keyframeAnimations.get(*it);
319         ASSERT(keyframeAnimation);
320         keyframeAnimation->getAnimatedStyle(resultStyle);
321     }
322
323     return resultStyle;
324 }
325
326 double CompositeAnimation::timeToNextService() const
327 {
328     // Returns the time at which next service is required. -1 means no service is required. 0 means
329     // service is required now, and > 0 means service is required that many seconds in the future.
330     double minT = -1;
331
332     if (!m_transitions.isEmpty()) {
333         CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
334         for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
335             ImplicitAnimation* transition = it->value.get();
336             double t = transition ? transition->timeToNextService() : -1;
337             if (t < minT || minT == -1)
338                 minT = t;
339             if (minT == 0)
340                 return 0;
341         }
342     }
343     if (!m_keyframeAnimations.isEmpty()) {
344         AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
345         for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
346             KeyframeAnimation* animation = it->value.get();
347             double t = animation ? animation->timeToNextService() : -1;
348             if (t < minT || minT == -1)
349                 minT = t;
350             if (minT == 0)
351                 return 0;
352         }
353     }
354
355     return minT;
356 }
357
358 double CompositeAnimation::timeToNextEvent() const
359 {
360     // Returns the time at which next service to trigger events is required. -1 means no service is required. 0 means
361     // service is required now, and > 0 means service is required that many seconds in the future.
362     double minT = -1;
363
364     if (!m_transitions.isEmpty()) {
365         CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
366         for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
367             ImplicitAnimation* transition = it->value.get();
368             double t = -1;
369             bool isLooping;
370             if (transition)
371                 transition->getTimeToNextEvent(t, isLooping);
372             if (t < minT || minT == -1)
373                 minT = t;
374             if (!minT)
375                 return 0;
376         }
377     }
378     if (!m_keyframeAnimations.isEmpty()) {
379         AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
380         for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
381             KeyframeAnimation* animation = it->value.get();
382             double t = -1;
383             bool isLooping;
384             if (animation)
385                 animation->getTimeToNextEvent(t, isLooping);
386             if (t < minT || minT == -1)
387                 minT = t;
388             if (!minT)
389                 return 0;
390         }
391     }
392
393     return minT;
394 }
395
396 PassRefPtr<KeyframeAnimation> CompositeAnimation::getAnimationForProperty(CSSPropertyID property) const
397 {
398     RefPtr<KeyframeAnimation> retval;
399
400     // We want to send back the last animation with the property if there are multiples.
401     // So we need to iterate through all animations
402     if (!m_keyframeAnimations.isEmpty()) {
403         AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
404         for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
405             RefPtr<KeyframeAnimation> anim = it->value;
406             if (anim->hasAnimationForProperty(property))
407                 retval = anim;
408         }
409     }
410
411     return retval;
412 }
413
414 void CompositeAnimation::overrideImplicitAnimations(CSSPropertyID property)
415 {
416     CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
417     if (!m_transitions.isEmpty()) {
418         for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
419             ImplicitAnimation* anim = it->value.get();
420             if (anim && anim->animatingProperty() == property)
421                 anim->setOverridden(true);
422         }
423     }
424 }
425
426 void CompositeAnimation::resumeOverriddenImplicitAnimations(CSSPropertyID property)
427 {
428     if (!m_transitions.isEmpty()) {
429         CSSPropertyTransitionsMap::const_iterator end = m_transitions.end();
430         for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) {
431             ImplicitAnimation* anim = it->value.get();
432             if (anim && anim->animatingProperty() == property)
433                 anim->setOverridden(false);
434         }
435     }
436 }
437
438 bool CompositeAnimation::isAnimatingProperty(CSSPropertyID property, bool acceleratedOnly, bool isRunningNow) const
439 {
440     if (!m_keyframeAnimations.isEmpty()) {
441         AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
442         for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
443             KeyframeAnimation* anim = it->value.get();
444             if (anim && anim->isAnimatingProperty(property, acceleratedOnly, isRunningNow))
445                 return true;
446         }
447     }
448
449     if (!m_transitions.isEmpty()) {
450         CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
451         for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
452             ImplicitAnimation* anim = it->value.get();
453             if (anim && anim->isAnimatingProperty(property, acceleratedOnly, isRunningNow))
454                 return true;
455         }
456     }
457     return false;
458 }
459
460 void CompositeAnimation::pauseAnimationsForTesting(double t)
461 {
462     AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
463     for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
464         RefPtr<KeyframeAnimation> keyframeAnim = it->value;
465         if (!keyframeAnim || !keyframeAnim->running())
466             continue;
467
468         double count = keyframeAnim->m_animation->iterationCount();
469         if ((t >= 0.0) && ((count == CSSAnimationData::IterationCountInfinite) || (t <= count * keyframeAnim->duration())))
470             keyframeAnim->freezeAtTime(t);
471     }
472
473     CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
474     for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
475         RefPtr<ImplicitAnimation> implAnim = it->value;
476
477         if (!implAnim->running())
478             continue;
479
480         if ((t >= 0.0) && (t <= implAnim->duration()))
481             implAnim->freezeAtTime(t);
482     }
483 }
484
485 unsigned CompositeAnimation::numberOfActiveAnimations() const
486 {
487     unsigned count = 0;
488
489     if (!m_keyframeAnimations.isEmpty()) {
490         AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end();
491         for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) {
492             KeyframeAnimation* anim = it->value.get();
493             if (anim->running())
494                 ++count;
495         }
496     }
497
498     if (!m_transitions.isEmpty()) {
499         CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end();
500         for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) {
501             ImplicitAnimation* anim = it->value.get();
502             if (anim->running())
503                 ++count;
504         }
505     }
506
507     return count;
508 }
509
510 } // namespace WebCore