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

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