Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(645)

Side by Side Diff: ui/base/clipboard/clipboard_android.cc

Issue 9264014: Upstream the clipboard implementation for Android (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed dcheng's comments Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 <jni.h>
8
9 #include "base/android/jni_android.h"
10 #include "base/android/scoped_java_ref.h"
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/synchronization/lock.h"
15 #include "base/utf_string_conversions.h"
16 #include "third_party/skia/include/core/SkBitmap.h"
17 #include "ui/gfx/size.h"
18
19 // Important note:
20 // Android's clipboard system only supports text content, so use it only when
21 // text is added to or retrieved from the system. For other data types, store
22 // the value in a map. This has the consequence that the clipboard's contents
23 // will only be available within the current process.
24
25 // Global contents map and its lock.
26 using base::android::AttachCurrentThread;
27 using base::android::CheckException;
28 using base::android::ClearException;
29 using base::android::ScopedJavaLocalRef;
30
31 namespace ui {
32
33 namespace {
34 // As Android only supports text in the clipboard, the following map will be
35 // used for other kinds of data. Use the lock to make this thread-safe.
36 // TODO(beverloo): http://crbug.com/112286 Investigate whether the locks in
37 // this file are required.
38 typedef std::map<std::string, std::string> ClipboardMap;
39 ClipboardMap* g_clipboard_map = NULL;
40 base::LazyInstance<base::Lock> g_clipboard_map_lock = LAZY_INSTANCE_INITIALIZER;
41
42 // Various format we support
43 const char kPlainTextFormat[] = "text";
44 const char kHTMLFormat[] = "html";
45 const char kBitmapFormat[] = "bitmap";
46 const char kWebKitSmartPasteFormat[] = "webkit_smart";
47 const char kBookmarkFormat[] = "bookmark";
48 const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data";
49
50 } // namespace
51
52 Clipboard::FormatType::FormatType() {
53 }
54
55 Clipboard::FormatType::FormatType(const std::string& native_format)
56 : data_(native_format) {
57 }
58
59 Clipboard::FormatType::~FormatType() {
60 }
61
62 std::string Clipboard::FormatType::Serialize() const {
63 return data_;
64 }
65
66 // static
67 Clipboard::FormatType Clipboard::FormatType::Deserialize(
68 const std::string& serialization) {
69 return FormatType(serialization);
70 }
71
72 bool Clipboard::FormatType::Equals(const FormatType& other) const {
73 return data_ == other.data_;
74 }
75
76 // The clipboard object on the Android platform is simply wrapping the Java
77 // object for the text data format. For non-text format, a global map is used.
78 Clipboard::Clipboard()
79 : clipboard_manager_(NULL),
80 set_text_(NULL),
81 has_text_(NULL),
82 get_text_(NULL),
83 to_string_(NULL) {
84 JNIEnv* env = AttachCurrentThread();
85 DCHECK(env);
86
87 // Get the context
88 jobject context = env->NewLocalRef(base::android::GetApplicationContext());
89
90 if (!context) {
91 // Should be during testing only
92 // Get the ActivityThread class
93 ScopedJavaLocalRef<jclass> activity_thread_class(env,
94 env->FindClass("android/app/ActivityThread"));
95 DCHECK(activity_thread_class.obj());
96
97 // Try to get the current activity thread.
98 jmethodID current_activity_method_id =
99 env->GetStaticMethodID(activity_thread_class.obj(),
100 "currentActivityThread",
101 "()Landroid/app/ActivityThread;");
102 DCHECK(current_activity_method_id);
103 jobject current_activity =
104 env->CallStaticObjectMethod(activity_thread_class.obj(),
105 current_activity_method_id);
106 if (ClearException(env))
107 current_activity = NULL;
108
109 if (!current_activity) {
110 // There is no current activity, create one
111 ScopedJavaLocalRef<jclass> looper_class(env,
112 env->FindClass("android/os/Looper"));
113 jmethodID prepare_method_id = env->GetStaticMethodID(looper_class.obj(),
114 "prepareMainLooper", "()V");
115 env->CallStaticVoidMethod(looper_class.obj(), prepare_method_id);
116 CheckException(env);
117
118 jmethodID system_main_method_id =
119 env->GetStaticMethodID(activity_thread_class.obj(), "systemMain",
120 "()Landroid/app/ActivityThread;");
121 DCHECK(system_main_method_id);
122
123 current_activity = env->CallStaticObjectMethod(
124 activity_thread_class.obj(), system_main_method_id);
125 DCHECK(current_activity);
126 CheckException(env);
127 }
128
129 // Get the context
130 jmethodID get_system_context_id =
131 env->GetMethodID(activity_thread_class.obj(),
132 "getSystemContext", "()Landroid/app/ContextImpl;");
133
134 DCHECK(get_system_context_id);
135 context = env->CallObjectMethod(current_activity,
136 get_system_context_id);
137 DCHECK(context);
138
139 env->DeleteLocalRef(current_activity);
140 }
141
142 // Get the context class
143 jclass context_class = env->FindClass("android/content/Context");
144 DCHECK(context_class);
145 // Get the system service method
146 jmethodID get_system_service =
147 env->GetMethodID(context_class, "getSystemService",
148 "(Ljava/lang/String;)Ljava/lang/Object;");
149 DCHECK(get_system_service);
150 env->DeleteLocalRef(context_class);
151
152 // Retrieve the system service
153 jstring service_name = env->NewStringUTF("clipboard");
154 jobject cm = env->CallObjectMethod(context, get_system_service, service_name);
155 if (ClearException(env))
156 cm = NULL;
157 DCHECK(cm);
158
159 // Make a global reference for our clipboard manager.
160 clipboard_manager_ = env->NewGlobalRef(cm);
161 DCHECK(clipboard_manager_);
162 env->DeleteLocalRef(cm);
163
164 // Retain a few methods we'll keep using
165 jclass clipboard_class = env->FindClass("android/text/ClipboardManager");
166 DCHECK(clipboard_class);
167 set_text_ = env->GetMethodID(clipboard_class, "setText",
168 "(Ljava/lang/CharSequence;)V");
169 DCHECK(set_text_);
170 has_text_ = env->GetMethodID(clipboard_class, "hasText", "()Z");
171 DCHECK(has_text_);
172 get_text_ = env->GetMethodID(clipboard_class, "getText",
173 "()Ljava/lang/CharSequence;");
174 DCHECK(get_text_);
175
176 // Will need to call toString as CharSequence is not always a String
177 jclass charsequence_class = env->FindClass("java/lang/CharSequence");
178 DCHECK(charsequence_class);
179 to_string_ = env->GetMethodID(charsequence_class, "toString",
180 "()Ljava/lang/String;");
181 DCHECK(to_string_);
182 env->DeleteLocalRef(clipboard_class);
sky 2012/02/03 17:14:35 Create scopying objects so that all this code isn'
Peter Beverloo 2012/02/07 12:25:03 This is under review downstream: https://gerrit-in
183 env->DeleteLocalRef(charsequence_class);
184
185 // Finally cleanup all our local reference so they don't stay around if this
186 // code was never called from Java. (unit test case)
187 env->DeleteLocalRef(service_name);
188 env->DeleteLocalRef(context);
189
190 // Create the object map if we are the first clipboard
191 g_clipboard_map_lock.Get().Acquire();
192 if (!g_clipboard_map)
193 g_clipboard_map = new ClipboardMap;
194 g_clipboard_map_lock.Get().Release();
195 }
196
197 Clipboard::~Clipboard() {
198 // Delete the clipboard manager global ref
199 if (clipboard_manager_) {
200 JNIEnv* env = AttachCurrentThread();
201 env->DeleteGlobalRef(clipboard_manager_);
202 }
203 }
204
205 // Main entry point used to write several values in the clipboard.
206 void Clipboard::WriteObjects(const ObjectMap& objects) {
207 Clear();
208 for (ObjectMap::const_iterator iter = objects.begin();
209 iter != objects.end(); ++iter) {
210 DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
211 }
212 }
213
214 uint64 Clipboard::GetSequenceNumber(Clipboard::Buffer /* buffer */) {
215 // TODO: Implement this. For now this interface will advertise
216 // that the clipboard never changes. That's fine as long as we
217 // don't rely on this signal.
218 return 0;
219 }
220
221 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format,
222 Clipboard::Buffer buffer) const {
223 DCHECK_EQ(buffer, BUFFER_STANDARD);
224
225 if (!format.compare(kPlainTextFormat))
226 return IsTextAvailableFromAndroid();
227
228 ValidateInternalClipboard();
229
230 base::AutoLock lock(g_clipboard_map_lock.Get());
231 return g_clipboard_map->find(format.ToString()) != g_clipboard_map->end();
232 }
233
234 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types,
235 bool* contains_filenames) const {
236 if (!types || !contains_filenames) {
237 NOTREACHED();
238 return;
239 }
240
241 // This is unimplemented on the other platforms (e.g. win, linux).
242 NOTIMPLEMENTED();
243
244 types->clear();
245 *contains_filenames = false;
246 }
247
248 void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const {
249 JNIEnv* env = AttachCurrentThread();
250
251 result->clear();
252 if (env->CallBooleanMethod(clipboard_manager_, has_text_)) {
253 jstring tmp_string =
254 static_cast<jstring>(env->CallObjectMethod(
255 env->CallObjectMethod(clipboard_manager_, get_text_), to_string_));
256 jboolean is_copy = JNI_FALSE;
257 const char* tmp_string_val = env->GetStringUTFChars(tmp_string, &is_copy);
258 jsize len = env->GetStringUTFLength(tmp_string);
259 UTF8ToUTF16(tmp_string_val, len, result);
260 env->ReleaseStringUTFChars(tmp_string, tmp_string_val);
261 env->DeleteLocalRef(static_cast<jobject>(tmp_string));
262 }
263 }
264
265 void Clipboard::ReadAsciiText(Clipboard::Buffer buffer,
266 std::string* result) const {
267 JNIEnv* env = AttachCurrentThread();
268
269 result->clear();
270 if (env->CallBooleanMethod(clipboard_manager_, has_text_)) {
271 jstring tmp_string =
272 static_cast<jstring>(env->CallObjectMethod(
273 env->CallObjectMethod(clipboard_manager_, get_text_), to_string_));
274 jboolean is_copy = JNI_FALSE;
275 const char* tmp_string_val = env->GetStringUTFChars(tmp_string, &is_copy);
276 jsize len = env->GetStringUTFLength(tmp_string);
277 *result = std::string(tmp_string_val, len);
278 env->ReleaseStringUTFChars(tmp_string, tmp_string_val);
279 env->DeleteLocalRef(static_cast<jobject>(tmp_string));
280 }
281 }
282
283 // Note: |src_url| isn't really used. It is only implemented in Windows
284 void Clipboard::ReadHTML(Clipboard::Buffer buffer,
285 string16* markup,
286 std::string* src_url,
287 uint32* fragment_start,
288 uint32* fragment_end) const {
289 markup->clear();
290 if (src_url)
291 src_url->clear();
292 *fragment_start = 0;
293 *fragment_end = 0;
294
295 std::string input;
296
297 ValidateInternalClipboard();
298
299 g_clipboard_map_lock.Get().Acquire();
300 ClipboardMap::const_iterator it = g_clipboard_map->find(kHTMLFormat);
301 if (it != g_clipboard_map->end())
302 input = it->second;
303 g_clipboard_map_lock.Get().Release();
304
305 if (input.empty())
306 return;
307
308 *fragment_end = static_cast<uint32>(input.length());
309
310 UTF8ToUTF16(input.c_str(), input.length(), markup);
311 }
312
313 SkBitmap Clipboard::ReadImage(Buffer buffer) const {
314 NOTIMPLEMENTED();
315 return SkBitmap();
316 }
317
318 void Clipboard::ReadCustomData(Buffer buffer,
319 const string16& type,
320 string16* result) const {
321 NOTIMPLEMENTED();
322 }
323
324 void Clipboard::ReadBookmark(string16* title, std::string* url) const {
325 NOTIMPLEMENTED();
326 }
327
328 void Clipboard::ReadData(const Clipboard::FormatType& format,
329 std::string* result) const {
330 result->clear();
331
332 ValidateInternalClipboard();
333
334 g_clipboard_map_lock.Get().Acquire();
335 ClipboardMap::const_iterator it = g_clipboard_map->find(format.ToString());
336 if (it != g_clipboard_map->end())
337 result->assign(it->second);
338 g_clipboard_map_lock.Get().Release();
339 }
340
341 // static
342 Clipboard::FormatType Clipboard::GetFormatType(
343 const std::string& format_string) {
344 return FormatType::Deserialize(format_string);
345 }
346
347 // static
348 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
349 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat));
350 return type;
351 }
352
353 // static
354 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
355 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat));
356 return type;
357 }
358
359 // static
360 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
361 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebKitSmartPasteFormat));
362 return type;
363 }
364
365 // static
366 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
367 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kHTMLFormat));
368 return type;
369 }
370
371 // static
372 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
373 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kBitmapFormat));
374 return type;
375 }
376
377 // static
378 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
379 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData));
380 return type;
381 }
382
383 void Clipboard::WriteText(const char* text_data, size_t text_len) {
384 // Write the text in the Android Clipboard
385 JNIEnv* env = AttachCurrentThread();
386 DCHECK(env);
387
388 std::string data(text_data, text_len);
389 jstring str = env->NewStringUTF(data.c_str());
390 DCHECK(str);
391
392 env->CallVoidMethod(clipboard_manager_, set_text_, str);
393 env->DeleteLocalRef(str);
394
395 // Then write it in our internal data structure. We keep it there to check if
396 // another app performed a copy. See ValidateInternalClipboard()
397 Set(kPlainTextFormat, std::string(text_data, text_len));
398 }
399
400 void Clipboard::WriteHTML(const char* markup_data,
401 size_t markup_len,
402 const char* url_data,
403 size_t url_len) {
404 Set(kHTMLFormat, std::string(markup_data, markup_len));
405 }
406
407 // Note: according to other platforms implementations, this really writes the
408 // URL spec
409 void Clipboard::WriteBookmark(const char* title_data, size_t title_len,
410 const char* url_data, size_t url_len) {
411 Set(kBookmarkFormat, std::string(url_data, url_len));
412 }
413
414 // Write an extra flavor that signifies WebKit was the last to modify the
415 // pasteboard. This flavor has no data.
416 void Clipboard::WriteWebSmartPaste() {
417 Set(kWebKitSmartPasteFormat, std::string());
418 }
419
420 // All platforms use gfx::Size for size data but it is passed as a const char*
421 // Further, pixel_data is expected to be 32 bits per pixel
422 // Note: we implement this to pass all unit tests but it is currently unclear
423 // how some code would consume this.
424 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
425 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
426 int bm_size = size->width() * size->height() * 4;
427 int total_size = (sizeof(int) * 2) + bm_size;
428 scoped_array<char> buffer(new char[total_size]);
429
430 char* p = buffer.get();
431 int n = size->width();
432 memcpy(p, &n, sizeof(n));
433 p += sizeof(n);
434 n = size->height();
435 memcpy(p, &n, sizeof(n));
436 p += sizeof(n);
437 memcpy(p, pixel_data, bm_size);
438
439 Set(kBitmapFormat, std::string(buffer.get(), total_size));
440 }
441
442 void Clipboard::WriteData(const Clipboard::FormatType& format,
443 const char* data_data, size_t data_len) {
444 Set(format.ToString(), std::string(data_data, data_len));
445 }
446
447 bool Clipboard::IsTextAvailableFromAndroid() const {
448 JNIEnv* env = AttachCurrentThread();
449 return env->CallBooleanMethod(clipboard_manager_, has_text_);
450 }
451
452 void Clipboard::ValidateInternalClipboard() const {
453 JNIEnv* env = AttachCurrentThread();
454
455 // First collect what text we currently have in our internal clipboard
456 bool has_internal_text;
457 std::string internal_text;
458 g_clipboard_map_lock.Get().Acquire();
459 ClipboardMap::const_iterator it = g_clipboard_map->find(kPlainTextFormat);
460 if (it != g_clipboard_map->end()) {
461 has_internal_text = true;
462 internal_text = it->second;
463 } else {
464 has_internal_text = false;
465 }
466 g_clipboard_map_lock.Get().Release();
467
468 if (IsTextAvailableFromAndroid()) {
469 // Make sure the text in the Android Clipboard matches what we think it
470 // should be.
471 jstring tmp_string =
472 static_cast<jstring>(env->CallObjectMethod(
473 env->CallObjectMethod(clipboard_manager_, get_text_), to_string_));
474 jboolean is_copy = JNI_FALSE;
475 const char* tmp_string_val = env->GetStringUTFChars(tmp_string, &is_copy);
476 jsize len = env->GetStringUTFLength(tmp_string);
477
478 std::string android_text(tmp_string_val, len);
479
480 // If the android text doesn't match what we think it should be, our
481 // internal representation is no longer valid.
482 if (android_text.compare(internal_text))
483 ClearInternalClipboard();
484
485 env->ReleaseStringUTFChars(tmp_string, tmp_string_val);
486 env->DeleteLocalRef(static_cast<jobject>(tmp_string));
487 } else {
488 // If Android has no text but we have some internal text, our internal
489 // representation is no longer valid.
490 if (has_internal_text)
491 ClearInternalClipboard();
492 }
493 }
494
495 void Clipboard::Clear() {
496 JNIEnv* env = AttachCurrentThread();
497 env->CallVoidMethod(clipboard_manager_, set_text_, NULL);
498 ClearInternalClipboard();
499 }
500
501 void Clipboard::ClearInternalClipboard() const {
502 base::AutoLock lock(g_clipboard_map_lock.Get());
503 g_clipboard_map->clear();
504 }
505
506 void Clipboard::Set(const std::string& key, const std::string& value) {
507 base::AutoLock lock(g_clipboard_map_lock.Get());
508 (*g_clipboard_map)[key] = value;
509 }
510
511 } // namespace ui
OLDNEW
« ui/base/clipboard/clipboard.h ('K') | « ui/base/clipboard/clipboard.h ('k') | ui/ui.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698