diff options
Diffstat (limited to 'dom/animation/TimingParams.h')
-rw-r--r-- | dom/animation/TimingParams.h | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/dom/animation/TimingParams.h b/dom/animation/TimingParams.h new file mode 100644 index 0000000000..67a1dade8f --- /dev/null +++ b/dom/animation/TimingParams.h @@ -0,0 +1,265 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_TimingParams_h +#define mozilla_TimingParams_h + +#include "X11UndefineNone.h" +#include "nsPrintfCString.h" +#include "nsStringFwd.h" +#include "nsPrintfCString.h" +#include "mozilla/dom/Nullable.h" +#include "mozilla/dom/UnionTypes.h" // For OwningUnrestrictedDoubleOrString +#include "mozilla/Maybe.h" +#include "mozilla/StickyTimeDuration.h" +#include "mozilla/TimeStamp.h" // for TimeDuration +#include "mozilla/ServoStyleConsts.h" + +#include "mozilla/dom/AnimationEffectBinding.h" // for FillMode + // and PlaybackDirection + +namespace mozilla { + +namespace dom { +class UnrestrictedDoubleOrKeyframeEffectOptions; +class UnrestrictedDoubleOrKeyframeAnimationOptions; +} // namespace dom + +struct TimingParams { + TimingParams() = default; + + TimingParams(float aDuration, float aDelay, float aIterationCount, + dom::PlaybackDirection aDirection, dom::FillMode aFillMode) + : mIterations(aIterationCount), mDirection(aDirection), mFill(aFillMode) { + mDuration.emplace(StickyTimeDuration::FromMilliseconds(aDuration)); + mDelay = TimeDuration::FromMilliseconds(aDelay); + Update(); + } + + TimingParams(const TimeDuration& aDuration, const TimeDuration& aDelay, + const TimeDuration& aEndDelay, float aIterations, + float aIterationStart, dom::PlaybackDirection aDirection, + dom::FillMode aFillMode, + const Maybe<StyleComputedTimingFunction>& aFunction) + : mDelay(aDelay), + mEndDelay(aEndDelay), + mIterations(aIterations), + mIterationStart(aIterationStart), + mDirection(aDirection), + mFill(aFillMode), + mFunction(aFunction) { + mDuration.emplace(aDuration); + Update(); + } + + template <class OptionsType> + static TimingParams FromOptionsType(const OptionsType& aOptions, + ErrorResult& aRv); + static TimingParams FromOptionsUnion( + const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, + ErrorResult& aRv); + static TimingParams FromOptionsUnion( + const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, + ErrorResult& aRv); + static TimingParams FromEffectTiming(const dom::EffectTiming& aEffectTiming, + ErrorResult& aRv); + // Returns a copy of |aSource| where each timing property in |aSource| that + // is also specified in |aEffectTiming| is replaced with the value from + // |aEffectTiming|. + // + // If any of the values in |aEffectTiming| are invalid, |aRv.Failed()| will be + // true and an unmodified copy of |aSource| will be returned. + static TimingParams MergeOptionalEffectTiming( + const TimingParams& aSource, + const dom::OptionalEffectTiming& aEffectTiming, ErrorResult& aRv); + + // Range-checks and validates an UnrestrictedDoubleOrString or + // OwningUnrestrictedDoubleOrString object and converts to a + // StickyTimeDuration value or Nothing() if aDuration is "auto". + // Caller must check aRv.Failed(). + template <class DoubleOrString> + static Maybe<StickyTimeDuration> ParseDuration(DoubleOrString& aDuration, + ErrorResult& aRv) { + Maybe<StickyTimeDuration> result; + if (aDuration.IsUnrestrictedDouble()) { + double durationInMs = aDuration.GetAsUnrestrictedDouble(); + if (durationInMs >= 0) { + result.emplace(StickyTimeDuration::FromMilliseconds(durationInMs)); + } else { + nsPrintfCString err("Duration (%g) must be nonnegative", durationInMs); + aRv.ThrowTypeError(err); + } + } else if (!aDuration.GetAsString().EqualsLiteral("auto")) { + aRv.ThrowTypeError<dom::MSG_INVALID_DURATION_ERROR>( + NS_ConvertUTF16toUTF8(aDuration.GetAsString())); + } + return result; + } + + static void ValidateIterationStart(double aIterationStart, ErrorResult& aRv) { + if (aIterationStart < 0) { + nsPrintfCString err("Iteration start (%g) must not be negative", + aIterationStart); + aRv.ThrowTypeError(err); + } + } + + static void ValidateIterations(double aIterations, ErrorResult& aRv) { + if (IsNaN(aIterations)) { + aRv.ThrowTypeError("Iterations must not be NaN"); + return; + } + + if (aIterations < 0) { + nsPrintfCString err("Iterations (%g) must not be negative", aIterations); + aRv.ThrowTypeError(err); + } + } + + static Maybe<StyleComputedTimingFunction> ParseEasing(const nsACString&, + ErrorResult&); + + static StickyTimeDuration CalcActiveDuration( + const Maybe<StickyTimeDuration>& aDuration, double aIterations) { + // If either the iteration duration or iteration count is zero, + // Web Animations says that the active duration is zero. This is to + // ensure that the result is defined when the other argument is Infinity. + static const StickyTimeDuration zeroDuration; + if (!aDuration || aDuration->IsZero() || aIterations == 0.0) { + return zeroDuration; + } + + MOZ_ASSERT(*aDuration >= zeroDuration && aIterations >= 0.0, + "Both animation duration and ieration count should be greater " + "than zero"); + + StickyTimeDuration result = aDuration->MultDouble(aIterations); + if (result < zeroDuration) { + // If the result of multiplying above is less than zero, it's likely an + // overflow happened. we consider it's +Inf here. + return StickyTimeDuration::Forever(); + } + return result; + } + // Return the duration of the active interval calculated by duration and + // iteration count. + StickyTimeDuration ActiveDuration() const { + MOZ_ASSERT(CalcActiveDuration(mDuration, mIterations) == mActiveDuration, + "Cached value of active duration should be up to date"); + return mActiveDuration; + } + + StickyTimeDuration EndTime() const { + MOZ_ASSERT(mEndTime == CalcEndTime(), + "Cached value of end time should be up to date"); + return mEndTime; + } + + StickyTimeDuration CalcBeforeActiveBoundary() const { + static constexpr StickyTimeDuration zeroDuration; + // https://drafts.csswg.org/web-animations-1/#before-active-boundary-time + return std::max(std::min(StickyTimeDuration(mDelay), mEndTime), + zeroDuration); + } + + StickyTimeDuration CalcActiveAfterBoundary() const { + if (mActiveDuration == StickyTimeDuration::Forever()) { + return StickyTimeDuration::Forever(); + } + + static constexpr StickyTimeDuration zeroDuration; + // https://drafts.csswg.org/web-animations-1/#active-after-boundary-time + return std::max( + std::min(StickyTimeDuration(mDelay + mActiveDuration), mEndTime), + zeroDuration); + } + + bool operator==(const TimingParams& aOther) const; + bool operator!=(const TimingParams& aOther) const { + return !(*this == aOther); + } + + void SetDuration(Maybe<StickyTimeDuration>&& aDuration) { + mDuration = std::move(aDuration); + Update(); + } + void SetDuration(const Maybe<StickyTimeDuration>& aDuration) { + mDuration = aDuration; + Update(); + } + const Maybe<StickyTimeDuration>& Duration() const { return mDuration; } + + void SetDelay(const TimeDuration& aDelay) { + mDelay = aDelay; + Update(); + } + const TimeDuration& Delay() const { return mDelay; } + + void SetEndDelay(const TimeDuration& aEndDelay) { + mEndDelay = aEndDelay; + Update(); + } + const TimeDuration& EndDelay() const { return mEndDelay; } + + void SetIterations(double aIterations) { + mIterations = aIterations; + Update(); + } + double Iterations() const { return mIterations; } + + void SetIterationStart(double aIterationStart) { + mIterationStart = aIterationStart; + } + double IterationStart() const { return mIterationStart; } + + void SetDirection(dom::PlaybackDirection aDirection) { + mDirection = aDirection; + } + dom::PlaybackDirection Direction() const { return mDirection; } + + void SetFill(dom::FillMode aFill) { mFill = aFill; } + dom::FillMode Fill() const { return mFill; } + + void SetTimingFunction(Maybe<StyleComputedTimingFunction>&& aFunction) { + mFunction = std::move(aFunction); + } + const Maybe<StyleComputedTimingFunction>& TimingFunction() const { + return mFunction; + } + + // This is called only for progress-based timeline (i.e. non-monotonic + // timeline). That is, |aTimelineDuration| should be resolved already. + TimingParams Normalize(const TimeDuration& aTimelineDuration) const; + + private: + void Update() { + mActiveDuration = CalcActiveDuration(mDuration, mIterations); + mEndTime = CalcEndTime(); + } + + StickyTimeDuration CalcEndTime() const { + if (mActiveDuration == StickyTimeDuration::Forever()) { + return StickyTimeDuration::Forever(); + } + return std::max(mDelay + mActiveDuration + mEndDelay, StickyTimeDuration()); + } + + // mDuration.isNothing() represents the "auto" value + Maybe<StickyTimeDuration> mDuration; + TimeDuration mDelay; // Initializes to zero + TimeDuration mEndDelay; + double mIterations = 1.0; // Can be NaN, negative, +/-Infinity + double mIterationStart = 0.0; + dom::PlaybackDirection mDirection = dom::PlaybackDirection::Normal; + dom::FillMode mFill = dom::FillMode::Auto; + Maybe<StyleComputedTimingFunction> mFunction; + StickyTimeDuration mActiveDuration = StickyTimeDuration(); + StickyTimeDuration mEndTime = StickyTimeDuration(); +}; + +} // namespace mozilla + +#endif // mozilla_TimingParams_h |