1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_TimingParams_h
8 #define mozilla_TimingParams_h
9 
10 #include "X11UndefineNone.h"
11 #include "nsPrintfCString.h"
12 #include "nsStringFwd.h"
13 #include "nsPrintfCString.h"
14 #include "mozilla/dom/Nullable.h"
15 #include "mozilla/dom/UnionTypes.h"  // For OwningUnrestrictedDoubleOrString
16 #include "mozilla/ComputedTimingFunction.h"
17 #include "mozilla/Maybe.h"
18 #include "mozilla/StickyTimeDuration.h"
19 #include "mozilla/TimeStamp.h"  // for TimeDuration
20 
21 #include "mozilla/dom/AnimationEffectBinding.h"  // for FillMode
22                                                  // and PlaybackDirection
23 
24 namespace mozilla {
25 
26 namespace dom {
27 class UnrestrictedDoubleOrKeyframeEffectOptions;
28 class UnrestrictedDoubleOrKeyframeAnimationOptions;
29 }  // namespace dom
30 
31 struct TimingParams {
32   TimingParams() = default;
33 
TimingParamsTimingParams34   TimingParams(float aDuration, float aDelay, float aIterationCount,
35                dom::PlaybackDirection aDirection, dom::FillMode aFillMode)
36       : mIterations(aIterationCount), mDirection(aDirection), mFill(aFillMode) {
37     mDuration.emplace(StickyTimeDuration::FromMilliseconds(aDuration));
38     mDelay = TimeDuration::FromMilliseconds(aDelay);
39     Update();
40   }
41 
TimingParamsTimingParams42   TimingParams(const TimeDuration& aDuration, const TimeDuration& aDelay,
43                const TimeDuration& aEndDelay, float aIterations,
44                float aIterationStart, dom::PlaybackDirection aDirection,
45                dom::FillMode aFillMode,
46                Maybe<ComputedTimingFunction>&& aFunction)
47       : mDelay(aDelay),
48         mEndDelay(aEndDelay),
49         mIterations(aIterations),
50         mIterationStart(aIterationStart),
51         mDirection(aDirection),
52         mFill(aFillMode),
53         mFunction(aFunction) {
54     mDuration.emplace(aDuration);
55     Update();
56   }
57 
58   template <class OptionsType>
59   static TimingParams FromOptionsType(const OptionsType& aOptions,
60                                       ErrorResult& aRv);
61   static TimingParams FromOptionsUnion(
62       const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
63       ErrorResult& aRv);
64   static TimingParams FromOptionsUnion(
65       const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
66       ErrorResult& aRv);
67   static TimingParams FromEffectTiming(const dom::EffectTiming& aEffectTiming,
68                                        ErrorResult& aRv);
69   // Returns a copy of |aSource| where each timing property in |aSource| that
70   // is also specified in |aEffectTiming| is replaced with the value from
71   // |aEffectTiming|.
72   //
73   // If any of the values in |aEffectTiming| are invalid, |aRv.Failed()| will be
74   // true and an unmodified copy of |aSource| will be returned.
75   static TimingParams MergeOptionalEffectTiming(
76       const TimingParams& aSource,
77       const dom::OptionalEffectTiming& aEffectTiming, ErrorResult& aRv);
78 
79   // Range-checks and validates an UnrestrictedDoubleOrString or
80   // OwningUnrestrictedDoubleOrString object and converts to a
81   // StickyTimeDuration value or Nothing() if aDuration is "auto".
82   // Caller must check aRv.Failed().
83   template <class DoubleOrString>
ParseDurationTimingParams84   static Maybe<StickyTimeDuration> ParseDuration(DoubleOrString& aDuration,
85                                                  ErrorResult& aRv) {
86     Maybe<StickyTimeDuration> result;
87     if (aDuration.IsUnrestrictedDouble()) {
88       double durationInMs = aDuration.GetAsUnrestrictedDouble();
89       if (durationInMs >= 0) {
90         result.emplace(StickyTimeDuration::FromMilliseconds(durationInMs));
91       } else {
92         nsPrintfCString err("Duration (%g) must be nonnegative", durationInMs);
93         aRv.ThrowTypeError(err);
94       }
95     } else if (!aDuration.GetAsString().EqualsLiteral("auto")) {
96       aRv.ThrowTypeError<dom::MSG_INVALID_DURATION_ERROR>(
97           NS_ConvertUTF16toUTF8(aDuration.GetAsString()));
98     }
99     return result;
100   }
101 
ValidateIterationStartTimingParams102   static void ValidateIterationStart(double aIterationStart, ErrorResult& aRv) {
103     if (aIterationStart < 0) {
104       nsPrintfCString err("Iteration start (%g) must not be negative",
105                           aIterationStart);
106       aRv.ThrowTypeError(err);
107     }
108   }
109 
ValidateIterationsTimingParams110   static void ValidateIterations(double aIterations, ErrorResult& aRv) {
111     if (IsNaN(aIterations)) {
112       aRv.ThrowTypeError("Iterations must not be NaN");
113       return;
114     }
115 
116     if (aIterations < 0) {
117       nsPrintfCString err("Iterations (%g) must not be negative", aIterations);
118       aRv.ThrowTypeError(err);
119     }
120   }
121 
122   static Maybe<ComputedTimingFunction> ParseEasing(const nsACString& aEasing,
123                                                    ErrorResult& aRv);
124 
CalcActiveDurationTimingParams125   static StickyTimeDuration CalcActiveDuration(
126       const Maybe<StickyTimeDuration>& aDuration, double aIterations) {
127     // If either the iteration duration or iteration count is zero,
128     // Web Animations says that the active duration is zero. This is to
129     // ensure that the result is defined when the other argument is Infinity.
130     static const StickyTimeDuration zeroDuration;
131     if (!aDuration || aDuration->IsZero() || aIterations == 0.0) {
132       return zeroDuration;
133     }
134 
135     return aDuration->MultDouble(aIterations);
136   }
137   // Return the duration of the active interval calculated by duration and
138   // iteration count.
ActiveDurationTimingParams139   StickyTimeDuration ActiveDuration() const {
140     MOZ_ASSERT(CalcActiveDuration(mDuration, mIterations) == mActiveDuration,
141                "Cached value of active duration should be up to date");
142     return mActiveDuration;
143   }
144 
EndTimeTimingParams145   StickyTimeDuration EndTime() const {
146     MOZ_ASSERT(mEndTime == std::max(mDelay + ActiveDuration() + mEndDelay,
147                                     StickyTimeDuration()),
148                "Cached value of end time should be up to date");
149     return mEndTime;
150   }
151 
152   bool operator==(const TimingParams& aOther) const;
153   bool operator!=(const TimingParams& aOther) const {
154     return !(*this == aOther);
155   }
156 
SetDurationTimingParams157   void SetDuration(Maybe<StickyTimeDuration>&& aDuration) {
158     mDuration = std::move(aDuration);
159     Update();
160   }
SetDurationTimingParams161   void SetDuration(const Maybe<StickyTimeDuration>& aDuration) {
162     mDuration = aDuration;
163     Update();
164   }
DurationTimingParams165   const Maybe<StickyTimeDuration>& Duration() const { return mDuration; }
166 
SetDelayTimingParams167   void SetDelay(const TimeDuration& aDelay) {
168     mDelay = aDelay;
169     Update();
170   }
DelayTimingParams171   const TimeDuration& Delay() const { return mDelay; }
172 
SetEndDelayTimingParams173   void SetEndDelay(const TimeDuration& aEndDelay) {
174     mEndDelay = aEndDelay;
175     Update();
176   }
EndDelayTimingParams177   const TimeDuration& EndDelay() const { return mEndDelay; }
178 
SetIterationsTimingParams179   void SetIterations(double aIterations) {
180     mIterations = aIterations;
181     Update();
182   }
IterationsTimingParams183   double Iterations() const { return mIterations; }
184 
SetIterationStartTimingParams185   void SetIterationStart(double aIterationStart) {
186     mIterationStart = aIterationStart;
187   }
IterationStartTimingParams188   double IterationStart() const { return mIterationStart; }
189 
SetDirectionTimingParams190   void SetDirection(dom::PlaybackDirection aDirection) {
191     mDirection = aDirection;
192   }
DirectionTimingParams193   dom::PlaybackDirection Direction() const { return mDirection; }
194 
SetFillTimingParams195   void SetFill(dom::FillMode aFill) { mFill = aFill; }
FillTimingParams196   dom::FillMode Fill() const { return mFill; }
197 
SetTimingFunctionTimingParams198   void SetTimingFunction(Maybe<ComputedTimingFunction>&& aFunction) {
199     mFunction = std::move(aFunction);
200   }
TimingFunctionTimingParams201   const Maybe<ComputedTimingFunction>& TimingFunction() const {
202     return mFunction;
203   }
204 
205  private:
UpdateTimingParams206   void Update() {
207     mActiveDuration = CalcActiveDuration(mDuration, mIterations);
208 
209     mEndTime =
210         std::max(mDelay + mActiveDuration + mEndDelay, StickyTimeDuration());
211   }
212 
213   // mDuration.isNothing() represents the "auto" value
214   Maybe<StickyTimeDuration> mDuration;
215   TimeDuration mDelay;  // Initializes to zero
216   TimeDuration mEndDelay;
217   double mIterations = 1.0;  // Can be NaN, negative, +/-Infinity
218   double mIterationStart = 0.0;
219   dom::PlaybackDirection mDirection = dom::PlaybackDirection::Normal;
220   dom::FillMode mFill = dom::FillMode::Auto;
221   Maybe<ComputedTimingFunction> mFunction;
222   StickyTimeDuration mActiveDuration = StickyTimeDuration();
223   StickyTimeDuration mEndTime = StickyTimeDuration();
224 };
225 
226 }  // namespace mozilla
227 
228 #endif  // mozilla_TimingParams_h
229