Commit c035b5fc authored by Majid Valipour's avatar Majid Valipour Committed by Commit Bot

[animation-worket] Ensure each worklet receives only its own input

Previously we would allow worklet to access and act on all of worklet inputs.
This is bad since it can leak information between different animation
worklet global scopes.

Summary of the fix:
- Introduce WorkletAnimationId which can uniquely identify both the worklet and animation.
- Give each worklet animation its own Id generated using its underlying worklet's scope.
- Plumb this new Id to cc.
- AnimationHost produces input state which is bucketed per scope.
- CompositorMutatorImpl only passes to each compositor animator the input.
  bucket that matches that compositor animator's scope Id.
- For additional safety added DCHECK to verify that each scope is receiving only its
  own input.

TEST: compositor_mutator_impl_test.cc
Bug: 857479

Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Ib4b8f610ef661bdbf09c4c0252a2b9c4ba04db16
Reviewed-on: https://chromium-review.googlesource.com/1120436Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Commit-Queue: Majid Valipour <majidvp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#572572}
parent 3e326e4a
......@@ -620,12 +620,14 @@ void AnimationHost::SetMutationUpdate(
TRACE_EVENT0("cc", "AnimationHost::SetMutationUpdate");
for (auto& animation_state : output_state->animations) {
int id = animation_state.animation_id;
WorkletAnimationId id = animation_state.worklet_animation_id;
// TODO(majidvp): Use a map to make lookup O(1)
auto to_update =
std::find_if(ticking_animations_.begin(), ticking_animations_.end(),
[id](auto& it) { return it->id() == id; });
auto to_update = std::find_if(
ticking_animations_.begin(), ticking_animations_.end(), [id](auto& it) {
return it->IsWorkletAnimation() &&
ToWorkletAnimation(it.get())->worklet_animation_id() == id;
});
if (to_update == ticking_animations_.end())
continue;
......
......@@ -4,6 +4,7 @@
#include "cc/animation/worklet_animation.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/animation/keyframe_effect.h"
#include "cc/animation/scroll_timeline.h"
#include "cc/trees/animation_options.h"
......@@ -11,12 +12,14 @@
namespace cc {
WorkletAnimation::WorkletAnimation(
int id,
int cc_animation_id,
WorkletAnimationId worklet_animation_id,
const std::string& name,
std::unique_ptr<ScrollTimeline> scroll_timeline,
std::unique_ptr<AnimationOptions> options,
bool is_controlling_instance)
: SingleKeyframeEffectAnimation(id),
: SingleKeyframeEffectAnimation(cc_animation_id),
worklet_animation_id_(worklet_animation_id),
name_(name),
scroll_timeline_(std::move(scroll_timeline)),
options_(std::move(options)),
......@@ -28,12 +31,13 @@ WorkletAnimation::WorkletAnimation(
WorkletAnimation::~WorkletAnimation() = default;
scoped_refptr<WorkletAnimation> WorkletAnimation::Create(
int id,
WorkletAnimationId worklet_animation_id,
const std::string& name,
std::unique_ptr<ScrollTimeline> scroll_timeline,
std::unique_ptr<AnimationOptions> options) {
return WrapRefCounted(new WorkletAnimation(
id, name, std::move(scroll_timeline), std::move(options), false));
AnimationIdProvider::NextAnimationId(), worklet_animation_id, name,
std::move(scroll_timeline), std::move(options), false));
}
scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const {
......@@ -41,8 +45,9 @@ scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const {
if (scroll_timeline_)
impl_timeline = scroll_timeline_->CreateImplInstance();
return WrapRefCounted(new WorkletAnimation(
id(), name(), std::move(impl_timeline), CloneOptions(), true));
return WrapRefCounted(new WorkletAnimation(id(), worklet_animation_id_,
name(), std::move(impl_timeline),
CloneOptions(), true));
}
void WorkletAnimation::PushPropertiesTo(Animation* animation_impl) {
......@@ -89,15 +94,15 @@ void WorkletAnimation::UpdateInputState(MutatorInputState* input_state,
switch (state_) {
case State::PENDING:
input_state->added_and_updated_animations.push_back(
{id(), name(), current_time, CloneOptions()});
input_state->Add(
{worklet_animation_id(), name(), current_time, CloneOptions()});
state_ = State::RUNNING;
break;
case State::RUNNING:
input_state->updated_animations.push_back({id(), current_time});
input_state->Update({worklet_animation_id(), current_time});
break;
case State::REMOVED:
input_state->removed_animations.push_back(id());
input_state->Remove(worklet_animation_id());
break;
}
}
......
......@@ -24,18 +24,20 @@ class CC_ANIMATION_EXPORT WorkletAnimation final
: public SingleKeyframeEffectAnimation {
public:
enum class State { PENDING, RUNNING, REMOVED };
WorkletAnimation(int id,
WorkletAnimation(int cc_animation_id,
WorkletAnimationId worklet_animation_id,
const std::string& name,
std::unique_ptr<ScrollTimeline> scroll_timeline,
std::unique_ptr<AnimationOptions> options,
bool is_controlling_instance);
static scoped_refptr<WorkletAnimation> Create(
int id,
WorkletAnimationId worklet_animation_id,
const std::string& name,
std::unique_ptr<ScrollTimeline> scroll_timeline,
std::unique_ptr<AnimationOptions> options);
scoped_refptr<Animation> CreateImplInstance() const override;
WorkletAnimationId worklet_animation_id() { return worklet_animation_id_; }
const std::string& name() const { return name_; }
const ScrollTimeline* scroll_timeline() const {
return scroll_timeline_.get();
......@@ -83,6 +85,7 @@ class CC_ANIMATION_EXPORT WorkletAnimation final
return options_ ? options_->Clone() : nullptr;
}
WorkletAnimationId worklet_animation_id_;
std::string name_;
// The ScrollTimeline associated with the underlying animation. If null, the
......
......@@ -33,20 +33,20 @@ class WorkletAnimationTest : public AnimationTimelinesTest {
worklet_animation_ = WorkletAnimation::Create(
worklet_animation_id_, "test_name", nullptr, nullptr);
int cc_id = worklet_animation_->id();
worklet_animation_->AttachElement(element_id_);
host_->AddAnimationTimeline(timeline_);
timeline_->AttachAnimation(worklet_animation_);
host_->PushPropertiesTo(host_impl_);
timeline_impl_ = host_impl_->GetTimelineById(timeline_id_);
worklet_animation_impl_ = ToWorkletAnimation(
timeline_impl_->GetAnimationById(worklet_animation_id_));
worklet_animation_impl_ =
ToWorkletAnimation(timeline_impl_->GetAnimationById(cc_id));
}
scoped_refptr<WorkletAnimation> worklet_animation_;
scoped_refptr<WorkletAnimation> worklet_animation_impl_;
int worklet_animation_id_ = 11;
WorkletAnimationId worklet_animation_id_{11, 12};
};
class MockScrollTimeline : public ScrollTimeline {
......@@ -73,8 +73,7 @@ TEST_F(WorkletAnimationTest, LocalTimeIsUsedWithAnimations) {
host_impl_->ActivateAnimations();
base::TimeDelta local_time = base::TimeDelta::FromSecondsD(duration / 2);
worklet_animation_impl_->SetOutputState({0, local_time});
worklet_animation_impl_->SetOutputState({worklet_animation_id_, local_time});
TickAnimationsTransferEvents(base::TimeTicks(), 0u);
......@@ -157,7 +156,9 @@ TEST_F(WorkletAnimationTest, CurrentTimeCorrectlyUsesScrollTimeline) {
std::make_unique<MutatorInputState>();
worklet_animation->UpdateInputState(state.get(), base::TimeTicks::Now(),
scroll_tree, true);
EXPECT_EQ(1234, state->added_and_updated_animations[0].current_time);
std::unique_ptr<AnimationWorkletInput> input =
state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(1234, input->added_and_updated_animations[0].current_time);
}
TEST_F(WorkletAnimationTest,
......@@ -173,23 +174,25 @@ TEST_F(WorkletAnimationTest,
base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 246.8);
ScrollTree scroll_tree;
std::unique_ptr<MutatorInputState> first_state =
std::unique_ptr<MutatorInputState> state =
std::make_unique<MutatorInputState>();
worklet_animation->UpdateInputState(first_state.get(), first_ticks,
scroll_tree, true);
worklet_animation->UpdateInputState(state.get(), first_ticks, scroll_tree,
true);
// First state request sets the start time and thus current time should be 0.
EXPECT_EQ(0, first_state->added_and_updated_animations[0].current_time);
std::unique_ptr<MutatorInputState> second_state =
std::make_unique<MutatorInputState>();
worklet_animation->UpdateInputState(second_state.get(), second_ticks,
scroll_tree, true);
EXPECT_EQ(123.4, second_state->updated_animations[0].current_time);
std::unique_ptr<AnimationWorkletInput> input =
state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(0, input->added_and_updated_animations[0].current_time);
state.reset(new MutatorInputState);
worklet_animation->UpdateInputState(state.get(), second_ticks, scroll_tree,
true);
input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(123.4, input->updated_animations[0].current_time);
// Should always offset from start time.
std::unique_ptr<MutatorInputState> third_state =
std::make_unique<MutatorInputState>();
worklet_animation->UpdateInputState(third_state.get(), third_ticks,
scroll_tree, true);
EXPECT_EQ(246.8, third_state->updated_animations[0].current_time);
state.reset(new MutatorInputState());
worklet_animation->UpdateInputState(state.get(), third_ticks, scroll_tree,
true);
input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(246.8, input->updated_animations[0].current_time);
}
// This test verifies that worklet animation state is properly updated.
......@@ -223,10 +226,12 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) {
base::TimeTicks time;
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->added_and_updated_animations.size(), 1u);
EXPECT_EQ("test_name", state->added_and_updated_animations[0].name);
EXPECT_EQ(state->updated_animations.size(), 0u);
EXPECT_EQ(state->removed_animations.size(), 0u);
std::unique_ptr<AnimationWorkletInput> input =
state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(input->added_and_updated_animations.size(), 1u);
EXPECT_EQ("test_name", input->added_and_updated_animations[0].name);
EXPECT_EQ(input->updated_animations.size(), 0u);
EXPECT_EQ(input->removed_animations.size(), 0u);
// The state of WorkletAnimation is updated to RUNNING after calling
// UpdateInputState above.
......@@ -234,9 +239,10 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) {
time += base::TimeDelta::FromSecondsD(0.1);
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->added_and_updated_animations.size(), 0u);
EXPECT_EQ(state->updated_animations.size(), 1u);
EXPECT_EQ(state->removed_animations.size(), 0u);
input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
EXPECT_EQ(input->updated_animations.size(), 1u);
EXPECT_EQ(input->removed_animations.size(), 0u);
// Operating on individual KeyframeModel doesn't affect the state of
// WorkletAnimation.
......@@ -245,9 +251,10 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) {
time += base::TimeDelta::FromSecondsD(0.1);
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->added_and_updated_animations.size(), 0u);
EXPECT_EQ(state->updated_animations.size(), 1u);
EXPECT_EQ(state->removed_animations.size(), 0u);
input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
EXPECT_EQ(input->updated_animations.size(), 1u);
EXPECT_EQ(input->removed_animations.size(), 0u);
// WorkletAnimation sets state to REMOVED when JavaScript fires cancel() which
// leads to RemoveKeyframeModels.
......@@ -256,10 +263,11 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) {
state.reset(new MutatorInputState());
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->added_and_updated_animations.size(), 0u);
EXPECT_EQ(state->updated_animations.size(), 0u);
EXPECT_EQ(state->removed_animations.size(), 1u);
EXPECT_EQ(state->removed_animations[0], worklet_animation_id_);
input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
EXPECT_EQ(input->updated_animations.size(), 0u);
EXPECT_EQ(input->removed_animations.size(), 1u);
EXPECT_EQ(input->removed_animations[0], worklet_animation_id_);
}
// This test verifies that worklet animation gets skipped properly.
......@@ -289,21 +297,25 @@ TEST_F(WorkletAnimationTest, SkipUnchangedAnimations) {
base::TimeTicks time;
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->added_and_updated_animations.size(), 1u);
EXPECT_EQ(state->updated_animations.size(), 0u);
std::unique_ptr<AnimationWorkletInput> input =
state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(input->added_and_updated_animations.size(), 1u);
EXPECT_EQ(input->updated_animations.size(), 0u);
state.reset(new MutatorInputState());
// No update on the input state if input time stays the same.
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->updated_animations.size(), 0u);
input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_FALSE(input);
state.reset(new MutatorInputState());
// Different input time causes the input state to be updated.
time += base::TimeDelta::FromSecondsD(0.1);
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->updated_animations.size(), 1u);
input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(input->updated_animations.size(), 1u);
state.reset(new MutatorInputState());
// Input state gets updated when the worklet animation is to be removed even
......@@ -311,8 +323,9 @@ TEST_F(WorkletAnimationTest, SkipUnchangedAnimations) {
worklet_animation_impl_->RemoveKeyframeModels();
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->updated_animations.size(), 0u);
EXPECT_EQ(state->removed_animations.size(), 1u);
input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(input->updated_animations.size(), 0u);
EXPECT_EQ(input->removed_animations.size(), 1u);
}
} // namespace
......
......@@ -4,25 +4,88 @@
#include "cc/trees/layer_tree_mutator.h"
#include <algorithm>
namespace cc {
MutatorInputState::AddAndUpdateState::AddAndUpdateState(
int animation_id,
AnimationWorkletInput::AddAndUpdateState::AddAndUpdateState(
WorkletAnimationId worklet_animation_id,
std::string name,
double current_time,
std::unique_ptr<AnimationOptions> options)
: animation_id(animation_id),
: worklet_animation_id(worklet_animation_id),
name(name),
current_time(current_time),
options(std::move(options)) {}
MutatorInputState::AddAndUpdateState::AddAndUpdateState(AddAndUpdateState&&) =
default;
MutatorInputState::AddAndUpdateState::~AddAndUpdateState() = default;
AnimationWorkletInput::AddAndUpdateState::AddAndUpdateState(
AddAndUpdateState&&) = default;
AnimationWorkletInput::AddAndUpdateState::~AddAndUpdateState() = default;
#if DCHECK_IS_ON()
bool AnimationWorkletInput::ValidateScope(int scope_id) const {
return std::all_of(added_and_updated_animations.cbegin(),
added_and_updated_animations.cend(),
[scope_id](auto& it) {
return it.worklet_animation_id.scope_id == scope_id;
}) &&
std::all_of(updated_animations.cbegin(), updated_animations.cend(),
[scope_id](auto& it) {
return it.worklet_animation_id.scope_id == scope_id;
}) &&
std::all_of(removed_animations.cbegin(), removed_animations.cend(),
[scope_id](auto& it) { return it.scope_id == scope_id; });
}
#endif
AnimationWorkletInput::AnimationWorkletInput() = default;
AnimationWorkletInput::~AnimationWorkletInput() = default;
MutatorInputState::MutatorInputState() = default;
MutatorInputState::~MutatorInputState() = default;
MutatorOutputState::MutatorOutputState() = default;
MutatorOutputState::~MutatorOutputState() = default;
bool MutatorInputState::IsEmpty() const {
// If there is an AnimationWorkletInput entry in the map then that entry is
// guranteed to be non-empty. So checking |inputs_| map emptiness is
// sufficient.
return inputs_.empty();
}
AnimationWorkletInput& MutatorInputState::EnsureWorkletEntry(int id) {
auto it = inputs_.find(id);
if (it == inputs_.end())
it = inputs_.emplace_hint(it, id, new AnimationWorkletInput);
return *it->second;
}
void MutatorInputState::Add(AnimationWorkletInput::AddAndUpdateState&& state) {
AnimationWorkletInput& worklet_input =
EnsureWorkletEntry(state.worklet_animation_id.scope_id);
worklet_input.added_and_updated_animations.push_back(std::move(state));
}
void MutatorInputState::Update(AnimationWorkletInput::UpdateState&& state) {
AnimationWorkletInput& worklet_input =
EnsureWorkletEntry(state.worklet_animation_id.scope_id);
worklet_input.updated_animations.push_back(std::move(state));
}
void MutatorInputState::Remove(WorkletAnimationId worklet_animation_id) {
AnimationWorkletInput& worklet_input =
EnsureWorkletEntry(worklet_animation_id.scope_id);
worklet_input.removed_animations.push_back(worklet_animation_id);
}
std::unique_ptr<AnimationWorkletInput> MutatorInputState::TakeWorkletState(
int scope_id) {
auto it = inputs_.find(scope_id);
if (it == inputs_.end())
return nullptr;
std::unique_ptr<AnimationWorkletInput> result = std::move(it->second);
inputs_.erase(it);
return result;
}
AnimationWorkletOutput::AnimationWorkletOutput() = default;
AnimationWorkletOutput::~AnimationWorkletOutput() = default;
} // namespace cc
......@@ -12,50 +12,96 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace cc {
struct CC_EXPORT MutatorInputState {
struct CC_EXPORT WorkletAnimationId {
// Uniquely identifies the animation worklet with which this animation is
// associated.
int scope_id;
// Uniquely identifies the animation within its animation worklet. Note that
// animation_id is only guaranteed to be unique per animation worklet.
int animation_id;
inline bool operator==(const WorkletAnimationId& rhs) const {
return (this->scope_id == rhs.scope_id) &&
(this->animation_id == rhs.animation_id);
}
};
struct CC_EXPORT AnimationWorkletInput {
struct CC_EXPORT AddAndUpdateState {
int animation_id;
WorkletAnimationId worklet_animation_id;
// Name associated with worklet animation.
std::string name;
// Worklet animation's current time, from its associated timeline.
double current_time;
std::unique_ptr<AnimationOptions> options;
AddAndUpdateState(int animation_id,
AddAndUpdateState(WorkletAnimationId worklet_animation_id,
std::string name,
double current_time,
std::unique_ptr<AnimationOptions> options);
AddAndUpdateState(AddAndUpdateState&&);
~AddAndUpdateState();
};
struct CC_EXPORT UpdateState {
int animation_id = 0;
WorkletAnimationId worklet_animation_id;
// Worklet animation's current time, from its associated timeline.
double current_time = 0;
};
// Note: When adding any new fields please also update ValidateScope to
// reflect them if necessary.
std::vector<AddAndUpdateState> added_and_updated_animations;
std::vector<UpdateState> updated_animations;
std::vector<WorkletAnimationId> removed_animations;
AnimationWorkletInput();
~AnimationWorkletInput();
#if DCHECK_IS_ON()
// Verifies all animation states have the expected scope id.
bool ValidateScope(int scope_id) const;
#endif
DISALLOW_COPY_AND_ASSIGN(AnimationWorkletInput);
};
class CC_EXPORT MutatorInputState {
public:
MutatorInputState();
~MutatorInputState();
bool IsEmpty() {
return added_and_updated_animations.empty() && updated_animations.empty() &&
removed_animations.empty();
}
bool IsEmpty() const;
void Add(AnimationWorkletInput::AddAndUpdateState&& state);
void Update(AnimationWorkletInput::UpdateState&& state);
void Remove(WorkletAnimationId worklet_animation_id);
std::vector<AddAndUpdateState> added_and_updated_animations;
std::vector<UpdateState> updated_animations;
std::vector<int> removed_animations;
// Returns input for animation worklet with the given |scope_id| and nullptr
// if there is no input.
std::unique_ptr<AnimationWorkletInput> TakeWorkletState(int scope_id);
private:
using InputMap =
std::unordered_map<int, std::unique_ptr<AnimationWorkletInput>>;
// Maps a scope id to its associated AnimationWorkletInput instance.
// Only contains scope ids for which there is a non-empty input.
InputMap inputs_;
// Returns iterator pointing to the entry in |inputs_| map whose key is id. It
// inserts a new entry if none exists.
AnimationWorkletInput& EnsureWorkletEntry(int id);
DISALLOW_COPY_AND_ASSIGN(MutatorInputState);
};
struct CC_EXPORT MutatorOutputState {
struct CC_EXPORT AnimationWorkletOutput {
struct CC_EXPORT AnimationState {
int animation_id = 0;
WorkletAnimationId worklet_animation_id;
// The animator effect's local time.
// TODO(majidvp): This assumes each animator has a single output effect
// which does not hold once we state support group effects.
......@@ -63,12 +109,16 @@ struct CC_EXPORT MutatorOutputState {
base::TimeDelta local_time;
};
MutatorOutputState();
~MutatorOutputState();
AnimationWorkletOutput();
~AnimationWorkletOutput();
std::vector<AnimationState> animations;
};
// LayerTreeMutatorClient processes worklet outputs individually so we can
// define mutator output to be the same as animation worklet output.
using MutatorOutputState = AnimationWorkletOutput;
class LayerTreeMutatorClient {
public:
// Called when mutator needs to update its output.
......
......@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/modules/animationworklet/animation_worklet.h"
#include "base/atomic_sequence_num.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/dom/animation_worklet_proxy_client.h"
#include "third_party/blink/renderer/core/dom/document.h"
......@@ -13,9 +14,18 @@
#include "third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.h"
#include "third_party/blink/renderer/modules/animationworklet/animation_worklet_thread.h"
base::AtomicSequenceNumber g_next_worklet_id;
int NextId() {
// Start id from 1. This way it safe to use it as key in hashmap with default
// key traits.
return g_next_worklet_id.GetNext() + 1;
}
namespace blink {
AnimationWorklet::AnimationWorklet(Document* document) : Worklet(document) {}
AnimationWorklet::AnimationWorklet(Document* document)
: Worklet(document), scope_id_(NextId()), last_animation_id_(0) {}
AnimationWorklet::~AnimationWorklet() = default;
......@@ -31,7 +41,7 @@ WorkletGlobalScopeProxy* AnimationWorklet::CreateGlobalScope() {
Document* document = ToDocument(GetExecutionContext());
AnimationWorkletProxyClient* proxy_client =
AnimationWorkletProxyClientImpl::FromDocument(document);
AnimationWorkletProxyClientImpl::FromDocument(document, scope_id_);
WorkerClients* worker_clients = WorkerClients::Create();
ProvideAnimationWorkletProxyClientTo(worker_clients, proxy_client);
......@@ -42,6 +52,12 @@ WorkletGlobalScopeProxy* AnimationWorklet::CreateGlobalScope() {
return proxy;
}
WorkletAnimationId AnimationWorklet::NextWorkletAnimationId() {
// Id starts from 1. This way it safe to use it as key in hashmap with default
// key traits.
return {.scope_id = scope_id_, .animation_id = ++last_animation_id_};
}
void AnimationWorklet::Trace(blink::Visitor* visitor) {
Worklet::Trace(visitor);
}
......
......@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/workers/worklet.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/graphics/compositor_animators_state.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink {
......@@ -25,9 +26,14 @@ class MODULES_EXPORT AnimationWorklet final : public Worklet {
explicit AnimationWorklet(Document*);
~AnimationWorklet() override;
WorkletAnimationId NextWorkletAnimationId();
void Trace(blink::Visitor*) override;
private:
// Unique id associated with this worklet that is used by cc to identify all
// animations associated it.
int scope_id_;
int last_animation_id_;
// Implements Worklet.
bool NeedsToCreateGlobalScope() final;
......
......@@ -21,12 +21,12 @@ namespace {
void UpdateAnimation(Animator* animator,
ScriptState* script_state,
int id,
WorkletAnimationId id,
double current_time,
CompositorMutatorOutputState* result) {
CompositorMutatorOutputState::AnimationState animation_output;
if (animator->Animate(script_state, current_time, &animation_output)) {
animation_output.animation_id = id;
animation_output.worklet_animation_id = id;
result->animations.push_back(std::move(animation_output));
}
}
......@@ -77,23 +77,22 @@ Animator* AnimationWorkletGlobalScope::CreateAnimatorFor(
return animator;
}
std::unique_ptr<CompositorMutatorOutputState>
AnimationWorkletGlobalScope::Mutate(
const CompositorMutatorInputState& mutator_input) {
std::unique_ptr<AnimationWorkletOutput> AnimationWorkletGlobalScope::Mutate(
const AnimationWorkletInput& mutator_input) {
DCHECK(IsContextThread());
ScriptState* script_state = ScriptController()->GetScriptState();
ScriptState::Scope scope(script_state);
std::unique_ptr<CompositorMutatorOutputState> result =
std::make_unique<CompositorMutatorOutputState>();
std::unique_ptr<AnimationWorkletOutput> result =
std::make_unique<AnimationWorkletOutput>();
for (const auto& id : mutator_input.removed_animations)
animators_.erase(id);
for (const auto& worklet_animation_id : mutator_input.removed_animations)
animators_.erase(worklet_animation_id.animation_id);
for (const auto& animation : mutator_input.added_and_updated_animations) {
int id = animation.animation_id;
DCHECK(!animators_.at(id));
int id = animation.worklet_animation_id.animation_id;
DCHECK(!animators_.Contains(id));
const String name =
String::FromUTF8(animation.name.data(), animation.name.size());
......@@ -105,19 +104,19 @@ AnimationWorkletGlobalScope::Mutate(
if (!animator)
continue;
UpdateAnimation(animator, script_state, id, animation.current_time,
result.get());
UpdateAnimation(animator, script_state, animation.worklet_animation_id,
animation.current_time, result.get());
}
for (const auto& animation : mutator_input.updated_animations) {
int id = animation.animation_id;
int id = animation.worklet_animation_id.animation_id;
Animator* animator = animators_.at(id);
// We don't try to create an animator if there isn't any.
if (!animator)
continue;
UpdateAnimation(animator, script_state, id, animation.current_time,
result.get());
UpdateAnimation(animator, script_state, animation.worklet_animation_id,
animation.current_time, result.get());
}
return result;
......
......@@ -42,8 +42,7 @@ class MODULES_EXPORT AnimationWorkletGlobalScope
bool IsAnimationWorkletGlobalScope() const final { return true; }
// Invokes the |animate| function of all of its active animators.
std::unique_ptr<CompositorMutatorOutputState> Mutate(
const CompositorMutatorInputState&);
std::unique_ptr<AnimationWorkletOutput> Mutate(const AnimationWorkletInput&);
// Registers a animator definition with the given name and constructor.
void registerAnimator(const String& name,
......
......@@ -222,10 +222,12 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase {
// Passing a new input state with a new animation id should cause the
// worklet to create and animate an animator.
CompositorMutatorInputState state;
state.added_and_updated_animations.emplace_back(1, "test", 5000, nullptr);
cc::WorkletAnimationId animation_id = {1, 1};
AnimationWorkletInput state;
state.added_and_updated_animations.emplace_back(animation_id, "test", 5000,
nullptr);
std::unique_ptr<CompositorMutatorOutputState> output =
std::unique_ptr<AnimationWorkletOutput> output =
global_scope->Mutate(state);
EXPECT_TRUE(output);
......@@ -270,10 +272,12 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase {
// Passing a new input state with a new animation id should cause the
// worklet to create and animate an animator.
CompositorMutatorInputState state;
state.added_and_updated_animations.emplace_back(1, "test", 5000, nullptr);
cc::WorkletAnimationId animation_id = {1, 1};
AnimationWorkletInput state;
state.added_and_updated_animations.emplace_back(animation_id, "test", 5000,
nullptr);
std::unique_ptr<CompositorMutatorOutputState> output =
std::unique_ptr<AnimationWorkletOutput> output =
global_scope->Mutate(state);
EXPECT_TRUE(output);
......@@ -311,8 +315,8 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase {
});
)JS"));
int animation_id = 1;
CompositorMutatorInputState state;
cc::WorkletAnimationId animation_id = {1, 1};
AnimationWorkletInput state;
state.updated_animations.push_back({animation_id, 5000});
EXPECT_EQ(state.added_and_updated_animations.size(), 0u);
EXPECT_EQ(state.updated_animations.size(), 1u);
......@@ -359,8 +363,8 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase {
});
)JS"));
int animation_id = 1;
CompositorMutatorInputState state;
cc::WorkletAnimationId animation_id = {1, 1};
AnimationWorkletInput state;
state.added_and_updated_animations.push_back(
{animation_id, "test", 5000, nullptr});
EXPECT_EQ(state.added_and_updated_animations.size(), 1u);
......
......@@ -14,9 +14,11 @@
namespace blink {
AnimationWorkletProxyClientImpl::AnimationWorkletProxyClientImpl(
int scope_id,
base::WeakPtr<CompositorMutatorImpl> mutator,
scoped_refptr<base::SingleThreadTaskRunner> mutator_runner)
: mutator_(std::move(mutator)),
: scope_id_(scope_id),
mutator_(std::move(mutator)),
mutator_runner_(std::move(mutator_runner)),
state_(RunState::kUninitialized) {
DCHECK(IsMainThread());
......@@ -71,29 +73,36 @@ void AnimationWorkletProxyClientImpl::Dispose() {
state_ = RunState::kDisposed;
}
std::unique_ptr<CompositorMutatorOutputState>
AnimationWorkletProxyClientImpl::Mutate(
const CompositorMutatorInputState& input_state) {
std::unique_ptr<AnimationWorkletOutput> AnimationWorkletProxyClientImpl::Mutate(
std::unique_ptr<AnimationWorkletInput> input) {
DCHECK(input);
#if DCHECK_IS_ON()
DCHECK(input->ValidateScope(scope_id_))
<< "Input has state that does not belong to this global scope: "
<< scope_id_;
#endif
if (!global_scope_)
return nullptr;
auto output_state = global_scope_->Mutate(input_state);
auto output = global_scope_->Mutate(*input);
// TODO(petermayo): https://crbug.com/791280 PostCrossThreadTask to supply
// this rather than return it.
return output_state;
return output;
}
// static
AnimationWorkletProxyClientImpl* AnimationWorkletProxyClientImpl::FromDocument(
Document* document) {
Document* document,
int scope_id) {
WebLocalFrameImpl* local_frame =
WebLocalFrameImpl::FromFrame(document->GetFrame());
scoped_refptr<base::SingleThreadTaskRunner> mutator_queue;
base::WeakPtr<CompositorMutatorImpl> mutator =
local_frame->LocalRootFrameWidget()->EnsureCompositorMutator(
&mutator_queue);
return new AnimationWorkletProxyClientImpl(std::move(mutator),
return new AnimationWorkletProxyClientImpl(scope_id, std::move(mutator),
std::move(mutator_queue));
}
......
......@@ -35,6 +35,7 @@ class MODULES_EXPORT AnimationWorkletProxyClientImpl final
// This client is hooked to the given |mutatee|, on the given
// |mutatee_runner|.
explicit AnimationWorkletProxyClientImpl(
int scope_id,
base::WeakPtr<CompositorMutatorImpl> mutatee,
scoped_refptr<base::SingleThreadTaskRunner> mutatee_runner);
void Trace(blink::Visitor*) override;
......@@ -45,12 +46,14 @@ class MODULES_EXPORT AnimationWorkletProxyClientImpl final
// CompositorAnimator:
// These methods are invoked on the animation worklet thread.
std::unique_ptr<CompositorMutatorOutputState> Mutate(
const CompositorMutatorInputState&) override;
int GetScopeId() const override { return scope_id_; }
std::unique_ptr<AnimationWorkletOutput> Mutate(
std::unique_ptr<AnimationWorkletInput> input) override;
static AnimationWorkletProxyClientImpl* FromDocument(Document*);
static AnimationWorkletProxyClientImpl* FromDocument(Document*, int scope_id);
private:
const int scope_id_;
base::WeakPtr<CompositorMutatorImpl> mutator_;
scoped_refptr<base::SingleThreadTaskRunner> mutator_runner_;
......
......@@ -14,13 +14,16 @@
#include "third_party/blink/renderer/core/animation/timing.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/modules/animationworklet/window_animation_worklet.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
namespace {
bool ConvertAnimationEffects(
const AnimationEffectOrAnimationEffectSequence& effects,
HeapVector<Member<KeyframeEffect>>& keyframe_effects,
......@@ -197,21 +200,26 @@ unsigned NextSequenceNumber() {
} // namespace
WorkletAnimation* WorkletAnimation::Create(
ExecutionContext* context,
String animator_name,
const AnimationEffectOrAnimationEffectSequence& effects,
ExceptionState& exception_state) {
return Create(animator_name, effects, DocumentTimelineOrScrollTimeline(),
nullptr, exception_state);
return Create(context, animator_name, effects,
DocumentTimelineOrScrollTimeline(), nullptr, exception_state);
}
WorkletAnimation* WorkletAnimation::Create(
ExecutionContext* context,
String animator_name,
const AnimationEffectOrAnimationEffectSequence& effects,
DocumentTimelineOrScrollTimeline timeline,
ExceptionState& exception_state) {
return Create(animator_name, effects, timeline, nullptr, exception_state);
return Create(context, animator_name, effects, timeline, nullptr,
exception_state);
}
WorkletAnimation* WorkletAnimation::Create(
ExecutionContext* context,
String animator_name,
const AnimationEffectOrAnimationEffectSequence& effects,
DocumentTimelineOrScrollTimeline timeline,
......@@ -240,24 +248,31 @@ WorkletAnimation* WorkletAnimation::Create(
return nullptr;
}
AnimationWorklet* worklet =
WindowAnimationWorklet::animationWorklet(*context->ExecutingWindow());
WorkletAnimationId id = worklet->NextWorkletAnimationId();
Document& document = keyframe_effects.at(0)->target()->GetDocument();
AnimationTimeline* animation_timeline =
ConvertAnimationTimeline(document, timeline);
WorkletAnimation* animation =
new WorkletAnimation(animator_name, document, keyframe_effects,
new WorkletAnimation(id, animator_name, document, keyframe_effects,
animation_timeline, std::move(options));
return animation;
}
WorkletAnimation::WorkletAnimation(
WorkletAnimationId id,
const String& animator_name,
Document& document,
const HeapVector<Member<KeyframeEffect>>& effects,
AnimationTimeline* timeline,
scoped_refptr<SerializedScriptValue> options)
: sequence_number_(NextSequenceNumber()),
id_(id),
animator_name_(animator_name),
play_state_(Animation::kIdle),
document_(document),
......@@ -402,7 +417,7 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) {
if (!compositor_animation_) {
compositor_animation_ = CompositorAnimation::CreateWorkletAnimation(
animator_name_, ToCompositorScrollTimeline(timeline_),
id_, animator_name_, ToCompositorScrollTimeline(timeline_),
std::move(options_));
compositor_animation_->SetAnimationDelegate(this);
}
......
......@@ -16,6 +16,7 @@
#include "third_party/blink/renderer/platform/animation/compositor_animation.h"
#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
#include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h"
#include "third_party/blink/renderer/platform/graphics/compositor_animators_state.h"
namespace blink {
......@@ -43,15 +44,18 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase,
public:
static WorkletAnimation* Create(
ExecutionContext*,
String animator_name,
const AnimationEffectOrAnimationEffectSequence&,
ExceptionState&);
static WorkletAnimation* Create(
ExecutionContext*,
String animator_name,
const AnimationEffectOrAnimationEffectSequence&,
DocumentTimelineOrScrollTimeline,
ExceptionState&);
static WorkletAnimation* Create(
ExecutionContext*,
String animator_name,
const AnimationEffectOrAnimationEffectSequence&,
DocumentTimelineOrScrollTimeline,
......@@ -106,7 +110,8 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase,
void Trace(blink::Visitor*) override;
private:
WorkletAnimation(const String& animator_name,
WorkletAnimation(WorkletAnimationId id,
const String& animator_name,
Document&,
const HeapVector<Member<KeyframeEffect>>&,
AnimationTimeline*,
......@@ -123,6 +128,8 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase,
unsigned sequence_number_;
WorkletAnimationId id_;
const String animator_name_;
Animation::AnimationPlayState play_state_;
// Start time in ms.
......
......@@ -12,6 +12,7 @@
optional (DocumentTimeline or ScrollTimeline) timeline,
optional SerializedScriptValue options),
RaisesException=Constructor,
ConstructorCallWith=ExecutionContext,
MeasureAs=WorkletAnimationConstructor,
OriginTrialEnabled=AnimationWorklet
] interface WorkletAnimation {
......
......@@ -40,15 +40,17 @@ KeyframeEffect* CreateKeyframeEffect(Element* element) {
return KeyframeEffect::Create(element, CreateEffectModel(), timing);
}
WorkletAnimation* CreateWorkletAnimation(Element* element) {
WorkletAnimation* CreateWorkletAnimation(ExecutionContext* context,
Element* element) {
AnimationEffectOrAnimationEffectSequence effects;
AnimationEffect* effect = CreateKeyframeEffect(element);
effects.SetAnimationEffect(effect);
DocumentTimelineOrScrollTimeline timeline;
scoped_refptr<SerializedScriptValue> options;
DummyExceptionStateForTesting exception_state;
return WorkletAnimation::Create("WorkletAnimation", effects, timeline,
std::move(options), exception_state);
return WorkletAnimation::Create(context, "WorkletAnimation", effects,
timeline, std::move(options),
exception_state);
}
} // namespace
......@@ -61,7 +63,7 @@ class WorkletAnimationTest : public RenderingTest {
void SetUp() override {
RenderingTest::SetUp();
element_ = GetDocument().CreateElementForBinding("test");
worklet_animation_ = CreateWorkletAnimation(element_);
worklet_animation_ = CreateWorkletAnimation(&GetDocument(), element_);
}
Persistent<Element> element_;
......
......@@ -2200,6 +2200,7 @@ jumbo_source_set("unit_tests") {
"graphics/canvas_color_params_test.cc",
"graphics/canvas_resource_test.cc",
"graphics/color_correction_test_utils.cc",
"graphics/compositor_mutator_impl_test.cc",
"graphics/deferred_image_decoder_test.cc",
"graphics/gpu/drawing_buffer_software_rendering_test.cc",
"graphics/graphics_layer_test.cc",
......
......@@ -20,12 +20,12 @@ std::unique_ptr<CompositorAnimation> CompositorAnimation::Create() {
std::unique_ptr<CompositorAnimation>
CompositorAnimation::CreateWorkletAnimation(
cc::WorkletAnimationId worklet_animation_id,
const String& name,
std::unique_ptr<CompositorScrollTimeline> scroll_timeline,
std::unique_ptr<cc::AnimationOptions> options) {
return std::make_unique<CompositorAnimation>(cc::WorkletAnimation::Create(
cc::AnimationIdProvider::NextAnimationId(),
std::string(name.Ascii().data(), name.length()),
worklet_animation_id, std::string(name.Ascii().data(), name.length()),
std::move(scroll_timeline), std::move(options)));
}
......
......@@ -34,6 +34,7 @@ class PLATFORM_EXPORT CompositorAnimation : public cc::AnimationDelegate {
public:
static std::unique_ptr<CompositorAnimation> Create();
static std::unique_ptr<CompositorAnimation> CreateWorkletAnimation(
cc::WorkletAnimationId,
const String& name,
std::unique_ptr<CompositorScrollTimeline>,
std::unique_ptr<cc::AnimationOptions>);
......
......@@ -13,9 +13,12 @@ namespace blink {
class PLATFORM_EXPORT CompositorAnimator : public GarbageCollectedMixin {
public:
virtual ~CompositorAnimator() = default;
virtual int GetScopeId() const = 0;
// Runs the animation frame callback.
virtual std::unique_ptr<CompositorMutatorOutputState> Mutate(
const CompositorMutatorInputState&) = 0;
virtual std::unique_ptr<AnimationWorkletOutput> Mutate(
std::unique_ptr<AnimationWorkletInput>) = 0;
void Trace(blink::Visitor* visitor) override {}
};
......
......@@ -9,9 +9,12 @@
namespace blink {
using AnimationWorkletInput = cc::AnimationWorkletInput;
using AnimationWorkletOutput = cc::AnimationWorkletOutput;
using CompositorMutatorInputState = cc::MutatorInputState;
using CompositorMutatorOutputState = cc::MutatorOutputState;
using WorkletAnimationId = cc::WorkletAnimationId;
} // namespace blink
......
......@@ -18,7 +18,7 @@ class PLATFORM_EXPORT CompositorMutatorClient : public cc::LayerTreeMutator {
explicit CompositorMutatorClient(std::unique_ptr<CompositorMutatorImpl>);
~CompositorMutatorClient() override;
void SetMutationUpdate(std::unique_ptr<cc::MutatorOutputState>);
virtual void SetMutationUpdate(std::unique_ptr<cc::MutatorOutputState>);
// cc::LayerTreeMutator
void SetClient(cc::LayerTreeMutatorClient*) override;
......
......@@ -59,8 +59,13 @@ void CompositorMutatorImpl::Mutate(
[](const CompositorAnimators* animators,
std::unique_ptr<CompositorMutatorInputState> state,
std::unique_ptr<AutoSignal> completion, Outputs* output) {
for (CompositorAnimator* animator : *animators)
output->push_back(animator->Mutate(*state));
for (CompositorAnimator* animator : *animators) {
std::unique_ptr<AnimationWorkletInput> worklet_input =
state->TakeWorkletState(animator->GetScopeId());
if (worklet_input)
output->push_back(animator->Mutate(std::move(worklet_input)));
}
},
CrossThreadUnretained(&animators_), WTF::Passed(std::move(state)),
WTF::Passed(std::make_unique<AutoSignal>(&done)),
......
// Copyright 2018 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 "third_party/blink/renderer/platform/graphics/compositor_mutator_impl.h"
#include "base/single_thread_task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_thread.h"
#include "third_party/blink/public/platform/web_thread_type.h"
#include "third_party/blink/renderer/platform/graphics/compositor_animator.h"
#include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include <memory>
using ::testing::_;
using ::testing::Mock;
using ::testing::StrictMock;
using ::testing::Return;
using ::testing::Truly;
// This test uses actual threads since mutator logic requires it. This means we
// have dependency on Blink platform to create threads.
namespace blink {
namespace {
std::unique_ptr<WebThread> CreateThread(const char* name) {
return Platform::Current()->CreateThread(
WebThreadCreationParams(WebThreadType::kTestThread)
.SetThreadNameForTest(name));
}
class MockCompositorAnimator
: public GarbageCollectedFinalized<MockCompositorAnimator>,
public CompositorAnimator {
USING_GARBAGE_COLLECTED_MIXIN(MockCompositorAnimator);
public:
MockCompositorAnimator(
scoped_refptr<base::SingleThreadTaskRunner> expected_runner)
: expected_runner_(expected_runner) {}
~MockCompositorAnimator() override {}
std::unique_ptr<AnimationWorkletOutput> Mutate(
std::unique_ptr<AnimationWorkletInput> input) override {
return MutateRef(*input);
}
MOCK_CONST_METHOD0(GetScopeId, int());
MOCK_METHOD1(
MutateRef,
std::unique_ptr<AnimationWorkletOutput>(const AnimationWorkletInput&));
scoped_refptr<base::SingleThreadTaskRunner> expected_runner_;
};
class MockCompositorMutatorClient : public CompositorMutatorClient {
public:
MockCompositorMutatorClient(std::unique_ptr<CompositorMutatorImpl> mutator)
: CompositorMutatorClient(std::move(mutator)) {}
~MockCompositorMutatorClient() override {}
// gmock cannot mock methods with move-only args so we forward it to ourself.
void SetMutationUpdate(
std::unique_ptr<cc::MutatorOutputState> output_state) override {
SetMutationUpdateRef(output_state.get());
}
MOCK_METHOD1(SetMutationUpdateRef,
void(cc::MutatorOutputState* output_state));
};
class CompositorMutatorImplTest : public ::testing::Test {
public:
void SetUp() override {
auto mutator = std::make_unique<CompositorMutatorImpl>();
mutator_ = mutator.get();
client_ =
std::make_unique<::testing::StrictMock<MockCompositorMutatorClient>>(
std::move(mutator));
}
void TearDown() override { mutator_ = nullptr; }
std::unique_ptr<::testing::StrictMock<MockCompositorMutatorClient>> client_;
CompositorMutatorImpl* mutator_;
};
bool OnlyIncludesAnimation1(const AnimationWorkletInput& in) {
return in.added_and_updated_animations.size() == 1 &&
in.added_and_updated_animations[0].worklet_animation_id.animation_id ==
1;
}
TEST_F(CompositorMutatorImplTest,
RegisteredAnimatorShouldOnlyReceiveInputForItself) {
std::unique_ptr<WebThread> first_thread = CreateThread("FirstThread");
MockCompositorAnimator* first_animator =
new ::testing::StrictMock<MockCompositorAnimator>(
first_thread->GetTaskRunner());
mutator_->RegisterCompositorAnimator(first_animator,
first_thread->GetTaskRunner());
EXPECT_CALL(*first_animator, GetScopeId()).Times(1).WillOnce(Return(11));
EXPECT_CALL(*first_animator, MutateRef(Truly(OnlyIncludesAnimation1)))
.Times(1);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
AnimationWorkletInput::AddAndUpdateState state1{
{11, 1}, "test1", 5000, nullptr};
AnimationWorkletInput::AddAndUpdateState state2{
{22, 2}, "test2", 5000, nullptr};
auto input = std::make_unique<CompositorMutatorInputState>();
input->Add(std::move(state1));
input->Add(std::move(state2));
mutator_->Mutate(std::move(input));
}
TEST_F(CompositorMutatorImplTest, AnimatorShouldNotBeMutatedWhenNoInput) {
std::unique_ptr<WebThread> first_thread = CreateThread("FirstThread");
MockCompositorAnimator* first_animator =
new ::testing::StrictMock<MockCompositorAnimator>(
first_thread->GetTaskRunner());
mutator_->RegisterCompositorAnimator(first_animator,
first_thread->GetTaskRunner());
EXPECT_CALL(*first_animator, GetScopeId()).Times(1).WillOnce(Return(11));
EXPECT_CALL(*first_animator, MutateRef(_)).Times(0);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
AnimationWorkletInput::AddAndUpdateState state2{
{22, 2}, "test2", 5000, nullptr};
auto input = std::make_unique<CompositorMutatorInputState>();
input->Add(std::move(state2));
mutator_->Mutate(std::move(input));
}
} // namespace
} // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment