OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 10 matching lines...) Expand all Loading... |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | 30 |
31 #include "core/animation/AnimationTimeline.h" | 31 #include "core/animation/DocumentTimeline.h" |
32 | 32 |
33 #include <algorithm> | 33 #include <algorithm> |
34 #include "core/animation/AnimationClock.h" | 34 #include "core/animation/AnimationClock.h" |
35 #include "core/animation/ElementAnimations.h" | 35 #include "core/animation/ElementAnimations.h" |
36 #include "core/dom/Document.h" | 36 #include "core/dom/Document.h" |
37 #include "core/frame/LocalFrameView.h" | 37 #include "core/frame/LocalFrameView.h" |
38 #include "core/loader/DocumentLoader.h" | 38 #include "core/loader/DocumentLoader.h" |
39 #include "core/page/Page.h" | 39 #include "core/page/Page.h" |
40 #include "platform/RuntimeEnabledFeatures.h" | 40 #include "platform/RuntimeEnabledFeatures.h" |
41 #include "platform/animation/CompositorAnimationTimeline.h" | 41 #include "platform/animation/CompositorAnimationTimeline.h" |
42 #include "platform/instrumentation/tracing/TraceEvent.h" | 42 #include "platform/instrumentation/tracing/TraceEvent.h" |
43 #include "platform/wtf/PtrUtil.h" | 43 #include "platform/wtf/PtrUtil.h" |
44 #include "public/platform/Platform.h" | 44 #include "public/platform/Platform.h" |
45 #include "public/platform/WebCompositorSupport.h" | 45 #include "public/platform/WebCompositorSupport.h" |
46 | 46 |
47 namespace blink { | 47 namespace blink { |
48 | 48 |
49 namespace { | 49 namespace { |
50 | 50 |
51 bool CompareAnimations(const Member<Animation>& left, | 51 bool CompareAnimations(const Member<Animation>& left, |
52 const Member<Animation>& right) { | 52 const Member<Animation>& right) { |
53 return Animation::HasLowerPriority(left.Get(), right.Get()); | 53 return Animation::HasLowerPriority(left.Get(), right.Get()); |
54 } | 54 } |
55 } | 55 } // namespace |
56 | 56 |
57 // This value represents 1 frame at 30Hz plus a little bit of wiggle room. | 57 // This value represents 1 frame at 30Hz plus a little bit of wiggle room. |
58 // TODO: Plumb a nominal framerate through and derive this value from that. | 58 // TODO: Plumb a nominal framerate through and derive this value from that. |
59 const double AnimationTimeline::kMinimumDelay = 0.04; | 59 const double DocumentTimeline::kMinimumDelay = 0.04; |
60 | 60 |
61 AnimationTimeline* AnimationTimeline::Create(Document* document, | 61 DocumentTimeline* DocumentTimeline::Create(Document* document, |
62 PlatformTiming* timing) { | 62 PlatformTiming* timing) { |
63 return new AnimationTimeline(document, timing); | 63 return new DocumentTimeline(document, timing); |
64 } | 64 } |
65 | 65 |
66 AnimationTimeline::AnimationTimeline(Document* document, PlatformTiming* timing) | 66 DocumentTimeline::DocumentTimeline(Document* document, PlatformTiming* timing) |
67 : document_(document), | 67 : document_(document), |
68 // 0 is used by unit tests which cannot initialize from the loader | 68 // 0 is used by unit tests which cannot initialize from the loader |
69 zero_time_(0), | 69 zero_time_(0), |
70 zero_time_initialized_(false), | 70 zero_time_initialized_(false), |
71 outdated_animation_count_(0), | 71 outdated_animation_count_(0), |
72 playback_rate_(1), | 72 playback_rate_(1), |
73 last_current_time_internal_(0) { | 73 last_current_time_internal_(0) { |
74 if (!timing) | 74 if (!timing) |
75 timing_ = new AnimationTimelineTiming(this); | 75 timing_ = new DocumentTimelineTiming(this); |
76 else | 76 else |
77 timing_ = timing; | 77 timing_ = timing; |
78 | 78 |
79 if (Platform::Current()->IsThreadedAnimationEnabled()) | 79 if (Platform::Current()->IsThreadedAnimationEnabled()) |
80 compositor_timeline_ = CompositorAnimationTimeline::Create(); | 80 compositor_timeline_ = CompositorAnimationTimeline::Create(); |
81 | 81 |
82 DCHECK(document); | 82 DCHECK(document); |
83 } | 83 } |
84 | 84 |
85 bool AnimationTimeline::IsActive() { | 85 bool DocumentTimeline::IsActive() { |
86 return document_ && document_->GetPage(); | 86 return document_ && document_->GetPage(); |
87 } | 87 } |
88 | 88 |
89 void AnimationTimeline::AnimationAttached(Animation& animation) { | 89 void DocumentTimeline::AnimationAttached(Animation& animation) { |
90 DCHECK_EQ(animation.TimelineInternal(), this); | 90 DCHECK_EQ(animation.TimelineInternal(), this); |
91 DCHECK(!animations_.Contains(&animation)); | 91 DCHECK(!animations_.Contains(&animation)); |
92 animations_.insert(&animation); | 92 animations_.insert(&animation); |
93 } | 93 } |
94 | 94 |
95 Animation* AnimationTimeline::Play(AnimationEffectReadOnly* child) { | 95 Animation* DocumentTimeline::Play(AnimationEffectReadOnly* child) { |
96 if (!document_) | 96 if (!document_) |
97 return nullptr; | 97 return nullptr; |
98 | 98 |
99 Animation* animation = Animation::Create(child, this); | 99 Animation* animation = Animation::Create(child, this); |
100 DCHECK(animations_.Contains(animation)); | 100 DCHECK(animations_.Contains(animation)); |
101 | 101 |
102 animation->play(); | 102 animation->play(); |
103 DCHECK(animations_needing_update_.Contains(animation)); | 103 DCHECK(animations_needing_update_.Contains(animation)); |
104 | 104 |
105 return animation; | 105 return animation; |
106 } | 106 } |
107 | 107 |
108 HeapVector<Member<Animation>> AnimationTimeline::getAnimations() { | 108 HeapVector<Member<Animation>> DocumentTimeline::getAnimations() { |
109 HeapVector<Member<Animation>> animations; | 109 HeapVector<Member<Animation>> animations; |
110 for (const auto& animation : animations_) { | 110 for (const auto& animation : animations_) { |
111 if (animation->effect() && | 111 if (animation->effect() && |
112 (animation->effect()->IsCurrent() || animation->effect()->IsInEffect())) | 112 (animation->effect()->IsCurrent() || animation->effect()->IsInEffect())) |
113 animations.push_back(animation); | 113 animations.push_back(animation); |
114 } | 114 } |
115 std::sort(animations.begin(), animations.end(), CompareAnimations); | 115 std::sort(animations.begin(), animations.end(), CompareAnimations); |
116 return animations; | 116 return animations; |
117 } | 117 } |
118 | 118 |
119 void AnimationTimeline::Wake() { | 119 void DocumentTimeline::Wake() { |
120 timing_->ServiceOnNextFrame(); | 120 timing_->ServiceOnNextFrame(); |
121 } | 121 } |
122 | 122 |
123 void AnimationTimeline::ServiceAnimations(TimingUpdateReason reason) { | 123 void DocumentTimeline::ServiceAnimations(TimingUpdateReason reason) { |
124 TRACE_EVENT0("blink", "AnimationTimeline::serviceAnimations"); | 124 TRACE_EVENT0("blink", "DocumentTimeline::serviceAnimations"); |
125 | 125 |
126 last_current_time_internal_ = CurrentTimeInternal(); | 126 last_current_time_internal_ = CurrentTimeInternal(); |
127 | 127 |
128 HeapVector<Member<Animation>> animations; | 128 HeapVector<Member<Animation>> animations; |
129 animations.ReserveInitialCapacity(animations_needing_update_.size()); | 129 animations.ReserveInitialCapacity(animations_needing_update_.size()); |
130 for (Animation* animation : animations_needing_update_) | 130 for (Animation* animation : animations_needing_update_) |
131 animations.push_back(animation); | 131 animations.push_back(animation); |
132 | 132 |
133 std::sort(animations.begin(), animations.end(), Animation::HasLowerPriority); | 133 std::sort(animations.begin(), animations.end(), Animation::HasLowerPriority); |
134 | 134 |
135 for (Animation* animation : animations) { | 135 for (Animation* animation : animations) { |
136 if (!animation->Update(reason)) | 136 if (!animation->Update(reason)) |
137 animations_needing_update_.erase(animation); | 137 animations_needing_update_.erase(animation); |
138 } | 138 } |
139 | 139 |
140 DCHECK_EQ(outdated_animation_count_, 0U); | 140 DCHECK_EQ(outdated_animation_count_, 0U); |
141 DCHECK(last_current_time_internal_ == CurrentTimeInternal() || | 141 DCHECK(last_current_time_internal_ == CurrentTimeInternal() || |
142 (std::isnan(CurrentTimeInternal()) && | 142 (std::isnan(CurrentTimeInternal()) && |
143 std::isnan(last_current_time_internal_))); | 143 std::isnan(last_current_time_internal_))); |
144 | 144 |
145 #if DCHECK_IS_ON() | 145 #if DCHECK_IS_ON() |
146 for (const auto& animation : animations_needing_update_) | 146 for (const auto& animation : animations_needing_update_) |
147 DCHECK(!animation->Outdated()); | 147 DCHECK(!animation->Outdated()); |
148 #endif | 148 #endif |
149 } | 149 } |
150 | 150 |
151 void AnimationTimeline::ScheduleNextService() { | 151 void DocumentTimeline::ScheduleNextService() { |
152 DCHECK_EQ(outdated_animation_count_, 0U); | 152 DCHECK_EQ(outdated_animation_count_, 0U); |
153 | 153 |
154 double time_to_next_effect = std::numeric_limits<double>::infinity(); | 154 double time_to_next_effect = std::numeric_limits<double>::infinity(); |
155 for (const auto& animation : animations_needing_update_) { | 155 for (const auto& animation : animations_needing_update_) { |
156 time_to_next_effect = | 156 time_to_next_effect = |
157 std::min(time_to_next_effect, animation->TimeToEffectChange()); | 157 std::min(time_to_next_effect, animation->TimeToEffectChange()); |
158 } | 158 } |
159 | 159 |
160 if (time_to_next_effect < kMinimumDelay) { | 160 if (time_to_next_effect < kMinimumDelay) { |
161 timing_->ServiceOnNextFrame(); | 161 timing_->ServiceOnNextFrame(); |
162 } else if (time_to_next_effect != std::numeric_limits<double>::infinity()) { | 162 } else if (time_to_next_effect != std::numeric_limits<double>::infinity()) { |
163 timing_->WakeAfter(time_to_next_effect - kMinimumDelay); | 163 timing_->WakeAfter(time_to_next_effect - kMinimumDelay); |
164 } | 164 } |
165 } | 165 } |
166 | 166 |
167 void AnimationTimeline::AnimationTimelineTiming::WakeAfter(double duration) { | 167 void DocumentTimeline::DocumentTimelineTiming::WakeAfter(double duration) { |
168 if (timer_.IsActive() && timer_.NextFireInterval() < duration) | 168 if (timer_.IsActive() && timer_.NextFireInterval() < duration) |
169 return; | 169 return; |
170 timer_.StartOneShot(duration, BLINK_FROM_HERE); | 170 timer_.StartOneShot(duration, BLINK_FROM_HERE); |
171 } | 171 } |
172 | 172 |
173 void AnimationTimeline::AnimationTimelineTiming::ServiceOnNextFrame() { | 173 void DocumentTimeline::DocumentTimelineTiming::ServiceOnNextFrame() { |
174 if (timeline_->document_ && timeline_->document_->View()) | 174 if (timeline_->document_ && timeline_->document_->View()) |
175 timeline_->document_->View()->ScheduleAnimation(); | 175 timeline_->document_->View()->ScheduleAnimation(); |
176 } | 176 } |
177 | 177 |
178 DEFINE_TRACE(AnimationTimeline::AnimationTimelineTiming) { | 178 DEFINE_TRACE(DocumentTimeline::DocumentTimelineTiming) { |
179 visitor->Trace(timeline_); | 179 visitor->Trace(timeline_); |
180 AnimationTimeline::PlatformTiming::Trace(visitor); | 180 DocumentTimeline::PlatformTiming::Trace(visitor); |
181 } | 181 } |
182 | 182 |
183 double AnimationTimeline::ZeroTime() { | 183 double DocumentTimeline::ZeroTime() { |
184 if (!zero_time_initialized_ && document_ && document_->Loader()) { | 184 if (!zero_time_initialized_ && document_ && document_->Loader()) { |
185 zero_time_ = document_->Loader()->GetTiming().ReferenceMonotonicTime(); | 185 zero_time_ = document_->Loader()->GetTiming().ReferenceMonotonicTime(); |
186 zero_time_initialized_ = true; | 186 zero_time_initialized_ = true; |
187 } | 187 } |
188 return zero_time_; | 188 return zero_time_; |
189 } | 189 } |
190 | 190 |
191 void AnimationTimeline::ResetForTesting() { | 191 void DocumentTimeline::ResetForTesting() { |
192 zero_time_ = 0; | 192 zero_time_ = 0; |
193 zero_time_initialized_ = true; | 193 zero_time_initialized_ = true; |
194 playback_rate_ = 1; | 194 playback_rate_ = 1; |
195 last_current_time_internal_ = 0; | 195 last_current_time_internal_ = 0; |
196 } | 196 } |
197 | 197 |
198 double AnimationTimeline::currentTime(bool& is_null) { | 198 double DocumentTimeline::currentTime(bool& is_null) { |
199 return CurrentTimeInternal(is_null) * 1000; | 199 return CurrentTimeInternal(is_null) * 1000; |
200 } | 200 } |
201 | 201 |
202 double AnimationTimeline::CurrentTimeInternal(bool& is_null) { | 202 double DocumentTimeline::CurrentTimeInternal(bool& is_null) { |
203 if (!IsActive()) { | 203 if (!IsActive()) { |
204 is_null = true; | 204 is_null = true; |
205 return std::numeric_limits<double>::quiet_NaN(); | 205 return std::numeric_limits<double>::quiet_NaN(); |
206 } | 206 } |
207 double result = | 207 double result = |
208 playback_rate_ == 0 | 208 playback_rate_ == 0 |
209 ? ZeroTime() | 209 ? ZeroTime() |
210 : (GetDocument()->GetAnimationClock().CurrentTime() - ZeroTime()) * | 210 : (GetDocument()->GetAnimationClock().CurrentTime() - ZeroTime()) * |
211 playback_rate_; | 211 playback_rate_; |
212 is_null = std::isnan(result); | 212 is_null = std::isnan(result); |
213 return result; | 213 return result; |
214 } | 214 } |
215 | 215 |
216 double AnimationTimeline::currentTime() { | 216 double DocumentTimeline::currentTime() { |
217 return CurrentTimeInternal() * 1000; | 217 return CurrentTimeInternal() * 1000; |
218 } | 218 } |
219 | 219 |
220 double AnimationTimeline::CurrentTimeInternal() { | 220 double DocumentTimeline::CurrentTimeInternal() { |
221 bool is_null; | 221 bool is_null; |
222 return CurrentTimeInternal(is_null); | 222 return CurrentTimeInternal(is_null); |
223 } | 223 } |
224 | 224 |
225 double AnimationTimeline::EffectiveTime() { | 225 double DocumentTimeline::EffectiveTime() { |
226 double time = CurrentTimeInternal(); | 226 double time = CurrentTimeInternal(); |
227 return std::isnan(time) ? 0 : time; | 227 return std::isnan(time) ? 0 : time; |
228 } | 228 } |
229 | 229 |
230 void AnimationTimeline::PauseAnimationsForTesting(double pause_time) { | 230 void DocumentTimeline::PauseAnimationsForTesting(double pause_time) { |
231 for (const auto& animation : animations_needing_update_) | 231 for (const auto& animation : animations_needing_update_) |
232 animation->PauseForTesting(pause_time); | 232 animation->PauseForTesting(pause_time); |
233 ServiceAnimations(kTimingUpdateOnDemand); | 233 ServiceAnimations(kTimingUpdateOnDemand); |
234 } | 234 } |
235 | 235 |
236 bool AnimationTimeline::NeedsAnimationTimingUpdate() { | 236 bool DocumentTimeline::NeedsAnimationTimingUpdate() { |
237 if (CurrentTimeInternal() == last_current_time_internal_) | 237 if (CurrentTimeInternal() == last_current_time_internal_) |
238 return false; | 238 return false; |
239 | 239 |
240 if (std::isnan(CurrentTimeInternal()) && | 240 if (std::isnan(CurrentTimeInternal()) && |
241 std::isnan(last_current_time_internal_)) | 241 std::isnan(last_current_time_internal_)) |
242 return false; | 242 return false; |
243 | 243 |
244 // We allow m_lastCurrentTimeInternal to advance here when there | 244 // We allow m_lastCurrentTimeInternal to advance here when there |
245 // are no animations to allow animations spawned during style | 245 // are no animations to allow animations spawned during style |
246 // recalc to not invalidate this flag. | 246 // recalc to not invalidate this flag. |
247 if (animations_needing_update_.IsEmpty()) | 247 if (animations_needing_update_.IsEmpty()) |
248 last_current_time_internal_ = CurrentTimeInternal(); | 248 last_current_time_internal_ = CurrentTimeInternal(); |
249 | 249 |
250 return !animations_needing_update_.IsEmpty(); | 250 return !animations_needing_update_.IsEmpty(); |
251 } | 251 } |
252 | 252 |
253 void AnimationTimeline::ClearOutdatedAnimation(Animation* animation) { | 253 void DocumentTimeline::ClearOutdatedAnimation(Animation* animation) { |
254 DCHECK(!animation->Outdated()); | 254 DCHECK(!animation->Outdated()); |
255 outdated_animation_count_--; | 255 outdated_animation_count_--; |
256 } | 256 } |
257 | 257 |
258 void AnimationTimeline::SetOutdatedAnimation(Animation* animation) { | 258 void DocumentTimeline::SetOutdatedAnimation(Animation* animation) { |
259 DCHECK(animation->Outdated()); | 259 DCHECK(animation->Outdated()); |
260 outdated_animation_count_++; | 260 outdated_animation_count_++; |
261 animations_needing_update_.insert(animation); | 261 animations_needing_update_.insert(animation); |
262 if (IsActive() && !document_->GetPage()->Animator().IsServicingAnimations()) | 262 if (IsActive() && !document_->GetPage()->Animator().IsServicingAnimations()) |
263 timing_->ServiceOnNextFrame(); | 263 timing_->ServiceOnNextFrame(); |
264 } | 264 } |
265 | 265 |
266 void AnimationTimeline::SetPlaybackRate(double playback_rate) { | 266 void DocumentTimeline::SetPlaybackRate(double playback_rate) { |
267 if (!IsActive()) | 267 if (!IsActive()) |
268 return; | 268 return; |
269 double current_time = CurrentTimeInternal(); | 269 double current_time = CurrentTimeInternal(); |
270 playback_rate_ = playback_rate; | 270 playback_rate_ = playback_rate; |
271 zero_time_ = playback_rate == 0 | 271 zero_time_ = playback_rate == 0 |
272 ? current_time | 272 ? current_time |
273 : GetDocument()->GetAnimationClock().CurrentTime() - | 273 : GetDocument()->GetAnimationClock().CurrentTime() - |
274 current_time / playback_rate; | 274 current_time / playback_rate; |
275 zero_time_initialized_ = true; | 275 zero_time_initialized_ = true; |
276 | 276 |
277 // Corresponding compositor animation may need to be restarted to pick up | 277 // Corresponding compositor animation may need to be restarted to pick up |
278 // the new playback rate. Marking the effect changed forces this. | 278 // the new playback rate. Marking the effect changed forces this. |
279 SetAllCompositorPending(true); | 279 SetAllCompositorPending(true); |
280 } | 280 } |
281 | 281 |
282 void AnimationTimeline::SetAllCompositorPending(bool source_changed) { | 282 void DocumentTimeline::SetAllCompositorPending(bool source_changed) { |
283 for (const auto& animation : animations_) { | 283 for (const auto& animation : animations_) { |
284 animation->SetCompositorPending(source_changed); | 284 animation->SetCompositorPending(source_changed); |
285 } | 285 } |
286 } | 286 } |
287 | 287 |
288 double AnimationTimeline::PlaybackRate() const { | 288 double DocumentTimeline::PlaybackRate() const { |
289 return playback_rate_; | 289 return playback_rate_; |
290 } | 290 } |
291 | 291 |
292 void AnimationTimeline::InvalidateKeyframeEffects(const TreeScope& tree_scope) { | 292 void DocumentTimeline::InvalidateKeyframeEffects(const TreeScope& tree_scope) { |
293 for (const auto& animation : animations_) | 293 for (const auto& animation : animations_) |
294 animation->InvalidateKeyframeEffect(tree_scope); | 294 animation->InvalidateKeyframeEffect(tree_scope); |
295 } | 295 } |
296 | 296 |
297 DEFINE_TRACE(AnimationTimeline) { | 297 DEFINE_TRACE(DocumentTimeline) { |
298 visitor->Trace(document_); | 298 visitor->Trace(document_); |
299 visitor->Trace(timing_); | 299 visitor->Trace(timing_); |
300 visitor->Trace(animations_needing_update_); | 300 visitor->Trace(animations_needing_update_); |
301 visitor->Trace(animations_); | 301 visitor->Trace(animations_); |
302 SuperAnimationTimeline::Trace(visitor); | 302 SuperAnimationTimeline::Trace(visitor); |
303 } | 303 } |
304 | 304 |
305 } // namespace blink | 305 } // namespace blink |
OLD | NEW |