#include "content/browser/renderer_host/cursor_manager.h"
#include <memory>
#include <utility>
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/mock_render_widget_host.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/site_instance_group.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/test/mock_render_widget_host_delegate.h"
#include "content/test/test_render_view_host.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/gfx/geometry/point.h"
#if defined(USE_AURA) || BUILDFLAG(IS_MAC)
namespace content {
namespace {
const ui::Cursor kCursorHand(ui::mojom::CursorType::kHand);
const ui::Cursor kCursorCross(ui::mojom::CursorType::kCross);
const ui::Cursor kCursorPointer(ui::mojom::CursorType::kPointer);
ui::Cursor CreateCustomCursor(int width, int height) {
SkBitmap bitmap;
bitmap.allocN32Pixels(width, height);
bitmap.eraseColor(SK_ColorRED);
return ui::Cursor::NewCustom(std::move(bitmap), gfx::Point(0, 0),
1.0f);
}
const ui::Cursor kCursorCustom = CreateCustomCursor(0, 0);
class MockRenderWidgetHostViewForCursors : public TestRenderWidgetHostView {
public:
MockRenderWidgetHostViewForCursors(RenderWidgetHost* host, bool top_view)
: TestRenderWidgetHostView(host) {
if (top_view)
cursor_manager_ = std::make_unique<CursorManager>(this);
}
void DisplayCursor(const ui::Cursor& cursor) override {
current_cursor_ = cursor;
}
CursorManager* GetCursorManager() override { return cursor_manager_.get(); }
const ui::Cursor& cursor() { return current_cursor_; }
private:
ui::Cursor current_cursor_;
std::unique_ptr<CursorManager> cursor_manager_;
};
class CursorManagerTest : public testing::Test {
public:
CursorManagerTest() = default;
CursorManagerTest(const CursorManagerTest&) = delete;
CursorManagerTest& operator=(const CursorManagerTest&) = delete;
void SetUp() override {
browser_context_ = std::make_unique<TestBrowserContext>();
process_host_ =
std::make_unique<MockRenderProcessHost>(browser_context_.get());
site_instance_group_ =
base::WrapRefCounted(SiteInstanceGroup::CreateForTesting(
browser_context_.get(), process_host_.get()));
widget_host_ = MakeNewWidgetHost();
top_view_ =
new MockRenderWidgetHostViewForCursors(widget_host_.get(), true);
}
std::unique_ptr<RenderWidgetHostImpl> MakeNewWidgetHost() {
int32_t routing_id = process_host_->GetNextRoutingID();
return MockRenderWidgetHost::Create(
nullptr, &delegate_, site_instance_group_->GetSafeRef(),
routing_id);
}
void TearDown() override {
if (top_view_)
delete top_view_;
widget_host_ = nullptr;
process_host_->Cleanup();
site_instance_group_.reset();
process_host_ = nullptr;
}
protected:
BrowserTaskEnvironment task_environment_;
std::unique_ptr<BrowserContext> browser_context_;
std::unique_ptr<MockRenderProcessHost> process_host_;
scoped_refptr<SiteInstanceGroup> site_instance_group_;
std::unique_ptr<RenderWidgetHostImpl> widget_host_;
raw_ptr<MockRenderWidgetHostViewForCursors> top_view_;
MockRenderWidgetHostDelegate delegate_;
};
}
TEST_F(CursorManagerTest, CursorOnSingleView) {
top_view_->GetCursorManager()->UpdateViewUnderCursor(top_view_);
EXPECT_EQ(top_view_->cursor(), ui::Cursor());
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorHand);
EXPECT_EQ(top_view_->cursor(), kCursorHand);
}
TEST_F(CursorManagerTest, CursorOverChildView) {
std::unique_ptr<RenderWidgetHostImpl> widget_host(MakeNewWidgetHost());
std::unique_ptr<MockRenderWidgetHostViewForCursors> child_view(
new MockRenderWidgetHostViewForCursors(widget_host.get(), false));
top_view_->GetCursorManager()->UpdateCursor(child_view.get(), kCursorHand);
EXPECT_NE(top_view_->cursor(), kCursorHand);
top_view_->GetCursorManager()->UpdateViewUnderCursor(child_view.get());
EXPECT_EQ(top_view_->cursor(), kCursorHand);
top_view_->GetCursorManager()->ViewBeingDestroyed(child_view.get());
EXPECT_NE(top_view_->cursor(), kCursorHand);
}
TEST_F(CursorManagerTest, CursorOverMultipleChildViews) {
std::unique_ptr<RenderWidgetHostImpl> widget_host1(MakeNewWidgetHost());
std::unique_ptr<MockRenderWidgetHostViewForCursors> child_view1(
new MockRenderWidgetHostViewForCursors(widget_host1.get(), false));
std::unique_ptr<RenderWidgetHostImpl> widget_host2(MakeNewWidgetHost());
std::unique_ptr<MockRenderWidgetHostViewForCursors> child_view2(
new MockRenderWidgetHostViewForCursors(widget_host2.get(), false));
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorHand);
top_view_->GetCursorManager()->UpdateCursor(child_view1.get(), kCursorCross);
top_view_->GetCursorManager()->UpdateCursor(child_view2.get(),
kCursorPointer);
EXPECT_EQ(top_view_->cursor(), kCursorHand);
top_view_->GetCursorManager()->UpdateViewUnderCursor(child_view1.get());
EXPECT_EQ(top_view_->cursor(), kCursorCross);
top_view_->GetCursorManager()->UpdateViewUnderCursor(child_view2.get());
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
top_view_->GetCursorManager()->UpdateCursor(child_view1.get(), kCursorHand);
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
top_view_->GetCursorManager()->UpdateCursor(child_view2.get(), kCursorCross);
EXPECT_EQ(top_view_->cursor(), kCursorCross);
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorHand);
EXPECT_EQ(top_view_->cursor(), kCursorCross);
top_view_->GetCursorManager()->ViewBeingDestroyed(child_view1.get());
EXPECT_EQ(top_view_->cursor(), kCursorCross);
top_view_->GetCursorManager()->ViewBeingDestroyed(child_view2.get());
EXPECT_EQ(top_view_->cursor(), kCursorHand);
}
TEST_F(CursorManagerTest,
CustomCursorDisallowedScope_CustomCursorsAreNotAllowed) {
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorCustom);
EXPECT_EQ(top_view_->cursor(), kCursorCustom);
{
auto disallow_scope =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope(
0);
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
}
EXPECT_EQ(top_view_->cursor(), kCursorCustom);
}
TEST_F(CursorManagerTest,
CustomCursorDisallowedScope_CustomCursorsAreNotAllowedAboveSizeLimit) {
const ui::Cursor kCursorCustomLarge = CreateCustomCursor(20, 50);
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorCustomLarge);
EXPECT_EQ(top_view_->cursor(), kCursorCustomLarge);
{
auto disallow_scope =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope(
40);
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
}
EXPECT_EQ(top_view_->cursor(), kCursorCustomLarge);
}
TEST_F(CursorManagerTest,
CustomCursorDisallowedScope_CustomCursorsAreAllowedBelowSizeLimit) {
const ui::Cursor kCursorCustomLarge = CreateCustomCursor(20, 35);
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorCustomLarge);
EXPECT_EQ(top_view_->cursor(), kCursorCustomLarge);
{
auto disallow_scope =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope(
40);
EXPECT_EQ(top_view_->cursor(), kCursorCustomLarge);
}
EXPECT_EQ(top_view_->cursor(), kCursorCustomLarge);
}
TEST_F(CursorManagerTest,
CustomCursorDisallowedScope_CustomCursorSubjectToMultipleSizeLimits) {
const ui::Cursor kCursorCustomLarge = CreateCustomCursor(20, 35);
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorCustomLarge);
EXPECT_EQ(top_view_->cursor(), kCursorCustomLarge);
{
auto disallow_scope1 =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope(
40);
EXPECT_EQ(top_view_->cursor(), kCursorCustomLarge);
auto disallow_scope2 =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope(
30);
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
disallow_scope1.RunAndReset();
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
}
EXPECT_EQ(top_view_->cursor(), kCursorCustomLarge);
}
TEST_F(CursorManagerTest,
CustomCursorDisallowedScope_OtherCursorsStillAllowed) {
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorHand);
EXPECT_EQ(top_view_->cursor(), kCursorHand);
{
auto disallow_scope =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope();
EXPECT_EQ(top_view_->cursor(), kCursorHand);
}
EXPECT_EQ(top_view_->cursor(), kCursorHand);
}
TEST_F(CursorManagerTest,
CustomCursorDisallowedScope_CustomCursorSetDuringScope) {
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorHand);
EXPECT_EQ(top_view_->cursor(), kCursorHand);
{
auto disallow_scope =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope();
EXPECT_EQ(top_view_->cursor(), kCursorHand);
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorCustom);
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
}
EXPECT_EQ(top_view_->cursor(), kCursorCustom);
}
TEST_F(CursorManagerTest,
CustomCursorDisallowedScope_CustomCursorRemovedDuringScope) {
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorCustom);
EXPECT_EQ(top_view_->cursor(), kCursorCustom);
{
auto disallow_scope =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope();
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorHand);
EXPECT_EQ(top_view_->cursor(), kCursorHand);
}
EXPECT_EQ(top_view_->cursor(), kCursorHand);
}
TEST_F(CursorManagerTest, CustomCursorDisallowedScope_MultipleScopes) {
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorCustom);
EXPECT_EQ(top_view_->cursor(), kCursorCustom);
auto disallow_scope1 =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope();
auto disallow_scope2 =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope();
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
disallow_scope1.RunAndReset();
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
auto disallow_scope3 =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope();
disallow_scope2.RunAndReset();
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
disallow_scope3.RunAndReset();
EXPECT_EQ(top_view_->cursor(), kCursorCustom);
}
TEST_F(CursorManagerTest, CustomCursorDisallowedScope_CustomCursorViewFocused) {
std::unique_ptr<RenderWidgetHostImpl> widget_host(MakeNewWidgetHost());
std::unique_ptr<MockRenderWidgetHostViewForCursors> child_view(
new MockRenderWidgetHostViewForCursors(widget_host.get(), false));
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorHand);
top_view_->GetCursorManager()->UpdateCursor(child_view.get(), kCursorCustom);
EXPECT_EQ(top_view_->cursor(), kCursorHand);
{
auto disallow_scope =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope();
EXPECT_EQ(top_view_->cursor(), kCursorHand);
top_view_->GetCursorManager()->UpdateViewUnderCursor(child_view.get());
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
}
EXPECT_EQ(top_view_->cursor(), kCursorCustom);
}
TEST_F(CursorManagerTest,
CustomCursorDisallowedScope_CustomCursorViewFocusRemoved) {
std::unique_ptr<RenderWidgetHostImpl> widget_host(MakeNewWidgetHost());
std::unique_ptr<MockRenderWidgetHostViewForCursors> child_view(
new MockRenderWidgetHostViewForCursors(widget_host.get(), false));
top_view_->GetCursorManager()->UpdateCursor(top_view_, kCursorHand);
top_view_->GetCursorManager()->UpdateCursor(child_view.get(), kCursorCustom);
top_view_->GetCursorManager()->UpdateViewUnderCursor(child_view.get());
EXPECT_EQ(top_view_->cursor(), kCursorCustom);
{
auto disallow_scope =
top_view_->GetCursorManager()->CreateDisallowCustomCursorScope();
EXPECT_EQ(top_view_->cursor(), kCursorPointer);
top_view_->GetCursorManager()->UpdateViewUnderCursor(top_view_);
EXPECT_EQ(top_view_->cursor(), kCursorHand);
}
EXPECT_EQ(top_view_->cursor(), kCursorHand);
}
}
#endif