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/AnimationHelpers.h"
12 #include "core/animation/AnimationNodeTiming.h"
13 #include "core/animation/AnimationTestHelper.h"
14 #include "core/animation/AnimationTimeline.h"
15 #include "core/animation/KeyframeEffectModel.h"
16 #include "core/animation/Timing.h"
17 #include "core/dom/Document.h"
18 #include <gtest/gtest.h>
23 class AnimationAnimationTest : public ::testing::Test {
25 AnimationAnimationTest()
26 : document(Document::create())
27 , element(document->createElement("foo", ASSERT_NO_EXCEPTION))
29 document->animationClock().resetTimeForTesting();
30 EXPECT_EQ(0, document->timeline().currentTime());
33 RefPtrWillBePersistent<Document> document;
34 RefPtrWillBePersistent<Element> element;
35 TrackExceptionState exceptionState;
38 class AnimationAnimationV8Test : public AnimationAnimationTest {
40 AnimationAnimationV8Test()
41 : m_isolate(v8::Isolate::GetCurrent())
47 static PassRefPtrWillBeRawPtr<Animation> createAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, T timingInput, ExceptionState& exceptionState)
49 return Animation::create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), timingInput);
51 static PassRefPtrWillBeRawPtr<Animation> createAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, ExceptionState& exceptionState)
53 return Animation::create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState));
56 v8::Isolate* m_isolate;
59 V8TestingScope m_scope;
62 TEST_F(AnimationAnimationV8Test, CanCreateAnAnimation)
64 Vector<Dictionary> jsKeyframes;
65 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
66 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);
68 setV8ObjectPropertyAsString(keyframe1, "width", "100px");
69 setV8ObjectPropertyAsString(keyframe1, "offset", "0");
70 setV8ObjectPropertyAsString(keyframe1, "easing", "ease-in-out");
71 setV8ObjectPropertyAsString(keyframe2, "width", "0px");
72 setV8ObjectPropertyAsString(keyframe2, "offset", "1");
73 setV8ObjectPropertyAsString(keyframe2, "easing", "cubic-bezier(1, 1, 0.3, 0.3)");
75 jsKeyframes.append(Dictionary(keyframe1, m_isolate));
76 jsKeyframes.append(Dictionary(keyframe2, m_isolate));
79 ASSERT_TRUE(DictionaryHelper::get(jsKeyframes[0], "width", value1));
80 ASSERT_EQ("100px", value1);
83 ASSERT_TRUE(DictionaryHelper::get(jsKeyframes[1], "width", value2));
84 ASSERT_EQ("0px", value2);
86 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, 0, exceptionState);
88 Element* target = animation->target();
89 EXPECT_EQ(*element.get(), *target);
91 const KeyframeVector keyframes = toKeyframeEffectModelBase(animation->effect())->getFrames();
93 EXPECT_EQ(0, keyframes[0]->offset());
94 EXPECT_EQ(1, keyframes[1]->offset());
96 const CSSValue* keyframe1Width = toStringKeyframe(keyframes[0].get())->propertyValue(CSSPropertyWidth);
97 const CSSValue* keyframe2Width = toStringKeyframe(keyframes[1].get())->propertyValue(CSSPropertyWidth);
98 ASSERT(keyframe1Width);
99 ASSERT(keyframe2Width);
101 EXPECT_EQ("100px", keyframe1Width->cssText());
102 EXPECT_EQ("0px", keyframe2Width->cssText());
104 EXPECT_EQ(*(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut)), *keyframes[0]->easing());
105 EXPECT_EQ(*(CubicBezierTimingFunction::create(1, 1, 0.3, 0.3).get()), *keyframes[1]->easing());
108 TEST_F(AnimationAnimationV8Test, CanSetDuration)
110 Vector<Dictionary, 0> jsKeyframes;
111 double duration = 2000;
113 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, duration, exceptionState);
115 EXPECT_EQ(duration / 1000, animation->specifiedTiming().iterationDuration);
118 TEST_F(AnimationAnimationV8Test, CanOmitSpecifiedDuration)
120 Vector<Dictionary, 0> jsKeyframes;
121 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, exceptionState);
122 EXPECT_TRUE(std::isnan(animation->specifiedTiming().iterationDuration));
125 TEST_F(AnimationAnimationV8Test, NegativeDurationIsAuto)
127 Vector<Dictionary, 0> jsKeyframes;
128 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, -2, exceptionState);
129 EXPECT_TRUE(std::isnan(animation->specifiedTiming().iterationDuration));
132 TEST_F(AnimationAnimationV8Test, MismatchedKeyframePropertyRaisesException)
134 Vector<Dictionary> jsKeyframes;
135 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
136 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);
138 setV8ObjectPropertyAsString(keyframe1, "width", "100px");
139 setV8ObjectPropertyAsString(keyframe1, "offset", "0");
141 // Height property appears only in keyframe2
142 setV8ObjectPropertyAsString(keyframe2, "height", "100px");
143 setV8ObjectPropertyAsString(keyframe2, "width", "0px");
144 setV8ObjectPropertyAsString(keyframe2, "offset", "1");
146 jsKeyframes.append(Dictionary(keyframe1, m_isolate));
147 jsKeyframes.append(Dictionary(keyframe2, m_isolate));
149 createAnimation(element.get(), jsKeyframes, 0, exceptionState);
151 EXPECT_TRUE(exceptionState.hadException());
152 EXPECT_EQ(NotSupportedError, exceptionState.code());
155 TEST_F(AnimationAnimationV8Test, MissingOffsetZeroRaisesException)
157 Vector<Dictionary> jsKeyframes;
158 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
159 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);
161 setV8ObjectPropertyAsString(keyframe1, "width", "100px");
162 setV8ObjectPropertyAsString(keyframe1, "offset", "0.1");
163 setV8ObjectPropertyAsString(keyframe2, "width", "0px");
164 setV8ObjectPropertyAsString(keyframe2, "offset", "1");
166 jsKeyframes.append(Dictionary(keyframe1, m_isolate));
167 jsKeyframes.append(Dictionary(keyframe2, m_isolate));
169 createAnimation(element.get(), jsKeyframes, 0, exceptionState);
171 EXPECT_TRUE(exceptionState.hadException());
172 EXPECT_EQ(NotSupportedError, exceptionState.code());
175 TEST_F(AnimationAnimationV8Test, MissingOffsetOneRaisesException)
177 Vector<Dictionary> jsKeyframes;
178 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
179 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);
181 setV8ObjectPropertyAsString(keyframe1, "width", "100px");
182 setV8ObjectPropertyAsString(keyframe1, "offset", "0");
183 setV8ObjectPropertyAsString(keyframe2, "width", "0px");
184 setV8ObjectPropertyAsString(keyframe2, "offset", "0.1");
186 jsKeyframes.append(Dictionary(keyframe1, m_isolate));
187 jsKeyframes.append(Dictionary(keyframe2, m_isolate));
189 createAnimation(element.get(), jsKeyframes, 0, exceptionState);
191 EXPECT_TRUE(exceptionState.hadException());
192 EXPECT_EQ(NotSupportedError, exceptionState.code());
195 TEST_F(AnimationAnimationV8Test, MissingOffsetZeroAndOneRaisesException)
197 Vector<Dictionary> jsKeyframes;
198 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate);
199 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate);
201 setV8ObjectPropertyAsString(keyframe1, "width", "100px");
202 setV8ObjectPropertyAsString(keyframe1, "offset", "0.1");
203 setV8ObjectPropertyAsString(keyframe2, "width", "0px");
204 setV8ObjectPropertyAsString(keyframe2, "offset", "0.2");
206 jsKeyframes.append(Dictionary(keyframe1, m_isolate));
207 jsKeyframes.append(Dictionary(keyframe2, m_isolate));
209 createAnimation(element.get(), jsKeyframes, 0, exceptionState);
211 EXPECT_TRUE(exceptionState.hadException());
212 EXPECT_EQ(NotSupportedError, exceptionState.code());
215 TEST_F(AnimationAnimationV8Test, SpecifiedGetters)
217 Vector<Dictionary, 0> jsKeyframes;
219 v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate);
220 setV8ObjectPropertyAsNumber(timingInput, "delay", 2);
221 setV8ObjectPropertyAsNumber(timingInput, "endDelay", 0.5);
222 setV8ObjectPropertyAsString(timingInput, "fill", "backwards");
223 setV8ObjectPropertyAsNumber(timingInput, "iterationStart", 2);
224 setV8ObjectPropertyAsNumber(timingInput, "iterations", 10);
225 setV8ObjectPropertyAsNumber(timingInput, "playbackRate", 2);
226 setV8ObjectPropertyAsString(timingInput, "direction", "reverse");
227 setV8ObjectPropertyAsString(timingInput, "easing", "step-start");
228 Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate);
230 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState);
232 RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing();
233 EXPECT_EQ(2, specified->delay());
234 EXPECT_EQ(0.5, specified->endDelay());
235 EXPECT_EQ("backwards", specified->fill());
236 EXPECT_EQ(2, specified->iterationStart());
237 EXPECT_EQ(10, specified->iterations());
238 EXPECT_EQ(2, specified->playbackRate());
239 EXPECT_EQ("reverse", specified->direction());
240 EXPECT_EQ("step-start", specified->easing());
243 TEST_F(AnimationAnimationV8Test, SpecifiedDurationGetter)
245 Vector<Dictionary, 0> jsKeyframes;
247 v8::Handle<v8::Object> timingInputWithDuration = v8::Object::New(m_isolate);
248 setV8ObjectPropertyAsNumber(timingInputWithDuration, "duration", 2.5);
249 Dictionary timingInputDictionaryWithDuration = Dictionary(v8::Handle<v8::Value>::Cast(timingInputWithDuration), m_isolate);
251 RefPtrWillBeRawPtr<Animation> animationWithDuration = createAnimation(element.get(), jsKeyframes, timingInputDictionaryWithDuration, exceptionState);
253 RefPtrWillBeRawPtr<AnimationNodeTiming> specifiedWithDuration = animationWithDuration->timing();
254 Nullable<double> numberDuration;
255 String stringDuration;
256 specifiedWithDuration->getDuration("duration", numberDuration, stringDuration);
257 EXPECT_FALSE(numberDuration.isNull());
258 EXPECT_EQ(2.5, numberDuration.get());
259 EXPECT_TRUE(stringDuration.isNull());
262 v8::Handle<v8::Object> timingInputNoDuration = v8::Object::New(m_isolate);
263 Dictionary timingInputDictionaryNoDuration = Dictionary(v8::Handle<v8::Value>::Cast(timingInputNoDuration), m_isolate);
265 RefPtrWillBeRawPtr<Animation> animationNoDuration = createAnimation(element.get(), jsKeyframes, timingInputDictionaryNoDuration, exceptionState);
267 RefPtrWillBeRawPtr<AnimationNodeTiming> specifiedNoDuration = animationNoDuration->timing();
268 Nullable<double> numberDuration2;
269 String stringDuration2;
270 specifiedNoDuration->getDuration("duration", numberDuration2, stringDuration2);
271 EXPECT_TRUE(numberDuration2.isNull());
272 EXPECT_FALSE(stringDuration2.isNull());
273 EXPECT_EQ("auto", stringDuration2);
276 TEST_F(AnimationAnimationV8Test, SpecifiedSetters)
278 Vector<Dictionary, 0> jsKeyframes;
279 v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate);
280 Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate);
281 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState);
283 RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing();
285 EXPECT_EQ(0, specified->delay());
286 specified->setDelay(2);
287 EXPECT_EQ(2, specified->delay());
289 EXPECT_EQ(0, specified->endDelay());
290 specified->setEndDelay(0.5);
291 EXPECT_EQ(0.5, specified->endDelay());
293 EXPECT_EQ("auto", specified->fill());
294 specified->setFill("backwards");
295 EXPECT_EQ("backwards", specified->fill());
297 EXPECT_EQ(0, specified->iterationStart());
298 specified->setIterationStart(2);
299 EXPECT_EQ(2, specified->iterationStart());
301 EXPECT_EQ(1, specified->iterations());
302 specified->setIterations(10);
303 EXPECT_EQ(10, specified->iterations());
305 EXPECT_EQ(1, specified->playbackRate());
306 specified->setPlaybackRate(2);
307 EXPECT_EQ(2, specified->playbackRate());
309 EXPECT_EQ("normal", specified->direction());
310 specified->setDirection("reverse");
311 EXPECT_EQ("reverse", specified->direction());
313 EXPECT_EQ("linear", specified->easing());
314 specified->setEasing("step-start");
315 EXPECT_EQ("step-start", specified->easing());
318 TEST_F(AnimationAnimationV8Test, SetSpecifiedDuration)
320 Vector<Dictionary, 0> jsKeyframes;
321 v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate);
322 Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate);
323 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState);
325 RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing();
327 Nullable<double> numberDuration;
328 String stringDuration;
329 specified->getDuration("duration", numberDuration, stringDuration);
330 EXPECT_TRUE(numberDuration.isNull());
331 EXPECT_FALSE(stringDuration.isNull());
332 EXPECT_EQ("auto", stringDuration);
334 specified->setDuration("duration", 2.5);
335 Nullable<double> numberDuration2;
336 String stringDuration2;
337 specified->getDuration("duration", numberDuration2, stringDuration2);
338 EXPECT_FALSE(numberDuration2.isNull());
339 EXPECT_EQ(2.5, numberDuration2.get());
340 EXPECT_TRUE(stringDuration2.isNull());
343 TEST_F(AnimationAnimationTest, TimeToEffectChange)
346 timing.iterationDuration = 100;
347 timing.startDelay = 100;
348 timing.endDelay = 100;
349 timing.fillMode = Timing::FillModeNone;
350 RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing);
351 RefPtrWillBeRawPtr<AnimationPlayer> player = document->timeline().play(animation.get());
352 double inf = std::numeric_limits<double>::infinity();
354 EXPECT_EQ(100, animation->timeToForwardsEffectChange());
355 EXPECT_EQ(inf, animation->timeToReverseEffectChange());
357 player->setCurrentTimeInternal(100);
358 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
359 EXPECT_EQ(0, animation->timeToReverseEffectChange());
361 player->setCurrentTimeInternal(199);
362 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
363 EXPECT_EQ(0, animation->timeToReverseEffectChange());
365 player->setCurrentTimeInternal(200);
367 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
368 EXPECT_EQ(0, animation->timeToReverseEffectChange());
370 player->setCurrentTimeInternal(300);
371 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
372 EXPECT_EQ(100, animation->timeToReverseEffectChange());
375 TEST_F(AnimationAnimationTest, TimeToEffectChangeWithPlaybackRate)
378 timing.iterationDuration = 100;
379 timing.startDelay = 100;
380 timing.endDelay = 100;
381 timing.playbackRate = 2;
382 timing.fillMode = Timing::FillModeNone;
383 RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing);
384 RefPtrWillBeRawPtr<AnimationPlayer> player = document->timeline().play(animation.get());
385 double inf = std::numeric_limits<double>::infinity();
387 EXPECT_EQ(100, animation->timeToForwardsEffectChange());
388 EXPECT_EQ(inf, animation->timeToReverseEffectChange());
390 player->setCurrentTimeInternal(100);
391 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
392 EXPECT_EQ(0, animation->timeToReverseEffectChange());
394 player->setCurrentTimeInternal(149);
395 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
396 EXPECT_EQ(0, animation->timeToReverseEffectChange());
398 player->setCurrentTimeInternal(150);
400 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
401 EXPECT_EQ(0, animation->timeToReverseEffectChange());
403 player->setCurrentTimeInternal(200);
404 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
405 EXPECT_EQ(50, animation->timeToReverseEffectChange());
408 TEST_F(AnimationAnimationTest, TimeToEffectChangeWithNegativePlaybackRate)
411 timing.iterationDuration = 100;
412 timing.startDelay = 100;
413 timing.endDelay = 100;
414 timing.playbackRate = -2;
415 timing.fillMode = Timing::FillModeNone;
416 RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing);
417 RefPtrWillBeRawPtr<AnimationPlayer> player = document->timeline().play(animation.get());
418 double inf = std::numeric_limits<double>::infinity();
420 EXPECT_EQ(100, animation->timeToForwardsEffectChange());
421 EXPECT_EQ(inf, animation->timeToReverseEffectChange());
423 player->setCurrentTimeInternal(100);
424 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
425 EXPECT_EQ(0, animation->timeToReverseEffectChange());
427 player->setCurrentTimeInternal(149);
428 EXPECT_EQ(0, animation->timeToForwardsEffectChange());
429 EXPECT_EQ(0, animation->timeToReverseEffectChange());
431 player->setCurrentTimeInternal(150);
432 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
433 EXPECT_EQ(0, animation->timeToReverseEffectChange());
435 player->setCurrentTimeInternal(200);
436 EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
437 EXPECT_EQ(50, animation->timeToReverseEffectChange());
440 TEST_F(AnimationAnimationTest, ElementDestructorClearsAnimationTarget)
442 // This test expects incorrect behaviour should be removed once Element
443 // and Animation are moved to Oilpan. See crbug.com/362404 for context.
445 timing.iterationDuration = 5;
446 RefPtrWillBeRawPtr<Animation> animation = Animation::create(element.get(), nullptr, timing);
447 EXPECT_EQ(element.get(), animation->target());
448 document->timeline().play(animation.get());
452 EXPECT_EQ(0, animation->target());