Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/base/clipboard/clipboard.h" | 5 #include "ui/base/clipboard/clipboard.h" |
| 6 | 6 |
| 7 #include "base/android/jni_string.h" | |
| 8 #include "base/lazy_instance.h" | |
| 9 #include "base/synchronization/lock.h" | |
| 10 #include "base/utf_string_conversions.h" | |
| 7 #include "third_party/skia/include/core/SkBitmap.h" | 11 #include "third_party/skia/include/core/SkBitmap.h" |
| 8 #include "ui/gfx/size.h" | 12 #include "ui/gfx/size.h" |
| 9 | 13 |
| 14 // Important note: | |
| 15 // The Android clipboard system only supports text format. So we use the | |
| 16 // Android system when some text is added or retrieved from the system. For | |
| 17 // anything else, we currently store the value in some process wide static | |
| 18 // variable protected by a lock. So the (non-text) clipboard will only work | |
| 19 // within the same process. | |
| 20 | |
| 21 using base::android::AttachCurrentThread; | |
| 22 using base::android::CheckException; | |
| 23 using base::android::ClearException; | |
| 24 using base::android::ConvertJavaStringToUTF16; | |
| 25 using base::android::ConvertJavaStringToUTF8; | |
| 26 using base::android::GetClass; | |
| 27 using base::android::GetMethodID; | |
| 28 using base::android::ScopedJavaLocalRef; | |
| 29 | |
| 10 namespace ui { | 30 namespace ui { |
| 11 | 31 |
| 12 namespace { | 32 namespace { |
| 13 | 33 // Various formats we support. |
| 14 // Various format we support. | |
| 15 const char kPlainTextFormat[] = "text"; | 34 const char kPlainTextFormat[] = "text"; |
| 16 const char kHTMLFormat[] = "html"; | 35 const char kHTMLFormat[] = "html"; |
| 17 const char kRTFFormat[] = "rtf"; | 36 const char kRTFFormat[] = "rtf"; |
| 18 const char kBitmapFormat[] = "bitmap"; | 37 const char kBitmapFormat[] = "bitmap"; |
| 19 const char kWebKitSmartPasteFormat[] = "webkit_smart"; | 38 const char kWebKitSmartPasteFormat[] = "webkit_smart"; |
| 20 const char kBookmarkFormat[] = "bookmark"; | 39 const char kBookmarkFormat[] = "bookmark"; |
| 21 const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; | 40 const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; |
| 22 | 41 |
| 42 class ClipboardMap { | |
| 43 public: | |
|
dcheng
2012/06/30 03:28:42
1-space indent for public/private
cjhopman
2012/07/02 17:29:11
Done.
| |
| 44 ClipboardMap(); | |
|
dcheng
2012/06/30 03:28:42
2 spaces, etc.
cjhopman
2012/07/02 17:29:11
Done.
| |
| 45 std::string Get(const std::string& format); | |
| 46 bool HasFormat(const std::string& format); | |
| 47 void Set(const std::string& format, const std::string& data); | |
| 48 void Clear(); | |
| 49 | |
| 50 private: | |
| 51 void ValidateWithLock(); | |
| 52 std::map<std::string, std::string> map_; | |
| 53 base::Lock lock_; | |
| 54 | |
| 55 // Java class and methods for the Android ClipboardManager. | |
| 56 base::android::ScopedJavaGlobalRef<jobject> clipboard_manager_; | |
| 57 jmethodID set_text_; | |
| 58 jmethodID get_text_; | |
| 59 jmethodID to_string_; | |
| 60 }; | |
| 61 base::LazyInstance<ClipboardMap>::Leaky g_map = LAZY_INSTANCE_INITIALIZER; | |
| 62 | |
| 63 ClipboardMap::ClipboardMap() { | |
| 64 JNIEnv* env = AttachCurrentThread(); | |
| 65 DCHECK(env); | |
| 66 | |
| 67 // Get the context. | |
| 68 jobject context = base::android::GetApplicationContext(); | |
| 69 DCHECK(context); | |
| 70 | |
| 71 // Get the context class. | |
| 72 ScopedJavaLocalRef<jclass> context_class = | |
| 73 GetClass(env, "android/content/Context"); | |
| 74 | |
| 75 // Get the system service method. | |
| 76 jmethodID get_system_service = GetMethodID(env, context_class, | |
| 77 "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); | |
| 78 | |
| 79 // Retrieve the system service. | |
| 80 ScopedJavaLocalRef<jstring> service_name(env, env->NewStringUTF("clipboard")); | |
| 81 clipboard_manager_.Reset(env, env->CallObjectMethod(context, | |
| 82 get_system_service, service_name.obj())); | |
| 83 DCHECK(clipboard_manager_.obj() && !ClearException(env)); | |
| 84 | |
| 85 // Retain a few methods we'll keep using. | |
| 86 ScopedJavaLocalRef<jclass> clipboard_class = | |
| 87 GetClass(env, "android/text/ClipboardManager"); | |
| 88 set_text_ = GetMethodID(env, clipboard_class, | |
| 89 "setText", "(Ljava/lang/CharSequence;)V"); | |
| 90 get_text_ = GetMethodID(env, clipboard_class, | |
| 91 "getText", "()Ljava/lang/CharSequence;"); | |
| 92 | |
| 93 // Will need to call toString as CharSequence is not always a String. | |
| 94 ScopedJavaLocalRef<jclass> charsequence_class = | |
| 95 GetClass(env, "java/lang/CharSequence"); | |
| 96 to_string_ = GetMethodID(env, charsequence_class, | |
| 97 "toString", "()Ljava/lang/String;"); | |
| 98 } | |
| 99 | |
| 100 std::string ClipboardMap::Get(const std::string& format) { | |
| 101 base::AutoLock lock(lock_); | |
| 102 ValidateWithLock(); | |
| 103 return map_[format]; | |
| 104 } | |
| 105 | |
| 106 bool ClipboardMap::HasFormat(const std::string& format) { | |
| 107 return !Get(format).empty(); | |
|
dcheng
2012/06/30 03:28:42
Consider using ContainsKey() from base/stl_util.h
cjhopman
2012/07/02 17:29:11
Added a comment to clarify. In addition, in Get()
dcheng
2012/07/02 20:22:30
We should use find(). Get() shouldn't mutate the m
cjhopman
2012/07/02 21:00:49
Currently, the Validate() call can also mutate the
dcheng
2012/07/02 21:09:52
Oh right, I forgot about Validate(). I would prefe
cjhopman
2012/07/02 22:33:25
Done. Required small change to Validate/Sync since
| |
| 108 } | |
| 109 | |
| 110 void ClipboardMap::Set(const std::string& format, const std::string& data) { | |
| 111 JNIEnv* env = AttachCurrentThread(); | |
| 112 base::AutoLock lock(lock_); | |
| 113 ValidateWithLock(); | |
| 114 | |
| 115 map_[format] = data; | |
| 116 if (format == kPlainTextFormat) { | |
| 117 ScopedJavaLocalRef<jstring> str( | |
| 118 env, env->NewStringUTF(data.c_str())); | |
| 119 DCHECK(str.obj() && !ClearException(env)); | |
| 120 env->CallVoidMethod(clipboard_manager_.obj(), set_text_, str.obj()); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 void ClipboardMap::Clear() { | |
| 125 JNIEnv* env = AttachCurrentThread(); | |
| 126 base::AutoLock lock(lock_); | |
| 127 map_.clear(); | |
| 128 env->CallVoidMethod(clipboard_manager_.obj(), set_text_, NULL); | |
| 129 } | |
| 130 | |
| 131 void ClipboardMap::ValidateWithLock() { | |
| 132 lock_.AssertAcquired(); | |
| 133 | |
| 134 JNIEnv* env = AttachCurrentThread(); | |
| 135 ScopedJavaLocalRef<jobject> char_seq_text( | |
| 136 env, env->CallObjectMethod(clipboard_manager_.obj(), get_text_)); | |
| 137 if (char_seq_text.is_null()) { | |
| 138 map_.clear(); | |
| 139 return; | |
| 140 } | |
| 141 | |
| 142 std::string internal_string = map_[kPlainTextFormat]; | |
|
dcheng
2012/06/30 03:28:42
You could use a const ref here to avoid a copy.
cjhopman
2012/07/02 17:29:11
Done.
| |
| 143 ScopedJavaLocalRef<jstring> tmp_string(env, | |
| 144 static_cast<jstring>(env->CallObjectMethod(char_seq_text.obj(), | |
| 145 to_string_))); | |
| 146 std::string android_string = ConvertJavaStringToUTF8(tmp_string); | |
| 147 if (internal_string != android_string) { | |
| 148 map_.clear(); | |
| 149 map_[kPlainTextFormat] = android_string; | |
| 150 } | |
| 151 } | |
| 152 | |
| 23 } // namespace | 153 } // namespace |
| 24 | 154 |
| 25 Clipboard::FormatType::FormatType() { | 155 Clipboard::FormatType::FormatType() { |
| 26 } | 156 } |
| 27 | 157 |
| 28 Clipboard::FormatType::FormatType(const std::string& native_format) | 158 Clipboard::FormatType::FormatType(const std::string& native_format) |
| 29 : data_(native_format) { | 159 : data_(native_format) { |
| 30 } | 160 } |
| 31 | 161 |
| 32 Clipboard::FormatType::~FormatType() { | 162 Clipboard::FormatType::~FormatType() { |
| 33 } | 163 } |
| 34 | 164 |
| 35 std::string Clipboard::FormatType::Serialize() const { | 165 std::string Clipboard::FormatType::Serialize() const { |
| 36 return data_; | 166 return data_; |
| 37 } | 167 } |
| 38 | 168 |
| 39 // static | 169 // static |
| 40 Clipboard::FormatType Clipboard::FormatType::Deserialize( | 170 Clipboard::FormatType Clipboard::FormatType::Deserialize( |
| 41 const std::string& serialization) { | 171 const std::string& serialization) { |
| 42 return FormatType(serialization); | 172 return FormatType(serialization); |
| 43 } | 173 } |
| 44 | 174 |
| 45 bool Clipboard::FormatType::Equals(const FormatType& other) const { | 175 bool Clipboard::FormatType::Equals(const FormatType& other) const { |
| 46 return data_ == other.data_; | 176 return data_ == other.data_; |
| 47 } | 177 } |
| 48 | 178 |
| 49 Clipboard::Clipboard() : set_text_(NULL), has_text_(NULL), get_text_(NULL) { | 179 Clipboard::Clipboard() { |
| 50 } | 180 } |
| 51 | 181 |
| 52 Clipboard::~Clipboard() { | 182 Clipboard::~Clipboard() { |
| 53 } | 183 } |
| 54 | 184 |
| 185 // Main entry point used to write several values in the clipboard. | |
| 55 void Clipboard::WriteObjects(Buffer buffer, const ObjectMap& objects) { | 186 void Clipboard::WriteObjects(Buffer buffer, const ObjectMap& objects) { |
| 187 g_map.Get().Clear(); | |
| 188 for (ObjectMap::const_iterator iter = objects.begin(); | |
| 189 iter != objects.end(); ++iter) { | |
| 190 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); | |
| 191 } | |
| 56 } | 192 } |
| 57 | 193 |
| 58 uint64 Clipboard::GetSequenceNumber(Clipboard::Buffer /* buffer */) { | 194 uint64 Clipboard::GetSequenceNumber(Clipboard::Buffer /* buffer */) { |
| 195 // TODO: implement this. For now this interface will advertise | |
| 196 // that the clipboard never changes. That's fine as long as we | |
| 197 // don't rely on this signal. | |
| 59 return 0; | 198 return 0; |
| 60 } | 199 } |
| 61 | 200 |
| 62 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, | 201 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, |
| 63 Clipboard::Buffer buffer) const { | 202 Clipboard::Buffer buffer) const { |
| 64 return false; | 203 DCHECK_EQ(buffer, BUFFER_STANDARD); |
| 204 return g_map.Get().HasFormat(format.data()); | |
| 65 } | 205 } |
| 66 | 206 |
| 67 void Clipboard::Clear(Buffer buffer) { | 207 void Clipboard::Clear(Buffer buffer) { |
| 68 | 208 DCHECK_EQ(buffer, BUFFER_STANDARD); |
| 209 g_map.Get().Clear(); | |
| 69 } | 210 } |
| 70 | 211 |
| 71 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, | 212 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, |
| 72 bool* contains_filenames) const { | 213 bool* contains_filenames) const { |
| 214 DCHECK_EQ(buffer, BUFFER_STANDARD); | |
| 215 | |
| 216 if (!types || !contains_filenames) { | |
| 217 NOTREACHED(); | |
| 218 return; | |
| 219 } | |
| 220 | |
| 221 NOTIMPLEMENTED(); | |
| 222 | |
| 223 types->clear(); | |
| 224 *contains_filenames = false; | |
| 73 } | 225 } |
| 74 | 226 |
| 75 void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const { | 227 void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const { |
| 228 std::string utf8; | |
| 229 ReadAsciiText(buffer, &utf8); | |
| 230 *result = UTF8ToUTF16(utf8); | |
| 76 } | 231 } |
| 77 | 232 |
| 78 void Clipboard::ReadAsciiText(Clipboard::Buffer buffer, | 233 void Clipboard::ReadAsciiText(Clipboard::Buffer buffer, |
| 79 std::string* result) const { | 234 std::string* result) const { |
| 235 DCHECK_EQ(buffer, BUFFER_STANDARD); | |
| 236 *result = g_map.Get().Get(kPlainTextFormat); | |
| 80 } | 237 } |
| 81 | 238 |
| 239 // Note: |src_url| isn't really used. It is only implemented in Windows | |
| 82 void Clipboard::ReadHTML(Clipboard::Buffer buffer, | 240 void Clipboard::ReadHTML(Clipboard::Buffer buffer, |
| 83 string16* markup, | 241 string16* markup, |
| 84 std::string* src_url, | 242 std::string* src_url, |
| 85 uint32* fragment_start, | 243 uint32* fragment_start, |
| 86 uint32* fragment_end) const { | 244 uint32* fragment_end) const { |
| 245 if (src_url) | |
| 246 src_url->clear(); | |
| 247 *fragment_start = 0; | |
| 248 | |
| 249 std::string input = g_map.Get().Get(kHTMLFormat); | |
| 250 | |
| 251 *fragment_end = static_cast<uint32>(input.length()); | |
|
dcheng
2012/06/30 03:28:42
This should be markup->length() after the conversi
cjhopman
2012/07/02 17:29:11
Done.
| |
| 252 *markup = UTF8ToUTF16(input); | |
| 87 } | 253 } |
| 88 | 254 |
| 89 void Clipboard::ReadRTF(Buffer buffer, std::string* result) const { | 255 void Clipboard::ReadRTF(Buffer buffer, std::string* result) const { |
| 256 NOTIMPLEMENTED(); | |
| 90 } | 257 } |
| 91 | 258 |
| 92 SkBitmap Clipboard::ReadImage(Buffer buffer) const { | 259 SkBitmap Clipboard::ReadImage(Buffer buffer) const { |
| 93 return SkBitmap(); | 260 std::string input = g_map.Get().Get(kBitmapFormat); |
| 261 | |
| 262 SkBitmap bmp; | |
| 263 if (!input.empty()) { | |
| 264 DCHECK_LE(sizeof(gfx::Size), input.size()); | |
| 265 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(input.c_str()); | |
| 266 | |
| 267 bmp.setConfig( | |
| 268 SkBitmap::kARGB_8888_Config, size->width(), size->height(), 0); | |
| 269 bmp.allocPixels(); | |
| 270 | |
| 271 int bm_size = size->width() * size->height() * 4; | |
| 272 DCHECK_EQ(sizeof(gfx::Size) + bm_size, input.size()); | |
| 273 | |
| 274 memcpy(bmp.getPixels(), input.c_str() + sizeof(gfx::Size), bm_size); | |
| 275 } | |
| 276 return bmp; | |
| 94 } | 277 } |
| 95 | 278 |
| 96 void Clipboard::ReadCustomData(Buffer buffer, | 279 void Clipboard::ReadCustomData(Buffer buffer, |
| 97 const string16& type, | 280 const string16& type, |
| 98 string16* result) const { | 281 string16* result) const { |
| 282 NOTIMPLEMENTED(); | |
| 99 } | 283 } |
| 100 | 284 |
| 101 void Clipboard::ReadBookmark(string16* title, std::string* url) const { | 285 void Clipboard::ReadBookmark(string16* title, std::string* url) const { |
| 286 NOTIMPLEMENTED(); | |
| 102 } | 287 } |
| 103 | 288 |
| 104 void Clipboard::ReadData(const Clipboard::FormatType& format, | 289 void Clipboard::ReadData(const Clipboard::FormatType& format, |
| 105 std::string* result) const { | 290 std::string* result) const { |
| 291 *result = g_map.Get().Get(format.data()); | |
| 106 } | 292 } |
| 107 | 293 |
| 108 // static | 294 // static |
| 109 Clipboard::FormatType Clipboard::GetFormatType( | 295 Clipboard::FormatType Clipboard::GetFormatType( |
| 110 const std::string& format_string) { | 296 const std::string& format_string) { |
| 111 return FormatType::Deserialize(format_string); | 297 return FormatType::Deserialize(format_string); |
| 112 } | 298 } |
| 113 | 299 |
| 114 // static | 300 // static |
| 115 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { | 301 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 147 return type; | 333 return type; |
| 148 } | 334 } |
| 149 | 335 |
| 150 // static | 336 // static |
| 151 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { | 337 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { |
| 152 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); | 338 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); |
| 153 return type; | 339 return type; |
| 154 } | 340 } |
| 155 | 341 |
| 156 void Clipboard::WriteText(const char* text_data, size_t text_len) { | 342 void Clipboard::WriteText(const char* text_data, size_t text_len) { |
| 343 g_map.Get().Set(kPlainTextFormat, std::string(text_data, text_len)); | |
| 157 } | 344 } |
| 158 | 345 |
| 159 void Clipboard::WriteHTML(const char* markup_data, | 346 void Clipboard::WriteHTML(const char* markup_data, |
| 160 size_t markup_len, | 347 size_t markup_len, |
| 161 const char* url_data, | 348 const char* url_data, |
| 162 size_t url_len) { | 349 size_t url_len) { |
| 350 static const char* html_prefix = "<meta http-equiv=\"content-type\" " | |
|
dcheng
2012/06/30 03:28:42
Do you actually need this? Since only Android Chro
cjhopman
2012/07/02 17:29:11
Done.
| |
| 351 "content=\"text/html; charset=utf-8\">"; | |
| 352 g_map.Get().Set( | |
| 353 kHTMLFormat, html_prefix + std::string(markup_data, markup_len)); | |
| 163 } | 354 } |
| 164 | 355 |
| 165 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { | 356 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { |
| 357 NOTIMPLEMENTED(); | |
| 166 } | 358 } |
| 167 | 359 |
| 360 // Note: according to other platforms implementations, this really writes the | |
| 361 // URL spec. | |
| 168 void Clipboard::WriteBookmark(const char* title_data, size_t title_len, | 362 void Clipboard::WriteBookmark(const char* title_data, size_t title_len, |
| 169 const char* url_data, size_t url_len) { | 363 const char* url_data, size_t url_len) { |
| 364 g_map.Get().Set(kBookmarkFormat, std::string(url_data, url_len)); | |
| 170 } | 365 } |
| 171 | 366 |
| 367 // Write an extra flavor that signifies WebKit was the last to modify the | |
| 368 // pasteboard. This flavor has no data. | |
| 172 void Clipboard::WriteWebSmartPaste() { | 369 void Clipboard::WriteWebSmartPaste() { |
| 370 g_map.Get().Set(kWebKitSmartPasteFormat, std::string("")); | |
|
dcheng
2012/06/30 03:28:42
std::string() instead of std::string("")
cjhopman
2012/07/02 17:29:11
Done.
| |
| 173 } | 371 } |
| 174 | 372 |
| 373 // All platforms use gfx::Size for size data but it is passed as a const char* | |
| 374 // Further, pixel_data is expected to be 32 bits per pixel | |
| 375 // Note: we implement this to pass all unit tests but it is currently unclear | |
| 376 // how some code would consume this. | |
| 175 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { | 377 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { |
| 378 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data); | |
| 379 int bm_size = size->width() * size->height() * 4; | |
| 380 | |
| 381 std::string packed(size_data, sizeof(gfx::Size)); | |
| 382 packed += std::string(pixel_data, bm_size); | |
| 383 g_map.Get().Set(kBitmapFormat, packed); | |
| 176 } | 384 } |
| 177 | 385 |
| 178 void Clipboard::WriteData(const Clipboard::FormatType& format, | 386 void Clipboard::WriteData(const Clipboard::FormatType& format, |
| 179 const char* data_data, size_t data_len) { | 387 const char* data_data, size_t data_len) { |
| 388 g_map.Get().Set(format.data(), std::string(data_data, data_len)); | |
| 180 } | 389 } |
| 181 | 390 |
| 182 bool Clipboard::IsTextAvailableFromAndroid() const { | 391 } // namespace ui |
| 183 return false; | |
| 184 } | |
| 185 | |
| 186 void Clipboard::ValidateInternalClipboard() const { | |
| 187 } | |
| 188 | |
| 189 void Clipboard::Clear() { | |
| 190 } | |
| 191 | |
| 192 void Clipboard::ClearInternalClipboard() const { | |
| 193 } | |
| 194 | |
| 195 void Clipboard::Set(const std::string& key, const std::string& value) { | |
| 196 } | |
| 197 | |
| 198 } // namespace ui | |
| OLD | NEW |