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 <X11/extensions/Xfixes.h>
      8 #include <X11/Xatom.h>
      9 #include <list>
     10 #include <set>
     11 
     12 #include "base/basictypes.h"
     13 #include "base/files/file_path.h"
     14 #include "base/logging.h"
     15 #include "base/memory/scoped_ptr.h"
     16 #include "base/memory/singleton.h"
     17 #include "base/message_loop/message_pump_aurax11.h"
     18 #include "base/message_loop/message_pump_observer.h"
     19 #include "base/stl_util.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "third_party/skia/include/core/SkBitmap.h"
     22 #include "ui/base/clipboard/custom_data_helper.h"
     23 #include "ui/base/x/selection_owner.h"
     24 #include "ui/base/x/selection_requestor.h"
     25 #include "ui/base/x/selection_utils.h"
     26 #include "ui/base/x/x11_atom_cache.h"
     27 #include "ui/base/x/x11_util.h"
     28 
     29 #include "ui/gfx/size.h"
     30 
     31 namespace ui {
     32 
     33 namespace {
     34 
     35 const char kClipboard[] = "CLIPBOARD";
     36 const char kMimeTypeBitmap[] = "image/bmp";
     37 const char kMimeTypeFilename[] = "chromium/filename";
     38 const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data";
     39 const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste";
     40 const char kTargets[] = "TARGETS";
     41 
     42 const char* kAtomsToCache[] = {
     43   kClipboard,
     44   kMimeTypeBitmap,
     45   kMimeTypeFilename,
     46   kMimeTypeMozillaURL,
     47   kMimeTypeWebkitSmartPaste,
     48   kString,
     49   kTargets,
     50   kText,
     51   kUtf8String,
     52   NULL
     53 };
     54 
     55 ///////////////////////////////////////////////////////////////////////////////
     56 
     57 // Uses the XFixes API to provide sequence numbers for GetSequenceNumber().
     58 class SelectionChangeObserver : public base::MessagePumpObserver {
     59  public:
     60   static SelectionChangeObserver* GetInstance();
     61 
     62   uint64 clipboard_sequence_number() const {
     63     return clipboard_sequence_number_;
     64   }
     65   uint64 primary_sequence_number() const { return primary_sequence_number_; }
     66 
     67  private:
     68   friend struct DefaultSingletonTraits<SelectionChangeObserver>;
     69 
     70   SelectionChangeObserver();
     71   virtual ~SelectionChangeObserver();
     72 
     73   // Overridden from base::MessagePumpObserver:
     74   virtual base::EventStatus WillProcessEvent(
     75       const base::NativeEvent& event) OVERRIDE;
     76   virtual void DidProcessEvent(
     77       const base::NativeEvent& event) OVERRIDE {}
     78 
     79   int event_base_;
     80   Atom clipboard_atom_;
     81   uint64 clipboard_sequence_number_;
     82   uint64 primary_sequence_number_;
     83 
     84   DISALLOW_COPY_AND_ASSIGN(SelectionChangeObserver);
     85 };
     86 
     87 SelectionChangeObserver::SelectionChangeObserver()
     88     : event_base_(-1),
     89       clipboard_atom_(None),
     90       clipboard_sequence_number_(0),
     91       primary_sequence_number_(0) {
     92   int ignored;
     93   if (XFixesQueryExtension(GetXDisplay(), &event_base_, &ignored)) {
     94     clipboard_atom_ = XInternAtom(GetXDisplay(), kClipboard, false);
     95     XFixesSelectSelectionInput(GetXDisplay(), GetX11RootWindow(),
     96                                clipboard_atom_,
     97                                XFixesSetSelectionOwnerNotifyMask |
     98                                XFixesSelectionWindowDestroyNotifyMask |
     99                                XFixesSelectionClientCloseNotifyMask);
    100     // This seems to be semi-optional. For some reason, registering for any
    101     // selection notify events seems to subscribe us to events for both the
    102     // primary and the clipboard buffers. Register anyway just to be safe.
    103     XFixesSelectSelectionInput(GetXDisplay(), GetX11RootWindow(),
    104                                XA_PRIMARY,
    105                                XFixesSetSelectionOwnerNotifyMask |
    106                                XFixesSelectionWindowDestroyNotifyMask |
    107                                XFixesSelectionClientCloseNotifyMask);
    108 
    109     base::MessagePumpAuraX11::Current()->AddObserver(this);
    110   }
    111 }
    112 
    113 SelectionChangeObserver::~SelectionChangeObserver() {
    114   // We are a singleton; we will outlive our message pump.
    115 }
    116 
    117 SelectionChangeObserver* SelectionChangeObserver::GetInstance() {
    118   return Singleton<SelectionChangeObserver>::get();
    119 }
    120 
    121 base::EventStatus SelectionChangeObserver::WillProcessEvent(
    122     const base::NativeEvent& event) {
    123   if (event->type == event_base_ + XFixesSelectionNotify) {
    124     XFixesSelectionNotifyEvent* ev =
    125         reinterpret_cast<XFixesSelectionNotifyEvent*>(event);
    126     if (ev->selection == clipboard_atom_) {
    127       clipboard_sequence_number_++;
    128     } else if (ev->selection == XA_PRIMARY) {
    129       primary_sequence_number_++;
    130     } else {
    131       DLOG(ERROR) << "Unexpected selection atom: " << ev->selection;
    132     }
    133   }
    134   return base::EVENT_CONTINUE;
    135 }
    136 
    137 ///////////////////////////////////////////////////////////////////////////////
    138 
    139 // Represents a list of possible return types. Copy constructable.
    140 class TargetList {
    141  public:
    142   typedef std::vector< ::Atom> AtomVector;
    143 
    144   TargetList(const AtomVector& target_list, X11AtomCache* atom_cache);
    145 
    146   const AtomVector& target_list() { return target_list_; }
    147 
    148   bool ContainsText() const;
    149   bool ContainsFormat(const Clipboard::FormatType& format_type) const;
    150   bool ContainsAtom(::Atom atom) const;
    151 
    152  private:
    153   AtomVector target_list_;
    154   X11AtomCache* atom_cache_;
    155 };
    156 
    157 TargetList::TargetList(const AtomVector& target_list,
    158                        X11AtomCache* atom_cache)
    159     : target_list_(target_list),
    160       atom_cache_(atom_cache) {
    161 }
    162 
    163 bool TargetList::ContainsText() const {
    164   std::vector< ::Atom> atoms = GetTextAtomsFrom(atom_cache_);
    165   for (std::vector< ::Atom>::const_iterator it = atoms.begin();
    166        it != atoms.end(); ++it) {
    167     if (ContainsAtom(*it))
    168       return true;
    169   }
    170 
    171   return false;
    172 }
    173 
    174 bool TargetList::ContainsFormat(
    175     const Clipboard::FormatType& format_type) const {
    176   ::Atom atom = atom_cache_->GetAtom(format_type.ToString().c_str());
    177   return ContainsAtom(atom);
    178 }
    179 
    180 bool TargetList::ContainsAtom(::Atom atom) const {
    181   return find(target_list_.begin(), target_list_.end(), atom)
    182       != target_list_.end();
    183 }
    184 
    185 }  // namespace
    186 
    187 ///////////////////////////////////////////////////////////////////////////////
    188 
    189 // I would love for the FormatType to really be a wrapper around an X11 ::Atom,
    190 // but there are a few problems. Chromeos unit tests spawn a new X11 server for
    191 // each test, so Atom numeric values don't persist across tests. We could still
    192 // maybe deal with that if we didn't have static accessor methods everywhere.
    193 
    194 Clipboard::FormatType::FormatType() {
    195 }
    196 
    197 Clipboard::FormatType::FormatType(const std::string& native_format)
    198     : data_(native_format) {
    199 }
    200 
    201 Clipboard::FormatType::~FormatType() {
    202 }
    203 
    204 std::string Clipboard::FormatType::Serialize() const {
    205   return data_;
    206 }
    207 
    208 // static
    209 Clipboard::FormatType Clipboard::FormatType::Deserialize(
    210     const std::string& serialization) {
    211   return FormatType(serialization);
    212 }
    213 
    214 bool Clipboard::FormatType::operator<(const FormatType& other) const {
    215   return data_ < other.data_;
    216 }
    217 
    218 bool Clipboard::FormatType::Equals(const FormatType& other) const {
    219   return data_ == other.data_;
    220 }
    221 
    222 ///////////////////////////////////////////////////////////////////////////////
    223 // Clipboard::AuraX11Details
    224 
    225 // Private implementation of our X11 integration. Keeps X11 headers out of the
    226 // majority of chrome, which break badly.
    227 class Clipboard::AuraX11Details : public base::MessagePumpDispatcher {
    228  public:
    229   AuraX11Details();
    230   virtual ~AuraX11Details();
    231 
    232   X11AtomCache* atom_cache() { return &atom_cache_; }
    233 
    234   // Returns the X11 type that we pass to various XSelection functions for the
    235   // given buffer.
    236   ::Atom LookupSelectionForBuffer(Buffer buffer) const;
    237 
    238   // Returns the object which is responsible for communication on |buffer|.
    239   SelectionRequestor* GetSelectionRequestorForBuffer(Buffer buffer);
    240 
    241   // Finds the SelectionFormatMap for the incoming selection atom.
    242   const SelectionFormatMap& LookupStorageForAtom(::Atom atom);
    243 
    244   // As we need to collect all the data types before we tell X11 that we own a
    245   // particular selection, we create a temporary clipboard mapping that
    246   // InsertMapping writes to. Then we commit it in TakeOwnershipOfSelection,
    247   // where we save it in one of the clipboard data slots.
    248   void CreateNewClipboardData();
    249 
    250   // Inserts a mapping into clipboard_data_.
    251   void InsertMapping(const std::string& key,
    252                      const scoped_refptr<base::RefCountedMemory>& memory);
    253 
    254   // Moves the temporary |clipboard_data_| to the long term data storage for
    255   // |buffer|.
    256   void TakeOwnershipOfSelection(Buffer buffer);
    257 
    258   // Returns the first of |types| offered by the current selection holder in
    259   // |data_out|, or returns NULL if none of those types are available.
    260   //
    261   // If the selection holder is us, this call is synchronous and we pull
    262   // the data out of |clipboard_selection_| or |primary_selection_|. If the
    263   // selection holder is some other window, we spin up a nested message loop
    264   // and do the asynchronous dance with whatever application is holding the
    265   // selection.
    266   ui::SelectionData RequestAndWaitForTypes(Buffer buffer,
    267                                            const std::vector< ::Atom>& types);
    268 
    269   // Retrieves the list of possible data types the current clipboard owner has.
    270   //
    271   // If the selection holder is us, this is synchronous, otherwise this runs a
    272   // blocking message loop.
    273   TargetList WaitAndGetTargetsList(Buffer buffer);
    274 
    275   // Returns a list of all text atoms that we handle.
    276   std::vector< ::Atom> GetTextAtoms() const;
    277 
    278   // Returns a vector with a |format| converted to an X11 atom.
    279   std::vector< ::Atom> GetAtomsForFormat(const Clipboard::FormatType& format);
    280 
    281   // Clears a certain data buffer.
    282   void Clear(Buffer buffer);
    283 
    284  private:
    285   // Overridden from base::MessagePumpDispatcher:
    286   virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
    287 
    288   // Our X11 state.
    289   Display* x_display_;
    290   ::Window x_root_window_;
    291 
    292   // Input-only window used as a selection owner.
    293   ::Window x_window_;
    294 
    295   X11AtomCache atom_cache_;
    296 
    297   // Objects which request and receive selection data.
    298   SelectionRequestor clipboard_requestor_;
    299   SelectionRequestor primary_requestor_;
    300 
    301   // Temporary target map that we write to during DispatchObects.
    302   SelectionFormatMap clipboard_data_;
    303 
    304   // Objects which offer selection data to other windows.
    305   SelectionOwner clipboard_owner_;
    306   SelectionOwner primary_owner_;
    307 
    308   DISALLOW_COPY_AND_ASSIGN(AuraX11Details);
    309 };
    310 
    311 Clipboard::AuraX11Details::AuraX11Details()
    312     : x_display_(GetXDisplay()),
    313       x_root_window_(DefaultRootWindow(x_display_)),
    314       x_window_(XCreateWindow(
    315           x_display_, x_root_window_,
    316           -100, -100, 10, 10,  // x, y, width, height
    317           0,                   // border width
    318           CopyFromParent,      // depth
    319           InputOnly,
    320           CopyFromParent,      // visual
    321           0,
    322           NULL)),
    323       atom_cache_(x_display_, kAtomsToCache),
    324       clipboard_requestor_(x_display_, x_window_,
    325                            atom_cache_.GetAtom(kClipboard)),
    326       primary_requestor_(x_display_, x_window_, XA_PRIMARY),
    327       clipboard_owner_(x_display_, x_window_, atom_cache_.GetAtom(kClipboard)),
    328       primary_owner_(x_display_, x_window_, XA_PRIMARY) {
    329   // We don't know all possible MIME types at compile time.
    330   atom_cache_.allow_uncached_atoms();
    331 
    332   XStoreName(x_display_, x_window_, "Chromium clipboard");
    333   XSelectInput(x_display_, x_window_, PropertyChangeMask);
    334 
    335   base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(this, x_window_);
    336 }
    337 
    338 Clipboard::AuraX11Details::~AuraX11Details() {
    339   base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(x_window_);
    340 
    341   XDestroyWindow(x_display_, x_window_);
    342 }
    343 
    344 ::Atom Clipboard::AuraX11Details::LookupSelectionForBuffer(
    345     Buffer buffer) const {
    346   if (buffer == BUFFER_STANDARD)
    347     return atom_cache_.GetAtom(kClipboard);
    348 
    349   return XA_PRIMARY;
    350 }
    351 
    352 const SelectionFormatMap& Clipboard::AuraX11Details::LookupStorageForAtom(
    353     ::Atom atom) {
    354   if (atom == XA_PRIMARY)
    355     return primary_owner_.selection_format_map();
    356 
    357   DCHECK_EQ(atom_cache_.GetAtom(kClipboard), atom);
    358   return clipboard_owner_.selection_format_map();
    359 }
    360 
    361 ui::SelectionRequestor*
    362 Clipboard::AuraX11Details::GetSelectionRequestorForBuffer(Buffer buffer) {
    363   if (buffer == BUFFER_STANDARD)
    364     return &clipboard_requestor_;
    365   else
    366     return &primary_requestor_;
    367 }
    368 
    369 void Clipboard::AuraX11Details::CreateNewClipboardData() {
    370   clipboard_data_ = SelectionFormatMap();
    371 }
    372 
    373 void Clipboard::AuraX11Details::InsertMapping(
    374     const std::string& key,
    375     const scoped_refptr<base::RefCountedMemory>& memory) {
    376   ::Atom atom_key = atom_cache_.GetAtom(key.c_str());
    377   clipboard_data_.Insert(atom_key, memory);
    378 }
    379 
    380 void Clipboard::AuraX11Details::TakeOwnershipOfSelection(Buffer buffer) {
    381   if (buffer == BUFFER_STANDARD)
    382     return clipboard_owner_.TakeOwnershipOfSelection(clipboard_data_);
    383   else
    384     return primary_owner_.TakeOwnershipOfSelection(clipboard_data_);
    385 }
    386 
    387 SelectionData Clipboard::AuraX11Details::RequestAndWaitForTypes(
    388     Buffer buffer,
    389     const std::vector< ::Atom>& types) {
    390   ::Atom selection_name = LookupSelectionForBuffer(buffer);
    391   if (XGetSelectionOwner(x_display_, selection_name) == x_window_) {
    392     // We can local fastpath instead of playing the nested message loop game
    393     // with the X server.
    394     const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name);
    395 
    396     for (std::vector< ::Atom>::const_iterator it = types.begin();
    397          it != types.end(); ++it) {
    398       SelectionFormatMap::const_iterator format_map_it = format_map.find(*it);
    399       if (format_map_it != format_map.end())
    400         return SelectionData(format_map_it->first, format_map_it->second);
    401     }
    402   } else {
    403     TargetList targets = WaitAndGetTargetsList(buffer);
    404     SelectionRequestor* receiver = GetSelectionRequestorForBuffer(buffer);
    405 
    406     std::vector< ::Atom> intersection;
    407     ui::GetAtomIntersection(targets.target_list(), types, &intersection);
    408     return receiver->RequestAndWaitForTypes(intersection);
    409   }
    410 
    411   return SelectionData();
    412 }
    413 
    414 TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList(
    415     Buffer buffer) {
    416   ::Atom selection_name = LookupSelectionForBuffer(buffer);
    417   std::vector< ::Atom> out;
    418   if (XGetSelectionOwner(x_display_, selection_name) == x_window_) {
    419     // We can local fastpath and return the list of local targets.
    420     const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name);
    421 
    422     for (SelectionFormatMap::const_iterator it = format_map.begin();
    423          it != format_map.end(); ++it) {
    424       out.push_back(it->first);
    425     }
    426   } else {
    427     scoped_refptr<base::RefCountedMemory> data;
    428     size_t out_data_items = 0;
    429     ::Atom out_type = None;
    430 
    431     SelectionRequestor* receiver = GetSelectionRequestorForBuffer(buffer);
    432     if (receiver->PerformBlockingConvertSelection(atom_cache_.GetAtom(kTargets),
    433                                                   &data,
    434                                                   NULL,
    435                                                   &out_data_items,
    436                                                   &out_type)) {
    437       const ::Atom* atom_array = reinterpret_cast<const ::Atom*>(data->front());
    438       for (size_t i = 0; i < out_data_items; ++i)
    439         out.push_back(atom_array[i]);
    440     } else {
    441       // There was no target list. Most Java apps doesn't offer a TARGETS list,
    442       // even though they AWT to. They will offer individual text types if you
    443       // ask. If this is the case we attempt to make sense of the contents as
    444       // text. This is pretty unfortunate since it means we have to actually
    445       // copy the data to see if it is available, but at least this path
    446       // shouldn't be hit for conforming programs.
    447       std::vector< ::Atom> types = GetTextAtoms();
    448       for (std::vector< ::Atom>::const_iterator it = types.begin();
    449            it != types.end(); ++it) {
    450         ::Atom type = None;
    451         if (receiver->PerformBlockingConvertSelection(*it,
    452                                                       NULL,
    453                                                       NULL,
    454                                                       NULL,
    455                                                       &type) &&
    456             type == *it) {
    457           out.push_back(*it);
    458         }
    459       }
    460     }
    461   }
    462 
    463   return TargetList(out, &atom_cache_);
    464 }
    465 
    466 std::vector< ::Atom> Clipboard::AuraX11Details::GetTextAtoms() const {
    467   return GetTextAtomsFrom(&atom_cache_);
    468 }
    469 
    470 std::vector< ::Atom> Clipboard::AuraX11Details::GetAtomsForFormat(
    471     const Clipboard::FormatType& format) {
    472   std::vector< ::Atom> atoms;
    473   atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
    474   return atoms;
    475 }
    476 
    477 void Clipboard::AuraX11Details::Clear(Buffer buffer) {
    478   if (buffer == BUFFER_STANDARD)
    479     return clipboard_owner_.Clear();
    480   else
    481     return primary_owner_.Clear();
    482 }
    483 
    484 bool Clipboard::AuraX11Details::Dispatch(const base::NativeEvent& event) {
    485   XEvent* xev = event;
    486 
    487   switch (xev->type) {
    488     case SelectionRequest: {
    489       if (xev->xselectionrequest.selection == XA_PRIMARY)
    490         primary_owner_.OnSelectionRequest(xev->xselectionrequest);
    491       else
    492         clipboard_owner_.OnSelectionRequest(xev->xselectionrequest);
    493       break;
    494     }
    495     case SelectionNotify: {
    496       if (xev->xselection.selection == XA_PRIMARY)
    497         primary_requestor_.OnSelectionNotify(xev->xselection);
    498       else
    499         clipboard_requestor_.OnSelectionNotify(xev->xselection);
    500       break;
    501     }
    502     case SelectionClear: {
    503       if (xev->xselectionclear.selection == XA_PRIMARY)
    504         primary_owner_.OnSelectionClear(xev->xselectionclear);
    505       else
    506         clipboard_owner_.OnSelectionClear(xev->xselectionclear);
    507       break;
    508     }
    509     default:
    510       break;
    511   }
    512 
    513   return true;
    514 }
    515 
    516 ///////////////////////////////////////////////////////////////////////////////
    517 // Clipboard
    518 
    519 Clipboard::Clipboard()
    520     : aurax11_details_(new AuraX11Details) {
    521   DCHECK(CalledOnValidThread());
    522 }
    523 
    524 Clipboard::~Clipboard() {
    525   DCHECK(CalledOnValidThread());
    526 
    527   // TODO(erg): We need to do whatever the equivalent of
    528   // gtk_clipboard_store(clipboard_) is here. When we shut down, we want the
    529   // current selection to live on.
    530 }
    531 
    532 void Clipboard::WriteObjects(Buffer buffer, const ObjectMap& objects) {
    533   DCHECK(CalledOnValidThread());
    534   DCHECK(IsValidBuffer(buffer));
    535 
    536   aurax11_details_->CreateNewClipboardData();
    537   for (ObjectMap::const_iterator iter = objects.begin();
    538        iter != objects.end(); ++iter) {
    539     DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
    540   }
    541   aurax11_details_->TakeOwnershipOfSelection(buffer);
    542 
    543   if (buffer == BUFFER_STANDARD) {
    544     ObjectMap::const_iterator text_iter = objects.find(CBF_TEXT);
    545     if (text_iter != objects.end()) {
    546       aurax11_details_->CreateNewClipboardData();
    547       const ObjectMapParam& char_vector = text_iter->second[0];
    548       WriteText(&char_vector.front(), char_vector.size());
    549       aurax11_details_->TakeOwnershipOfSelection(BUFFER_SELECTION);
    550     }
    551   }
    552 }
    553 
    554 bool Clipboard::IsFormatAvailable(const FormatType& format,
    555                                   Buffer buffer) const {
    556   DCHECK(CalledOnValidThread());
    557   DCHECK(IsValidBuffer(buffer));
    558 
    559   TargetList target_list =
    560       aurax11_details_->WaitAndGetTargetsList(buffer);
    561   return target_list.ContainsFormat(format);
    562 }
    563 
    564 void Clipboard::Clear(Buffer buffer) {
    565   DCHECK(CalledOnValidThread());
    566   DCHECK(IsValidBuffer(buffer));
    567   aurax11_details_->Clear(buffer);
    568 }
    569 
    570 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types,
    571     bool* contains_filenames) const {
    572   DCHECK(CalledOnValidThread());
    573   if (!types || !contains_filenames) {
    574     NOTREACHED();
    575     return;
    576   }
    577 
    578   TargetList target_list =
    579       aurax11_details_->WaitAndGetTargetsList(buffer);
    580 
    581   types->clear();
    582 
    583   if (target_list.ContainsText())
    584     types->push_back(UTF8ToUTF16(kMimeTypeText));
    585   if (target_list.ContainsFormat(GetHtmlFormatType()))
    586     types->push_back(UTF8ToUTF16(kMimeTypeHTML));
    587   if (target_list.ContainsFormat(GetRtfFormatType()))
    588     types->push_back(UTF8ToUTF16(kMimeTypeRTF));
    589   if (target_list.ContainsFormat(GetBitmapFormatType()))
    590     types->push_back(UTF8ToUTF16(kMimeTypePNG));
    591   *contains_filenames = false;
    592 
    593   SelectionData data(aurax11_details_->RequestAndWaitForTypes(
    594       buffer,
    595       aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType())));
    596   if (data.IsValid())
    597     ReadCustomDataTypes(data.GetData(), data.GetSize(), types);
    598 }
    599 
    600 void Clipboard::ReadText(Buffer buffer, string16* result) const {
    601   DCHECK(CalledOnValidThread());
    602 
    603   SelectionData data(aurax11_details_->RequestAndWaitForTypes(
    604       buffer, aurax11_details_->GetTextAtoms()));
    605   if (data.IsValid()) {
    606     std::string text = data.GetText();
    607     *result = UTF8ToUTF16(text);
    608   }
    609 }
    610 
    611 void Clipboard::ReadAsciiText(Buffer buffer, std::string* result) const {
    612   DCHECK(CalledOnValidThread());
    613 
    614   SelectionData data(aurax11_details_->RequestAndWaitForTypes(
    615       buffer, aurax11_details_->GetTextAtoms()));
    616   if (data.IsValid())
    617     result->assign(data.GetText());
    618 }
    619 
    620 // TODO(estade): handle different charsets.
    621 // TODO(port): set *src_url.
    622 void Clipboard::ReadHTML(Buffer buffer,
    623                          string16* markup,
    624                          std::string* src_url,
    625                          uint32* fragment_start,
    626                          uint32* fragment_end) const {
    627   DCHECK(CalledOnValidThread());
    628   markup->clear();
    629   if (src_url)
    630     src_url->clear();
    631   *fragment_start = 0;
    632   *fragment_end = 0;
    633 
    634   SelectionData data(aurax11_details_->RequestAndWaitForTypes(
    635       buffer, aurax11_details_->GetAtomsForFormat(GetHtmlFormatType())));
    636   if (data.IsValid()) {
    637     *markup = data.GetHtml();
    638 
    639     *fragment_start = 0;
    640     DCHECK(markup->length() <= kuint32max);
    641     *fragment_end = static_cast<uint32>(markup->length());
    642   }
    643 }
    644 
    645 void Clipboard::ReadRTF(Buffer buffer, std::string* result) const {
    646   DCHECK(CalledOnValidThread());
    647 
    648   SelectionData data(aurax11_details_->RequestAndWaitForTypes(
    649       buffer, aurax11_details_->GetAtomsForFormat(GetRtfFormatType())));
    650   if (data.IsValid())
    651     data.AssignTo(result);
    652 }
    653 
    654 SkBitmap Clipboard::ReadImage(Buffer buffer) const {
    655   DCHECK(CalledOnValidThread());
    656   NOTIMPLEMENTED();
    657   return SkBitmap();
    658 }
    659 
    660 void Clipboard::ReadCustomData(Buffer buffer,
    661                                const string16& type,
    662                                string16* result) const {
    663   DCHECK(CalledOnValidThread());
    664 
    665   SelectionData data(aurax11_details_->RequestAndWaitForTypes(
    666       buffer,
    667       aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType())));
    668   if (data.IsValid())
    669     ReadCustomDataForType(data.GetData(), data.GetSize(), type, result);
    670 }
    671 
    672 void Clipboard::ReadBookmark(string16* title, std::string* url) const {
    673   DCHECK(CalledOnValidThread());
    674   // TODO(erg): This was left NOTIMPLEMENTED() in the gtk port too.
    675   NOTIMPLEMENTED();
    676 }
    677 
    678 void Clipboard::ReadData(const FormatType& format, std::string* result) const {
    679   DCHECK(CalledOnValidThread());
    680 
    681   SelectionData data(aurax11_details_->RequestAndWaitForTypes(
    682       BUFFER_STANDARD, aurax11_details_->GetAtomsForFormat(format)));
    683   if (data.IsValid())
    684     data.AssignTo(result);
    685 }
    686 
    687 uint64 Clipboard::GetSequenceNumber(Buffer buffer) {
    688   DCHECK(CalledOnValidThread());
    689   if (buffer == BUFFER_STANDARD)
    690     return SelectionChangeObserver::GetInstance()->clipboard_sequence_number();
    691   else
    692     return SelectionChangeObserver::GetInstance()->primary_sequence_number();
    693 }
    694 
    695 void Clipboard::WriteText(const char* text_data, size_t text_len) {
    696   std::string str(text_data, text_len);
    697   scoped_refptr<base::RefCountedMemory> mem(
    698       base::RefCountedString::TakeString(&str));
    699 
    700   aurax11_details_->InsertMapping(kMimeTypeText, mem);
    701   aurax11_details_->InsertMapping(kText, mem);
    702   aurax11_details_->InsertMapping(kString, mem);
    703   aurax11_details_->InsertMapping(kUtf8String, mem);
    704 }
    705 
    706 void Clipboard::WriteHTML(const char* markup_data,
    707                           size_t markup_len,
    708                           const char* url_data,
    709                           size_t url_len) {
    710   // TODO(estade): We need to expand relative links with |url_data|.
    711   static const char* html_prefix = "<meta http-equiv=\"content-type\" "
    712                                    "content=\"text/html; charset=utf-8\">";
    713   std::string data = html_prefix;
    714   data += std::string(markup_data, markup_len);
    715   // Some programs expect NULL-terminated data. See http://crbug.com/42624
    716   data += '\0';
    717 
    718   scoped_refptr<base::RefCountedMemory> mem(
    719       base::RefCountedString::TakeString(&data));
    720   aurax11_details_->InsertMapping(kMimeTypeHTML, mem);
    721 }
    722 
    723 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) {
    724   WriteData(GetRtfFormatType(), rtf_data, data_len);
    725 }
    726 
    727 void Clipboard::WriteBookmark(const char* title_data,
    728                               size_t title_len,
    729                               const char* url_data,
    730                               size_t url_len) {
    731   // Write as a mozilla url (UTF16: URL, newline, title).
    732   string16 url = UTF8ToUTF16(std::string(url_data, url_len) + "\n");
    733   string16 title = UTF8ToUTF16(std::string(title_data, title_len));
    734 
    735   std::vector<unsigned char> data;
    736   ui::AddString16ToVector(url, &data);
    737   ui::AddString16ToVector(title, &data);
    738   scoped_refptr<base::RefCountedMemory> mem(
    739       base::RefCountedBytes::TakeVector(&data));
    740 
    741   aurax11_details_->InsertMapping(kMimeTypeMozillaURL, mem);
    742 }
    743 
    744 // Write an extra flavor that signifies WebKit was the last to modify the
    745 // pasteboard. This flavor has no data.
    746 void Clipboard::WriteWebSmartPaste() {
    747   aurax11_details_->InsertMapping(kMimeTypeWebkitSmartPaste,
    748                                   scoped_refptr<base::RefCountedMemory>());
    749 }
    750 
    751 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
    752   // TODO(erg): I'm not sure if we should be writting BMP data here or
    753   // not. It's what the GTK port does, but I'm not sure it's the right thing to
    754   // do.
    755   NOTIMPLEMENTED();
    756 }
    757 
    758 void Clipboard::WriteData(const FormatType& format,
    759                           const char* data_data,
    760                           size_t data_len) {
    761   // We assume that certain mapping types are only written by trusted code.
    762   // Therefore we must upkeep their integrity.
    763   if (format.Equals(GetBitmapFormatType()))
    764     return;
    765 
    766   std::vector<unsigned char> bytes(data_data, data_data + data_len);
    767   scoped_refptr<base::RefCountedMemory> mem(
    768       base::RefCountedBytes::TakeVector(&bytes));
    769   aurax11_details_->InsertMapping(format.ToString(), mem);
    770 }
    771 
    772 // static
    773 Clipboard::FormatType Clipboard::GetFormatType(
    774     const std::string& format_string) {
    775   return FormatType::Deserialize(format_string);
    776 }
    777 
    778 // static
    779 const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
    780   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList));
    781   return type;
    782 }
    783 
    784 // static
    785 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
    786   return GetUrlFormatType();
    787 }
    788 
    789 // static
    790 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
    791   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText));
    792   return type;
    793 }
    794 
    795 // static
    796 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
    797   return GetPlainTextFormatType();
    798 }
    799 
    800 // static
    801 const Clipboard::FormatType& Clipboard::GetFilenameFormatType() {
    802   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeFilename));
    803   return type;
    804 }
    805 
    806 // static
    807 const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() {
    808   return Clipboard::GetFilenameFormatType();
    809 }
    810 
    811 // static
    812 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
    813   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML));
    814   return type;
    815 }
    816 
    817 // static
    818 const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
    819   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF));
    820   return type;
    821 }
    822 
    823 // static
    824 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
    825   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeBitmap));
    826   return type;
    827 }
    828 
    829 // static
    830 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
    831   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste));
    832   return type;
    833 }
    834 
    835 // static
    836 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
    837   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData));
    838   return type;
    839 }
    840 
    841 // static
    842 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
    843   CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData));
    844   return type;
    845 }
    846 
    847 }  // namespace ui
    848