1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/animation/effect_stack.h"
6
7 #include <memory>
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "third_party/blink/renderer/core/animation/animation_clock.h"
10 #include "third_party/blink/renderer/core/animation/animation_test_helper.h"
11 #include "third_party/blink/renderer/core/animation/document_timeline.h"
12 #include "third_party/blink/renderer/core/animation/element_animations.h"
13 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
14 #include "third_party/blink/renderer/core/animation/invalidatable_interpolation.h"
15 #include "third_party/blink/renderer/core/animation/keyframe_effect_model.h"
16 #include "third_party/blink/renderer/core/animation/pending_animations.h"
17 #include "third_party/blink/renderer/core/animation/string_keyframe.h"
18 #include "third_party/blink/renderer/core/testing/page_test_base.h"
19 #include "third_party/blink/renderer/platform/heap/heap.h"
20
21 namespace blink {
22
23 class AnimationEffectStackTest : public PageTestBase {
24 protected:
SetUp()25 void SetUp() override {
26 PageTestBase::SetUp(IntSize());
27 GetDocument().GetAnimationClock().ResetTimeForTesting();
28 timeline = GetDocument().Timeline();
29 element = GetDocument().CreateElementForBinding("foo");
30 }
31
Play(KeyframeEffect * effect,double start_time)32 Animation* Play(KeyframeEffect* effect, double start_time) {
33 Animation* animation = timeline->Play(effect);
34 animation->setStartTime(start_time * 1000, false);
35 animation->Update(kTimingUpdateOnDemand);
36 return animation;
37 }
38
UpdateTimeline(base::TimeDelta time)39 void UpdateTimeline(base::TimeDelta time) {
40 GetDocument().GetAnimationClock().UpdateTime(
41 GetDocument().Timeline().ZeroTime() + time);
42 timeline->ServiceAnimations(kTimingUpdateForAnimationFrame);
43 }
44
SampledEffectCount()45 size_t SampledEffectCount() {
46 return element->EnsureElementAnimations()
47 .GetEffectStack()
48 .sampled_effects_.size();
49 }
50
MakeEffectModel(CSSPropertyID id,const String & value)51 KeyframeEffectModelBase* MakeEffectModel(CSSPropertyID id,
52 const String& value) {
53 StringKeyframeVector keyframes(2);
54 keyframes[0] = MakeGarbageCollected<StringKeyframe>();
55 keyframes[0]->SetOffset(0.0);
56 keyframes[0]->SetCSSPropertyValue(
57 id, value, SecureContextMode::kInsecureContext, nullptr);
58 keyframes[1] = MakeGarbageCollected<StringKeyframe>();
59 keyframes[1]->SetOffset(1.0);
60 keyframes[1]->SetCSSPropertyValue(
61 id, value, SecureContextMode::kInsecureContext, nullptr);
62 return MakeGarbageCollected<StringKeyframeEffectModel>(keyframes);
63 }
64
MakeInertEffect(KeyframeEffectModelBase * effect)65 InertEffect* MakeInertEffect(KeyframeEffectModelBase* effect) {
66 Timing timing;
67 timing.fill_mode = Timing::FillMode::BOTH;
68 return MakeGarbageCollected<InertEffect>(effect, timing, false, 0);
69 }
70
MakeKeyframeEffect(KeyframeEffectModelBase * effect,double duration=10)71 KeyframeEffect* MakeKeyframeEffect(KeyframeEffectModelBase* effect,
72 double duration = 10) {
73 Timing timing;
74 timing.fill_mode = Timing::FillMode::BOTH;
75 timing.iteration_duration = AnimationTimeDelta::FromSecondsD(duration);
76 return MakeGarbageCollected<KeyframeEffect>(element.Get(), effect, timing);
77 }
78
GetFontSizeValue(const ActiveInterpolationsMap & active_interpolations)79 double GetFontSizeValue(
80 const ActiveInterpolationsMap& active_interpolations) {
81 const ActiveInterpolations& interpolations =
82 active_interpolations.at(PropertyHandle(GetCSSPropertyFontSize()));
83 EnsureInterpolatedValueCached(interpolations, GetDocument(), element);
84
85 const auto* typed_value =
86 To<InvalidatableInterpolation>(*interpolations.at(0))
87 .GetCachedValueForTesting();
88 // font-size is stored as an |InterpolableLength|; here we assume pixels.
89 EXPECT_TRUE(typed_value->GetInterpolableValue().IsLength());
90 const InterpolableLength& length =
91 To<InterpolableLength>(typed_value->GetInterpolableValue());
92 return length.CreateCSSValue(kValueRangeAll)->GetDoubleValue();
93 }
94
GetZIndexValue(const ActiveInterpolationsMap & active_interpolations)95 double GetZIndexValue(const ActiveInterpolationsMap& active_interpolations) {
96 const ActiveInterpolations& interpolations =
97 active_interpolations.at(PropertyHandle(GetCSSPropertyZIndex()));
98 EnsureInterpolatedValueCached(interpolations, GetDocument(), element);
99
100 const auto* typed_value =
101 To<InvalidatableInterpolation>(*interpolations.at(0))
102 .GetCachedValueForTesting();
103 // z-index is stored as a straight number value.
104 EXPECT_TRUE(typed_value->GetInterpolableValue().IsNumber());
105 return To<InterpolableNumber>(&typed_value->GetInterpolableValue())
106 ->Value();
107 }
108
109 Persistent<DocumentTimeline> timeline;
110 Persistent<Element> element;
111 };
112
TEST_F(AnimationEffectStackTest,ElementAnimationsSorted)113 TEST_F(AnimationEffectStackTest, ElementAnimationsSorted) {
114 Play(MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kFontSize, "1px")),
115 10);
116 Play(MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kFontSize, "2px")),
117 15);
118 Play(MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kFontSize, "3px")), 5);
119 ActiveInterpolationsMap result = EffectStack::ActiveInterpolations(
120 &element->GetElementAnimations()->GetEffectStack(), nullptr, nullptr,
121 KeyframeEffect::kDefaultPriority);
122 EXPECT_EQ(1u, result.size());
123 EXPECT_EQ(GetFontSizeValue(result), 3);
124 }
125
TEST_F(AnimationEffectStackTest,NewAnimations)126 TEST_F(AnimationEffectStackTest, NewAnimations) {
127 Play(MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kFontSize, "1px")),
128 15);
129 Play(MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kZIndex, "2")), 10);
130 HeapVector<Member<const InertEffect>> new_animations;
131 InertEffect* inert1 =
132 MakeInertEffect(MakeEffectModel(CSSPropertyID::kFontSize, "3px"));
133 InertEffect* inert2 =
134 MakeInertEffect(MakeEffectModel(CSSPropertyID::kZIndex, "4"));
135 new_animations.push_back(inert1);
136 new_animations.push_back(inert2);
137 ActiveInterpolationsMap result = EffectStack::ActiveInterpolations(
138 &element->GetElementAnimations()->GetEffectStack(), &new_animations,
139 nullptr, KeyframeEffect::kDefaultPriority);
140 EXPECT_EQ(2u, result.size());
141 EXPECT_EQ(GetFontSizeValue(result), 3);
142 EXPECT_EQ(GetZIndexValue(result), 4);
143 }
144
TEST_F(AnimationEffectStackTest,CancelledAnimations)145 TEST_F(AnimationEffectStackTest, CancelledAnimations) {
146 HeapHashSet<Member<const Animation>> cancelled_animations;
147 Animation* animation = Play(
148 MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kFontSize, "1px")), 0);
149 cancelled_animations.insert(animation);
150 Play(MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kZIndex, "2")), 0);
151 ActiveInterpolationsMap result = EffectStack::ActiveInterpolations(
152 &element->GetElementAnimations()->GetEffectStack(), nullptr,
153 &cancelled_animations, KeyframeEffect::kDefaultPriority);
154 EXPECT_EQ(1u, result.size());
155 EXPECT_EQ(GetZIndexValue(result), 2);
156 }
157
TEST_F(AnimationEffectStackTest,ClearedEffectsRemoved)158 TEST_F(AnimationEffectStackTest, ClearedEffectsRemoved) {
159 Animation* animation = Play(
160 MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kFontSize, "1px")), 10);
161 ActiveInterpolationsMap result = EffectStack::ActiveInterpolations(
162 &element->GetElementAnimations()->GetEffectStack(), nullptr, nullptr,
163 KeyframeEffect::kDefaultPriority);
164 EXPECT_EQ(1u, result.size());
165 EXPECT_EQ(GetFontSizeValue(result), 1);
166
167 animation->setEffect(nullptr);
168 result = EffectStack::ActiveInterpolations(
169 &element->GetElementAnimations()->GetEffectStack(), nullptr, nullptr,
170 KeyframeEffect::kDefaultPriority);
171 EXPECT_EQ(0u, result.size());
172 }
173
TEST_F(AnimationEffectStackTest,ForwardsFillDiscarding)174 TEST_F(AnimationEffectStackTest, ForwardsFillDiscarding) {
175 Play(MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kFontSize, "1px")), 2);
176 Play(MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kFontSize, "2px")), 6);
177 Play(MakeKeyframeEffect(MakeEffectModel(CSSPropertyID::kFontSize, "3px")), 4);
178 GetDocument().GetPendingAnimations().Update(nullptr);
179
180 // Because we will be forcing a naive GC that assumes there are no Oilpan
181 // objects on the stack (e.g. passes BlinkGC::kNoHeapPointersOnStack), we have
182 // to keep the ActiveInterpolationsMap in a Persistent.
183 Persistent<ActiveInterpolationsMap> interpolations;
184
185 UpdateTimeline(base::TimeDelta::FromSeconds(11));
186 ThreadState::Current()->CollectAllGarbageForTesting();
187 interpolations = MakeGarbageCollected<ActiveInterpolationsMap>(
188 EffectStack::ActiveInterpolations(
189 &element->GetElementAnimations()->GetEffectStack(), nullptr, nullptr,
190 KeyframeEffect::kDefaultPriority));
191 EXPECT_EQ(1u, interpolations->size());
192 EXPECT_EQ(GetFontSizeValue(*interpolations), 3);
193 EXPECT_EQ(3u, SampledEffectCount());
194
195 UpdateTimeline(base::TimeDelta::FromSeconds(13));
196 ThreadState::Current()->CollectAllGarbageForTesting();
197 interpolations = MakeGarbageCollected<ActiveInterpolationsMap>(
198 EffectStack::ActiveInterpolations(
199 &element->GetElementAnimations()->GetEffectStack(), nullptr, nullptr,
200 KeyframeEffect::kDefaultPriority));
201 EXPECT_EQ(1u, interpolations->size());
202 EXPECT_EQ(GetFontSizeValue(*interpolations), 3);
203 EXPECT_EQ(3u, SampledEffectCount());
204
205 UpdateTimeline(base::TimeDelta::FromSeconds(15));
206 ThreadState::Current()->CollectAllGarbageForTesting();
207 interpolations = MakeGarbageCollected<ActiveInterpolationsMap>(
208 EffectStack::ActiveInterpolations(
209 &element->GetElementAnimations()->GetEffectStack(), nullptr, nullptr,
210 KeyframeEffect::kDefaultPriority));
211 EXPECT_EQ(1u, interpolations->size());
212 EXPECT_EQ(GetFontSizeValue(*interpolations), 3);
213 EXPECT_EQ(2u, SampledEffectCount());
214
215 UpdateTimeline(base::TimeDelta::FromSeconds(17));
216 ThreadState::Current()->CollectAllGarbageForTesting();
217 interpolations = MakeGarbageCollected<ActiveInterpolationsMap>(
218 EffectStack::ActiveInterpolations(
219 &element->GetElementAnimations()->GetEffectStack(), nullptr, nullptr,
220 KeyframeEffect::kDefaultPriority));
221 EXPECT_EQ(1u, interpolations->size());
222 EXPECT_EQ(GetFontSizeValue(*interpolations), 3);
223 EXPECT_EQ(1u, SampledEffectCount());
224 }
225
226 } // namespace blink
227