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() ? "" : it->second; | |
dcheng
2012/07/02 22:50:19
Minor nit:
"" -> std::string()
cjhopman
2012/07/02 23:26:51
Done.
| |
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) { |
dcheng
2012/07/02 22:50:19
DCHECK_EQ(buffer, BUFFER_STANDARD);
cjhopman
2012/07/02 23:26:51
Done.
| |
200 g_map.Get().Clear(); | |
201 for (ObjectMap::const_iterator iter = objects.begin(); | |
202 iter != objects.end(); ++iter) { | |
203 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); | |
204 } | |
56 } | 205 } |
57 | 206 |
58 uint64 Clipboard::GetSequenceNumber(Clipboard::Buffer /* buffer */) { | 207 uint64 Clipboard::GetSequenceNumber(Clipboard::Buffer /* buffer */) { |
208 // TODO: implement this. For now this interface will advertise | |
209 // that the clipboard never changes. That's fine as long as we | |
210 // don't rely on this signal. | |
59 return 0; | 211 return 0; |
60 } | 212 } |
61 | 213 |
62 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, | 214 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, |
63 Clipboard::Buffer buffer) const { | 215 Clipboard::Buffer buffer) const { |
64 return false; | 216 DCHECK_EQ(buffer, BUFFER_STANDARD); |
217 return g_map.Get().HasFormat(format.data()); | |
65 } | 218 } |
66 | 219 |
67 void Clipboard::Clear(Buffer buffer) { | 220 void Clipboard::Clear(Buffer buffer) { |
68 | 221 DCHECK_EQ(buffer, BUFFER_STANDARD); |
222 g_map.Get().Clear(); | |
69 } | 223 } |
70 | 224 |
71 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, | 225 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, |
72 bool* contains_filenames) const { | 226 bool* contains_filenames) const { |
227 DCHECK_EQ(buffer, BUFFER_STANDARD); | |
228 | |
229 if (!types || !contains_filenames) { | |
230 NOTREACHED(); | |
231 return; | |
232 } | |
233 | |
234 NOTIMPLEMENTED(); | |
235 | |
236 types->clear(); | |
237 *contains_filenames = false; | |
73 } | 238 } |
74 | 239 |
75 void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const { | 240 void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const { |
dcheng
2012/07/02 22:50:19
DCHECK_EQ(buffer, BUFFER_STANDARD);
cjhopman
2012/07/02 23:26:51
Done.
| |
241 std::string utf8; | |
242 ReadAsciiText(buffer, &utf8); | |
243 *result = UTF8ToUTF16(utf8); | |
76 } | 244 } |
77 | 245 |
78 void Clipboard::ReadAsciiText(Clipboard::Buffer buffer, | 246 void Clipboard::ReadAsciiText(Clipboard::Buffer buffer, |
79 std::string* result) const { | 247 std::string* result) const { |
248 DCHECK_EQ(buffer, BUFFER_STANDARD); | |
249 *result = g_map.Get().Get(kPlainTextFormat); | |
80 } | 250 } |
81 | 251 |
252 // Note: |src_url| isn't really used. It is only implemented in Windows | |
82 void Clipboard::ReadHTML(Clipboard::Buffer buffer, | 253 void Clipboard::ReadHTML(Clipboard::Buffer buffer, |
83 string16* markup, | 254 string16* markup, |
84 std::string* src_url, | 255 std::string* src_url, |
85 uint32* fragment_start, | 256 uint32* fragment_start, |
86 uint32* fragment_end) const { | 257 uint32* fragment_end) const { |
dcheng
2012/07/02 22:50:19
DCHECK_EQ(buffer, BUFFER_STANDARD);
cjhopman
2012/07/02 23:26:51
Done.
| |
258 if (src_url) | |
259 src_url->clear(); | |
260 | |
261 std::string input = g_map.Get().Get(kHTMLFormat); | |
262 *markup = UTF8ToUTF16(input); | |
263 | |
264 *fragment_start = 0; | |
265 *fragment_end = static_cast<uint32>(markup->length()); | |
87 } | 266 } |
88 | 267 |
89 void Clipboard::ReadRTF(Buffer buffer, std::string* result) const { | 268 void Clipboard::ReadRTF(Buffer buffer, std::string* result) const { |
269 NOTIMPLEMENTED(); | |
90 } | 270 } |
91 | 271 |
92 SkBitmap Clipboard::ReadImage(Buffer buffer) const { | 272 SkBitmap Clipboard::ReadImage(Buffer buffer) const { |
dcheng
2012/07/02 22:50:19
DCHECK_EQ(...);
cjhopman
2012/07/02 23:26:51
Done.
| |
93 return SkBitmap(); | 273 std::string input = g_map.Get().Get(kBitmapFormat); |
274 | |
275 SkBitmap bmp; | |
276 if (!input.empty()) { | |
277 DCHECK_LE(sizeof(gfx::Size), input.size()); | |
278 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(input.c_str()); | |
dcheng
2012/07/02 22:50:19
Prefer data() instead of c_str() here because of \
cjhopman
2012/07/02 23:26:51
Done.
| |
279 | |
280 bmp.setConfig( | |
281 SkBitmap::kARGB_8888_Config, size->width(), size->height(), 0); | |
282 bmp.allocPixels(); | |
283 | |
284 int bm_size = size->width() * size->height() * 4; | |
285 DCHECK_EQ(sizeof(gfx::Size) + bm_size, input.size()); | |
286 | |
287 memcpy(bmp.getPixels(), input.c_str() + sizeof(gfx::Size), bm_size); | |
dcheng
2012/07/02 22:50:19
Same.
cjhopman
2012/07/02 23:26:51
Done.
| |
288 } | |
289 return bmp; | |
94 } | 290 } |
95 | 291 |
96 void Clipboard::ReadCustomData(Buffer buffer, | 292 void Clipboard::ReadCustomData(Buffer buffer, |
97 const string16& type, | 293 const string16& type, |
98 string16* result) const { | 294 string16* result) const { |
295 NOTIMPLEMENTED(); | |
99 } | 296 } |
100 | 297 |
101 void Clipboard::ReadBookmark(string16* title, std::string* url) const { | 298 void Clipboard::ReadBookmark(string16* title, std::string* url) const { |
299 NOTIMPLEMENTED(); | |
102 } | 300 } |
103 | 301 |
104 void Clipboard::ReadData(const Clipboard::FormatType& format, | 302 void Clipboard::ReadData(const Clipboard::FormatType& format, |
105 std::string* result) const { | 303 std::string* result) const { |
dcheng
2012/07/02 22:50:19
DCHECK_EQ(...);
cjhopman
2012/07/02 23:26:51
Neither ReadData nor ReadBookmark take a buffer ar
| |
304 *result = g_map.Get().Get(format.data()); | |
106 } | 305 } |
107 | 306 |
108 // static | 307 // static |
109 Clipboard::FormatType Clipboard::GetFormatType( | 308 Clipboard::FormatType Clipboard::GetFormatType( |
110 const std::string& format_string) { | 309 const std::string& format_string) { |
111 return FormatType::Deserialize(format_string); | 310 return FormatType::Deserialize(format_string); |
112 } | 311 } |
113 | 312 |
114 // static | 313 // static |
115 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { | 314 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
147 return type; | 346 return type; |
148 } | 347 } |
149 | 348 |
150 // static | 349 // static |
151 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { | 350 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { |
152 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); | 351 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); |
153 return type; | 352 return type; |
154 } | 353 } |
155 | 354 |
156 void Clipboard::WriteText(const char* text_data, size_t text_len) { | 355 void Clipboard::WriteText(const char* text_data, size_t text_len) { |
356 g_map.Get().Set(kPlainTextFormat, std::string(text_data, text_len)); | |
157 } | 357 } |
158 | 358 |
159 void Clipboard::WriteHTML(const char* markup_data, | 359 void Clipboard::WriteHTML(const char* markup_data, |
160 size_t markup_len, | 360 size_t markup_len, |
161 const char* url_data, | 361 const char* url_data, |
162 size_t url_len) { | 362 size_t url_len) { |
363 g_map.Get().Set(kHTMLFormat, std::string(markup_data, markup_len)); | |
163 } | 364 } |
164 | 365 |
165 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { | 366 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { |
367 NOTIMPLEMENTED(); | |
166 } | 368 } |
167 | 369 |
370 // Note: according to other platforms implementations, this really writes the | |
371 // URL spec. | |
168 void Clipboard::WriteBookmark(const char* title_data, size_t title_len, | 372 void Clipboard::WriteBookmark(const char* title_data, size_t title_len, |
169 const char* url_data, size_t url_len) { | 373 const char* url_data, size_t url_len) { |
374 g_map.Get().Set(kBookmarkFormat, std::string(url_data, url_len)); | |
170 } | 375 } |
171 | 376 |
377 // Write an extra flavor that signifies WebKit was the last to modify the | |
378 // pasteboard. This flavor has no data. | |
172 void Clipboard::WriteWebSmartPaste() { | 379 void Clipboard::WriteWebSmartPaste() { |
380 g_map.Get().Set(kWebKitSmartPasteFormat, ""); | |
173 } | 381 } |
174 | 382 |
383 // All platforms use gfx::Size for size data but it is passed as a const char* | |
384 // Further, pixel_data is expected to be 32 bits per pixel | |
385 // Note: we implement this to pass all unit tests but it is currently unclear | |
386 // how some code would consume this. | |
175 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { | 387 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { |
388 const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data); | |
389 int bm_size = size->width() * size->height() * 4; | |
390 | |
391 std::string packed(size_data, sizeof(gfx::Size)); | |
392 packed += std::string(pixel_data, bm_size); | |
393 g_map.Get().Set(kBitmapFormat, packed); | |
176 } | 394 } |
177 | 395 |
178 void Clipboard::WriteData(const Clipboard::FormatType& format, | 396 void Clipboard::WriteData(const Clipboard::FormatType& format, |
179 const char* data_data, size_t data_len) { | 397 const char* data_data, size_t data_len) { |
398 g_map.Get().Set(format.data(), std::string(data_data, data_len)); | |
180 } | 399 } |
181 | 400 |
182 bool Clipboard::IsTextAvailableFromAndroid() const { | 401 } // 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 |