Bug 1698238 return default dictionary from GetUserMediaRequest#getConstraints() if...
[gecko.git] / dom / animation / PendingAnimationTracker.cpp
blob19ccf290635b745b9b7fbad4251277407b09c748
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/. */
7 #include "PendingAnimationTracker.h"
9 #include "mozilla/PresShell.h"
10 #include "mozilla/dom/AnimationEffect.h"
11 #include "mozilla/dom/AnimationTimeline.h"
12 #include "mozilla/dom/Nullable.h"
13 #include "nsIFrame.h"
14 #include "nsTransitionManager.h" // For CSSTransition
16 using mozilla::dom::Nullable;
18 namespace mozilla {
20 NS_IMPL_CYCLE_COLLECTION(PendingAnimationTracker, mPlayPendingSet,
21 mPausePendingSet, mDocument)
23 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PendingAnimationTracker, AddRef)
24 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PendingAnimationTracker, Release)
26 PendingAnimationTracker::PendingAnimationTracker(dom::Document* aDocument)
27 : mDocument(aDocument) {}
29 void PendingAnimationTracker::AddPending(dom::Animation& aAnimation,
30 AnimationSet& aSet) {
31 aSet.Insert(&aAnimation);
33 // Schedule a paint. Otherwise animations that don't trigger a paint by
34 // themselves (e.g. CSS animations with an empty keyframes rule) won't
35 // start until something else paints.
36 EnsurePaintIsScheduled();
39 void PendingAnimationTracker::RemovePending(dom::Animation& aAnimation,
40 AnimationSet& aSet) {
41 aSet.Remove(&aAnimation);
44 bool PendingAnimationTracker::IsWaiting(const dom::Animation& aAnimation,
45 const AnimationSet& aSet) const {
46 return aSet.Contains(const_cast<dom::Animation*>(&aAnimation));
49 void PendingAnimationTracker::TriggerPendingAnimationsOnNextTick(
50 const TimeStamp& aReadyTime) {
51 auto triggerAnimationsAtReadyTime = [aReadyTime](
52 AnimationSet& aAnimationSet) {
53 for (auto iter = aAnimationSet.begin(), end = aAnimationSet.end();
54 iter != end; ++iter) {
55 dom::Animation* animation = *iter;
56 dom::AnimationTimeline* timeline = animation->GetTimeline();
58 // If the animation does not have a timeline, just drop it from the map.
59 // The animation will detect that it is not being tracked and will trigger
60 // itself on the next tick where it has a timeline.
61 if (!timeline) {
62 aAnimationSet.Remove(iter);
63 continue;
66 // When the timeline's refresh driver is under test control, its values
67 // have no correspondance to wallclock times so we shouldn't try to
68 // convert aReadyTime (which is a wallclock time) to a timeline value.
69 // Instead, the animation will be started/paused when the refresh driver
70 // is next advanced since this will trigger a call to
71 // TriggerPendingAnimationsNow.
72 if (!timeline->TracksWallclockTime()) {
73 continue;
76 Nullable<TimeDuration> readyTime = timeline->ToTimelineTime(aReadyTime);
77 animation->TriggerOnNextTick(readyTime);
79 aAnimationSet.Remove(iter);
83 triggerAnimationsAtReadyTime(mPlayPendingSet);
84 triggerAnimationsAtReadyTime(mPausePendingSet);
86 mHasPlayPendingGeometricAnimations =
87 mPlayPendingSet.Count() ? CheckState::Indeterminate : CheckState::Absent;
90 void PendingAnimationTracker::TriggerPendingAnimationsNow() {
91 auto triggerAndClearAnimations = [](AnimationSet& aAnimationSet) {
92 for (const auto& animation : aAnimationSet) {
93 animation->TriggerNow();
95 aAnimationSet.Clear();
98 triggerAndClearAnimations(mPlayPendingSet);
99 triggerAndClearAnimations(mPausePendingSet);
101 mHasPlayPendingGeometricAnimations = CheckState::Absent;
104 static bool IsTransition(const dom::Animation& aAnimation) {
105 const dom::CSSTransition* transition = aAnimation.AsCSSTransition();
106 return transition && transition->IsTiedToMarkup();
109 void PendingAnimationTracker::MarkAnimationsThatMightNeedSynchronization() {
110 // We only set mHasPlayPendingGeometricAnimations to "present" in this method
111 // and nowhere else. After setting the state to "present", if there is any
112 // change to the set of play-pending animations we will reset
113 // mHasPlayPendingGeometricAnimations to either "indeterminate" or "absent".
115 // As a result, if mHasPlayPendingGeometricAnimations is "present", we can
116 // assume that this method has already been called for the current set of
117 // play-pending animations and it is not necessary to run this method again.
119 // If mHasPlayPendingGeometricAnimations is "absent", then we can also skip
120 // the body of this method since there are no notifications to be sent.
122 // Therefore, the only case we need to be concerned about is the
123 // "indeterminate" case. For all other cases we can return early.
125 // Note that *without* this optimization, starting animations would become
126 // O(n^2) in the case where each animation is on a different element and
127 // contains a compositor-animatable property since we would end up iterating
128 // over all animations in the play-pending set for each target element.
129 if (mHasPlayPendingGeometricAnimations != CheckState::Indeterminate) {
130 return;
133 // We only synchronize CSS transitions with other CSS transitions (and we only
134 // synchronize non-transition animations with non-transition animations)
135 // since typically the author will not trigger both CSS animations and
136 // CSS transitions simultaneously and expect them to be synchronized.
138 // If we try to synchronize CSS transitions with non-transitions then for some
139 // content we will end up degrading performance by forcing animations to run
140 // on the main thread that really don't need to.
142 mHasPlayPendingGeometricAnimations = CheckState::Absent;
143 for (const auto& animation : mPlayPendingSet) {
144 if (animation->GetEffect() && animation->GetEffect()->AffectsGeometry()) {
145 mHasPlayPendingGeometricAnimations &= ~CheckState::Absent;
146 mHasPlayPendingGeometricAnimations |= IsTransition(*animation)
147 ? CheckState::TransitionsPresent
148 : CheckState::AnimationsPresent;
150 // If we have both transitions and animations we don't need to look any
151 // further.
152 if (mHasPlayPendingGeometricAnimations ==
153 (CheckState::TransitionsPresent | CheckState::AnimationsPresent)) {
154 break;
159 if (mHasPlayPendingGeometricAnimations == CheckState::Absent) {
160 return;
163 for (const auto& animation : mPlayPendingSet) {
164 bool isTransition = IsTransition(*animation);
165 if ((isTransition &&
166 mHasPlayPendingGeometricAnimations & CheckState::TransitionsPresent) ||
167 (!isTransition &&
168 mHasPlayPendingGeometricAnimations & CheckState::AnimationsPresent)) {
169 animation->NotifyGeometricAnimationsStartingThisFrame();
174 void PendingAnimationTracker::EnsurePaintIsScheduled() {
175 if (!mDocument) {
176 return;
179 PresShell* presShell = mDocument->GetPresShell();
180 if (!presShell) {
181 return;
184 nsIFrame* rootFrame = presShell->GetRootFrame();
185 if (!rootFrame) {
186 return;
189 rootFrame->SchedulePaintWithoutInvalidatingObservers();
192 } // namespace mozilla