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