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