Home | History | Annotate | Download | only in animation
      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