blob: c0a9e879bd5cb06f867a6ec3273db51964236e2e [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/animation.h"
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/animation/animatable/animatable_double.h"
#include "third_party/blink/renderer/core/animation/animation_clock.h"
#include "third_party/blink/renderer/core/animation/css_number_interpolation_type.h"
#include "third_party/blink/renderer/core/animation/document_timeline.h"
#include "third_party/blink/renderer/core/animation/element_animations.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/dom_node_ids.h"
#include "third_party/blink/renderer/core/dom/qualified_name.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
namespace blink {
class AnimationAnimationTest : public RenderingTest {
public:
AnimationAnimationTest()
: RenderingTest(SingleChildLocalFrameClient::Create()) {}
void SetUp() override {
RenderingTest::SetUp();
SetUpWithoutStartingTimeline();
StartTimeline();
}
void SetUpWithoutStartingTimeline() {
page_holder = DummyPageHolder::Create();
document = &page_holder->GetDocument();
document->GetAnimationClock().ResetTimeForTesting();
timeline = DocumentTimeline::Create(document.Get());
timeline->ResetForTesting();
animation = timeline->Play(nullptr);
animation->setStartTime(0, false);
animation->setEffect(MakeAnimation());
}
void StartTimeline() { SimulateFrame(0); }
KeyframeEffectModelBase* MakeSimpleEffectModel() {
PropertyHandle PropertyHandleOpacity(GetCSSPropertyOpacity());
TransitionKeyframe* start_keyframe =
TransitionKeyframe::Create(PropertyHandleOpacity);
start_keyframe->SetValue(TypedInterpolationValue::Create(
CSSNumberInterpolationType(PropertyHandleOpacity),
InterpolableNumber::Create(1.0)));
start_keyframe->SetOffset(0.0);
// Egregious hack: Sideload the compositor value.
// This is usually set in a part of the rendering process SimulateFrame
// doesn't call.
start_keyframe->SetCompositorValue(AnimatableDouble::Create(1.0));
TransitionKeyframe* end_keyframe =
TransitionKeyframe::Create(PropertyHandleOpacity);
end_keyframe->SetValue(TypedInterpolationValue::Create(
CSSNumberInterpolationType(PropertyHandleOpacity),
InterpolableNumber::Create(0.0)));
end_keyframe->SetOffset(1.0);
// Egregious hack: Sideload the compositor value.
end_keyframe->SetCompositorValue(AnimatableDouble::Create(0.0));
TransitionKeyframeVector keyframes;
keyframes.push_back(start_keyframe);
keyframes.push_back(end_keyframe);
return TransitionKeyframeEffectModel::Create(keyframes);
}
void ResetWithCompositedAnimation() {
// Get rid of the default animation.
animation->cancel();
EnableCompositing();
SetBodyInnerHTML("<div id='target'></div>");
// Create a compositable animation; in this case opacity from 1 to 0.
Timing timing;
timing.iteration_duration = AnimationTimeDelta::FromSecondsD(30);
Persistent<StringKeyframe> start_keyframe = StringKeyframe::Create();
start_keyframe->SetCSSPropertyValue(CSSPropertyOpacity, "1.0",
SecureContextMode::kInsecureContext,
nullptr);
Persistent<StringKeyframe> end_keyframe = StringKeyframe::Create();
end_keyframe->SetCSSPropertyValue(CSSPropertyOpacity, "0.0",
SecureContextMode::kInsecureContext,
nullptr);
StringKeyframeVector keyframes;
keyframes.push_back(start_keyframe);
keyframes.push_back(end_keyframe);
Element* element = GetElementById("target");
StringKeyframeEffectModel* model =
StringKeyframeEffectModel::Create(keyframes);
animation = timeline->Play(KeyframeEffect::Create(element, model, timing));
// After creating the animation we need to clean the lifecycle so that the
// animation can be pushed to the compositor.
UpdateAllLifecyclePhasesForTest();
document->GetAnimationClock().UpdateTime(base::TimeTicks());
document->GetPendingAnimations().Update(base::nullopt, true);
}
KeyframeEffectModelBase* MakeEmptyEffectModel() {
return StringKeyframeEffectModel::Create(StringKeyframeVector());
}
KeyframeEffect* MakeAnimation(double duration = 30) {
Timing timing;
timing.iteration_duration = AnimationTimeDelta::FromSecondsD(duration);
return KeyframeEffect::Create(nullptr, MakeEmptyEffectModel(), timing);
}
bool SimulateFrame(
double time,
base::Optional<CompositorElementIdSet> composited_element_ids =
base::Optional<CompositorElementIdSet>()) {
document->GetAnimationClock().UpdateTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(time));
document->GetPendingAnimations().Update(composited_element_ids, false);
// The timeline does not know about our animation, so we have to explicitly
// call update().
return animation->Update(kTimingUpdateForAnimationFrame);
}
Persistent<Document> document;
Persistent<DocumentTimeline> timeline;
Persistent<Animation> animation;
std::unique_ptr<DummyPageHolder> page_holder;
};
TEST_F(AnimationAnimationTest, InitialState) {
SetUpWithoutStartingTimeline();
animation = timeline->Play(nullptr);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_FALSE(animation->Paused());
EXPECT_EQ(1, animation->playbackRate());
EXPECT_FALSE(animation->StartTimeInternal());
StartTimeline();
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_EQ(0, timeline->CurrentTimeInternal());
EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_FALSE(animation->Paused());
EXPECT_EQ(1, animation->playbackRate());
EXPECT_EQ(0, animation->StartTimeInternal());
}
TEST_F(AnimationAnimationTest, CurrentTimeDoesNotSetOutdated) {
EXPECT_FALSE(animation->Outdated());
EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_FALSE(animation->Outdated());
// FIXME: We should split simulateFrame into a version that doesn't update
// the animation and one that does, as most of the tests don't require
// update() to be called.
document->GetAnimationClock().UpdateTime(base::TimeTicks() +
base::TimeDelta::FromSecondsD(10));
EXPECT_EQ(10, animation->CurrentTimeInternal());
EXPECT_FALSE(animation->Outdated());
}
TEST_F(AnimationAnimationTest, SetCurrentTime) {
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
animation->SetCurrentTimeInternal(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(10, animation->CurrentTimeInternal());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(20, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, SetCurrentTimeNegative) {
animation->SetCurrentTimeInternal(-10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(-10, animation->CurrentTimeInternal());
SimulateFrame(20);
EXPECT_EQ(10, animation->CurrentTimeInternal());
animation->setPlaybackRate(-2);
animation->SetCurrentTimeInternal(-10);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(-10, animation->CurrentTimeInternal());
SimulateFrame(40);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_EQ(-10, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest,
SetCurrentTimeNegativeWithoutSimultaneousPlaybackRateChange) {
SimulateFrame(20);
EXPECT_EQ(20, animation->CurrentTimeInternal());
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
animation->setPlaybackRate(-1);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
SimulateFrame(30);
EXPECT_EQ(20, animation->CurrentTimeInternal());
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
animation->setCurrentTime(-10 * 1000, false);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
}
TEST_F(AnimationAnimationTest, SetCurrentTimePastContentEnd) {
animation->setCurrentTime(50 * 1000, false);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_EQ(50, animation->CurrentTimeInternal());
SimulateFrame(20);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_EQ(50, animation->CurrentTimeInternal());
animation->setPlaybackRate(-2);
animation->setCurrentTime(50 * 1000, false);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(50, animation->CurrentTimeInternal());
SimulateFrame(20);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
SimulateFrame(40);
EXPECT_EQ(10, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, SetCurrentTimeMax) {
animation->SetCurrentTimeInternal(std::numeric_limits<double>::max());
EXPECT_EQ(std::numeric_limits<double>::max(),
animation->CurrentTimeInternal());
SimulateFrame(100);
EXPECT_EQ(std::numeric_limits<double>::max(),
animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, SetCurrentTimeSetsStartTime) {
EXPECT_EQ(0, animation->startTime());
animation->setCurrentTime(1000, false);
EXPECT_EQ(-1000, animation->startTime());
SimulateFrame(1);
EXPECT_EQ(-1000, animation->startTime());
EXPECT_EQ(2000, animation->currentTime());
}
TEST_F(AnimationAnimationTest, SetStartTime) {
SimulateFrame(20);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(0, animation->StartTimeInternal());
EXPECT_EQ(20 * 1000, animation->currentTime());
animation->setStartTime(10 * 1000, false);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(10, animation->StartTimeInternal());
EXPECT_EQ(10 * 1000, animation->currentTime());
SimulateFrame(30);
EXPECT_EQ(10, animation->StartTimeInternal());
EXPECT_EQ(20 * 1000, animation->currentTime());
animation->setStartTime(-20 * 1000, false);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
}
TEST_F(AnimationAnimationTest, SetStartTimeLimitsAnimation) {
animation->setStartTime(-50 * 1000, false);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_EQ(30, animation->CurrentTimeInternal());
animation->setPlaybackRate(-1);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
animation->setStartTime(-100 * 1000, false);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_TRUE(animation->Limited());
}
TEST_F(AnimationAnimationTest, SetStartTimeOnLimitedAnimation) {
SimulateFrame(30);
animation->setStartTime(-10 * 1000, false);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_EQ(30, animation->CurrentTimeInternal());
animation->SetCurrentTimeInternal(50);
animation->setStartTime(-40 * 1000, false);
EXPECT_EQ(30, animation->CurrentTimeInternal());
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_TRUE(animation->Limited());
}
TEST_F(AnimationAnimationTest, StartTimePauseFinish) {
NonThrowableExceptionState exception_state;
animation->pause();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_FALSE(animation->startTime());
animation->finish(exception_state);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_EQ(-30000, animation->startTime());
}
TEST_F(AnimationAnimationTest, FinishWhenPaused) {
NonThrowableExceptionState exception_state;
animation->pause();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
SimulateFrame(10);
EXPECT_EQ(Animation::kPaused, animation->PlayStateInternal());
animation->finish(exception_state);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
}
TEST_F(AnimationAnimationTest, StartTimeFinishPause) {
NonThrowableExceptionState exception_state;
animation->finish(exception_state);
EXPECT_EQ(-30 * 1000, animation->startTime());
animation->pause();
EXPECT_FALSE(animation->startTime());
}
TEST_F(AnimationAnimationTest, StartTimeWithZeroPlaybackRate) {
animation->setPlaybackRate(0);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_FALSE(animation->startTime());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
}
TEST_F(AnimationAnimationTest, PausePlay) {
SimulateFrame(10);
animation->pause();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_TRUE(animation->Paused());
EXPECT_EQ(10, animation->CurrentTimeInternal());
SimulateFrame(20);
EXPECT_EQ(Animation::kPaused, animation->PlayStateInternal());
animation->play();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
SimulateFrame(20);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_FALSE(animation->Paused());
EXPECT_EQ(10, animation->CurrentTimeInternal());
SimulateFrame(30);
EXPECT_EQ(20, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, PlayRewindsToStart) {
animation->SetCurrentTimeInternal(30);
animation->play();
EXPECT_EQ(0, animation->CurrentTimeInternal());
animation->SetCurrentTimeInternal(40);
animation->play();
EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
animation->SetCurrentTimeInternal(-10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
animation->play();
EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
}
TEST_F(AnimationAnimationTest, PlayRewindsToEnd) {
animation->setPlaybackRate(-1);
animation->play();
EXPECT_EQ(30, animation->CurrentTimeInternal());
animation->SetCurrentTimeInternal(40);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
animation->play();
EXPECT_EQ(30, animation->CurrentTimeInternal());
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
animation->SetCurrentTimeInternal(-10);
animation->play();
EXPECT_EQ(30, animation->CurrentTimeInternal());
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
SimulateFrame(20);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
}
TEST_F(AnimationAnimationTest, PlayWithPlaybackRateZeroDoesNotSeek) {
animation->setPlaybackRate(0);
animation->play();
EXPECT_EQ(0, animation->CurrentTimeInternal());
animation->SetCurrentTimeInternal(40);
animation->play();
EXPECT_EQ(40, animation->CurrentTimeInternal());
animation->SetCurrentTimeInternal(-10);
animation->play();
EXPECT_EQ(-10, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest,
PlayAfterPauseWithPlaybackRateZeroUpdatesPlayState) {
animation->pause();
animation->setPlaybackRate(0);
SimulateFrame(1);
EXPECT_EQ(Animation::kPaused, animation->PlayStateInternal());
animation->play();
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
}
TEST_F(AnimationAnimationTest, Reverse) {
animation->SetCurrentTimeInternal(10);
animation->pause();
animation->reverse();
EXPECT_FALSE(animation->Paused());
EXPECT_EQ(-1, animation->playbackRate());
EXPECT_EQ(10, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, ReverseDoesNothingWithPlaybackRateZero) {
animation->SetCurrentTimeInternal(10);
animation->setPlaybackRate(0);
animation->pause();
animation->reverse();
EXPECT_TRUE(animation->Paused());
EXPECT_EQ(0, animation->playbackRate());
EXPECT_EQ(10, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, ReverseSeeksToStart) {
animation->SetCurrentTimeInternal(-10);
animation->setPlaybackRate(-1);
animation->reverse();
EXPECT_EQ(0, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, ReverseSeeksToEnd) {
animation->setCurrentTime(40 * 1000, false);
animation->reverse();
EXPECT_EQ(30, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, ReverseBeyondLimit) {
animation->SetCurrentTimeInternal(40);
animation->setPlaybackRate(-1);
animation->reverse();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(0, animation->CurrentTimeInternal());
animation->SetCurrentTimeInternal(-10);
animation->reverse();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(30, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, Finish) {
NonThrowableExceptionState exception_state;
animation->finish(exception_state);
EXPECT_EQ(30, animation->CurrentTimeInternal());
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
animation->setPlaybackRate(-1);
animation->finish(exception_state);
EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
}
TEST_F(AnimationAnimationTest, FinishAfterEffectEnd) {
NonThrowableExceptionState exception_state;
animation->setCurrentTime(40 * 1000, false);
animation->finish(exception_state);
EXPECT_EQ(40, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, FinishBeforeStart) {
NonThrowableExceptionState exception_state;
animation->SetCurrentTimeInternal(-10);
animation->setPlaybackRate(-1);
animation->finish(exception_state);
EXPECT_EQ(0, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, FinishDoesNothingWithPlaybackRateZero) {
DummyExceptionStateForTesting exception_state;
animation->SetCurrentTimeInternal(10);
animation->setPlaybackRate(0);
animation->finish(exception_state);
EXPECT_EQ(10, animation->CurrentTimeInternal());
EXPECT_TRUE(exception_state.HadException());
}
TEST_F(AnimationAnimationTest, FinishRaisesException) {
Timing timing;
timing.iteration_duration = AnimationTimeDelta::FromSecondsD(1);
timing.iteration_count = std::numeric_limits<double>::infinity();
animation->setEffect(
KeyframeEffect::Create(nullptr, MakeEmptyEffectModel(), timing));
animation->SetCurrentTimeInternal(10);
DummyExceptionStateForTesting exception_state;
animation->finish(exception_state);
EXPECT_EQ(10, animation->CurrentTimeInternal());
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(DOMExceptionCode::kInvalidStateError,
exception_state.CodeAs<DOMExceptionCode>());
}
TEST_F(AnimationAnimationTest, LimitingAtEffectEnd) {
SimulateFrame(30);
EXPECT_EQ(30, animation->CurrentTimeInternal());
EXPECT_TRUE(animation->Limited());
SimulateFrame(40);
EXPECT_EQ(30, animation->CurrentTimeInternal());
EXPECT_FALSE(animation->Paused());
}
TEST_F(AnimationAnimationTest, LimitingAtStart) {
SimulateFrame(30);
animation->setPlaybackRate(-2);
SimulateFrame(30);
SimulateFrame(45);
EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_TRUE(animation->Limited());
SimulateFrame(60);
EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_FALSE(animation->Paused());
}
TEST_F(AnimationAnimationTest, LimitingWithNoEffect) {
animation->setEffect(nullptr);
EXPECT_TRUE(animation->Limited());
SimulateFrame(30);
EXPECT_EQ(0, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, SetPlaybackRate) {
animation->setPlaybackRate(2);
SimulateFrame(0);
EXPECT_EQ(2, animation->playbackRate());
EXPECT_EQ(0, animation->CurrentTimeInternal());
SimulateFrame(10);
EXPECT_EQ(20, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, SetPlaybackRateWhilePaused) {
SimulateFrame(10);
animation->pause();
animation->setPlaybackRate(2);
SimulateFrame(20);
animation->play();
EXPECT_EQ(10, animation->CurrentTimeInternal());
SimulateFrame(20);
SimulateFrame(25);
EXPECT_EQ(20, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, SetPlaybackRateWhileLimited) {
SimulateFrame(40);
EXPECT_EQ(30, animation->CurrentTimeInternal());
animation->setPlaybackRate(2);
SimulateFrame(50);
EXPECT_EQ(30, animation->CurrentTimeInternal());
animation->setPlaybackRate(-2);
SimulateFrame(50);
SimulateFrame(60);
EXPECT_FALSE(animation->Limited());
EXPECT_EQ(10, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, SetPlaybackRateZero) {
SimulateFrame(0);
SimulateFrame(10);
animation->setPlaybackRate(0);
SimulateFrame(10);
EXPECT_EQ(10, animation->CurrentTimeInternal());
SimulateFrame(20);
EXPECT_EQ(10, animation->CurrentTimeInternal());
animation->SetCurrentTimeInternal(20);
EXPECT_EQ(20, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, SetPlaybackRateMax) {
animation->setPlaybackRate(std::numeric_limits<double>::max());
SimulateFrame(0);
EXPECT_EQ(std::numeric_limits<double>::max(), animation->playbackRate());
EXPECT_EQ(0, animation->CurrentTimeInternal());
SimulateFrame(1);
EXPECT_EQ(30, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, SetEffect) {
animation = timeline->Play(nullptr);
animation->setStartTime(0, false);
AnimationEffect* effect1 = MakeAnimation();
AnimationEffect* effect2 = MakeAnimation();
animation->setEffect(effect1);
EXPECT_EQ(effect1, animation->effect());
EXPECT_EQ(0, animation->CurrentTimeInternal());
animation->SetCurrentTimeInternal(15);
animation->setEffect(effect2);
EXPECT_EQ(15, animation->CurrentTimeInternal());
EXPECT_EQ(nullptr, effect1->GetAnimationForTesting());
EXPECT_EQ(animation, effect2->GetAnimationForTesting());
EXPECT_EQ(effect2, animation->effect());
}
TEST_F(AnimationAnimationTest, SetEffectLimitsAnimation) {
animation->SetCurrentTimeInternal(20);
animation->setEffect(MakeAnimation(10));
EXPECT_EQ(20, animation->CurrentTimeInternal());
EXPECT_TRUE(animation->Limited());
SimulateFrame(10);
EXPECT_EQ(20, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, SetEffectUnlimitsAnimation) {
animation->SetCurrentTimeInternal(40);
animation->setEffect(MakeAnimation(60));
EXPECT_FALSE(animation->Limited());
EXPECT_EQ(40, animation->CurrentTimeInternal());
SimulateFrame(10);
EXPECT_EQ(50, animation->CurrentTimeInternal());
}
TEST_F(AnimationAnimationTest, EmptyAnimationsDontUpdateEffects) {
animation = timeline->Play(nullptr);
animation->Update(kTimingUpdateOnDemand);
EXPECT_EQ(std::numeric_limits<double>::infinity(),
animation->TimeToEffectChange());
SimulateFrame(1234);
EXPECT_EQ(std::numeric_limits<double>::infinity(),
animation->TimeToEffectChange());
}
TEST_F(AnimationAnimationTest, AnimationsDisassociateFromEffect) {
AnimationEffect* animation_node = animation->effect();
Animation* animation2 = timeline->Play(animation_node);
EXPECT_EQ(nullptr, animation->effect());
animation->setEffect(animation_node);
EXPECT_EQ(nullptr, animation2->effect());
}
TEST_F(AnimationAnimationTest, AnimationsReturnTimeToNextEffect) {
Timing timing;
timing.start_delay = 1;
timing.iteration_duration = AnimationTimeDelta::FromSecondsD(1);
timing.end_delay = 1;
KeyframeEffect* keyframe_effect =
KeyframeEffect::Create(nullptr, MakeEmptyEffectModel(), timing);
animation = timeline->Play(keyframe_effect);
animation->setStartTime(0, false);
SimulateFrame(0);
EXPECT_EQ(1, animation->TimeToEffectChange());
SimulateFrame(0.5);
EXPECT_EQ(0.5, animation->TimeToEffectChange());
SimulateFrame(1);
EXPECT_EQ(0, animation->TimeToEffectChange());
SimulateFrame(1.5);
EXPECT_EQ(0, animation->TimeToEffectChange());
SimulateFrame(2);
EXPECT_EQ(std::numeric_limits<double>::infinity(),
animation->TimeToEffectChange());
SimulateFrame(3);
EXPECT_EQ(std::numeric_limits<double>::infinity(),
animation->TimeToEffectChange());
animation->SetCurrentTimeInternal(0);
SimulateFrame(3);
EXPECT_EQ(1, animation->TimeToEffectChange());
animation->setPlaybackRate(2);
SimulateFrame(3);
EXPECT_EQ(0.5, animation->TimeToEffectChange());
animation->setPlaybackRate(0);
animation->Update(kTimingUpdateOnDemand);
EXPECT_EQ(std::numeric_limits<double>::infinity(),
animation->TimeToEffectChange());
animation->SetCurrentTimeInternal(3);
animation->setPlaybackRate(-1);
animation->Update(kTimingUpdateOnDemand);
SimulateFrame(3);
EXPECT_EQ(1, animation->TimeToEffectChange());
animation->setPlaybackRate(-2);
animation->Update(kTimingUpdateOnDemand);
SimulateFrame(3);
EXPECT_EQ(0.5, animation->TimeToEffectChange());
}
TEST_F(AnimationAnimationTest, TimeToNextEffectWhenPaused) {
EXPECT_EQ(0, animation->TimeToEffectChange());
animation->pause();
animation->Update(kTimingUpdateOnDemand);
EXPECT_EQ(std::numeric_limits<double>::infinity(),
animation->TimeToEffectChange());
}
TEST_F(AnimationAnimationTest, TimeToNextEffectWhenCancelledBeforeStart) {
EXPECT_EQ(0, animation->TimeToEffectChange());
animation->SetCurrentTimeInternal(-8);
animation->setPlaybackRate(2);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
animation->Update(kTimingUpdateOnDemand);
// This frame will fire the finish event event though no start time has been
// received from the compositor yet, as cancel() nukes start times.
SimulateFrame(0);
EXPECT_EQ(std::numeric_limits<double>::infinity(),
animation->TimeToEffectChange());
}
TEST_F(AnimationAnimationTest,
TimeToNextEffectWhenCancelledBeforeStartReverse) {
EXPECT_EQ(0, animation->TimeToEffectChange());
animation->SetCurrentTimeInternal(9);
animation->setPlaybackRate(-3);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
animation->Update(kTimingUpdateOnDemand);
// This frame will fire the finish event event though no start time has been
// received from the compositor yet, as cancel() nukes start times.
SimulateFrame(0);
EXPECT_EQ(std::numeric_limits<double>::infinity(),
animation->TimeToEffectChange());
}
TEST_F(AnimationAnimationTest, TimeToNextEffectSimpleCancelledBeforeStart) {
EXPECT_EQ(0, animation->TimeToEffectChange());
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
animation->cancel();
animation->Update(kTimingUpdateOnDemand);
// This frame will fire the finish event event though no start time has been
// received from the compositor yet, as cancel() nukes start times.
SimulateFrame(0);
EXPECT_EQ(std::numeric_limits<double>::infinity(),
animation->TimeToEffectChange());
}
TEST_F(AnimationAnimationTest, AttachedAnimations) {
Persistent<Element> element = document->CreateElementForBinding("foo");
Timing timing;
KeyframeEffect* keyframe_effect =
KeyframeEffect::Create(element.Get(), MakeEmptyEffectModel(), timing);
Animation* animation = timeline->Play(keyframe_effect);
SimulateFrame(0);
timeline->ServiceAnimations(kTimingUpdateForAnimationFrame);
EXPECT_EQ(
1U, element->GetElementAnimations()->Animations().find(animation)->value);
ThreadState::Current()->CollectAllGarbage();
EXPECT_TRUE(element->GetElementAnimations()->Animations().IsEmpty());
}
TEST_F(AnimationAnimationTest, HasLowerPriority) {
Animation* animation1 = timeline->Play(nullptr);
Animation* animation2 = timeline->Play(nullptr);
EXPECT_TRUE(Animation::HasLowerPriority(animation1, animation2));
}
TEST_F(AnimationAnimationTest, PlayAfterCancel) {
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_FALSE(animation->startTime());
animation->play();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(0, animation->currentTime());
EXPECT_FALSE(animation->startTime());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(0, animation->currentTime());
EXPECT_EQ(10 * 1000, animation->startTime());
}
TEST_F(AnimationAnimationTest, PlayBackwardsAfterCancel) {
animation->setPlaybackRate(-1);
animation->setCurrentTime(15 * 1000, false);
SimulateFrame(0);
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_FALSE(animation->startTime());
animation->play();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime());
EXPECT_FALSE(animation->startTime());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime());
EXPECT_EQ(40 * 1000, animation->startTime());
}
TEST_F(AnimationAnimationTest, ReverseAfterCancel) {
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_FALSE(animation->startTime());
animation->reverse();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime());
EXPECT_FALSE(animation->startTime());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime());
EXPECT_EQ(40 * 1000, animation->startTime());
}
TEST_F(AnimationAnimationTest, FinishAfterCancel) {
NonThrowableExceptionState exception_state;
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_FALSE(animation->startTime());
animation->finish(exception_state);
EXPECT_EQ(30000, animation->currentTime());
EXPECT_EQ(-30000, animation->startTime());
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
}
TEST_F(AnimationAnimationTest, PauseAfterCancel) {
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_FALSE(animation->startTime());
animation->pause();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(0, animation->currentTime());
EXPECT_FALSE(animation->startTime());
}
TEST_F(AnimationAnimationTest, NoCompositeWithoutCompositedElementId) {
ScopedCompositeAfterPaintForTest enable_cap(true);
SetBodyInnerHTML(
"<div id='foo' style='position: relative'></div>"
"<div id='bar' style='position: relative'></div>");
LayoutObject* object_composited = GetLayoutObjectByElementId("foo");
LayoutObject* object_not_composited = GetLayoutObjectByElementId("bar");
base::Optional<CompositorElementIdSet> composited_element_ids =
CompositorElementIdSet();
CompositorElementId expected_compositor_element_id =
CompositorElementIdFromUniqueObjectId(
ToLayoutBoxModelObject(object_composited)->UniqueId(),
CompositorElementIdNamespace::kPrimaryEffect);
composited_element_ids->insert(expected_compositor_element_id);
Timing timing;
timing.iteration_duration = AnimationTimeDelta::FromSecondsD(30);
KeyframeEffect* keyframe_effect_composited = KeyframeEffect::Create(
ToElement(object_composited->GetNode()), MakeSimpleEffectModel(), timing);
Animation* animation_composited = timeline->Play(keyframe_effect_composited);
KeyframeEffect* keyframe_effect_not_composited =
KeyframeEffect::Create(ToElement(object_not_composited->GetNode()),
MakeSimpleEffectModel(), timing);
Animation* animation_not_composited =
timeline->Play(keyframe_effect_not_composited);
SimulateFrame(0, composited_element_ids);
EXPECT_TRUE(
animation_composited->CheckCanStartAnimationOnCompositorInternal().Ok());
EXPECT_TRUE(animation_composited
->CheckCanStartAnimationOnCompositor(composited_element_ids)
.Ok());
EXPECT_FALSE(animation_not_composited
->CheckCanStartAnimationOnCompositor(composited_element_ids)
.Ok());
}
// Regression test for http://crbug.com/819591 . If a compositable animation is
// played and then paused before any start time is set (either blink or
// compositor side), the pausing must still set compositor pending or the pause
// won't be synced.
TEST_F(AnimationAnimationTest, SetCompositorPendingWithUnresolvedStartTimes) {
ResetWithCompositedAnimation();
// At this point, the animation exists on both the compositor and blink side,
// but no start time has arrived on either side. The compositor is currently
// synced, no update is pending.
EXPECT_FALSE(animation->CompositorPendingForTesting());
// However, if we pause the animation then the compositor should still be
// marked pending. This is required because otherwise the compositor will go
// ahead and start playing the animation once it receives a start time (e.g.
// on the next compositor frame).
animation->pause();
EXPECT_TRUE(animation->CompositorPendingForTesting());
}
TEST_F(AnimationAnimationTest, PreCommitWithUnresolvedStartTimes) {
ResetWithCompositedAnimation();
// At this point, the animation exists on both the compositor and blink side,
// but no start time has arrived on either side. The compositor is currently
// synced, no update is pending.
EXPECT_FALSE(animation->CompositorPendingForTesting());
// At this point, a call to PreCommit should bail out and tell us to wait for
// next commit because there are no resolved start times.
EXPECT_FALSE(animation->PreCommit(0, base::nullopt, true));
}
TEST_F(AnimationAnimationTest, SetKeyframesCausesCompositorPending) {
ResetWithCompositedAnimation();
// At this point, the animation exists on both the compositor and blink side,
// but no start time has arrived on either side. The compositor is currently
// synced, no update is pending.
EXPECT_FALSE(animation->CompositorPendingForTesting());
// Now change the keyframes; this should mark the animation as compositor
// pending as we need to sync the compositor side.
Persistent<StringKeyframe> start_keyframe = StringKeyframe::Create();
start_keyframe->SetCSSPropertyValue(
CSSPropertyOpacity, "0.0", SecureContextMode::kInsecureContext, nullptr);
Persistent<StringKeyframe> end_keyframe = StringKeyframe::Create();
end_keyframe->SetCSSPropertyValue(
CSSPropertyOpacity, "1.0", SecureContextMode::kInsecureContext, nullptr);
StringKeyframeVector keyframes;
keyframes.push_back(start_keyframe);
keyframes.push_back(end_keyframe);
ToKeyframeEffect(animation->effect())->SetKeyframes(keyframes);
EXPECT_TRUE(animation->CompositorPendingForTesting());
}
} // namespace blink