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