1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/common/cursors/webcursor.h"
6
7#include "base/logging.h"
8#include "base/pickle.h"
9#include "third_party/WebKit/public/platform/WebImage.h"
10
11using blink::WebCursorInfo;
12
13static const int kMaxCursorDimension = 1024;
14
15namespace content {
16
17WebCursor::WebCursor()
18    : type_(WebCursorInfo::TypePointer),
19      custom_scale_(1) {
20#if defined(OS_WIN)
21  external_cursor_ = NULL;
22#endif
23  InitPlatformData();
24}
25
26WebCursor::WebCursor(const CursorInfo& cursor_info)
27    : type_(WebCursorInfo::TypePointer) {
28#if defined(OS_WIN)
29  external_cursor_ = NULL;
30#endif
31  InitPlatformData();
32  InitFromCursorInfo(cursor_info);
33}
34
35WebCursor::~WebCursor() {
36  Clear();
37}
38
39WebCursor::WebCursor(const WebCursor& other) {
40  InitPlatformData();
41  Copy(other);
42}
43
44const WebCursor& WebCursor::operator=(const WebCursor& other) {
45  if (this == &other)
46    return *this;
47
48  Clear();
49  Copy(other);
50  return *this;
51}
52
53void WebCursor::InitFromCursorInfo(const CursorInfo& cursor_info) {
54  Clear();
55
56#if defined(OS_WIN)
57  if (cursor_info.external_handle) {
58    InitFromExternalCursor(cursor_info.external_handle);
59    return;
60  }
61#endif
62
63  type_ = cursor_info.type;
64  hotspot_ = cursor_info.hotspot;
65  if (IsCustom())
66    SetCustomData(cursor_info.custom_image);
67  custom_scale_ = cursor_info.image_scale_factor;
68  CHECK(custom_scale_ > 0);
69  ClampHotspot();
70}
71
72void WebCursor::GetCursorInfo(CursorInfo* cursor_info) const {
73  cursor_info->type = static_cast<WebCursorInfo::Type>(type_);
74  cursor_info->hotspot = hotspot_;
75  ImageFromCustomData(&cursor_info->custom_image);
76  cursor_info->image_scale_factor = custom_scale_;
77
78#if defined(OS_WIN)
79  cursor_info->external_handle = external_cursor_;
80#endif
81}
82
83bool WebCursor::Deserialize(PickleIterator* iter) {
84  int type, hotspot_x, hotspot_y, size_x, size_y, data_len;
85  float scale;
86  const char* data;
87
88  // Leave |this| unmodified unless we are going to return success.
89  if (!iter->ReadInt(&type) ||
90      !iter->ReadInt(&hotspot_x) ||
91      !iter->ReadInt(&hotspot_y) ||
92      !iter->ReadLength(&size_x) ||
93      !iter->ReadLength(&size_y) ||
94      !iter->ReadFloat(&scale) ||
95      !iter->ReadData(&data, &data_len))
96    return false;
97
98  // Ensure the size is sane, and there is enough data.
99  if (size_x > kMaxCursorDimension ||
100      size_y > kMaxCursorDimension)
101    return false;
102
103  // Ensure scale isn't ridiculous, and the scaled image size is still sane.
104  if (scale < 0.01 || scale > 100 ||
105      size_x / scale > kMaxCursorDimension ||
106      size_y / scale > kMaxCursorDimension)
107    return false;
108
109  type_ = type;
110
111  if (type == WebCursorInfo::TypeCustom) {
112    if (size_x > 0 && size_y > 0) {
113      // The * 4 is because the expected format is an array of RGBA pixel
114      // values.
115      if (size_x * size_y * 4 > data_len)
116        return false;
117
118      hotspot_.set_x(hotspot_x);
119      hotspot_.set_y(hotspot_y);
120      custom_size_.set_width(size_x);
121      custom_size_.set_height(size_y);
122      custom_scale_ = scale;
123      ClampHotspot();
124
125      custom_data_.clear();
126      if (data_len > 0) {
127        custom_data_.resize(data_len);
128        memcpy(&custom_data_[0], data, data_len);
129      }
130    }
131  }
132  return DeserializePlatformData(iter);
133}
134
135bool WebCursor::Serialize(Pickle* pickle) const {
136  if (!pickle->WriteInt(type_) ||
137      !pickle->WriteInt(hotspot_.x()) ||
138      !pickle->WriteInt(hotspot_.y()) ||
139      !pickle->WriteInt(custom_size_.width()) ||
140      !pickle->WriteInt(custom_size_.height()) ||
141      !pickle->WriteFloat(custom_scale_))
142    return false;
143
144  const char* data = NULL;
145  if (!custom_data_.empty())
146    data = &custom_data_[0];
147  if (!pickle->WriteData(data, custom_data_.size()))
148    return false;
149
150  return SerializePlatformData(pickle);
151}
152
153bool WebCursor::IsCustom() const {
154  return type_ == WebCursorInfo::TypeCustom;
155}
156
157bool WebCursor::IsEqual(const WebCursor& other) const {
158  if (type_ != other.type_)
159    return false;
160
161  if (!IsPlatformDataEqual(other))
162    return false;
163
164  return hotspot_ == other.hotspot_ &&
165         custom_size_ == other.custom_size_ &&
166         custom_scale_ == other.custom_scale_ &&
167         custom_data_ == other.custom_data_;
168}
169
170#if defined(OS_WIN)
171
172static WebCursorInfo::Type ToCursorType(HCURSOR cursor) {
173  static struct {
174    HCURSOR cursor;
175    WebCursorInfo::Type type;
176  } kStandardCursors[] = {
177    { LoadCursor(NULL, IDC_ARROW),       WebCursorInfo::TypePointer },
178    { LoadCursor(NULL, IDC_CROSS),       WebCursorInfo::TypeCross },
179    { LoadCursor(NULL, IDC_HAND),        WebCursorInfo::TypeHand },
180    { LoadCursor(NULL, IDC_IBEAM),       WebCursorInfo::TypeIBeam },
181    { LoadCursor(NULL, IDC_WAIT),        WebCursorInfo::TypeWait },
182    { LoadCursor(NULL, IDC_HELP),        WebCursorInfo::TypeHelp },
183    { LoadCursor(NULL, IDC_SIZENESW),    WebCursorInfo::TypeNorthEastResize },
184    { LoadCursor(NULL, IDC_SIZENWSE),    WebCursorInfo::TypeNorthWestResize },
185    { LoadCursor(NULL, IDC_SIZENS),      WebCursorInfo::TypeNorthSouthResize },
186    { LoadCursor(NULL, IDC_SIZEWE),      WebCursorInfo::TypeEastWestResize },
187    { LoadCursor(NULL, IDC_SIZEALL),     WebCursorInfo::TypeMove },
188    { LoadCursor(NULL, IDC_APPSTARTING), WebCursorInfo::TypeProgress },
189    { LoadCursor(NULL, IDC_NO),          WebCursorInfo::TypeNotAllowed },
190  };
191  for (int i = 0; i < arraysize(kStandardCursors); ++i) {
192    if (cursor == kStandardCursors[i].cursor)
193      return kStandardCursors[i].type;
194  }
195  return WebCursorInfo::TypeCustom;
196}
197
198void WebCursor::InitFromExternalCursor(HCURSOR cursor) {
199  WebCursorInfo::Type cursor_type = ToCursorType(cursor);
200
201  InitFromCursorInfo(CursorInfo(cursor_type));
202
203  if (cursor_type == WebCursorInfo::TypeCustom)
204    external_cursor_ = cursor;
205}
206
207#endif  // defined(OS_WIN)
208
209void WebCursor::Clear() {
210  type_ = WebCursorInfo::TypePointer;
211  hotspot_.set_x(0);
212  hotspot_.set_y(0);
213  custom_size_.set_width(0);
214  custom_size_.set_height(0);
215  custom_scale_ = 1;
216  custom_data_.clear();
217  CleanupPlatformData();
218}
219
220void WebCursor::Copy(const WebCursor& other) {
221  type_ = other.type_;
222  hotspot_ = other.hotspot_;
223  custom_size_ = other.custom_size_;
224  custom_scale_ = other.custom_scale_;
225  custom_data_ = other.custom_data_;
226  CopyPlatformData(other);
227}
228
229void WebCursor::SetCustomData(const SkBitmap& bitmap) {
230  if (bitmap.empty())
231    return;
232
233  // Fill custom_data_ directly with the NativeImage pixels.
234  SkAutoLockPixels bitmap_lock(bitmap);
235  custom_data_.resize(bitmap.getSize());
236  if (!custom_data_.empty())
237    memcpy(&custom_data_[0], bitmap.getPixels(), bitmap.getSize());
238  custom_size_.set_width(bitmap.width());
239  custom_size_.set_height(bitmap.height());
240}
241
242void WebCursor::ImageFromCustomData(SkBitmap* image) const {
243  if (custom_data_.empty())
244    return;
245
246  if (!image->tryAllocN32Pixels(custom_size_.width(), custom_size_.height()))
247    return;
248  memcpy(image->getPixels(), &custom_data_[0], custom_data_.size());
249}
250
251void WebCursor::ClampHotspot() {
252  if (!IsCustom())
253    return;
254
255  // Clamp the hotspot to the custom image's dimensions.
256  hotspot_.set_x(std::max(0,
257                          std::min(custom_size_.width() - 1, hotspot_.x())));
258  hotspot_.set_y(std::max(0,
259                          std::min(custom_size_.height() - 1, hotspot_.y())));
260}
261
262}  // namespace content
263