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 // Update the html clipboard entry
160 ScopedJavaLocalRef
<jstring
> java_string_html
=
161 Java_Clipboard_getHTMLText(env
, clipboard_manager_
.obj());
162 if (java_string_html
.obj()) {
163 std::string android_string
= ConvertJavaStringToUTF8(java_string_html
);
164 if (!android_string
.empty()) {
165 map_
[kHTMLFormat
] = android_string
;
169 it
= map_
.find(kHTMLFormat
);
170 if (it
!= map_
.end()) {
171 map_
.erase(kHTMLFormat
);
177 // Clipboard::FormatType implementation.
178 Clipboard::FormatType::FormatType() {
181 Clipboard::FormatType::FormatType(const std::string
& native_format
)
182 : data_(native_format
) {
185 Clipboard::FormatType::~FormatType() {
188 std::string
Clipboard::FormatType::Serialize() const {
193 Clipboard::FormatType
Clipboard::FormatType::Deserialize(
194 const std::string
& serialization
) {
195 return FormatType(serialization
);
198 bool Clipboard::FormatType::operator<(const FormatType
& other
) const {
199 return data_
< other
.data_
;
202 bool Clipboard::FormatType::Equals(const FormatType
& other
) const {
203 return data_
== other
.data_
;
206 // Various predefined FormatTypes.
208 Clipboard::FormatType
Clipboard::GetFormatType(
209 const std::string
& format_string
) {
210 return FormatType::Deserialize(format_string
);
214 const Clipboard::FormatType
& Clipboard::GetUrlWFormatType() {
215 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kURLFormat
));
220 const Clipboard::FormatType
& Clipboard::GetPlainTextFormatType() {
221 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kPlainTextFormat
));
226 const Clipboard::FormatType
& Clipboard::GetPlainTextWFormatType() {
227 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kPlainTextFormat
));
232 const Clipboard::FormatType
& Clipboard::GetWebKitSmartPasteFormatType() {
233 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kWebKitSmartPasteFormat
));
238 const Clipboard::FormatType
& Clipboard::GetHtmlFormatType() {
239 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kHTMLFormat
));
244 const Clipboard::FormatType
& Clipboard::GetRtfFormatType() {
245 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kRTFFormat
));
250 const Clipboard::FormatType
& Clipboard::GetBitmapFormatType() {
251 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kBitmapFormat
));
256 const Clipboard::FormatType
& Clipboard::GetWebCustomDataFormatType() {
257 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kMimeTypeWebCustomData
));
262 const Clipboard::FormatType
& Clipboard::GetPepperCustomDataFormatType() {
263 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kMimeTypePepperCustomData
));
267 // Clipboard factory method.
269 Clipboard
* Clipboard::Create() {
270 return new ClipboardAndroid
;
273 // ClipboardAndroid implementation.
274 ClipboardAndroid::ClipboardAndroid() {
275 DCHECK(CalledOnValidThread());
278 ClipboardAndroid::~ClipboardAndroid() {
279 DCHECK(CalledOnValidThread());
282 uint64
ClipboardAndroid::GetSequenceNumber(ClipboardType
/* type */) const {
283 DCHECK(CalledOnValidThread());
284 // TODO: implement this. For now this interface will advertise
285 // that the clipboard never changes. That's fine as long as we
286 // don't rely on this signal.
290 bool ClipboardAndroid::IsFormatAvailable(const Clipboard::FormatType
& format
,
291 ClipboardType type
) const {
292 DCHECK(CalledOnValidThread());
293 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
294 return g_map
.Get().HasFormat(format
.ToString());
297 void ClipboardAndroid::Clear(ClipboardType type
) {
298 DCHECK(CalledOnValidThread());
299 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
303 void ClipboardAndroid::ReadAvailableTypes(ClipboardType type
,
304 std::vector
<base::string16
>* types
,
305 bool* contains_filenames
) const {
306 DCHECK(CalledOnValidThread());
307 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
309 if (!types
|| !contains_filenames
) {
316 // would be nice to ask the ClipboardMap to enumerate the types it supports,
317 // rather than hardcode the list here.
318 if (IsFormatAvailable(Clipboard::GetPlainTextFormatType(), type
))
319 types
->push_back(base::UTF8ToUTF16(kMimeTypeText
));
320 if (IsFormatAvailable(Clipboard::GetHtmlFormatType(), type
))
321 types
->push_back(base::UTF8ToUTF16(kMimeTypeHTML
));
323 // these formats aren't supported by the ClipboardMap currently, but might
325 if (IsFormatAvailable(Clipboard::GetRtfFormatType(), type
))
326 types
->push_back(base::UTF8ToUTF16(kMimeTypeRTF
));
327 if (IsFormatAvailable(Clipboard::GetBitmapFormatType(), type
))
328 types
->push_back(base::UTF8ToUTF16(kMimeTypePNG
));
329 *contains_filenames
= false;
332 void ClipboardAndroid::ReadText(ClipboardType type
,
333 base::string16
* result
) const {
334 DCHECK(CalledOnValidThread());
335 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
337 ReadAsciiText(type
, &utf8
);
338 *result
= base::UTF8ToUTF16(utf8
);
341 void ClipboardAndroid::ReadAsciiText(ClipboardType type
,
342 std::string
* result
) const {
343 DCHECK(CalledOnValidThread());
344 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
345 *result
= g_map
.Get().Get(kPlainTextFormat
);
348 // Note: |src_url| isn't really used. It is only implemented in Windows
349 void ClipboardAndroid::ReadHTML(ClipboardType type
,
350 base::string16
* markup
,
351 std::string
* src_url
,
352 uint32
* fragment_start
,
353 uint32
* fragment_end
) const {
354 DCHECK(CalledOnValidThread());
355 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
359 std::string input
= g_map
.Get().Get(kHTMLFormat
);
360 *markup
= base::UTF8ToUTF16(input
);
363 *fragment_end
= static_cast<uint32
>(markup
->length());
366 void ClipboardAndroid::ReadRTF(ClipboardType type
, std::string
* result
) const {
367 DCHECK(CalledOnValidThread());
371 SkBitmap
ClipboardAndroid::ReadImage(ClipboardType type
) const {
372 DCHECK(CalledOnValidThread());
373 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
374 std::string input
= g_map
.Get().Get(kBitmapFormat
);
377 if (!input
.empty()) {
378 DCHECK_LE(sizeof(gfx::Size
), input
.size());
379 const gfx::Size
* size
= reinterpret_cast<const gfx::Size
*>(input
.data());
381 bmp
.allocN32Pixels(size
->width(), size
->height());
383 DCHECK_EQ(sizeof(gfx::Size
) + bmp
.getSize(), input
.size());
385 memcpy(bmp
.getPixels(), input
.data() + sizeof(gfx::Size
), bmp
.getSize());
390 void ClipboardAndroid::ReadCustomData(ClipboardType clipboard_type
,
391 const base::string16
& type
,
392 base::string16
* result
) const {
393 DCHECK(CalledOnValidThread());
397 void ClipboardAndroid::ReadBookmark(base::string16
* title
,
398 std::string
* url
) const {
399 DCHECK(CalledOnValidThread());
403 void ClipboardAndroid::ReadData(const Clipboard::FormatType
& format
,
404 std::string
* result
) const {
405 DCHECK(CalledOnValidThread());
406 *result
= g_map
.Get().Get(format
.ToString());
409 // Main entry point used to write several values in the clipboard.
410 void ClipboardAndroid::WriteObjects(ClipboardType type
,
411 const ObjectMap
& objects
) {
412 DCHECK(CalledOnValidThread());
413 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
415 for (ObjectMap::const_iterator iter
= objects
.begin(); iter
!= objects
.end();
417 DispatchObject(static_cast<ObjectType
>(iter
->first
), iter
->second
);
421 void ClipboardAndroid::WriteText(const char* text_data
, size_t text_len
) {
422 g_map
.Get().Set(kPlainTextFormat
, std::string(text_data
, text_len
));
425 void ClipboardAndroid::WriteHTML(const char* markup_data
,
427 const char* url_data
,
429 g_map
.Get().Set(kHTMLFormat
, std::string(markup_data
, markup_len
));
432 void ClipboardAndroid::WriteRTF(const char* rtf_data
, size_t data_len
) {
436 // Note: according to other platforms implementations, this really writes the
438 void ClipboardAndroid::WriteBookmark(const char* title_data
,
440 const char* url_data
,
442 g_map
.Get().Set(kBookmarkFormat
, std::string(url_data
, url_len
));
445 // Write an extra flavor that signifies WebKit was the last to modify the
446 // pasteboard. This flavor has no data.
447 void ClipboardAndroid::WriteWebSmartPaste() {
448 g_map
.Get().Set(kWebKitSmartPasteFormat
, std::string());
451 // Note: we implement this to pass all unit tests but it is currently unclear
452 // how some code would consume this.
453 void ClipboardAndroid::WriteBitmap(const SkBitmap
& bitmap
) {
454 gfx::Size
size(bitmap
.width(), bitmap
.height());
456 std::string
packed(reinterpret_cast<const char*>(&size
), sizeof(size
));
458 SkAutoLockPixels
bitmap_lock(bitmap
);
459 packed
+= std::string(static_cast<const char*>(bitmap
.getPixels()),
462 g_map
.Get().Set(kBitmapFormat
, packed
);
465 void ClipboardAndroid::WriteData(const Clipboard::FormatType
& format
,
466 const char* data_data
,
468 g_map
.Get().Set(format
.ToString(), std::string(data_data
, data_len
));
471 bool RegisterClipboardAndroid(JNIEnv
* env
) {
472 return RegisterNativesImpl(env
);