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