summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cc/animation/animation.cc5
-rw-r--r--cc/animation/animation.h24
-rw-r--r--cc/animation/layer_animation_controller.cc528
-rw-r--r--cc/animation/layer_animation_controller.h25
-rw-r--r--cc/animation/layer_animation_controller_unittest.cc301
-rw-r--r--cc/trees/layer_tree_host.cc1
-rw-r--r--cc/trees/layer_tree_host_impl.cc15
-rw-r--r--cc/trees/layer_tree_host_impl.h1
-rw-r--r--cc/trees/layer_tree_host_unittest_animation.cc79
9 files changed, 743 insertions, 236 deletions
diff --git a/cc/animation/animation.cc b/cc/animation/animation.cc
index 81659cb..1af35d2 100644
--- a/cc/animation/animation.cc
+++ b/cc/animation/animation.cc
@@ -73,7 +73,10 @@ Animation::Animation(scoped_ptr<AnimationCurve> curve,
pause_time_(0),
total_paused_time_(0),
is_controlling_instance_(false),
- is_impl_only_(false) {}
+ is_impl_only_(false),
+ affects_active_observers_(true),
+ affects_pending_observers_(true) {
+}
Animation::~Animation() {
if (run_state_ == Running || run_state_ == Paused)
diff --git a/cc/animation/animation.h b/cc/animation/animation.h
index 3cfc8bb..ecc48b5 100644
--- a/cc/animation/animation.h
+++ b/cc/animation/animation.h
@@ -126,6 +126,16 @@ class CC_EXPORT Animation {
void set_is_impl_only(bool is_impl_only) { is_impl_only_ = is_impl_only; }
bool is_impl_only() const { return is_impl_only_; }
+ void set_affects_active_observers(bool affects_active_observers) {
+ affects_active_observers_ = affects_active_observers;
+ }
+ bool affects_active_observers() const { return affects_active_observers_; }
+
+ void set_affects_pending_observers(bool affects_pending_observers) {
+ affects_pending_observers_ = affects_pending_observers;
+ }
+ bool affects_pending_observers() const { return affects_pending_observers_; }
+
private:
Animation(scoped_ptr<AnimationCurve> curve,
int animation_id,
@@ -182,6 +192,20 @@ class CC_EXPORT Animation {
bool is_impl_only_;
+ // When pushed from a main-thread controller to a compositor-thread
+ // controller, an animation will initially only affect pending observers
+ // (corresponding to layers in the pending tree). Animations that only
+ // affect pending observers are able to reach the Starting state and tick
+ // pending observers, but cannot proceed any further and do not tick active
+ // observers. After activation, such animations affect both kinds of observers
+ // and are able to proceed past the Starting state. When the removal of
+ // an animation is pushed from a main-thread controller to a
+ // compositor-thread controller, this initially only makes the animation
+ // stop affecting pending observers. After activation, such animations no
+ // longer affect any observers, and are deleted.
+ bool affects_active_observers_;
+ bool affects_pending_observers_;
+
DISALLOW_COPY_AND_ASSIGN(Animation);
};
diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc
index 464801c..62b8a5a 100644
--- a/cc/animation/layer_animation_controller.cc
+++ b/cc/animation/layer_animation_controller.cc
@@ -40,10 +40,10 @@ scoped_refptr<LayerAnimationController> LayerAnimationController::Create(
void LayerAnimationController::PauseAnimation(int animation_id,
double time_offset) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->id() == animation_id) {
- active_animations_[i]->SetRunState(
- Animation::Paused, time_offset + active_animations_[i]->start_time());
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->id() == animation_id) {
+ animations_[i]->SetRunState(Animation::Paused,
+ time_offset + animations_[i]->start_time());
}
}
}
@@ -59,12 +59,11 @@ struct HasAnimationId {
};
void LayerAnimationController::RemoveAnimation(int animation_id) {
- ScopedPtrVector<Animation>& animations = active_animations_;
- animations.erase(cc::remove_if(&animations,
- animations.begin(),
- animations.end(),
- HasAnimationId(animation_id)),
- animations.end());
+ animations_.erase(cc::remove_if(&animations_,
+ animations_.begin(),
+ animations_.end(),
+ HasAnimationId(animation_id)),
+ animations_.end());
UpdateActivation(NormalActivation);
}
@@ -84,22 +83,21 @@ struct HasAnimationIdAndProperty {
void LayerAnimationController::RemoveAnimation(
int animation_id,
Animation::TargetProperty target_property) {
- ScopedPtrVector<Animation>& animations = active_animations_;
- animations.erase(
- cc::remove_if(&animations,
- animations.begin(),
- animations.end(),
+ animations_.erase(
+ cc::remove_if(&animations_,
+ animations_.begin(),
+ animations_.end(),
HasAnimationIdAndProperty(animation_id, target_property)),
- animations.end());
+ animations_.end());
UpdateActivation(NormalActivation);
}
void LayerAnimationController::AbortAnimations(
Animation::TargetProperty target_property) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->target_property() == target_property &&
- !active_animations_[i]->is_finished())
- active_animations_[i]->SetRunState(Animation::Aborted, last_tick_time_);
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->target_property() == target_property &&
+ !animations_[i]->is_finished())
+ animations_[i]->SetRunState(Animation::Aborted, last_tick_time_);
}
}
@@ -139,8 +137,8 @@ void LayerAnimationController::AccumulatePropertyUpdates(
if (!events)
return;
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- Animation* animation = active_animations_[i];
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ Animation* animation = animations_[i];
if (!animation->is_impl_only())
continue;
@@ -221,34 +219,54 @@ void LayerAnimationController::UpdateState(bool start_ready_animations,
UpdateActivation(NormalActivation);
}
+struct AffectsNoObservers {
+ bool operator()(Animation* animation) const {
+ return !animation->affects_active_observers() &&
+ !animation->affects_pending_observers();
+ }
+};
+
+void LayerAnimationController::ActivateAnimations() {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ animations_[i]->set_affects_active_observers(
+ animations_[i]->affects_pending_observers());
+ }
+ animations_.erase(cc::remove_if(&animations_,
+ animations_.begin(),
+ animations_.end(),
+ AffectsNoObservers()),
+ animations_.end());
+ UpdateActivation(NormalActivation);
+}
+
void LayerAnimationController::AddAnimation(scoped_ptr<Animation> animation) {
- active_animations_.push_back(animation.Pass());
+ animations_.push_back(animation.Pass());
UpdateActivation(NormalActivation);
}
Animation* LayerAnimationController::GetAnimation(
int group_id,
Animation::TargetProperty target_property) const {
- for (size_t i = 0; i < active_animations_.size(); ++i)
- if (active_animations_[i]->group() == group_id &&
- active_animations_[i]->target_property() == target_property)
- return active_animations_[i];
+ for (size_t i = 0; i < animations_.size(); ++i)
+ if (animations_[i]->group() == group_id &&
+ animations_[i]->target_property() == target_property)
+ return animations_[i];
return 0;
}
Animation* LayerAnimationController::GetAnimation(
Animation::TargetProperty target_property) const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- size_t index = active_animations_.size() - i - 1;
- if (active_animations_[index]->target_property() == target_property)
- return active_animations_[index];
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ size_t index = animations_.size() - i - 1;
+ if (animations_[index]->target_property() == target_property)
+ return animations_[index];
}
return 0;
}
bool LayerAnimationController::HasActiveAnimation() const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (!active_animations_[i]->is_finished())
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (!animations_[i]->is_finished())
return true;
}
return false;
@@ -256,9 +274,9 @@ bool LayerAnimationController::HasActiveAnimation() const {
bool LayerAnimationController::IsAnimatingProperty(
Animation::TargetProperty target_property) const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (!active_animations_[i]->is_finished() &&
- active_animations_[i]->target_property() == target_property)
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (!animations_[i]->is_finished() &&
+ animations_[i]->target_property() == target_property)
return true;
}
return false;
@@ -293,13 +311,13 @@ void LayerAnimationController::NotifyAnimationStarted(
return;
}
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->group() == event.group_id &&
- active_animations_[i]->target_property() == event.target_property &&
- active_animations_[i]->needs_synchronized_start_time()) {
- active_animations_[i]->set_needs_synchronized_start_time(false);
- if (!active_animations_[i]->has_set_start_time())
- active_animations_[i]->set_start_time(event.monotonic_time);
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->group() == event.group_id &&
+ animations_[i]->target_property() == event.target_property &&
+ animations_[i]->needs_synchronized_start_time()) {
+ animations_[i]->set_needs_synchronized_start_time(false);
+ if (!animations_[i]->has_set_start_time())
+ animations_[i]->set_start_time(event.monotonic_time);
FOR_EACH_OBSERVER(LayerAnimationEventObserver, event_observers_,
OnAnimationStarted(event));
@@ -323,10 +341,10 @@ void LayerAnimationController::NotifyAnimationFinished(
return;
}
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->group() == event.group_id &&
- active_animations_[i]->target_property() == event.target_property) {
- active_animations_[i]->set_received_finished_event(true);
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->group() == event.group_id &&
+ animations_[i]->target_property() == event.target_property) {
+ animations_[i]->set_received_finished_event(true);
if (layer_animation_delegate_)
layer_animation_delegate_->NotifyAnimationFinished(
monotonic_time, event.target_property);
@@ -338,23 +356,26 @@ void LayerAnimationController::NotifyAnimationFinished(
void LayerAnimationController::NotifyAnimationAborted(
const AnimationEvent& event) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->group() == event.group_id &&
- active_animations_[i]->target_property() == event.target_property) {
- active_animations_[i]->SetRunState(Animation::Aborted,
- event.monotonic_time);
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->group() == event.group_id &&
+ animations_[i]->target_property() == event.target_property) {
+ animations_[i]->SetRunState(Animation::Aborted, event.monotonic_time);
}
}
}
void LayerAnimationController::NotifyAnimationPropertyUpdate(
const AnimationEvent& event) {
+ bool notify_active_observers = true;
+ bool notify_pending_observers = true;
switch (event.target_property) {
case Animation::Opacity:
- NotifyObserversOpacityAnimated(event.opacity);
+ NotifyObserversOpacityAnimated(
+ event.opacity, notify_active_observers, notify_pending_observers);
break;
case Animation::Transform:
- NotifyObserversTransformAnimated(event.transform);
+ NotifyObserversTransformAnimated(
+ event.transform, notify_active_observers, notify_pending_observers);
break;
default:
NOTREACHED();
@@ -384,10 +405,10 @@ void LayerAnimationController::RemoveEventObserver(
}
bool LayerAnimationController::HasFilterAnimationThatInflatesBounds() const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (!active_animations_[i]->is_finished() &&
- active_animations_[i]->target_property() == Animation::Filter &&
- active_animations_[i]
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (!animations_[i]->is_finished() &&
+ animations_[i]->target_property() == Animation::Filter &&
+ animations_[i]
->curve()
->ToFilterAnimationCurve()
->HasFilterThatMovesPixels())
@@ -420,13 +441,13 @@ bool LayerAnimationController::TransformAnimationBoundsForBox(
// that callers will take care of computing bounds based on the owning layer's
// actual transform.
*bounds = gfx::BoxF();
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->is_finished() ||
- active_animations_[i]->target_property() != Animation::Transform)
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != Animation::Transform)
continue;
const TransformAnimationCurve* transform_animation_curve =
- active_animations_[i]->curve()->ToTransformAnimationCurve();
+ animations_[i]->curve()->ToTransformAnimationCurve();
gfx::BoxF animation_bounds;
bool success =
transform_animation_curve->AnimatedBoundsForBox(box, &animation_bounds);
@@ -439,13 +460,13 @@ bool LayerAnimationController::TransformAnimationBoundsForBox(
}
bool LayerAnimationController::HasAnimationThatAffectsScale() const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->is_finished() ||
- active_animations_[i]->target_property() != Animation::Transform)
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != Animation::Transform)
continue;
const TransformAnimationCurve* transform_animation_curve =
- active_animations_[i]->curve()->ToTransformAnimationCurve();
+ animations_[i]->curve()->ToTransformAnimationCurve();
if (transform_animation_curve->AffectsScale())
return true;
}
@@ -454,13 +475,13 @@ bool LayerAnimationController::HasAnimationThatAffectsScale() const {
}
bool LayerAnimationController::HasOnlyTranslationTransforms() const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->is_finished() ||
- active_animations_[i]->target_property() != Animation::Transform)
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != Animation::Transform)
continue;
const TransformAnimationCurve* transform_animation_curve =
- active_animations_[i]->curve()->ToTransformAnimationCurve();
+ animations_[i]->curve()->ToTransformAnimationCurve();
if (!transform_animation_curve->IsTranslation())
return false;
}
@@ -470,13 +491,13 @@ bool LayerAnimationController::HasOnlyTranslationTransforms() const {
bool LayerAnimationController::MaximumScale(float* max_scale) const {
*max_scale = 0.f;
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->is_finished() ||
- active_animations_[i]->target_property() != Animation::Transform)
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != Animation::Transform)
continue;
const TransformAnimationCurve* transform_animation_curve =
- active_animations_[i]->curve()->ToTransformAnimationCurve();
+ animations_[i]->curve()->ToTransformAnimationCurve();
float animation_scale = 0.f;
if (!transform_animation_curve->MaximumScale(&animation_scale))
return false;
@@ -490,11 +511,11 @@ void LayerAnimationController::PushNewAnimationsToImplThread(
LayerAnimationController* controller_impl) const {
// Any new animations owned by the main thread's controller are cloned and
// add to the impl thread's controller.
- for (size_t i = 0; i < active_animations_.size(); ++i) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
// If the animation is already running on the impl thread, there is no
// need to copy it over.
- if (controller_impl->GetAnimation(active_animations_[i]->group(),
- active_animations_[i]->target_property()))
+ if (controller_impl->GetAnimation(animations_[i]->group(),
+ animations_[i]->target_property()))
continue;
// If the animation is not running on the impl thread, it does not
@@ -503,11 +524,11 @@ void LayerAnimationController::PushNewAnimationsToImplThread(
// have already notified that it has started and the main thread animation
// will no longer need
// a synchronized start time.
- if (!active_animations_[i]->needs_synchronized_start_time())
+ if (!animations_[i]->needs_synchronized_start_time())
continue;
// Scroll animations always start at the current scroll offset.
- if (active_animations_[i]->target_property() == Animation::ScrollOffset) {
+ if (animations_[i]->target_property() == Animation::ScrollOffset) {
gfx::Vector2dF current_scroll_offset;
if (controller_impl->value_provider_) {
current_scroll_offset =
@@ -517,107 +538,128 @@ void LayerAnimationController::PushNewAnimationsToImplThread(
// scroll offset will be up-to-date.
current_scroll_offset = value_provider_->ScrollOffsetForAnimation();
}
- active_animations_[i]->curve()->ToScrollOffsetAnimationCurve()
- ->SetInitialValue(current_scroll_offset);
+ animations_[i]->curve()->ToScrollOffsetAnimationCurve()->SetInitialValue(
+ current_scroll_offset);
}
// The new animation should be set to run as soon as possible.
Animation::RunState initial_run_state =
Animation::WaitingForTargetAvailability;
- scoped_ptr<Animation> to_add(active_animations_[i]->CloneAndInitialize(
- initial_run_state));
+ scoped_ptr<Animation> to_add(
+ animations_[i]->CloneAndInitialize(initial_run_state));
DCHECK(!to_add->needs_synchronized_start_time());
+ to_add->set_affects_active_observers(false);
controller_impl->AddAnimation(to_add.Pass());
}
}
-struct IsCompleted {
- explicit IsCompleted(const LayerAnimationController& main_thread_controller)
- : main_thread_controller_(main_thread_controller) {}
- bool operator()(Animation* animation) const {
- if (animation->is_impl_only()) {
- return (animation->run_state() == Animation::WaitingForDeletion);
- } else {
- return !main_thread_controller_.GetAnimation(
- animation->group(),
- animation->target_property());
- }
+static bool IsCompleted(
+ Animation* animation,
+ const LayerAnimationController* main_thread_controller) {
+ if (animation->is_impl_only()) {
+ return (animation->run_state() == Animation::WaitingForDeletion);
+ } else {
+ return !main_thread_controller->GetAnimation(animation->group(),
+ animation->target_property());
}
+}
- private:
- const LayerAnimationController& main_thread_controller_;
-};
+static bool AffectsActiveOnlyAndIsWaitingForDeletion(Animation* animation) {
+ return animation->run_state() == Animation::WaitingForDeletion &&
+ !animation->affects_pending_observers();
+}
void LayerAnimationController::RemoveAnimationsCompletedOnMainThread(
LayerAnimationController* controller_impl) const {
- // Delete all impl thread animations for which there is no corresponding
- // main thread animation. Each iteration,
- // controller->active_animations_.size() is decremented or i is incremented
- // guaranteeing progress towards loop termination.
- ScopedPtrVector<Animation>& animations =
- controller_impl->active_animations_;
+ // Animations removed on the main thread should no longer affect pending
+ // observers, and should stop affecting active observers after the next call
+ // to ActivateAnimations. If already WaitingForDeletion, they can be removed
+ // immediately.
+ ScopedPtrVector<Animation>& animations = controller_impl->animations_;
+ for (size_t i = 0; i < animations.size(); ++i) {
+ if (IsCompleted(animations[i], this))
+ animations[i]->set_affects_pending_observers(false);
+ }
animations.erase(cc::remove_if(&animations,
animations.begin(),
animations.end(),
- IsCompleted(*this)),
+ AffectsActiveOnlyAndIsWaitingForDeletion),
animations.end());
}
void LayerAnimationController::PushPropertiesToImplThread(
LayerAnimationController* controller_impl) const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- Animation* current_impl =
- controller_impl->GetAnimation(
- active_animations_[i]->group(),
- active_animations_[i]->target_property());
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ Animation* current_impl = controller_impl->GetAnimation(
+ animations_[i]->group(), animations_[i]->target_property());
if (current_impl)
- active_animations_[i]->PushPropertiesTo(current_impl);
+ animations_[i]->PushPropertiesTo(current_impl);
}
}
void LayerAnimationController::StartAnimations(double monotonic_time) {
- // First collect running properties.
- TargetProperties blocked_properties;
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() == Animation::Starting ||
- active_animations_[i]->run_state() == Animation::Running)
- blocked_properties.insert(active_animations_[i]->target_property());
+ // First collect running properties affecting each type of observer.
+ TargetProperties blocked_properties_for_active_observers;
+ TargetProperties blocked_properties_for_pending_observers;
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() == Animation::Starting ||
+ animations_[i]->run_state() == Animation::Running) {
+ if (animations_[i]->affects_active_observers()) {
+ blocked_properties_for_active_observers.insert(
+ animations_[i]->target_property());
+ }
+ if (animations_[i]->affects_pending_observers()) {
+ blocked_properties_for_pending_observers.insert(
+ animations_[i]->target_property());
+ }
+ }
}
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() ==
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() ==
Animation::WaitingForTargetAvailability) {
// Collect all properties for animations with the same group id (they
// should all also be in the list of animations).
TargetProperties enqueued_properties;
- enqueued_properties.insert(active_animations_[i]->target_property());
- for (size_t j = i + 1; j < active_animations_.size(); ++j) {
- if (active_animations_[i]->group() == active_animations_[j]->group())
- enqueued_properties.insert(active_animations_[j]->target_property());
+ bool affects_active_observers =
+ animations_[i]->affects_active_observers();
+ bool affects_pending_observers =
+ animations_[i]->affects_pending_observers();
+ enqueued_properties.insert(animations_[i]->target_property());
+ for (size_t j = i + 1; j < animations_.size(); ++j) {
+ if (animations_[i]->group() == animations_[j]->group()) {
+ enqueued_properties.insert(animations_[j]->target_property());
+ affects_active_observers |=
+ animations_[j]->affects_active_observers();
+ affects_pending_observers |=
+ animations_[j]->affects_pending_observers();
+ }
}
// Check to see if intersection of the list of properties affected by
- // the group and the list of currently blocked properties is null. In
- // any case, the group's target properties need to be added to the list
- // of blocked properties.
+ // the group and the list of currently blocked properties is null, taking
+ // into account the type(s) of observers affected by the group. In any
+ // case, the group's target properties need to be added to the lists of
+ // blocked properties.
bool null_intersection = true;
for (TargetProperties::iterator p_iter = enqueued_properties.begin();
p_iter != enqueued_properties.end();
++p_iter) {
- if (!blocked_properties.insert(*p_iter).second)
+ if (affects_active_observers &&
+ !blocked_properties_for_active_observers.insert(*p_iter).second)
+ null_intersection = false;
+ if (affects_pending_observers &&
+ !blocked_properties_for_pending_observers.insert(*p_iter).second)
null_intersection = false;
}
// If the intersection is null, then we are free to start the animations
// in the group.
if (null_intersection) {
- active_animations_[i]->SetRunState(
- Animation::Starting, monotonic_time);
- for (size_t j = i + 1; j < active_animations_.size(); ++j) {
- if (active_animations_[i]->group() ==
- active_animations_[j]->group()) {
- active_animations_[j]->SetRunState(
- Animation::Starting, monotonic_time);
+ animations_[i]->SetRunState(Animation::Starting, monotonic_time);
+ for (size_t j = i + 1; j < animations_.size(); ++j) {
+ if (animations_[i]->group() == animations_[j]->group()) {
+ animations_[j]->SetRunState(Animation::Starting, monotonic_time);
}
}
}
@@ -628,20 +670,20 @@ void LayerAnimationController::StartAnimations(double monotonic_time) {
void LayerAnimationController::PromoteStartedAnimations(
double monotonic_time,
AnimationEventsVector* events) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() == Animation::Starting) {
- active_animations_[i]->SetRunState(Animation::Running, monotonic_time);
- if (!active_animations_[i]->has_set_start_time() &&
- !active_animations_[i]->needs_synchronized_start_time())
- active_animations_[i]->set_start_time(monotonic_time);
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() == Animation::Starting &&
+ animations_[i]->affects_active_observers()) {
+ animations_[i]->SetRunState(Animation::Running, monotonic_time);
+ if (!animations_[i]->has_set_start_time() &&
+ !animations_[i]->needs_synchronized_start_time())
+ animations_[i]->set_start_time(monotonic_time);
if (events) {
- AnimationEvent started_event(
- AnimationEvent::Started,
- id_,
- active_animations_[i]->group(),
- active_animations_[i]->target_property(),
- monotonic_time);
- started_event.is_impl_only = active_animations_[i]->is_impl_only();
+ AnimationEvent started_event(AnimationEvent::Started,
+ id_,
+ animations_[i]->group(),
+ animations_[i]->target_property(),
+ monotonic_time);
+ started_event.is_impl_only = animations_[i]->is_impl_only();
events->push_back(started_event);
}
}
@@ -649,11 +691,11 @@ void LayerAnimationController::PromoteStartedAnimations(
}
void LayerAnimationController::MarkFinishedAnimations(double monotonic_time) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->IsFinishedAt(monotonic_time) &&
- active_animations_[i]->run_state() != Animation::Aborted &&
- active_animations_[i]->run_state() != Animation::WaitingForDeletion)
- active_animations_[i]->SetRunState(Animation::Finished, monotonic_time);
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->IsFinishedAt(monotonic_time) &&
+ animations_[i]->run_state() != Animation::Aborted &&
+ animations_[i]->run_state() != Animation::WaitingForDeletion)
+ animations_[i]->SetRunState(Animation::Finished, monotonic_time);
}
}
@@ -665,20 +707,19 @@ void LayerAnimationController::MarkAnimationsForDeletion(
// AnimationEvent::Finished event is sent or received. This means that if
// we don't have an events vector, we must ensure that non-aborted animations
// have received a finished event before marking them for deletion.
- for (size_t i = 0; i < active_animations_.size(); i++) {
- int group_id = active_animations_[i]->group();
- if (active_animations_[i]->run_state() == Animation::Aborted) {
- if (events && !active_animations_[i]->is_impl_only()) {
- AnimationEvent aborted_event(
- AnimationEvent::Aborted,
- id_,
- group_id,
- active_animations_[i]->target_property(),
- monotonic_time);
+ for (size_t i = 0; i < animations_.size(); i++) {
+ int group_id = animations_[i]->group();
+ if (animations_[i]->run_state() == Animation::Aborted) {
+ if (events && !animations_[i]->is_impl_only()) {
+ AnimationEvent aborted_event(AnimationEvent::Aborted,
+ id_,
+ group_id,
+ animations_[i]->target_property(),
+ monotonic_time);
events->push_back(aborted_event);
}
- active_animations_[i]->SetRunState(Animation::WaitingForDeletion,
- monotonic_time);
+ animations_[i]->SetRunState(Animation::WaitingForDeletion,
+ monotonic_time);
marked_animations_for_deletions = true;
continue;
}
@@ -689,18 +730,18 @@ void LayerAnimationController::MarkAnimationsForDeletion(
// on the impl thread, we only mark a Finished main thread animation for
// deletion once it has received a Finished event from the impl thread.
bool animation_i_will_send_or_has_received_finish_event =
- events || active_animations_[i]->received_finished_event();
+ events || animations_[i]->received_finished_event();
// If an animation is finished, and not already marked for deletion,
// find out if all other animations in the same group are also finished.
- if (active_animations_[i]->run_state() == Animation::Finished &&
+ if (animations_[i]->run_state() == Animation::Finished &&
animation_i_will_send_or_has_received_finish_event) {
all_anims_with_same_id_are_finished = true;
- for (size_t j = 0; j < active_animations_.size(); ++j) {
+ for (size_t j = 0; j < animations_.size(); ++j) {
bool animation_j_will_send_or_has_received_finish_event =
- events || active_animations_[j]->received_finished_event();
- if (group_id == active_animations_[j]->group() &&
- (!active_animations_[j]->is_finished() ||
- (active_animations_[j]->run_state() == Animation::Finished &&
+ events || animations_[j]->received_finished_event();
+ if (group_id == animations_[j]->group() &&
+ (!animations_[j]->is_finished() ||
+ (animations_[j]->run_state() == Animation::Finished &&
!animation_j_will_send_or_has_received_finish_event))) {
all_anims_with_same_id_are_finished = false;
break;
@@ -711,21 +752,20 @@ void LayerAnimationController::MarkAnimationsForDeletion(
// We now need to remove all animations with the same group id as
// group_id (and send along animation finished notifications, if
// necessary).
- for (size_t j = i; j < active_animations_.size(); j++) {
- if (active_animations_[j]->group() == group_id &&
- active_animations_[j]->run_state() != Animation::Aborted) {
+ for (size_t j = i; j < animations_.size(); j++) {
+ if (animations_[j]->group() == group_id &&
+ animations_[j]->run_state() != Animation::Aborted) {
if (events) {
- AnimationEvent finished_event(
- AnimationEvent::Finished,
- id_,
- active_animations_[j]->group(),
- active_animations_[j]->target_property(),
- monotonic_time);
- finished_event.is_impl_only = active_animations_[j]->is_impl_only();
+ AnimationEvent finished_event(AnimationEvent::Finished,
+ id_,
+ animations_[j]->group(),
+ animations_[j]->target_property(),
+ monotonic_time);
+ finished_event.is_impl_only = animations_[j]->is_impl_only();
events->push_back(finished_event);
}
- active_animations_[j]->SetRunState(Animation::WaitingForDeletion,
- monotonic_time);
+ animations_[j]->SetRunState(Animation::WaitingForDeletion,
+ monotonic_time);
}
}
marked_animations_for_deletions = true;
@@ -740,46 +780,54 @@ static bool IsWaitingForDeletion(Animation* animation) {
}
void LayerAnimationController::PurgeAnimationsMarkedForDeletion() {
- ScopedPtrVector<Animation>& animations = active_animations_;
- animations.erase(cc::remove_if(&animations,
- animations.begin(),
- animations.end(),
- IsWaitingForDeletion),
- animations.end());
+ animations_.erase(cc::remove_if(&animations_,
+ animations_.begin(),
+ animations_.end(),
+ IsWaitingForDeletion),
+ animations_.end());
}
void LayerAnimationController::TickAnimations(double monotonic_time) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() == Animation::Starting ||
- active_animations_[i]->run_state() == Animation::Running ||
- active_animations_[i]->run_state() == Animation::Paused) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() == Animation::Starting ||
+ animations_[i]->run_state() == Animation::Running ||
+ animations_[i]->run_state() == Animation::Paused) {
double trimmed =
- active_animations_[i]->TrimTimeToCurrentIteration(monotonic_time);
+ animations_[i]->TrimTimeToCurrentIteration(monotonic_time);
- switch (active_animations_[i]->target_property()) {
+ switch (animations_[i]->target_property()) {
case Animation::Transform: {
const TransformAnimationCurve* transform_animation_curve =
- active_animations_[i]->curve()->ToTransformAnimationCurve();
+ animations_[i]->curve()->ToTransformAnimationCurve();
const gfx::Transform transform =
transform_animation_curve->GetValue(trimmed);
- NotifyObserversTransformAnimated(transform);
+ NotifyObserversTransformAnimated(
+ transform,
+ animations_[i]->affects_active_observers(),
+ animations_[i]->affects_pending_observers());
break;
}
case Animation::Opacity: {
const FloatAnimationCurve* float_animation_curve =
- active_animations_[i]->curve()->ToFloatAnimationCurve();
+ animations_[i]->curve()->ToFloatAnimationCurve();
const float opacity = float_animation_curve->GetValue(trimmed);
- NotifyObserversOpacityAnimated(opacity);
+ NotifyObserversOpacityAnimated(
+ opacity,
+ animations_[i]->affects_active_observers(),
+ animations_[i]->affects_pending_observers());
break;
}
case Animation::Filter: {
const FilterAnimationCurve* filter_animation_curve =
- active_animations_[i]->curve()->ToFilterAnimationCurve();
+ animations_[i]->curve()->ToFilterAnimationCurve();
const FilterOperations filter =
filter_animation_curve->GetValue(trimmed);
- NotifyObserversFilterAnimated(filter);
+ NotifyObserversFilterAnimated(
+ filter,
+ animations_[i]->affects_active_observers(),
+ animations_[i]->affects_pending_observers());
break;
}
@@ -790,10 +838,13 @@ void LayerAnimationController::TickAnimations(double monotonic_time) {
case Animation::ScrollOffset: {
const ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
- active_animations_[i]->curve()->ToScrollOffsetAnimationCurve();
+ animations_[i]->curve()->ToScrollOffsetAnimationCurve();
const gfx::Vector2dF scroll_offset =
scroll_offset_animation_curve->GetValue(trimmed);
- NotifyObserversScrollOffsetAnimated(scroll_offset);
+ NotifyObserversScrollOffsetAnimated(
+ scroll_offset,
+ animations_[i]->affects_active_observers(),
+ animations_[i]->affects_pending_observers());
break;
}
@@ -810,8 +861,8 @@ void LayerAnimationController::UpdateActivation(UpdateActivationType type) {
if (registrar_) {
bool was_active = is_active_;
is_active_ = false;
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() != Animation::WaitingForDeletion) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() != Animation::WaitingForDeletion) {
is_active_ = true;
break;
}
@@ -824,31 +875,68 @@ void LayerAnimationController::UpdateActivation(UpdateActivationType type) {
}
}
-void LayerAnimationController::NotifyObserversOpacityAnimated(float opacity) {
- FOR_EACH_OBSERVER(LayerAnimationValueObserver,
- value_observers_,
- OnOpacityAnimated(opacity));
+void LayerAnimationController::NotifyObserversOpacityAnimated(
+ float opacity,
+ bool notify_active_observers,
+ bool notify_pending_observers) {
+ if (value_observers_.might_have_observers()) {
+ ObserverListBase<LayerAnimationValueObserver>::Iterator it(
+ value_observers_);
+ LayerAnimationValueObserver* obs;
+ while ((obs = it.GetNext()) != NULL) {
+ if ((notify_active_observers && obs->IsActive()) ||
+ (notify_pending_observers && !obs->IsActive()))
+ obs->OnOpacityAnimated(opacity);
+ }
+ }
}
void LayerAnimationController::NotifyObserversTransformAnimated(
- const gfx::Transform& transform) {
- FOR_EACH_OBSERVER(LayerAnimationValueObserver,
- value_observers_,
- OnTransformAnimated(transform));
+ const gfx::Transform& transform,
+ bool notify_active_observers,
+ bool notify_pending_observers) {
+ if (value_observers_.might_have_observers()) {
+ ObserverListBase<LayerAnimationValueObserver>::Iterator it(
+ value_observers_);
+ LayerAnimationValueObserver* obs;
+ while ((obs = it.GetNext()) != NULL) {
+ if ((notify_active_observers && obs->IsActive()) ||
+ (notify_pending_observers && !obs->IsActive()))
+ obs->OnTransformAnimated(transform);
+ }
+ }
}
void LayerAnimationController::NotifyObserversFilterAnimated(
- const FilterOperations& filters) {
- FOR_EACH_OBSERVER(LayerAnimationValueObserver,
- value_observers_,
- OnFilterAnimated(filters));
+ const FilterOperations& filters,
+ bool notify_active_observers,
+ bool notify_pending_observers) {
+ if (value_observers_.might_have_observers()) {
+ ObserverListBase<LayerAnimationValueObserver>::Iterator it(
+ value_observers_);
+ LayerAnimationValueObserver* obs;
+ while ((obs = it.GetNext()) != NULL) {
+ if ((notify_active_observers && obs->IsActive()) ||
+ (notify_pending_observers && !obs->IsActive()))
+ obs->OnFilterAnimated(filters);
+ }
+ }
}
void LayerAnimationController::NotifyObserversScrollOffsetAnimated(
- const gfx::Vector2dF& scroll_offset) {
- FOR_EACH_OBSERVER(LayerAnimationValueObserver,
- value_observers_,
- OnScrollOffsetAnimated(scroll_offset));
+ const gfx::Vector2dF& scroll_offset,
+ bool notify_active_observers,
+ bool notify_pending_observers) {
+ if (value_observers_.might_have_observers()) {
+ ObserverListBase<LayerAnimationValueObserver>::Iterator it(
+ value_observers_);
+ LayerAnimationValueObserver* obs;
+ while ((obs = it.GetNext()) != NULL) {
+ if ((notify_active_observers && obs->IsActive()) ||
+ (notify_pending_observers && !obs->IsActive()))
+ obs->OnScrollOffsetAnimated(scroll_offset);
+ }
+ }
}
void LayerAnimationController::NotifyObserversAnimationWaitingForDeletion() {
diff --git a/cc/animation/layer_animation_controller.h b/cc/animation/layer_animation_controller.h
index c5f0a31..c98a879 100644
--- a/cc/animation/layer_animation_controller.h
+++ b/cc/animation/layer_animation_controller.h
@@ -59,6 +59,11 @@ class CC_EXPORT LayerAnimationController
void UpdateState(bool start_ready_animations,
AnimationEventsVector* events);
+ // Make animations affect active observers if and only if they affect
+ // pending observers. Any animations that no longer affect any observers
+ // are deleted.
+ void ActivateAnimations();
+
// Returns the active animation in the given group, animating the given
// property, if such an animation exists.
Animation* GetAnimation(int group_id,
@@ -73,7 +78,7 @@ class CC_EXPORT LayerAnimationController
bool HasActiveAnimation() const;
// Returns true if there are any animations at all to process.
- bool has_any_animation() const { return !active_animations_.empty(); }
+ bool has_any_animation() const { return !animations_.empty(); }
// Returns true if there is an animation currently animating the given
// property, or if there is an animation scheduled to animate this property in
@@ -159,10 +164,18 @@ class CC_EXPORT LayerAnimationController
};
void UpdateActivation(UpdateActivationType type);
- void NotifyObserversOpacityAnimated(float opacity);
- void NotifyObserversTransformAnimated(const gfx::Transform& transform);
- void NotifyObserversFilterAnimated(const FilterOperations& filter);
- void NotifyObserversScrollOffsetAnimated(const gfx::Vector2dF& scroll_offset);
+ void NotifyObserversOpacityAnimated(float opacity,
+ bool notify_active_observers,
+ bool notify_pending_observers);
+ void NotifyObserversTransformAnimated(const gfx::Transform& transform,
+ bool notify_active_observers,
+ bool notify_pending_observers);
+ void NotifyObserversFilterAnimated(const FilterOperations& filter,
+ bool notify_active_observers,
+ bool notify_pending_observers);
+ void NotifyObserversScrollOffsetAnimated(const gfx::Vector2dF& scroll_offset,
+ bool notify_active_observers,
+ bool notify_pending_observers);
void NotifyObserversAnimationWaitingForDeletion();
@@ -171,7 +184,7 @@ class CC_EXPORT LayerAnimationController
AnimationRegistrar* registrar_;
int id_;
- ScopedPtrVector<Animation> active_animations_;
+ ScopedPtrVector<Animation> animations_;
// This is used to ensure that we don't spam the registrar.
bool is_active_;
diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc
index 4dca3f6..a6a5363 100644
--- a/cc/animation/layer_animation_controller_unittest.cc
+++ b/cc/animation/layer_animation_controller_unittest.cc
@@ -47,6 +47,7 @@ TEST(LayerAnimationControllerTest, SyncNewAnimation) {
int group_id = controller->GetAnimation(Animation::Opacity)->group();
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity));
EXPECT_EQ(Animation::WaitingForTargetAvailability,
@@ -72,6 +73,7 @@ TEST(LayerAnimationControllerTest, DoNotClobberStartTimes) {
int group_id = controller->GetAnimation(Animation::Opacity)->group();
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity));
EXPECT_EQ(Animation::WaitingForTargetAvailability,
@@ -116,6 +118,7 @@ TEST(LayerAnimationControllerTest, UseSpecifiedStartTimes) {
controller->GetAnimation(Animation::Opacity)->set_start_time(start_time);
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity));
EXPECT_EQ(Animation::WaitingForTargetAvailability,
@@ -180,6 +183,7 @@ TEST(LayerAnimationControllerTest, Activation) {
EXPECT_EQ(1u, registrar->active_animation_controllers().size());
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
// Both controllers should now be active.
EXPECT_EQ(1u, registrar->active_animation_controllers().size());
EXPECT_EQ(1u, registrar_impl->active_animation_controllers().size());
@@ -222,6 +226,7 @@ TEST(LayerAnimationControllerTest, Activation) {
EXPECT_EQ(0u, registrar->active_animation_controllers().size());
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_FALSE(controller->has_any_animation());
EXPECT_FALSE(controller_impl->has_any_animation());
EXPECT_EQ(0u, registrar->active_animation_controllers().size());
@@ -248,6 +253,7 @@ TEST(LayerAnimationControllerTest, SyncPause) {
int animation_id = controller->GetAnimation(Animation::Opacity)->id();
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity));
EXPECT_EQ(Animation::WaitingForTargetAvailability,
@@ -275,6 +281,7 @@ TEST(LayerAnimationControllerTest, SyncPause) {
// The pause run state change should make it to the impl thread controller.
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_EQ(Animation::Paused,
controller_impl->GetAnimation(group_id,
Animation::Opacity)->run_state());
@@ -297,6 +304,7 @@ TEST(LayerAnimationControllerTest, DoNotSyncFinishedAnimation) {
int group_id = controller->GetAnimation(Animation::Opacity)->group();
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity));
EXPECT_EQ(Animation::WaitingForTargetAvailability,
@@ -317,6 +325,7 @@ TEST(LayerAnimationControllerTest, DoNotSyncFinishedAnimation) {
EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity));
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
// Even though the main thread has a 'new' animation, it should not be pushed
// because the animation has already completed on the impl thread.
@@ -341,6 +350,7 @@ TEST(LayerAnimationControllerTest, AnimationsAreDeleted) {
controller->Animate(kInitialTickTime);
controller->UpdateState(true, NULL);
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
controller_impl->Animate(kInitialTickTime + 0.5);
controller_impl->UpdateState(true, events.get());
@@ -378,7 +388,10 @@ TEST(LayerAnimationControllerTest, AnimationsAreDeleted) {
controller->PushAnimationUpdatesTo(controller_impl.get());
- // Both controllers should now have deleted the animation.
+ // Both controllers should now have deleted the animation. The impl controller
+ // should have deleted the animation even though activation has not occurred,
+ // since the animation was already waiting for deletion when
+ // PushAnimationUpdatesTo was called.
EXPECT_FALSE(controller->has_any_animation());
EXPECT_FALSE(controller_impl->has_any_animation());
}
@@ -641,6 +654,7 @@ TEST(LayerAnimationControllerTest, ScrollOffsetTransition) {
dummy_provider_impl.set_scroll_offset(initial_value);
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_TRUE(controller_impl->GetAnimation(Animation::ScrollOffset));
double duration = controller_impl->GetAnimation(
Animation::ScrollOffset)->curve()->Duration();
@@ -719,6 +733,7 @@ TEST(LayerAnimationControllerTest, ScrollOffsetTransitionNoImplProvider) {
dummy_provider.set_scroll_offset(initial_value);
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_TRUE(controller_impl->GetAnimation(Animation::ScrollOffset));
double duration = controller_impl->GetAnimation(
Animation::ScrollOffset)->curve()->Duration();
@@ -1306,6 +1321,7 @@ TEST(LayerAnimationControllerTest, PushUpdatesWhenSynchronizedStartTimeNeeded) {
EXPECT_TRUE(active_animation->needs_synchronized_start_time());
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
active_animation = controller_impl->GetAnimation(0, Animation::Opacity);
EXPECT_TRUE(active_animation);
@@ -1358,13 +1374,13 @@ TEST(LayerAnimationControllerTest, SkipUpdateState) {
EXPECT_FALSE(controller->HasActiveAnimation());
}
-// Tests that an animation controller with only an inactive observer gets ticked
+// Tests that an animation controller with only a pending observer gets ticked
// but doesn't progress animations past the Starting state.
TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) {
scoped_ptr<AnimationEventsVector> events(
make_scoped_ptr(new AnimationEventsVector));
FakeLayerAnimationValueObserver dummy;
- FakeInactiveLayerAnimationValueObserver inactive_dummy;
+ FakeInactiveLayerAnimationValueObserver pending_dummy;
scoped_refptr<LayerAnimationController> controller(
LayerAnimationController::Create(0));
@@ -1382,9 +1398,9 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) {
EXPECT_EQ(Animation::WaitingForTargetAvailability,
controller->GetAnimation(id, Animation::Opacity)->run_state());
- controller->AddValueObserver(&inactive_dummy);
+ controller->AddValueObserver(&pending_dummy);
- // With only an inactive observer, the animation should progress to the
+ // With only a pending observer, the animation should progress to the
// Starting state and get ticked at its starting point, but should not
// progress to Running.
controller->Animate(kInitialTickTime + 1.0);
@@ -1392,7 +1408,7 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) {
EXPECT_EQ(0u, events->size());
EXPECT_EQ(Animation::Starting,
controller->GetAnimation(id, Animation::Opacity)->run_state());
- EXPECT_EQ(0.5f, inactive_dummy.opacity());
+ EXPECT_EQ(0.5f, pending_dummy.opacity());
// Even when already in the Starting state, the animation should stay
// there, and shouldn't be ticked past its starting point.
@@ -1401,7 +1417,7 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) {
EXPECT_EQ(0u, events->size());
EXPECT_EQ(Animation::Starting,
controller->GetAnimation(id, Animation::Opacity)->run_state());
- EXPECT_EQ(0.5f, inactive_dummy.opacity());
+ EXPECT_EQ(0.5f, pending_dummy.opacity());
controller->AddValueObserver(&dummy);
@@ -1412,12 +1428,12 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) {
EXPECT_EQ(1u, events->size());
EXPECT_EQ(Animation::Running,
controller->GetAnimation(id, Animation::Opacity)->run_state());
- EXPECT_EQ(0.5f, inactive_dummy.opacity());
+ EXPECT_EQ(0.5f, pending_dummy.opacity());
EXPECT_EQ(0.5f, dummy.opacity());
// The animation should now tick past its starting point.
controller->Animate(kInitialTickTime + 3.5);
- EXPECT_NE(0.5f, inactive_dummy.opacity());
+ EXPECT_NE(0.5f, pending_dummy.opacity());
EXPECT_NE(0.5f, dummy.opacity());
}
@@ -1568,6 +1584,7 @@ TEST(LayerAnimationControllerTest, MainThreadAbortedAnimationGetsDeleted) {
int group_id = controller->GetAnimation(Animation::Opacity)->group();
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity));
controller->AbortAnimations(Animation::Opacity);
@@ -1583,6 +1600,7 @@ TEST(LayerAnimationControllerTest, MainThreadAbortedAnimationGetsDeleted) {
controller->GetAnimation(Animation::Opacity)->run_state());
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_FALSE(controller->GetAnimation(group_id, Animation::Opacity));
EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity));
}
@@ -1602,6 +1620,7 @@ TEST(LayerAnimationControllerTest, ImplThreadAbortedAnimationGetsDeleted) {
int group_id = controller->GetAnimation(Animation::Opacity)->group();
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity));
controller_impl->AbortAnimations(Animation::Opacity);
@@ -1630,6 +1649,7 @@ TEST(LayerAnimationControllerTest, ImplThreadAbortedAnimationGetsDeleted) {
controller->GetAnimation(Animation::Opacity)->run_state());
controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
EXPECT_FALSE(controller->GetAnimation(group_id, Animation::Opacity));
EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity));
}
@@ -1908,5 +1928,268 @@ TEST(LayerAnimationControllerTest, MaximumScale) {
EXPECT_EQ(4.f, max_scale);
}
+TEST(LayerAnimationControllerTest, NewlyPushedAnimationWaitsForActivation) {
+ scoped_ptr<AnimationEventsVector> events(
+ make_scoped_ptr(new AnimationEventsVector));
+ FakeLayerAnimationValueObserver dummy_impl;
+ FakeInactiveLayerAnimationValueObserver pending_dummy_impl;
+ scoped_refptr<LayerAnimationController> controller_impl(
+ LayerAnimationController::Create(0));
+ controller_impl->AddValueObserver(&dummy_impl);
+ controller_impl->AddValueObserver(&pending_dummy_impl);
+ FakeLayerAnimationValueObserver dummy;
+ scoped_refptr<LayerAnimationController> controller(
+ LayerAnimationController::Create(0));
+ controller->AddValueObserver(&dummy);
+
+ AddOpacityTransitionToController(controller.get(), 1, 0.5f, 1.f, false);
+ int group_id = controller->GetAnimation(Animation::Opacity)->group();
+
+ controller->PushAnimationUpdatesTo(controller_impl.get());
+
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity));
+ EXPECT_EQ(
+ Animation::WaitingForTargetAvailability,
+ controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state());
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_pending_observers());
+ EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_active_observers());
+
+ controller_impl->Animate(kInitialTickTime);
+ controller_impl->UpdateState(true, events.get());
+
+ // Since the animation hasn't been activated, it should still be Starting
+ // rather than Running.
+ EXPECT_EQ(
+ Animation::Starting,
+ controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state());
+
+ // Since the animation hasn't been activated, only the pending observer
+ // should have been ticked.
+ EXPECT_EQ(0.5f, pending_dummy_impl.opacity());
+ EXPECT_EQ(0.f, dummy_impl.opacity());
+
+ controller_impl->ActivateAnimations();
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_pending_observers());
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_active_observers());
+
+ controller_impl->Animate(kInitialTickTime + 1.0);
+ controller_impl->UpdateState(true, events.get());
+
+ // Since the animation has been activated, it should have reached the
+ // Running state and the active observer should start to get ticked.
+ EXPECT_EQ(
+ Animation::Running,
+ controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state());
+ EXPECT_EQ(0.5f, pending_dummy_impl.opacity());
+ EXPECT_EQ(0.5f, dummy_impl.opacity());
+}
+
+TEST(LayerAnimationControllerTest, ActivationBetweenAnimateAndUpdateState) {
+ scoped_ptr<AnimationEventsVector> events(
+ make_scoped_ptr(new AnimationEventsVector));
+ FakeLayerAnimationValueObserver dummy_impl;
+ FakeInactiveLayerAnimationValueObserver pending_dummy_impl;
+ scoped_refptr<LayerAnimationController> controller_impl(
+ LayerAnimationController::Create(0));
+ controller_impl->AddValueObserver(&dummy_impl);
+ controller_impl->AddValueObserver(&pending_dummy_impl);
+ FakeLayerAnimationValueObserver dummy;
+ scoped_refptr<LayerAnimationController> controller(
+ LayerAnimationController::Create(0));
+ controller->AddValueObserver(&dummy);
+
+ AddOpacityTransitionToController(controller.get(), 1, 0.5f, 1.f, true);
+ int group_id = controller->GetAnimation(Animation::Opacity)->group();
+
+ controller->PushAnimationUpdatesTo(controller_impl.get());
+
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity));
+ EXPECT_EQ(
+ Animation::WaitingForTargetAvailability,
+ controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state());
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_pending_observers());
+ EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_active_observers());
+
+ controller_impl->Animate(kInitialTickTime);
+
+ // Since the animation hasn't been activated, only the pending observer
+ // should have been ticked.
+ EXPECT_EQ(0.5f, pending_dummy_impl.opacity());
+ EXPECT_EQ(0.f, dummy_impl.opacity());
+
+ controller_impl->ActivateAnimations();
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_pending_observers());
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_active_observers());
+
+ controller_impl->UpdateState(true, events.get());
+
+ // Since the animation has been activated, it should have reached the
+ // Running state.
+ EXPECT_EQ(
+ Animation::Running,
+ controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state());
+
+ controller_impl->Animate(kInitialTickTime + 0.5);
+
+ // Both observers should have been ticked.
+ EXPECT_EQ(0.75f, pending_dummy_impl.opacity());
+ EXPECT_EQ(0.75f, dummy_impl.opacity());
+}
+
+TEST(LayerAnimationControllerTest, PushedDeletedAnimationWaitsForActivation) {
+ scoped_ptr<AnimationEventsVector> events(
+ make_scoped_ptr(new AnimationEventsVector));
+ FakeLayerAnimationValueObserver dummy_impl;
+ FakeInactiveLayerAnimationValueObserver pending_dummy_impl;
+ scoped_refptr<LayerAnimationController> controller_impl(
+ LayerAnimationController::Create(0));
+ controller_impl->AddValueObserver(&dummy_impl);
+ controller_impl->AddValueObserver(&pending_dummy_impl);
+ FakeLayerAnimationValueObserver dummy;
+ scoped_refptr<LayerAnimationController> controller(
+ LayerAnimationController::Create(0));
+ controller->AddValueObserver(&dummy);
+
+ AddOpacityTransitionToController(controller.get(), 1, 0.5f, 1.f, true);
+ int group_id = controller->GetAnimation(Animation::Opacity)->group();
+
+ controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
+ controller_impl->Animate(kInitialTickTime);
+ controller_impl->UpdateState(true, events.get());
+ EXPECT_EQ(
+ Animation::Running,
+ controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state());
+ EXPECT_EQ(0.5f, pending_dummy_impl.opacity());
+ EXPECT_EQ(0.5f, dummy_impl.opacity());
+
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_pending_observers());
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_active_observers());
+
+ // Delete the animation on the main-thread controller.
+ controller->RemoveAnimation(
+ controller->GetAnimation(Animation::Opacity)->id());
+ controller->PushAnimationUpdatesTo(controller_impl.get());
+
+ // The animation should no longer affect pending observers.
+ EXPECT_FALSE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_pending_observers());
+ EXPECT_TRUE(controller_impl->GetAnimation(group_id, Animation::Opacity)
+ ->affects_active_observers());
+
+ controller_impl->Animate(kInitialTickTime + 0.5);
+ controller_impl->UpdateState(true, events.get());
+
+ // Only the active observer should have been ticked.
+ EXPECT_EQ(0.5f, pending_dummy_impl.opacity());
+ EXPECT_EQ(0.75f, dummy_impl.opacity());
+
+ controller_impl->ActivateAnimations();
+
+ // Activation should cause the animation to be deleted.
+ EXPECT_FALSE(controller_impl->has_any_animation());
+}
+
+// Tests that an animation that affects only active observers won't block
+// an animation that affects only pending observers from starting.
+TEST(LayerAnimationControllerTest, StartAnimationsAffectingDifferentObservers) {
+ scoped_ptr<AnimationEventsVector> events(
+ make_scoped_ptr(new AnimationEventsVector));
+ FakeLayerAnimationValueObserver dummy_impl;
+ FakeInactiveLayerAnimationValueObserver pending_dummy_impl;
+ scoped_refptr<LayerAnimationController> controller_impl(
+ LayerAnimationController::Create(0));
+ controller_impl->AddValueObserver(&dummy_impl);
+ controller_impl->AddValueObserver(&pending_dummy_impl);
+ FakeLayerAnimationValueObserver dummy;
+ scoped_refptr<LayerAnimationController> controller(
+ LayerAnimationController::Create(0));
+ controller->AddValueObserver(&dummy);
+
+ AddOpacityTransitionToController(controller.get(), 1, 0.f, 1.f, true);
+ int first_animation_group_id =
+ controller->GetAnimation(Animation::Opacity)->group();
+
+ controller->PushAnimationUpdatesTo(controller_impl.get());
+ controller_impl->ActivateAnimations();
+ controller_impl->Animate(kInitialTickTime);
+ controller_impl->UpdateState(true, events.get());
+
+ // Remove the first animation from the main-thread controller, and add a
+ // new animation affecting the same property.
+ controller->RemoveAnimation(
+ controller->GetAnimation(Animation::Opacity)->id());
+ AddOpacityTransitionToController(controller.get(), 1, 1.f, 0.5f, true);
+ int second_animation_group_id =
+ controller->GetAnimation(Animation::Opacity)->group();
+ controller->PushAnimationUpdatesTo(controller_impl.get());
+
+ // The original animation should only affect active observers, and the new
+ // animation should only affect pending observers.
+ EXPECT_FALSE(controller_impl->GetAnimation(first_animation_group_id,
+ Animation::Opacity)
+ ->affects_pending_observers());
+ EXPECT_TRUE(controller_impl->GetAnimation(first_animation_group_id,
+ Animation::Opacity)
+ ->affects_active_observers());
+ EXPECT_TRUE(controller_impl->GetAnimation(second_animation_group_id,
+ Animation::Opacity)
+ ->affects_pending_observers());
+ EXPECT_FALSE(controller_impl->GetAnimation(second_animation_group_id,
+ Animation::Opacity)
+ ->affects_active_observers());
+
+ controller_impl->Animate(kInitialTickTime + 0.5);
+ controller_impl->UpdateState(true, events.get());
+
+ // The original animation should still be running, and the new animation
+ // should be starting.
+ EXPECT_EQ(Animation::Running,
+ controller_impl->GetAnimation(first_animation_group_id,
+ Animation::Opacity)->run_state());
+ EXPECT_EQ(Animation::Starting,
+ controller_impl->GetAnimation(second_animation_group_id,
+ Animation::Opacity)->run_state());
+
+ // The active observer should have been ticked by the original animation,
+ // and the pending observer should have been ticked by the new animation.
+ EXPECT_EQ(1.f, pending_dummy_impl.opacity());
+ EXPECT_EQ(0.5f, dummy_impl.opacity());
+
+ controller_impl->ActivateAnimations();
+
+ // The original animation should have been deleted, and the new animation
+ // should now affect both observers.
+ EXPECT_FALSE(controller_impl->GetAnimation(first_animation_group_id,
+ Animation::Opacity));
+ EXPECT_TRUE(controller_impl->GetAnimation(second_animation_group_id,
+ Animation::Opacity)
+ ->affects_pending_observers());
+ EXPECT_TRUE(controller_impl->GetAnimation(second_animation_group_id,
+ Animation::Opacity)
+ ->affects_active_observers());
+
+ controller_impl->Animate(kInitialTickTime + 1.0);
+ controller_impl->UpdateState(true, events.get());
+
+ // The new animation should be running, and the active observer should have
+ // been ticked at the new animation's starting point.
+ EXPECT_EQ(Animation::Running,
+ controller_impl->GetAnimation(second_animation_group_id,
+ Animation::Opacity)->run_state());
+ EXPECT_EQ(1.f, pending_dummy_impl.opacity());
+ EXPECT_EQ(1.f, dummy_impl.opacity());
+}
+
} // namespace
} // namespace cc
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index dcf124e..65d5dd6 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -387,6 +387,7 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) {
// If we're not in impl-side painting, the tree is immediately
// considered active.
sync_tree->DidBecomeActive();
+ host_impl->ActivateAnimations();
devtools_instrumentation::DidActivateLayerTree(id_, source_frame_number_);
}
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 1ec6d33..306fd79 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1693,6 +1693,7 @@ void LayerTreeHostImpl::ActivatePendingTree() {
active_tree_->DidBecomeActive();
active_tree_->SetRootLayerScrollOffsetDelegate(
root_layer_scroll_offset_delegate_);
+ ActivateAnimations();
client_->OnCanDrawStateChanged(CanDraw());
SetNeedsRedraw();
@@ -2799,6 +2800,20 @@ void LayerTreeHostImpl::UpdateAnimationState(bool start_ready_animations) {
}
}
+void LayerTreeHostImpl::ActivateAnimations() {
+ if (!settings_.accelerated_animation_enabled || !needs_animate_layers() ||
+ !active_tree_->root_layer())
+ return;
+
+ TRACE_EVENT0("cc", "LayerTreeHostImpl::ActivateAnimations");
+ AnimationRegistrar::AnimationControllerMap copy =
+ animation_registrar_->active_animation_controllers();
+ for (AnimationRegistrar::AnimationControllerMap::iterator iter = copy.begin();
+ iter != copy.end();
+ ++iter)
+ (*iter).second->ActivateAnimations();
+}
+
base::TimeDelta LayerTreeHostImpl::LowFrequencyAnimationInterval() const {
return base::TimeDelta::FromSeconds(1);
}
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index aa02278..28eddb8 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -173,6 +173,7 @@ class CC_EXPORT LayerTreeHostImpl
virtual void CommitComplete();
virtual void Animate(base::TimeTicks monotonic_time);
virtual void UpdateAnimationState(bool start_ready_animations);
+ void ActivateAnimations();
void MainThreadHasStoppedFlinging();
void UpdateBackgroundAnimateTicking(bool should_background_tick);
void DidAnimateScrollOffset();
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index aeeb4f5..636c7b1 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -1244,5 +1244,84 @@ class LayerTreeHostAnimationTestFrozenAnimationTickTime
// Only the non-impl-paint multi-threaded compositor freezes animations.
MULTI_THREAD_NOIMPL_TEST_F(LayerTreeHostAnimationTestFrozenAnimationTickTime);
+// When animations are simultaneously added to an existing layer and to a new
+// layer, they should start at the same time, even when there's already a
+// running animation on the existing layer.
+class LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers
+ : public LayerTreeHostAnimationTest {
+ public:
+ LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers()
+ : frame_count_with_pending_tree_(0) {}
+
+ virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
+
+ virtual void DidCommit() OVERRIDE {
+ if (layer_tree_host()->source_frame_number() == 1) {
+ AddAnimatedTransformToLayer(layer_tree_host()->root_layer(), 4, 1, 1);
+ } else if (layer_tree_host()->source_frame_number() == 2) {
+ AddOpacityTransitionToLayer(
+ layer_tree_host()->root_layer(), 1, 0.f, 0.5f, true);
+
+ scoped_refptr<Layer> layer = Layer::Create();
+ layer_tree_host()->root_layer()->AddChild(layer);
+ layer->set_layer_animation_delegate(this);
+ layer->SetBounds(gfx::Size(4, 4));
+ AddOpacityTransitionToLayer(layer, 1, 0.f, 0.5f, true);
+ }
+ }
+
+ virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ host_impl->BlockNotifyReadyToActivateForTesting(true);
+ }
+
+ virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ // For the commit that added animations to new and existing layers, keep
+ // blocking activation. We want to verify that even with activation blocked,
+ // the animation on the layer that's already in the active tree won't get a
+ // head start.
+ if (!host_impl->settings().impl_side_painting ||
+ host_impl->pending_tree()->source_frame_number() != 2)
+ host_impl->BlockNotifyReadyToActivateForTesting(false);
+ }
+
+ virtual void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
+ const BeginFrameArgs& args) OVERRIDE {
+ if (!host_impl->pending_tree() ||
+ host_impl->pending_tree()->source_frame_number() != 2)
+ return;
+
+ frame_count_with_pending_tree_++;
+ if (frame_count_with_pending_tree_ == 2)
+ host_impl->BlockNotifyReadyToActivateForTesting(false);
+ }
+
+ virtual void UpdateAnimationState(LayerTreeHostImpl* host_impl,
+ bool has_unfinished_animation) OVERRIDE {
+ Animation* root_animation = host_impl->active_tree()
+ ->root_layer()
+ ->layer_animation_controller()
+ ->GetAnimation(Animation::Opacity);
+ if (!root_animation || root_animation->run_state() != Animation::Running)
+ return;
+
+ Animation* child_animation = host_impl->active_tree()
+ ->root_layer()
+ ->children()[0]
+ ->layer_animation_controller()
+ ->GetAnimation(Animation::Opacity);
+ EXPECT_EQ(Animation::Running, child_animation->run_state());
+ EXPECT_EQ(root_animation->start_time(), child_animation->start_time());
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ private:
+ int frame_count_with_pending_tree_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers);
+
} // namespace
} // namespace cc