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