OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/base/clipboard/clipboard.h" | 5 #include "ui/base/clipboard/clipboard.h" |
6 | 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" |
7 #include "third_party/skia/include/core/SkBitmap.h" | 12 #include "third_party/skia/include/core/SkBitmap.h" |
8 #include "ui/gfx/size.h" | 13 #include "ui/gfx/size.h" |
9 | 14 |
| 15 // Important note: |
| 16 // The Android clipboard system only supports text format. So we use the |
| 17 // Android system when some text is added or retrieved from the system. For |
| 18 // anything else, we currently store the value in some process wide static |
| 19 // variable protected by a lock. So the (non-text) clipboard will only work |
| 20 // within the same process. |
| 21 |
| 22 using base::android::AttachCurrentThread; |
| 23 using base::android::CheckException; |
| 24 using base::android::ClearException; |
| 25 using base::android::ConvertJavaStringToUTF16; |
| 26 using base::android::ConvertJavaStringToUTF8; |
| 27 using base::android::GetClass; |
| 28 using base::android::GetMethodID; |
| 29 using base::android::ScopedJavaLocalRef; |
| 30 |
10 namespace ui { | 31 namespace ui { |
11 | 32 |
12 namespace { | 33 namespace { |
13 | 34 // Various formats we support. |
14 // Various format we support. | |
15 const char kPlainTextFormat[] = "text"; | 35 const char kPlainTextFormat[] = "text"; |
16 const char kHTMLFormat[] = "html"; | 36 const char kHTMLFormat[] = "html"; |
17 const char kRTFFormat[] = "rtf"; | 37 const char kRTFFormat[] = "rtf"; |
18 const char kBitmapFormat[] = "bitmap"; | 38 const char kBitmapFormat[] = "bitmap"; |
19 const char kWebKitSmartPasteFormat[] = "webkit_smart"; | 39 const char kWebKitSmartPasteFormat[] = "webkit_smart"; |
20 const char kBookmarkFormat[] = "bookmark"; | 40 const char kBookmarkFormat[] = "bookmark"; |
21 const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; | 41 const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; |
22 | 42 |
| 43 class ClipboardMap { |
| 44 public: |
| 45 ClipboardMap(); |
| 46 std::string Get(const std::string& format); |
| 47 bool HasFormat(const std::string& format); |
| 48 void Set(const std::string& format, const std::string& data); |
| 49 void Clear(); |
| 50 |
| 51 private: |
| 52 void SyncWithAndroidClipboard(); |
| 53 std::map<std::string, std::string> map_; |
| 54 base::Lock lock_; |
| 55 |
| 56 // Java class and methods for the Android ClipboardManager. |
| 57 base::android::ScopedJavaGlobalRef<jobject> clipboard_manager_; |
| 58 jmethodID set_text_; |
| 59 jmethodID get_text_; |
| 60 jmethodID has_text_; |
| 61 jmethodID to_string_; |
| 62 }; |
| 63 base::LazyInstance<ClipboardMap>::Leaky g_map = LAZY_INSTANCE_INITIALIZER; |
| 64 |
| 65 ClipboardMap::ClipboardMap() { |
| 66 JNIEnv* env = AttachCurrentThread(); |
| 67 DCHECK(env); |
| 68 |
| 69 // Get the context. |
| 70 jobject context = base::android::GetApplicationContext(); |
| 71 DCHECK(context); |
| 72 |
| 73 // Get the context class. |
| 74 ScopedJavaLocalRef<jclass> context_class = |
| 75 GetClass(env, "android/content/Context"); |
| 76 |
| 77 // Get the system service method. |
| 78 jmethodID get_system_service = GetMethodID(env, context_class, |
| 79 "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); |
| 80 |
| 81 // Retrieve the system service. |
| 82 ScopedJavaLocalRef<jstring> service_name(env, env->NewStringUTF("clipboard")); |
| 83 clipboard_manager_.Reset(env, env->CallObjectMethod(context, |
| 84 get_system_service, service_name.obj())); |
| 85 DCHECK(clipboard_manager_.obj() && !ClearException(env)); |
| 86 |
| 87 // Retain a few methods we'll keep using. |
| 88 ScopedJavaLocalRef<jclass> clipboard_class = |
| 89 GetClass(env, "android/text/ClipboardManager"); |
| 90 set_text_ = GetMethodID(env, clipboard_class, |
| 91 "setText", "(Ljava/lang/CharSequence;)V"); |
| 92 get_text_ = GetMethodID(env, clipboard_class, |
| 93 "getText", "()Ljava/lang/CharSequence;"); |
| 94 has_text_ = GetMethodID(env, clipboard_class, |
| 95 "hasText", "()Z"); |
| 96 |
| 97 // Will need to call toString as CharSequence is not always a String. |
| 98 ScopedJavaLocalRef<jclass> charsequence_class = |
| 99 GetClass(env, "java/lang/CharSequence"); |
| 100 to_string_ = GetMethodID(env, charsequence_class, |
| 101 "toString", "()Ljava/lang/String;"); |
| 102 } |
| 103 |
| 104 std::string ClipboardMap::Get(const std::string& format) { |
| 105 base::AutoLock lock(lock_); |
| 106 SyncWithAndroidClipboard(); |
| 107 std::map<std::string, std::string>::const_iterator it = map_.find(format); |
| 108 return it == map_.end() ? std::string() : it->second; |
| 109 } |
| 110 |
| 111 bool ClipboardMap::HasFormat(const std::string& format) { |
| 112 base::AutoLock lock(lock_); |
| 113 SyncWithAndroidClipboard(); |
| 114 return ContainsKey(map_, format); |
| 115 } |
| 116 |
| 117 void ClipboardMap::Set(const std::string& format, const std::string& data) { |
| 118 JNIEnv* env = AttachCurrentThread(); |
| 119 base::AutoLock lock(lock_); |
| 120 SyncWithAndroidClipboard(); |
| 121 |
| 122 map_[format] = data; |
| 123 if (format == kPlainTextFormat) { |
| 124 ScopedJavaLocalRef<jstring> str( |
| 125 env, env->NewStringUTF(data.c_str())); |
| 126 DCHECK(str.obj() && !ClearException(env)); |
| 127 env->CallVoidMethod(clipboard_manager_.obj(), set_text_, str.obj()); |
| 128 } |
| 129 } |
| 130 |
| 131 void ClipboardMap::Clear() { |
| 132 JNIEnv* env = AttachCurrentThread(); |
| 133 base::AutoLock lock(lock_); |
| 134 map_.clear(); |
| 135 env->CallVoidMethod(clipboard_manager_.obj(), set_text_, NULL); |
| 136 } |
| 137 |
| 138 // If the text in the internal map does not match that in the Android clipboard, |
| 139 // clear the map and insert the Android text into it. |
| 140 void ClipboardMap::SyncWithAndroidClipboard() { |
| 141 lock_.AssertAcquired(); |
| 142 JNIEnv* env = AttachCurrentThread(); |
| 143 |
| 144 std::map<std::string, std::string>::const_iterator it = |
| 145 map_.find(kPlainTextFormat); |
| 146 |
| 147 if (!env->CallBooleanMethod(clipboard_manager_.obj(), has_text_)) { |
| 148 if (it != map_.end()) |
| 149 map_.clear(); |
| 150 return; |
| 151 } |
| 152 |
| 153 ScopedJavaLocalRef<jobject> char_seq_text( |
| 154 env, env->CallObjectMethod(clipboard_manager_.obj(), get_text_)); |
| 155 ScopedJavaLocalRef<jstring> tmp_string(env, |
| 156 static_cast<jstring>(env->CallObjectMethod(char_seq_text.obj(), |
| 157 to_string_))); |
| 158 std::string android_string = ConvertJavaStringToUTF8(tmp_string); |
| 159 |
| 160 if (it == map_.end() || it->second != android_string) { |
| 161 map_.clear(); |
| 162 map_[kPlainTextFormat] = android_string; |
| 163 } |
| 164 } |
| 165 |
23 } // namespace | 166 } // namespace |
24 | 167 |
25 Clipboard::FormatType::FormatType() { | 168 Clipboard::FormatType::FormatType() { |
26 } | 169 } |
27 | 170 |
28 Clipboard::FormatType::FormatType(const std::string& native_format) | 171 Clipboard::FormatType::FormatType(const std::string& native_format) |
29 : data_(native_format) { | 172 : data_(native_format) { |
30 } | 173 } |
31 | 174 |
32 Clipboard::FormatType::~FormatType() { | 175 Clipboard::FormatType::~FormatType() { |
33 } | 176 } |
34 | 177 |
35 std::string Clipboard::FormatType::Serialize() const { | 178 std::string Clipboard::FormatType::Serialize() const { |
36 return data_; | 179 return data_; |
37 } | 180 } |
38 | 181 |
39 // static | 182 // static |
40 Clipboard::FormatType Clipboard::FormatType::Deserialize( | 183 Clipboard::FormatType Clipboard::FormatType::Deserialize( |
41 const std::string& serialization) { | 184 const std::string& serialization) { |
42 return FormatType(serialization); | 185 return FormatType(serialization); |
43 } | 186 } |
44 | 187 |
45 bool Clipboard::FormatType::Equals(const FormatType& other) const { | 188 bool Clipboard::FormatType::Equals(const FormatType& other) const { |
46 return data_ == other.data_; | 189 return data_ == other.data_; |
47 } | 190 } |
48 | 191 |
49 Clipboard::Clipboard() : set_text_(NULL), has_text_(NULL), get_text_(NULL) { | 192 Clipboard::Clipboard() { |
50 } | 193 } |
51 | 194 |
52 Clipboard::~Clipboard() { | 195 Clipboard::~Clipboard() { |
53 } | 196 } |
54 | 197 |
| 198 // Main entry point used to write several values in the clipboard. |
55 void Clipboard::WriteObjects(Buffer buffer, const ObjectMap& objects) { | 199 void Clipboard::WriteObjects(Buffer buffer, const ObjectMap& objects) { |
| 200 DCHECK_EQ(buffer, BUFFER_STANDARD); |
| 201 g_map.Get().Clear(); |
| 202 for (ObjectMap::const_iterator iter = objects.begin(); |
| 203 iter != objects.end(); ++iter) { |
| 204 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); |
| 205 } |
56 } | 206 } |
57 | 207 |
58 uint64 Clipboard::GetSequenceNumber(Clipboard::Buffer /* buffer */) { | 208 uint64 Clipboard::GetSequenceNumber(Clipboard::Buffer /* buffer */) { |
| 209 // TODO: implement this. For now this interface will advertise |
| 210 // that the clipboard never changes. That's fine as long as we |
| 211 // don't rely on this signal. |
59 return 0; | 212 return 0; |
60 } | 213 } |
61 | 214 |
62 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, | 215 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, |
63 Clipboard::Buffer buffer) const { | 216 Clipboard::Buffer buffer) const { |
64 return false; | 217 DCHECK_EQ(buffer, BUFFER_STANDARD); |
| 218 return g_map.Get().HasFormat(format.data()); |
65 } | 219 } |
66 | 220 |
67 void Clipboard::Clear(Buffer buffer) { | 221 void Clipboard::Clear(Buffer buffer) { |
68 | 222 DCHECK_EQ(buffer, BUFFER_STANDARD); |
| 223 g_map.Get().Clear(); |
69 } | 224 } |
70 | 225 |
71 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, | 226 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, |
72 bool* contains_filenames) const { | 227 bool* contains_filenames) const { |
| 228 DCHECK_EQ(buffer, BUFFER_STANDARD); |
| 229 |
| 230 if (!types || !contains_filenames) { |
| 231 NOTREACHED(); |
| 232 return; |
| 233 } |
| 234 |
| 235 NOTIMPLEMENTED(); |
| 236 |
| 237 types->clear(); |
| 238 *contains_filenames = false; |
73 } | 239 } |
74 | 240 |
75 void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const { | 241 void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const { |
| 242 DCHECK_EQ(buffer, BUFFER_STANDARD); |
| 243 std::string utf8; |
| 244 ReadAsciiText(buffer, &utf8); |
| 245 *result = UTF8ToUTF16(utf8); |
76 } | 246 } |
77 | 247 |
78 void Clipboard::ReadAsciiText(Clipboard::Buffer buffer, | 248 void Clipboard::ReadAsciiText(Clipboard::Buffer buffer, |
79 std::string* result) const { | 249 std::string* result) const { |
| 250 DCHECK_EQ(buffer, BUFFER_STANDARD); |
| 251 *result = g_map.Get().Get(kPlainTextFormat); |
80 } | 252 } |
81 | 253 |
| 254 // Note: |src_url| isn't really used. It is only implemented in Windows |
82 void Clipboard::ReadHTML(Clipboard::Buffer buffer, | 255 void Clipboard::ReadHTML(Clipboard::Buffer buffer, |
83 string16* markup, | 256 string16* markup, |
84 std::string* src_url, | 257 std::string* src_url, |
85 uint32* fragment_start, | 258 uint32* fragment_start, |
86 uint32* fragment_end) const { | 259 uint32* fragment_end) const { |
| 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()); |
87 } | 269 } |
88 | 270 |
89 void Clipboard::ReadRTF(Buffer buffer, std::string* result) const { | 271 void Clipboard::ReadRTF(Buffer buffer, std::string* result) const { |
| 272 NOTIMPLEMENTED(); |
90 } | 273 } |
91 | 274 |
92 SkBitmap Clipboard::ReadImage(Buffer buffer) const { | 275 SkBitmap Clipboard::ReadImage(Buffer buffer) const { |
93 return SkBitmap(); | 276 DCHECK_EQ(buffer, BUFFER_STANDARD); |
| 277 std::string input = g_map.Get().Get(kBitmapFormat); |
| 278 |
| 279 SkBitmap bmp; |
| 280 if (!input.empty()) { |
| 281 DCHECK_LE(sizeof(gfx::Size), input.size()); |
| 282 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(input.data()); |
| 283 |
| 284 bmp.setConfig( |
| 285 SkBitmap::kARGB_8888_Config, size->width(), size->height(), 0); |
| 286 bmp.allocPixels(); |
| 287 |
| 288 int bm_size = size->width() * size->height() * 4; |
| 289 DCHECK_EQ(sizeof(gfx::Size) + bm_size, input.size()); |
| 290 |
| 291 memcpy(bmp.getPixels(), input.data() + sizeof(gfx::Size), bm_size); |
| 292 } |
| 293 return bmp; |
94 } | 294 } |
95 | 295 |
96 void Clipboard::ReadCustomData(Buffer buffer, | 296 void Clipboard::ReadCustomData(Buffer buffer, |
97 const string16& type, | 297 const string16& type, |
98 string16* result) const { | 298 string16* result) const { |
| 299 NOTIMPLEMENTED(); |
99 } | 300 } |
100 | 301 |
101 void Clipboard::ReadBookmark(string16* title, std::string* url) const { | 302 void Clipboard::ReadBookmark(string16* title, std::string* url) const { |
| 303 NOTIMPLEMENTED(); |
102 } | 304 } |
103 | 305 |
104 void Clipboard::ReadData(const Clipboard::FormatType& format, | 306 void Clipboard::ReadData(const Clipboard::FormatType& format, |
105 std::string* result) const { | 307 std::string* result) const { |
| 308 *result = g_map.Get().Get(format.data()); |
106 } | 309 } |
107 | 310 |
108 // static | 311 // static |
109 Clipboard::FormatType Clipboard::GetFormatType( | 312 Clipboard::FormatType Clipboard::GetFormatType( |
110 const std::string& format_string) { | 313 const std::string& format_string) { |
111 return FormatType::Deserialize(format_string); | 314 return FormatType::Deserialize(format_string); |
112 } | 315 } |
113 | 316 |
114 // static | 317 // static |
115 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { | 318 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 return type; | 350 return type; |
148 } | 351 } |
149 | 352 |
150 // static | 353 // static |
151 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { | 354 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { |
152 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); | 355 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); |
153 return type; | 356 return type; |
154 } | 357 } |
155 | 358 |
156 void Clipboard::WriteText(const char* text_data, size_t text_len) { | 359 void Clipboard::WriteText(const char* text_data, size_t text_len) { |
| 360 g_map.Get().Set(kPlainTextFormat, std::string(text_data, text_len)); |
157 } | 361 } |
158 | 362 |
159 void Clipboard::WriteHTML(const char* markup_data, | 363 void Clipboard::WriteHTML(const char* markup_data, |
160 size_t markup_len, | 364 size_t markup_len, |
161 const char* url_data, | 365 const char* url_data, |
162 size_t url_len) { | 366 size_t url_len) { |
| 367 g_map.Get().Set(kHTMLFormat, std::string(markup_data, markup_len)); |
163 } | 368 } |
164 | 369 |
165 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { | 370 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { |
| 371 NOTIMPLEMENTED(); |
166 } | 372 } |
167 | 373 |
| 374 // Note: according to other platforms implementations, this really writes the |
| 375 // URL spec. |
168 void Clipboard::WriteBookmark(const char* title_data, size_t title_len, | 376 void Clipboard::WriteBookmark(const char* title_data, size_t title_len, |
169 const char* url_data, size_t url_len) { | 377 const char* url_data, size_t url_len) { |
| 378 g_map.Get().Set(kBookmarkFormat, std::string(url_data, url_len)); |
170 } | 379 } |
171 | 380 |
| 381 // Write an extra flavor that signifies WebKit was the last to modify the |
| 382 // pasteboard. This flavor has no data. |
172 void Clipboard::WriteWebSmartPaste() { | 383 void Clipboard::WriteWebSmartPaste() { |
| 384 g_map.Get().Set(kWebKitSmartPasteFormat, std::string()); |
173 } | 385 } |
174 | 386 |
| 387 // All platforms use gfx::Size for size data but it is passed as a const char* |
| 388 // Further, pixel_data is expected to be 32 bits per pixel |
| 389 // Note: we implement this to pass all unit tests but it is currently unclear |
| 390 // how some code would consume this. |
175 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { | 391 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { |
| 392 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data); |
| 393 int bm_size = size->width() * size->height() * 4; |
| 394 |
| 395 std::string packed(size_data, sizeof(gfx::Size)); |
| 396 packed += std::string(pixel_data, bm_size); |
| 397 g_map.Get().Set(kBitmapFormat, packed); |
176 } | 398 } |
177 | 399 |
178 void Clipboard::WriteData(const Clipboard::FormatType& format, | 400 void Clipboard::WriteData(const Clipboard::FormatType& format, |
179 const char* data_data, size_t data_len) { | 401 const char* data_data, size_t data_len) { |
| 402 g_map.Get().Set(format.data(), std::string(data_data, data_len)); |
180 } | 403 } |
181 | 404 |
182 bool Clipboard::IsTextAvailableFromAndroid() const { | 405 } // namespace ui |
183 return false; | |
184 } | |
185 | |
186 void Clipboard::ValidateInternalClipboard() const { | |
187 } | |
188 | |
189 void Clipboard::Clear() { | |
190 } | |
191 | |
192 void Clipboard::ClearInternalClipboard() const { | |
193 } | |
194 | |
195 void Clipboard::Set(const std::string& key, const std::string& value) { | |
196 } | |
197 | |
198 } // namespace ui | |
OLD | NEW |