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