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/message_loop/message_pump_observer.h"
19#include "base/message_loop/message_pump_x11.h"
20#include "base/stl_util.h"
21#include "base/strings/utf_string_conversions.h"
22#include "third_party/skia/include/core/SkBitmap.h"
23#include "ui/base/clipboard/custom_data_helper.h"
24#include "ui/base/x/selection_owner.h"
25#include "ui/base/x/selection_requestor.h"
26#include "ui/base/x/selection_utils.h"
27#include "ui/base/x/x11_util.h"
28#include "ui/gfx/codec/png_codec.h"
29#include "ui/gfx/size.h"
30#include "ui/gfx/x/x11_atom_cache.h"
31
32namespace ui {
33
34namespace {
35
36const char kClipboard[] = "CLIPBOARD";
37const char kMimeTypeFilename[] = "chromium/filename";
38const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data";
39const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste";
40const char kTargets[] = "TARGETS";
41
42const char* kAtomsToCache[] = {
43  kClipboard,
44  Clipboard::kMimeTypePNG,
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().
58class 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
87SelectionChangeObserver::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(gfx::GetXDisplay(), &event_base_, &ignored)) {
94    clipboard_atom_ = XInternAtom(gfx::GetXDisplay(), kClipboard, false);
95    XFixesSelectSelectionInput(gfx::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(gfx::GetXDisplay(), GetX11RootWindow(),
104                               XA_PRIMARY,
105                               XFixesSetSelectionOwnerNotifyMask |
106                               XFixesSelectionWindowDestroyNotifyMask |
107                               XFixesSelectionClientCloseNotifyMask);
108
109    base::MessagePumpX11::Current()->AddObserver(this);
110  }
111}
112
113SelectionChangeObserver::~SelectionChangeObserver() {
114  // We are a singleton; we will outlive our message pump.
115}
116
117SelectionChangeObserver* SelectionChangeObserver::GetInstance() {
118  return Singleton<SelectionChangeObserver>::get();
119}
120
121base::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.
140class 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
157TargetList::TargetList(const AtomVector& target_list,
158                       X11AtomCache* atom_cache)
159    : target_list_(target_list),
160      atom_cache_(atom_cache) {
161}
162
163bool 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
174bool 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
180bool 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
194Clipboard::FormatType::FormatType() {
195}
196
197Clipboard::FormatType::FormatType(const std::string& native_format)
198    : data_(native_format) {
199}
200
201Clipboard::FormatType::~FormatType() {
202}
203
204std::string Clipboard::FormatType::Serialize() const {
205  return data_;
206}
207
208// static
209Clipboard::FormatType Clipboard::FormatType::Deserialize(
210    const std::string& serialization) {
211  return FormatType(serialization);
212}
213
214bool Clipboard::FormatType::operator<(const FormatType& other) const {
215  return data_ < other.data_;
216}
217
218bool 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.
227class 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 type.
236  ::Atom LookupSelectionForClipboardType(ClipboardType type) const;
237
238  // Returns the object which is responsible for communication on |type|.
239  SelectionRequestor* GetSelectionRequestorForClipboardType(ClipboardType type);
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  // |type|.
256  void TakeOwnershipOfSelection(ClipboardType type);
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(ClipboardType type,
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(ClipboardType type);
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 clipboard type.
282  void Clear(ClipboardType type);
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
311Clipboard::AuraX11Details::AuraX11Details()
312    : x_display_(gfx::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::MessagePumpX11::Current()->AddDispatcherForWindow(this, x_window_);
336}
337
338Clipboard::AuraX11Details::~AuraX11Details() {
339  base::MessagePumpX11::Current()->RemoveDispatcherForWindow(x_window_);
340
341  XDestroyWindow(x_display_, x_window_);
342}
343
344::Atom Clipboard::AuraX11Details::LookupSelectionForClipboardType(
345    ClipboardType type) const {
346  if (type == CLIPBOARD_TYPE_COPY_PASTE)
347    return atom_cache_.GetAtom(kClipboard);
348
349  return XA_PRIMARY;
350}
351
352const 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
361ui::SelectionRequestor*
362Clipboard::AuraX11Details::GetSelectionRequestorForClipboardType(
363    ClipboardType type) {
364  if (type == CLIPBOARD_TYPE_COPY_PASTE)
365    return &clipboard_requestor_;
366  else
367    return &primary_requestor_;
368}
369
370void Clipboard::AuraX11Details::CreateNewClipboardData() {
371  clipboard_data_ = SelectionFormatMap();
372}
373
374void Clipboard::AuraX11Details::InsertMapping(
375    const std::string& key,
376    const scoped_refptr<base::RefCountedMemory>& memory) {
377  ::Atom atom_key = atom_cache_.GetAtom(key.c_str());
378  clipboard_data_.Insert(atom_key, memory);
379}
380
381void Clipboard::AuraX11Details::TakeOwnershipOfSelection(ClipboardType type) {
382  if (type == CLIPBOARD_TYPE_COPY_PASTE)
383    return clipboard_owner_.TakeOwnershipOfSelection(clipboard_data_);
384  else
385    return primary_owner_.TakeOwnershipOfSelection(clipboard_data_);
386}
387
388SelectionData Clipboard::AuraX11Details::RequestAndWaitForTypes(
389    ClipboardType type,
390    const std::vector< ::Atom>& types) {
391  ::Atom selection_name = LookupSelectionForClipboardType(type);
392  if (XGetSelectionOwner(x_display_, selection_name) == x_window_) {
393    // We can local fastpath instead of playing the nested message loop game
394    // with the X server.
395    const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name);
396
397    for (std::vector< ::Atom>::const_iterator it = types.begin();
398         it != types.end(); ++it) {
399      SelectionFormatMap::const_iterator format_map_it = format_map.find(*it);
400      if (format_map_it != format_map.end())
401        return SelectionData(format_map_it->first, format_map_it->second);
402    }
403  } else {
404    TargetList targets = WaitAndGetTargetsList(type);
405    SelectionRequestor* receiver = GetSelectionRequestorForClipboardType(type);
406
407    std::vector< ::Atom> intersection;
408    ui::GetAtomIntersection(types, targets.target_list(), &intersection);
409    return receiver->RequestAndWaitForTypes(intersection);
410  }
411
412  return SelectionData();
413}
414
415TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList(
416    ClipboardType type) {
417  ::Atom selection_name = LookupSelectionForClipboardType(type);
418  std::vector< ::Atom> out;
419  if (XGetSelectionOwner(x_display_, selection_name) == x_window_) {
420    // We can local fastpath and return the list of local targets.
421    const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name);
422
423    for (SelectionFormatMap::const_iterator it = format_map.begin();
424         it != format_map.end(); ++it) {
425      out.push_back(it->first);
426    }
427  } else {
428    scoped_refptr<base::RefCountedMemory> data;
429    size_t out_data_items = 0;
430    ::Atom out_type = None;
431
432    SelectionRequestor* receiver = GetSelectionRequestorForClipboardType(type);
433    if (receiver->PerformBlockingConvertSelection(atom_cache_.GetAtom(kTargets),
434                                                  &data,
435                                                  NULL,
436                                                  &out_data_items,
437                                                  &out_type)) {
438      const ::Atom* atom_array = reinterpret_cast<const ::Atom*>(data->front());
439      for (size_t i = 0; i < out_data_items; ++i)
440        out.push_back(atom_array[i]);
441    } else {
442      // There was no target list. Most Java apps doesn't offer a TARGETS list,
443      // even though they AWT to. They will offer individual text types if you
444      // ask. If this is the case we attempt to make sense of the contents as
445      // text. This is pretty unfortunate since it means we have to actually
446      // copy the data to see if it is available, but at least this path
447      // shouldn't be hit for conforming programs.
448      std::vector< ::Atom> types = GetTextAtoms();
449      for (std::vector< ::Atom>::const_iterator it = types.begin();
450           it != types.end(); ++it) {
451        ::Atom type = None;
452        if (receiver->PerformBlockingConvertSelection(*it,
453                                                      NULL,
454                                                      NULL,
455                                                      NULL,
456                                                      &type) &&
457            type == *it) {
458          out.push_back(*it);
459        }
460      }
461    }
462  }
463
464  return TargetList(out, &atom_cache_);
465}
466
467std::vector< ::Atom> Clipboard::AuraX11Details::GetTextAtoms() const {
468  return GetTextAtomsFrom(&atom_cache_);
469}
470
471std::vector< ::Atom> Clipboard::AuraX11Details::GetAtomsForFormat(
472    const Clipboard::FormatType& format) {
473  std::vector< ::Atom> atoms;
474  atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
475  return atoms;
476}
477
478void Clipboard::AuraX11Details::Clear(ClipboardType type) {
479  if (type == CLIPBOARD_TYPE_COPY_PASTE)
480    return clipboard_owner_.Clear();
481  else
482    return primary_owner_.Clear();
483}
484
485bool Clipboard::AuraX11Details::Dispatch(const base::NativeEvent& event) {
486  XEvent* xev = event;
487
488  switch (xev->type) {
489    case SelectionRequest: {
490      if (xev->xselectionrequest.selection == XA_PRIMARY)
491        primary_owner_.OnSelectionRequest(xev->xselectionrequest);
492      else
493        clipboard_owner_.OnSelectionRequest(xev->xselectionrequest);
494      break;
495    }
496    case SelectionNotify: {
497      if (xev->xselection.selection == XA_PRIMARY)
498        primary_requestor_.OnSelectionNotify(xev->xselection);
499      else
500        clipboard_requestor_.OnSelectionNotify(xev->xselection);
501      break;
502    }
503    case SelectionClear: {
504      if (xev->xselectionclear.selection == XA_PRIMARY)
505        primary_owner_.OnSelectionClear(xev->xselectionclear);
506      else
507        clipboard_owner_.OnSelectionClear(xev->xselectionclear);
508      break;
509    }
510    default:
511      break;
512  }
513
514  return true;
515}
516
517///////////////////////////////////////////////////////////////////////////////
518// Clipboard
519
520Clipboard::Clipboard()
521    : aurax11_details_(new AuraX11Details) {
522  DCHECK(CalledOnValidThread());
523}
524
525Clipboard::~Clipboard() {
526  DCHECK(CalledOnValidThread());
527
528  // TODO(erg): We need to do whatever the equivalent of
529  // gtk_clipboard_store(clipboard_) is here. When we shut down, we want the
530  // current selection to live on.
531}
532
533void Clipboard::WriteObjects(ClipboardType type, const ObjectMap& objects) {
534  DCHECK(CalledOnValidThread());
535  DCHECK(IsSupportedClipboardType(type));
536
537  aurax11_details_->CreateNewClipboardData();
538  for (ObjectMap::const_iterator iter = objects.begin();
539       iter != objects.end(); ++iter) {
540    DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
541  }
542  aurax11_details_->TakeOwnershipOfSelection(type);
543
544  if (type == CLIPBOARD_TYPE_COPY_PASTE) {
545    ObjectMap::const_iterator text_iter = objects.find(CBF_TEXT);
546    if (text_iter != objects.end()) {
547      aurax11_details_->CreateNewClipboardData();
548      const ObjectMapParam& char_vector = text_iter->second[0];
549      WriteText(&char_vector.front(), char_vector.size());
550      aurax11_details_->TakeOwnershipOfSelection(CLIPBOARD_TYPE_SELECTION);
551    }
552  }
553}
554
555bool Clipboard::IsFormatAvailable(const FormatType& format,
556                                  ClipboardType type) const {
557  DCHECK(CalledOnValidThread());
558  DCHECK(IsSupportedClipboardType(type));
559
560  TargetList target_list = aurax11_details_->WaitAndGetTargetsList(type);
561  return target_list.ContainsFormat(format);
562}
563
564void Clipboard::Clear(ClipboardType type) {
565  DCHECK(CalledOnValidThread());
566  DCHECK(IsSupportedClipboardType(type));
567  aurax11_details_->Clear(type);
568}
569
570void Clipboard::ReadAvailableTypes(ClipboardType type,
571                                   std::vector<string16>* types,
572                                   bool* contains_filenames) const {
573  DCHECK(CalledOnValidThread());
574  if (!types || !contains_filenames) {
575    NOTREACHED();
576    return;
577  }
578
579  TargetList target_list = aurax11_details_->WaitAndGetTargetsList(type);
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      type, aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType())));
595  if (data.IsValid())
596    ReadCustomDataTypes(data.GetData(), data.GetSize(), types);
597}
598
599void Clipboard::ReadText(ClipboardType type, string16* result) const {
600  DCHECK(CalledOnValidThread());
601
602  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
603      type, aurax11_details_->GetTextAtoms()));
604  if (data.IsValid()) {
605    std::string text = data.GetText();
606    *result = UTF8ToUTF16(text);
607  }
608}
609
610void Clipboard::ReadAsciiText(ClipboardType type, std::string* result) const {
611  DCHECK(CalledOnValidThread());
612
613  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
614      type, aurax11_details_->GetTextAtoms()));
615  if (data.IsValid())
616    result->assign(data.GetText());
617}
618
619// TODO(estade): handle different charsets.
620// TODO(port): set *src_url.
621void Clipboard::ReadHTML(ClipboardType type,
622                         string16* markup,
623                         std::string* src_url,
624                         uint32* fragment_start,
625                         uint32* fragment_end) const {
626  DCHECK(CalledOnValidThread());
627  markup->clear();
628  if (src_url)
629    src_url->clear();
630  *fragment_start = 0;
631  *fragment_end = 0;
632
633  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
634      type, aurax11_details_->GetAtomsForFormat(GetHtmlFormatType())));
635  if (data.IsValid()) {
636    *markup = data.GetHtml();
637
638    *fragment_start = 0;
639    DCHECK(markup->length() <= kuint32max);
640    *fragment_end = static_cast<uint32>(markup->length());
641  }
642}
643
644void Clipboard::ReadRTF(ClipboardType type, std::string* result) const {
645  DCHECK(CalledOnValidThread());
646
647  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
648      type, aurax11_details_->GetAtomsForFormat(GetRtfFormatType())));
649  if (data.IsValid())
650    data.AssignTo(result);
651}
652
653SkBitmap Clipboard::ReadImage(ClipboardType type) const {
654  DCHECK(CalledOnValidThread());
655
656  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
657      type, aurax11_details_->GetAtomsForFormat(GetBitmapFormatType())));
658  if (data.IsValid()) {
659    SkBitmap bitmap;
660    if (gfx::PNGCodec::Decode(data.GetData(), data.GetSize(), &bitmap))
661      return SkBitmap(bitmap);
662  }
663
664  return SkBitmap();
665}
666
667void Clipboard::ReadCustomData(ClipboardType clipboard_type,
668                               const string16& type,
669                               string16* result) const {
670  DCHECK(CalledOnValidThread());
671
672  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
673      clipboard_type,
674      aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType())));
675  if (data.IsValid())
676    ReadCustomDataForType(data.GetData(), data.GetSize(), type, result);
677}
678
679void Clipboard::ReadBookmark(string16* title, std::string* url) const {
680  DCHECK(CalledOnValidThread());
681  // TODO(erg): This was left NOTIMPLEMENTED() in the gtk port too.
682  NOTIMPLEMENTED();
683}
684
685void Clipboard::ReadData(const FormatType& format, std::string* result) const {
686  DCHECK(CalledOnValidThread());
687
688  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
689      CLIPBOARD_TYPE_COPY_PASTE, aurax11_details_->GetAtomsForFormat(format)));
690  if (data.IsValid())
691    data.AssignTo(result);
692}
693
694uint64 Clipboard::GetSequenceNumber(ClipboardType type) {
695  DCHECK(CalledOnValidThread());
696  if (type == CLIPBOARD_TYPE_COPY_PASTE)
697    return SelectionChangeObserver::GetInstance()->clipboard_sequence_number();
698  else
699    return SelectionChangeObserver::GetInstance()->primary_sequence_number();
700}
701
702void Clipboard::WriteText(const char* text_data, size_t text_len) {
703  std::string str(text_data, text_len);
704  scoped_refptr<base::RefCountedMemory> mem(
705      base::RefCountedString::TakeString(&str));
706
707  aurax11_details_->InsertMapping(kMimeTypeText, mem);
708  aurax11_details_->InsertMapping(kText, mem);
709  aurax11_details_->InsertMapping(kString, mem);
710  aurax11_details_->InsertMapping(kUtf8String, mem);
711}
712
713void Clipboard::WriteHTML(const char* markup_data,
714                          size_t markup_len,
715                          const char* url_data,
716                          size_t url_len) {
717  // TODO(estade): We need to expand relative links with |url_data|.
718  static const char* html_prefix = "<meta http-equiv=\"content-type\" "
719                                   "content=\"text/html; charset=utf-8\">";
720  std::string data = html_prefix;
721  data += std::string(markup_data, markup_len);
722  // Some programs expect NULL-terminated data. See http://crbug.com/42624
723  data += '\0';
724
725  scoped_refptr<base::RefCountedMemory> mem(
726      base::RefCountedString::TakeString(&data));
727  aurax11_details_->InsertMapping(kMimeTypeHTML, mem);
728}
729
730void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) {
731  WriteData(GetRtfFormatType(), rtf_data, data_len);
732}
733
734void Clipboard::WriteBookmark(const char* title_data,
735                              size_t title_len,
736                              const char* url_data,
737                              size_t url_len) {
738  // Write as a mozilla url (UTF16: URL, newline, title).
739  string16 url = UTF8ToUTF16(std::string(url_data, url_len) + "\n");
740  string16 title = UTF8ToUTF16(std::string(title_data, title_len));
741
742  std::vector<unsigned char> data;
743  ui::AddString16ToVector(url, &data);
744  ui::AddString16ToVector(title, &data);
745  scoped_refptr<base::RefCountedMemory> mem(
746      base::RefCountedBytes::TakeVector(&data));
747
748  aurax11_details_->InsertMapping(kMimeTypeMozillaURL, mem);
749}
750
751// Write an extra flavor that signifies WebKit was the last to modify the
752// pasteboard. This flavor has no data.
753void Clipboard::WriteWebSmartPaste() {
754  aurax11_details_->InsertMapping(kMimeTypeWebkitSmartPaste,
755                                  scoped_refptr<base::RefCountedMemory>());
756}
757
758void Clipboard::WriteBitmap(const SkBitmap& bitmap) {
759  // Encode the bitmap as a PNG for transport.
760  std::vector<unsigned char> output;
761  if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &output)) {
762    aurax11_details_->InsertMapping(kMimeTypePNG,
763                                    base::RefCountedBytes::TakeVector(
764                                        &output));
765  }
766}
767
768void Clipboard::WriteData(const FormatType& format,
769                          const char* data_data,
770                          size_t data_len) {
771  // We assume that certain mapping types are only written by trusted code.
772  // Therefore we must upkeep their integrity.
773  if (format.Equals(GetBitmapFormatType()))
774    return;
775
776  std::vector<unsigned char> bytes(data_data, data_data + data_len);
777  scoped_refptr<base::RefCountedMemory> mem(
778      base::RefCountedBytes::TakeVector(&bytes));
779  aurax11_details_->InsertMapping(format.ToString(), mem);
780}
781
782// static
783Clipboard::FormatType Clipboard::GetFormatType(
784    const std::string& format_string) {
785  return FormatType::Deserialize(format_string);
786}
787
788// static
789const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
790  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList));
791  return type;
792}
793
794// static
795const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
796  return GetUrlFormatType();
797}
798
799// static
800const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
801  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText));
802  return type;
803}
804
805// static
806const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
807  return GetPlainTextFormatType();
808}
809
810// static
811const Clipboard::FormatType& Clipboard::GetFilenameFormatType() {
812  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeFilename));
813  return type;
814}
815
816// static
817const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() {
818  return Clipboard::GetFilenameFormatType();
819}
820
821// static
822const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
823  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML));
824  return type;
825}
826
827// static
828const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
829  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF));
830  return type;
831}
832
833// static
834const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
835  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePNG));
836  return type;
837}
838
839// static
840const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
841  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste));
842  return type;
843}
844
845// static
846const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
847  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData));
848  return type;
849}
850
851// static
852const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
853  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData));
854  return type;
855}
856
857}  // namespace ui
858