| /* |
| * 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 "core/animation/CompositorAnimations.h" |
| |
| #include <memory> |
| #include "base/memory/scoped_refptr.h" |
| #include "core/animation/Animation.h" |
| #include "core/animation/DocumentTimeline.h" |
| #include "core/animation/ElementAnimations.h" |
| #include "core/animation/KeyframeEffect.h" |
| #include "core/animation/PendingAnimations.h" |
| #include "core/animation/animatable/AnimatableDouble.h" |
| #include "core/animation/animatable/AnimatableFilterOperations.h" |
| #include "core/animation/animatable/AnimatableTransform.h" |
| #include "core/dom/Document.h" |
| #include "core/frame/FrameTestHelpers.h" |
| #include "core/frame/WebLocalFrameImpl.h" |
| #include "core/layout/LayoutObject.h" |
| #include "core/layout/LayoutTestHelper.h" |
| #include "core/paint/ObjectPaintProperties.h" |
| #include "core/style/ComputedStyle.h" |
| #include "core/style/FilterOperations.h" |
| #include "core/testing/DummyPageHolder.h" |
| #include "platform/animation/CompositorAnimation.h" |
| #include "platform/animation/CompositorFloatAnimationCurve.h" |
| #include "platform/animation/CompositorFloatKeyframe.h" |
| #include "platform/geometry/FloatBox.h" |
| #include "platform/geometry/IntSize.h" |
| #include "platform/testing/HistogramTester.h" |
| #include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h" |
| #include "platform/testing/URLTestHelpers.h" |
| #include "platform/testing/UnitTestHelpers.h" |
| #include "platform/transforms/TransformOperations.h" |
| #include "platform/transforms/TranslateTransformOperation.h" |
| #include "platform/wtf/HashFunctions.h" |
| #include "platform/wtf/PtrUtil.h" |
| #include "public/web/WebSettings.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace blink { |
| |
| class AnimationCompositorAnimationsTest : public RenderingTest { |
| protected: |
| scoped_refptr<TimingFunction> linear_timing_function_; |
| scoped_refptr<TimingFunction> cubic_ease_timing_function_; |
| scoped_refptr<TimingFunction> cubic_custom_timing_function_; |
| scoped_refptr<TimingFunction> step_timing_function_; |
| scoped_refptr<TimingFunction> frames_timing_function_; |
| |
| Timing timing_; |
| CompositorAnimations::CompositorTiming compositor_timing_; |
| std::unique_ptr<StringKeyframeVector> keyframe_vector2_; |
| Persistent<StringKeyframeEffectModel> keyframe_animation_effect2_; |
| std::unique_ptr<StringKeyframeVector> keyframe_vector5_; |
| Persistent<StringKeyframeEffectModel> keyframe_animation_effect5_; |
| |
| Persistent<Document> document_; |
| Persistent<Element> element_; |
| Persistent<DocumentTimeline> timeline_; |
| std::unique_ptr<DummyPageHolder> page_holder_; |
| |
| void SetUp() override { |
| RenderingTest::SetUp(); |
| EnableCompositing(); |
| linear_timing_function_ = LinearTimingFunction::Shared(); |
| cubic_ease_timing_function_ = CubicBezierTimingFunction::Preset( |
| CubicBezierTimingFunction::EaseType::EASE); |
| cubic_custom_timing_function_ = |
| CubicBezierTimingFunction::Create(1, 2, 3, 4); |
| step_timing_function_ = |
| StepsTimingFunction::Create(1, StepsTimingFunction::StepPosition::END); |
| frames_timing_function_ = FramesTimingFunction::Create(2); |
| |
| timing_ = CreateCompositableTiming(); |
| compositor_timing_ = CompositorAnimations::CompositorTiming(); |
| // Make sure the CompositableTiming is really compositable, otherwise |
| // most other tests will fail. |
| ASSERT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| |
| keyframe_vector2_ = CreateCompositableFloatKeyframeVector(2); |
| keyframe_animation_effect2_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector2_); |
| |
| keyframe_vector5_ = CreateCompositableFloatKeyframeVector(5); |
| keyframe_animation_effect5_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector5_); |
| |
| page_holder_ = DummyPageHolder::Create(); |
| document_ = &page_holder_->GetDocument(); |
| document_->GetAnimationClock().ResetTimeForTesting(); |
| |
| timeline_ = DocumentTimeline::Create(document_.Get()); |
| timeline_->ResetForTesting(); |
| element_ = document_->createElement("test"); |
| |
| helper_.Initialize(nullptr, nullptr, nullptr, &ConfigureSettings); |
| base_url_ = "http://www.test.com/"; |
| } |
| |
| public: |
| bool ConvertTimingForCompositor(const Timing& t, |
| CompositorAnimations::CompositorTiming& out) { |
| return CompositorAnimations::ConvertTimingForCompositor(t, 0, out, 1); |
| } |
| bool CanStartEffectOnCompositor(const Timing& timing, |
| const KeyframeEffectModelBase& effect) { |
| // As the compositor code only understands AnimatableValues, we must |
| // snapshot the effect to make those available. |
| // TODO(crbug.com/725385): Remove once compositor uses InterpolationTypes. |
| auto style = ComputedStyle::Create(); |
| effect.SnapshotAllCompositorKeyframes(*element_.Get(), *style, nullptr); |
| return CompositorAnimations::CheckCanStartEffectOnCompositor( |
| timing, *element_.Get(), nullptr, effect, 1) |
| .Ok(); |
| } |
| void GetAnimationOnCompositor( |
| Timing& timing, |
| StringKeyframeEffectModel& effect, |
| Vector<std::unique_ptr<CompositorAnimation>>& animations) { |
| GetAnimationOnCompositor(timing, effect, animations, 1); |
| } |
| void GetAnimationOnCompositor( |
| Timing& timing, |
| StringKeyframeEffectModel& effect, |
| Vector<std::unique_ptr<CompositorAnimation>>& animations, |
| double player_playback_rate) { |
| CompositorAnimations::GetAnimationOnCompositor( |
| timing, 0, std::numeric_limits<double>::quiet_NaN(), 0, effect, |
| animations, player_playback_rate); |
| } |
| bool GetAnimationBounds(FloatBox& bounding_box, |
| const KeyframeEffectModelBase& effect, |
| double min_value, |
| double max_value) { |
| // As the compositor code only understands AnimatableValues, we must |
| // snapshot the effect to make those available. |
| // TODO(crbug.com/725385): Remove once compositor uses InterpolationTypes. |
| auto style = ComputedStyle::Create(); |
| effect.SnapshotAllCompositorKeyframes(*element_.Get(), *style, nullptr); |
| return CompositorAnimations::GetAnimatedBoundingBox(bounding_box, effect, |
| min_value, max_value); |
| } |
| |
| bool DuplicateSingleKeyframeAndTestIsCandidateOnResult( |
| StringKeyframe* frame) { |
| EXPECT_EQ(frame->Offset(), 0); |
| StringKeyframeVector frames; |
| scoped_refptr<Keyframe> second = frame->CloneWithOffset(1); |
| |
| frames.push_back(frame); |
| frames.push_back(ToStringKeyframe(second.get())); |
| return CanStartEffectOnCompositor( |
| timing_, *StringKeyframeEffectModel::Create(frames)); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| Timing CreateCompositableTiming() { |
| Timing timing; |
| timing.start_delay = 0; |
| timing.fill_mode = Timing::FillMode::NONE; |
| timing.iteration_start = 0; |
| timing.iteration_count = 1; |
| timing.iteration_duration = 1.0; |
| timing.playback_rate = 1.0; |
| timing.direction = Timing::PlaybackDirection::NORMAL; |
| timing.timing_function = linear_timing_function_; |
| return timing; |
| } |
| |
| scoped_refptr<StringKeyframe> CreateReplaceOpKeyframe(CSSPropertyID id, |
| const String& value, |
| double offset = 0) { |
| scoped_refptr<StringKeyframe> keyframe = StringKeyframe::Create(); |
| keyframe->SetCSSPropertyValue(id, value, |
| SecureContextMode::kInsecureContext, nullptr); |
| keyframe->SetComposite(EffectModel::kCompositeReplace); |
| keyframe->SetOffset(offset); |
| keyframe->SetEasing(LinearTimingFunction::Shared()); |
| return keyframe; |
| } |
| |
| scoped_refptr<StringKeyframe> CreateDefaultKeyframe( |
| CSSPropertyID id, |
| EffectModel::CompositeOperation op, |
| double offset = 0) { |
| String value = "0.1"; |
| if (id == CSSPropertyTransform) |
| value = "none"; // AnimatableTransform::Create(TransformOperations(), 1); |
| else if (id == CSSPropertyColor) |
| value = "red"; |
| |
| scoped_refptr<StringKeyframe> keyframe = |
| CreateReplaceOpKeyframe(id, value, offset); |
| keyframe->SetComposite(op); |
| return keyframe; |
| } |
| |
| std::unique_ptr<StringKeyframeVector> CreateCompositableFloatKeyframeVector( |
| size_t n) { |
| Vector<double> values; |
| for (size_t i = 0; i < n; i++) { |
| values.push_back(static_cast<double>(i)); |
| } |
| return CreateCompositableFloatKeyframeVector(values); |
| } |
| |
| std::unique_ptr<StringKeyframeVector> CreateCompositableFloatKeyframeVector( |
| Vector<double>& values) { |
| std::unique_ptr<StringKeyframeVector> frames = |
| WTF::WrapUnique(new StringKeyframeVector); |
| for (size_t i = 0; i < values.size(); i++) { |
| double offset = 1.0 / (values.size() - 1) * i; |
| String value = String::Number(values[i]); |
| frames->push_back( |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, value, offset).get()); |
| } |
| return frames; |
| } |
| |
| std::unique_ptr<StringKeyframeVector> |
| CreateCompositableTransformKeyframeVector(const Vector<String>& values) { |
| std::unique_ptr<StringKeyframeVector> frames = |
| WTF::WrapUnique(new StringKeyframeVector); |
| for (size_t i = 0; i < values.size(); ++i) { |
| double offset = 1.0f / (values.size() - 1) * i; |
| frames->push_back( |
| CreateReplaceOpKeyframe(CSSPropertyTransform, values[i], offset) |
| .get()); |
| } |
| return frames; |
| } |
| |
| StringKeyframeEffectModel* CreateKeyframeEffectModel( |
| scoped_refptr<StringKeyframe> from, |
| scoped_refptr<StringKeyframe> to, |
| scoped_refptr<StringKeyframe> c = nullptr, |
| scoped_refptr<StringKeyframe> d = nullptr) { |
| EXPECT_EQ(from->Offset(), 0); |
| StringKeyframeVector frames; |
| frames.push_back(from); |
| EXPECT_LE(from->Offset(), to->Offset()); |
| frames.push_back(to); |
| if (c) { |
| EXPECT_LE(to->Offset(), c->Offset()); |
| frames.push_back(c); |
| } |
| if (d) { |
| frames.push_back(d); |
| EXPECT_LE(c->Offset(), d->Offset()); |
| EXPECT_EQ(d->Offset(), 1.0); |
| } else { |
| EXPECT_EQ(to->Offset(), 1.0); |
| } |
| if (!HasFatalFailure()) { |
| return StringKeyframeEffectModel::Create(frames); |
| } |
| return nullptr; |
| } |
| |
| void SimulateFrame(double time) { |
| document_->GetAnimationClock().UpdateTime(time); |
| document_->GetPendingAnimations().Update(Optional<CompositorElementIdSet>(), |
| false); |
| timeline_->ServiceAnimations(kTimingUpdateForAnimationFrame); |
| } |
| |
| std::unique_ptr<CompositorAnimation> ConvertToCompositorAnimation( |
| StringKeyframeEffectModel& effect, |
| double player_playback_rate) { |
| // As the compositor code only understands AnimatableValues, we must |
| // snapshot the effect to make those available. |
| // TODO(crbug.com/725385): Remove once compositor uses InterpolationTypes. |
| auto style = ComputedStyle::Create(); |
| effect.SnapshotAllCompositorKeyframes(*element_.Get(), *style, nullptr); |
| |
| Vector<std::unique_ptr<CompositorAnimation>> result; |
| GetAnimationOnCompositor(timing_, effect, result, player_playback_rate); |
| DCHECK_EQ(1U, result.size()); |
| return std::move(result[0]); |
| } |
| |
| std::unique_ptr<CompositorAnimation> ConvertToCompositorAnimation( |
| StringKeyframeEffectModel& effect) { |
| return ConvertToCompositorAnimation(effect, 1.0); |
| } |
| |
| void ExpectKeyframeTimingFunctionCubic( |
| const CompositorFloatKeyframe& keyframe, |
| const CubicBezierTimingFunction::EaseType ease_type) { |
| auto keyframe_timing_function = keyframe.GetTimingFunctionForTesting(); |
| DCHECK_EQ(keyframe_timing_function->GetType(), |
| TimingFunction::Type::CUBIC_BEZIER); |
| const auto& cubic_timing_function = |
| ToCubicBezierTimingFunction(*keyframe_timing_function); |
| EXPECT_EQ(cubic_timing_function.GetEaseType(), ease_type); |
| } |
| |
| void LoadTestData(const std::string& file_name) { |
| String testing_path = testing::BlinkRootDir(); |
| testing_path.append("/Source/core/animation/test_data/"); |
| WebURL url = URLTestHelpers::RegisterMockedURLLoadFromBase( |
| WebString::FromUTF8(base_url_), testing_path, |
| WebString::FromUTF8(file_name)); |
| FrameTestHelpers::LoadFrame(helper_.WebView()->MainFrameImpl(), |
| base_url_ + file_name); |
| ForceFullCompositingUpdate(); |
| URLTestHelpers::RegisterMockedURLUnregister(url); |
| } |
| |
| LocalFrame* GetFrame() const { return helper_.LocalMainFrame()->GetFrame(); } |
| |
| void ForceFullCompositingUpdate() { |
| helper_.WebView()->UpdateAllLifecyclePhases(); |
| } |
| |
| private: |
| static void ConfigureSettings(WebSettings* settings) { |
| settings->SetAcceleratedCompositingEnabled(true); |
| } |
| FrameTestHelpers::WebViewHelper helper_; |
| std::string base_url_; |
| }; |
| |
| class LayoutObjectProxy : public LayoutObject { |
| public: |
| static LayoutObjectProxy* Create(Node* node) { |
| return new LayoutObjectProxy(node); |
| } |
| |
| static void Dispose(LayoutObjectProxy* proxy) { proxy->Destroy(); } |
| |
| const char* GetName() const override { return nullptr; } |
| void UpdateLayout() override {} |
| FloatRect LocalBoundingBoxRectForAccessibility() const { return FloatRect(); } |
| |
| private: |
| explicit LayoutObjectProxy(Node* node) : LayoutObject(node) {} |
| }; |
| |
| // ----------------------------------------------------------------------- |
| // ----------------------------------------------------------------------- |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorKeyframeMultipleCSSProperties) { |
| scoped_refptr<StringKeyframe> keyframe_good_multiple = |
| CreateDefaultKeyframe(CSSPropertyOpacity, EffectModel::kCompositeReplace); |
| keyframe_good_multiple->SetCSSPropertyValue( |
| CSSPropertyTransform, "none", SecureContextMode::kInsecureContext, |
| nullptr); |
| EXPECT_TRUE(DuplicateSingleKeyframeAndTestIsCandidateOnResult( |
| keyframe_good_multiple.get())); |
| |
| scoped_refptr<StringKeyframe> keyframe_bad_multiple_id = |
| CreateDefaultKeyframe(CSSPropertyColor, EffectModel::kCompositeReplace); |
| keyframe_bad_multiple_id->SetCSSPropertyValue( |
| CSSPropertyOpacity, "0.1", SecureContextMode::kInsecureContext, nullptr); |
| EXPECT_FALSE(DuplicateSingleKeyframeAndTestIsCandidateOnResult( |
| keyframe_bad_multiple_id.get())); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| isNotCandidateForCompositorAnimationTransformDependsOnBoxSize) { |
| // Absolute transforms can be animated on the compositor. |
| String transform = "translateX(2px) translateY(2px)"; |
| scoped_refptr<StringKeyframe> good_keyframe = |
| CreateReplaceOpKeyframe(CSSPropertyTransform, transform); |
| EXPECT_TRUE( |
| DuplicateSingleKeyframeAndTestIsCandidateOnResult(good_keyframe.get())); |
| |
| // Transforms that rely on the box size, such as percent calculations, cannot |
| // be animated on the compositor (as the box size may change). |
| String transform2 = "translateX(50%) translateY(2px)"; |
| scoped_refptr<StringKeyframe> bad_keyframe = |
| CreateReplaceOpKeyframe(CSSPropertyTransform, transform2); |
| EXPECT_FALSE( |
| DuplicateSingleKeyframeAndTestIsCandidateOnResult(bad_keyframe.get())); |
| |
| // Similarly, calc transforms cannot be animated on the compositor. |
| String transform3 = "translateX(calc(100% + (0.5 * 100px)))"; |
| scoped_refptr<StringKeyframe> bad_keyframe2 = |
| CreateReplaceOpKeyframe(CSSPropertyTransform, transform3); |
| EXPECT_FALSE( |
| DuplicateSingleKeyframeAndTestIsCandidateOnResult(bad_keyframe2.get())); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorKeyframeEffectModel) { |
| StringKeyframeVector frames_same; |
| frames_same.push_back(CreateDefaultKeyframe(CSSPropertyColor, |
| EffectModel::kCompositeReplace, |
| 0.0) |
| .get()); |
| frames_same.push_back(CreateDefaultKeyframe(CSSPropertyColor, |
| EffectModel::kCompositeReplace, |
| 1.0) |
| .get()); |
| EXPECT_FALSE(CanStartEffectOnCompositor( |
| timing_, *StringKeyframeEffectModel::Create(frames_same))); |
| |
| StringKeyframeVector frames_mixed_properties; |
| scoped_refptr<StringKeyframe> keyframe = StringKeyframe::Create(); |
| keyframe->SetOffset(0); |
| keyframe->SetCSSPropertyValue(CSSPropertyColor, "red", |
| SecureContextMode::kInsecureContext, nullptr); |
| keyframe->SetCSSPropertyValue(CSSPropertyOpacity, "0", |
| SecureContextMode::kInsecureContext, nullptr); |
| frames_mixed_properties.push_back(std::move(keyframe)); |
| keyframe = StringKeyframe::Create(); |
| keyframe->SetOffset(1); |
| keyframe->SetCSSPropertyValue(CSSPropertyColor, "green", |
| SecureContextMode::kInsecureContext, nullptr); |
| keyframe->SetCSSPropertyValue(CSSPropertyOpacity, "1", |
| SecureContextMode::kInsecureContext, nullptr); |
| frames_mixed_properties.push_back(std::move(keyframe)); |
| EXPECT_FALSE(CanStartEffectOnCompositor( |
| timing_, *StringKeyframeEffectModel::Create(frames_mixed_properties))); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, AnimatedBoundingBox) { |
| Vector<String> transform_vector; |
| transform_vector.push_back("translate3d(0, 0, 0)"); |
| transform_vector.push_back("translate3d(200px, 200px, 0)"); |
| std::unique_ptr<StringKeyframeVector> frames = |
| CreateCompositableTransformKeyframeVector(transform_vector); |
| FloatBox bounds; |
| EXPECT_TRUE(GetAnimationBounds( |
| bounds, *StringKeyframeEffectModel::Create(*frames), 0, 1)); |
| EXPECT_EQ(FloatBox(0.0f, 0.f, 0.0f, 200.0f, 200.0f, 0.0f), bounds); |
| bounds = FloatBox(); |
| EXPECT_TRUE(GetAnimationBounds( |
| bounds, *StringKeyframeEffectModel::Create(*frames), -1, 1)); |
| EXPECT_EQ(FloatBox(-200.0f, -200.0, 0.0, 400.0f, 400.0f, 0.0f), bounds); |
| |
| transform_vector.push_back("translate3d(-300px, -400px, 1px)"); |
| bounds = FloatBox(); |
| frames = CreateCompositableTransformKeyframeVector(transform_vector); |
| EXPECT_TRUE(GetAnimationBounds( |
| bounds, *StringKeyframeEffectModel::Create(*frames), 0, 1)); |
| EXPECT_EQ(FloatBox(-300.0f, -400.f, 0.0f, 500.0f, 600.0f, 1.0f), bounds); |
| bounds = FloatBox(); |
| EXPECT_TRUE(GetAnimationBounds( |
| bounds, *StringKeyframeEffectModel::Create(*frames), -1, 2)); |
| EXPECT_EQ(FloatBox(-1300.0f, -1600.f, 0.0f, 1500.0f, 1800.0f, 3.0f), bounds); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| ConvertTimingForCompositorStartDelay) { |
| timing_.iteration_duration = 20.0; |
| |
| timing_.start_delay = 2.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(-2.0, compositor_timing_.scaled_time_offset); |
| |
| timing_.start_delay = -2.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(2.0, compositor_timing_.scaled_time_offset); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| ConvertTimingForCompositorIterationStart) { |
| timing_.iteration_start = 2.2; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| ConvertTimingForCompositorIterationCount) { |
| timing_.iteration_count = 5.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_EQ(5, compositor_timing_.adjusted_iteration_count); |
| |
| timing_.iteration_count = 5.5; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_EQ(5.5, compositor_timing_.adjusted_iteration_count); |
| |
| timing_.iteration_count = std::numeric_limits<double>::infinity(); |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_EQ(-1, compositor_timing_.adjusted_iteration_count); |
| |
| timing_.iteration_count = std::numeric_limits<double>::infinity(); |
| timing_.iteration_duration = 5.0; |
| timing_.start_delay = -6.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(6.0, compositor_timing_.scaled_time_offset); |
| EXPECT_EQ(-1, compositor_timing_.adjusted_iteration_count); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| ConvertTimingForCompositorIterationsAndStartDelay) { |
| timing_.iteration_count = 4.0; |
| timing_.iteration_duration = 5.0; |
| |
| timing_.start_delay = 6.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(-6.0, compositor_timing_.scaled_time_offset); |
| EXPECT_DOUBLE_EQ(4.0, compositor_timing_.adjusted_iteration_count); |
| |
| timing_.start_delay = -6.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(6.0, compositor_timing_.scaled_time_offset); |
| EXPECT_DOUBLE_EQ(4.0, compositor_timing_.adjusted_iteration_count); |
| |
| timing_.start_delay = 21.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| ConvertTimingForCompositorPlaybackRate) { |
| timing_.playback_rate = 1.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(1.0, compositor_timing_.playback_rate); |
| |
| timing_.playback_rate = -2.3; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(-2.3, compositor_timing_.playback_rate); |
| |
| timing_.playback_rate = 1.6; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(1.6, compositor_timing_.playback_rate); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, ConvertTimingForCompositorDirection) { |
| timing_.direction = Timing::PlaybackDirection::NORMAL; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_EQ(compositor_timing_.direction, Timing::PlaybackDirection::NORMAL); |
| |
| timing_.direction = Timing::PlaybackDirection::ALTERNATE_NORMAL; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_EQ(compositor_timing_.direction, |
| Timing::PlaybackDirection::ALTERNATE_NORMAL); |
| |
| timing_.direction = Timing::PlaybackDirection::ALTERNATE_REVERSE; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_EQ(compositor_timing_.direction, |
| Timing::PlaybackDirection::ALTERNATE_REVERSE); |
| |
| timing_.direction = Timing::PlaybackDirection::REVERSE; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_EQ(compositor_timing_.direction, Timing::PlaybackDirection::REVERSE); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| ConvertTimingForCompositorDirectionIterationsAndStartDelay) { |
| timing_.direction = Timing::PlaybackDirection::ALTERNATE_NORMAL; |
| timing_.iteration_count = 4.0; |
| timing_.iteration_duration = 5.0; |
| timing_.start_delay = -6.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(6.0, compositor_timing_.scaled_time_offset); |
| EXPECT_EQ(4, compositor_timing_.adjusted_iteration_count); |
| EXPECT_EQ(compositor_timing_.direction, |
| Timing::PlaybackDirection::ALTERNATE_NORMAL); |
| |
| timing_.direction = Timing::PlaybackDirection::ALTERNATE_NORMAL; |
| timing_.iteration_count = 4.0; |
| timing_.iteration_duration = 5.0; |
| timing_.start_delay = -11.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(11.0, compositor_timing_.scaled_time_offset); |
| EXPECT_EQ(4, compositor_timing_.adjusted_iteration_count); |
| EXPECT_EQ(compositor_timing_.direction, |
| Timing::PlaybackDirection::ALTERNATE_NORMAL); |
| |
| timing_.direction = Timing::PlaybackDirection::ALTERNATE_REVERSE; |
| timing_.iteration_count = 4.0; |
| timing_.iteration_duration = 5.0; |
| timing_.start_delay = -6.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(6.0, compositor_timing_.scaled_time_offset); |
| EXPECT_EQ(4, compositor_timing_.adjusted_iteration_count); |
| EXPECT_EQ(compositor_timing_.direction, |
| Timing::PlaybackDirection::ALTERNATE_REVERSE); |
| |
| timing_.direction = Timing::PlaybackDirection::ALTERNATE_REVERSE; |
| timing_.iteration_count = 4.0; |
| timing_.iteration_duration = 5.0; |
| timing_.start_delay = -11.0; |
| EXPECT_TRUE(ConvertTimingForCompositor(timing_, compositor_timing_)); |
| EXPECT_DOUBLE_EQ(11.0, compositor_timing_.scaled_time_offset); |
| EXPECT_EQ(4, compositor_timing_.adjusted_iteration_count); |
| EXPECT_EQ(compositor_timing_.direction, |
| Timing::PlaybackDirection::ALTERNATE_REVERSE); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorTimingFunctionLinear) { |
| timing_.timing_function = linear_timing_function_; |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorTimingFunctionCubic) { |
| timing_.timing_function = cubic_ease_timing_function_; |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| |
| timing_.timing_function = cubic_custom_timing_function_; |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorTimingFunctionSteps) { |
| timing_.timing_function = step_timing_function_; |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorTimingFunctionFrames) { |
| timing_.timing_function = frames_timing_function_; |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorTimingFunctionChainedLinear) { |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorNonLinearTimingFunctionOnFirstOrLastFrame) { |
| (*keyframe_vector2_)[0]->SetEasing(cubic_ease_timing_function_.get()); |
| keyframe_animation_effect2_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector2_); |
| |
| (*keyframe_vector5_)[3]->SetEasing(cubic_ease_timing_function_.get()); |
| keyframe_animation_effect5_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector5_); |
| |
| timing_.timing_function = cubic_ease_timing_function_; |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| |
| timing_.timing_function = cubic_custom_timing_function_; |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorTimingFunctionChainedCubicMatchingOffsets) { |
| (*keyframe_vector2_)[0]->SetEasing(cubic_ease_timing_function_.get()); |
| keyframe_animation_effect2_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector2_); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| |
| (*keyframe_vector2_)[0]->SetEasing(cubic_custom_timing_function_.get()); |
| keyframe_animation_effect2_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector2_); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| |
| (*keyframe_vector5_)[0]->SetEasing(cubic_ease_timing_function_.get()); |
| (*keyframe_vector5_)[1]->SetEasing(cubic_custom_timing_function_.get()); |
| (*keyframe_vector5_)[2]->SetEasing(cubic_custom_timing_function_.get()); |
| (*keyframe_vector5_)[3]->SetEasing(cubic_custom_timing_function_.get()); |
| keyframe_animation_effect5_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector5_); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorTimingFunctionMixedGood) { |
| (*keyframe_vector5_)[0]->SetEasing(linear_timing_function_.get()); |
| (*keyframe_vector5_)[1]->SetEasing(cubic_ease_timing_function_.get()); |
| (*keyframe_vector5_)[2]->SetEasing(cubic_ease_timing_function_.get()); |
| (*keyframe_vector5_)[3]->SetEasing(linear_timing_function_.get()); |
| keyframe_animation_effect5_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector5_); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| CanStartEffectOnCompositorTimingFunctionWithStepOrFrameOkay) { |
| (*keyframe_vector2_)[0]->SetEasing(step_timing_function_.get()); |
| keyframe_animation_effect2_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector2_); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| |
| (*keyframe_vector2_)[0]->SetEasing(frames_timing_function_.get()); |
| keyframe_animation_effect2_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector2_); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect2_)); |
| |
| (*keyframe_vector5_)[0]->SetEasing(step_timing_function_.get()); |
| (*keyframe_vector5_)[1]->SetEasing(linear_timing_function_.get()); |
| (*keyframe_vector5_)[2]->SetEasing(cubic_ease_timing_function_.get()); |
| (*keyframe_vector5_)[3]->SetEasing(frames_timing_function_.get()); |
| keyframe_animation_effect5_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector5_); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| |
| (*keyframe_vector5_)[0]->SetEasing(frames_timing_function_.get()); |
| (*keyframe_vector5_)[1]->SetEasing(step_timing_function_.get()); |
| (*keyframe_vector5_)[2]->SetEasing(cubic_ease_timing_function_.get()); |
| (*keyframe_vector5_)[3]->SetEasing(linear_timing_function_.get()); |
| keyframe_animation_effect5_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector5_); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| |
| (*keyframe_vector5_)[0]->SetEasing(linear_timing_function_.get()); |
| (*keyframe_vector5_)[1]->SetEasing(frames_timing_function_.get()); |
| (*keyframe_vector5_)[2]->SetEasing(cubic_ease_timing_function_.get()); |
| (*keyframe_vector5_)[3]->SetEasing(step_timing_function_.get()); |
| keyframe_animation_effect5_ = |
| StringKeyframeEffectModel::Create(*keyframe_vector5_); |
| EXPECT_TRUE( |
| CanStartEffectOnCompositor(timing_, *keyframe_animation_effect5_)); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, CanStartEffectOnCompositor) { |
| StringKeyframeVector basic_frames_vector; |
| basic_frames_vector.push_back( |
| CreateDefaultKeyframe(CSSPropertyOpacity, EffectModel::kCompositeReplace, |
| 0.0) |
| .get()); |
| basic_frames_vector.push_back( |
| CreateDefaultKeyframe(CSSPropertyOpacity, EffectModel::kCompositeReplace, |
| 1.0) |
| .get()); |
| |
| StringKeyframeVector non_basic_frames_vector; |
| non_basic_frames_vector.push_back( |
| CreateDefaultKeyframe(CSSPropertyOpacity, EffectModel::kCompositeReplace, |
| 0.0) |
| .get()); |
| non_basic_frames_vector.push_back( |
| CreateDefaultKeyframe(CSSPropertyOpacity, EffectModel::kCompositeReplace, |
| 0.5) |
| .get()); |
| non_basic_frames_vector.push_back( |
| CreateDefaultKeyframe(CSSPropertyOpacity, EffectModel::kCompositeReplace, |
| 1.0) |
| .get()); |
| |
| basic_frames_vector[0]->SetEasing(linear_timing_function_.get()); |
| StringKeyframeEffectModel* basic_frames = |
| StringKeyframeEffectModel::Create(basic_frames_vector); |
| EXPECT_TRUE(CanStartEffectOnCompositor(timing_, *basic_frames)); |
| |
| basic_frames_vector[0]->SetEasing(CubicBezierTimingFunction::Preset( |
| CubicBezierTimingFunction::EaseType::EASE_IN)); |
| basic_frames = StringKeyframeEffectModel::Create(basic_frames_vector); |
| EXPECT_TRUE(CanStartEffectOnCompositor(timing_, *basic_frames)); |
| |
| non_basic_frames_vector[0]->SetEasing(linear_timing_function_.get()); |
| non_basic_frames_vector[1]->SetEasing(CubicBezierTimingFunction::Preset( |
| CubicBezierTimingFunction::EaseType::EASE_IN)); |
| StringKeyframeEffectModel* non_basic_frames = |
| StringKeyframeEffectModel::Create(non_basic_frames_vector); |
| EXPECT_TRUE(CanStartEffectOnCompositor(timing_, *non_basic_frames)); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // ----------------------------------------------------------------------- |
| |
| TEST_F(AnimationCompositorAnimationsTest, createSimpleOpacityAnimation) { |
| // KeyframeEffect to convert |
| StringKeyframeEffectModel* effect = CreateKeyframeEffectModel( |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect); |
| EXPECT_EQ(CompositorTargetProperty::OPACITY, animation->TargetProperty()); |
| EXPECT_EQ(1.0, animation->Iterations()); |
| EXPECT_EQ(0, animation->TimeOffset()); |
| EXPECT_EQ(CompositorAnimation::Direction::NORMAL, animation->GetDirection()); |
| EXPECT_EQ(1.0, animation->PlaybackRate()); |
| |
| std::unique_ptr<CompositorFloatAnimationCurve> keyframed_float_curve = |
| animation->FloatCurveForTesting(); |
| |
| CompositorFloatAnimationCurve::Keyframes keyframes = |
| keyframed_float_curve->KeyframesForTesting(); |
| ASSERT_EQ(2UL, keyframes.size()); |
| |
| EXPECT_EQ(0, keyframes[0]->Time()); |
| EXPECT_EQ(0.2f, keyframes[0]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[0]->GetTimingFunctionForTesting()->GetType()); |
| |
| EXPECT_EQ(1.0, keyframes[1]->Time()); |
| EXPECT_EQ(0.5f, keyframes[1]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[1]->GetTimingFunctionForTesting()->GetType()); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| createSimpleOpacityAnimationDuration) { |
| // KeyframeEffect to convert |
| StringKeyframeEffectModel* effect = CreateKeyframeEffectModel( |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| |
| const double kDuration = 10.0; |
| timing_.iteration_duration = kDuration; |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect); |
| std::unique_ptr<CompositorFloatAnimationCurve> keyframed_float_curve = |
| animation->FloatCurveForTesting(); |
| |
| CompositorFloatAnimationCurve::Keyframes keyframes = |
| keyframed_float_curve->KeyframesForTesting(); |
| ASSERT_EQ(2UL, keyframes.size()); |
| |
| EXPECT_EQ(kDuration, keyframes[1]->Time() * kDuration); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| createMultipleKeyframeOpacityAnimationLinear) { |
| // KeyframeEffect to convert |
| StringKeyframeEffectModel* effect = CreateKeyframeEffectModel( |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.0", 0.25), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.25", 0.5), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| |
| timing_.iteration_count = 5; |
| timing_.direction = Timing::PlaybackDirection::ALTERNATE_NORMAL; |
| timing_.playback_rate = 2.0; |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect); |
| EXPECT_EQ(CompositorTargetProperty::OPACITY, animation->TargetProperty()); |
| EXPECT_EQ(5.0, animation->Iterations()); |
| EXPECT_EQ(0, animation->TimeOffset()); |
| EXPECT_EQ(CompositorAnimation::Direction::ALTERNATE_NORMAL, |
| animation->GetDirection()); |
| EXPECT_EQ(2.0, animation->PlaybackRate()); |
| |
| std::unique_ptr<CompositorFloatAnimationCurve> keyframed_float_curve = |
| animation->FloatCurveForTesting(); |
| |
| CompositorFloatAnimationCurve::Keyframes keyframes = |
| keyframed_float_curve->KeyframesForTesting(); |
| ASSERT_EQ(4UL, keyframes.size()); |
| |
| EXPECT_EQ(0, keyframes[0]->Time()); |
| EXPECT_EQ(0.2f, keyframes[0]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[0]->GetTimingFunctionForTesting()->GetType()); |
| |
| EXPECT_EQ(0.25, keyframes[1]->Time()); |
| EXPECT_EQ(0, keyframes[1]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[1]->GetTimingFunctionForTesting()->GetType()); |
| |
| EXPECT_EQ(0.5, keyframes[2]->Time()); |
| EXPECT_EQ(0.25f, keyframes[2]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[2]->GetTimingFunctionForTesting()->GetType()); |
| |
| EXPECT_EQ(1.0, keyframes[3]->Time()); |
| EXPECT_EQ(0.5f, keyframes[3]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[3]->GetTimingFunctionForTesting()->GetType()); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| createSimpleOpacityAnimationStartDelay) { |
| // KeyframeEffect to convert |
| StringKeyframeEffectModel* effect = CreateKeyframeEffectModel( |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| |
| const double kStartDelay = 3.25; |
| |
| timing_.iteration_count = 5.0; |
| timing_.iteration_duration = 1.75; |
| timing_.start_delay = kStartDelay; |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect); |
| |
| EXPECT_EQ(CompositorTargetProperty::OPACITY, animation->TargetProperty()); |
| EXPECT_EQ(5.0, animation->Iterations()); |
| EXPECT_EQ(-kStartDelay, animation->TimeOffset()); |
| |
| std::unique_ptr<CompositorFloatAnimationCurve> keyframed_float_curve = |
| animation->FloatCurveForTesting(); |
| |
| CompositorFloatAnimationCurve::Keyframes keyframes = |
| keyframed_float_curve->KeyframesForTesting(); |
| ASSERT_EQ(2UL, keyframes.size()); |
| |
| EXPECT_EQ(1.75, keyframes[1]->Time() * timing_.iteration_duration); |
| EXPECT_EQ(0.5f, keyframes[1]->Value()); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| createMultipleKeyframeOpacityAnimationChained) { |
| // KeyframeEffect to convert |
| StringKeyframeVector frames; |
| frames.push_back(CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0)); |
| frames.push_back(CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.0", 0.25)); |
| frames.push_back(CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.35", 0.5)); |
| frames.push_back(CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| frames[0]->SetEasing(cubic_ease_timing_function_.get()); |
| frames[1]->SetEasing(linear_timing_function_.get()); |
| frames[2]->SetEasing(cubic_custom_timing_function_.get()); |
| StringKeyframeEffectModel* effect = StringKeyframeEffectModel::Create(frames); |
| |
| timing_.timing_function = linear_timing_function_.get(); |
| timing_.iteration_duration = 2.0; |
| timing_.iteration_count = 10; |
| timing_.direction = Timing::PlaybackDirection::ALTERNATE_NORMAL; |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect); |
| EXPECT_EQ(CompositorTargetProperty::OPACITY, animation->TargetProperty()); |
| EXPECT_EQ(10.0, animation->Iterations()); |
| EXPECT_EQ(0, animation->TimeOffset()); |
| EXPECT_EQ(CompositorAnimation::Direction::ALTERNATE_NORMAL, |
| animation->GetDirection()); |
| EXPECT_EQ(1.0, animation->PlaybackRate()); |
| |
| std::unique_ptr<CompositorFloatAnimationCurve> keyframed_float_curve = |
| animation->FloatCurveForTesting(); |
| |
| CompositorFloatAnimationCurve::Keyframes keyframes = |
| keyframed_float_curve->KeyframesForTesting(); |
| ASSERT_EQ(4UL, keyframes.size()); |
| |
| EXPECT_EQ(0, keyframes[0]->Time() * timing_.iteration_duration); |
| EXPECT_EQ(0.2f, keyframes[0]->Value()); |
| ExpectKeyframeTimingFunctionCubic(*keyframes[0], |
| CubicBezierTimingFunction::EaseType::EASE); |
| |
| EXPECT_EQ(0.5, keyframes[1]->Time() * timing_.iteration_duration); |
| EXPECT_EQ(0, keyframes[1]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[1]->GetTimingFunctionForTesting()->GetType()); |
| |
| EXPECT_EQ(1.0, keyframes[2]->Time() * timing_.iteration_duration); |
| EXPECT_EQ(0.35f, keyframes[2]->Value()); |
| ExpectKeyframeTimingFunctionCubic( |
| *keyframes[2], CubicBezierTimingFunction::EaseType::CUSTOM); |
| |
| EXPECT_EQ(2.0, keyframes[3]->Time() * timing_.iteration_duration); |
| EXPECT_EQ(0.5f, keyframes[3]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[3]->GetTimingFunctionForTesting()->GetType()); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, createReversedOpacityAnimation) { |
| scoped_refptr<TimingFunction> cubic_easy_flip_timing_function = |
| CubicBezierTimingFunction::Create(0.0, 0.0, 0.0, 1.0); |
| |
| // KeyframeEffect to convert |
| StringKeyframeVector frames; |
| frames.push_back(CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0)); |
| frames.push_back(CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.0", 0.25)); |
| frames.push_back(CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.25", 0.5)); |
| frames.push_back(CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| frames[0]->SetEasing(CubicBezierTimingFunction::Preset( |
| CubicBezierTimingFunction::EaseType::EASE_IN)); |
| frames[1]->SetEasing(linear_timing_function_.get()); |
| frames[2]->SetEasing(cubic_easy_flip_timing_function.get()); |
| StringKeyframeEffectModel* effect = StringKeyframeEffectModel::Create(frames); |
| |
| timing_.timing_function = linear_timing_function_.get(); |
| timing_.iteration_count = 10; |
| timing_.direction = Timing::PlaybackDirection::ALTERNATE_REVERSE; |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect); |
| EXPECT_EQ(CompositorTargetProperty::OPACITY, animation->TargetProperty()); |
| EXPECT_EQ(10.0, animation->Iterations()); |
| EXPECT_EQ(0, animation->TimeOffset()); |
| EXPECT_EQ(CompositorAnimation::Direction::ALTERNATE_REVERSE, |
| animation->GetDirection()); |
| EXPECT_EQ(1.0, animation->PlaybackRate()); |
| |
| std::unique_ptr<CompositorFloatAnimationCurve> keyframed_float_curve = |
| animation->FloatCurveForTesting(); |
| |
| CompositorFloatAnimationCurve::Keyframes keyframes = |
| keyframed_float_curve->KeyframesForTesting(); |
| ASSERT_EQ(4UL, keyframes.size()); |
| |
| EXPECT_EQ(keyframed_float_curve->GetTimingFunctionForTesting()->GetType(), |
| TimingFunction::Type::LINEAR); |
| |
| EXPECT_EQ(0, keyframes[0]->Time()); |
| EXPECT_EQ(0.2f, keyframes[0]->Value()); |
| ExpectKeyframeTimingFunctionCubic( |
| *keyframes[0], CubicBezierTimingFunction::EaseType::EASE_IN); |
| |
| EXPECT_EQ(0.25, keyframes[1]->Time()); |
| EXPECT_EQ(0, keyframes[1]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[1]->GetTimingFunctionForTesting()->GetType()); |
| |
| EXPECT_EQ(0.5, keyframes[2]->Time()); |
| EXPECT_EQ(0.25f, keyframes[2]->Value()); |
| ExpectKeyframeTimingFunctionCubic( |
| *keyframes[2], CubicBezierTimingFunction::EaseType::CUSTOM); |
| |
| EXPECT_EQ(1.0, keyframes[3]->Time()); |
| EXPECT_EQ(0.5f, keyframes[3]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[3]->GetTimingFunctionForTesting()->GetType()); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| createReversedOpacityAnimationNegativeStartDelay) { |
| // KeyframeEffect to convert |
| StringKeyframeEffectModel* effect = CreateKeyframeEffectModel( |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| |
| const double kNegativeStartDelay = -3; |
| |
| timing_.iteration_count = 5.0; |
| timing_.iteration_duration = 1.5; |
| timing_.start_delay = kNegativeStartDelay; |
| timing_.direction = Timing::PlaybackDirection::ALTERNATE_REVERSE; |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect); |
| EXPECT_EQ(CompositorTargetProperty::OPACITY, animation->TargetProperty()); |
| EXPECT_EQ(5.0, animation->Iterations()); |
| EXPECT_EQ(-kNegativeStartDelay, animation->TimeOffset()); |
| EXPECT_EQ(CompositorAnimation::Direction::ALTERNATE_REVERSE, |
| animation->GetDirection()); |
| EXPECT_EQ(1.0, animation->PlaybackRate()); |
| |
| std::unique_ptr<CompositorFloatAnimationCurve> keyframed_float_curve = |
| animation->FloatCurveForTesting(); |
| |
| CompositorFloatAnimationCurve::Keyframes keyframes = |
| keyframed_float_curve->KeyframesForTesting(); |
| ASSERT_EQ(2UL, keyframes.size()); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| createSimpleOpacityAnimationPlaybackRates) { |
| // KeyframeEffect to convert |
| StringKeyframeEffectModel* effect = CreateKeyframeEffectModel( |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| |
| const double kPlaybackRate = 2; |
| const double kPlayerPlaybackRate = -1.5; |
| |
| timing_.playback_rate = kPlaybackRate; |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect, kPlayerPlaybackRate); |
| EXPECT_EQ(CompositorTargetProperty::OPACITY, animation->TargetProperty()); |
| EXPECT_EQ(1.0, animation->Iterations()); |
| EXPECT_EQ(0, animation->TimeOffset()); |
| EXPECT_EQ(CompositorAnimation::Direction::NORMAL, animation->GetDirection()); |
| EXPECT_EQ(kPlaybackRate * kPlayerPlaybackRate, animation->PlaybackRate()); |
| |
| std::unique_ptr<CompositorFloatAnimationCurve> keyframed_float_curve = |
| animation->FloatCurveForTesting(); |
| |
| CompositorFloatAnimationCurve::Keyframes keyframes = |
| keyframed_float_curve->KeyframesForTesting(); |
| ASSERT_EQ(2UL, keyframes.size()); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| createSimpleOpacityAnimationFillModeNone) { |
| // KeyframeEffect to convert |
| StringKeyframeEffectModel* effect = CreateKeyframeEffectModel( |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| |
| timing_.fill_mode = Timing::FillMode::NONE; |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect); |
| EXPECT_EQ(CompositorAnimation::FillMode::NONE, animation->GetFillMode()); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| createSimpleOpacityAnimationFillModeAuto) { |
| // KeyframeEffect to convert |
| StringKeyframeEffectModel* effect = CreateKeyframeEffectModel( |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| |
| timing_.fill_mode = Timing::FillMode::AUTO; |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect); |
| EXPECT_EQ(CompositorTargetProperty::OPACITY, animation->TargetProperty()); |
| EXPECT_EQ(1.0, animation->Iterations()); |
| EXPECT_EQ(0, animation->TimeOffset()); |
| EXPECT_EQ(CompositorAnimation::Direction::NORMAL, animation->GetDirection()); |
| EXPECT_EQ(1.0, animation->PlaybackRate()); |
| EXPECT_EQ(CompositorAnimation::FillMode::NONE, animation->GetFillMode()); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| createSimpleOpacityAnimationWithTimingFunction) { |
| // KeyframeEffect to convert |
| StringKeyframeEffectModel* effect = CreateKeyframeEffectModel( |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.2", 0), |
| CreateReplaceOpKeyframe(CSSPropertyOpacity, "0.5", 1.0)); |
| |
| timing_.timing_function = cubic_custom_timing_function_; |
| |
| std::unique_ptr<CompositorAnimation> animation = |
| ConvertToCompositorAnimation(*effect); |
| |
| std::unique_ptr<CompositorFloatAnimationCurve> keyframed_float_curve = |
| animation->FloatCurveForTesting(); |
| |
| auto curve_timing_function = |
| keyframed_float_curve->GetTimingFunctionForTesting(); |
| EXPECT_EQ(curve_timing_function->GetType(), |
| TimingFunction::Type::CUBIC_BEZIER); |
| const auto& cubic_timing_function = |
| ToCubicBezierTimingFunction(*curve_timing_function); |
| EXPECT_EQ(cubic_timing_function.GetEaseType(), |
| CubicBezierTimingFunction::EaseType::CUSTOM); |
| EXPECT_EQ(cubic_timing_function.X1(), 1.0); |
| EXPECT_EQ(cubic_timing_function.Y1(), 2.0); |
| EXPECT_EQ(cubic_timing_function.X2(), 3.0); |
| EXPECT_EQ(cubic_timing_function.Y2(), 4.0); |
| |
| CompositorFloatAnimationCurve::Keyframes keyframes = |
| keyframed_float_curve->KeyframesForTesting(); |
| ASSERT_EQ(2UL, keyframes.size()); |
| |
| EXPECT_EQ(0, keyframes[0]->Time()); |
| EXPECT_EQ(0.2f, keyframes[0]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[0]->GetTimingFunctionForTesting()->GetType()); |
| |
| EXPECT_EQ(1.0, keyframes[1]->Time()); |
| EXPECT_EQ(0.5f, keyframes[1]->Value()); |
| EXPECT_EQ(TimingFunction::Type::LINEAR, |
| keyframes[1]->GetTimingFunctionForTesting()->GetType()); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| cancelIncompatibleCompositorAnimations) { |
| Persistent<Element> element = document_->createElement("shared"); |
| |
| LayoutObjectProxy* layout_object = LayoutObjectProxy::Create(element.Get()); |
| element->SetLayoutObject(layout_object); |
| |
| StringKeyframeVector key_frames; |
| key_frames.push_back(CreateDefaultKeyframe(CSSPropertyOpacity, |
| EffectModel::kCompositeReplace, |
| 0.0) |
| .get()); |
| key_frames.push_back(CreateDefaultKeyframe(CSSPropertyOpacity, |
| EffectModel::kCompositeReplace, |
| 1.0) |
| .get()); |
| KeyframeEffectModelBase* animation_effect1 = |
| StringKeyframeEffectModel::Create(key_frames); |
| KeyframeEffectModelBase* animation_effect2 = |
| StringKeyframeEffectModel::Create(key_frames); |
| |
| Timing timing; |
| timing.iteration_duration = 1.f; |
| |
| // The first animation for opacity is ok to run on compositor. |
| KeyframeEffect* keyframe_effect1 = |
| KeyframeEffect::Create(element.Get(), animation_effect1, timing); |
| Animation* animation1 = timeline_->Play(keyframe_effect1); |
| auto style = ComputedStyle::Create(); |
| animation_effect1->SnapshotAllCompositorKeyframes(*element_.Get(), *style, |
| nullptr); |
| EXPECT_TRUE(CompositorAnimations::CheckCanStartEffectOnCompositor( |
| timing, *element.Get(), animation1, *animation_effect1, 1) |
| .Ok()); |
| |
| // simulate KeyframeEffect::maybeStartAnimationOnCompositor |
| Vector<int> compositor_animation_ids; |
| compositor_animation_ids.push_back(1); |
| keyframe_effect1->SetCompositorAnimationIdsForTesting( |
| compositor_animation_ids); |
| EXPECT_TRUE(animation1->HasActiveAnimationsOnCompositor()); |
| |
| // The second animation for opacity is not ok to run on compositor. |
| KeyframeEffect* keyframe_effect2 = |
| KeyframeEffect::Create(element.Get(), animation_effect2, timing); |
| Animation* animation2 = timeline_->Play(keyframe_effect2); |
| animation_effect2->SnapshotAllCompositorKeyframes(*element_.Get(), *style, |
| nullptr); |
| EXPECT_FALSE(CompositorAnimations::CheckCanStartEffectOnCompositor( |
| timing, *element.Get(), animation2, *animation_effect2, 1) |
| .Ok()); |
| EXPECT_FALSE(animation2->HasActiveAnimationsOnCompositor()); |
| |
| // A fallback to blink implementation needed, so cancel all compositor-side |
| // opacity animations for this element. |
| animation2->CancelIncompatibleAnimationsOnCompositor(); |
| |
| EXPECT_FALSE(animation1->HasActiveAnimationsOnCompositor()); |
| EXPECT_FALSE(animation2->HasActiveAnimationsOnCompositor()); |
| |
| SimulateFrame(0); |
| EXPECT_EQ(2U, element->GetElementAnimations()->Animations().size()); |
| SimulateFrame(1.); |
| |
| element->SetLayoutObject(nullptr); |
| LayoutObjectProxy::Dispose(layout_object); |
| |
| ThreadState::Current()->CollectAllGarbage(); |
| EXPECT_TRUE(element->GetElementAnimations()->Animations().IsEmpty()); |
| } |
| |
| namespace { |
| |
| void UpdateDummyTransformNode(ObjectPaintProperties& properties, |
| CompositingReasons reasons) { |
| properties.UpdateTransform(TransformPaintPropertyNode::Root(), |
| TransformationMatrix(), FloatPoint3D(), false, 0, |
| reasons); |
| } |
| |
| void UpdateDummyEffectNode(ObjectPaintProperties& properties, |
| CompositingReasons reasons) { |
| properties.UpdateEffect( |
| EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(), |
| ClipPaintPropertyNode::Root(), kColorFilterNone, |
| CompositorFilterOperations(), 1.0, SkBlendMode::kSrcOver, reasons); |
| } |
| |
| } // namespace |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| canStartElementOnCompositorTransformSPv2) { |
| Persistent<Element> element = document_->createElement("shared"); |
| LayoutObjectProxy* layout_object = LayoutObjectProxy::Create(element.Get()); |
| element->SetLayoutObject(layout_object); |
| |
| ScopedSlimmingPaintV2ForTest enable_s_pv2(true); |
| auto& properties = layout_object->GetMutableForPainting() |
| .FirstFragment() |
| .EnsureRarePaintData() |
| .EnsurePaintProperties(); |
| |
| // Add a transform with a compositing reason, which should allow starting |
| // animation. |
| UpdateDummyTransformNode(properties, kCompositingReasonActiveAnimation); |
| EXPECT_TRUE( |
| CompositorAnimations::CheckCanStartElementOnCompositor(*element).Ok()); |
| |
| // Setting to CompositingReasonNone should produce false. |
| UpdateDummyTransformNode(properties, kCompositingReasonNone); |
| EXPECT_FALSE( |
| CompositorAnimations::CheckCanStartElementOnCompositor(*element).Ok()); |
| |
| // Clearing the transform node entirely should also produce false. |
| properties.ClearTransform(); |
| EXPECT_FALSE( |
| CompositorAnimations::CheckCanStartElementOnCompositor(*element).Ok()); |
| |
| element->SetLayoutObject(nullptr); |
| LayoutObjectProxy::Dispose(layout_object); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, |
| canStartElementOnCompositorEffectSPv2) { |
| Persistent<Element> element = document_->createElement("shared"); |
| LayoutObjectProxy* layout_object = LayoutObjectProxy::Create(element.Get()); |
| element->SetLayoutObject(layout_object); |
| |
| ScopedSlimmingPaintV2ForTest enable_s_pv2(true); |
| auto& properties = layout_object->GetMutableForPainting() |
| .FirstFragment() |
| .EnsureRarePaintData() |
| .EnsurePaintProperties(); |
| |
| // Add an effect with a compositing reason, which should allow starting |
| // animation. |
| UpdateDummyEffectNode(properties, kCompositingReasonActiveAnimation); |
| EXPECT_TRUE( |
| CompositorAnimations::CheckCanStartElementOnCompositor(*element).Ok()); |
| |
| // Setting to CompositingReasonNone should produce false. |
| UpdateDummyEffectNode(properties, kCompositingReasonNone); |
| EXPECT_FALSE( |
| CompositorAnimations::CheckCanStartElementOnCompositor(*element).Ok()); |
| |
| // Clearing the effect node entirely should also produce false. |
| properties.ClearEffect(); |
| EXPECT_FALSE( |
| CompositorAnimations::CheckCanStartElementOnCompositor(*element).Ok()); |
| |
| element->SetLayoutObject(nullptr); |
| LayoutObjectProxy::Dispose(layout_object); |
| } |
| |
| TEST_F(AnimationCompositorAnimationsTest, canStartElementOnCompositorEffect) { |
| LoadTestData("transform-animation.html"); |
| Document* document = GetFrame()->GetDocument(); |
| Element* target = document->getElementById("target"); |
| const ObjectPaintProperties* properties = |
| target->GetLayoutObject()->FirstFragment().PaintProperties(); |
| EXPECT_TRUE(properties->Transform()->HasDirectCompositingReasons()); |
| CompositorAnimations::FailureCode code = |
| CompositorAnimations::CheckCanStartElementOnCompositor(*target); |
| EXPECT_EQ(code, CompositorAnimations::FailureCode::None()); |
| EXPECT_EQ(document->Timeline().PendingAnimationsCount(), 1u); |
| EXPECT_EQ(document->Timeline().MainThreadCompositableAnimationsCount(), 0u); |
| CompositorAnimationHost* host = |
| document->View()->GetCompositorAnimationHost(); |
| EXPECT_EQ(host->GetMainThreadAnimationsCountForTesting(), 0u); |
| EXPECT_EQ(host->GetMainThreadCompositableAnimationsCountForTesting(), 0u); |
| EXPECT_EQ(host->GetCompositedAnimationsCountForTesting(), 1u); |
| } |
| // TODO(xidachen): test temporary disabled due to crbug.com/781305. |
| TEST_F(AnimationCompositorAnimationsTest, |
| DISABLED_cannotStartElementOnCompositorEffectWithRuntimeFeature) { |
| ScopedTurnOff2DAndOpacityCompositorAnimationsForTest |
| turn_off_2d_and_opacity_compositors_animation(true); |
| LoadTestData("transform-animation.html"); |
| Document* document = GetFrame()->GetDocument(); |
| Element* target = document->getElementById("target"); |
| const ObjectPaintProperties* properties = |
| target->GetLayoutObject()->FirstFragment().PaintProperties(); |
| EXPECT_TRUE(properties->Transform()->HasDirectCompositingReasons()); |
| CompositorAnimations::FailureCode code = |
| CompositorAnimations::CheckCanStartElementOnCompositor(*target); |
| EXPECT_EQ( |
| code, |
| CompositorAnimations::FailureCode::NotPaintIntoOwnBacking( |
| "Acceleratable animation not accelerated due to an experiment")); |
| EXPECT_EQ(document->Timeline().PendingAnimationsCount(), 1u); |
| EXPECT_EQ(document->Timeline().MainThreadCompositableAnimationsCount(), 1u); |
| CompositorAnimationHost* host = |
| document->View()->GetCompositorAnimationHost(); |
| EXPECT_EQ(host->GetMainThreadAnimationsCountForTesting(), 1u); |
| EXPECT_EQ(host->GetMainThreadCompositableAnimationsCountForTesting(), 1u); |
| EXPECT_EQ(host->GetCompositedAnimationsCountForTesting(), 0u); |
| } |
| |
| } // namespace blink |