Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/animation/PendingAnimationTracker.cpp
Line
Count
Source (jump to first uncovered line)
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 "mozilla/dom/Nullable.h"
11
#include "nsIFrame.h"
12
#include "nsIPresShell.h"
13
14
using mozilla::dom::Nullable;
15
16
namespace mozilla {
17
18
NS_IMPL_CYCLE_COLLECTION(PendingAnimationTracker,
19
                         mPlayPendingSet,
20
                         mPausePendingSet,
21
                         mDocument)
22
23
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PendingAnimationTracker, AddRef)
24
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PendingAnimationTracker, Release)
25
26
void
27
PendingAnimationTracker::AddPending(dom::Animation& aAnimation,
28
                                    AnimationSet& aSet)
29
0
{
30
0
  aSet.PutEntry(&aAnimation);
31
0
32
0
  // Schedule a paint. Otherwise animations that don't trigger a paint by
33
0
  // themselves (e.g. CSS animations with an empty keyframes rule) won't
34
0
  // start until something else paints.
35
0
  EnsurePaintIsScheduled();
36
0
}
37
38
void
39
PendingAnimationTracker::RemovePending(dom::Animation& aAnimation,
40
                                       AnimationSet& aSet)
41
0
{
42
0
  aSet.RemoveEntry(&aAnimation);
43
0
}
44
45
bool
46
PendingAnimationTracker::IsWaiting(const dom::Animation& aAnimation,
47
                                   const AnimationSet& aSet) const
48
0
{
49
0
  return aSet.Contains(const_cast<dom::Animation*>(&aAnimation));
50
0
}
51
52
void
53
PendingAnimationTracker::TriggerPendingAnimationsOnNextTick(const TimeStamp&
54
                                                        aReadyTime)
55
0
{
56
0
  auto triggerAnimationsAtReadyTime = [aReadyTime](AnimationSet& aAnimationSet)
57
0
  {
58
0
    for (auto iter = aAnimationSet.Iter(); !iter.Done(); iter.Next()) {
59
0
      dom::Animation* animation = iter.Get()->GetKey();
60
0
      dom::AnimationTimeline* timeline = animation->GetTimeline();
61
0
62
0
      // If the animation does not have a timeline, just drop it from the map.
63
0
      // The animation will detect that it is not being tracked and will trigger
64
0
      // itself on the next tick where it has a timeline.
65
0
      if (!timeline) {
66
0
        iter.Remove();
67
0
        continue;
68
0
      }
69
0
70
0
      // When the timeline's refresh driver is under test control, its values
71
0
      // have no correspondance to wallclock times so we shouldn't try to
72
0
      // convert aReadyTime (which is a wallclock time) to a timeline value.
73
0
      // Instead, the animation will be started/paused when the refresh driver
74
0
      // is next advanced since this will trigger a call to
75
0
      // TriggerPendingAnimationsNow.
76
0
      if (!timeline->TracksWallclockTime()) {
77
0
        continue;
78
0
      }
79
0
80
0
      Nullable<TimeDuration> readyTime = timeline->ToTimelineTime(aReadyTime);
81
0
      animation->TriggerOnNextTick(readyTime);
82
0
83
0
      iter.Remove();
84
0
    }
85
0
  };
86
0
87
0
  triggerAnimationsAtReadyTime(mPlayPendingSet);
88
0
  triggerAnimationsAtReadyTime(mPausePendingSet);
89
0
90
0
  mHasPlayPendingGeometricAnimations = mPlayPendingSet.Count()
91
0
                                       ? CheckState::Indeterminate
92
0
                                       : CheckState::Absent;
93
0
}
94
95
void
96
PendingAnimationTracker::TriggerPendingAnimationsNow()
97
0
{
98
0
  auto triggerAndClearAnimations = [](AnimationSet& aAnimationSet) {
99
0
    for (auto iter = aAnimationSet.Iter(); !iter.Done(); iter.Next()) {
100
0
      iter.Get()->GetKey()->TriggerNow();
101
0
    }
102
0
    aAnimationSet.Clear();
103
0
  };
104
0
105
0
  triggerAndClearAnimations(mPlayPendingSet);
106
0
  triggerAndClearAnimations(mPausePendingSet);
107
0
108
0
  mHasPlayPendingGeometricAnimations = CheckState::Absent;
109
0
}
110
111
void
112
PendingAnimationTracker::MarkAnimationsThatMightNeedSynchronization()
113
0
{
114
0
  // We only ever set mHasPlayPendingGeometricAnimations to 'present' in
115
0
  // HasPlayPendingGeometricAnimations(). So, if it is 'present' already,
116
0
  // (i.e. before calling HasPlayPendingGeometricAnimations()) we can assume
117
0
  // that this method has already been called for the current set of
118
0
  // play-pending animations and it is not necessary to run again.
119
0
  //
120
0
  // We can't make the same assumption about 'absent', but if this method
121
0
  // was already called and the result was 'absent', then this method is
122
0
  // a no-op anyway so it's ok to run again.
123
0
  //
124
0
  // Note that *without* this optimization, starting animations would become
125
0
  // O(n^2) in that case where each animation is on a different element and
126
0
  // contains a compositor-animatable property since we would end up iterating
127
0
  // over all animations in the play-pending set for each target element.
128
0
  if (mHasPlayPendingGeometricAnimations == CheckState::Present) {
129
0
    return;
130
0
  }
131
0
132
0
  if (!HasPlayPendingGeometricAnimations()) {
133
0
    return;
134
0
  }
135
0
136
0
  for (auto iter = mPlayPendingSet.Iter(); !iter.Done(); iter.Next()) {
137
0
    iter.Get()->GetKey()->NotifyGeometricAnimationsStartingThisFrame();
138
0
  }
139
0
}
140
141
bool
142
PendingAnimationTracker::HasPlayPendingGeometricAnimations()
143
0
{
144
0
  if (mHasPlayPendingGeometricAnimations != CheckState::Indeterminate) {
145
0
    return mHasPlayPendingGeometricAnimations == CheckState::Present;
146
0
  }
147
0
148
0
  mHasPlayPendingGeometricAnimations = CheckState::Absent;
149
0
  for (auto iter = mPlayPendingSet.ConstIter(); !iter.Done(); iter.Next()) {
150
0
    auto animation = iter.Get()->GetKey();
151
0
    if (animation->GetEffect() && animation->GetEffect()->AffectsGeometry()) {
152
0
      mHasPlayPendingGeometricAnimations = CheckState::Present;
153
0
      break;
154
0
    }
155
0
  }
156
0
157
0
  return mHasPlayPendingGeometricAnimations == CheckState::Present;
158
0
}
159
160
void
161
PendingAnimationTracker::EnsurePaintIsScheduled()
162
0
{
163
0
  if (!mDocument) {
164
0
    return;
165
0
  }
166
0
167
0
  nsIPresShell* presShell = mDocument->GetShell();
168
0
  if (!presShell) {
169
0
    return;
170
0
  }
171
0
172
0
  nsIFrame* rootFrame = presShell->GetRootFrame();
173
0
  if (!rootFrame) {
174
0
    return;
175
0
  }
176
0
177
0
  rootFrame->SchedulePaintWithoutInvalidatingObservers();
178
0
}
179
180
} // namespace mozilla