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// Many of these functions are based on those found in
6// webkit/port/platform/PasteboardWin.cpp
7
8#include "ui/base/clipboard/clipboard.h"
9
10#include <shellapi.h>
11#include <shlobj.h>
12
13#include "base/basictypes.h"
14#include "base/bind.h"
15#include "base/files/file_path.h"
16#include "base/logging.h"
17#include "base/memory/shared_memory.h"
18#include "base/message_loop/message_loop.h"
19#include "base/numerics/safe_conversions.h"
20#include "base/stl_util.h"
21#include "base/strings/string_number_conversions.h"
22#include "base/strings/string_util.h"
23#include "base/strings/utf_offset_string_conversions.h"
24#include "base/strings/utf_string_conversions.h"
25#include "base/win/message_window.h"
26#include "base/win/scoped_gdi_object.h"
27#include "base/win/scoped_hdc.h"
28#include "third_party/skia/include/core/SkBitmap.h"
29#include "ui/base/clipboard/clipboard_util_win.h"
30#include "ui/base/clipboard/custom_data_helper.h"
31#include "ui/gfx/canvas.h"
32#include "ui/gfx/size.h"
33
34namespace ui {
35
36namespace {
37
38// A scoper to manage acquiring and automatically releasing the clipboard.
39class ScopedClipboard {
40 public:
41  ScopedClipboard() : opened_(false) { }
42
43  ~ScopedClipboard() {
44    if (opened_)
45      Release();
46  }
47
48  bool Acquire(HWND owner) {
49    const int kMaxAttemptsToOpenClipboard = 5;
50
51    if (opened_) {
52      NOTREACHED();
53      return false;
54    }
55
56    // Attempt to open the clipboard, which will acquire the Windows clipboard
57    // lock.  This may fail if another process currently holds this lock.
58    // We're willing to try a few times in the hopes of acquiring it.
59    //
60    // This turns out to be an issue when using remote desktop because the
61    // rdpclip.exe process likes to read what we've written to the clipboard and
62    // send it to the RDP client.  If we open and close the clipboard in quick
63    // succession, we might be trying to open it while rdpclip.exe has it open,
64    // See Bug 815425.
65    //
66    // In fact, we believe we'll only spin this loop over remote desktop.  In
67    // normal situations, the user is initiating clipboard operations and there
68    // shouldn't be contention.
69
70    for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) {
71      // If we didn't manage to open the clipboard, sleep a bit and be hopeful.
72      if (attempts != 0)
73        ::Sleep(5);
74
75      if (::OpenClipboard(owner)) {
76        opened_ = true;
77        return true;
78      }
79    }
80
81    // We failed to acquire the clipboard.
82    return false;
83  }
84
85  void Release() {
86    if (opened_) {
87      ::CloseClipboard();
88      opened_ = false;
89    } else {
90      NOTREACHED();
91    }
92  }
93
94 private:
95  bool opened_;
96};
97
98bool ClipboardOwnerWndProc(UINT message,
99                           WPARAM wparam,
100                           LPARAM lparam,
101                           LRESULT* result) {
102  switch (message) {
103  case WM_RENDERFORMAT:
104    // This message comes when SetClipboardData was sent a null data handle
105    // and now it's come time to put the data on the clipboard.
106    // We always set data, so there isn't a need to actually do anything here.
107    break;
108  case WM_RENDERALLFORMATS:
109    // This message comes when SetClipboardData was sent a null data handle
110    // and now this application is about to quit, so it must put data on
111    // the clipboard before it exits.
112    // We always set data, so there isn't a need to actually do anything here.
113    break;
114  case WM_DRAWCLIPBOARD:
115    break;
116  case WM_DESTROY:
117    break;
118  case WM_CHANGECBCHAIN:
119    break;
120  default:
121    return false;
122  }
123
124  *result = 0;
125  return true;
126}
127
128template <typename charT>
129HGLOBAL CreateGlobalData(const std::basic_string<charT>& str) {
130  HGLOBAL data =
131    ::GlobalAlloc(GMEM_MOVEABLE, ((str.size() + 1) * sizeof(charT)));
132  if (data) {
133    charT* raw_data = static_cast<charT*>(::GlobalLock(data));
134    memcpy(raw_data, str.data(), str.size() * sizeof(charT));
135    raw_data[str.size()] = '\0';
136    ::GlobalUnlock(data);
137  }
138  return data;
139};
140
141bool BitmapHasInvalidPremultipliedColors(const SkBitmap& bitmap) {
142  for (int x = 0; x < bitmap.width(); ++x) {
143    for (int y = 0; y < bitmap.height(); ++y) {
144      uint32_t pixel = *bitmap.getAddr32(x, y);
145      if (SkColorGetR(pixel) > SkColorGetA(pixel) ||
146          SkColorGetG(pixel) > SkColorGetA(pixel) ||
147          SkColorGetB(pixel) > SkColorGetA(pixel))
148        return true;
149    }
150  }
151  return false;
152}
153
154void MakeBitmapOpaque(const SkBitmap& bitmap) {
155  for (int x = 0; x < bitmap.width(); ++x) {
156    for (int y = 0; y < bitmap.height(); ++y) {
157      *bitmap.getAddr32(x, y) = SkColorSetA(*bitmap.getAddr32(x, y), 0xFF);
158    }
159  }
160}
161
162}  // namespace
163
164Clipboard::FormatType::FormatType() : data_() {}
165
166Clipboard::FormatType::FormatType(UINT native_format) : data_() {
167  // There's no good way to actually initialize this in the constructor in
168  // C++03.
169  data_.cfFormat = native_format;
170  data_.dwAspect = DVASPECT_CONTENT;
171  data_.lindex = -1;
172  data_.tymed = TYMED_HGLOBAL;
173}
174
175Clipboard::FormatType::FormatType(UINT native_format, LONG index) : data_() {
176  // There's no good way to actually initialize this in the constructor in
177  // C++03.
178  data_.cfFormat = native_format;
179  data_.dwAspect = DVASPECT_CONTENT;
180  data_.lindex = index;
181  data_.tymed = TYMED_HGLOBAL;
182}
183
184Clipboard::FormatType::~FormatType() {
185}
186
187std::string Clipboard::FormatType::Serialize() const {
188  return base::IntToString(data_.cfFormat);
189}
190
191// static
192Clipboard::FormatType Clipboard::FormatType::Deserialize(
193    const std::string& serialization) {
194  int clipboard_format = -1;
195  if (!base::StringToInt(serialization, &clipboard_format)) {
196    NOTREACHED();
197    return FormatType();
198  }
199  return FormatType(clipboard_format);
200}
201
202bool Clipboard::FormatType::operator<(const FormatType& other) const {
203  return ToUINT() < other.ToUINT();
204}
205
206bool Clipboard::FormatType::Equals(const FormatType& other) const {
207  return ToUINT() == other.ToUINT();
208}
209
210Clipboard::Clipboard() {
211  if (base::MessageLoopForUI::IsCurrent())
212    clipboard_owner_.reset(new base::win::MessageWindow());
213}
214
215Clipboard::~Clipboard() {
216}
217
218void Clipboard::WriteObjects(ClipboardType type, const ObjectMap& objects) {
219  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
220
221  ScopedClipboard clipboard;
222  if (!clipboard.Acquire(GetClipboardWindow()))
223    return;
224
225  ::EmptyClipboard();
226
227  for (ObjectMap::const_iterator iter = objects.begin();
228       iter != objects.end(); ++iter) {
229    DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
230  }
231}
232
233void Clipboard::WriteText(const char* text_data, size_t text_len) {
234  base::string16 text;
235  base::UTF8ToUTF16(text_data, text_len, &text);
236  HGLOBAL glob = CreateGlobalData(text);
237
238  WriteToClipboard(CF_UNICODETEXT, glob);
239}
240
241void Clipboard::WriteHTML(const char* markup_data,
242                          size_t markup_len,
243                          const char* url_data,
244                          size_t url_len) {
245  std::string markup(markup_data, markup_len);
246  std::string url;
247
248  if (url_len > 0)
249    url.assign(url_data, url_len);
250
251  std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url);
252  HGLOBAL glob = CreateGlobalData(html_fragment);
253
254  WriteToClipboard(Clipboard::GetHtmlFormatType().ToUINT(), glob);
255}
256
257void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) {
258  WriteData(GetRtfFormatType(), rtf_data, data_len);
259}
260
261void Clipboard::WriteBookmark(const char* title_data,
262                              size_t title_len,
263                              const char* url_data,
264                              size_t url_len) {
265  std::string bookmark(title_data, title_len);
266  bookmark.append(1, L'\n');
267  bookmark.append(url_data, url_len);
268
269  base::string16 wide_bookmark = base::UTF8ToWide(bookmark);
270  HGLOBAL glob = CreateGlobalData(wide_bookmark);
271
272  WriteToClipboard(GetUrlWFormatType().ToUINT(), glob);
273}
274
275void Clipboard::WriteWebSmartPaste() {
276  DCHECK(clipboard_owner_->hwnd() != NULL);
277  ::SetClipboardData(GetWebKitSmartPasteFormatType().ToUINT(), NULL);
278}
279
280void Clipboard::WriteBitmap(const SkBitmap& bitmap) {
281  HDC dc = ::GetDC(NULL);
282
283  // This doesn't actually cost us a memcpy when the bitmap comes from the
284  // renderer as we load it into the bitmap using setPixels which just sets a
285  // pointer.  Someone has to memcpy it into GDI, it might as well be us here.
286
287  // TODO(darin): share data in gfx/bitmap_header.cc somehow
288  BITMAPINFO bm_info = {0};
289  bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
290  bm_info.bmiHeader.biWidth = bitmap.width();
291  bm_info.bmiHeader.biHeight = -bitmap.height();  // sets vertical orientation
292  bm_info.bmiHeader.biPlanes = 1;
293  bm_info.bmiHeader.biBitCount = 32;
294  bm_info.bmiHeader.biCompression = BI_RGB;
295
296  // ::CreateDIBSection allocates memory for us to copy our bitmap into.
297  // Unfortunately, we can't write the created bitmap to the clipboard,
298  // (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
299  void *bits;
300  HBITMAP source_hbitmap =
301      ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
302
303  if (bits && source_hbitmap) {
304    {
305      SkAutoLockPixels bitmap_lock(bitmap);
306      // Copy the bitmap out of shared memory and into GDI
307      memcpy(bits, bitmap.getPixels(), bitmap.getSize());
308    }
309
310    // Now we have an HBITMAP, we can write it to the clipboard
311    WriteBitmapFromHandle(source_hbitmap,
312                          gfx::Size(bitmap.width(), bitmap.height()));
313  }
314
315  ::DeleteObject(source_hbitmap);
316  ::ReleaseDC(NULL, dc);
317}
318
319void Clipboard::WriteBitmapFromHandle(HBITMAP source_hbitmap,
320                                      const gfx::Size& size) {
321  // We would like to just call ::SetClipboardData on the source_hbitmap,
322  // but that bitmap might not be of a sort we can write to the clipboard.
323  // For this reason, we create a new bitmap, copy the bits over, and then
324  // write that to the clipboard.
325
326  HDC dc = ::GetDC(NULL);
327  HDC compatible_dc = ::CreateCompatibleDC(NULL);
328  HDC source_dc = ::CreateCompatibleDC(NULL);
329
330  // This is the HBITMAP we will eventually write to the clipboard
331  HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
332  if (!hbitmap) {
333    // Failed to create the bitmap
334    ::DeleteDC(compatible_dc);
335    ::DeleteDC(source_dc);
336    ::ReleaseDC(NULL, dc);
337    return;
338  }
339
340  HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
341  HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
342
343  // Now we need to blend it into an HBITMAP we can place on the clipboard
344  BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
345  ::GdiAlphaBlend(compatible_dc, 0, 0, size.width(), size.height(),
346                  source_dc, 0, 0, size.width(), size.height(), bf);
347
348  // Clean up all the handles we just opened
349  ::SelectObject(compatible_dc, old_hbitmap);
350  ::SelectObject(source_dc, old_source);
351  ::DeleteObject(old_hbitmap);
352  ::DeleteObject(old_source);
353  ::DeleteDC(compatible_dc);
354  ::DeleteDC(source_dc);
355  ::ReleaseDC(NULL, dc);
356
357  WriteToClipboard(CF_BITMAP, hbitmap);
358}
359
360void Clipboard::WriteData(const FormatType& format,
361                          const char* data_data,
362                          size_t data_len) {
363  HGLOBAL hdata = ::GlobalAlloc(GMEM_MOVEABLE, data_len);
364  if (!hdata)
365    return;
366
367  char* data = static_cast<char*>(::GlobalLock(hdata));
368  memcpy(data, data_data, data_len);
369  ::GlobalUnlock(data);
370  WriteToClipboard(format.ToUINT(), hdata);
371}
372
373void Clipboard::WriteToClipboard(unsigned int format, HANDLE handle) {
374  DCHECK(clipboard_owner_->hwnd() != NULL);
375  if (handle && !::SetClipboardData(format, handle)) {
376    DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError());
377    FreeData(format, handle);
378  }
379}
380
381uint64 Clipboard::GetSequenceNumber(ClipboardType type) {
382  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
383  return ::GetClipboardSequenceNumber();
384}
385
386bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format,
387                                  ClipboardType type) const {
388  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
389  return ::IsClipboardFormatAvailable(format.ToUINT()) != FALSE;
390}
391
392void Clipboard::Clear(ClipboardType type) {
393  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
394  ScopedClipboard clipboard;
395  if (!clipboard.Acquire(GetClipboardWindow()))
396    return;
397
398  ::EmptyClipboard();
399}
400
401void Clipboard::ReadAvailableTypes(ClipboardType type,
402                                   std::vector<base::string16>* types,
403                                   bool* contains_filenames) const {
404  if (!types || !contains_filenames) {
405    NOTREACHED();
406    return;
407  }
408
409  types->clear();
410  if (::IsClipboardFormatAvailable(GetPlainTextFormatType().ToUINT()))
411    types->push_back(base::UTF8ToUTF16(kMimeTypeText));
412  if (::IsClipboardFormatAvailable(GetHtmlFormatType().ToUINT()))
413    types->push_back(base::UTF8ToUTF16(kMimeTypeHTML));
414  if (::IsClipboardFormatAvailable(GetRtfFormatType().ToUINT()))
415    types->push_back(base::UTF8ToUTF16(kMimeTypeRTF));
416  if (::IsClipboardFormatAvailable(CF_DIB))
417    types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
418  *contains_filenames = false;
419
420  // Acquire the clipboard.
421  ScopedClipboard clipboard;
422  if (!clipboard.Acquire(GetClipboardWindow()))
423    return;
424
425  HANDLE hdata = ::GetClipboardData(GetWebCustomDataFormatType().ToUINT());
426  if (!hdata)
427    return;
428
429  ReadCustomDataTypes(::GlobalLock(hdata), ::GlobalSize(hdata), types);
430  ::GlobalUnlock(hdata);
431}
432
433void Clipboard::ReadText(ClipboardType type, base::string16* result) const {
434  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
435  if (!result) {
436    NOTREACHED();
437    return;
438  }
439
440  result->clear();
441
442  // Acquire the clipboard.
443  ScopedClipboard clipboard;
444  if (!clipboard.Acquire(GetClipboardWindow()))
445    return;
446
447  HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
448  if (!data)
449    return;
450
451  result->assign(static_cast<const base::char16*>(::GlobalLock(data)));
452  ::GlobalUnlock(data);
453}
454
455void Clipboard::ReadAsciiText(ClipboardType type, std::string* result) const {
456  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
457  if (!result) {
458    NOTREACHED();
459    return;
460  }
461
462  result->clear();
463
464  // Acquire the clipboard.
465  ScopedClipboard clipboard;
466  if (!clipboard.Acquire(GetClipboardWindow()))
467    return;
468
469  HANDLE data = ::GetClipboardData(CF_TEXT);
470  if (!data)
471    return;
472
473  result->assign(static_cast<const char*>(::GlobalLock(data)));
474  ::GlobalUnlock(data);
475}
476
477void Clipboard::ReadHTML(ClipboardType type,
478                         base::string16* markup,
479                         std::string* src_url,
480                         uint32* fragment_start,
481                         uint32* fragment_end) const {
482  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
483
484  markup->clear();
485  // TODO(dcheng): Remove these checks, I don't think they should be optional.
486  DCHECK(src_url);
487  if (src_url)
488    src_url->clear();
489  *fragment_start = 0;
490  *fragment_end = 0;
491
492  // Acquire the clipboard.
493  ScopedClipboard clipboard;
494  if (!clipboard.Acquire(GetClipboardWindow()))
495    return;
496
497  HANDLE data = ::GetClipboardData(GetHtmlFormatType().ToUINT());
498  if (!data)
499    return;
500
501  std::string cf_html(static_cast<const char*>(::GlobalLock(data)));
502  ::GlobalUnlock(data);
503
504  size_t html_start = std::string::npos;
505  size_t start_index = std::string::npos;
506  size_t end_index = std::string::npos;
507  ClipboardUtil::CFHtmlExtractMetadata(cf_html, src_url, &html_start,
508                                       &start_index, &end_index);
509
510  // This might happen if the contents of the clipboard changed and CF_HTML is
511  // no longer available.
512  if (start_index == std::string::npos ||
513      end_index == std::string::npos ||
514      html_start == std::string::npos)
515    return;
516
517  if (start_index < html_start || end_index < start_index)
518    return;
519
520  std::vector<size_t> offsets;
521  offsets.push_back(start_index - html_start);
522  offsets.push_back(end_index - html_start);
523  markup->assign(base::UTF8ToUTF16AndAdjustOffsets(cf_html.data() + html_start,
524                                                   &offsets));
525  *fragment_start = base::checked_cast<uint32>(offsets[0]);
526  *fragment_end = base::checked_cast<uint32>(offsets[1]);
527}
528
529void Clipboard::ReadRTF(ClipboardType type, std::string* result) const {
530  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
531
532  ReadData(GetRtfFormatType(), result);
533}
534
535SkBitmap Clipboard::ReadImage(ClipboardType type) const {
536  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
537
538  // Acquire the clipboard.
539  ScopedClipboard clipboard;
540  if (!clipboard.Acquire(GetClipboardWindow()))
541    return SkBitmap();
542
543  // We use a DIB rather than a DDB here since ::GetObject() with the
544  // HBITMAP returned from ::GetClipboardData(CF_BITMAP) always reports a color
545  // depth of 32bpp.
546  BITMAPINFO* bitmap = static_cast<BITMAPINFO*>(::GetClipboardData(CF_DIB));
547  if (!bitmap)
548    return SkBitmap();
549  int color_table_length = 0;
550  switch (bitmap->bmiHeader.biBitCount) {
551    case 1:
552    case 4:
553    case 8:
554      color_table_length = bitmap->bmiHeader.biClrUsed
555          ? bitmap->bmiHeader.biClrUsed
556          : 1 << bitmap->bmiHeader.biBitCount;
557      break;
558    case 16:
559    case 32:
560      if (bitmap->bmiHeader.biCompression == BI_BITFIELDS)
561        color_table_length = 3;
562      break;
563    case 24:
564      break;
565    default:
566      NOTREACHED();
567  }
568  const void* bitmap_bits = reinterpret_cast<const char*>(bitmap)
569      + bitmap->bmiHeader.biSize + color_table_length * sizeof(RGBQUAD);
570
571  gfx::Canvas canvas(gfx::Size(bitmap->bmiHeader.biWidth,
572                               bitmap->bmiHeader.biHeight),
573                     1.0f,
574                     false);
575  {
576    skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
577    HDC dc = scoped_platform_paint.GetPlatformSurface();
578    ::SetDIBitsToDevice(dc, 0, 0, bitmap->bmiHeader.biWidth,
579                        bitmap->bmiHeader.biHeight, 0, 0, 0,
580                        bitmap->bmiHeader.biHeight, bitmap_bits, bitmap,
581                        DIB_RGB_COLORS);
582  }
583  // Windows doesn't really handle alpha channels well in many situations. When
584  // the source image is < 32 bpp, we force the bitmap to be opaque. When the
585  // source image is 32 bpp, the alpha channel might still contain garbage data.
586  // Since Windows uses premultiplied alpha, we scan for instances where
587  // (R, G, B) > A. If there are any invalid premultiplied colors in the image,
588  // we assume the alpha channel contains garbage and force the bitmap to be
589  // opaque as well. Note that this  heuristic will fail on a transparent bitmap
590  // containing only black pixels...
591  const SkBitmap& device_bitmap =
592      canvas.sk_canvas()->getDevice()->accessBitmap(true);
593  {
594    SkAutoLockPixels lock(device_bitmap);
595    bool has_invalid_alpha_channel = bitmap->bmiHeader.biBitCount < 32 ||
596        BitmapHasInvalidPremultipliedColors(device_bitmap);
597    if (has_invalid_alpha_channel) {
598      MakeBitmapOpaque(device_bitmap);
599    }
600  }
601
602  return canvas.ExtractImageRep().sk_bitmap();
603}
604
605void Clipboard::ReadCustomData(ClipboardType clipboard_type,
606                               const base::string16& type,
607                               base::string16* result) const {
608  DCHECK_EQ(clipboard_type, CLIPBOARD_TYPE_COPY_PASTE);
609
610  // Acquire the clipboard.
611  ScopedClipboard clipboard;
612  if (!clipboard.Acquire(GetClipboardWindow()))
613    return;
614
615  HANDLE hdata = ::GetClipboardData(GetWebCustomDataFormatType().ToUINT());
616  if (!hdata)
617    return;
618
619  ReadCustomDataForType(::GlobalLock(hdata), ::GlobalSize(hdata), type, result);
620  ::GlobalUnlock(hdata);
621}
622
623void Clipboard::ReadBookmark(base::string16* title, std::string* url) const {
624  if (title)
625    title->clear();
626
627  if (url)
628    url->clear();
629
630  // Acquire the clipboard.
631  ScopedClipboard clipboard;
632  if (!clipboard.Acquire(GetClipboardWindow()))
633    return;
634
635  HANDLE data = ::GetClipboardData(GetUrlWFormatType().ToUINT());
636  if (!data)
637    return;
638
639  base::string16 bookmark(static_cast<const base::char16*>(::GlobalLock(data)));
640  ::GlobalUnlock(data);
641
642  ParseBookmarkClipboardFormat(bookmark, title, url);
643}
644
645void Clipboard::ReadData(const FormatType& format, std::string* result) const {
646  if (!result) {
647    NOTREACHED();
648    return;
649  }
650
651  ScopedClipboard clipboard;
652  if (!clipboard.Acquire(GetClipboardWindow()))
653    return;
654
655  HANDLE data = ::GetClipboardData(format.ToUINT());
656  if (!data)
657    return;
658
659  result->assign(static_cast<const char*>(::GlobalLock(data)),
660                 ::GlobalSize(data));
661  ::GlobalUnlock(data);
662}
663
664// static
665void Clipboard::ParseBookmarkClipboardFormat(const base::string16& bookmark,
666                                             base::string16* title,
667                                             std::string* url) {
668  const base::string16 kDelim = base::ASCIIToUTF16("\r\n");
669
670  const size_t title_end = bookmark.find_first_of(kDelim);
671  if (title)
672    title->assign(bookmark.substr(0, title_end));
673
674  if (url) {
675    const size_t url_start = bookmark.find_first_not_of(kDelim, title_end);
676    if (url_start != base::string16::npos) {
677      *url = base::UTF16ToUTF8(
678          bookmark.substr(url_start, base::string16::npos));
679    }
680  }
681}
682
683// static
684Clipboard::FormatType Clipboard::GetFormatType(
685    const std::string& format_string) {
686  return FormatType(
687      ::RegisterClipboardFormat(base::ASCIIToWide(format_string).c_str()));
688}
689
690// static
691const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
692  CR_DEFINE_STATIC_LOCAL(
693      FormatType, type, (::RegisterClipboardFormat(CFSTR_INETURLA)));
694  return type;
695}
696
697// static
698const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
699  CR_DEFINE_STATIC_LOCAL(
700      FormatType, type, (::RegisterClipboardFormat(CFSTR_INETURLW)));
701  return type;
702}
703
704// static
705const Clipboard::FormatType& Clipboard::GetMozUrlFormatType() {
706  CR_DEFINE_STATIC_LOCAL(
707      FormatType, type, (::RegisterClipboardFormat(L"text/x-moz-url")));
708  return type;
709}
710
711// static
712const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
713  CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_TEXT));
714  return type;
715}
716
717// static
718const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
719  CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_UNICODETEXT));
720  return type;
721}
722
723// static
724const Clipboard::FormatType& Clipboard::GetFilenameFormatType() {
725  CR_DEFINE_STATIC_LOCAL(
726      FormatType, type, (::RegisterClipboardFormat(CFSTR_FILENAMEA)));
727  return type;
728}
729
730// static
731const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() {
732  CR_DEFINE_STATIC_LOCAL(
733      FormatType, type, (::RegisterClipboardFormat(CFSTR_FILENAMEW)));
734  return type;
735}
736
737// MS HTML Format
738// static
739const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
740  CR_DEFINE_STATIC_LOCAL(
741      FormatType, type, (::RegisterClipboardFormat(L"HTML Format")));
742  return type;
743}
744
745// MS RTF Format
746// static
747const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
748  CR_DEFINE_STATIC_LOCAL(
749      FormatType, type, (::RegisterClipboardFormat(L"Rich Text Format")));
750  return type;
751}
752
753// static
754const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
755  CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_BITMAP));
756  return type;
757}
758
759// Firefox text/html
760// static
761const Clipboard::FormatType& Clipboard::GetTextHtmlFormatType() {
762  CR_DEFINE_STATIC_LOCAL(
763      FormatType, type, (::RegisterClipboardFormat(L"text/html")));
764  return type;
765}
766
767// static
768const Clipboard::FormatType& Clipboard::GetCFHDropFormatType() {
769  CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_HDROP));
770  return type;
771}
772
773// static
774const Clipboard::FormatType& Clipboard::GetFileDescriptorFormatType() {
775  CR_DEFINE_STATIC_LOCAL(
776      FormatType, type, (::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR)));
777  return type;
778}
779
780// static
781const Clipboard::FormatType& Clipboard::GetFileContentZeroFormatType() {
782  CR_DEFINE_STATIC_LOCAL(
783      FormatType, type, (::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0));
784  return type;
785}
786
787// static
788const Clipboard::FormatType& Clipboard::GetIDListFormatType() {
789  CR_DEFINE_STATIC_LOCAL(
790      FormatType, type, (::RegisterClipboardFormat(CFSTR_SHELLIDLIST)));
791  return type;
792}
793
794// static
795const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
796  CR_DEFINE_STATIC_LOCAL(
797      FormatType,
798      type,
799      (::RegisterClipboardFormat(L"WebKit Smart Paste Format")));
800  return type;
801}
802
803// static
804const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
805  // TODO(dcheng): This name is temporary. See http://crbug.com/106449.
806  CR_DEFINE_STATIC_LOCAL(
807      FormatType,
808      type,
809      (::RegisterClipboardFormat(L"Chromium Web Custom MIME Data Format")));
810  return type;
811}
812
813// static
814const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
815  CR_DEFINE_STATIC_LOCAL(
816      FormatType,
817      type,
818      (::RegisterClipboardFormat(L"Chromium Pepper MIME Data Format")));
819  return type;
820}
821
822// static
823void Clipboard::FreeData(unsigned int format, HANDLE data) {
824  if (format == CF_BITMAP)
825    ::DeleteObject(static_cast<HBITMAP>(data));
826  else
827    ::GlobalFree(data);
828}
829
830HWND Clipboard::GetClipboardWindow() const {
831  if (!clipboard_owner_)
832    return NULL;
833
834  if (clipboard_owner_->hwnd() == NULL)
835    clipboard_owner_->Create(base::Bind(&ClipboardOwnerWndProc));
836
837  return clipboard_owner_->hwnd();
838}
839
840}  // namespace ui
841