blob: 2d8daa7ce8aad57a3497ae46797dac7c69a39682 [file] [log] [blame]
/*
* 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 <memory>
#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<MockPlatformTiming>();
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<CompositorElementIdSet>(),
false);
timeline->ServiceAnimations(kTimingUpdateForAnimationFrame);
timeline->ScheduleNextService();
}
KeyframeEffectModelBase* CreateEmptyEffectModel() {
return StringKeyframeEffectModel::Create(StringKeyframeVector());
}
Persistent<Document> document;
Persistent<Element> element;
Persistent<DocumentTimeline> timeline;
Timing timing;
Persistent<MockPlatformTiming> 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