1// Copyright (c) 2011 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/animation/animation_container.h"
6
7#include "ui/gfx/animation/animation_container_element.h"
8#include "ui/gfx/animation/animation_container_observer.h"
9#include "ui/gfx/frame_time.h"
10
11using base::TimeDelta;
12using base::TimeTicks;
13
14namespace gfx {
15
16AnimationContainer::AnimationContainer()
17    : last_tick_time_(gfx::FrameTime::Now()),
18      observer_(NULL) {
19}
20
21AnimationContainer::~AnimationContainer() {
22  // The animations own us and stop themselves before being deleted. If
23  // elements_ is not empty, something is wrong.
24  DCHECK(elements_.empty());
25}
26
27void AnimationContainer::Start(AnimationContainerElement* element) {
28  DCHECK(elements_.count(element) == 0);  // Start should only be invoked if the
29                                          // element isn't running.
30
31  if (elements_.empty()) {
32    last_tick_time_ = gfx::FrameTime::Now();
33    SetMinTimerInterval(element->GetTimerInterval());
34  } else if (element->GetTimerInterval() < min_timer_interval_) {
35    SetMinTimerInterval(element->GetTimerInterval());
36  }
37
38  element->SetStartTime(last_tick_time_);
39  elements_.insert(element);
40}
41
42void AnimationContainer::Stop(AnimationContainerElement* element) {
43  DCHECK(elements_.count(element) > 0);  // The element must be running.
44
45  elements_.erase(element);
46
47  if (elements_.empty()) {
48    timer_.Stop();
49    if (observer_)
50      observer_->AnimationContainerEmpty(this);
51  } else {
52    TimeDelta min_timer_interval = GetMinInterval();
53    if (min_timer_interval > min_timer_interval_)
54      SetMinTimerInterval(min_timer_interval);
55  }
56}
57
58void AnimationContainer::Run() {
59  // We notify the observer after updating all the elements. If all the elements
60  // are deleted as a result of updating then our ref count would go to zero and
61  // we would be deleted before we notify our observer. We add a reference to
62  // ourself here to make sure we're still valid after running all the elements.
63  scoped_refptr<AnimationContainer> this_ref(this);
64
65  TimeTicks current_time = gfx::FrameTime::Now();
66
67  last_tick_time_ = current_time;
68
69  // Make a copy of the elements to iterate over so that if any elements are
70  // removed as part of invoking Step there aren't any problems.
71  Elements elements = elements_;
72
73  for (Elements::const_iterator i = elements.begin();
74       i != elements.end(); ++i) {
75    // Make sure the element is still valid.
76    if (elements_.find(*i) != elements_.end())
77      (*i)->Step(current_time);
78  }
79
80  if (observer_)
81    observer_->AnimationContainerProgressed(this);
82}
83
84void AnimationContainer::SetMinTimerInterval(base::TimeDelta delta) {
85  // This doesn't take into account how far along the current element is, but
86  // that shouldn't be a problem for uses of Animation/AnimationContainer.
87  timer_.Stop();
88  min_timer_interval_ = delta;
89  timer_.Start(FROM_HERE, min_timer_interval_, this, &AnimationContainer::Run);
90}
91
92TimeDelta AnimationContainer::GetMinInterval() {
93  DCHECK(!elements_.empty());
94
95  TimeDelta min;
96  Elements::const_iterator i = elements_.begin();
97  min = (*i)->GetTimerInterval();
98  for (++i; i != elements_.end(); ++i) {
99    if ((*i)->GetTimerInterval() < min)
100      min = (*i)->GetTimerInterval();
101  }
102  return min;
103}
104
105}  // namespace gfx
106