Bumping manifests a=b2g-bump
[gecko.git] / layout / style / nsAnimationManager.cpp
blobd85f0beb0c172138ba41cd95a29e38c57928a1a1
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 "nsAnimationManager.h"
7 #include "nsTransitionManager.h"
9 #include "mozilla/EventDispatcher.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/StyleAnimationValue.h"
13 #include "nsPresContext.h"
14 #include "nsStyleSet.h"
15 #include "nsStyleChangeList.h"
16 #include "nsCSSRules.h"
17 #include "RestyleManager.h"
18 #include "nsLayoutUtils.h"
19 #include "nsIFrame.h"
20 #include "nsIDocument.h"
21 #include <math.h>
23 using namespace mozilla;
24 using namespace mozilla::css;
25 using mozilla::dom::Animation;
26 using mozilla::dom::AnimationPlayer;
27 using mozilla::CSSAnimationPlayer;
29 mozilla::dom::Promise*
30 CSSAnimationPlayer::GetReady(ErrorResult& aRv)
32 FlushStyle();
33 return AnimationPlayer::GetReady(aRv);
36 void
37 CSSAnimationPlayer::Play()
39 mPauseShouldStick = false;
40 AnimationPlayer::Play();
43 void
44 CSSAnimationPlayer::Pause()
46 mPauseShouldStick = true;
47 AnimationPlayer::Pause();
50 mozilla::dom::AnimationPlayState
51 CSSAnimationPlayer::PlayStateFromJS() const
53 // Flush style to ensure that any properties controlling animation state
54 // (e.g. animation-play-state) are fully updated.
55 FlushStyle();
56 return AnimationPlayer::PlayStateFromJS();
59 void
60 CSSAnimationPlayer::PlayFromJS()
62 // Note that flushing style below might trigger calls to
63 // PlayFromStyle()/PauseFromStyle() on this object.
64 FlushStyle();
65 AnimationPlayer::PlayFromJS();
68 void
69 CSSAnimationPlayer::PlayFromStyle()
71 mIsStylePaused = false;
72 if (!mPauseShouldStick) {
73 DoPlay();
77 void
78 CSSAnimationPlayer::PauseFromStyle()
80 // Check if the pause state is being overridden
81 if (mIsStylePaused) {
82 return;
85 mIsStylePaused = true;
86 DoPause();
89 void
90 CSSAnimationPlayer::QueueEvents(EventArray& aEventsToDispatch)
92 if (!mSource) {
93 return;
96 ComputedTiming computedTiming = mSource->GetComputedTiming();
98 dom::Element* target;
99 nsCSSPseudoElements::Type targetPseudoType;
100 mSource->GetTarget(target, targetPseudoType);
102 switch (computedTiming.mPhase) {
103 case ComputedTiming::AnimationPhase_Null:
104 case ComputedTiming::AnimationPhase_Before:
105 // Do nothing
106 break;
108 case ComputedTiming::AnimationPhase_Active:
109 // Dispatch 'animationstart' or 'animationiteration' when needed.
110 if (computedTiming.mCurrentIteration != mLastNotification) {
111 // Notify 'animationstart' even if a negative delay puts us
112 // past the first iteration.
113 // Note that when somebody changes the animation-duration
114 // dynamically, this will fire an extra iteration event
115 // immediately in many cases. It's not clear to me if that's the
116 // right thing to do.
117 uint32_t message = mLastNotification == LAST_NOTIFICATION_NONE
118 ? NS_ANIMATION_START
119 : NS_ANIMATION_ITERATION;
120 mLastNotification = computedTiming.mCurrentIteration;
121 TimeDuration iterationStart =
122 mSource->Timing().mIterationDuration *
123 computedTiming.mCurrentIteration;
124 TimeDuration elapsedTime =
125 std::max(iterationStart, mSource->InitialAdvance());
126 AnimationEventInfo ei(target, Name(), message,
127 StickyTimeDuration(elapsedTime),
128 PseudoTypeAsString(targetPseudoType));
129 aEventsToDispatch.AppendElement(ei);
131 break;
133 case ComputedTiming::AnimationPhase_After:
134 // If we skipped the animation interval entirely, dispatch
135 // 'animationstart' first
136 if (mLastNotification == LAST_NOTIFICATION_NONE) {
137 // Notifying for start of 0th iteration.
138 // (This is overwritten below but we set it here to maintain
139 // internal consistency.)
140 mLastNotification = 0;
141 StickyTimeDuration elapsedTime =
142 std::min(StickyTimeDuration(mSource->InitialAdvance()),
143 computedTiming.mActiveDuration);
144 AnimationEventInfo ei(target, Name(), NS_ANIMATION_START,
145 elapsedTime,
146 PseudoTypeAsString(targetPseudoType));
147 aEventsToDispatch.AppendElement(ei);
149 // Dispatch 'animationend' when needed.
150 if (mLastNotification != LAST_NOTIFICATION_END) {
151 mLastNotification = LAST_NOTIFICATION_END;
152 AnimationEventInfo ei(target, Name(), NS_ANIMATION_END,
153 computedTiming.mActiveDuration,
154 PseudoTypeAsString(targetPseudoType));
155 aEventsToDispatch.AppendElement(ei);
157 break;
161 CommonAnimationManager*
162 CSSAnimationPlayer::GetAnimationManager() const
164 nsPresContext* context = GetPresContext();
165 if (!context) {
166 return nullptr;
169 return context->AnimationManager();
172 /* static */ nsString
173 CSSAnimationPlayer::PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType)
175 switch (aPseudoType) {
176 case nsCSSPseudoElements::ePseudo_before:
177 return NS_LITERAL_STRING("::before");
178 case nsCSSPseudoElements::ePseudo_after:
179 return NS_LITERAL_STRING("::after");
180 default:
181 return EmptyString();
185 void
186 nsAnimationManager::UpdateStyleAndEvents(AnimationPlayerCollection*
187 aCollection,
188 TimeStamp aRefreshTime,
189 EnsureStyleRuleFlags aFlags)
191 aCollection->EnsureStyleRuleFor(aRefreshTime, aFlags);
192 QueueEvents(aCollection, mPendingEvents);
195 void
196 nsAnimationManager::QueueEvents(AnimationPlayerCollection* aCollection,
197 EventArray& aEventsToDispatch)
199 for (size_t playerIdx = aCollection->mPlayers.Length(); playerIdx-- != 0; ) {
200 CSSAnimationPlayer* player =
201 aCollection->mPlayers[playerIdx]->AsCSSAnimationPlayer();
202 MOZ_ASSERT(player, "Expected a collection of CSS Animation players");
203 player->QueueEvents(aEventsToDispatch);
207 /* virtual */ size_t
208 nsAnimationManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
210 return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf);
212 // Measurement of the following members may be added later if DMD finds it is
213 // worthwhile:
214 // - mPendingEvents
217 /* virtual */ size_t
218 nsAnimationManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
220 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
223 nsIStyleRule*
224 nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
225 mozilla::dom::Element* aElement)
227 // FIXME (bug 960465): This test should go away.
228 if (!mPresContext->RestyleManager()->IsProcessingAnimationStyleChange()) {
229 if (!mPresContext->IsDynamic()) {
230 // For print or print preview, ignore animations.
231 return nullptr;
234 // Everything that causes our animation data to change triggers a
235 // style change, which in turn triggers a non-animation restyle.
236 // Likewise, when we initially construct frames, we're not in a
237 // style change, but also not in an animation restyle.
239 const nsStyleDisplay* disp = aStyleContext->StyleDisplay();
240 AnimationPlayerCollection* collection =
241 GetAnimationPlayers(aElement, aStyleContext->GetPseudoType(), false);
242 if (!collection &&
243 disp->mAnimationNameCount == 1 &&
244 disp->mAnimations[0].GetName().IsEmpty()) {
245 return nullptr;
248 // build the animations list
249 dom::AnimationTimeline* timeline = aElement->OwnerDoc()->Timeline();
250 AnimationPlayerPtrArray newPlayers;
251 BuildAnimations(aStyleContext, aElement, timeline, newPlayers);
253 if (newPlayers.IsEmpty()) {
254 if (collection) {
255 collection->Destroy();
257 return nullptr;
260 if (collection) {
261 collection->mStyleRule = nullptr;
262 collection->mStyleRuleRefreshTime = TimeStamp();
263 collection->UpdateAnimationGeneration(mPresContext);
265 // Copy over the start times and (if still paused) pause starts
266 // for each animation (matching on name only) that was also in the
267 // old list of animations.
268 // This means that we honor dynamic changes, which isn't what the
269 // spec says to do, but WebKit seems to honor at least some of
270 // them. See
271 // http://lists.w3.org/Archives/Public/www-style/2011Apr/0079.html
272 // In order to honor what the spec said, we'd copy more data over
273 // (or potentially optimize BuildAnimations to avoid rebuilding it
274 // in the first place).
275 if (!collection->mPlayers.IsEmpty()) {
277 for (size_t newIdx = newPlayers.Length(); newIdx-- != 0;) {
278 AnimationPlayer* newPlayer = newPlayers[newIdx];
280 // Find the matching animation with this name in the old list
281 // of animations. We iterate through both lists in a backwards
282 // direction which means that if there are more animations in
283 // the new list of animations with a given name than in the old
284 // list, it will be the animations towards the of the beginning of
285 // the list that do not match and are treated as new animations.
286 nsRefPtr<CSSAnimationPlayer> oldPlayer;
287 size_t oldIdx = collection->mPlayers.Length();
288 while (oldIdx-- != 0) {
289 CSSAnimationPlayer* a =
290 collection->mPlayers[oldIdx]->AsCSSAnimationPlayer();
291 MOZ_ASSERT(a, "All players in the CSS Animation collection should"
292 " be CSSAnimationPlayer objects");
293 if (a->Name() == newPlayer->Name()) {
294 oldPlayer = a;
295 break;
298 if (!oldPlayer) {
299 continue;
302 // Update the old from the new so we can keep the original object
303 // identity (and any expando properties attached to it).
304 if (oldPlayer->GetSource() && newPlayer->GetSource()) {
305 Animation* oldAnim = oldPlayer->GetSource();
306 Animation* newAnim = newPlayer->GetSource();
307 oldAnim->Timing() = newAnim->Timing();
308 oldAnim->Properties() = newAnim->Properties();
311 // Reset compositor state so animation will be re-synchronized.
312 oldPlayer->ClearIsRunningOnCompositor();
314 // Handle changes in play state.
315 // CSSAnimationPlayer takes care of override behavior so that,
316 // for example, if the author has called pause(), that will
317 // override the animation-play-state.
318 // (We should check newPlayer->IsStylePaused() but that requires
319 // downcasting to CSSAnimationPlayer and we happen to know that
320 // newPlayer will only ever be paused by calling PauseFromStyle
321 // making IsPaused synonymous in this case.)
322 if (!oldPlayer->IsStylePaused() && newPlayer->IsPaused()) {
323 oldPlayer->PauseFromStyle();
324 } else if (oldPlayer->IsStylePaused() && !newPlayer->IsPaused()) {
325 oldPlayer->PlayFromStyle();
328 // Replace new animation with the (updated) old one and remove the
329 // old one from the array so we don't try to match it any more.
331 // Although we're doing this while iterating this is safe because
332 // we're not changing the length of newPlayers and we've finished
333 // iterating over the list of old iterations.
334 newPlayer->Cancel();
335 newPlayer = nullptr;
336 newPlayers.ReplaceElementAt(newIdx, oldPlayer);
337 collection->mPlayers.RemoveElementAt(oldIdx);
340 } else {
341 collection =
342 GetAnimationPlayers(aElement, aStyleContext->GetPseudoType(), true);
344 collection->mPlayers.SwapElements(newPlayers);
345 collection->mNeedsRefreshes = true;
346 collection->Tick();
348 // Cancel removed animations
349 for (size_t newPlayerIdx = newPlayers.Length(); newPlayerIdx-- != 0; ) {
350 newPlayers[newPlayerIdx]->Cancel();
353 TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
354 UpdateStyleAndEvents(collection, refreshTime,
355 EnsureStyleRule_IsNotThrottled);
356 // We don't actually dispatch the mPendingEvents now. We'll either
357 // dispatch them the next time we get a refresh driver notification
358 // or the next time somebody calls
359 // nsPresShell::FlushPendingNotifications.
360 if (!mPendingEvents.IsEmpty()) {
361 mPresContext->Document()->SetNeedStyleFlush();
365 return GetAnimationRule(aElement, aStyleContext->GetPseudoType());
368 struct KeyframeData {
369 float mKey;
370 uint32_t mIndex; // store original order since sort algorithm is not stable
371 nsCSSKeyframeRule *mRule;
374 struct KeyframeDataComparator {
375 bool Equals(const KeyframeData& A, const KeyframeData& B) const {
376 return A.mKey == B.mKey && A.mIndex == B.mIndex;
378 bool LessThan(const KeyframeData& A, const KeyframeData& B) const {
379 return A.mKey < B.mKey || (A.mKey == B.mKey && A.mIndex < B.mIndex);
383 class ResolvedStyleCache {
384 public:
385 ResolvedStyleCache() : mCache() {}
386 nsStyleContext* Get(nsPresContext *aPresContext,
387 nsStyleContext *aParentStyleContext,
388 nsCSSKeyframeRule *aKeyframe);
390 private:
391 nsRefPtrHashtable<nsPtrHashKey<nsCSSKeyframeRule>, nsStyleContext> mCache;
394 nsStyleContext*
395 ResolvedStyleCache::Get(nsPresContext *aPresContext,
396 nsStyleContext *aParentStyleContext,
397 nsCSSKeyframeRule *aKeyframe)
399 // FIXME (spec): The css3-animations spec isn't very clear about how
400 // properties are resolved when they have values that depend on other
401 // properties (e.g., values in 'em'). I presume that they're resolved
402 // relative to the other styles of the element. The question is
403 // whether they are resolved relative to other animations: I assume
404 // that they're not, since that would prevent us from caching a lot of
405 // data that we'd really like to cache (in particular, the
406 // StyleAnimationValue values in AnimationPropertySegment).
407 nsStyleContext *result = mCache.GetWeak(aKeyframe);
408 if (!result) {
409 nsCOMArray<nsIStyleRule> rules;
410 rules.AppendObject(aKeyframe);
411 nsRefPtr<nsStyleContext> resultStrong = aPresContext->StyleSet()->
412 ResolveStyleByAddingRules(aParentStyleContext, rules);
413 mCache.Put(aKeyframe, resultStrong);
414 result = resultStrong;
416 return result;
419 void
420 nsAnimationManager::BuildAnimations(nsStyleContext* aStyleContext,
421 dom::Element* aTarget,
422 dom::AnimationTimeline* aTimeline,
423 AnimationPlayerPtrArray& aPlayers)
425 NS_ABORT_IF_FALSE(aPlayers.IsEmpty(), "expect empty array");
427 ResolvedStyleCache resolvedStyles;
429 const nsStyleDisplay *disp = aStyleContext->StyleDisplay();
431 for (size_t animIdx = 0, animEnd = disp->mAnimationNameCount;
432 animIdx != animEnd; ++animIdx) {
433 const StyleAnimation& src = disp->mAnimations[animIdx];
435 // CSS Animations whose animation-name does not match a @keyframes rule do
436 // not generate animation events. This includes when the animation-name is
437 // "none" which is represented by an empty name in the StyleAnimation.
438 // Since such animations neither affect style nor dispatch events, we do
439 // not generate a corresponding AnimationPlayer for them.
440 nsCSSKeyframesRule* rule =
441 src.GetName().IsEmpty()
442 ? nullptr
443 : mPresContext->StyleSet()->KeyframesRuleForName(mPresContext,
444 src.GetName());
445 if (!rule) {
446 continue;
449 nsRefPtr<CSSAnimationPlayer> dest = new CSSAnimationPlayer(aTimeline);
450 aPlayers.AppendElement(dest);
452 AnimationTiming timing;
453 timing.mIterationDuration =
454 TimeDuration::FromMilliseconds(src.GetDuration());
455 timing.mDelay = TimeDuration::FromMilliseconds(src.GetDelay());
456 timing.mIterationCount = src.GetIterationCount();
457 timing.mDirection = src.GetDirection();
458 timing.mFillMode = src.GetFillMode();
460 nsRefPtr<Animation> destAnim =
461 new Animation(mPresContext->Document(), aTarget,
462 aStyleContext->GetPseudoType(), timing, src.GetName());
463 dest->SetSource(destAnim);
465 // Even in the case where we call PauseFromStyle below, we still need to
466 // call PlayFromStyle first. This is because a newly-created player is idle
467 // and has no effect until it is played (or otherwise given a start time).
468 dest->PlayFromStyle();
470 if (src.GetPlayState() == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED) {
471 dest->PauseFromStyle();
474 // While current drafts of css3-animations say that later keyframes
475 // with the same key entirely replace earlier ones (no cascading),
476 // this is a bad idea and contradictory to the rest of CSS. So
477 // we're going to keep all the keyframes for each key and then do
478 // the replacement on a per-property basis rather than a per-rule
479 // basis, just like everything else in CSS.
481 AutoInfallibleTArray<KeyframeData, 16> sortedKeyframes;
483 for (uint32_t ruleIdx = 0, ruleEnd = rule->StyleRuleCount();
484 ruleIdx != ruleEnd; ++ruleIdx) {
485 css::Rule* cssRule = rule->GetStyleRuleAt(ruleIdx);
486 NS_ABORT_IF_FALSE(cssRule, "must have rule");
487 NS_ABORT_IF_FALSE(cssRule->GetType() == css::Rule::KEYFRAME_RULE,
488 "must be keyframe rule");
489 nsCSSKeyframeRule *kfRule = static_cast<nsCSSKeyframeRule*>(cssRule);
491 const nsTArray<float> &keys = kfRule->GetKeys();
492 for (uint32_t keyIdx = 0, keyEnd = keys.Length();
493 keyIdx != keyEnd; ++keyIdx) {
494 float key = keys[keyIdx];
495 // FIXME (spec): The spec doesn't say what to do with
496 // out-of-range keyframes. We'll ignore them.
497 if (0.0f <= key && key <= 1.0f) {
498 KeyframeData *data = sortedKeyframes.AppendElement();
499 data->mKey = key;
500 data->mIndex = ruleIdx;
501 data->mRule = kfRule;
506 sortedKeyframes.Sort(KeyframeDataComparator());
508 if (sortedKeyframes.Length() == 0) {
509 // no segments
510 continue;
513 // Record the properties that are present in any keyframe rules we
514 // are using.
515 nsCSSPropertySet properties;
517 for (uint32_t kfIdx = 0, kfEnd = sortedKeyframes.Length();
518 kfIdx != kfEnd; ++kfIdx) {
519 css::Declaration *decl = sortedKeyframes[kfIdx].mRule->Declaration();
520 for (uint32_t propIdx = 0, propEnd = decl->Count();
521 propIdx != propEnd; ++propIdx) {
522 nsCSSProperty prop = decl->GetPropertyAt(propIdx);
523 if (prop != eCSSPropertyExtra_variable) {
524 // CSS Variables are not animatable
525 properties.AddProperty(prop);
530 for (nsCSSProperty prop = nsCSSProperty(0);
531 prop < eCSSProperty_COUNT_no_shorthands;
532 prop = nsCSSProperty(prop + 1)) {
533 if (!properties.HasProperty(prop) ||
534 nsCSSProps::kAnimTypeTable[prop] == eStyleAnimType_None) {
535 continue;
538 // Build a list of the keyframes to use for this property. This
539 // means we need every keyframe with the property in it, except
540 // for those keyframes where a later keyframe with the *same key*
541 // also has the property.
542 AutoInfallibleTArray<uint32_t, 16> keyframesWithProperty;
543 float lastKey = 100.0f; // an invalid key
544 for (uint32_t kfIdx = 0, kfEnd = sortedKeyframes.Length();
545 kfIdx != kfEnd; ++kfIdx) {
546 KeyframeData &kf = sortedKeyframes[kfIdx];
547 if (!kf.mRule->Declaration()->HasProperty(prop)) {
548 continue;
550 if (kf.mKey == lastKey) {
551 // Replace previous occurrence of same key.
552 keyframesWithProperty[keyframesWithProperty.Length() - 1] = kfIdx;
553 } else {
554 keyframesWithProperty.AppendElement(kfIdx);
556 lastKey = kf.mKey;
559 AnimationProperty &propData = *destAnim->Properties().AppendElement();
560 propData.mProperty = prop;
562 KeyframeData *fromKeyframe = nullptr;
563 nsRefPtr<nsStyleContext> fromContext;
564 bool interpolated = true;
565 for (uint32_t wpIdx = 0, wpEnd = keyframesWithProperty.Length();
566 wpIdx != wpEnd; ++wpIdx) {
567 uint32_t kfIdx = keyframesWithProperty[wpIdx];
568 KeyframeData &toKeyframe = sortedKeyframes[kfIdx];
570 nsRefPtr<nsStyleContext> toContext =
571 resolvedStyles.Get(mPresContext, aStyleContext, toKeyframe.mRule);
573 if (fromKeyframe) {
574 interpolated = interpolated &&
575 BuildSegment(propData.mSegments, prop, src,
576 fromKeyframe->mKey, fromContext,
577 fromKeyframe->mRule->Declaration(),
578 toKeyframe.mKey, toContext);
579 } else {
580 if (toKeyframe.mKey != 0.0f) {
581 // There's no data for this property at 0%, so use the
582 // cascaded value above us.
583 interpolated = interpolated &&
584 BuildSegment(propData.mSegments, prop, src,
585 0.0f, aStyleContext, nullptr,
586 toKeyframe.mKey, toContext);
590 fromContext = toContext;
591 fromKeyframe = &toKeyframe;
594 if (fromKeyframe->mKey != 1.0f) {
595 // There's no data for this property at 100%, so use the
596 // cascaded value above us.
597 interpolated = interpolated &&
598 BuildSegment(propData.mSegments, prop, src,
599 fromKeyframe->mKey, fromContext,
600 fromKeyframe->mRule->Declaration(),
601 1.0f, aStyleContext);
604 // If we failed to build any segments due to inability to
605 // interpolate, remove the property from the animation. (It's not
606 // clear if this is the right thing to do -- we could run some of
607 // the segments, but it's really not clear whether we should skip
608 // values (which?) or skip segments, so best to skip the whole
609 // thing for now.)
610 if (!interpolated) {
611 destAnim->Properties().RemoveElementAt(
612 destAnim->Properties().Length() - 1);
618 bool
619 nsAnimationManager::BuildSegment(InfallibleTArray<AnimationPropertySegment>&
620 aSegments,
621 nsCSSProperty aProperty,
622 const StyleAnimation& aAnimation,
623 float aFromKey, nsStyleContext* aFromContext,
624 mozilla::css::Declaration* aFromDeclaration,
625 float aToKey, nsStyleContext* aToContext)
627 StyleAnimationValue fromValue, toValue, dummyValue;
628 if (!ExtractComputedValueForTransition(aProperty, aFromContext, fromValue) ||
629 !ExtractComputedValueForTransition(aProperty, aToContext, toValue) ||
630 // Check that we can interpolate between these values
631 // (If this is ever a performance problem, we could add a
632 // CanInterpolate method, but it seems fine for now.)
633 !StyleAnimationValue::Interpolate(aProperty, fromValue, toValue,
634 0.5, dummyValue)) {
635 return false;
638 AnimationPropertySegment &segment = *aSegments.AppendElement();
640 segment.mFromValue = fromValue;
641 segment.mToValue = toValue;
642 segment.mFromKey = aFromKey;
643 segment.mToKey = aToKey;
644 const nsTimingFunction *tf;
645 if (aFromDeclaration &&
646 aFromDeclaration->HasProperty(eCSSProperty_animation_timing_function)) {
647 tf = &aFromContext->StyleDisplay()->mAnimations[0].GetTimingFunction();
648 } else {
649 tf = &aAnimation.GetTimingFunction();
651 segment.mTimingFunction.Init(*tf);
653 return true;
656 /* virtual */ void
657 nsAnimationManager::WillRefresh(mozilla::TimeStamp aTime)
659 NS_ABORT_IF_FALSE(mPresContext,
660 "refresh driver should not notify additional observers "
661 "after pres context has been destroyed");
662 if (!mPresContext->GetPresShell()) {
663 // Someone might be keeping mPresContext alive past the point
664 // where it has been torn down; don't bother doing anything in
665 // this case. But do get rid of all our transitions so we stop
666 // triggering refreshes.
667 RemoveAllElementCollections();
668 return;
671 FlushAnimations(Can_Throttle);
674 void
675 nsAnimationManager::FlushAnimations(FlushFlags aFlags)
677 // FIXME: check that there's at least one style rule that's not
678 // in its "done" state, and if there isn't, remove ourselves from
679 // the refresh driver (but leave the animations!).
680 TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
681 bool didThrottle = false;
682 for (PRCList *l = PR_LIST_HEAD(&mElementCollections);
683 l != &mElementCollections;
684 l = PR_NEXT_LINK(l)) {
685 AnimationPlayerCollection* collection =
686 static_cast<AnimationPlayerCollection*>(l);
687 collection->Tick();
688 bool canThrottleTick = aFlags == Can_Throttle &&
689 collection->CanPerformOnCompositorThread(
690 AnimationPlayerCollection::CanAnimateFlags(0)) &&
691 collection->CanThrottleAnimation(now);
693 nsRefPtr<css::AnimValuesStyleRule> oldStyleRule = collection->mStyleRule;
694 UpdateStyleAndEvents(collection, now, canThrottleTick
695 ? EnsureStyleRule_IsThrottled
696 : EnsureStyleRule_IsNotThrottled);
697 if (oldStyleRule != collection->mStyleRule) {
698 collection->PostRestyleForAnimation(mPresContext);
699 } else {
700 didThrottle = true;
704 if (didThrottle) {
705 mPresContext->Document()->SetNeedStyleFlush();
708 DispatchEvents(); // may destroy us
711 void
712 nsAnimationManager::DoDispatchEvents()
714 EventArray events;
715 mPendingEvents.SwapElements(events);
716 for (uint32_t i = 0, i_end = events.Length(); i < i_end; ++i) {
717 AnimationEventInfo &info = events[i];
718 EventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent);
720 if (!mPresContext) {
721 break;