clipboard_android.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 "ui/base/clipboard/clipboard.h" 6 7#include "base/android/jni_string.h" 8#include "base/lazy_instance.h" 9#include "base/stl_util.h" 10#include "base/synchronization/lock.h" 11#include "base/utf_string_conversions.h" 12#include "jni/Clipboard_jni.h" 13#include "third_party/skia/include/core/SkBitmap.h" 14#include "ui/base/clipboard/clipboard_android_initialization.h" 15#include "ui/gfx/size.h" 16 17// TODO:(andrewhayden) Support additional formats in Android: Bitmap, URI, HTML, 18// HTML+text now that Android's clipboard system supports them, then nuke the 19// legacy implementation note below. 20 21// Legacy implementation note: 22// The Android clipboard system used to only support text format. So we used the 23// Android system when some text was added or retrieved from the system. For 24// anything else, we STILL store the value in some process wide static 25// variable protected by a lock. So the (non-text) clipboard will only work 26// within the same process. 27 28using base::android::AttachCurrentThread; 29using base::android::ClearException; 30using base::android::ConvertJavaStringToUTF8; 31using base::android::ScopedJavaGlobalRef; 32using base::android::ScopedJavaLocalRef; 33 34namespace ui { 35 36namespace { 37// Various formats we support. 38const char kPlainTextFormat[] = "text"; 39const char kHTMLFormat[] = "html"; 40const char kRTFFormat[] = "rtf"; 41const char kBitmapFormat[] = "bitmap"; 42const char kWebKitSmartPasteFormat[] = "webkit_smart"; 43const char kBookmarkFormat[] = "bookmark"; 44const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data"; 45const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; 46const char kSourceTagFormat[] = "source_tag"; 47 48class ClipboardMap { 49 public: 50 ClipboardMap(); 51 std::string Get(const std::string& format); 52 bool HasFormat(const std::string& format); 53 void Set(const std::string& format, const std::string& data); 54 void Clear(); 55 56 private: 57 void SyncWithAndroidClipboard(); 58 std::map<std::string, std::string> map_; 59 base::Lock lock_; 60 61 // Java class and methods for the Android ClipboardManager. 62 ScopedJavaGlobalRef<jobject> clipboard_manager_; 63}; 64base::LazyInstance<ClipboardMap>::Leaky g_map = LAZY_INSTANCE_INITIALIZER; 65 66ClipboardMap::ClipboardMap() { 67 JNIEnv* env = AttachCurrentThread(); 68 DCHECK(env); 69 70 // Get the context. 71 jobject context = base::android::GetApplicationContext(); 72 DCHECK(context); 73 74 ScopedJavaLocalRef<jobject> local_ref = 75 Java_Clipboard_create(env, context); 76 DCHECK(local_ref.obj()); 77 clipboard_manager_.Reset(env, local_ref.Release()); 78} 79 80std::string ClipboardMap::Get(const std::string& format) { 81 base::AutoLock lock(lock_); 82 SyncWithAndroidClipboard(); 83 std::map<std::string, std::string>::const_iterator it = map_.find(format); 84 return it == map_.end() ? std::string() : it->second; 85} 86 87bool ClipboardMap::HasFormat(const std::string& format) { 88 base::AutoLock lock(lock_); 89 SyncWithAndroidClipboard(); 90 return ContainsKey(map_, format); 91} 92 93void ClipboardMap::Set(const std::string& format, const std::string& data) { 94 JNIEnv* env = AttachCurrentThread(); 95 base::AutoLock lock(lock_); 96 SyncWithAndroidClipboard(); 97 98 map_[format] = data; 99 if (format == kPlainTextFormat) { 100 ScopedJavaLocalRef<jstring> str( 101 env, env->NewStringUTF(data.c_str())); 102 DCHECK(str.obj() && !ClearException(env)); 103 Java_Clipboard_setText(env, clipboard_manager_.obj(), str.obj()); 104 } 105} 106 107void ClipboardMap::Clear() { 108 JNIEnv* env = AttachCurrentThread(); 109 base::AutoLock lock(lock_); 110 map_.clear(); 111 Java_Clipboard_setText(env, clipboard_manager_.obj(), NULL); 112} 113 114// If the internal map contains a plain-text entry and it does not match that 115// in the Android clipboard, clear the map and insert the Android text into it. 116void ClipboardMap::SyncWithAndroidClipboard() { 117 lock_.AssertAcquired(); 118 JNIEnv* env = AttachCurrentThread(); 119 120 std::map<std::string, std::string>::const_iterator it = 121 map_.find(kPlainTextFormat); 122 123 if (!Java_Clipboard_hasPlainText(env, clipboard_manager_.obj())) { 124 if (it != map_.end()) 125 // We have plain text on this side, but Android doesn't. Nuke ours. 126 map_.clear(); 127 return; 128 } 129 130 ScopedJavaLocalRef<jstring> java_string = 131 Java_Clipboard_getCoercedText(env, clipboard_manager_.obj()); 132 133 if (!java_string.obj()) { 134 // Tolerate a null value from the Java side, even though that should not 135 // happen since hasPlainText has already returned true. 136 // Should only happen if someone is using the clipboard on multiple 137 // threads and clears it out after hasPlainText but before we get here... 138 if (it != map_.end()) 139 // We have plain text on this side, but Android doesn't. Nuke ours. 140 map_.clear(); 141 return; 142 } 143 144 // If Android text differs from ours (or we have none), then copy Android's. 145 std::string android_string = ConvertJavaStringToUTF8(java_string); 146 if (it == map_.end() || it->second != android_string) { 147 map_.clear(); 148 map_[kPlainTextFormat] = android_string; 149 } 150} 151 152} // namespace 153 154Clipboard::FormatType::FormatType() { 155} 156 157Clipboard::FormatType::FormatType(const std::string& native_format) 158 : data_(native_format) { 159} 160 161Clipboard::FormatType::~FormatType() { 162} 163 164std::string Clipboard::FormatType::Serialize() const { 165 return data_; 166} 167 168// static 169Clipboard::FormatType Clipboard::FormatType::Deserialize( 170 const std::string& serialization) { 171 return FormatType(serialization); 172} 173 174bool Clipboard::FormatType::Equals(const FormatType& other) const { 175 return data_ == other.data_; 176} 177 178Clipboard::Clipboard() { 179 DCHECK(CalledOnValidThread()); 180} 181 182Clipboard::~Clipboard() { 183 DCHECK(CalledOnValidThread()); 184} 185 186// Main entry point used to write several values in the clipboard. 187void Clipboard::WriteObjectsImpl(Buffer buffer, 188 const ObjectMap& objects, 189 SourceTag tag) { 190 DCHECK(CalledOnValidThread()); 191 DCHECK_EQ(buffer, BUFFER_STANDARD); 192 g_map.Get().Clear(); 193 for (ObjectMap::const_iterator iter = objects.begin(); 194 iter != objects.end(); ++iter) { 195 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); 196 } 197 WriteSourceTag(tag); 198} 199 200uint64 Clipboard::GetSequenceNumber(Clipboard::Buffer /* buffer */) { 201 DCHECK(CalledOnValidThread()); 202 // TODO: implement this. For now this interface will advertise 203 // that the clipboard never changes. That's fine as long as we 204 // don't rely on this signal. 205 return 0; 206} 207 208bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, 209 Clipboard::Buffer buffer) const { 210 DCHECK(CalledOnValidThread()); 211 DCHECK_EQ(buffer, BUFFER_STANDARD); 212 return g_map.Get().HasFormat(format.data()); 213} 214 215void Clipboard::Clear(Buffer buffer) { 216 DCHECK(CalledOnValidThread()); 217 DCHECK_EQ(buffer, BUFFER_STANDARD); 218 g_map.Get().Clear(); 219} 220 221void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, 222 bool* contains_filenames) const { 223 DCHECK(CalledOnValidThread()); 224 DCHECK_EQ(buffer, BUFFER_STANDARD); 225 226 if (!types || !contains_filenames) { 227 NOTREACHED(); 228 return; 229 } 230 231 NOTIMPLEMENTED(); 232 233 types->clear(); 234 *contains_filenames = false; 235} 236 237void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const { 238 DCHECK(CalledOnValidThread()); 239 DCHECK_EQ(buffer, BUFFER_STANDARD); 240 std::string utf8; 241 ReadAsciiText(buffer, &utf8); 242 *result = UTF8ToUTF16(utf8); 243} 244 245void Clipboard::ReadAsciiText(Clipboard::Buffer buffer, 246 std::string* result) const { 247 DCHECK(CalledOnValidThread()); 248 DCHECK_EQ(buffer, BUFFER_STANDARD); 249 ReportAction(buffer, READ_TEXT); 250 *result = g_map.Get().Get(kPlainTextFormat); 251} 252 253// Note: |src_url| isn't really used. It is only implemented in Windows 254void Clipboard::ReadHTML(Clipboard::Buffer buffer, 255 string16* markup, 256 std::string* src_url, 257 uint32* fragment_start, 258 uint32* fragment_end) const { 259 DCHECK(CalledOnValidThread()); 260 DCHECK_EQ(buffer, BUFFER_STANDARD); 261 if (src_url) 262 src_url->clear(); 263 264 std::string input = g_map.Get().Get(kHTMLFormat); 265 *markup = UTF8ToUTF16(input); 266 267 *fragment_start = 0; 268 *fragment_end = static_cast<uint32>(markup->length()); 269} 270 271void Clipboard::ReadRTF(Buffer buffer, std::string* result) const { 272 DCHECK(CalledOnValidThread()); 273 NOTIMPLEMENTED(); 274} 275 276SkBitmap Clipboard::ReadImage(Buffer buffer) const { 277 DCHECK(CalledOnValidThread()); 278 DCHECK_EQ(buffer, BUFFER_STANDARD); 279 std::string input = g_map.Get().Get(kBitmapFormat); 280 281 SkBitmap bmp; 282 if (!input.empty()) { 283 DCHECK_LE(sizeof(gfx::Size), input.size()); 284 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(input.data()); 285 286 bmp.setConfig( 287 SkBitmap::kARGB_8888_Config, size->width(), size->height(), 0); 288 bmp.allocPixels(); 289 290 int bm_size = size->width() * size->height() * 4; 291 DCHECK_EQ(sizeof(gfx::Size) + bm_size, input.size()); 292 293 memcpy(bmp.getPixels(), input.data() + sizeof(gfx::Size), bm_size); 294 } 295 return bmp; 296} 297 298void Clipboard::ReadCustomData(Buffer buffer, 299 const string16& type, 300 string16* result) const { 301 DCHECK(CalledOnValidThread()); 302 NOTIMPLEMENTED(); 303} 304 305void Clipboard::ReadBookmark(string16* title, std::string* url) const { 306 DCHECK(CalledOnValidThread()); 307 NOTIMPLEMENTED(); 308} 309 310void Clipboard::ReadData(const Clipboard::FormatType& format, 311 std::string* result) const { 312 DCHECK(CalledOnValidThread()); 313 *result = g_map.Get().Get(format.data()); 314} 315 316Clipboard::SourceTag Clipboard::ReadSourceTag(Buffer buffer) const { 317 DCHECK(CalledOnValidThread()); 318 DCHECK_EQ(buffer, BUFFER_STANDARD); 319 std::string result; 320 ReadData(GetSourceTagFormatType(), &result); 321 return Binary2SourceTag(result); 322} 323 324// static 325Clipboard::FormatType Clipboard::GetFormatType( 326 const std::string& format_string) { 327 return FormatType::Deserialize(format_string); 328} 329 330// static 331const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { 332 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat)); 333 return type; 334} 335 336// static 337const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { 338 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat)); 339 return type; 340} 341 342// static 343const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { 344 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebKitSmartPasteFormat)); 345 return type; 346} 347 348// static 349const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { 350 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kHTMLFormat)); 351 return type; 352} 353 354// static 355const Clipboard::FormatType& Clipboard::GetRtfFormatType() { 356 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kRTFFormat)); 357 return type; 358} 359 360// static 361const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { 362 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kBitmapFormat)); 363 return type; 364} 365 366// static 367const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { 368 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); 369 return type; 370} 371 372// static 373const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { 374 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); 375 return type; 376} 377 378// static 379const Clipboard::FormatType& Clipboard::GetSourceTagFormatType() { 380 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kSourceTagFormat)); 381 return type; 382} 383 384void Clipboard::WriteText(const char* text_data, size_t text_len) { 385 g_map.Get().Set(kPlainTextFormat, std::string(text_data, text_len)); 386} 387 388void Clipboard::WriteHTML(const char* markup_data, 389 size_t markup_len, 390 const char* url_data, 391 size_t url_len) { 392 g_map.Get().Set(kHTMLFormat, std::string(markup_data, markup_len)); 393} 394 395void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { 396 NOTIMPLEMENTED(); 397} 398 399// Note: according to other platforms implementations, this really writes the 400// URL spec. 401void Clipboard::WriteBookmark(const char* title_data, size_t title_len, 402 const char* url_data, size_t url_len) { 403 g_map.Get().Set(kBookmarkFormat, std::string(url_data, url_len)); 404} 405 406// Write an extra flavor that signifies WebKit was the last to modify the 407// pasteboard. This flavor has no data. 408void Clipboard::WriteWebSmartPaste() { 409 g_map.Get().Set(kWebKitSmartPasteFormat, std::string()); 410} 411 412// All platforms use gfx::Size for size data but it is passed as a const char* 413// Further, pixel_data is expected to be 32 bits per pixel 414// Note: we implement this to pass all unit tests but it is currently unclear 415// how some code would consume this. 416void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { 417 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data); 418 int bm_size = size->width() * size->height() * 4; 419 420 std::string packed(size_data, sizeof(gfx::Size)); 421 packed += std::string(pixel_data, bm_size); 422 g_map.Get().Set(kBitmapFormat, packed); 423} 424 425void Clipboard::WriteData(const Clipboard::FormatType& format, 426 const char* data_data, size_t data_len) { 427 g_map.Get().Set(format.data(), std::string(data_data, data_len)); 428} 429 430void Clipboard::WriteSourceTag(SourceTag tag) { 431 if (tag != SourceTag()) { 432 ObjectMapParam binary = SourceTag2Binary(tag); 433 WriteData(GetSourceTagFormatType(), &binary[0], binary.size()); 434 } 435} 436 437// See clipboard_android_initialization.h for more information. 438bool RegisterClipboardAndroid(JNIEnv* env) { 439 return RegisterNativesImpl(env); 440} 441 442} // namespace ui 443