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