[go: nahoru, domu]

CC Animations: Establish AnimationHost, AnimationTimeline and AnimationPlayer.

The compositor should not need to worry about servicing animations.
Instead, it should be able to request property updates as needed from external
mutators.

- We setup CC representations for blink::AnimationPlayer and blink::AnimationTimeline.
- We want to move all the animation-related code from LayerTreeHost to AnimationHost.
- We move LayerAnimatedController ownership to cc::AnimationPlayer.
- We move AnimationRegistrar ownership to cc::AnimationHost.
- We add/remove animations to cc::AnimationPlayer from now.
- LayerAnimatedController to be merged into cc::AnimaitonPlayer.
- AnimationRegistrar to be merged into cc::AnimationHost/cc::AnimationTimeline.

A chromium part. Blink part: https://codereview.chromium.org/946323002

Next episode:
https://codereview.chromium.org/1010663002/

BUG=394777
R=dstockwell@chromium.org
R=shane@chromium.org
R=ajuma@chromium.org
R=vollick@chromium.org
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

Review URL: https://codereview.chromium.org/947033002

Cr-Commit-Position: refs/heads/master@{#337266}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index de54f0d..2d640ea 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -13,10 +13,18 @@
     "animation/animation_delegate.h",
     "animation/animation_events.cc",
     "animation/animation_events.h",
+    "animation/animation_host.cc",
+    "animation/animation_host.h",
     "animation/animation_id_provider.cc",
     "animation/animation_id_provider.h",
+    "animation/animation_player.cc",
+    "animation/animation_player.h",
     "animation/animation_registrar.cc",
     "animation/animation_registrar.h",
+    "animation/animation_timeline.cc",
+    "animation/animation_timeline.h",
+    "animation/element_animations.cc",
+    "animation/element_animations.h",
     "animation/keyframed_animation_curve.cc",
     "animation/keyframed_animation_curve.h",
     "animation/layer_animation_controller.cc",
@@ -472,6 +480,7 @@
     "trees/layer_tree_impl.h",
     "trees/layer_tree_settings.cc",
     "trees/layer_tree_settings.h",
+    "trees/mutator_host_client.h",
     "trees/occlusion.cc",
     "trees/occlusion.h",
     "trees/occlusion_tracker.cc",
@@ -529,6 +538,8 @@
   sources = [
     "test/animation_test_common.cc",
     "test/animation_test_common.h",
+    "test/animation_timelines_test_common.cc",
+    "test/animation_timelines_test_common.h",
     "test/begin_frame_args_test.cc",
     "test/begin_frame_args_test.h",
     "test/failure_output_surface.cc",
@@ -686,7 +697,11 @@
 
 test("cc_unittests") {
   sources = [
+    "animation/animation_host_unittest.cc",
+    "animation/animation_player_unittest.cc",
+    "animation/animation_timeline_unittest.cc",
     "animation/animation_unittest.cc",
+    "animation/element_animations_unittest.cc",
     "animation/keyframed_animation_curve_unittest.cc",
     "animation/layer_animation_controller_unittest.cc",
     "animation/scroll_offset_animation_curve_unittest.cc",
diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc
new file mode 100644
index 0000000..99e13e60
--- /dev/null
+++ b/cc/animation/animation_host.cc
@@ -0,0 +1,205 @@
+// 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_host.h"
+
+#include <algorithm>
+
+#include "cc/animation/animation_player.h"
+#include "cc/animation/animation_registrar.h"
+#include "cc/animation/animation_timeline.h"
+#include "cc/animation/element_animations.h"
+
+namespace cc {
+
+scoped_ptr<AnimationHost> AnimationHost::Create(
+    ThreadInstance thread_instance) {
+  return make_scoped_ptr(new AnimationHost(thread_instance));
+}
+
+AnimationHost::AnimationHost(ThreadInstance thread_instance)
+    : animation_registrar_(AnimationRegistrar::Create()),
+      mutator_host_client_(),
+      thread_instance_(thread_instance) {
+}
+
+AnimationHost::~AnimationHost() {
+  ClearTimelines();
+  DCHECK(!mutator_host_client());
+  DCHECK(layer_to_element_animations_map_.empty());
+}
+
+AnimationTimeline* AnimationHost::GetTimelineById(int timeline_id) const {
+  for (auto& timeline : timelines_)
+    if (timeline->id() == timeline_id)
+      return timeline.get();
+  return nullptr;
+}
+
+void AnimationHost::ClearTimelines() {
+  EraseTimelines(timelines_.begin(), timelines_.end());
+}
+
+void AnimationHost::EraseTimelines(AnimationTimelineList::iterator begin,
+                                   AnimationTimelineList::iterator end) {
+  for (auto i = begin; i != end; ++i) {
+    auto& timeline = *i;
+    timeline->ClearPlayers();
+    timeline->SetAnimationHost(nullptr);
+  }
+
+  timelines_.erase(begin, end);
+}
+
+void AnimationHost::AddAnimationTimeline(
+    scoped_refptr<AnimationTimeline> timeline) {
+  timeline->SetAnimationHost(this);
+  timelines_.push_back(timeline);
+}
+
+void AnimationHost::RemoveAnimationTimeline(
+    scoped_refptr<AnimationTimeline> timeline) {
+  for (auto iter = timelines_.begin(); iter != timelines_.end(); ++iter) {
+    if (iter->get() != timeline)
+      continue;
+
+    EraseTimelines(iter, iter + 1);
+    break;
+  }
+}
+
+void AnimationHost::RegisterLayer(int layer_id, LayerTreeType tree_type) {
+  ElementAnimations* element_animations =
+      GetElementAnimationsForLayerId(layer_id);
+  if (element_animations)
+    element_animations->LayerRegistered(layer_id, tree_type);
+}
+
+void AnimationHost::UnregisterLayer(int layer_id, LayerTreeType tree_type) {
+  ElementAnimations* element_animations =
+      GetElementAnimationsForLayerId(layer_id);
+  if (element_animations)
+    element_animations->LayerUnregistered(layer_id, tree_type);
+}
+
+void AnimationHost::RegisterPlayerForLayer(int layer_id,
+                                           AnimationPlayer* player) {
+  DCHECK(layer_id);
+  DCHECK(player);
+
+  ElementAnimations* element_animations =
+      GetElementAnimationsForLayerId(layer_id);
+  if (!element_animations) {
+    auto new_element_animations = ElementAnimations::Create(this);
+    element_animations = new_element_animations.get();
+
+    layer_to_element_animations_map_.add(layer_id,
+                                         new_element_animations.Pass());
+    element_animations->CreateLayerAnimationController(layer_id);
+  }
+
+  DCHECK(element_animations);
+  element_animations->AddPlayer(player);
+}
+
+void AnimationHost::UnregisterPlayerForLayer(int layer_id,
+                                             AnimationPlayer* player) {
+  DCHECK(layer_id);
+  DCHECK(player);
+
+  ElementAnimations* element_animations =
+      GetElementAnimationsForLayerId(layer_id);
+  DCHECK(element_animations);
+  element_animations->RemovePlayer(player);
+
+  if (element_animations->IsEmpty()) {
+    element_animations->DestroyLayerAnimationController();
+    layer_to_element_animations_map_.erase(layer_id);
+    element_animations = nullptr;
+  }
+}
+
+void AnimationHost::SetMutatorHostClient(MutatorHostClient* client) {
+  if (mutator_host_client_ == client)
+    return;
+
+  mutator_host_client_ = client;
+}
+
+void AnimationHost::SetNeedsCommit() {
+  DCHECK(mutator_host_client_);
+  mutator_host_client_->SetMutatorsNeedCommit();
+}
+
+void AnimationHost::PushPropertiesTo(AnimationHost* host_impl) {
+  PushTimelinesToImplThread(host_impl);
+  RemoveTimelinesFromImplThread(host_impl);
+  PushPropertiesToImplThread(host_impl);
+}
+
+void AnimationHost::PushTimelinesToImplThread(AnimationHost* host_impl) const {
+  for (auto& timeline : timelines_) {
+    AnimationTimeline* timeline_impl =
+        host_impl->GetTimelineById(timeline->id());
+    if (timeline_impl)
+      continue;
+
+    scoped_refptr<AnimationTimeline> to_add = timeline->CreateImplInstance();
+    host_impl->AddAnimationTimeline(to_add.get());
+  }
+}
+
+void AnimationHost::RemoveTimelinesFromImplThread(
+    AnimationHost* host_impl) const {
+  AnimationTimelineList& timelines_impl = host_impl->timelines_;
+
+  auto to_erase =
+      std::partition(timelines_impl.begin(), timelines_impl.end(),
+                     [this](AnimationTimelineList::value_type timeline_impl) {
+                       return timeline_impl->is_impl_only() ||
+                              GetTimelineById(timeline_impl->id());
+                     });
+
+  host_impl->EraseTimelines(to_erase, timelines_impl.end());
+}
+
+void AnimationHost::PushPropertiesToImplThread(AnimationHost* host_impl) {
+  // Firstly, sync all players with impl thread to create ElementAnimations and
+  // layer animation controllers.
+  for (auto& timeline : timelines_) {
+    AnimationTimeline* timeline_impl =
+        host_impl->GetTimelineById(timeline->id());
+    if (timeline_impl)
+      timeline->PushPropertiesTo(timeline_impl);
+  }
+
+  // Secondly, sync properties for created layer animation controllers.
+  for (auto& kv : layer_to_element_animations_map_) {
+    ElementAnimations* element_animations = kv.second;
+    ElementAnimations* element_animations_impl =
+        host_impl->GetElementAnimationsForLayerId(kv.first);
+    if (element_animations_impl)
+      element_animations->PushPropertiesTo(element_animations_impl);
+  }
+}
+
+LayerAnimationController* AnimationHost::GetControllerForLayerId(
+    int layer_id) const {
+  const ElementAnimations* element_animations =
+      GetElementAnimationsForLayerId(layer_id);
+  if (!element_animations)
+    return nullptr;
+
+  return element_animations->layer_animation_controller();
+}
+
+ElementAnimations* AnimationHost::GetElementAnimationsForLayerId(
+    int layer_id) const {
+  DCHECK(layer_id);
+  auto iter = layer_to_element_animations_map_.find(layer_id);
+  return iter == layer_to_element_animations_map_.end() ? nullptr
+                                                        : iter->second;
+}
+
+}  // namespace cc
diff --git a/cc/animation/animation_host.h b/cc/animation/animation_host.h
new file mode 100644
index 0000000..5ae46ee
--- /dev/null
+++ b/cc/animation/animation_host.h
@@ -0,0 +1,104 @@
+// 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.
+
+#ifndef CC_ANIMATION_ANIMATION_HOST_H_
+#define CC_ANIMATION_ANIMATION_HOST_H_
+
+#include <vector>
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/trees/mutator_host_client.h"
+
+namespace cc {
+
+class AnimationPlayer;
+class AnimationRegistrar;
+class AnimationTimeline;
+class ElementAnimations;
+class LayerAnimationController;
+class LayerTreeHost;
+
+enum class ThreadInstance { MAIN, IMPL };
+
+typedef std::vector<scoped_refptr<AnimationTimeline>> AnimationTimelineList;
+
+// An AnimationHost contains all the state required to play animations.
+// Specifically, it owns all the AnimationTimelines objects.
+// There is just one AnimationHost for LayerTreeHost on main renderer thread
+// and just one AnimationHost for LayerTreeHostImpl on impl thread.
+// We synchronize them during the commit process in a one-way data flow process
+// (PushPropertiesTo).
+// An AnimationHost talks to its correspondent LayerTreeHost via
+// LayerTreeMutatorsClient interface.
+// AnimationHost has it's own instance of AnimationRegistrar,
+// we want to merge AnimationRegistrar into AnimationHost.
+class CC_EXPORT AnimationHost {
+ public:
+  static scoped_ptr<AnimationHost> Create(ThreadInstance thread_instance);
+  virtual ~AnimationHost();
+
+  void AddAnimationTimeline(scoped_refptr<AnimationTimeline> timeline);
+  void RemoveAnimationTimeline(scoped_refptr<AnimationTimeline> timeline);
+  AnimationTimeline* GetTimelineById(int timeline_id) const;
+
+  void ClearTimelines();
+
+  void RegisterLayer(int layer_id, LayerTreeType tree_type);
+  void UnregisterLayer(int layer_id, LayerTreeType tree_type);
+
+  void RegisterPlayerForLayer(int layer_id, AnimationPlayer* player);
+  void UnregisterPlayerForLayer(int layer_id, AnimationPlayer* player);
+
+  ElementAnimations* GetElementAnimationsForLayerId(int layer_id) const;
+
+  // TODO(loyso): Get rid of LayerAnimationController.
+  LayerAnimationController* GetControllerForLayerId(int layer_id) const;
+
+  // Parent LayerTreeHost or LayerTreeHostImpl.
+  MutatorHostClient* mutator_host_client() { return mutator_host_client_; }
+  const MutatorHostClient* mutator_host_client() const {
+    return mutator_host_client_;
+  }
+  void SetMutatorHostClient(MutatorHostClient* client);
+
+  void SetNeedsCommit();
+
+  void PushPropertiesTo(AnimationHost* host_impl);
+
+  AnimationRegistrar* animation_registrar() const {
+    return animation_registrar_.get();
+  }
+
+ private:
+  explicit AnimationHost(ThreadInstance thread_instance);
+
+  void PushTimelinesToImplThread(AnimationHost* host_impl) const;
+  void RemoveTimelinesFromImplThread(AnimationHost* host_impl) const;
+  void PushPropertiesToImplThread(AnimationHost* host_impl);
+
+  void EraseTimelines(AnimationTimelineList::iterator begin,
+                      AnimationTimelineList::iterator end);
+
+  // TODO(loyso): For now AnimationPlayers share LayerAnimationController object
+  // if they are attached to the same element(layer). Note that Element can
+  // contain many Layers.
+  typedef base::ScopedPtrHashMap<int, scoped_ptr<ElementAnimations>>
+      LayerToElementAnimationsMap;
+  LayerToElementAnimationsMap layer_to_element_animations_map_;
+
+  AnimationTimelineList timelines_;
+  scoped_ptr<AnimationRegistrar> animation_registrar_;
+  MutatorHostClient* mutator_host_client_;
+
+  const ThreadInstance thread_instance_;
+
+  DISALLOW_COPY_AND_ASSIGN(AnimationHost);
+};
+
+}  // namespace cc
+
+#endif  // CC_ANIMATION_ANIMATION_HOST_H_
diff --git a/cc/animation/animation_host_unittest.cc b/cc/animation/animation_host_unittest.cc
new file mode 100644
index 0000000..18d6c9f6
--- /dev/null
+++ b/cc/animation/animation_host_unittest.cc
@@ -0,0 +1,75 @@
+// 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_host.h"
+
+#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/animation_timeline.h"
+#include "cc/test/animation_test_common.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+// See AnimationPlayer tests on layer registration/unregistration in
+// animation_player_unittest.cc.
+
+TEST(AnimationHostTest, SyncTimelinesAddRemove) {
+  scoped_ptr<AnimationHost> host(AnimationHost::Create(ThreadInstance::MAIN));
+  scoped_ptr<AnimationHost> host_impl(
+      AnimationHost::Create(ThreadInstance::IMPL));
+
+  const int timeline_id = AnimationIdProvider::NextTimelineId();
+  scoped_refptr<AnimationTimeline> timeline(
+      AnimationTimeline::Create(timeline_id));
+  host->AddAnimationTimeline(timeline.get());
+  EXPECT_TRUE(timeline->animation_host());
+
+  EXPECT_FALSE(host_impl->GetTimelineById(timeline_id));
+
+  host->PushPropertiesTo(host_impl.get());
+
+  scoped_refptr<AnimationTimeline> timeline_impl =
+      host_impl->GetTimelineById(timeline_id);
+  EXPECT_TRUE(timeline_impl);
+  EXPECT_EQ(timeline_impl->id(), timeline_id);
+
+  host->PushPropertiesTo(host_impl.get());
+  EXPECT_EQ(timeline_impl, host_impl->GetTimelineById(timeline_id));
+
+  host->RemoveAnimationTimeline(timeline.get());
+  EXPECT_FALSE(timeline->animation_host());
+
+  host->PushPropertiesTo(host_impl.get());
+  EXPECT_FALSE(host_impl->GetTimelineById(timeline_id));
+
+  EXPECT_FALSE(timeline_impl->animation_host());
+}
+
+TEST(AnimationHostTest, ImplOnlyTimeline) {
+  scoped_ptr<AnimationHost> host(AnimationHost::Create(ThreadInstance::MAIN));
+  scoped_ptr<AnimationHost> host_impl(
+      AnimationHost::Create(ThreadInstance::IMPL));
+
+  const int timeline_id1 = AnimationIdProvider::NextTimelineId();
+  const int timeline_id2 = AnimationIdProvider::NextTimelineId();
+
+  scoped_refptr<AnimationTimeline> timeline(
+      AnimationTimeline::Create(timeline_id1));
+  scoped_refptr<AnimationTimeline> timeline_impl(
+      AnimationTimeline::Create(timeline_id2));
+  timeline_impl->set_is_impl_only(true);
+
+  host->AddAnimationTimeline(timeline.get());
+  host_impl->AddAnimationTimeline(timeline_impl.get());
+
+  host->PushPropertiesTo(host_impl.get());
+
+  EXPECT_TRUE(host->GetTimelineById(timeline_id1));
+  EXPECT_TRUE(host_impl->GetTimelineById(timeline_id2));
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/animation/animation_id_provider.cc b/cc/animation/animation_id_provider.cc
index caefdb8..9c87d20 100644
--- a/cc/animation/animation_id_provider.cc
+++ b/cc/animation/animation_id_provider.cc
@@ -9,6 +9,8 @@
 
 base::StaticAtomicSequenceNumber g_next_animation_id;
 base::StaticAtomicSequenceNumber g_next_group_id;
+base::StaticAtomicSequenceNumber g_next_timeline_id;
+base::StaticAtomicSequenceNumber g_next_player_id;
 
 int AnimationIdProvider::NextAnimationId() {
   // Animation IDs start from 1.
@@ -20,4 +22,12 @@
   return g_next_group_id.GetNext() + 1;
 }
 
+int AnimationIdProvider::NextTimelineId() {
+  return g_next_timeline_id.GetNext() + 1;
+}
+
+int AnimationIdProvider::NextPlayerId() {
+  return g_next_player_id.GetNext() + 1;
+}
+
 }  // namespace cc
diff --git a/cc/animation/animation_id_provider.h b/cc/animation/animation_id_provider.h
index b4038413..3b14801 100644
--- a/cc/animation/animation_id_provider.h
+++ b/cc/animation/animation_id_provider.h
@@ -15,6 +15,8 @@
   // These functions each return monotonically increasing values.
   static int NextAnimationId();
   static int NextGroupId();
+  static int NextTimelineId();
+  static int NextPlayerId();
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(AnimationIdProvider);
diff --git a/cc/animation/animation_player.cc b/cc/animation/animation_player.cc
new file mode 100644
index 0000000..603ffa9
--- /dev/null
+++ b/cc/animation/animation_player.cc
@@ -0,0 +1,185 @@
+// 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_player.h"
+
+#include "cc/animation/animation_delegate.h"
+#include "cc/animation/animation_host.h"
+#include "cc/animation/animation_timeline.h"
+#include "cc/animation/element_animations.h"
+#include "cc/animation/layer_animation_controller.h"
+
+namespace cc {
+
+scoped_refptr<AnimationPlayer> AnimationPlayer::Create(int id) {
+  return make_scoped_refptr(new AnimationPlayer(id));
+}
+
+AnimationPlayer::AnimationPlayer(int id)
+    : animation_host_(),
+      animation_timeline_(),
+      element_animations_(),
+      layer_animation_delegate_(),
+      id_(id),
+      layer_id_(0) {
+  DCHECK(id_);
+}
+
+AnimationPlayer::~AnimationPlayer() {
+  DCHECK(!animation_timeline_);
+  DCHECK(!element_animations_);
+  DCHECK(!layer_id_);
+}
+
+scoped_refptr<AnimationPlayer> AnimationPlayer::CreateImplInstance() const {
+  scoped_refptr<AnimationPlayer> player = AnimationPlayer::Create(id());
+  return player;
+}
+
+void AnimationPlayer::SetAnimationHost(AnimationHost* animation_host) {
+  animation_host_ = animation_host;
+}
+
+void AnimationPlayer::SetAnimationTimeline(AnimationTimeline* timeline) {
+  if (animation_timeline_ == timeline)
+    return;
+
+  // We need to unregister player to manage ElementAnimations and observers
+  // properly.
+  if (layer_id_ && element_animations_)
+    UnregisterPlayer();
+
+  animation_timeline_ = timeline;
+
+  // Register player only if layer AND host attached.
+  if (layer_id_ && animation_host_)
+    RegisterPlayer();
+}
+
+void AnimationPlayer::AttachLayer(int layer_id) {
+  DCHECK_EQ(layer_id_, 0);
+  DCHECK(layer_id);
+
+  layer_id_ = layer_id;
+
+  // Register player only if layer AND host attached.
+  if (animation_host_)
+    RegisterPlayer();
+}
+
+void AnimationPlayer::DetachLayer() {
+  DCHECK(layer_id_);
+
+  if (animation_host_)
+    UnregisterPlayer();
+
+  layer_id_ = 0;
+}
+
+void AnimationPlayer::RegisterPlayer() {
+  DCHECK(layer_id_);
+  DCHECK(animation_host_);
+  DCHECK(!element_animations_);
+
+  // Create LAC or re-use existing.
+  animation_host_->RegisterPlayerForLayer(layer_id_, this);
+  // Get local reference to shared LAC.
+  BindElementAnimations();
+}
+
+void AnimationPlayer::UnregisterPlayer() {
+  DCHECK(layer_id_);
+  DCHECK(animation_host_);
+  DCHECK(element_animations_);
+
+  UnbindElementAnimations();
+  // Destroy LAC or release it if it's still needed.
+  animation_host_->UnregisterPlayerForLayer(layer_id_, this);
+}
+
+void AnimationPlayer::BindElementAnimations() {
+  DCHECK(!element_animations_);
+  element_animations_ =
+      animation_host_->GetElementAnimationsForLayerId(layer_id_);
+  DCHECK(element_animations_);
+
+  // Pass all accumulated animations to LAC.
+  for (auto it = animations_.begin(); it != animations_.end(); ++it)
+    element_animations_->layer_animation_controller()->AddAnimation(
+        animations_.take(it));
+  if (!animations_.empty())
+    SetNeedsCommit();
+  animations_.clear();
+}
+
+void AnimationPlayer::UnbindElementAnimations() {
+  element_animations_ = nullptr;
+  DCHECK(animations_.empty());
+}
+
+void AnimationPlayer::AddAnimation(scoped_ptr<Animation> animation) {
+  if (element_animations_) {
+    element_animations_->layer_animation_controller()->AddAnimation(
+        animation.Pass());
+    SetNeedsCommit();
+  } else {
+    animations_.push_back(animation.Pass());
+  }
+}
+
+void AnimationPlayer::PauseAnimation(int animation_id, double time_offset) {
+  DCHECK(element_animations_);
+  element_animations_->layer_animation_controller()->PauseAnimation(
+      animation_id, base::TimeDelta::FromSecondsD(time_offset));
+  SetNeedsCommit();
+}
+
+void AnimationPlayer::RemoveAnimation(int animation_id) {
+  if (element_animations_) {
+    element_animations_->layer_animation_controller()->RemoveAnimation(
+        animation_id);
+    SetNeedsCommit();
+  } else {
+    auto animations_to_remove = animations_.remove_if([animation_id](
+        Animation* animation) { return animation->id() == animation_id; });
+    animations_.erase(animations_to_remove, animations_.end());
+  }
+}
+
+void AnimationPlayer::PushPropertiesTo(AnimationPlayer* player_impl) {
+  if (!element_animations_) {
+    if (player_impl->element_animations())
+      player_impl->DetachLayer();
+    return;
+  }
+
+  DCHECK(layer_id_);
+  if (!player_impl->element_animations())
+    player_impl->AttachLayer(layer_id_);
+}
+
+void AnimationPlayer::NotifyAnimationStarted(
+    base::TimeTicks monotonic_time,
+    Animation::TargetProperty target_property,
+    int group) {
+  if (layer_animation_delegate_)
+    layer_animation_delegate_->NotifyAnimationStarted(monotonic_time,
+                                                      target_property, group);
+}
+
+void AnimationPlayer::NotifyAnimationFinished(
+    base::TimeTicks monotonic_time,
+    Animation::TargetProperty target_property,
+    int group) {
+  if (layer_animation_delegate_)
+    layer_animation_delegate_->NotifyAnimationFinished(monotonic_time,
+                                                       target_property, group);
+}
+
+void AnimationPlayer::SetNeedsCommit() {
+  DCHECK(animation_host_);
+  animation_host_->SetNeedsCommit();
+}
+
+}  // namespace cc
diff --git a/cc/animation/animation_player.h b/cc/animation/animation_player.h
new file mode 100644
index 0000000..afb819d
--- /dev/null
+++ b/cc/animation/animation_player.h
@@ -0,0 +1,114 @@
+// 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.
+
+#ifndef CC_ANIMATION_ANIMATION_PLAYER_H_
+#define CC_ANIMATION_ANIMATION_PLAYER_H_
+
+#include "base/containers/linked_list.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "cc/animation/animation.h"
+#include "cc/base/cc_export.h"
+#include "cc/base/scoped_ptr_vector.h"
+
+namespace cc {
+
+class AnimationDelegate;
+class AnimationHost;
+class AnimationTimeline;
+class ElementAnimations;
+class LayerAnimationController;
+enum class LayerTreeType;
+
+// An AnimationPlayer owns all animations to be run on particular CC Layer.
+// Multiple AnimationPlayers can be attached to one layer. In this case,
+// they share common LayerAnimationController (temp solution) so the
+// LayerAnimationController-to-Layer relationship stays the same (1:1, LACs
+// have same IDs as their respective Layers).
+// For now, the blink logic is responsible for handling of conflicting
+// same-property animations.
+// Each AnimationPlayer has its copy on the impl thread.
+// This is a CC counterpart for blink::AnimationPlayer (in 1:1 relationship).
+class CC_EXPORT AnimationPlayer : public base::RefCounted<AnimationPlayer>,
+                                  public base::LinkNode<AnimationPlayer> {
+ public:
+  static scoped_refptr<AnimationPlayer> Create(int id);
+  scoped_refptr<AnimationPlayer> CreateImplInstance() const;
+
+  int id() const { return id_; }
+  int layer_id() const { return layer_id_; }
+
+  // Parent AnimationHost. AnimationPlayer can be detached from
+  // AnimationTimeline.
+  AnimationHost* animation_host() { return animation_host_; }
+  const AnimationHost* animation_host() const { return animation_host_; }
+  void SetAnimationHost(AnimationHost* animation_host);
+
+  // Parent AnimationTimeline.
+  AnimationTimeline* animation_timeline() { return animation_timeline_; }
+  const AnimationTimeline* animation_timeline() const {
+    return animation_timeline_;
+  }
+  void SetAnimationTimeline(AnimationTimeline* timeline);
+
+  // ElementAnimations object where this player is listed.
+  // ElementAnimations has a reference to shared LayerAnimationController.
+  ElementAnimations* element_animations() const { return element_animations_; }
+
+  void set_layer_animation_delegate(AnimationDelegate* delegate) {
+    layer_animation_delegate_ = delegate;
+  }
+
+  void AttachLayer(int layer_id);
+  void DetachLayer();
+
+  void AddAnimation(scoped_ptr<Animation> animation);
+  void PauseAnimation(int animation_id, double time_offset);
+  void RemoveAnimation(int animation_id);
+
+  void PushPropertiesTo(AnimationPlayer* player_impl);
+
+  // AnimationDelegate routing.
+  void NotifyAnimationStarted(base::TimeTicks monotonic_time,
+                              Animation::TargetProperty target_property,
+                              int group);
+  void NotifyAnimationFinished(base::TimeTicks monotonic_time,
+                               Animation::TargetProperty target_property,
+                               int group);
+
+ private:
+  friend class base::RefCounted<AnimationPlayer>;
+
+  explicit AnimationPlayer(int id);
+  ~AnimationPlayer();
+
+  void SetNeedsCommit();
+
+  void RegisterPlayer();
+  void UnregisterPlayer();
+
+  void BindElementAnimations();
+  void UnbindElementAnimations();
+
+  // We accumulate added animations in animations_ container
+  // if element_animations_ is a nullptr. It allows us to add/remove animations
+  // to non-attached AnimationPlayers.
+  typedef ScopedPtrVector<Animation> AnimationList;
+  AnimationList animations_;
+
+  AnimationHost* animation_host_;
+  AnimationTimeline* animation_timeline_;
+  // element_animations isn't null if player attached to an element (layer).
+  ElementAnimations* element_animations_;
+  AnimationDelegate* layer_animation_delegate_;
+
+  int id_;
+  int layer_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(AnimationPlayer);
+};
+
+}  // namespace cc
+
+#endif  // CC_ANIMATION_ANIMATION_PLAYER_H_
diff --git a/cc/animation/animation_player_unittest.cc b/cc/animation/animation_player_unittest.cc
new file mode 100644
index 0000000..466964f
--- /dev/null
+++ b/cc/animation/animation_player_unittest.cc
@@ -0,0 +1,336 @@
+// 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_player.h"
+
+#include "cc/animation/animation_delegate.h"
+#include "cc/animation/animation_host.h"
+#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/animation_registrar.h"
+#include "cc/animation/animation_timeline.h"
+#include "cc/animation/element_animations.h"
+#include "cc/test/animation_test_common.h"
+#include "cc/test/animation_timelines_test_common.h"
+
+namespace cc {
+namespace {
+
+class AnimationPlayerTest : public AnimationTimelinesTest {
+ public:
+  AnimationPlayerTest() {}
+  ~AnimationPlayerTest() override {}
+};
+
+// See element_animations_unittest.cc for active/pending observers tests.
+
+TEST_F(AnimationPlayerTest, AttachDetachLayerIfTimelineAttached) {
+  host_->AddAnimationTimeline(timeline_);
+  timeline_->AttachPlayer(player_);
+  EXPECT_FALSE(player_->element_animations());
+  EXPECT_FALSE(player_->layer_id());
+
+  host_->PushPropertiesTo(host_impl_);
+
+  EXPECT_FALSE(GetImplPlayerForLayerId(layer_id_));
+
+  GetImplTimelineAndPlayerByID();
+
+  EXPECT_FALSE(player_impl_->element_animations());
+  EXPECT_FALSE(player_impl_->layer_id());
+
+  player_->AttachLayer(layer_id_);
+  EXPECT_EQ(player_, GetPlayerForLayerId(layer_id_));
+  EXPECT_TRUE(player_->element_animations());
+  EXPECT_EQ(player_->layer_id(), layer_id_);
+
+  host_->PushPropertiesTo(host_impl_);
+
+  EXPECT_EQ(player_impl_, GetImplPlayerForLayerId(layer_id_));
+  EXPECT_TRUE(player_impl_->element_animations());
+  EXPECT_EQ(player_impl_->layer_id(), layer_id_);
+
+  player_->DetachLayer();
+  EXPECT_FALSE(GetPlayerForLayerId(layer_id_));
+  EXPECT_FALSE(player_->element_animations());
+  EXPECT_FALSE(player_->layer_id());
+
+  host_->PushPropertiesTo(host_impl_);
+
+  EXPECT_FALSE(GetImplPlayerForLayerId(layer_id_));
+  EXPECT_FALSE(player_impl_->element_animations());
+  EXPECT_FALSE(player_impl_->layer_id());
+
+  timeline_->DetachPlayer(player_);
+  EXPECT_FALSE(player_->animation_timeline());
+  EXPECT_FALSE(player_->element_animations());
+  EXPECT_FALSE(player_->layer_id());
+}
+
+TEST_F(AnimationPlayerTest, AttachDetachTimelineIfLayerAttached) {
+  host_->AddAnimationTimeline(timeline_);
+
+  EXPECT_FALSE(player_->element_animations());
+  EXPECT_FALSE(player_->layer_id());
+
+  player_->AttachLayer(layer_id_);
+  EXPECT_FALSE(player_->animation_timeline());
+  EXPECT_FALSE(GetPlayerForLayerId(layer_id_));
+  EXPECT_FALSE(player_->element_animations());
+  EXPECT_EQ(player_->layer_id(), layer_id_);
+
+  timeline_->AttachPlayer(player_);
+  EXPECT_EQ(timeline_, player_->animation_timeline());
+  EXPECT_EQ(player_, GetPlayerForLayerId(layer_id_));
+  EXPECT_TRUE(player_->element_animations());
+  EXPECT_EQ(player_->layer_id(), layer_id_);
+
+  // Removing player from timeline detaches layer.
+  timeline_->DetachPlayer(player_);
+  EXPECT_FALSE(player_->animation_timeline());
+  EXPECT_FALSE(GetPlayerForLayerId(layer_id_));
+  EXPECT_FALSE(player_->element_animations());
+  EXPECT_FALSE(player_->layer_id());
+}
+
+TEST_F(AnimationPlayerTest, PropertiesMutate) {
+  client_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+  client_impl_.RegisterLayer(layer_id_, LayerTreeType::PENDING);
+  client_impl_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+
+  host_->AddAnimationTimeline(timeline_);
+  timeline_->AttachPlayer(player_);
+  player_->AttachLayer(layer_id_);
+
+  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 double duration = 1.;
+
+  AddOpacityTransitionToPlayer(player_.get(), duration, start_opacity,
+                               end_opacity, false);
+  AddAnimatedTransformToPlayer(player_.get(), duration, transform_x,
+                               transform_y);
+  AddAnimatedFilterToPlayer(player_.get(), duration, start_brightness,
+                            end_brightness);
+
+  host_->PushPropertiesTo(host_impl_);
+
+  EXPECT_FALSE(client_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                         Animation::OPACITY));
+  EXPECT_FALSE(client_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                         Animation::TRANSFORM));
+  EXPECT_FALSE(client_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                         Animation::FILTER));
+
+  EXPECT_FALSE(client_impl_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                              Animation::OPACITY));
+  EXPECT_FALSE(client_impl_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                              Animation::TRANSFORM));
+  EXPECT_FALSE(client_impl_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                              Animation::FILTER));
+
+  host_impl_->animation_registrar()->ActivateAnimations();
+
+  base::TimeTicks time;
+  time += base::TimeDelta::FromSecondsD(0.1);
+  AnimateLayersTransferEvents(time, 3u);
+
+  time += base::TimeDelta::FromSecondsD(duration);
+  AnimateLayersTransferEvents(time, 3u);
+
+  client_.ExpectOpacityPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                       end_opacity);
+  client_.ExpectTransformPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                         transform_x, transform_y);
+  client_.ExpectFilterPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                      end_brightness);
+
+  client_impl_.ExpectOpacityPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                            end_opacity);
+  client_impl_.ExpectTransformPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                              transform_x, transform_y);
+  client_impl_.ExpectFilterPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                           end_brightness);
+
+  client_impl_.ExpectOpacityPropertyMutated(layer_id_, LayerTreeType::PENDING,
+                                            end_opacity);
+  client_impl_.ExpectTransformPropertyMutated(layer_id_, LayerTreeType::PENDING,
+                                              transform_x, transform_y);
+  client_impl_.ExpectFilterPropertyMutated(layer_id_, LayerTreeType::PENDING,
+                                           end_brightness);
+}
+
+TEST_F(AnimationPlayerTest, AttachTwoPlayersToOneLayer) {
+  TestAnimationDelegate delegate1;
+  TestAnimationDelegate delegate2;
+
+  client_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+  client_impl_.RegisterLayer(layer_id_, LayerTreeType::PENDING);
+  client_impl_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+
+  scoped_refptr<AnimationPlayer> player1 =
+      AnimationPlayer::Create(AnimationIdProvider::NextPlayerId());
+  scoped_refptr<AnimationPlayer> player2 =
+      AnimationPlayer::Create(AnimationIdProvider::NextPlayerId());
+
+  host_->AddAnimationTimeline(timeline_);
+  timeline_->AttachPlayer(player1);
+  timeline_->AttachPlayer(player2);
+
+  player1->set_layer_animation_delegate(&delegate1);
+  player2->set_layer_animation_delegate(&delegate2);
+
+  // Attach players to the same layer.
+  player1->AttachLayer(layer_id_);
+  player2->AttachLayer(layer_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.;
+
+  AddOpacityTransitionToPlayer(player1.get(), duration, start_opacity,
+                               end_opacity, false);
+  AddAnimatedTransformToPlayer(player2.get(), duration, transform_x,
+                               transform_y);
+
+  host_->PushPropertiesTo(host_impl_);
+  host_impl_->animation_registrar()->ActivateAnimations();
+
+  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);
+  AnimateLayersTransferEvents(time, 2u);
+
+  EXPECT_TRUE(delegate1.started_);
+  EXPECT_FALSE(delegate1.finished_);
+
+  EXPECT_TRUE(delegate2.started_);
+  EXPECT_FALSE(delegate2.finished_);
+
+  time += base::TimeDelta::FromSecondsD(duration);
+  AnimateLayersTransferEvents(time, 2u);
+
+  EXPECT_TRUE(delegate1.finished_);
+  EXPECT_TRUE(delegate2.finished_);
+
+  client_.ExpectOpacityPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                       end_opacity);
+  client_.ExpectTransformPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                         transform_x, transform_y);
+
+  client_impl_.ExpectOpacityPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                            end_opacity);
+  client_impl_.ExpectTransformPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                              transform_x, transform_y);
+
+  client_impl_.ExpectOpacityPropertyMutated(layer_id_, LayerTreeType::PENDING,
+                                            end_opacity);
+  client_impl_.ExpectTransformPropertyMutated(layer_id_, LayerTreeType::PENDING,
+                                              transform_x, transform_y);
+}
+
+TEST_F(AnimationPlayerTest, AddRemoveAnimationToNonAttachedPlayer) {
+  client_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+  client_impl_.RegisterLayer(layer_id_, LayerTreeType::PENDING);
+  client_impl_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+
+  const double duration = 1.;
+  const float start_opacity = .7f;
+  const float end_opacity = .3f;
+
+  const int filter_id =
+      AddAnimatedFilterToPlayer(player_.get(), duration, 0.1f, 0.9f);
+  const int opacity_id = AddOpacityTransitionToPlayer(
+      player_.get(), duration, start_opacity, end_opacity, false);
+
+  host_->AddAnimationTimeline(timeline_);
+  timeline_->AttachPlayer(player_);
+
+  EXPECT_FALSE(player_->element_animations());
+  player_->RemoveAnimation(filter_id);
+
+  player_->AttachLayer(layer_id_);
+
+  EXPECT_TRUE(player_->element_animations());
+  EXPECT_FALSE(player_->element_animations()
+                   ->layer_animation_controller()
+                   ->GetAnimationById(filter_id));
+  EXPECT_TRUE(player_->element_animations()
+                  ->layer_animation_controller()
+                  ->GetAnimationById(opacity_id));
+
+  host_->PushPropertiesTo(host_impl_);
+
+  EXPECT_FALSE(client_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                         Animation::OPACITY));
+  EXPECT_FALSE(client_impl_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                              Animation::OPACITY));
+
+  EXPECT_FALSE(client_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                         Animation::FILTER));
+  EXPECT_FALSE(client_impl_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                              Animation::FILTER));
+
+  host_impl_->animation_registrar()->ActivateAnimations();
+
+  base::TimeTicks time;
+  time += base::TimeDelta::FromSecondsD(0.1);
+  AnimateLayersTransferEvents(time, 1u);
+
+  time += base::TimeDelta::FromSecondsD(duration);
+  AnimateLayersTransferEvents(time, 1u);
+
+  client_.ExpectOpacityPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                       end_opacity);
+  client_impl_.ExpectOpacityPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                            end_opacity);
+  client_impl_.ExpectOpacityPropertyMutated(layer_id_, LayerTreeType::PENDING,
+                                            end_opacity);
+
+  EXPECT_FALSE(client_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                         Animation::FILTER));
+  EXPECT_FALSE(client_impl_.IsPropertyMutated(layer_id_, LayerTreeType::ACTIVE,
+                                              Animation::FILTER));
+}
+
+TEST_F(AnimationPlayerTest, AddRemoveAnimationCausesSetNeedsCommit) {
+  client_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+  host_->AddAnimationTimeline(timeline_);
+  timeline_->AttachPlayer(player_);
+  player_->AttachLayer(layer_id_);
+
+  EXPECT_FALSE(client_.mutators_need_commit());
+
+  const int animation_id =
+      AddOpacityTransitionToPlayer(player_.get(), 1., .7f, .3f, false);
+
+  EXPECT_TRUE(client_.mutators_need_commit());
+  client_.set_mutators_need_commit(false);
+
+  player_->PauseAnimation(animation_id, 1.);
+  EXPECT_TRUE(client_.mutators_need_commit());
+  client_.set_mutators_need_commit(false);
+
+  player_->RemoveAnimation(animation_id);
+  EXPECT_TRUE(client_.mutators_need_commit());
+  client_.set_mutators_need_commit(false);
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/animation/animation_timeline.cc b/cc/animation/animation_timeline.cc
new file mode 100644
index 0000000..9d19f8fb
--- /dev/null
+++ b/cc/animation/animation_timeline.cc
@@ -0,0 +1,120 @@
+// 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_timeline.h"
+
+#include <algorithm>
+
+#include "cc/animation/animation_player.h"
+
+namespace cc {
+
+scoped_refptr<AnimationTimeline> AnimationTimeline::Create(int id) {
+  return make_scoped_refptr(new AnimationTimeline(id));
+}
+
+AnimationTimeline::AnimationTimeline(int id)
+    : id_(id), animation_host_(), is_impl_only_(false) {
+}
+
+AnimationTimeline::~AnimationTimeline() {
+  for (auto& player : players_)
+    player->SetAnimationTimeline(nullptr);
+}
+
+scoped_refptr<AnimationTimeline> AnimationTimeline::CreateImplInstance() const {
+  scoped_refptr<AnimationTimeline> timeline = AnimationTimeline::Create(id());
+  return timeline;
+}
+
+void AnimationTimeline::SetAnimationHost(AnimationHost* animation_host) {
+  animation_host_ = animation_host;
+  for (auto& player : players_)
+    player->SetAnimationHost(animation_host);
+}
+
+void AnimationTimeline::AttachPlayer(scoped_refptr<AnimationPlayer> player) {
+  DCHECK(animation_host_);
+  player->SetAnimationHost(animation_host_);
+  player->SetAnimationTimeline(this);
+  players_.push_back(player);
+}
+
+void AnimationTimeline::DetachPlayer(scoped_refptr<AnimationPlayer> player) {
+  for (AnimationPlayerList::iterator iter = players_.begin();
+       iter != players_.end(); ++iter) {
+    if (iter->get() != player)
+      continue;
+
+    ErasePlayers(iter, iter + 1);
+    break;
+  }
+
+  player->SetAnimationHost(nullptr);
+}
+
+AnimationPlayer* AnimationTimeline::GetPlayerById(int player_id) const {
+  for (auto& player : players_)
+    if (player->id() == player_id)
+      return player.get();
+  return nullptr;
+}
+
+void AnimationTimeline::ClearPlayers() {
+  ErasePlayers(players_.begin(), players_.end());
+}
+
+void AnimationTimeline::PushPropertiesTo(AnimationTimeline* timeline_impl) {
+  PushAttachedPlayersToImplThread(timeline_impl);
+  RemoveDetachedPlayersFromImplThread(timeline_impl);
+  PushPropertiesToImplThread(timeline_impl);
+}
+
+void AnimationTimeline::PushAttachedPlayersToImplThread(
+    AnimationTimeline* timeline_impl) const {
+  for (auto& player : players_) {
+    AnimationPlayer* player_impl = timeline_impl->GetPlayerById(player->id());
+    if (player_impl)
+      continue;
+
+    scoped_refptr<AnimationPlayer> to_add = player->CreateImplInstance();
+    timeline_impl->AttachPlayer(to_add.get());
+  }
+}
+
+void AnimationTimeline::RemoveDetachedPlayersFromImplThread(
+    AnimationTimeline* timeline_impl) const {
+  AnimationPlayerList& players_impl = timeline_impl->players_;
+
+  auto to_erase =
+      std::partition(players_impl.begin(), players_impl.end(),
+                     [this](AnimationPlayerList::value_type player_impl) {
+                       return GetPlayerById(player_impl->id());
+                     });
+
+  timeline_impl->ErasePlayers(to_erase, players_impl.end());
+}
+
+void AnimationTimeline::ErasePlayers(AnimationPlayerList::iterator begin,
+                                     AnimationPlayerList::iterator end) {
+  for (auto i = begin; i != end; ++i) {
+    auto& player = *i;
+    if (player->element_animations())
+      player->DetachLayer();
+    player->SetAnimationTimeline(nullptr);
+  }
+
+  players_.erase(begin, end);
+}
+
+void AnimationTimeline::PushPropertiesToImplThread(
+    AnimationTimeline* timeline_impl) {
+  for (auto& player : players_) {
+    AnimationPlayer* player_impl = timeline_impl->GetPlayerById(player->id());
+    if (player_impl)
+      player->PushPropertiesTo(player_impl);
+  }
+}
+
+}  // namespace cc
diff --git a/cc/animation/animation_timeline.h b/cc/animation/animation_timeline.h
new file mode 100644
index 0000000..4cb31f5c
--- /dev/null
+++ b/cc/animation/animation_timeline.h
@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef CC_ANIMATION_ANIMATION_TIMELINE_H_
+#define CC_ANIMATION_ANIMATION_TIMELINE_H_
+
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+class AnimationHost;
+class AnimationPlayer;
+
+typedef std::vector<scoped_refptr<AnimationPlayer>> AnimationPlayerList;
+
+// An AnimationTimeline owns a group of AnimationPlayers.
+// This is a cc counterpart for blink::AnimationTimeline (in 1:1 relationship).
+// Each AnimationTimeline and its AnimationPlayers have their copies on
+// the impl thread. We synchronize main thread and impl thread instances
+// using integer IDs.
+class CC_EXPORT AnimationTimeline : public base::RefCounted<AnimationTimeline> {
+ public:
+  static scoped_refptr<AnimationTimeline> Create(int id);
+  scoped_refptr<AnimationTimeline> CreateImplInstance() const;
+
+  int id() const { return id_; }
+
+  // Parent AnimationHost.
+  AnimationHost* animation_host() { return animation_host_; }
+  const AnimationHost* animation_host() const { return animation_host_; }
+  void SetAnimationHost(AnimationHost* animation_host);
+
+  void set_is_impl_only(bool is_impl_only) { is_impl_only_ = is_impl_only; }
+  bool is_impl_only() const { return is_impl_only_; }
+
+  void AttachPlayer(scoped_refptr<AnimationPlayer> player);
+  void DetachPlayer(scoped_refptr<AnimationPlayer> player);
+
+  void ClearPlayers();
+
+  void PushPropertiesTo(AnimationTimeline* timeline_impl);
+
+  AnimationPlayer* GetPlayerById(int player_id) const;
+
+ private:
+  friend class base::RefCounted<AnimationTimeline>;
+
+  explicit AnimationTimeline(int id);
+  virtual ~AnimationTimeline();
+
+  void PushAttachedPlayersToImplThread(AnimationTimeline* timeline) const;
+  void RemoveDetachedPlayersFromImplThread(AnimationTimeline* timeline) const;
+  void PushPropertiesToImplThread(AnimationTimeline* timeline);
+
+  void ErasePlayers(AnimationPlayerList::iterator begin,
+                    AnimationPlayerList::iterator end);
+
+  AnimationPlayerList players_;
+  int id_;
+  AnimationHost* animation_host_;
+
+  // Impl-only AnimationTimeline has no main thread instance and lives on
+  // it's own.
+  bool is_impl_only_;
+
+  DISALLOW_COPY_AND_ASSIGN(AnimationTimeline);
+};
+
+}  // namespace cc
+
+#endif  // CC_ANIMATION_ANIMATION_TIMELINE_H_
diff --git a/cc/animation/animation_timeline_unittest.cc b/cc/animation/animation_timeline_unittest.cc
new file mode 100644
index 0000000..3b0bc5ab
--- /dev/null
+++ b/cc/animation/animation_timeline_unittest.cc
@@ -0,0 +1,99 @@
+// 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_timeline.h"
+
+#include "cc/animation/animation_host.h"
+#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/animation_player.h"
+#include "cc/test/animation_test_common.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+TEST(AnimationTimelineTest, SyncPlayersAttachDetach) {
+  scoped_ptr<AnimationHost> host(AnimationHost::Create(ThreadInstance::MAIN));
+  scoped_ptr<AnimationHost> host_impl(
+      AnimationHost::Create(ThreadInstance::IMPL));
+
+  const int timeline_id = AnimationIdProvider::NextTimelineId();
+  const int player_id = AnimationIdProvider::NextPlayerId();
+
+  scoped_refptr<AnimationTimeline> timeline_impl(
+      AnimationTimeline::Create(timeline_id));
+  scoped_refptr<AnimationTimeline> timeline(
+      AnimationTimeline::Create(timeline_id));
+
+  host->AddAnimationTimeline(timeline.get());
+  EXPECT_TRUE(timeline->animation_host());
+
+  host_impl->AddAnimationTimeline(timeline_impl.get());
+  EXPECT_TRUE(timeline_impl->animation_host());
+
+  scoped_refptr<AnimationPlayer> player(AnimationPlayer::Create(player_id));
+  timeline->AttachPlayer(player.get());
+  EXPECT_TRUE(player->animation_timeline());
+
+  EXPECT_FALSE(timeline_impl->GetPlayerById(player_id));
+
+  timeline->PushPropertiesTo(timeline_impl.get());
+
+  scoped_refptr<AnimationPlayer> player_impl =
+      timeline_impl->GetPlayerById(player_id);
+  EXPECT_TRUE(player_impl);
+  EXPECT_EQ(player_impl->id(), player_id);
+  EXPECT_TRUE(player_impl->animation_timeline());
+
+  timeline->PushPropertiesTo(timeline_impl.get());
+  EXPECT_EQ(player_impl, timeline_impl->GetPlayerById(player_id));
+
+  timeline->DetachPlayer(player.get());
+  EXPECT_FALSE(player->animation_timeline());
+
+  timeline->PushPropertiesTo(timeline_impl.get());
+  EXPECT_FALSE(timeline_impl->GetPlayerById(player_id));
+
+  EXPECT_FALSE(player_impl->animation_timeline());
+}
+
+TEST(AnimationTimelineTest, ClearPlayers) {
+  scoped_ptr<AnimationHost> host(AnimationHost::Create(ThreadInstance::MAIN));
+  scoped_ptr<AnimationHost> host_impl(
+      AnimationHost::Create(ThreadInstance::IMPL));
+
+  const int timeline_id = AnimationIdProvider::NextTimelineId();
+  const int player_id1 = AnimationIdProvider::NextPlayerId();
+  const int player_id2 = AnimationIdProvider::NextPlayerId();
+
+  scoped_refptr<AnimationTimeline> timeline_impl(
+      AnimationTimeline::Create(timeline_id));
+  scoped_refptr<AnimationTimeline> timeline(
+      AnimationTimeline::Create(timeline_id));
+
+  host->AddAnimationTimeline(timeline.get());
+  host_impl->AddAnimationTimeline(timeline_impl.get());
+
+  scoped_refptr<AnimationPlayer> player1(AnimationPlayer::Create(player_id1));
+  timeline->AttachPlayer(player1.get());
+  scoped_refptr<AnimationPlayer> player2(AnimationPlayer::Create(player_id2));
+  timeline->AttachPlayer(player2.get());
+
+  timeline->PushPropertiesTo(timeline_impl.get());
+
+  EXPECT_TRUE(timeline_impl->GetPlayerById(player_id1));
+  EXPECT_TRUE(timeline_impl->GetPlayerById(player_id2));
+
+  timeline->ClearPlayers();
+  EXPECT_FALSE(timeline->GetPlayerById(player_id1));
+  EXPECT_FALSE(timeline->GetPlayerById(player_id2));
+
+  timeline_impl->ClearPlayers();
+  EXPECT_FALSE(timeline_impl->GetPlayerById(player_id1));
+  EXPECT_FALSE(timeline_impl->GetPlayerById(player_id2));
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/animation/element_animations.cc b/cc/animation/element_animations.cc
new file mode 100644
index 0000000..934aec4
--- /dev/null
+++ b/cc/animation/element_animations.cc
@@ -0,0 +1,243 @@
+// 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/element_animations.h"
+
+#include "cc/animation/animation_host.h"
+#include "cc/animation/animation_player.h"
+#include "cc/animation/animation_registrar.h"
+#include "cc/animation/layer_animation_value_observer.h"
+#include "cc/trees/mutator_host_client.h"
+
+namespace cc {
+
+class ElementAnimations::ValueObserver : public LayerAnimationValueObserver {
+ public:
+  ValueObserver(ElementAnimations* element_animation, LayerTreeType tree_type)
+      : element_animations_(element_animation), tree_type_(tree_type) {
+    DCHECK(element_animations_);
+  }
+
+  // LayerAnimationValueObserver implementation.
+  void OnFilterAnimated(const FilterOperations& filters) override {
+    element_animations_->SetFilterMutated(tree_type_, filters);
+  }
+
+  void OnOpacityAnimated(float opacity) override {
+    element_animations_->SetOpacityMutated(tree_type_, opacity);
+  }
+
+  void OnTransformAnimated(const gfx::Transform& transform) override {
+    element_animations_->SetTransformMutated(tree_type_, transform);
+  }
+
+  void OnScrollOffsetAnimated(const gfx::ScrollOffset& scroll_offset) override {
+    element_animations_->SetScrollOffsetMutated(tree_type_, scroll_offset);
+  }
+
+  void OnAnimationWaitingForDeletion() override {
+    // TODO(loyso): implement it.
+  }
+
+  bool IsActive() const override { return tree_type_ == LayerTreeType::ACTIVE; }
+
+ private:
+  ElementAnimations* element_animations_;
+  const LayerTreeType tree_type_;
+
+  DISALLOW_COPY_AND_ASSIGN(ValueObserver);
+};
+
+scoped_ptr<ElementAnimations> ElementAnimations::Create(AnimationHost* host) {
+  return make_scoped_ptr(new ElementAnimations(host));
+}
+
+ElementAnimations::ElementAnimations(AnimationHost* host)
+    : players_list_(make_scoped_ptr(new PlayersList())), animation_host_(host) {
+  DCHECK(animation_host_);
+}
+
+ElementAnimations::~ElementAnimations() {
+  DCHECK(!layer_animation_controller_);
+}
+
+void ElementAnimations::CreateLayerAnimationController(int layer_id) {
+  DCHECK(layer_id);
+  DCHECK(!layer_animation_controller_);
+  DCHECK(animation_host_);
+
+  AnimationRegistrar* registrar = animation_host_->animation_registrar();
+  DCHECK(registrar);
+
+  layer_animation_controller_ =
+      registrar->GetAnimationControllerForId(layer_id);
+  layer_animation_controller_->SetAnimationRegistrar(registrar);
+  layer_animation_controller_->set_layer_animation_delegate(this);
+  layer_animation_controller_->set_value_provider(this);
+
+  DCHECK(animation_host_->mutator_host_client());
+  if (animation_host_->mutator_host_client()->IsLayerInTree(
+          layer_id, LayerTreeType::ACTIVE))
+    CreateActiveValueObserver();
+  if (animation_host_->mutator_host_client()->IsLayerInTree(
+          layer_id, LayerTreeType::PENDING))
+    CreatePendingValueObserver();
+}
+
+void ElementAnimations::DestroyLayerAnimationController() {
+  DCHECK(animation_host_);
+
+  DestroyPendingValueObserver();
+  DestroyActiveValueObserver();
+
+  if (layer_animation_controller_) {
+    layer_animation_controller_->remove_value_provider(this);
+    layer_animation_controller_->remove_layer_animation_delegate(this);
+    layer_animation_controller_->SetAnimationRegistrar(nullptr);
+    layer_animation_controller_ = nullptr;
+  }
+}
+
+void ElementAnimations::LayerRegistered(int layer_id, LayerTreeType tree_type) {
+  DCHECK(layer_animation_controller_);
+  DCHECK_EQ(layer_animation_controller_->id(), layer_id);
+
+  if (tree_type == LayerTreeType::ACTIVE) {
+    if (!active_value_observer_)
+      CreateActiveValueObserver();
+  } else {
+    if (!pending_value_observer_)
+      CreatePendingValueObserver();
+  }
+}
+
+void ElementAnimations::LayerUnregistered(int layer_id,
+                                          LayerTreeType tree_type) {
+  DCHECK_EQ(this->layer_id(), layer_id);
+  tree_type == LayerTreeType::ACTIVE ? DestroyActiveValueObserver()
+                                     : DestroyPendingValueObserver();
+}
+
+void ElementAnimations::AddPlayer(AnimationPlayer* player) {
+  players_list_->Append(player);
+}
+
+void ElementAnimations::RemovePlayer(AnimationPlayer* player) {
+  for (PlayersListNode* node = players_list_->head();
+       node != players_list_->end(); node = node->next()) {
+    if (node->value() == player) {
+      node->RemoveFromList();
+      return;
+    }
+  }
+}
+
+bool ElementAnimations::IsEmpty() const {
+  return players_list_->empty();
+}
+
+void ElementAnimations::PushPropertiesTo(
+    ElementAnimations* element_animations_impl) {
+  DCHECK(layer_animation_controller_);
+  DCHECK(element_animations_impl->layer_animation_controller());
+
+  layer_animation_controller_->PushAnimationUpdatesTo(
+      element_animations_impl->layer_animation_controller());
+}
+
+void ElementAnimations::SetFilterMutated(LayerTreeType tree_type,
+                                         const FilterOperations& filters) {
+  DCHECK(layer_id());
+  DCHECK(animation_host());
+  DCHECK(animation_host()->mutator_host_client());
+  animation_host()->mutator_host_client()->SetLayerFilterMutated(
+      layer_id(), tree_type, filters);
+}
+
+void ElementAnimations::SetOpacityMutated(LayerTreeType tree_type,
+                                          float opacity) {
+  DCHECK(layer_id());
+  DCHECK(animation_host());
+  DCHECK(animation_host()->mutator_host_client());
+  animation_host()->mutator_host_client()->SetLayerOpacityMutated(
+      layer_id(), tree_type, opacity);
+}
+
+void ElementAnimations::SetTransformMutated(LayerTreeType tree_type,
+                                            const gfx::Transform& transform) {
+  DCHECK(layer_id());
+  DCHECK(animation_host());
+  DCHECK(animation_host()->mutator_host_client());
+  animation_host()->mutator_host_client()->SetLayerTransformMutated(
+      layer_id(), tree_type, transform);
+}
+
+void ElementAnimations::SetScrollOffsetMutated(
+    LayerTreeType tree_type,
+    const gfx::ScrollOffset& scroll_offset) {
+  DCHECK(layer_id());
+  DCHECK(animation_host());
+  DCHECK(animation_host()->mutator_host_client());
+  animation_host()->mutator_host_client()->SetLayerScrollOffsetMutated(
+      layer_id(), tree_type, scroll_offset);
+}
+
+void ElementAnimations::CreateActiveValueObserver() {
+  DCHECK(layer_animation_controller_);
+  DCHECK(!active_value_observer_);
+  active_value_observer_ =
+      make_scoped_ptr(new ValueObserver(this, LayerTreeType::ACTIVE));
+  layer_animation_controller_->AddValueObserver(active_value_observer_.get());
+}
+
+void ElementAnimations::DestroyActiveValueObserver() {
+  if (layer_animation_controller_ && active_value_observer_)
+    layer_animation_controller_->RemoveValueObserver(
+        active_value_observer_.get());
+  active_value_observer_ = nullptr;
+}
+
+void ElementAnimations::CreatePendingValueObserver() {
+  DCHECK(layer_animation_controller_);
+  DCHECK(!pending_value_observer_);
+  pending_value_observer_ =
+      make_scoped_ptr(new ValueObserver(this, LayerTreeType::PENDING));
+  layer_animation_controller_->AddValueObserver(pending_value_observer_.get());
+}
+
+void ElementAnimations::DestroyPendingValueObserver() {
+  if (layer_animation_controller_ && pending_value_observer_)
+    layer_animation_controller_->RemoveValueObserver(
+        pending_value_observer_.get());
+  pending_value_observer_ = nullptr;
+}
+
+void ElementAnimations::NotifyAnimationStarted(
+    base::TimeTicks monotonic_time,
+    Animation::TargetProperty target_property,
+    int group) {
+  for (PlayersListNode* node = players_list_->head();
+       node != players_list_->end(); node = node->next()) {
+    AnimationPlayer* player = node->value();
+    player->NotifyAnimationStarted(monotonic_time, target_property, group);
+  }
+}
+
+void ElementAnimations::NotifyAnimationFinished(
+    base::TimeTicks monotonic_time,
+    Animation::TargetProperty target_property,
+    int group) {
+  for (PlayersListNode* node = players_list_->head();
+       node != players_list_->end(); node = node->next()) {
+    AnimationPlayer* player = node->value();
+    player->NotifyAnimationFinished(monotonic_time, target_property, group);
+  }
+}
+
+gfx::ScrollOffset ElementAnimations::ScrollOffsetForAnimation() const {
+  // TODO(loyso): implement it.
+  return gfx::ScrollOffset();
+}
+
+}  // namespace cc
diff --git a/cc/animation/element_animations.h b/cc/animation/element_animations.h
new file mode 100644
index 0000000..6fb2a42
--- /dev/null
+++ b/cc/animation/element_animations.h
@@ -0,0 +1,119 @@
+// 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.
+
+#ifndef CC_ANIMATION_ELEMENT_ANIMATIONS_H_
+#define CC_ANIMATION_ELEMENT_ANIMATIONS_H_
+
+#include "base/containers/linked_list.h"
+#include "base/memory/ref_counted.h"
+#include "cc/animation/animation_delegate.h"
+#include "cc/animation/layer_animation_controller.h"
+#include "cc/animation/layer_animation_value_provider.h"
+#include "cc/base/cc_export.h"
+
+namespace gfx {
+class ScrollOffset;
+class Transform;
+}
+
+namespace cc {
+
+class AnimationHost;
+class AnimationPlayer;
+class FilterOperations;
+class LayerAnimationController;
+enum class LayerTreeType;
+
+// An ElementAnimations owns a list of all AnimationPlayers, attached to
+// the layer. Also, it owns LayerAnimationController instance (1:1
+// relationship)
+// ElementAnimations object redirects all events from LAC to the list
+// of animation layers.
+// This is a CC counterpart for blink::ElementAnimations (in 1:1 relationship).
+// No pointer to/from respective blink::ElementAnimations object for now.
+class CC_EXPORT ElementAnimations : public AnimationDelegate,
+                                    public LayerAnimationValueProvider {
+ public:
+  static scoped_ptr<ElementAnimations> Create(AnimationHost* host);
+  ~ElementAnimations() override;
+
+  int layer_id() const {
+    return layer_animation_controller_ ? layer_animation_controller_->id() : 0;
+  }
+
+  // Parent AnimationHost.
+  AnimationHost* animation_host() { return animation_host_; }
+  const AnimationHost* animation_host() const { return animation_host_; }
+
+  LayerAnimationController* layer_animation_controller() const {
+    return layer_animation_controller_.get();
+  }
+
+  void CreateLayerAnimationController(int layer_id);
+  void DestroyLayerAnimationController();
+
+  void LayerRegistered(int layer_id, LayerTreeType tree_type);
+  void LayerUnregistered(int layer_id, LayerTreeType tree_type);
+
+  bool has_active_value_observer_for_testing() const {
+    return active_value_observer_;
+  }
+  bool has_pending_value_observer_for_testing() const {
+    return pending_value_observer_;
+  }
+
+  void AddPlayer(AnimationPlayer* player);
+  void RemovePlayer(AnimationPlayer* player);
+  bool IsEmpty() const;
+
+  typedef base::LinkedList<AnimationPlayer> PlayersList;
+  typedef base::LinkNode<AnimationPlayer> PlayersListNode;
+  const PlayersList& players_list() const { return *players_list_.get(); }
+
+  void PushPropertiesTo(ElementAnimations* element_animations_impl);
+
+ private:
+  explicit ElementAnimations(AnimationHost* host);
+
+  void SetFilterMutated(LayerTreeType tree_type,
+                        const FilterOperations& filters);
+  void SetOpacityMutated(LayerTreeType tree_type, float opacity);
+  void SetTransformMutated(LayerTreeType tree_type,
+                           const gfx::Transform& transform);
+  void SetScrollOffsetMutated(LayerTreeType tree_type,
+                              const gfx::ScrollOffset& scroll_offset);
+
+  void CreateActiveValueObserver();
+  void DestroyActiveValueObserver();
+
+  void CreatePendingValueObserver();
+  void DestroyPendingValueObserver();
+
+  // AnimationDelegate implementation
+  void NotifyAnimationStarted(base::TimeTicks monotonic_time,
+                              Animation::TargetProperty target_property,
+                              int group) override;
+  void NotifyAnimationFinished(base::TimeTicks monotonic_time,
+                               Animation::TargetProperty target_property,
+                               int group) override;
+
+  // LayerAnimationValueProvider implementation.
+  gfx::ScrollOffset ScrollOffsetForAnimation() const override;
+
+  scoped_ptr<PlayersList> players_list_;
+
+  class ValueObserver;
+  scoped_ptr<ValueObserver> active_value_observer_;
+  scoped_ptr<ValueObserver> pending_value_observer_;
+
+  // LAC is owned by ElementAnimations (1:1 relationship).
+  scoped_refptr<LayerAnimationController> layer_animation_controller_;
+  AnimationHost* animation_host_;
+
+  DISALLOW_COPY_AND_ASSIGN(ElementAnimations);
+};
+
+}  // namespace cc
+
+#endif  // CC_ANIMATION_ELEMENT_ANIMATIONS_H_
diff --git a/cc/animation/element_animations_unittest.cc b/cc/animation/element_animations_unittest.cc
new file mode 100644
index 0000000..b676906c
--- /dev/null
+++ b/cc/animation/element_animations_unittest.cc
@@ -0,0 +1,218 @@
+// 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/element_animations.h"
+
+#include "cc/animation/animation_delegate.h"
+#include "cc/animation/animation_host.h"
+#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/animation_player.h"
+#include "cc/animation/animation_registrar.h"
+#include "cc/animation/animation_timeline.h"
+#include "cc/test/animation_test_common.h"
+#include "cc/test/animation_timelines_test_common.h"
+
+namespace cc {
+namespace {
+
+class ElementAnimationsTest : public AnimationTimelinesTest {
+ public:
+  ElementAnimationsTest() {}
+  ~ElementAnimationsTest() override {}
+};
+
+// See animation_player_unittest.cc for integration with AnimationPlayer.
+
+TEST_F(ElementAnimationsTest, AttachToLayerInActiveTree) {
+  // Set up the layer which is in active tree for main thread and not
+  // yet passed onto the impl thread.
+  client_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+  client_impl_.RegisterLayer(layer_id_, LayerTreeType::PENDING);
+
+  EXPECT_TRUE(client_.IsLayerInTree(layer_id_, LayerTreeType::ACTIVE));
+  EXPECT_FALSE(client_.IsLayerInTree(layer_id_, LayerTreeType::PENDING));
+
+  host_->AddAnimationTimeline(timeline_);
+
+  timeline_->AttachPlayer(player_);
+  player_->AttachLayer(layer_id_);
+
+  ElementAnimations* element_animations = player_->element_animations();
+  EXPECT_TRUE(element_animations);
+
+  EXPECT_TRUE(element_animations->has_active_value_observer_for_testing());
+  EXPECT_FALSE(element_animations->has_pending_value_observer_for_testing());
+
+  host_->PushPropertiesTo(host_impl_);
+
+  GetImplTimelineAndPlayerByID();
+
+  ElementAnimations* element_animations_impl =
+      player_impl_->element_animations();
+  EXPECT_TRUE(element_animations_impl);
+
+  EXPECT_FALSE(
+      element_animations_impl->has_active_value_observer_for_testing());
+  EXPECT_TRUE(
+      element_animations_impl->has_pending_value_observer_for_testing());
+
+  // Create the layer in the impl active tree.
+  client_impl_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+  EXPECT_TRUE(element_animations_impl->has_active_value_observer_for_testing());
+  EXPECT_TRUE(
+      element_animations_impl->has_pending_value_observer_for_testing());
+
+  EXPECT_TRUE(client_impl_.IsLayerInTree(layer_id_, LayerTreeType::ACTIVE));
+  EXPECT_TRUE(client_impl_.IsLayerInTree(layer_id_, LayerTreeType::PENDING));
+
+  // kill layer on main thread.
+  client_.UnregisterLayer(layer_id_, LayerTreeType::ACTIVE);
+  EXPECT_EQ(element_animations, player_->element_animations());
+  EXPECT_FALSE(element_animations->has_active_value_observer_for_testing());
+  EXPECT_FALSE(element_animations->has_pending_value_observer_for_testing());
+
+  // Sync doesn't detach LayerImpl.
+  host_->PushPropertiesTo(host_impl_);
+  EXPECT_EQ(element_animations_impl, player_impl_->element_animations());
+  EXPECT_TRUE(element_animations_impl->has_active_value_observer_for_testing());
+  EXPECT_TRUE(
+      element_animations_impl->has_pending_value_observer_for_testing());
+
+  // Kill layer on impl thread in pending tree.
+  client_impl_.UnregisterLayer(layer_id_, LayerTreeType::PENDING);
+  EXPECT_EQ(element_animations_impl, player_impl_->element_animations());
+  EXPECT_TRUE(element_animations_impl->has_active_value_observer_for_testing());
+  EXPECT_FALSE(
+      element_animations_impl->has_pending_value_observer_for_testing());
+
+  // Kill layer on impl thread in active tree.
+  client_impl_.UnregisterLayer(layer_id_, LayerTreeType::ACTIVE);
+  EXPECT_EQ(element_animations_impl, player_impl_->element_animations());
+  EXPECT_FALSE(
+      element_animations_impl->has_active_value_observer_for_testing());
+  EXPECT_FALSE(
+      element_animations_impl->has_pending_value_observer_for_testing());
+
+  // Sync doesn't change anything.
+  host_->PushPropertiesTo(host_impl_);
+  EXPECT_EQ(element_animations_impl, player_impl_->element_animations());
+  EXPECT_FALSE(
+      element_animations_impl->has_active_value_observer_for_testing());
+  EXPECT_FALSE(
+      element_animations_impl->has_pending_value_observer_for_testing());
+
+  player_->DetachLayer();
+  EXPECT_FALSE(player_->element_animations());
+
+  // Release ptrs now to test the order of destruction.
+  ReleaseRefPtrs();
+}
+
+TEST_F(ElementAnimationsTest, AttachToNotYetCreatedLayer) {
+  host_->AddAnimationTimeline(timeline_);
+  timeline_->AttachPlayer(player_);
+
+  host_->PushPropertiesTo(host_impl_);
+
+  GetImplTimelineAndPlayerByID();
+
+  player_->AttachLayer(layer_id_);
+
+  ElementAnimations* element_animations = player_->element_animations();
+  EXPECT_TRUE(element_animations);
+
+  EXPECT_FALSE(element_animations->has_active_value_observer_for_testing());
+  EXPECT_FALSE(element_animations->has_pending_value_observer_for_testing());
+
+  host_->PushPropertiesTo(host_impl_);
+
+  ElementAnimations* element_animations_impl =
+      player_impl_->element_animations();
+  EXPECT_TRUE(element_animations_impl);
+
+  EXPECT_FALSE(
+      element_animations_impl->has_active_value_observer_for_testing());
+  EXPECT_FALSE(
+      element_animations_impl->has_pending_value_observer_for_testing());
+
+  // Create layer.
+  client_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+  EXPECT_TRUE(element_animations->has_active_value_observer_for_testing());
+  EXPECT_FALSE(element_animations->has_pending_value_observer_for_testing());
+
+  client_impl_.RegisterLayer(layer_id_, LayerTreeType::PENDING);
+  EXPECT_FALSE(
+      element_animations_impl->has_active_value_observer_for_testing());
+  EXPECT_TRUE(
+      element_animations_impl->has_pending_value_observer_for_testing());
+
+  client_impl_.RegisterLayer(layer_id_, LayerTreeType::ACTIVE);
+  EXPECT_TRUE(element_animations_impl->has_active_value_observer_for_testing());
+  EXPECT_TRUE(
+      element_animations_impl->has_pending_value_observer_for_testing());
+}
+
+TEST_F(ElementAnimationsTest, AddRemovePlayers) {
+  host_->AddAnimationTimeline(timeline_);
+  timeline_->AttachPlayer(player_);
+  player_->AttachLayer(layer_id_);
+
+  ElementAnimations* element_animations = player_->element_animations();
+  EXPECT_TRUE(element_animations);
+
+  scoped_refptr<AnimationPlayer> player1 =
+      AnimationPlayer::Create(AnimationIdProvider::NextPlayerId());
+  scoped_refptr<AnimationPlayer> player2 =
+      AnimationPlayer::Create(AnimationIdProvider::NextPlayerId());
+
+  timeline_->AttachPlayer(player1);
+  timeline_->AttachPlayer(player2);
+
+  // Attach players to the same layer.
+  player1->AttachLayer(layer_id_);
+  player2->AttachLayer(layer_id_);
+
+  EXPECT_EQ(element_animations, player1->element_animations());
+  EXPECT_EQ(element_animations, player2->element_animations());
+
+  host_->PushPropertiesTo(host_impl_);
+  GetImplTimelineAndPlayerByID();
+
+  ElementAnimations* element_animations_impl =
+      player_impl_->element_animations();
+  EXPECT_TRUE(element_animations_impl);
+
+  int list_size_before = 0;
+  for (const ElementAnimations::PlayersListNode* node =
+           element_animations_impl->players_list().head();
+       node != element_animations_impl->players_list().end();
+       node = node->next()) {
+    const AnimationPlayer* player_impl = node->value();
+    EXPECT_TRUE(timeline_->GetPlayerById(player_impl->id()));
+    ++list_size_before;
+  }
+  EXPECT_EQ(3, list_size_before);
+
+  player2->DetachLayer();
+  EXPECT_FALSE(player2->element_animations());
+  EXPECT_EQ(element_animations, player_->element_animations());
+  EXPECT_EQ(element_animations, player1->element_animations());
+
+  host_->PushPropertiesTo(host_impl_);
+  EXPECT_EQ(element_animations_impl, player_impl_->element_animations());
+
+  int list_size_after = 0;
+  for (const ElementAnimations::PlayersListNode* node =
+           element_animations_impl->players_list().head();
+       node != element_animations_impl->players_list().end();
+       node = node->next()) {
+    const AnimationPlayer* player_impl = node->value();
+    EXPECT_TRUE(timeline_->GetPlayerById(player_impl->id()));
+    ++list_size_after;
+  }
+  EXPECT_EQ(2, list_size_after);
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/blink/BUILD.gn b/cc/blink/BUILD.gn
index 4854ead..af5df9e 100644
--- a/cc/blink/BUILD.gn
+++ b/cc/blink/BUILD.gn
@@ -17,6 +17,10 @@
     "web_animation_curve_common.h",
     "web_animation_impl.cc",
     "web_animation_impl.h",
+    "web_compositor_animation_player_impl.cc",
+    "web_compositor_animation_player_impl.h",
+    "web_compositor_animation_timeline_impl.cc",
+    "web_compositor_animation_timeline_impl.h",
     "web_compositor_support_impl.cc",
     "web_compositor_support_impl.h",
     "web_content_layer_impl.cc",
diff --git a/cc/blink/cc_blink.gyp b/cc/blink/cc_blink.gyp
index e9934bf..c55213c 100644
--- a/cc/blink/cc_blink.gyp
+++ b/cc/blink/cc_blink.gyp
@@ -33,6 +33,10 @@
         'web_animation_curve_common.h',
         'web_animation_impl.cc',
         'web_animation_impl.h',
+        'web_compositor_animation_player_impl.cc',
+        'web_compositor_animation_player_impl.h',
+        'web_compositor_animation_timeline_impl.cc',
+        'web_compositor_animation_timeline_impl.h',
         'web_compositor_support_impl.cc',
         'web_compositor_support_impl.h',
         'web_content_layer_impl.cc',
diff --git a/cc/blink/web_compositor_animation_player_impl.cc b/cc/blink/web_compositor_animation_player_impl.cc
new file mode 100644
index 0000000..fd146de8
--- /dev/null
+++ b/cc/blink/web_compositor_animation_player_impl.cc
@@ -0,0 +1,66 @@
+// 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/blink/web_compositor_animation_player_impl.h"
+
+#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/animation_player.h"
+#include "cc/blink/web_animation_impl.h"
+#include "cc/blink/web_to_cc_animation_delegate_adapter.h"
+#include "third_party/WebKit/public/platform/WebLayer.h"
+
+using cc::AnimationPlayer;
+
+namespace cc_blink {
+
+WebCompositorAnimationPlayerImpl::WebCompositorAnimationPlayerImpl()
+    : animation_player_(
+          AnimationPlayer::Create(cc::AnimationIdProvider::NextPlayerId())) {
+}
+
+WebCompositorAnimationPlayerImpl::~WebCompositorAnimationPlayerImpl() {
+}
+
+CC_BLINK_EXPORT cc::AnimationPlayer*
+WebCompositorAnimationPlayerImpl::animation_player() const {
+  return animation_player_.get();
+}
+
+void WebCompositorAnimationPlayerImpl::setAnimationDelegate(
+    blink::WebCompositorAnimationDelegate* delegate) {
+  animation_delegate_adapter_.reset(
+      new WebToCCAnimationDelegateAdapter(delegate));
+  animation_player_->set_layer_animation_delegate(
+      animation_delegate_adapter_.get());
+}
+
+void WebCompositorAnimationPlayerImpl::attachLayer(blink::WebLayer* web_layer) {
+  animation_player_->AttachLayer(web_layer->id());
+}
+
+void WebCompositorAnimationPlayerImpl::detachLayer() {
+  animation_player_->DetachLayer();
+}
+
+bool WebCompositorAnimationPlayerImpl::isLayerAttached() const {
+  return animation_player_->layer_id() != 0;
+}
+
+void WebCompositorAnimationPlayerImpl::addAnimation(
+    blink::WebCompositorAnimation* animation) {
+  animation_player_->AddAnimation(
+      static_cast<WebCompositorAnimationImpl*>(animation)->PassAnimation());
+  delete animation;
+}
+
+void WebCompositorAnimationPlayerImpl::removeAnimation(int animation_id) {
+  animation_player_->RemoveAnimation(animation_id);
+}
+
+void WebCompositorAnimationPlayerImpl::pauseAnimation(int animation_id,
+                                                      double time_offset) {
+  animation_player_->PauseAnimation(animation_id, time_offset);
+}
+
+}  // namespace cc_blink
diff --git a/cc/blink/web_compositor_animation_player_impl.h b/cc/blink/web_compositor_animation_player_impl.h
new file mode 100644
index 0000000..9b5ea44
--- /dev/null
+++ b/cc/blink/web_compositor_animation_player_impl.h
@@ -0,0 +1,48 @@
+// Copyright 2014 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.
+
+#ifndef CC_BLINK_WEB_COMPOSITOR_ANIMATION_PLAYER_IMPL_H_
+#define CC_BLINK_WEB_COMPOSITOR_ANIMATION_PLAYER_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/blink/cc_blink_export.h"
+#include "third_party/WebKit/public/platform/WebCompositorAnimationPlayer.h"
+
+namespace cc {
+class AnimationPlayer;
+}
+
+namespace cc_blink {
+
+class WebToCCAnimationDelegateAdapter;
+
+class WebCompositorAnimationPlayerImpl
+    : public blink::WebCompositorAnimationPlayer {
+ public:
+  CC_BLINK_EXPORT WebCompositorAnimationPlayerImpl();
+  virtual ~WebCompositorAnimationPlayerImpl();
+
+  CC_BLINK_EXPORT cc::AnimationPlayer* animation_player() const;
+
+  // blink::WebCompositorAnimationPlayer implementation
+  virtual void setAnimationDelegate(
+      blink::WebCompositorAnimationDelegate* delegate);
+  virtual void attachLayer(blink::WebLayer* web_layer);
+  virtual void detachLayer();
+  virtual bool isLayerAttached() const;
+  virtual void addAnimation(blink::WebCompositorAnimation* animation);
+  virtual void removeAnimation(int animation_id);
+  virtual void pauseAnimation(int animation_id, double time_offset);
+
+ private:
+  scoped_refptr<cc::AnimationPlayer> animation_player_;
+  scoped_ptr<WebToCCAnimationDelegateAdapter> animation_delegate_adapter_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebCompositorAnimationPlayerImpl);
+};
+
+}  // namespace cc_blink
+
+#endif  // CC_BLINK_WEB_COMPOSITOR_ANIMATION_PLAYER_IMPL_H_
diff --git a/cc/blink/web_compositor_animation_timeline_impl.cc b/cc/blink/web_compositor_animation_timeline_impl.cc
new file mode 100644
index 0000000..a877783
--- /dev/null
+++ b/cc/blink/web_compositor_animation_timeline_impl.cc
@@ -0,0 +1,46 @@
+// 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/blink/web_compositor_animation_timeline_impl.h"
+
+#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/animation_player.h"
+#include "cc/animation/animation_timeline.h"
+#include "cc/blink/web_compositor_animation_player_impl.h"
+#include "third_party/WebKit/public/platform/WebCompositorAnimationPlayerClient.h"
+
+using cc::AnimationTimeline;
+
+namespace cc_blink {
+
+WebCompositorAnimationTimelineImpl::WebCompositorAnimationTimelineImpl()
+    : animation_timeline_(AnimationTimeline::Create(
+          cc::AnimationIdProvider::NextTimelineId())) {
+}
+
+WebCompositorAnimationTimelineImpl::~WebCompositorAnimationTimelineImpl() {
+}
+
+cc::AnimationTimeline* WebCompositorAnimationTimelineImpl::animation_timeline()
+    const {
+  return animation_timeline_.get();
+}
+
+void WebCompositorAnimationTimelineImpl::playerAttached(
+    const blink::WebCompositorAnimationPlayerClient& client) {
+  if (client.compositorPlayer())
+    animation_timeline_->AttachPlayer(
+        static_cast<WebCompositorAnimationPlayerImpl*>(
+            client.compositorPlayer())->animation_player());
+}
+
+void WebCompositorAnimationTimelineImpl::playerDestroyed(
+    const blink::WebCompositorAnimationPlayerClient& client) {
+  if (client.compositorPlayer())
+    animation_timeline_->DetachPlayer(
+        static_cast<WebCompositorAnimationPlayerImpl*>(
+            client.compositorPlayer())->animation_player());
+}
+
+}  // namespace cc_blink
diff --git a/cc/blink/web_compositor_animation_timeline_impl.h b/cc/blink/web_compositor_animation_timeline_impl.h
new file mode 100644
index 0000000..65d05d8
--- /dev/null
+++ b/cc/blink/web_compositor_animation_timeline_impl.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef CC_BLINK_WEB_COMPOSITOR_ANIMATION_TIMELINE_IMPL_H_
+#define CC_BLINK_WEB_COMPOSITOR_ANIMATION_TIMELINE_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/blink/cc_blink_export.h"
+#include "third_party/WebKit/public/platform/WebCompositorAnimationTimeline.h"
+
+namespace blink {
+class WebCompositorAnimationPlayerClient;
+}
+
+namespace cc {
+class AnimationTimeline;
+}
+
+namespace cc_blink {
+
+class WebCompositorAnimationTimelineImpl
+    : public blink::WebCompositorAnimationTimeline {
+ public:
+  CC_BLINK_EXPORT explicit WebCompositorAnimationTimelineImpl();
+  virtual ~WebCompositorAnimationTimelineImpl();
+
+  CC_BLINK_EXPORT cc::AnimationTimeline* animation_timeline() const;
+
+  // blink::WebCompositorAnimationTimeline implementation
+  virtual void playerAttached(
+      const blink::WebCompositorAnimationPlayerClient& client);
+  virtual void playerDestroyed(
+      const blink::WebCompositorAnimationPlayerClient& client);
+
+ private:
+  scoped_refptr<cc::AnimationTimeline> animation_timeline_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebCompositorAnimationTimelineImpl);
+};
+
+}  // namespace cc_blink
+
+#endif  // CC_BLINK_WEB_COMPOSITOR_ANIMATION_TIMELINE_IMPL_H_
diff --git a/cc/blink/web_compositor_support_impl.cc b/cc/blink/web_compositor_support_impl.cc
index 39acd02..13a868d 100644
--- a/cc/blink/web_compositor_support_impl.cc
+++ b/cc/blink/web_compositor_support_impl.cc
@@ -7,6 +7,8 @@
 #include "base/memory/scoped_ptr.h"
 #include "cc/animation/transform_operations.h"
 #include "cc/blink/web_animation_impl.h"
+#include "cc/blink/web_compositor_animation_player_impl.h"
+#include "cc/blink/web_compositor_animation_timeline_impl.h"
 #include "cc/blink/web_content_layer_impl.h"
 #include "cc/blink/web_display_item_list_impl.h"
 #include "cc/blink/web_external_texture_layer_impl.h"
@@ -25,6 +27,8 @@
 
 using blink::WebCompositorAnimation;
 using blink::WebCompositorAnimationCurve;
+using blink::WebCompositorAnimationPlayer;
+using blink::WebCompositorAnimationTimeline;
 using blink::WebContentLayer;
 using blink::WebContentLayerClient;
 using blink::WebDisplayItemList;
@@ -133,4 +137,14 @@
   return new WebDisplayItemListImpl();
 }
 
+WebCompositorAnimationPlayer*
+WebCompositorSupportImpl::createAnimationPlayer() {
+  return new WebCompositorAnimationPlayerImpl();
+}
+
+WebCompositorAnimationTimeline*
+WebCompositorSupportImpl::createAnimationTimeline() {
+  return new WebCompositorAnimationTimelineImpl();
+}
+
 }  // namespace cc_blink
diff --git a/cc/blink/web_compositor_support_impl.h b/cc/blink/web_compositor_support_impl.h
index fa962c3..989966989e 100644
--- a/cc/blink/web_compositor_support_impl.h
+++ b/cc/blink/web_compositor_support_impl.h
@@ -58,6 +58,9 @@
   virtual blink::WebFilterOperations* createFilterOperations();
   virtual blink::WebDisplayItemList* createDisplayItemList();
 
+  virtual blink::WebCompositorAnimationPlayer* createAnimationPlayer();
+  virtual blink::WebCompositorAnimationTimeline* createAnimationTimeline();
+
  private:
   DISALLOW_COPY_AND_ASSIGN(WebCompositorSupportImpl);
 };
diff --git a/cc/cc.gyp b/cc/cc.gyp
index dc86881..d4a2ea0 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -39,10 +39,18 @@
         'animation/animation_delegate.h',
         'animation/animation_events.cc',
         'animation/animation_events.h',
+        'animation/animation_host.cc',
+        'animation/animation_host.h',
         'animation/animation_id_provider.cc',
         'animation/animation_id_provider.h',
+        'animation/animation_player.cc',
+        'animation/animation_player.h',
         'animation/animation_registrar.cc',
         'animation/animation_registrar.h',
+        'animation/animation_timeline.cc',
+        'animation/animation_timeline.h',
+        'animation/element_animations.cc',
+        'animation/element_animations.h',
         'animation/keyframed_animation_curve.cc',
         'animation/keyframed_animation_curve.h',
         'animation/layer_animation_controller.cc',
@@ -529,6 +537,7 @@
         'trees/layer_tree_impl.h',
         'trees/layer_tree_settings.cc',
         'trees/layer_tree_settings.h',
+        'trees/mutator_host_client.h',
         'trees/occlusion.cc',
         'trees/occlusion.h',
         'trees/occlusion_tracker.cc',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index 54671f6..58c71018 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -6,7 +6,11 @@
   'variables': {
     'chromium_code': 1,
     'cc_unit_tests_source_files': [
+      'animation/animation_host_unittest.cc',
+      'animation/animation_player_unittest.cc',
+      'animation/animation_timeline_unittest.cc',
       'animation/animation_unittest.cc',
+      'animation/element_animations_unittest.cc',
       'animation/keyframed_animation_curve_unittest.cc',
       'animation/layer_animation_controller_unittest.cc',
       'animation/scroll_offset_animation_curve_unittest.cc',
@@ -145,6 +149,8 @@
     'cc_tests_support_files': [
       'test/animation_test_common.cc',
       'test/animation_test_common.h',
+      'test/animation_timelines_test_common.cc',
+      'test/animation_timelines_test_common.h',
       'test/begin_frame_args_test.cc',
       'test/begin_frame_args_test.h',
       'test/failure_output_surface.cc',
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 207ab936..6476b28 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -128,11 +128,14 @@
   if (layer_tree_host_ == host)
     return;
 
-  if (layer_tree_host_)
+  if (layer_tree_host_) {
     layer_tree_host_->property_trees()->needs_rebuild = true;
-
-  if (host)
+    layer_tree_host_->UnregisterLayer(this);
+  }
+  if (host) {
     host->property_trees()->needs_rebuild = true;
+    host->RegisterLayer(this);
+  }
 
   InvalidatePropertyTreesIndices();
 
diff --git a/cc/test/animation_test_common.cc b/cc/test/animation_test_common.cc
index ef852797..8a232ba 100644
--- a/cc/test/animation_test_common.cc
+++ b/cc/test/animation_test_common.cc
@@ -5,6 +5,7 @@
 #include "cc/test/animation_test_common.h"
 
 #include "cc/animation/animation_id_provider.h"
+#include "cc/animation/animation_player.h"
 #include "cc/animation/keyframed_animation_curve.h"
 #include "cc/animation/layer_animation_controller.h"
 #include "cc/animation/transform_operations.h"
@@ -86,7 +87,7 @@
                          int delta_y) {
   TransformOperations start_operations;
   if (duration > 0.0) {
-    start_operations.AppendTranslate(delta_x, delta_y, 0.0);
+    start_operations.AppendTranslate(0, 0, 0.0);
   }
 
   TransformOperations operations;
@@ -358,4 +359,27 @@
                            end_brightness);
 }
 
+int AddAnimatedTransformToPlayer(AnimationPlayer* player,
+                                 double duration,
+                                 int delta_x,
+                                 int delta_y) {
+  return AddAnimatedTransform(player, duration, delta_x, delta_y);
+}
+
+int AddOpacityTransitionToPlayer(AnimationPlayer* player,
+                                 double duration,
+                                 float start_opacity,
+                                 float end_opacity,
+                                 bool use_timing_function) {
+  return AddOpacityTransition(player, duration, start_opacity, end_opacity,
+                              use_timing_function);
+}
+
+int AddAnimatedFilterToPlayer(AnimationPlayer* player,
+                              double duration,
+                              float start_brightness,
+                              float end_brightness) {
+  return AddAnimatedFilter(player, duration, start_brightness, end_brightness);
+}
+
 }  // namespace cc
diff --git a/cc/test/animation_test_common.h b/cc/test/animation_test_common.h
index 6f4b6cb..5d0d4b5 100644
--- a/cc/test/animation_test_common.h
+++ b/cc/test/animation_test_common.h
@@ -14,6 +14,7 @@
 #include "cc/test/geometry_test_utils.h"
 
 namespace cc {
+class AnimationPlayer;
 class LayerImpl;
 class Layer;
 }
@@ -179,6 +180,22 @@
                              float start_brightness,
                              float end_brightness);
 
+int AddAnimatedTransformToPlayer(AnimationPlayer* player,
+                                 double duration,
+                                 int delta_x,
+                                 int delta_y);
+
+int AddOpacityTransitionToPlayer(AnimationPlayer* player,
+                                 double duration,
+                                 float start_opacity,
+                                 float end_opacity,
+                                 bool use_timing_function);
+
+int AddAnimatedFilterToPlayer(AnimationPlayer* player,
+                              double duration,
+                              float start_brightness,
+                              float end_brightness);
+
 }  // namespace cc
 
 #endif  // CC_TEST_ANIMATION_TEST_COMMON_H_
diff --git a/cc/test/animation_timelines_test_common.cc b/cc/test/animation_timelines_test_common.cc
new file mode 100644
index 0000000..ea9d0b0
--- /dev/null
+++ b/cc/test/animation_timelines_test_common.cc
@@ -0,0 +1,256 @@
+// 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/test/animation_timelines_test_common.h"
+
+#include "cc/animation/animation_events.h"
+#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/animation_player.h"
+#include "cc/animation/animation_registrar.h"
+#include "cc/animation/animation_timeline.h"
+#include "cc/animation/element_animations.h"
+#include "cc/output/filter_operation.h"
+#include "cc/output/filter_operations.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+
+scoped_ptr<TestLayer> TestLayer::Create() {
+  return make_scoped_ptr(new TestLayer());
+}
+
+TestLayer::TestLayer() {
+  ClearMutatedProperties();
+}
+
+void TestLayer::ClearMutatedProperties() {
+  transform_x_ = 0;
+  transform_y_ = 0;
+
+  opacity_ = 0;
+  brightness_ = 0;
+
+  for (int i = 0; i <= Animation::LAST_TARGET_PROPERTY; ++i)
+    mutated_properties_[i] = false;
+}
+
+TestHostClient::TestHostClient(ThreadInstance thread_instance)
+    : host_(AnimationHost::Create(thread_instance)),
+      mutators_need_commit_(false) {
+  host_->SetMutatorHostClient(this);
+}
+
+TestHostClient::~TestHostClient() {
+  host_->SetMutatorHostClient(nullptr);
+}
+
+void TestHostClient::ClearMutatedProperties() {
+  for (auto& kv : layers_in_pending_tree_)
+    kv.second->ClearMutatedProperties();
+  for (auto& kv : layers_in_active_tree_)
+    kv.second->ClearMutatedProperties();
+}
+
+bool TestHostClient::IsLayerInTree(int layer_id,
+                                   LayerTreeType tree_type) const {
+  return tree_type == LayerTreeType::ACTIVE
+             ? layers_in_active_tree_.count(layer_id)
+             : layers_in_pending_tree_.count(layer_id);
+}
+
+void TestHostClient::SetMutatorsNeedCommit() {
+  mutators_need_commit_ = true;
+}
+
+void TestHostClient::SetLayerFilterMutated(int layer_id,
+                                           LayerTreeType tree_type,
+                                           const FilterOperations& filters) {
+  for (unsigned i = 0; i < filters.size(); ++i) {
+    const FilterOperation& filter = filters.at(i);
+    if (filter.type() == FilterOperation::BRIGHTNESS) {
+      TestLayer* layer = FindTestLayer(layer_id, tree_type);
+      layer->set_brightness(filter.amount());
+    }
+  }
+}
+
+void TestHostClient::SetLayerOpacityMutated(int layer_id,
+                                            LayerTreeType tree_type,
+                                            float opacity) {
+  TestLayer* layer = FindTestLayer(layer_id, tree_type);
+  layer->set_opacity(opacity);
+}
+
+void TestHostClient::SetLayerTransformMutated(int layer_id,
+                                              LayerTreeType tree_type,
+                                              const gfx::Transform& transform) {
+  TestLayer* layer = FindTestLayer(layer_id, tree_type);
+  gfx::Vector2dF vec = transform.To2dTranslation();
+  layer->set_transform(static_cast<int>(vec.x()), static_cast<int>(vec.y()));
+}
+
+void TestHostClient::SetLayerScrollOffsetMutated(
+    int layer_id,
+    LayerTreeType tree_type,
+    const gfx::ScrollOffset& scroll_offset) {
+  TestLayer* layer = FindTestLayer(layer_id, tree_type);
+  layer->set_scroll_offset(scroll_offset);
+}
+
+void TestHostClient::RegisterLayer(int layer_id, LayerTreeType tree_type) {
+  LayerIdToTestLayer& layers_in_tree = tree_type == LayerTreeType::ACTIVE
+                                           ? layers_in_active_tree_
+                                           : layers_in_pending_tree_;
+  DCHECK(layers_in_tree.find(layer_id) == layers_in_tree.end());
+  layers_in_tree.add(layer_id, TestLayer::Create());
+
+  DCHECK(host_);
+  host_->RegisterLayer(layer_id, tree_type);
+}
+
+void TestHostClient::UnregisterLayer(int layer_id, LayerTreeType tree_type) {
+  DCHECK(host_);
+  host_->UnregisterLayer(layer_id, tree_type);
+
+  LayerIdToTestLayer& layers_in_tree = tree_type == LayerTreeType::ACTIVE
+                                           ? layers_in_active_tree_
+                                           : layers_in_pending_tree_;
+  auto kv = layers_in_tree.find(layer_id);
+  DCHECK(kv != layers_in_tree.end());
+  layers_in_tree.erase(kv);
+}
+
+bool TestHostClient::IsPropertyMutated(
+    int layer_id,
+    LayerTreeType tree_type,
+    Animation::TargetProperty property) const {
+  TestLayer* layer = FindTestLayer(layer_id, tree_type);
+  return layer->is_property_mutated(property);
+}
+
+void TestHostClient::ExpectFilterPropertyMutated(int layer_id,
+                                                 LayerTreeType tree_type,
+                                                 float brightness) const {
+  TestLayer* layer = FindTestLayer(layer_id, tree_type);
+  EXPECT_TRUE(layer->is_property_mutated(Animation::OPACITY));
+  EXPECT_EQ(brightness, layer->brightness());
+}
+
+void TestHostClient::ExpectOpacityPropertyMutated(int layer_id,
+                                                  LayerTreeType tree_type,
+                                                  float opacity) const {
+  TestLayer* layer = FindTestLayer(layer_id, tree_type);
+  EXPECT_TRUE(layer->is_property_mutated(Animation::OPACITY));
+  EXPECT_EQ(opacity, layer->opacity());
+}
+
+void TestHostClient::ExpectTransformPropertyMutated(int layer_id,
+                                                    LayerTreeType tree_type,
+                                                    int transform_x,
+                                                    int transform_y) const {
+  TestLayer* layer = FindTestLayer(layer_id, tree_type);
+  EXPECT_TRUE(layer->is_property_mutated(Animation::OPACITY));
+  EXPECT_EQ(transform_x, layer->transform_x());
+  EXPECT_EQ(transform_y, layer->transform_y());
+}
+
+void TestHostClient::ExpectScrollOffsetPropertyMutated(
+    int layer_id,
+    LayerTreeType tree_type,
+    const gfx::ScrollOffset& scroll_offset) const {
+  TestLayer* layer = FindTestLayer(layer_id, tree_type);
+  EXPECT_TRUE(layer->is_property_mutated(Animation::OPACITY));
+  EXPECT_EQ(scroll_offset, layer->scroll_offset());
+}
+
+TestLayer* TestHostClient::FindTestLayer(int layer_id,
+                                         LayerTreeType tree_type) const {
+  const LayerIdToTestLayer& layers_in_tree = tree_type == LayerTreeType::ACTIVE
+                                                 ? layers_in_active_tree_
+                                                 : layers_in_pending_tree_;
+  auto kv = layers_in_tree.find(layer_id);
+  DCHECK(kv != layers_in_tree.end());
+  DCHECK(kv->second);
+  return kv->second;
+}
+
+TestAnimationDelegate::TestAnimationDelegate()
+    : started_(false), finished_(false) {
+}
+
+void TestAnimationDelegate::NotifyAnimationStarted(
+    base::TimeTicks monotonic_time,
+    Animation::TargetProperty target_property,
+    int group) {
+  started_ = true;
+}
+void TestAnimationDelegate::NotifyAnimationFinished(
+    base::TimeTicks monotonic_time,
+    Animation::TargetProperty target_property,
+    int group) {
+  finished_ = true;
+}
+
+AnimationTimelinesTest::AnimationTimelinesTest()
+    : client_(ThreadInstance::MAIN),
+      client_impl_(ThreadInstance::IMPL),
+      timeline_id_(AnimationIdProvider::NextTimelineId()),
+      player_id_(AnimationIdProvider::NextPlayerId()),
+      layer_id_(1) {
+  host_ = client_.host();
+  host_impl_ = client_impl_.host();
+}
+
+AnimationTimelinesTest::~AnimationTimelinesTest() {
+}
+
+void AnimationTimelinesTest::SetUp() {
+  timeline_ = AnimationTimeline::Create(timeline_id_);
+  player_ = AnimationPlayer::Create(player_id_);
+}
+
+void AnimationTimelinesTest::GetImplTimelineAndPlayerByID() {
+  timeline_impl_ = host_impl_->GetTimelineById(timeline_id_);
+  EXPECT_TRUE(timeline_impl_);
+  player_impl_ = timeline_impl_->GetPlayerById(player_id_);
+  EXPECT_TRUE(player_impl_);
+}
+
+void AnimationTimelinesTest::ReleaseRefPtrs() {
+  player_ = nullptr;
+  timeline_ = nullptr;
+  player_impl_ = nullptr;
+  timeline_impl_ = nullptr;
+}
+
+void AnimationTimelinesTest::AnimateLayersTransferEvents(
+    base::TimeTicks time,
+    unsigned expect_events) {
+  scoped_ptr<AnimationEventsVector> events =
+      host_->animation_registrar()->CreateEvents();
+
+  host_impl_->animation_registrar()->AnimateLayers(time);
+  host_impl_->animation_registrar()->UpdateAnimationState(true, events.get());
+  EXPECT_EQ(expect_events, events->size());
+
+  host_->animation_registrar()->AnimateLayers(time);
+  host_->animation_registrar()->UpdateAnimationState(true, nullptr);
+  host_->animation_registrar()->SetAnimationEvents(events.Pass());
+}
+
+AnimationPlayer* AnimationTimelinesTest::GetPlayerForLayerId(int layer_id) {
+  const ElementAnimations* element_animations =
+      host_->GetElementAnimationsForLayerId(layer_id);
+  return element_animations ? element_animations->players_list().head()->value()
+                            : nullptr;
+}
+
+AnimationPlayer* AnimationTimelinesTest::GetImplPlayerForLayerId(int layer_id) {
+  const ElementAnimations* element_animations =
+      host_impl_->GetElementAnimationsForLayerId(layer_id);
+  return element_animations ? element_animations->players_list().head()->value()
+                            : nullptr;
+}
+
+}  // namespace cc
diff --git a/cc/test/animation_timelines_test_common.h b/cc/test/animation_timelines_test_common.h
new file mode 100644
index 0000000..e28926c
--- /dev/null
+++ b/cc/test/animation_timelines_test_common.h
@@ -0,0 +1,190 @@
+// 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.
+
+#ifndef CC_TEST_ANIMATION_TIMELINES_TEST_COMMON_H_
+#define CC_TEST_ANIMATION_TIMELINES_TEST_COMMON_H_
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/animation/animation.h"
+#include "cc/animation/animation_delegate.h"
+#include "cc/animation/animation_host.h"
+#include "cc/trees/mutator_host_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/scroll_offset.h"
+
+namespace cc {
+
+class TestLayer {
+ public:
+  static scoped_ptr<TestLayer> Create();
+
+  void ClearMutatedProperties();
+
+  int transform_x() const { return transform_x_; }
+  int transform_y() const { return transform_y_; }
+
+  void set_transform(int transform_x, int transform_y) {
+    transform_x_ = transform_x;
+    transform_y_ = transform_y;
+    mutated_properties_[Animation::TRANSFORM] = true;
+  }
+
+  float opacity() const { return opacity_; }
+  void set_opacity(float opacity) {
+    opacity_ = opacity;
+    mutated_properties_[Animation::OPACITY] = true;
+  }
+
+  float brightness() const { return brightness_; }
+  void set_brightness(float brightness) {
+    brightness_ = brightness;
+    mutated_properties_[Animation::FILTER] = true;
+  }
+
+  gfx::ScrollOffset scroll_offset() const { return scroll_offset_; }
+  void set_scroll_offset(const gfx::ScrollOffset& scroll_offset) {
+    scroll_offset_ = scroll_offset;
+    mutated_properties_[Animation::SCROLL_OFFSET] = true;
+  }
+
+  bool is_property_mutated(Animation::TargetProperty property) const {
+    return mutated_properties_[property];
+  }
+
+ private:
+  TestLayer();
+
+  int transform_x_;
+  int transform_y_;
+
+  float opacity_;
+  float brightness_;
+  gfx::ScrollOffset scroll_offset_;
+
+  bool mutated_properties_[Animation::LAST_TARGET_PROPERTY + 1];
+};
+
+class TestHostClient : public MutatorHostClient {
+ public:
+  explicit TestHostClient(ThreadInstance thread_instance);
+  ~TestHostClient();
+
+  void ClearMutatedProperties();
+
+  bool IsLayerInTree(int layer_id, LayerTreeType tree_type) const override;
+
+  void SetMutatorsNeedCommit() override;
+
+  void SetLayerFilterMutated(int layer_id,
+                             LayerTreeType tree_type,
+                             const FilterOperations& filters) override;
+
+  void SetLayerOpacityMutated(int layer_id,
+                              LayerTreeType tree_type,
+                              float opacity) override;
+
+  void SetLayerTransformMutated(int layer_id,
+                                LayerTreeType tree_type,
+                                const gfx::Transform& transform) override;
+
+  void SetLayerScrollOffsetMutated(
+      int layer_id,
+      LayerTreeType tree_type,
+      const gfx::ScrollOffset& scroll_offset) override;
+
+  bool mutators_need_commit() const { return mutators_need_commit_; }
+  void set_mutators_need_commit(bool need) { mutators_need_commit_ = need; }
+
+  void RegisterLayer(int layer_id, LayerTreeType tree_type);
+  void UnregisterLayer(int layer_id, LayerTreeType tree_type);
+
+  AnimationHost* host() {
+    DCHECK(host_);
+    return host_.get();
+  }
+
+  bool IsPropertyMutated(int layer_id,
+                         LayerTreeType tree_type,
+                         Animation::TargetProperty property) const;
+
+  void ExpectFilterPropertyMutated(int layer_id,
+                                   LayerTreeType tree_type,
+                                   float brightness) const;
+  void ExpectOpacityPropertyMutated(int layer_id,
+                                    LayerTreeType tree_type,
+                                    float opacity) const;
+  void ExpectTransformPropertyMutated(int layer_id,
+                                      LayerTreeType tree_type,
+                                      int transform_x,
+                                      int transform_y) const;
+  void ExpectScrollOffsetPropertyMutated(
+      int layer_id,
+      LayerTreeType tree_type,
+      const gfx::ScrollOffset& scroll_offset) const;
+
+  TestLayer* FindTestLayer(int layer_id, LayerTreeType tree_type) const;
+
+ private:
+  scoped_ptr<AnimationHost> host_;
+
+  typedef base::ScopedPtrHashMap<int, scoped_ptr<TestLayer>> LayerIdToTestLayer;
+  LayerIdToTestLayer layers_in_active_tree_;
+  LayerIdToTestLayer layers_in_pending_tree_;
+
+  bool mutators_need_commit_;
+};
+
+class TestAnimationDelegate : public AnimationDelegate {
+ public:
+  TestAnimationDelegate();
+
+  void NotifyAnimationStarted(base::TimeTicks monotonic_time,
+                              Animation::TargetProperty target_property,
+                              int group) override;
+  void NotifyAnimationFinished(base::TimeTicks monotonic_time,
+                               Animation::TargetProperty target_property,
+                               int group) override;
+  bool started_;
+  bool finished_;
+};
+
+class AnimationTimelinesTest : public testing::Test {
+ public:
+  AnimationTimelinesTest();
+  ~AnimationTimelinesTest() override;
+
+ protected:
+  void SetUp() override;
+
+  void GetImplTimelineAndPlayerByID();
+
+  void ReleaseRefPtrs();
+
+  void AnimateLayersTransferEvents(base::TimeTicks time,
+                                   unsigned expect_events);
+
+  AnimationPlayer* GetPlayerForLayerId(int layer_id);
+  AnimationPlayer* GetImplPlayerForLayerId(int layer_id);
+
+  TestHostClient client_;
+  TestHostClient client_impl_;
+
+  AnimationHost* host_;
+  AnimationHost* host_impl_;
+
+  const int timeline_id_;
+  const int player_id_;
+  const int layer_id_;
+
+  scoped_refptr<AnimationTimeline> timeline_;
+  scoped_refptr<AnimationPlayer> player_;
+
+  scoped_refptr<AnimationTimeline> timeline_impl_;
+  scoped_refptr<AnimationPlayer> player_impl_;
+};
+
+}  // namespace cc
+
+#endif  // CC_TEST_ANIMATION_TIMELINES_TEST_COMMON_H_
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 6ccbdbbd..79e6b24 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -20,6 +20,7 @@
 #include "base/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_argument.h"
+#include "cc/animation/animation_host.h"
 #include "cc/animation/animation_registrar.h"
 #include "cc/animation/layer_animation_controller.h"
 #include "cc/base/math_util.h"
@@ -124,6 +125,11 @@
     animation_registrar_ = AnimationRegistrar::Create();
   rendering_stats_instrumentation_->set_record_rendering_stats(
       debug_state_.RecordRenderingStats());
+
+  if (settings_.use_compositor_animation_timelines) {
+    animation_host_ = AnimationHost::Create(ThreadInstance::MAIN);
+    animation_host_->SetMutatorHostClient(this);
+  }
 }
 
 void LayerTreeHost::InitializeThreaded(
@@ -165,6 +171,9 @@
 LayerTreeHost::~LayerTreeHost() {
   TRACE_EVENT0("cc", "LayerTreeHost::~LayerTreeHost");
 
+  if (animation_host_)
+    animation_host_->SetMutatorHostClient(nullptr);
+
   if (root_layer_.get())
     root_layer_->SetLayerTreeHost(NULL);
 
@@ -323,6 +332,11 @@
   {
     TRACE_EVENT0("cc", "LayerTreeHost::PushProperties");
     TreeSynchronizer::PushProperties(root_layer(), sync_tree->root_layer());
+
+    if (animation_host_) {
+      DCHECK(host_impl->animation_host());
+      animation_host_->PushPropertiesTo(host_impl->animation_host());
+    }
   }
 
   // This must happen after synchronizing property trees and after push
@@ -1060,4 +1074,64 @@
                                    main_frame_events.Pass());
 }
 
+Layer* LayerTreeHost::LayerById(int id) const {
+  LayerIdMap::const_iterator iter = layer_id_map_.find(id);
+  return iter != layer_id_map_.end() ? iter->second : NULL;
+}
+
+void LayerTreeHost::RegisterLayer(Layer* layer) {
+  DCHECK(!LayerById(layer->id()));
+  layer_id_map_[layer->id()] = layer;
+  if (animation_host_)
+    animation_host_->RegisterLayer(layer->id(), LayerTreeType::ACTIVE);
+}
+
+void LayerTreeHost::UnregisterLayer(Layer* layer) {
+  DCHECK(LayerById(layer->id()));
+  if (animation_host_)
+    animation_host_->UnregisterLayer(layer->id(), LayerTreeType::ACTIVE);
+  layer_id_map_.erase(layer->id());
+}
+
+bool LayerTreeHost::IsLayerInTree(int layer_id, LayerTreeType tree_type) const {
+  return tree_type == LayerTreeType::ACTIVE;
+}
+
+void LayerTreeHost::SetMutatorsNeedCommit() {
+  SetNeedsCommit();
+}
+
+void LayerTreeHost::SetLayerFilterMutated(int layer_id,
+                                          LayerTreeType tree_type,
+                                          const FilterOperations& filters) {
+  LayerAnimationValueObserver* layer = LayerById(layer_id);
+  DCHECK(layer);
+  layer->OnFilterAnimated(filters);
+}
+
+void LayerTreeHost::SetLayerOpacityMutated(int layer_id,
+                                           LayerTreeType tree_type,
+                                           float opacity) {
+  LayerAnimationValueObserver* layer = LayerById(layer_id);
+  DCHECK(layer);
+  layer->OnOpacityAnimated(opacity);
+}
+
+void LayerTreeHost::SetLayerTransformMutated(int layer_id,
+                                             LayerTreeType tree_type,
+                                             const gfx::Transform& transform) {
+  LayerAnimationValueObserver* layer = LayerById(layer_id);
+  DCHECK(layer);
+  layer->OnTransformAnimated(transform);
+}
+
+void LayerTreeHost::SetLayerScrollOffsetMutated(
+    int layer_id,
+    LayerTreeType tree_type,
+    const gfx::ScrollOffset& scroll_offset) {
+  LayerAnimationValueObserver* layer = LayerById(layer_id);
+  DCHECK(layer);
+  layer->OnScrollOffsetAnimated(scroll_offset);
+}
+
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 1c867be..d18d7dab 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -38,6 +38,7 @@
 #include "cc/trees/layer_tree_host_client.h"
 #include "cc/trees/layer_tree_host_common.h"
 #include "cc/trees/layer_tree_settings.h"
+#include "cc/trees/mutator_host_client.h"
 #include "cc/trees/proxy.h"
 #include "cc/trees/swap_promise_monitor.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -49,6 +50,7 @@
 
 namespace cc {
 class AnimationRegistrar;
+class AnimationHost;
 class BeginFrameSource;
 class HeadsUpDisplayLayer;
 class Layer;
@@ -68,7 +70,7 @@
 struct RenderingStats;
 struct ScrollAndScaleSet;
 
-class CC_EXPORT LayerTreeHost {
+class CC_EXPORT LayerTreeHost : public MutatorHostClient {
  public:
   // TODO(sad): InitParams should be a movable type so that it can be
   // std::move()d to the Create* functions.
@@ -258,6 +260,7 @@
   AnimationRegistrar* animation_registrar() const {
     return animation_registrar_.get();
   }
+  AnimationHost* animation_host() const { return animation_host_.get(); }
 
   bool in_paint_layer_contents() const { return in_paint_layer_contents_; }
 
@@ -318,6 +321,26 @@
       scoped_ptr<FrameTimingTracker::CompositeTimingSet> composite_events,
       scoped_ptr<FrameTimingTracker::MainFrameTimingSet> main_frame_events);
 
+  Layer* LayerById(int id) const;
+  void RegisterLayer(Layer* layer);
+  void UnregisterLayer(Layer* layer);
+  // LayerTreeMutatorsClient implementation.
+  bool IsLayerInTree(int layer_id, LayerTreeType tree_type) const override;
+  void SetMutatorsNeedCommit() override;
+  void SetLayerFilterMutated(int layer_id,
+                             LayerTreeType tree_type,
+                             const FilterOperations& filters) override;
+  void SetLayerOpacityMutated(int layer_id,
+                              LayerTreeType tree_type,
+                              float opacity) override;
+  void SetLayerTransformMutated(int layer_id,
+                                LayerTreeType tree_type,
+                                const gfx::Transform& transform) override;
+  void SetLayerScrollOffsetMutated(
+      int layer_id,
+      LayerTreeType tree_type,
+      const gfx::ScrollOffset& scroll_offset) override;
+
  protected:
   explicit LayerTreeHost(InitParams* params);
   void InitializeThreaded(
@@ -420,6 +443,7 @@
   bool has_transparent_background_;
 
   scoped_ptr<AnimationRegistrar> animation_registrar_;
+  scoped_ptr<AnimationHost> animation_host_;
 
   scoped_ptr<PendingPageScaleAnimation> pending_page_scale_animation_;
 
@@ -448,6 +472,9 @@
 
   PropertyTrees property_trees_;
 
+  typedef base::hash_map<int, Layer*> LayerIdMap;
+  LayerIdMap layer_id_map_;
+
   uint32_t surface_id_namespace_;
   uint32_t next_surface_sequence_;
 
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index ddb4dc95..4533bab 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -18,6 +18,7 @@
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event_argument.h"
+#include "cc/animation/animation_host.h"
 #include "cc/animation/animation_id_provider.h"
 #include "cc/animation/scroll_offset_animation_curve.h"
 #include "cc/animation/scrollbar_animation_controller.h"
@@ -239,6 +240,11 @@
       requires_high_res_to_draw_(false),
       is_likely_to_require_a_draw_(false),
       frame_timing_tracker_(FrameTimingTracker::Create(this)) {
+  if (settings.use_compositor_animation_timelines) {
+    animation_host_ = AnimationHost::Create(ThreadInstance::IMPL);
+    animation_host_->SetMutatorHostClient(this);
+  }
+
   DCHECK(proxy_->IsImplThread());
   DCHECK_IMPLIES(settings.use_one_copy, !settings.use_zero_copy);
   DCHECK_IMPLIES(settings.use_zero_copy, !settings.use_one_copy);
@@ -3441,4 +3447,117 @@
 
   return true;
 }
+
+bool LayerTreeHostImpl::IsLayerInTree(int layer_id,
+                                      LayerTreeType tree_type) const {
+  if (tree_type == LayerTreeType::ACTIVE) {
+    return active_tree() ? active_tree()->LayerById(layer_id) != nullptr
+                         : false;
+  } else {
+    if (pending_tree() && pending_tree()->LayerById(layer_id))
+      return true;
+    if (recycle_tree() && recycle_tree()->LayerById(layer_id))
+      return true;
+
+    return false;
+  }
+}
+
+void LayerTreeHostImpl::SetMutatorsNeedCommit() {
+  SetNeedsCommit();
+}
+
+void LayerTreeHostImpl::SetTreeLayerFilterMutated(
+    int layer_id,
+    LayerTreeImpl* tree,
+    const FilterOperations& filters) {
+  if (!tree)
+    return;
+
+  LayerAnimationValueObserver* layer = tree->LayerById(layer_id);
+  if (layer)
+    layer->OnFilterAnimated(filters);
+}
+
+void LayerTreeHostImpl::SetTreeLayerOpacityMutated(int layer_id,
+                                                   LayerTreeImpl* tree,
+                                                   float opacity) {
+  if (!tree)
+    return;
+
+  LayerAnimationValueObserver* layer = tree->LayerById(layer_id);
+  if (layer)
+    layer->OnOpacityAnimated(opacity);
+}
+
+void LayerTreeHostImpl::SetTreeLayerTransformMutated(
+    int layer_id,
+    LayerTreeImpl* tree,
+    const gfx::Transform& transform) {
+  if (!tree)
+    return;
+
+  LayerAnimationValueObserver* layer = tree->LayerById(layer_id);
+  if (layer)
+    layer->OnTransformAnimated(transform);
+}
+
+void LayerTreeHostImpl::SetTreeLayerScrollOffsetMutated(
+    int layer_id,
+    LayerTreeImpl* tree,
+    const gfx::ScrollOffset& scroll_offset) {
+  if (!tree)
+    return;
+
+  LayerAnimationValueObserver* layer = tree->LayerById(layer_id);
+  if (layer)
+    layer->OnScrollOffsetAnimated(scroll_offset);
+}
+
+void LayerTreeHostImpl::SetLayerFilterMutated(int layer_id,
+                                              LayerTreeType tree_type,
+                                              const FilterOperations& filters) {
+  if (tree_type == LayerTreeType::ACTIVE) {
+    SetTreeLayerFilterMutated(layer_id, active_tree(), filters);
+  } else {
+    SetTreeLayerFilterMutated(layer_id, pending_tree(), filters);
+    SetTreeLayerFilterMutated(layer_id, recycle_tree(), filters);
+  }
+}
+
+void LayerTreeHostImpl::SetLayerOpacityMutated(int layer_id,
+                                               LayerTreeType tree_type,
+                                               float opacity) {
+  if (tree_type == LayerTreeType::ACTIVE) {
+    SetTreeLayerOpacityMutated(layer_id, active_tree(), opacity);
+  } else {
+    SetTreeLayerOpacityMutated(layer_id, pending_tree(), opacity);
+    SetTreeLayerOpacityMutated(layer_id, recycle_tree(), opacity);
+  }
+}
+
+void LayerTreeHostImpl::SetLayerTransformMutated(
+    int layer_id,
+    LayerTreeType tree_type,
+    const gfx::Transform& transform) {
+  if (tree_type == LayerTreeType::ACTIVE) {
+    SetTreeLayerTransformMutated(layer_id, active_tree(), transform);
+  } else {
+    SetTreeLayerTransformMutated(layer_id, pending_tree(), transform);
+    SetTreeLayerTransformMutated(layer_id, recycle_tree(), transform);
+  }
+}
+
+void LayerTreeHostImpl::SetLayerScrollOffsetMutated(
+    int layer_id,
+    LayerTreeType tree_type,
+    const gfx::ScrollOffset& scroll_offset) {
+  if (tree_type == LayerTreeType::ACTIVE) {
+    SetTreeLayerScrollOffsetMutated(layer_id, active_tree(), scroll_offset);
+  } else {
+    SetTreeLayerScrollOffsetMutated(layer_id, pending_tree(), scroll_offset);
+    SetTreeLayerScrollOffsetMutated(layer_id, recycle_tree(), scroll_offset);
+  }
+}
+
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index f7a3e99..7f02ee0 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -38,6 +38,7 @@
 #include "cc/scheduler/video_frame_controller.h"
 #include "cc/tiles/tile_manager.h"
 #include "cc/trees/layer_tree_settings.h"
+#include "cc/trees/mutator_host_client.h"
 #include "cc/trees/proxy.h"
 #include "skia/ext/refptr.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -49,6 +50,7 @@
 
 namespace cc {
 
+class AnimationHost;
 class CompletionEvent;
 class CompositorFrameMetadata;
 class DebugRectHistory;
@@ -141,6 +143,7 @@
       public TopControlsManagerClient,
       public ScrollbarAnimationControllerClient,
       public VideoFrameControllerClient,
+      public MutatorHostClient,
       public base::SupportsWeakPtr<LayerTreeHostImpl> {
  public:
   static scoped_ptr<LayerTreeHostImpl> Create(
@@ -229,6 +232,36 @@
   void DidAnimateScrollOffset();
   void SetViewportDamage(const gfx::Rect& damage_rect);
 
+  void SetTreeLayerFilterMutated(int layer_id,
+                                 LayerTreeImpl* tree,
+                                 const FilterOperations& filters);
+  void SetTreeLayerOpacityMutated(int layer_id,
+                                  LayerTreeImpl* tree,
+                                  float opacity);
+  void SetTreeLayerTransformMutated(int layer_id,
+                                    LayerTreeImpl* tree,
+                                    const gfx::Transform& transform);
+  void SetTreeLayerScrollOffsetMutated(int layer_id,
+                                       LayerTreeImpl* tree,
+                                       const gfx::ScrollOffset& scroll_offset);
+
+  // LayerTreeMutatorsClient implementation.
+  bool IsLayerInTree(int layer_id, LayerTreeType tree_type) const override;
+  void SetMutatorsNeedCommit() override;
+  void SetLayerFilterMutated(int layer_id,
+                             LayerTreeType tree_type,
+                             const FilterOperations& filters) override;
+  void SetLayerOpacityMutated(int layer_id,
+                              LayerTreeType tree_type,
+                              float opacity) override;
+  void SetLayerTransformMutated(int layer_id,
+                                LayerTreeType tree_type,
+                                const gfx::Transform& transform) override;
+  void SetLayerScrollOffsetMutated(
+      int layer_id,
+      LayerTreeType tree_type,
+      const gfx::ScrollOffset& scroll_offset) override;
+
   virtual void PrepareTiles();
 
   // Returns DRAW_SUCCESS unless problems occured preparing the frame, and we
@@ -443,6 +476,7 @@
   AnimationRegistrar* animation_registrar() const {
     return animation_registrar_.get();
   }
+  AnimationHost* animation_host() const { return animation_host_.get(); }
 
   void SetDebugState(const LayerTreeDebugState& new_debug_state);
   const LayerTreeDebugState& debug_state() const { return debug_state_; }
@@ -757,6 +791,7 @@
   gfx::Rect viewport_damage_rect_;
 
   scoped_ptr<AnimationRegistrar> animation_registrar_;
+  scoped_ptr<AnimationHost> animation_host_;
   std::set<ScrollbarAnimationController*> scrollbar_animation_controllers_;
   std::set<VideoFrameController*> video_frame_controllers_;
 
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 932e032..3fc5441 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -10,6 +10,7 @@
 
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_argument.h"
+#include "cc/animation/animation_host.h"
 #include "cc/animation/keyframed_animation_curve.h"
 #include "cc/animation/scrollbar_animation_controller.h"
 #include "cc/animation/scrollbar_animation_controller_linear_fade.h"
@@ -778,18 +779,26 @@
   return root_scroll_layer->children()[0]->bounds();
 }
 
-LayerImpl* LayerTreeImpl::LayerById(int id) {
-  LayerIdMap::iterator iter = layer_id_map_.find(id);
+LayerImpl* LayerTreeImpl::LayerById(int id) const {
+  LayerIdMap::const_iterator iter = layer_id_map_.find(id);
   return iter != layer_id_map_.end() ? iter->second : NULL;
 }
 
 void LayerTreeImpl::RegisterLayer(LayerImpl* layer) {
   DCHECK(!LayerById(layer->id()));
   layer_id_map_[layer->id()] = layer;
+  if (layer_tree_host_impl_->animation_host())
+    layer_tree_host_impl_->animation_host()->RegisterLayer(
+        layer->id(),
+        IsActiveTree() ? LayerTreeType::ACTIVE : LayerTreeType::PENDING);
 }
 
 void LayerTreeImpl::UnregisterLayer(LayerImpl* layer) {
   DCHECK(LayerById(layer->id()));
+  if (layer_tree_host_impl_->animation_host())
+    layer_tree_host_impl_->animation_host()->UnregisterLayer(
+        layer->id(),
+        IsActiveTree() ? LayerTreeType::ACTIVE : LayerTreeType::PENDING);
   layer_id_map_.erase(layer->id());
 }
 
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index e236194..11a59f7 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -247,7 +247,7 @@
 
   gfx::Rect RootScrollLayerDeviceViewportBounds() const;
 
-  LayerImpl* LayerById(int id);
+  LayerImpl* LayerById(int id) const;
 
   // These should be called by LayerImpl's ctor/dtor.
   void RegisterLayer(LayerImpl* layer);
diff --git a/cc/trees/mutator_host_client.h b/cc/trees/mutator_host_client.h
new file mode 100644
index 0000000..ffb2329
--- /dev/null
+++ b/cc/trees/mutator_host_client.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef CC_TREES_MUTATOR_HOST_CLIENT_H_
+#define CC_TREES_MUTATOR_HOST_CLIENT_H_
+
+namespace gfx {
+class Transform;
+class ScrollOffset;
+}
+
+namespace cc {
+
+class FilterOperations;
+class Layer;
+
+enum class LayerTreeType { ACTIVE, PENDING };
+
+class MutatorHostClient {
+ public:
+  virtual bool IsLayerInTree(int layer_id, LayerTreeType tree_type) const = 0;
+  virtual void SetMutatorsNeedCommit() = 0;
+
+  virtual void SetLayerFilterMutated(int layer_id,
+                                     LayerTreeType tree_type,
+                                     const FilterOperations& filters) = 0;
+  virtual void SetLayerOpacityMutated(int layer_id,
+                                      LayerTreeType tree_type,
+                                      float opacity) = 0;
+  virtual void SetLayerTransformMutated(int layer_id,
+                                        LayerTreeType tree_type,
+                                        const gfx::Transform& transform) = 0;
+  virtual void SetLayerScrollOffsetMutated(
+      int layer_id,
+      LayerTreeType tree_type,
+      const gfx::ScrollOffset& scroll_offset) = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_TREES_MUTATOR_HOST_CLIENT_H_
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index f0a15aa..5f3e91b 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -19,7 +19,10 @@
 #include "base/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "cc/animation/animation_host.h"
+#include "cc/animation/animation_timeline.h"
 #include "cc/base/switches.h"
+#include "cc/blink/web_compositor_animation_timeline_impl.h"
 #include "cc/blink/web_layer_impl.h"
 #include "cc/debug/layer_tree_debug_state.h"
 #include "cc/debug/micro_benchmark.h"
@@ -597,6 +600,24 @@
   layer_tree_host_->SetRootLayer(scoped_refptr<cc::Layer>());
 }
 
+void RenderWidgetCompositor::attachCompositorAnimationTimeline(
+    blink::WebCompositorAnimationTimeline* compositor_timeline) {
+  DCHECK(compositor_timeline);
+  DCHECK(layer_tree_host_->animation_host());
+  layer_tree_host_->animation_host()->AddAnimationTimeline(
+      static_cast<const cc_blink::WebCompositorAnimationTimelineImpl*>(
+          compositor_timeline)->animation_timeline());
+}
+
+void RenderWidgetCompositor::detachCompositorAnimationTimeline(
+    blink::WebCompositorAnimationTimeline* compositor_timeline) {
+  DCHECK(compositor_timeline);
+  DCHECK(layer_tree_host_->animation_host());
+  layer_tree_host_->animation_host()->RemoveAnimationTimeline(
+      static_cast<const cc_blink::WebCompositorAnimationTimelineImpl*>(
+          compositor_timeline)->animation_timeline());
+}
+
 void RenderWidgetCompositor::setViewportSize(
     const WebSize&,
     const WebSize& device_viewport_size) {
diff --git a/content/renderer/gpu/render_widget_compositor.h b/content/renderer/gpu/render_widget_compositor.h
index e22ff25..b11f7a2 100644
--- a/content/renderer/gpu/render_widget_compositor.h
+++ b/content/renderer/gpu/render_widget_compositor.h
@@ -82,6 +82,10 @@
   // WebLayerTreeView implementation.
   virtual void setRootLayer(const blink::WebLayer& layer);
   virtual void clearRootLayer();
+  virtual void attachCompositorAnimationTimeline(
+      blink::WebCompositorAnimationTimeline* compositor_timeline);
+  virtual void detachCompositorAnimationTimeline(
+      blink::WebCompositorAnimationTimeline* compositor_timeline);
   virtual void setViewportSize(
       const blink::WebSize& unused_deprecated,
       const blink::WebSize& device_viewport_size);