OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/gfx/compositor/layer_animator.h" | |
6 | |
7 #include "base/debug/trace_event.h" | |
8 #include "base/logging.h" | |
9 #include "base/memory/scoped_ptr.h" | |
10 #include "ui/base/animation/animation_container.h" | |
11 #include "ui/gfx/compositor/compositor.h" | |
12 #include "ui/gfx/compositor/layer.h" | |
13 #include "ui/gfx/compositor/layer_animation_delegate.h" | |
14 #include "ui/gfx/compositor/layer_animation_observer.h" | |
15 #include "ui/gfx/compositor/layer_animation_sequence.h" | |
16 | |
17 namespace ui { | |
18 | |
19 class LayerAnimator; | |
20 | |
21 namespace { | |
22 | |
23 static const base::TimeDelta kDefaultTransitionDuration = | |
24 base::TimeDelta::FromMilliseconds(120); | |
25 | |
26 static const base::TimeDelta kTimerInterval = | |
27 base::TimeDelta::FromMilliseconds(10); | |
28 | |
29 } // namespace; | |
30 | |
31 // LayerAnimator public -------------------------------------------------------- | |
32 | |
33 LayerAnimator::LayerAnimator(base::TimeDelta transition_duration) | |
34 : delegate_(NULL), | |
35 preemption_strategy_(IMMEDIATELY_SET_NEW_TARGET), | |
36 transition_duration_(transition_duration), | |
37 tween_type_(Tween::LINEAR), | |
38 is_started_(false), | |
39 disable_timer_for_test_(false) { | |
40 } | |
41 | |
42 LayerAnimator::~LayerAnimator() { | |
43 for (size_t i = 0; i < running_animations_.size(); ++i) | |
44 running_animations_[i].sequence->OnAnimatorDestroyed(); | |
45 ClearAnimations(); | |
46 } | |
47 | |
48 // static | |
49 bool LayerAnimator::disable_animations_for_test_ = false; | |
50 | |
51 // static | |
52 LayerAnimator* LayerAnimator::CreateDefaultAnimator() { | |
53 return new LayerAnimator(base::TimeDelta::FromMilliseconds(0)); | |
54 } | |
55 | |
56 // static | |
57 LayerAnimator* LayerAnimator::CreateImplicitAnimator() { | |
58 return new LayerAnimator(kDefaultTransitionDuration); | |
59 } | |
60 | |
61 void LayerAnimator::SetTransform(const Transform& transform) { | |
62 base::TimeDelta duration = transition_duration_; | |
63 scoped_ptr<LayerAnimationElement> element( | |
64 LayerAnimationElement::CreateTransformElement(transform, duration)); | |
65 element->set_tween_type(tween_type_); | |
66 StartAnimation(new LayerAnimationSequence(element.release())); | |
67 } | |
68 | |
69 Transform LayerAnimator::GetTargetTransform() const { | |
70 LayerAnimationElement::TargetValue target(delegate()); | |
71 GetTargetValue(&target); | |
72 return target.transform; | |
73 } | |
74 | |
75 void LayerAnimator::SetBounds(const gfx::Rect& bounds) { | |
76 base::TimeDelta duration = transition_duration_; | |
77 scoped_ptr<LayerAnimationElement> element( | |
78 LayerAnimationElement::CreateBoundsElement(bounds, duration)); | |
79 element->set_tween_type(tween_type_); | |
80 StartAnimation(new LayerAnimationSequence(element.release())); | |
81 } | |
82 | |
83 gfx::Rect LayerAnimator::GetTargetBounds() const { | |
84 LayerAnimationElement::TargetValue target(delegate()); | |
85 GetTargetValue(&target); | |
86 return target.bounds; | |
87 } | |
88 | |
89 void LayerAnimator::SetOpacity(float opacity) { | |
90 base::TimeDelta duration = transition_duration_; | |
91 scoped_ptr<LayerAnimationElement> element( | |
92 LayerAnimationElement::CreateOpacityElement(opacity, duration)); | |
93 element->set_tween_type(tween_type_); | |
94 StartAnimation(new LayerAnimationSequence(element.release())); | |
95 } | |
96 | |
97 float LayerAnimator::GetTargetOpacity() const { | |
98 LayerAnimationElement::TargetValue target(delegate()); | |
99 GetTargetValue(&target); | |
100 return target.opacity; | |
101 } | |
102 | |
103 void LayerAnimator::SetVisibility(bool visibility) { | |
104 base::TimeDelta duration = transition_duration_; | |
105 | |
106 // Tween type doesn't matter for visibility. | |
107 StartAnimation(new LayerAnimationSequence( | |
108 LayerAnimationElement::CreateVisibilityElement( | |
109 visibility, duration))); | |
110 } | |
111 | |
112 bool LayerAnimator::GetTargetVisibility() const { | |
113 LayerAnimationElement::TargetValue target(delegate()); | |
114 GetTargetValue(&target); | |
115 return target.visibility; | |
116 } | |
117 | |
118 void LayerAnimator::SetDelegate(LayerAnimationDelegate* delegate) { | |
119 DCHECK(delegate); | |
120 delegate_ = delegate; | |
121 } | |
122 | |
123 void LayerAnimator::StartAnimation(LayerAnimationSequence* animation) { | |
124 OnScheduled(animation); | |
125 if (!StartSequenceImmediately(animation)) { | |
126 // Attempt to preempt a running animation. | |
127 switch (preemption_strategy_) { | |
128 case IMMEDIATELY_SET_NEW_TARGET: | |
129 ImmediatelySetNewTarget(animation); | |
130 break; | |
131 case IMMEDIATELY_ANIMATE_TO_NEW_TARGET: | |
132 ImmediatelyAnimateToNewTarget(animation); | |
133 break; | |
134 case ENQUEUE_NEW_ANIMATION: | |
135 EnqueueNewAnimation(animation); | |
136 break; | |
137 case REPLACE_QUEUED_ANIMATIONS: | |
138 ReplaceQueuedAnimations(animation); | |
139 break; | |
140 case BLEND_WITH_CURRENT_ANIMATION: { | |
141 // TODO(vollick) Add support for blended sequences and use them here. | |
142 NOTIMPLEMENTED(); | |
143 break; | |
144 } | |
145 } | |
146 } | |
147 FinishAnyAnimationWithZeroDuration(); | |
148 UpdateAnimationState(); | |
149 } | |
150 | |
151 void LayerAnimator::ScheduleAnimation(LayerAnimationSequence* animation) { | |
152 OnScheduled(animation); | |
153 if (is_animating()) { | |
154 animation_queue_.push_back(make_linked_ptr(animation)); | |
155 ProcessQueue(); | |
156 } else { | |
157 StartSequenceImmediately(animation); | |
158 } | |
159 UpdateAnimationState(); | |
160 } | |
161 | |
162 void LayerAnimator::ScheduleTogether( | |
163 const std::vector<LayerAnimationSequence*>& animations) { | |
164 // Collect all the affected properties. | |
165 LayerAnimationElement::AnimatableProperties animated_properties; | |
166 std::vector<LayerAnimationSequence*>::const_iterator iter; | |
167 for (iter = animations.begin(); iter != animations.end(); ++iter) { | |
168 animated_properties.insert((*iter)->properties().begin(), | |
169 (*iter)->properties().end()); | |
170 } | |
171 | |
172 // Scheduling a zero duration pause that affects all the animated properties | |
173 // will prevent any of the sequences from animating until there are no | |
174 // running animations that affect any of these properties. | |
175 ScheduleAnimation(new LayerAnimationSequence( | |
176 LayerAnimationElement::CreatePauseElement(animated_properties, | |
177 base::TimeDelta()))); | |
178 | |
179 // These animations (provided they don't animate any common properties) will | |
180 // now animate together if trivially scheduled. | |
181 for (iter = animations.begin(); iter != animations.end(); ++iter) { | |
182 ScheduleAnimation(*iter); | |
183 } | |
184 | |
185 UpdateAnimationState(); | |
186 } | |
187 | |
188 bool LayerAnimator::IsAnimatingProperty( | |
189 LayerAnimationElement::AnimatableProperty property) const { | |
190 for (AnimationQueue::const_iterator queue_iter = animation_queue_.begin(); | |
191 queue_iter != animation_queue_.end(); ++queue_iter) { | |
192 if ((*queue_iter)->properties().find(property) != | |
193 (*queue_iter)->properties().end()) { | |
194 return true; | |
195 } | |
196 } | |
197 return false; | |
198 } | |
199 | |
200 void LayerAnimator::StopAnimatingProperty( | |
201 LayerAnimationElement::AnimatableProperty property) { | |
202 while (true) { | |
203 RunningAnimation* running = GetRunningAnimation(property); | |
204 if (!running) | |
205 break; | |
206 FinishAnimation(running->sequence); | |
207 } | |
208 } | |
209 | |
210 void LayerAnimator::StopAnimating() { | |
211 while (is_animating()) | |
212 FinishAnimation(running_animations_[0].sequence); | |
213 } | |
214 | |
215 void LayerAnimator::AddObserver(LayerAnimationObserver* observer) { | |
216 if (!observers_.HasObserver(observer)) | |
217 observers_.AddObserver(observer); | |
218 } | |
219 | |
220 void LayerAnimator::RemoveObserver(LayerAnimationObserver* observer) { | |
221 observers_.RemoveObserver(observer); | |
222 // Remove the observer from all sequences as well. | |
223 for (AnimationQueue::iterator queue_iter = animation_queue_.begin(); | |
224 queue_iter != animation_queue_.end(); ++queue_iter) { | |
225 (*queue_iter)->RemoveObserver(observer); | |
226 } | |
227 } | |
228 | |
229 // LayerAnimator protected ----------------------------------------------------- | |
230 | |
231 bool LayerAnimator::ProgressAnimation(LayerAnimationSequence* sequence, | |
232 base::TimeDelta delta) { | |
233 return sequence->Progress(delta, delegate()); | |
234 } | |
235 | |
236 | |
237 bool LayerAnimator::HasAnimation(LayerAnimationSequence* sequence) const { | |
238 for (AnimationQueue::const_iterator queue_iter = animation_queue_.begin(); | |
239 queue_iter != animation_queue_.end(); ++queue_iter) { | |
240 if ((*queue_iter).get() == sequence) | |
241 return true; | |
242 } | |
243 return false; | |
244 } | |
245 | |
246 // LayerAnimator private ------------------------------------------------------- | |
247 | |
248 void LayerAnimator::Step(base::TimeTicks now) { | |
249 TRACE_EVENT0("ui", "LayerAnimator::Step"); | |
250 | |
251 last_step_time_ = now; | |
252 // We need to make a copy of the running animations because progressing them | |
253 // and finishing them may indirectly affect the collection of running | |
254 // animations. | |
255 RunningAnimations running_animations_copy = running_animations_; | |
256 bool needs_redraw = false; | |
257 for (size_t i = 0; i < running_animations_copy.size(); ++i) { | |
258 if (!HasAnimation(running_animations_copy[i].sequence)) | |
259 continue; | |
260 | |
261 base::TimeDelta delta = now - running_animations_copy[i].start_time; | |
262 if (delta >= running_animations_copy[i].sequence->duration() && | |
263 !running_animations_copy[i].sequence->is_cyclic()) { | |
264 FinishAnimation(running_animations_copy[i].sequence); | |
265 } else if (ProgressAnimation(running_animations_copy[i].sequence, delta)) | |
266 needs_redraw = true; | |
267 } | |
268 | |
269 if (needs_redraw) | |
270 delegate()->ScheduleDrawForAnimation(); | |
271 } | |
272 | |
273 void LayerAnimator::SetStartTime(base::TimeTicks start_time) { | |
274 // Do nothing. | |
275 } | |
276 | |
277 base::TimeDelta LayerAnimator::GetTimerInterval() const { | |
278 return kTimerInterval; | |
279 } | |
280 | |
281 void LayerAnimator::UpdateAnimationState() { | |
282 if (disable_timer_for_test_) | |
283 return; | |
284 | |
285 static ui::AnimationContainer* container = NULL; | |
286 if (!container) { | |
287 container = new AnimationContainer(); | |
288 container->AddRef(); | |
289 } | |
290 | |
291 const bool should_start = is_animating(); | |
292 if (should_start && !is_started_) | |
293 container->Start(this); | |
294 else if (!should_start && is_started_) | |
295 container->Stop(this); | |
296 | |
297 is_started_ = should_start; | |
298 } | |
299 | |
300 LayerAnimationSequence* LayerAnimator::RemoveAnimation( | |
301 LayerAnimationSequence* sequence) { | |
302 linked_ptr<LayerAnimationSequence> to_return; | |
303 | |
304 // First remove from running animations | |
305 for (RunningAnimations::iterator iter = running_animations_.begin(); | |
306 iter != running_animations_.end(); ++iter) { | |
307 if ((*iter).sequence == sequence) { | |
308 running_animations_.erase(iter); | |
309 break; | |
310 } | |
311 } | |
312 | |
313 // Then remove from the queue | |
314 for (AnimationQueue::iterator queue_iter = animation_queue_.begin(); | |
315 queue_iter != animation_queue_.end(); ++queue_iter) { | |
316 if ((*queue_iter) == sequence) { | |
317 to_return = *queue_iter; | |
318 animation_queue_.erase(queue_iter); | |
319 break; | |
320 } | |
321 } | |
322 | |
323 return to_return.release(); | |
324 } | |
325 | |
326 void LayerAnimator::FinishAnimation(LayerAnimationSequence* sequence) { | |
327 scoped_ptr<LayerAnimationSequence> removed(RemoveAnimation(sequence)); | |
328 sequence->Progress(sequence->duration(), delegate()); | |
329 ProcessQueue(); | |
330 UpdateAnimationState(); | |
331 } | |
332 | |
333 void LayerAnimator::FinishAnyAnimationWithZeroDuration() { | |
334 // Special case: if we've started a 0 duration animation, just finish it now | |
335 // and get rid of it. We need to make a copy because Progress may indirectly | |
336 // cause new animations to start running. | |
337 RunningAnimations running_animations_copy = running_animations_; | |
338 for (size_t i = 0; i < running_animations_copy.size(); ++i) { | |
339 if (!HasAnimation(running_animations_copy[i].sequence)) | |
340 continue; | |
341 | |
342 if (running_animations_copy[i].sequence->duration() == base::TimeDelta()) { | |
343 running_animations_copy[i].sequence->Progress( | |
344 running_animations_copy[i].sequence->duration(), delegate()); | |
345 scoped_ptr<LayerAnimationSequence> removed( | |
346 RemoveAnimation(running_animations_copy[i].sequence)); | |
347 } | |
348 } | |
349 ProcessQueue(); | |
350 UpdateAnimationState(); | |
351 } | |
352 | |
353 void LayerAnimator::ClearAnimations() { | |
354 // Abort should never affect the set of running animations, but just in case | |
355 // clients are badly behaved, we will use a copy of the running animations. | |
356 RunningAnimations running_animations_copy = running_animations_; | |
357 for (size_t i = 0; i < running_animations_copy.size(); ++i) { | |
358 if (!HasAnimation(running_animations_copy[i].sequence)) | |
359 continue; | |
360 | |
361 scoped_ptr<LayerAnimationSequence> removed( | |
362 RemoveAnimation(running_animations_copy[i].sequence)); | |
363 if (removed.get()) | |
364 removed->Abort(); | |
365 } | |
366 // This *should* have cleared the list of running animations. | |
367 DCHECK(running_animations_.empty()); | |
368 running_animations_.clear(); | |
369 animation_queue_.clear(); | |
370 UpdateAnimationState(); | |
371 } | |
372 | |
373 LayerAnimator::RunningAnimation* LayerAnimator::GetRunningAnimation( | |
374 LayerAnimationElement::AnimatableProperty property) { | |
375 for (RunningAnimations::iterator iter = running_animations_.begin(); | |
376 iter != running_animations_.end(); ++iter) { | |
377 if ((*iter).sequence->properties().find(property) != | |
378 (*iter).sequence->properties().end()) | |
379 return &(*iter); | |
380 } | |
381 return NULL; | |
382 } | |
383 | |
384 void LayerAnimator::AddToQueueIfNotPresent(LayerAnimationSequence* animation) { | |
385 // If we don't have the animation in the queue yet, add it. | |
386 bool found_sequence = false; | |
387 for (AnimationQueue::iterator queue_iter = animation_queue_.begin(); | |
388 queue_iter != animation_queue_.end(); ++queue_iter) { | |
389 if ((*queue_iter) == animation) { | |
390 found_sequence = true; | |
391 break; | |
392 } | |
393 } | |
394 | |
395 if (!found_sequence) | |
396 animation_queue_.push_front(make_linked_ptr(animation)); | |
397 } | |
398 | |
399 void LayerAnimator::RemoveAllAnimationsWithACommonProperty( | |
400 LayerAnimationSequence* sequence, bool abort) { | |
401 // For all the running animations, if they animate the same property, | |
402 // progress them to the end and remove them. Note, Aborting or Progressing | |
403 // animations may affect the collection of running animations, so we need to | |
404 // operate on a copy. | |
405 RunningAnimations running_animations_copy = running_animations_; | |
406 for (size_t i = 0; i < running_animations_copy.size(); ++i) { | |
407 if (!HasAnimation(running_animations_copy[i].sequence)) | |
408 continue; | |
409 | |
410 if (running_animations_copy[i].sequence->HasCommonProperty( | |
411 sequence->properties())) { | |
412 scoped_ptr<LayerAnimationSequence> removed( | |
413 RemoveAnimation(running_animations_copy[i].sequence)); | |
414 if (abort) | |
415 running_animations_copy[i].sequence->Abort(); | |
416 else | |
417 running_animations_copy[i].sequence->Progress( | |
418 running_animations_copy[i].sequence->duration(), delegate()); | |
419 } | |
420 } | |
421 | |
422 // Same for the queued animations that haven't been started. Again, we'll | |
423 // need to operate on a copy. | |
424 std::vector<LayerAnimationSequence*> sequences; | |
425 for (AnimationQueue::iterator queue_iter = animation_queue_.begin(); | |
426 queue_iter != animation_queue_.end(); ++queue_iter) | |
427 sequences.push_back((*queue_iter).get()); | |
428 | |
429 for (size_t i = 0; i < sequences.size(); ++i) { | |
430 if (!HasAnimation(sequences[i])) | |
431 continue; | |
432 | |
433 if (sequences[i]->HasCommonProperty(sequence->properties())) { | |
434 scoped_ptr<LayerAnimationSequence> removed( | |
435 RemoveAnimation(sequences[i])); | |
436 if (abort) | |
437 sequences[i]->Abort(); | |
438 else | |
439 sequences[i]->Progress(sequences[i]->duration(), delegate()); | |
440 } | |
441 } | |
442 } | |
443 | |
444 void LayerAnimator::ImmediatelySetNewTarget(LayerAnimationSequence* sequence) { | |
445 // Ensure that sequence is disposed of when this function completes. | |
446 scoped_ptr<LayerAnimationSequence> to_dispose(sequence); | |
447 const bool abort = false; | |
448 RemoveAllAnimationsWithACommonProperty(sequence, abort); | |
449 LayerAnimationSequence* removed = RemoveAnimation(sequence); | |
450 DCHECK(removed == NULL || removed == sequence); | |
451 sequence->Progress(sequence->duration(), delegate()); | |
452 } | |
453 | |
454 void LayerAnimator::ImmediatelyAnimateToNewTarget( | |
455 LayerAnimationSequence* sequence) { | |
456 const bool abort = true; | |
457 RemoveAllAnimationsWithACommonProperty(sequence, abort); | |
458 AddToQueueIfNotPresent(sequence); | |
459 StartSequenceImmediately(sequence); | |
460 } | |
461 | |
462 void LayerAnimator::EnqueueNewAnimation(LayerAnimationSequence* sequence) { | |
463 // It is assumed that if there was no conflicting animation, we would | |
464 // not have been called. No need to check for a collision; just | |
465 // add to the queue. | |
466 animation_queue_.push_back(make_linked_ptr(sequence)); | |
467 ProcessQueue(); | |
468 } | |
469 | |
470 void LayerAnimator::ReplaceQueuedAnimations(LayerAnimationSequence* sequence) { | |
471 // Remove all animations that aren't running. Note: at each iteration i is | |
472 // incremented or an element is removed from the queue, so | |
473 // animation_queue_.size() - i is always decreasing and we are always making | |
474 // progress towards the loop terminating. | |
475 for (size_t i = 0; i < animation_queue_.size();) { | |
476 bool is_running = false; | |
477 for (RunningAnimations::const_iterator iter = running_animations_.begin(); | |
478 iter != running_animations_.end(); ++iter) { | |
479 if ((*iter).sequence == animation_queue_[i]) { | |
480 is_running = true; | |
481 break; | |
482 } | |
483 } | |
484 if (!is_running) | |
485 scoped_ptr<LayerAnimationSequence>( | |
486 RemoveAnimation(animation_queue_[i].get())); | |
487 else | |
488 ++i; | |
489 } | |
490 animation_queue_.push_back(make_linked_ptr(sequence)); | |
491 ProcessQueue(); | |
492 } | |
493 | |
494 void LayerAnimator::ProcessQueue() { | |
495 bool started_sequence = false; | |
496 do { | |
497 started_sequence = false; | |
498 // Build a list of all currently animated properties. | |
499 LayerAnimationElement::AnimatableProperties animated; | |
500 for (RunningAnimations::const_iterator iter = running_animations_.begin(); | |
501 iter != running_animations_.end(); ++iter) { | |
502 animated.insert((*iter).sequence->properties().begin(), | |
503 (*iter).sequence->properties().end()); | |
504 } | |
505 | |
506 // Try to find an animation that doesn't conflict with an animated | |
507 // property or a property that will be animated before it. Note: starting | |
508 // the animation may indirectly cause more animations to be started, so we | |
509 // need to operate on a copy. | |
510 std::vector<LayerAnimationSequence*> sequences; | |
511 for (AnimationQueue::iterator queue_iter = animation_queue_.begin(); | |
512 queue_iter != animation_queue_.end(); ++queue_iter) | |
513 sequences.push_back((*queue_iter).get()); | |
514 | |
515 for (size_t i = 0; i < sequences.size(); ++i) { | |
516 if (!sequences[i]->HasCommonProperty(animated)) { | |
517 StartSequenceImmediately(sequences[i]); | |
518 started_sequence = true; | |
519 break; | |
520 } | |
521 | |
522 // Animation couldn't be started. Add its properties to the collection so | |
523 // that we don't start a conflicting animation. For example, if our queue | |
524 // has the elements { {T,B}, {B} } (that is, an element that animates both | |
525 // the transform and the bounds followed by an element that animates the | |
526 // bounds), and we're currently animating the transform, we can't start | |
527 // the first element because it animates the transform, too. We cannot | |
528 // start the second element, either, because the first element animates | |
529 // bounds too, and needs to go first. | |
530 animated.insert(sequences[i]->properties().begin(), | |
531 sequences[i]->properties().end()); | |
532 } | |
533 | |
534 // If we started a sequence, try again. We may be able to start several. | |
535 } while (started_sequence); | |
536 } | |
537 | |
538 bool LayerAnimator::StartSequenceImmediately(LayerAnimationSequence* sequence) { | |
539 // Ensure that no one is animating one of the sequence's properties already. | |
540 for (RunningAnimations::const_iterator iter = running_animations_.begin(); | |
541 iter != running_animations_.end(); ++iter) { | |
542 if ((*iter).sequence->HasCommonProperty(sequence->properties())) | |
543 return false; | |
544 } | |
545 | |
546 // All clear, actually start the sequence. Note: base::TimeTicks::Now has | |
547 // a resolution that can be as bad as 15ms. If this causes glitches in the | |
548 // animations, this can be switched to HighResNow() (animation uses Now() | |
549 // internally). | |
550 base::TimeTicks start_time = is_animating() | |
551 ? last_step_time_ | |
552 : base::TimeTicks::Now(); | |
553 | |
554 running_animations_.push_back(RunningAnimation(sequence, start_time)); | |
555 | |
556 // Need to keep a reference to the animation. | |
557 AddToQueueIfNotPresent(sequence); | |
558 | |
559 // Ensure that animations get stepped at their start time. | |
560 Step(start_time); | |
561 | |
562 return true; | |
563 } | |
564 | |
565 void LayerAnimator::GetTargetValue( | |
566 LayerAnimationElement::TargetValue* target) const { | |
567 for (AnimationQueue::const_iterator iter = animation_queue_.begin(); | |
568 iter != animation_queue_.end(); ++iter) { | |
569 (*iter)->GetTargetValue(target); | |
570 } | |
571 } | |
572 | |
573 void LayerAnimator::OnScheduled(LayerAnimationSequence* sequence) { | |
574 if (observers_.might_have_observers()) { | |
575 ObserverListBase<LayerAnimationObserver>::Iterator it(observers_); | |
576 LayerAnimationObserver* obs; | |
577 while ((obs = it.GetNext()) != NULL) { | |
578 sequence->AddObserver(obs); | |
579 } | |
580 } | |
581 sequence->OnScheduled(); | |
582 } | |
583 | |
584 } // namespace ui | |
OLD | NEW |