| 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 <gtk/gtk.h> |  | 
|    8 #include <X11/extensions/Xfixes.h> |  | 
|    9 #include <X11/Xatom.h> |  | 
|   10 #include <map> |  | 
|   11 #include <set> |  | 
|   12 #include <string> |  | 
|   13 #include <utility> |  | 
|   14  |  | 
|   15 #include "base/basictypes.h" |  | 
|   16 #include "base/files/file_path.h" |  | 
|   17 #include "base/logging.h" |  | 
|   18 #include "base/memory/singleton.h" |  | 
|   19 #include "base/strings/utf_string_conversions.h" |  | 
|   20 #include "third_party/skia/include/core/SkBitmap.h" |  | 
|   21 #include "ui/base/clipboard/custom_data_helper.h" |  | 
|   22 #include "ui/base/gtk/gtk_signal.h" |  | 
|   23 #include "ui/base/x/x11_util.h" |  | 
|   24 #include "ui/gfx/canvas.h" |  | 
|   25 #include "ui/gfx/gtk_util.h" |  | 
|   26 #include "ui/gfx/scoped_gobject.h" |  | 
|   27 #include "ui/gfx/size.h" |  | 
|   28  |  | 
|   29 namespace ui { |  | 
|   30  |  | 
|   31 namespace { |  | 
|   32  |  | 
|   33 class SelectionChangeObserver { |  | 
|   34  public: |  | 
|   35   static SelectionChangeObserver* GetInstance(); |  | 
|   36  |  | 
|   37   uint64 clipboard_sequence_number() const { |  | 
|   38     return clipboard_sequence_number_; |  | 
|   39   } |  | 
|   40   uint64 primary_sequence_number() const { return primary_sequence_number_; } |  | 
|   41  |  | 
|   42  private: |  | 
|   43   friend struct DefaultSingletonTraits<SelectionChangeObserver>; |  | 
|   44  |  | 
|   45   SelectionChangeObserver(); |  | 
|   46   ~SelectionChangeObserver(); |  | 
|   47  |  | 
|   48   CHROMEG_CALLBACK_1(SelectionChangeObserver, GdkFilterReturn, OnXEvent, |  | 
|   49                      GdkXEvent*, GdkEvent*); |  | 
|   50  |  | 
|   51   int event_base_; |  | 
|   52   Atom clipboard_atom_; |  | 
|   53   uint64 clipboard_sequence_number_; |  | 
|   54   uint64 primary_sequence_number_; |  | 
|   55  |  | 
|   56   DISALLOW_COPY_AND_ASSIGN(SelectionChangeObserver); |  | 
|   57 }; |  | 
|   58  |  | 
|   59 SelectionChangeObserver::SelectionChangeObserver() |  | 
|   60     : event_base_(-1), |  | 
|   61       clipboard_atom_(None), |  | 
|   62       clipboard_sequence_number_(0), |  | 
|   63       primary_sequence_number_(0) { |  | 
|   64   int ignored; |  | 
|   65   if (XFixesQueryExtension(gfx::GetXDisplay(), &event_base_, &ignored)) { |  | 
|   66     clipboard_atom_ = XInternAtom(gfx::GetXDisplay(), "CLIPBOARD", false); |  | 
|   67     XFixesSelectSelectionInput(gfx::GetXDisplay(), GetX11RootWindow(), |  | 
|   68                                clipboard_atom_, |  | 
|   69                                XFixesSetSelectionOwnerNotifyMask | |  | 
|   70                                XFixesSelectionWindowDestroyNotifyMask | |  | 
|   71                                XFixesSelectionClientCloseNotifyMask); |  | 
|   72     // This seems to be semi-optional. For some reason, registering for any |  | 
|   73     // selection notify events seems to subscribe us to events for both the |  | 
|   74     // primary and the clipboard buffers. Register anyway just to be safe. |  | 
|   75     XFixesSelectSelectionInput(gfx::GetXDisplay(), GetX11RootWindow(), |  | 
|   76                                XA_PRIMARY, |  | 
|   77                                XFixesSetSelectionOwnerNotifyMask | |  | 
|   78                                XFixesSelectionWindowDestroyNotifyMask | |  | 
|   79                                XFixesSelectionClientCloseNotifyMask); |  | 
|   80     gdk_window_add_filter(NULL, &SelectionChangeObserver::OnXEventThunk, this); |  | 
|   81   } |  | 
|   82 } |  | 
|   83  |  | 
|   84 SelectionChangeObserver::~SelectionChangeObserver() { |  | 
|   85 } |  | 
|   86  |  | 
|   87 SelectionChangeObserver* SelectionChangeObserver::GetInstance() { |  | 
|   88   return Singleton<SelectionChangeObserver>::get(); |  | 
|   89 } |  | 
|   90  |  | 
|   91 GdkFilterReturn SelectionChangeObserver::OnXEvent(GdkXEvent* xevent, |  | 
|   92                                                   GdkEvent* event) { |  | 
|   93   XEvent* xev = static_cast<XEvent*>(xevent); |  | 
|   94  |  | 
|   95   if (xev->type == event_base_ + XFixesSelectionNotify) { |  | 
|   96     XFixesSelectionNotifyEvent* ev = |  | 
|   97         reinterpret_cast<XFixesSelectionNotifyEvent*>(xev); |  | 
|   98     if (ev->selection == clipboard_atom_) { |  | 
|   99       clipboard_sequence_number_++; |  | 
|  100     } else if (ev->selection == XA_PRIMARY) { |  | 
|  101       primary_sequence_number_++; |  | 
|  102     } else { |  | 
|  103       DLOG(ERROR) << "Unexpected selection atom: " << ev->selection; |  | 
|  104     } |  | 
|  105   } |  | 
|  106   return GDK_FILTER_CONTINUE; |  | 
|  107 } |  | 
|  108  |  | 
|  109 const char kMimeTypeBitmap[] = "image/bmp"; |  | 
|  110 const char kMimeTypeMozillaURL[] = "text/x-moz-url"; |  | 
|  111 const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data"; |  | 
|  112 const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste"; |  | 
|  113  |  | 
|  114 std::string GdkAtomToString(const GdkAtom& atom) { |  | 
|  115   gchar* name = gdk_atom_name(atom); |  | 
|  116   std::string rv(name); |  | 
|  117   g_free(name); |  | 
|  118   return rv; |  | 
|  119 } |  | 
|  120  |  | 
|  121 GdkAtom StringToGdkAtom(const std::string& str) { |  | 
|  122   return gdk_atom_intern(str.c_str(), FALSE); |  | 
|  123 } |  | 
|  124  |  | 
|  125 // GtkClipboardGetFunc callback. |  | 
|  126 // GTK will call this when an application wants data we copied to the clipboard. |  | 
|  127 void GetData(GtkClipboard* clipboard, |  | 
|  128              GtkSelectionData* selection_data, |  | 
|  129              guint info, |  | 
|  130              gpointer user_data) { |  | 
|  131   Clipboard::TargetMap* data_map = |  | 
|  132       reinterpret_cast<Clipboard::TargetMap*>(user_data); |  | 
|  133  |  | 
|  134   std::string target_string = GdkAtomToString( |  | 
|  135       gtk_selection_data_get_target(selection_data)); |  | 
|  136   Clipboard::TargetMap::iterator iter = data_map->find(target_string); |  | 
|  137  |  | 
|  138   if (iter == data_map->end()) |  | 
|  139     return; |  | 
|  140  |  | 
|  141   if (target_string == kMimeTypeBitmap) { |  | 
|  142     gtk_selection_data_set_pixbuf(selection_data, |  | 
|  143         reinterpret_cast<GdkPixbuf*>(iter->second.first)); |  | 
|  144   } else { |  | 
|  145     gtk_selection_data_set(selection_data, |  | 
|  146                            gtk_selection_data_get_target(selection_data), 8, |  | 
|  147                            reinterpret_cast<guchar*>(iter->second.first), |  | 
|  148                            iter->second.second); |  | 
|  149   } |  | 
|  150 } |  | 
|  151  |  | 
|  152 // GtkClipboardClearFunc callback. |  | 
|  153 // We are guaranteed this will be called exactly once for each call to |  | 
|  154 // gtk_clipboard_set_with_data. |  | 
|  155 void ClearData(GtkClipboard* /*clipboard*/, |  | 
|  156                gpointer user_data) { |  | 
|  157   Clipboard::TargetMap* map = |  | 
|  158       reinterpret_cast<Clipboard::TargetMap*>(user_data); |  | 
|  159   // The same data may be inserted under multiple keys, so use a set to |  | 
|  160   // uniq them. |  | 
|  161   std::set<char*> ptrs; |  | 
|  162  |  | 
|  163   for (Clipboard::TargetMap::iterator iter = map->begin(); |  | 
|  164        iter != map->end(); ++iter) { |  | 
|  165     if (iter->first == kMimeTypeBitmap) |  | 
|  166       g_object_unref(reinterpret_cast<GdkPixbuf*>(iter->second.first)); |  | 
|  167     else |  | 
|  168       ptrs.insert(iter->second.first); |  | 
|  169   } |  | 
|  170  |  | 
|  171   for (std::set<char*>::iterator iter = ptrs.begin(); |  | 
|  172        iter != ptrs.end(); ++iter) { |  | 
|  173     delete[] *iter; |  | 
|  174   } |  | 
|  175  |  | 
|  176   delete map; |  | 
|  177 } |  | 
|  178  |  | 
|  179 }  // namespace |  | 
|  180  |  | 
|  181 Clipboard::FormatType::FormatType() : data_(GDK_NONE) { |  | 
|  182 } |  | 
|  183  |  | 
|  184 Clipboard::FormatType::FormatType(const std::string& format_string) |  | 
|  185     : data_(StringToGdkAtom(format_string)) { |  | 
|  186 } |  | 
|  187  |  | 
|  188 Clipboard::FormatType::FormatType(const GdkAtom& native_format) |  | 
|  189     : data_(native_format) { |  | 
|  190 } |  | 
|  191  |  | 
|  192 Clipboard::FormatType::~FormatType() { |  | 
|  193 } |  | 
|  194  |  | 
|  195 std::string Clipboard::FormatType::Serialize() const { |  | 
|  196   return GdkAtomToString(data_); |  | 
|  197 } |  | 
|  198  |  | 
|  199 // static |  | 
|  200 Clipboard::FormatType Clipboard::FormatType::Deserialize( |  | 
|  201     const std::string& serialization) { |  | 
|  202   return FormatType(serialization); |  | 
|  203 } |  | 
|  204  |  | 
|  205 bool Clipboard::FormatType::Equals(const FormatType& other) const { |  | 
|  206   return data_ == other.data_; |  | 
|  207 } |  | 
|  208  |  | 
|  209 Clipboard::Clipboard() : clipboard_data_(NULL) { |  | 
|  210   DCHECK(CalledOnValidThread()); |  | 
|  211   clipboard_ = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); |  | 
|  212   primary_selection_ = gtk_clipboard_get(GDK_SELECTION_PRIMARY); |  | 
|  213 } |  | 
|  214  |  | 
|  215 Clipboard::~Clipboard() { |  | 
|  216   DCHECK(CalledOnValidThread()); |  | 
|  217   gtk_clipboard_store(clipboard_); |  | 
|  218 } |  | 
|  219  |  | 
|  220 void Clipboard::WriteObjects(ClipboardType type, const ObjectMap& objects) { |  | 
|  221   DCHECK(CalledOnValidThread()); |  | 
|  222   clipboard_data_ = new TargetMap(); |  | 
|  223  |  | 
|  224   for (ObjectMap::const_iterator iter = objects.begin(); |  | 
|  225        iter != objects.end(); ++iter) { |  | 
|  226     DispatchObject(static_cast<ObjectType>(iter->first), iter->second); |  | 
|  227   } |  | 
|  228   SetGtkClipboard(type); |  | 
|  229  |  | 
|  230   if (type == CLIPBOARD_TYPE_COPY_PASTE) { |  | 
|  231     ObjectMap::const_iterator text_iter = objects.find(CBF_TEXT); |  | 
|  232     if (text_iter != objects.end()) { |  | 
|  233       // Copy text and SourceTag to the selection clipboard. |  | 
|  234       ObjectMap::const_iterator next_iter = text_iter; |  | 
|  235       WriteObjects(CLIPBOARD_TYPE_SELECTION, ObjectMap(text_iter, ++next_iter)); |  | 
|  236     } |  | 
|  237   } |  | 
|  238 } |  | 
|  239  |  | 
|  240 // Take ownership of the GTK clipboard and inform it of the targets we support. |  | 
|  241 void Clipboard::SetGtkClipboard(ClipboardType type) { |  | 
|  242   scoped_ptr<GtkTargetEntry[]> targets( |  | 
|  243       new GtkTargetEntry[clipboard_data_->size()]); |  | 
|  244  |  | 
|  245   int i = 0; |  | 
|  246   for (Clipboard::TargetMap::iterator iter = clipboard_data_->begin(); |  | 
|  247        iter != clipboard_data_->end(); ++iter, ++i) { |  | 
|  248     targets[i].target = const_cast<char*>(iter->first.c_str()); |  | 
|  249     targets[i].flags = 0; |  | 
|  250     targets[i].info = 0; |  | 
|  251   } |  | 
|  252  |  | 
|  253   GtkClipboard *clipboard = LookupBackingClipboard(type); |  | 
|  254  |  | 
|  255   if (gtk_clipboard_set_with_data(clipboard, targets.get(), |  | 
|  256                                   clipboard_data_->size(), |  | 
|  257                                   GetData, ClearData, |  | 
|  258                                   clipboard_data_)) { |  | 
|  259     gtk_clipboard_set_can_store(clipboard, |  | 
|  260                                 targets.get(), |  | 
|  261                                 clipboard_data_->size()); |  | 
|  262   } |  | 
|  263  |  | 
|  264   // clipboard_data_ now owned by the GtkClipboard. |  | 
|  265   clipboard_data_ = NULL; |  | 
|  266 } |  | 
|  267  |  | 
|  268 void Clipboard::WriteText(const char* text_data, size_t text_len) { |  | 
|  269   char* data = new char[text_len]; |  | 
|  270   memcpy(data, text_data, text_len); |  | 
|  271  |  | 
|  272   InsertMapping(kMimeTypeText, data, text_len); |  | 
|  273   InsertMapping("TEXT", data, text_len); |  | 
|  274   InsertMapping("STRING", data, text_len); |  | 
|  275   InsertMapping("UTF8_STRING", data, text_len); |  | 
|  276   InsertMapping("COMPOUND_TEXT", data, text_len); |  | 
|  277 } |  | 
|  278  |  | 
|  279 void Clipboard::WriteHTML(const char* markup_data, |  | 
|  280                           size_t markup_len, |  | 
|  281                           const char* url_data, |  | 
|  282                           size_t url_len) { |  | 
|  283   // TODO(estade): We need to expand relative links with |url_data|. |  | 
|  284   static const char* html_prefix = "<meta http-equiv=\"content-type\" " |  | 
|  285                                    "content=\"text/html; charset=utf-8\">"; |  | 
|  286   size_t html_prefix_len = strlen(html_prefix); |  | 
|  287   size_t total_len = html_prefix_len + markup_len + 1; |  | 
|  288  |  | 
|  289   char* data = new char[total_len]; |  | 
|  290   snprintf(data, total_len, "%s", html_prefix); |  | 
|  291   memcpy(data + html_prefix_len, markup_data, markup_len); |  | 
|  292   // Some programs expect NULL-terminated data. See http://crbug.com/42624 |  | 
|  293   data[total_len - 1] = '\0'; |  | 
|  294  |  | 
|  295   InsertMapping(kMimeTypeHTML, data, total_len); |  | 
|  296 } |  | 
|  297  |  | 
|  298 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { |  | 
|  299   WriteData(GetRtfFormatType(), rtf_data, data_len); |  | 
|  300 } |  | 
|  301  |  | 
|  302 // Write an extra flavor that signifies WebKit was the last to modify the |  | 
|  303 // pasteboard. This flavor has no data. |  | 
|  304 void Clipboard::WriteWebSmartPaste() { |  | 
|  305   InsertMapping(kMimeTypeWebkitSmartPaste, NULL, 0); |  | 
|  306 } |  | 
|  307  |  | 
|  308 void Clipboard::WriteBitmap(const SkBitmap& bitmap) { |  | 
|  309   GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(bitmap); |  | 
|  310  |  | 
|  311   // We store the GdkPixbuf*, and the size_t half of the pair is meaningless. |  | 
|  312   // Note that this contrasts with the vast majority of entries in our target |  | 
|  313   // map, which directly store the data and its length. |  | 
|  314   InsertMapping(kMimeTypeBitmap, reinterpret_cast<char*>(pixbuf), 0); |  | 
|  315 } |  | 
|  316  |  | 
|  317 void Clipboard::WriteBookmark(const char* title_data, size_t title_len, |  | 
|  318                               const char* url_data, size_t url_len) { |  | 
|  319   // Write as a mozilla url (UTF16: URL, newline, title). |  | 
|  320   base::string16 url = base::UTF8ToUTF16(std::string(url_data, url_len) + "\n"); |  | 
|  321   base::string16 title = base::UTF8ToUTF16(std::string(title_data, title_len)); |  | 
|  322   if (title.length() >= std::numeric_limits<size_t>::max() / 4 || |  | 
|  323       url.length() >= std::numeric_limits<size_t>::max() / 4) |  | 
|  324     return; |  | 
|  325   size_t data_len = 2 * (title.length() + url.length()); |  | 
|  326  |  | 
|  327   char* data = new char[data_len]; |  | 
|  328   memcpy(data, url.data(), 2 * url.length()); |  | 
|  329   memcpy(data + 2 * url.length(), title.data(), 2 * title.length()); |  | 
|  330   InsertMapping(kMimeTypeMozillaURL, data, data_len); |  | 
|  331 } |  | 
|  332  |  | 
|  333 void Clipboard::WriteData(const FormatType& format, |  | 
|  334                           const char* data_data, |  | 
|  335                           size_t data_len) { |  | 
|  336   // We assume that certain mapping types are only written by trusted code. |  | 
|  337   // Therefore we must upkeep their integrity. |  | 
|  338   if (format.Equals(GetBitmapFormatType())) |  | 
|  339     return; |  | 
|  340   char* data = new char[data_len]; |  | 
|  341   memcpy(data, data_data, data_len); |  | 
|  342   // TODO(dcheng): Maybe this map should use GdkAtoms... |  | 
|  343   InsertMapping(GdkAtomToString(format.ToGdkAtom()).c_str(), data, data_len); |  | 
|  344 } |  | 
|  345  |  | 
|  346 // We do not use gtk_clipboard_wait_is_target_available because of |  | 
|  347 // a bug with the gtk clipboard. It caches the available targets |  | 
|  348 // and does not always refresh the cache when it is appropriate. |  | 
|  349 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format, |  | 
|  350                                   ClipboardType type) const { |  | 
|  351   DCHECK(CalledOnValidThread()); |  | 
|  352   GtkClipboard* clipboard = LookupBackingClipboard(type); |  | 
|  353   if (clipboard == NULL) |  | 
|  354     return false; |  | 
|  355  |  | 
|  356   bool retval = false; |  | 
|  357   GtkSelectionData* data = gtk_clipboard_wait_for_contents( |  | 
|  358       clipboard, gdk_atom_intern_static_string("TARGETS")); |  | 
|  359  |  | 
|  360   bool format_is_plain_text = GetPlainTextFormatType().Equals(format); |  | 
|  361   if (format_is_plain_text) { |  | 
|  362     // This tries a number of common text targets. |  | 
|  363     if (data) { |  | 
|  364       retval = gtk_selection_data_targets_include_text(data); |  | 
|  365     } |  | 
|  366     // Some programs like Java decide to set an empty TARGETS list, so even if |  | 
|  367     // data is not NULL, we still have to fall back. |  | 
|  368     if (!retval) { |  | 
|  369       // Some programs post data to the clipboard without any targets. If this |  | 
|  370       // is the case we attempt to make sense of the contents as text. This is |  | 
|  371       // pretty unfortunate since it means we have to actually copy the data to |  | 
|  372       // see if it is available, but at least this path shouldn't be hit for |  | 
|  373       // conforming programs. |  | 
|  374       gchar* text = gtk_clipboard_wait_for_text(clipboard); |  | 
|  375       if (text) { |  | 
|  376         g_free(text); |  | 
|  377         retval = true; |  | 
|  378       } |  | 
|  379     } |  | 
|  380   } else if (data) { |  | 
|  381     GdkAtom* targets = NULL; |  | 
|  382     int num = 0; |  | 
|  383     gtk_selection_data_get_targets(data, &targets, &num); |  | 
|  384  |  | 
|  385     for (int i = 0; i < num; i++) { |  | 
|  386       if (targets[i] == format.ToGdkAtom()) { |  | 
|  387         retval = true; |  | 
|  388         break; |  | 
|  389       } |  | 
|  390     } |  | 
|  391  |  | 
|  392     g_free(targets); |  | 
|  393   } |  | 
|  394  |  | 
|  395   if (data) |  | 
|  396     gtk_selection_data_free(data); |  | 
|  397  |  | 
|  398   return retval; |  | 
|  399 } |  | 
|  400  |  | 
|  401 void Clipboard::Clear(ClipboardType type) { |  | 
|  402   DCHECK(CalledOnValidThread()); |  | 
|  403   GtkClipboard* clipboard = LookupBackingClipboard(type); |  | 
|  404   if (clipboard == NULL) |  | 
|  405     return; |  | 
|  406   gtk_clipboard_clear(clipboard); |  | 
|  407 } |  | 
|  408  |  | 
|  409 void Clipboard::ReadAvailableTypes(ClipboardType type, |  | 
|  410                                    std::vector<base::string16>* types, |  | 
|  411                                    bool* contains_filenames) const { |  | 
|  412   DCHECK(CalledOnValidThread()); |  | 
|  413   if (!types || !contains_filenames) { |  | 
|  414     NOTREACHED(); |  | 
|  415     return; |  | 
|  416   } |  | 
|  417  |  | 
|  418   types->clear(); |  | 
|  419   if (IsFormatAvailable(GetPlainTextFormatType(), type)) |  | 
|  420     types->push_back(base::UTF8ToUTF16(kMimeTypeText)); |  | 
|  421   if (IsFormatAvailable(GetHtmlFormatType(), type)) |  | 
|  422     types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); |  | 
|  423   if (IsFormatAvailable(GetRtfFormatType(), type)) |  | 
|  424     types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); |  | 
|  425   if (IsFormatAvailable(GetBitmapFormatType(), type)) |  | 
|  426     types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); |  | 
|  427   *contains_filenames = false; |  | 
|  428  |  | 
|  429   GtkClipboard* clipboard = LookupBackingClipboard(type); |  | 
|  430   if (!clipboard) |  | 
|  431     return; |  | 
|  432  |  | 
|  433   GtkSelectionData* data = gtk_clipboard_wait_for_contents( |  | 
|  434       clipboard, GetWebCustomDataFormatType().ToGdkAtom()); |  | 
|  435   if (!data) |  | 
|  436     return; |  | 
|  437   ReadCustomDataTypes(gtk_selection_data_get_data(data), |  | 
|  438                       gtk_selection_data_get_length(data), |  | 
|  439                       types); |  | 
|  440   gtk_selection_data_free(data); |  | 
|  441 } |  | 
|  442  |  | 
|  443  |  | 
|  444 void Clipboard::ReadText(ClipboardType type, base::string16* result) const { |  | 
|  445   DCHECK(CalledOnValidThread()); |  | 
|  446   GtkClipboard* clipboard = LookupBackingClipboard(type); |  | 
|  447   if (clipboard == NULL) |  | 
|  448     return; |  | 
|  449  |  | 
|  450   result->clear(); |  | 
|  451   gchar* text = gtk_clipboard_wait_for_text(clipboard); |  | 
|  452  |  | 
|  453   if (text == NULL) |  | 
|  454     return; |  | 
|  455  |  | 
|  456   // TODO(estade): do we want to handle the possible error here? |  | 
|  457   base::UTF8ToUTF16(text, strlen(text), result); |  | 
|  458   g_free(text); |  | 
|  459 } |  | 
|  460  |  | 
|  461 void Clipboard::ReadAsciiText(ClipboardType type, |  | 
|  462                               std::string* result) const { |  | 
|  463   DCHECK(CalledOnValidThread()); |  | 
|  464   GtkClipboard* clipboard = LookupBackingClipboard(type); |  | 
|  465   if (clipboard == NULL) |  | 
|  466     return; |  | 
|  467  |  | 
|  468   result->clear(); |  | 
|  469   gchar* text = gtk_clipboard_wait_for_text(clipboard); |  | 
|  470  |  | 
|  471   if (text == NULL) |  | 
|  472     return; |  | 
|  473  |  | 
|  474   result->assign(text); |  | 
|  475   g_free(text); |  | 
|  476 } |  | 
|  477  |  | 
|  478 // TODO(estade): handle different charsets. |  | 
|  479 // TODO(port): set *src_url. |  | 
|  480 void Clipboard::ReadHTML(ClipboardType type, |  | 
|  481                          base::string16* markup, |  | 
|  482                          std::string* src_url, |  | 
|  483                          uint32* fragment_start, |  | 
|  484                          uint32* fragment_end) const { |  | 
|  485   DCHECK(CalledOnValidThread()); |  | 
|  486   markup->clear(); |  | 
|  487   if (src_url) |  | 
|  488     src_url->clear(); |  | 
|  489   *fragment_start = 0; |  | 
|  490   *fragment_end = 0; |  | 
|  491  |  | 
|  492   GtkClipboard* clipboard = LookupBackingClipboard(type); |  | 
|  493   if (clipboard == NULL) |  | 
|  494     return; |  | 
|  495   GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard, |  | 
|  496       GetHtmlFormatType().ToGdkAtom()); |  | 
|  497  |  | 
|  498   if (!data) |  | 
|  499     return; |  | 
|  500  |  | 
|  501   // If the data starts with 0xFEFF, i.e., Byte Order Mark, assume it is |  | 
|  502   // UTF-16, otherwise assume UTF-8. |  | 
|  503   gint data_length = gtk_selection_data_get_length(data); |  | 
|  504   const guchar* raw_data = gtk_selection_data_get_data(data); |  | 
|  505  |  | 
|  506   if (data_length >= 2 && |  | 
|  507       reinterpret_cast<const uint16_t*>(raw_data)[0] == 0xFEFF) { |  | 
|  508     markup->assign(reinterpret_cast<const uint16_t*>(raw_data) + 1, |  | 
|  509                    (data_length / 2) - 1); |  | 
|  510   } else { |  | 
|  511     base::UTF8ToUTF16( |  | 
|  512         reinterpret_cast<const char*>(raw_data), data_length, markup); |  | 
|  513   } |  | 
|  514  |  | 
|  515   // If there is a terminating NULL, drop it. |  | 
|  516   if (!markup->empty() && markup->at(markup->length() - 1) == '\0') |  | 
|  517     markup->resize(markup->length() - 1); |  | 
|  518  |  | 
|  519   *fragment_start = 0; |  | 
|  520   DCHECK(markup->length() <= kuint32max); |  | 
|  521   *fragment_end = static_cast<uint32>(markup->length()); |  | 
|  522  |  | 
|  523   gtk_selection_data_free(data); |  | 
|  524 } |  | 
|  525  |  | 
|  526 void Clipboard::ReadRTF(ClipboardType type, std::string* result) const { |  | 
|  527   DCHECK(CalledOnValidThread()); |  | 
|  528   ReadData(GetRtfFormatType(), result); |  | 
|  529 } |  | 
|  530  |  | 
|  531 SkBitmap Clipboard::ReadImage(ClipboardType type) const { |  | 
|  532   DCHECK(CalledOnValidThread()); |  | 
|  533   ScopedGObject<GdkPixbuf>::Type pixbuf( |  | 
|  534       gtk_clipboard_wait_for_image(clipboard_)); |  | 
|  535   if (!pixbuf.get()) |  | 
|  536     return SkBitmap(); |  | 
|  537  |  | 
|  538   gfx::Canvas canvas(gfx::Size(gdk_pixbuf_get_width(pixbuf.get()), |  | 
|  539                                gdk_pixbuf_get_height(pixbuf.get())), |  | 
|  540                      1.0f, false); |  | 
|  541   { |  | 
|  542     skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas()); |  | 
|  543     cairo_t* context = scoped_platform_paint.GetPlatformSurface(); |  | 
|  544     gdk_cairo_set_source_pixbuf(context, pixbuf.get(), 0.0, 0.0); |  | 
|  545     cairo_paint(context); |  | 
|  546   } |  | 
|  547   return canvas.ExtractImageRep().sk_bitmap(); |  | 
|  548 } |  | 
|  549  |  | 
|  550 void Clipboard::ReadCustomData(ClipboardType clipboard_type, |  | 
|  551                                const base::string16& type, |  | 
|  552                                base::string16* result) const { |  | 
|  553   DCHECK(CalledOnValidThread()); |  | 
|  554   GtkClipboard* clipboard = LookupBackingClipboard(clipboard_type); |  | 
|  555   if (!clipboard) |  | 
|  556     return; |  | 
|  557  |  | 
|  558   GtkSelectionData* data = gtk_clipboard_wait_for_contents( |  | 
|  559       clipboard, GetWebCustomDataFormatType().ToGdkAtom()); |  | 
|  560   if (!data) |  | 
|  561     return; |  | 
|  562   ReadCustomDataForType(gtk_selection_data_get_data(data), |  | 
|  563                         gtk_selection_data_get_length(data), |  | 
|  564                         type, result); |  | 
|  565   gtk_selection_data_free(data); |  | 
|  566 } |  | 
|  567  |  | 
|  568 void Clipboard::ReadBookmark(base::string16* title, std::string* url) const { |  | 
|  569   // TODO(estade): implement this. |  | 
|  570   NOTIMPLEMENTED(); |  | 
|  571 } |  | 
|  572  |  | 
|  573 void Clipboard::ReadData(const FormatType& format, std::string* result) const { |  | 
|  574   DCHECK(CalledOnValidThread()); |  | 
|  575   result->clear(); |  | 
|  576   GtkSelectionData* data = |  | 
|  577       gtk_clipboard_wait_for_contents(clipboard_, format.ToGdkAtom()); |  | 
|  578   if (!data) |  | 
|  579     return; |  | 
|  580   result->assign(reinterpret_cast<const char*>( |  | 
|  581                      gtk_selection_data_get_data(data)), |  | 
|  582                  gtk_selection_data_get_length(data)); |  | 
|  583   gtk_selection_data_free(data); |  | 
|  584 } |  | 
|  585  |  | 
|  586 uint64 Clipboard::GetSequenceNumber(ClipboardType type) { |  | 
|  587   DCHECK(CalledOnValidThread()); |  | 
|  588   if (type == CLIPBOARD_TYPE_COPY_PASTE) |  | 
|  589     return SelectionChangeObserver::GetInstance()->clipboard_sequence_number(); |  | 
|  590   else |  | 
|  591     return SelectionChangeObserver::GetInstance()->primary_sequence_number(); |  | 
|  592 } |  | 
|  593  |  | 
|  594 //static |  | 
|  595 Clipboard::FormatType Clipboard::GetFormatType( |  | 
|  596     const std::string& format_string) { |  | 
|  597   return FormatType::Deserialize(format_string); |  | 
|  598 } |  | 
|  599  |  | 
|  600 // static |  | 
|  601 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { |  | 
|  602   CR_DEFINE_STATIC_LOCAL( |  | 
|  603       FormatType, type, (GDK_TARGET_STRING)); |  | 
|  604   return type; |  | 
|  605 } |  | 
|  606  |  | 
|  607 // static |  | 
|  608 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { |  | 
|  609   return GetPlainTextFormatType(); |  | 
|  610 } |  | 
|  611  |  | 
|  612 // static |  | 
|  613 const Clipboard::FormatType& Clipboard::GetUrlFormatType() { |  | 
|  614   return GetPlainTextFormatType(); |  | 
|  615 } |  | 
|  616  |  | 
|  617 // static |  | 
|  618 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { |  | 
|  619   return GetPlainTextWFormatType(); |  | 
|  620 } |  | 
|  621  |  | 
|  622 // static |  | 
|  623 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { |  | 
|  624   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML)); |  | 
|  625   return type; |  | 
|  626 } |  | 
|  627  |  | 
|  628 // static |  | 
|  629 const Clipboard::FormatType& Clipboard::GetRtfFormatType() { |  | 
|  630   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF)); |  | 
|  631   return type; |  | 
|  632 } |  | 
|  633  |  | 
|  634 // static |  | 
|  635 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { |  | 
|  636   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeBitmap)); |  | 
|  637   return type; |  | 
|  638 } |  | 
|  639  |  | 
|  640 // static |  | 
|  641 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { |  | 
|  642   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste)); |  | 
|  643   return type; |  | 
|  644 } |  | 
|  645  |  | 
|  646 // static |  | 
|  647 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { |  | 
|  648   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); |  | 
|  649   return type; |  | 
|  650 } |  | 
|  651  |  | 
|  652 // static |  | 
|  653 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { |  | 
|  654   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); |  | 
|  655   return type; |  | 
|  656 } |  | 
|  657  |  | 
|  658 void Clipboard::InsertMapping(const char* key, |  | 
|  659                               char* data, |  | 
|  660                               size_t data_len) { |  | 
|  661   DCHECK(clipboard_data_->find(key) == clipboard_data_->end()); |  | 
|  662   (*clipboard_data_)[key] = std::make_pair(data, data_len); |  | 
|  663 } |  | 
|  664  |  | 
|  665 GtkClipboard* Clipboard::LookupBackingClipboard(ClipboardType type) const { |  | 
|  666   switch (type) { |  | 
|  667     case CLIPBOARD_TYPE_COPY_PASTE: |  | 
|  668       return clipboard_; |  | 
|  669     case CLIPBOARD_TYPE_SELECTION: |  | 
|  670       return primary_selection_; |  | 
|  671     default: |  | 
|  672       NOTREACHED(); |  | 
|  673       return NULL; |  | 
|  674   } |  | 
|  675 } |  | 
|  676  |  | 
|  677 }  // namespace ui |  | 
| OLD | NEW |