diff options
author | ajuma@chromium.org <ajuma@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-30 18:17:34 +0000 |
---|---|---|
committer | ajuma@chromium.org <ajuma@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-30 18:17:34 +0000 |
commit | b33348f4524c917d65e9ebb4d1cc6acc9b82bb39 (patch) | |
tree | d22d6c0331f2932d021629b79257c55b554a3232 /cc/animation | |
parent | 25df7ada51fd9b262daba11f7d308e97e08e4651 (diff) | |
download | chromium_src-b33348f4524c917d65e9ebb4d1cc6acc9b82bb39.zip chromium_src-b33348f4524c917d65e9ebb4d1cc6acc9b82bb39.tar.gz chromium_src-b33348f4524c917d65e9ebb4d1cc6acc9b82bb39.tar.bz2 |
cc: Animations committed together should start together
This change makes newly-committed animations wait for tree
activation before they're able to affect the active tree, and
makes newly-deleted animations wait for tree activation before
they no longer affect the active tree. This is accomplished by
tracking whether each animation affects active observers,
inactive (pending) observers, or both.
As a result, animations committed together will start
together, after the next activation.
BUG=225643
Review URL: https://codereview.chromium.org/259043004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@267269 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc/animation')
-rw-r--r-- | cc/animation/animation.cc | 5 | ||||
-rw-r--r-- | cc/animation/animation.h | 24 | ||||
-rw-r--r-- | cc/animation/layer_animation_controller.cc | 528 | ||||
-rw-r--r-- | cc/animation/layer_animation_controller.h | 25 | ||||
-rw-r--r-- | cc/animation/layer_animation_controller_unittest.cc | 301 |
5 files changed, 647 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 |