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.
5 #include "ui/base/clipboard/clipboard_android.h"
7 #include "base/android/jni_string.h"
8 #include "base/lazy_instance.h"
9 #include "base/stl_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/synchronization/lock.h"
12 #include "jni/Clipboard_jni.h"
13 #include "third_party/skia/include/core/SkBitmap.h"
14 #include "ui/gfx/geometry/size.h"
16 // TODO:(andrewhayden) Support additional formats in Android: Bitmap, URI, HTML,
17 // HTML+text now that Android's clipboard system supports them, then nuke the
18 // legacy implementation note below.
20 // Legacy implementation note:
21 // The Android clipboard system used to only support text format. So we used the
22 // Android system when some text was added or retrieved from the system. For
23 // anything else, we STILL store the value in some process wide static
24 // variable protected by a lock. So the (non-text) clipboard will only work
25 // within the same process.
27 using base::android::AttachCurrentThread
;
28 using base::android::ClearException
;
29 using base::android::ConvertJavaStringToUTF8
;
30 using base::android::ConvertUTF8ToJavaString
;
31 using base::android::ScopedJavaGlobalRef
;
32 using base::android::ScopedJavaLocalRef
;
37 // Various formats we support.
38 const char kURLFormat
[] = "url";
39 const char kPlainTextFormat
[] = "text";
40 const char kHTMLFormat
[] = "html";
41 const char kRTFFormat
[] = "rtf";
42 const char kBitmapFormat
[] = "bitmap";
43 const char kWebKitSmartPasteFormat
[] = "webkit_smart";
44 const char kBookmarkFormat
[] = "bookmark";
45 const char kMimeTypePepperCustomData
[] = "chromium/x-pepper-custom-data";
46 const char kMimeTypeWebCustomData
[] = "chromium/x-web-custom-data";
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
);
57 void SyncWithAndroidClipboard();
58 std::map
<std::string
, std::string
> map_
;
61 // Java class and methods for the Android ClipboardManager.
62 ScopedJavaGlobalRef
<jobject
> clipboard_manager_
;
64 base::LazyInstance
<ClipboardMap
>::Leaky g_map
= LAZY_INSTANCE_INITIALIZER
;
66 ClipboardMap::ClipboardMap() {
67 JNIEnv
* env
= AttachCurrentThread();
71 jobject context
= base::android::GetApplicationContext();
74 ScopedJavaLocalRef
<jobject
> local_ref
=
75 Java_Clipboard_create(env
, context
);
76 DCHECK(local_ref
.obj());
77 clipboard_manager_
.Reset(env
, local_ref
.Release());
80 std::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
;
87 bool ClipboardMap::HasFormat(const std::string
& format
) {
88 base::AutoLock
lock(lock_
);
89 SyncWithAndroidClipboard();
90 return ContainsKey(map_
, format
);
93 void ClipboardMap::Set(const std::string
& format
, const std::string
& data
) {
94 JNIEnv
* env
= AttachCurrentThread();
95 base::AutoLock
lock(lock_
);
96 SyncWithAndroidClipboard();
99 if (format
== kPlainTextFormat
) {
100 ScopedJavaLocalRef
<jstring
> str
= ConvertUTF8ToJavaString(env
, data
);
103 Java_Clipboard_setText(env
, clipboard_manager_
.obj(), str
.obj());
104 } else if (format
== kHTMLFormat
) {
105 // Android's API for storing HTML content on the clipboard requires a plain-
106 // text representation to be available as well. ScopedClipboardWriter has a
107 // stable order for setting clipboard data, ensuring that plain-text data
108 // is available first. Do not write to the clipboard when only HTML data is
109 // available, because otherwise others apps may not be able to paste it.
110 if (!ContainsKey(map_
, kPlainTextFormat
))
113 ScopedJavaLocalRef
<jstring
> html
= ConvertUTF8ToJavaString(env
, data
);
114 ScopedJavaLocalRef
<jstring
> text
= ConvertUTF8ToJavaString(
115 env
, map_
[kPlainTextFormat
].c_str());
117 DCHECK(html
.obj() && text
.obj());
118 Java_Clipboard_setHTMLText(
119 env
, clipboard_manager_
.obj(), html
.obj(), text
.obj());
123 void ClipboardMap::Clear() {
124 JNIEnv
* env
= AttachCurrentThread();
125 base::AutoLock
lock(lock_
);
127 Java_Clipboard_setText(env
, clipboard_manager_
.obj(), NULL
);
130 // If the internal map contains a plain-text entry and it does not match that
131 // in the Android clipboard, clear the map and insert the Android text into it.
132 // If there is an HTML entry in the Android clipboard it gets inserted in the
134 void ClipboardMap::SyncWithAndroidClipboard() {
135 lock_
.AssertAcquired();
136 JNIEnv
* env
= AttachCurrentThread();
138 // Update the plain text clipboard entry
139 std::map
<std::string
, std::string
>::const_iterator it
=
140 map_
.find(kPlainTextFormat
);
141 ScopedJavaLocalRef
<jstring
> java_string_text
=
142 Java_Clipboard_getCoercedText(env
, clipboard_manager_
.obj());
143 if (java_string_text
.obj()) {
144 std::string android_string
= ConvertJavaStringToUTF8(java_string_text
);
145 if (!android_string
.empty() &&
146 (it
== map_
.end() || it
->second
!= android_string
)) {
147 // There is a different string in the Android clipboard than we have.
148 // Clear the map on our side.
150 map_
[kPlainTextFormat
] = android_string
;
153 if (it
!= map_
.end()) {
154 // We have plain text on this side, but Android doesn't. Nuke ours.
159 if (!Java_Clipboard_isHTMLClipboardSupported(env
)) {
163 // Update the html clipboard entry
164 ScopedJavaLocalRef
<jstring
> java_string_html
=
165 Java_Clipboard_getHTMLText(env
, clipboard_manager_
.obj());
166 if (java_string_html
.obj()) {
167 std::string android_string
= ConvertJavaStringToUTF8(java_string_html
);
168 if (!android_string
.empty()) {
169 map_
[kHTMLFormat
] = android_string
;
173 it
= map_
.find(kHTMLFormat
);
174 if (it
!= map_
.end()) {
175 map_
.erase(kHTMLFormat
);
181 // Clipboard::FormatType implementation.
182 Clipboard::FormatType::FormatType() {
185 Clipboard::FormatType::FormatType(const std::string
& native_format
)
186 : data_(native_format
) {
189 Clipboard::FormatType::~FormatType() {
192 std::string
Clipboard::FormatType::Serialize() const {
197 Clipboard::FormatType
Clipboard::FormatType::Deserialize(
198 const std::string
& serialization
) {
199 return FormatType(serialization
);
202 bool Clipboard::FormatType::operator<(const FormatType
& other
) const {
203 return data_
< other
.data_
;
206 bool Clipboard::FormatType::Equals(const FormatType
& other
) const {
207 return data_
== other
.data_
;
210 // Various predefined FormatTypes.
212 Clipboard::FormatType
Clipboard::GetFormatType(
213 const std::string
& format_string
) {
214 return FormatType::Deserialize(format_string
);
218 const Clipboard::FormatType
& Clipboard::GetUrlWFormatType() {
219 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kURLFormat
));
224 const Clipboard::FormatType
& Clipboard::GetPlainTextFormatType() {
225 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kPlainTextFormat
));
230 const Clipboard::FormatType
& Clipboard::GetPlainTextWFormatType() {
231 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kPlainTextFormat
));
236 const Clipboard::FormatType
& Clipboard::GetWebKitSmartPasteFormatType() {
237 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kWebKitSmartPasteFormat
));
242 const Clipboard::FormatType
& Clipboard::GetHtmlFormatType() {
243 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kHTMLFormat
));
248 const Clipboard::FormatType
& Clipboard::GetRtfFormatType() {
249 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kRTFFormat
));
254 const Clipboard::FormatType
& Clipboard::GetBitmapFormatType() {
255 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kBitmapFormat
));
260 const Clipboard::FormatType
& Clipboard::GetWebCustomDataFormatType() {
261 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kMimeTypeWebCustomData
));
266 const Clipboard::FormatType
& Clipboard::GetPepperCustomDataFormatType() {
267 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kMimeTypePepperCustomData
));
271 // Clipboard factory method.
273 Clipboard
* Clipboard::Create() {
274 return new ClipboardAndroid
;
277 // ClipboardAndroid implementation.
278 ClipboardAndroid::ClipboardAndroid() {
279 DCHECK(CalledOnValidThread());
282 ClipboardAndroid::~ClipboardAndroid() {
283 DCHECK(CalledOnValidThread());
286 uint64
ClipboardAndroid::GetSequenceNumber(ClipboardType
/* type */) const {
287 DCHECK(CalledOnValidThread());
288 // TODO: implement this. For now this interface will advertise
289 // that the clipboard never changes. That's fine as long as we
290 // don't rely on this signal.
294 bool ClipboardAndroid::IsFormatAvailable(const Clipboard::FormatType
& format
,
295 ClipboardType type
) const {
296 DCHECK(CalledOnValidThread());
297 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
298 return g_map
.Get().HasFormat(format
.ToString());
301 void ClipboardAndroid::Clear(ClipboardType type
) {
302 DCHECK(CalledOnValidThread());
303 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
307 void ClipboardAndroid::ReadAvailableTypes(ClipboardType type
,
308 std::vector
<base::string16
>* types
,
309 bool* contains_filenames
) const {
310 DCHECK(CalledOnValidThread());
311 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
313 if (!types
|| !contains_filenames
) {
321 *contains_filenames
= false;
324 void ClipboardAndroid::ReadText(ClipboardType type
,
325 base::string16
* result
) const {
326 DCHECK(CalledOnValidThread());
327 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
329 ReadAsciiText(type
, &utf8
);
330 *result
= base::UTF8ToUTF16(utf8
);
333 void ClipboardAndroid::ReadAsciiText(ClipboardType type
,
334 std::string
* result
) const {
335 DCHECK(CalledOnValidThread());
336 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
337 *result
= g_map
.Get().Get(kPlainTextFormat
);
340 // Note: |src_url| isn't really used. It is only implemented in Windows
341 void ClipboardAndroid::ReadHTML(ClipboardType type
,
342 base::string16
* markup
,
343 std::string
* src_url
,
344 uint32
* fragment_start
,
345 uint32
* fragment_end
) const {
346 DCHECK(CalledOnValidThread());
347 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
351 std::string input
= g_map
.Get().Get(kHTMLFormat
);
352 *markup
= base::UTF8ToUTF16(input
);
355 *fragment_end
= static_cast<uint32
>(markup
->length());
358 void ClipboardAndroid::ReadRTF(ClipboardType type
, std::string
* result
) const {
359 DCHECK(CalledOnValidThread());
363 SkBitmap
ClipboardAndroid::ReadImage(ClipboardType type
) const {
364 DCHECK(CalledOnValidThread());
365 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
366 std::string input
= g_map
.Get().Get(kBitmapFormat
);
369 if (!input
.empty()) {
370 DCHECK_LE(sizeof(gfx::Size
), input
.size());
371 const gfx::Size
* size
= reinterpret_cast<const gfx::Size
*>(input
.data());
373 bmp
.allocN32Pixels(size
->width(), size
->height());
375 DCHECK_EQ(sizeof(gfx::Size
) + bmp
.getSize(), input
.size());
377 memcpy(bmp
.getPixels(), input
.data() + sizeof(gfx::Size
), bmp
.getSize());
382 void ClipboardAndroid::ReadCustomData(ClipboardType clipboard_type
,
383 const base::string16
& type
,
384 base::string16
* result
) const {
385 DCHECK(CalledOnValidThread());
389 void ClipboardAndroid::ReadBookmark(base::string16
* title
,
390 std::string
* url
) const {
391 DCHECK(CalledOnValidThread());
395 void ClipboardAndroid::ReadData(const Clipboard::FormatType
& format
,
396 std::string
* result
) const {
397 DCHECK(CalledOnValidThread());
398 *result
= g_map
.Get().Get(format
.ToString());
401 // Main entry point used to write several values in the clipboard.
402 void ClipboardAndroid::WriteObjects(ClipboardType type
,
403 const ObjectMap
& objects
) {
404 DCHECK(CalledOnValidThread());
405 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
407 for (ObjectMap::const_iterator iter
= objects
.begin(); iter
!= objects
.end();
409 DispatchObject(static_cast<ObjectType
>(iter
->first
), iter
->second
);
413 void ClipboardAndroid::WriteText(const char* text_data
, size_t text_len
) {
414 g_map
.Get().Set(kPlainTextFormat
, std::string(text_data
, text_len
));
417 void ClipboardAndroid::WriteHTML(const char* markup_data
,
419 const char* url_data
,
421 g_map
.Get().Set(kHTMLFormat
, std::string(markup_data
, markup_len
));
424 void ClipboardAndroid::WriteRTF(const char* rtf_data
, size_t data_len
) {
428 // Note: according to other platforms implementations, this really writes the
430 void ClipboardAndroid::WriteBookmark(const char* title_data
,
432 const char* url_data
,
434 g_map
.Get().Set(kBookmarkFormat
, std::string(url_data
, url_len
));
437 // Write an extra flavor that signifies WebKit was the last to modify the
438 // pasteboard. This flavor has no data.
439 void ClipboardAndroid::WriteWebSmartPaste() {
440 g_map
.Get().Set(kWebKitSmartPasteFormat
, std::string());
443 // Note: we implement this to pass all unit tests but it is currently unclear
444 // how some code would consume this.
445 void ClipboardAndroid::WriteBitmap(const SkBitmap
& bitmap
) {
446 gfx::Size
size(bitmap
.width(), bitmap
.height());
448 std::string
packed(reinterpret_cast<const char*>(&size
), sizeof(size
));
450 SkAutoLockPixels
bitmap_lock(bitmap
);
451 packed
+= std::string(static_cast<const char*>(bitmap
.getPixels()),
454 g_map
.Get().Set(kBitmapFormat
, packed
);
457 void ClipboardAndroid::WriteData(const Clipboard::FormatType
& format
,
458 const char* data_data
,
460 g_map
.Get().Set(format
.ToString(), std::string(data_data
, data_len
));
463 bool RegisterClipboardAndroid(JNIEnv
* env
) {
464 return RegisterNativesImpl(env
);