[Web Animations] Enqueue and dispatch animation events
[WebKit-https.git] / Source / WebCore / animation / WebAnimation.cpp
1 /*
2  * Copyright (C) 2017 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 "WebAnimation.h"
28
29 #include "AnimationEffect.h"
30 #include "AnimationPlaybackEvent.h"
31 #include "AnimationTimeline.h"
32 #include "Document.h"
33 #include "KeyframeEffect.h"
34 #include <wtf/text/WTFString.h>
35
36 namespace WebCore {
37
38 Ref<WebAnimation> WebAnimation::create(Document& document, AnimationEffect* effect, AnimationTimeline* timeline)
39 {
40     auto result = adoptRef(*new WebAnimation(document));
41
42     result->setEffect(effect);
43
44     // FIXME: the spec mandates distinguishing between an omitted timeline parameter
45     // and an explicit null or undefined value (webkit.org/b/179065).
46     result->setTimeline(timeline ? timeline : &document.timeline());
47
48     return result;
49 }
50
51 WebAnimation::WebAnimation(Document& document)
52     : ActiveDOMObject(&document)
53 {
54     suspendIfNeeded();
55 }
56
57 WebAnimation::~WebAnimation()
58 {
59     if (m_timeline)
60         m_timeline->removeAnimation(*this);
61 }
62
63 void WebAnimation::setEffect(RefPtr<AnimationEffect>&& effect)
64 {
65     if (effect == m_effect)
66         return;
67
68     if (m_effect) {
69         m_effect->setAnimation(nullptr);
70
71         // Update the Element to Animation map.
72         if (m_timeline && m_effect->isKeyframeEffect()) {
73             auto* keyframeEffect = downcast<KeyframeEffect>(m_effect.get());
74             auto* target = keyframeEffect->target();
75             if (target)
76                 m_timeline->animationWasRemovedFromElement(*this, *target);
77         }
78     }
79
80     if (effect) {
81         // An animation effect can only be associated with a single animation.
82         if (effect->animation())
83             effect->animation()->setEffect(nullptr);
84
85         effect->setAnimation(this);
86
87         if (m_timeline && effect->isKeyframeEffect()) {
88             auto* keyframeEffect = downcast<KeyframeEffect>(effect.get());
89             auto* target = keyframeEffect->target();
90             if (target)
91                 m_timeline->animationWasAddedToElement(*this, *target);
92         }
93     }
94
95     m_effect = WTFMove(effect);
96 }
97
98 void WebAnimation::setTimeline(RefPtr<AnimationTimeline>&& timeline)
99 {
100     if (timeline == m_timeline)
101         return;
102
103     // FIXME: If the animation start time of animation is resolved, make animation’s
104     // hold time unresolved (webkit.org/b/178932).
105
106     if (m_timeline)
107         m_timeline->removeAnimation(*this);
108
109     if (timeline)
110         timeline->addAnimation(*this);
111
112     if (m_effect && m_effect->isKeyframeEffect()) {
113         auto* keyframeEffect = downcast<KeyframeEffect>(m_effect.get());
114         auto* target = keyframeEffect->target();
115         if (target) {
116             if (m_timeline)
117                 m_timeline->animationWasRemovedFromElement(*this, *target);
118             if (timeline)
119                 timeline->animationWasAddedToElement(*this, *target);
120         }
121     }
122
123     m_timeline = WTFMove(timeline);
124 }
125     
126 std::optional<double> WebAnimation::bindingsStartTime() const
127 {
128     if (m_startTime)
129         return m_startTime->milliseconds();
130     return std::nullopt;
131 }
132
133 void WebAnimation::setBindingsStartTime(std::optional<double> startTime)
134 {
135     if (startTime == std::nullopt)
136         setStartTime(std::nullopt);
137     else
138         setStartTime(Seconds::fromMilliseconds(startTime.value()));
139 }
140
141 std::optional<Seconds> WebAnimation::startTime() const
142 {
143     return m_startTime;
144 }
145
146 void WebAnimation::setStartTime(std::optional<Seconds> startTime)
147 {
148     if (startTime == m_startTime)
149         return;
150
151     m_startTime = startTime;
152     
153     if (m_timeline)
154         m_timeline->animationTimingModelDidChange();
155 }
156
157 std::optional<double> WebAnimation::bindingsCurrentTime() const
158 {
159     auto time = currentTime();
160     if (!time)
161         return std::nullopt;
162     return time->milliseconds();
163 }
164
165 ExceptionOr<void> WebAnimation::setBindingsCurrentTime(std::optional<double> currentTime)
166 {
167     if (!currentTime)
168         return Exception { TypeError };
169     setCurrentTime(Seconds::fromMilliseconds(currentTime.value()));
170     return { };
171 }
172
173 std::optional<Seconds> WebAnimation::currentTime() const
174 {
175     // FIXME: return the hold time when we support pausing (webkit.org/b/178932).
176
177     if (!m_timeline || !m_startTime)
178         return std::nullopt;
179
180     auto timelineTime = m_timeline->currentTime();
181     if (!timelineTime)
182         return std::nullopt;
183
184     return (timelineTime.value() - m_startTime.value()) * m_playbackRate;
185 }
186
187 void WebAnimation::setCurrentTime(std::optional<Seconds> seekTime)
188 {
189     // FIXME: account for hold time when we support it (webkit.org/b/178932),
190     // including situations where playbackRate is 0.
191
192     if (!m_timeline) {
193         setStartTime(std::nullopt);
194         return;
195     }
196
197     auto timelineTime = m_timeline->currentTime();
198     if (!timelineTime) {
199         setStartTime(std::nullopt);
200         return;
201     }
202
203     setStartTime(timelineTime.value() - (seekTime.value() / m_playbackRate));
204 }
205
206 void WebAnimation::setPlaybackRate(double newPlaybackRate)
207 {
208     if (m_playbackRate == newPlaybackRate)
209         return;
210
211     // 3.5.17.1. Updating the playback rate of an animation
212     // Changes to the playback rate trigger a compensatory seek so that that the animation's current time
213     // is unaffected by the change to the playback rate.
214     auto previousTime = currentTime();
215     m_playbackRate = newPlaybackRate;
216     if (previousTime)
217         setCurrentTime(previousTime);
218 }
219
220 void WebAnimation::enqueueAnimationPlaybackEvent(const AtomicString& type, std::optional<Seconds> currentTime, std::optional<Seconds> timelineTime)
221 {
222     auto event = AnimationPlaybackEvent::create(type, currentTime, timelineTime);
223     event->setTarget(this);
224
225     if (is<DocumentTimeline>(m_timeline)) {
226         // If animation has a document for timing, then append event to its document for timing's pending animation event queue along
227         // with its target, animation. If animation is associated with an active timeline that defines a procedure to convert timeline times
228         // to origin-relative time, let the scheduled event time be the result of applying that procedure to timeline time. Otherwise, the
229         // scheduled event time is an unresolved time value.
230         downcast<DocumentTimeline>(*m_timeline).enqueueAnimationPlaybackEvent(WTFMove(event));
231     } else {
232         // Otherwise, queue a task to dispatch event at animation. The task source for this task is the DOM manipulation task source.
233         callOnMainThread([this, pendingActivity = makePendingActivity(*this), event = WTFMove(event)]() {
234             if (!m_isStopped)
235                 this->dispatchEvent(event);
236         });
237     }
238 }
239
240 Seconds WebAnimation::timeToNextRequiredTick(Seconds timelineTime) const
241 {
242     if (!m_timeline || !m_startTime || !m_effect || !m_playbackRate)
243         return Seconds::infinity();
244
245     auto startTime = m_startTime.value();
246     auto endTime = startTime + (m_effect->timing()->duration() / m_playbackRate);
247
248     // If we haven't started yet, return the interval until our active start time.
249     auto activeStartTime = std::min(startTime, endTime);
250     if (timelineTime <= activeStartTime)
251         return activeStartTime - timelineTime;
252
253     // If we're in the middle of our active duration, we want to be called as soon as possible.
254     auto activeEndTime = std::max(startTime, endTime);
255     if (timelineTime <= activeEndTime)
256         return 0_ms;
257
258     // If none of the previous cases match, then we're already past our active duration
259     // and do not need scheduling.
260     return Seconds::infinity();
261 }
262
263 void WebAnimation::resolve(RenderStyle& targetStyle)
264 {
265     if (m_effect && currentTime())
266         m_effect->applyAtLocalTime(currentTime().value(), targetStyle);
267 }
268
269 void WebAnimation::acceleratedRunningStateDidChange()
270 {
271     if (is<DocumentTimeline>(m_timeline))
272         downcast<DocumentTimeline>(*m_timeline).animationAcceleratedRunningStateDidChange(*this);
273 }
274
275 void WebAnimation::startOrStopAccelerated()
276 {
277     if (m_effect && m_effect->isKeyframeEffect())
278         downcast<KeyframeEffect>(*m_effect).startOrStopAccelerated();
279 }
280
281 String WebAnimation::description()
282 {
283     return "Animation";
284 }
285
286 const char* WebAnimation::activeDOMObjectName() const
287 {
288     return "Animation";
289 }
290
291 bool WebAnimation::canSuspendForDocumentSuspension() const
292 {
293     return !hasPendingActivity();
294 }
295
296 void WebAnimation::stop()
297 {
298     m_isStopped = true;
299     removeAllEventListeners();
300 }
301
302 } // namespace WebCore