Chromium Code Reviews| Index: ui/base/clipboard/clipboard_android.cc |
| diff --git a/ui/base/clipboard/clipboard_android.cc b/ui/base/clipboard/clipboard_android.cc |
| index 1a7d6ba13f64ae89df1dfc1c8b42a02bf6e725ba..51d809a3caa8ce1bebb7e61419c5f6c696c29ca9 100644 |
| --- a/ui/base/clipboard/clipboard_android.cc |
| +++ b/ui/base/clipboard/clipboard_android.cc |
| @@ -4,14 +4,34 @@ |
| #include "ui/base/clipboard/clipboard.h" |
| +#include "base/android/jni_string.h" |
| +#include "base/lazy_instance.h" |
| +#include "base/stl_util.h" |
| +#include "base/synchronization/lock.h" |
| +#include "base/utf_string_conversions.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/gfx/size.h" |
| +// Important note: |
| +// The Android clipboard system only supports text format. So we use the |
| +// Android system when some text is added or retrieved from the system. For |
| +// anything else, we currently store the value in some process wide static |
| +// variable protected by a lock. So the (non-text) clipboard will only work |
| +// within the same process. |
| + |
| +using base::android::AttachCurrentThread; |
| +using base::android::CheckException; |
| +using base::android::ClearException; |
| +using base::android::ConvertJavaStringToUTF16; |
| +using base::android::ConvertJavaStringToUTF8; |
| +using base::android::GetClass; |
| +using base::android::GetMethodID; |
| +using base::android::ScopedJavaLocalRef; |
| + |
| namespace ui { |
| namespace { |
| - |
| -// Various format we support. |
| +// Various formats we support. |
| const char kPlainTextFormat[] = "text"; |
| const char kHTMLFormat[] = "html"; |
| const char kRTFFormat[] = "rtf"; |
| @@ -20,6 +40,129 @@ const char kWebKitSmartPasteFormat[] = "webkit_smart"; |
| const char kBookmarkFormat[] = "bookmark"; |
| const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; |
| +class ClipboardMap { |
| + public: |
| + ClipboardMap(); |
| + std::string Get(const std::string& format); |
| + bool HasFormat(const std::string& format); |
| + void Set(const std::string& format, const std::string& data); |
| + void Clear(); |
| + |
| + private: |
| + void SyncWithAndroidClipboard(); |
| + std::map<std::string, std::string> map_; |
| + base::Lock lock_; |
| + |
| + // Java class and methods for the Android ClipboardManager. |
| + base::android::ScopedJavaGlobalRef<jobject> clipboard_manager_; |
| + jmethodID set_text_; |
| + jmethodID get_text_; |
| + jmethodID has_text_; |
| + jmethodID to_string_; |
| +}; |
| +base::LazyInstance<ClipboardMap>::Leaky g_map = LAZY_INSTANCE_INITIALIZER; |
| + |
| +ClipboardMap::ClipboardMap() { |
| + JNIEnv* env = AttachCurrentThread(); |
| + DCHECK(env); |
| + |
| + // Get the context. |
| + jobject context = base::android::GetApplicationContext(); |
| + DCHECK(context); |
| + |
| + // Get the context class. |
| + ScopedJavaLocalRef<jclass> context_class = |
| + GetClass(env, "android/content/Context"); |
| + |
| + // Get the system service method. |
| + jmethodID get_system_service = GetMethodID(env, context_class, |
| + "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); |
| + |
| + // Retrieve the system service. |
| + ScopedJavaLocalRef<jstring> service_name(env, env->NewStringUTF("clipboard")); |
| + clipboard_manager_.Reset(env, env->CallObjectMethod(context, |
| + get_system_service, service_name.obj())); |
| + DCHECK(clipboard_manager_.obj() && !ClearException(env)); |
| + |
| + // Retain a few methods we'll keep using. |
| + ScopedJavaLocalRef<jclass> clipboard_class = |
| + GetClass(env, "android/text/ClipboardManager"); |
| + set_text_ = GetMethodID(env, clipboard_class, |
| + "setText", "(Ljava/lang/CharSequence;)V"); |
| + get_text_ = GetMethodID(env, clipboard_class, |
| + "getText", "()Ljava/lang/CharSequence;"); |
| + has_text_ = GetMethodID(env, clipboard_class, |
| + "hasText", "()Z"); |
| + |
| + // Will need to call toString as CharSequence is not always a String. |
| + ScopedJavaLocalRef<jclass> charsequence_class = |
| + GetClass(env, "java/lang/CharSequence"); |
| + to_string_ = GetMethodID(env, charsequence_class, |
| + "toString", "()Ljava/lang/String;"); |
| +} |
| + |
| +std::string ClipboardMap::Get(const std::string& format) { |
| + base::AutoLock lock(lock_); |
| + SyncWithAndroidClipboard(); |
| + std::map<std::string, std::string>::const_iterator it = map_.find(format); |
| + return it == map_.end() ? std::string() : it->second; |
| +} |
| + |
| +bool ClipboardMap::HasFormat(const std::string& format) { |
| + base::AutoLock lock(lock_); |
| + SyncWithAndroidClipboard(); |
| + return ContainsKey(map_, format); |
| +} |
| + |
| +void ClipboardMap::Set(const std::string& format, const std::string& data) { |
| + JNIEnv* env = AttachCurrentThread(); |
| + base::AutoLock lock(lock_); |
| + SyncWithAndroidClipboard(); |
| + |
| + map_[format] = data; |
| + if (format == kPlainTextFormat) { |
| + ScopedJavaLocalRef<jstring> str( |
| + env, env->NewStringUTF(data.c_str())); |
| + DCHECK(str.obj() && !ClearException(env)); |
| + env->CallVoidMethod(clipboard_manager_.obj(), set_text_, str.obj()); |
| + } |
| +} |
| + |
| +void ClipboardMap::Clear() { |
| + JNIEnv* env = AttachCurrentThread(); |
| + base::AutoLock lock(lock_); |
| + map_.clear(); |
| + env->CallVoidMethod(clipboard_manager_.obj(), set_text_, NULL); |
| +} |
| + |
| +// If the text in the internal map does not match that in the Android clipboard, |
| +// clear the map and insert the Android text into it. |
| +void ClipboardMap::SyncWithAndroidClipboard() { |
| + lock_.AssertAcquired(); |
| + JNIEnv* env = AttachCurrentThread(); |
| + |
| + std::map<std::string, std::string>::const_iterator it = |
| + map_.find(kPlainTextFormat); |
| + |
| + if (!env->CallBooleanMethod(clipboard_manager_.obj(), has_text_)) { |
| + if (it != map_.end()) |
| + map_.clear(); |
| + return; |
| + } |
| + |
| + ScopedJavaLocalRef<jobject> char_seq_text( |
| + env, env->CallObjectMethod(clipboard_manager_.obj(), get_text_)); |
| + ScopedJavaLocalRef<jstring> tmp_string(env, |
| + static_cast<jstring>(env->CallObjectMethod(char_seq_text.obj(), |
| + to_string_))); |
| + std::string android_string = ConvertJavaStringToUTF8(tmp_string); |
| + |
| + if (it == map_.end() || it->second != android_string) { |
| + map_.clear(); |
| + map_[kPlainTextFormat] = android_string; |
| + } |
| +} |
| + |
| } // namespace |
| Clipboard::FormatType::FormatType() { |
| @@ -46,63 +189,123 @@ bool Clipboard::FormatType::Equals(const FormatType& other) const { |
| return data_ == other.data_; |
| } |
| -Clipboard::Clipboard() : set_text_(NULL), has_text_(NULL), get_text_(NULL) { |
| +Clipboard::Clipboard() { |
| } |
| Clipboard::~Clipboard() { |
| } |
| +// Main entry point used to write several values in the clipboard. |
| void Clipboard::WriteObjects(Buffer buffer, const ObjectMap& objects) { |
| + DCHECK_EQ(buffer, BUFFER_STANDARD); |
| + g_map.Get().Clear(); |
| + for (ObjectMap::const_iterator iter = objects.begin(); |
| + iter != objects.end(); ++iter) { |
| + DispatchObject(static_cast<ObjectType>(iter->first), iter->second); |
| + } |
| } |
| uint64 Clipboard::GetSequenceNumber(Clipboard::Buffer /* buffer */) { |
| + // TODO: implement this. For now this interface will advertise |
| + // that the clipboard never changes. That's fine as long as we |
| + // don't rely on this signal. |
| return 0; |
| } |
| bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, |
| Clipboard::Buffer buffer) const { |
| - return false; |
| + DCHECK_EQ(buffer, BUFFER_STANDARD); |
| + return g_map.Get().HasFormat(format.data()); |
| } |
| void Clipboard::Clear(Buffer buffer) { |
| - |
| + DCHECK_EQ(buffer, BUFFER_STANDARD); |
| + g_map.Get().Clear(); |
| } |
| void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, |
| bool* contains_filenames) const { |
| + DCHECK_EQ(buffer, BUFFER_STANDARD); |
| + |
| + if (!types || !contains_filenames) { |
| + NOTREACHED(); |
| + return; |
| + } |
| + |
| + NOTIMPLEMENTED(); |
| + |
| + types->clear(); |
| + *contains_filenames = false; |
| } |
| void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const { |
| + DCHECK_EQ(buffer, BUFFER_STANDARD); |
| + std::string utf8; |
| + ReadAsciiText(buffer, &utf8); |
| + *result = UTF8ToUTF16(utf8); |
| } |
| void Clipboard::ReadAsciiText(Clipboard::Buffer buffer, |
| std::string* result) const { |
| + DCHECK_EQ(buffer, BUFFER_STANDARD); |
| + *result = g_map.Get().Get(kPlainTextFormat); |
| } |
| +// Note: |src_url| isn't really used. It is only implemented in Windows |
| void Clipboard::ReadHTML(Clipboard::Buffer buffer, |
| string16* markup, |
| std::string* src_url, |
| uint32* fragment_start, |
| uint32* fragment_end) const { |
| + DCHECK_EQ(buffer, BUFFER_STANDARD); |
| + if (src_url) |
| + src_url->clear(); |
| + |
| + std::string input = g_map.Get().Get(kHTMLFormat); |
| + *markup = UTF8ToUTF16(input); |
| + |
| + *fragment_start = 0; |
| + *fragment_end = static_cast<uint32>(markup->length()); |
| } |
| void Clipboard::ReadRTF(Buffer buffer, std::string* result) const { |
| + NOTIMPLEMENTED(); |
| } |
| SkBitmap Clipboard::ReadImage(Buffer buffer) const { |
| - return SkBitmap(); |
| + DCHECK_EQ(buffer, BUFFER_STANDARD); |
| + std::string input = g_map.Get().Get(kBitmapFormat); |
| + |
| + SkBitmap bmp; |
| + if (!input.empty()) { |
| + DCHECK_LE(sizeof(gfx::Size), input.size()); |
| + const gfx::Size* size = reinterpret_cast<const gfx::Size*>(input.data()); |
| + |
| + bmp.setConfig( |
| + SkBitmap::kARGB_8888_Config, size->width(), size->height(), 0); |
| + bmp.allocPixels(); |
| + |
| + int bm_size = size->width() * size->height() * 4; |
| + DCHECK_EQ(sizeof(gfx::Size) + bm_size, input.size()); |
| + |
| + memcpy(bmp.getPixels(), input.data() + sizeof(gfx::Size), bm_size); |
| + } |
| + return bmp; |
| } |
| void Clipboard::ReadCustomData(Buffer buffer, |
| const string16& type, |
| string16* result) const { |
| + NOTIMPLEMENTED(); |
| } |
| void Clipboard::ReadBookmark(string16* title, std::string* url) const { |
| + NOTIMPLEMENTED(); |
| } |
| void Clipboard::ReadData(const Clipboard::FormatType& format, |
| std::string* result) const { |
| + *result = g_map.Get().Get(format.data()); |
| } |
| // static |
| @@ -154,45 +357,49 @@ const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { |
| } |
| void Clipboard::WriteText(const char* text_data, size_t text_len) { |
| + g_map.Get().Set(kPlainTextFormat, std::string(text_data, text_len)); |
| } |
| void Clipboard::WriteHTML(const char* markup_data, |
| size_t markup_len, |
| const char* url_data, |
| size_t url_len) { |
| + g_map.Get().Set(kHTMLFormat, std::string(markup_data, markup_len)); |
| } |
| void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { |
| + NOTIMPLEMENTED(); |
| } |
| +// Note: according to other platforms implementations, this really writes the |
| +// URL spec. |
| void Clipboard::WriteBookmark(const char* title_data, size_t title_len, |
| const char* url_data, size_t url_len) { |
| + g_map.Get().Set(kBookmarkFormat, std::string(url_data, url_len)); |
| } |
| +// Write an extra flavor that signifies WebKit was the last to modify the |
| +// pasteboard. This flavor has no data. |
| void Clipboard::WriteWebSmartPaste() { |
| + g_map.Get().Set(kWebKitSmartPasteFormat, ""); |
|
dcheng
2012/07/02 23:40:26
std::string() here as well.
cjhopman
2012/07/02 23:53:28
Done.
|
| } |
| +// All platforms use gfx::Size for size data but it is passed as a const char* |
| +// Further, pixel_data is expected to be 32 bits per pixel |
| +// Note: we implement this to pass all unit tests but it is currently unclear |
| +// how some code would consume this. |
| void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { |
| + const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data); |
| + int bm_size = size->width() * size->height() * 4; |
| + |
| + std::string packed(size_data, sizeof(gfx::Size)); |
| + packed += std::string(pixel_data, bm_size); |
| + g_map.Get().Set(kBitmapFormat, packed); |
| } |
| void Clipboard::WriteData(const Clipboard::FormatType& format, |
| const char* data_data, size_t data_len) { |
| + g_map.Get().Set(format.data(), std::string(data_data, data_len)); |
| } |
| -bool Clipboard::IsTextAvailableFromAndroid() const { |
| - return false; |
| -} |
| - |
| -void Clipboard::ValidateInternalClipboard() const { |
| -} |
| - |
| -void Clipboard::Clear() { |
| -} |
| - |
| -void Clipboard::ClearInternalClipboard() const { |
| -} |
| - |
| -void Clipboard::Set(const std::string& key, const std::string& value) { |
| -} |
| - |
| -} // namespace ui |
| +} // namespace ui |