Line data Source code
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 :
17 0 : NS_IMPL_CYCLE_COLLECTION(PendingAnimationTracker,
18 : mPlayPendingSet,
19 : mPausePendingSet,
20 : mDocument)
21 :
22 0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PendingAnimationTracker, AddRef)
23 0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PendingAnimationTracker, Release)
24 :
25 : void
26 2 : PendingAnimationTracker::AddPending(dom::Animation& aAnimation,
27 : AnimationSet& aSet)
28 : {
29 2 : 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 2 : EnsurePaintIsScheduled();
35 2 : }
36 :
37 : void
38 0 : PendingAnimationTracker::RemovePending(dom::Animation& aAnimation,
39 : AnimationSet& aSet)
40 : {
41 0 : aSet.RemoveEntry(&aAnimation);
42 0 : }
43 :
44 : bool
45 20 : PendingAnimationTracker::IsWaiting(const dom::Animation& aAnimation,
46 : const AnimationSet& aSet) const
47 : {
48 20 : return aSet.Contains(const_cast<dom::Animation*>(&aAnimation));
49 : }
50 :
51 : void
52 24 : PendingAnimationTracker::TriggerPendingAnimationsOnNextTick(const TimeStamp&
53 : aReadyTime)
54 : {
55 48 : auto triggerAnimationsAtReadyTime = [aReadyTime](AnimationSet& aAnimationSet)
56 : {
57 50 : for (auto iter = aAnimationSet.Iter(); !iter.Done(); iter.Next()) {
58 2 : dom::Animation* animation = iter.Get()->GetKey();
59 2 : 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 2 : if (!timeline) {
65 0 : iter.Remove();
66 0 : 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 2 : if (!timeline->TracksWallclockTime()) {
76 0 : continue;
77 : }
78 :
79 4 : Nullable<TimeDuration> readyTime = timeline->ToTimelineTime(aReadyTime);
80 2 : animation->TriggerOnNextTick(readyTime);
81 :
82 2 : iter.Remove();
83 : }
84 72 : };
85 :
86 24 : triggerAnimationsAtReadyTime(mPlayPendingSet);
87 24 : triggerAnimationsAtReadyTime(mPausePendingSet);
88 :
89 48 : mHasPlayPendingGeometricAnimations = mPlayPendingSet.Count()
90 24 : ? CheckState::Indeterminate
91 : : CheckState::Absent;
92 24 : }
93 :
94 : void
95 0 : PendingAnimationTracker::TriggerPendingAnimationsNow()
96 : {
97 0 : auto triggerAndClearAnimations = [](AnimationSet& aAnimationSet) {
98 0 : for (auto iter = aAnimationSet.Iter(); !iter.Done(); iter.Next()) {
99 0 : iter.Get()->GetKey()->TriggerNow();
100 : }
101 0 : aAnimationSet.Clear();
102 0 : };
103 :
104 0 : triggerAndClearAnimations(mPlayPendingSet);
105 0 : triggerAndClearAnimations(mPausePendingSet);
106 :
107 0 : mHasPlayPendingGeometricAnimations = CheckState::Absent;
108 0 : }
109 :
110 : void
111 0 : PendingAnimationTracker::MarkAnimationsThatMightNeedSynchronization()
112 : {
113 : // We only ever set mHasPlayPendingGeometricAnimations to 'present' in
114 : // HasPlayPendingGeometricAnimations(). So, if it is 'present' already,
115 : // (i.e. before calling HasPlayPendingGeometricAnimations()) we can assume
116 : // that this method has already been called for the current set of
117 : // play-pending animations and it is not necessary to run again.
118 : //
119 : // We can't make the same assumption about 'absent', but if this method
120 : // was already called and the result was 'absent', then this method is
121 : // a no-op anyway so it's ok to run again.
122 : //
123 : // Note that *without* this optimization, starting animations would become
124 : // O(n^2) in that case where each animation is on a different element and
125 : // contains a compositor-animatable property since we would end up iterating
126 : // over all animations in the play-pending set for each target element.
127 0 : if (mHasPlayPendingGeometricAnimations == CheckState::Present) {
128 0 : return;
129 : }
130 :
131 0 : if (!HasPlayPendingGeometricAnimations()) {
132 0 : return;
133 : }
134 :
135 0 : for (auto iter = mPlayPendingSet.Iter(); !iter.Done(); iter.Next()) {
136 0 : iter.Get()->GetKey()->NotifyGeometricAnimationsStartingThisFrame();
137 : }
138 : }
139 :
140 : bool
141 0 : PendingAnimationTracker::HasPlayPendingGeometricAnimations()
142 : {
143 0 : if (mHasPlayPendingGeometricAnimations != CheckState::Indeterminate) {
144 0 : return mHasPlayPendingGeometricAnimations == CheckState::Present;
145 : }
146 :
147 0 : mHasPlayPendingGeometricAnimations = CheckState::Absent;
148 0 : for (auto iter = mPlayPendingSet.ConstIter(); !iter.Done(); iter.Next()) {
149 0 : auto animation = iter.Get()->GetKey();
150 0 : if (animation->GetEffect() && animation->GetEffect()->AffectsGeometry()) {
151 0 : mHasPlayPendingGeometricAnimations = CheckState::Present;
152 0 : break;
153 : }
154 : }
155 :
156 0 : return mHasPlayPendingGeometricAnimations == CheckState::Present;
157 : }
158 :
159 : void
160 2 : PendingAnimationTracker::EnsurePaintIsScheduled()
161 : {
162 2 : if (!mDocument) {
163 0 : return;
164 : }
165 :
166 2 : nsIPresShell* presShell = mDocument->GetShell();
167 2 : if (!presShell) {
168 0 : return;
169 : }
170 :
171 2 : nsIFrame* rootFrame = presShell->GetRootFrame();
172 2 : if (!rootFrame) {
173 0 : return;
174 : }
175 :
176 2 : rootFrame->SchedulePaint();
177 : }
178 :
179 : } // namespace mozilla
|