|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "ui/compositor/layer_animation_sequence.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <iterator> | 
|  |  | 
|  | #include "base/debug/trace_event.h" | 
|  | #include "cc/animation/animation_id_provider.h" | 
|  | #include "ui/compositor/layer_animation_delegate.h" | 
|  | #include "ui/compositor/layer_animation_element.h" | 
|  | #include "ui/compositor/layer_animation_observer.h" | 
|  |  | 
|  | namespace ui { | 
|  |  | 
|  | LayerAnimationSequence::LayerAnimationSequence() | 
|  | : properties_(LayerAnimationElement::UNKNOWN), | 
|  | is_cyclic_(false), | 
|  | last_element_(0), | 
|  | waiting_for_group_start_(false), | 
|  | animation_group_id_(0), | 
|  | last_progressed_fraction_(0.0), | 
|  | weak_ptr_factory_(this) { | 
|  | } | 
|  |  | 
|  | LayerAnimationSequence::LayerAnimationSequence(LayerAnimationElement* element) | 
|  | : properties_(LayerAnimationElement::UNKNOWN), | 
|  | is_cyclic_(false), | 
|  | last_element_(0), | 
|  | waiting_for_group_start_(false), | 
|  | animation_group_id_(0), | 
|  | last_progressed_fraction_(0.0), | 
|  | weak_ptr_factory_(this) { | 
|  | AddElement(element); | 
|  | } | 
|  |  | 
|  | LayerAnimationSequence::~LayerAnimationSequence() { | 
|  | FOR_EACH_OBSERVER(LayerAnimationObserver, | 
|  | observers_, | 
|  | DetachedFromSequence(this, true)); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::Start(LayerAnimationDelegate* delegate) { | 
|  | DCHECK(start_time_ != base::TimeTicks()); | 
|  | last_progressed_fraction_ = 0.0; | 
|  | if (elements_.empty()) | 
|  | return; | 
|  |  | 
|  | elements_[0]->set_requested_start_time(start_time_); | 
|  | elements_[0]->Start(delegate, animation_group_id_); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::Progress(base::TimeTicks now, | 
|  | LayerAnimationDelegate* delegate) { | 
|  | DCHECK(start_time_ != base::TimeTicks()); | 
|  | bool redraw_required = false; | 
|  |  | 
|  | if (elements_.empty()) | 
|  | return; | 
|  |  | 
|  | if (last_element_ == 0) | 
|  | last_start_ = start_time_; | 
|  |  | 
|  | size_t current_index = last_element_ % elements_.size(); | 
|  | base::TimeDelta element_duration; | 
|  | while (is_cyclic_ || last_element_ < elements_.size()) { | 
|  | elements_[current_index]->set_requested_start_time(last_start_); | 
|  | if (!elements_[current_index]->IsFinished(now, &element_duration)) | 
|  | break; | 
|  |  | 
|  | // Let the element we're passing finish. | 
|  | if (elements_[current_index]->ProgressToEnd(delegate)) | 
|  | redraw_required = true; | 
|  | last_start_ += element_duration; | 
|  | ++last_element_; | 
|  | last_progressed_fraction_ = | 
|  | elements_[current_index]->last_progressed_fraction(); | 
|  | current_index = last_element_ % elements_.size(); | 
|  | } | 
|  |  | 
|  | if (is_cyclic_ || last_element_ < elements_.size()) { | 
|  | if (!elements_[current_index]->Started()) { | 
|  | animation_group_id_ = cc::AnimationIdProvider::NextGroupId(); | 
|  | elements_[current_index]->Start(delegate, animation_group_id_); | 
|  | } | 
|  | base::WeakPtr<LayerAnimationSequence> alive(weak_ptr_factory_.GetWeakPtr()); | 
|  | if (elements_[current_index]->Progress(now, delegate)) | 
|  | redraw_required = true; | 
|  | if (!alive) | 
|  | return; | 
|  | last_progressed_fraction_ = | 
|  | elements_[current_index]->last_progressed_fraction(); | 
|  | } | 
|  |  | 
|  | // Since the delegate may be deleted due to the notifications below, it is | 
|  | // important that we schedule a draw before sending them. | 
|  | if (redraw_required) | 
|  | delegate->ScheduleDrawForAnimation(); | 
|  |  | 
|  | if (!is_cyclic_ && last_element_ == elements_.size()) { | 
|  | last_element_ = 0; | 
|  | waiting_for_group_start_ = false; | 
|  | animation_group_id_ = 0; | 
|  | NotifyEnded(); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool LayerAnimationSequence::IsFinished(base::TimeTicks time) { | 
|  | if (is_cyclic_ || waiting_for_group_start_) | 
|  | return false; | 
|  |  | 
|  | if (elements_.empty()) | 
|  | return true; | 
|  |  | 
|  | if (last_element_ == 0) | 
|  | last_start_ = start_time_; | 
|  |  | 
|  | base::TimeTicks current_start = last_start_; | 
|  | size_t current_index = last_element_; | 
|  | base::TimeDelta element_duration; | 
|  | while (current_index < elements_.size()) { | 
|  | elements_[current_index]->set_requested_start_time(current_start); | 
|  | if (!elements_[current_index]->IsFinished(time, &element_duration)) | 
|  | break; | 
|  |  | 
|  | current_start += element_duration; | 
|  | ++current_index; | 
|  | } | 
|  |  | 
|  | return (current_index == elements_.size()); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::ProgressToEnd(LayerAnimationDelegate* delegate) { | 
|  | bool redraw_required = false; | 
|  |  | 
|  | if (elements_.empty()) | 
|  | return; | 
|  |  | 
|  | size_t current_index = last_element_ % elements_.size(); | 
|  | while (current_index < elements_.size()) { | 
|  | if (elements_[current_index]->ProgressToEnd(delegate)) | 
|  | redraw_required = true; | 
|  | last_progressed_fraction_ = | 
|  | elements_[current_index]->last_progressed_fraction(); | 
|  | ++current_index; | 
|  | ++last_element_; | 
|  | } | 
|  |  | 
|  | if (redraw_required) | 
|  | delegate->ScheduleDrawForAnimation(); | 
|  |  | 
|  | if (!is_cyclic_) { | 
|  | last_element_ = 0; | 
|  | waiting_for_group_start_ = false; | 
|  | animation_group_id_ = 0; | 
|  | NotifyEnded(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::GetTargetValue( | 
|  | LayerAnimationElement::TargetValue* target) const { | 
|  | if (is_cyclic_) | 
|  | return; | 
|  |  | 
|  | for (size_t i = last_element_; i < elements_.size(); ++i) | 
|  | elements_[i]->GetTargetValue(target); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::Abort(LayerAnimationDelegate* delegate) { | 
|  | size_t current_index = last_element_ % elements_.size(); | 
|  | while (current_index < elements_.size()) { | 
|  | elements_[current_index]->Abort(delegate); | 
|  | ++current_index; | 
|  | } | 
|  | last_element_ = 0; | 
|  | waiting_for_group_start_ = false; | 
|  | NotifyAborted(); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::AddElement(LayerAnimationElement* element) { | 
|  | properties_ |= element->properties(); | 
|  | elements_.push_back(make_linked_ptr(element)); | 
|  | } | 
|  |  | 
|  | bool LayerAnimationSequence::HasConflictingProperty( | 
|  | LayerAnimationElement::AnimatableProperties other) const { | 
|  | return (properties_ & other) != LayerAnimationElement::UNKNOWN; | 
|  | } | 
|  |  | 
|  | bool LayerAnimationSequence::IsFirstElementThreaded() const { | 
|  | if (!elements_.empty()) | 
|  | return elements_[0]->IsThreaded(); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::AddObserver(LayerAnimationObserver* observer) { | 
|  | if (!observers_.HasObserver(observer)) { | 
|  | observers_.AddObserver(observer); | 
|  | observer->AttachedToSequence(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::RemoveObserver(LayerAnimationObserver* observer) { | 
|  | observers_.RemoveObserver(observer); | 
|  | observer->DetachedFromSequence(this, true); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::OnThreadedAnimationStarted( | 
|  | const cc::AnimationEvent& event) { | 
|  | if (elements_.empty() || event.group_id != animation_group_id_) | 
|  | return; | 
|  |  | 
|  | size_t current_index = last_element_ % elements_.size(); | 
|  | LayerAnimationElement::AnimatableProperties element_properties = | 
|  | elements_[current_index]->properties(); | 
|  | LayerAnimationElement::AnimatableProperty event_property = | 
|  | LayerAnimationElement::ToAnimatableProperty(event.target_property); | 
|  | DCHECK(element_properties & event_property); | 
|  | elements_[current_index]->set_effective_start_time(event.monotonic_time); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::OnScheduled() { | 
|  | NotifyScheduled(); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::OnAnimatorDestroyed() { | 
|  | if (observers_.might_have_observers()) { | 
|  | ObserverListBase<LayerAnimationObserver>::Iterator it(observers_); | 
|  | LayerAnimationObserver* obs; | 
|  | while ((obs = it.GetNext()) != NULL) { | 
|  | if (!obs->RequiresNotificationWhenAnimatorDestroyed()) { | 
|  | // Remove the observer, but do not allow notifications to be sent. | 
|  | observers_.RemoveObserver(obs); | 
|  | obs->DetachedFromSequence(this, false); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t LayerAnimationSequence::size() const { | 
|  | return elements_.size(); | 
|  | } | 
|  |  | 
|  | LayerAnimationElement* LayerAnimationSequence::FirstElement() const { | 
|  | if (elements_.empty()) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | return elements_[0].get(); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::NotifyScheduled() { | 
|  | FOR_EACH_OBSERVER(LayerAnimationObserver, | 
|  | observers_, | 
|  | OnLayerAnimationScheduled(this)); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::NotifyEnded() { | 
|  | FOR_EACH_OBSERVER(LayerAnimationObserver, | 
|  | observers_, | 
|  | OnLayerAnimationEnded(this)); | 
|  | } | 
|  |  | 
|  | void LayerAnimationSequence::NotifyAborted() { | 
|  | FOR_EACH_OBSERVER(LayerAnimationObserver, | 
|  | observers_, | 
|  | OnLayerAnimationAborted(this)); | 
|  | } | 
|  |  | 
|  | LayerAnimationElement* LayerAnimationSequence::CurrentElement() const { | 
|  | if (elements_.empty()) | 
|  | return NULL; | 
|  |  | 
|  | size_t current_index = last_element_ % elements_.size(); | 
|  | return elements_[current_index].get(); | 
|  | } | 
|  |  | 
|  | }  // namespace ui |