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/Nullable.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/testing/DummyPageHolder.h"
18 #include <gtest/gtest.h>
23 class AnimationAnimationTest : public ::testing::Test {
25 AnimationAnimationTest()
26 : pageHolder(DummyPageHolder::create())
27 , document(pageHolder->document())
28 , element(document.createElement("foo", ASSERT_NO_EXCEPTION))
30 document.animationClock().resetTimeForTesting(document.timeline().zeroTime());
31 EXPECT_EQ(0, document.timeline().currentTime());
34 OwnPtr<DummyPageHolder> pageHolder;
36 RefPtrWillBePersistent<Element> element;
37 TrackExceptionState exceptionState;
40 class AnimationAnimationV8Test : public AnimationAnimationTest {
42 AnimationAnimationV8Test()
43 : m_isolate(v8::Isolate::GetCurrent())
49 static PassRefPtrWillBeRawPtr<Animation> createAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, T timingInput, ExceptionState& exceptionState)
51 return Animation::create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), timingInput);
53 static PassRefPtrWillBeRawPtr<Animation> createAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, ExceptionState& exceptionState)
55 return Animation::create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState));
58 v8::Isolate* m_isolate;
61 V8TestingScope m_scope;
64 TEST_F(AnimationAnimationV8Test, CanCreateAnAnimation)
66 Vector<Dictionary> jsKeyframes;
67 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
68 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);
70 setV8ObjectPropertyAsString(keyframe1, "width", "100px");
71 setV8ObjectPropertyAsString(keyframe1, "offset", "0");
72 setV8ObjectPropertyAsString(keyframe1, "easing", "ease-in-out");
73 setV8ObjectPropertyAsString(keyframe2, "width", "0px");
74 setV8ObjectPropertyAsString(keyframe2, "offset", "1");
75 setV8ObjectPropertyAsString(keyframe2, "easing", "cubic-bezier(1, 1, 0.3, 0.3)");
77 jsKeyframes.append(Dictionary(keyframe1, m_isolate));
78 jsKeyframes.append(Dictionary(keyframe2, m_isolate));
81 ASSERT_TRUE(DictionaryHelper::get(jsKeyframes[0], "width", value1));
82 ASSERT_EQ("100px", value1);
85 ASSERT_TRUE(DictionaryHelper::get(jsKeyframes[1], "width", value2));
86 ASSERT_EQ("0px", value2);
88 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, 0, exceptionState);
90 Element* target = animation->target();
91 EXPECT_EQ(*element.get(), *target);
93 const KeyframeVector keyframes = toKeyframeEffectModelBase(animation->effect())->getFrames();
95 EXPECT_EQ(0, keyframes[0]->offset());
96 EXPECT_EQ(1, keyframes[1]->offset());
98 const CSSValue* keyframe1Width = toStringKeyframe(keyframes[0].get())->propertyValue(CSSPropertyWidth);
99 const CSSValue* keyframe2Width = toStringKeyframe(keyframes[1].get())->propertyValue(CSSPropertyWidth);
100 ASSERT(keyframe1Width);
101 ASSERT(keyframe2Width);
103 EXPECT_EQ("100px", keyframe1Width->cssText());
104 EXPECT_EQ("0px", keyframe2Width->cssText());
106 EXPECT_EQ(*(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut)), keyframes[0]->easing());
107 EXPECT_EQ(*(CubicBezierTimingFunction::create(1, 1, 0.3, 0.3).get()), keyframes[1]->easing());
110 TEST_F(AnimationAnimationV8Test, CanSetDuration)
112 Vector<Dictionary, 0> jsKeyframes;
113 double duration = 2000;
115 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, duration, exceptionState);
117 EXPECT_EQ(duration / 1000, animation->specifiedTiming().iterationDuration);
120 TEST_F(AnimationAnimationV8Test, CanOmitSpecifiedDuration)
122 Vector<Dictionary, 0> jsKeyframes;
123 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, exceptionState);
124 EXPECT_TRUE(std::isnan(animation->specifiedTiming().iterationDuration));
127 TEST_F(AnimationAnimationV8Test, NegativeDurationIsAuto)
129 Vector<Dictionary, 0> jsKeyframes;
130 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, -2, exceptionState);
131 EXPECT_TRUE(std::isnan(animation->specifiedTiming().iterationDuration));
134 TEST_F(AnimationAnimationV8Test, MismatchedKeyframePropertyRaisesException)
136 Vector<Dictionary> jsKeyframes;
137 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
138 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);
140 setV8ObjectPropertyAsString(keyframe1, "width", "100px");
141 setV8ObjectPropertyAsString(keyframe1, "offset", "0");
143 // Height property appears only in keyframe2
144 setV8ObjectPropertyAsString(keyframe2, "height", "100px");
145 setV8ObjectPropertyAsString(keyframe2, "width", "0px");
146 setV8ObjectPropertyAsString(keyframe2, "offset", "1");
148 jsKeyframes.append(Dictionary(keyframe1, m_isolate));
149 jsKeyframes.append(Dictionary(keyframe2, m_isolate));
151 createAnimation(element.get(), jsKeyframes, 0, exceptionState);
153 EXPECT_TRUE(exceptionState.hadException());
154 EXPECT_EQ(NotSupportedError, exceptionState.code());
157 TEST_F(AnimationAnimationV8Test, MissingOffsetZeroRaisesException)
159 Vector<Dictionary> jsKeyframes;
160 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
161 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);
163 setV8ObjectPropertyAsString(keyframe1, "width", "100px");
164 setV8ObjectPropertyAsString(keyframe1, "offset", "0.1");
165 setV8ObjectPropertyAsString(keyframe2, "width", "0px");
166 setV8ObjectPropertyAsString(keyframe2, "offset", "1");
168 jsKeyframes.append(Dictionary(keyframe1, m_isolate));
169 jsKeyframes.append(Dictionary(keyframe2, m_isolate));
171 createAnimation(element.get(), jsKeyframes, 0, exceptionState);
173 EXPECT_TRUE(exceptionState.hadException());
174 EXPECT_EQ(NotSupportedError, exceptionState.code());
177 TEST_F(AnimationAnimationV8Test, MissingOffsetOneRaisesException)
179 Vector<Dictionary> jsKeyframes;
180 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
181 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);
183 setV8ObjectPropertyAsString(keyframe1, "width", "100px");
184 setV8ObjectPropertyAsString(keyframe1, "offset", "0");
185 setV8ObjectPropertyAsString(keyframe2, "width", "0px");
186 setV8ObjectPropertyAsString(keyframe2, "offset", "0.1");
188 jsKeyframes.append(Dictionary(keyframe1, m_isolate));
189 jsKeyframes.append(Dictionary(keyframe2, m_isolate));
191 createAnimation(element.get(), jsKeyframes, 0, exceptionState);
193 EXPECT_TRUE(exceptionState.hadException());
194 EXPECT_EQ(NotSupportedError, exceptionState.code());
197 TEST_F(AnimationAnimationV8Test, MissingOffsetZeroAndOneRaisesException)
199 Vector<Dictionary> jsKeyframes;
200 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
201 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);
203 setV8ObjectPropertyAsString(keyframe1, "width", "100px");
204 setV8ObjectPropertyAsString(keyframe1, "offset", "0.1");
205 setV8ObjectPropertyAsString(keyframe2, "width", "0px");
206 setV8ObjectPropertyAsString(keyframe2, "offset", "0.2");
208 jsKeyframes.append(Dictionary(keyframe1, m_isolate));
209 jsKeyframes.append(Dictionary(keyframe2, m_isolate));
211 createAnimation(element.get(), jsKeyframes, 0, exceptionState);
213 EXPECT_TRUE(exceptionState.hadException());
214 EXPECT_EQ(NotSupportedError, exceptionState.code());
217 TEST_F(AnimationAnimationV8Test, SpecifiedGetters)
219 Vector<Dictionary, 0> jsKeyframes;
221 v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate);
222 setV8ObjectPropertyAsNumber(timingInput, "delay", 2);
223 setV8ObjectPropertyAsNumber(timingInput, "endDelay", 0.5);
224 setV8ObjectPropertyAsString(timingInput, "fill", "backwards");
225 setV8ObjectPropertyAsNumber(timingInput, "iterationStart", 2);
226 setV8ObjectPropertyAsNumber(timingInput, "iterations", 10);
227 setV8ObjectPropertyAsNumber(timingInput, "playbackRate", 2);
228 setV8ObjectPropertyAsString(timingInput, "direction", "reverse");
229 setV8ObjectPropertyAsString(timingInput, "easing", "step-start");
230 Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate);
232 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState);
234 RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing();
235 EXPECT_EQ(2, specified->delay());
236 EXPECT_EQ(0.5, specified->endDelay());
237 EXPECT_EQ("backwards", specified->fill());
238 EXPECT_EQ(2, specified->iterationStart());
239 EXPECT_EQ(10, specified->iterations());
240 EXPECT_EQ(2, specified->playbackRate());
241 EXPECT_EQ("reverse", specified->direction());
242 EXPECT_EQ("step-start", specified->easing());
245 TEST_F(AnimationAnimationV8Test, SpecifiedDurationGetter)
247 Vector<Dictionary, 0> jsKeyframes;
249 v8::Handle<v8::Object> timingInputWithDuration = v8::Object::New(m_isolate);
250 setV8ObjectPropertyAsNumber(timingInputWithDuration, "duration", 2.5);
251 Dictionary timingInputDictionaryWithDuration = Dictionary(v8::Handle<v8::Value>::Cast(timingInputWithDuration), m_isolate);
253 RefPtrWillBeRawPtr<Animation> animationWithDuration = createAnimation(element.get(), jsKeyframes, timingInputDictionaryWithDuration, exceptionState);
255 RefPtrWillBeRawPtr<AnimationNodeTiming> specifiedWithDuration = animationWithDuration->timing();
256 Nullable<double> numberDuration;
257 String stringDuration;
258 specifiedWithDuration->getDuration("duration", numberDuration, stringDuration);
259 EXPECT_FALSE(numberDuration.isNull());
260 EXPECT_EQ(2.5, numberDuration.get());
261 EXPECT_TRUE(stringDuration.isNull());
264 v8::Handle<v8::Object> timingInputNoDuration = v8::Object::New(m_isolate);
265 Dictionary timingInputDictionaryNoDuration = Dictionary(v8::Handle<v8::Value>::Cast(timingInputNoDuration), m_isolate);
267 RefPtrWillBeRawPtr<Animation> animationNoDuration = createAnimation(element.get(), jsKeyframes, timingInputDictionaryNoDuration, exceptionState);
269 RefPtrWillBeRawPtr<AnimationNodeTiming> specifiedNoDuration = animationNoDuration->timing();
270 Nullable<double> numberDuration2;
271 String stringDuration2;
272 specifiedNoDuration->getDuration("duration", numberDuration2, stringDuration2);
273 EXPECT_TRUE(numberDuration2.isNull());
274 EXPECT_FALSE(stringDuration2.isNull());
275 EXPECT_EQ("auto", stringDuration2);
278 TEST_F(AnimationAnimationV8Test, SpecifiedSetters)
280 Vector<Dictionary, 0> jsKeyframes;
281 v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate);
282 Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate);
283 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState);
285 RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing();
287 EXPECT_EQ(0, specified->delay());
288 specified->setDelay(2);
289 EXPECT_EQ(2, specified->delay());
291 EXPECT_EQ(0, specified->endDelay());
292 specified->setEndDelay(0.5);
293 EXPECT_EQ(0.5, specified->endDelay());
295 EXPECT_EQ("auto", specified->fill());
296 specified->setFill("backwards");
297 EXPECT_EQ("backwards", specified->fill());
299 EXPECT_EQ(0, specified->iterationStart());
300 specified->setIterationStart(2);
301 EXPECT_EQ(2, specified->iterationStart());
303 EXPECT_EQ(1, specified->iterations());
304 specified->setIterations(10);
305 EXPECT_EQ(10, specified->iterations());
307 EXPECT_EQ(1, specified->playbackRate());
308 specified->setPlaybackRate(2);
309 EXPECT_EQ(2, specified->playbackRate());
311 EXPECT_EQ("normal", specified->direction());
312 specified->setDirection("reverse");
313 EXPECT_EQ("reverse", specified->direction());
315 EXPECT_EQ("linear", specified->easing());
316 specified->setEasing("step-start");
317 EXPECT_EQ("step-start", specified->easing());
320 TEST_F(AnimationAnimationV8Test, SetSpecifiedDuration)
322 Vector<Dictionary, 0> jsKeyframes;
323 v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate);
324 Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate);
325 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState);
327 RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing();
329 Nullable<double> numberDuration;
330 String stringDuration;
331 specified->getDuration("duration", numberDuration, stringDuration);
332 EXPECT_TRUE(numberDuration.isNull());
333 EXPECT_FALSE(stringDuration.isNull());
334 EXPECT_EQ("auto", stringDuration);
336 specified->setDuration("duration", 2.5);
337 Nullable<double> numberDuration2;
338 String stringDuration2;
339 specified->getDuration("duration", numberDuration2, stringDuration2);
340 EXPECT_FALSE(numberDuration2.isNull());
341 EXPECT_EQ(2.5, numberDuration2.get());
342 EXPECT_TRUE(stringDuration2.isNull());
345 TEST_F(AnimationAnimationTest, TimeToEffectChange)
348 timing.iterationDuration = 100;
349 timing.startDelay = 100;
350 timing.endDelay = 100;
351 timing.fillMode = Timing::FillModeNone;
352 RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing);
353 RefPtrWillBeRawPtr<AnimationPlayer> player = document.timeline().play(animation.get());
354 double inf = std::numeric_limits<double>::infinity();
356 EXPECT_EQ(100, animation->timeToForwardsEffectChange());
357 EXPECT_EQ(inf, animation->timeToReverseEffectChange());
359 player->setCurrentTimeInternal(100);
360 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
361 EXPECT_EQ(0, animation->timeToReverseEffectChange());
363 player->setCurrentTimeInternal(199);
364 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
365 EXPECT_EQ(0, animation->timeToReverseEffectChange());
367 player->setCurrentTimeInternal(200);
369 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
370 EXPECT_EQ(0, animation->timeToReverseEffectChange());
372 player->setCurrentTimeInternal(300);
373 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
374 EXPECT_EQ(100, animation->timeToReverseEffectChange());
377 TEST_F(AnimationAnimationTest, TimeToEffectChangeWithPlaybackRate)
380 timing.iterationDuration = 100;
381 timing.startDelay = 100;
382 timing.endDelay = 100;
383 timing.playbackRate = 2;
384 timing.fillMode = Timing::FillModeNone;
385 RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing);
386 RefPtrWillBeRawPtr<AnimationPlayer> player = document.timeline().play(animation.get());
387 double inf = std::numeric_limits<double>::infinity();
389 EXPECT_EQ(100, animation->timeToForwardsEffectChange());
390 EXPECT_EQ(inf, animation->timeToReverseEffectChange());
392 player->setCurrentTimeInternal(100);
393 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
394 EXPECT_EQ(0, animation->timeToReverseEffectChange());
396 player->setCurrentTimeInternal(149);
397 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
398 EXPECT_EQ(0, animation->timeToReverseEffectChange());
400 player->setCurrentTimeInternal(150);
402 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
403 EXPECT_EQ(0, animation->timeToReverseEffectChange());
405 player->setCurrentTimeInternal(200);
406 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
407 EXPECT_EQ(50, animation->timeToReverseEffectChange());
410 TEST_F(AnimationAnimationTest, TimeToEffectChangeWithNegativePlaybackRate)
413 timing.iterationDuration = 100;
414 timing.startDelay = 100;
415 timing.endDelay = 100;
416 timing.playbackRate = -2;
417 timing.fillMode = Timing::FillModeNone;
418 RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing);
419 RefPtrWillBeRawPtr<AnimationPlayer> player = document.timeline().play(animation.get());
420 double inf = std::numeric_limits<double>::infinity();
422 EXPECT_EQ(100, animation->timeToForwardsEffectChange());
423 EXPECT_EQ(inf, animation->timeToReverseEffectChange());
425 player->setCurrentTimeInternal(100);
426 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
427 EXPECT_EQ(0, animation->timeToReverseEffectChange());
429 player->setCurrentTimeInternal(149);
430 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
431 EXPECT_EQ(0, animation->timeToReverseEffectChange());
433 player->setCurrentTimeInternal(150);
434 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
435 EXPECT_EQ(0, animation->timeToReverseEffectChange());
437 player->setCurrentTimeInternal(200);
438 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
439 EXPECT_EQ(50, animation->timeToReverseEffectChange());
442 TEST_F(AnimationAnimationTest, ElementDestructorClearsAnimationTarget)
444 // This test expects incorrect behaviour should be removed once Element
445 // and Animation are moved to Oilpan. See crbug.com/362404 for context.
447 timing.iterationDuration = 5;
448 RefPtrWillBeRawPtr<Animation> animation = Animation::create(element.get(), nullptr, timing);
449 EXPECT_EQ(element.get(), animation->target());
450 document.timeline().play(animation.get());
454 EXPECT_EQ(0, animation->target());