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