Home | History | Annotate | Download | only in clipboard
      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/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/safe_numerics.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 
     34 namespace ui {
     35 
     36 namespace {
     37 
     38 // A scoper to manage acquiring and automatically releasing the clipboard.
     39 class 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 
     98 bool 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 
    128 template <typename charT>
    129 HGLOBAL 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 
    141 bool 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 
    154 void 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 
    164 Clipboard::FormatType::FormatType() : data_() {}
    165 
    166 Clipboard::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 
    175 Clipboard::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 
    184 Clipboard::FormatType::~FormatType() {
    185 }
    186 
    187 std::string Clipboard::FormatType::Serialize() const {
    188   return base::IntToString(data_.cfFormat);
    189 }
    190 
    191 // static
    192 Clipboard::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 
    202 bool Clipboard::FormatType::operator<(const FormatType& other) const {
    203   return ToUINT() < other.ToUINT();
    204 }
    205 
    206 Clipboard::Clipboard() {
    207   if (base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI)
    208     clipboard_owner_.reset(new base::win::MessageWindow());
    209 }
    210 
    211 Clipboard::~Clipboard() {
    212 }
    213 
    214 void Clipboard::WriteObjects(ClipboardType type, const ObjectMap& objects) {
    215   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
    216 
    217   ScopedClipboard clipboard;
    218   if (!clipboard.Acquire(GetClipboardWindow()))
    219     return;
    220 
    221   ::EmptyClipboard();
    222 
    223   for (ObjectMap::const_iterator iter = objects.begin();
    224        iter != objects.end(); ++iter) {
    225     DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
    226   }
    227 }
    228 
    229 void Clipboard::WriteText(const char* text_data, size_t text_len) {
    230   string16 text;
    231   UTF8ToUTF16(text_data, text_len, &text);
    232   HGLOBAL glob = CreateGlobalData(text);
    233 
    234   WriteToClipboard(CF_UNICODETEXT, glob);
    235 }
    236 
    237 void Clipboard::WriteHTML(const char* markup_data,
    238                           size_t markup_len,
    239                           const char* url_data,
    240                           size_t url_len) {
    241   std::string markup(markup_data, markup_len);
    242   std::string url;
    243 
    244   if (url_len > 0)
    245     url.assign(url_data, url_len);
    246 
    247   std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url);
    248   HGLOBAL glob = CreateGlobalData(html_fragment);
    249 
    250   WriteToClipboard(Clipboard::GetHtmlFormatType().ToUINT(), glob);
    251 }
    252 
    253 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) {
    254   WriteData(GetRtfFormatType(), rtf_data, data_len);
    255 }
    256 
    257 void Clipboard::WriteBookmark(const char* title_data,
    258                               size_t title_len,
    259                               const char* url_data,
    260                               size_t url_len) {
    261   std::string bookmark(title_data, title_len);
    262   bookmark.append(1, L'\n');
    263   bookmark.append(url_data, url_len);
    264 
    265   string16 wide_bookmark = UTF8ToWide(bookmark);
    266   HGLOBAL glob = CreateGlobalData(wide_bookmark);
    267 
    268   WriteToClipboard(GetUrlWFormatType().ToUINT(), glob);
    269 }
    270 
    271 void Clipboard::WriteWebSmartPaste() {
    272   DCHECK(clipboard_owner_->hwnd() != NULL);
    273   ::SetClipboardData(GetWebKitSmartPasteFormatType().ToUINT(), NULL);
    274 }
    275 
    276 void Clipboard::WriteBitmap(const SkBitmap& bitmap) {
    277   HDC dc = ::GetDC(NULL);
    278 
    279   // This doesn't actually cost us a memcpy when the bitmap comes from the
    280   // renderer as we load it into the bitmap using setPixels which just sets a
    281   // pointer.  Someone has to memcpy it into GDI, it might as well be us here.
    282 
    283   // TODO(darin): share data in gfx/bitmap_header.cc somehow
    284   BITMAPINFO bm_info = {0};
    285   bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    286   bm_info.bmiHeader.biWidth = bitmap.width();
    287   bm_info.bmiHeader.biHeight = -bitmap.height();  // sets vertical orientation
    288   bm_info.bmiHeader.biPlanes = 1;
    289   bm_info.bmiHeader.biBitCount = 32;
    290   bm_info.bmiHeader.biCompression = BI_RGB;
    291 
    292   // ::CreateDIBSection allocates memory for us to copy our bitmap into.
    293   // Unfortunately, we can't write the created bitmap to the clipboard,
    294   // (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
    295   void *bits;
    296   HBITMAP source_hbitmap =
    297       ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
    298 
    299   if (bits && source_hbitmap) {
    300     {
    301       SkAutoLockPixels bitmap_lock(bitmap);
    302       // Copy the bitmap out of shared memory and into GDI
    303       memcpy(bits, bitmap.getPixels(), bitmap.getSize());
    304     }
    305 
    306     // Now we have an HBITMAP, we can write it to the clipboard
    307     WriteBitmapFromHandle(source_hbitmap,
    308                           gfx::Size(bitmap.width(), bitmap.height()));
    309   }
    310 
    311   ::DeleteObject(source_hbitmap);
    312   ::ReleaseDC(NULL, dc);
    313 }
    314 
    315 void Clipboard::WriteBitmapFromHandle(HBITMAP source_hbitmap,
    316                                       const gfx::Size& size) {
    317   // We would like to just call ::SetClipboardData on the source_hbitmap,
    318   // but that bitmap might not be of a sort we can write to the clipboard.
    319   // For this reason, we create a new bitmap, copy the bits over, and then
    320   // write that to the clipboard.
    321 
    322   HDC dc = ::GetDC(NULL);
    323   HDC compatible_dc = ::CreateCompatibleDC(NULL);
    324   HDC source_dc = ::CreateCompatibleDC(NULL);
    325 
    326   // This is the HBITMAP we will eventually write to the clipboard
    327   HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
    328   if (!hbitmap) {
    329     // Failed to create the bitmap
    330     ::DeleteDC(compatible_dc);
    331     ::DeleteDC(source_dc);
    332     ::ReleaseDC(NULL, dc);
    333     return;
    334   }
    335 
    336   HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
    337   HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
    338 
    339   // Now we need to blend it into an HBITMAP we can place on the clipboard
    340   BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
    341   ::GdiAlphaBlend(compatible_dc, 0, 0, size.width(), size.height(),
    342                   source_dc, 0, 0, size.width(), size.height(), bf);
    343 
    344   // Clean up all the handles we just opened
    345   ::SelectObject(compatible_dc, old_hbitmap);
    346   ::SelectObject(source_dc, old_source);
    347   ::DeleteObject(old_hbitmap);
    348   ::DeleteObject(old_source);
    349   ::DeleteDC(compatible_dc);
    350   ::DeleteDC(source_dc);
    351   ::ReleaseDC(NULL, dc);
    352 
    353   WriteToClipboard(CF_BITMAP, hbitmap);
    354 }
    355 
    356 void Clipboard::WriteData(const FormatType& format,
    357                           const char* data_data,
    358                           size_t data_len) {
    359   HGLOBAL hdata = ::GlobalAlloc(GMEM_MOVEABLE, data_len);
    360   if (!hdata)
    361     return;
    362 
    363   char* data = static_cast<char*>(::GlobalLock(hdata));
    364   memcpy(data, data_data, data_len);
    365   ::GlobalUnlock(data);
    366   WriteToClipboard(format.ToUINT(), hdata);
    367 }
    368 
    369 void Clipboard::WriteToClipboard(unsigned int format, HANDLE handle) {
    370   DCHECK(clipboard_owner_->hwnd() != NULL);
    371   if (handle && !::SetClipboardData(format, handle)) {
    372     DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError());
    373     FreeData(format, handle);
    374   }
    375 }
    376 
    377 uint64 Clipboard::GetSequenceNumber(ClipboardType type) {
    378   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
    379   return ::GetClipboardSequenceNumber();
    380 }
    381 
    382 bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format,
    383                                   ClipboardType type) const {
    384   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
    385   return ::IsClipboardFormatAvailable(format.ToUINT()) != FALSE;
    386 }
    387 
    388 void Clipboard::Clear(ClipboardType type) {
    389   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
    390   ScopedClipboard clipboard;
    391   if (!clipboard.Acquire(GetClipboardWindow()))
    392     return;
    393 
    394   ::EmptyClipboard();
    395 }
    396 
    397 void Clipboard::ReadAvailableTypes(ClipboardType type,
    398                                    std::vector<string16>* types,
    399                                    bool* contains_filenames) const {
    400   if (!types || !contains_filenames) {
    401     NOTREACHED();
    402     return;
    403   }
    404 
    405   types->clear();
    406   if (::IsClipboardFormatAvailable(GetPlainTextFormatType().ToUINT()))
    407     types->push_back(UTF8ToUTF16(kMimeTypeText));
    408   if (::IsClipboardFormatAvailable(GetHtmlFormatType().ToUINT()))
    409     types->push_back(UTF8ToUTF16(kMimeTypeHTML));
    410   if (::IsClipboardFormatAvailable(GetRtfFormatType().ToUINT()))
    411     types->push_back(UTF8ToUTF16(kMimeTypeRTF));
    412   if (::IsClipboardFormatAvailable(CF_DIB))
    413     types->push_back(UTF8ToUTF16(kMimeTypePNG));
    414   *contains_filenames = false;
    415 
    416   // Acquire the clipboard.
    417   ScopedClipboard clipboard;
    418   if (!clipboard.Acquire(GetClipboardWindow()))
    419     return;
    420 
    421   HANDLE hdata = ::GetClipboardData(GetWebCustomDataFormatType().ToUINT());
    422   if (!hdata)
    423     return;
    424 
    425   ReadCustomDataTypes(::GlobalLock(hdata), ::GlobalSize(hdata), types);
    426   ::GlobalUnlock(hdata);
    427 }
    428 
    429 void Clipboard::ReadText(ClipboardType type, string16* result) const {
    430   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
    431   if (!result) {
    432     NOTREACHED();
    433     return;
    434   }
    435 
    436   result->clear();
    437 
    438   // Acquire the clipboard.
    439   ScopedClipboard clipboard;
    440   if (!clipboard.Acquire(GetClipboardWindow()))
    441     return;
    442 
    443   HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
    444   if (!data)
    445     return;
    446 
    447   result->assign(static_cast<const char16*>(::GlobalLock(data)));
    448   ::GlobalUnlock(data);
    449 }
    450 
    451 void Clipboard::ReadAsciiText(ClipboardType type, std::string* result) const {
    452   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
    453   if (!result) {
    454     NOTREACHED();
    455     return;
    456   }
    457 
    458   result->clear();
    459 
    460   // Acquire the clipboard.
    461   ScopedClipboard clipboard;
    462   if (!clipboard.Acquire(GetClipboardWindow()))
    463     return;
    464 
    465   HANDLE data = ::GetClipboardData(CF_TEXT);
    466   if (!data)
    467     return;
    468 
    469   result->assign(static_cast<const char*>(::GlobalLock(data)));
    470   ::GlobalUnlock(data);
    471 }
    472 
    473 void Clipboard::ReadHTML(ClipboardType type,
    474                          string16* markup,
    475                          std::string* src_url,
    476                          uint32* fragment_start,
    477                          uint32* fragment_end) const {
    478   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
    479 
    480   markup->clear();
    481   // TODO(dcheng): Remove these checks, I don't think they should be optional.
    482   DCHECK(src_url);
    483   if (src_url)
    484     src_url->clear();
    485   *fragment_start = 0;
    486   *fragment_end = 0;
    487 
    488   // Acquire the clipboard.
    489   ScopedClipboard clipboard;
    490   if (!clipboard.Acquire(GetClipboardWindow()))
    491     return;
    492 
    493   HANDLE data = ::GetClipboardData(GetHtmlFormatType().ToUINT());
    494   if (!data)
    495     return;
    496 
    497   std::string cf_html(static_cast<const char*>(::GlobalLock(data)));
    498   ::GlobalUnlock(data);
    499 
    500   size_t html_start = std::string::npos;
    501   size_t start_index = std::string::npos;
    502   size_t end_index = std::string::npos;
    503   ClipboardUtil::CFHtmlExtractMetadata(cf_html, src_url, &html_start,
    504                                        &start_index, &end_index);
    505 
    506   // This might happen if the contents of the clipboard changed and CF_HTML is
    507   // no longer available.
    508   if (start_index == std::string::npos ||
    509       end_index == std::string::npos ||
    510       html_start == std::string::npos)
    511     return;
    512 
    513   if (start_index < html_start || end_index < start_index)
    514     return;
    515 
    516   std::vector<size_t> offsets;
    517   offsets.push_back(start_index - html_start);
    518   offsets.push_back(end_index - html_start);
    519   markup->assign(base::UTF8ToUTF16AndAdjustOffsets(cf_html.data() + html_start,
    520                                                    &offsets));
    521   *fragment_start = base::checked_numeric_cast<uint32>(offsets[0]);
    522   *fragment_end = base::checked_numeric_cast<uint32>(offsets[1]);
    523 }
    524 
    525 void Clipboard::ReadRTF(ClipboardType type, std::string* result) const {
    526   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
    527 
    528   ReadData(GetRtfFormatType(), result);
    529 }
    530 
    531 SkBitmap Clipboard::ReadImage(ClipboardType type) const {
    532   DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
    533 
    534   // Acquire the clipboard.
    535   ScopedClipboard clipboard;
    536   if (!clipboard.Acquire(GetClipboardWindow()))
    537     return SkBitmap();
    538 
    539   // We use a DIB rather than a DDB here since ::GetObject() with the
    540   // HBITMAP returned from ::GetClipboardData(CF_BITMAP) always reports a color
    541   // depth of 32bpp.
    542   BITMAPINFO* bitmap = static_cast<BITMAPINFO*>(::GetClipboardData(CF_DIB));
    543   if (!bitmap)
    544     return SkBitmap();
    545   int color_table_length = 0;
    546   switch (bitmap->bmiHeader.biBitCount) {
    547     case 1:
    548     case 4:
    549     case 8:
    550       color_table_length = bitmap->bmiHeader.biClrUsed
    551           ? bitmap->bmiHeader.biClrUsed
    552           : 1 << bitmap->bmiHeader.biBitCount;
    553       break;
    554     case 16:
    555     case 32:
    556       if (bitmap->bmiHeader.biCompression == BI_BITFIELDS)
    557         color_table_length = 3;
    558       break;
    559     case 24:
    560       break;
    561     default:
    562       NOTREACHED();
    563   }
    564   const void* bitmap_bits = reinterpret_cast<const char*>(bitmap)
    565       + bitmap->bmiHeader.biSize + color_table_length * sizeof(RGBQUAD);
    566 
    567   gfx::Canvas canvas(gfx::Size(bitmap->bmiHeader.biWidth,
    568                                bitmap->bmiHeader.biHeight),
    569                      1.0f,
    570                      false);
    571   {
    572     skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
    573     HDC dc = scoped_platform_paint.GetPlatformSurface();
    574     ::SetDIBitsToDevice(dc, 0, 0, bitmap->bmiHeader.biWidth,
    575                         bitmap->bmiHeader.biHeight, 0, 0, 0,
    576                         bitmap->bmiHeader.biHeight, bitmap_bits, bitmap,
    577                         DIB_RGB_COLORS);
    578   }
    579   // Windows doesn't really handle alpha channels well in many situations. When
    580   // the source image is < 32 bpp, we force the bitmap to be opaque. When the
    581   // source image is 32 bpp, the alpha channel might still contain garbage data.
    582   // Since Windows uses premultiplied alpha, we scan for instances where
    583   // (R, G, B) > A. If there are any invalid premultiplied colors in the image,
    584   // we assume the alpha channel contains garbage and force the bitmap to be
    585   // opaque as well. Note that this  heuristic will fail on a transparent bitmap
    586   // containing only black pixels...
    587   const SkBitmap& device_bitmap =
    588       canvas.sk_canvas()->getDevice()->accessBitmap(true);
    589   {
    590     SkAutoLockPixels lock(device_bitmap);
    591     bool has_invalid_alpha_channel = bitmap->bmiHeader.biBitCount < 32 ||
    592         BitmapHasInvalidPremultipliedColors(device_bitmap);
    593     if (has_invalid_alpha_channel) {
    594       MakeBitmapOpaque(device_bitmap);
    595     }
    596   }
    597 
    598   return canvas.ExtractImageRep().sk_bitmap();
    599 }
    600 
    601 void Clipboard::ReadCustomData(ClipboardType clipboard_type,
    602                                const string16& type,
    603                                string16* result) const {
    604   DCHECK_EQ(clipboard_type, CLIPBOARD_TYPE_COPY_PASTE);
    605 
    606   // Acquire the clipboard.
    607   ScopedClipboard clipboard;
    608   if (!clipboard.Acquire(GetClipboardWindow()))
    609     return;
    610 
    611   HANDLE hdata = ::GetClipboardData(GetWebCustomDataFormatType().ToUINT());
    612   if (!hdata)
    613     return;
    614 
    615   ReadCustomDataForType(::GlobalLock(hdata), ::GlobalSize(hdata), type, result);
    616   ::GlobalUnlock(hdata);
    617 }
    618 
    619 void Clipboard::ReadBookmark(string16* title, std::string* url) const {
    620   if (title)
    621     title->clear();
    622 
    623   if (url)
    624     url->clear();
    625 
    626   // Acquire the clipboard.
    627   ScopedClipboard clipboard;
    628   if (!clipboard.Acquire(GetClipboardWindow()))
    629     return;
    630 
    631   HANDLE data = ::GetClipboardData(GetUrlWFormatType().ToUINT());
    632   if (!data)
    633     return;
    634 
    635   string16 bookmark(static_cast<const char16*>(::GlobalLock(data)));
    636   ::GlobalUnlock(data);
    637 
    638   ParseBookmarkClipboardFormat(bookmark, title, url);
    639 }
    640 
    641 void Clipboard::ReadData(const FormatType& format, std::string* result) const {
    642   if (!result) {
    643     NOTREACHED();
    644     return;
    645   }
    646 
    647   ScopedClipboard clipboard;
    648   if (!clipboard.Acquire(GetClipboardWindow()))
    649     return;
    650 
    651   HANDLE data = ::GetClipboardData(format.ToUINT());
    652   if (!data)
    653     return;
    654 
    655   result->assign(static_cast<const char*>(::GlobalLock(data)),
    656                  ::GlobalSize(data));
    657   ::GlobalUnlock(data);
    658 }
    659 
    660 // static
    661 void Clipboard::ParseBookmarkClipboardFormat(const string16& bookmark,
    662                                              string16* title,
    663                                              std::string* url) {
    664   const string16 kDelim = ASCIIToUTF16("\r\n");
    665 
    666   const size_t title_end = bookmark.find_first_of(kDelim);
    667   if (title)
    668     title->assign(bookmark.substr(0, title_end));
    669 
    670   if (url) {
    671     const size_t url_start = bookmark.find_first_not_of(kDelim, title_end);
    672     if (url_start != string16::npos)
    673       *url = UTF16ToUTF8(bookmark.substr(url_start, string16::npos));
    674   }
    675 }
    676 
    677 // static
    678 Clipboard::FormatType Clipboard::GetFormatType(
    679     const std::string& format_string) {
    680   return FormatType(
    681       ::RegisterClipboardFormat(ASCIIToWide(format_string).c_str()));
    682 }
    683 
    684 // static
    685 const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
    686   CR_DEFINE_STATIC_LOCAL(
    687       FormatType, type, (::RegisterClipboardFormat(CFSTR_INETURLA)));
    688   return type;
    689 }
    690 
    691 // static
    692 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
    693   CR_DEFINE_STATIC_LOCAL(
    694       FormatType, type, (::RegisterClipboardFormat(CFSTR_INETURLW)));
    695   return type;
    696 }
    697 
    698 // static
    699 const Clipboard::FormatType& Clipboard::GetMozUrlFormatType() {
    700   CR_DEFINE_STATIC_LOCAL(
    701       FormatType, type, (::RegisterClipboardFormat(L"text/x-moz-url")));
    702   return type;
    703 }
    704 
    705 // static
    706 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
    707   CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_TEXT));
    708   return type;
    709 }
    710 
    711 // static
    712 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
    713   CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_UNICODETEXT));
    714   return type;
    715 }
    716 
    717 // static
    718 const Clipboard::FormatType& Clipboard::GetFilenameFormatType() {
    719   CR_DEFINE_STATIC_LOCAL(
    720       FormatType, type, (::RegisterClipboardFormat(CFSTR_FILENAMEA)));
    721   return type;
    722 }
    723 
    724 // static
    725 const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() {
    726   CR_DEFINE_STATIC_LOCAL(
    727       FormatType, type, (::RegisterClipboardFormat(CFSTR_FILENAMEW)));
    728   return type;
    729 }
    730 
    731 // MS HTML Format
    732 // static
    733 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
    734   CR_DEFINE_STATIC_LOCAL(
    735       FormatType, type, (::RegisterClipboardFormat(L"HTML Format")));
    736   return type;
    737 }
    738 
    739 // MS RTF Format
    740 // static
    741 const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
    742   CR_DEFINE_STATIC_LOCAL(
    743       FormatType, type, (::RegisterClipboardFormat(L"Rich Text Format")));
    744   return type;
    745 }
    746 
    747 // static
    748 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
    749   CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_BITMAP));
    750   return type;
    751 }
    752 
    753 // Firefox text/html
    754 // static
    755 const Clipboard::FormatType& Clipboard::GetTextHtmlFormatType() {
    756   CR_DEFINE_STATIC_LOCAL(
    757       FormatType, type, (::RegisterClipboardFormat(L"text/html")));
    758   return type;
    759 }
    760 
    761 // static
    762 const Clipboard::FormatType& Clipboard::GetCFHDropFormatType() {
    763   CR_DEFINE_STATIC_LOCAL(FormatType, type, (CF_HDROP));
    764   return type;
    765 }
    766 
    767 // static
    768 const Clipboard::FormatType& Clipboard::GetFileDescriptorFormatType() {
    769   CR_DEFINE_STATIC_LOCAL(
    770       FormatType, type, (::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR)));
    771   return type;
    772 }
    773 
    774 // static
    775 const Clipboard::FormatType& Clipboard::GetFileContentZeroFormatType() {
    776   CR_DEFINE_STATIC_LOCAL(
    777       FormatType, type, (::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0));
    778   return type;
    779 }
    780 
    781 // static
    782 const Clipboard::FormatType& Clipboard::GetIDListFormatType() {
    783   CR_DEFINE_STATIC_LOCAL(
    784       FormatType, type, (::RegisterClipboardFormat(CFSTR_SHELLIDLIST)));
    785   return type;
    786 }
    787 
    788 // static
    789 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
    790   CR_DEFINE_STATIC_LOCAL(
    791       FormatType,
    792       type,
    793       (::RegisterClipboardFormat(L"WebKit Smart Paste Format")));
    794   return type;
    795 }
    796 
    797 // static
    798 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
    799   // TODO(dcheng): This name is temporary. See http://crbug.com/106449.
    800   CR_DEFINE_STATIC_LOCAL(
    801       FormatType,
    802       type,
    803       (::RegisterClipboardFormat(L"Chromium Web Custom MIME Data Format")));
    804   return type;
    805 }
    806 
    807 // static
    808 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
    809   CR_DEFINE_STATIC_LOCAL(
    810       FormatType,
    811       type,
    812       (::RegisterClipboardFormat(L"Chromium Pepper MIME Data Format")));
    813   return type;
    814 }
    815 
    816 // static
    817 void Clipboard::FreeData(unsigned int format, HANDLE data) {
    818   if (format == CF_BITMAP)
    819     ::DeleteObject(static_cast<HBITMAP>(data));
    820   else
    821     ::GlobalFree(data);
    822 }
    823 
    824 HWND Clipboard::GetClipboardWindow() const {
    825   if (!clipboard_owner_)
    826     return NULL;
    827 
    828   if (clipboard_owner_->hwnd() == NULL)
    829     clipboard_owner_->Create(base::Bind(&ClipboardOwnerWndProc));
    830 
    831   return clipboard_owner_->hwnd();
    832 }
    833 
    834 }  // namespace ui
    835