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 |