Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / animation / AnimationTest.cpp
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 "config.h"
6 #include "core/animation/Animation.h"
7
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>
19 #include <v8.h>
20
21 namespace blink {
22
23 class AnimationAnimationTest : public ::testing::Test {
24 protected:
25     AnimationAnimationTest()
26         : document(Document::create())
27         , element(document->createElement("foo", ASSERT_NO_EXCEPTION))
28     {
29         document->animationClock().resetTimeForTesting();
30         EXPECT_EQ(0, document->timeline().currentTime());
31     }
32
33     RefPtrWillBePersistent<Document> document;
34     RefPtrWillBePersistent<Element> element;
35     TrackExceptionState exceptionState;
36 };
37
38 class AnimationAnimationV8Test : public AnimationAnimationTest {
39 protected:
40     AnimationAnimationV8Test()
41         : m_isolate(v8::Isolate::GetCurrent())
42         , m_scope(m_isolate)
43     {
44     }
45
46     template<typename T>
47     static PassRefPtrWillBeRawPtr<Animation> createAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, T timingInput, ExceptionState& exceptionState)
48     {
49         return Animation::create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), timingInput);
50     }
51     static PassRefPtrWillBeRawPtr<Animation> createAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, ExceptionState& exceptionState)
52     {
53         return Animation::create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState));
54     }
55
56     v8::Isolate* m_isolate;
57
58 private:
59     V8TestingScope m_scope;
60 };
61
62 TEST_F(AnimationAnimationV8Test, CanCreateAnAnimation)
63 {
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);
67
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)");
74
75     jsKeyframes.append(Dictionary(keyframe1, m_isolate));
76     jsKeyframes.append(Dictionary(keyframe2, m_isolate));
77
78     String value1;
79     ASSERT_TRUE(DictionaryHelper::get(jsKeyframes[0], "width", value1));
80     ASSERT_EQ("100px", value1);
81
82     String value2;
83     ASSERT_TRUE(DictionaryHelper::get(jsKeyframes[1], "width", value2));
84     ASSERT_EQ("0px", value2);
85
86     RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, 0, exceptionState);
87
88     Element* target = animation->target();
89     EXPECT_EQ(*element.get(), *target);
90
91     const KeyframeVector keyframes = toKeyframeEffectModelBase(animation->effect())->getFrames();
92
93     EXPECT_EQ(0, keyframes[0]->offset());
94     EXPECT_EQ(1, keyframes[1]->offset());
95
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);
100
101     EXPECT_EQ("100px", keyframe1Width->cssText());
102     EXPECT_EQ("0px", keyframe2Width->cssText());
103
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());
106 }
107
108 TEST_F(AnimationAnimationV8Test, CanSetDuration)
109 {
110     Vector<Dictionary, 0> jsKeyframes;
111     double duration = 2000;
112
113     RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, duration, exceptionState);
114
115     EXPECT_EQ(duration / 1000, animation->specifiedTiming().iterationDuration);
116 }
117
118 TEST_F(AnimationAnimationV8Test, CanOmitSpecifiedDuration)
119 {
120     Vector<Dictionary, 0> jsKeyframes;
121     RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, exceptionState);
122     EXPECT_TRUE(std::isnan(animation->specifiedTiming().iterationDuration));
123 }
124
125 TEST_F(AnimationAnimationV8Test, NegativeDurationIsAuto)
126 {
127     Vector<Dictionary, 0> jsKeyframes;
128     RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, -2, exceptionState);
129     EXPECT_TRUE(std::isnan(animation->specifiedTiming().iterationDuration));
130 }
131
132 TEST_F(AnimationAnimationV8Test, MismatchedKeyframePropertyRaisesException)
133 {
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);
137
138     setV8ObjectPropertyAsString(keyframe1, "width", "100px");
139     setV8ObjectPropertyAsString(keyframe1, "offset", "0");
140
141     // Height property appears only in keyframe2
142     setV8ObjectPropertyAsString(keyframe2, "height", "100px");
143     setV8ObjectPropertyAsString(keyframe2, "width", "0px");
144     setV8ObjectPropertyAsString(keyframe2, "offset", "1");
145
146     jsKeyframes.append(Dictionary(keyframe1, m_isolate));
147     jsKeyframes.append(Dictionary(keyframe2, m_isolate));
148
149     createAnimation(element.get(), jsKeyframes, 0, exceptionState);
150
151     EXPECT_TRUE(exceptionState.hadException());
152     EXPECT_EQ(NotSupportedError, exceptionState.code());
153 }
154
155 TEST_F(AnimationAnimationV8Test, MissingOffsetZeroRaisesException)
156 {
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);
160
161     setV8ObjectPropertyAsString(keyframe1, "width", "100px");
162     setV8ObjectPropertyAsString(keyframe1, "offset", "0.1");
163     setV8ObjectPropertyAsString(keyframe2, "width", "0px");
164     setV8ObjectPropertyAsString(keyframe2, "offset", "1");
165
166     jsKeyframes.append(Dictionary(keyframe1, m_isolate));
167     jsKeyframes.append(Dictionary(keyframe2, m_isolate));
168
169     createAnimation(element.get(), jsKeyframes, 0, exceptionState);
170
171     EXPECT_TRUE(exceptionState.hadException());
172     EXPECT_EQ(NotSupportedError, exceptionState.code());
173 }
174
175 TEST_F(AnimationAnimationV8Test, MissingOffsetOneRaisesException)
176 {
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);
180
181     setV8ObjectPropertyAsString(keyframe1, "width", "100px");
182     setV8ObjectPropertyAsString(keyframe1, "offset", "0");
183     setV8ObjectPropertyAsString(keyframe2, "width", "0px");
184     setV8ObjectPropertyAsString(keyframe2, "offset", "0.1");
185
186     jsKeyframes.append(Dictionary(keyframe1, m_isolate));
187     jsKeyframes.append(Dictionary(keyframe2, m_isolate));
188
189     createAnimation(element.get(), jsKeyframes, 0, exceptionState);
190
191     EXPECT_TRUE(exceptionState.hadException());
192     EXPECT_EQ(NotSupportedError, exceptionState.code());
193 }
194
195 TEST_F(AnimationAnimationV8Test, MissingOffsetZeroAndOneRaisesException)
196 {
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);
200
201     setV8ObjectPropertyAsString(keyframe1, "width", "100px");
202     setV8ObjectPropertyAsString(keyframe1, "offset", "0.1");
203     setV8ObjectPropertyAsString(keyframe2, "width", "0px");
204     setV8ObjectPropertyAsString(keyframe2, "offset", "0.2");
205
206     jsKeyframes.append(Dictionary(keyframe1, m_isolate));
207     jsKeyframes.append(Dictionary(keyframe2, m_isolate));
208
209     createAnimation(element.get(), jsKeyframes, 0, exceptionState);
210
211     EXPECT_TRUE(exceptionState.hadException());
212     EXPECT_EQ(NotSupportedError, exceptionState.code());
213 }
214
215 TEST_F(AnimationAnimationV8Test, SpecifiedGetters)
216 {
217     Vector<Dictionary, 0> jsKeyframes;
218
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);
229
230     RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState);
231
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());
241 }
242
243 TEST_F(AnimationAnimationV8Test, SpecifiedDurationGetter)
244 {
245     Vector<Dictionary, 0> jsKeyframes;
246
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);
250
251     RefPtrWillBeRawPtr<Animation> animationWithDuration = createAnimation(element.get(), jsKeyframes, timingInputDictionaryWithDuration, exceptionState);
252
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());
260
261
262     v8::Handle<v8::Object> timingInputNoDuration = v8::Object::New(m_isolate);
263     Dictionary timingInputDictionaryNoDuration = Dictionary(v8::Handle<v8::Value>::Cast(timingInputNoDuration), m_isolate);
264
265     RefPtrWillBeRawPtr<Animation> animationNoDuration = createAnimation(element.get(), jsKeyframes, timingInputDictionaryNoDuration, exceptionState);
266
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);
274 }
275
276 TEST_F(AnimationAnimationV8Test, SpecifiedSetters)
277 {
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);
282
283     RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing();
284
285     EXPECT_EQ(0, specified->delay());
286     specified->setDelay(2);
287     EXPECT_EQ(2, specified->delay());
288
289     EXPECT_EQ(0, specified->endDelay());
290     specified->setEndDelay(0.5);
291     EXPECT_EQ(0.5, specified->endDelay());
292
293     EXPECT_EQ("auto", specified->fill());
294     specified->setFill("backwards");
295     EXPECT_EQ("backwards", specified->fill());
296
297     EXPECT_EQ(0, specified->iterationStart());
298     specified->setIterationStart(2);
299     EXPECT_EQ(2, specified->iterationStart());
300
301     EXPECT_EQ(1, specified->iterations());
302     specified->setIterations(10);
303     EXPECT_EQ(10, specified->iterations());
304
305     EXPECT_EQ(1, specified->playbackRate());
306     specified->setPlaybackRate(2);
307     EXPECT_EQ(2, specified->playbackRate());
308
309     EXPECT_EQ("normal", specified->direction());
310     specified->setDirection("reverse");
311     EXPECT_EQ("reverse", specified->direction());
312
313     EXPECT_EQ("linear", specified->easing());
314     specified->setEasing("step-start");
315     EXPECT_EQ("step-start", specified->easing());
316 }
317
318 TEST_F(AnimationAnimationV8Test, SetSpecifiedDuration)
319 {
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);
324
325     RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing();
326
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);
333
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());
341 }
342
343 TEST_F(AnimationAnimationTest, TimeToEffectChange)
344 {
345     Timing timing;
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();
353
354     EXPECT_EQ(100, animation->timeToForwardsEffectChange());
355     EXPECT_EQ(inf, animation->timeToReverseEffectChange());
356
357     player->setCurrentTimeInternal(100);
358     EXPECT_EQ(0, animation->timeToForwardsEffectChange());
359     EXPECT_EQ(0, animation->timeToReverseEffectChange());
360
361     player->setCurrentTimeInternal(199);
362     EXPECT_EQ(0, animation->timeToForwardsEffectChange());
363     EXPECT_EQ(0, animation->timeToReverseEffectChange());
364
365     player->setCurrentTimeInternal(200);
366     // End-exclusive.
367     EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
368     EXPECT_EQ(0, animation->timeToReverseEffectChange());
369
370     player->setCurrentTimeInternal(300);
371     EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
372     EXPECT_EQ(100, animation->timeToReverseEffectChange());
373 }
374
375 TEST_F(AnimationAnimationTest, TimeToEffectChangeWithPlaybackRate)
376 {
377     Timing timing;
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();
386
387     EXPECT_EQ(100, animation->timeToForwardsEffectChange());
388     EXPECT_EQ(inf, animation->timeToReverseEffectChange());
389
390     player->setCurrentTimeInternal(100);
391     EXPECT_EQ(0, animation->timeToForwardsEffectChange());
392     EXPECT_EQ(0, animation->timeToReverseEffectChange());
393
394     player->setCurrentTimeInternal(149);
395     EXPECT_EQ(0, animation->timeToForwardsEffectChange());
396     EXPECT_EQ(0, animation->timeToReverseEffectChange());
397
398     player->setCurrentTimeInternal(150);
399     // End-exclusive.
400     EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
401     EXPECT_EQ(0, animation->timeToReverseEffectChange());
402
403     player->setCurrentTimeInternal(200);
404     EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
405     EXPECT_EQ(50, animation->timeToReverseEffectChange());
406 }
407
408 TEST_F(AnimationAnimationTest, TimeToEffectChangeWithNegativePlaybackRate)
409 {
410     Timing timing;
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();
419
420     EXPECT_EQ(100, animation->timeToForwardsEffectChange());
421     EXPECT_EQ(inf, animation->timeToReverseEffectChange());
422
423     player->setCurrentTimeInternal(100);
424     EXPECT_EQ(0, animation->timeToForwardsEffectChange());
425     EXPECT_EQ(0, animation->timeToReverseEffectChange());
426
427     player->setCurrentTimeInternal(149);
428     EXPECT_EQ(0, animation->timeToForwardsEffectChange());
429     EXPECT_EQ(0, animation->timeToReverseEffectChange());
430
431     player->setCurrentTimeInternal(150);
432     EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
433     EXPECT_EQ(0, animation->timeToReverseEffectChange());
434
435     player->setCurrentTimeInternal(200);
436     EXPECT_EQ(inf, animation->timeToForwardsEffectChange());
437     EXPECT_EQ(50, animation->timeToReverseEffectChange());
438 }
439
440 TEST_F(AnimationAnimationTest, ElementDestructorClearsAnimationTarget)
441 {
442     // This test expects incorrect behaviour should be removed once Element
443     // and Animation are moved to Oilpan. See crbug.com/362404 for context.
444     Timing timing;
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());
449     document.clear();
450     element.clear();
451 #if !ENABLE(OILPAN)
452     EXPECT_EQ(0, animation->target());
453 #endif
454 }
455
456 } // namespace blink