Home | History | Annotate | Download | only in clipboard
      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 = UTF8ToUTF16(std::string(url_data, url_len) + "\n");
    321   base::string16 title = 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(UTF8ToUTF16(kMimeTypeText));
    421   if (IsFormatAvailable(GetHtmlFormatType(), type))
    422     types->push_back(UTF8ToUTF16(kMimeTypeHTML));
    423   if (IsFormatAvailable(GetRtfFormatType(), type))
    424     types->push_back(UTF8ToUTF16(kMimeTypeRTF));
    425   if (IsFormatAvailable(GetBitmapFormatType(), type))
    426     types->push_back(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   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     UTF8ToUTF16(reinterpret_cast<const char*>(raw_data), data_length, markup);
    512   }
    513 
    514   // If there is a terminating NULL, drop it.
    515   if (!markup->empty() && markup->at(markup->length() - 1) == '\0')
    516     markup->resize(markup->length() - 1);
    517 
    518   *fragment_start = 0;
    519   DCHECK(markup->length() <= kuint32max);
    520   *fragment_end = static_cast<uint32>(markup->length());
    521 
    522   gtk_selection_data_free(data);
    523 }
    524 
    525 void Clipboard::ReadRTF(ClipboardType type, std::string* result) const {
    526   DCHECK(CalledOnValidThread());
    527   ReadData(GetRtfFormatType(), result);
    528 }
    529 
    530 SkBitmap Clipboard::ReadImage(ClipboardType type) const {
    531   DCHECK(CalledOnValidThread());
    532   ScopedGObject<GdkPixbuf>::Type pixbuf(
    533       gtk_clipboard_wait_for_image(clipboard_));
    534   if (!pixbuf.get())
    535     return SkBitmap();
    536 
    537   gfx::Canvas canvas(gfx::Size(gdk_pixbuf_get_width(pixbuf.get()),
    538                                gdk_pixbuf_get_height(pixbuf.get())),
    539                      1.0f, false);
    540   {
    541     skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
    542     cairo_t* context = scoped_platform_paint.GetPlatformSurface();
    543     gdk_cairo_set_source_pixbuf(context, pixbuf.get(), 0.0, 0.0);
    544     cairo_paint(context);
    545   }
    546   return canvas.ExtractImageRep().sk_bitmap();
    547 }
    548 
    549 void Clipboard::ReadCustomData(ClipboardType clipboard_type,
    550                                const base::string16& type,
    551                                base::string16* result) const {
    552   DCHECK(CalledOnValidThread());
    553   GtkClipboard* clipboard = LookupBackingClipboard(clipboard_type);
    554   if (!clipboard)
    555     return;
    556 
    557   GtkSelectionData* data = gtk_clipboard_wait_for_contents(
    558       clipboard, GetWebCustomDataFormatType().ToGdkAtom());
    559   if (!data)
    560     return;
    561   ReadCustomDataForType(gtk_selection_data_get_data(data),
    562                         gtk_selection_data_get_length(data),
    563                         type, result);
    564   gtk_selection_data_free(data);
    565 }
    566 
    567 void Clipboard::ReadBookmark(base::string16* title, std::string* url) const {
    568   // TODO(estade): implement this.
    569   NOTIMPLEMENTED();
    570 }
    571 
    572 void Clipboard::ReadData(const FormatType& format, std::string* result) const {
    573   DCHECK(CalledOnValidThread());
    574   result->clear();
    575   GtkSelectionData* data =
    576       gtk_clipboard_wait_for_contents(clipboard_, format.ToGdkAtom());
    577   if (!data)
    578     return;
    579   result->assign(reinterpret_cast<const char*>(
    580                      gtk_selection_data_get_data(data)),
    581                  gtk_selection_data_get_length(data));
    582   gtk_selection_data_free(data);
    583 }
    584 
    585 uint64 Clipboard::GetSequenceNumber(ClipboardType type) {
    586   DCHECK(CalledOnValidThread());
    587   if (type == CLIPBOARD_TYPE_COPY_PASTE)
    588     return SelectionChangeObserver::GetInstance()->clipboard_sequence_number();
    589   else
    590     return SelectionChangeObserver::GetInstance()->primary_sequence_number();
    591 }
    592 
    593 //static
    594 Clipboard::FormatType Clipboard::GetFormatType(
    595     const std::string& format_string) {
    596   return FormatType::Deserialize(format_string);
    597 }
    598 
    599 // static
    600 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
    601   CR_DEFINE_STATIC_LOCAL(
    602       FormatType, type, (GDK_TARGET_STRING));
    603   return type;
    604 }
    605 
    606 // static
    607 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
    608   return GetPlainTextFormatType();
    609 }
    610 
    611 // static
    612 const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
    613   return GetPlainTextFormatType();
    614 }
    615 
    616 // static
    617 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
    618   return GetPlainTextWFormatType();
    619 }
    620 
    621 // static
    622 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
    623   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML));
    624   return type;
    625 }
    626 
    627 // static
    628 const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
    629   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF));
    630   return type;
    631 }
    632 
    633 // static
    634 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
    635   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeBitmap));
    636   return type;
    637 }
    638 
    639 // static
    640 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
    641   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste));
    642   return type;
    643 }
    644 
    645 // static
    646 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
    647   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData));
    648   return type;
    649 }
    650 
    651 // static
    652 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
    653   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData));
    654   return type;
    655 }
    656 
    657 void Clipboard::InsertMapping(const char* key,
    658                               char* data,
    659                               size_t data_len) {
    660   DCHECK(clipboard_data_->find(key) == clipboard_data_->end());
    661   (*clipboard_data_)[key] = std::make_pair(data, data_len);
    662 }
    663 
    664 GtkClipboard* Clipboard::LookupBackingClipboard(ClipboardType type) const {
    665   switch (type) {
    666     case CLIPBOARD_TYPE_COPY_PASTE:
    667       return clipboard_;
    668     case CLIPBOARD_TYPE_SELECTION:
    669       return primary_selection_;
    670     default:
    671       NOTREACHED();
    672       return NULL;
    673   }
    674 }
    675 
    676 }  // namespace ui
    677