diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-06-27 11:18:24 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-07-03 09:38:30 +0000 |
commit | d94af01c90575348c4e81a418257f254b6f8d225 (patch) | |
tree | 77a26669b33eaa4d46b88b07e17dacc61eba6001 /chromium/cc | |
parent | 5d87695f37678f96492b258bbab36486c59866b4 (diff) |
BASELINE: Update Chromium to 75.0.3770.116
Change-Id: Ifcd5227841577e8ce81a1b7a54c56caba4d85e02
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/cc')
-rw-r--r-- | chromium/cc/animation/animation_host.cc | 4 | ||||
-rw-r--r-- | chromium/cc/animation/animation_unittest.cc | 3 | ||||
-rw-r--r-- | chromium/cc/animation/worklet_animation.cc | 4 | ||||
-rw-r--r-- | chromium/cc/layers/layer_impl.cc | 10 | ||||
-rw-r--r-- | chromium/cc/layers/layer_unittest.cc | 5 | ||||
-rw-r--r-- | chromium/cc/trees/layer_tree_host.cc | 25 | ||||
-rw-r--r-- | chromium/cc/trees/layer_tree_host_impl.cc | 25 | ||||
-rw-r--r-- | chromium/cc/trees/layer_tree_host_impl_unittest.cc | 85 | ||||
-rw-r--r-- | chromium/cc/trees/layer_tree_host_unittest_animation.cc | 145 | ||||
-rw-r--r-- | chromium/cc/trees/proxy_main.cc | 4 |
10 files changed, 272 insertions, 38 deletions
diff --git a/chromium/cc/animation/animation_host.cc b/chromium/cc/animation/animation_host.cc index 8be2eef3d07..cbd6d8e5ad8 100644 --- a/chromium/cc/animation/animation_host.cc +++ b/chromium/cc/animation/animation_host.cc @@ -199,6 +199,8 @@ void AnimationHost::SetMutatorHostClient(MutatorHostClient* client) { return; mutator_host_client_ = client; + if (mutator_host_client_ && needs_push_properties_) + mutator_host_client_->SetMutatorsNeedCommit(); } void AnimationHost::SetNeedsCommit() { @@ -210,6 +212,8 @@ void AnimationHost::SetNeedsCommit() { void AnimationHost::SetNeedsPushProperties() { needs_push_properties_ = true; + if (mutator_host_client_) + mutator_host_client_->SetMutatorsNeedCommit(); } void AnimationHost::PushPropertiesTo(MutatorHost* mutator_host_impl) { diff --git a/chromium/cc/animation/animation_unittest.cc b/chromium/cc/animation/animation_unittest.cc index 273491d00ca..1ffadf9a9cf 100644 --- a/chromium/cc/animation/animation_unittest.cc +++ b/chromium/cc/animation/animation_unittest.cc @@ -444,7 +444,8 @@ TEST_F(AnimationTest, AddRemoveAnimationCausesSetNeedsCommit) { timeline_->AttachAnimation(animation_); animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id_); - EXPECT_FALSE(client_.mutators_need_commit()); + EXPECT_TRUE(client_.mutators_need_commit()); + client_.set_mutators_need_commit(false); const int keyframe_model_id = AddOpacityTransitionToAnimation( animation_.get(), 1., .7f, .3f, false, keyframe_effect_id_); diff --git a/chromium/cc/animation/worklet_animation.cc b/chromium/cc/animation/worklet_animation.cc index 32e078ca895..09f4de569cb 100644 --- a/chromium/cc/animation/worklet_animation.cc +++ b/chromium/cc/animation/worklet_animation.cc @@ -84,8 +84,8 @@ void WorkletAnimation::PushPropertiesTo(Animation* animation_impl) { } void WorkletAnimation::Tick(base::TimeTicks monotonic_time) { - // Do not tick worklet animations on main thread. This should be removed if we - // skip ticking all animations on main thread in http://crbug.com/762717. + // Do not tick worklet animations on main thread as we will tick them on the + // compositor and the tick is more expensive than regular animations. if (!is_impl_instance_) return; if (!local_time_.has_value()) diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index 36a262b3c93..580dd90ba22 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -183,6 +183,16 @@ bool LayerImpl::WillDraw(DrawMode draw_mode, return false; } + // Resourceless mode does not support non-default blend mode. If we draw, + // the result will be just like kSrcOver which is not too bad for blend modes + // other than kDstIn. For kDstIn mode, we should ignore the source because + // otherwise we would draw a bad black mask over the destination. + if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) { + const auto* effect_node = GetEffectTree().Node(effect_tree_index()); + if (effect_node && effect_node->blend_mode == SkBlendMode::kDstIn) + return false; + } + current_draw_mode_ = draw_mode; return true; } diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index 9a6e33429b5..f969da4f3bb 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -1578,8 +1578,9 @@ TEST_F(LayerTest, SetLayerTreeHostNotUsingLayerListsManagesElementId) { ElementId element_id = ElementId(2); test_layer->SetElementId(element_id); - // Expect additional call due to has-animation check. - EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(2); + // Expect additional calls due to has-animation check and initialization + // of keyframes. + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(7); scoped_refptr<AnimationTimeline> timeline = AnimationTimeline::Create(AnimationIdProvider::NextTimelineId()); animation_host_->AddAnimationTimeline(timeline); diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index 313f92f57e7..cf7a0bc85ef 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -615,6 +615,12 @@ void LayerTreeHost::SetAnimationEvents( std::unique_ptr<MutatorEvents> events) { DCHECK(task_runner_provider_->IsMainThread()); mutator_host_->SetAnimationEvents(std::move(events)); + + // Events are added to a queue to be dispatched but we need a main frame + // in order to dispatch the events. Also, finished animations require + // a commit in order to clean up their KeyframeModels but without a main + // frame we could indefinitely delay cleaning up the animation. + SetNeedsAnimate(); } void LayerTreeHost::SetDebugState( @@ -993,22 +999,21 @@ void LayerTreeHost::UpdateBrowserControlsState( } void LayerTreeHost::AnimateLayers(base::TimeTicks monotonic_time) { - // We do not need to animate on the main thread in this case because all - // relevant changes will be processed on the compositor thread, or proxy, - // and propagated back to the correct trees. - // TODO(majidvp): We should be able to eliminate this in the non- - // slimming path and will do so in a follow up. (762717) - if (IsUsingLayerLists()) - return; - std::unique_ptr<MutatorEvents> events = mutator_host_->CreateEvents(); if (mutator_host_->TickAnimations(monotonic_time, property_trees()->scroll_tree, true)) mutator_host_->UpdateAnimationState(true, events.get()); - if (!events->IsEmpty()) - property_trees_.needs_rebuild = true; + if (!events->IsEmpty()) { + // If not using layer lists, animation state changes will require + // rebuilding property trees to track them. + if (!IsUsingLayerLists()) + property_trees_.needs_rebuild = true; + + // A commit is required to push animation changes to the compositor. + SetNeedsCommit(); + } } int LayerTreeHost::ScheduleMicroBenchmark( diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index f9e0cb0c095..cb2dbe413f0 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -2522,16 +2522,25 @@ void LayerTreeHostImpl::UpdateViewportContainerSizes() { ClipNode* outer_clip_node = property_trees->clip_tree.Node( OuterViewportScrollLayer()->clip_tree_index()); - float container_height = - OuterViewportScrollNode()->container_bounds.height(); - - // TODO(bokan): The container bounds for the outer viewport are incorrectly - // computed pre-Blink-Gen-Property-Trees so we must apply the minimum page - // scale factor. https://crbug.com/901083 + float adjusted_container_height = + OuterViewportScrollNode()->container_bounds.height() + + scaled_bounds_delta.y(); + + // TODO(bokan): The clip node bounds for the outer viewport are incorrectly + // computed pre-Blink-Gen-Property-Trees: they assume the outer viewport is + // the same size as the inner viewport. In reality, the outer viewport is + // sized such that it equals the inner viewport when at minimum page scale. + // This happens on mobile when a page doesn't have a |width=device-width| + // viewport meta tag (e.g. legacy desktop page). Thus, we must scale + // the container height and its adjustment to match the incorrect space of + // the clip node. https://crbug.com/901083, https://crbug.com/961649. + // Note: we don't fix this in the property tree builder since that code + // path is going away and it's not clear whether it depends on ClipNode + // being in this coordinate space pre-BGPT. if (!settings().use_layer_lists) - container_height *= active_tree_->min_page_scale_factor(); + adjusted_container_height *= active_tree_->min_page_scale_factor(); - outer_clip_node->clip.set_height(container_height + bounds_delta.y()); + outer_clip_node->clip.set_height(adjusted_container_height); // Expand all clips between the outer viewport and the inner viewport. auto* outer_ancestor = property_trees->clip_tree.parent(outer_clip_node); diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc index 470a819eefb..13a8b429571 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -5496,14 +5496,19 @@ class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest { void SetupBrowserControlsAndScrollLayerWithVirtualViewport( const gfx::Size& inner_viewport_size, const gfx::Size& outer_viewport_size, - const gfx::Size& scroll_layer_size) { + const gfx::Size& scroll_layer_size, + base::OnceCallback<void(LayerTreeSettings*)> modify_settings = + base::DoNothing()) { settings_ = DefaultSettings(); + settings_.use_layer_lists = true; + std::move(modify_settings).Run(&settings_); CreateHostImpl(settings_, CreateLayerTreeFrameSink()); SetupBrowserControlsAndScrollLayerWithVirtualViewport( host_impl_->active_tree(), inner_viewport_size, outer_viewport_size, scroll_layer_size); } + protected: void SetupBrowserControlsAndScrollLayerWithVirtualViewport( LayerTreeImpl* tree_impl, const gfx::Size& inner_viewport_size, @@ -5514,7 +5519,6 @@ class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest { outer_viewport_size, scroll_layer_size); } - protected: gfx::Size layer_size_; gfx::Size clip_size_; gfx::Size viewport_size_; @@ -5596,15 +5600,82 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, host_impl_->ScrollEnd(EndState().get()); } +// Ensure that moving the browser controls (i.e. omnibox/url-bar on mobile) on +// pages with a non-1 minimum page scale factor (e.g. legacy desktop page) +// correctly scales the clipping adjustment performed to show the newly exposed +// region of the page. +TEST_F(LayerTreeHostImplBrowserControlsTest, + MovingBrowserControlsOuterClipDeltaScaled) { + gfx::Size inner_size = gfx::Size(100, 100); + gfx::Size outer_size = gfx::Size(100, 100); + gfx::Size content_size = gfx::Size(200, 1000); + SetupBrowserControlsAndScrollLayerWithVirtualViewport(inner_size, outer_size, + content_size); + + LayerTreeImpl* active_tree = host_impl_->active_tree(); + + // Create a content layer beneath the outer viewport scroll layer. + int id = host_impl_->OuterViewportScrollLayer()->id(); + host_impl_->OuterViewportScrollLayer()->test_properties()->AddChild( + LayerImpl::Create(host_impl_->active_tree(), id + 2)); + LayerImpl* content = + active_tree->OuterViewportScrollLayer()->test_properties()->children[0]; + content->SetBounds(gfx::Size(100, 100)); + host_impl_->active_tree()->PushPageScaleFromMainThread(0.5f, 0.5f, 4.f); + host_impl_->active_tree()->BuildPropertyTreesForTesting(); + + DrawFrame(); + + LayerImpl* inner_container = active_tree->InnerViewportContainerLayer(); + LayerImpl* outer_container = active_tree->OuterViewportContainerLayer(); + LayerImpl* outer_scroll = active_tree->OuterViewportScrollLayer(); + auto* property_trees = host_impl_->active_tree()->property_trees(); + ClipNode* outer_clip_node = + property_trees->clip_tree.Node(outer_scroll->clip_tree_index()); + + // The browser controls should start off showing so the viewport should be + // shrunk. + ASSERT_EQ(50, host_impl_->browser_controls_manager()->ContentTopOffset()); + ASSERT_EQ(gfx::Size(100, 100), inner_container->bounds()); + ASSERT_EQ(gfx::Size(100, 100), outer_container->bounds()); + ASSERT_EQ(gfx::SizeF(200, 1000), active_tree->ScrollableSize()); + ASSERT_EQ(gfx::SizeF(100, 100), outer_clip_node->clip.size()); + + ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(gfx::Point()).get(), + InputHandler::TOUCHSCREEN) + .thread); + + // Hide the browser controls by 10px. The outer clip should expand by 20px as + // because the outer viewport is sized based on the minimum scale, in this + // case 0.5. Therefore, changes to the outer viewport need to be divided by + // the minimum scale as well. + { + host_impl_->ScrollBy( + UpdateState(gfx::Point(0, 0), gfx::Vector2dF(0.f, 10.f)).get()); + ASSERT_EQ(40, host_impl_->browser_controls_manager()->ContentTopOffset()); + EXPECT_EQ(gfx::SizeF(100, 120), outer_clip_node->clip.size()); + } + + host_impl_->ScrollEnd(EndState().get()); +} + +// This test is the same as above but ensures the behavior prior to +// BlinkGenPropertyTrees because the outer viewport's clip node size is +// inconsistent with the scroll node's content bounds. Pre-BGPT, the outer +// viewport clip node is always the same size as the inner viewport clip node, +// even if the outer viewport changes size as a result of a non-1 minimum page +// scale factor. +// TODO(bokan): This test can be safely removed post-BGPT. See crbug.com/901083. TEST_F(LayerTreeHostImplBrowserControlsTest, MovingBrowserControlsChangesViewportClip) { - // TODO(bokan): This test is checking pre-blink-gen-property-tree behavior - // and can be removed once that flag ships. See crbug.com/901083. - if (DefaultSettings().use_layer_lists) - return; + auto turn_off_layer_lists = base::BindOnce( + [](LayerTreeSettings* settings) { settings->use_layer_lists = false; }); SetupBrowserControlsAndScrollLayerWithVirtualViewport( - gfx::Size(50, 50), gfx::Size(25, 25), gfx::Size(100, 100)); + gfx::Size(50, 50), gfx::Size(25, 25), gfx::Size(100, 100), + std::move(turn_off_layer_lists)); LayerTreeImpl* active_tree = host_impl_->active_tree(); diff --git a/chromium/cc/trees/layer_tree_host_unittest_animation.cc b/chromium/cc/trees/layer_tree_host_unittest_animation.cc index ad615452fbb..6cd39fc0b0d 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_animation.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_animation.cc @@ -28,6 +28,7 @@ #include "cc/test/layer_tree_test.h" #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_impl.h" +#include "cc/trees/target_property.h" #include "cc/trees/transform_node.h" #include "components/viz/common/quads/compositor_frame.h" @@ -1207,6 +1208,109 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationRemoval MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationRemoval); +// Verifies that the state of a scroll animation is tracked correctly on the +// main and compositor thread and that the KeyframeModel is removed when +// the scroll animation completes. This is a regression test for +// https://crbug.com/962346 and demonstrates the necessity of +// LayerTreeHost::AnimateLayers for https://crbug.com/762717. +class LayerTreeHostAnimationTestScrollOffsetAnimationCompletion + : public LayerTreeHostAnimationTest { + public: + LayerTreeHostAnimationTestScrollOffsetAnimationCompletion() + : final_position_(80.0, 180.0) {} + + void SetupTree() override { + LayerTreeHostAnimationTest::SetupTree(); + + scroll_layer_ = FakePictureLayer::Create(&client_); + scroll_layer_->SetScrollable(gfx::Size(100, 100)); + scroll_layer_->SetBounds(gfx::Size(10000, 10000)); + client_.set_bounds(scroll_layer_->bounds()); + scroll_layer_->SetScrollOffset(gfx::ScrollOffset(100.0, 200.0)); + layer_tree_host()->root_layer()->AddChild(scroll_layer_); + + std::unique_ptr<ScrollOffsetAnimationCurve> curve( + ScrollOffsetAnimationCurve::Create( + final_position_, + CubicBezierTimingFunction::CreatePreset( + CubicBezierTimingFunction::EaseType::EASE_IN_OUT))); + std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( + std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + keyframe_model->set_needs_synchronized_start_time(true); + + AttachAnimationsToTimeline(); + animation_child_->AttachElement(scroll_layer_->element_id()); + animation_child_->AddKeyframeModel(std::move(keyframe_model)); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void BeginMainFrame(const viz::BeginFrameArgs& args) override { + KeyframeModel* keyframe_model = + animation_child_->GetKeyframeModel(TargetProperty::SCROLL_OFFSET); + switch (layer_tree_host()->SourceFrameNumber()) { + case 0: + EXPECT_EQ(scroll_layer_->CurrentScrollOffset().x(), 100); + EXPECT_EQ(scroll_layer_->CurrentScrollOffset().y(), 200); + EXPECT_EQ(KeyframeModel::RunState::WAITING_FOR_TARGET_AVAILABILITY, + keyframe_model->run_state()); + break; + case 1: + EXPECT_EQ(KeyframeModel::RunState::RUNNING, + keyframe_model->run_state()); + break; + default: + break; + } + } + + void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { + if (host_impl->sync_tree()->source_frame_number() == 0) { + GetImplTimelineAndAnimationByID(*host_impl); + return; + } + KeyframeModel* keyframe_model = + animation_child_impl_->GetKeyframeModel(TargetProperty::SCROLL_OFFSET); + if (!keyframe_model || keyframe_model->run_state() == + KeyframeModel::RunState::WAITING_FOR_DELETION) + EndTest(); + } + + void DidFinishImplFrameOnThread(LayerTreeHostImpl* host_impl) override { + if (!animation_child_impl_) + return; + if (KeyframeModel* keyframe_model = animation_child_impl_->GetKeyframeModel( + TargetProperty::SCROLL_OFFSET)) { + if (keyframe_model->run_state() == KeyframeModel::RunState::RUNNING) { + ran_animation_ = true; + } + } + } + + void AfterTest() override { + // The animation should have run for some frames. + EXPECT_TRUE(ran_animation_); + + // The finished KeyframeModel should have been removed from both the + // main and impl side animations. + EXPECT_EQ(nullptr, animation_child_->GetKeyframeModel( + TargetProperty::SCROLL_OFFSET)); + EXPECT_EQ(nullptr, animation_child_impl_->GetKeyframeModel( + TargetProperty::SCROLL_OFFSET)); + + // The scroll should have been completed. + EXPECT_EQ(final_position_, scroll_layer_->CurrentScrollOffset()); + } + + private: + FakeContentLayerClient client_; + scoped_refptr<FakePictureLayer> scroll_layer_; + const gfx::ScrollOffset final_position_; + bool ran_animation_ = false; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationCompletion); + // 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. @@ -1695,6 +1799,8 @@ class LayerTreeHostAnimationTestIsAnimating case 2: KeyframeModel* keyframe_model = animation_->GetKeyframeModel(TargetProperty::TRANSFORM); + EXPECT_EQ(KeyframeModel::RunState::RUNNING, + keyframe_model->run_state()); animation_->RemoveKeyframeModel(keyframe_model->id()); break; } @@ -1713,6 +1819,36 @@ class LayerTreeHostAnimationTestIsAnimating case 2: // The animation is removed/stopped. EXPECT_FALSE(child->screen_space_transform_is_animating()); + break; + case 3: + break; + default: + NOTREACHED(); + } + } + + void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { + GetImplTimelineAndAnimationByID(*host_impl); + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + // No animation yet. + break; + case 1: + // Animation is starting. + EXPECT_EQ(KeyframeModel::RunState::STARTING, + animation_impl_->GetKeyframeModel(TargetProperty::TRANSFORM) + ->run_state()); + break; + case 2: + // After activation, the KeyframeModel should be waiting for deletion. + EXPECT_EQ(KeyframeModel::RunState::WAITING_FOR_DELETION, + animation_impl_->GetKeyframeModel(TargetProperty::TRANSFORM) + ->run_state()); + break; + case 3: + // The animation KeyframeModel is cleaned up. + EXPECT_EQ(nullptr, + animation_impl_->GetKeyframeModel(TargetProperty::TRANSFORM)); EndTest(); break; default: @@ -1731,9 +1867,9 @@ class LayerTreeHostAnimationTestIsAnimating EXPECT_TRUE(child->screen_space_transform_is_animating()); break; case 2: + case 3: // The animation is removed/stopped. EXPECT_FALSE(child->screen_space_transform_is_animating()); - EndTest(); break; default: NOTREACHED(); @@ -2317,12 +2453,9 @@ class LayerTreeHostAnimationTestRebuildPropertyTreesOnAnimationSetNeedsCommit } void UpdateLayerTreeHost() override { - if (layer_tree_host()->SourceFrameNumber() == 1) { - EXPECT_FALSE(layer_tree_host()->property_trees()->needs_rebuild); + if (layer_tree_host()->SourceFrameNumber() == 1) AddAnimatedTransformToAnimation(animation_child_.get(), 1.0, 5, 5); - } - - EXPECT_TRUE(layer_tree_host()->property_trees()->needs_rebuild); + EXPECT_TRUE(layer_tree_host()->proxy()->CommitRequested()); } void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc index 3c4b947f371..8cb9c0516b2 100644 --- a/chromium/cc/trees/proxy_main.cc +++ b/chromium/cc/trees/proxy_main.cc @@ -215,8 +215,8 @@ void ProxyMain::BeginMainFrame( // what this does. layer_tree_host_->BeginMainFrame(begin_main_frame_state->begin_frame_args); - // Updates cc animations on the main-thread. This appears to be entirely - // duplicated by work done in LayerTreeHost::BeginMainFrame. crbug.com/762717. + // Updates cc animations on the main-thread. This is necessary in order + // to track animation states such that they are cleaned up properly. layer_tree_host_->AnimateLayers( begin_main_frame_state->begin_frame_args.frame_time); |