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.
6 #include "core/animation/Animation.h"
8 #include "bindings/core/v8/Dictionary.h"
9 #include "bindings/core/v8/UnionTypesCore.h"
10 #include "core/animation/AnimationClock.h"
11 #include "core/animation/AnimationNodeTiming.h"
12 #include "core/animation/AnimationTestHelper.h"
13 #include "core/animation/AnimationTimeline.h"
14 #include "core/animation/KeyframeEffectModel.h"
15 #include "core/animation/Timing.h"
16 #include "core/dom/Document.h"
17 #include "core/dom/ExceptionCode.h"
18 #include "core/testing/DummyPageHolder.h"
19 #include <gtest/gtest.h>
24 class AnimationAnimationTest
: public ::testing::Test
{
26 AnimationAnimationTest()
27 : pageHolder(DummyPageHolder::create())
28 , document(pageHolder
->document())
29 , element(document
.createElement("foo", ASSERT_NO_EXCEPTION
))
31 document
.animationClock().resetTimeForTesting(document
.timeline().zeroTime());
32 EXPECT_EQ(0, document
.timeline().currentTime());
35 OwnPtr
<DummyPageHolder
> pageHolder
;
37 RefPtrWillBePersistent
<Element
> element
;
38 TrackExceptionState exceptionState
;
41 class AnimationAnimationV8Test
: public AnimationAnimationTest
{
43 AnimationAnimationV8Test()
44 : m_isolate(v8::Isolate::GetCurrent())
50 static PassRefPtrWillBeRawPtr
<Animation
> createAnimation(Element
* element
, Vector
<Dictionary
> keyframeDictionaryVector
, T timingInput
, ExceptionState
& exceptionState
)
52 return Animation::create(element
, EffectInput::convert(element
, keyframeDictionaryVector
, exceptionState
), timingInput
);
54 static PassRefPtrWillBeRawPtr
<Animation
> createAnimation(Element
* element
, Vector
<Dictionary
> keyframeDictionaryVector
, ExceptionState
& exceptionState
)
56 return Animation::create(element
, EffectInput::convert(element
, keyframeDictionaryVector
, exceptionState
));
59 v8::Isolate
* m_isolate
;
62 V8TestingScope m_scope
;
65 TEST_F(AnimationAnimationV8Test
, CanCreateAnAnimation
)
67 Vector
<Dictionary
> jsKeyframes
;
68 v8::Handle
<v8::Object
> keyframe1
= v8::Object::New(m_isolate
);
69 v8::Handle
<v8::Object
> keyframe2
= v8::Object::New(m_isolate
);
71 setV8ObjectPropertyAsString(keyframe1
, "width", "100px");
72 setV8ObjectPropertyAsString(keyframe1
, "offset", "0");
73 setV8ObjectPropertyAsString(keyframe1
, "easing", "ease-in-out");
74 setV8ObjectPropertyAsString(keyframe2
, "width", "0px");
75 setV8ObjectPropertyAsString(keyframe2
, "offset", "1");
76 setV8ObjectPropertyAsString(keyframe2
, "easing", "cubic-bezier(1, 1, 0.3, 0.3)");
78 jsKeyframes
.append(Dictionary(keyframe1
, m_isolate
, exceptionState
));
79 jsKeyframes
.append(Dictionary(keyframe2
, m_isolate
, exceptionState
));
82 ASSERT_TRUE(DictionaryHelper::get(jsKeyframes
[0], "width", value1
));
83 ASSERT_EQ("100px", value1
);
86 ASSERT_TRUE(DictionaryHelper::get(jsKeyframes
[1], "width", value2
));
87 ASSERT_EQ("0px", value2
);
89 RefPtrWillBeRawPtr
<Animation
> animation
= createAnimation(element
.get(), jsKeyframes
, 0, exceptionState
);
91 Element
* target
= animation
->target();
92 EXPECT_EQ(*element
.get(), *target
);
94 const KeyframeVector keyframes
= toKeyframeEffectModelBase(animation
->effect())->getFrames();
96 EXPECT_EQ(0, keyframes
[0]->offset());
97 EXPECT_EQ(1, keyframes
[1]->offset());
99 const CSSValue
* keyframe1Width
= toStringKeyframe(keyframes
[0].get())->propertyValue(CSSPropertyWidth
);
100 const CSSValue
* keyframe2Width
= toStringKeyframe(keyframes
[1].get())->propertyValue(CSSPropertyWidth
);
101 ASSERT(keyframe1Width
);
102 ASSERT(keyframe2Width
);
104 EXPECT_EQ("100px", keyframe1Width
->cssText());
105 EXPECT_EQ("0px", keyframe2Width
->cssText());
107 EXPECT_EQ(*(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut
)), keyframes
[0]->easing());
108 EXPECT_EQ(*(CubicBezierTimingFunction::create(1, 1, 0.3, 0.3).get()), keyframes
[1]->easing());
111 TEST_F(AnimationAnimationV8Test
, CanSetDuration
)
113 Vector
<Dictionary
, 0> jsKeyframes
;
114 double duration
= 2000;
116 RefPtrWillBeRawPtr
<Animation
> animation
= createAnimation(element
.get(), jsKeyframes
, duration
, exceptionState
);
118 EXPECT_EQ(duration
/ 1000, animation
->specifiedTiming().iterationDuration
);
121 TEST_F(AnimationAnimationV8Test
, CanOmitSpecifiedDuration
)
123 Vector
<Dictionary
, 0> jsKeyframes
;
124 RefPtrWillBeRawPtr
<Animation
> animation
= createAnimation(element
.get(), jsKeyframes
, exceptionState
);
125 EXPECT_TRUE(std::isnan(animation
->specifiedTiming().iterationDuration
));
128 TEST_F(AnimationAnimationV8Test
, NegativeDurationIsAuto
)
130 Vector
<Dictionary
, 0> jsKeyframes
;
131 RefPtrWillBeRawPtr
<Animation
> animation
= createAnimation(element
.get(), jsKeyframes
, -2, exceptionState
);
132 EXPECT_TRUE(std::isnan(animation
->specifiedTiming().iterationDuration
));
135 TEST_F(AnimationAnimationV8Test
, MismatchedKeyframePropertyRaisesException
)
137 Vector
<Dictionary
> jsKeyframes
;
138 v8::Handle
<v8::Object
> keyframe1
= v8::Object::New(m_isolate
);
139 v8::Handle
<v8::Object
> keyframe2
= v8::Object::New(m_isolate
);
141 setV8ObjectPropertyAsString(keyframe1
, "width", "100px");
142 setV8ObjectPropertyAsString(keyframe1
, "offset", "0");
144 // Height property appears only in keyframe2
145 setV8ObjectPropertyAsString(keyframe2
, "height", "100px");
146 setV8ObjectPropertyAsString(keyframe2
, "width", "0px");
147 setV8ObjectPropertyAsString(keyframe2
, "offset", "1");
149 jsKeyframes
.append(Dictionary(keyframe1
, m_isolate
, exceptionState
));
150 jsKeyframes
.append(Dictionary(keyframe2
, m_isolate
, exceptionState
));
152 createAnimation(element
.get(), jsKeyframes
, 0, exceptionState
);
154 EXPECT_TRUE(exceptionState
.hadException());
155 EXPECT_EQ(NotSupportedError
, exceptionState
.code());
158 TEST_F(AnimationAnimationV8Test
, MissingOffsetZeroRaisesException
)
160 Vector
<Dictionary
> jsKeyframes
;
161 v8::Handle
<v8::Object
> keyframe1
= v8::Object::New(m_isolate
);
162 v8::Handle
<v8::Object
> keyframe2
= v8::Object::New(m_isolate
);
164 setV8ObjectPropertyAsString(keyframe1
, "width", "100px");
165 setV8ObjectPropertyAsString(keyframe1
, "offset", "0.1");
166 setV8ObjectPropertyAsString(keyframe2
, "width", "0px");
167 setV8ObjectPropertyAsString(keyframe2
, "offset", "1");
169 jsKeyframes
.append(Dictionary(keyframe1
, m_isolate
, exceptionState
));
170 jsKeyframes
.append(Dictionary(keyframe2
, m_isolate
, exceptionState
));
172 createAnimation(element
.get(), jsKeyframes
, 0, exceptionState
);
174 EXPECT_TRUE(exceptionState
.hadException());
175 EXPECT_EQ(NotSupportedError
, exceptionState
.code());
178 TEST_F(AnimationAnimationV8Test
, MissingOffsetOneRaisesException
)
180 Vector
<Dictionary
> jsKeyframes
;
181 v8::Handle
<v8::Object
> keyframe1
= v8::Object::New(m_isolate
);
182 v8::Handle
<v8::Object
> keyframe2
= v8::Object::New(m_isolate
);
184 setV8ObjectPropertyAsString(keyframe1
, "width", "100px");
185 setV8ObjectPropertyAsString(keyframe1
, "offset", "0");
186 setV8ObjectPropertyAsString(keyframe2
, "width", "0px");
187 setV8ObjectPropertyAsString(keyframe2
, "offset", "0.1");
189 jsKeyframes
.append(Dictionary(keyframe1
, m_isolate
, exceptionState
));
190 jsKeyframes
.append(Dictionary(keyframe2
, m_isolate
, exceptionState
));
192 createAnimation(element
.get(), jsKeyframes
, 0, exceptionState
);
194 EXPECT_TRUE(exceptionState
.hadException());
195 EXPECT_EQ(NotSupportedError
, exceptionState
.code());
198 TEST_F(AnimationAnimationV8Test
, MissingOffsetZeroAndOneRaisesException
)
200 Vector
<Dictionary
> jsKeyframes
;
201 v8::Handle
<v8::Object
> keyframe1
= v8::Object::New(m_isolate
);
202 v8::Handle
<v8::Object
> keyframe2
= v8::Object::New(m_isolate
);
204 setV8ObjectPropertyAsString(keyframe1
, "width", "100px");
205 setV8ObjectPropertyAsString(keyframe1
, "offset", "0.1");
206 setV8ObjectPropertyAsString(keyframe2
, "width", "0px");
207 setV8ObjectPropertyAsString(keyframe2
, "offset", "0.2");
209 jsKeyframes
.append(Dictionary(keyframe1
, m_isolate
, exceptionState
));
210 jsKeyframes
.append(Dictionary(keyframe2
, m_isolate
, exceptionState
));
212 createAnimation(element
.get(), jsKeyframes
, 0, exceptionState
);
214 EXPECT_TRUE(exceptionState
.hadException());
215 EXPECT_EQ(NotSupportedError
, exceptionState
.code());
218 TEST_F(AnimationAnimationV8Test
, SpecifiedGetters
)
220 Vector
<Dictionary
, 0> jsKeyframes
;
222 v8::Handle
<v8::Object
> timingInput
= v8::Object::New(m_isolate
);
223 setV8ObjectPropertyAsNumber(timingInput
, "delay", 2);
224 setV8ObjectPropertyAsNumber(timingInput
, "endDelay", 0.5);
225 setV8ObjectPropertyAsString(timingInput
, "fill", "backwards");
226 setV8ObjectPropertyAsNumber(timingInput
, "iterationStart", 2);
227 setV8ObjectPropertyAsNumber(timingInput
, "iterations", 10);
228 setV8ObjectPropertyAsNumber(timingInput
, "playbackRate", 2);
229 setV8ObjectPropertyAsString(timingInput
, "direction", "reverse");
230 setV8ObjectPropertyAsString(timingInput
, "easing", "step-start");
231 Dictionary timingInputDictionary
= Dictionary(v8::Handle
<v8::Value
>::Cast(timingInput
), m_isolate
, exceptionState
);
233 RefPtrWillBeRawPtr
<Animation
> animation
= createAnimation(element
.get(), jsKeyframes
, timingInputDictionary
, exceptionState
);
235 RefPtrWillBeRawPtr
<AnimationNodeTiming
> specified
= animation
->timing();
236 EXPECT_EQ(2, specified
->delay());
237 EXPECT_EQ(0.5, specified
->endDelay());
238 EXPECT_EQ("backwards", specified
->fill());
239 EXPECT_EQ(2, specified
->iterationStart());
240 EXPECT_EQ(10, specified
->iterations());
241 EXPECT_EQ(2, specified
->playbackRate());
242 EXPECT_EQ("reverse", specified
->direction());
243 EXPECT_EQ("step-start", specified
->easing());
246 TEST_F(AnimationAnimationV8Test
, SpecifiedDurationGetter
)
248 Vector
<Dictionary
, 0> jsKeyframes
;
250 v8::Handle
<v8::Object
> timingInputWithDuration
= v8::Object::New(m_isolate
);
251 setV8ObjectPropertyAsNumber(timingInputWithDuration
, "duration", 2.5);
252 Dictionary timingInputDictionaryWithDuration
= Dictionary(v8::Handle
<v8::Value
>::Cast(timingInputWithDuration
), m_isolate
, exceptionState
);
254 RefPtrWillBeRawPtr
<Animation
> animationWithDuration
= createAnimation(element
.get(), jsKeyframes
, timingInputDictionaryWithDuration
, exceptionState
);
256 RefPtrWillBeRawPtr
<AnimationNodeTiming
> specifiedWithDuration
= animationWithDuration
->timing();
257 UnrestrictedDoubleOrString duration
;
258 specifiedWithDuration
->duration(duration
);
259 EXPECT_TRUE(duration
.isUnrestrictedDouble());
260 EXPECT_EQ(2.5, duration
.getAsUnrestrictedDouble());
261 EXPECT_FALSE(duration
.isString());
264 v8::Handle
<v8::Object
> timingInputNoDuration
= v8::Object::New(m_isolate
);
265 Dictionary timingInputDictionaryNoDuration
= Dictionary(v8::Handle
<v8::Value
>::Cast(timingInputNoDuration
), m_isolate
, exceptionState
);
267 RefPtrWillBeRawPtr
<Animation
> animationNoDuration
= createAnimation(element
.get(), jsKeyframes
, timingInputDictionaryNoDuration
, exceptionState
);
269 RefPtrWillBeRawPtr
<AnimationNodeTiming
> specifiedNoDuration
= animationNoDuration
->timing();
270 UnrestrictedDoubleOrString duration2
;
271 specifiedNoDuration
->duration(duration2
);
272 EXPECT_FALSE(duration2
.isUnrestrictedDouble());
273 EXPECT_TRUE(duration2
.isString());
274 EXPECT_EQ("auto", duration2
.getAsString());
277 TEST_F(AnimationAnimationV8Test
, SpecifiedSetters
)
279 Vector
<Dictionary
, 0> jsKeyframes
;
280 v8::Handle
<v8::Object
> timingInput
= v8::Object::New(m_isolate
);
281 Dictionary timingInputDictionary
= Dictionary(v8::Handle
<v8::Value
>::Cast(timingInput
), m_isolate
, exceptionState
);
282 RefPtrWillBeRawPtr
<Animation
> animation
= createAnimation(element
.get(), jsKeyframes
, timingInputDictionary
, exceptionState
);
284 RefPtrWillBeRawPtr
<AnimationNodeTiming
> specified
= animation
->timing();
286 EXPECT_EQ(0, specified
->delay());
287 specified
->setDelay(2);
288 EXPECT_EQ(2, specified
->delay());
290 EXPECT_EQ(0, specified
->endDelay());
291 specified
->setEndDelay(0.5);
292 EXPECT_EQ(0.5, specified
->endDelay());
294 EXPECT_EQ("auto", specified
->fill());
295 specified
->setFill("backwards");
296 EXPECT_EQ("backwards", specified
->fill());
298 EXPECT_EQ(0, specified
->iterationStart());
299 specified
->setIterationStart(2);
300 EXPECT_EQ(2, specified
->iterationStart());
302 EXPECT_EQ(1, specified
->iterations());
303 specified
->setIterations(10);
304 EXPECT_EQ(10, specified
->iterations());
306 EXPECT_EQ(1, specified
->playbackRate());
307 specified
->setPlaybackRate(2);
308 EXPECT_EQ(2, specified
->playbackRate());
310 EXPECT_EQ("normal", specified
->direction());
311 specified
->setDirection("reverse");
312 EXPECT_EQ("reverse", specified
->direction());
314 EXPECT_EQ("linear", specified
->easing());
315 specified
->setEasing("step-start");
316 EXPECT_EQ("step-start", specified
->easing());
319 TEST_F(AnimationAnimationV8Test
, SetSpecifiedDuration
)
321 Vector
<Dictionary
, 0> jsKeyframes
;
322 v8::Handle
<v8::Object
> timingInput
= v8::Object::New(m_isolate
);
323 Dictionary timingInputDictionary
= Dictionary(v8::Handle
<v8::Value
>::Cast(timingInput
), m_isolate
, exceptionState
);
324 RefPtrWillBeRawPtr
<Animation
> animation
= createAnimation(element
.get(), jsKeyframes
, timingInputDictionary
, exceptionState
);
326 RefPtrWillBeRawPtr
<AnimationNodeTiming
> specified
= animation
->timing();
328 UnrestrictedDoubleOrString duration
;
329 specified
->duration(duration
);
330 EXPECT_FALSE(duration
.isUnrestrictedDouble());
331 EXPECT_TRUE(duration
.isString());
332 EXPECT_EQ("auto", duration
.getAsString());
334 UnrestrictedDoubleOrString inDuration
;
335 inDuration
.setUnrestrictedDouble(2.5);
336 specified
->setDuration(inDuration
);
337 UnrestrictedDoubleOrString duration2
;
338 specified
->duration(duration2
);
339 EXPECT_TRUE(duration2
.isUnrestrictedDouble());
340 EXPECT_EQ(2.5, duration2
.getAsUnrestrictedDouble());
341 EXPECT_FALSE(duration2
.isString());
344 TEST_F(AnimationAnimationTest
, TimeToEffectChange
)
347 timing
.iterationDuration
= 100;
348 timing
.startDelay
= 100;
349 timing
.endDelay
= 100;
350 timing
.fillMode
= Timing::FillModeNone
;
351 RefPtrWillBeRawPtr
<Animation
> animation
= Animation::create(0, nullptr, timing
);
352 RefPtrWillBeRawPtr
<AnimationPlayer
> player
= document
.timeline().play(animation
.get());
353 double inf
= std::numeric_limits
<double>::infinity();
355 EXPECT_EQ(100, animation
->timeToForwardsEffectChange());
356 EXPECT_EQ(inf
, animation
->timeToReverseEffectChange());
358 player
->setCurrentTimeInternal(100);
359 EXPECT_EQ(100, animation
->timeToForwardsEffectChange());
360 EXPECT_EQ(0, animation
->timeToReverseEffectChange());
362 player
->setCurrentTimeInternal(199);
363 EXPECT_EQ(1, animation
->timeToForwardsEffectChange());
364 EXPECT_EQ(0, animation
->timeToReverseEffectChange());
366 player
->setCurrentTimeInternal(200);
368 EXPECT_EQ(inf
, animation
->timeToForwardsEffectChange());
369 EXPECT_EQ(0, animation
->timeToReverseEffectChange());
371 player
->setCurrentTimeInternal(300);
372 EXPECT_EQ(inf
, animation
->timeToForwardsEffectChange());
373 EXPECT_EQ(100, animation
->timeToReverseEffectChange());
376 TEST_F(AnimationAnimationTest
, TimeToEffectChangeWithPlaybackRate
)
379 timing
.iterationDuration
= 100;
380 timing
.startDelay
= 100;
381 timing
.endDelay
= 100;
382 timing
.playbackRate
= 2;
383 timing
.fillMode
= Timing::FillModeNone
;
384 RefPtrWillBeRawPtr
<Animation
> animation
= Animation::create(0, nullptr, timing
);
385 RefPtrWillBeRawPtr
<AnimationPlayer
> player
= document
.timeline().play(animation
.get());
386 double inf
= std::numeric_limits
<double>::infinity();
388 EXPECT_EQ(100, animation
->timeToForwardsEffectChange());
389 EXPECT_EQ(inf
, animation
->timeToReverseEffectChange());
391 player
->setCurrentTimeInternal(100);
392 EXPECT_EQ(50, animation
->timeToForwardsEffectChange());
393 EXPECT_EQ(0, animation
->timeToReverseEffectChange());
395 player
->setCurrentTimeInternal(149);
396 EXPECT_EQ(1, animation
->timeToForwardsEffectChange());
397 EXPECT_EQ(0, animation
->timeToReverseEffectChange());
399 player
->setCurrentTimeInternal(150);
401 EXPECT_EQ(inf
, animation
->timeToForwardsEffectChange());
402 EXPECT_EQ(0, animation
->timeToReverseEffectChange());
404 player
->setCurrentTimeInternal(200);
405 EXPECT_EQ(inf
, animation
->timeToForwardsEffectChange());
406 EXPECT_EQ(50, animation
->timeToReverseEffectChange());
409 TEST_F(AnimationAnimationTest
, TimeToEffectChangeWithNegativePlaybackRate
)
412 timing
.iterationDuration
= 100;
413 timing
.startDelay
= 100;
414 timing
.endDelay
= 100;
415 timing
.playbackRate
= -2;
416 timing
.fillMode
= Timing::FillModeNone
;
417 RefPtrWillBeRawPtr
<Animation
> animation
= Animation::create(0, nullptr, timing
);
418 RefPtrWillBeRawPtr
<AnimationPlayer
> player
= document
.timeline().play(animation
.get());
419 double inf
= std::numeric_limits
<double>::infinity();
421 EXPECT_EQ(100, animation
->timeToForwardsEffectChange());
422 EXPECT_EQ(inf
, animation
->timeToReverseEffectChange());
424 player
->setCurrentTimeInternal(100);
425 EXPECT_EQ(50, animation
->timeToForwardsEffectChange());
426 EXPECT_EQ(0, animation
->timeToReverseEffectChange());
428 player
->setCurrentTimeInternal(149);
429 EXPECT_EQ(1, animation
->timeToForwardsEffectChange());
430 EXPECT_EQ(0, animation
->timeToReverseEffectChange());
432 player
->setCurrentTimeInternal(150);
433 EXPECT_EQ(inf
, animation
->timeToForwardsEffectChange());
434 EXPECT_EQ(0, animation
->timeToReverseEffectChange());
436 player
->setCurrentTimeInternal(200);
437 EXPECT_EQ(inf
, animation
->timeToForwardsEffectChange());
438 EXPECT_EQ(50, animation
->timeToReverseEffectChange());
441 TEST_F(AnimationAnimationTest
, ElementDestructorClearsAnimationTarget
)
443 // This test expects incorrect behaviour should be removed once Element
444 // and Animation are moved to Oilpan. See crbug.com/362404 for context.
446 timing
.iterationDuration
= 5;
447 RefPtrWillBeRawPtr
<Animation
> animation
= Animation::create(element
.get(), nullptr, timing
);
448 EXPECT_EQ(element
.get(), animation
->target());
449 document
.timeline().play(animation
.get());
453 EXPECT_EQ(0, animation
->target());