// 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 "content/common/cursors/webcursor.h" #include "base/logging.h" #include "base/pickle.h" #include "third_party/WebKit/public/platform/WebImage.h" using blink::WebCursorInfo; static const int kMaxCursorDimension = 1024; namespace content { WebCursor::WebCursor() : type_(WebCursorInfo::TypePointer), custom_scale_(1) { #if defined(OS_WIN) external_cursor_ = NULL; #endif InitPlatformData(); } WebCursor::WebCursor(const CursorInfo& cursor_info) : type_(WebCursorInfo::TypePointer) { #if defined(OS_WIN) external_cursor_ = NULL; #endif InitPlatformData(); InitFromCursorInfo(cursor_info); } WebCursor::~WebCursor() { Clear(); } 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(); #if defined(OS_WIN) if (cursor_info.external_handle) { InitFromExternalCursor(cursor_info.external_handle); return; } #endif 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); 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_; #if defined(OS_WIN) cursor_info->external_handle = external_cursor_; #endif } bool WebCursor::Deserialize(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)) 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::TypeCustom) { 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) 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(); custom_data_.clear(); if (data_len > 0) { custom_data_.resize(data_len); memcpy(&custom_data_[0], data, data_len); } } } return DeserializePlatformData(iter); } bool WebCursor::Serialize(Pickle* pickle) const { if (!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_)) return false; const char* data = NULL; if (!custom_data_.empty()) data = &custom_data_[0]; if (!pickle->WriteData(data, custom_data_.size())) return false; return SerializePlatformData(pickle); } bool WebCursor::IsCustom() const { return type_ == WebCursorInfo::TypeCustom; } 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_; } #if defined(OS_WIN) static WebCursorInfo::Type ToCursorType(HCURSOR cursor) { static struct { HCURSOR cursor; WebCursorInfo::Type type; } kStandardCursors[] = { { LoadCursor(NULL, IDC_ARROW), WebCursorInfo::TypePointer }, { LoadCursor(NULL, IDC_CROSS), WebCursorInfo::TypeCross }, { LoadCursor(NULL, IDC_HAND), WebCursorInfo::TypeHand }, { LoadCursor(NULL, IDC_IBEAM), WebCursorInfo::TypeIBeam }, { LoadCursor(NULL, IDC_WAIT), WebCursorInfo::TypeWait }, { LoadCursor(NULL, IDC_HELP), WebCursorInfo::TypeHelp }, { LoadCursor(NULL, IDC_SIZENESW), WebCursorInfo::TypeNorthEastResize }, { LoadCursor(NULL, IDC_SIZENWSE), WebCursorInfo::TypeNorthWestResize }, { LoadCursor(NULL, IDC_SIZENS), WebCursorInfo::TypeNorthSouthResize }, { LoadCursor(NULL, IDC_SIZEWE), WebCursorInfo::TypeEastWestResize }, { LoadCursor(NULL, IDC_SIZEALL), WebCursorInfo::TypeMove }, { LoadCursor(NULL, IDC_APPSTARTING), WebCursorInfo::TypeProgress }, { LoadCursor(NULL, IDC_NO), WebCursorInfo::TypeNotAllowed }, }; for (int i = 0; i < arraysize(kStandardCursors); ++i) { if (cursor == kStandardCursors[i].cursor) return kStandardCursors[i].type; } return WebCursorInfo::TypeCustom; } void WebCursor::InitFromExternalCursor(HCURSOR cursor) { WebCursorInfo::Type cursor_type = ToCursorType(cursor); InitFromCursorInfo(CursorInfo(cursor_type)); if (cursor_type == WebCursorInfo::TypeCustom) external_cursor_ = cursor; } #endif // defined(OS_WIN) void WebCursor::Clear() { type_ = WebCursorInfo::TypePointer; 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) { if (bitmap.empty()) return; // Fill custom_data_ directly with the NativeImage pixels. SkAutoLockPixels bitmap_lock(bitmap); custom_data_.resize(bitmap.getSize()); if (!custom_data_.empty()) memcpy(&custom_data_[0], bitmap.getPixels(), bitmap.getSize()); custom_size_.set_width(bitmap.width()); custom_size_.set_height(bitmap.height()); } void WebCursor::ImageFromCustomData(SkBitmap* image) const { if (custom_data_.empty()) return; image->setConfig(SkBitmap::kARGB_8888_Config, custom_size_.width(), custom_size_.height()); if (!image->allocPixels()) return; memcpy(image->getPixels(), &custom_data_[0], custom_data_.size()); } void WebCursor::ClampHotspot() { if (!IsCustom()) 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()))); } } // namespace content