564f489573d5408aff72e59500529a196b075815
[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 "CSSAnimationControllerPrivate.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 "StylePendingResources.h"
41 #include "StyleResolver.h"
42 #include "StyleScope.h"
43 #include "TranslateTransformOperation.h"
44 #include "WillChangeData.h"
45
46 namespace WebCore {
47
48 KeyframeAnimation::KeyframeAnimation(const Animation& animation, Element& element, CompositeAnimation& compositeAnimation, const RenderStyle& unanimatedStyle)
49     : AnimationBase(animation, element, compositeAnimation)
50     , m_keyframes(animation.name())
51     , m_unanimatedStyle(RenderStyle::clonePtr(unanimatedStyle))
52 {
53     resolveKeyframeStyles();
54
55     // Update the m_transformFunctionListValid flag based on whether the function lists in the keyframes match.
56     validateTransformFunctionList();
57     checkForMatchingFilterFunctionLists();
58 #if ENABLE(FILTERS_LEVEL_2)
59     checkForMatchingBackdropFilterFunctionLists();
60 #endif
61     computeStackingContextImpact();
62     computeLayoutDependency();
63 }
64
65 KeyframeAnimation::~KeyframeAnimation()
66 {
67     // Make sure to tell the renderer that we are ending. This will make sure any accelerated animations are removed.
68     if (!postActive())
69         endAnimation();
70 }
71
72 void KeyframeAnimation::computeStackingContextImpact()
73 {
74     for (auto propertyID : m_keyframes.properties()) {
75         if (WillChangeData::propertyCreatesStackingContext(propertyID)) {
76             m_triggersStackingContext = true;
77             break;
78         }
79     }
80 }
81
82 void KeyframeAnimation::computeLayoutDependency()
83 {
84     if (!m_keyframes.containsProperty(CSSPropertyTransform))
85         return;
86
87     size_t numKeyframes = m_keyframes.size();
88     for (size_t i = 0; i < numKeyframes; i++) {
89         auto* keyframeStyle = m_keyframes[i].style();
90         if (!keyframeStyle) {
91             ASSERT_NOT_REACHED();
92             continue;
93         }
94         if (keyframeStyle->hasTransform()) {
95             auto& transformOperations = keyframeStyle->transform();
96             for (auto operation : transformOperations.operations()) {
97                 if (operation->isTranslateTransformOperationType()) {
98                     auto translation = downcast<TranslateTransformOperation>(operation.get());
99                     if (translation->x().isPercent() || translation->y().isPercent()) {
100                         m_dependsOnLayout = true;
101                         return;
102                     }
103                 }
104             }
105         }
106     }
107 }
108
109 void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) const
110 {
111     size_t numKeyframes = m_keyframes.size();
112     if (!numKeyframes)
113         return;
114
115     // Find the first key
116     double elapsedTime = getElapsedTime();
117     if (m_animation->duration() && m_animation->iterationCount() != Animation::IterationCountInfinite)
118         elapsedTime = std::min(elapsedTime, m_animation->duration() * m_animation->iterationCount());
119
120     const double fractionalTime = this->fractionalTime(1, elapsedTime, 0);
121     ASSERT(!m_keyframes[0].key());
122     ASSERT(m_keyframes[m_keyframes.size() - 1].key() == 1);
123
124     int prevIndex = -1;
125     int nextIndex = -1;
126     // FIXME: with a lot of keys, this linear search will be slow. We could binary search.
127     for (size_t i = 0; i < numKeyframes; ++i) {
128         const KeyframeValue& currKeyFrame = m_keyframes[i];
129
130         if (!currKeyFrame.containsProperty(property))
131             continue;
132
133         if (fractionalTime < currKeyFrame.key()) {
134             nextIndex = i;
135             break;
136         }
137         prevIndex = i;
138     }
139
140     if (prevIndex == -1)
141         prevIndex = 0;
142     if (nextIndex == -1)
143         nextIndex = m_keyframes.size() - 1;
144
145     const KeyframeValue& prevKeyframe = m_keyframes[prevIndex];
146     const KeyframeValue& nextKeyframe = m_keyframes[nextIndex];
147
148     fromStyle = prevKeyframe.style();
149     toStyle = nextKeyframe.style();
150
151     double offset = prevKeyframe.key();
152     double scale = 1.0 / (nextIndex == prevIndex ?  1 : (nextKeyframe.key() - prevKeyframe.key()));
153
154     prog = progress(scale, offset, prevKeyframe.timingFunction());
155 }
156
157 bool KeyframeAnimation::animate(CompositeAnimation& compositeAnimation, const RenderStyle& targetStyle, std::unique_ptr<RenderStyle>& animatedStyle, bool& didBlendStyle)
158 {
159     // Fire the start timeout if needed
160     fireAnimationEventsIfNeeded();
161     
162     // If we have not yet started, we will not have a valid start time, so just start the animation if needed.
163     if (isNew()) {
164         if (m_animation->playState() == AnimPlayStatePlaying && !compositeAnimation.isSuspended())
165             updateStateMachine(AnimationStateInput::StartAnimation, -1);
166         else if (m_animation->playState() == AnimPlayStatePaused)
167             updateStateMachine(AnimationStateInput::PlayStatePaused, -1);
168     }
169
170     // If we get this far and the animation is done, it means we are cleaning up a just finished animation.
171     // If so, we need to send back the targetStyle.
172     if (postActive()) {
173         if (!animatedStyle)
174             animatedStyle = RenderStyle::clonePtr(targetStyle);
175         return false;
176     }
177
178     // If we are waiting for the start timer, we don't want to change the style yet.
179     // Special case 1 - if the delay time is 0, then we do want to set the first frame of the
180     // animation right away. This avoids a flash when the animation starts.
181     // Special case 2 - if there is a backwards fill mode, then we want to continue
182     // through to the style blend so that we get the fromStyle.
183     if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())
184         return false;
185     
186     // If we have no keyframes, don't animate.
187     if (!m_keyframes.size()) {
188         updateStateMachine(AnimationStateInput::EndAnimation, -1);
189         return false;
190     }
191
192     // FIXME: the code below never changes the state, so this function always returns false.
193     AnimationState oldState = state();
194
195     // Run a cycle of animation.
196     // We know we will need a new render style, so make one if needed.
197     if (!animatedStyle)
198         animatedStyle = RenderStyle::clonePtr(targetStyle);
199
200     // FIXME: we need to be more efficient about determining which keyframes we are animating between.
201     // We should cache the last pair or something.
202     for (auto propertyID : m_keyframes.properties()) {
203         // Get the from/to styles and progress between
204         const RenderStyle* fromStyle = nullptr;
205         const RenderStyle* toStyle = nullptr;
206         double progress = 0;
207         fetchIntervalEndpointsForProperty(propertyID, fromStyle, toStyle, progress);
208
209         CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress);
210     }
211     
212     didBlendStyle = true;
213     return state() != oldState;
214 }
215
216 void KeyframeAnimation::getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyle)
217 {
218     // If we're done, or in the delay phase and we're not backwards filling, tell the caller to use the current style.
219     if (postActive() || (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards()))
220         return;
221
222     if (!m_keyframes.size())
223         return;
224
225     if (!animatedStyle)
226         animatedStyle = RenderStyle::clonePtr(renderer()->style());
227
228     for (auto propertyID : m_keyframes.properties()) {
229         // Get the from/to styles and progress between
230         const RenderStyle* fromStyle = nullptr;
231         const RenderStyle* toStyle = nullptr;
232         double progress = 0;
233         fetchIntervalEndpointsForProperty(propertyID, fromStyle, toStyle, progress);
234
235         CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress);
236     }
237 }
238
239 bool KeyframeAnimation::computeExtentOfTransformAnimation(LayoutRect& bounds) const
240 {
241     ASSERT(m_keyframes.containsProperty(CSSPropertyTransform));
242
243     if (!is<RenderBox>(renderer()))
244         return true; // Non-boxes don't get transformed;
245
246     RenderBox& box = downcast<RenderBox>(*renderer());
247     FloatRect rendererBox = snapRectToDevicePixels(box.borderBoxRect(), box.document().deviceScaleFactor());
248
249     FloatRect cumulativeBounds = bounds;
250
251     for (auto& keyframe : m_keyframes.keyframes()) {
252         if (!keyframe.containsProperty(CSSPropertyTransform))
253             continue;
254
255         LayoutRect keyframeBounds = bounds;
256         
257         bool canCompute;
258         if (transformFunctionListsMatch())
259             canCompute = computeTransformedExtentViaTransformList(rendererBox, *keyframe.style(), keyframeBounds);
260         else
261             canCompute = computeTransformedExtentViaMatrix(rendererBox, *keyframe.style(), keyframeBounds);
262         
263         if (!canCompute)
264             return false;
265         
266         cumulativeBounds.unite(keyframeBounds);
267     }
268
269     bounds = LayoutRect(cumulativeBounds);
270     return true;
271 }
272
273 bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const
274 {
275     return m_keyframes.containsProperty(property);
276 }
277
278 bool KeyframeAnimation::startAnimation(double timeOffset)
279 {
280     if (auto* renderer = compositedRenderer())
281         return renderer->startAnimation(timeOffset, m_animation.ptr(), m_keyframes);
282     return false;
283 }
284
285 void KeyframeAnimation::pauseAnimation(double timeOffset)
286 {
287     if (!element())
288         return;
289
290     if (auto* renderer = compositedRenderer())
291         renderer->animationPaused(timeOffset, m_keyframes.animationName());
292
293     // Restore the original (unanimated) style
294     if (!paused())
295         setNeedsStyleRecalc(element());
296 }
297
298 void KeyframeAnimation::endAnimation()
299 {
300     if (!element())
301         return;
302
303     if (auto* renderer = compositedRenderer())
304         renderer->animationFinished(m_keyframes.animationName());
305
306     // Restore the original (unanimated) style
307     if (!paused())
308         setNeedsStyleRecalc(element());
309 }
310
311 bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listenerType) const
312 {
313     return element()->document().hasListenerType(listenerType);
314 }
315
316 void KeyframeAnimation::onAnimationStart(double elapsedTime)
317 {
318     sendAnimationEvent(eventNames().animationstartEvent, elapsedTime);
319 }
320
321 void KeyframeAnimation::onAnimationIteration(double elapsedTime)
322 {
323     sendAnimationEvent(eventNames().animationiterationEvent, elapsedTime);
324 }
325
326 void KeyframeAnimation::onAnimationEnd(double elapsedTime)
327 {
328     sendAnimationEvent(eventNames().animationendEvent, elapsedTime);
329     // End the animation if we don't fill forwards. Forward filling
330     // animations are ended properly in the class destructor.
331     if (!m_animation->fillsForwards())
332         endAnimation();
333 }
334
335 bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime)
336 {
337     Document::ListenerType listenerType;
338     if (eventType == eventNames().webkitAnimationIterationEvent || eventType == eventNames().animationiterationEvent)
339         listenerType = Document::ANIMATIONITERATION_LISTENER;
340     else if (eventType == eventNames().webkitAnimationEndEvent || eventType == eventNames().animationendEvent)
341         listenerType = Document::ANIMATIONEND_LISTENER;
342     else {
343         ASSERT(eventType == eventNames().webkitAnimationStartEvent || eventType == eventNames().animationstartEvent);
344         if (m_startEventDispatched)
345             return false;
346         m_startEventDispatched = true;
347         listenerType = Document::ANIMATIONSTART_LISTENER;
348     }
349
350     if (shouldSendEventForListener(listenerType)) {
351         // Dispatch the event
352         auto element = makeRefPtr(this->element());
353
354         ASSERT(!element || element->document().pageCacheState() == Document::NotInPageCache);
355         if (!element)
356             return false;
357
358         // Schedule event handling
359         m_compositeAnimation->animationController().addEventToDispatch(*element, eventType, m_keyframes.animationName(), elapsedTime);
360
361         // Restore the original (unanimated) style
362         if ((eventType == eventNames().webkitAnimationEndEvent || eventType == eventNames().animationendEvent) && element->renderer())
363             setNeedsStyleRecalc(element.get());
364
365         return true; // Did dispatch an event
366     }
367
368     return false; // Did not dispatch an event
369 }
370
371 void KeyframeAnimation::overrideAnimations()
372 {
373     // This will override implicit animations that match the properties in the keyframe animation
374     for (auto propertyID : m_keyframes.properties())
375         compositeAnimation()->overrideImplicitAnimations(propertyID);
376 }
377
378 void KeyframeAnimation::resumeOverriddenAnimations()
379 {
380     // This will resume overridden implicit animations
381     for (auto propertyID : m_keyframes.properties())
382         compositeAnimation()->resumeOverriddenImplicitAnimations(propertyID);
383 }
384
385 bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const
386 {
387     return m_keyframes.containsProperty(property);
388 }
389
390 void KeyframeAnimation::resolveKeyframeStyles()
391 {
392     if (!element())
393         return;
394
395     if (auto* styleScope = Style::Scope::forOrdinal(*element(), m_animation->nameStyleScopeOrdinal()))
396         styleScope->resolver().keyframeStylesForAnimation(*element(), m_unanimatedStyle.get(), m_keyframes);
397
398     // Ensure resource loads for all the frames.
399     for (auto& keyframe : m_keyframes.keyframes()) {
400         if (auto* style = const_cast<RenderStyle*>(keyframe.style()))
401             Style::loadPendingResources(*style, element()->document(), element());
402     }
403 }
404
405 void KeyframeAnimation::validateTransformFunctionList()
406 {
407     m_transformFunctionListsMatch = false;
408     
409     if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyTransform))
410         return;
411
412     // Empty transforms match anything, so find the first non-empty entry as the reference
413     size_t numKeyframes = m_keyframes.size();
414     size_t firstNonEmptyTransformKeyframeIndex = numKeyframes;
415
416     for (size_t i = 0; i < numKeyframes; ++i) {
417         const KeyframeValue& currentKeyframe = m_keyframes[i];
418         if (currentKeyframe.style()->transform().operations().size()) {
419             firstNonEmptyTransformKeyframeIndex = i;
420             break;
421         }
422     }
423     
424     if (firstNonEmptyTransformKeyframeIndex == numKeyframes)
425         return;
426         
427     const TransformOperations* firstVal = &m_keyframes[firstNonEmptyTransformKeyframeIndex].style()->transform();
428     
429     // See if the keyframes are valid
430     for (size_t i = firstNonEmptyTransformKeyframeIndex + 1; i < numKeyframes; ++i) {
431         const KeyframeValue& currentKeyframe = m_keyframes[i];
432         const TransformOperations* val = &currentKeyframe.style()->transform();
433         
434         // An emtpy transform list matches anything.
435         if (val->operations().isEmpty())
436             continue;
437         
438         if (!firstVal->operationsMatch(*val))
439             return;
440     }
441
442     m_transformFunctionListsMatch = true;
443 }
444
445 bool KeyframeAnimation::checkForMatchingFilterFunctionLists(CSSPropertyID propertyID, const std::function<const FilterOperations& (const RenderStyle&)>& filtersGetter) const
446 {
447     if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(propertyID))
448         return false;
449
450     // Empty filters match anything, so find the first non-empty entry as the reference
451     size_t numKeyframes = m_keyframes.size();
452     size_t firstNonEmptyKeyframeIndex = numKeyframes;
453
454     for (size_t i = 0; i < numKeyframes; ++i) {
455         if (filtersGetter(*m_keyframes[i].style()).operations().size()) {
456             firstNonEmptyKeyframeIndex = i;
457             break;
458         }
459     }
460     
461     if (firstNonEmptyKeyframeIndex == numKeyframes)
462         return false;
463
464     auto& firstVal = filtersGetter(*m_keyframes[firstNonEmptyKeyframeIndex].style());
465     
466     for (size_t i = firstNonEmptyKeyframeIndex + 1; i < numKeyframes; ++i) {
467         auto& value = filtersGetter(*m_keyframes[i].style());
468
469         // An emtpy filter list matches anything.
470         if (value.operations().isEmpty())
471             continue;
472
473         if (!firstVal.operationsMatch(value))
474             return false;
475     }
476
477     return true;
478 }
479
480 void KeyframeAnimation::checkForMatchingFilterFunctionLists()
481 {
482     m_filterFunctionListsMatch = checkForMatchingFilterFunctionLists(CSSPropertyFilter, [] (const RenderStyle& style) -> const FilterOperations& {
483         return style.filter();
484     });
485 }
486
487 #if ENABLE(FILTERS_LEVEL_2)
488 void KeyframeAnimation::checkForMatchingBackdropFilterFunctionLists()
489 {
490     m_backdropFilterFunctionListsMatch = checkForMatchingFilterFunctionLists(CSSPropertyWebkitBackdropFilter, [] (const RenderStyle& style) -> const FilterOperations& {
491         return style.backdropFilter();
492     });
493 }
494 #endif
495
496 std::optional<Seconds> KeyframeAnimation::timeToNextService()
497 {
498     std::optional<Seconds> t = AnimationBase::timeToNextService();
499     if (!t || t.value() != 0_s || preActive())
500         return t;
501
502     // A return value of 0 means we need service. But if we only have accelerated animations we 
503     // only need service at the end of the transition.
504     bool acceleratedPropertiesOnly = true;
505     
506     for (auto propertyID : m_keyframes.properties()) {
507         if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(propertyID) || !isAccelerated()) {
508             acceleratedPropertiesOnly = false;
509             break;
510         }
511     }
512
513     if (acceleratedPropertiesOnly) {
514         bool isLooping;
515         getTimeToNextEvent(t.value(), isLooping);
516     }
517
518     return t;
519 }
520
521 } // namespace WebCore