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/v8/Dictionary.h" 9 #include "core/animation/AnimationClock.h" 10 #include "core/animation/AnimationHelpers.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 <gtest/gtest.h> 18 #include <v8.h> 19 20 namespace WebCore { 21 22 class AnimationAnimationTest : public ::testing::Test { 23 protected: 24 AnimationAnimationTest() 25 : document(Document::create()) 26 , element(document->createElement("foo", ASSERT_NO_EXCEPTION)) 27 { 28 document->animationClock().resetTimeForTesting(); 29 EXPECT_EQ(0, document->timeline().currentTime()); 30 } 31 32 RefPtrWillBePersistent<Document> document; 33 RefPtrWillBePersistent<Element> element; 34 TrackExceptionState exceptionState; 35 }; 36 37 class AnimationAnimationV8Test : public AnimationAnimationTest { 38 protected: 39 AnimationAnimationV8Test() 40 : m_isolate(v8::Isolate::GetCurrent()) 41 , m_scope(m_isolate) 42 { 43 } 44 45 template<typename T> 46 static PassRefPtrWillBeRawPtr<Animation> createAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, T timingInput, ExceptionState& exceptionState) 47 { 48 return Animation::create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState), timingInput); 49 } 50 static PassRefPtrWillBeRawPtr<Animation> createAnimation(Element* element, Vector<Dictionary> keyframeDictionaryVector, ExceptionState& exceptionState) 51 { 52 return Animation::create(element, EffectInput::convert(element, keyframeDictionaryVector, exceptionState)); 53 } 54 55 v8::Isolate* m_isolate; 56 57 private: 58 V8TestingScope m_scope; 59 }; 60 61 TEST_F(AnimationAnimationV8Test, CanCreateAnAnimation) 62 { 63 Vector<Dictionary> jsKeyframes; 64 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); 65 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); 66 67 setV8ObjectPropertyAsString(keyframe1, "width", "100px"); 68 setV8ObjectPropertyAsString(keyframe1, "offset", "0"); 69 setV8ObjectPropertyAsString(keyframe1, "easing", "ease-in-out"); 70 setV8ObjectPropertyAsString(keyframe2, "width", "0px"); 71 setV8ObjectPropertyAsString(keyframe2, "offset", "1"); 72 setV8ObjectPropertyAsString(keyframe2, "easing", "cubic-bezier(1, 1, 0.3, 0.3)"); 73 74 jsKeyframes.append(Dictionary(keyframe1, m_isolate)); 75 jsKeyframes.append(Dictionary(keyframe2, m_isolate)); 76 77 String value1; 78 ASSERT_TRUE(jsKeyframes[0].get("width", value1)); 79 ASSERT_EQ("100px", value1); 80 81 String value2; 82 ASSERT_TRUE(jsKeyframes[1].get("width", value2)); 83 ASSERT_EQ("0px", value2); 84 85 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, 0, exceptionState); 86 87 Element* target = animation->target(); 88 EXPECT_EQ(*element.get(), *target); 89 90 const KeyframeVector keyframes = toKeyframeEffectModelBase(animation->effect())->getFrames(); 91 92 EXPECT_EQ(0, keyframes[0]->offset()); 93 EXPECT_EQ(1, keyframes[1]->offset()); 94 95 const CSSValue* keyframe1Width = toStringKeyframe(keyframes[0].get())->propertyValue(CSSPropertyWidth); 96 const CSSValue* keyframe2Width = toStringKeyframe(keyframes[1].get())->propertyValue(CSSPropertyWidth); 97 ASSERT(keyframe1Width); 98 ASSERT(keyframe2Width); 99 100 EXPECT_EQ("100px", keyframe1Width->cssText()); 101 EXPECT_EQ("0px", keyframe2Width->cssText()); 102 103 EXPECT_EQ(*(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut)), *keyframes[0]->easing()); 104 EXPECT_EQ(*(CubicBezierTimingFunction::create(1, 1, 0.3, 0.3).get()), *keyframes[1]->easing()); 105 } 106 107 TEST_F(AnimationAnimationV8Test, CanSetDuration) 108 { 109 Vector<Dictionary, 0> jsKeyframes; 110 double duration = 2000; 111 112 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, duration, exceptionState); 113 114 EXPECT_EQ(duration / 1000, animation->specifiedTiming().iterationDuration); 115 } 116 117 TEST_F(AnimationAnimationV8Test, CanOmitSpecifiedDuration) 118 { 119 Vector<Dictionary, 0> jsKeyframes; 120 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, exceptionState); 121 EXPECT_TRUE(std::isnan(animation->specifiedTiming().iterationDuration)); 122 } 123 124 TEST_F(AnimationAnimationV8Test, NegativeDurationIsAuto) 125 { 126 Vector<Dictionary, 0> jsKeyframes; 127 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, -2, exceptionState); 128 EXPECT_TRUE(std::isnan(animation->specifiedTiming().iterationDuration)); 129 } 130 131 TEST_F(AnimationAnimationV8Test, MismatchedKeyframePropertyRaisesException) 132 { 133 Vector<Dictionary> jsKeyframes; 134 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); 135 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); 136 137 setV8ObjectPropertyAsString(keyframe1, "width", "100px"); 138 setV8ObjectPropertyAsString(keyframe1, "offset", "0"); 139 140 // Height property appears only in keyframe2 141 setV8ObjectPropertyAsString(keyframe2, "height", "100px"); 142 setV8ObjectPropertyAsString(keyframe2, "width", "0px"); 143 setV8ObjectPropertyAsString(keyframe2, "offset", "1"); 144 145 jsKeyframes.append(Dictionary(keyframe1, m_isolate)); 146 jsKeyframes.append(Dictionary(keyframe2, m_isolate)); 147 148 createAnimation(element.get(), jsKeyframes, 0, exceptionState); 149 150 EXPECT_TRUE(exceptionState.hadException()); 151 EXPECT_EQ(NotSupportedError, exceptionState.code()); 152 } 153 154 TEST_F(AnimationAnimationV8Test, MissingOffsetZeroRaisesException) 155 { 156 Vector<Dictionary> jsKeyframes; 157 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); 158 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); 159 160 setV8ObjectPropertyAsString(keyframe1, "width", "100px"); 161 setV8ObjectPropertyAsString(keyframe1, "offset", "0.1"); 162 setV8ObjectPropertyAsString(keyframe2, "width", "0px"); 163 setV8ObjectPropertyAsString(keyframe2, "offset", "1"); 164 165 jsKeyframes.append(Dictionary(keyframe1, m_isolate)); 166 jsKeyframes.append(Dictionary(keyframe2, m_isolate)); 167 168 createAnimation(element.get(), jsKeyframes, 0, exceptionState); 169 170 EXPECT_TRUE(exceptionState.hadException()); 171 EXPECT_EQ(NotSupportedError, exceptionState.code()); 172 } 173 174 TEST_F(AnimationAnimationV8Test, MissingOffsetOneRaisesException) 175 { 176 Vector<Dictionary> jsKeyframes; 177 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); 178 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); 179 180 setV8ObjectPropertyAsString(keyframe1, "width", "100px"); 181 setV8ObjectPropertyAsString(keyframe1, "offset", "0"); 182 setV8ObjectPropertyAsString(keyframe2, "width", "0px"); 183 setV8ObjectPropertyAsString(keyframe2, "offset", "0.1"); 184 185 jsKeyframes.append(Dictionary(keyframe1, m_isolate)); 186 jsKeyframes.append(Dictionary(keyframe2, m_isolate)); 187 188 createAnimation(element.get(), jsKeyframes, 0, exceptionState); 189 190 EXPECT_TRUE(exceptionState.hadException()); 191 EXPECT_EQ(NotSupportedError, exceptionState.code()); 192 } 193 194 TEST_F(AnimationAnimationV8Test, MissingOffsetZeroAndOneRaisesException) 195 { 196 Vector<Dictionary> jsKeyframes; 197 v8::Handle<v8::Object> keyframe1 = v8::Object::New(m_isolate); 198 v8::Handle<v8::Object> keyframe2 = v8::Object::New(m_isolate); 199 200 setV8ObjectPropertyAsString(keyframe1, "width", "100px"); 201 setV8ObjectPropertyAsString(keyframe1, "offset", "0.1"); 202 setV8ObjectPropertyAsString(keyframe2, "width", "0px"); 203 setV8ObjectPropertyAsString(keyframe2, "offset", "0.2"); 204 205 jsKeyframes.append(Dictionary(keyframe1, m_isolate)); 206 jsKeyframes.append(Dictionary(keyframe2, m_isolate)); 207 208 createAnimation(element.get(), jsKeyframes, 0, exceptionState); 209 210 EXPECT_TRUE(exceptionState.hadException()); 211 EXPECT_EQ(NotSupportedError, exceptionState.code()); 212 } 213 214 TEST_F(AnimationAnimationV8Test, SpecifiedGetters) 215 { 216 Vector<Dictionary, 0> jsKeyframes; 217 218 v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate); 219 setV8ObjectPropertyAsNumber(timingInput, "delay", 2); 220 setV8ObjectPropertyAsNumber(timingInput, "endDelay", 0.5); 221 setV8ObjectPropertyAsString(timingInput, "fill", "backwards"); 222 setV8ObjectPropertyAsNumber(timingInput, "iterationStart", 2); 223 setV8ObjectPropertyAsNumber(timingInput, "iterations", 10); 224 setV8ObjectPropertyAsNumber(timingInput, "playbackRate", 2); 225 setV8ObjectPropertyAsString(timingInput, "direction", "reverse"); 226 setV8ObjectPropertyAsString(timingInput, "easing", "step-start"); 227 Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate); 228 229 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState); 230 231 RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing(); 232 EXPECT_EQ(2, specified->delay()); 233 EXPECT_EQ(0.5, specified->endDelay()); 234 EXPECT_EQ("backwards", specified->fill()); 235 EXPECT_EQ(2, specified->iterationStart()); 236 EXPECT_EQ(10, specified->iterations()); 237 EXPECT_EQ(2, specified->playbackRate()); 238 EXPECT_EQ("reverse", specified->direction()); 239 EXPECT_EQ("step-start", specified->easing()); 240 } 241 242 TEST_F(AnimationAnimationV8Test, SpecifiedDurationGetter) 243 { 244 Vector<Dictionary, 0> jsKeyframes; 245 246 v8::Handle<v8::Object> timingInputWithDuration = v8::Object::New(m_isolate); 247 setV8ObjectPropertyAsNumber(timingInputWithDuration, "duration", 2.5); 248 Dictionary timingInputDictionaryWithDuration = Dictionary(v8::Handle<v8::Value>::Cast(timingInputWithDuration), m_isolate); 249 250 RefPtrWillBeRawPtr<Animation> animationWithDuration = createAnimation(element.get(), jsKeyframes, timingInputDictionaryWithDuration, exceptionState); 251 252 RefPtrWillBeRawPtr<AnimationNodeTiming> specifiedWithDuration = animationWithDuration->timing(); 253 bool isNumber = false; 254 double numberDuration = std::numeric_limits<double>::quiet_NaN(); 255 bool isString = false; 256 String stringDuration = ""; 257 specifiedWithDuration->getDuration("duration", isNumber, numberDuration, isString, stringDuration); 258 EXPECT_TRUE(isNumber); 259 EXPECT_EQ(2.5, numberDuration); 260 EXPECT_FALSE(isString); 261 EXPECT_EQ("", stringDuration); 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 isNumber = false; 271 numberDuration = std::numeric_limits<double>::quiet_NaN(); 272 isString = false; 273 stringDuration = ""; 274 specifiedNoDuration->getDuration("duration", isNumber, numberDuration, isString, stringDuration); 275 EXPECT_FALSE(isNumber); 276 EXPECT_TRUE(std::isnan(numberDuration)); 277 EXPECT_TRUE(isString); 278 EXPECT_EQ("auto", stringDuration); 279 } 280 281 TEST_F(AnimationAnimationV8Test, SpecifiedSetters) 282 { 283 Vector<Dictionary, 0> jsKeyframes; 284 v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate); 285 Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate); 286 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState); 287 288 RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing(); 289 290 EXPECT_EQ(0, specified->delay()); 291 specified->setDelay(2); 292 EXPECT_EQ(2, specified->delay()); 293 294 EXPECT_EQ(0, specified->endDelay()); 295 specified->setEndDelay(0.5); 296 EXPECT_EQ(0.5, specified->endDelay()); 297 298 EXPECT_EQ("auto", specified->fill()); 299 specified->setFill("backwards"); 300 EXPECT_EQ("backwards", specified->fill()); 301 302 EXPECT_EQ(0, specified->iterationStart()); 303 specified->setIterationStart(2); 304 EXPECT_EQ(2, specified->iterationStart()); 305 306 EXPECT_EQ(1, specified->iterations()); 307 specified->setIterations(10); 308 EXPECT_EQ(10, specified->iterations()); 309 310 EXPECT_EQ(1, specified->playbackRate()); 311 specified->setPlaybackRate(2); 312 EXPECT_EQ(2, specified->playbackRate()); 313 314 EXPECT_EQ("normal", specified->direction()); 315 specified->setDirection("reverse"); 316 EXPECT_EQ("reverse", specified->direction()); 317 318 EXPECT_EQ("linear", specified->easing()); 319 specified->setEasing("step-start"); 320 EXPECT_EQ("step-start", specified->easing()); 321 } 322 323 TEST_F(AnimationAnimationV8Test, SetSpecifiedDuration) 324 { 325 Vector<Dictionary, 0> jsKeyframes; 326 v8::Handle<v8::Object> timingInput = v8::Object::New(m_isolate); 327 Dictionary timingInputDictionary = Dictionary(v8::Handle<v8::Value>::Cast(timingInput), m_isolate); 328 RefPtrWillBeRawPtr<Animation> animation = createAnimation(element.get(), jsKeyframes, timingInputDictionary, exceptionState); 329 330 RefPtrWillBeRawPtr<AnimationNodeTiming> specified = animation->timing(); 331 332 bool isNumber = false; 333 double numberDuration = std::numeric_limits<double>::quiet_NaN(); 334 bool isString = false; 335 String stringDuration = ""; 336 specified->getDuration("duration", isNumber, numberDuration, isString, stringDuration); 337 EXPECT_FALSE(isNumber); 338 EXPECT_TRUE(std::isnan(numberDuration)); 339 EXPECT_TRUE(isString); 340 EXPECT_EQ("auto", stringDuration); 341 342 specified->setDuration("duration", 2.5); 343 isNumber = false; 344 numberDuration = std::numeric_limits<double>::quiet_NaN(); 345 isString = false; 346 stringDuration = ""; 347 specified->getDuration("duration", isNumber, numberDuration, isString, stringDuration); 348 EXPECT_TRUE(isNumber); 349 EXPECT_EQ(2.5, numberDuration); 350 EXPECT_FALSE(isString); 351 EXPECT_EQ("", stringDuration); 352 } 353 354 TEST_F(AnimationAnimationTest, TimeToEffectChange) 355 { 356 Timing timing; 357 timing.iterationDuration = 100; 358 timing.startDelay = 100; 359 timing.endDelay = 100; 360 timing.fillMode = Timing::FillModeNone; 361 RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing); 362 RefPtrWillBeRawPtr<AnimationPlayer> player = document->timeline().play(animation.get()); 363 double inf = std::numeric_limits<double>::infinity(); 364 365 EXPECT_EQ(100, animation->timeToForwardsEffectChange()); 366 EXPECT_EQ(inf, animation->timeToReverseEffectChange()); 367 368 player->setCurrentTimeInternal(100); 369 EXPECT_EQ(0, animation->timeToForwardsEffectChange()); 370 EXPECT_EQ(0, animation->timeToReverseEffectChange()); 371 372 player->setCurrentTimeInternal(199); 373 EXPECT_EQ(0, animation->timeToForwardsEffectChange()); 374 EXPECT_EQ(0, animation->timeToReverseEffectChange()); 375 376 player->setCurrentTimeInternal(200); 377 // End-exclusive. 378 EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); 379 EXPECT_EQ(0, animation->timeToReverseEffectChange()); 380 381 player->setCurrentTimeInternal(300); 382 EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); 383 EXPECT_EQ(100, animation->timeToReverseEffectChange()); 384 } 385 386 TEST_F(AnimationAnimationTest, TimeToEffectChangeWithPlaybackRate) 387 { 388 Timing timing; 389 timing.iterationDuration = 100; 390 timing.startDelay = 100; 391 timing.endDelay = 100; 392 timing.playbackRate = 2; 393 timing.fillMode = Timing::FillModeNone; 394 RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing); 395 RefPtrWillBeRawPtr<AnimationPlayer> player = document->timeline().play(animation.get()); 396 double inf = std::numeric_limits<double>::infinity(); 397 398 EXPECT_EQ(100, animation->timeToForwardsEffectChange()); 399 EXPECT_EQ(inf, animation->timeToReverseEffectChange()); 400 401 player->setCurrentTimeInternal(100); 402 EXPECT_EQ(0, animation->timeToForwardsEffectChange()); 403 EXPECT_EQ(0, animation->timeToReverseEffectChange()); 404 405 player->setCurrentTimeInternal(149); 406 EXPECT_EQ(0, animation->timeToForwardsEffectChange()); 407 EXPECT_EQ(0, animation->timeToReverseEffectChange()); 408 409 player->setCurrentTimeInternal(150); 410 // End-exclusive. 411 EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); 412 EXPECT_EQ(0, animation->timeToReverseEffectChange()); 413 414 player->setCurrentTimeInternal(200); 415 EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); 416 EXPECT_EQ(50, animation->timeToReverseEffectChange()); 417 } 418 419 TEST_F(AnimationAnimationTest, TimeToEffectChangeWithNegativePlaybackRate) 420 { 421 Timing timing; 422 timing.iterationDuration = 100; 423 timing.startDelay = 100; 424 timing.endDelay = 100; 425 timing.playbackRate = -2; 426 timing.fillMode = Timing::FillModeNone; 427 RefPtrWillBeRawPtr<Animation> animation = Animation::create(0, nullptr, timing); 428 RefPtrWillBeRawPtr<AnimationPlayer> player = document->timeline().play(animation.get()); 429 double inf = std::numeric_limits<double>::infinity(); 430 431 EXPECT_EQ(100, animation->timeToForwardsEffectChange()); 432 EXPECT_EQ(inf, animation->timeToReverseEffectChange()); 433 434 player->setCurrentTimeInternal(100); 435 EXPECT_EQ(0, animation->timeToForwardsEffectChange()); 436 EXPECT_EQ(0, animation->timeToReverseEffectChange()); 437 438 player->setCurrentTimeInternal(149); 439 EXPECT_EQ(0, animation->timeToForwardsEffectChange()); 440 EXPECT_EQ(0, animation->timeToReverseEffectChange()); 441 442 player->setCurrentTimeInternal(150); 443 EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); 444 EXPECT_EQ(0, animation->timeToReverseEffectChange()); 445 446 player->setCurrentTimeInternal(200); 447 EXPECT_EQ(inf, animation->timeToForwardsEffectChange()); 448 EXPECT_EQ(50, animation->timeToReverseEffectChange()); 449 } 450 451 TEST_F(AnimationAnimationTest, ElementDestructorClearsAnimationTarget) 452 { 453 // This test expects incorrect behaviour should be removed once Element 454 // and Animation are moved to Oilpan. See crbug.com/362404 for context. 455 Timing timing; 456 timing.iterationDuration = 5; 457 RefPtrWillBeRawPtr<Animation> animation = Animation::create(element.get(), nullptr, timing); 458 EXPECT_EQ(element.get(), animation->target()); 459 document->timeline().play(animation.get()); 460 document.clear(); 461 element.clear(); 462 #if !ENABLE(OILPAN) 463 EXPECT_EQ(0, animation->target()); 464 #endif 465 } 466 467 } // namespace WebCore 468