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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "PendingAnimationTracker.h"
8
9 #include "mozilla/dom/AnimationTimeline.h"
10 #include "nsIFrame.h"
11 #include "nsIPresShell.h"
12
13 using namespace mozilla;
14
15 namespace mozilla {
16
NS_IMPL_CYCLE_COLLECTION(PendingAnimationTracker,mPlayPendingSet,mPausePendingSet,mDocument)17 NS_IMPL_CYCLE_COLLECTION(PendingAnimationTracker,
18 mPlayPendingSet,
19 mPausePendingSet,
20 mDocument)
21
22 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PendingAnimationTracker, AddRef)
23 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PendingAnimationTracker, Release)
24
25 void
26 PendingAnimationTracker::AddPending(dom::Animation& aAnimation,
27 AnimationSet& aSet)
28 {
29 aSet.PutEntry(&aAnimation);
30
31 // Schedule a paint. Otherwise animations that don't trigger a paint by
32 // themselves (e.g. CSS animations with an empty keyframes rule) won't
33 // start until something else paints.
34 EnsurePaintIsScheduled();
35 }
36
37 void
RemovePending(dom::Animation & aAnimation,AnimationSet & aSet)38 PendingAnimationTracker::RemovePending(dom::Animation& aAnimation,
39 AnimationSet& aSet)
40 {
41 aSet.RemoveEntry(&aAnimation);
42 }
43
44 bool
IsWaiting(const dom::Animation & aAnimation,const AnimationSet & aSet) const45 PendingAnimationTracker::IsWaiting(const dom::Animation& aAnimation,
46 const AnimationSet& aSet) const
47 {
48 return aSet.Contains(const_cast<dom::Animation*>(&aAnimation));
49 }
50
51 void
TriggerPendingAnimationsOnNextTick(const TimeStamp & aReadyTime)52 PendingAnimationTracker::TriggerPendingAnimationsOnNextTick(const TimeStamp&
53 aReadyTime)
54 {
55 auto triggerAnimationsAtReadyTime = [aReadyTime](AnimationSet& aAnimationSet)
56 {
57 for (auto iter = aAnimationSet.Iter(); !iter.Done(); iter.Next()) {
58 dom::Animation* animation = iter.Get()->GetKey();
59 dom::AnimationTimeline* timeline = animation->GetTimeline();
60
61 // If the animation does not have a timeline, just drop it from the map.
62 // The animation will detect that it is not being tracked and will trigger
63 // itself on the next tick where it has a timeline.
64 if (!timeline) {
65 iter.Remove();
66 continue;
67 }
68
69 // When the timeline's refresh driver is under test control, its values
70 // have no correspondance to wallclock times so we shouldn't try to
71 // convert aReadyTime (which is a wallclock time) to a timeline value.
72 // Instead, the animation will be started/paused when the refresh driver
73 // is next advanced since this will trigger a call to
74 // TriggerPendingAnimationsNow.
75 if (!timeline->TracksWallclockTime()) {
76 continue;
77 }
78
79 Nullable<TimeDuration> readyTime = timeline->ToTimelineTime(aReadyTime);
80 animation->TriggerOnNextTick(readyTime);
81
82 iter.Remove();
83 }
84 };
85
86 triggerAnimationsAtReadyTime(mPlayPendingSet);
87 triggerAnimationsAtReadyTime(mPausePendingSet);
88 }
89
90 void
TriggerPendingAnimationsNow()91 PendingAnimationTracker::TriggerPendingAnimationsNow()
92 {
93 auto triggerAndClearAnimations = [](AnimationSet& aAnimationSet) {
94 for (auto iter = aAnimationSet.Iter(); !iter.Done(); iter.Next()) {
95 iter.Get()->GetKey()->TriggerNow();
96 }
97 aAnimationSet.Clear();
98 };
99
100 triggerAndClearAnimations(mPlayPendingSet);
101 triggerAndClearAnimations(mPausePendingSet);
102 }
103
104 void
EnsurePaintIsScheduled()105 PendingAnimationTracker::EnsurePaintIsScheduled()
106 {
107 if (!mDocument) {
108 return;
109 }
110
111 nsIPresShell* presShell = mDocument->GetShell();
112 if (!presShell) {
113 return;
114 }
115
116 nsIFrame* rootFrame = presShell->GetRootFrame();
117 if (!rootFrame) {
118 return;
119 }
120
121 rootFrame->SchedulePaint();
122 }
123
124 } // namespace mozilla
125