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/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