• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 "RenderLayer.h"
38 #include "RenderLayerBacking.h"
39 #include <wtf/UnusedParam.h>
40 
41 namespace WebCore {
42 
KeyframeAnimation(const Animation * animation,RenderObject * renderer,int index,CompositeAnimation * compAnim,RenderStyle * unanimatedStyle)43 KeyframeAnimation::KeyframeAnimation(const Animation* animation, RenderObject* renderer, int index, CompositeAnimation* compAnim, RenderStyle* unanimatedStyle)
44     : AnimationBase(animation, renderer, compAnim)
45     , m_keyframes(renderer, animation->name())
46     , m_index(index)
47     , m_unanimatedStyle(unanimatedStyle)
48 {
49     // Get the keyframe RenderStyles
50     if (m_object && m_object->node() && m_object->node()->isElementNode())
51         m_object->document()->styleSelector()->keyframeStylesForAnimation(static_cast<Element*>(m_object->node()), unanimatedStyle, m_keyframes);
52 
53     // Update the m_transformFunctionListValid flag based on whether the function lists in the keyframes match.
54     validateTransformFunctionList();
55 }
56 
~KeyframeAnimation()57 KeyframeAnimation::~KeyframeAnimation()
58 {
59     // Make sure to tell the renderer that we are ending. This will make sure any accelerated animations are removed.
60     if (!postActive())
61         endAnimation(true);
62 }
63 
getKeyframeAnimationInterval(const RenderStyle * & fromStyle,const RenderStyle * & toStyle,double & prog) const64 void KeyframeAnimation::getKeyframeAnimationInterval(const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) const
65 {
66     // Find the first key
67     double elapsedTime = getElapsedTime();
68 
69     double t = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1;
70     int i = static_cast<int>(t);
71     t -= i;
72     if (m_animation->direction() && (i & 1))
73         t = 1 - t;
74 
75     double scale = 1;
76     double offset = 0;
77     Vector<KeyframeValue>::const_iterator endKeyframes = m_keyframes.endKeyframes();
78     for (Vector<KeyframeValue>::const_iterator it = m_keyframes.beginKeyframes(); it != endKeyframes; ++it) {
79         if (t < it->key()) {
80             // The first key should always be 0, so we should never succeed on the first key
81             if (!fromStyle)
82                 break;
83             scale = 1.0 / (it->key() - offset);
84             toStyle = it->style();
85             break;
86         }
87 
88         offset = it->key();
89         fromStyle = it->style();
90     }
91 
92     if (!fromStyle || !toStyle)
93         return;
94 
95     const TimingFunction* timingFunction = 0;
96     if (fromStyle->animations() && fromStyle->animations()->size() > 0) {
97         // We get the timing function from the first animation, because we've synthesized a RenderStyle for each keyframe.
98         timingFunction = &(fromStyle->animations()->animation(0)->timingFunction());
99     }
100 
101     prog = progress(scale, offset, timingFunction);
102 }
103 
animate(CompositeAnimation *,RenderObject *,const RenderStyle *,RenderStyle * targetStyle,RefPtr<RenderStyle> & animatedStyle)104 void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle)
105 {
106     // Fire the start timeout if needed
107     fireAnimationEventsIfNeeded();
108 
109     // If we have not yet started, we will not have a valid start time, so just start the animation if needed.
110     if (isNew() && m_animation->playState() == AnimPlayStatePlaying)
111         updateStateMachine(AnimationStateInputStartAnimation, -1);
112 
113     // If we get this far and the animation is done, it means we are cleaning up a just finished animation.
114     // If so, we need to send back the targetStyle.
115     if (postActive()) {
116         if (!animatedStyle)
117             animatedStyle = const_cast<RenderStyle*>(targetStyle);
118         return;
119     }
120 
121     // If we are waiting for the start timer, we don't want to change the style yet.
122     // Special case - if the delay time is 0, then we do want to set the first frame of the
123     // animation right away. This avoids a flash when the animation starts.
124     if (waitingToStart() && m_animation->delay() > 0)
125         return;
126 
127     // FIXME: we need to be more efficient about determining which keyframes we are animating between.
128     // We should cache the last pair or something.
129 
130     // Get the from/to styles and progress between
131     const RenderStyle* fromStyle = 0;
132     const RenderStyle* toStyle = 0;
133     double progress;
134     getKeyframeAnimationInterval(fromStyle, toStyle, progress);
135 
136     // If either style is 0 we have an invalid case, just stop the animation.
137     if (!fromStyle || !toStyle) {
138         updateStateMachine(AnimationStateInputEndAnimation, -1);
139         return;
140     }
141 
142     // Run a cycle of animation.
143     // We know we will need a new render style, so make one if needed.
144     if (!animatedStyle)
145         animatedStyle = RenderStyle::clone(targetStyle);
146 
147     HashSet<int>::const_iterator endProperties = m_keyframes.endProperties();
148     for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
149         bool needsAnim = blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress);
150         if (needsAnim)
151             setAnimating();
152         else {
153 #if USE(ACCELERATED_COMPOSITING)
154             // If we are running an accelerated animation, set a flag in the style
155             // to indicate it. This can be used to make sure we get an updated
156             // style for hit testing, etc.
157             animatedStyle->setIsRunningAcceleratedAnimation();
158 #endif
159         }
160     }
161 }
162 
getAnimatedStyle(RefPtr<RenderStyle> & animatedStyle)163 void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle)
164 {
165     // Get the from/to styles and progress between
166     const RenderStyle* fromStyle = 0;
167     const RenderStyle* toStyle = 0;
168     double progress;
169     getKeyframeAnimationInterval(fromStyle, toStyle, progress);
170 
171     // If either style is 0 we have an invalid case
172     if (!fromStyle || !toStyle)
173         return;
174 
175     if (!animatedStyle)
176         animatedStyle = RenderStyle::clone(m_object->style());
177 
178     HashSet<int>::const_iterator endProperties = m_keyframes.endProperties();
179     for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it)
180         blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress);
181 }
182 
hasAnimationForProperty(int property) const183 bool KeyframeAnimation::hasAnimationForProperty(int property) const
184 {
185     HashSet<int>::const_iterator end = m_keyframes.endProperties();
186     for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) {
187         if (*it == property)
188             return true;
189     }
190 
191     return false;
192 }
193 
startAnimation(double beginTime)194 bool KeyframeAnimation::startAnimation(double beginTime)
195 {
196 #if USE(ACCELERATED_COMPOSITING)
197     if (m_object && m_object->hasLayer()) {
198         RenderLayer* layer = toRenderBoxModelObject(m_object)->layer();
199         if (layer->isComposited())
200             return layer->backing()->startAnimation(beginTime, m_animation.get(), m_keyframes);
201     }
202 #else
203     UNUSED_PARAM(beginTime);
204 #endif
205     return false;
206 }
207 
endAnimation(bool reset)208 void KeyframeAnimation::endAnimation(bool reset)
209 {
210     if (m_object) {
211 #if USE(ACCELERATED_COMPOSITING)
212         if (m_object->hasLayer()) {
213             RenderLayer* layer = toRenderBoxModelObject(m_object)->layer();
214             if (layer->isComposited()) {
215                 if (reset)
216                     layer->backing()->animationFinished(m_keyframes.animationName());
217                 else
218                     layer->backing()->animationPaused(m_keyframes.animationName());
219             }
220         }
221 #else
222         UNUSED_PARAM(reset);
223 #endif
224         // Restore the original (unanimated) style
225         if (!paused())
226             setNeedsStyleRecalc(m_object->node());
227     }
228 }
229 
shouldSendEventForListener(Document::ListenerType listenerType) const230 bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listenerType) const
231 {
232     return m_object->document()->hasListenerType(listenerType);
233 }
234 
onAnimationStart(double elapsedTime)235 void KeyframeAnimation::onAnimationStart(double elapsedTime)
236 {
237     sendAnimationEvent(eventNames().webkitAnimationStartEvent, elapsedTime);
238 }
239 
onAnimationIteration(double elapsedTime)240 void KeyframeAnimation::onAnimationIteration(double elapsedTime)
241 {
242     sendAnimationEvent(eventNames().webkitAnimationIterationEvent, elapsedTime);
243 }
244 
onAnimationEnd(double elapsedTime)245 void KeyframeAnimation::onAnimationEnd(double elapsedTime)
246 {
247     if (!sendAnimationEvent(eventNames().webkitAnimationEndEvent, elapsedTime)) {
248         // We didn't dispatch an event, which would call endAnimation(), so we'll just call it here.
249         endAnimation(true);
250     }
251 }
252 
sendAnimationEvent(const AtomicString & eventType,double elapsedTime)253 bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime)
254 {
255     Document::ListenerType listenerType;
256     if (eventType == eventNames().webkitAnimationIterationEvent)
257         listenerType = Document::ANIMATIONITERATION_LISTENER;
258     else if (eventType == eventNames().webkitAnimationEndEvent)
259         listenerType = Document::ANIMATIONEND_LISTENER;
260     else {
261         ASSERT(eventType == eventNames().webkitAnimationStartEvent);
262         listenerType = Document::ANIMATIONSTART_LISTENER;
263     }
264 
265     if (shouldSendEventForListener(listenerType)) {
266         // Dispatch the event
267         RefPtr<Element> element;
268         if (m_object->node() && m_object->node()->isElementNode())
269             element = static_cast<Element*>(m_object->node());
270 
271         ASSERT(!element || element->document() && !element->document()->inPageCache());
272         if (!element)
273             return false;
274 
275         // Schedule event handling
276         m_compAnim->animationController()->addEventToDispatch(element, eventType, m_keyframes.animationName(), elapsedTime);
277 
278         // Restore the original (unanimated) style
279         if (eventType == eventNames().webkitAnimationEndEvent && element->renderer())
280             setNeedsStyleRecalc(element.get());
281 
282         return true; // Did dispatch an event
283     }
284 
285     return false; // Did not dispatch an event
286 }
287 
overrideAnimations()288 void KeyframeAnimation::overrideAnimations()
289 {
290     // This will override implicit animations that match the properties in the keyframe animation
291     HashSet<int>::const_iterator end = m_keyframes.endProperties();
292     for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it)
293         compositeAnimation()->overrideImplicitAnimations(*it);
294 }
295 
resumeOverriddenAnimations()296 void KeyframeAnimation::resumeOverriddenAnimations()
297 {
298     // This will resume overridden implicit animations
299     HashSet<int>::const_iterator end = m_keyframes.endProperties();
300     for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it)
301         compositeAnimation()->resumeOverriddenImplicitAnimations(*it);
302 }
303 
affectsProperty(int property) const304 bool KeyframeAnimation::affectsProperty(int property) const
305 {
306     HashSet<int>::const_iterator end = m_keyframes.endProperties();
307     for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) {
308         if (*it == property)
309             return true;
310     }
311     return false;
312 }
313 
validateTransformFunctionList()314 void KeyframeAnimation::validateTransformFunctionList()
315 {
316     m_transformFunctionListValid = false;
317 
318     if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitTransform))
319         return;
320 
321     Vector<KeyframeValue>::const_iterator end = m_keyframes.endKeyframes();
322 
323     // Empty transforms match anything, so find the first non-empty entry as the reference
324     size_t firstIndex = 0;
325     Vector<KeyframeValue>::const_iterator firstIt = end;
326 
327     for (Vector<KeyframeValue>::const_iterator it = m_keyframes.beginKeyframes(); it != end; ++it, ++firstIndex) {
328         if (it->style()->transform().operations().size() > 0) {
329             firstIt = it;
330             break;
331         }
332     }
333 
334     if (firstIt == end)
335         return;
336 
337     const TransformOperations* firstVal = &firstIt->style()->transform();
338 
339     // See if the keyframes are valid
340     for (Vector<KeyframeValue>::const_iterator it = firstIt+1; it != end; ++it) {
341         const TransformOperations* val = &it->style()->transform();
342 
343         // A null transform matches anything
344         if (val->operations().isEmpty())
345             continue;
346 
347         // If the sizes of the function lists don't match, the lists don't match
348         if (firstVal->operations().size() != val->operations().size())
349             return;
350 
351         // If the types of each function are not the same, the lists don't match
352         for (size_t j = 0; j < firstVal->operations().size(); ++j) {
353             if (!firstVal->operations()[j]->isSameType(*val->operations()[j]))
354                 return;
355         }
356     }
357 
358     // Keyframes are valid
359     m_transformFunctionListValid = true;
360 }
361 
timeToNextService()362 double KeyframeAnimation::timeToNextService()
363 {
364     double t = AnimationBase::timeToNextService();
365 #if USE(ACCELERATED_COMPOSITING)
366     if (t != 0 || preActive())
367         return t;
368 
369     // A return value of 0 means we need service. But if we only have accelerated animations we
370     // only need service at the end of the transition
371     HashSet<int>::const_iterator endProperties = m_keyframes.endProperties();
372     bool acceleratedPropertiesOnly = true;
373 
374     for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) {
375         if (!animationOfPropertyIsAccelerated(*it) || isFallbackAnimating()) {
376             acceleratedPropertiesOnly = false;
377             break;
378         }
379     }
380 
381     if (acceleratedPropertiesOnly) {
382         bool isLooping;
383         getTimeToNextEvent(t, isLooping);
384     }
385 #endif
386     return t;
387 }
388 
389 } // namespace WebCore
390