1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/animation/timing.h"
6 
7 #include "third_party/blink/renderer/bindings/core/v8/v8_computed_effect_timing.h"
8 #include "third_party/blink/renderer/bindings/core/v8/v8_effect_timing.h"
9 #include "third_party/blink/renderer/core/animation/timing_calculations.h"
10 
11 namespace blink {
12 
FillModeString(FillMode fill_mode)13 String Timing::FillModeString(FillMode fill_mode) {
14   switch (fill_mode) {
15     case FillMode::NONE:
16       return "none";
17     case FillMode::FORWARDS:
18       return "forwards";
19     case FillMode::BACKWARDS:
20       return "backwards";
21     case FillMode::BOTH:
22       return "both";
23     case FillMode::AUTO:
24       return "auto";
25   }
26   NOTREACHED();
27   return "none";
28 }
29 
StringToFillMode(const String & fill_mode)30 Timing::FillMode Timing::StringToFillMode(const String& fill_mode) {
31   if (fill_mode == "none")
32     return Timing::FillMode::NONE;
33   if (fill_mode == "backwards")
34     return Timing::FillMode::BACKWARDS;
35   if (fill_mode == "both")
36     return Timing::FillMode::BOTH;
37   if (fill_mode == "forwards")
38     return Timing::FillMode::FORWARDS;
39   DCHECK_EQ(fill_mode, "auto");
40   return Timing::FillMode::AUTO;
41 }
42 
PlaybackDirectionString(PlaybackDirection playback_direction)43 String Timing::PlaybackDirectionString(PlaybackDirection playback_direction) {
44   switch (playback_direction) {
45     case PlaybackDirection::NORMAL:
46       return "normal";
47     case PlaybackDirection::REVERSE:
48       return "reverse";
49     case PlaybackDirection::ALTERNATE_NORMAL:
50       return "alternate";
51     case PlaybackDirection::ALTERNATE_REVERSE:
52       return "alternate-reverse";
53   }
54   NOTREACHED();
55   return "normal";
56 }
57 
ResolvedFillMode(bool is_keyframe_effect) const58 Timing::FillMode Timing::ResolvedFillMode(bool is_keyframe_effect) const {
59   if (fill_mode != Timing::FillMode::AUTO)
60     return fill_mode;
61 
62   // https://drafts.csswg.org/web-animations/#the-effecttiming-dictionaries
63   if (is_keyframe_effect)
64     return Timing::FillMode::NONE;
65   return Timing::FillMode::BOTH;
66 }
67 
IterationDuration() const68 AnimationTimeDelta Timing::IterationDuration() const {
69   AnimationTimeDelta result = iteration_duration.value_or(AnimationTimeDelta());
70   DCHECK_GE(result, AnimationTimeDelta());
71   return result;
72 }
73 
ActiveDuration() const74 double Timing::ActiveDuration() const {
75   const double result =
76       MultiplyZeroAlwaysGivesZero(IterationDuration(), iteration_count);
77   DCHECK_GE(result, 0);
78   return result;
79 }
80 
EndTimeInternal() const81 double Timing::EndTimeInternal() const {
82   // Per the spec, the end time has a lower bound of 0.0:
83   // https://drafts.csswg.org/web-animations-1/#end-time
84   return std::max(start_delay + ActiveDuration() + end_delay, 0.0);
85 }
86 
ConvertToEffectTiming() const87 EffectTiming* Timing::ConvertToEffectTiming() const {
88   EffectTiming* effect_timing = EffectTiming::Create();
89 
90   effect_timing->setDelay(start_delay * 1000);
91   effect_timing->setEndDelay(end_delay * 1000);
92   effect_timing->setFill(FillModeString(fill_mode));
93   effect_timing->setIterationStart(iteration_start);
94   effect_timing->setIterations(iteration_count);
95   UnrestrictedDoubleOrString duration;
96   if (iteration_duration) {
97     duration.SetUnrestrictedDouble(iteration_duration->InMillisecondsF());
98   } else {
99     duration.SetString("auto");
100   }
101   effect_timing->setDuration(duration);
102   effect_timing->setDirection(PlaybackDirectionString(direction));
103   effect_timing->setEasing(timing_function->ToString());
104 
105   return effect_timing;
106 }
107 
getComputedTiming(const CalculatedTiming & calculated_timing,bool is_keyframe_effect) const108 ComputedEffectTiming* Timing::getComputedTiming(
109     const CalculatedTiming& calculated_timing,
110     bool is_keyframe_effect) const {
111   ComputedEffectTiming* computed_timing = ComputedEffectTiming::Create();
112 
113   // ComputedEffectTiming members.
114   computed_timing->setEndTime(EndTimeInternal() * 1000);
115   computed_timing->setActiveDuration(ActiveDuration() * 1000);
116 
117   if (calculated_timing.local_time)
118     computed_timing->setLocalTime(calculated_timing.local_time.value() * 1000);
119   else
120     computed_timing->setLocalTimeToNull();
121 
122   if (calculated_timing.is_in_effect) {
123     DCHECK(calculated_timing.current_iteration);
124     DCHECK(calculated_timing.progress);
125     computed_timing->setProgress(calculated_timing.progress.value());
126     computed_timing->setCurrentIteration(
127         calculated_timing.current_iteration.value());
128   } else {
129     computed_timing->setProgressToNull();
130     computed_timing->setCurrentIterationToNull();
131   }
132 
133   // For the EffectTiming members, getComputedTiming is equivalent to getTiming
134   // except that the fill and duration must be resolved.
135   //
136   // https://drafts.csswg.org/web-animations-1/#dom-animationeffect-getcomputedtiming
137   computed_timing->setDelay(start_delay * 1000);
138   computed_timing->setEndDelay(end_delay * 1000);
139   computed_timing->setFill(
140       Timing::FillModeString(ResolvedFillMode(is_keyframe_effect)));
141   computed_timing->setIterationStart(iteration_start);
142   computed_timing->setIterations(iteration_count);
143 
144   UnrestrictedDoubleOrString duration;
145   duration.SetUnrestrictedDouble(IterationDuration().InMillisecondsF());
146   computed_timing->setDuration(duration);
147 
148   computed_timing->setDirection(Timing::PlaybackDirectionString(direction));
149   computed_timing->setEasing(timing_function->ToString());
150 
151   return computed_timing;
152 }
153 
CalculateTimings(base::Optional<double> local_time,AnimationDirection animation_direction,bool is_keyframe_effect,base::Optional<double> playback_rate) const154 Timing::CalculatedTiming Timing::CalculateTimings(
155     base::Optional<double> local_time,
156     AnimationDirection animation_direction,
157     bool is_keyframe_effect,
158     base::Optional<double> playback_rate) const {
159   const double active_duration = ActiveDuration();
160 
161   const Timing::Phase current_phase =
162       CalculatePhase(active_duration, local_time, animation_direction, *this);
163   const base::Optional<AnimationTimeDelta> active_time =
164       CalculateActiveTime(active_duration, ResolvedFillMode(is_keyframe_effect),
165                           local_time, current_phase, *this);
166 
167   base::Optional<double> progress;
168   const double iteration_duration = IterationDuration().InSecondsF();
169 
170   const base::Optional<double> overall_progress =
171       CalculateOverallProgress(current_phase, active_time, iteration_duration,
172                                iteration_count, iteration_start);
173   const base::Optional<double> simple_iteration_progress =
174       CalculateSimpleIterationProgress(current_phase, overall_progress,
175                                        iteration_start, active_time,
176                                        active_duration, iteration_count);
177   const base::Optional<double> current_iteration =
178       CalculateCurrentIteration(current_phase, active_time, iteration_count,
179                                 overall_progress, simple_iteration_progress);
180   const bool current_direction_is_forwards =
181       IsCurrentDirectionForwards(current_iteration, direction);
182   const base::Optional<double> directed_progress = CalculateDirectedProgress(
183       simple_iteration_progress, current_iteration, direction);
184 
185   progress = CalculateTransformedProgress(current_phase, directed_progress,
186                                           current_direction_is_forwards,
187                                           timing_function);
188 
189   AnimationTimeDelta time_to_next_iteration = AnimationTimeDelta::Max();
190   // Conditionally compute the time to next iteration, which is only
191   // applicable if the iteration duration is non-zero.
192   if (iteration_duration) {
193     const double start_offset =
194         MultiplyZeroAlwaysGivesZero(iteration_start, iteration_duration);
195     DCHECK_GE(start_offset, 0);
196     const base::Optional<AnimationTimeDelta> offset_active_time =
197         CalculateOffsetActiveTime(active_duration, active_time, start_offset);
198     const base::Optional<AnimationTimeDelta> iteration_time =
199         CalculateIterationTime(iteration_duration, active_duration,
200                                offset_active_time, start_offset, current_phase,
201                                *this);
202     if (iteration_time) {
203       // active_time cannot be null if iteration_time is not null.
204       DCHECK(active_time);
205       time_to_next_iteration =
206           AnimationTimeDelta::FromSecondsD(iteration_duration) -
207           iteration_time.value();
208       if (AnimationTimeDelta::FromSecondsD(active_duration) -
209               active_time.value() <
210           time_to_next_iteration)
211         time_to_next_iteration = AnimationTimeDelta::Max();
212     }
213   }
214 
215   CalculatedTiming calculated = CalculatedTiming();
216   calculated.phase = current_phase;
217   calculated.current_iteration = current_iteration;
218   calculated.progress = progress;
219   calculated.is_in_effect = active_time.has_value();
220   // If active_time is not null then current_iteration and (transformed)
221   // progress are also non-null).
222   DCHECK(!calculated.is_in_effect ||
223          (current_iteration.has_value() && progress.has_value()));
224   calculated.is_in_play = calculated.phase == Timing::kPhaseActive;
225   // https://drafts.csswg.org/web-animations-1/#current
226   calculated.is_current = calculated.is_in_play ||
227                           (playback_rate.has_value() && playback_rate > 0 &&
228                            calculated.phase == Timing::kPhaseBefore) ||
229                           (playback_rate.has_value() && playback_rate < 0 &&
230                            calculated.phase == Timing::kPhaseAfter);
231   calculated.local_time = local_time;
232   calculated.time_to_next_iteration = time_to_next_iteration;
233 
234   return calculated;
235 }
236 
237 }  // namespace blink
238