Eliminate styleDidChange with StyleDifferenceEqual when updates are actually necessary
[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 bool 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 false;
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 false;
145     
146     // If we have no keyframes, don't animate.
147     if (!m_keyframes.size()) {
148         updateStateMachine(AnimationStateInput::EndAnimation, -1);
149         return false;
150     }
151
152     AnimationState oldState = state();
153
154     // Run a cycle of animation.
155     // We know we will need a new render style, so make one if needed.
156     if (!animatedStyle)
157         animatedStyle = RenderStyle::clone(targetStyle);
158
159     // FIXME: we need to be more efficient about determining which keyframes we are animating between.
160     // We should cache the last pair or something.
161     for (auto propertyID : m_keyframes.properties()) {
162         // Get the from/to styles and progress between
163         const RenderStyle* fromStyle = nullptr;
164         const RenderStyle* toStyle = nullptr;
165         double progress = 0;
166         fetchIntervalEndpointsForProperty(propertyID, fromStyle, toStyle, progress);
167
168         bool needsAnim = CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress);
169         if (!needsAnim)
170             // If we are running an accelerated animation, set a flag in the style
171             // to indicate it. This can be used to make sure we get an updated
172             // style for hit testing, etc.
173             // FIXME: still need this?
174             animatedStyle->setIsRunningAcceleratedAnimation();
175     }
176     
177     return state() != oldState;
178 }
179
180 void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle)
181 {
182     // If we're in the delay phase and we're not backwards filling, tell the caller
183     // to use the current style.
184     if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())
185         return;
186
187     if (!m_keyframes.size())
188         return;
189
190     if (!animatedStyle)
191         animatedStyle = RenderStyle::clone(&m_object->style());
192
193     for (auto propertyID : m_keyframes.properties()) {
194         // Get the from/to styles and progress between
195         const RenderStyle* fromStyle = nullptr;
196         const RenderStyle* toStyle = nullptr;
197         double progress = 0;
198         fetchIntervalEndpointsForProperty(propertyID, fromStyle, toStyle, progress);
199
200         CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress);
201     }
202 }
203
204 bool KeyframeAnimation::computeExtentOfTransformAnimation(LayoutRect& bounds) const
205 {
206     ASSERT(m_keyframes.containsProperty(CSSPropertyTransform));
207
208     if (!is<RenderBox>(m_object))
209         return true; // Non-boxes don't get transformed;
210
211     RenderBox& box = downcast<RenderBox>(*m_object);
212     FloatRect rendererBox = snapRectToDevicePixels(box.borderBoxRect(), box.document().deviceScaleFactor());
213
214     FloatRect cumulativeBounds = bounds;
215
216     for (auto& keyframe : m_keyframes.keyframes()) {
217         if (!keyframe.containsProperty(CSSPropertyTransform))
218             continue;
219
220         LayoutRect keyframeBounds = bounds;
221         
222         bool canCompute;
223         if (isTransformFunctionListValid())
224             canCompute = computeTransformedExtentViaTransformList(rendererBox, *keyframe.style(), keyframeBounds);
225         else
226             canCompute = computeTransformedExtentViaMatrix(rendererBox, *keyframe.style(), keyframeBounds);
227         
228         if (!canCompute)
229             return false;
230         
231         cumulativeBounds.unite(keyframeBounds);
232     }
233
234     bounds = LayoutRect(cumulativeBounds);
235     return true;
236 }
237
238 bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const
239 {
240     return m_keyframes.containsProperty(property);
241 }
242
243 bool KeyframeAnimation::startAnimation(double timeOffset)
244 {
245     if (m_object && m_object->isComposited())
246         return downcast<RenderBoxModelObject>(*m_object).startAnimation(timeOffset, m_animation.ptr(), m_keyframes);
247     return false;
248 }
249
250 void KeyframeAnimation::pauseAnimation(double timeOffset)
251 {
252     if (!m_object)
253         return;
254
255     if (m_object->isComposited())
256         downcast<RenderBoxModelObject>(*m_object).animationPaused(timeOffset, m_keyframes.animationName());
257
258     // Restore the original (unanimated) style
259     if (!paused())
260         setNeedsStyleRecalc(m_object->element());
261 }
262
263 void KeyframeAnimation::endAnimation()
264 {
265     if (!m_object)
266         return;
267
268     if (m_object->isComposited())
269         downcast<RenderBoxModelObject>(*m_object).animationFinished(m_keyframes.animationName());
270
271     // Restore the original (unanimated) style
272     if (!paused())
273         setNeedsStyleRecalc(m_object->element());
274 }
275
276 bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listenerType) const
277 {
278     return m_object->document().hasListenerType(listenerType);
279 }
280
281 void KeyframeAnimation::onAnimationStart(double elapsedTime)
282 {
283     sendAnimationEvent(eventNames().animationstartEvent, elapsedTime);
284 }
285
286 void KeyframeAnimation::onAnimationIteration(double elapsedTime)
287 {
288     sendAnimationEvent(eventNames().animationiterationEvent, elapsedTime);
289 }
290
291 void KeyframeAnimation::onAnimationEnd(double elapsedTime)
292 {
293     sendAnimationEvent(eventNames().animationendEvent, elapsedTime);
294     // End the animation if we don't fill forwards. Forward filling
295     // animations are ended properly in the class destructor.
296     if (!m_animation->fillsForwards())
297         endAnimation();
298 }
299
300 bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime)
301 {
302     Document::ListenerType listenerType;
303     if (eventType == eventNames().webkitAnimationIterationEvent || eventType == eventNames().animationiterationEvent)
304         listenerType = Document::ANIMATIONITERATION_LISTENER;
305     else if (eventType == eventNames().webkitAnimationEndEvent || eventType == eventNames().animationendEvent)
306         listenerType = Document::ANIMATIONEND_LISTENER;
307     else {
308         ASSERT(eventType == eventNames().webkitAnimationStartEvent || eventType == eventNames().animationstartEvent);
309         if (m_startEventDispatched)
310             return false;
311         m_startEventDispatched = true;
312         listenerType = Document::ANIMATIONSTART_LISTENER;
313     }
314
315     if (shouldSendEventForListener(listenerType)) {
316         // Dispatch the event
317         RefPtr<Element> element = m_object->element();
318
319         ASSERT(!element || !element->document().inPageCache());
320         if (!element)
321             return false;
322
323         // Schedule event handling
324         m_compositeAnimation->animationController().addEventToDispatch(element, eventType, m_keyframes.animationName(), elapsedTime);
325
326         // Restore the original (unanimated) style
327         if ((eventType == eventNames().webkitAnimationEndEvent || eventType == eventNames().animationendEvent) && 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     for (auto propertyID : m_keyframes.properties())
340         compositeAnimation()->overrideImplicitAnimations(propertyID);
341 }
342
343 void KeyframeAnimation::resumeOverriddenAnimations()
344 {
345     // This will resume overridden implicit animations
346     for (auto propertyID : m_keyframes.properties())
347         compositeAnimation()->resumeOverriddenImplicitAnimations(propertyID);
348 }
349
350 bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const
351 {
352     return m_keyframes.containsProperty(property);
353 }
354
355 void KeyframeAnimation::validateTransformFunctionList()
356 {
357     m_transformFunctionListValid = false;
358     
359     if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyTransform))
360         return;
361
362     // Empty transforms match anything, so find the first non-empty entry as the reference
363     size_t numKeyframes = m_keyframes.size();
364     size_t firstNonEmptyTransformKeyframeIndex = numKeyframes;
365
366     for (size_t i = 0; i < numKeyframes; ++i) {
367         const KeyframeValue& currentKeyframe = m_keyframes[i];
368         if (currentKeyframe.style()->transform().operations().size()) {
369             firstNonEmptyTransformKeyframeIndex = i;
370             break;
371         }
372     }
373     
374     if (firstNonEmptyTransformKeyframeIndex == numKeyframes)
375         return;
376         
377     const TransformOperations* firstVal = &m_keyframes[firstNonEmptyTransformKeyframeIndex].style()->transform();
378     
379     // See if the keyframes are valid
380     for (size_t i = firstNonEmptyTransformKeyframeIndex + 1; i < numKeyframes; ++i) {
381         const KeyframeValue& currentKeyframe = m_keyframes[i];
382         const TransformOperations* val = &currentKeyframe.style()->transform();
383         
384         // An emtpy transform list matches anything.
385         if (val->operations().isEmpty())
386             continue;
387         
388         if (!firstVal->operationsMatch(*val))
389             return;
390     }
391
392     // Keyframes are valid
393     m_transformFunctionListValid = true;
394 }
395
396 void KeyframeAnimation::checkForMatchingFilterFunctionLists()
397 {
398     m_filterFunctionListsMatch = false;
399
400 #if ENABLE(FILTERS_LEVEL_2)
401     if (m_keyframes.size() < 2 || (!m_keyframes.containsProperty(CSSPropertyWebkitFilter) && !m_keyframes.containsProperty(CSSPropertyWebkitBackdropFilter)))
402 #else
403     if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitFilter))
404 #endif
405         return;
406
407     // Empty filters match anything, so find the first non-empty entry as the reference
408     size_t numKeyframes = m_keyframes.size();
409     size_t firstNonEmptyFilterKeyframeIndex = numKeyframes;
410
411     for (size_t i = 0; i < numKeyframes; ++i) {
412         const KeyframeValue& currentKeyframe = m_keyframes[i];
413         if (currentKeyframe.style()->filter().operations().size()) {
414             firstNonEmptyFilterKeyframeIndex = i;
415             break;
416         }
417     }
418     
419     if (firstNonEmptyFilterKeyframeIndex == numKeyframes)
420         return;
421         
422     const FilterOperations* firstVal = &m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->filter();
423     
424     for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) {
425         const KeyframeValue& currentKeyframe = m_keyframes[i];
426         const FilterOperations* val = &currentKeyframe.style()->filter();
427         
428         // An emtpy filter list matches anything.
429         if (val->operations().isEmpty())
430             continue;
431         
432         if (!firstVal->operationsMatch(*val))
433             return;
434     }
435     
436     m_filterFunctionListsMatch = true;
437 }
438
439 double KeyframeAnimation::timeToNextService()
440 {
441     double t = AnimationBase::timeToNextService();
442     if (t != 0 || preActive())
443         return t;
444         
445     // A return value of 0 means we need service. But if we only have accelerated animations we 
446     // only need service at the end of the transition
447     bool acceleratedPropertiesOnly = true;
448     
449     for (auto propertyID : m_keyframes.properties()) {
450         if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(propertyID) || !isAccelerated()) {
451             acceleratedPropertiesOnly = false;
452             break;
453         }
454     }
455
456     if (acceleratedPropertiesOnly) {
457         bool isLooping;
458         getTimeToNextEvent(t, isLooping);
459     }
460
461     return t;
462 }
463
464 } // namespace WebCore