Fix the ACCELERATED_COMPOSITING code to not expose RenderLayer outside rendering
[WebKit.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 "CSSPropertyNames.h"
34 #include "CSSStyleSelector.h"
35 #include "CompositeAnimation.h"
36 #include "EventNames.h"
37 #include "RenderBoxModelObject.h"
38 #include "RenderStyle.h"
39 #include <wtf/UnusedParam.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()->styleSelector()->keyframeStylesForAnimation(static_cast<Element*>(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*, 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)
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     
188         bool needsAnim = blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress);
189         if (needsAnim)
190             setAnimating();
191         else {
192 #if USE(ACCELERATED_COMPOSITING)
193             // If we are running an accelerated animation, set a flag in the style
194             // to indicate it. This can be used to make sure we get an updated
195             // style for hit testing, etc.
196             animatedStyle->setIsRunningAcceleratedAnimation();
197 #endif
198         }
199     }
200 }
201
202 void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle)
203 {
204     // If we're in the delay phase and we're not backwards filling, tell the caller
205     // to use the current style.
206     if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())
207         return;
208
209     if (!m_keyframes.size())
210         return;
211
212     if (!animatedStyle)
213         animatedStyle = RenderStyle::clone(m_object->style());
214
215     HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties();
216     for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
217         // Get the from/to styles and progress between
218         const RenderStyle* fromStyle = 0;
219         const RenderStyle* toStyle = 0;
220         double progress = 0.0;
221         fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress);
222
223         blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress);
224     }
225 }
226
227 bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const
228 {
229     return m_keyframes.containsProperty(property);
230 }
231
232 bool KeyframeAnimation::startAnimation(double timeOffset)
233 {
234 #if USE(ACCELERATED_COMPOSITING)
235     if (m_object && m_object->isComposited()) {
236         return toRenderBoxModelObject(m_object)->startAnimation(timeOffset, m_animation.get(), m_keyframes);
237     }
238 #else
239     UNUSED_PARAM(timeOffset);
240 #endif
241     return false;
242 }
243
244 void KeyframeAnimation::pauseAnimation(double timeOffset)
245 {
246     if (!m_object)
247         return;
248
249 #if USE(ACCELERATED_COMPOSITING)
250     if (m_object->isComposited())
251         toRenderBoxModelObject(m_object)->animationPaused(timeOffset, m_keyframes.animationName());
252 #else
253     UNUSED_PARAM(timeOffset);
254 #endif
255     // Restore the original (unanimated) style
256     if (!paused())
257         setNeedsStyleRecalc(m_object->node());
258 }
259
260 void KeyframeAnimation::endAnimation()
261 {
262     if (!m_object)
263         return;
264
265 #if USE(ACCELERATED_COMPOSITING)
266     if (m_object->isComposited())
267         toRenderBoxModelObject(m_object)->animationFinished(m_keyframes.animationName());
268 #endif
269     // Restore the original (unanimated) style
270     if (!paused())
271         setNeedsStyleRecalc(m_object->node());
272 }
273
274 bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listenerType) const
275 {
276     return m_object->document()->hasListenerType(listenerType);
277 }
278
279 void KeyframeAnimation::onAnimationStart(double elapsedTime)
280 {
281     sendAnimationEvent(eventNames().webkitAnimationStartEvent, elapsedTime);
282 }
283
284 void KeyframeAnimation::onAnimationIteration(double elapsedTime)
285 {
286     sendAnimationEvent(eventNames().webkitAnimationIterationEvent, elapsedTime);
287 }
288
289 void KeyframeAnimation::onAnimationEnd(double elapsedTime)
290 {
291     sendAnimationEvent(eventNames().webkitAnimationEndEvent, elapsedTime);
292     // End the animation if we don't fill forwards. Forward filling
293     // animations are ended properly in the class destructor.
294     if (!m_animation->fillsForwards())
295         endAnimation();
296 }
297
298 bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime)
299 {
300     Document::ListenerType listenerType;
301     if (eventType == eventNames().webkitAnimationIterationEvent)
302         listenerType = Document::ANIMATIONITERATION_LISTENER;
303     else if (eventType == eventNames().webkitAnimationEndEvent)
304         listenerType = Document::ANIMATIONEND_LISTENER;
305     else {
306         ASSERT(eventType == eventNames().webkitAnimationStartEvent);
307         if (m_startEventDispatched)
308             return false;
309         m_startEventDispatched = true;
310         listenerType = Document::ANIMATIONSTART_LISTENER;
311     }
312
313     if (shouldSendEventForListener(listenerType)) {
314         // Dispatch the event
315         RefPtr<Element> element;
316         if (m_object->node() && m_object->node()->isElementNode())
317             element = static_cast<Element*>(m_object->node());
318
319         ASSERT(!element || (element->document() && !element->document()->inPageCache()));
320         if (!element)
321             return false;
322
323         // Schedule event handling
324         m_compAnim->animationController()->addEventToDispatch(element, eventType, m_keyframes.animationName(), elapsedTime);
325
326         // Restore the original (unanimated) style
327         if (eventType == eventNames().webkitAnimationEndEvent && element->renderer())
328             setNeedsStyleRecalc(element.get());
329
330         return true; // Did dispatch an event
331     }
332
333     return false; // Did not dispatch an event
334 }
335
336 void KeyframeAnimation::overrideAnimations()
337 {
338     // This will override implicit animations that match the properties in the keyframe animation
339     HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties();
340     for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it)
341         compositeAnimation()->overrideImplicitAnimations(*it);
342 }
343
344 void KeyframeAnimation::resumeOverriddenAnimations()
345 {
346     // This will resume overridden implicit animations
347     HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties();
348     for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it)
349         compositeAnimation()->resumeOverriddenImplicitAnimations(*it);
350 }
351
352 bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const
353 {
354     return m_keyframes.containsProperty(property);
355 }
356
357 void KeyframeAnimation::validateTransformFunctionList()
358 {
359     m_transformFunctionListValid = false;
360     
361     if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitTransform))
362         return;
363
364     // Empty transforms match anything, so find the first non-empty entry as the reference
365     size_t numKeyframes = m_keyframes.size();
366     size_t firstNonEmptyTransformKeyframeIndex = numKeyframes;
367
368     for (size_t i = 0; i < numKeyframes; ++i) {
369         const KeyframeValue& currentKeyframe = m_keyframes[i];
370         if (currentKeyframe.style()->transform().operations().size()) {
371             firstNonEmptyTransformKeyframeIndex = i;
372             break;
373         }
374     }
375     
376     if (firstNonEmptyTransformKeyframeIndex == numKeyframes)
377         return;
378         
379     const TransformOperations* firstVal = &m_keyframes[firstNonEmptyTransformKeyframeIndex].style()->transform();
380     
381     // See if the keyframes are valid
382     for (size_t i = firstNonEmptyTransformKeyframeIndex + 1; i < numKeyframes; ++i) {
383         const KeyframeValue& currentKeyframe = m_keyframes[i];
384         const TransformOperations* val = &currentKeyframe.style()->transform();
385         
386         // An emtpy transform list matches anything.
387         if (val->operations().isEmpty())
388             continue;
389         
390         if (!firstVal->operationsMatch(*val))
391             return;
392     }
393
394     // Keyframes are valid
395     m_transformFunctionListValid = true;
396 }
397
398 #if ENABLE(CSS_FILTERS)
399 void KeyframeAnimation::checkForMatchingFilterFunctionLists()
400 {
401     m_filterFunctionListsMatch = false;
402
403     if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitFilter))
404         return;
405
406     // Empty filters match anything, so find the first non-empty entry as the reference
407     size_t numKeyframes = m_keyframes.size();
408     size_t firstNonEmptyFilterKeyframeIndex = numKeyframes;
409
410     for (size_t i = 0; i < numKeyframes; ++i) {
411         const KeyframeValue& currentKeyframe = m_keyframes[i];
412         if (currentKeyframe.style()->filter().operations().size()) {
413             firstNonEmptyFilterKeyframeIndex = i;
414             break;
415         }
416     }
417     
418     if (firstNonEmptyFilterKeyframeIndex == numKeyframes)
419         return;
420         
421     const FilterOperations* firstVal = &m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->filter();
422     
423     for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) {
424         const KeyframeValue& currentKeyframe = m_keyframes[i];
425         const FilterOperations* val = &currentKeyframe.style()->filter();
426         
427         // An emtpy filter list matches anything.
428         if (val->operations().isEmpty())
429             continue;
430         
431         if (!firstVal->operationsMatch(*val))
432             return;
433     }
434     
435     m_filterFunctionListsMatch = true;
436 }
437 #endif
438
439 double KeyframeAnimation::timeToNextService()
440 {
441     double t = AnimationBase::timeToNextService();
442 #if USE(ACCELERATED_COMPOSITING)
443     if (t != 0 || preActive())
444         return t;
445         
446     // A return value of 0 means we need service. But if we only have accelerated animations we 
447     // only need service at the end of the transition
448     HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties();
449     bool acceleratedPropertiesOnly = true;
450     
451     for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
452         if (!animationOfPropertyIsAccelerated(*it) || !isAccelerated()) {
453             acceleratedPropertiesOnly = false;
454             break;
455         }
456     }
457
458     if (acceleratedPropertiesOnly) {
459         bool isLooping;
460         getTimeToNextEvent(t, isLooping);
461     }
462 #endif
463     return t;
464 }
465
466 } // namespace WebCore