0

Refactor content::WebCursor to use SkBitmap via CursorInfo.

Replace some WebCursor fields with a CursorInfo struct member.
(avoids inefficient custom SkBitmap handling and some redundancy)

Other content::WebCursor refactoring:
 Replace InitFromCursorInfo with a similar constructor.
 Replace IsEqual function with an equality operator.
 Remove "custom data" (bitmap byte array) handling.
 Remove assignment operator (copy ctor is sufficient).
 Remove unused (after test removal) InitFromNSCursor.
 Use standard SkBitmap [de]serialization helpers.
 Inline/cleanup/remove more; update unit tests.

Minor content::CursorInfo refactoring:
 Add a ctor that takes a blink::WebCursorInfo.
 Add a function that retrieves a blink::WebCursorInfo.
 Remove similar content/renderer/cursor_utils.h helpers.
 Add an equality operator to support WebCursor.

Refactor CreateCursorFromDIB to take an SkBitmap.

TODO: All IsPlatformDataEqual impls just return true; fix or remove.
TODO: Leverage CreateHICONFromSkBitmap in IconUtil::CreateCursorFromSkBitmap?
TODO: Restore SkBitmap alpha type conversion? (see WebCursor::CreateCustomData)
TODO: Consolidate blink::WebCursorInfo and content::CursorInfo?
TODO: Consolidate ui::Cursor and content::WebCursor?
TODO: Further reduce cursor operations on each mouse movement.

Bug: 950176
Test: No [custom] web cursor regressions, eg. alpha (crbug.com/432043)
Change-Id: I48f74035f98190338127b6d7945418a7c8d1f728
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1562755
Reviewed-by: Martin Barbella <mbarbella@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: Nico Weber <thakis@chromium.org>
Reviewed-by: Bret Sepulveda <bsep@chromium.org>
Commit-Queue: Michael Wasserman <msw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#651547}
This commit is contained in:
Mike Wasserman
2019-04-17 00:44:21 +00:00
committed by Commit Bot
parent d8ab0716ed
commit f95c2d9dd0
29 changed files with 366 additions and 857 deletions

@ -39,7 +39,7 @@ class MockRenderWidgetHostViewForCursors : public TestRenderWidgetHostView {
CursorManager* GetCursorManager() override { return cursor_manager_.get(); }
WebCursor cursor() { return current_cursor_; }
const WebCursor& cursor() { return current_cursor_; }
private:
WebCursor current_cursor_;
@ -128,17 +128,16 @@ TEST_F(CursorManagerTest, CursorOnSingleView) {
top_view_->GetCursorManager()->UpdateViewUnderCursor(top_view_);
// The view should be using the default cursor.
EXPECT_TRUE(top_view_->cursor().IsEqual(WebCursor()));
EXPECT_EQ(top_view_->cursor(), WebCursor());
CursorInfo cursor_info(blink::WebCursorInfo::kTypeHand);
WebCursor cursor_hand;
cursor_hand.InitFromCursorInfo(cursor_info);
WebCursor cursor_hand(cursor_info);
// Update the view with a non-default cursor.
top_view_->GetCursorManager()->UpdateCursor(top_view_, cursor_hand);
// Verify the RenderWidgetHostView now uses the correct cursor.
EXPECT_TRUE(top_view_->cursor().IsEqual(cursor_hand));
EXPECT_EQ(top_view_->cursor(), cursor_hand);
}
// Verify cursor interactions between a parent frame and an out-of-process
@ -149,21 +148,20 @@ TEST_F(CursorManagerTest, CursorOverChildView) {
new MockRenderWidgetHostViewForCursors(widget_host.get(), false));
CursorInfo cursor_info(blink::WebCursorInfo::kTypeHand);
WebCursor cursor_hand;
cursor_hand.InitFromCursorInfo(cursor_info);
WebCursor cursor_hand(cursor_info);
// Set the child frame's cursor to a hand. This should not propagate to the
// top-level view without the mouse moving over the child frame.
top_view_->GetCursorManager()->UpdateCursor(child_view.get(), cursor_hand);
EXPECT_FALSE(top_view_->cursor().IsEqual(cursor_hand));
EXPECT_NE(top_view_->cursor(), cursor_hand);
// Now moving the mouse over the child frame should update the overall cursor.
top_view_->GetCursorManager()->UpdateViewUnderCursor(child_view.get());
EXPECT_TRUE(top_view_->cursor().IsEqual(cursor_hand));
EXPECT_EQ(top_view_->cursor(), cursor_hand);
// Destruction of the child view should restore the parent frame's cursor.
top_view_->GetCursorManager()->ViewBeingDestroyed(child_view.get());
EXPECT_FALSE(top_view_->cursor().IsEqual(cursor_hand));
EXPECT_NE(top_view_->cursor(), cursor_hand);
}
// Verify interactions between two independent OOPIFs, including interleaving
@ -178,46 +176,43 @@ TEST_F(CursorManagerTest, CursorOverMultipleChildViews) {
new MockRenderWidgetHostViewForCursors(widget_host2.get(), false));
CursorInfo cursor_info_hand(blink::WebCursorInfo::kTypeHand);
WebCursor cursor_hand;
cursor_hand.InitFromCursorInfo(cursor_info_hand);
WebCursor cursor_hand(cursor_info_hand);
CursorInfo cursor_info_cross(blink::WebCursorInfo::kTypeCross);
WebCursor cursor_cross;
cursor_cross.InitFromCursorInfo(cursor_info_cross);
WebCursor cursor_cross(cursor_info_cross);
CursorInfo cursor_info_pointer(blink::WebCursorInfo::kTypePointer);
WebCursor cursor_pointer;
cursor_pointer.InitFromCursorInfo(cursor_info_pointer);
WebCursor cursor_pointer(cursor_info_pointer);
// Initialize each View to a different cursor.
top_view_->GetCursorManager()->UpdateCursor(top_view_, cursor_hand);
top_view_->GetCursorManager()->UpdateCursor(child_view1.get(), cursor_cross);
top_view_->GetCursorManager()->UpdateCursor(child_view2.get(),
cursor_pointer);
EXPECT_TRUE(top_view_->cursor().IsEqual(cursor_hand));
EXPECT_EQ(top_view_->cursor(), cursor_hand);
// Simulate moving the mouse between child views and receiving cursor updates.
top_view_->GetCursorManager()->UpdateViewUnderCursor(child_view1.get());
EXPECT_TRUE(top_view_->cursor().IsEqual(cursor_cross));
EXPECT_EQ(top_view_->cursor(), cursor_cross);
top_view_->GetCursorManager()->UpdateViewUnderCursor(child_view2.get());
EXPECT_TRUE(top_view_->cursor().IsEqual(cursor_pointer));
EXPECT_EQ(top_view_->cursor(), cursor_pointer);
// Simulate cursor updates to both child views and the parent view. An
// update to child_view1 or the parent view should not change the current
// cursor because the mouse is over child_view2.
top_view_->GetCursorManager()->UpdateCursor(child_view1.get(), cursor_hand);
EXPECT_TRUE(top_view_->cursor().IsEqual(cursor_pointer));
EXPECT_EQ(top_view_->cursor(), cursor_pointer);
top_view_->GetCursorManager()->UpdateCursor(child_view2.get(), cursor_cross);
EXPECT_TRUE(top_view_->cursor().IsEqual(cursor_cross));
EXPECT_EQ(top_view_->cursor(), cursor_cross);
top_view_->GetCursorManager()->UpdateCursor(top_view_, cursor_hand);
EXPECT_TRUE(top_view_->cursor().IsEqual(cursor_cross));
EXPECT_EQ(top_view_->cursor(), cursor_cross);
// Similarly, destroying child_view1 should have no effect on the cursor,
// but destroying child_view2 should change it.
top_view_->GetCursorManager()->ViewBeingDestroyed(child_view1.get());
EXPECT_TRUE(top_view_->cursor().IsEqual(cursor_cross));
EXPECT_EQ(top_view_->cursor(), cursor_cross);
top_view_->GetCursorManager()->ViewBeingDestroyed(child_view2.get());
EXPECT_TRUE(top_view_->cursor().IsEqual(cursor_hand));
EXPECT_EQ(top_view_->cursor(), cursor_hand);
}
} // namespace content

@ -158,9 +158,7 @@ bool TouchEmulator::InitCursors(float device_scale_factor, bool force) {
use_2x ? IDR_DEVTOOLS_PINCH_CURSOR_ICON_2X :
IDR_DEVTOOLS_PINCH_CURSOR_ICON);
CursorInfo cursor_info;
cursor_info.type = blink::WebCursorInfo::kTypePointer;
pointer_cursor_.InitFromCursorInfo(cursor_info);
pointer_cursor_ = WebCursor(CursorInfo(blink::WebCursorInfo::kTypePointer));
return true;
}
@ -175,7 +173,7 @@ gfx::SizeF TouchEmulator::InitCursorFromResource(
cursor_info.hotspot =
gfx::Point(cursor_image.Width() / 2, cursor_image.Height() / 2);
cursor->InitFromCursorInfo(cursor_info);
*cursor = WebCursor(cursor_info);
return gfx::ScaleSize(gfx::SizeF(cursor_image.Size()), 1.f / scale);
}

@ -250,11 +250,7 @@ class TouchEmulatorTest : public testing::Test,
void DisableSynchronousTouchAck() { ack_touches_synchronously_ = false; }
float GetCursorScaleFactor() {
CursorInfo info;
cursor_.GetCursorInfo(&info);
return info.image_scale_factor;
}
float GetCursorScaleFactor() { return cursor_.info().image_scale_factor; }
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;

@ -1800,9 +1800,7 @@ void RenderWidgetHostImpl::FilterDropData(DropData* drop_data) {
}
void RenderWidgetHostImpl::SetCursor(const CursorInfo& cursor_info) {
WebCursor cursor;
cursor.InitFromCursorInfo(cursor_info);
SetCursor(cursor);
SetCursor(WebCursor(cursor_info));
}
RenderProcessHost::Priority RenderWidgetHostImpl::GetPriority() {

@ -217,7 +217,7 @@ void RenderWidgetHostNSViewBridgeLocal::OnDisplayMetricsChanged(
}
void RenderWidgetHostNSViewBridgeLocal::DisplayCursor(const WebCursor& cursor) {
WebCursor non_const_cursor = cursor;
WebCursor non_const_cursor(cursor);
[cocoa_view_ updateCursor:non_const_cursor.GetNativeCursor()];
}

@ -530,10 +530,8 @@ int RenderWidgetHostViewAndroid::GetMouseWheelMinimumGranularity() const {
}
void RenderWidgetHostViewAndroid::UpdateCursor(const WebCursor& cursor) {
CursorInfo cursor_info;
cursor.GetCursorInfo(&cursor_info);
view_.OnCursorChanged(cursor_info.type, cursor_info.custom_image,
cursor_info.hotspot);
const CursorInfo& info = cursor.info();
view_.OnCursorChanged(info.type, info.custom_image, info.hotspot);
}
void RenderWidgetHostViewAndroid::SetIsLoading(bool is_loading) {

@ -3906,9 +3906,7 @@ void CursorUpdateReceivedFromCrossSiteIframeHelper(
EXPECT_TRUE(
root_view->GetCursorManager()->GetCursorForTesting(child_view, cursor));
// Since this moused over a text box, this should not be the default cursor.
CursorInfo cursor_info;
cursor.GetCursorInfo(&cursor_info);
EXPECT_EQ(cursor_info.type, blink::WebCursorInfo::kTypeIBeam);
EXPECT_EQ(cursor.info().type, blink::WebCursorInfo::kTypeIBeam);
}
} // namespace

@ -48,7 +48,7 @@ struct ParamTraits<content::WebCursor> {
static bool Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* r) {
return r->Deserialize(iter);
return r->Deserialize(m, iter);
}
static void Log(const param_type& p, std::string* l) {
l->append("<WebCursor>");

@ -1,4 +1,4 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Copyright 2019 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.
@ -10,202 +10,103 @@
#include "base/pickle.h"
#include "build/build_config.h"
#include "third_party/blink/public/platform/web_image.h"
#include "ui/gfx/ipc/skia/gfx_skia_param_traits.h"
using blink::WebCursorInfo;
static const int kMaxCursorDimension = 1024;
constexpr int kMaxImageSize = 1024;
namespace content {
WebCursor::WebCursor() : type_(WebCursorInfo::kTypePointer), custom_scale_(1) {
InitPlatformData();
namespace {
// Checks for a reasonable value of a |CursorInfo::image_scale_factor|.
bool IsReasonableScale(float scale) {
return scale >= 0.01f && scale <= 100.f;
}
} // namespace
WebCursor::~WebCursor() {
Clear();
CleanupPlatformData();
}
WebCursor::WebCursor(const WebCursor& other) {
InitPlatformData();
Copy(other);
}
const WebCursor& WebCursor::operator=(const WebCursor& other) {
if (this == &other)
return *this;
Clear();
Copy(other);
return *this;
}
void WebCursor::InitFromCursorInfo(const CursorInfo& cursor_info) {
Clear();
type_ = cursor_info.type;
hotspot_ = cursor_info.hotspot;
if (IsCustom())
SetCustomData(cursor_info.custom_image);
custom_scale_ = cursor_info.image_scale_factor;
CHECK(custom_scale_ > 0);
WebCursor::WebCursor(const CursorInfo& info) : info_(info) {
CHECK(IsReasonableScale(info.image_scale_factor)) << info.image_scale_factor;
CHECK_LE(info.custom_image.width(), kMaxImageSize);
CHECK_LE(info.custom_image.height(), kMaxImageSize);
CHECK_LE(info.custom_image.width() / info.image_scale_factor, kMaxImageSize);
CHECK_LE(info.custom_image.height() / info.image_scale_factor, kMaxImageSize);
ClampHotspot();
}
void WebCursor::GetCursorInfo(CursorInfo* cursor_info) const {
cursor_info->type = static_cast<WebCursorInfo::Type>(type_);
cursor_info->hotspot = hotspot_;
ImageFromCustomData(&cursor_info->custom_image);
cursor_info->image_scale_factor = custom_scale_;
WebCursor::WebCursor(const WebCursor& other) : info_(other.info_) {
CopyPlatformData(other);
}
bool WebCursor::Deserialize(base::PickleIterator* iter) {
int type, hotspot_x, hotspot_y, size_x, size_y, data_len;
float scale;
const char* data;
// Leave |this| unmodified unless we are going to return success.
if (!iter->ReadInt(&type) ||
!iter->ReadInt(&hotspot_x) ||
!iter->ReadInt(&hotspot_y) ||
!iter->ReadLength(&size_x) ||
!iter->ReadLength(&size_y) ||
!iter->ReadFloat(&scale) ||
!iter->ReadData(&data, &data_len))
bool WebCursor::Deserialize(const base::Pickle* m, base::PickleIterator* iter) {
// Leave |this| unmodified unless deserialization is successful.
int type = 0, hotspot_x = 0, hotspot_y = 0;
float scale = 1.f;
SkBitmap bitmap;
if (!iter->ReadInt(&type))
return false;
// Ensure the size is sane, and there is enough data.
if (size_x > kMaxCursorDimension ||
size_y > kMaxCursorDimension)
return false;
// Ensure scale isn't ridiculous, and the scaled image size is still sane.
if (scale < 0.01 || scale > 100 ||
size_x / scale > kMaxCursorDimension ||
size_y / scale > kMaxCursorDimension)
return false;
type_ = type;
if (type == WebCursorInfo::kTypeCustom) {
if (size_x > 0 && size_y > 0) {
// The * 4 is because the expected format is an array of RGBA pixel
// values.
if (size_x * size_y * 4 != data_len) {
DLOG(WARNING) << "WebCursor's data length and image size mismatch: "
<< size_x << "x" << size_y << "x4 != " << data_len;
return false;
}
if (!iter->ReadInt(&hotspot_x) || !iter->ReadInt(&hotspot_y) ||
!iter->ReadFloat(&scale) || !IsReasonableScale(scale)) {
return false;
}
hotspot_.set_x(hotspot_x);
hotspot_.set_y(hotspot_y);
custom_size_.set_width(size_x);
custom_size_.set_height(size_y);
custom_scale_ = scale;
ClampHotspot();
if (!IPC::ParamTraits<SkBitmap>::Read(m, iter, &bitmap))
return false;
custom_data_.clear();
if (data_len > 0) {
custom_data_.resize(data_len);
memcpy(&custom_data_[0], data, data_len);
}
if (bitmap.width() > kMaxImageSize || bitmap.height() > kMaxImageSize ||
bitmap.width() / scale > kMaxImageSize ||
bitmap.height() / scale > kMaxImageSize) {
return false;
}
}
info_.type = static_cast<WebCursorInfo::Type>(type);
info_.custom_image = bitmap;
info_.hotspot = gfx::Point(hotspot_x, hotspot_y);
info_.image_scale_factor = scale;
ClampHotspot();
return true;
}
void WebCursor::Serialize(base::Pickle* pickle) const {
pickle->WriteInt(type_);
pickle->WriteInt(hotspot_.x());
pickle->WriteInt(hotspot_.y());
pickle->WriteInt(custom_size_.width());
pickle->WriteInt(custom_size_.height());
pickle->WriteFloat(custom_scale_);
const char* data = nullptr;
if (!custom_data_.empty())
data = &custom_data_[0];
pickle->WriteData(data, custom_data_.size());
}
bool WebCursor::IsCustom() const {
return type_ == WebCursorInfo::kTypeCustom;
}
bool WebCursor::IsEqual(const WebCursor& other) const {
if (type_ != other.type_)
return false;
if (!IsPlatformDataEqual(other))
return false;
return hotspot_ == other.hotspot_ &&
custom_size_ == other.custom_size_ &&
custom_scale_ == other.custom_scale_ &&
custom_data_ == other.custom_data_;
}
void WebCursor::Clear() {
type_ = WebCursorInfo::kTypePointer;
hotspot_.set_x(0);
hotspot_.set_y(0);
custom_size_.set_width(0);
custom_size_.set_height(0);
custom_scale_ = 1;
custom_data_.clear();
CleanupPlatformData();
}
void WebCursor::Copy(const WebCursor& other) {
type_ = other.type_;
hotspot_ = other.hotspot_;
custom_size_ = other.custom_size_;
custom_scale_ = other.custom_scale_;
custom_data_ = other.custom_data_;
CopyPlatformData(other);
}
void WebCursor::SetCustomData(const SkBitmap& bitmap) {
CreateCustomData(bitmap, &custom_data_, &custom_size_);
}
void WebCursor::CreateCustomData(const SkBitmap& bitmap,
std::vector<char>* custom_data,
gfx::Size* custom_size) {
if (bitmap.empty())
return;
// Fill custom_data directly with the NativeImage pixels.
custom_data->resize(bitmap.computeByteSize());
if (!custom_data->empty()) {
//This will divide color values by alpha (un-premultiply) if necessary
SkImageInfo dstInfo = bitmap.info().makeAlphaType(kUnpremul_SkAlphaType);
bitmap.readPixels(dstInfo, &(*custom_data)[0], dstInfo.minRowBytes(), 0, 0);
pickle->WriteInt(info_.type);
if (info_.type == WebCursorInfo::kTypeCustom) {
pickle->WriteInt(info_.hotspot.x());
pickle->WriteInt(info_.hotspot.y());
pickle->WriteFloat(info_.image_scale_factor);
IPC::ParamTraits<SkBitmap>::Write(pickle, info_.custom_image);
}
custom_size->set_width(bitmap.width());
custom_size->set_height(bitmap.height());
}
void WebCursor::ImageFromCustomData(SkBitmap* image) const {
if (custom_data_.empty())
return;
bool WebCursor::operator==(const WebCursor& other) const {
return info_ == other.info_ &&
#if defined(USE_AURA) || defined(USE_OZONE)
rotation_ == other.rotation_ &&
#endif
IsPlatformDataEqual(other);
}
SkImageInfo image_info = SkImageInfo::MakeN32(custom_size_.width(),
custom_size_.height(),
kUnpremul_SkAlphaType);
if (!image->tryAllocPixels(image_info))
return;
memcpy(image->getPixels(), &custom_data_[0], custom_data_.size());
bool WebCursor::operator!=(const WebCursor& other) const {
return !(*this == other);
}
void WebCursor::ClampHotspot() {
if (!IsCustom())
if (info_.type != WebCursorInfo::kTypeCustom)
return;
// Clamp the hotspot to the custom image's dimensions.
hotspot_.set_x(std::max(0,
std::min(custom_size_.width() - 1, hotspot_.x())));
hotspot_.set_y(std::max(0,
std::min(custom_size_.height() - 1, hotspot_.y())));
info_.hotspot.set_x(
std::max(0, std::min(info_.custom_image.width() - 1, info_.hotspot.x())));
info_.hotspot.set_y(std::max(
0, std::min(info_.custom_image.height() - 1, info_.hotspot.y())));
}
} // namespace content

@ -1,4 +1,4 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Copyright 2019 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.
@ -19,18 +19,6 @@
#include "ui/base/cursor/cursor.h"
#endif
#if defined(OS_WIN)
typedef struct HINSTANCE__* HINSTANCE;
typedef struct HICON__* HICON;
typedef HICON HCURSOR;
#elif defined(OS_MACOSX)
#ifdef __OBJC__
@class NSCursor;
#else
class NSCursor;
#endif
#endif
namespace base {
class Pickle;
class PickleIterator;
@ -41,32 +29,23 @@ namespace content {
// This class encapsulates a cross-platform description of a cursor. Platform
// specific methods are provided to translate the cross-platform cursor into a
// platform specific cursor. It is also possible to serialize / de-serialize a
// WebCursor.
// WebCursor. This class is highly similar to ui::Cursor.
class CONTENT_EXPORT WebCursor {
public:
WebCursor();
WebCursor() = default;
explicit WebCursor(const CursorInfo& info);
explicit WebCursor(const WebCursor& other);
~WebCursor();
// Copy constructor/assignment operator combine.
WebCursor(const WebCursor& other);
const WebCursor& operator=(const WebCursor& other);
// Conversion from/to CursorInfo.
void InitFromCursorInfo(const CursorInfo& cursor_info);
void GetCursorInfo(CursorInfo* cursor_info) const;
const CursorInfo& info() const { return info_; }
// Serialization / De-serialization
bool Deserialize(base::PickleIterator* iter);
bool Deserialize(const base::Pickle* m, base::PickleIterator* iter);
void Serialize(base::Pickle* pickle) const;
// Returns true if GetCustomCursor should be used to allocate a platform
// specific cursor object. Otherwise GetCursor should be used.
bool IsCustom() const;
// Returns true if the current cursor object contains the same cursor as the
// cursor object passed in. If the current cursor is a custom cursor, we also
// compare the bitmaps to verify whether they are equal.
bool IsEqual(const WebCursor& other) const;
// Equality operator; performs bitmap content comparison as needed.
bool operator==(const WebCursor& other) const;
bool operator!=(const WebCursor& other) const;
// Returns a native cursor representing the current WebCursor instance.
gfx::NativeCursor GetNativeCursor();
@ -79,31 +58,14 @@ class CONTENT_EXPORT WebCursor {
void CreateScaledBitmapAndHotspotFromCustomData(SkBitmap* bitmap,
gfx::Point* hotspot,
float* scale_factor);
#elif defined(OS_WIN)
// Returns a HCURSOR representing the current WebCursor instance.
// The ownership of the HCURSOR remains with the WebCursor instance.
HCURSOR GetCursor(HINSTANCE module_handle);
#elif defined(OS_MACOSX)
// Initialize this from the given Cocoa NSCursor.
void InitFromNSCursor(NSCursor* cursor);
float* scale);
#endif
void set_info_for_testing(const CursorInfo& info) { info_ = info; }
private:
// Copies the contents of the WebCursor instance passed in.
void Copy(const WebCursor& other);
// Cleans up the WebCursor instance.
void Clear();
// Platform specific initialization goes here.
void InitPlatformData();
// Returns true if the platform data in the current cursor object
// matches that of the cursor passed in.
bool IsPlatformDataEqual(const WebCursor& other) const ;
// Returns true if this cursor's platform data matches that of |other|.
bool IsPlatformDataEqual(const WebCursor& other) const;
// Copies platform specific data from the WebCursor instance passed in.
void CopyPlatformData(const WebCursor& other);
@ -111,48 +73,25 @@ class CONTENT_EXPORT WebCursor {
// Platform specific cleanup.
void CleanupPlatformData();
void SetCustomData(const SkBitmap& image);
// Fills the custom_data vector and custom_size object with the image data
// taken from the bitmap.
void CreateCustomData(const SkBitmap& bitmap,
std::vector<char>* custom_data,
gfx::Size* custom_size);
void ImageFromCustomData(SkBitmap* image) const;
// Clamp the hotspot to the custom image's bounds, if this is a custom cursor.
void ClampHotspot();
float GetCursorScaleFactor(SkBitmap* bitmap);
// WebCore::PlatformCursor type.
int type_;
// The basic cursor info.
CursorInfo info_;
// Hotspot in cursor image in pixels.
gfx::Point hotspot_;
// Custom cursor data, as 32-bit RGBA.
// Platform-inspecific because it can be serialized.
gfx::Size custom_size_; // In pixels.
float custom_scale_;
std::vector<char> custom_data_;
#if defined(USE_AURA) && (defined(USE_X11) || defined(USE_OZONE))
#if defined(USE_AURA) || defined(USE_OZONE)
// Only used for custom cursors.
ui::PlatformCursor platform_cursor_;
#elif defined(OS_WIN)
// A custom cursor created from custom bitmap data by Webkit.
HCURSOR custom_cursor_;
#endif
#if defined(USE_AURA)
float device_scale_factor_;
#endif
ui::PlatformCursor platform_cursor_ = 0;
float device_scale_factor_ = 1.f;
display::Display::Rotation rotation_ = display::Display::ROTATE_0;
#endif
#if defined(USE_OZONE)
gfx::Size maximum_cursor_size_;
// This matches ozone drm_util.cc's kDefaultCursorWidth/Height.
static constexpr int kDefaultMaxSize = 64;
gfx::Size maximum_cursor_size_ = {kDefaultMaxSize, kDefaultMaxSize};
#endif
};

@ -17,21 +17,15 @@ gfx::NativeCursor WebCursor::GetNativeCursor() {
// In the future when we want to support cursors of various kinds in Aura on
// Android, we should switch to using webcursor_aura rather than add an
// implementation here.
void WebCursor::SetDisplayInfo(const display::Display& display) {
}
void WebCursor::SetDisplayInfo(const display::Display& display) {}
#endif
void WebCursor::InitPlatformData() {
}
bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
return true;
}
void WebCursor::CleanupPlatformData() {
}
void WebCursor::CleanupPlatformData() {}
void WebCursor::CopyPlatformData(const WebCursor& other) {
}
void WebCursor::CopyPlatformData(const WebCursor& other) {}
} // namespace content

@ -14,7 +14,7 @@ using blink::WebCursorInfo;
namespace content {
gfx::NativeCursor WebCursor::GetNativeCursor() {
switch (type_) {
switch (info_.type) {
case WebCursorInfo::kTypePointer:
return ui::CursorType::kPointer;
case WebCursorInfo::kTypeCross:
@ -105,12 +105,11 @@ gfx::NativeCursor WebCursor::GetNativeCursor() {
ui::Cursor cursor(ui::CursorType::kCustom);
SkBitmap bitmap;
gfx::Point hotspot;
float scale_factor = 1.f;
CreateScaledBitmapAndHotspotFromCustomData(&bitmap, &hotspot,
&scale_factor);
float scale;
CreateScaledBitmapAndHotspotFromCustomData(&bitmap, &hotspot, &scale);
cursor.set_custom_bitmap(bitmap);
cursor.set_custom_hotspot(hotspot);
cursor.set_device_scale_factor(scale_factor);
cursor.set_device_scale_factor(scale);
cursor.SetPlatformCursor(GetPlatformCursor(cursor));
return cursor;
}
@ -120,17 +119,13 @@ gfx::NativeCursor WebCursor::GetNativeCursor() {
}
}
void WebCursor::CreateScaledBitmapAndHotspotFromCustomData(
SkBitmap* bitmap,
gfx::Point* hotspot,
float* scale_factor) {
if (custom_data_.empty())
return;
ImageFromCustomData(bitmap);
*hotspot = hotspot_;
*scale_factor = GetCursorScaleFactor(bitmap);
ui::ScaleAndRotateCursorBitmapAndHotpoint(*scale_factor, rotation_, bitmap,
hotspot);
void WebCursor::CreateScaledBitmapAndHotspotFromCustomData(SkBitmap* bitmap,
gfx::Point* hotspot,
float* scale) {
*bitmap = info_.custom_image;
*hotspot = info_.hotspot;
*scale = GetCursorScaleFactor(bitmap);
ui::ScaleAndRotateCursorBitmapAndHotpoint(*scale, rotation_, bitmap, hotspot);
}
#if !defined(USE_OZONE)
@ -146,8 +141,8 @@ void WebCursor::SetDisplayInfo(const display::Display& display) {
// ozone also has extra calculations for scale factor (taking max cursor size
// into account).
float WebCursor::GetCursorScaleFactor(SkBitmap* bitmap) {
DCHECK(custom_scale_ != 0);
return device_scale_factor_ / custom_scale_;
DCHECK_NE(0, info_.image_scale_factor);
return device_scale_factor_ / info_.image_scale_factor;
}
#endif

@ -12,27 +12,17 @@
namespace content {
ui::PlatformCursor WebCursor::GetPlatformCursor(const ui::Cursor& cursor) {
if (!IsCustom())
return LoadCursor(NULL, IDC_ARROW);
if (info_.type != blink::WebCursorInfo::kTypeCustom)
return LoadCursor(nullptr, IDC_ARROW);
if (custom_cursor_)
return custom_cursor_;
if (platform_cursor_)
return platform_cursor_;
gfx::Size custom_size;
std::vector<char> custom_data;
CreateCustomData(cursor.GetBitmap(), &custom_data, &custom_size);
custom_cursor_ =
IconUtil::CreateCursorFromDIB(
custom_size, cursor.GetHotspot(),
!custom_data.empty() ? &custom_data[0] : NULL, custom_data.size())
.release();
return custom_cursor_;
}
void WebCursor::InitPlatformData() {
custom_cursor_ = NULL;
device_scale_factor_ = 1.f;
DCHECK_EQ(kN32_SkColorType, info_.custom_image.colorType());
platform_cursor_ = IconUtil::CreateCursorFromSkBitmap(info_.custom_image,
cursor.GetHotspot())
.release();
return platform_cursor_;
}
bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
@ -40,9 +30,9 @@ bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
}
void WebCursor::CleanupPlatformData() {
if (custom_cursor_) {
DestroyIcon(custom_cursor_);
custom_cursor_ = NULL;
if (platform_cursor_) {
DestroyIcon(platform_cursor_);
platform_cursor_ = nullptr;
}
}

@ -26,11 +26,6 @@ ui::PlatformCursor WebCursor::GetPlatformCursor(const ui::Cursor& cursor) {
return platform_cursor_;
}
void WebCursor::InitPlatformData() {
platform_cursor_ = 0;
device_scale_factor_ = 1.f;
}
bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
return true;
}

@ -127,34 +127,9 @@ NSCursor* GetCoreCursorWithFallback(CrCoreCursorType type,
return LoadCursor(resource_id, hotspot_x, hotspot_y);
}
NSCursor* CreateCustomCursor(const std::vector<char>& custom_data,
const gfx::Size& custom_size,
float custom_scale,
const gfx::Point& hotspot) {
// If the data is missing, leave the backing transparent.
void* data = NULL;
size_t data_size = 0;
if (!custom_data.empty()) {
// This is safe since we're not going to draw into the context we're
// creating.
data = const_cast<char*>(&custom_data[0]);
data_size = custom_data.size();
}
// If the size is empty, use a 1x1 transparent image.
gfx::Size size = custom_size;
if (size.IsEmpty()) {
size.SetSize(1, 1);
data = NULL;
}
SkBitmap bitmap;
SkImageInfo image_info = SkImageInfo::MakeN32(size.width(), size.height(),
kUnpremul_SkAlphaType);
if (bitmap.tryAllocPixels(image_info) && data)
memcpy(bitmap.getAddr32(0, 0), data, data_size);
else
bitmap.eraseARGB(0, 0, 0, 0);
NSCursor* CreateCustomCursor(const content::CursorInfo& info) {
float custom_scale = info.image_scale_factor;
gfx::Size custom_size(info.custom_image.width(), info.custom_image.height());
// Convert from pixels to view units.
if (custom_scale == 0)
@ -162,12 +137,12 @@ NSCursor* CreateCustomCursor(const std::vector<char>& custom_data,
NSSize dip_size = NSSizeFromCGSize(
gfx::ScaleToFlooredSize(custom_size, 1 / custom_scale).ToCGSize());
NSPoint dip_hotspot = NSPointFromCGPoint(
gfx::ScaleToFlooredPoint(hotspot, 1 / custom_scale).ToCGPoint());
gfx::ScaleToFlooredPoint(info.hotspot, 1 / custom_scale).ToCGPoint());
// Both the image and its representation need to have the same size for
// cursors to appear in high resolution on retina displays. Note that the
// size of a representation is not the same as pixelsWide or pixelsHigh.
NSImage* cursor_image = skia::SkBitmapToNSImage(bitmap);
NSImage* cursor_image = skia::SkBitmapToNSImage(info.custom_image);
[cursor_image setSize:dip_size];
[[[cursor_image representations] objectAtIndex:0] setSize:dip_size];
@ -183,7 +158,7 @@ namespace content {
// Match Safari's cursor choices; see platform/mac/CursorMac.mm .
gfx::NativeCursor WebCursor::GetNativeCursor() {
switch (type_) {
switch (info_.type) {
case WebCursorInfo::kTypePointer:
return [NSCursor arrowCursor];
case WebCursorInfo::kTypeCross:
@ -287,79 +262,12 @@ gfx::NativeCursor WebCursor::GetNativeCursor() {
case WebCursorInfo::kTypeGrabbing:
return [NSCursor closedHandCursor];
case WebCursorInfo::kTypeCustom:
return CreateCustomCursor(
custom_data_, custom_size_, custom_scale_, hotspot_);
return CreateCustomCursor(info_);
}
NOTREACHED();
return nil;
}
void WebCursor::InitFromNSCursor(NSCursor* cursor) {
CursorInfo cursor_info;
if ([cursor isEqual:[NSCursor arrowCursor]]) {
cursor_info.type = WebCursorInfo::kTypePointer;
} else if ([cursor isEqual:[NSCursor IBeamCursor]]) {
cursor_info.type = WebCursorInfo::kTypeIBeam;
} else if ([cursor isEqual:[NSCursor crosshairCursor]]) {
cursor_info.type = WebCursorInfo::kTypeCross;
} else if ([cursor isEqual:[NSCursor pointingHandCursor]]) {
cursor_info.type = WebCursorInfo::kTypeHand;
} else if ([cursor isEqual:[NSCursor resizeLeftCursor]]) {
cursor_info.type = WebCursorInfo::kTypeWestResize;
} else if ([cursor isEqual:[NSCursor resizeRightCursor]]) {
cursor_info.type = WebCursorInfo::kTypeEastResize;
} else if ([cursor isEqual:[NSCursor resizeLeftRightCursor]]) {
cursor_info.type = WebCursorInfo::kTypeEastWestResize;
} else if ([cursor isEqual:[NSCursor resizeUpCursor]]) {
cursor_info.type = WebCursorInfo::kTypeNorthResize;
} else if ([cursor isEqual:[NSCursor resizeDownCursor]]) {
cursor_info.type = WebCursorInfo::kTypeSouthResize;
} else if ([cursor isEqual:[NSCursor resizeUpDownCursor]]) {
cursor_info.type = WebCursorInfo::kTypeNorthSouthResize;
} else if ([cursor isEqual:[NSCursor openHandCursor]]) {
cursor_info.type = WebCursorInfo::kTypeGrab;
} else if ([cursor isEqual:[NSCursor closedHandCursor]]) {
cursor_info.type = WebCursorInfo::kTypeGrabbing;
} else if ([cursor isEqual:[NSCursor operationNotAllowedCursor]]) {
cursor_info.type = WebCursorInfo::kTypeNotAllowed;
} else if ([cursor isEqual:[NSCursor dragCopyCursor]]) {
cursor_info.type = WebCursorInfo::kTypeCopy;
} else if ([cursor isEqual:[NSCursor contextualMenuCursor]]) {
cursor_info.type = WebCursorInfo::kTypeContextMenu;
} else if (
[NSCursor respondsToSelector:@selector(IBeamCursorForVerticalLayout)] &&
[cursor isEqual:[NSCursor IBeamCursorForVerticalLayout]]) {
cursor_info.type = WebCursorInfo::kTypeVerticalText;
} else {
// Also handles the [NSCursor disappearingItemCursor] case. Quick-and-dirty
// image conversion; TODO(avi): do better.
CGImageRef cg_image = nil;
NSImage* image = [cursor image];
for (id rep in [image representations]) {
if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
cg_image = [rep CGImage];
break;
}
}
if (cg_image) {
cursor_info.type = WebCursorInfo::kTypeCustom;
NSPoint hot_spot = [cursor hotSpot];
cursor_info.hotspot = gfx::Point(hot_spot.x, hot_spot.y);
cursor_info.custom_image = skia::CGImageToSkBitmap(cg_image);
} else {
cursor_info.type = WebCursorInfo::kTypePointer;
}
}
InitFromCursorInfo(cursor_info);
}
void WebCursor::InitPlatformData() {
return;
}
bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
return true;
}

@ -12,11 +12,6 @@
#include "ui/base/ui_base_features.h"
#include "ui/ozone/public/cursor_factory_ozone.h"
namespace {
const int kDefaultMaxCursorWidth = 64;
const int kDefaultMaxCursorHeight = 64;
}
namespace content {
ui::PlatformCursor WebCursor::GetPlatformCursor(const ui::Cursor& cursor) {
@ -40,11 +35,9 @@ void WebCursor::SetDisplayInfo(const display::Display& display) {
rotation_ = display.rotation();
maximum_cursor_size_ = display.maximum_cursor_size();
// TODO(oshima): Identify if it's possible to remove this check here and move
// the kDefaultMaxCursor{Width,Height} constants to a single place.
// crbug.com/603512
// the kDefaultMaxSize constants to a single place. crbug.com/603512
if (maximum_cursor_size_.width() == 0 || maximum_cursor_size_.height() == 0)
maximum_cursor_size_ =
gfx::Size(kDefaultMaxCursorWidth, kDefaultMaxCursorHeight);
maximum_cursor_size_ = gfx::Size(kDefaultMaxSize, kDefaultMaxSize);
if (platform_cursor_)
ui::CursorFactoryOzone::GetInstance()->UnrefImageCursor(platform_cursor_);
platform_cursor_ = NULL;
@ -56,19 +49,11 @@ float WebCursor::GetCursorScaleFactor(SkBitmap* bitmap) {
DCHECK_LT(0, maximum_cursor_size_.width());
DCHECK_LT(0, maximum_cursor_size_.height());
return std::min(
{device_scale_factor_ / custom_scale_,
{device_scale_factor_ / info_.image_scale_factor,
static_cast<float>(maximum_cursor_size_.width()) / bitmap->width(),
static_cast<float>(maximum_cursor_size_.height()) / bitmap->height()});
}
void WebCursor::InitPlatformData() {
platform_cursor_ = NULL;
device_scale_factor_ = 1.f;
rotation_ = display::Display::ROTATE_0;
maximum_cursor_size_ =
gfx::Size(kDefaultMaxCursorWidth, kDefaultMaxCursorHeight);
}
bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
return true;
}

@ -1,4 +1,4 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Copyright 2019 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.
@ -8,272 +8,153 @@
#include "build/build_config.h"
#include "content/common/cursors/webcursor.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_cursor_info.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkBitmap.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
using blink::WebCursorInfo;
namespace content {
namespace {
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::kTypeCustom);
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));
// Creates a basic bitmap for testing with the given width and height.
SkBitmap CreateTestBitmap(int width, int height) {
SkBitmap bitmap;
bitmap.allocN32Pixels(width, height);
bitmap.eraseColor(SK_ColorRED);
return bitmap;
}
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::kTypeCustom);
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));
TEST(WebCursorTest, DefaultConstructor) {
WebCursor cursor;
EXPECT_EQ(blink::WebCursorInfo::kTypePointer, cursor.info().type);
EXPECT_TRUE(cursor.info().custom_image.isNull());
EXPECT_TRUE(cursor.info().hotspot.IsOrigin());
EXPECT_EQ(1.f, cursor.info().image_scale_factor);
}
// This custom cursor has enough data but is too big.
base::Pickle large_custom_pickle;
// Type and hotspots.
large_custom_pickle.WriteInt(WebCursorInfo::kTypeCustom);
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));
TEST(WebCursorTest, CursorInfoConstructor) {
CursorInfo info(blink::WebCursorInfo::kTypeHand);
WebCursor cursor(info);
EXPECT_EQ(info, cursor.info());
}
// This custom cursor uses negative lengths.
base::Pickle neg_custom_pickle;
// Type and hotspots.
neg_custom_pickle.WriteInt(WebCursorInfo::kTypeCustom);
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));
TEST(WebCursorTest, CursorInfoConstructorCustom) {
CursorInfo info(blink::WebCursorInfo::kTypeCustom);
info.custom_image = CreateTestBitmap(32, 32);
info.hotspot = gfx::Point(10, 20);
info.image_scale_factor = 1.5f;
WebCursor cursor(info);
EXPECT_EQ(info, cursor.info());
}
// This custom cursor uses zero scale.
base::Pickle scale_zero_custom_pickle;
// Type and hotspots.
scale_zero_custom_pickle.WriteInt(WebCursorInfo::kTypeCustom);
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));
TEST(WebCursorTest, CopyConstructorType) {
CursorInfo info(blink::WebCursorInfo::kTypeHand);
WebCursor cursor(info);
WebCursor copy(cursor);
EXPECT_EQ(cursor, copy);
}
// This custom cursor uses tiny scale.
base::Pickle scale_tiny_custom_pickle;
// Type and hotspots.
scale_tiny_custom_pickle.WriteInt(WebCursorInfo::kTypeCustom);
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, CopyConstructorCustom) {
CursorInfo info(blink::WebCursorInfo::kTypeCustom);
info.custom_image = CreateTestBitmap(32, 32);
info.hotspot = gfx::Point(10, 20);
info.image_scale_factor = 1.5f;
WebCursor cursor(info);
WebCursor copy(cursor);
EXPECT_EQ(cursor, copy);
}
TEST(WebCursorTest, SerializationDefault) {
WebCursor cursor;
base::Pickle pickle;
cursor.Serialize(&pickle);
WebCursor copy;
base::PickleIterator iter(pickle);
ASSERT_TRUE(copy.Deserialize(&pickle, &iter));
EXPECT_EQ(cursor, copy);
}
TEST(WebCursorTest, SerializationCustom) {
CursorInfo info(blink::WebCursorInfo::kTypeCustom);
info.custom_image = CreateTestBitmap(32, 32);
info.hotspot = gfx::Point(10, 20);
info.image_scale_factor = 1.5f;
WebCursor cursor(info);
base::Pickle pickle;
cursor.Serialize(&pickle);
WebCursor copy;
base::PickleIterator iter(pickle);
ASSERT_TRUE(copy.Deserialize(&pickle, &iter));
EXPECT_EQ(cursor, copy);
}
TEST(WebCursorTest, DeserializeBadMessage) {
WebCursor cursor((CursorInfo(blink::WebCursorInfo::kTypeHand)));
WebCursor unmodified_cursor(cursor);
WebCursor invalid_cursor;
// Serialize a cursor with a small scale; deserialization should fail.
CursorInfo small_scale_info(blink::WebCursorInfo::kTypeCustom);
small_scale_info.image_scale_factor = 0.001f;
invalid_cursor.set_info_for_testing(small_scale_info);
base::Pickle small_scale_pickle;
invalid_cursor.Serialize(&small_scale_pickle);
base::PickleIterator iter = base::PickleIterator(small_scale_pickle);
EXPECT_FALSE(cursor.Deserialize(&small_scale_pickle, &iter));
EXPECT_EQ(unmodified_cursor, cursor);
// Serialize a cursor with a big scale; deserialization should fail.
CursorInfo big_scale_info(blink::WebCursorInfo::kTypeCustom);
big_scale_info.image_scale_factor = 1000.f;
invalid_cursor.set_info_for_testing(big_scale_info);
base::Pickle big_scale_pickle;
invalid_cursor.Serialize(&big_scale_pickle);
iter = base::PickleIterator(big_scale_pickle);
EXPECT_FALSE(cursor.Deserialize(&big_scale_pickle, &iter));
EXPECT_EQ(unmodified_cursor, cursor);
// Serialize a cursor with a big image; deserialization should fail.
CursorInfo big_image_info(blink::WebCursorInfo::kTypeCustom);
big_image_info.custom_image = CreateTestBitmap(1025, 3);
invalid_cursor.set_info_for_testing(big_image_info);
base::Pickle big_image_pickle;
invalid_cursor.Serialize(&big_image_pickle);
iter = base::PickleIterator(big_image_pickle);
EXPECT_FALSE(cursor.Deserialize(&big_image_pickle, &iter));
EXPECT_EQ(unmodified_cursor, cursor);
}
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::kTypeCustom);
// 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));
// Initialize a cursor with an invalid hotspot; it should be clamped.
CursorInfo info(blink::WebCursorInfo::kTypeCustom);
info.hotspot = gfx::Point(100, 100);
info.custom_image = CreateTestBitmap(5, 7);
WebCursor cursor(info);
EXPECT_EQ(gfx::Point(4, 6), cursor.info().hotspot);
// Convert to WebCursorInfo, make sure the hotspot got clamped.
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::kTypeCustom);
// 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::kTypeCustom);
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);
*bitmap.getAddr32(0, 0) = testColor;
CursorInfo cursor_info;
cursor_info.type = WebCursorInfo::kTypeCustom;
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);
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);
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);
EXPECT_EQ(kUnpremul_SkAlphaType, cursor_info.custom_image.alphaType());
EXPECT_EQ(testColor,
SkPreMultiplyColor(*cursor_info.custom_image.getAddr32(0, 0)));
#endif
// Serialize a cursor with an invalid hotspot; it should be clamped.
cursor.set_info_for_testing(info);
base::Pickle pickle;
cursor.Serialize(&pickle);
base::PickleIterator iter = base::PickleIterator(pickle);
EXPECT_TRUE(cursor.Deserialize(&pickle, &iter));
EXPECT_EQ(gfx::Point(4, 6), cursor.info().hotspot);
}
#if defined(USE_AURA)
TEST(WebCursorTest, CursorScaleFactor) {
display::Display display;
display.set_device_scale_factor(4.2f);
CursorInfo info;
info.type = WebCursorInfo::kTypeCustom;
info.type = blink::WebCursorInfo::kTypeCustom;
info.hotspot = gfx::Point(0, 1);
info.image_scale_factor = 2.0f;
info.custom_image = CreateTestBitmap(128, 128);
WebCursor cursor(info);
SkImageInfo image_info =
SkImageInfo::MakeN32(128, 128, kUnpremul_SkAlphaType);
info.custom_image = SkBitmap();
info.custom_image.setInfo(image_info);
info.custom_image.allocN32Pixels(128, 128);
info.custom_image.eraseColor(0xFFFFFFFF);
WebCursor cursor;
cursor.InitFromCursorInfo(info);
display::Display display;
display.set_device_scale_factor(4.2f);
cursor.SetDisplayInfo(display);
#if defined(USE_OZONE)
@ -285,73 +166,51 @@ TEST(WebCursorTest, CursorScaleFactor) {
#endif
// Test that the Display dsf is copied.
WebCursor cursor2 = cursor;
WebCursor copy(cursor);
EXPECT_EQ(cursor.GetNativeCursor().device_scale_factor(),
cursor2.GetNativeCursor().device_scale_factor());
copy.GetNativeCursor().device_scale_factor());
}
TEST(WebCursorTest, UnscaledImageCopy) {
CursorInfo info;
info.type = WebCursorInfo::kTypeCustom;
info.type = blink::WebCursorInfo::kTypeCustom;
info.hotspot = gfx::Point(0, 1);
info.custom_image = CreateTestBitmap(2, 2);
WebCursor cursor(info);
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;
SkBitmap copy;
gfx::Point hotspot;
float dsf;
cursor.CreateScaledBitmapAndHotspotFromCustomData(&image_copy, &hotspot,
&dsf);
EXPECT_EQ(1.0f, dsf);
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());
float dsf = 0.f;
cursor.CreateScaledBitmapAndHotspotFromCustomData(&copy, &hotspot, &dsf);
EXPECT_EQ(1.f, dsf);
EXPECT_EQ(2, copy.width());
EXPECT_EQ(2, copy.height());
EXPECT_EQ(0, hotspot.x());
EXPECT_EQ(1, hotspot.y());
}
#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);
void ScaleCursor(float scale, int hotspot_x, int hotspot_y) {
CursorInfo info;
info.type = WebCursorInfo::kTypeCustom;
info.type = blink::WebCursorInfo::kTypeCustom;
info.hotspot = gfx::Point(hotspot_x, hotspot_y);
info.custom_image = CreateTestBitmap(10, 10);
WebCursor cursor(info);
info.custom_image = SkBitmap();
info.custom_image.allocN32Pixels(10, 10);
info.custom_image.eraseColor(0);
WebCursor cursor;
display::Display display;
display.set_device_scale_factor(scale);
cursor.SetDisplayInfo(display);
cursor.InitFromCursorInfo(info);
HCURSOR windows_cursor_handle = cursor.GetNativeCursor().platform();
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);
EXPECT_EQ(static_cast<DWORD>(scale * hotspot_x), windows_icon_info.xHotspot);
EXPECT_EQ(static_cast<DWORD>(scale * hotspot_y), windows_icon_info.yHotspot);
}
} // namespace
TEST(WebCursorTest, WindowsCursorScaledAtHiDpi) {
ScaleCursor(2.0f, 4, 6);
ScaleCursor(1.5f, 2, 8);
@ -359,4 +218,5 @@ TEST(WebCursorTest, WindowsCursorScaledAtHiDpi) {
}
#endif
} // namespace
} // namespace content

@ -139,6 +139,7 @@ jumbo_source_set("common_sources") {
"content_switches.h",
"context_menu_params.cc",
"context_menu_params.h",
"cursor_info.cc",
"cursor_info.h",
"drop_data.cc",
"drop_data.h",

@ -0,0 +1,36 @@
// Copyright 2019 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 "content/public/common/cursor_info.h"
#include "ui/gfx/skia_util.h"
namespace content {
CursorInfo::CursorInfo(blink::WebCursorInfo::Type cursor) : type(cursor) {}
CursorInfo::CursorInfo(const blink::WebCursorInfo& info)
: type(info.type),
custom_image(info.custom_image),
hotspot(info.hot_spot),
image_scale_factor(info.image_scale_factor) {}
bool CursorInfo::operator==(const CursorInfo& other) const {
return type == other.type && hotspot == other.hotspot &&
image_scale_factor == other.image_scale_factor &&
(custom_image.getGenerationID() ==
other.custom_image.getGenerationID() ||
gfx::BitmapsAreEqual(custom_image, other.custom_image));
}
blink::WebCursorInfo CursorInfo::GetWebCursorInfo() const {
blink::WebCursorInfo info;
info.type = type;
info.hot_spot = hotspot;
info.custom_image = custom_image;
info.image_scale_factor = image_scale_factor;
return info;
}
} // namespace content

@ -1,11 +1,10 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Copyright 2019 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.
#ifndef CONTENT_PUBLIC_COMMON_CURSOR_INFO_H_
#define CONTENT_PUBLIC_COMMON_CURSOR_INFO_H_
#include "build/build_config.h"
#include "content/common/content_export.h"
#include "third_party/blink/public/platform/web_cursor_info.h"
#include "third_party/skia/include/core/SkBitmap.h"
@ -14,15 +13,20 @@ namespace content {
// This struct represents the data sufficient to create a cross-platform cursor:
// either a predefined cursor type (from WebCursorInfo) or custom image.
// This structure is highly similar to blink::WebCursorInfo.
struct CONTENT_EXPORT CursorInfo {
explicit CursorInfo(blink::WebCursorInfo::Type cursor_type)
: type(cursor_type), image_scale_factor(1) {}
CursorInfo() = default;
explicit CursorInfo(blink::WebCursorInfo::Type cursor);
explicit CursorInfo(const blink::WebCursorInfo& info);
CursorInfo()
: type(blink::WebCursorInfo::kTypePointer), image_scale_factor(1) {}
// Equality operator; performs bitmap content comparison as needed.
bool operator==(const CursorInfo& other) const;
// Get a blink::WebCursorInfo struct with fields matching this struct.
blink::WebCursorInfo GetWebCursorInfo() const;
// One of the predefined cursors.
blink::WebCursorInfo::Type type;
blink::WebCursorInfo::Type type = blink::WebCursorInfo::kTypePointer;
// Custom cursor image.
SkBitmap custom_image;
@ -32,7 +36,7 @@ struct CONTENT_EXPORT CursorInfo {
// The scale factor of custom image, used to possibly re-scale the image
// for a different density display.
float image_scale_factor;
float image_scale_factor = 1.f;
};
} // namespace content

@ -71,8 +71,6 @@ target(link_target_type, "renderer") {
"context_menu_params_builder.h",
"crash_helpers.cc",
"crash_helpers.h",
"cursor_utils.cc",
"cursor_utils.h",
"dom_automation_controller.cc",
"dom_automation_controller.h",
"dom_storage/dom_storage_cached_area.cc",

@ -33,7 +33,6 @@
#include "content/renderer/accessibility/render_accessibility_impl.h"
#include "content/renderer/browser_plugin/browser_plugin_manager.h"
#include "content/renderer/child_frame_compositing_helper.h"
#include "content/renderer/cursor_utils.h"
#include "content/renderer/drop_data_builder.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/sad_plugin.h"
@ -684,7 +683,7 @@ blink::WebInputEventResult BrowserPlugin::HandleInputEvent(
BrowserPluginManager::Get()->Send(
new BrowserPluginHostMsg_HandleInputEvent(browser_plugin_instance_id_,
&event));
GetWebCursorInfo(cursor_, &cursor_info);
cursor_info = cursor_.info().GetWebCursorInfo();
// Although we forward this event to the guest, we don't report it as consumed
// since other targets of this event in Blink never get that chance either.

@ -1,37 +0,0 @@
// Copyright 2013 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 "content/renderer/cursor_utils.h"
#include "build/build_config.h"
#include "content/common/cursors/webcursor.h"
#include "third_party/blink/public/platform/web_cursor_info.h"
using blink::WebCursorInfo;
namespace content {
bool GetWebCursorInfo(const WebCursor& cursor,
WebCursorInfo* web_cursor_info) {
CursorInfo cursor_info;
cursor.GetCursorInfo(&cursor_info);
web_cursor_info->type = cursor_info.type;
web_cursor_info->hot_spot = cursor_info.hotspot;
web_cursor_info->custom_image = cursor_info.custom_image;
web_cursor_info->image_scale_factor = cursor_info.image_scale_factor;
return true;
}
void InitializeCursorFromWebCursorInfo(WebCursor* cursor,
const WebCursorInfo& web_cursor_info) {
CursorInfo cursor_info;
cursor_info.type = web_cursor_info.type;
cursor_info.image_scale_factor = web_cursor_info.image_scale_factor;
cursor_info.hotspot = web_cursor_info.hot_spot;
cursor_info.custom_image = web_cursor_info.custom_image;
cursor->InitFromCursorInfo(cursor_info);
}
} // namespce content

@ -1,27 +0,0 @@
// Copyright 2013 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.
#ifndef CONTENT_RENDERER_CURSOR_UTILS_H_
#define CONTENT_RENDERER_CURSOR_UTILS_H_
class WebCursor;
namespace blink {
struct WebCursorInfo;
}
namespace content {
// Adapts our cursor info to blink::WebCursorInfo.
bool GetWebCursorInfo(const WebCursor& cursor,
blink::WebCursorInfo* web_cursor_info);
// Adapts blink::WebCursorInfo to our cursor.
void InitializeCursorFromWebCursorInfo(
WebCursor* cursor,
const blink::WebCursorInfo& web_cursor_info);
} // namespace content
#endif // CONTENT_RENDERER_CURSOR_UTILS_H_

@ -504,11 +504,10 @@ void RenderWidgetInputHandler::DidOverscrollFromBlink(
}
bool RenderWidgetInputHandler::DidChangeCursor(const WebCursor& cursor) {
if (!current_cursor_ || !current_cursor_->IsEqual(cursor)) {
current_cursor_ = cursor;
return true;
}
return false;
if (current_cursor_.has_value() && current_cursor_.value() == cursor)
return false;
current_cursor_ = cursor;
return true;
}
bool RenderWidgetInputHandler::ProcessTouchAction(

@ -56,7 +56,6 @@
#include "content/public/renderer/render_thread.h"
#include "content/renderer/browser_plugin/browser_plugin.h"
#include "content/renderer/compositor/layer_tree_view.h"
#include "content/renderer/cursor_utils.h"
#include "content/renderer/drop_data_builder.h"
#include "content/renderer/external_popup_menu.h"
#include "content/renderer/frame_swap_message_queue.h"
@ -1712,8 +1711,7 @@ void RenderWidget::QueueMessage(IPC::Message* msg) {
void RenderWidget::DidChangeCursor(const WebCursorInfo& cursor_info) {
// TODO(darin): Eliminate this temporary.
WebCursor cursor;
InitializeCursorFromWebCursorInfo(&cursor, cursor_info);
WebCursor cursor((CursorInfo(cursor_info)));
// Only send a SetCursor message if we need to make a change.
if (input_handler_->DidChangeCursor(cursor))
Send(new WidgetHostMsg_SetCursor(routing_id_, cursor));

@ -748,8 +748,7 @@ struct FuzzTraits<content::PageState> {
template <>
struct FuzzTraits<content::WebCursor> {
static bool Fuzz(content::WebCursor* p, Fuzzer* fuzzer) {
content::CursorInfo info;
p->GetCursorInfo(&info);
content::CursorInfo info = p->info();
// |type| enum is not validated on de-serialization, so pick random value.
if (!FuzzParam(reinterpret_cast<int*>(&info.type), fuzzer))
@ -768,8 +767,7 @@ struct FuzzTraits<content::WebCursor> {
if (!(info.image_scale_factor > 0.0))
info.image_scale_factor = 1;
*p = content::WebCursor();
p->InitFromCursorInfo(info);
*p = content::WebCursor(info);
return true;
}
};

@ -314,14 +314,16 @@ SkBitmap IconUtil::CreateSkBitmapFromHICON(HICON icon) {
return CreateSkBitmapFromHICONHelper(icon, icon_size);
}
base::win::ScopedHICON IconUtil::CreateCursorFromDIB(const gfx::Size& icon_size,
const gfx::Point& hotspot,
const void* dib_bits,
size_t dib_size) {
base::win::ScopedHICON IconUtil::CreateCursorFromSkBitmap(
const SkBitmap& bitmap,
const gfx::Point& hotspot) {
// Only 32 bit ARGB bitmaps are supported.
if (bitmap.empty() || bitmap.colorType() != kN32_SkColorType)
return base::win::ScopedHICON();
BITMAPINFO icon_bitmap_info = {};
skia::CreateBitmapHeader(
icon_size.width(),
icon_size.height(),
bitmap.width(), bitmap.height(),
reinterpret_cast<BITMAPINFOHEADER*>(&icon_bitmap_info));
base::win::ScopedGetDC dc(NULL);
@ -333,15 +335,8 @@ base::win::ScopedHICON IconUtil::CreateCursorFromDIB(const gfx::Size& icon_size,
0,
0,
0));
if (dib_size > 0) {
SetDIBits(0,
bitmap_handle.get(),
0,
icon_size.height(),
dib_bits,
&icon_bitmap_info,
DIB_RGB_COLORS);
}
SetDIBits(0, bitmap_handle.get(), 0, bitmap.height(), bitmap.getPixels(),
&icon_bitmap_info, DIB_RGB_COLORS);
HBITMAP old_bitmap = reinterpret_cast<HBITMAP>(
SelectObject(working_dc.Get(), bitmap_handle.get()));
@ -349,11 +344,7 @@ base::win::ScopedHICON IconUtil::CreateCursorFromDIB(const gfx::Size& icon_size,
SelectObject(working_dc.Get(), old_bitmap);
base::win::ScopedGDIObject<HBITMAP> mask(
CreateBitmap(icon_size.width(),
icon_size.height(),
1,
1,
NULL));
CreateBitmap(bitmap.width(), bitmap.height(), 1, 1, NULL));
ICONINFO ii = {0};
ii.fIcon = FALSE;
ii.xHotspot = hotspot.x();

@ -129,12 +129,11 @@ class GFX_EXPORT IconUtil {
const base::FilePath& icon_path,
WriteType write_type = ATOMIC_WRITE);
// Creates a cursor of the specified size from the DIB passed in.
// Creates a cursor of the specified size from the SkBitmap passed in.
// Returns the cursor on success or NULL on failure.
static base::win::ScopedHICON CreateCursorFromDIB(const gfx::Size& icon_size,
const gfx::Point& hotspot,
const void* dib_bits,
size_t dib_size);
static base::win::ScopedHICON CreateCursorFromSkBitmap(
const SkBitmap& bitmap,
const gfx::Point& hotspot);
// Given a valid HICON handle representing an icon, this function retrieves
// the hot spot of the icon.