Avi Drissman | 4e1b7bc3 | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2017 The Chromium Authors |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "content/browser/android/overscroll_controller_android.h" |
Kartar Singh | 30ae5d5 | 2024-11-28 17:13:16 | [diff] [blame] | 6 | |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 7 | #include <memory> |
Kartar Singh | 30ae5d5 | 2024-11-28 17:13:16 | [diff] [blame] | 8 | |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 9 | #include "base/memory/raw_ptr.h" |
Jinsuk Kim | d04b59e4 | 2019-01-25 02:44:36 | [diff] [blame] | 10 | #include "cc/input/overscroll_behavior.h" |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 11 | #include "cc/layers/layer.h" |
Kartar Singh | 30ae5d5 | 2024-11-28 17:13:16 | [diff] [blame] | 12 | #include "gpu/ipc/common/surface_handle.h" |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 13 | #include "testing/gmock/include/gmock/gmock.h" |
| 14 | #include "testing/gtest/include/gtest/gtest.h" |
Dave Tapuska | 129cef8 | 2019-12-19 16:36:48 | [diff] [blame] | 15 | #include "third_party/blink/public/common/input/web_gesture_event.h" |
| 16 | #include "third_party/blink/public/common/input/web_input_event.h" |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 17 | #include "ui/android/overscroll_glow.h" |
| 18 | #include "ui/android/overscroll_refresh.h" |
| 19 | #include "ui/android/resources/resource_manager_impl.h" |
| 20 | #include "ui/android/window_android_compositor.h" |
Sandra Sun | 3fbdd163 | 2017-09-05 15:01:47 | [diff] [blame] | 21 | #include "ui/events/base_event_utils.h" |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 22 | #include "ui/events/blink/did_overscroll_params.h" |
| 23 | |
Andrew Grieve | 68dd21b4 | 2020-07-16 17:54:07 | [diff] [blame] | 24 | using ::testing::_; |
| 25 | using ::testing::Return; |
| 26 | using ui::EdgeEffect; |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 27 | using ui::OverscrollGlow; |
| 28 | using ui::OverscrollGlowClient; |
| 29 | using ui::OverscrollRefresh; |
Andrew Grieve | 68dd21b4 | 2020-07-16 17:54:07 | [diff] [blame] | 30 | using ui::ResourceManager; |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 31 | using ui::WindowAndroidCompositor; |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 32 | |
| 33 | namespace content { |
| 34 | |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 35 | class MockCompositor : public WindowAndroidCompositor { |
| 36 | public: |
William Liu | 3ac45a3c | 2023-05-11 18:35:28 | [diff] [blame] | 37 | ~MockCompositor() override = default; |
William Liu | f38f2a7 | 2024-10-06 04:02:44 | [diff] [blame] | 38 | ui::WindowAndroidCompositor::ScopedKeepSurfaceAliveCallback |
| 39 | TakeScopedKeepSurfaceAliveCallback(const viz::SurfaceId&) override { |
| 40 | return base::OnceClosure(); |
William Liu | 3ac45a3c | 2023-05-11 18:35:28 | [diff] [blame] | 41 | } |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 42 | void RequestCopyOfOutputOnRootLayer( |
| 43 | std::unique_ptr<viz::CopyOutputRequest>) override {} |
| 44 | void SetNeedsAnimate() override {} |
| 45 | MOCK_METHOD0(GetResourceManager, ResourceManager&()); |
| 46 | MOCK_METHOD0(GetFrameSinkId, viz::FrameSinkId()); |
Kartar Singh | 30ae5d5 | 2024-11-28 17:13:16 | [diff] [blame] | 47 | gpu::SurfaceHandle GetSurfaceHandle() override { |
| 48 | return gpu::kNullSurfaceHandle; |
| 49 | } |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 50 | void AddChildFrameSink(const viz::FrameSinkId& frame_sink_id) override {} |
| 51 | void RemoveChildFrameSink(const viz::FrameSinkId& frame_sink_id) override {} |
Eric Karl | 465a525 | 2018-01-10 21:16:27 | [diff] [blame] | 52 | bool IsDrawingFirstVisibleFrame() const override { return false; } |
Khushal | a042481 | 2019-03-13 10:05:43 | [diff] [blame] | 53 | void OnUpdateRefreshRate(float refresh_rate) override {} |
Khushal | 5187432 | 2019-04-18 23:55:40 | [diff] [blame] | 54 | void OnUpdateSupportedRefreshRates( |
| 55 | const std::vector<float>& supported_refresh_rates) override {} |
Bo Liu | 15828fe4 | 2025-03-13 01:34:44 | [diff] [blame] | 56 | void OnAdaptiveRefreshRateInfoChanged() override {} |
Clark DuVall | 3d91432d | 2021-12-07 20:29:44 | [diff] [blame] | 57 | std::unique_ptr<ui::CompositorLock> GetCompositorLock( |
| 58 | base::TimeDelta timeout) override { |
| 59 | return nullptr; |
| 60 | } |
sangheon77.kim | dd441f9 | 2022-08-31 01:15:08 | [diff] [blame] | 61 | void OnUpdateOverlayTransform() override {} |
Mohsen Izadi | cc48229 | 2023-02-01 16:18:57 | [diff] [blame] | 62 | void PostRequestSuccessfulPresentationTimeForNextFrame( |
| 63 | SuccessfulPresentationTimeCallback callback) override {} |
Peilin Wang | a1e9e2ea | 2024-05-14 14:44:59 | [diff] [blame] | 64 | void AddFrameSubmissionObserver(FrameSubmissionObserver* observer) override {} |
| 65 | void RemoveFrameSubmissionObserver( |
| 66 | FrameSubmissionObserver* observer) override {} |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 67 | }; |
| 68 | |
| 69 | class MockGlowClient : public OverscrollGlowClient { |
| 70 | public: |
Andrew Grieve | 68dd21b4 | 2020-07-16 17:54:07 | [diff] [blame] | 71 | MOCK_METHOD0(CreateEdgeEffect, std::unique_ptr<EdgeEffect>()); |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 72 | }; |
| 73 | |
| 74 | class MockGlow : public OverscrollGlow { |
| 75 | public: |
| 76 | MockGlow() : OverscrollGlow(new MockGlowClient()) {} |
| 77 | MOCK_METHOD5(OnOverscrolled, |
| 78 | bool(base::TimeTicks, |
| 79 | gfx::Vector2dF, |
| 80 | gfx::Vector2dF, |
| 81 | gfx::Vector2dF, |
| 82 | gfx::Vector2dF)); |
| 83 | }; |
| 84 | |
| 85 | class MockRefresh : public OverscrollRefresh { |
| 86 | public: |
| 87 | MockRefresh() : OverscrollRefresh() {} |
Aman Verma | 5508a3bc0 | 2025-03-24 13:15:39 | [diff] [blame] | 88 | MOCK_METHOD2(OnOverscrolled, |
| 89 | void(const cc::OverscrollBehavior& behavior, |
| 90 | gfx::Vector2dF accumulated_overscroll)); |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 91 | MOCK_METHOD0(Reset, void()); |
| 92 | MOCK_CONST_METHOD0(IsActive, bool()); |
| 93 | MOCK_CONST_METHOD0(IsAwaitingScrollUpdateAck, bool()); |
| 94 | }; |
| 95 | |
| 96 | class OverscrollControllerAndroidUnitTest : public testing::Test { |
| 97 | public: |
| 98 | OverscrollControllerAndroidUnitTest() { |
Jaebaek Seo | d055eca | 2018-01-31 00:57:42 | [diff] [blame] | 99 | dip_scale_ = 560; |
Jeremy Roman | 04f27c37 | 2017-10-27 15:20:55 | [diff] [blame] | 100 | std::unique_ptr<MockGlow> glow_ptr = std::make_unique<MockGlow>(); |
| 101 | std::unique_ptr<MockRefresh> refresh_ptr = std::make_unique<MockRefresh>(); |
| 102 | compositor_ = std::make_unique<MockCompositor>(); |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 103 | glow_ = glow_ptr.get(); |
| 104 | refresh_ = refresh_ptr.get(); |
| 105 | controller_ = OverscrollControllerAndroid::CreateForTests( |
Jaebaek Seo | d055eca | 2018-01-31 00:57:42 | [diff] [blame] | 106 | compositor_.get(), dip_scale_, std::move(glow_ptr), |
| 107 | std::move(refresh_ptr)); |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 108 | } |
| 109 | |
| 110 | ui::DidOverscrollParams CreateVerticalOverscrollParams() { |
| 111 | ui::DidOverscrollParams params; |
| 112 | params.accumulated_overscroll = gfx::Vector2dF(0, 1); |
| 113 | params.latest_overscroll_delta = gfx::Vector2dF(0, 1); |
| 114 | params.current_fling_velocity = gfx::Vector2dF(0, 1); |
| 115 | params.causal_event_viewport_point = gfx::PointF(100, 100); |
Chris Harrelson | 317f184 | 2022-03-23 19:04:17 | [diff] [blame] | 116 | params.accumulated_overscroll.Scale(dip_scale_); |
| 117 | params.latest_overscroll_delta.Scale(dip_scale_); |
| 118 | params.current_fling_velocity.Scale(dip_scale_); |
| 119 | params.causal_event_viewport_point.Scale(dip_scale_); |
| 120 | |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 121 | return params; |
| 122 | } |
| 123 | |
| 124 | protected: |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 125 | raw_ptr<MockGlow> glow_; |
Nan Lin | 3c31905 | 2024-06-13 17:18:38 | [diff] [blame] | 126 | raw_ptr<MockRefresh> refresh_; |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 127 | std::unique_ptr<MockCompositor> compositor_; |
| 128 | std::unique_ptr<OverscrollControllerAndroid> controller_; |
Jaebaek Seo | d055eca | 2018-01-31 00:57:42 | [diff] [blame] | 129 | float dip_scale_; |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 130 | }; |
| 131 | |
| 132 | TEST_F(OverscrollControllerAndroidUnitTest, |
Jinsuk Kim | d04b59e4 | 2019-01-25 02:44:36 | [diff] [blame] | 133 | OverscrollBehaviorYAutoAllowsRefresh) { |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 134 | ui::DidOverscrollParams params = CreateVerticalOverscrollParams(); |
Xida Chen | 209548b | 2020-09-01 16:18:51 | [diff] [blame] | 135 | params.overscroll_behavior.y = cc::OverscrollBehavior::Type::kAuto; |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 136 | |
Jinsuk Kim | d04b59e4 | 2019-01-25 02:44:36 | [diff] [blame] | 137 | // Test that refresh is activated but glow is not rendered. |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 138 | EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(true)); |
| 139 | EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).Times(0); |
| 140 | EXPECT_CALL(*glow_, OnOverscrolled(_, _, _, _, _)).Times(0); |
| 141 | |
| 142 | controller_->OnOverscrolled(params); |
| 143 | testing::Mock::VerifyAndClearExpectations(&refresh_); |
| 144 | } |
| 145 | |
| 146 | TEST_F(OverscrollControllerAndroidUnitTest, |
Jinsuk Kim | d04b59e4 | 2019-01-25 02:44:36 | [diff] [blame] | 147 | OverscrollBehaviorYContainAllowsGlowOnly) { |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 148 | ui::DidOverscrollParams params = CreateVerticalOverscrollParams(); |
Xida Chen | 209548b | 2020-09-01 16:18:51 | [diff] [blame] | 149 | params.overscroll_behavior.y = cc::OverscrollBehavior::Type::kContain; |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 150 | |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 151 | EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(false)); |
| 152 | EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).WillOnce(Return(false)); |
| 153 | EXPECT_CALL(*glow_, |
| 154 | OnOverscrolled(_, gfx::Vector2dF(0, 560), gfx::Vector2dF(0, 560), |
| 155 | gfx::Vector2dF(0, 560), _)); |
| 156 | |
| 157 | controller_->OnOverscrolled(params); |
| 158 | testing::Mock::VerifyAndClearExpectations(refresh_); |
| 159 | testing::Mock::VerifyAndClearExpectations(glow_); |
| 160 | |
| 161 | // Test that the "contain" set on x-axis would not affect navigation. |
Xida Chen | 209548b | 2020-09-01 16:18:51 | [diff] [blame] | 162 | params.overscroll_behavior.y = cc::OverscrollBehavior::Type::kAuto; |
| 163 | params.overscroll_behavior.x = cc::OverscrollBehavior::Type::kContain; |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 164 | |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 165 | EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(true)); |
| 166 | EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).Times(0); |
| 167 | EXPECT_CALL(*glow_, OnOverscrolled(_, _, _, _, _)).Times(0); |
| 168 | |
| 169 | controller_->OnOverscrolled(params); |
| 170 | testing::Mock::VerifyAndClearExpectations(refresh_); |
| 171 | testing::Mock::VerifyAndClearExpectations(glow_); |
| 172 | } |
| 173 | |
| 174 | TEST_F(OverscrollControllerAndroidUnitTest, |
Jinsuk Kim | d04b59e4 | 2019-01-25 02:44:36 | [diff] [blame] | 175 | OverscrollBehaviorYNonePreventsGlowAndRefresh) { |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 176 | ui::DidOverscrollParams params = CreateVerticalOverscrollParams(); |
Xida Chen | 209548b | 2020-09-01 16:18:51 | [diff] [blame] | 177 | params.overscroll_behavior.y = cc::OverscrollBehavior::Type::kNone; |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 178 | |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 179 | EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(false)); |
| 180 | EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).WillOnce(Return(false)); |
| 181 | EXPECT_CALL(*glow_, OnOverscrolled(_, gfx::Vector2dF(), gfx::Vector2dF(), |
| 182 | gfx::Vector2dF(), _)); |
| 183 | |
| 184 | controller_->OnOverscrolled(params); |
| 185 | testing::Mock::VerifyAndClearExpectations(refresh_); |
| 186 | testing::Mock::VerifyAndClearExpectations(glow_); |
| 187 | |
| 188 | // Test that the "none" set on y-axis would not affect glow on x-axis. |
| 189 | params.accumulated_overscroll = gfx::Vector2dF(1, 1); |
| 190 | params.latest_overscroll_delta = gfx::Vector2dF(1, 1); |
| 191 | params.current_fling_velocity = gfx::Vector2dF(1, 1); |
Chris Harrelson | 317f184 | 2022-03-23 19:04:17 | [diff] [blame] | 192 | params.accumulated_overscroll.Scale(dip_scale_); |
| 193 | params.latest_overscroll_delta.Scale(dip_scale_); |
| 194 | params.current_fling_velocity.Scale(dip_scale_); |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 195 | |
sunyunjia | bbea8a9 | 2017-08-31 11:18:54 | [diff] [blame] | 196 | EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(false)); |
| 197 | EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).WillOnce(Return(false)); |
| 198 | EXPECT_CALL(*glow_, |
| 199 | OnOverscrolled(_, gfx::Vector2dF(560, 0), gfx::Vector2dF(560, 0), |
| 200 | gfx::Vector2dF(560, 0), _)); |
| 201 | |
| 202 | controller_->OnOverscrolled(params); |
| 203 | testing::Mock::VerifyAndClearExpectations(refresh_); |
| 204 | testing::Mock::VerifyAndClearExpectations(glow_); |
| 205 | } |
| 206 | |
Sandra Sun | 3fbdd163 | 2017-09-05 15:01:47 | [diff] [blame] | 207 | TEST_F(OverscrollControllerAndroidUnitTest, |
Aman Verma | 5508a3bc0 | 2025-03-24 13:15:39 | [diff] [blame] | 208 | ConsumedBeginDoesNotResetEnabledRefresh) { |
Sandra Sun | 3fbdd163 | 2017-09-05 15:01:47 | [diff] [blame] | 209 | ui::DidOverscrollParams params = CreateVerticalOverscrollParams(); |
Xida Chen | 209548b | 2020-09-01 16:18:51 | [diff] [blame] | 210 | params.overscroll_behavior.y = cc::OverscrollBehavior::Type::kAuto; |
Sandra Sun | 3fbdd163 | 2017-09-05 15:01:47 | [diff] [blame] | 211 | |
Sandra Sun | 3fbdd163 | 2017-09-05 15:01:47 | [diff] [blame] | 212 | EXPECT_CALL(*refresh_, IsActive()).WillOnce(Return(true)); |
| 213 | EXPECT_CALL(*refresh_, IsAwaitingScrollUpdateAck()).WillOnce(Return(false)); |
Sandra Sun | 3fbdd163 | 2017-09-05 15:01:47 | [diff] [blame] | 214 | |
| 215 | // Enable the refresh effect. |
| 216 | controller_->OnOverscrolled(params); |
| 217 | |
Aman Verma | 5508a3bc0 | 2025-03-24 13:15:39 | [diff] [blame] | 218 | // Generate a consumed scroll begin. |
| 219 | blink::WebGestureEvent event(blink::WebInputEvent::Type::kGestureScrollBegin, |
Daniel Cheng | 224569ee | 2018-04-25 05:45:06 | [diff] [blame] | 220 | blink::WebInputEvent::kNoModifiers, |
| 221 | ui::EventTimeForNow()); |
Dave Tapuska | e01e0fde | 2020-04-20 18:28:41 | [diff] [blame] | 222 | controller_->OnGestureEventAck( |
| 223 | event, blink::mojom::InputEventResultState::kConsumed); |
Sandra Sun | 3fbdd163 | 2017-09-05 15:01:47 | [diff] [blame] | 224 | |
| 225 | testing::Mock::VerifyAndClearExpectations(&refresh_); |
| 226 | } |
| 227 | |
Sandra Sun | 2288e853 | 2017-11-30 22:50:16 | [diff] [blame] | 228 | } // namespace content |