Unreviewed, rolling out r247292.
[WebKit-https.git] / Source / WebCore / animation / AnimationEffect.cpp
1 /*
2  * Copyright (C) 2017-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 "AnimationEffect.h"
28
29 #include "FillMode.h"
30 #include "JSComputedEffectTiming.h"
31 #include "WebAnimationUtilities.h"
32
33 namespace WebCore {
34
35 AnimationEffect::AnimationEffect()
36     : m_timingFunction(LinearTimingFunction::create())
37 {
38 }
39
40 AnimationEffect::~AnimationEffect()
41 {
42 }
43
44 EffectTiming AnimationEffect::getTiming() const
45 {
46     EffectTiming timing;
47     timing.delay = secondsToWebAnimationsAPITime(m_delay);
48     timing.endDelay = secondsToWebAnimationsAPITime(m_endDelay);
49     timing.fill = m_fill;
50     timing.iterationStart = m_iterationStart;
51     timing.iterations = m_iterations;
52     if (m_iterationDuration == 0_s)
53         timing.duration = "auto";
54     else
55         timing.duration = secondsToWebAnimationsAPITime(m_iterationDuration);
56     timing.direction = m_direction;
57     timing.easing = m_timingFunction->cssText();
58     return timing;
59 }
60
61 BasicEffectTiming AnimationEffect::getBasicTiming() const
62 {
63     // The Web Animations spec introduces a number of animation effect time-related definitions that refer
64     // to each other a fair bit, so rather than implementing them as individual methods, it's more efficient
65     // to return them all as a single BasicEffectTiming.
66
67     auto activeDuration = [this]() -> Seconds {
68         // 3.8.2. Calculating the active duration
69         // https://drafts.csswg.org/web-animations-1/#calculating-the-active-duration
70
71         // The active duration is calculated as follows:
72         // active duration = iteration duration × iteration count
73         // If either the iteration duration or iteration count are zero, the active duration is zero.
74         if (!m_iterationDuration || !m_iterations)
75             return 0_s;
76         return m_iterationDuration * m_iterations;
77     }();
78
79     auto endTime = [this, activeDuration]() -> Seconds {
80         // 3.5.3 The active interval
81         // https://drafts.csswg.org/web-animations-1/#end-time
82
83         // The end time of an animation effect is the result of evaluating max(start delay + active duration + end delay, 0).
84         auto endTime = m_delay + activeDuration + m_endDelay;
85         return endTime > 0_s ? endTime : 0_s;
86     }();
87
88     auto localTime = [this]() -> Optional<Seconds> {
89         // 4.5.4. Local time
90         // https://drafts.csswg.org/web-animations-1/#local-time-section
91
92         // The local time of an animation effect at a given moment is based on the first matching condition from the following:
93         // If the animation effect is associated with an animation, the local time is the current time of the animation.
94         // Otherwise, the local time is unresolved.
95         if (m_animation)
96             return m_animation->currentTime();
97         return WTF::nullopt;
98     }();
99
100     auto phase = [this, endTime, localTime, activeDuration]() -> AnimationEffectPhase {
101         // 3.5.5. Animation effect phases and states
102         // https://drafts.csswg.org/web-animations-1/#animation-effect-phases-and-states
103
104         bool animationIsBackwards = m_animation && m_animation->playbackRate() < 0;
105         auto beforeActiveBoundaryTime = std::max(std::min(m_delay, endTime), 0_s);
106         auto activeAfterBoundaryTime = std::max(std::min(m_delay + activeDuration, endTime), 0_s);
107
108         // (This should be the last statement, but it's more efficient to cache the local time and return right away if it's not resolved.)
109         // Furthermore, it is often convenient to refer to the case when an animation effect is in none of the above phases
110         // as being in the idle phase.
111         if (!localTime)
112             return AnimationEffectPhase::Idle;
113
114         // An animation effect is in the before phase if the animation effect’s local time is not unresolved and
115         // either of the following conditions are met:
116         //     1. the local time is less than the before-active boundary time, or
117         //     2. the animation direction is ‘backwards’ and the local time is equal to the before-active boundary time.
118         if ((*localTime + timeEpsilon) < beforeActiveBoundaryTime || (animationIsBackwards && std::abs(localTime->microseconds() - beforeActiveBoundaryTime.microseconds()) < timeEpsilon.microseconds()))
119             return AnimationEffectPhase::Before;
120
121         // An animation effect is in the after phase if the animation effect’s local time is not unresolved and
122         // either of the following conditions are met:
123         //     1. the local time is greater than the active-after boundary time, or
124         //     2. the animation direction is ‘forwards’ and the local time is equal to the active-after boundary time.
125         if ((*localTime - timeEpsilon) > activeAfterBoundaryTime || (!animationIsBackwards && std::abs(localTime->microseconds() - activeAfterBoundaryTime.microseconds()) < timeEpsilon.microseconds()))
126             return AnimationEffectPhase::After;
127
128         // An animation effect is in the active phase if the animation effect’s local time is not unresolved and it is not
129         // in either the before phase nor the after phase.
130         // (No need to check, we've already established that local time was resolved).
131         return AnimationEffectPhase::Active;
132     }();
133
134     auto activeTime = [this, localTime, phase, activeDuration]() -> Optional<Seconds> {
135         // 3.8.3.1. Calculating the active time
136         // https://drafts.csswg.org/web-animations-1/#calculating-the-active-time
137
138         // The active time is based on the local time and start delay. However, it is only defined
139         // when the animation effect should produce an output and hence depends on its fill mode
140         // and phase as follows,
141
142         // If the animation effect is in the before phase, the result depends on the first matching
143         // condition from the following,
144         if (phase == AnimationEffectPhase::Before) {
145             // If the fill mode is backwards or both, return the result of evaluating
146             // max(local time - start delay, 0).
147             if (m_fill == FillMode::Backwards || m_fill == FillMode::Both)
148                 return std::max(*localTime - m_delay, 0_s);
149             // Otherwise, return an unresolved time value.
150             return WTF::nullopt;
151         }
152
153         // If the animation effect is in the active phase, return the result of evaluating local time - start delay.
154         if (phase == AnimationEffectPhase::Active)
155             return *localTime - m_delay;
156
157         // If the animation effect is in the after phase, the result depends on the first matching
158         // condition from the following,
159         if (phase == AnimationEffectPhase::After) {
160             // If the fill mode is forwards or both, return the result of evaluating
161             // max(min(local time - start delay, active duration), 0).
162             if (m_fill == FillMode::Forwards || m_fill == FillMode::Both)
163                 return std::max(std::min(*localTime - m_delay, activeDuration), 0_s);
164             // Otherwise, return an unresolved time value.
165             return WTF::nullopt;
166         }
167
168         // Otherwise (the local time is unresolved), return an unresolved time value.
169         return WTF::nullopt;
170     }();
171
172     return { localTime, activeTime, endTime, activeDuration, phase };
173 }
174
175 ComputedEffectTiming AnimationEffect::getComputedTiming() const
176 {
177     // The Web Animations spec introduces a number of animation effect time-related definitions that refer
178     // to each other a fair bit, so rather than implementing them as individual methods, it's more efficient
179     // to return them all as a single ComputedEffectTiming.
180
181     auto basicEffectTiming = getBasicTiming();
182     auto activeTime = basicEffectTiming.activeTime;
183     auto activeDuration = basicEffectTiming.activeDuration;
184     auto phase = basicEffectTiming.phase;
185
186     auto overallProgress = [this, phase, activeTime]() -> Optional<double> {
187         // 3.8.3.2. Calculating the overall progress
188         // https://drafts.csswg.org/web-animations-1/#calculating-the-overall-progress
189
190         // The overall progress describes the number of iterations that have completed (including partial iterations) and is defined as follows:
191
192         // 1. If the active time is unresolved, return unresolved.
193         if (!activeTime)
194             return WTF::nullopt;
195
196         // 2. Calculate an initial value for overall progress based on the first matching condition from below,
197         double overallProgress;
198
199         if (!m_iterationDuration) {
200             // If the iteration duration is zero, if the animation effect is in the before phase, let overall progress be zero,
201             // otherwise, let it be equal to the iteration count.
202             overallProgress = phase == AnimationEffectPhase::Before ? 0 : m_iterations;
203         } else {
204             // Otherwise, let overall progress be the result of calculating active time / iteration duration.
205             overallProgress = secondsToWebAnimationsAPITime(*activeTime) / secondsToWebAnimationsAPITime(m_iterationDuration);
206         }
207
208         // 3. Return the result of calculating overall progress + iteration start.
209         overallProgress += m_iterationStart;
210         return std::abs(overallProgress);
211     }();
212
213     auto simpleIterationProgress = [this, overallProgress, phase, activeTime, activeDuration]() -> Optional<double> {
214         // 3.8.3.3. Calculating the simple iteration progress
215         // https://drafts.csswg.org/web-animations-1/#calculating-the-simple-iteration-progress
216
217         // The simple iteration progress is a fraction of the progress through the current iteration that
218         // ignores transformations to the time introduced by the playback direction or timing functions
219         // applied to the effect, and is calculated as follows:
220
221         // 1. If the overall progress is unresolved, return unresolved.
222         if (!overallProgress)
223             return WTF::nullopt;
224
225         // 2. If overall progress is infinity, let the simple iteration progress be iteration start % 1.0,
226         // otherwise, let the simple iteration progress be overall progress % 1.0.
227         double simpleIterationProgress = std::isinf(*overallProgress) ? fmod(m_iterationStart, 1) : fmod(*overallProgress, 1);
228
229         // 3. If all of the following conditions are true,
230         //
231         // the simple iteration progress calculated above is zero, and
232         // the animation effect is in the active phase or the after phase, and
233         // the active time is equal to the active duration, and
234         // the iteration count is not equal to zero.
235         // let the simple iteration progress be 1.0.
236         if (!simpleIterationProgress && (phase == AnimationEffectPhase::Active || phase == AnimationEffectPhase::After) && std::abs(activeTime->microseconds() - activeDuration.microseconds()) < timeEpsilon.microseconds() && m_iterations)
237             return 1;
238
239         return simpleIterationProgress;
240     }();
241
242     auto currentIteration = [this, activeTime, phase, simpleIterationProgress, overallProgress]() -> Optional<double> {
243         // 3.8.4. Calculating the current iteration
244         // https://drafts.csswg.org/web-animations-1/#calculating-the-current-iteration
245
246         // The current iteration can be calculated using the following steps:
247
248         // 1. If the active time is unresolved, return unresolved.
249         if (!activeTime)
250             return WTF::nullopt;
251
252         // 2. If the animation effect is in the after phase and the iteration count is infinity, return infinity.
253         if (phase == AnimationEffectPhase::After && std::isinf(m_iterations))
254             return std::numeric_limits<double>::infinity();
255
256         // 3. If the simple iteration progress is 1.0, return floor(overall progress) - 1.
257         if (*simpleIterationProgress == 1)
258             return floor(*overallProgress) - 1;
259
260         // 4. Otherwise, return floor(overall progress).
261         return floor(*overallProgress);
262     }();
263
264     auto currentDirection = [this, currentIteration]() -> AnimationEffect::ComputedDirection {
265         // 3.9.1. Calculating the directed progress
266         // https://drafts.csswg.org/web-animations-1/#calculating-the-directed-progress
267
268         // If playback direction is normal, let the current direction be forwards.
269         if (m_direction == PlaybackDirection::Normal)
270             return AnimationEffect::ComputedDirection::Forwards;
271     
272         // If playback direction is reverse, let the current direction be reverse.
273         if (m_direction == PlaybackDirection::Reverse)
274             return AnimationEffect::ComputedDirection::Reverse;
275
276         if (!currentIteration)
277             return AnimationEffect::ComputedDirection::Forwards;
278     
279         // Otherwise, let d be the current iteration.
280         auto d = *currentIteration;
281         // If playback direction is alternate-reverse increment d by 1.
282         if (m_direction == PlaybackDirection::AlternateReverse)
283             d++;
284         // If d % 2 == 0, let the current direction be forwards, otherwise let the current direction be reverse.
285         // If d is infinity, let the current direction be forwards.
286         if (std::isinf(d) || !fmod(d, 2))
287             return AnimationEffect::ComputedDirection::Forwards;
288         return AnimationEffect::ComputedDirection::Reverse;
289     }();
290
291     auto directedProgress = [simpleIterationProgress, currentDirection]() -> Optional<double> {
292         // 3.9.1. Calculating the directed progress
293         // https://drafts.csswg.org/web-animations-1/#calculating-the-directed-progress
294
295         // The directed progress is calculated from the simple iteration progress using the following steps:
296
297         // 1. If the simple iteration progress is unresolved, return unresolved.
298         if (!simpleIterationProgress)
299             return WTF::nullopt;
300
301         // 2. Calculate the current direction (we implement this as a separate method).
302
303         // 3. If the current direction is forwards then return the simple iteration progress.
304         if (currentDirection == AnimationEffect::ComputedDirection::Forwards)
305             return *simpleIterationProgress;
306
307         // Otherwise, return 1.0 - simple iteration progress.
308         return 1 - *simpleIterationProgress;
309     }();
310
311     auto transformedProgress = [this, directedProgress, currentDirection, phase]() -> Optional<double> {
312         // 3.10.1. Calculating the transformed progress
313         // https://drafts.csswg.org/web-animations-1/#calculating-the-transformed-progress
314
315         // The transformed progress is calculated from the directed progress using the following steps:
316         //
317         // 1. If the directed progress is unresolved, return unresolved.
318         if (!directedProgress)
319             return WTF::nullopt;
320
321         if (auto iterationDuration = m_iterationDuration.seconds()) {
322             bool before = false;
323             // 2. Calculate the value of the before flag as follows:
324             if (is<StepsTimingFunction>(m_timingFunction)) {
325                 // 1. Determine the current direction using the procedure defined in §3.9.1 Calculating the directed progress.
326                 // 2. If the current direction is forwards, let going forwards be true, otherwise it is false.
327                 bool goingForwards = currentDirection == AnimationEffect::ComputedDirection::Forwards;
328                 // 3. The before flag is set if the animation effect is in the before phase and going forwards is true;
329                 //    or if the animation effect is in the after phase and going forwards is false.
330                 before = (phase == AnimationEffectPhase::Before && goingForwards) || (phase == AnimationEffectPhase::After && !goingForwards);
331             }
332
333             // 3. Return the result of evaluating the animation effect’s timing function passing directed progress as the
334             //    input progress value and before flag as the before flag.
335             return m_timingFunction->transformTime(*directedProgress, iterationDuration, before);
336         }
337
338         return *directedProgress;
339     }();
340
341     ComputedEffectTiming computedTiming;
342     computedTiming.delay = secondsToWebAnimationsAPITime(m_delay);
343     computedTiming.endDelay = secondsToWebAnimationsAPITime(m_endDelay);
344     computedTiming.fill = m_fill == FillMode::Auto ? FillMode::None : m_fill;
345     computedTiming.iterationStart = m_iterationStart;
346     computedTiming.iterations = m_iterations;
347     computedTiming.duration = secondsToWebAnimationsAPITime(m_iterationDuration);
348     computedTiming.direction = m_direction;
349     computedTiming.easing = m_timingFunction->cssText();
350     computedTiming.endTime = secondsToWebAnimationsAPITime(basicEffectTiming.endTime);
351     computedTiming.activeDuration = secondsToWebAnimationsAPITime(activeDuration);
352     if (basicEffectTiming.localTime)
353         computedTiming.localTime = secondsToWebAnimationsAPITime(*basicEffectTiming.localTime);
354     computedTiming.simpleIterationProgress = simpleIterationProgress;
355     computedTiming.progress = transformedProgress;
356     computedTiming.currentIteration = currentIteration;
357     computedTiming.phase = phase;
358     return computedTiming;
359 }
360
361 ExceptionOr<void> AnimationEffect::updateTiming(Optional<OptionalEffectTiming> timing)
362 {
363     // 6.5.4. Updating the timing of an AnimationEffect
364     // https://drafts.csswg.org/web-animations/#updating-animationeffect-timing
365
366     // To update the timing properties of an animation effect, effect, from an EffectTiming or OptionalEffectTiming object, input, perform the following steps:
367     if (!timing)
368         return { };
369
370     // 1. If the iterationStart member of input is present and less than zero, throw a TypeError and abort this procedure.
371     if (timing->iterationStart) {
372         if (timing->iterationStart.value() < 0)
373             return Exception { TypeError };
374     }
375
376     // 2. If the iterations member of input is present, and less than zero or is the value NaN, throw a TypeError and abort this procedure.
377     if (timing->iterations) {
378         if (timing->iterations.value() < 0 || std::isnan(timing->iterations.value()))
379             return Exception { TypeError };
380     }
381
382     // 3. If the duration member of input is present, and less than zero or is the value NaN, throw a TypeError and abort this procedure.
383     // FIXME: should it not throw an exception on a string other than "auto"?
384     if (timing->duration) {
385         if (WTF::holds_alternative<double>(timing->duration.value())) {
386             auto durationAsDouble = WTF::get<double>(timing->duration.value());
387             if (durationAsDouble < 0 || std::isnan(durationAsDouble))
388                 return Exception { TypeError };
389         } else {
390             if (WTF::get<String>(timing->duration.value()) != "auto")
391                 return Exception { TypeError };
392         }
393     }
394
395     // 4. If the easing member of input is present but cannot be parsed using the <timing-function> production [CSS-EASING-1], throw a TypeError and abort this procedure.
396     if (!timing->easing.isNull()) {
397         auto timingFunctionResult = TimingFunction::createFromCSSText(timing->easing);
398         if (timingFunctionResult.hasException())
399             return timingFunctionResult.releaseException();
400         m_timingFunction = timingFunctionResult.returnValue();
401     }
402
403     // 5. Assign each member present in input to the corresponding timing property of effect as follows:
404     //
405     //    delay → start delay
406     //    endDelay → end delay
407     //    fill → fill mode
408     //    iterationStart → iteration start
409     //    iterations → iteration count
410     //    duration → iteration duration
411     //    direction → playback direction
412     //    easing → timing function
413
414     if (timing->delay)
415         m_delay = Seconds::fromMilliseconds(timing->delay.value());
416
417     if (timing->endDelay)
418         m_endDelay = Seconds::fromMilliseconds(timing->endDelay.value());
419
420     if (timing->fill)
421         m_fill = timing->fill.value();
422
423     if (timing->iterationStart)
424         m_iterationStart = timing->iterationStart.value();
425
426     if (timing->iterations)
427         m_iterations = timing->iterations.value();
428
429     if (timing->duration)
430         m_iterationDuration = WTF::holds_alternative<double>(timing->duration.value()) ? Seconds::fromMilliseconds(WTF::get<double>(timing->duration.value())) : 0_s;
431
432     if (timing->direction)
433         m_direction = timing->direction.value();
434
435     if (m_animation)
436         m_animation->effectTimingDidChange();
437
438     return { };
439 }
440
441 ExceptionOr<void> AnimationEffect::setIterationStart(double iterationStart)
442 {
443     // https://drafts.csswg.org/web-animations-1/#dom-animationeffecttiming-iterationstart
444     // If an attempt is made to set this attribute to a value less than zero, a TypeError must
445     // be thrown and the value of the iterationStart attribute left unchanged.
446     if (iterationStart < 0)
447         return Exception { TypeError };
448
449     if (m_iterationStart == iterationStart)
450         return { };
451
452     m_iterationStart = iterationStart;
453
454     return { };
455 }
456
457 ExceptionOr<void> AnimationEffect::setIterations(double iterations)
458 {
459     // https://drafts.csswg.org/web-animations-1/#dom-animationeffecttiming-iterations
460     // If an attempt is made to set this attribute to a value less than zero or a NaN value, a
461     // TypeError must be thrown and the value of the iterations attribute left unchanged.
462     if (iterations < 0 || std::isnan(iterations))
463         return Exception { TypeError };
464
465     if (m_iterations == iterations)
466         return { };
467         
468     m_iterations = iterations;
469
470     return { };
471 }
472
473 void AnimationEffect::setDelay(const Seconds& delay)
474 {
475     if (m_delay == delay)
476         return;
477
478     m_delay = delay;
479 }
480
481 void AnimationEffect::setEndDelay(const Seconds& endDelay)
482 {
483     if (m_endDelay == endDelay)
484         return;
485
486     m_endDelay = endDelay;
487 }
488
489 void AnimationEffect::setFill(FillMode fill)
490 {
491     if (m_fill == fill)
492         return;
493
494     m_fill = fill;
495 }
496
497 void AnimationEffect::setIterationDuration(const Seconds& duration)
498 {
499     if (m_iterationDuration == duration)
500         return;
501
502     m_iterationDuration = duration;
503 }
504
505 void AnimationEffect::setDirection(PlaybackDirection direction)
506 {
507     if (m_direction == direction)
508         return;
509
510     m_direction = direction;
511 }
512
513 void AnimationEffect::setTimingFunction(const RefPtr<TimingFunction>& timingFunction)
514 {
515     m_timingFunction = timingFunction;
516 }
517
518 } // namespace WebCore