Bug 1073336 part 13 - Add AnimationPlayer::PostUpdate; r=dbaron
[gecko.git] / dom / animation / AnimationPlayer.cpp
blobfd824c81d4d1c85897d117e47ac7779f423f4122
1 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
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
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "AnimationPlayer.h"
7 #include "AnimationUtils.h"
8 #include "mozilla/dom/AnimationPlayerBinding.h"
9 #include "AnimationCommon.h" // For AnimationPlayerCollection,
10 // CommonAnimationManager
11 #include "nsIDocument.h" // For nsIDocument
12 #include "nsIPresShell.h" // For nsIPresShell
13 #include "nsLayoutUtils.h" // For PostRestyleEvent (remove after bug 1073336)
15 namespace mozilla {
16 namespace dom {
18 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline, mSource)
20 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationPlayer, AddRef)
21 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationPlayer, Release)
23 JSObject*
24 AnimationPlayer::WrapObject(JSContext* aCx)
26 return dom::AnimationPlayerBinding::Wrap(aCx, this);
29 Nullable<double>
30 AnimationPlayer::GetStartTime() const
32 return AnimationUtils::TimeDurationToDouble(mStartTime);
35 Nullable<TimeDuration>
36 AnimationPlayer::GetCurrentTime() const
38 Nullable<TimeDuration> result;
39 if (!mHoldTime.IsNull()) {
40 result = mHoldTime;
41 } else {
42 Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
43 if (!timelineTime.IsNull() && !mStartTime.IsNull()) {
44 result.SetValue(timelineTime.Value() - mStartTime.Value());
47 return result;
50 AnimationPlayState
51 AnimationPlayer::PlayState() const
53 Nullable<TimeDuration> currentTime = GetCurrentTime();
54 if (currentTime.IsNull()) {
55 return AnimationPlayState::Idle;
58 if (mIsPaused) {
59 return AnimationPlayState::Paused;
62 if (currentTime.Value() >= SourceContentEnd()) {
63 return AnimationPlayState::Finished;
66 return AnimationPlayState::Running;
69 void
70 AnimationPlayer::Play(UpdateFlags aFlags)
72 // FIXME: When we implement finishing behavior (bug 1074630) we should
73 // not return early if mIsPaused is false since we may still need to seek.
74 // (However, we will need to pass a flag so that when we start playing due to
75 // a change in animation-play-state we *don't* trigger finishing behavior.)
76 if (!mIsPaused) {
77 return;
79 mIsPaused = false;
81 Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
82 if (timelineTime.IsNull()) {
83 // FIXME: We should just sit in the pending state in this case.
84 // We will introduce the pending state in Bug 927349.
85 return;
88 // Update start time to an appropriate offset from the current timeline time
89 MOZ_ASSERT(!mHoldTime.IsNull(), "Hold time should not be null when paused");
90 mStartTime.SetValue(timelineTime.Value() - mHoldTime.Value());
91 mHoldTime.SetNull();
93 if (aFlags == eUpdateStyle) {
94 MaybePostRestyle();
98 void
99 AnimationPlayer::Pause(UpdateFlags aFlags)
101 if (mIsPaused) {
102 return;
104 mIsPaused = true;
105 mIsRunningOnCompositor = false;
107 // Bug 927349 - check for null result here and go to pending state
108 mHoldTime = GetCurrentTime();
109 mStartTime.SetNull();
111 if (aFlags == eUpdateStyle) {
112 MaybePostRestyle();
116 Nullable<double>
117 AnimationPlayer::GetCurrentTimeAsDouble() const
119 return AnimationUtils::TimeDurationToDouble(GetCurrentTime());
122 void
123 AnimationPlayer::PlayFromJS()
125 Play(eUpdateStyle);
128 void
129 AnimationPlayer::PauseFromJS()
131 Pause(eUpdateStyle);
134 void
135 AnimationPlayer::SetSource(Animation* aSource)
137 if (mSource) {
138 mSource->SetParentTime(Nullable<TimeDuration>());
140 mSource = aSource;
141 if (mSource) {
142 mSource->SetParentTime(GetCurrentTime());
146 void
147 AnimationPlayer::Tick()
149 if (mSource) {
150 mSource->SetParentTime(GetCurrentTime());
154 bool
155 AnimationPlayer::IsRunning() const
157 if (IsPaused() || !GetSource() || GetSource()->IsFinishedTransition()) {
158 return false;
161 ComputedTiming computedTiming = GetSource()->GetComputedTiming();
162 return computedTiming.mPhase == ComputedTiming::AnimationPhase_Active;
165 bool
166 AnimationPlayer::CanThrottle() const
168 if (!mSource ||
169 mSource->IsFinishedTransition() ||
170 mSource->Properties().IsEmpty()) {
171 return true;
174 if (!mIsRunningOnCompositor) {
175 return false;
178 if (PlayState() != AnimationPlayState::Finished) {
179 // Unfinished animations can be throttled.
180 return true;
183 // The animation has finished but, if this is the first sample since
184 // finishing, we need an unthrottled sample so we can apply the correct
185 // end-of-animation behavior on the main thread (either removing the
186 // animation style or applying the fill mode).
187 return mIsPreviousStateFinished;
190 void
191 AnimationPlayer::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
192 nsCSSPropertySet& aSetProperties,
193 bool& aNeedsRefreshes)
195 if (!mSource || mSource->IsFinishedTransition()) {
196 return;
199 AnimationPlayState playState = PlayState();
200 if (playState == AnimationPlayState::Running) {
201 aNeedsRefreshes = true;
204 mSource->ComposeStyle(aStyleRule, aSetProperties);
206 mIsPreviousStateFinished = (playState == AnimationPlayState::Finished);
209 void
210 AnimationPlayer::FlushStyle() const
212 nsIDocument* doc = GetRenderedDocument();
213 if (doc) {
214 doc->FlushPendingNotifications(Flush_Style);
218 void
219 AnimationPlayer::MaybePostRestyle() const
221 if (!mSource) {
222 return;
225 Element* targetElement;
226 nsCSSPseudoElements::Type pseudoType;
227 mSource->GetTarget(targetElement, pseudoType);
228 if (!targetElement) {
229 return;
232 // FIXME: This is a bit heavy-handed but in bug 1073336 we hope to
233 // introduce a better means for players to update style.
234 nsLayoutUtils::PostRestyleEvent(targetElement,
235 eRestyle_Self,
236 nsChangeHint_AllReflowHints);
239 void
240 AnimationPlayer::PostUpdate()
242 AnimationPlayerCollection* collection = GetCollection();
243 if (collection) {
244 collection->NotifyPlayerUpdated();
248 StickyTimeDuration
249 AnimationPlayer::SourceContentEnd() const
251 if (!mSource) {
252 return StickyTimeDuration(0);
255 return mSource->Timing().mDelay
256 + mSource->GetComputedTiming().mActiveDuration;
259 nsIDocument*
260 AnimationPlayer::GetRenderedDocument() const
262 if (!mSource) {
263 return nullptr;
266 Element* targetElement;
267 nsCSSPseudoElements::Type pseudoType;
268 mSource->GetTarget(targetElement, pseudoType);
269 if (!targetElement) {
270 return nullptr;
273 return targetElement->GetComposedDoc();
276 nsPresContext*
277 AnimationPlayer::GetPresContext() const
279 nsIDocument* doc = GetRenderedDocument();
280 if (!doc) {
281 return nullptr;
283 nsIPresShell* shell = doc->GetShell();
284 if (!shell) {
285 return nullptr;
287 return shell->GetPresContext();
290 AnimationPlayerCollection*
291 AnimationPlayer::GetCollection() const
293 css::CommonAnimationManager* manager = GetAnimationManager();
294 if (!manager) {
295 return nullptr;
297 MOZ_ASSERT(mSource, "A player with an animation manager must have a source");
299 Element* targetElement;
300 nsCSSPseudoElements::Type targetPseudoType;
301 mSource->GetTarget(targetElement, targetPseudoType);
302 MOZ_ASSERT(targetElement,
303 "A player with an animation manager must have a target");
305 return manager->GetAnimationPlayers(targetElement, targetPseudoType, false);
308 } // namespace dom
309 } // namespace mozilla