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.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/base/clipboard/clipboard_android_initialization.h"
15 #include "ui/gfx/size.h"
17 // TODO:(andrewhayden) Support additional formats in Android: Bitmap, URI, HTML,
18 // HTML+text now that Android's clipboard system supports them, then nuke the
19 // legacy implementation note below.
21 // Legacy implementation note:
22 // The Android clipboard system used to only support text format. So we used the
23 // Android system when some text was added or retrieved from the system. For
24 // anything else, we STILL store the value in some process wide static
25 // variable protected by a lock. So the (non-text) clipboard will only work
26 // within the same process.
28 using base::android::AttachCurrentThread
;
29 using base::android::ClearException
;
30 using base::android::ConvertJavaStringToUTF8
;
31 using base::android::ConvertUTF8ToJavaString
;
32 using base::android::ScopedJavaGlobalRef
;
33 using base::android::ScopedJavaLocalRef
;
38 // Various formats we support.
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::FormatType() {
184 Clipboard::FormatType::FormatType(const std::string
& native_format
)
185 : data_(native_format
) {
188 Clipboard::FormatType::~FormatType() {
191 std::string
Clipboard::FormatType::Serialize() const {
196 Clipboard::FormatType
Clipboard::FormatType::Deserialize(
197 const std::string
& serialization
) {
198 return FormatType(serialization
);
201 bool Clipboard::FormatType::Equals(const FormatType
& other
) const {
202 return data_
== other
.data_
;
205 Clipboard::Clipboard() {
206 DCHECK(CalledOnValidThread());
209 Clipboard::~Clipboard() {
210 DCHECK(CalledOnValidThread());
213 // Main entry point used to write several values in the clipboard.
214 void Clipboard::WriteObjects(ClipboardType type
, const ObjectMap
& objects
) {
215 DCHECK(CalledOnValidThread());
216 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
218 for (ObjectMap::const_iterator iter
= objects
.begin();
219 iter
!= objects
.end(); ++iter
) {
220 DispatchObject(static_cast<ObjectType
>(iter
->first
), iter
->second
);
224 uint64
Clipboard::GetSequenceNumber(ClipboardType
/* type */) {
225 DCHECK(CalledOnValidThread());
226 // TODO: implement this. For now this interface will advertise
227 // that the clipboard never changes. That's fine as long as we
228 // don't rely on this signal.
232 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType
& format
,
233 ClipboardType type
) const {
234 DCHECK(CalledOnValidThread());
235 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
236 return g_map
.Get().HasFormat(format
.data());
239 void Clipboard::Clear(ClipboardType type
) {
240 DCHECK(CalledOnValidThread());
241 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
245 void Clipboard::ReadAvailableTypes(ClipboardType type
,
246 std::vector
<base::string16
>* types
,
247 bool* contains_filenames
) const {
248 DCHECK(CalledOnValidThread());
249 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
251 if (!types
|| !contains_filenames
) {
259 *contains_filenames
= false;
262 void Clipboard::ReadText(ClipboardType type
, base::string16
* result
) const {
263 DCHECK(CalledOnValidThread());
264 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
266 ReadAsciiText(type
, &utf8
);
267 *result
= base::UTF8ToUTF16(utf8
);
270 void Clipboard::ReadAsciiText(ClipboardType type
, std::string
* result
) const {
271 DCHECK(CalledOnValidThread());
272 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
273 *result
= g_map
.Get().Get(kPlainTextFormat
);
276 // Note: |src_url| isn't really used. It is only implemented in Windows
277 void Clipboard::ReadHTML(ClipboardType type
,
278 base::string16
* markup
,
279 std::string
* src_url
,
280 uint32
* fragment_start
,
281 uint32
* fragment_end
) const {
282 DCHECK(CalledOnValidThread());
283 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
287 std::string input
= g_map
.Get().Get(kHTMLFormat
);
288 *markup
= base::UTF8ToUTF16(input
);
291 *fragment_end
= static_cast<uint32
>(markup
->length());
294 void Clipboard::ReadRTF(ClipboardType type
, std::string
* result
) const {
295 DCHECK(CalledOnValidThread());
299 SkBitmap
Clipboard::ReadImage(ClipboardType type
) const {
300 DCHECK(CalledOnValidThread());
301 DCHECK_EQ(type
, CLIPBOARD_TYPE_COPY_PASTE
);
302 std::string input
= g_map
.Get().Get(kBitmapFormat
);
305 if (!input
.empty()) {
306 DCHECK_LE(sizeof(gfx::Size
), input
.size());
307 const gfx::Size
* size
= reinterpret_cast<const gfx::Size
*>(input
.data());
309 bmp
.allocN32Pixels(size
->width(), size
->height());
311 DCHECK_EQ(sizeof(gfx::Size
) + bmp
.getSize(), input
.size());
313 memcpy(bmp
.getPixels(), input
.data() + sizeof(gfx::Size
), bmp
.getSize());
318 void Clipboard::ReadCustomData(ClipboardType clipboard_type
,
319 const base::string16
& type
,
320 base::string16
* result
) const {
321 DCHECK(CalledOnValidThread());
325 void Clipboard::ReadBookmark(base::string16
* title
, std::string
* url
) const {
326 DCHECK(CalledOnValidThread());
330 void Clipboard::ReadData(const Clipboard::FormatType
& format
,
331 std::string
* result
) const {
332 DCHECK(CalledOnValidThread());
333 *result
= g_map
.Get().Get(format
.data());
337 Clipboard::FormatType
Clipboard::GetFormatType(
338 const std::string
& format_string
) {
339 return FormatType::Deserialize(format_string
);
343 const Clipboard::FormatType
& Clipboard::GetPlainTextFormatType() {
344 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kPlainTextFormat
));
349 const Clipboard::FormatType
& Clipboard::GetPlainTextWFormatType() {
350 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kPlainTextFormat
));
355 const Clipboard::FormatType
& Clipboard::GetWebKitSmartPasteFormatType() {
356 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kWebKitSmartPasteFormat
));
361 const Clipboard::FormatType
& Clipboard::GetHtmlFormatType() {
362 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kHTMLFormat
));
367 const Clipboard::FormatType
& Clipboard::GetRtfFormatType() {
368 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kRTFFormat
));
373 const Clipboard::FormatType
& Clipboard::GetBitmapFormatType() {
374 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kBitmapFormat
));
379 const Clipboard::FormatType
& Clipboard::GetWebCustomDataFormatType() {
380 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kMimeTypeWebCustomData
));
385 const Clipboard::FormatType
& Clipboard::GetPepperCustomDataFormatType() {
386 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kMimeTypePepperCustomData
));
390 void Clipboard::WriteText(const char* text_data
, size_t text_len
) {
391 g_map
.Get().Set(kPlainTextFormat
, std::string(text_data
, text_len
));
394 void Clipboard::WriteHTML(const char* markup_data
,
396 const char* url_data
,
398 g_map
.Get().Set(kHTMLFormat
, std::string(markup_data
, markup_len
));
401 void Clipboard::WriteRTF(const char* rtf_data
, size_t data_len
) {
405 // Note: according to other platforms implementations, this really writes the
407 void Clipboard::WriteBookmark(const char* title_data
, size_t title_len
,
408 const char* url_data
, size_t url_len
) {
409 g_map
.Get().Set(kBookmarkFormat
, std::string(url_data
, url_len
));
412 // Write an extra flavor that signifies WebKit was the last to modify the
413 // pasteboard. This flavor has no data.
414 void Clipboard::WriteWebSmartPaste() {
415 g_map
.Get().Set(kWebKitSmartPasteFormat
, std::string());
418 // Note: we implement this to pass all unit tests but it is currently unclear
419 // how some code would consume this.
420 void Clipboard::WriteBitmap(const SkBitmap
& bitmap
) {
421 gfx::Size
size(bitmap
.width(), bitmap
.height());
423 std::string
packed(reinterpret_cast<const char*>(&size
), sizeof(size
));
425 SkAutoLockPixels
bitmap_lock(bitmap
);
426 packed
+= std::string(static_cast<const char*>(bitmap
.getPixels()),
429 g_map
.Get().Set(kBitmapFormat
, packed
);
432 void Clipboard::WriteData(const Clipboard::FormatType
& format
,
433 const char* data_data
, size_t data_len
) {
434 g_map
.Get().Set(format
.data(), std::string(data_data
, data_len
));
437 // See clipboard_android_initialization.h for more information.
438 bool RegisterClipboardAndroid(JNIEnv
* env
) {
439 return RegisterNativesImpl(env
);