Make some things that return never-null pointers return references instead.
[WebKit-https.git] / Source / WebCore / page / animation / KeyframeAnimation.cpp
1 /*
2  * Copyright (C) 2007, 2012 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 "KeyframeAnimation.h"
31
32 #include "AnimationControllerPrivate.h"
33 #include "CSSPropertyAnimation.h"
34 #include "CSSPropertyNames.h"
35 #include "CompositeAnimation.h"
36 #include "EventNames.h"
37 #include "RenderBoxModelObject.h"
38 #include "RenderStyle.h"
39 #include "StyleResolver.h"
40
41 using namespace std;
42
43 namespace WebCore {
44
45 KeyframeAnimation::KeyframeAnimation(const Animation* animation, RenderObject* renderer, int index, CompositeAnimation* compAnim, RenderStyle* unanimatedStyle)
46     : AnimationBase(animation, renderer, compAnim)
47     , m_keyframes(renderer, animation->name())
48     , m_index(index)
49     , m_startEventDispatched(false)
50     , m_unanimatedStyle(unanimatedStyle)
51 {
52     // Get the keyframe RenderStyles
53     if (m_object && m_object->node() && m_object->node()->isElementNode())
54         m_object->document()->ensureStyleResolver().keyframeStylesForAnimation(toElement(m_object->node()), unanimatedStyle, m_keyframes);
55
56     // Update the m_transformFunctionListValid flag based on whether the function lists in the keyframes match.
57     validateTransformFunctionList();
58 #if ENABLE(CSS_FILTERS)
59     checkForMatchingFilterFunctionLists();
60 #endif
61 }
62
63 KeyframeAnimation::~KeyframeAnimation()
64 {
65     // Make sure to tell the renderer that we are ending. This will make sure any accelerated animations are removed.
66     if (!postActive())
67         endAnimation();
68 }
69
70 static const Animation* getAnimationFromStyleByName(const RenderStyle* style, const AtomicString& name)
71 {
72     if (!style->animations())
73         return 0;
74
75     for (size_t i = 0; i < style->animations()->size(); i++) {
76         if (name == style->animations()->animation(i)->name())
77             return style->animations()->animation(i);
78     }
79
80     return 0;
81 }
82
83 void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) const
84 {
85     // Find the first key
86     double elapsedTime = getElapsedTime();
87     if (m_animation->duration() && m_animation->iterationCount() != Animation::IterationCountInfinite)
88         elapsedTime = min(elapsedTime, m_animation->duration() * m_animation->iterationCount());
89
90     const double fractionalTime = this->fractionalTime(1, elapsedTime, 0);
91
92     size_t numKeyframes = m_keyframes.size();
93     if (!numKeyframes)
94         return;
95     
96     ASSERT(!m_keyframes[0].key());
97     ASSERT(m_keyframes[m_keyframes.size() - 1].key() == 1);
98     
99     int prevIndex = -1;
100     int nextIndex = -1;
101     
102     // FIXME: with a lot of keys, this linear search will be slow. We could binary search.
103     for (size_t i = 0; i < numKeyframes; ++i) {
104         const KeyframeValue& currKeyFrame = m_keyframes[i];
105
106         if (!currKeyFrame.containsProperty(property))
107             continue;
108
109         if (fractionalTime < currKeyFrame.key()) {
110             nextIndex = i;
111             break;
112         }
113         
114         prevIndex = i;
115     }
116
117     double scale = 1;
118     double offset = 0;
119
120     if (prevIndex == -1)
121         prevIndex = 0;
122
123     if (nextIndex == -1)
124         nextIndex = m_keyframes.size() - 1;
125
126     const KeyframeValue& prevKeyframe = m_keyframes[prevIndex];
127     const KeyframeValue& nextKeyframe = m_keyframes[nextIndex];
128
129     fromStyle = prevKeyframe.style();
130     toStyle = nextKeyframe.style();
131     
132     offset = prevKeyframe.key();
133     scale = 1.0 / (nextKeyframe.key() - prevKeyframe.key());
134
135     const TimingFunction* timingFunction = 0;
136     if (const Animation* matchedAnimation = getAnimationFromStyleByName(fromStyle, name()))
137         timingFunction = matchedAnimation->timingFunction().get();
138
139     prog = progress(scale, offset, timingFunction);
140 }
141
142 void KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderObject*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle)
143 {
144     // Fire the start timeout if needed
145     fireAnimationEventsIfNeeded();
146     
147     // If we have not yet started, we will not have a valid start time, so just start the animation if needed.
148     if (isNew() && m_animation->playState() == AnimPlayStatePlaying && !compositeAnimation->isSuspended())
149         updateStateMachine(AnimationStateInputStartAnimation, -1);
150
151     // If we get this far and the animation is done, it means we are cleaning up a just finished animation.
152     // If so, we need to send back the targetStyle.
153     if (postActive()) {
154         if (!animatedStyle)
155             animatedStyle = const_cast<RenderStyle*>(targetStyle);
156         return;
157     }
158
159     // If we are waiting for the start timer, we don't want to change the style yet.
160     // Special case 1 - if the delay time is 0, then we do want to set the first frame of the
161     // animation right away. This avoids a flash when the animation starts.
162     // Special case 2 - if there is a backwards fill mode, then we want to continue
163     // through to the style blend so that we get the fromStyle.
164     if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())
165         return;
166     
167     // If we have no keyframes, don't animate.
168     if (!m_keyframes.size()) {
169         updateStateMachine(AnimationStateInputEndAnimation, -1);
170         return;
171     }
172
173     // Run a cycle of animation.
174     // We know we will need a new render style, so make one if needed.
175     if (!animatedStyle)
176         animatedStyle = RenderStyle::clone(targetStyle);
177
178     // FIXME: we need to be more efficient about determining which keyframes we are animating between.
179     // We should cache the last pair or something.
180     HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties();
181     for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
182         // Get the from/to styles and progress between
183         const RenderStyle* fromStyle = 0;
184         const RenderStyle* toStyle = 0;
185         double progress = 0.0;
186         fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress);
187 #if USE(ACCELERATED_COMPOSITING)
188         bool needsAnim = CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress);
189         if (!needsAnim)
190             // If we are running an accelerated animation, set a flag in the style
191             // to indicate it. This can be used to make sure we get an updated
192             // style for hit testing, etc.
193             animatedStyle->setIsRunningAcceleratedAnimation();
194 #endif
195     }
196 }
197
198 void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle)
199 {
200     // If we're in the delay phase and we're not backwards filling, tell the caller
201     // to use the current style.
202     if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())
203         return;
204
205     if (!m_keyframes.size())
206         return;
207
208     if (!animatedStyle)
209         animatedStyle = RenderStyle::clone(m_object->style());
210
211     HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties();
212     for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
213         // Get the from/to styles and progress between
214         const RenderStyle* fromStyle = 0;
215         const RenderStyle* toStyle = 0;
216         double progress = 0.0;
217         fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress);
218
219         CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress);
220     }
221 }
222
223 bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const
224 {
225     return m_keyframes.containsProperty(property);
226 }
227
228 bool KeyframeAnimation::startAnimation(double timeOffset)
229 {
230 #if USE(ACCELERATED_COMPOSITING)
231     if (m_object && m_object->isComposited()) {
232         return toRenderBoxModelObject(m_object)->startAnimation(timeOffset, m_animation.get(), m_keyframes);
233     }
234 #else
235     UNUSED_PARAM(timeOffset);
236 #endif
237     return false;
238 }
239
240 void KeyframeAnimation::pauseAnimation(double timeOffset)
241 {
242     if (!m_object)
243         return;
244
245 #if USE(ACCELERATED_COMPOSITING)
246     if (m_object->isComposited())
247         toRenderBoxModelObject(m_object)->animationPaused(timeOffset, m_keyframes.animationName());
248 #else
249     UNUSED_PARAM(timeOffset);
250 #endif
251     // Restore the original (unanimated) style
252     if (!paused())
253         setNeedsStyleRecalc(m_object->node());
254 }
255
256 void KeyframeAnimation::endAnimation()
257 {
258     if (!m_object)
259         return;
260
261 #if USE(ACCELERATED_COMPOSITING)
262     if (m_object->isComposited())
263         toRenderBoxModelObject(m_object)->animationFinished(m_keyframes.animationName());
264 #endif
265     // Restore the original (unanimated) style
266     if (!paused())
267         setNeedsStyleRecalc(m_object->node());
268 }
269
270 bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listenerType) const
271 {
272     return m_object->document()->hasListenerType(listenerType);
273 }
274
275 void KeyframeAnimation::onAnimationStart(double elapsedTime)
276 {
277     sendAnimationEvent(eventNames().webkitAnimationStartEvent, elapsedTime);
278 }
279
280 void KeyframeAnimation::onAnimationIteration(double elapsedTime)
281 {
282     sendAnimationEvent(eventNames().webkitAnimationIterationEvent, elapsedTime);
283 }
284
285 void KeyframeAnimation::onAnimationEnd(double elapsedTime)
286 {
287     sendAnimationEvent(eventNames().webkitAnimationEndEvent, elapsedTime);
288     // End the animation if we don't fill forwards. Forward filling
289     // animations are ended properly in the class destructor.
290     if (!m_animation->fillsForwards())
291         endAnimation();
292 }
293
294 bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime)
295 {
296     Document::ListenerType listenerType;
297     if (eventType == eventNames().webkitAnimationIterationEvent)
298         listenerType = Document::ANIMATIONITERATION_LISTENER;
299     else if (eventType == eventNames().webkitAnimationEndEvent)
300         listenerType = Document::ANIMATIONEND_LISTENER;
301     else {
302         ASSERT(eventType == eventNames().webkitAnimationStartEvent);
303         if (m_startEventDispatched)
304             return false;
305         m_startEventDispatched = true;
306         listenerType = Document::ANIMATIONSTART_LISTENER;
307     }
308
309     if (shouldSendEventForListener(listenerType)) {
310         // Dispatch the event
311         RefPtr<Element> element;
312         if (m_object->node() && m_object->node()->isElementNode())
313             element = toElement(m_object->node());
314
315         ASSERT(!element || (element->document() && !element->document()->inPageCache()));
316         if (!element)
317             return false;
318
319         // Schedule event handling
320         m_compAnim->animationController()->addEventToDispatch(element, eventType, m_keyframes.animationName(), elapsedTime);
321
322         // Restore the original (unanimated) style
323         if (eventType == eventNames().webkitAnimationEndEvent && element->renderer())
324             setNeedsStyleRecalc(element.get());
325
326         return true; // Did dispatch an event
327     }
328
329     return false; // Did not dispatch an event
330 }
331
332 void KeyframeAnimation::overrideAnimations()
333 {
334     // This will override implicit animations that match the properties in the keyframe animation
335     HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties();
336     for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it)
337         compositeAnimation()->overrideImplicitAnimations(*it);
338 }
339
340 void KeyframeAnimation::resumeOverriddenAnimations()
341 {
342     // This will resume overridden implicit animations
343     HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties();
344     for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it)
345         compositeAnimation()->resumeOverriddenImplicitAnimations(*it);
346 }
347
348 bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const
349 {
350     return m_keyframes.containsProperty(property);
351 }
352
353 void KeyframeAnimation::validateTransformFunctionList()
354 {
355     m_transformFunctionListValid = false;
356     
357     if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitTransform))
358         return;
359
360     // Empty transforms match anything, so find the first non-empty entry as the reference
361     size_t numKeyframes = m_keyframes.size();
362     size_t firstNonEmptyTransformKeyframeIndex = numKeyframes;
363
364     for (size_t i = 0; i < numKeyframes; ++i) {
365         const KeyframeValue& currentKeyframe = m_keyframes[i];
366         if (currentKeyframe.style()->transform().operations().size()) {
367             firstNonEmptyTransformKeyframeIndex = i;
368             break;
369         }
370     }
371     
372     if (firstNonEmptyTransformKeyframeIndex == numKeyframes)
373         return;
374         
375     const TransformOperations* firstVal = &m_keyframes[firstNonEmptyTransformKeyframeIndex].style()->transform();
376     
377     // See if the keyframes are valid
378     for (size_t i = firstNonEmptyTransformKeyframeIndex + 1; i < numKeyframes; ++i) {
379         const KeyframeValue& currentKeyframe = m_keyframes[i];
380         const TransformOperations* val = &currentKeyframe.style()->transform();
381         
382         // An emtpy transform list matches anything.
383         if (val->operations().isEmpty())
384             continue;
385         
386         if (!firstVal->operationsMatch(*val))
387             return;
388     }
389
390     // Keyframes are valid
391     m_transformFunctionListValid = true;
392 }
393
394 #if ENABLE(CSS_FILTERS)
395 void KeyframeAnimation::checkForMatchingFilterFunctionLists()
396 {
397     m_filterFunctionListsMatch = false;
398
399     if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitFilter))
400         return;
401
402     // Empty filters match anything, so find the first non-empty entry as the reference
403     size_t numKeyframes = m_keyframes.size();
404     size_t firstNonEmptyFilterKeyframeIndex = numKeyframes;
405
406     for (size_t i = 0; i < numKeyframes; ++i) {
407         const KeyframeValue& currentKeyframe = m_keyframes[i];
408         if (currentKeyframe.style()->filter().operations().size()) {
409             firstNonEmptyFilterKeyframeIndex = i;
410             break;
411         }
412     }
413     
414     if (firstNonEmptyFilterKeyframeIndex == numKeyframes)
415         return;
416         
417     const FilterOperations* firstVal = &m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->filter();
418     
419     for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) {
420         const KeyframeValue& currentKeyframe = m_keyframes[i];
421         const FilterOperations* val = &currentKeyframe.style()->filter();
422         
423         // An emtpy filter list matches anything.
424         if (val->operations().isEmpty())
425             continue;
426         
427         if (!firstVal->operationsMatch(*val))
428             return;
429     }
430     
431     m_filterFunctionListsMatch = true;
432 }
433 #endif
434
435 double KeyframeAnimation::timeToNextService()
436 {
437     double t = AnimationBase::timeToNextService();
438 #if USE(ACCELERATED_COMPOSITING)
439     if (t != 0 || preActive())
440         return t;
441         
442     // A return value of 0 means we need service. But if we only have accelerated animations we 
443     // only need service at the end of the transition
444     HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties();
445     bool acceleratedPropertiesOnly = true;
446     
447     for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
448         if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(*it) || !isAccelerated()) {
449             acceleratedPropertiesOnly = false;
450             break;
451         }
452     }
453
454     if (acceleratedPropertiesOnly) {
455         bool isLooping;
456         getTimeToNextEvent(t, isLooping);
457     }
458 #endif
459     return t;
460 }
461
462 } // namespace WebCore