// 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 "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 {}
void AddFrameSubmissionObserver(FrameSubmissionObserver* observer) override {}
void RemoveFrameSubmissionObserver(
FrameSubmissionObserver* observer) 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_;
raw_ptr<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