DeclarativeAnimation should suspend, resume, & stop m_eventQueue
[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 "AnimationEffectTimingReadOnly.h"
31 #include "AnimationEvent.h"
32 #include "Element.h"
33 #include "EventNames.h"
34 #include "KeyframeEffectReadOnly.h"
35 #include "PseudoElement.h"
36 #include "TransitionEvent.h"
37
38 namespace WebCore {
39
40 DeclarativeAnimation::DeclarativeAnimation(Element& target, const Animation& backingAnimation)
41     : WebAnimation(target.document())
42     , m_target(target)
43     , m_backingAnimation(const_cast<Animation&>(backingAnimation))
44     , m_eventQueue(target)
45 {
46 }
47
48 DeclarativeAnimation::~DeclarativeAnimation()
49 {
50 }
51
52 void DeclarativeAnimation::remove()
53 {
54     m_eventQueue.close();
55     WebAnimation::remove();
56 }
57
58 void DeclarativeAnimation::setBackingAnimation(const Animation& backingAnimation)
59 {
60     m_backingAnimation = const_cast<Animation&>(backingAnimation);
61     syncPropertiesWithBackingAnimation();
62 }
63
64 void DeclarativeAnimation::initialize(const Element& target, const RenderStyle* oldStyle, const RenderStyle& newStyle)
65 {
66     // We need to suspend invalidation of the animation's keyframe effect during its creation
67     // as it would otherwise trigger invalidation of the document's style and this would be
68     // incorrect since it would happen during style invalidation.
69     suspendEffectInvalidation();
70
71     setEffect(KeyframeEffectReadOnly::create(target));
72     setTimeline(&target.document().timeline());
73     downcast<KeyframeEffectReadOnly>(effect())->computeDeclarativeAnimationBlendingKeyframes(oldStyle, newStyle);
74     syncPropertiesWithBackingAnimation();
75     if (backingAnimation().playState() == AnimationPlayState::Playing)
76         play();
77     else
78         pause();
79
80     unsuspendEffectInvalidation();
81 }
82
83 void DeclarativeAnimation::syncPropertiesWithBackingAnimation()
84 {
85 }
86
87 void DeclarativeAnimation::setTimeline(RefPtr<AnimationTimeline>&& newTimeline)
88 {
89     if (timeline() && !newTimeline)
90         cancel();
91
92     WebAnimation::setTimeline(WTFMove(newTimeline));
93 }
94
95 void DeclarativeAnimation::cancel()
96 {
97     auto cancelationTime = 0_s;
98     if (auto animationEffect = effect())
99         cancelationTime = animationEffect->activeTime().value_or(0_s);
100
101     WebAnimation::cancel();
102
103     invalidateDOMEvents(cancelationTime);
104 }
105
106 AnimationEffectReadOnly::Phase DeclarativeAnimation::phaseWithoutEffect() const
107 {
108     // This shouldn't be called if we actually have an effect.
109     ASSERT(!effect());
110
111     auto animationCurrentTime = currentTime();
112     if (!animationCurrentTime)
113         return AnimationEffectReadOnly::Phase::Idle;
114
115     // 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.
116     return animationCurrentTime.value() < 0_s ? AnimationEffectReadOnly::Phase::Before : AnimationEffectReadOnly::Phase::After;
117 }
118
119 void DeclarativeAnimation::invalidateDOMEvents(Seconds elapsedTime)
120 {
121     auto* animationEffect = effect();
122
123     auto isPending = pending();
124     if (isPending && m_wasPending)
125         return;
126
127     auto iteration = animationEffect ? animationEffect->currentIteration().value_or(0) : 0;
128     auto currentPhase = animationEffect ? animationEffect->phase() : phaseWithoutEffect();
129
130     bool wasActive = m_previousPhase == AnimationEffectReadOnly::Phase::Active;
131     bool wasAfter = m_previousPhase == AnimationEffectReadOnly::Phase::After;
132     bool wasBefore = m_previousPhase == AnimationEffectReadOnly::Phase::Before;
133     bool wasIdle = m_previousPhase == AnimationEffectReadOnly::Phase::Idle;
134
135     bool isActive = currentPhase == AnimationEffectReadOnly::Phase::Active;
136     bool isAfter = currentPhase == AnimationEffectReadOnly::Phase::After;
137     bool isBefore = currentPhase == AnimationEffectReadOnly::Phase::Before;
138     bool isIdle = currentPhase == AnimationEffectReadOnly::Phase::Idle;
139
140     auto* effectTiming = animationEffect ? animationEffect->timing() : nullptr;
141     auto intervalStart = effectTiming ? std::max(0_s, std::min(-effectTiming->delay(), effectTiming->activeDuration())) : 0_s;
142     auto intervalEnd = effectTiming ? std::max(0_s, std::min(effectTiming->endTime() - effectTiming->delay(), effectTiming->activeDuration())) : 0_s;
143
144     if (is<CSSAnimation>(this)) {
145         // https://drafts.csswg.org/css-animations-2/#events
146         if ((wasIdle || wasBefore) && isActive)
147             enqueueDOMEvent(eventNames().animationstartEvent, intervalStart);
148         else if ((wasIdle || wasBefore) && isAfter) {
149             enqueueDOMEvent(eventNames().animationstartEvent, intervalStart);
150             enqueueDOMEvent(eventNames().animationendEvent, intervalEnd);
151         } else if (wasActive && isBefore)
152             enqueueDOMEvent(eventNames().animationendEvent, intervalStart);
153         else if (wasActive && isActive && m_previousIteration != iteration) {
154             auto iterationBoundary = iteration;
155             if (m_previousIteration > iteration)
156                 iterationBoundary++;
157             auto elapsedTime = effectTiming ? effectTiming->iterationDuration() * (iterationBoundary - effectTiming->iterationStart()) : 0_s;
158             enqueueDOMEvent(eventNames().animationiterationEvent, elapsedTime);
159         } else if (wasActive && isAfter)
160             enqueueDOMEvent(eventNames().animationendEvent, intervalEnd);
161         else if (wasAfter && isActive)
162             enqueueDOMEvent(eventNames().animationstartEvent, intervalEnd);
163         else if (wasAfter && isBefore) {
164             enqueueDOMEvent(eventNames().animationstartEvent, intervalEnd);
165             enqueueDOMEvent(eventNames().animationendEvent, intervalStart);
166         } else if ((!wasIdle && !wasAfter) && isIdle)
167             enqueueDOMEvent(eventNames().animationcancelEvent, elapsedTime);
168     } else if (is<CSSTransition>(this)) {
169         // https://drafts.csswg.org/css-transitions-2/#transition-events
170         if (wasIdle && (isPending || isBefore))
171             enqueueDOMEvent(eventNames().transitionrunEvent, intervalStart);
172         else if (wasIdle && isActive) {
173             enqueueDOMEvent(eventNames().transitionrunEvent, intervalStart);
174             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
175         } else if (wasIdle && isAfter) {
176             enqueueDOMEvent(eventNames().transitionrunEvent, intervalStart);
177             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
178             enqueueDOMEvent(eventNames().transitionendEvent, intervalEnd);
179         } else if ((m_wasPending || wasBefore) && isActive)
180             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
181         else if ((m_wasPending || wasBefore) && isAfter) {
182             enqueueDOMEvent(eventNames().transitionstartEvent, intervalStart);
183             enqueueDOMEvent(eventNames().transitionendEvent, intervalEnd);
184         } else if (wasActive && isAfter)
185             enqueueDOMEvent(eventNames().transitionendEvent, intervalEnd);
186         else if (wasActive && isBefore)
187             enqueueDOMEvent(eventNames().transitionendEvent, intervalStart);
188         else if (wasAfter && isActive)
189             enqueueDOMEvent(eventNames().transitionstartEvent, intervalEnd);
190         else if (wasAfter && isBefore) {
191             enqueueDOMEvent(eventNames().transitionstartEvent, intervalEnd);
192             enqueueDOMEvent(eventNames().transitionendEvent, intervalStart);
193         } else if ((!wasIdle && !wasAfter) && isIdle)
194             enqueueDOMEvent(eventNames().transitioncancelEvent, elapsedTime);
195     }
196
197     m_wasPending = isPending;
198     m_previousPhase = currentPhase;
199     m_previousIteration = iteration;
200 }
201
202 void DeclarativeAnimation::enqueueDOMEvent(const AtomicString& eventType, Seconds elapsedTime)
203 {
204     auto time = secondsToWebAnimationsAPITime(elapsedTime) / 1000;
205     if (is<CSSAnimation>(this))
206         m_eventQueue.enqueueEvent(AnimationEvent::create(eventType, downcast<CSSAnimation>(this)->animationName(), time));
207     else if (is<CSSTransition>(this))
208         m_eventQueue.enqueueEvent(TransitionEvent::create(eventType, downcast<CSSTransition>(this)->transitionProperty(), time, PseudoElement::pseudoElementNameForEvents(m_target.pseudoId())));
209 }
210
211 void DeclarativeAnimation::stop()
212 {
213     m_eventQueue.close();
214     WebAnimation::stop();
215 }
216
217 void DeclarativeAnimation::suspend(ReasonForSuspension reason)
218 {
219     m_eventQueue.suspend();
220     WebAnimation::suspend(reason);
221 }
222
223 void DeclarativeAnimation::resume()
224 {
225     m_eventQueue.resume();
226     WebAnimation::resume();
227 }
228
229 } // namespace WebCore