tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TimingParams.cpp (10019B)


      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 #include "mozilla/TimingParams.h"
      8 
      9 #include "mozilla/AnimationUtils.h"
     10 #include "mozilla/ServoCSSParser.h"
     11 #include "mozilla/dom/AnimatableBinding.h"
     12 #include "mozilla/dom/Document.h"
     13 #include "mozilla/dom/KeyframeAnimationOptionsBinding.h"
     14 #include "mozilla/dom/KeyframeEffectBinding.h"
     15 
     16 namespace mozilla {
     17 
     18 template <class OptionsType>
     19 static const dom::EffectTiming& GetTimingProperties(
     20    const OptionsType& aOptions);
     21 
     22 template <>
     23 /* static */
     24 const dom::EffectTiming& GetTimingProperties(
     25    const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions) {
     26  MOZ_ASSERT(aOptions.IsKeyframeEffectOptions());
     27  return aOptions.GetAsKeyframeEffectOptions();
     28 }
     29 
     30 template <>
     31 /* static */
     32 const dom::EffectTiming& GetTimingProperties(
     33    const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions) {
     34  MOZ_ASSERT(aOptions.IsKeyframeAnimationOptions());
     35  return aOptions.GetAsKeyframeAnimationOptions();
     36 }
     37 
     38 template <class OptionsType>
     39 /* static */
     40 TimingParams TimingParams::FromOptionsType(const OptionsType& aOptions,
     41                                           ErrorResult& aRv) {
     42  TimingParams result;
     43 
     44  if (aOptions.IsUnrestrictedDouble()) {
     45    double durationInMs = aOptions.GetAsUnrestrictedDouble();
     46    if (durationInMs >= 0) {
     47      result.mDuration.emplace(
     48          StickyTimeDuration::FromMilliseconds(durationInMs));
     49    } else {
     50      nsPrintfCString error("Duration value %g is less than 0", durationInMs);
     51      aRv.ThrowTypeError(error);
     52      return result;
     53    }
     54    result.Update();
     55  } else {
     56    const dom::EffectTiming& timing = GetTimingProperties(aOptions);
     57    result = FromEffectTiming(timing, aRv);
     58  }
     59 
     60  return result;
     61 }
     62 
     63 /* static */
     64 TimingParams TimingParams::FromOptionsUnion(
     65    const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
     66    ErrorResult& aRv) {
     67  return FromOptionsType(aOptions, aRv);
     68 }
     69 
     70 /* static */
     71 TimingParams TimingParams::FromOptionsUnion(
     72    const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
     73    ErrorResult& aRv) {
     74  return FromOptionsType(aOptions, aRv);
     75 }
     76 
     77 /* static */
     78 TimingParams TimingParams::FromEffectTiming(
     79    const dom::EffectTiming& aEffectTiming, ErrorResult& aRv) {
     80  TimingParams result;
     81 
     82  Maybe<StickyTimeDuration> duration =
     83      TimingParams::ParseDuration(aEffectTiming.mDuration, aRv);
     84  if (aRv.Failed()) {
     85    return result;
     86  }
     87  TimingParams::ValidateIterationStart(aEffectTiming.mIterationStart, aRv);
     88  if (aRv.Failed()) {
     89    return result;
     90  }
     91  TimingParams::ValidateIterations(aEffectTiming.mIterations, aRv);
     92  if (aRv.Failed()) {
     93    return result;
     94  }
     95  Maybe<StyleComputedTimingFunction> easing =
     96      ParseEasing(aEffectTiming.mEasing, aRv);
     97  if (aRv.Failed()) {
     98    return result;
     99  }
    100 
    101  result.mDuration = duration;
    102  result.mDelay = TimeDuration::FromMilliseconds(aEffectTiming.mDelay);
    103  result.mEndDelay = TimeDuration::FromMilliseconds(aEffectTiming.mEndDelay);
    104  result.mIterations = aEffectTiming.mIterations;
    105  result.mIterationStart = aEffectTiming.mIterationStart;
    106  result.mDirection = aEffectTiming.mDirection;
    107  result.mFill = aEffectTiming.mFill;
    108  result.mFunction = std::move(easing);
    109 
    110  result.Update();
    111 
    112  return result;
    113 }
    114 
    115 /* static */
    116 TimingParams TimingParams::MergeOptionalEffectTiming(
    117    const TimingParams& aSource, const dom::OptionalEffectTiming& aEffectTiming,
    118    ErrorResult& aRv) {
    119  MOZ_ASSERT(!aRv.Failed(), "Initially return value should be ok");
    120 
    121  TimingParams result = aSource;
    122 
    123  // Check for errors first
    124 
    125  Maybe<StickyTimeDuration> duration;
    126  if (aEffectTiming.mDuration.WasPassed()) {
    127    duration =
    128        TimingParams::ParseDuration(aEffectTiming.mDuration.Value(), aRv);
    129    if (aRv.Failed()) {
    130      return result;
    131    }
    132  }
    133 
    134  if (aEffectTiming.mIterationStart.WasPassed()) {
    135    TimingParams::ValidateIterationStart(aEffectTiming.mIterationStart.Value(),
    136                                         aRv);
    137    if (aRv.Failed()) {
    138      return result;
    139    }
    140  }
    141 
    142  if (aEffectTiming.mIterations.WasPassed()) {
    143    TimingParams::ValidateIterations(aEffectTiming.mIterations.Value(), aRv);
    144    if (aRv.Failed()) {
    145      return result;
    146    }
    147  }
    148 
    149  Maybe<StyleComputedTimingFunction> easing;
    150  if (aEffectTiming.mEasing.WasPassed()) {
    151    easing = ParseEasing(aEffectTiming.mEasing.Value(), aRv);
    152    if (aRv.Failed()) {
    153      return result;
    154    }
    155  }
    156 
    157  // Assign values
    158 
    159  if (aEffectTiming.mDuration.WasPassed()) {
    160    result.mDuration = duration;
    161  }
    162  if (aEffectTiming.mDelay.WasPassed()) {
    163    result.mDelay =
    164        TimeDuration::FromMilliseconds(aEffectTiming.mDelay.Value());
    165  }
    166  if (aEffectTiming.mEndDelay.WasPassed()) {
    167    result.mEndDelay =
    168        TimeDuration::FromMilliseconds(aEffectTiming.mEndDelay.Value());
    169  }
    170  if (aEffectTiming.mIterations.WasPassed()) {
    171    result.mIterations = aEffectTiming.mIterations.Value();
    172  }
    173  if (aEffectTiming.mIterationStart.WasPassed()) {
    174    result.mIterationStart = aEffectTiming.mIterationStart.Value();
    175  }
    176  if (aEffectTiming.mDirection.WasPassed()) {
    177    result.mDirection = aEffectTiming.mDirection.Value();
    178  }
    179  if (aEffectTiming.mFill.WasPassed()) {
    180    result.mFill = aEffectTiming.mFill.Value();
    181  }
    182  if (aEffectTiming.mEasing.WasPassed()) {
    183    result.mFunction = easing;
    184  }
    185 
    186  result.Update();
    187 
    188  return result;
    189 }
    190 
    191 /* static */
    192 Maybe<StyleComputedTimingFunction> TimingParams::ParseEasing(
    193    const nsACString& aEasing, ErrorResult& aRv) {
    194  auto timingFunction = StyleComputedTimingFunction::LinearKeyword();
    195  if (!ServoCSSParser::ParseEasing(aEasing, timingFunction)) {
    196    aRv.ThrowTypeError<dom::MSG_INVALID_EASING_ERROR>(aEasing);
    197    return Nothing();
    198  }
    199 
    200  if (timingFunction.IsLinearKeyword()) {
    201    return Nothing();
    202  }
    203 
    204  return Some(std::move(timingFunction));
    205 }
    206 
    207 bool TimingParams::operator==(const TimingParams& aOther) const {
    208  // We don't compare mActiveDuration and mEndTime because they are calculated
    209  // from other timing parameters.
    210  return mDuration == aOther.mDuration && mDelay == aOther.mDelay &&
    211         mEndDelay == aOther.mEndDelay && mIterations == aOther.mIterations &&
    212         mIterationStart == aOther.mIterationStart &&
    213         mDirection == aOther.mDirection && mFill == aOther.mFill &&
    214         mFunction == aOther.mFunction;
    215 }
    216 
    217 // FIXME: This is a tentative way to normalize the timing which is defined in
    218 // [web-animations-2] [1]. I borrow this implementation and some concepts for
    219 // the edge cases from Chromium [2] so we can match the behavior with them. The
    220 // implementation here ignores the case of percentage of start delay, end delay,
    221 // and duration because Gecko doesn't support them.
    222 //
    223 // FIXME: Bug 1804775. We may have to update the calculation here after we
    224 // introduce animaiton ranges.
    225 //
    226 // [1]
    227 // https://drafts.csswg.org/web-animations-2/#time-based-animation-to-a-proportional-animation
    228 // [2] https://chromium-review.googlesource.com/c/chromium/src/+/2992387
    229 TimingParams TimingParams::Normalize(
    230    const TimeDuration& aTimelineDuration) const {
    231  MOZ_ASSERT(aTimelineDuration,
    232             "the timeline duration of scroll-timeline is always non-zero now");
    233 
    234  TimingParams normalizedTiming(*this);
    235 
    236  // Handle iteration duration value of "auto" first.
    237  if (!mDuration) {
    238    // If the iteration duration is auto, then:
    239    //   Set start delay and end delay to 0, as it is not possible to mix time
    240    //   and proportions.
    241    normalizedTiming.mDelay = TimeDuration();
    242    normalizedTiming.mEndDelay = TimeDuration();
    243    // FIXME: Bug 1804775. We may have to tweak here once we introduce timeline
    244    // range (e.g. animation-range-{start|end}). For now, we use the default
    245    // timeline duration as the normalized duration of this timing.
    246    normalizedTiming.mDuration.emplace(aTimelineDuration);
    247    normalizedTiming.Update();
    248    return normalizedTiming;
    249  }
    250 
    251  if (mEndTime.IsZero()) {
    252    // mEndTime of zero causes division by zero so we handle it here.
    253    //
    254    // FIXME: The spec doesn't mention this case, so we might have to update
    255    // this based on the spec issue,
    256    // https://github.com/w3c/csswg-drafts/issues/7459.
    257    normalizedTiming.mDelay = TimeDuration();
    258    normalizedTiming.mEndDelay = TimeDuration();
    259    normalizedTiming.mDuration = Some(TimeDuration());
    260  } else if (mEndTime == TimeDuration::Forever()) {
    261    // The iteration count or duration may be infinite; however, start and
    262    // end delays are strictly finite. Thus, in the limit when end time
    263    // approaches infinity:
    264    //    start delay / end time = finite / infinite = 0
    265    //    end delay / end time = finite / infinite = 0
    266    //    iteration duration / end time = 1 / iteration count
    267    // This condition can be reached by switching to a scroll timeline on
    268    // an existing infinite duration animation.
    269    //
    270    // FIXME: The spec doesn't mention this case, so we might have to update
    271    // this based on the spec issue,
    272    // https://github.com/w3c/csswg-drafts/issues/7459.
    273    normalizedTiming.mDelay = TimeDuration();
    274    normalizedTiming.mEndDelay = TimeDuration();
    275    normalizedTiming.mDuration =
    276        Some(aTimelineDuration.MultDouble(1.0 / mIterations));
    277  } else {
    278    // Convert to percentages then multiply by the timeline duration.
    279    const double endTimeInSec = mEndTime.ToSeconds();
    280    normalizedTiming.mDelay =
    281        aTimelineDuration.MultDouble(mDelay.ToSeconds() / endTimeInSec);
    282    normalizedTiming.mEndDelay =
    283        aTimelineDuration.MultDouble(mEndDelay.ToSeconds() / endTimeInSec);
    284    normalizedTiming.mDuration = Some(StickyTimeDuration(
    285        aTimelineDuration.MultDouble(mDuration->ToSeconds() / endTimeInSec)));
    286  }
    287 
    288  normalizedTiming.Update();
    289  return normalizedTiming;
    290 }
    291 
    292 }  // namespace mozilla