[Web Animations] Refactor cancelDeclarativeAnimationsForElement and willDestroyRender...
[WebKit-https.git] / Source / WebCore / animation / DeclarativeAnimation.cpp
1 /*
2  * Copyright (C) 2018 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "DeclarativeAnimation.h"
28
29 #include "Animation.h"
30 #include "AnimationEvent.h"
31 #include "CSSAnimation.h"
32 #include "CSSTransition.h"
33 #include "DocumentTimeline.h"
34 #include "Element.h"
35 #include "EventNames.h"
36 #include "KeyframeEffect.h"
37 #include "PseudoElement.h"
38 #include <wtf/IsoMallocInlines.h>
39
40 namespace WebCore {
41
42 WTF_MAKE_ISO_ALLOCATED_IMPL(DeclarativeAnimation);
43
44 DeclarativeAnimation::DeclarativeAnimation(Element& owningElement, const Animation& backingAnimation)
45     : WebAnimation(owningElement.document())
46     , m_owningElement(makeWeakPtr(owningElement))
47     , m_backingAnimation(const_cast<Animation&>(backingAnimation))
48 {
49 }
50
51 DeclarativeAnimation::~DeclarativeAnimation()
52 {
53 }
54
55 Element* DeclarativeAnimation::owningElement() const
56 {
57     return m_owningElement.get();
58 }
59
60 void DeclarativeAnimation::tick()
61 {
62     bool wasRelevant = isRelevant();
63     
64     WebAnimation::tick();
65     invalidateDOMEvents();
66
67     // If a declarative animation transitions from a non-idle state to an idle state, it means it was
68     // canceled using the Web Animations API and it should be disassociated from its owner element.
69     // From this point on, this animation is like any other animation and should not appear in the
70     // maps containing running CSS Transitions and CSS Animations for a given element.
71     if (wasRelevant && playState() == WebAnimation::PlayState::Idle)
72         disassociateFromOwningElement();
73 }
74
75 bool DeclarativeAnimation::canHaveGlobalPosition()
76 {
77     // https://drafts.csswg.org/css-animations-2/#animation-composite-order
78     // https://drafts.csswg.org/css-transitions-2/#animation-composite-order
79     // CSS Animations and CSS Transitions generated using the markup defined in this specification are not added
80     // to the global animation list when they are created. Instead, these animations are appended to the global
81     // animation list at the first moment when they transition out of the idle play state after being disassociated
82     // from their owning element.
83     return !m_owningElement && playState() != WebAnimation::PlayState::Idle;
84 }
85
86 void DeclarativeAnimation::disassociateFromOwningElement()
87 {
88     if (!m_owningElement)
89         return;
90
91     if (auto* animationTimeline = timeline())
92         animationTimeline->removeDeclarativeAnimationFromListsForOwningElement(*this, *m_owningElement);
93     m_owningElement = nullptr;
94 }
95
96 void DeclarativeAnimation::setBackingAnimation(const Animation& backingAnimation)
97 {
98     m_backingAnimation = const_cast<Animation&>(backingAnimation);
99     syncPropertiesWithBackingAnimation();
100 }
101
102 void DeclarativeAnimation::initialize(const RenderStyle* oldStyle, const RenderStyle& newStyle)
103 {
104     // We need to suspend invalidation of the animation's keyframe effect during its creation
105     // as it would otherwise trigger invalidation of the document's style and this would be
106     // incorrect since it would happen during style invalidation.
107     suspendEffectInvalidation();
108
109     ASSERT(m_owningElement);
110
111     setEffect(KeyframeEffect::create(*m_owningElement));
112     setTimeline(&m_owningElement->document().timeline());
113     downcast<KeyframeEffect>(effect())->computeDeclarativeAnimationBlendingKeyframes(oldStyle, newStyle);
114     syncPropertiesWithBackingAnimation();
115     if (backingAnimation().playState() == AnimationPlayState::Playing)
116         play();
117     else
118         pause();
119
120     unsuspendEffectInvalidation();
121 }
122
123 void DeclarativeAnimation::syncPropertiesWithBackingAnimation()
124 {
125 }
126
127 Optional<double> DeclarativeAnimation::startTime() const
128 {
129     flushPendingStyleChanges();
130     return WebAnimation::startTime();
131 }
132
133 void DeclarativeAnimation::setStartTime(Optional<double> startTime)
134 {
135     flushPendingStyleChanges();
136     return WebAnimation::setStartTime(startTime);
137 }
138
139 Optional<double> DeclarativeAnimation::bindingsCurrentTime() const
140 {
141     flushPendingStyleChanges();
142     return WebAnimation::bindingsCurrentTime();
143 }
144
145 ExceptionOr<void> DeclarativeAnimation::setBindingsCurrentTime(Optional<double> currentTime)
146 {
147     flushPendingStyleChanges();
148     return WebAnimation::setBindingsCurrentTime(currentTime);
149 }
150
151 WebAnimation::PlayState DeclarativeAnimation::bindingsPlayState() const
152 {
153     flushPendingStyleChanges();
154     return WebAnimation::bindingsPlayState();
155 }
156
157 WebAnimation::ReplaceState DeclarativeAnimation::bindingsReplaceState() const
158 {
159     flushPendingStyleChanges();
160     return WebAnimation::bindingsReplaceState();
161 }
162
163 bool DeclarativeAnimation::bindingsPending() const
164 {
165     flushPendingStyleChanges();
166     return WebAnimation::bindingsPending();
167 }
168
169 WebAnimation::ReadyPromise& DeclarativeAnimation::bindingsReady()
170 {
171     flushPendingStyleChanges();
172     return WebAnimation::bindingsReady();
173 }
174
175 WebAnimation::FinishedPromise& DeclarativeAnimation::bindingsFinished()
176 {
177     flushPendingStyleChanges();
178     return WebAnimation::bindingsFinished();
179 }
180
181 ExceptionOr<void> DeclarativeAnimation::bindingsPlay()
182 {
183     flushPendingStyleChanges();
184     return WebAnimation::bindingsPlay();
185 }
186
187 ExceptionOr<void> DeclarativeAnimation::bindingsPause()
188 {
189     flushPendingStyleChanges();
190     return WebAnimation::bindingsPause();
191 }
192
193 void DeclarativeAnimation::flushPendingStyleChanges() const
194 {
195     if (auto* animationEffect = effect()) {
196         if (is<KeyframeEffect>(animationEffect)) {
197             if (auto* target = downcast<KeyframeEffect>(animationEffect)->target())
198                 target->document().updateStyleIfNeeded();
199         }
200     }
201 }
202
203 void DeclarativeAnimation::setTimeline(RefPtr<AnimationTimeline>&& newTimeline)
204 {
205     if (timeline() && !newTimeline)
206         cancel();
207
208     WebAnimation::setTimeline(WTFMove(newTimeline));
209 }
210
211 void DeclarativeAnimation::cancel(Silently silently)
212 {
213     auto cancelationTime = 0_s;
214     if (auto* animationEffect = effect()) {
215         if (auto activeTime = animationEffect->getBasicTiming().activeTime)
216             cancelationTime = *activeTime;
217     }
218
219     WebAnimation::cancel(silently);
220
221     invalidateDOMEvents(cancelationTime);
222 }
223
224 void DeclarativeAnimation::cancelFromStyle()
225 {
226     cancel();
227     disassociateFromOwningElement();
228 }
229
230 AnimationEffectPhase DeclarativeAnimation::phaseWithoutEffect() const
231 {
232     // This shouldn't be called if we actually have an effect.
233     ASSERT(!effect());
234
235     auto animationCurrentTime = currentTime();
236     if (!animationCurrentTime)
237         return AnimationEffectPhase::Idle;
238
239     // Since we don't have an effect, the duration will be zero so the phase is 'before' if the current time is less than zero.
240     return *animationCurrentTime < 0_s ? AnimationEffectPhase::Before : AnimationEffectPhase::After;
241 }
242
243 void DeclarativeAnimation::invalidateDOMEvents(Seconds elapsedTime)
244 {
245     if (!m_owningElement)
246         return;
247     
248     auto isPending = pending();
249     if (isPending && m_wasPending)
250         return;
251
252     double iteration = 0;
253     AnimationEffectPhase currentPhase;
254     Seconds intervalStart;
255     Seconds intervalEnd;
256
257     auto* animationEffect = effect();
258     if (animationEffect) {
259         auto timing = animationEffect->getComputedTiming();
260         if (auto computedIteration = timing.currentIteration)
261             iteration = *computedIteration;
262         currentPhase = timing.phase;
263         intervalStart = std::max(0_s, Seconds::fromMilliseconds(std::min(-timing.delay, timing.activeDuration)));
264         intervalEnd = std::max(0_s, Seconds::fromMilliseconds(std::min(timing.endTime - timing.delay, timing.activeDuration)));
265     } else {
266         iteration = 0;
267         currentPhase = phaseWithoutEffect();
268         intervalStart = 0_s;
269         intervalEnd = 0_s;
270     }
271
272     bool wasActive = m_previousPhase == AnimationEffectPhase::Active;
273     bool wasAfter = m_previousPhase == AnimationEffectPhase::After;
274     bool wasBefore = m_previousPhase == AnimationEffectPhase::Before;
275     bool wasIdle = m_previousPhase == AnimationEffectPhase::Idle;
276
277     bool isActive = currentPhase == AnimationEffectPhase::Active;
278     bool isAfter = currentPhase == AnimationEffectPhase::After;
279     bool isBefore = currentPhase == AnimationEffectPhase::Before;
280     bool isIdle = currentPhase == AnimationEffectPhase::Idle;
281
282     if (is<CSSAnimation>(this)) {
283         // https://drafts.csswg.org/css-animations-2/#events
284         if ((wasIdle || wasBefore) && isActive)
285             enqueueDOMEvent(eventNames().animationstartEvent, intervalStart);
286         else if ((wasIdle || wasBefore) && isAfter) {
287             enqueueDOMEvent(eventNames().animationstartEvent, intervalStart);
288             enqueueDOMEvent(eventNames().animationendEvent, intervalEnd);
289         } else if (wasActive && isBefore)
290             enqueueDOMEvent(eventNames().animationendEvent, intervalStart);
291         else if (wasActive && isActive && m_previousIteration != iteration) {
292             auto iterationBoundary = iteration;
293             if (m_previousIteration > iteration)
294                 iterationBoundary++;
295             auto elapsedTime = animationEffect ? animationEffect->iterationDuration() * (iterationBoundary - animationEffect->iterationStart()) : 0_s;
296             enqueueDOMEvent(eventNames().animationiterationEvent, elapsedTime);
297         } else if (wasActive && isAfter)
298             enqueueDOMEvent(eventNames().animationendEvent, intervalEnd);
299         else if (wasAfter && isActive)
300             enqueueDOMEvent(eventNames().animationstartEvent, intervalEnd);
301         else if (wasAfter && isBefore) {
302             enqueueDOMEvent(eventNames().animationstartEvent, intervalEnd);
303             enqueueDOMEvent(eventNames().animationendEvent, intervalStart);
304         } else if ((!wasIdle && !wasAfter) && isIdle)
305             enqueueDOMEvent(eventNames().animationcancelEvent, elapsedTime);
306     } else if (is<CSSTransition>(this)) {
307         // https://drafts.csswg.org/css-transitions-2/#transition-events
308         if (wasIdle && (isPending || isBefore))
309             enqueueDOMEvent(eventNames().transitionrunEvent, intervalStart);
310         else if (wasIdle && isActive) {
311             enqueueDOMEvent(eventNames().transitionrunEvent, intervalStart);
312             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
313         } else if (wasIdle && isAfter) {
314             enqueueDOMEvent(eventNames().transitionrunEvent, intervalStart);
315             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
316             enqueueDOMEvent(eventNames().transitionendEvent, intervalEnd);
317         } else if ((m_wasPending || wasBefore) && isActive)
318             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
319         else if ((m_wasPending || wasBefore) && isAfter) {
320             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
321             enqueueDOMEvent(eventNames().transitionendEvent, intervalEnd);
322         } else if (wasActive && isAfter)
323             enqueueDOMEvent(eventNames().transitionendEvent, intervalEnd);
324         else if (wasActive && isBefore)
325             enqueueDOMEvent(eventNames().transitionendEvent, intervalStart);
326         else if (wasAfter && isActive)
327             enqueueDOMEvent(eventNames().transitionstartEvent, intervalEnd);
328         else if (wasAfter && isBefore) {
329             enqueueDOMEvent(eventNames().transitionstartEvent, intervalEnd);
330             enqueueDOMEvent(eventNames().transitionendEvent, intervalStart);
331         } else if ((!wasIdle && !wasAfter) && isIdle)
332             enqueueDOMEvent(eventNames().transitioncancelEvent, elapsedTime);
333     }
334
335     m_wasPending = isPending;
336     m_previousPhase = currentPhase;
337     m_previousIteration = iteration;
338 }
339
340 void DeclarativeAnimation::enqueueDOMEvent(const AtomString& eventType, Seconds elapsedTime)
341 {
342     if (!m_owningElement)
343         return;
344
345     auto time = secondsToWebAnimationsAPITime(elapsedTime) / 1000;
346     const auto& pseudoId = PseudoElement::pseudoElementNameForEvents(m_owningElement->pseudoId());
347     auto timelineTime = timeline() ? timeline()->currentTime() : WTF::nullopt;
348     auto event = createEvent(eventType, time, pseudoId, timelineTime);
349     event->setTarget(m_owningElement.get());
350     enqueueAnimationEvent(WTFMove(event));
351 }
352
353 } // namespace WebCore