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/synchronization/lock.h"
11 #include "base/utf_string_conversions.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::ScopedJavaGlobalRef
;
32 using base::android::ScopedJavaLocalRef
;
37 // Various formats we support.
38 const char kPlainTextFormat
[] = "text";
39 const char kHTMLFormat
[] = "html";
40 const char kRTFFormat
[] = "rtf";
41 const char kBitmapFormat
[] = "bitmap";
42 const char kWebKitSmartPasteFormat
[] = "webkit_smart";
43 const char kBookmarkFormat
[] = "bookmark";
44 const char kMimeTypePepperCustomData
[] = "chromium/x-pepper-custom-data";
45 const char kMimeTypeWebCustomData
[] = "chromium/x-web-custom-data";
46 const char kSourceTagFormat
[] = "source_tag";
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(
101 env
, env
->NewStringUTF(data
.c_str()));
102 DCHECK(str
.obj() && !ClearException(env
));
103 Java_Clipboard_setText(env
, clipboard_manager_
.obj(), str
.obj());
107 void ClipboardMap::Clear() {
108 JNIEnv
* env
= AttachCurrentThread();
109 base::AutoLock
lock(lock_
);
111 Java_Clipboard_setText(env
, clipboard_manager_
.obj(), NULL
);
114 // If the internal map contains a plain-text entry and it does not match that
115 // in the Android clipboard, clear the map and insert the Android text into it.
116 void ClipboardMap::SyncWithAndroidClipboard() {
117 lock_
.AssertAcquired();
118 JNIEnv
* env
= AttachCurrentThread();
120 std::map
<std::string
, std::string
>::const_iterator it
=
121 map_
.find(kPlainTextFormat
);
123 if (!Java_Clipboard_hasPlainText(env
, clipboard_manager_
.obj())) {
124 if (it
!= map_
.end())
125 // We have plain text on this side, but Android doesn't. Nuke ours.
130 ScopedJavaLocalRef
<jstring
> java_string
=
131 Java_Clipboard_getCoercedText(env
, clipboard_manager_
.obj());
133 if (!java_string
.obj()) {
134 // Tolerate a null value from the Java side, even though that should not
135 // happen since hasPlainText has already returned true.
136 // Should only happen if someone is using the clipboard on multiple
137 // threads and clears it out after hasPlainText but before we get here...
138 if (it
!= map_
.end())
139 // We have plain text on this side, but Android doesn't. Nuke ours.
144 // If Android text differs from ours (or we have none), then copy Android's.
145 std::string android_string
= ConvertJavaStringToUTF8(java_string
);
146 if (it
== map_
.end() || it
->second
!= android_string
) {
148 map_
[kPlainTextFormat
] = android_string
;
154 Clipboard::FormatType::FormatType() {
157 Clipboard::FormatType::FormatType(const std::string
& native_format
)
158 : data_(native_format
) {
161 Clipboard::FormatType::~FormatType() {
164 std::string
Clipboard::FormatType::Serialize() const {
169 Clipboard::FormatType
Clipboard::FormatType::Deserialize(
170 const std::string
& serialization
) {
171 return FormatType(serialization
);
174 bool Clipboard::FormatType::Equals(const FormatType
& other
) const {
175 return data_
== other
.data_
;
178 Clipboard::Clipboard() {
179 DCHECK(CalledOnValidThread());
182 Clipboard::~Clipboard() {
183 DCHECK(CalledOnValidThread());
186 // Main entry point used to write several values in the clipboard.
187 void Clipboard::WriteObjectsImpl(Buffer buffer
,
188 const ObjectMap
& objects
,
190 DCHECK(CalledOnValidThread());
191 DCHECK_EQ(buffer
, BUFFER_STANDARD
);
193 for (ObjectMap::const_iterator iter
= objects
.begin();
194 iter
!= objects
.end(); ++iter
) {
195 DispatchObject(static_cast<ObjectType
>(iter
->first
), iter
->second
);
200 uint64
Clipboard::GetSequenceNumber(Clipboard::Buffer
/* buffer */) {
201 DCHECK(CalledOnValidThread());
202 // TODO: implement this. For now this interface will advertise
203 // that the clipboard never changes. That's fine as long as we
204 // don't rely on this signal.
208 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType
& format
,
209 Clipboard::Buffer buffer
) const {
210 DCHECK(CalledOnValidThread());
211 DCHECK_EQ(buffer
, BUFFER_STANDARD
);
212 return g_map
.Get().HasFormat(format
.data());
215 void Clipboard::Clear(Buffer buffer
) {
216 DCHECK(CalledOnValidThread());
217 DCHECK_EQ(buffer
, BUFFER_STANDARD
);
221 void Clipboard::ReadAvailableTypes(Buffer buffer
, std::vector
<string16
>* types
,
222 bool* contains_filenames
) const {
223 DCHECK(CalledOnValidThread());
224 DCHECK_EQ(buffer
, BUFFER_STANDARD
);
226 if (!types
|| !contains_filenames
) {
234 *contains_filenames
= false;
237 void Clipboard::ReadText(Clipboard::Buffer buffer
, string16
* result
) const {
238 DCHECK(CalledOnValidThread());
239 DCHECK_EQ(buffer
, BUFFER_STANDARD
);
241 ReadAsciiText(buffer
, &utf8
);
242 *result
= UTF8ToUTF16(utf8
);
245 void Clipboard::ReadAsciiText(Clipboard::Buffer buffer
,
246 std::string
* result
) const {
247 DCHECK(CalledOnValidThread());
248 DCHECK_EQ(buffer
, BUFFER_STANDARD
);
249 *result
= g_map
.Get().Get(kPlainTextFormat
);
252 // Note: |src_url| isn't really used. It is only implemented in Windows
253 void Clipboard::ReadHTML(Clipboard::Buffer buffer
,
255 std::string
* src_url
,
256 uint32
* fragment_start
,
257 uint32
* fragment_end
) const {
258 DCHECK(CalledOnValidThread());
259 DCHECK_EQ(buffer
, BUFFER_STANDARD
);
263 std::string input
= g_map
.Get().Get(kHTMLFormat
);
264 *markup
= UTF8ToUTF16(input
);
267 *fragment_end
= static_cast<uint32
>(markup
->length());
270 void Clipboard::ReadRTF(Buffer buffer
, std::string
* result
) const {
271 DCHECK(CalledOnValidThread());
275 SkBitmap
Clipboard::ReadImage(Buffer buffer
) const {
276 DCHECK(CalledOnValidThread());
277 DCHECK_EQ(buffer
, BUFFER_STANDARD
);
278 std::string input
= g_map
.Get().Get(kBitmapFormat
);
281 if (!input
.empty()) {
282 DCHECK_LE(sizeof(gfx::Size
), input
.size());
283 const gfx::Size
* size
= reinterpret_cast<const gfx::Size
*>(input
.data());
286 SkBitmap::kARGB_8888_Config
, size
->width(), size
->height(), 0);
289 int bm_size
= size
->width() * size
->height() * 4;
290 DCHECK_EQ(sizeof(gfx::Size
) + bm_size
, input
.size());
292 memcpy(bmp
.getPixels(), input
.data() + sizeof(gfx::Size
), bm_size
);
297 void Clipboard::ReadCustomData(Buffer buffer
,
298 const string16
& type
,
299 string16
* result
) const {
300 DCHECK(CalledOnValidThread());
304 void Clipboard::ReadBookmark(string16
* title
, std::string
* url
) const {
305 DCHECK(CalledOnValidThread());
309 void Clipboard::ReadData(const Clipboard::FormatType
& format
,
310 std::string
* result
) const {
311 DCHECK(CalledOnValidThread());
312 *result
= g_map
.Get().Get(format
.data());
315 SourceTag
Clipboard::ReadSourceTag(Buffer buffer
) const {
316 DCHECK(CalledOnValidThread());
317 DCHECK_EQ(buffer
, BUFFER_STANDARD
);
319 ReadData(GetSourceTagFormatType(), &result
);
320 return Binary2SourceTag(result
);
324 Clipboard::FormatType
Clipboard::GetFormatType(
325 const std::string
& format_string
) {
326 return FormatType::Deserialize(format_string
);
330 const Clipboard::FormatType
& Clipboard::GetPlainTextFormatType() {
331 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kPlainTextFormat
));
336 const Clipboard::FormatType
& Clipboard::GetPlainTextWFormatType() {
337 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kPlainTextFormat
));
342 const Clipboard::FormatType
& Clipboard::GetWebKitSmartPasteFormatType() {
343 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kWebKitSmartPasteFormat
));
348 const Clipboard::FormatType
& Clipboard::GetHtmlFormatType() {
349 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kHTMLFormat
));
354 const Clipboard::FormatType
& Clipboard::GetRtfFormatType() {
355 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kRTFFormat
));
360 const Clipboard::FormatType
& Clipboard::GetBitmapFormatType() {
361 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kBitmapFormat
));
366 const Clipboard::FormatType
& Clipboard::GetWebCustomDataFormatType() {
367 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kMimeTypeWebCustomData
));
372 const Clipboard::FormatType
& Clipboard::GetPepperCustomDataFormatType() {
373 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kMimeTypePepperCustomData
));
378 const Clipboard::FormatType
& Clipboard::GetSourceTagFormatType() {
379 CR_DEFINE_STATIC_LOCAL(FormatType
, type
, (kSourceTagFormat
));
383 void Clipboard::WriteText(const char* text_data
, size_t text_len
) {
384 g_map
.Get().Set(kPlainTextFormat
, std::string(text_data
, text_len
));
387 void Clipboard::WriteHTML(const char* markup_data
,
389 const char* url_data
,
391 g_map
.Get().Set(kHTMLFormat
, std::string(markup_data
, markup_len
));
394 void Clipboard::WriteRTF(const char* rtf_data
, size_t data_len
) {
398 // Note: according to other platforms implementations, this really writes the
400 void Clipboard::WriteBookmark(const char* title_data
, size_t title_len
,
401 const char* url_data
, size_t url_len
) {
402 g_map
.Get().Set(kBookmarkFormat
, std::string(url_data
, url_len
));
405 // Write an extra flavor that signifies WebKit was the last to modify the
406 // pasteboard. This flavor has no data.
407 void Clipboard::WriteWebSmartPaste() {
408 g_map
.Get().Set(kWebKitSmartPasteFormat
, std::string());
411 // All platforms use gfx::Size for size data but it is passed as a const char*
412 // Further, pixel_data is expected to be 32 bits per pixel
413 // Note: we implement this to pass all unit tests but it is currently unclear
414 // how some code would consume this.
415 void Clipboard::WriteBitmap(const char* pixel_data
, const char* size_data
) {
416 const gfx::Size
* size
= reinterpret_cast<const gfx::Size
*>(size_data
);
417 int bm_size
= size
->width() * size
->height() * 4;
419 std::string
packed(size_data
, sizeof(gfx::Size
));
420 packed
+= std::string(pixel_data
, bm_size
);
421 g_map
.Get().Set(kBitmapFormat
, packed
);
424 void Clipboard::WriteData(const Clipboard::FormatType
& format
,
425 const char* data_data
, size_t data_len
) {
426 g_map
.Get().Set(format
.data(), std::string(data_data
, data_len
));
429 void Clipboard::WriteSourceTag(SourceTag tag
) {
430 if (tag
!= SourceTag()) {
431 ObjectMapParam binary
= SourceTag2Binary(tag
);
432 WriteData(GetSourceTagFormatType(), &binary
[0], binary
.size());
436 // See clipboard_android_initialization.h for more information.
437 bool RegisterClipboardAndroid(JNIEnv
* env
) {
438 return RegisterNativesImpl(env
);