AnimationEffectReadOnly.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  4. * You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/dom/AnimationEffectReadOnly.h"
  6. #include "mozilla/dom/AnimationEffectReadOnlyBinding.h"
  7. #include "mozilla/dom/Animation.h"
  8. #include "mozilla/AnimationUtils.h"
  9. #include "mozilla/FloatingPoint.h"
  10. namespace mozilla {
  11. namespace dom {
  12. NS_IMPL_CYCLE_COLLECTION_CLASS(AnimationEffectReadOnly)
  13. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AnimationEffectReadOnly)
  14. if (tmp->mTiming) {
  15. tmp->mTiming->Unlink();
  16. }
  17. NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument, mTiming, mAnimation)
  18. NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
  19. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  20. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AnimationEffectReadOnly)
  21. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument, mTiming, mAnimation)
  22. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  23. NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(AnimationEffectReadOnly)
  24. NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationEffectReadOnly)
  25. NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationEffectReadOnly)
  26. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationEffectReadOnly)
  27. NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  28. NS_INTERFACE_MAP_ENTRY(nsISupports)
  29. NS_INTERFACE_MAP_END
  30. AnimationEffectReadOnly::AnimationEffectReadOnly(
  31. nsIDocument* aDocument, AnimationEffectTimingReadOnly* aTiming)
  32. : mDocument(aDocument)
  33. , mTiming(aTiming)
  34. {
  35. MOZ_ASSERT(aTiming);
  36. }
  37. // https://w3c.github.io/web-animations/#current
  38. bool
  39. AnimationEffectReadOnly::IsCurrent() const
  40. {
  41. if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
  42. return false;
  43. }
  44. ComputedTiming computedTiming = GetComputedTiming();
  45. return computedTiming.mPhase == ComputedTiming::AnimationPhase::Before ||
  46. computedTiming.mPhase == ComputedTiming::AnimationPhase::Active;
  47. }
  48. // https://w3c.github.io/web-animations/#in-effect
  49. bool
  50. AnimationEffectReadOnly::IsInEffect() const
  51. {
  52. ComputedTiming computedTiming = GetComputedTiming();
  53. return !computedTiming.mProgress.IsNull();
  54. }
  55. already_AddRefed<AnimationEffectTimingReadOnly>
  56. AnimationEffectReadOnly::Timing()
  57. {
  58. RefPtr<AnimationEffectTimingReadOnly> temp(mTiming);
  59. return temp.forget();
  60. }
  61. void
  62. AnimationEffectReadOnly::SetSpecifiedTiming(const TimingParams& aTiming)
  63. {
  64. if (mTiming->AsTimingParams() == aTiming) {
  65. return;
  66. }
  67. mTiming->SetTimingParams(aTiming);
  68. if (mAnimation) {
  69. mAnimation->NotifyEffectTimingUpdated();
  70. }
  71. // For keyframe effects, NotifyEffectTimingUpdated above will eventually cause
  72. // KeyframeEffectReadOnly::NotifyAnimationTimingUpdated to be called so it can
  73. // update its registration with the target element as necessary.
  74. }
  75. ComputedTiming
  76. AnimationEffectReadOnly::GetComputedTimingAt(
  77. const Nullable<TimeDuration>& aLocalTime,
  78. const TimingParams& aTiming,
  79. double aPlaybackRate)
  80. {
  81. const StickyTimeDuration zeroDuration;
  82. // Always return the same object to benefit from return-value optimization.
  83. ComputedTiming result;
  84. if (aTiming.mDuration) {
  85. MOZ_ASSERT(aTiming.mDuration.ref() >= zeroDuration,
  86. "Iteration duration should be positive");
  87. result.mDuration = aTiming.mDuration.ref();
  88. }
  89. MOZ_ASSERT(aTiming.mIterations >= 0.0 && !IsNaN(aTiming.mIterations),
  90. "mIterations should be nonnegative & finite, as ensured by "
  91. "ValidateIterations or CSSParser");
  92. result.mIterations = aTiming.mIterations;
  93. MOZ_ASSERT(aTiming.mIterationStart >= 0.0,
  94. "mIterationStart should be nonnegative, as ensured by "
  95. "ValidateIterationStart");
  96. result.mIterationStart = aTiming.mIterationStart;
  97. result.mActiveDuration = aTiming.ActiveDuration();
  98. result.mEndTime = aTiming.EndTime();
  99. result.mFill = aTiming.mFill == dom::FillMode::Auto ?
  100. dom::FillMode::None :
  101. aTiming.mFill;
  102. // The default constructor for ComputedTiming sets all other members to
  103. // values consistent with an animation that has not been sampled.
  104. if (aLocalTime.IsNull()) {
  105. return result;
  106. }
  107. const TimeDuration& localTime = aLocalTime.Value();
  108. StickyTimeDuration beforeActiveBoundary =
  109. std::max(std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime),
  110. zeroDuration);
  111. StickyTimeDuration activeAfterBoundary =
  112. std::max(std::min(StickyTimeDuration(aTiming.mDelay +
  113. result.mActiveDuration),
  114. result.mEndTime),
  115. zeroDuration);
  116. if (localTime > activeAfterBoundary ||
  117. (aPlaybackRate >= 0 && localTime == activeAfterBoundary)) {
  118. result.mPhase = ComputedTiming::AnimationPhase::After;
  119. if (!result.FillsForwards()) {
  120. // The animation isn't active or filling at this time.
  121. return result;
  122. }
  123. result.mActiveTime =
  124. std::max(std::min(StickyTimeDuration(localTime - aTiming.mDelay),
  125. result.mActiveDuration),
  126. zeroDuration);
  127. } else if (localTime < beforeActiveBoundary ||
  128. (aPlaybackRate < 0 && localTime == beforeActiveBoundary)) {
  129. result.mPhase = ComputedTiming::AnimationPhase::Before;
  130. if (!result.FillsBackwards()) {
  131. // The animation isn't active or filling at this time.
  132. return result;
  133. }
  134. result.mActiveTime
  135. = std::max(StickyTimeDuration(localTime - aTiming.mDelay),
  136. zeroDuration);
  137. } else {
  138. MOZ_ASSERT(result.mActiveDuration != zeroDuration,
  139. "How can we be in the middle of a zero-duration interval?");
  140. result.mPhase = ComputedTiming::AnimationPhase::Active;
  141. result.mActiveTime = localTime - aTiming.mDelay;
  142. }
  143. // Convert active time to a multiple of iterations.
  144. // https://w3c.github.io/web-animations/#overall-progress
  145. double overallProgress;
  146. if (result.mDuration == zeroDuration) {
  147. overallProgress = result.mPhase == ComputedTiming::AnimationPhase::Before
  148. ? 0.0
  149. : result.mIterations;
  150. } else {
  151. overallProgress = result.mActiveTime / result.mDuration;
  152. }
  153. // Factor in iteration start offset.
  154. if (IsFinite(overallProgress)) {
  155. overallProgress += result.mIterationStart;
  156. }
  157. // Determine the 0-based index of the current iteration.
  158. // https://w3c.github.io/web-animations/#current-iteration
  159. result.mCurrentIteration =
  160. IsInfinite(result.mIterations) &&
  161. result.mPhase == ComputedTiming::AnimationPhase::After
  162. ? UINT64_MAX // In GetComputedTimingDictionary(),
  163. // we will convert this into Infinity
  164. : static_cast<uint64_t>(overallProgress);
  165. // Convert the overall progress to a fraction of a single iteration--the
  166. // simply iteration progress.
  167. // https://w3c.github.io/web-animations/#simple-iteration-progress
  168. double progress = IsFinite(overallProgress)
  169. ? fmod(overallProgress, 1.0)
  170. : fmod(result.mIterationStart, 1.0);
  171. // When we finish exactly at the end of an iteration we need to report
  172. // the end of the final iteration and not the start of the next iteration.
  173. // We *don't* want to do this when we have a zero-iteration animation or
  174. // when the animation has been effectively made into a zero-duration animation
  175. // using a negative end-delay, however.
  176. if (result.mPhase == ComputedTiming::AnimationPhase::After &&
  177. progress == 0.0 &&
  178. result.mIterations != 0.0 &&
  179. (result.mActiveTime != zeroDuration ||
  180. result.mDuration == zeroDuration)) {
  181. // The only way we can be in the after phase with a progress of zero and
  182. // a current iteration of zero, is if we have a zero iteration count or
  183. // were clipped using a negative end delay--both of which we should have
  184. // detected above.
  185. MOZ_ASSERT(result.mCurrentIteration != 0,
  186. "Should not have zero current iteration");
  187. progress = 1.0;
  188. if (result.mCurrentIteration != UINT64_MAX) {
  189. result.mCurrentIteration--;
  190. }
  191. }
  192. // Factor in the direction.
  193. bool thisIterationReverse = false;
  194. switch (aTiming.mDirection) {
  195. case PlaybackDirection::Normal:
  196. thisIterationReverse = false;
  197. break;
  198. case PlaybackDirection::Reverse:
  199. thisIterationReverse = true;
  200. break;
  201. case PlaybackDirection::Alternate:
  202. thisIterationReverse = (result.mCurrentIteration & 1) == 1;
  203. break;
  204. case PlaybackDirection::Alternate_reverse:
  205. thisIterationReverse = (result.mCurrentIteration & 1) == 0;
  206. break;
  207. default:
  208. MOZ_ASSERT(true, "Unknown PlaybackDirection type");
  209. }
  210. if (thisIterationReverse) {
  211. progress = 1.0 - progress;
  212. }
  213. // Calculate the 'before flag' which we use when applying step timing
  214. // functions.
  215. if ((result.mPhase == ComputedTiming::AnimationPhase::After &&
  216. thisIterationReverse) ||
  217. (result.mPhase == ComputedTiming::AnimationPhase::Before &&
  218. !thisIterationReverse)) {
  219. result.mBeforeFlag = ComputedTimingFunction::BeforeFlag::Set;
  220. }
  221. // Apply the easing.
  222. if (aTiming.mFunction) {
  223. progress = aTiming.mFunction->GetValue(progress, result.mBeforeFlag);
  224. }
  225. MOZ_ASSERT(IsFinite(progress), "Progress value should be finite");
  226. result.mProgress.SetValue(progress);
  227. return result;
  228. }
  229. ComputedTiming
  230. AnimationEffectReadOnly::GetComputedTiming(const TimingParams* aTiming) const
  231. {
  232. double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
  233. return GetComputedTimingAt(GetLocalTime(),
  234. aTiming ? *aTiming : SpecifiedTiming(),
  235. playbackRate);
  236. }
  237. // Helper functions for generating a ComputedTimingProperties dictionary
  238. static void
  239. GetComputedTimingDictionary(const ComputedTiming& aComputedTiming,
  240. const Nullable<TimeDuration>& aLocalTime,
  241. const TimingParams& aTiming,
  242. ComputedTimingProperties& aRetVal)
  243. {
  244. // AnimationEffectTimingProperties
  245. aRetVal.mDelay = aTiming.mDelay.ToMilliseconds();
  246. aRetVal.mEndDelay = aTiming.mEndDelay.ToMilliseconds();
  247. aRetVal.mFill = aComputedTiming.mFill;
  248. aRetVal.mIterations = aComputedTiming.mIterations;
  249. aRetVal.mIterationStart = aComputedTiming.mIterationStart;
  250. aRetVal.mDuration.SetAsUnrestrictedDouble() =
  251. aComputedTiming.mDuration.ToMilliseconds();
  252. aRetVal.mDirection = aTiming.mDirection;
  253. // ComputedTimingProperties
  254. aRetVal.mActiveDuration = aComputedTiming.mActiveDuration.ToMilliseconds();
  255. aRetVal.mEndTime = aComputedTiming.mEndTime.ToMilliseconds();
  256. aRetVal.mLocalTime = AnimationUtils::TimeDurationToDouble(aLocalTime);
  257. aRetVal.mProgress = aComputedTiming.mProgress;
  258. if (!aRetVal.mProgress.IsNull()) {
  259. // Convert the returned currentIteration into Infinity if we set
  260. // (uint64_t) aComputedTiming.mCurrentIteration to UINT64_MAX
  261. double iteration = aComputedTiming.mCurrentIteration == UINT64_MAX
  262. ? PositiveInfinity<double>()
  263. : static_cast<double>(aComputedTiming.mCurrentIteration);
  264. aRetVal.mCurrentIteration.SetValue(iteration);
  265. }
  266. }
  267. void
  268. AnimationEffectReadOnly::GetComputedTimingAsDict(
  269. ComputedTimingProperties& aRetVal) const
  270. {
  271. double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
  272. const Nullable<TimeDuration> currentTime = GetLocalTime();
  273. GetComputedTimingDictionary(GetComputedTimingAt(currentTime,
  274. SpecifiedTiming(),
  275. playbackRate),
  276. currentTime,
  277. SpecifiedTiming(),
  278. aRetVal);
  279. }
  280. AnimationEffectReadOnly::~AnimationEffectReadOnly()
  281. {
  282. // mTiming is cycle collected, so we have to do null check first even though
  283. // mTiming shouldn't be null during the lifetime of KeyframeEffect.
  284. if (mTiming) {
  285. mTiming->Unlink();
  286. }
  287. }
  288. Nullable<TimeDuration>
  289. AnimationEffectReadOnly::GetLocalTime() const
  290. {
  291. // Since the *animation* start time is currently always zero, the local
  292. // time is equal to the parent time.
  293. Nullable<TimeDuration> result;
  294. if (mAnimation) {
  295. result = mAnimation->GetCurrentTime();
  296. }
  297. return result;
  298. }
  299. } // namespace dom
  300. } // namespace mozilla