| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/android/overscroll_controller_android.h" |
| #include <memory> |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/raw_ptr_exclusion.h" |
| #include "cc/input/overscroll_behavior.h" |
| #include "cc/layers/layer.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/input/web_gesture_event.h" |
| #include "third_party/blink/public/common/input/web_input_event.h" |
| #include "ui/android/overscroll_glow.h" |
| #include "ui/android/overscroll_refresh.h" |
| #include "ui/android/resources/resource_manager_impl.h" |
| #include "ui/android/window_android_compositor.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/events/blink/did_overscroll_params.h" |
| |
| using ::testing::_; |
| using ::testing::Return; |
| using ui::EdgeEffect; |
| using ui::OverscrollGlow; |
| using ui::OverscrollGlowClient; |
| using ui::OverscrollRefresh; |
| using ui::ResourceManager; |
| using ui::WindowAndroidCompositor; |
| |
| namespace content { |
| |
| namespace { |
| |
| class MockCompositor : public WindowAndroidCompositor { |
| public: |
| ~MockCompositor() override = default; |
| std::unique_ptr<ReadbackRef> TakeReadbackRef(const viz::SurfaceId&) override { |
| return nullptr; |
| } |
| void RequestCopyOfOutputOnRootLayer( |
| std::unique_ptr<viz::CopyOutputRequest>) override {} |
| void SetNeedsAnimate() override {} |
| MOCK_METHOD0(GetResourceManager, ResourceManager&()); |
| MOCK_METHOD0(GetFrameSinkId, viz::FrameSinkId()); |
| void AddChildFrameSink(const viz::FrameSinkId& frame_sink_id) override {} |
| void RemoveChildFrameSink(const viz::FrameSinkId& frame_sink_id) override {} |
| bool IsDrawingFirstVisibleFrame() const override { return false; } |
| void SetVSyncPaused(bool paused) override {} |
| void OnUpdateRefreshRate(float refresh_rate) override {} |
| void OnUpdateSupportedRefreshRates( |
| const std::vector<float>& supported_refresh_rates) override {} |
| std::unique_ptr<ui::CompositorLock> GetCompositorLock( |
| base::TimeDelta timeout) override { |
| return nullptr; |
| } |
| void OnUpdateOverlayTransform() override {} |
| void PostRequestSuccessfulPresentationTimeForNextFrame( |
| SuccessfulPresentationTimeCallback callback) override {} |
| }; |
| |
| class MockGlowClient : public OverscrollGlowClient { |
| public: |
| MOCK_METHOD0(CreateEdgeEffect, std::unique_ptr<EdgeEffect>()); |
| }; |
| |
| class MockGlow : public OverscrollGlow { |
| public: |
| MockGlow() : OverscrollGlow(new MockGlowClient()) {} |
| MOCK_METHOD5(OnOverscrolled, |
| bool(base::TimeTicks, |
| gfx::Vector2dF, |
| gfx::Vector2dF, |
| gfx::Vector2dF, |
| gfx::Vector2dF)); |
| }; |
| |
| class MockRefresh : public OverscrollRefresh { |
| public: |
| MockRefresh() : OverscrollRefresh() {} |
| MOCK_METHOD1(OnOverscrolled, void(const cc::OverscrollBehavior& behavior)); |
| MOCK_METHOD0(Reset, void()); |
| MOCK_CONST_METHOD0(IsActive, bool()); |
| MOCK_CONST_METHOD0(IsAwaitingScrollUpdateAck, bool()); |
| }; |
| |
| class OverscrollControllerAndroidUnitTest : public testing::Test { |
| public: |
| OverscrollControllerAndroidUnitTest() { |
| dip_scale_ = 560; |
| std::unique_ptr<MockGlow> glow_ptr = std::make_unique<MockGlow>(); |
| std::unique_ptr<MockRefresh> refresh_ptr = std::make_unique<MockRefresh>(); |
| compositor_ = std::make_unique<MockCompositor>(); |
| glow_ = glow_ptr.get(); |
| refresh_ = refresh_ptr.get(); |
| controller_ = OverscrollControllerAndroid::CreateForTests( |
| compositor_.get(), dip_scale_, std::move(glow_ptr), |
| std::move(refresh_ptr)); |
| } |
| |
| ui::DidOverscrollParams CreateVerticalOverscrollParams() { |
| ui::DidOverscrollParams params; |
| params.accumulated_overscroll = gfx::Vector2dF(0, 1); |
| params.latest_overscroll_delta = gfx::Vector2dF(0, 1); |
| params.current_fling_velocity = gfx::Vector2dF(0, 1); |
| params.causal_event_viewport_point = gfx::PointF(100, 100); |
| params.accumulated_overscroll.Scale(dip_scale_); |
| params.latest_overscroll_delta.Scale(dip_scale_); |
| params.current_fling_velocity.Scale(dip_scale_); |
| params.causal_event_viewport_point.Scale(dip_scale_); |
| |
| return params; |
| } |
| |
| protected: |
| raw_ptr<MockGlow> glow_; |
| // This field is not a raw_ptr<> because it was filtered by the rewriter for: |
| // #addr-of |
| RAW_PTR_EXCLUSION MockRefresh* refresh_; |
| std::unique_ptr<MockCompositor> compositor_; |
| std::unique_ptr<OverscrollControllerAndroid> controller_; |
| float dip_scale_; |
| }; |
| |
| TEST_F(OverscrollControllerAndroidUnitTest, |
| OverscrollBehaviorYAutoAllowsRefresh) { |
| ui::DidOverscrollParams params = CreateVerticalOverscrollParams(); |
| params.overscroll_behavior.y = cc::OverscrollBehavior::Type::kAuto; |
| |
| // Test that refresh is activated but glow is not rendered. |
| EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(true)); |
| EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).Times(0); |
| EXPECT_CALL(*glow_, OnOverscrolled(_, _, _, _, _)).Times(0); |
| |
| controller_->OnOverscrolled(params); |
| testing::Mock::VerifyAndClearExpectations(&refresh_); |
| } |
| |
| TEST_F(OverscrollControllerAndroidUnitTest, |
| OverscrollBehaviorYContainAllowsGlowOnly) { |
| ui::DidOverscrollParams params = CreateVerticalOverscrollParams(); |
| params.overscroll_behavior.y = cc::OverscrollBehavior::Type::kContain; |
| |
| EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(false)); |
| EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).WillOnce(Return(false)); |
| EXPECT_CALL(*glow_, |
| OnOverscrolled(_, gfx::Vector2dF(0, 560), gfx::Vector2dF(0, 560), |
| gfx::Vector2dF(0, 560), _)); |
| |
| controller_->OnOverscrolled(params); |
| testing::Mock::VerifyAndClearExpectations(refresh_); |
| testing::Mock::VerifyAndClearExpectations(glow_); |
| |
| // Test that the "contain" set on x-axis would not affect navigation. |
| params.overscroll_behavior.y = cc::OverscrollBehavior::Type::kAuto; |
| params.overscroll_behavior.x = cc::OverscrollBehavior::Type::kContain; |
| |
| EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(true)); |
| EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).Times(0); |
| EXPECT_CALL(*glow_, OnOverscrolled(_, _, _, _, _)).Times(0); |
| |
| controller_->OnOverscrolled(params); |
| testing::Mock::VerifyAndClearExpectations(refresh_); |
| testing::Mock::VerifyAndClearExpectations(glow_); |
| } |
| |
| TEST_F(OverscrollControllerAndroidUnitTest, |
| OverscrollBehaviorYNonePreventsGlowAndRefresh) { |
| ui::DidOverscrollParams params = CreateVerticalOverscrollParams(); |
| params.overscroll_behavior.y = cc::OverscrollBehavior::Type::kNone; |
| |
| EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(false)); |
| EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).WillOnce(Return(false)); |
| EXPECT_CALL(*glow_, OnOverscrolled(_, gfx::Vector2dF(), gfx::Vector2dF(), |
| gfx::Vector2dF(), _)); |
| |
| controller_->OnOverscrolled(params); |
| testing::Mock::VerifyAndClearExpectations(refresh_); |
| testing::Mock::VerifyAndClearExpectations(glow_); |
| |
| // Test that the "none" set on y-axis would not affect glow on x-axis. |
| params.accumulated_overscroll = gfx::Vector2dF(1, 1); |
| params.latest_overscroll_delta = gfx::Vector2dF(1, 1); |
| params.current_fling_velocity = gfx::Vector2dF(1, 1); |
| params.accumulated_overscroll.Scale(dip_scale_); |
| params.latest_overscroll_delta.Scale(dip_scale_); |
| params.current_fling_velocity.Scale(dip_scale_); |
| |
| EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(false)); |
| EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).WillOnce(Return(false)); |
| EXPECT_CALL(*glow_, |
| OnOverscrolled(_, gfx::Vector2dF(560, 0), gfx::Vector2dF(560, 0), |
| gfx::Vector2dF(560, 0), _)); |
| |
| controller_->OnOverscrolled(params); |
| testing::Mock::VerifyAndClearExpectations(refresh_); |
| testing::Mock::VerifyAndClearExpectations(glow_); |
| } |
| |
| TEST_F(OverscrollControllerAndroidUnitTest, |
| ConsumedUpdateDoesNotResetEnabledRefresh) { |
| ui::DidOverscrollParams params = CreateVerticalOverscrollParams(); |
| params.overscroll_behavior.y = cc::OverscrollBehavior::Type::kAuto; |
| |
| EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(true)); |
| EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).WillOnce(Return(false)); |
| |
| // Enable the refresh effect. |
| controller_->OnOverscrolled(params); |
| |
| // Generate a consumed scroll update. |
| blink::WebGestureEvent event(blink::WebInputEvent::Type::kGestureScrollUpdate, |
| blink::WebInputEvent::kNoModifiers, |
| ui::EventTimeForNow()); |
| controller_->OnGestureEventAck( |
| event, blink::mojom::InputEventResultState::kConsumed); |
| |
| testing::Mock::VerifyAndClearExpectations(&refresh_); |
| } |
| |
| } // namespace |
| |
| } // namespace content |