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