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
);
54 void CommitToAndroidClipboard();
58 void UpdateFromAndroidClipboard();
59 std::map
<std::string
, std::string
> map_
;
62 // Java class and methods for the Android ClipboardManager.
63 ScopedJavaGlobalRef
<jobject
> clipboard_manager_
;
65 base::LazyInstance
<ClipboardMap
>::Leaky g_map
= LAZY_INSTANCE_INITIALIZER
;
67 ClipboardMap::ClipboardMap() {
68 JNIEnv
* env
= AttachCurrentThread();
72 jobject context
= base::android::GetApplicationContext();
75 clipboard_manager_
.Reset(Java_Clipboard_create(env
, context
));
76 DCHECK(clipboard_manager_
.obj());
79 std::string
ClipboardMap::Get(const std::string
& format
) {
80 base::AutoLock
lock(lock_
);
81 UpdateFromAndroidClipboard();
82 std::map
<std::string
, std::string
>::const_iterator it
= map_
.find(format
);
83 return it
== map_
.end() ? std::string() : it
->second
;
86 bool ClipboardMap::HasFormat(const std::string
& format
) {
87 base::AutoLock
lock(lock_
);
88 UpdateFromAndroidClipboard();
89 return ContainsKey(map_
, format
);
92 void ClipboardMap::Set(const std::string
& format
, const std::string
& data
) {
93 base::AutoLock
lock(lock_
);
97 void ClipboardMap::CommitToAndroidClipboard() {
98 JNIEnv
* env
= AttachCurrentThread();
99 base::AutoLock
lock(lock_
);
100 if (ContainsKey(map_
, kHTMLFormat
)) {
101 // Android's API for storing HTML content on the clipboard requires a plain-
102 // text representation to be available as well.
103 if (!ContainsKey(map_
, kPlainTextFormat
))
106 ScopedJavaLocalRef
<jstring
> html
=
107 ConvertUTF8ToJavaString(env
, map_
[kHTMLFormat
].c_str());
108 ScopedJavaLocalRef
<jstring
> text
=
109 ConvertUTF8ToJavaString(env
, map_
[kPlainTextFormat
].c_str());
111 DCHECK(html
.obj() && text
.obj());
112 Java_Clipboard_setHTMLText(env
, clipboard_manager_
.obj(), html
.obj(),
114 } else if (ContainsKey(map_
, kPlainTextFormat
)) {
115 ScopedJavaLocalRef
<jstring
> str
=
116 ConvertUTF8ToJavaString(env
, map_
[kPlainTextFormat
].c_str());
119 Java_Clipboard_setText(env
, clipboard_manager_
.obj(), str
.obj());
121 Java_Clipboard_setText(env
, clipboard_manager_
.obj(), nullptr);
126 void ClipboardMap::Clear() {
127 JNIEnv
* env
= AttachCurrentThread();
128 base::AutoLock
lock(lock_
);
130 Java_Clipboard_setText(env
, clipboard_manager_
.obj(), NULL
);
133 // Add a key:jstr pair to map, but only if jstr is not null, and also
135 void AddMapEntry(JNIEnv
* env
,
136 std::map
<std::string
, std::string
>* map
,
138 const ScopedJavaLocalRef
<jstring
>& jstr
) {
139 if (!jstr
.is_null()) {
140 std::string str
= ConvertJavaStringToUTF8(env
, jstr
.obj());
146 // Return true if all the key-value pairs in map1 are also in map2.
147 bool MapIsSubset(const std::map
<std::string
, std::string
>& map1
,
148 const std::map
<std::string
, std::string
>& map2
) {
149 for (const auto& val
: map1
) {
150 auto iter
= map2
.find(val
.first
);
151 if (iter
== map2
.end() || iter
->second
!= val
.second
)
157 void ClipboardMap::UpdateFromAndroidClipboard() {
158 // Fetch the current Android clipboard state. Replace our state with
159 // the Android state if the Android state has been changed.
160 lock_
.AssertAcquired();
161 JNIEnv
* env
= AttachCurrentThread();
163 std::map
<std::string
, std::string
> android_clipboard_state
;
165 ScopedJavaLocalRef
<jstring
> jtext
=
166 Java_Clipboard_getCoercedText(env
, clipboard_manager_
.obj());
167 ScopedJavaLocalRef
<jstring
> jhtml
=
168 Java_Clipboard_getHTMLText(env
, clipboard_manager_
.obj());
170 AddMapEntry(env
, &android_clipboard_state
, kPlainTextFormat
, jtext
);
171 AddMapEntry(env
, &android_clipboard_state
, kHTMLFormat
, jhtml
);
173 if (!MapIsSubset(android_clipboard_state
, map_
))
174 android_clipboard_state
.swap(map_
);
179 // Clipboard::FormatType implementation.
180 Clipboard::FormatType::FormatType() {
183 Clipboard::FormatType::FormatType(const std::string
& native_format
)
184 : data_(native_format
) {
187 Clipboard::FormatType::~FormatType() {
190 std::string
Clipboard::FormatType::Serialize() const {
195 Clipboard::FormatType
Clipboard::FormatType::Deserialize(
196 const std::string
& serialization
) {
197 return FormatType(serialization
);
200 bool Clipboard::FormatType::operator<(const FormatType
& other
) const {
201 return data_
< other
.data_
;
204 bool Clipboard::FormatType::Equals(const FormatType
& other
) const {
205 return data_
== other
.data_
;
208 // Various predefined FormatTypes.
210 Clipboard::FormatType
Clipboard::GetFormatType(
211 const std::string
& format_string
) {
212 return FormatType::Deserialize(format_string
);
216 const Clipboard::FormatType
& Clipboard::GetUrlWFormatType() {
217 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kURLFormat
));
222 const Clipboard::FormatType
& Clipboard::GetPlainTextFormatType() {
223 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kPlainTextFormat
));
228 const Clipboard::FormatType
& Clipboard::GetPlainTextWFormatType() {
229 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kPlainTextFormat
));
234 const Clipboard::FormatType
& Clipboard::GetWebKitSmartPasteFormatType() {
235 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kWebKitSmartPasteFormat
));
240 const Clipboard::FormatType
& Clipboard::GetHtmlFormatType() {
241 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kHTMLFormat
));
246 const Clipboard::FormatType
& Clipboard::GetRtfFormatType() {
247 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kRTFFormat
));
252 const Clipboard::FormatType
& Clipboard::GetBitmapFormatType() {
253 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kBitmapFormat
));
258 const Clipboard::FormatType
& Clipboard::GetWebCustomDataFormatType() {
259 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kMimeTypeWebCustomData
));
264 const Clipboard::FormatType
& Clipboard::GetPepperCustomDataFormatType() {
265 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kMimeTypePepperCustomData
));
269 // Clipboard factory method.
271 Clipboard
* Clipboard::Create() {
272 return new ClipboardAndroid
;
275 // ClipboardAndroid implementation.
276 ClipboardAndroid::ClipboardAndroid() {
277 DCHECK(CalledOnValidThread());
280 ClipboardAndroid::~ClipboardAndroid() {
281 DCHECK(CalledOnValidThread());
284 uint64
ClipboardAndroid::GetSequenceNumber(ClipboardType
/* type */) const {
285 DCHECK(CalledOnValidThread());
286 // TODO: implement this. For now this interface will advertise
287 // that the clipboard never changes. That's fine as long as we
288 // don't rely on this signal.
292 bool ClipboardAndroid::IsFormatAvailable(const Clipboard::FormatType
& format
,
293 ClipboardType type
) const {
294 DCHECK(CalledOnValidThread());
295 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
296 return g_map
.Get().HasFormat(format
.ToString());
299 void ClipboardAndroid::Clear(ClipboardType type
) {
300 DCHECK(CalledOnValidThread());
301 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
305 void ClipboardAndroid::ReadAvailableTypes(ClipboardType type
,
306 std::vector
<base::string16
>* types
,
307 bool* contains_filenames
) const {
308 DCHECK(CalledOnValidThread());
309 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
311 if (!types
|| !contains_filenames
) {
318 // would be nice to ask the ClipboardMap to enumerate the types it supports,
319 // rather than hardcode the list here.
320 if (IsFormatAvailable(Clipboard::GetPlainTextFormatType(), type
))
321 types
->push_back(base::UTF8ToUTF16(kMimeTypeText
));
322 if (IsFormatAvailable(Clipboard::GetHtmlFormatType(), type
))
323 types
->push_back(base::UTF8ToUTF16(kMimeTypeHTML
));
325 // these formats aren't supported by the ClipboardMap currently, but might
327 if (IsFormatAvailable(Clipboard::GetRtfFormatType(), type
))
328 types
->push_back(base::UTF8ToUTF16(kMimeTypeRTF
));
329 if (IsFormatAvailable(Clipboard::GetBitmapFormatType(), type
))
330 types
->push_back(base::UTF8ToUTF16(kMimeTypePNG
));
331 *contains_filenames
= false;
334 void ClipboardAndroid::ReadText(ClipboardType type
,
335 base::string16
* result
) const {
336 DCHECK(CalledOnValidThread());
337 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
339 ReadAsciiText(type
, &utf8
);
340 *result
= base::UTF8ToUTF16(utf8
);
343 void ClipboardAndroid::ReadAsciiText(ClipboardType type
,
344 std::string
* result
) const {
345 DCHECK(CalledOnValidThread());
346 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
347 *result
= g_map
.Get().Get(kPlainTextFormat
);
350 // Note: |src_url| isn't really used. It is only implemented in Windows
351 void ClipboardAndroid::ReadHTML(ClipboardType type
,
352 base::string16
* markup
,
353 std::string
* src_url
,
354 uint32
* fragment_start
,
355 uint32
* fragment_end
) const {
356 DCHECK(CalledOnValidThread());
357 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
361 std::string input
= g_map
.Get().Get(kHTMLFormat
);
362 *markup
= base::UTF8ToUTF16(input
);
365 *fragment_end
= static_cast<uint32
>(markup
->length());
368 void ClipboardAndroid::ReadRTF(ClipboardType type
, std::string
* result
) const {
369 DCHECK(CalledOnValidThread());
373 SkBitmap
ClipboardAndroid::ReadImage(ClipboardType type
) const {
374 DCHECK(CalledOnValidThread());
375 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
376 std::string input
= g_map
.Get().Get(kBitmapFormat
);
379 if (!input
.empty()) {
380 DCHECK_LE(sizeof(gfx::Size
), input
.size());
381 const gfx::Size
* size
= reinterpret_cast<const gfx::Size
*>(input
.data());
383 bmp
.allocN32Pixels(size
->width(), size
->height());
385 DCHECK_EQ(sizeof(gfx::Size
) + bmp
.getSize(), input
.size());
387 memcpy(bmp
.getPixels(), input
.data() + sizeof(gfx::Size
), bmp
.getSize());
392 void ClipboardAndroid::ReadCustomData(ClipboardType clipboard_type
,
393 const base::string16
& type
,
394 base::string16
* result
) const {
395 DCHECK(CalledOnValidThread());
399 void ClipboardAndroid::ReadBookmark(base::string16
* title
,
400 std::string
* url
) const {
401 DCHECK(CalledOnValidThread());
405 void ClipboardAndroid::ReadData(const Clipboard::FormatType
& format
,
406 std::string
* result
) const {
407 DCHECK(CalledOnValidThread());
408 *result
= g_map
.Get().Get(format
.ToString());
411 // Main entry point used to write several values in the clipboard.
412 void ClipboardAndroid::WriteObjects(ClipboardType type
,
413 const ObjectMap
& objects
) {
414 DCHECK(CalledOnValidThread());
415 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
418 for (ObjectMap::const_iterator iter
= objects
.begin(); iter
!= objects
.end();
420 DispatchObject(static_cast<ObjectType
>(iter
->first
), iter
->second
);
423 g_map
.Get().CommitToAndroidClipboard();
426 void ClipboardAndroid::WriteText(const char* text_data
, size_t text_len
) {
427 g_map
.Get().Set(kPlainTextFormat
, std::string(text_data
, text_len
));
430 void ClipboardAndroid::WriteHTML(const char* markup_data
,
432 const char* url_data
,
434 g_map
.Get().Set(kHTMLFormat
, std::string(markup_data
, markup_len
));
437 void ClipboardAndroid::WriteRTF(const char* rtf_data
, size_t data_len
) {
441 // Note: according to other platforms implementations, this really writes the
443 void ClipboardAndroid::WriteBookmark(const char* title_data
,
445 const char* url_data
,
447 g_map
.Get().Set(kBookmarkFormat
, std::string(url_data
, url_len
));
450 // Write an extra flavor that signifies WebKit was the last to modify the
451 // pasteboard. This flavor has no data.
452 void ClipboardAndroid::WriteWebSmartPaste() {
453 g_map
.Get().Set(kWebKitSmartPasteFormat
, std::string());
456 // Note: we implement this to pass all unit tests but it is currently unclear
457 // how some code would consume this.
458 void ClipboardAndroid::WriteBitmap(const SkBitmap
& bitmap
) {
459 gfx::Size
size(bitmap
.width(), bitmap
.height());
461 std::string
packed(reinterpret_cast<const char*>(&size
), sizeof(size
));
463 SkAutoLockPixels
bitmap_lock(bitmap
);
464 packed
+= std::string(static_cast<const char*>(bitmap
.getPixels()),
467 g_map
.Get().Set(kBitmapFormat
, packed
);
470 void ClipboardAndroid::WriteData(const Clipboard::FormatType
& format
,
471 const char* data_data
,
473 g_map
.Get().Set(format
.ToString(), std::string(data_data
, data_len
));
476 bool RegisterClipboardAndroid(JNIEnv
* env
) {
477 return RegisterNativesImpl(env
);