blob: 9ff76704c44f7486341fdfb00c28be83ecb75eff [file] [log] [blame]
// Copyright 2019 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/common/cursors/webcursor.h"
#include <stddef.h>
#include <optional>
#include "build/build_config.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/aura/client/cursor_shape_client.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/display/screen_base.h"
#include "ui/display/test/test_screen.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/wm/core/cursor_loader.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "ui/base/win/win_cursor.h"
#endif
namespace content {
namespace {
class TestScreen : public display::test::TestScreen {
public:
explicit TestScreen(display::Display display)
: display::test::TestScreen(/*create_display=*/false,
/*register_screen=*/true) {
display_list().AddDisplay(display, display::DisplayList::Type::PRIMARY);
}
};
TEST(WebCursorTest, DefaultConstructor) {
WebCursor webcursor;
EXPECT_EQ(ui::mojom::CursorType::kNull, webcursor.cursor().type());
}
TEST(WebCursorTest, WebCursorCursorConstructor) {
ui::Cursor cursor(ui::mojom::CursorType::kHand);
WebCursor webcursor(cursor);
EXPECT_EQ(cursor, webcursor.cursor());
}
TEST(WebCursorTest, WebCursorCursorConstructorCustom) {
const ui::Cursor cursor = ui::Cursor::NewCustom(
gfx::test::CreateBitmap(/*size=*/32), gfx::Point(10, 20), 2.0f);
WebCursor webcursor(cursor);
EXPECT_EQ(cursor, webcursor.cursor());
#if defined(USE_AURA)
auto cursor_shape_client = std::make_unique<wm::CursorLoader>();
aura::client::SetCursorShapeClient(cursor_shape_client.get());
// Test if the custom cursor is correctly cached and updated
// on aura platform.
EXPECT_FALSE(webcursor.has_custom_cursor_for_test());
gfx::NativeCursor native_cursor = webcursor.GetNativeCursor();
EXPECT_EQ(gfx::Point(5, 10), native_cursor.custom_hotspot());
EXPECT_TRUE(webcursor.has_custom_cursor_for_test());
// Test if the rotating custom cursor works correctly.
display::Display display(/*id=*/1);
display.set_panel_rotation(display::Display::ROTATE_90);
TestScreen screen(display);
webcursor.UpdateDisplayInfoForWindow(nullptr);
#if BUILDFLAG(IS_CHROMEOS)
EXPECT_FALSE(webcursor.has_custom_cursor_for_test());
#else
EXPECT_TRUE(webcursor.has_custom_cursor_for_test());
#endif
native_cursor = webcursor.GetNativeCursor();
EXPECT_TRUE(webcursor.has_custom_cursor_for_test());
#if BUILDFLAG(IS_CHROMEOS)
// Hotspot should be scaled & rotated. We're using the icon created for 2.0,
// on the display with dsf=1.0, so the hotspot should be
// ((32 - 20) / 2, 10 / 2) = (6, 5).
EXPECT_EQ(gfx::Point(6, 5), native_cursor.custom_hotspot());
#else
// For non-CrOS platforms, the cursor mustn't be rotated as logical and
// physical location is the same.
EXPECT_EQ(gfx::Point(5, 10), native_cursor.custom_hotspot());
#endif
aura::client::SetCursorShapeClient(nullptr);
#endif // defined(USE_AURA)
}
#if defined(USE_AURA)
TEST(WebCursorTest, CursorScaleFactor) {
auto cursor_shape_client = std::make_unique<wm::CursorLoader>();
aura::client::SetCursorShapeClient(cursor_shape_client.get());
constexpr float kImageScale = 2.0f;
constexpr float kDeviceScale = 4.2f;
WebCursor webcursor(ui::Cursor::NewCustom(
gfx::test::CreateBitmap(/*size=*/128), gfx::Point(0, 1), kImageScale));
display::Display display(/*id=*/1);
display.set_device_scale_factor(kDeviceScale);
TestScreen screen(display);
webcursor.UpdateDisplayInfoForWindow(nullptr);
#if BUILDFLAG(IS_CHROMEOS)
// In Ash, the size of the cursor is capped at 64px unless the hardware
// advertises support for bigger cursors.
const gfx::Size kDefaultMaxSize = gfx::Size(64, 64);
EXPECT_EQ(gfx::SkISizeToSize(
webcursor.GetNativeCursor().custom_bitmap().dimensions()),
kDefaultMaxSize);
#else
EXPECT_EQ(
gfx::SkISizeToSize(
webcursor.GetNativeCursor().custom_bitmap().dimensions()),
gfx::ScaleToFlooredSize(gfx::Size(128, 128), kDeviceScale / kImageScale));
#endif
// The scale factor of the cursor image should match the device scale factor,
// regardless of the cursor size.
EXPECT_EQ(webcursor.GetNativeCursor().image_scale_factor(), kDeviceScale);
aura::client::SetCursorShapeClient(nullptr);
}
#endif // defined(USE_AURA)
#if BUILDFLAG(IS_WIN)
void ScaleCursor(float scale, int hotspot_x, int hotspot_y) {
WebCursor webcursor(ui::Cursor::NewCustom(
gfx::test::CreateBitmap(/*size=*/10), gfx::Point(hotspot_x, hotspot_y)));
display::Display display(/*id=*/1);
display.set_device_scale_factor(scale);
TestScreen screen(display);
webcursor.UpdateDisplayInfoForWindow(nullptr);
HCURSOR windows_cursor_handle =
ui::WinCursor::FromPlatformCursor(webcursor.GetNativeCursor().platform())
->hcursor();
EXPECT_NE(nullptr, windows_cursor_handle);
ICONINFO windows_icon_info;
EXPECT_TRUE(GetIconInfo(windows_cursor_handle, &windows_icon_info));
EXPECT_FALSE(windows_icon_info.fIcon);
EXPECT_EQ(static_cast<DWORD>(scale * hotspot_x), windows_icon_info.xHotspot);
EXPECT_EQ(static_cast<DWORD>(scale * hotspot_y), windows_icon_info.yHotspot);
}
TEST(WebCursorTest, WindowsCursorScaledAtHiDpi) {
auto cursor_shape_client = std::make_unique<wm::CursorLoader>();
aura::client::SetCursorShapeClient(cursor_shape_client.get());
ScaleCursor(2.0f, 4, 6);
ScaleCursor(1.5f, 2, 8);
ScaleCursor(1.25f, 3, 7);
aura::client::SetCursorShapeClient(nullptr);
}
#endif
} // namespace
} // namespace content