| // 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 |