// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/trees/layer_tree_host.h"

#include <stdint.h>

#include "cc/animation/animation_curve.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/animation/animation_player.h"
#include "cc/animation/animation_timeline.h"
#include "cc/animation/element_animations.h"
#include "cc/animation/scroll_offset_animation_curve.h"
#include "cc/animation/scroll_offset_animations.h"
#include "cc/animation/timing_function.h"
#include "cc/animation/transform_operations.h"
#include "cc/base/completion_event.h"
#include "cc/base/time_util.h"
#include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_test.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/transform_node.h"

namespace cc {
namespace {

class LayerTreeHostAnimationTest : public LayerTreeTest {
 public:
  LayerTreeHostAnimationTest()
      : timeline_id_(AnimationIdProvider::NextTimelineId()),
        player_id_(AnimationIdProvider::NextPlayerId()),
        player_child_id_(AnimationIdProvider::NextPlayerId()) {
    timeline_ = AnimationTimeline::Create(timeline_id_);
    player_ = AnimationPlayer::Create(player_id_);
    player_child_ = AnimationPlayer::Create(player_child_id_);

    player_->set_animation_delegate(this);
  }

  void AttachPlayersToTimeline() {
    animation_host()->AddAnimationTimeline(timeline_.get());
    layer_tree()->SetElementIdsForTesting();
    timeline_->AttachPlayer(player_.get());
    timeline_->AttachPlayer(player_child_.get());
  }

  void GetImplTimelineAndPlayerByID(const LayerTreeHostImpl& host_impl) {
    AnimationHost* animation_host_impl = GetImplAnimationHost(&host_impl);
    timeline_impl_ = animation_host_impl->GetTimelineById(timeline_id_);
    EXPECT_TRUE(timeline_impl_);
    player_impl_ = timeline_impl_->GetPlayerById(player_id_);
    EXPECT_TRUE(player_impl_);
    player_child_impl_ = timeline_impl_->GetPlayerById(player_child_id_);
    EXPECT_TRUE(player_child_impl_);
  }

  AnimationHost* GetImplAnimationHost(
      const LayerTreeHostImpl* host_impl) const {
    return static_cast<AnimationHost*>(host_impl->mutator_host());
  }

 protected:
  scoped_refptr<AnimationTimeline> timeline_;
  scoped_refptr<AnimationPlayer> player_;
  scoped_refptr<AnimationPlayer> player_child_;

  scoped_refptr<AnimationTimeline> timeline_impl_;
  scoped_refptr<AnimationPlayer> player_impl_;
  scoped_refptr<AnimationPlayer> player_child_impl_;

  const int timeline_id_;
  const int player_id_;
  const int player_child_id_;
};

// Makes sure that SetNeedsAnimate does not cause the CommitRequested() state to
// be set.
class LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested()
      : num_commits_(0) {}

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void BeginMainFrame(const BeginFrameArgs& args) override {
    // We skip the first commit because its the commit that populates the
    // impl thread with a tree. After the second commit, the test is done.
    if (num_commits_ != 1)
      return;

    layer_tree_host()->SetNeedsAnimate();
    // Right now, CommitRequested is going to be true, because during
    // BeginFrame, we force CommitRequested to true to prevent requests from
    // hitting the impl thread. But, when the next DidCommit happens, we should
    // verify that CommitRequested has gone back to false.
  }

  void DidCommit() override {
    if (!num_commits_) {
      EXPECT_FALSE(layer_tree_host()->CommitRequested());
      layer_tree_host()->SetNeedsAnimate();
      EXPECT_FALSE(layer_tree_host()->CommitRequested());
    }

    // Verifies that the SetNeedsAnimate we made in ::Animate did not
    // trigger CommitRequested.
    EXPECT_FALSE(layer_tree_host()->CommitRequested());
    EndTest();
    num_commits_++;
  }

  void AfterTest() override {}

 private:
  int num_commits_;
};

MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestSetNeedsAnimateShouldNotSetCommitRequested);

// Trigger a frame with SetNeedsCommit. Then, inside the resulting animate
// callback, request another frame using SetNeedsAnimate. End the test when
// animate gets called yet-again, indicating that the proxy is correctly
// handling the case where SetNeedsAnimate() is called inside the BeginFrame
// flow.
class LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback()
      : num_begin_frames_(0) {}

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void BeginMainFrame(const BeginFrameArgs& args) override {
    if (!num_begin_frames_) {
      layer_tree_host()->SetNeedsAnimate();
      num_begin_frames_++;
      return;
    }
    EndTest();
  }

  void AfterTest() override {}

 private:
  int num_begin_frames_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestSetNeedsAnimateInsideAnimationCallback);

// Add a layer animation and confirm that
// LayerTreeHostImpl::UpdateAnimationState does get called.
class LayerTreeHostAnimationTestAddAnimation
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestAddAnimation()
      : update_animation_state_was_called_(false) {}

  void BeginTest() override {
    AttachPlayersToTimeline();
    player_->AttachElement(layer_tree()->root_layer()->element_id());
    PostAddInstantAnimationToMainThreadPlayer(player_.get());
  }

  void UpdateAnimationState(LayerTreeHostImpl* host_impl,
                            bool has_unfinished_animation) override {
    EXPECT_FALSE(has_unfinished_animation);
    update_animation_state_was_called_ = true;
  }

  void NotifyAnimationStarted(base::TimeTicks monotonic_time,
                              TargetProperty::Type target_property,
                              int group) override {
    EXPECT_LT(base::TimeTicks(), monotonic_time);

    Animation* animation = player_->GetAnimation(TargetProperty::OPACITY);
    if (animation)
      player_->RemoveAnimation(animation->id());

    EndTest();
  }

  void AfterTest() override { EXPECT_TRUE(update_animation_state_was_called_); }

 private:
  bool update_animation_state_was_called_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAddAnimation);

// Add a layer animation to a layer, but continually fail to draw. Confirm that
// after a while, we do eventually force a draw.
class LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws()
      : started_animating_(false) {}

  void BeginTest() override {
    AttachPlayersToTimeline();
    player_->AttachElement(layer_tree()->root_layer()->element_id());
    PostAddAnimationToMainThreadPlayer(player_.get());
  }

  void AnimateLayers(LayerTreeHostImpl* host_impl,
                     base::TimeTicks monotonic_time) override {
    started_animating_ = true;
  }

  void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
    if (started_animating_)
      EndTest();
  }

  DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                   LayerTreeHostImpl::FrameData* frame,
                                   DrawResult draw_result) override {
    return DRAW_ABORTED_CHECKERBOARD_ANIMATIONS;
  }

  void AfterTest() override {}

 private:
  bool started_animating_;
};

// Starvation can only be an issue with the MT compositor.
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws);

// Ensures that animations eventually get deleted.
class LayerTreeHostAnimationTestAnimationsGetDeleted
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestAnimationsGetDeleted()
      : started_animating_(false) {}

  void BeginTest() override {
    AttachPlayersToTimeline();
    player_->AttachElement(layer_tree()->root_layer()->element_id());
    PostAddAnimationToMainThreadPlayer(player_.get());
  }

  void AnimateLayers(LayerTreeHostImpl* host_impl,
                     base::TimeTicks monotonic_time) override {
    bool have_animations = !GetImplAnimationHost(host_impl)
                                ->active_element_animations_for_testing()
                                .empty();
    if (!started_animating_ && have_animations) {
      started_animating_ = true;
      return;
    }

    if (started_animating_ && !have_animations)
      EndTest();
  }

  void NotifyAnimationFinished(base::TimeTicks monotonic_time,
                               TargetProperty::Type target_property,
                               int group) override {
    // Animations on the impl-side ElementAnimations only get deleted during
    // a commit, so we need to schedule a commit.
    layer_tree_host()->SetNeedsCommit();
  }

  void AfterTest() override {}

 private:
  bool started_animating_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimationsGetDeleted);

// Ensure that an animation's timing function is respected.
class LayerTreeHostAnimationTestAddAnimationWithTimingFunction
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    picture_ = FakePictureLayer::Create(&client_);
    picture_->SetBounds(gfx::Size(4, 4));
    client_.set_bounds(picture_->bounds());
    layer_tree()->root_layer()->AddChild(picture_);

    AttachPlayersToTimeline();
    player_child_->AttachElement(picture_->element_id());
  }

  void BeginTest() override {
    PostAddAnimationToMainThreadPlayer(player_child_.get());
  }

  void AnimateLayers(LayerTreeHostImpl* host_impl,
                     base::TimeTicks monotonic_time) override {
    // TODO(ajuma): This test only checks the active tree. Add checks for
    // pending tree too.
    if (!host_impl->active_tree()->root_layer_for_testing())
      return;

    // Wait for the commit with the animation to happen.
    if (host_impl->sync_tree()->source_frame_number() != 0)
      return;

    scoped_refptr<AnimationTimeline> timeline_impl =
        GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
    scoped_refptr<AnimationPlayer> player_child_impl =
        timeline_impl->GetPlayerById(player_child_id_);

    Animation* animation =
        player_child_impl->GetAnimation(TargetProperty::OPACITY);

    const FloatAnimationCurve* curve =
        animation->curve()->ToFloatAnimationCurve();
    float start_opacity = curve->GetValue(base::TimeDelta());
    float end_opacity = curve->GetValue(curve->Duration());
    float linearly_interpolated_opacity =
        0.25f * end_opacity + 0.75f * start_opacity;
    base::TimeDelta time = TimeUtil::Scale(curve->Duration(), 0.25f);
    // If the linear timing function associated with this animation was not
    // picked up, then the linearly interpolated opacity would be different
    // because of the default ease timing function.
    EXPECT_FLOAT_EQ(linearly_interpolated_opacity, curve->GetValue(time));

    EndTest();
  }

  void AfterTest() override {}

  FakeContentLayerClient client_;
  scoped_refptr<FakePictureLayer> picture_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestAddAnimationWithTimingFunction);

// Ensures that main thread animations have their start times synchronized with
// impl thread animations.
class LayerTreeHostAnimationTestSynchronizeAnimationStartTimes
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    picture_ = FakePictureLayer::Create(&client_);
    picture_->SetBounds(gfx::Size(4, 4));
    client_.set_bounds(picture_->bounds());

    layer_tree()->root_layer()->AddChild(picture_);

    AttachPlayersToTimeline();
    player_child_->set_animation_delegate(this);
    player_child_->AttachElement(picture_->element_id());
  }

  void BeginTest() override {
    PostAddAnimationToMainThreadPlayer(player_child_.get());
  }

  void NotifyAnimationStarted(base::TimeTicks monotonic_time,
                              TargetProperty::Type target_property,
                              int group) override {
    Animation* animation = player_child_->GetAnimation(TargetProperty::OPACITY);
    main_start_time_ = animation->start_time();
    player_child_->RemoveAnimation(animation->id());
    EndTest();
  }

  void UpdateAnimationState(LayerTreeHostImpl* impl_host,
                            bool has_unfinished_animation) override {
    scoped_refptr<AnimationTimeline> timeline_impl =
        GetImplAnimationHost(impl_host)->GetTimelineById(timeline_id_);
    scoped_refptr<AnimationPlayer> player_child_impl =
        timeline_impl->GetPlayerById(player_child_id_);

    Animation* animation =
        player_child_impl->GetAnimation(TargetProperty::OPACITY);
    if (!animation)
      return;

    impl_start_time_ = animation->start_time();
  }

  void AfterTest() override {
    EXPECT_EQ(impl_start_time_, main_start_time_);
    EXPECT_LT(base::TimeTicks(), impl_start_time_);
  }

 private:
  base::TimeTicks main_start_time_;
  base::TimeTicks impl_start_time_;
  FakeContentLayerClient client_;
  scoped_refptr<FakePictureLayer> picture_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestSynchronizeAnimationStartTimes);

// Ensures that notify animation finished is called.
class LayerTreeHostAnimationTestAnimationFinishedEvents
    : public LayerTreeHostAnimationTest {
 public:
  void BeginTest() override {
    AttachPlayersToTimeline();
    player_->AttachElement(layer_tree()->root_layer()->element_id());
    PostAddInstantAnimationToMainThreadPlayer(player_.get());
  }

  void NotifyAnimationFinished(base::TimeTicks monotonic_time,
                               TargetProperty::Type target_property,
                               int group) override {
    Animation* animation = player_->GetAnimation(TargetProperty::OPACITY);
    if (animation)
      player_->RemoveAnimation(animation->id());
    EndTest();
  }

  void AfterTest() override {}
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestAnimationFinishedEvents);

// Ensures that when opacity is being animated, this value does not cause the
// subtree to be skipped.
class LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity()
      : update_check_layer_() {}

  void SetupTree() override {
    update_check_layer_ = FakePictureLayer::Create(&client_);
    update_check_layer_->SetOpacity(0.f);
    layer_tree()->SetRootLayer(update_check_layer_);
    client_.set_bounds(update_check_layer_->bounds());
    LayerTreeHostAnimationTest::SetupTree();

    AttachPlayersToTimeline();
    player_->AttachElement(update_check_layer_->element_id());
  }

  void BeginTest() override {
    PostAddAnimationToMainThreadPlayer(player_.get());
  }

  void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
    scoped_refptr<AnimationTimeline> timeline_impl =
        GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
    scoped_refptr<AnimationPlayer> player_impl =
        timeline_impl->GetPlayerById(player_id_);

    Animation* animation_impl =
        player_impl->GetAnimation(TargetProperty::OPACITY);
    player_impl->RemoveAnimation(animation_impl->id());
    EndTest();
  }

  void AfterTest() override {
    // Update() should have been called once, proving that the layer was not
    // skipped.
    EXPECT_EQ(1, update_check_layer_->update_count());

    // clear update_check_layer_ so LayerTreeHost dies.
    update_check_layer_ = NULL;
  }

 private:
  FakeContentLayerClient client_;
  scoped_refptr<FakePictureLayer> update_check_layer_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestDoNotSkipLayersWithAnimatedOpacity);

// Layers added to tree with existing active animations should have the
// animation correctly recognized.
class LayerTreeHostAnimationTestLayerAddedWithAnimation
    : public LayerTreeHostAnimationTest {
 public:
  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override {
    if (layer_tree_host()->SourceFrameNumber() == 1) {
      AttachPlayersToTimeline();

      scoped_refptr<Layer> layer = Layer::Create();
      layer->SetElementId(ElementId(42, 0));
      player_->AttachElement(layer->element_id());
      player_->set_animation_delegate(this);

      // Any valid AnimationCurve will do here.
      std::unique_ptr<AnimationCurve> curve(new FakeFloatAnimationCurve());
      std::unique_ptr<Animation> animation(
          Animation::Create(std::move(curve), 1, 1, TargetProperty::OPACITY));
      player_->AddAnimation(std::move(animation));

      // We add the animation *before* attaching the layer to the tree.
      layer_tree()->root_layer()->AddChild(layer);
    }
  }

  void AnimateLayers(LayerTreeHostImpl* impl_host,
                     base::TimeTicks monotonic_time) override {
    EndTest();
  }

  void AfterTest() override {}
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestLayerAddedWithAnimation);

class LayerTreeHostAnimationTestCancelAnimateCommit
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestCancelAnimateCommit()
      : num_begin_frames_(0), num_commit_calls_(0), num_draw_calls_(0) {}

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void BeginMainFrame(const BeginFrameArgs& args) override {
    num_begin_frames_++;
    // No-op animate will cancel the commit.
    if (layer_tree_host()->SourceFrameNumber() == 1) {
      EndTest();
      return;
    }
    layer_tree_host()->SetNeedsAnimate();
  }

  void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
    num_commit_calls_++;
    if (impl->active_tree()->source_frame_number() > 1)
      FAIL() << "Commit should have been canceled.";
  }

  void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
    num_draw_calls_++;
    if (impl->active_tree()->source_frame_number() > 1)
      FAIL() << "Draw should have been canceled.";
  }

  void AfterTest() override {
    EXPECT_EQ(2, num_begin_frames_);
    EXPECT_EQ(1, num_commit_calls_);
    EXPECT_EQ(1, num_draw_calls_);
  }

 private:
  int num_begin_frames_;
  int num_commit_calls_;
  int num_draw_calls_;
};

MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestCancelAnimateCommit);

class LayerTreeHostAnimationTestForceRedraw
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestForceRedraw()
      : num_animate_(0), num_draw_layers_(0) {}

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void BeginMainFrame(const BeginFrameArgs& args) override {
    if (++num_animate_ < 2)
      layer_tree_host()->SetNeedsAnimate();
  }

  void UpdateLayerTreeHost() override {
    layer_tree_host()->SetNextCommitForcesRedraw();
  }

  void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
    if (++num_draw_layers_ == 2)
      EndTest();
  }

  void AfterTest() override {
    // The first commit will always draw; make sure the second draw triggered
    // by the animation was not cancelled.
    EXPECT_EQ(2, num_draw_layers_);
    EXPECT_EQ(2, num_animate_);
  }

 private:
  int num_animate_;
  int num_draw_layers_;
};

MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestForceRedraw);

class LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit()
      : num_animate_(0), num_draw_layers_(0) {}

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void BeginMainFrame(const BeginFrameArgs& args) override {
    if (++num_animate_ <= 2) {
      layer_tree_host()->SetNeedsCommit();
      layer_tree_host()->SetNeedsAnimate();
    }
  }

  void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
    if (++num_draw_layers_ == 2)
      EndTest();
  }

  void AfterTest() override {
    // The first commit will always draw; make sure the second draw triggered
    // by the SetNeedsCommit was not cancelled.
    EXPECT_EQ(2, num_draw_layers_);
    EXPECT_GE(num_animate_, 2);
  }

 private:
  int num_animate_;
  int num_draw_layers_;
};

MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimateAfterSetNeedsCommit);

// Animations should not be started when frames are being skipped due to
// checkerboard.
class LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations
    : public LayerTreeHostAnimationTest {
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    picture_ = FakePictureLayer::Create(&client_);
    picture_->SetBounds(gfx::Size(4, 4));
    client_.set_bounds(picture_->bounds());
    layer_tree()->root_layer()->AddChild(picture_);

    AttachPlayersToTimeline();
    player_child_->AttachElement(picture_->element_id());
    player_child_->set_animation_delegate(this);
  }

  void InitializeSettings(LayerTreeSettings* settings) override {
    // Make sure that drawing many times doesn't cause a checkerboarded
    // animation to start so we avoid flake in this test.
    settings->timeout_and_draw_when_animation_checkerboards = false;
    LayerTreeHostAnimationTest::InitializeSettings(settings);
  }

  void BeginTest() override {
    prevented_draw_ = 0;
    started_times_ = 0;

    PostSetNeedsCommitToMainThread();
  }

  DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                   LayerTreeHostImpl::FrameData* frame_data,
                                   DrawResult draw_result) override {
    // Don't checkerboard when the first animation wants to start.
    if (host_impl->active_tree()->source_frame_number() < 2)
      return draw_result;
    if (TestEnded())
      return draw_result;
    // Act like there is checkerboard when the second animation wants to draw.
    ++prevented_draw_;
    if (prevented_draw_ > 2)
      EndTest();
    return DRAW_ABORTED_CHECKERBOARD_ANIMATIONS;
  }

  void DidCommitAndDrawFrame() override {
    switch (layer_tree_host()->SourceFrameNumber()) {
      case 1:
        // The animation is longer than 1 BeginFrame interval.
        AddOpacityTransitionToPlayer(player_child_.get(), 0.1, 0.2f, 0.8f,
                                     false);
        break;
      case 2:
        // This second animation will not be drawn so it should not start.
        AddAnimatedTransformToPlayer(player_child_.get(), 0.1, 5, 5);
        break;
    }
  }

  void NotifyAnimationStarted(base::TimeTicks monotonic_time,
                              TargetProperty::Type target_property,
                              int group) override {
    if (TestEnded())
      return;
    started_times_++;
  }

  void AfterTest() override {
    // Make sure we tried to draw the second animation but failed.
    EXPECT_LT(0, prevented_draw_);
    // The first animation should be started, but the second should not because
    // of checkerboard.
    EXPECT_EQ(1, started_times_);
  }

  int prevented_draw_;
  int started_times_;
  FakeContentLayerClient client_;
  scoped_refptr<FakePictureLayer> picture_;
};

MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations);

// Verifies that scroll offset animations are only accepted when impl-scrolling
// is supported, and that when scroll offset animations are accepted,
// scroll offset updates are sent back to the main thread.
class LayerTreeHostAnimationTestScrollOffsetChangesArePropagated
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();

    scroll_layer_ = FakePictureLayer::Create(&client_);
    scroll_layer_->SetScrollClipLayerId(layer_tree()->root_layer()->id());
    scroll_layer_->SetBounds(gfx::Size(1000, 1000));
    client_.set_bounds(scroll_layer_->bounds());
    scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
    layer_tree()->root_layer()->AddChild(scroll_layer_);

    AttachPlayersToTimeline();
    player_child_->AttachElement(scroll_layer_->element_id());
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override {
    switch (layer_tree_host()->SourceFrameNumber()) {
      case 1: {
        std::unique_ptr<ScrollOffsetAnimationCurve> curve(
            ScrollOffsetAnimationCurve::Create(
                gfx::ScrollOffset(500.f, 550.f),
                CubicBezierTimingFunction::CreatePreset(
                    CubicBezierTimingFunction::EaseType::EASE_IN_OUT)));
        std::unique_ptr<Animation> animation(Animation::Create(
            std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET));
        animation->set_needs_synchronized_start_time(true);
        bool impl_scrolling_supported = proxy()->SupportsImplScrolling();
        if (impl_scrolling_supported)
          player_child_->AddAnimation(std::move(animation));
        else
          EndTest();
        break;
      }
      default:
        if (scroll_layer_->scroll_offset().x() > 10 &&
            scroll_layer_->scroll_offset().y() > 20)
          EndTest();
    }
  }

  void AfterTest() override {}

 private:
  FakeContentLayerClient client_;
  scoped_refptr<FakePictureLayer> scroll_layer_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestScrollOffsetChangesArePropagated);

// Verifies that when a main thread scrolling reason gets added, the
// notification to takover the animation on the main thread gets sent.
class LayerTreeHostAnimationTestScrollOffsetAnimationTakeover
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestScrollOffsetAnimationTakeover() {}

  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();

    scroll_layer_ = FakePictureLayer::Create(&client_);
    scroll_layer_->SetBounds(gfx::Size(10000, 10000));
    client_.set_bounds(scroll_layer_->bounds());
    scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
    scroll_layer_->SetScrollClipLayerId(layer_tree()->root_layer()->id());
    layer_tree()->root_layer()->AddChild(scroll_layer_);

    AttachPlayersToTimeline();
    player_child_->AttachElement(scroll_layer_->element_id());
    // Allows NotifyAnimationTakeover to get called.
    player_child_->set_animation_delegate(this);
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override {
    if (layer_tree_host()->SourceFrameNumber() == 1) {
      // Add an update after the first commit to trigger the animation takeover
      // path.
      animation_host()->scroll_offset_animations().AddTakeoverUpdate(
          scroll_layer_->element_id());
      EXPECT_TRUE(
          animation_host()->scroll_offset_animations().HasUpdatesForTesting());
    }
  }

  void WillCommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
    if (host_impl->sync_tree()->source_frame_number() == 0) {
      GetImplAnimationHost(host_impl)->ImplOnlyScrollAnimationCreate(
          scroll_layer_->element_id(), gfx::ScrollOffset(650.f, 750.f),
          gfx::ScrollOffset(10, 20), base::TimeDelta());
    }
  }

  void NotifyAnimationTakeover(base::TimeTicks monotonic_time,
                               TargetProperty::Type target_property,
                               double animation_start_time,
                               std::unique_ptr<AnimationCurve> curve) override {
    EndTest();
  }

  void AfterTest() override {}

 private:
  FakeContentLayerClient client_;
  scoped_refptr<FakePictureLayer> scroll_layer_;
};

MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationTakeover);

// Verifies that an impl-only scroll offset animation gets updated when the
// scroll offset is adjusted on the main thread.
class LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted() {}

  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();

    scroll_layer_ = FakePictureLayer::Create(&client_);
    scroll_layer_->SetBounds(gfx::Size(10000, 10000));
    client_.set_bounds(scroll_layer_->bounds());
    scroll_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20));
    scroll_layer_->SetScrollClipLayerId(layer_tree()->root_layer()->id());
    layer_tree()->root_layer()->AddChild(scroll_layer_);

    AttachPlayersToTimeline();
  }

  AnimationPlayer& ScrollOffsetPlayer(
      const LayerTreeHostImpl& host_impl,
      scoped_refptr<FakePictureLayer> layer) const {
    scoped_refptr<ElementAnimations> element_animations =
        GetImplAnimationHost(&host_impl)
            ->GetElementAnimationsForElementId(layer->element_id());
    DCHECK(element_animations);
    DCHECK(element_animations->players_list().might_have_observers());
    AnimationPlayer* player = &*element_animations->players_list().begin();
    DCHECK(player);
    return *player;
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override {
    if (layer_tree_host()->SourceFrameNumber() == 1) {
      // Add an update after the first commit to trigger the animation update
      // path.
      animation_host()->scroll_offset_animations().AddAdjustmentUpdate(
          scroll_layer_->element_id(), gfx::Vector2dF(100.f, 100.f));
      EXPECT_TRUE(
          animation_host()->scroll_offset_animations().HasUpdatesForTesting());
    } else if (layer_tree_host()->SourceFrameNumber() == 2) {
      // Verify that the update queue is cleared after the update is applied.
      EXPECT_FALSE(
          animation_host()->scroll_offset_animations().HasUpdatesForTesting());
    }
  }

  void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override {
    // Note that the frame number gets incremented after BeginCommitOnThread but
    // before WillCommitCompleteOnThread and CommitCompleteOnThread.
    if (host_impl->sync_tree()->source_frame_number() == 0) {
      GetImplTimelineAndPlayerByID(*host_impl);
      // This happens after the impl-only animation is added in
      // WillCommitCompleteOnThread.
      Animation* animation = ScrollOffsetPlayer(*host_impl, scroll_layer_)
                                 .GetAnimation(TargetProperty::SCROLL_OFFSET);
      DCHECK(animation);
      ScrollOffsetAnimationCurve* curve =
          animation->curve()->ToScrollOffsetAnimationCurve();

      // Verifiy the initial and target position before the scroll offset
      // update from MT.
      EXPECT_EQ(Animation::RunState::RUNNING, animation->run_state());
      EXPECT_EQ(gfx::ScrollOffset(10.f, 20.f),
                curve->GetValue(base::TimeDelta()));
      EXPECT_EQ(gfx::ScrollOffset(650.f, 750.f), curve->target_value());
    }
  }

  void WillCommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
    if (host_impl->sync_tree()->source_frame_number() == 0) {
      GetImplAnimationHost(host_impl)->ImplOnlyScrollAnimationCreate(
          scroll_layer_->element_id(), gfx::ScrollOffset(650.f, 750.f),
          gfx::ScrollOffset(10, 20), base::TimeDelta());
    }
  }

  void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
    if (host_impl->sync_tree()->source_frame_number() == 1) {
      Animation* animation = ScrollOffsetPlayer(*host_impl, scroll_layer_)
                                 .GetAnimation(TargetProperty::SCROLL_OFFSET);
      DCHECK(animation);
      ScrollOffsetAnimationCurve* curve =
          animation->curve()->ToScrollOffsetAnimationCurve();
      // Verifiy the initial and target position after the scroll offset
      // update from MT
      EXPECT_EQ(Animation::RunState::STARTING, animation->run_state());
      EXPECT_EQ(gfx::ScrollOffset(110.f, 120.f),
                curve->GetValue(base::TimeDelta()));
      EXPECT_EQ(gfx::ScrollOffset(750.f, 850.f), curve->target_value());

      EndTest();
    }
  }

  void AfterTest() override {}

 private:
  FakeContentLayerClient client_;
  scoped_refptr<FakePictureLayer> scroll_layer_;
};

MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted);

// Verifies that when the main thread removes a scroll animation and sets a new
// scroll position, the active tree takes on exactly this new scroll position
// after activation, and the main thread doesn't receive a spurious scroll
// delta.
class LayerTreeHostAnimationTestScrollOffsetAnimationRemoval
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestScrollOffsetAnimationRemoval()
      : final_postion_(50.0, 100.0) {}

  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();

    scroll_layer_ = FakePictureLayer::Create(&client_);
    scroll_layer_->SetScrollClipLayerId(layer_tree()->root_layer()->id());
    scroll_layer_->SetBounds(gfx::Size(10000, 10000));
    client_.set_bounds(scroll_layer_->bounds());
    scroll_layer_->SetScrollOffset(gfx::ScrollOffset(100.0, 200.0));
    layer_tree()->root_layer()->AddChild(scroll_layer_);

    std::unique_ptr<ScrollOffsetAnimationCurve> curve(
        ScrollOffsetAnimationCurve::Create(
            gfx::ScrollOffset(6500.f, 7500.f),
            CubicBezierTimingFunction::CreatePreset(
                CubicBezierTimingFunction::EaseType::EASE_IN_OUT)));
    std::unique_ptr<Animation> animation(Animation::Create(
        std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET));
    animation->set_needs_synchronized_start_time(true);

    AttachPlayersToTimeline();
    player_child_->AttachElement(scroll_layer_->element_id());
    player_child_->AddAnimation(std::move(animation));
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void BeginMainFrame(const BeginFrameArgs& args) override {
    switch (layer_tree_host()->SourceFrameNumber()) {
      case 0:
        break;
      case 1: {
        Animation* animation =
            player_child_->GetAnimation(TargetProperty::SCROLL_OFFSET);
        player_child_->RemoveAnimation(animation->id());
        scroll_layer_->SetScrollOffset(final_postion_);
        break;
      }
      default:
        EXPECT_EQ(final_postion_, scroll_layer_->scroll_offset());
    }
  }

  void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override {
    host_impl->BlockNotifyReadyToActivateForTesting(
        ShouldBlockActivation(host_impl));
  }

  void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
                                  const BeginFrameArgs& args) override {
    host_impl->BlockNotifyReadyToActivateForTesting(
        ShouldBlockActivation(host_impl));
  }

  void WillActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
    if (host_impl->pending_tree()->source_frame_number() != 1)
      return;
    LayerImpl* scroll_layer_impl =
        host_impl->pending_tree()->LayerById(scroll_layer_->id());
    EXPECT_EQ(final_postion_, scroll_layer_impl->CurrentScrollOffset());
  }

  void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
    if (host_impl->active_tree()->source_frame_number() != 1)
      return;
    LayerImpl* scroll_layer_impl =
        host_impl->active_tree()->LayerById(scroll_layer_->id());
    EXPECT_EQ(final_postion_, scroll_layer_impl->CurrentScrollOffset());
    EndTest();
  }

  void AfterTest() override {
    EXPECT_EQ(final_postion_, scroll_layer_->scroll_offset());
  }

 private:
  bool ShouldBlockActivation(LayerTreeHostImpl* host_impl) {
    if (!host_impl->pending_tree())
      return false;

    if (!host_impl->active_tree()->root_layer_for_testing())
      return false;

    scoped_refptr<AnimationTimeline> timeline_impl =
        GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
    scoped_refptr<AnimationPlayer> player_impl =
        timeline_impl->GetPlayerById(player_child_id_);

    LayerImpl* scroll_layer_impl =
        host_impl->active_tree()->LayerById(scroll_layer_->id());
    Animation* animation =
        player_impl->GetAnimation(TargetProperty::SCROLL_OFFSET);

    if (!animation || animation->run_state() != Animation::RUNNING)
      return false;

    // Block activation until the running animation has a chance to produce a
    // scroll delta.
    gfx::Vector2dF scroll_delta = ScrollDelta(scroll_layer_impl);
    if (scroll_delta.x() > 0.f || scroll_delta.y() > 0.f)
      return false;

    return true;
  }

  FakeContentLayerClient client_;
  scoped_refptr<FakePictureLayer> scroll_layer_;
  const gfx::ScrollOffset final_postion_;
};

MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationRemoval);

// When animations are simultaneously added to an existing layer and to a new
// layer, they should start at the same time, even when there's already a
// running animation on the existing layer.
class LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers()
      : frame_count_with_pending_tree_(0) {}

  void BeginTest() override {
    AttachPlayersToTimeline();
    PostSetNeedsCommitToMainThread();
  }

  void DidCommit() override {
    if (layer_tree_host()->SourceFrameNumber() == 1) {
      player_->AttachElement(layer_tree()->root_layer()->element_id());
      AddAnimatedTransformToPlayer(player_.get(), 4, 1, 1);
    } else if (layer_tree_host()->SourceFrameNumber() == 2) {
      AddOpacityTransitionToPlayer(player_.get(), 1, 0.f, 0.5f, true);

      scoped_refptr<Layer> layer = Layer::Create();
      layer_tree()->root_layer()->AddChild(layer);

      layer_tree()->SetElementIdsForTesting();
      layer->SetBounds(gfx::Size(4, 4));

      player_child_->AttachElement(layer->element_id());
      player_child_->set_animation_delegate(this);
      AddOpacityTransitionToPlayer(player_child_.get(), 1, 0.f, 0.5f, true);
    }
  }

  void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override {
    host_impl->BlockNotifyReadyToActivateForTesting(true);
  }

  void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
    // For the commit that added animations to new and existing layers, keep
    // blocking activation. We want to verify that even with activation blocked,
    // the animation on the layer that's already in the active tree won't get a
    // head start.
    if (host_impl->pending_tree()->source_frame_number() != 2) {
      host_impl->BlockNotifyReadyToActivateForTesting(false);
    }
  }

  void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
                                  const BeginFrameArgs& args) override {
    if (!host_impl->pending_tree() ||
        host_impl->pending_tree()->source_frame_number() != 2)
      return;

    frame_count_with_pending_tree_++;
    if (frame_count_with_pending_tree_ == 2) {
      host_impl->BlockNotifyReadyToActivateForTesting(false);
    }
  }

  void UpdateAnimationState(LayerTreeHostImpl* host_impl,
                            bool has_unfinished_animation) override {
    scoped_refptr<AnimationTimeline> timeline_impl =
        GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
    scoped_refptr<AnimationPlayer> player_impl =
        timeline_impl->GetPlayerById(player_id_);
    scoped_refptr<AnimationPlayer> player_child_impl =
        timeline_impl->GetPlayerById(player_child_id_);

    // wait for tree activation.
    if (!player_impl->element_animations())
      return;

    Animation* root_animation =
        player_impl->GetAnimation(TargetProperty::OPACITY);
    if (!root_animation || root_animation->run_state() != Animation::RUNNING)
      return;

    Animation* child_animation =
        player_child_impl->GetAnimation(TargetProperty::OPACITY);
    EXPECT_EQ(Animation::RUNNING, child_animation->run_state());
    EXPECT_EQ(root_animation->start_time(), child_animation->start_time());
    player_impl->AbortAnimations(TargetProperty::OPACITY, false);
    player_impl->AbortAnimations(TargetProperty::TRANSFORM, false);
    player_child_impl->AbortAnimations(TargetProperty::OPACITY, false);
    EndTest();
  }

  void AfterTest() override {}

 private:
  int frame_count_with_pending_tree_;
};

// This test blocks activation which is not supported for single thread mode.
MULTI_THREAD_BLOCKNOTIFY_TEST_F(
    LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers);

class LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();

    layer_ = FakePictureLayer::Create(&client_);
    layer_->SetBounds(gfx::Size(2, 2));
    client_.set_bounds(layer_->bounds());
    // Transform the layer to 4,4 to start.
    gfx::Transform start_transform;
    start_transform.Translate(4.0, 4.0);
    layer_->SetTransform(start_transform);

    layer_tree()->root_layer()->AddChild(layer_);
    layer_tree()->SetElementIdsForTesting();

    player_->AttachElement(layer_->element_id());

    AttachPlayersToTimeline();
  }

  void BeginTest() override {
    // Add a translate from 6,7 to 8,9.
    TransformOperations start;
    start.AppendTranslate(6.f, 7.f, 0.f);
    TransformOperations end;
    end.AppendTranslate(8.f, 9.f, 0.f);
    AddAnimatedTransformToPlayer(player_.get(), 4.0, start, end);

    PostSetNeedsCommitToMainThread();
  }

  void WillPrepareTilesOnThread(LayerTreeHostImpl* host_impl) override {
    if (host_impl->sync_tree()->source_frame_number() != 0)
      return;

    // After checking this on the sync tree, we will activate, which will cause
    // PrepareTiles to happen again (which races with the test exiting).
    if (TestEnded())
      return;

    scoped_refptr<AnimationTimeline> timeline_impl =
        GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
    scoped_refptr<AnimationPlayer> player_impl =
        timeline_impl->GetPlayerById(player_id_);

    LayerImpl* child = host_impl->sync_tree()->LayerById(layer_->id());
    Animation* animation = player_impl->GetAnimation(TargetProperty::TRANSFORM);

    // The animation should be starting for the first frame.
    EXPECT_EQ(Animation::STARTING, animation->run_state());

    // And the transform should be propogated to the sync tree layer, at its
    // starting state which is 6,7.
    gfx::Transform expected_transform;
    expected_transform.Translate(6.0, 7.0);
    EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, child->DrawTransform());
    // And the sync tree layer should know it is animating.
    EXPECT_TRUE(child->screen_space_transform_is_animating());

    player_impl->AbortAnimations(TargetProperty::TRANSFORM, false);
    EndTest();
  }

  void AfterTest() override {}

  FakeContentLayerClient client_;
  scoped_refptr<Layer> layer_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit);

// When a layer with an animation is removed from the tree and later re-added,
// the animation should resume.
class LayerTreeHostAnimationTestAnimatedLayerRemovedAndAdded
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    layer_ = Layer::Create();
    layer_->SetBounds(gfx::Size(4, 4));
    layer_tree()->root_layer()->AddChild(layer_);

    layer_tree()->SetElementIdsForTesting();

    animation_host()->AddAnimationTimeline(timeline_.get());
    timeline_->AttachPlayer(player_.get());
    player_->AttachElement(layer_->element_id());
    DCHECK(player_->element_animations());

    AddOpacityTransitionToPlayer(player_.get(), 10000.0, 0.1f, 0.9f, true);
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override {
    switch (layer_tree_host()->SourceFrameNumber()) {
      case 0:
        EXPECT_TRUE(
            player_->element_animations()->has_element_in_active_list());
        EXPECT_FALSE(
            player_->element_animations()->has_element_in_pending_list());
        EXPECT_TRUE(animation_host()->NeedsAnimateLayers());
        break;
      case 1:
        layer_->RemoveFromParent();
        EXPECT_FALSE(
            player_->element_animations()->has_element_in_active_list());
        EXPECT_FALSE(
            player_->element_animations()->has_element_in_pending_list());
        EXPECT_FALSE(animation_host()->NeedsAnimateLayers());
        break;
      case 2:
        layer_tree()->root_layer()->AddChild(layer_);
        EXPECT_TRUE(
            player_->element_animations()->has_element_in_active_list());
        EXPECT_FALSE(
            player_->element_animations()->has_element_in_pending_list());
        EXPECT_TRUE(animation_host()->NeedsAnimateLayers());
        break;
    }
  }

  void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
    scoped_refptr<AnimationTimeline> timeline_impl =
        GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_);
    scoped_refptr<AnimationPlayer> player_impl =
        timeline_impl->GetPlayerById(player_id_);

    switch (host_impl->active_tree()->source_frame_number()) {
      case 0:
        EXPECT_TRUE(
            player_impl->element_animations()->has_element_in_active_list());
        EXPECT_TRUE(GetImplAnimationHost(host_impl)->NeedsAnimateLayers());
        break;
      case 1:
        EXPECT_FALSE(
            player_impl->element_animations()->has_element_in_active_list());
        EXPECT_FALSE(GetImplAnimationHost(host_impl)->NeedsAnimateLayers());
        break;
      case 2:
        EXPECT_TRUE(
            player_impl->element_animations()->has_element_in_active_list());
        EXPECT_TRUE(GetImplAnimationHost(host_impl)->NeedsAnimateLayers());
        EndTest();
        break;
    }
  }

  void AfterTest() override {}

 private:
  scoped_refptr<Layer> layer_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestAnimatedLayerRemovedAndAdded);

class LayerTreeHostAnimationTestAddAnimationAfterAnimating
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    layer_ = Layer::Create();
    layer_->SetBounds(gfx::Size(4, 4));
    layer_tree()->root_layer()->AddChild(layer_);

    AttachPlayersToTimeline();

    player_->AttachElement(layer_tree()->root_layer()->element_id());
    player_child_->AttachElement(layer_->element_id());
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override {
    switch (layer_tree_host()->SourceFrameNumber()) {
      case 1:
        // First frame: add an animation to the root layer.
        AddAnimatedTransformToPlayer(player_.get(), 0.1, 5, 5);
        break;
      case 2:
        // Second frame: add an animation to the content layer. The root layer
        // animation has caused us to animate already during this frame.
        AddOpacityTransitionToPlayer(player_child_.get(), 0.1, 5, 5, false);
        break;
    }
  }

  void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
    // After both animations have started, verify that they have valid
    // start times.
    if (host_impl->active_tree()->source_frame_number() < 2)
      return;

    // Animation state is updated after drawing. Only do this once.
    if (!TestEnded()) {
      ImplThreadTaskRunner()->PostTask(
          FROM_HERE,
          base::Bind(&LayerTreeHostAnimationTestAddAnimationAfterAnimating::
                         CheckAnimations,
                     base::Unretained(this), host_impl));
    }
  }

  void CheckAnimations(LayerTreeHostImpl* host_impl) {
    GetImplTimelineAndPlayerByID(*host_impl);

    EXPECT_EQ(2u, GetImplAnimationHost(host_impl)
                      ->active_element_animations_for_testing()
                      .size());

    Animation* root_anim =
        player_impl_->GetAnimation(TargetProperty::TRANSFORM);
    EXPECT_GT((root_anim->start_time() - base::TimeTicks()).InSecondsF(), 0);

    Animation* anim = player_child_impl_->GetAnimation(TargetProperty::OPACITY);
    EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0);

    EndTest();
  }

  void AfterTest() override {}

 private:
  scoped_refptr<Layer> layer_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestAddAnimationAfterAnimating);

class LayerTreeHostAnimationTestRemoveAnimation
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    layer_ = FakePictureLayer::Create(&client_);
    layer_->SetBounds(gfx::Size(4, 4));
    client_.set_bounds(layer_->bounds());
    layer_tree()->root_layer()->AddChild(layer_);

    AttachPlayersToTimeline();

    player_->AttachElement(layer_tree()->root_layer()->element_id());
    player_child_->AttachElement(layer_->element_id());
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override {
    switch (layer_tree_host()->SourceFrameNumber()) {
      case 1:
        AddAnimatedTransformToPlayer(player_child_.get(), 1.0, 5, 5);
        break;
      case 2:
        Animation* animation =
            player_child_->GetAnimation(TargetProperty::TRANSFORM);
        player_child_->RemoveAnimation(animation->id());
        gfx::Transform transform;
        transform.Translate(10.f, 10.f);
        layer_->SetTransform(transform);

        // Do something that causes property trees to get rebuilt. This is
        // intended to simulate the conditions that caused the bug whose fix
        // this is testing (the test will pass without it but won't test what
        // we want it to). We were updating the wrong transform node at the end
        // of an animation (we were assuming the layer with the finished
        // animation still had its own transform node). But nodes can only get
        // added/deleted when something triggers a rebuild. Adding a layer
        // triggers a rebuild, and since the layer that had an animation before
        // no longer has one, it doesn't get a transform node in the rebuild.
        layer_->AddChild(Layer::Create());
        break;
    }
  }

  void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
    LayerImpl* child = host_impl->active_tree()->LayerById(layer_->id());
    switch (host_impl->active_tree()->source_frame_number()) {
      case 0:
        // No animation yet.
        break;
      case 1:
        // Animation is started.
        EXPECT_TRUE(child->screen_space_transform_is_animating());
        break;
      case 2: {
        // The animation is removed, the transform that was set afterward is
        // applied.
        gfx::Transform expected_transform;
        expected_transform.Translate(10.f, 10.f);
        EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform,
                                        child->DrawTransform());
        EXPECT_FALSE(child->screen_space_transform_is_animating());
        EndTest();
        break;
      }
      default:
        NOTREACHED();
    }
  }

  void AfterTest() override {}

 private:
  scoped_refptr<Layer> layer_;
  FakeContentLayerClient client_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestRemoveAnimation);

class LayerTreeHostAnimationTestIsAnimating
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    layer_ = FakePictureLayer::Create(&client_);
    layer_->SetBounds(gfx::Size(4, 4));
    client_.set_bounds(layer_->bounds());
    layer_tree()->root_layer()->AddChild(layer_);

    AttachPlayersToTimeline();
    player_->AttachElement(layer_->element_id());
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override {
    switch (layer_tree_host()->SourceFrameNumber()) {
      case 1:
        AddAnimatedTransformToPlayer(player_.get(), 1.0, 5, 5);
        break;
      case 2:
        Animation* animation = player_->GetAnimation(TargetProperty::TRANSFORM);
        player_->RemoveAnimation(animation->id());
        break;
    }
  }

  void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
    LayerImpl* child = host_impl->sync_tree()->LayerById(layer_->id());
    switch (host_impl->sync_tree()->source_frame_number()) {
      case 0:
        // No animation yet.
        break;
      case 1:
        // Animation is started.
        EXPECT_TRUE(child->screen_space_transform_is_animating());
        break;
      case 2:
        // The animation is removed/stopped.
        EXPECT_FALSE(child->screen_space_transform_is_animating());
        EndTest();
        break;
      default:
        NOTREACHED();
    }
  }

  void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
    LayerImpl* child = host_impl->active_tree()->LayerById(layer_->id());
    switch (host_impl->active_tree()->source_frame_number()) {
      case 0:
        // No animation yet.
        break;
      case 1:
        // Animation is started.
        EXPECT_TRUE(child->screen_space_transform_is_animating());
        break;
      case 2:
        // The animation is removed/stopped.
        EXPECT_FALSE(child->screen_space_transform_is_animating());
        EndTest();
        break;
      default:
        NOTREACHED();
    }
  }

  void AfterTest() override {}

 private:
  scoped_refptr<Layer> layer_;
  FakeContentLayerClient client_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestIsAnimating);

class LayerTreeHostAnimationTestAnimationFinishesDuringCommit
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestAnimationFinishesDuringCommit()
      : signalled_(false) {}

  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    layer_ = FakePictureLayer::Create(&client_);
    layer_->SetBounds(gfx::Size(4, 4));
    client_.set_bounds(layer_->bounds());
    layer_tree()->root_layer()->AddChild(layer_);

    AttachPlayersToTimeline();

    player_->AttachElement(layer_tree()->root_layer()->element_id());
    player_child_->AttachElement(layer_->element_id());
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override {
    if (layer_tree_host()->SourceFrameNumber() == 1)
      AddAnimatedTransformToPlayer(player_child_.get(), 0.04, 5, 5);
  }

  void WillCommit() override {
    if (layer_tree_host()->SourceFrameNumber() == 2) {
      // Block until the animation finishes on the compositor thread. Since
      // animations have already been ticked on the main thread, when the commit
      // happens the state on the main thread will be consistent with having a
      // running animation but the state on the compositor thread will be
      // consistent with having only a finished animation.
      completion_.Wait();
    }
  }

  void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
    switch (host_impl->sync_tree()->source_frame_number()) {
      case 1:
        PostSetNeedsCommitToMainThread();
        break;
      case 2:
        gfx::Transform expected_transform;
        expected_transform.Translate(5.f, 5.f);
        LayerImpl* layer_impl = host_impl->sync_tree()->LayerById(layer_->id());
        EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform,
                                        layer_impl->DrawTransform());
        EndTest();
        break;
    }
  }

  void UpdateAnimationState(LayerTreeHostImpl* host_impl,
                            bool has_unfinished_animation) override {
    if (host_impl->active_tree()->source_frame_number() == 1 &&
        !has_unfinished_animation && !signalled_) {
      // The animation has finished, so allow the main thread to commit.
      completion_.Signal();
      signalled_ = true;
    }
  }

  void AfterTest() override {}

 private:
  scoped_refptr<Layer> layer_;
  FakeContentLayerClient client_;
  CompletionEvent completion_;
  bool signalled_;
};

// An animation finishing during commit can only happen when we have a separate
// compositor thread.
MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimationFinishesDuringCommit);

class LayerTreeHostAnimationTestNotifyAnimationFinished
    : public LayerTreeHostAnimationTest {
 public:
  LayerTreeHostAnimationTestNotifyAnimationFinished()
      : called_animation_started_(false), called_animation_finished_(false) {}

  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    picture_ = FakePictureLayer::Create(&client_);
    picture_->SetBounds(gfx::Size(4, 4));
    client_.set_bounds(picture_->bounds());
    layer_tree()->root_layer()->AddChild(picture_);

    AttachPlayersToTimeline();
    player_->AttachElement(picture_->element_id());
    player_->set_animation_delegate(this);
  }

  void BeginTest() override {
    PostAddLongAnimationToMainThreadPlayer(player_.get());
  }

  void NotifyAnimationStarted(base::TimeTicks monotonic_time,
                              TargetProperty::Type target_property,
                              int group) override {
    called_animation_started_ = true;
    layer_tree_host_in_process()->AnimateLayers(
        base::TimeTicks::FromInternalValue(
            std::numeric_limits<int64_t>::max()));
    PostSetNeedsCommitToMainThread();
  }

  void NotifyAnimationFinished(base::TimeTicks monotonic_time,
                               TargetProperty::Type target_property,
                               int group) override {
    called_animation_finished_ = true;
    EndTest();
  }

  void AfterTest() override {
    EXPECT_TRUE(called_animation_started_);
    EXPECT_TRUE(called_animation_finished_);
  }

 private:
  bool called_animation_started_;
  bool called_animation_finished_;
  FakeContentLayerClient client_;
  scoped_refptr<FakePictureLayer> picture_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestNotifyAnimationFinished);

// Check that transform sync happens correctly at commit when we remove and add
// a different animation player to an element.
class LayerTreeHostAnimationTestChangeAnimationPlayer
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    AttachPlayersToTimeline();
    timeline_->DetachPlayer(player_child_.get());
    player_->AttachElement(layer_tree()->root_layer()->element_id());

    TransformOperations start;
    start.AppendTranslate(5.f, 5.f, 0.f);
    TransformOperations end;
    end.AppendTranslate(5.f, 5.f, 0.f);
    AddAnimatedTransformToPlayer(player_.get(), 1.0, start, end);
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
    PropertyTrees* property_trees = host_impl->sync_tree()->property_trees();
    TransformNode* node =
        property_trees->transform_tree.Node(host_impl->sync_tree()
                                                ->root_layer_for_testing()
                                                ->transform_tree_index());
    gfx::Transform translate;
    translate.Translate(5, 5);
    switch (host_impl->sync_tree()->source_frame_number()) {
      case 2:
        EXPECT_TRANSFORMATION_MATRIX_EQ(node->local, translate);
        EndTest();
        break;
      default:
        break;
    }
  }

  void DidCommit() override { PostSetNeedsCommitToMainThread(); }

  void WillBeginMainFrame() override {
    if (layer_tree_host()->SourceFrameNumber() == 2) {
      // Destroy player.
      timeline_->DetachPlayer(player_.get());
      player_ = nullptr;
      timeline_->AttachPlayer(player_child_.get());
      player_child_->AttachElement(layer_tree()->root_layer()->element_id());
      AddAnimatedTransformToPlayer(player_child_.get(), 1.0, 10, 10);
      Animation* animation =
          player_child_->GetAnimation(TargetProperty::TRANSFORM);
      animation->set_start_time(base::TimeTicks::Now() +
                                base::TimeDelta::FromSecondsD(1000));
      animation->set_fill_mode(Animation::FillMode::NONE);
    }
  }

  void AfterTest() override {}
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestChangeAnimationPlayer);

// Check that SetTransformIsPotentiallyAnimatingChanged is called
// if we destroy ElementAnimations.
class LayerTreeHostAnimationTestSetPotentiallyAnimatingOnLacDestruction
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    prev_screen_space_transform_is_animating_ = true;
    screen_space_transform_animation_stopped_ = false;

    LayerTreeHostAnimationTest::SetupTree();
    AttachPlayersToTimeline();
    player_->AttachElement(layer_tree()->root_layer()->element_id());
    AddAnimatedTransformToPlayer(player_.get(), 1.0, 5, 5);
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
    if (host_impl->pending_tree()->source_frame_number() <= 1) {
      EXPECT_TRUE(host_impl->pending_tree()
                      ->root_layer_for_testing()
                      ->screen_space_transform_is_animating());
    } else {
      EXPECT_FALSE(host_impl->pending_tree()
                       ->root_layer_for_testing()
                       ->screen_space_transform_is_animating());
    }
  }

  void DidCommit() override { PostSetNeedsCommitToMainThread(); }

  void UpdateLayerTreeHost() override {
    if (layer_tree_host()->SourceFrameNumber() == 2) {
      // Destroy player.
      timeline_->DetachPlayer(player_.get());
      player_ = nullptr;
    }
  }

  DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                   LayerTreeHostImpl::FrameData* frame_data,
                                   DrawResult draw_result) override {
    const bool screen_space_transform_is_animating =
        host_impl->active_tree()
            ->root_layer_for_testing()
            ->screen_space_transform_is_animating();

    // Check that screen_space_transform_is_animating changes only once.
    if (screen_space_transform_is_animating &&
        prev_screen_space_transform_is_animating_)
      EXPECT_FALSE(screen_space_transform_animation_stopped_);
    if (!screen_space_transform_is_animating &&
        prev_screen_space_transform_is_animating_) {
      EXPECT_FALSE(screen_space_transform_animation_stopped_);
      screen_space_transform_animation_stopped_ = true;
    }
    if (!screen_space_transform_is_animating &&
        !prev_screen_space_transform_is_animating_)
      EXPECT_TRUE(screen_space_transform_animation_stopped_);

    prev_screen_space_transform_is_animating_ =
        screen_space_transform_is_animating;

    return draw_result;
  }

  void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
    if (host_impl->active_tree()->source_frame_number() >= 2)
      EndTest();
  }

  void AfterTest() override {
    EXPECT_TRUE(screen_space_transform_animation_stopped_);
  }

  bool prev_screen_space_transform_is_animating_;
  bool screen_space_transform_animation_stopped_;
};

MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestSetPotentiallyAnimatingOnLacDestruction);

// Check that we invalidate property trees on AnimationPlayer::SetNeedsCommit.
class LayerTreeHostAnimationTestRebuildPropertyTreesOnAnimationSetNeedsCommit
    : public LayerTreeHostAnimationTest {
 public:
  void SetupTree() override {
    LayerTreeHostAnimationTest::SetupTree();
    layer_ = FakePictureLayer::Create(&client_);
    layer_->SetBounds(gfx::Size(4, 4));
    client_.set_bounds(layer_->bounds());
    layer_tree()->root_layer()->AddChild(layer_);

    AttachPlayersToTimeline();

    player_->AttachElement(layer_tree()->root_layer()->element_id());
    player_child_->AttachElement(layer_->element_id());
  }

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override {
    if (layer_tree_host()->SourceFrameNumber() == 1 ||
        layer_tree_host()->SourceFrameNumber() == 2)
      PostSetNeedsCommitToMainThread();
  }

  void UpdateLayerTreeHost() override {
    if (layer_tree_host()->SourceFrameNumber() == 1) {
      EXPECT_FALSE(layer_tree()->property_trees()->needs_rebuild);
      AddAnimatedTransformToPlayer(player_child_.get(), 1.0, 5, 5);
    }

    EXPECT_TRUE(layer_tree()->property_trees()->needs_rebuild);
  }

  void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
    if (host_impl->active_tree()->source_frame_number() >= 2)
      EndTest();
  }

  void AfterTest() override {}

 private:
  scoped_refptr<Layer> layer_;
  FakeContentLayerClient client_;
};

MULTI_THREAD_TEST_F(
    LayerTreeHostAnimationTestRebuildPropertyTreesOnAnimationSetNeedsCommit);

}  // namespace
}  // namespace cc
