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

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