Unreviewed, rolling out r247292.
[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 "TransitionEvent.h"
39 #include <wtf/IsoMallocInlines.h>
40
41 namespace WebCore {
42
43 WTF_MAKE_ISO_ALLOCATED_IMPL(DeclarativeAnimation);
44
45 DeclarativeAnimation::DeclarativeAnimation(Element& owningElement, const Animation& backingAnimation)
46     : WebAnimation(owningElement.document())
47     , m_eventQueue(owningElement)
48     , m_owningElement(&owningElement)
49     , m_backingAnimation(const_cast<Animation&>(backingAnimation))
50 {
51 }
52
53 DeclarativeAnimation::~DeclarativeAnimation()
54 {
55 }
56
57 void DeclarativeAnimation::tick()
58 {
59     bool wasRelevant = isRelevant();
60     
61     WebAnimation::tick();
62     invalidateDOMEvents();
63
64     // If a declarative animation transitions from a non-idle state to an idle state, it means it was
65     // canceled using the Web Animations API and it should be disassociated from its owner element.
66     // From this point on, this animation is like any other animation and should not appear in the
67     // maps containing running CSS Transitions and CSS Animations for a given element.
68     if (wasRelevant && playState() == WebAnimation::PlayState::Idle) {
69         disassociateFromOwningElement();
70         m_eventQueue.close();
71     }
72 }
73
74 void DeclarativeAnimation::disassociateFromOwningElement()
75 {
76     if (!m_owningElement)
77         return;
78
79     if (auto* animationTimeline = timeline())
80         animationTimeline->removeDeclarativeAnimationFromListsForOwningElement(*this, *m_owningElement);
81     m_owningElement = nullptr;
82 }
83
84 bool DeclarativeAnimation::needsTick() const
85 {
86     return WebAnimation::needsTick() || m_eventQueue.hasPendingEvents();
87 }
88
89 void DeclarativeAnimation::remove()
90 {
91     m_eventQueue.close();
92     WebAnimation::remove();
93 }
94
95 void DeclarativeAnimation::setBackingAnimation(const Animation& backingAnimation)
96 {
97     m_backingAnimation = const_cast<Animation&>(backingAnimation);
98     syncPropertiesWithBackingAnimation();
99 }
100
101 void DeclarativeAnimation::initialize(const RenderStyle* oldStyle, const RenderStyle& newStyle)
102 {
103     // We need to suspend invalidation of the animation's keyframe effect during its creation
104     // as it would otherwise trigger invalidation of the document's style and this would be
105     // incorrect since it would happen during style invalidation.
106     suspendEffectInvalidation();
107
108     ASSERT(m_owningElement);
109
110     setEffect(KeyframeEffect::create(*m_owningElement));
111     setTimeline(&m_owningElement->document().timeline());
112     downcast<KeyframeEffect>(effect())->computeDeclarativeAnimationBlendingKeyframes(oldStyle, newStyle);
113     syncPropertiesWithBackingAnimation();
114     if (backingAnimation().playState() == AnimationPlayState::Playing)
115         play();
116     else
117         pause();
118
119     unsuspendEffectInvalidation();
120 }
121
122 void DeclarativeAnimation::syncPropertiesWithBackingAnimation()
123 {
124 }
125
126 Optional<double> DeclarativeAnimation::startTime() const
127 {
128     flushPendingStyleChanges();
129     return WebAnimation::startTime();
130 }
131
132 void DeclarativeAnimation::setStartTime(Optional<double> startTime)
133 {
134     flushPendingStyleChanges();
135     return WebAnimation::setStartTime(startTime);
136 }
137
138 Optional<double> DeclarativeAnimation::bindingsCurrentTime() const
139 {
140     flushPendingStyleChanges();
141     return WebAnimation::bindingsCurrentTime();
142 }
143
144 ExceptionOr<void> DeclarativeAnimation::setBindingsCurrentTime(Optional<double> currentTime)
145 {
146     flushPendingStyleChanges();
147     return WebAnimation::setBindingsCurrentTime(currentTime);
148 }
149
150 WebAnimation::PlayState DeclarativeAnimation::bindingsPlayState() const
151 {
152     flushPendingStyleChanges();
153     return WebAnimation::bindingsPlayState();
154 }
155
156 bool DeclarativeAnimation::bindingsPending() const
157 {
158     flushPendingStyleChanges();
159     return WebAnimation::bindingsPending();
160 }
161
162 WebAnimation::ReadyPromise& DeclarativeAnimation::bindingsReady()
163 {
164     flushPendingStyleChanges();
165     return WebAnimation::bindingsReady();
166 }
167
168 WebAnimation::FinishedPromise& DeclarativeAnimation::bindingsFinished()
169 {
170     flushPendingStyleChanges();
171     return WebAnimation::bindingsFinished();
172 }
173
174 ExceptionOr<void> DeclarativeAnimation::bindingsPlay()
175 {
176     flushPendingStyleChanges();
177     return WebAnimation::bindingsPlay();
178 }
179
180 ExceptionOr<void> DeclarativeAnimation::bindingsPause()
181 {
182     flushPendingStyleChanges();
183     return WebAnimation::bindingsPause();
184 }
185
186 void DeclarativeAnimation::flushPendingStyleChanges() const
187 {
188     if (auto* animationEffect = effect()) {
189         if (is<KeyframeEffect>(animationEffect)) {
190             if (auto* target = downcast<KeyframeEffect>(animationEffect)->target())
191                 target->document().updateStyleIfNeeded();
192         }
193     }
194 }
195
196 void DeclarativeAnimation::setTimeline(RefPtr<AnimationTimeline>&& newTimeline)
197 {
198     if (timeline() && !newTimeline)
199         cancel();
200
201     WebAnimation::setTimeline(WTFMove(newTimeline));
202 }
203
204 void DeclarativeAnimation::cancel()
205 {
206     auto cancelationTime = 0_s;
207     if (auto* animationEffect = effect()) {
208         if (auto activeTime = animationEffect->getBasicTiming().activeTime)
209             cancelationTime = *activeTime;
210     }
211
212     WebAnimation::cancel();
213
214     invalidateDOMEvents(cancelationTime);
215 }
216
217 void DeclarativeAnimation::cancelFromStyle()
218 {
219     cancel();
220     disassociateFromOwningElement();
221 }
222
223 AnimationEffectPhase DeclarativeAnimation::phaseWithoutEffect() const
224 {
225     // This shouldn't be called if we actually have an effect.
226     ASSERT(!effect());
227
228     auto animationCurrentTime = currentTime();
229     if (!animationCurrentTime)
230         return AnimationEffectPhase::Idle;
231
232     // 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.
233     return *animationCurrentTime < 0_s ? AnimationEffectPhase::Before : AnimationEffectPhase::After;
234 }
235
236 void DeclarativeAnimation::invalidateDOMEvents(Seconds elapsedTime)
237 {
238     if (!m_owningElement)
239         return;
240     
241     auto isPending = pending();
242     if (isPending && m_wasPending)
243         return;
244
245     double iteration = 0;
246     AnimationEffectPhase currentPhase;
247     Seconds intervalStart;
248     Seconds intervalEnd;
249
250     auto* animationEffect = effect();
251     if (animationEffect) {
252         auto timing = animationEffect->getComputedTiming();
253         if (auto computedIteration = timing.currentIteration)
254             iteration = *computedIteration;
255         currentPhase = timing.phase;
256         intervalStart = std::max(0_s, Seconds::fromMilliseconds(std::min(-timing.delay, timing.activeDuration)));
257         intervalEnd = std::max(0_s, Seconds::fromMilliseconds(std::min(timing.endTime - timing.delay, timing.activeDuration)));
258     } else {
259         iteration = 0;
260         currentPhase = phaseWithoutEffect();
261         intervalStart = 0_s;
262         intervalEnd = 0_s;
263     }
264
265     bool wasActive = m_previousPhase == AnimationEffectPhase::Active;
266     bool wasAfter = m_previousPhase == AnimationEffectPhase::After;
267     bool wasBefore = m_previousPhase == AnimationEffectPhase::Before;
268     bool wasIdle = m_previousPhase == AnimationEffectPhase::Idle;
269
270     bool isActive = currentPhase == AnimationEffectPhase::Active;
271     bool isAfter = currentPhase == AnimationEffectPhase::After;
272     bool isBefore = currentPhase == AnimationEffectPhase::Before;
273     bool isIdle = currentPhase == AnimationEffectPhase::Idle;
274
275     if (is<CSSAnimation>(this)) {
276         // https://drafts.csswg.org/css-animations-2/#events
277         if ((wasIdle || wasBefore) && isActive)
278             enqueueDOMEvent(eventNames().animationstartEvent, intervalStart);
279         else if ((wasIdle || wasBefore) && isAfter) {
280             enqueueDOMEvent(eventNames().animationstartEvent, intervalStart);
281             enqueueDOMEvent(eventNames().animationendEvent, intervalEnd);
282         } else if (wasActive && isBefore)
283             enqueueDOMEvent(eventNames().animationendEvent, intervalStart);
284         else if (wasActive && isActive && m_previousIteration != iteration) {
285             auto iterationBoundary = iteration;
286             if (m_previousIteration > iteration)
287                 iterationBoundary++;
288             auto elapsedTime = animationEffect ? animationEffect->iterationDuration() * (iterationBoundary - animationEffect->iterationStart()) : 0_s;
289             enqueueDOMEvent(eventNames().animationiterationEvent, elapsedTime);
290         } else if (wasActive && isAfter)
291             enqueueDOMEvent(eventNames().animationendEvent, intervalEnd);
292         else if (wasAfter && isActive)
293             enqueueDOMEvent(eventNames().animationstartEvent, intervalEnd);
294         else if (wasAfter && isBefore) {
295             enqueueDOMEvent(eventNames().animationstartEvent, intervalEnd);
296             enqueueDOMEvent(eventNames().animationendEvent, intervalStart);
297         } else if ((!wasIdle && !wasAfter) && isIdle)
298             enqueueDOMEvent(eventNames().animationcancelEvent, elapsedTime);
299     } else if (is<CSSTransition>(this)) {
300         // https://drafts.csswg.org/css-transitions-2/#transition-events
301         if (wasIdle && (isPending || isBefore))
302             enqueueDOMEvent(eventNames().transitionrunEvent, intervalStart);
303         else if (wasIdle && isActive) {
304             enqueueDOMEvent(eventNames().transitionrunEvent, intervalStart);
305             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
306         } else if (wasIdle && isAfter) {
307             enqueueDOMEvent(eventNames().transitionrunEvent, intervalStart);
308             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
309             enqueueDOMEvent(eventNames().transitionendEvent, intervalEnd);
310         } else if ((m_wasPending || wasBefore) && isActive)
311             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
312         else if ((m_wasPending || wasBefore) && isAfter) {
313             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
314             enqueueDOMEvent(eventNames().transitionendEvent, intervalEnd);
315         } else if (wasActive && isAfter)
316             enqueueDOMEvent(eventNames().transitionendEvent, intervalEnd);
317         else if (wasActive && isBefore)
318             enqueueDOMEvent(eventNames().transitionendEvent, intervalStart);
319         else if (wasAfter && isActive)
320             enqueueDOMEvent(eventNames().transitionstartEvent, intervalEnd);
321         else if (wasAfter && isBefore) {
322             enqueueDOMEvent(eventNames().transitionstartEvent, intervalEnd);
323             enqueueDOMEvent(eventNames().transitionendEvent, intervalStart);
324         } else if ((!wasIdle && !wasAfter) && isIdle)
325             enqueueDOMEvent(eventNames().transitioncancelEvent, elapsedTime);
326     }
327
328     m_wasPending = isPending;
329     m_previousPhase = currentPhase;
330     m_previousIteration = iteration;
331 }
332
333 void DeclarativeAnimation::enqueueDOMEvent(const AtomString& eventType, Seconds elapsedTime)
334 {
335     ASSERT(m_owningElement);
336     auto time = secondsToWebAnimationsAPITime(elapsedTime) / 1000;
337     if (is<CSSAnimation>(this))
338         m_eventQueue.enqueueEvent(AnimationEvent::create(eventType, downcast<CSSAnimation>(this)->animationName(), time));
339     else if (is<CSSTransition>(this))
340         m_eventQueue.enqueueEvent(TransitionEvent::create(eventType, downcast<CSSTransition>(this)->transitionProperty(), time, PseudoElement::pseudoElementNameForEvents(m_owningElement->pseudoId())));
341 }
342
343 void DeclarativeAnimation::stop()
344 {
345     m_eventQueue.close();
346     WebAnimation::stop();
347 }
348
349 void DeclarativeAnimation::suspend(ReasonForSuspension reason)
350 {
351     m_eventQueue.suspend();
352     WebAnimation::suspend(reason);
353 }
354
355 void DeclarativeAnimation::resume()
356 {
357     m_eventQueue.resume();
358     WebAnimation::resume();
359 }
360
361 } // namespace WebCore