/* * Copyright (c) 2013, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "third_party/blink/renderer/core/animation/document_timeline.h" #include #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/animation/animation_clock.h" #include "third_party/blink/renderer/core/animation/animation_effect.h" #include "third_party/blink/renderer/core/animation/keyframe_effect.h" #include "third_party/blink/renderer/core/animation/keyframe_effect_model.h" #include "third_party/blink/renderer/core/animation/pending_animations.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/qualified_name.h" #include "third_party/blink/renderer/core/testing/page_test_base.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" namespace blink { class MockPlatformTiming : public DocumentTimeline::PlatformTiming { public: MOCK_METHOD1(WakeAfter, void(double)); MOCK_METHOD0(ServiceOnNextFrame, void()); void Trace(blink::Visitor* visitor) override { DocumentTimeline::PlatformTiming::Trace(visitor); } }; class AnimationDocumentTimelineTest : public PageTestBase { protected: void SetUp() override { PageTestBase::SetUp(IntSize()); document = &GetDocument(); GetAnimationClock().ResetTimeForTesting(); element = Element::Create(QualifiedName::Null(), document.Get()); platform_timing = MakeGarbageCollected(); timeline = DocumentTimeline::Create(document.Get(), TimeDelta(), platform_timing); timeline->ResetForTesting(); ASSERT_EQ(0, timeline->CurrentTimeInternal()); } void TearDown() override { document.Release(); element.Release(); timeline.Release(); ThreadState::Current()->CollectAllGarbage(); } void UpdateClockAndService(double time) { GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(time)); GetPendingAnimations().Update(base::Optional(), false); timeline->ServiceAnimations(kTimingUpdateForAnimationFrame); timeline->ScheduleNextService(); } KeyframeEffectModelBase* CreateEmptyEffectModel() { return StringKeyframeEffectModel::Create(StringKeyframeVector()); } Persistent document; Persistent element; Persistent timeline; Timing timing; Persistent platform_timing; void Wake() { timeline->Wake(); } double MinimumDelay() { return DocumentTimeline::kMinimumDelay; } }; TEST_F(AnimationDocumentTimelineTest, EmptyKeyframeAnimation) { StringKeyframeEffectModel* effect = StringKeyframeEffectModel::Create(StringKeyframeVector()); KeyframeEffect* keyframe_effect = KeyframeEffect::Create(element.Get(), effect, timing); timeline->Play(keyframe_effect); UpdateClockAndService(0); EXPECT_FLOAT_EQ(0, timeline->CurrentTimeInternal()); EXPECT_FALSE(keyframe_effect->IsInEffect()); UpdateClockAndService(100); EXPECT_FLOAT_EQ(100, timeline->CurrentTimeInternal()); } TEST_F(AnimationDocumentTimelineTest, EmptyForwardsKeyframeAnimation) { StringKeyframeEffectModel* effect = StringKeyframeEffectModel::Create(StringKeyframeVector()); timing.fill_mode = Timing::FillMode::FORWARDS; KeyframeEffect* keyframe_effect = KeyframeEffect::Create(element.Get(), effect, timing); timeline->Play(keyframe_effect); UpdateClockAndService(0); EXPECT_FLOAT_EQ(0, timeline->CurrentTimeInternal()); EXPECT_TRUE(keyframe_effect->IsInEffect()); UpdateClockAndService(100); EXPECT_FLOAT_EQ(100, timeline->CurrentTimeInternal()); } TEST_F(AnimationDocumentTimelineTest, ZeroTime) { bool is_null; GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(100)); EXPECT_EQ(100, timeline->CurrentTimeInternal()); EXPECT_EQ(100, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(200)); EXPECT_EQ(200, timeline->CurrentTimeInternal()); EXPECT_EQ(200, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); } // EffectiveTime is identical to CurrentTimeInternal except that it returns 0 // when the timeline is inactive. TEST_F(AnimationDocumentTimelineTest, EffectiveTime) { GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(200)); EXPECT_EQ(200, timeline->EffectiveTime()); EXPECT_EQ(200, timeline->CurrentTimeInternal()); bool is_null; EXPECT_EQ(200, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); Document* document_without_frame = Document::CreateForTest(); DocumentTimeline* inactive_timeline = DocumentTimeline::Create( document_without_frame, TimeDelta(), platform_timing); EXPECT_EQ(0, inactive_timeline->EffectiveTime()); is_null = false; inactive_timeline->CurrentTimeInternal(is_null); EXPECT_TRUE(is_null); } TEST_F(AnimationDocumentTimelineTest, PlaybackRateNormal) { TimeTicks zero_time = timeline->ZeroTime(); bool is_null; timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(100)); EXPECT_EQ(zero_time, timeline->ZeroTime()); EXPECT_EQ(100, timeline->CurrentTimeInternal()); EXPECT_EQ(100, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(200)); EXPECT_EQ(zero_time, timeline->ZeroTime()); EXPECT_EQ(200, timeline->CurrentTimeInternal()); EXPECT_EQ(200, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); } TEST_F(AnimationDocumentTimelineTest, PlaybackRateNormalWithOriginTime) { TimeDelta origin_time = TimeDelta::FromSeconds(-1000); timeline = DocumentTimeline::Create(document.Get(), origin_time, platform_timing); timeline->ResetForTesting(); bool is_null; EXPECT_EQ(1.0, timeline->PlaybackRate()); EXPECT_EQ(TimeTicks() + origin_time, timeline->ZeroTime()); EXPECT_EQ(1000, timeline->CurrentTimeInternal()); EXPECT_EQ(1000, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(100)); EXPECT_EQ(TimeTicks() + origin_time, timeline->ZeroTime()); EXPECT_EQ(1100, timeline->CurrentTimeInternal()); EXPECT_EQ(1100, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(200)); EXPECT_EQ(TimeTicks() + origin_time, timeline->ZeroTime()); EXPECT_EQ(1200, timeline->CurrentTimeInternal()); EXPECT_EQ(1200, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); } TEST_F(AnimationDocumentTimelineTest, PlaybackRatePause) { bool is_null; GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(100)); EXPECT_EQ(TimeTicks(), timeline->ZeroTime()); EXPECT_EQ(100, timeline->CurrentTimeInternal()); EXPECT_EQ(100, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); timeline->SetPlaybackRate(0.0); EXPECT_EQ(0.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(200)); EXPECT_EQ(TimeTicksFromSeconds(100), timeline->ZeroTime()); EXPECT_EQ(100, timeline->CurrentTimeInternal()); EXPECT_EQ(100, timeline->CurrentTimeInternal(is_null)); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(400)); EXPECT_EQ(TimeTicksFromSeconds(100), timeline->ZeroTime()); EXPECT_EQ(300, timeline->CurrentTimeInternal()); EXPECT_EQ(300, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); } TEST_F(AnimationDocumentTimelineTest, PlaybackRatePauseWithOriginTime) { bool is_null; TimeDelta origin_time = TimeDelta::FromSeconds(-1000); timeline = DocumentTimeline::Create(document.Get(), origin_time, platform_timing); timeline->ResetForTesting(); EXPECT_EQ(TimeTicks() + origin_time, timeline->ZeroTime()); EXPECT_EQ(1000, timeline->CurrentTimeInternal()); EXPECT_EQ(1000, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(100)); EXPECT_EQ(TimeTicks() + origin_time, timeline->ZeroTime()); EXPECT_EQ(1100, timeline->CurrentTimeInternal()); EXPECT_EQ(1100, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); timeline->SetPlaybackRate(0.0); EXPECT_EQ(0.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(200)); EXPECT_EQ(TimeTicksFromSeconds(1100), timeline->ZeroTime()); EXPECT_EQ(1100, timeline->CurrentTimeInternal()); EXPECT_EQ(1100, timeline->CurrentTimeInternal(is_null)); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); EXPECT_EQ(TimeTicksFromSeconds(-900), timeline->ZeroTime()); EXPECT_EQ(1100, timeline->CurrentTimeInternal()); EXPECT_EQ(1100, timeline->CurrentTimeInternal(is_null)); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(400)); EXPECT_EQ(TimeTicksFromSeconds(-900), timeline->ZeroTime()); EXPECT_EQ(1300, timeline->CurrentTimeInternal()); EXPECT_EQ(1300, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); } TEST_F(AnimationDocumentTimelineTest, PlaybackRateSlow) { bool is_null; GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(100)); EXPECT_EQ(TimeTicks(), timeline->ZeroTime()); EXPECT_EQ(100, timeline->CurrentTimeInternal()); EXPECT_EQ(100, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); timeline->SetPlaybackRate(0.5); EXPECT_EQ(0.5, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(300)); EXPECT_EQ(TimeTicksFromSeconds(-100), timeline->ZeroTime()); EXPECT_EQ(200, timeline->CurrentTimeInternal()); EXPECT_EQ(200, timeline->CurrentTimeInternal(is_null)); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(400)); EXPECT_EQ(TimeTicksFromSeconds(100), timeline->ZeroTime()); EXPECT_EQ(300, timeline->CurrentTimeInternal()); EXPECT_EQ(300, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); } TEST_F(AnimationDocumentTimelineTest, PlaybackRateFast) { bool is_null; GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(100)); EXPECT_EQ(TimeTicks(), timeline->ZeroTime()); EXPECT_EQ(100, timeline->CurrentTimeInternal()); EXPECT_EQ(100, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); timeline->SetPlaybackRate(2.0); EXPECT_EQ(2.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(300)); EXPECT_EQ(TimeTicksFromSeconds(50), timeline->ZeroTime()); EXPECT_EQ(500, timeline->CurrentTimeInternal()); EXPECT_EQ(500, timeline->CurrentTimeInternal(is_null)); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(400)); EXPECT_EQ(TimeTicksFromSeconds(-200), timeline->ZeroTime()); EXPECT_EQ(600, timeline->CurrentTimeInternal()); EXPECT_EQ(600, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); } TEST_F(AnimationDocumentTimelineTest, PlaybackRateFastWithOriginTime) { bool is_null; timeline = DocumentTimeline::Create( document.Get(), TimeDelta::FromSeconds(-1000), platform_timing); timeline->ResetForTesting(); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(100)); EXPECT_EQ(TimeTicksFromSeconds(-1000), timeline->ZeroTime()); EXPECT_EQ(1100, timeline->CurrentTimeInternal()); EXPECT_EQ(1100, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); timeline->SetPlaybackRate(2.0); EXPECT_EQ(2.0, timeline->PlaybackRate()); EXPECT_EQ(TimeTicksFromSeconds(-450), timeline->ZeroTime()); EXPECT_EQ(1100, timeline->CurrentTimeInternal()); EXPECT_EQ(1100, timeline->CurrentTimeInternal(is_null)); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(300)); EXPECT_EQ(TimeTicksFromSeconds(-450), timeline->ZeroTime()); EXPECT_EQ(1500, timeline->CurrentTimeInternal()); EXPECT_EQ(1500, timeline->CurrentTimeInternal(is_null)); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); EXPECT_EQ(TimeTicksFromSeconds(-1200), timeline->ZeroTime()); EXPECT_EQ(1500, timeline->CurrentTimeInternal()); EXPECT_EQ(1500, timeline->CurrentTimeInternal(is_null)); GetAnimationClock().UpdateTime(base::TimeTicks() + base::TimeDelta::FromSecondsD(400)); EXPECT_EQ(TimeTicksFromSeconds(-1200), timeline->ZeroTime()); EXPECT_EQ(1600, timeline->CurrentTimeInternal()); EXPECT_EQ(1600, timeline->CurrentTimeInternal(is_null)); EXPECT_FALSE(is_null); } TEST_F(AnimationDocumentTimelineTest, PauseForTesting) { float seek_time = 1; timing.fill_mode = Timing::FillMode::FORWARDS; KeyframeEffect* anim1 = KeyframeEffect::Create(element.Get(), CreateEmptyEffectModel(), timing); KeyframeEffect* anim2 = KeyframeEffect::Create(element.Get(), CreateEmptyEffectModel(), timing); Animation* animation1 = timeline->Play(anim1); Animation* animation2 = timeline->Play(anim2); timeline->PauseAnimationsForTesting(seek_time); EXPECT_FLOAT_EQ(seek_time, animation1->CurrentTimeInternal()); EXPECT_FLOAT_EQ(seek_time, animation2->CurrentTimeInternal()); } TEST_F(AnimationDocumentTimelineTest, DelayBeforeAnimationStart) { timing.iteration_duration = AnimationTimeDelta::FromSecondsD(2); timing.start_delay = 5; KeyframeEffect* keyframe_effect = KeyframeEffect::Create(element.Get(), CreateEmptyEffectModel(), timing); timeline->Play(keyframe_effect); // TODO: Put the animation startTime in the future when we add the capability // to change animation startTime EXPECT_CALL(*platform_timing, WakeAfter(timing.start_delay - MinimumDelay())); UpdateClockAndService(0); EXPECT_CALL(*platform_timing, WakeAfter(timing.start_delay - MinimumDelay() - 1.5)); UpdateClockAndService(1.5); EXPECT_CALL(*platform_timing, ServiceOnNextFrame()); Wake(); EXPECT_CALL(*platform_timing, ServiceOnNextFrame()); UpdateClockAndService(4.98); } TEST_F(AnimationDocumentTimelineTest, UseAnimationAfterTimelineDeref) { Animation* animation = timeline->Play(nullptr); timeline.Clear(); // Test passes if this does not crash. animation->setStartTime(0, false); } TEST_F(AnimationDocumentTimelineTest, PlayAfterDocumentDeref) { timing.iteration_duration = AnimationTimeDelta::FromSecondsD(2); timing.start_delay = 5; timeline = &document->Timeline(); document = nullptr; KeyframeEffect* keyframe_effect = KeyframeEffect::Create(nullptr, CreateEmptyEffectModel(), timing); // Test passes if this does not crash. timeline->Play(keyframe_effect); } } // namespace blink