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);