TimingParams.h (9734B)
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 "mozilla/Maybe.h" 12 #include "mozilla/ServoStyleConsts.h" 13 #include "mozilla/StickyTimeDuration.h" 14 #include "mozilla/TimeStamp.h" // for TimeDuration 15 #include "mozilla/dom/AnimationEffectBinding.h" // for FillMode 16 #include "mozilla/dom/Nullable.h" 17 #include "mozilla/dom/UnionTypes.h" // For OwningUnrestrictedDoubleOrString 18 #include "nsPrintfCString.h" 19 #include "nsStringFwd.h" 20 // and PlaybackDirection 21 22 namespace mozilla { 23 24 namespace dom { 25 class UnrestrictedDoubleOrKeyframeEffectOptions; 26 class UnrestrictedDoubleOrKeyframeAnimationOptions; 27 } // namespace dom 28 29 struct TimingParams { 30 TimingParams() = default; 31 32 TimingParams(Maybe<float> aDuration, float aDelay, float aIterationCount, 33 dom::PlaybackDirection aDirection, dom::FillMode aFillMode) 34 : mIterations(aIterationCount), mDirection(aDirection), mFill(aFillMode) { 35 if (aDuration) { 36 mDuration.emplace(StickyTimeDuration::FromMilliseconds(*aDuration)); 37 } 38 mDelay = TimeDuration::FromMilliseconds(aDelay); 39 Update(); 40 } 41 42 TimingParams(const TimeDuration& aDuration, const TimeDuration& aDelay, 43 const TimeDuration& aEndDelay, float aIterations, 44 float aIterationStart, dom::PlaybackDirection aDirection, 45 dom::FillMode aFillMode, 46 const Maybe<StyleComputedTimingFunction>& 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> 84 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 102 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 110 static void ValidateIterations(double aIterations, ErrorResult& aRv) { 111 if (std::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<StyleComputedTimingFunction> ParseEasing(const nsACString&, 123 ErrorResult&); 124 125 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 MOZ_ASSERT(*aDuration >= zeroDuration && aIterations >= 0.0, 136 "Both animation duration and ieration count should be greater " 137 "than zero"); 138 139 StickyTimeDuration result = aDuration->MultDouble(aIterations); 140 if (result < zeroDuration) { 141 // If the result of multiplying above is less than zero, it's likely an 142 // overflow happened. we consider it's +Inf here. 143 return StickyTimeDuration::Forever(); 144 } 145 return result; 146 } 147 // Return the duration of the active interval calculated by duration and 148 // iteration count. 149 StickyTimeDuration ActiveDuration() const { 150 MOZ_ASSERT(CalcActiveDuration(mDuration, mIterations) == mActiveDuration, 151 "Cached value of active duration should be up to date"); 152 return mActiveDuration; 153 } 154 155 StickyTimeDuration EndTime() const { 156 MOZ_ASSERT(mEndTime == CalcEndTime(), 157 "Cached value of end time should be up to date"); 158 return mEndTime; 159 } 160 161 StickyTimeDuration CalcBeforeActiveBoundary() const { 162 static constexpr StickyTimeDuration zeroDuration; 163 // https://drafts.csswg.org/web-animations-1/#before-active-boundary-time 164 return std::clamp(StickyTimeDuration(mDelay), zeroDuration, mEndTime); 165 } 166 167 StickyTimeDuration CalcActiveAfterBoundary() const { 168 if (mActiveDuration == StickyTimeDuration::Forever()) { 169 return StickyTimeDuration::Forever(); 170 } 171 172 static constexpr StickyTimeDuration zeroDuration; 173 // https://drafts.csswg.org/web-animations-1/#active-after-boundary-time 174 return std::max( 175 std::min(StickyTimeDuration(mDelay + mActiveDuration), mEndTime), 176 zeroDuration); 177 } 178 179 bool operator==(const TimingParams& aOther) const; 180 bool operator!=(const TimingParams& aOther) const { 181 return !(*this == aOther); 182 } 183 184 void SetDuration(Maybe<StickyTimeDuration>&& aDuration) { 185 mDuration = std::move(aDuration); 186 Update(); 187 } 188 void SetDuration(const Maybe<StickyTimeDuration>& aDuration) { 189 mDuration = aDuration; 190 Update(); 191 } 192 const Maybe<StickyTimeDuration>& Duration() const { return mDuration; } 193 194 void SetDelay(const TimeDuration& aDelay) { 195 mDelay = aDelay; 196 Update(); 197 } 198 const TimeDuration& Delay() const { return mDelay; } 199 200 void SetEndDelay(const TimeDuration& aEndDelay) { 201 mEndDelay = aEndDelay; 202 Update(); 203 } 204 const TimeDuration& EndDelay() const { return mEndDelay; } 205 206 void SetIterations(double aIterations) { 207 mIterations = aIterations; 208 Update(); 209 } 210 double Iterations() const { return mIterations; } 211 212 void SetIterationStart(double aIterationStart) { 213 mIterationStart = aIterationStart; 214 } 215 double IterationStart() const { return mIterationStart; } 216 217 void SetDirection(dom::PlaybackDirection aDirection) { 218 mDirection = aDirection; 219 } 220 dom::PlaybackDirection Direction() const { return mDirection; } 221 222 void SetFill(dom::FillMode aFill) { mFill = aFill; } 223 dom::FillMode Fill() const { return mFill; } 224 225 void SetTimingFunction(Maybe<StyleComputedTimingFunction>&& aFunction) { 226 mFunction = std::move(aFunction); 227 } 228 const Maybe<StyleComputedTimingFunction>& TimingFunction() const { 229 return mFunction; 230 } 231 232 // This is called only for progress-based timeline (i.e. non-monotonic 233 // timeline). That is, |aTimelineDuration| should be resolved already. 234 TimingParams Normalize(const TimeDuration& aTimelineDuration) const; 235 236 private: 237 void Update() { 238 mActiveDuration = CalcActiveDuration(mDuration, mIterations); 239 mEndTime = CalcEndTime(); 240 } 241 242 StickyTimeDuration CalcEndTime() const { 243 if (mActiveDuration == StickyTimeDuration::Forever()) { 244 return StickyTimeDuration::Forever(); 245 } 246 return std::max(mDelay + mActiveDuration + mEndDelay, StickyTimeDuration()); 247 } 248 249 // mDuration.isNothing() represents the "auto" value 250 Maybe<StickyTimeDuration> mDuration; 251 TimeDuration mDelay; // Initializes to zero 252 TimeDuration mEndDelay; 253 double mIterations = 1.0; // Can be NaN, negative, +/-Infinity 254 double mIterationStart = 0.0; 255 dom::PlaybackDirection mDirection = dom::PlaybackDirection::Normal; 256 dom::FillMode mFill = dom::FillMode::Auto; 257 Maybe<StyleComputedTimingFunction> mFunction; 258 StickyTimeDuration mActiveDuration = StickyTimeDuration(); 259 StickyTimeDuration mEndTime = StickyTimeDuration(); 260 }; 261 262 } // namespace mozilla 263 264 #endif // mozilla_TimingParams_h