| OLD | NEW | 
| (Empty) |  | 
 |    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 | 
 |    3 // found in the LICENSE file. | 
 |    4  | 
 |    5 #include "ui/base/clipboard/clipboard.h" | 
 |    6  | 
 |    7 #include <jni.h> | 
 |    8  | 
 |    9 #include "base/android/jni_android.h" | 
 |   10 #include "base/android/jni_string.h" | 
 |   11 #include "base/lazy_instance.h" | 
 |   12 #include "base/logging.h" | 
 |   13 #include "base/memory/scoped_ptr.h" | 
 |   14 #include "base/synchronization/lock.h" | 
 |   15 #include "base/utf_string_conversions.h" | 
 |   16 #include "third_party/skia/include/core/SkBitmap.h" | 
 |   17 #include "ui/gfx/size.h" | 
 |   18  | 
 |   19 // Important note: | 
 |   20 // Android's clipboard system only supports text content, so use it only when | 
 |   21 // text is added to or retrieved from the system. For other data types, store | 
 |   22 // the value in a map. This has the consequence that the clipboard's contents | 
 |   23 // will only be available within the current process. | 
 |   24  | 
 |   25 using base::android::AttachCurrentThread; | 
 |   26 using base::android::CheckException; | 
 |   27 using base::android::ClearException; | 
 |   28 using base::android::ConvertJavaStringToUTF16; | 
 |   29 using base::android::ConvertJavaStringToUTF8; | 
 |   30 using base::android::GetClass; | 
 |   31 using base::android::GetMethodID; | 
 |   32 using base::android::ScopedJavaLocalRef; | 
 |   33  | 
 |   34 namespace ui { | 
 |   35  | 
 |   36 namespace { | 
 |   37 // As Android only supports text in the clipboard, the following map will be | 
 |   38 // used for other kinds of data. Use the lock to make this thread-safe. | 
 |   39 // TODO(beverloo): http://crbug.com/112286 Investigate whether the locks in | 
 |   40 // this file are required. | 
 |   41 typedef std::map<std::string, std::string> ClipboardMap; | 
 |   42 ClipboardMap* g_clipboard_map = NULL; | 
 |   43 base::LazyInstance<base::Lock> g_clipboard_map_lock = LAZY_INSTANCE_INITIALIZER; | 
 |   44  | 
 |   45 // Various format we support. | 
 |   46 const char kPlainTextFormat[] = "text"; | 
 |   47 const char kHTMLFormat[] = "html"; | 
 |   48 const char kBitmapFormat[] = "bitmap"; | 
 |   49 const char kWebKitSmartPasteFormat[] = "webkit_smart"; | 
 |   50 const char kBookmarkFormat[] = "bookmark"; | 
 |   51 const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; | 
 |   52  | 
 |   53 }  // namespace | 
 |   54  | 
 |   55 Clipboard::FormatType::FormatType() { | 
 |   56 } | 
 |   57  | 
 |   58 Clipboard::FormatType::FormatType(const std::string& native_format) | 
 |   59     : data_(native_format) { | 
 |   60 } | 
 |   61  | 
 |   62 Clipboard::FormatType::~FormatType() { | 
 |   63 } | 
 |   64  | 
 |   65 std::string Clipboard::FormatType::Serialize() const { | 
 |   66   return data_; | 
 |   67 } | 
 |   68  | 
 |   69 // static | 
 |   70 Clipboard::FormatType Clipboard::FormatType::Deserialize( | 
 |   71     const std::string& serialization) { | 
 |   72   return FormatType(serialization); | 
 |   73 } | 
 |   74  | 
 |   75 bool Clipboard::FormatType::Equals(const FormatType& other) const { | 
 |   76   return data_ == other.data_; | 
 |   77 } | 
 |   78  | 
 |   79 // The clipboard object on the Android platform is simply wrapping the Java | 
 |   80 // object for the text data format. For non-text format, a global map is used. | 
 |   81 Clipboard::Clipboard() : set_text_(NULL), has_text_(NULL), get_text_(NULL) { | 
 |   82   JNIEnv* env = AttachCurrentThread(); | 
 |   83   DCHECK(env); | 
 |   84  | 
 |   85   // Get the context. | 
 |   86   // We have need a ScopedJavaLocalRef to clean-up the local ref if we have to | 
 |   87   // create the Context. | 
 |   88   jobject context = base::android::GetApplicationContext(); | 
 |   89   ScopedJavaLocalRef<jobject> scoped_context; | 
 |   90   if (!context) { | 
 |   91     // Should be during testing only. | 
 |   92     // Get the ActivityThread class. | 
 |   93     ScopedJavaLocalRef<jclass> activity_thread_class = | 
 |   94         GetClass(env, "android/app/ActivityThread"); | 
 |   95  | 
 |   96     // Try to get the current activity thread. | 
 |   97     jmethodID current_activity_method_id = GetMethodID( | 
 |   98         env, activity_thread_class, | 
 |   99         "currentActivityThread", "()Landroid/app/ActivityThread;"); | 
 |  100  | 
 |  101     ScopedJavaLocalRef<jobject> current_activity(env, | 
 |  102         env->CallStaticObjectMethod(activity_thread_class.obj(), | 
 |  103                                     current_activity_method_id)); | 
 |  104     ClearException(env); | 
 |  105  | 
 |  106     if (!current_activity.obj()) { | 
 |  107       // There is no current activity, create one. | 
 |  108       ScopedJavaLocalRef<jclass> looper_class = | 
 |  109           GetClass(env, "android/os/Looper"); | 
 |  110       jmethodID prepare_method_id = | 
 |  111           GetStaticMethodID(env, looper_class, "prepareMainLooper", "()V"); | 
 |  112       env->CallStaticVoidMethod(looper_class.obj(), prepare_method_id); | 
 |  113       CheckException(env); | 
 |  114  | 
 |  115       jmethodID system_main_method_id = | 
 |  116           GetStaticMethodID(env, activity_thread_class, "systemMain", | 
 |  117                             "()Landroid/app/ActivityThread;"); | 
 |  118  | 
 |  119       current_activity.Reset(env, env->CallStaticObjectMethod( | 
 |  120           activity_thread_class.obj(), system_main_method_id)); | 
 |  121       DCHECK(current_activity.obj()); | 
 |  122       CheckException(env); | 
 |  123     } | 
 |  124  | 
 |  125     // Get the context. | 
 |  126     jmethodID get_system_context_id = GetMethodID(env, activity_thread_class, | 
 |  127        "getSystemContext", "()Landroid/app/ContextImpl;"); | 
 |  128     scoped_context.Reset(env, env->CallObjectMethod(current_activity.obj(), | 
 |  129                                                     get_system_context_id)); | 
 |  130     context = scoped_context.obj(); | 
 |  131     DCHECK(context); | 
 |  132   } | 
 |  133  | 
 |  134   // Get the context class. | 
 |  135   ScopedJavaLocalRef<jclass> context_class = | 
 |  136       GetClass(env, "android/content/Context"); | 
 |  137   // Get the system service method. | 
 |  138   jmethodID get_system_service = GetMethodID(env, context_class, | 
 |  139       "getSystemService",  "(Ljava/lang/String;)Ljava/lang/Object;"); | 
 |  140  | 
 |  141   // Retrieve the system service. | 
 |  142   ScopedJavaLocalRef<jstring> service_name(env, env->NewStringUTF("clipboard")); | 
 |  143   clipboard_manager_.Reset(env, env->CallObjectMethod(context, | 
 |  144       get_system_service, service_name.obj())); | 
 |  145   ClearException(env); | 
 |  146   DCHECK(clipboard_manager_.obj()); | 
 |  147  | 
 |  148   // Retain a few methods we'll keep using. | 
 |  149   ScopedJavaLocalRef<jclass> clipboard_class = | 
 |  150       GetClass(env, "android/text/ClipboardManager"); | 
 |  151   set_text_ = GetMethodID(env, clipboard_class, | 
 |  152                           "setText", "(Ljava/lang/CharSequence;)V"); | 
 |  153   has_text_ = GetMethodID(env, clipboard_class, "hasText", "()Z"); | 
 |  154   get_text_ = GetMethodID(env, clipboard_class, | 
 |  155                           "getText", "()Ljava/lang/CharSequence;"); | 
 |  156  | 
 |  157   // Will need to call toString as CharSequence is not always a String. | 
 |  158   ScopedJavaLocalRef<jclass> charsequence_class = | 
 |  159       GetClass(env, "java/lang/CharSequence"); | 
 |  160   to_string_ = GetMethodID(env, charsequence_class, | 
 |  161                            "toString", "()Ljava/lang/String;"); | 
 |  162  | 
 |  163   // Create the object map if we are the first clipboard | 
 |  164   base::AutoLock lock(g_clipboard_map_lock.Get()); | 
 |  165   if (!g_clipboard_map) | 
 |  166     g_clipboard_map = new ClipboardMap; | 
 |  167 } | 
 |  168  | 
 |  169 Clipboard::~Clipboard() { | 
 |  170 } | 
 |  171  | 
 |  172 // Main entry point used to write several values in the clipboard. | 
 |  173 void Clipboard::WriteObjects(const ObjectMap& objects) { | 
 |  174   Clear(); | 
 |  175   for (ObjectMap::const_iterator iter = objects.begin(); | 
 |  176        iter != objects.end(); ++iter) { | 
 |  177     DispatchObject(static_cast<ObjectType>(iter->first), iter->second); | 
 |  178   } | 
 |  179 } | 
 |  180  | 
 |  181 uint64 Clipboard::GetSequenceNumber(Clipboard::Buffer /* buffer */) { | 
 |  182   // TODO: Implement this. For now this interface will advertise | 
 |  183   // that the clipboard never changes. That's fine as long as we | 
 |  184   // don't rely on this signal. | 
 |  185   return 0; | 
 |  186 } | 
 |  187  | 
 |  188 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, | 
 |  189                                   Clipboard::Buffer buffer) const { | 
 |  190   DCHECK_EQ(buffer, BUFFER_STANDARD); | 
 |  191  | 
 |  192   if (!format.compare(kPlainTextFormat)) | 
 |  193     return IsTextAvailableFromAndroid(); | 
 |  194  | 
 |  195   ValidateInternalClipboard(); | 
 |  196  | 
 |  197   base::AutoLock lock(g_clipboard_map_lock.Get()); | 
 |  198   return g_clipboard_map->find(format.ToString()) != g_clipboard_map->end(); | 
 |  199 } | 
 |  200  | 
 |  201 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, | 
 |  202                                    bool* contains_filenames) const { | 
 |  203   if (!types || !contains_filenames) { | 
 |  204     NOTREACHED(); | 
 |  205     return; | 
 |  206   } | 
 |  207  | 
 |  208   // This is unimplemented on the other platforms (e.g. win, linux). | 
 |  209   NOTIMPLEMENTED(); | 
 |  210  | 
 |  211   types->clear(); | 
 |  212   *contains_filenames = false; | 
 |  213 } | 
 |  214  | 
 |  215 void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const { | 
 |  216   JNIEnv* env = AttachCurrentThread(); | 
 |  217  | 
 |  218   result->clear(); | 
 |  219   if (!env->CallBooleanMethod(clipboard_manager_.obj(), has_text_)) | 
 |  220     return; | 
 |  221  | 
 |  222   ScopedJavaLocalRef<jobject> char_seq_text(env, | 
 |  223       env->CallObjectMethod(clipboard_manager_.obj(), get_text_)); | 
 |  224   ScopedJavaLocalRef<jstring> tmp_string(env, | 
 |  225       static_cast<jstring>(env->CallObjectMethod(char_seq_text.obj(), | 
 |  226                                                  to_string_))); | 
 |  227   *result = ConvertJavaStringToUTF16(tmp_string); | 
 |  228 } | 
 |  229  | 
 |  230 void Clipboard::ReadAsciiText(Clipboard::Buffer buffer, | 
 |  231                               std::string* result) const { | 
 |  232   JNIEnv* env = AttachCurrentThread(); | 
 |  233  | 
 |  234   result->clear(); | 
 |  235   if (!env->CallBooleanMethod(clipboard_manager_.obj(), has_text_)) | 
 |  236     return; | 
 |  237  | 
 |  238   ScopedJavaLocalRef<jobject> char_seq_text(env, | 
 |  239       env->CallObjectMethod(clipboard_manager_.obj(), get_text_)); | 
 |  240   ScopedJavaLocalRef<jstring> tmp_string(env, | 
 |  241       static_cast<jstring>(env->CallObjectMethod(char_seq_text.obj(), | 
 |  242                                                  to_string_))); | 
 |  243   *result = ConvertJavaStringToUTF8(tmp_string); | 
 |  244 } | 
 |  245  | 
 |  246 // Note: |src_url| isn't really used. It is only implemented in Windows. | 
 |  247 void Clipboard::ReadHTML(Clipboard::Buffer buffer, | 
 |  248                          string16* markup, | 
 |  249                          std::string* src_url, | 
 |  250                          uint32* fragment_start, | 
 |  251                          uint32* fragment_end) const { | 
 |  252   markup->clear(); | 
 |  253   if (src_url) | 
 |  254     src_url->clear(); | 
 |  255   *fragment_start = 0; | 
 |  256   *fragment_end = 0; | 
 |  257  | 
 |  258   std::string input; | 
 |  259  | 
 |  260   ValidateInternalClipboard(); | 
 |  261  | 
 |  262   base::AutoLock lock(g_clipboard_map_lock.Get()); | 
 |  263   ClipboardMap::const_iterator it = g_clipboard_map->find(kHTMLFormat); | 
 |  264   if (it != g_clipboard_map->end()) | 
 |  265     input = it->second; | 
 |  266  | 
 |  267   if (input.empty()) | 
 |  268     return; | 
 |  269  | 
 |  270   *fragment_end = static_cast<uint32>(input.length()); | 
 |  271  | 
 |  272   UTF8ToUTF16(input.c_str(), input.length(), markup); | 
 |  273 } | 
 |  274  | 
 |  275 SkBitmap Clipboard::ReadImage(Buffer buffer) const { | 
 |  276   NOTIMPLEMENTED(); | 
 |  277   return SkBitmap(); | 
 |  278 } | 
 |  279  | 
 |  280 void Clipboard::ReadCustomData(Buffer buffer, | 
 |  281                                const string16& type, | 
 |  282                                string16* result) const { | 
 |  283   NOTIMPLEMENTED(); | 
 |  284 } | 
 |  285  | 
 |  286 void Clipboard::ReadBookmark(string16* title, std::string* url) const { | 
 |  287   NOTIMPLEMENTED(); | 
 |  288 } | 
 |  289  | 
 |  290 void Clipboard::ReadData(const Clipboard::FormatType& format, | 
 |  291                          std::string* result) const { | 
 |  292   result->clear(); | 
 |  293  | 
 |  294   ValidateInternalClipboard(); | 
 |  295  | 
 |  296   base::AutoLock lock(g_clipboard_map_lock.Get()); | 
 |  297   ClipboardMap::const_iterator it = g_clipboard_map->find(format.ToString()); | 
 |  298   if (it != g_clipboard_map->end()) | 
 |  299     result->assign(it->second); | 
 |  300 } | 
 |  301  | 
 |  302 // static | 
 |  303 Clipboard::FormatType Clipboard::GetFormatType( | 
 |  304     const std::string& format_string) { | 
 |  305   return FormatType::Deserialize(format_string); | 
 |  306 } | 
 |  307  | 
 |  308 // static | 
 |  309 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { | 
 |  310   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat)); | 
 |  311   return type; | 
 |  312 } | 
 |  313  | 
 |  314 // static | 
 |  315 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { | 
 |  316   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat)); | 
 |  317   return type; | 
 |  318 } | 
 |  319  | 
 |  320 // static | 
 |  321 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { | 
 |  322   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebKitSmartPasteFormat)); | 
 |  323   return type; | 
 |  324 } | 
 |  325  | 
 |  326 // static | 
 |  327 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { | 
 |  328   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kHTMLFormat)); | 
 |  329   return type; | 
 |  330 } | 
 |  331  | 
 |  332 // static | 
 |  333 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { | 
 |  334   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kBitmapFormat)); | 
 |  335   return type; | 
 |  336 } | 
 |  337  | 
 |  338 // static | 
 |  339 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { | 
 |  340   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); | 
 |  341   return type; | 
 |  342 } | 
 |  343  | 
 |  344 void Clipboard::WriteText(const char* text_data, size_t text_len) { | 
 |  345   // Write the text in the Android Clipboard. | 
 |  346   JNIEnv* env = AttachCurrentThread(); | 
 |  347   DCHECK(env); | 
 |  348  | 
 |  349   std::string data(text_data, text_len); | 
 |  350   ScopedJavaLocalRef<jstring> str(env, env->NewStringUTF(data.c_str())); | 
 |  351   DCHECK(str.obj() && !ClearException(env)); | 
 |  352  | 
 |  353   env->CallVoidMethod(clipboard_manager_.obj(), set_text_, str.obj()); | 
 |  354  | 
 |  355   // Then write it in our internal data structure. We keep it there to check if | 
 |  356   // another app performed a copy. See ValidateInternalClipboard(). | 
 |  357   Set(kPlainTextFormat, std::string(text_data, text_len)); | 
 |  358 } | 
 |  359  | 
 |  360 void Clipboard::WriteHTML(const char* markup_data, | 
 |  361                           size_t markup_len, | 
 |  362                           const char* url_data, | 
 |  363                           size_t url_len) { | 
 |  364   Set(kHTMLFormat, std::string(markup_data, markup_len)); | 
 |  365 } | 
 |  366  | 
 |  367 // Note: according to other platforms implementations, this really writes the | 
 |  368 // URL spec. | 
 |  369 void Clipboard::WriteBookmark(const char* title_data, size_t title_len, | 
 |  370                               const char* url_data, size_t url_len) { | 
 |  371   Set(kBookmarkFormat, std::string(url_data, url_len)); | 
 |  372 } | 
 |  373  | 
 |  374 // Write an extra flavor that signifies WebKit was the last to modify the | 
 |  375 // pasteboard. This flavor has no data. | 
 |  376 void Clipboard::WriteWebSmartPaste() { | 
 |  377   Set(kWebKitSmartPasteFormat, std::string()); | 
 |  378 } | 
 |  379  | 
 |  380 // All platforms use gfx::Size for size data but it is passed as a const char* | 
 |  381 // Further, pixel_data is expected to be 32 bits per pixel. | 
 |  382 // Note: we implement this to pass all unit tests but it is currently unclear | 
 |  383 // how some code would consume this. | 
 |  384 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { | 
 |  385   const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data); | 
 |  386   int bm_size = size->width() * size->height() * 4; | 
 |  387   int total_size = (sizeof(int) * 2) + bm_size; | 
 |  388   scoped_array<char> buffer(new char[total_size]); | 
 |  389  | 
 |  390   char* p = buffer.get(); | 
 |  391   int n = size->width(); | 
 |  392   memcpy(p, &n, sizeof(int)); | 
 |  393   p += sizeof(int); | 
 |  394   n = size->height(); | 
 |  395   memcpy(p, &n, sizeof(int)); | 
 |  396   p += sizeof(int); | 
 |  397   memcpy(p, pixel_data, bm_size); | 
 |  398  | 
 |  399   Set(kBitmapFormat, std::string(buffer.get(), total_size)); | 
 |  400 } | 
 |  401  | 
 |  402 void Clipboard::WriteData(const Clipboard::FormatType& format, | 
 |  403                           const char* data_data, size_t data_len) { | 
 |  404   Set(format.ToString(), std::string(data_data, data_len)); | 
 |  405 } | 
 |  406  | 
 |  407 bool Clipboard::IsTextAvailableFromAndroid() const { | 
 |  408   JNIEnv* env = AttachCurrentThread(); | 
 |  409   return env->CallBooleanMethod(clipboard_manager_.obj(), has_text_); | 
 |  410 } | 
 |  411  | 
 |  412 void Clipboard::ValidateInternalClipboard() const { | 
 |  413   JNIEnv* env = AttachCurrentThread(); | 
 |  414  | 
 |  415   // First collect what text we currently have in our internal clipboard. | 
 |  416   bool has_internal_text; | 
 |  417   std::string internal_text; | 
 |  418  | 
 |  419   { | 
 |  420     base::AutoLock lock(g_clipboard_map_lock.Get()); | 
 |  421     ClipboardMap::const_iterator it = g_clipboard_map->find(kPlainTextFormat); | 
 |  422     if (it != g_clipboard_map->end()) { | 
 |  423       has_internal_text = true; | 
 |  424       internal_text = it->second; | 
 |  425     } else { | 
 |  426       has_internal_text = false; | 
 |  427     } | 
 |  428   } | 
 |  429  | 
 |  430   if (IsTextAvailableFromAndroid()) { | 
 |  431     // Make sure the text in the Android Clipboard matches what we think it | 
 |  432     // should be. | 
 |  433     ScopedJavaLocalRef<jstring> tmp_string(env, | 
 |  434         static_cast<jstring>(env->CallObjectMethod( | 
 |  435             env->CallObjectMethod(clipboard_manager_.obj(), get_text_), | 
 |  436             to_string_))); | 
 |  437     std::string android_text = ConvertJavaStringToUTF8(tmp_string); | 
 |  438  | 
 |  439     // If the android text doesn't match what we think it should be, our | 
 |  440     // internal representation is no longer valid. | 
 |  441     if (android_text.compare(internal_text)) | 
 |  442       ClearInternalClipboard(); | 
 |  443   } else { | 
 |  444     // If Android has no text but we have some internal text, our internal | 
 |  445     // representation is no longer valid. | 
 |  446     if (has_internal_text) | 
 |  447       ClearInternalClipboard(); | 
 |  448   } | 
 |  449 } | 
 |  450  | 
 |  451 void Clipboard::Clear() { | 
 |  452   JNIEnv* env = AttachCurrentThread(); | 
 |  453   env->CallVoidMethod(clipboard_manager_.obj(), set_text_, NULL); | 
 |  454   ClearInternalClipboard(); | 
 |  455 } | 
 |  456  | 
 |  457 void Clipboard::ClearInternalClipboard() const { | 
 |  458   base::AutoLock lock(g_clipboard_map_lock.Get()); | 
 |  459   g_clipboard_map->clear(); | 
 |  460 } | 
 |  461  | 
 |  462 void Clipboard::Set(const std::string& key, const std::string& value) { | 
 |  463   base::AutoLock lock(g_clipboard_map_lock.Get()); | 
 |  464   (*g_clipboard_map)[key] = value; | 
 |  465 } | 
 |  466  | 
 |  467 }  // namespace ui | 
| OLD | NEW |