diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:20:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:28:57 +0000 |
commit | d17ea114e5ef69ad5d5d7413280a13e6428098aa (patch) | |
tree | 2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/third_party/blink/renderer/core/animation/animation_test.cc | |
parent | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff) | |
download | qtwebengine-chromium-d17ea114e5ef69ad5d5d7413280a13e6428098aa.tar.gz |
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/core/animation/animation_test.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/core/animation/animation_test.cc | 944 |
1 files changed, 944 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/core/animation/animation_test.cc b/chromium/third_party/blink/renderer/core/animation/animation_test.cc new file mode 100644 index 00000000000..14088078a8c --- /dev/null +++ b/chromium/third_party/blink/renderer/core/animation/animation_test.cc @@ -0,0 +1,944 @@ +/* + * 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/animation_clock.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* MakeEmptyEffectModel() { + return StringKeyframeEffectModel::Create(StringKeyframeVector()); + } + + KeyframeEffect* MakeAnimation(double duration = 30, + double playback_rate = 1) { + Timing timing; + timing.iteration_duration = duration; + timing.playback_rate = playback_rate; + return KeyframeEffect::Create(nullptr, MakeEmptyEffectModel(), timing); + } + + bool SimulateFrame(double time, + Optional<CompositorElementIdSet> composited_element_ids = + Optional<CompositorElementIdSet>()) { + document->GetAnimationClock().UpdateTime(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(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 = 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(kInvalidStateError, exception_state.Code()); +} + +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 = 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) { + ScopedSlimmingPaintV2ForTest enable_s_pv2(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"); + + Optional<CompositorElementIdSet> composited_element_ids = + CompositorElementIdSet(); + CompositorElementId expected_compositor_element_id = + CompositorElementIdFromUniqueObjectId( + ToLayoutBoxModelObject(object_composited)->UniqueId(), + CompositorElementIdNamespace::kPrimary); + composited_element_ids->insert(expected_compositor_element_id); + + Timing timing; + timing.iteration_duration = 30; + timing.playback_rate = 1; + KeyframeEffect* keyframe_effect_composited = KeyframeEffect::Create( + ToElement(object_composited->GetNode()), MakeEmptyEffectModel(), timing); + Animation* animation_composited = timeline->Play(keyframe_effect_composited); + KeyframeEffect* keyframe_effect_not_composited = + KeyframeEffect::Create(ToElement(object_not_composited->GetNode()), + MakeEmptyEffectModel(), timing); + Animation* animation_not_composited = + timeline->Play(keyframe_effect_not_composited); + + SimulateFrame(0, composited_element_ids); + EXPECT_TRUE( + animation_composited + ->CheckCanStartAnimationOnCompositorInternal(composited_element_ids) + .Ok()); + EXPECT_FALSE( + animation_not_composited + ->CheckCanStartAnimationOnCompositorInternal(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) { + // 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 = 30; + + scoped_refptr<StringKeyframe> start_keyframe = StringKeyframe::Create(); + start_keyframe->SetCSSPropertyValue( + CSSPropertyOpacity, "1.0", SecureContextMode::kInsecureContext, nullptr); + scoped_refptr<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); + + auto* element = GetElementById("target"); + StringKeyframeEffectModel* model = + StringKeyframeEffectModel::Create(keyframes); + KeyframeEffect* keyframe_effect_composited = + KeyframeEffect::Create(element, model, timing); + Animation* animation = timeline->Play(keyframe_effect_composited); + + // After creating the animation we need to clean the lifecycle so that the + // animation can be pushed to the compositor. + UpdateAllLifecyclePhases(); + + document->GetAnimationClock().UpdateTime(0); + document->GetPendingAnimations().Update(WTF::nullopt, true); + + // 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) { + // 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 = 30; + + scoped_refptr<StringKeyframe> start_keyframe = StringKeyframe::Create(); + start_keyframe->SetCSSPropertyValue( + CSSPropertyOpacity, "1.0", SecureContextMode::kInsecureContext, nullptr); + scoped_refptr<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); + + auto* element = GetElementById("target"); + StringKeyframeEffectModel* model = + StringKeyframeEffectModel::Create(keyframes); + KeyframeEffect* keyframe_effect_composited = + KeyframeEffect::Create(element, model, timing); + Animation* animation = timeline->Play(keyframe_effect_composited); + + // After creating the animation we need to clean the lifecycle so that the + // animation can be pushed to the compositor. + UpdateAllLifecyclePhases(); + + document->GetAnimationClock().UpdateTime(0); + document->GetPendingAnimations().Update(WTF::nullopt, true); + + // 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, WTF::nullopt, true)); +} + +} // namespace blink |