| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <stddef.h> | 
 |  | 
 | #include "base/pickle.h" | 
 | #include "build/build_config.h" | 
 | #include "content/common/cursors/webcursor.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "third_party/WebKit/public/platform/WebCursorInfo.h" | 
 | #include "third_party/skia/include/core/SkImageInfo.h" | 
 |  | 
 | #if defined(OS_WIN) | 
 | #include <windows.h> | 
 | #endif | 
 |  | 
 | using blink::WebCursorInfo; | 
 |  | 
 | namespace content { | 
 |  | 
 | TEST(WebCursorTest, OKCursorSerialization) { | 
 |   WebCursor custom_cursor; | 
 |   // This is a valid custom cursor. | 
 |   base::Pickle ok_custom_pickle; | 
 |   // Type and hotspots. | 
 |   ok_custom_pickle.WriteInt(WebCursorInfo::TypeCustom); | 
 |   ok_custom_pickle.WriteInt(0); | 
 |   ok_custom_pickle.WriteInt(0); | 
 |   // X & Y | 
 |   ok_custom_pickle.WriteInt(1); | 
 |   ok_custom_pickle.WriteInt(1); | 
 |   // Scale | 
 |   ok_custom_pickle.WriteFloat(1.0); | 
 |   // Data len including enough data for a 1x1 image. | 
 |   ok_custom_pickle.WriteInt(4); | 
 |   ok_custom_pickle.WriteUInt32(0); | 
 |   // Custom Windows message. | 
 |   ok_custom_pickle.WriteUInt32(0); | 
 |   base::PickleIterator iter(ok_custom_pickle); | 
 |   EXPECT_TRUE(custom_cursor.Deserialize(&iter)); | 
 | } | 
 |  | 
 | TEST(WebCursorTest, BrokenCursorSerialization) { | 
 |   WebCursor custom_cursor; | 
 |   // This custom cursor has not been send with enough data. | 
 |   base::Pickle short_custom_pickle; | 
 |   // Type and hotspots. | 
 |   short_custom_pickle.WriteInt(WebCursorInfo::TypeCustom); | 
 |   short_custom_pickle.WriteInt(0); | 
 |   short_custom_pickle.WriteInt(0); | 
 |   // X & Y | 
 |   short_custom_pickle.WriteInt(1); | 
 |   short_custom_pickle.WriteInt(1); | 
 |   // Scale | 
 |   short_custom_pickle.WriteFloat(1.0); | 
 |   // Data len not including enough data for a 1x1 image. | 
 |   short_custom_pickle.WriteInt(3); | 
 |   short_custom_pickle.WriteUInt32(0); | 
 |   base::PickleIterator iter(short_custom_pickle); | 
 |   EXPECT_FALSE(custom_cursor.Deserialize(&iter)); | 
 |  | 
 |   // This custom cursor has enough data but is too big. | 
 |   base::Pickle large_custom_pickle; | 
 |   // Type and hotspots. | 
 |   large_custom_pickle.WriteInt(WebCursorInfo::TypeCustom); | 
 |   large_custom_pickle.WriteInt(0); | 
 |   large_custom_pickle.WriteInt(0); | 
 |   // X & Y | 
 |   static const int kTooBigSize = 4096 + 1; | 
 |   large_custom_pickle.WriteInt(kTooBigSize); | 
 |   large_custom_pickle.WriteInt(1); | 
 |   // Scale | 
 |   large_custom_pickle.WriteFloat(1.0); | 
 |   // Data len including enough data for a 4097x1 image. | 
 |   large_custom_pickle.WriteInt(kTooBigSize * 4); | 
 |   for (int i = 0; i < kTooBigSize; ++i) | 
 |     large_custom_pickle.WriteUInt32(0); | 
 |   iter = base::PickleIterator(large_custom_pickle); | 
 |   EXPECT_FALSE(custom_cursor.Deserialize(&iter)); | 
 |  | 
 |   // This custom cursor uses negative lengths. | 
 |   base::Pickle neg_custom_pickle; | 
 |   // Type and hotspots. | 
 |   neg_custom_pickle.WriteInt(WebCursorInfo::TypeCustom); | 
 |   neg_custom_pickle.WriteInt(0); | 
 |   neg_custom_pickle.WriteInt(0); | 
 |   // X & Y | 
 |   neg_custom_pickle.WriteInt(-1); | 
 |   neg_custom_pickle.WriteInt(-1); | 
 |   // Scale | 
 |   neg_custom_pickle.WriteFloat(1.0); | 
 |   // Data len including enough data for a 1x1 image. | 
 |   neg_custom_pickle.WriteInt(4); | 
 |   neg_custom_pickle.WriteUInt32(0); | 
 |   // Custom Windows message. | 
 |   neg_custom_pickle.WriteUInt32(0); | 
 |   iter = base::PickleIterator(neg_custom_pickle); | 
 |   EXPECT_FALSE(custom_cursor.Deserialize(&iter)); | 
 |  | 
 |   // This custom cursor uses zero scale. | 
 |   base::Pickle scale_zero_custom_pickle; | 
 |   // Type and hotspots. | 
 |   scale_zero_custom_pickle.WriteInt(WebCursorInfo::TypeCustom); | 
 |   scale_zero_custom_pickle.WriteInt(0); | 
 |   scale_zero_custom_pickle.WriteInt(0); | 
 |   // X & Y | 
 |   scale_zero_custom_pickle.WriteInt(1); | 
 |   scale_zero_custom_pickle.WriteInt(1); | 
 |   // Scale | 
 |   scale_zero_custom_pickle.WriteFloat(0); | 
 |   // Data len including enough data for a 1x1 image. | 
 |   scale_zero_custom_pickle.WriteInt(4); | 
 |   scale_zero_custom_pickle.WriteUInt32(0); | 
 |   // Custom Windows message. | 
 |   scale_zero_custom_pickle.WriteUInt32(0); | 
 |   iter = base::PickleIterator(scale_zero_custom_pickle); | 
 |   EXPECT_FALSE(custom_cursor.Deserialize(&iter)); | 
 |  | 
 |   // This custom cursor uses tiny scale. | 
 |   base::Pickle scale_tiny_custom_pickle; | 
 |   // Type and hotspots. | 
 |   scale_tiny_custom_pickle.WriteInt(WebCursorInfo::TypeCustom); | 
 |   scale_tiny_custom_pickle.WriteInt(0); | 
 |   scale_tiny_custom_pickle.WriteInt(0); | 
 |   // X & Y | 
 |   scale_tiny_custom_pickle.WriteInt(1); | 
 |   scale_tiny_custom_pickle.WriteInt(1); | 
 |   // Scale | 
 |   scale_tiny_custom_pickle.WriteFloat(0.001f); | 
 |   // Data len including enough data for a 1x1 image. | 
 |   scale_tiny_custom_pickle.WriteInt(4); | 
 |   scale_tiny_custom_pickle.WriteUInt32(0); | 
 |   // Custom Windows message. | 
 |   scale_tiny_custom_pickle.WriteUInt32(0); | 
 |   iter = base::PickleIterator(scale_tiny_custom_pickle); | 
 |   EXPECT_FALSE(custom_cursor.Deserialize(&iter)); | 
 | } | 
 |  | 
 | TEST(WebCursorTest, ClampHotspot) { | 
 |   WebCursor custom_cursor; | 
 |   // This is a valid custom cursor. | 
 |   base::Pickle ok_custom_pickle; | 
 |   // Type and hotspots. | 
 |   ok_custom_pickle.WriteInt(WebCursorInfo::TypeCustom); | 
 |   // Hotspot is invalid --- outside the bounds of the image. | 
 |   ok_custom_pickle.WriteInt(5); | 
 |   ok_custom_pickle.WriteInt(5); | 
 |   // X & Y | 
 |   ok_custom_pickle.WriteInt(2); | 
 |   ok_custom_pickle.WriteInt(2); | 
 |   // Scale | 
 |   ok_custom_pickle.WriteFloat(1.0); | 
 |   // Data len including enough data for a 2x2 image. | 
 |   ok_custom_pickle.WriteInt(4 * 4); | 
 |   for (size_t i = 0; i < 4; i++) | 
 |     ok_custom_pickle.WriteUInt32(0); | 
 |   // Custom Windows message. | 
 |   ok_custom_pickle.WriteUInt32(0); | 
 |   base::PickleIterator iter(ok_custom_pickle); | 
 |   EXPECT_TRUE(custom_cursor.Deserialize(&iter)); | 
 |  | 
 |   // Convert to WebCursorInfo, make sure the hotspot got clamped. | 
 |   WebCursor::CursorInfo info; | 
 |   custom_cursor.GetCursorInfo(&info); | 
 |   EXPECT_EQ(gfx::Point(1, 1), info.hotspot); | 
 |  | 
 |   // Set hotspot to an invalid point again, pipe back through WebCursor, | 
 |   // and make sure the hotspot got clamped again. | 
 |   info.hotspot = gfx::Point(-1, -1); | 
 |   custom_cursor.InitFromCursorInfo(info); | 
 |   custom_cursor.GetCursorInfo(&info); | 
 |   EXPECT_EQ(gfx::Point(0, 0), info.hotspot); | 
 | } | 
 |  | 
 | TEST(WebCursorTest, EmptyImage) { | 
 |   WebCursor custom_cursor; | 
 |   base::Pickle broken_cursor_pickle; | 
 |   broken_cursor_pickle.WriteInt(WebCursorInfo::TypeCustom); | 
 |   // Hotspot is at origin | 
 |   broken_cursor_pickle.WriteInt(0); | 
 |   broken_cursor_pickle.WriteInt(0); | 
 |   // X & Y are empty | 
 |   broken_cursor_pickle.WriteInt(0); | 
 |   broken_cursor_pickle.WriteInt(0); | 
 |   // Scale | 
 |   broken_cursor_pickle.WriteFloat(1.0); | 
 |   // No data for the image since the size is 0. | 
 |   broken_cursor_pickle.WriteInt(0); | 
 |   // Custom Windows message. | 
 |   broken_cursor_pickle.WriteInt(0); | 
 |  | 
 |   // Make sure we can read this on all platforms; it is technicaally a valid | 
 |   // cursor. | 
 |   base::PickleIterator iter(broken_cursor_pickle); | 
 |   EXPECT_TRUE(custom_cursor.Deserialize(&iter)); | 
 | } | 
 |  | 
 | TEST(WebCursorTest, Scale2) { | 
 |   WebCursor custom_cursor; | 
 |   // This is a valid custom cursor. | 
 |   base::Pickle ok_custom_pickle; | 
 |   // Type and hotspots. | 
 |   ok_custom_pickle.WriteInt(WebCursorInfo::TypeCustom); | 
 |   ok_custom_pickle.WriteInt(0); | 
 |   ok_custom_pickle.WriteInt(0); | 
 |   // X & Y | 
 |   ok_custom_pickle.WriteInt(1); | 
 |   ok_custom_pickle.WriteInt(1); | 
 |   // Scale - 2 image pixels per UI pixel. | 
 |   ok_custom_pickle.WriteFloat(2.0); | 
 |   // Data len including enough data for a 1x1 image. | 
 |   ok_custom_pickle.WriteInt(4); | 
 |   ok_custom_pickle.WriteUInt32(0); | 
 |   // Custom Windows message. | 
 |   ok_custom_pickle.WriteUInt32(0); | 
 |   base::PickleIterator iter(ok_custom_pickle); | 
 |   EXPECT_TRUE(custom_cursor.Deserialize(&iter)); | 
 | } | 
 |  | 
 | TEST(WebCursorTest, AlphaConversion) { | 
 |   SkBitmap bitmap; | 
 |   SkPMColor testColor = SkPreMultiplyARGB(10, 255, 255, 255); | 
 |   bitmap.allocN32Pixels(1,1); | 
 |   SkAutoLockPixels bitmap_lock(bitmap); | 
 |   *bitmap.getAddr32(0, 0) = testColor; | 
 |   WebCursor::CursorInfo cursor_info; | 
 |   cursor_info.type = WebCursorInfo::TypeCustom; | 
 |   cursor_info.custom_image = bitmap; | 
 |   cursor_info.image_scale_factor = 1; | 
 |   WebCursor custom_cursor; | 
 |  | 
 |   // This round trip will convert the cursor to unpremultiplied form. | 
 |   custom_cursor.InitFromCursorInfo(cursor_info); | 
 |   custom_cursor.GetCursorInfo(&cursor_info); | 
 |   { | 
 |     SkAutoLockPixels lock(cursor_info.custom_image); | 
 |     EXPECT_EQ(kUnpremul_SkAlphaType, cursor_info.custom_image.alphaType()); | 
 |     EXPECT_EQ(testColor, | 
 |               SkPreMultiplyColor(*cursor_info.custom_image.getAddr32(0,0))); | 
 |   } | 
 |  | 
 |   // Second round trip should not do any conversion because data is already | 
 |   // unpremultiplied. | 
 |   custom_cursor.InitFromCursorInfo(cursor_info); | 
 |   custom_cursor.GetCursorInfo(&cursor_info); | 
 |   { | 
 |     SkAutoLockPixels lock(cursor_info.custom_image); | 
 |     EXPECT_EQ(kUnpremul_SkAlphaType, cursor_info.custom_image.alphaType()); | 
 |     EXPECT_EQ(testColor, | 
 |               SkPreMultiplyColor(*cursor_info.custom_image.getAddr32(0,0))); | 
 |   } | 
 |  | 
 | #if defined(OS_MACOSX) | 
 |   // On MacOS, test roundtrip through NSCursor conversion. | 
 |   WebCursor custom_cursor_copy; | 
 |   custom_cursor_copy.InitFromNSCursor(custom_cursor.GetNativeCursor()); | 
 |   custom_cursor_copy.GetCursorInfo(&cursor_info); | 
 |   { | 
 |     SkAutoLockPixels lock(cursor_info.custom_image); | 
 |     EXPECT_EQ(kUnpremul_SkAlphaType, cursor_info.custom_image.alphaType()); | 
 |     EXPECT_EQ(testColor, | 
 |               SkPreMultiplyColor(*cursor_info.custom_image.getAddr32(0,0))); | 
 |   } | 
 | #endif | 
 | } | 
 |  | 
 | #if defined(USE_AURA) | 
 | TEST(WebCursorTest, CursorScaleFactor) { | 
 |   display::Display display; | 
 |   display.set_device_scale_factor(80.2f); | 
 |  | 
 |   WebCursor::CursorInfo info; | 
 |   info.image_scale_factor = 2.0f; | 
 |  | 
 |   WebCursor cursor; | 
 |   cursor.InitFromCursorInfo(info); | 
 |   cursor.SetDisplayInfo(display); | 
 |  | 
 |   EXPECT_EQ(40.1f, cursor.GetCursorScaleFactor()); | 
 | } | 
 |  | 
 | TEST(WebCursorTest, UnscaledImageCopy) { | 
 |   WebCursor::CursorInfo info; | 
 |   info.type = WebCursorInfo::TypeCustom; | 
 |   info.hotspot = gfx::Point(0, 1); | 
 |  | 
 |   SkImageInfo image_info = SkImageInfo::MakeN32(2, 2, kUnpremul_SkAlphaType); | 
 |   info.custom_image = SkBitmap(); | 
 |   info.custom_image.setInfo(image_info); | 
 |   info.custom_image.allocN32Pixels(2, 2); | 
 |   info.custom_image.eraseColor(0xFFFFFFFF); | 
 |  | 
 |   WebCursor cursor; | 
 |   cursor.InitFromCursorInfo(info); | 
 |  | 
 |   SkBitmap image_copy; | 
 |   gfx::Point hotspot; | 
 |   cursor.CreateScaledBitmapAndHotspotFromCustomData(&image_copy, &hotspot); | 
 |  | 
 |   EXPECT_EQ(kBGRA_8888_SkColorType, image_copy.colorType()); | 
 |   EXPECT_EQ(kUnpremul_SkAlphaType, image_copy.alphaType()); | 
 |   EXPECT_EQ(2, image_copy.width()); | 
 |   EXPECT_EQ(2, image_copy.height()); | 
 |   EXPECT_EQ(0, hotspot.x()); | 
 |   EXPECT_EQ(1, hotspot.y()); | 
 | } | 
 |  | 
 | TEST(WebCursorTest, CopyDeviceScaleFactor) { | 
 |   WebCursor cursor1; | 
 |   EXPECT_EQ(1.f, cursor1.GetCursorScaleFactor()); | 
 |  | 
 |   display::Display display; | 
 |   display.set_device_scale_factor(19.333f); | 
 |   cursor1.SetDisplayInfo(display); | 
 |   WebCursor cursor2 = cursor1; | 
 |   EXPECT_EQ(19.333f, cursor2.GetCursorScaleFactor()); | 
 | } | 
 | #endif | 
 |  | 
 | #if defined(OS_WIN) | 
 | namespace { | 
 |  | 
 | void ScaleCursor(float scale_factor, int hotspot_x, int hotspot_y) { | 
 |   display::Display display; | 
 |   display.set_device_scale_factor(scale_factor); | 
 |  | 
 |   WebCursor::CursorInfo info; | 
 |   info.type = WebCursorInfo::TypeCustom; | 
 |   info.hotspot = gfx::Point(hotspot_x, hotspot_y); | 
 |  | 
 |   info.custom_image = SkBitmap(); | 
 |   info.custom_image.allocN32Pixels(10, 10); | 
 |   info.custom_image.eraseColor(0); | 
 |  | 
 |   WebCursor cursor; | 
 |   cursor.SetDisplayInfo(display); | 
 |   cursor.InitFromCursorInfo(info); | 
 |  | 
 |   HCURSOR windows_cursor_handle = cursor.GetPlatformCursor(); | 
 |   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_factor * hotspot_x), | 
 |             windows_icon_info.xHotspot); | 
 |   EXPECT_EQ(static_cast<DWORD>(scale_factor * hotspot_y), | 
 |             windows_icon_info.yHotspot); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | TEST(WebCursorTest, WindowsCursorScaledAtHiDpi) { | 
 |   ScaleCursor(2.0f, 4, 6); | 
 |   ScaleCursor(1.5f, 2, 8); | 
 |   ScaleCursor(1.25f, 3, 7); | 
 | } | 
 | #endif | 
 |  | 
 | }  // namespace content |