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