clipboard_aura.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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 <list>
8
9#include "base/basictypes.h"
10#include "base/files/file_path.h"
11#include "base/logging.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/stl_util.h"
14#include "base/strings/utf_string_conversions.h"
15#include "third_party/skia/include/core/SkBitmap.h"
16#include "ui/base/clipboard/custom_data_helper.h"
17#include "ui/gfx/size.h"
18
19namespace ui {
20
21namespace {
22const char kMimeTypeFilename[] = "chromium/filename";
23const char kMimeTypeBitmap[] = "image/bmp";
24const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data";
25const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste";
26const size_t kMaxClipboardSize = 1;
27
28// Clipboard data format used by AuraClipboard.
29enum AuraClipboardFormat {
30  TEXT      = 1 << 0,
31  HTML      = 1 << 1,
32  RTF       = 1 << 2,
33  BOOKMARK  = 1 << 3,
34  BITMAP    = 1 << 4,
35  CUSTOM    = 1 << 5,
36  WEB       = 1 << 6,
37};
38
39// ClipboardData contains data copied to the Clipboard for a variety of formats.
40// It mostly just provides APIs to cleanly access and manipulate this data.
41class ClipboardData {
42 public:
43  ClipboardData()
44      : web_smart_paste_(false),
45        format_(0) {}
46
47  virtual ~ClipboardData() {}
48
49  // Bitmask of AuraClipboardFormat types.
50  const int format() const { return format_; }
51
52  const std::string& text() const { return text_; }
53  void set_text(const std::string& text) {
54    text_ = text;
55    format_ |= TEXT;
56  }
57
58  const std::string& markup_data() const { return markup_data_; }
59  void set_markup_data(const std::string& markup_data) {
60    markup_data_ = markup_data;
61    format_ |= HTML;
62  }
63
64  const std::string& rtf_data() const { return rtf_data_; }
65  void SetRTFData(const std::string& rtf_data) {
66    rtf_data_ = rtf_data;
67    format_ |= RTF;
68  }
69
70  const std::string& url() const { return url_; }
71  void set_url(const std::string& url) {
72    url_ = url;
73    format_ |= HTML;
74  }
75
76  const std::string& bookmark_title() const { return bookmark_title_; }
77  void set_bookmark_title(const std::string& bookmark_title) {
78    bookmark_title_ = bookmark_title;
79    format_ |= BOOKMARK;
80  }
81
82  const std::string& bookmark_url() const { return bookmark_url_; }
83  void set_bookmark_url(const std::string& bookmark_url) {
84    bookmark_url_ = bookmark_url;
85    format_ |= BOOKMARK;
86  }
87
88  const SkBitmap& bitmap() const { return bitmap_; }
89  void SetBitmapData(const SkBitmap& bitmap) {
90    bitmap.copyTo(&bitmap_, bitmap.getConfig());
91    format_ |= BITMAP;
92  }
93
94  const std::string& custom_data_format() const { return custom_data_format_; }
95  const std::string& custom_data_data() const { return custom_data_data_; }
96  void SetCustomData(const std::string& data_format,
97                     const std::string& data_data) {
98    if (data_data.size() == 0) {
99      custom_data_data_.clear();
100      custom_data_format_.clear();
101      return;
102    }
103    custom_data_data_ = data_data;
104    custom_data_format_ = data_format;
105    format_ |= CUSTOM;
106  }
107
108  bool web_smart_paste() const { return web_smart_paste_; }
109  void set_web_smart_paste(bool web_smart_paste) {
110    web_smart_paste_ = web_smart_paste;
111    format_ |= WEB;
112  }
113
114 private:
115  // Plain text in UTF8 format.
116  std::string text_;
117
118  // HTML markup data in UTF8 format.
119  std::string markup_data_;
120  std::string url_;
121
122  // RTF data.
123  std::string rtf_data_;
124
125  // Bookmark title in UTF8 format.
126  std::string bookmark_title_;
127  std::string bookmark_url_;
128
129  // Filenames.
130  std::vector<std::string> files_;
131
132  // Bitmap images.
133  SkBitmap bitmap_;
134
135  // Data with custom format.
136  std::string custom_data_format_;
137  std::string custom_data_data_;
138
139  // WebKit smart paste data.
140  bool web_smart_paste_;
141
142  int format_;
143
144  DISALLOW_COPY_AND_ASSIGN(ClipboardData);
145};
146
147// Platform clipboard implementation for Aura. This handles things like format
148// conversion, versioning of clipboard items etc. The goal is to roughly provide
149// a substitute to platform clipboards on other platforms such as GtkClipboard
150// on gtk or winapi clipboard on win.
151class AuraClipboard {
152 public:
153  AuraClipboard() {}
154
155  ~AuraClipboard() {
156    Clear();
157  }
158
159  void Clear() {
160    STLDeleteContainerPointers(data_list_.begin(), data_list_.end());
161    data_list_.clear();
162  }
163
164  // Returns the number of entries currently in the clipboard stack.
165  size_t GetNumClipboardEntries() {
166    return data_list_.size();
167  }
168
169  // Returns the data currently on the top of the clipboard stack, NULL if the
170  // clipboard stack is empty.
171  const ClipboardData* GetData() const {
172    if (data_list_.empty())
173      return NULL;
174    return data_list_.front();
175  }
176
177  // Returns true if the data on top of the clipboard stack has format |format|
178  // or another format that can be converted to |format|.
179  bool IsFormatAvailable(AuraClipboardFormat format) const {
180    switch (format) {
181      case TEXT:
182        return HasFormat(TEXT) || HasFormat(BOOKMARK);
183      default:
184        return HasFormat(format);
185    }
186  }
187
188  // Reads text from the data at the top of clipboard stack.
189  void ReadText(string16* result) const {
190    std::string utf8_result;
191    ReadAsciiText(&utf8_result);
192    *result = UTF8ToUTF16(utf8_result);
193  }
194
195  // Reads ascii text from the data at the top of clipboard stack.
196  void ReadAsciiText(std::string* result) const {
197    result->clear();
198    const ClipboardData* data = GetData();
199    if (!data)
200      return;
201    if (HasFormat(TEXT))
202      *result = data->text();
203    else if (HasFormat(HTML))
204      *result = data->markup_data();
205    else if (HasFormat(BOOKMARK))
206      *result = data->bookmark_url();
207  }
208
209  // Reads HTML from the data at the top of clipboard stack.
210  void ReadHTML(string16* markup,
211                std::string* src_url,
212                uint32* fragment_start,
213                uint32* fragment_end) const {
214    markup->clear();
215    if (src_url)
216      src_url->clear();
217    *fragment_start = 0;
218    *fragment_end = 0;
219
220    if (!HasFormat(HTML))
221      return;
222
223    const ClipboardData* data = GetData();
224    *markup = UTF8ToUTF16(data->markup_data());
225    *src_url = data->url();
226
227    *fragment_start = 0;
228    DCHECK_LE(markup->length(), kuint32max);
229    *fragment_end = static_cast<uint32>(markup->length());
230  }
231
232  // Reads RTF from the data at the top of clipboard stack.
233  void ReadRTF(std::string* result) const {
234    result->clear();
235    const ClipboardData* data = GetData();
236    if (!HasFormat(RTF))
237      return;
238
239    *result = data->rtf_data();
240  }
241
242  // Reads image from the data at the top of clipboard stack.
243  SkBitmap ReadImage() const {
244    SkBitmap img;
245    if (!HasFormat(BITMAP))
246      return img;
247
248    // A shallow copy should be fine here, but just to be safe...
249    const SkBitmap& clipboard_bitmap = GetData()->bitmap();
250    clipboard_bitmap.copyTo(&img, clipboard_bitmap.getConfig());
251    return img;
252  }
253
254  // Reads data of type |type| from the data at the top of clipboard stack.
255  void ReadCustomData(const string16& type, string16* result) const {
256    result->clear();
257    const ClipboardData* data = GetData();
258    if (!HasFormat(CUSTOM))
259      return;
260
261    ui::ReadCustomDataForType(data->custom_data_data().c_str(),
262        data->custom_data_data().size(),
263        type, result);
264  }
265
266  // Reads bookmark from the data at the top of clipboard stack.
267  void ReadBookmark(string16* title, std::string* url) const {
268    title->clear();
269    url->clear();
270    if (!HasFormat(BOOKMARK))
271      return;
272
273    const ClipboardData* data = GetData();
274    *title = UTF8ToUTF16(data->bookmark_title());
275    *url = data->bookmark_url();
276  }
277
278  void ReadData(const std::string& type, std::string* result) const {
279    result->clear();
280    const ClipboardData* data = GetData();
281    if (!HasFormat(CUSTOM) || type != data->custom_data_format())
282      return;
283
284    *result = data->custom_data_data();
285  }
286
287  // Writes |data| to the top of the clipboard stack.
288  void WriteData(ClipboardData* data) {
289    DCHECK(data);
290    AddToListEnsuringSize(data);
291  }
292
293 private:
294  // True if the data on top of the clipboard stack has format |format|.
295  bool HasFormat(AuraClipboardFormat format) const {
296    const ClipboardData* data = GetData();
297    if (!data)
298      return false;
299
300    return data->format() & format;
301  }
302
303  void AddToListEnsuringSize(ClipboardData* data) {
304    DCHECK(data);
305    data_list_.push_front(data);
306
307    // If the size of list becomes more than the maximum allowed, we delete the
308    // last element.
309    if (data_list_.size() > kMaxClipboardSize) {
310      ClipboardData* last = data_list_.back();
311      data_list_.pop_back();
312      delete last;
313    }
314  }
315
316  // Stack containing various versions of ClipboardData.
317  std::list<ClipboardData*> data_list_;
318
319  DISALLOW_COPY_AND_ASSIGN(AuraClipboard);
320};
321
322AuraClipboard* aura_clipboard = NULL;
323
324AuraClipboard* GetClipboard() {
325  if (!aura_clipboard)
326    aura_clipboard = new AuraClipboard();
327  return aura_clipboard;
328}
329
330void DeleteClipboard() {
331  if (aura_clipboard)
332    delete aura_clipboard;
333  aura_clipboard = NULL;
334}
335
336// Helper class to build a ClipboardData object and write it to clipboard.
337class ClipboardDataBuilder {
338 public:
339  static void CommitToClipboard() {
340    GetClipboard()->WriteData(GetCurrentData());
341    current_data_ = NULL;
342  }
343
344  static void WriteText(const char* text_data, size_t text_len) {
345    ClipboardData* data = GetCurrentData();
346    data->set_text(std::string(text_data, text_len));
347  }
348
349  static void WriteHTML(const char* markup_data,
350                        size_t markup_len,
351                        const char* url_data,
352                        size_t url_len) {
353    ClipboardData* data = GetCurrentData();
354    data->set_markup_data(std::string(markup_data, markup_len));
355    data->set_url(std::string(url_data, url_len));
356  }
357
358  static void WriteRTF(const char* rtf_data, size_t rtf_len) {
359    ClipboardData* data = GetCurrentData();
360    data->SetRTFData(std::string(rtf_data, rtf_len));
361  }
362
363  static void WriteBookmark(const char* title_data,
364                            size_t title_len,
365                            const char* url_data,
366                            size_t url_len) {
367    ClipboardData* data = GetCurrentData();
368    data->set_bookmark_title(std::string(title_data, title_len));
369    data->set_bookmark_url(std::string(url_data, url_len));
370  }
371
372  static void WriteWebSmartPaste() {
373    ClipboardData* data = GetCurrentData();
374    data->set_web_smart_paste(true);
375  }
376
377  static void WriteBitmap(const SkBitmap& bitmap) {
378    ClipboardData* data = GetCurrentData();
379    data->SetBitmapData(bitmap);
380  }
381
382  static void WriteData(const std::string& format,
383                        const char* data_data,
384                        size_t data_len) {
385    ClipboardData* data = GetCurrentData();
386    data->SetCustomData(format, std::string(data_data, data_len));
387  }
388
389 private:
390  static ClipboardData* GetCurrentData() {
391    if (!current_data_)
392      current_data_ = new ClipboardData;
393    return current_data_;
394  }
395
396  static ClipboardData* current_data_;
397};
398
399ClipboardData* ClipboardDataBuilder::current_data_ = NULL;
400
401}  // namespace
402
403Clipboard::FormatType::FormatType() {
404}
405
406Clipboard::FormatType::FormatType(const std::string& native_format)
407    : data_(native_format) {
408}
409
410Clipboard::FormatType::~FormatType() {
411}
412
413std::string Clipboard::FormatType::Serialize() const {
414  return data_;
415}
416
417// static
418Clipboard::FormatType Clipboard::FormatType::Deserialize(
419    const std::string& serialization) {
420  return FormatType(serialization);
421}
422
423bool Clipboard::FormatType::operator<(const FormatType& other) const {
424  return data_ < other.data_;
425}
426
427bool Clipboard::FormatType::Equals(const FormatType& other) const {
428  return data_ == other.data_;
429}
430
431Clipboard::Clipboard() {
432  DCHECK(CalledOnValidThread());
433  // Make sure clipboard is created.
434  GetClipboard();
435}
436
437Clipboard::~Clipboard() {
438  DCHECK(CalledOnValidThread());
439  DeleteClipboard();
440}
441
442void Clipboard::WriteObjects(ClipboardType type, const ObjectMap& objects) {
443  DCHECK(CalledOnValidThread());
444  DCHECK(IsSupportedClipboardType(type));
445  for (ObjectMap::const_iterator iter = objects.begin();
446       iter != objects.end(); ++iter) {
447    DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
448  }
449  ClipboardDataBuilder::CommitToClipboard();
450}
451
452bool Clipboard::IsFormatAvailable(const FormatType& format,
453                                  ClipboardType type) const {
454  DCHECK(CalledOnValidThread());
455  DCHECK(IsSupportedClipboardType(type));
456  AuraClipboard* clipboard = GetClipboard();
457  if (GetPlainTextFormatType().Equals(format) ||
458      GetUrlFormatType().Equals(format))
459    return clipboard->IsFormatAvailable(TEXT);
460  else if (GetHtmlFormatType().Equals(format))
461    return clipboard->IsFormatAvailable(HTML);
462  else if (GetRtfFormatType().Equals(format))
463    return clipboard->IsFormatAvailable(RTF);
464  else if (GetBitmapFormatType().Equals(format))
465    return clipboard->IsFormatAvailable(BITMAP);
466  else if (GetWebKitSmartPasteFormatType().Equals(format))
467    return clipboard->IsFormatAvailable(WEB);
468  else {
469    const ClipboardData* data = clipboard->GetData();
470    if (data && data->custom_data_format() == format.ToString())
471      return true;
472  }
473  return false;
474}
475
476void Clipboard::Clear(ClipboardType type) {
477  DCHECK(CalledOnValidThread());
478  DCHECK(IsSupportedClipboardType(type));
479  AuraClipboard* clipboard = GetClipboard();
480  clipboard->Clear();
481}
482
483void Clipboard::ReadAvailableTypes(ClipboardType type,
484                                   std::vector<string16>* types,
485                                   bool* contains_filenames) const {
486  DCHECK(CalledOnValidThread());
487  if (!types || !contains_filenames) {
488    NOTREACHED();
489    return;
490  }
491
492  types->clear();
493  *contains_filenames = false;
494  if (IsFormatAvailable(GetPlainTextFormatType(), type))
495    types->push_back(UTF8ToUTF16(GetPlainTextFormatType().ToString()));
496  if (IsFormatAvailable(GetHtmlFormatType(), type))
497    types->push_back(UTF8ToUTF16(GetHtmlFormatType().ToString()));
498  if (IsFormatAvailable(GetRtfFormatType(), type))
499    types->push_back(UTF8ToUTF16(GetRtfFormatType().ToString()));
500  if (IsFormatAvailable(GetBitmapFormatType(), type))
501    types->push_back(UTF8ToUTF16(kMimeTypePNG));
502
503  AuraClipboard* clipboard = GetClipboard();
504  if (clipboard->IsFormatAvailable(CUSTOM) && clipboard->GetData()) {
505    ui::ReadCustomDataTypes(clipboard->GetData()->custom_data_data().c_str(),
506        clipboard->GetData()->custom_data_data().size(), types);
507  }
508}
509
510void Clipboard::ReadText(ClipboardType type, string16* result) const {
511  DCHECK(CalledOnValidThread());
512  GetClipboard()->ReadText(result);
513}
514
515void Clipboard::ReadAsciiText(ClipboardType type, std::string* result) const {
516  DCHECK(CalledOnValidThread());
517  GetClipboard()->ReadAsciiText(result);
518}
519
520void Clipboard::ReadHTML(ClipboardType type,
521                         string16* markup,
522                         std::string* src_url,
523                         uint32* fragment_start,
524                         uint32* fragment_end) const {
525  DCHECK(CalledOnValidThread());
526  GetClipboard()->ReadHTML(markup, src_url, fragment_start, fragment_end);
527}
528
529void Clipboard::ReadRTF(ClipboardType type, std::string* result) const {
530  DCHECK(CalledOnValidThread());
531  GetClipboard()->ReadRTF(result);
532}
533
534SkBitmap Clipboard::ReadImage(ClipboardType type) const {
535  DCHECK(CalledOnValidThread());
536  return GetClipboard()->ReadImage();
537}
538
539void Clipboard::ReadCustomData(ClipboardType clipboard_type,
540                               const string16& type,
541                               string16* result) const {
542  DCHECK(CalledOnValidThread());
543  GetClipboard()->ReadCustomData(type, result);
544}
545
546void Clipboard::ReadBookmark(string16* title, std::string* url) const {
547  DCHECK(CalledOnValidThread());
548  GetClipboard()->ReadBookmark(title, url);
549}
550
551void Clipboard::ReadData(const FormatType& format, std::string* result) const {
552  DCHECK(CalledOnValidThread());
553  GetClipboard()->ReadData(format.ToString(), result);
554}
555
556uint64 Clipboard::GetSequenceNumber(ClipboardType type) {
557  DCHECK(CalledOnValidThread());
558  return GetClipboard()->GetNumClipboardEntries();
559}
560
561void Clipboard::WriteText(const char* text_data, size_t text_len) {
562  ClipboardDataBuilder::WriteText(text_data, text_len);
563}
564
565void Clipboard::WriteHTML(const char* markup_data,
566                          size_t markup_len,
567                          const char* url_data,
568                          size_t url_len) {
569  ClipboardDataBuilder::WriteHTML(markup_data, markup_len, url_data, url_len);
570}
571
572void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) {
573  ClipboardDataBuilder::WriteRTF(rtf_data, data_len);
574}
575
576void Clipboard::WriteBookmark(const char* title_data,
577                              size_t title_len,
578                              const char* url_data,
579                              size_t url_len) {
580  ClipboardDataBuilder::WriteBookmark(title_data, title_len, url_data, url_len);
581}
582
583void Clipboard::WriteWebSmartPaste() {
584  ClipboardDataBuilder::WriteWebSmartPaste();
585}
586
587void Clipboard::WriteBitmap(const SkBitmap& bitmap) {
588  ClipboardDataBuilder::WriteBitmap(bitmap);
589}
590
591void Clipboard::WriteData(const FormatType& format,
592                          const char* data_data,
593                          size_t data_len) {
594  ClipboardDataBuilder::WriteData(format.ToString(), data_data, data_len);
595}
596
597// static
598Clipboard::FormatType Clipboard::GetFormatType(
599    const std::string& format_string) {
600  return FormatType::Deserialize(format_string);
601}
602
603// static
604const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
605  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList));
606  return type;
607}
608
609// static
610const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
611  return GetUrlFormatType();
612}
613
614// static
615const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
616  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText));
617  return type;
618}
619
620// static
621const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
622  return GetPlainTextFormatType();
623}
624
625// static
626const Clipboard::FormatType& Clipboard::GetFilenameFormatType() {
627  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeFilename));
628  return type;
629}
630
631// static
632const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() {
633  return Clipboard::GetFilenameFormatType();
634}
635
636// static
637const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
638  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML));
639  return type;
640}
641
642// static
643const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
644  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF));
645  return type;
646}
647
648// static
649const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
650  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeBitmap));
651  return type;
652}
653
654// static
655const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
656  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste));
657  return type;
658}
659
660// static
661const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
662  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData));
663  return type;
664}
665
666// static
667const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
668  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData));
669  return type;
670}
671
672}  // namespace ui
673