// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "cc/animation/animation.h" #include #include "base/strings/stringprintf.h" #include "cc/animation/animation_delegate.h" #include "cc/animation/animation_host.h" #include "cc/animation/animation_id_provider.h" #include "cc/animation/animation_timeline.h" #include "cc/animation/element_animations.h" #include "cc/animation/keyframe_effect.h" #include "cc/test/animation_test_common.h" #include "cc/test/animation_timelines_test_common.h" namespace cc { namespace { class AnimationTest : public AnimationTimelinesTest { public: AnimationTest() = default; ~AnimationTest() override = default; }; // See element_animations_unittest.cc for active/pending observers tests. TEST_F(AnimationTest, AttachDetachLayerIfTimelineAttached) { EXPECT_TRUE(CheckKeyframeEffectTimelineNeedsPushProperties(false)); host_->AddAnimationTimeline(timeline_); EXPECT_TRUE(timeline_->needs_push_properties()); EXPECT_FALSE(animation_->keyframe_effect()->needs_push_properties()); timeline_->AttachAnimation(animation_); EXPECT_FALSE(animation_->element_animations()); EXPECT_TRUE(timeline_->needs_push_properties()); EXPECT_FALSE(animation_->keyframe_effect()->needs_push_properties()); host_->PushPropertiesTo(host_impl_); EXPECT_FALSE(GetImplKeyframeEffectForLayerId(element_id_)); timeline_impl_ = host_impl_->GetTimelineById(timeline_id_); EXPECT_TRUE(timeline_impl_); animation_impl_ = timeline_impl_->GetAnimationById(animation_id_); EXPECT_TRUE(animation_impl_); EXPECT_FALSE(animation_impl_->element_animations()); EXPECT_FALSE(animation_impl_->keyframe_effect()->element_id()); EXPECT_FALSE(animation_->keyframe_effect()->needs_push_properties()); EXPECT_FALSE(timeline_->needs_push_properties()); animation_->AttachElement(element_id_); EXPECT_EQ(animation_->keyframe_effect(), GetKeyframeEffectForElementId(element_id_)); EXPECT_TRUE(animation_->element_animations()); EXPECT_EQ(animation_->keyframe_effect()->element_id(), element_id_); CheckKeyframeEffectTimelineNeedsPushProperties(true); host_->PushPropertiesTo(host_impl_); EXPECT_EQ(animation_impl_->keyframe_effect(), GetImplKeyframeEffectForLayerId(element_id_)); EXPECT_TRUE(animation_impl_->element_animations()); EXPECT_EQ(animation_impl_->keyframe_effect()->element_id(), element_id_); CheckKeyframeEffectTimelineNeedsPushProperties(false); animation_->DetachElement(); EXPECT_FALSE(GetKeyframeEffectForElementId(element_id_)); EXPECT_FALSE(animation_->element_animations()); EXPECT_FALSE(animation_->keyframe_effect()->element_id()); CheckKeyframeEffectTimelineNeedsPushProperties(true); host_->PushPropertiesTo(host_impl_); EXPECT_FALSE(GetImplKeyframeEffectForLayerId(element_id_)); EXPECT_FALSE(animation_impl_->element_animations()); EXPECT_FALSE(animation_impl_->keyframe_effect()->element_id()); CheckKeyframeEffectTimelineNeedsPushProperties(false); timeline_->DetachAnimation(animation_); EXPECT_FALSE(animation_->animation_timeline()); EXPECT_FALSE(animation_->element_animations()); EXPECT_FALSE(animation_->keyframe_effect()->element_id()); EXPECT_TRUE(timeline_->needs_push_properties()); EXPECT_FALSE(animation_->keyframe_effect()->needs_push_properties()); host_->PushPropertiesTo(host_impl_); CheckKeyframeEffectTimelineNeedsPushProperties(false); } TEST_F(AnimationTest, AttachDetachTimelineIfLayerAttached) { host_->AddAnimationTimeline(timeline_); EXPECT_FALSE(animation_->keyframe_effect()->element_animations()); EXPECT_FALSE(animation_->keyframe_effect()->element_id()); EXPECT_FALSE(animation_->keyframe_effect()->needs_push_properties()); animation_->AttachElement(element_id_); EXPECT_FALSE(animation_->animation_timeline()); EXPECT_FALSE(GetKeyframeEffectForElementId(element_id_)); EXPECT_FALSE(animation_->keyframe_effect()->element_animations()); EXPECT_EQ(animation_->keyframe_effect()->element_id(), element_id_); EXPECT_FALSE(animation_->keyframe_effect()->needs_push_properties()); timeline_->AttachAnimation(animation_); EXPECT_EQ(timeline_, animation_->animation_timeline()); EXPECT_EQ(animation_->keyframe_effect(), GetKeyframeEffectForElementId(element_id_)); EXPECT_TRUE(animation_->keyframe_effect()->element_animations()); EXPECT_EQ(animation_->keyframe_effect()->element_id(), element_id_); EXPECT_TRUE(animation_->keyframe_effect()->needs_push_properties()); // Removing animation from timeline detaches layer. timeline_->DetachAnimation(animation_); EXPECT_FALSE(animation_->animation_timeline()); EXPECT_FALSE(GetKeyframeEffectForElementId(element_id_)); EXPECT_FALSE(animation_->keyframe_effect()->element_animations()); EXPECT_FALSE(animation_->keyframe_effect()->element_id()); EXPECT_TRUE(animation_->keyframe_effect()->needs_push_properties()); } TEST_F(AnimationTest, PropertiesMutate) { client_.RegisterElementId(element_id_, ElementListType::ACTIVE); client_impl_.RegisterElementId(element_id_, ElementListType::PENDING); client_impl_.RegisterElementId(element_id_, ElementListType::ACTIVE); host_->AddAnimationTimeline(timeline_); timeline_->AttachAnimation(animation_); animation_->AttachElement(element_id_); CheckKeyframeEffectTimelineNeedsPushProperties(true); host_->PushPropertiesTo(host_impl_); CheckKeyframeEffectTimelineNeedsPushProperties(false); const float start_opacity = .7f; const float end_opacity = .3f; const float start_brightness = .6f; const float end_brightness = .4f; const int transform_x = 10; const int transform_y = 20; const float start_invert = .8f; const float end_invert = .6f; const double duration = 1.; AddOpacityTransitionToAnimation(animation_.get(), duration, start_opacity, end_opacity, false); AddAnimatedTransformToAnimation(animation_.get(), duration, transform_x, transform_y); AddAnimatedFilterToAnimation(animation_.get(), duration, start_brightness, end_brightness); AddAnimatedBackdropFilterToAnimation(animation_.get(), duration, start_invert, end_invert); CheckKeyframeEffectTimelineNeedsPushProperties(true); host_->PushPropertiesTo(host_impl_); CheckKeyframeEffectTimelineNeedsPushProperties(false); EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE, TargetProperty::OPACITY)); EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE, TargetProperty::TRANSFORM)); EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE, TargetProperty::FILTER)); EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE, TargetProperty::BACKDROP_FILTER)); EXPECT_FALSE(client_impl_.IsPropertyMutated( element_id_, ElementListType::ACTIVE, TargetProperty::OPACITY)); EXPECT_FALSE(client_impl_.IsPropertyMutated( element_id_, ElementListType::ACTIVE, TargetProperty::TRANSFORM)); EXPECT_FALSE(client_impl_.IsPropertyMutated( element_id_, ElementListType::ACTIVE, TargetProperty::FILTER)); EXPECT_FALSE(client_impl_.IsPropertyMutated( element_id_, ElementListType::ACTIVE, TargetProperty::BACKDROP_FILTER)); host_impl_->ActivateAnimations(nullptr); base::TimeTicks time; time += base::TimeDelta::FromSecondsD(0.1); TickAnimationsTransferEvents(time, 4u); CheckKeyframeEffectTimelineNeedsPushProperties(false); time += base::TimeDelta::FromSecondsD(duration); TickAnimationsTransferEvents(time, 4u); CheckKeyframeEffectTimelineNeedsPushProperties(true); client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE, end_opacity); client_.ExpectTransformPropertyMutated(element_id_, ElementListType::ACTIVE, transform_x, transform_y); client_.ExpectFilterPropertyMutated(element_id_, ElementListType::ACTIVE, end_brightness); client_.ExpectBackdropFilterPropertyMutated( element_id_, ElementListType::ACTIVE, end_invert); client_impl_.ExpectOpacityPropertyMutated( element_id_, ElementListType::ACTIVE, end_opacity); client_impl_.ExpectTransformPropertyMutated( element_id_, ElementListType::ACTIVE, transform_x, transform_y); client_impl_.ExpectFilterPropertyMutated(element_id_, ElementListType::ACTIVE, end_brightness); client_impl_.ExpectBackdropFilterPropertyMutated( element_id_, ElementListType::ACTIVE, end_invert); client_impl_.ExpectOpacityPropertyMutated( element_id_, ElementListType::PENDING, end_opacity); client_impl_.ExpectTransformPropertyMutated( element_id_, ElementListType::PENDING, transform_x, transform_y); client_impl_.ExpectFilterPropertyMutated( element_id_, ElementListType::PENDING, end_brightness); client_impl_.ExpectBackdropFilterPropertyMutated( element_id_, ElementListType::PENDING, end_invert); } TEST_F(AnimationTest, AttachTwoAnimationsToOneLayer) { TestAnimationDelegate delegate1; TestAnimationDelegate delegate2; client_.RegisterElementId(element_id_, ElementListType::ACTIVE); client_impl_.RegisterElementId(element_id_, ElementListType::PENDING); client_impl_.RegisterElementId(element_id_, ElementListType::ACTIVE); scoped_refptr animation1 = Animation::Create(AnimationIdProvider::NextAnimationId()); scoped_refptr animation2 = Animation::Create(AnimationIdProvider::NextAnimationId()); host_->AddAnimationTimeline(timeline_); timeline_->AttachAnimation(animation1); EXPECT_TRUE(timeline_->needs_push_properties()); timeline_->AttachAnimation(animation2); EXPECT_TRUE(timeline_->needs_push_properties()); animation1->set_animation_delegate(&delegate1); animation2->set_animation_delegate(&delegate2); // Attach animations to the same layer. animation1->AttachElement(element_id_); animation2->AttachElement(element_id_); const float start_opacity = .7f; const float end_opacity = .3f; const int transform_x = 10; const int transform_y = 20; const double duration = 1.; AddOpacityTransitionToAnimation(animation1.get(), duration, start_opacity, end_opacity, false); AddAnimatedTransformToAnimation(animation2.get(), duration, transform_x, transform_y); host_->PushPropertiesTo(host_impl_); host_impl_->ActivateAnimations(nullptr); EXPECT_FALSE(delegate1.started()); EXPECT_FALSE(delegate1.finished()); EXPECT_FALSE(delegate2.started()); EXPECT_FALSE(delegate2.finished()); base::TimeTicks time; time += base::TimeDelta::FromSecondsD(0.1); TickAnimationsTransferEvents(time, 2u); EXPECT_TRUE(delegate1.started()); EXPECT_FALSE(delegate1.finished()); EXPECT_TRUE(delegate2.started()); EXPECT_FALSE(delegate2.finished()); EXPECT_FALSE(animation1->keyframe_effect()->needs_push_properties()); EXPECT_FALSE(animation2->keyframe_effect()->needs_push_properties()); time += base::TimeDelta::FromSecondsD(duration); TickAnimationsTransferEvents(time, 2u); EXPECT_TRUE(delegate1.finished()); EXPECT_TRUE(delegate2.finished()); EXPECT_TRUE(animation1->keyframe_effect()->needs_push_properties()); EXPECT_TRUE(animation2->keyframe_effect()->needs_push_properties()); client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE, end_opacity); client_.ExpectTransformPropertyMutated(element_id_, ElementListType::ACTIVE, transform_x, transform_y); client_impl_.ExpectOpacityPropertyMutated( element_id_, ElementListType::ACTIVE, end_opacity); client_impl_.ExpectTransformPropertyMutated( element_id_, ElementListType::ACTIVE, transform_x, transform_y); client_impl_.ExpectOpacityPropertyMutated( element_id_, ElementListType::PENDING, end_opacity); client_impl_.ExpectTransformPropertyMutated( element_id_, ElementListType::PENDING, transform_x, transform_y); } TEST_F(AnimationTest, AddRemoveAnimationToNonAttachedAnimation) { client_.RegisterElementId(element_id_, ElementListType::ACTIVE); client_impl_.RegisterElementId(element_id_, ElementListType::PENDING); client_impl_.RegisterElementId(element_id_, ElementListType::ACTIVE); const double duration = 1.; const float start_opacity = .7f; const float end_opacity = .3f; const int filter_id = AddAnimatedFilterToAnimation(animation_.get(), duration, 0.1f, 0.9f); AddOpacityTransitionToAnimation(animation_.get(), duration, start_opacity, end_opacity, false); EXPECT_FALSE(animation_->keyframe_effect()->needs_push_properties()); host_->AddAnimationTimeline(timeline_); timeline_->AttachAnimation(animation_); EXPECT_FALSE(animation_->keyframe_effect()->needs_push_properties()); EXPECT_FALSE(animation_->keyframe_effect()->element_animations()); animation_->RemoveKeyframeModel(filter_id); EXPECT_FALSE(animation_->keyframe_effect()->needs_push_properties()); animation_->AttachElement(element_id_); EXPECT_TRUE(animation_->keyframe_effect()->element_animations()); EXPECT_FALSE(animation_->keyframe_effect() ->element_animations() ->HasAnyAnimationTargetingProperty(TargetProperty::FILTER)); EXPECT_TRUE(animation_->keyframe_effect() ->element_animations() ->HasAnyAnimationTargetingProperty(TargetProperty::OPACITY)); EXPECT_TRUE(animation_->keyframe_effect()->needs_push_properties()); host_->PushPropertiesTo(host_impl_); EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE, TargetProperty::OPACITY)); EXPECT_FALSE(client_impl_.IsPropertyMutated( element_id_, ElementListType::ACTIVE, TargetProperty::OPACITY)); EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE, TargetProperty::FILTER)); EXPECT_FALSE(client_impl_.IsPropertyMutated( element_id_, ElementListType::ACTIVE, TargetProperty::FILTER)); host_impl_->ActivateAnimations(nullptr); base::TimeTicks time; time += base::TimeDelta::FromSecondsD(0.1); TickAnimationsTransferEvents(time, 1u); time += base::TimeDelta::FromSecondsD(duration); TickAnimationsTransferEvents(time, 1u); client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE, end_opacity); client_impl_.ExpectOpacityPropertyMutated( element_id_, ElementListType::ACTIVE, end_opacity); client_impl_.ExpectOpacityPropertyMutated( element_id_, ElementListType::PENDING, end_opacity); EXPECT_FALSE(client_.IsPropertyMutated(element_id_, ElementListType::ACTIVE, TargetProperty::FILTER)); EXPECT_FALSE(client_impl_.IsPropertyMutated( element_id_, ElementListType::ACTIVE, TargetProperty::FILTER)); } TEST_F(AnimationTest, AddRemoveAnimationCausesSetNeedsCommit) { client_.RegisterElementId(element_id_, ElementListType::ACTIVE); host_->AddAnimationTimeline(timeline_); timeline_->AttachAnimation(animation_); animation_->AttachElement(element_id_); EXPECT_TRUE(client_.mutators_need_commit()); client_.set_mutators_need_commit(false); const int keyframe_model_id = AddOpacityTransitionToAnimation(animation_.get(), 1., .7f, .3f, false); EXPECT_TRUE(client_.mutators_need_commit()); client_.set_mutators_need_commit(false); animation_->PauseKeyframeModel(keyframe_model_id, base::TimeDelta::FromSeconds(1)); EXPECT_TRUE(client_.mutators_need_commit()); client_.set_mutators_need_commit(false); animation_->RemoveKeyframeModel(keyframe_model_id); EXPECT_TRUE(client_.mutators_need_commit()); client_.set_mutators_need_commit(false); } // If main-thread animation switches to another layer within one frame then // impl-thread animation must be switched as well. TEST_F(AnimationTest, SwitchToLayer) { host_->AddAnimationTimeline(timeline_); timeline_->AttachAnimation(animation_); animation_->AttachElement(element_id_); host_->PushPropertiesTo(host_impl_); timeline_impl_ = host_impl_->GetTimelineById(timeline_id_); EXPECT_TRUE(timeline_impl_); animation_impl_ = timeline_impl_->GetAnimationById(animation_id_); EXPECT_TRUE(animation_impl_); EXPECT_EQ(animation_->keyframe_effect(), GetKeyframeEffectForElementId(element_id_)); EXPECT_TRUE(animation_->keyframe_effect()->element_animations()); EXPECT_EQ(animation_->keyframe_effect()->element_id(), element_id_); timeline_impl_ = host_impl_->GetTimelineById(timeline_id_); EXPECT_TRUE(timeline_impl_); animation_impl_ = timeline_impl_->GetAnimationById(animation_id_); EXPECT_TRUE(animation_impl_); EXPECT_EQ(animation_impl_->keyframe_effect(), GetImplKeyframeEffectForLayerId(element_id_)); EXPECT_TRUE(animation_impl_->keyframe_effect()->element_animations()); EXPECT_EQ(animation_impl_->keyframe_effect()->element_id(), element_id_); CheckKeyframeEffectTimelineNeedsPushProperties(false); const ElementId new_element_id(NextTestLayerId()); animation_->DetachElement(); animation_->AttachElement(new_element_id); EXPECT_EQ(animation_->keyframe_effect(), GetKeyframeEffectForElementId(new_element_id)); EXPECT_TRUE(animation_->keyframe_effect()->element_animations()); EXPECT_EQ(animation_->keyframe_effect()->element_id(), new_element_id); CheckKeyframeEffectTimelineNeedsPushProperties(true); host_->PushPropertiesTo(host_impl_); EXPECT_EQ(animation_impl_->keyframe_effect(), GetImplKeyframeEffectForLayerId(new_element_id)); EXPECT_TRUE(animation_impl_->keyframe_effect()->element_animations()); EXPECT_EQ(animation_impl_->keyframe_effect()->element_id(), new_element_id); } TEST_F(AnimationTest, ToString) { animation_->AttachElement(element_id_); EXPECT_EQ( base::StringPrintf("Animation{id=%d, element_id=%s, keyframe_models=[]}", animation_->id(), element_id_.ToString().c_str()), animation_->ToString()); animation_->AddKeyframeModel( KeyframeModel::Create(std::make_unique(15), 42, 73, TargetProperty::OPACITY)); EXPECT_EQ( base::StringPrintf("Animation{id=%d, element_id=%s, " "keyframe_models=[KeyframeModel{id=42, " "group=73, target_property_id=1, " "run_state=WAITING_FOR_TARGET_AVAILABILITY}]}", animation_->id(), element_id_.ToString().c_str()), animation_->ToString()); animation_->AddKeyframeModel( KeyframeModel::Create(std::make_unique(18), 45, 76, TargetProperty::BOUNDS)); EXPECT_EQ( base::StringPrintf( "Animation{id=%d, element_id=%s, " "keyframe_models=[KeyframeModel{id=42, " "group=73, target_property_id=1, " "run_state=WAITING_FOR_TARGET_AVAILABILITY}, KeyframeModel{id=45, " "group=76, " "target_property_id=5, run_state=WAITING_FOR_TARGET_AVAILABILITY}]}", animation_->id(), element_id_.ToString().c_str()), animation_->ToString()); } } // namespace } // namespace cc