summaryrefslogtreecommitdiffstats
path: root/app/clipboard/clipboard_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'app/clipboard/clipboard_win.cc')
-rw-r--r--app/clipboard/clipboard_win.cc686
1 files changed, 686 insertions, 0 deletions
diff --git a/app/clipboard/clipboard_win.cc b/app/clipboard/clipboard_win.cc
new file mode 100644
index 0000000..288c820
--- /dev/null
+++ b/app/clipboard/clipboard_win.cc
@@ -0,0 +1,686 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Many of these functions are based on those found in
+// webkit/port/platform/PasteboardWin.cpp
+
+#include "app/clipboard/clipboard.h"
+
+#include <shlobj.h>
+#include <shellapi.h>
+
+#include "app/clipboard/clipboard_util_win.h"
+#include "base/file_path.h"
+#include "base/gfx/size.h"
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/shared_memory.h"
+#include "base/string_util.h"
+
+namespace {
+
+// A scoper to manage acquiring and automatically releasing the clipboard.
+class ScopedClipboard {
+ public:
+ ScopedClipboard() : opened_(false) { }
+
+ ~ScopedClipboard() {
+ if (opened_)
+ Release();
+ }
+
+ bool Acquire(HWND owner) {
+ const int kMaxAttemptsToOpenClipboard = 5;
+
+ if (opened_) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Attempt to open the clipboard, which will acquire the Windows clipboard
+ // lock. This may fail if another process currently holds this lock.
+ // We're willing to try a few times in the hopes of acquiring it.
+ //
+ // This turns out to be an issue when using remote desktop because the
+ // rdpclip.exe process likes to read what we've written to the clipboard and
+ // send it to the RDP client. If we open and close the clipboard in quick
+ // succession, we might be trying to open it while rdpclip.exe has it open,
+ // See Bug 815425.
+ //
+ // In fact, we believe we'll only spin this loop over remote desktop. In
+ // normal situations, the user is initiating clipboard operations and there
+ // shouldn't be contention.
+
+ for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) {
+ // If we didn't manage to open the clipboard, sleep a bit and be hopeful.
+ if (attempts != 0)
+ ::Sleep(5);
+
+ if (::OpenClipboard(owner)) {
+ opened_ = true;
+ return true;
+ }
+ }
+
+ // We failed to acquire the clipboard.
+ return false;
+ }
+
+ void Release() {
+ if (opened_) {
+ ::CloseClipboard();
+ opened_ = false;
+ } else {
+ NOTREACHED();
+ }
+ }
+
+ private:
+ bool opened_;
+};
+
+LRESULT CALLBACK ClipboardOwnerWndProc(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ LRESULT lresult = 0;
+
+ switch (message) {
+ case WM_RENDERFORMAT:
+ // This message comes when SetClipboardData was sent a null data handle
+ // and now it's come time to put the data on the clipboard.
+ // We always set data, so there isn't a need to actually do anything here.
+ break;
+ case WM_RENDERALLFORMATS:
+ // This message comes when SetClipboardData was sent a null data handle
+ // and now this application is about to quit, so it must put data on
+ // the clipboard before it exits.
+ // We always set data, so there isn't a need to actually do anything here.
+ break;
+ case WM_DRAWCLIPBOARD:
+ break;
+ case WM_DESTROY:
+ break;
+ case WM_CHANGECBCHAIN:
+ break;
+ default:
+ lresult = DefWindowProc(hwnd, message, wparam, lparam);
+ break;
+ }
+ return lresult;
+}
+
+template <typename charT>
+HGLOBAL CreateGlobalData(const std::basic_string<charT>& str) {
+ HGLOBAL data =
+ ::GlobalAlloc(GMEM_MOVEABLE, ((str.size() + 1) * sizeof(charT)));
+ if (data) {
+ charT* raw_data = static_cast<charT*>(::GlobalLock(data));
+ memcpy(raw_data, str.data(), str.size() * sizeof(charT));
+ raw_data[str.size()] = '\0';
+ ::GlobalUnlock(data);
+ }
+ return data;
+};
+
+} // namespace
+
+Clipboard::Clipboard() : create_window_(false) {
+ if (MessageLoop::current()->type() == MessageLoop::TYPE_UI) {
+ // Make a dummy HWND to be the clipboard's owner.
+ WNDCLASSEX wcex = {0};
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.lpfnWndProc = ClipboardOwnerWndProc;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.lpszClassName = L"ClipboardOwnerWindowClass";
+ ::RegisterClassEx(&wcex);
+ create_window_ = true;
+ }
+
+ clipboard_owner_ = NULL;
+}
+
+Clipboard::~Clipboard() {
+ if (clipboard_owner_)
+ ::DestroyWindow(clipboard_owner_);
+ clipboard_owner_ = NULL;
+}
+
+void Clipboard::WriteObjects(const ObjectMap& objects) {
+ WriteObjects(objects, NULL);
+}
+
+void Clipboard::WriteObjects(const ObjectMap& objects,
+ base::ProcessHandle process) {
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ ::EmptyClipboard();
+
+ for (ObjectMap::const_iterator iter = objects.begin();
+ iter != objects.end(); ++iter) {
+ if (iter->first == CBF_SMBITMAP)
+ WriteBitmapFromSharedMemory(&(iter->second[0].front()),
+ &(iter->second[1].front()),
+ process);
+ else
+ DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
+ }
+}
+
+void Clipboard::WriteText(const char* text_data, size_t text_len) {
+ string16 text;
+ UTF8ToUTF16(text_data, text_len, &text);
+ HGLOBAL glob = CreateGlobalData(text);
+
+ WriteToClipboard(CF_UNICODETEXT, glob);
+}
+
+void Clipboard::WriteHTML(const char* markup_data,
+ size_t markup_len,
+ const char* url_data,
+ size_t url_len) {
+ std::string markup(markup_data, markup_len);
+ std::string url;
+
+ if (url_len > 0)
+ url.assign(url_data, url_len);
+
+ std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url);
+ HGLOBAL glob = CreateGlobalData(html_fragment);
+
+ WriteToClipboard(StringToInt(GetHtmlFormatType()), glob);
+}
+
+void Clipboard::WriteBookmark(const char* title_data,
+ size_t title_len,
+ const char* url_data,
+ size_t url_len) {
+ std::string bookmark(title_data, title_len);
+ bookmark.append(1, L'\n');
+ bookmark.append(url_data, url_len);
+
+ string16 wide_bookmark = UTF8ToWide(bookmark);
+ HGLOBAL glob = CreateGlobalData(wide_bookmark);
+
+ WriteToClipboard(StringToInt(GetUrlWFormatType()), glob);
+}
+
+void Clipboard::WriteWebSmartPaste() {
+ DCHECK(clipboard_owner_);
+ ::SetClipboardData(StringToInt(GetWebKitSmartPasteFormatType()), NULL);
+}
+
+void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
+ const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
+ HDC dc = ::GetDC(NULL);
+
+ // This doesn't actually cost us a memcpy when the bitmap comes from the
+ // renderer as we load it into the bitmap using setPixels which just sets a
+ // pointer. Someone has to memcpy it into GDI, it might as well be us here.
+
+ // TODO(darin): share data in gfx/bitmap_header.cc somehow
+ BITMAPINFO bm_info = {0};
+ bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bm_info.bmiHeader.biWidth = size->width();
+ bm_info.bmiHeader.biHeight = -size->height(); // sets vertical orientation
+ bm_info.bmiHeader.biPlanes = 1;
+ bm_info.bmiHeader.biBitCount = 32;
+ bm_info.bmiHeader.biCompression = BI_RGB;
+
+ // ::CreateDIBSection allocates memory for us to copy our bitmap into.
+ // Unfortunately, we can't write the created bitmap to the clipboard,
+ // (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
+ void *bits;
+ HBITMAP source_hbitmap =
+ ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
+
+ if (bits && source_hbitmap) {
+ // Copy the bitmap out of shared memory and into GDI
+ memcpy(bits, pixel_data, 4 * size->width() * size->height());
+
+ // Now we have an HBITMAP, we can write it to the clipboard
+ WriteBitmapFromHandle(source_hbitmap, *size);
+ }
+
+ ::DeleteObject(source_hbitmap);
+ ::ReleaseDC(NULL, dc);
+}
+
+void Clipboard::WriteBitmapFromSharedMemory(const char* bitmap_data,
+ const char* size_data,
+ base::ProcessHandle process) {
+ const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
+
+ // bitmap_data has an encoded shared memory object. See
+ // DuplicateRemoteHandles().
+ char* ptr = const_cast<char*>(bitmap_data);
+ scoped_ptr<const base::SharedMemory> bitmap(*
+ reinterpret_cast<const base::SharedMemory**>(ptr));
+
+ // TODO(darin): share data in gfx/bitmap_header.cc somehow.
+ BITMAPINFO bm_info = {0};
+ bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bm_info.bmiHeader.biWidth = size->width();
+ // Sets the vertical orientation.
+ bm_info.bmiHeader.biHeight = -size->height();
+ bm_info.bmiHeader.biPlanes = 1;
+ bm_info.bmiHeader.biBitCount = 32;
+ bm_info.bmiHeader.biCompression = BI_RGB;
+
+ HDC dc = ::GetDC(NULL);
+
+ // We can create an HBITMAP directly using the shared memory handle, saving
+ // a memcpy.
+ HBITMAP source_hbitmap =
+ ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, NULL,
+ bitmap->handle(), 0);
+
+ if (source_hbitmap) {
+ // Now we can write the HBITMAP to the clipboard
+ WriteBitmapFromHandle(source_hbitmap, *size);
+ }
+
+ ::DeleteObject(source_hbitmap);
+ ::ReleaseDC(NULL, dc);
+}
+
+void Clipboard::WriteBitmapFromHandle(HBITMAP source_hbitmap,
+ const gfx::Size& size) {
+ // We would like to just call ::SetClipboardData on the source_hbitmap,
+ // but that bitmap might not be of a sort we can write to the clipboard.
+ // For this reason, we create a new bitmap, copy the bits over, and then
+ // write that to the clipboard.
+
+ HDC dc = ::GetDC(NULL);
+ HDC compatible_dc = ::CreateCompatibleDC(NULL);
+ HDC source_dc = ::CreateCompatibleDC(NULL);
+
+ // This is the HBITMAP we will eventually write to the clipboard
+ HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
+ if (!hbitmap) {
+ // Failed to create the bitmap
+ ::DeleteDC(compatible_dc);
+ ::DeleteDC(source_dc);
+ ::ReleaseDC(NULL, dc);
+ return;
+ }
+
+ HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
+ HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
+
+ // Now we need to blend it into an HBITMAP we can place on the clipboard
+ BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
+ ::GdiAlphaBlend(compatible_dc, 0, 0, size.width(), size.height(),
+ source_dc, 0, 0, size.width(), size.height(), bf);
+
+ // Clean up all the handles we just opened
+ ::SelectObject(compatible_dc, old_hbitmap);
+ ::SelectObject(source_dc, old_source);
+ ::DeleteObject(old_hbitmap);
+ ::DeleteObject(old_source);
+ ::DeleteDC(compatible_dc);
+ ::DeleteDC(source_dc);
+ ::ReleaseDC(NULL, dc);
+
+ WriteToClipboard(CF_BITMAP, hbitmap);
+}
+
+// Write a file or set of files to the clipboard in HDROP format. When the user
+// invokes a paste command (in a Windows explorer shell, for example), the files
+// will be copied to the paste location.
+void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
+ // Calculate the amount of space we'll need store the strings and
+ // a DROPFILES struct.
+ size_t bytes = sizeof(DROPFILES) + file_len;
+
+ HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes);
+ if (!hdata)
+ return;
+
+ char* data = static_cast<char*>(::GlobalLock(hdata));
+ DROPFILES* drop_files = reinterpret_cast<DROPFILES*>(data);
+ drop_files->pFiles = sizeof(DROPFILES);
+ drop_files->fWide = TRUE;
+
+ memcpy(data + sizeof(DROPFILES), file_data, file_len);
+
+ ::GlobalUnlock(hdata);
+ WriteToClipboard(CF_HDROP, hdata);
+}
+
+void Clipboard::WriteData(const char* format_name, size_t format_len,
+ const char* data_data, size_t data_len) {
+ std::string format(format_name, format_len);
+ CLIPFORMAT clip_format =
+ ::RegisterClipboardFormat(ASCIIToWide(format).c_str());
+
+ HGLOBAL hdata = ::GlobalAlloc(GMEM_MOVEABLE, data_len);
+ if (!hdata)
+ return;
+
+ char* data = static_cast<char*>(::GlobalLock(hdata));
+ memcpy(data, data_data, data_len);
+ ::GlobalUnlock(data);
+ WriteToClipboard(clip_format, hdata);
+}
+
+void Clipboard::WriteToClipboard(unsigned int format, HANDLE handle) {
+ DCHECK(clipboard_owner_);
+ if (handle && !::SetClipboardData(format, handle)) {
+ DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError());
+ FreeData(format, handle);
+ }
+}
+
+bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format,
+ Clipboard::Buffer buffer) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ return ::IsClipboardFormatAvailable(StringToInt(format)) != FALSE;
+}
+
+bool Clipboard::IsFormatAvailableByString(
+ const std::string& ascii_format, Clipboard::Buffer buffer) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ std::wstring wide_format = ASCIIToWide(ascii_format);
+ CLIPFORMAT format = ::RegisterClipboardFormat(wide_format.c_str());
+ return ::IsClipboardFormatAvailable(format) != FALSE;
+}
+
+void Clipboard::ReadText(Clipboard::Buffer buffer, string16* result) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ if (!result) {
+ NOTREACHED();
+ return;
+ }
+
+ result->clear();
+
+ // Acquire the clipboard.
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
+ if (!data)
+ return;
+
+ result->assign(static_cast<const char16*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+}
+
+void Clipboard::ReadAsciiText(Clipboard::Buffer buffer,
+ std::string* result) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ if (!result) {
+ NOTREACHED();
+ return;
+ }
+
+ result->clear();
+
+ // Acquire the clipboard.
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HANDLE data = ::GetClipboardData(CF_TEXT);
+ if (!data)
+ return;
+
+ result->assign(static_cast<const char*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+}
+
+void Clipboard::ReadHTML(Clipboard::Buffer buffer, string16* markup,
+ std::string* src_url) const {
+ DCHECK_EQ(buffer, BUFFER_STANDARD);
+ if (markup)
+ markup->clear();
+
+ if (src_url)
+ src_url->clear();
+
+ // Acquire the clipboard.
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HANDLE data = ::GetClipboardData(StringToInt(GetHtmlFormatType()));
+ if (!data)
+ return;
+
+ std::string html_fragment(static_cast<const char*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+
+ std::string markup_utf8;
+ ClipboardUtil::CFHtmlToHtml(html_fragment, markup ? &markup_utf8 : NULL,
+ src_url);
+ if (markup)
+ markup->assign(UTF8ToWide(markup_utf8));
+}
+
+void Clipboard::ReadBookmark(string16* title, std::string* url) const {
+ if (title)
+ title->clear();
+
+ if (url)
+ url->clear();
+
+ // Acquire the clipboard.
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HANDLE data = ::GetClipboardData(StringToInt(GetUrlWFormatType()));
+ if (!data)
+ return;
+
+ string16 bookmark(static_cast<const char16*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+
+ ParseBookmarkClipboardFormat(bookmark, title, url);
+}
+
+// Read a file in HDROP format from the clipboard.
+void Clipboard::ReadFile(FilePath* file) const {
+ if (!file) {
+ NOTREACHED();
+ return;
+ }
+
+ *file = FilePath();
+ std::vector<FilePath> files;
+ ReadFiles(&files);
+
+ // Take the first file, if available.
+ if (!files.empty())
+ *file = files[0];
+}
+
+// Read a set of files in HDROP format from the clipboard.
+void Clipboard::ReadFiles(std::vector<FilePath>* files) const {
+ if (!files) {
+ NOTREACHED();
+ return;
+ }
+
+ files->clear();
+
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HDROP drop = static_cast<HDROP>(::GetClipboardData(CF_HDROP));
+ if (!drop)
+ return;
+
+ // Count of files in the HDROP.
+ int count = ::DragQueryFile(drop, 0xffffffff, NULL, 0);
+
+ if (count) {
+ for (int i = 0; i < count; ++i) {
+ int size = ::DragQueryFile(drop, i, NULL, 0) + 1;
+ std::wstring file;
+ ::DragQueryFile(drop, i, WriteInto(&file, size), size);
+ files->push_back(FilePath(file));
+ }
+ }
+}
+
+void Clipboard::ReadData(const std::string& format, std::string* result) {
+ if (!result) {
+ NOTREACHED();
+ return;
+ }
+
+ CLIPFORMAT clip_format =
+ ::RegisterClipboardFormat(ASCIIToWide(format).c_str());
+
+ ScopedClipboard clipboard;
+ if (!clipboard.Acquire(GetClipboardWindow()))
+ return;
+
+ HANDLE data = ::GetClipboardData(clip_format);
+ if (!data)
+ return;
+
+ result->assign(static_cast<const char*>(::GlobalLock(data)),
+ ::GlobalSize(data));
+ ::GlobalUnlock(data);
+}
+
+// static
+void Clipboard::ParseBookmarkClipboardFormat(const string16& bookmark,
+ string16* title,
+ std::string* url) {
+ const string16 kDelim = ASCIIToUTF16("\r\n");
+
+ const size_t title_end = bookmark.find_first_of(kDelim);
+ if (title)
+ title->assign(bookmark.substr(0, title_end));
+
+ if (url) {
+ const size_t url_start = bookmark.find_first_not_of(kDelim, title_end);
+ if (url_start != string16::npos)
+ *url = UTF16ToUTF8(bookmark.substr(url_start, string16::npos));
+ }
+}
+
+// static
+Clipboard::FormatType Clipboard::GetUrlFormatType() {
+ return IntToString(ClipboardUtil::GetUrlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetUrlWFormatType() {
+ return IntToString(ClipboardUtil::GetUrlWFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetMozUrlFormatType() {
+ return IntToString(ClipboardUtil::GetMozUrlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
+ return IntToString(ClipboardUtil::GetPlainTextFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
+ return IntToString(ClipboardUtil::GetPlainTextWFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFilenameFormatType() {
+ return IntToString(ClipboardUtil::GetFilenameFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFilenameWFormatType() {
+ return IntToString(ClipboardUtil::GetFilenameWFormat()->cfFormat);
+}
+
+// MS HTML Format
+// static
+Clipboard::FormatType Clipboard::GetHtmlFormatType() {
+ return IntToString(ClipboardUtil::GetHtmlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetBitmapFormatType() {
+ return IntToString(CF_BITMAP);
+}
+
+// Firefox text/html
+// static
+Clipboard::FormatType Clipboard::GetTextHtmlFormatType() {
+ return IntToString(ClipboardUtil::GetTextHtmlFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetCFHDropFormatType() {
+ return IntToString(ClipboardUtil::GetCFHDropFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFileDescriptorFormatType() {
+ return IntToString(ClipboardUtil::GetFileDescriptorFormat()->cfFormat);
+}
+
+// static
+Clipboard::FormatType Clipboard::GetFileContentFormatZeroType() {
+ return IntToString(ClipboardUtil::GetFileContentFormatZero()->cfFormat);
+}
+
+// static
+void Clipboard::DuplicateRemoteHandles(base::ProcessHandle process,
+ ObjectMap* objects) {
+ for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
+ ++iter) {
+ if (iter->first == CBF_SMBITMAP) {
+ // There is a shared memory handle encoded on the first ObjectMapParam.
+ // Use it to open a local handle to the memory.
+ char* bitmap_data = &(iter->second[0].front());
+ base::SharedMemoryHandle* remote_bitmap_handle =
+ reinterpret_cast<base::SharedMemoryHandle*>(bitmap_data);
+
+ base::SharedMemory* bitmap = new base::SharedMemory(*remote_bitmap_handle,
+ false, process);
+
+ // We store the object where the remote handle was located so it can
+ // be retrieved by the UI thread (see WriteBitmapFromSharedMemory()).
+ iter->second[0].clear();
+ for (size_t i = 0; i < sizeof(bitmap); i++)
+ iter->second[0].push_back(reinterpret_cast<char*>(&bitmap)[i]);
+ }
+ }
+}
+
+// static
+Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
+ return IntToString(ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat);
+}
+
+// static
+void Clipboard::FreeData(unsigned int format, HANDLE data) {
+ if (format == CF_BITMAP)
+ ::DeleteObject(static_cast<HBITMAP>(data));
+ else
+ ::GlobalFree(data);
+}
+
+HWND Clipboard::GetClipboardWindow() const {
+ if (!clipboard_owner_ && create_window_) {
+ clipboard_owner_ = ::CreateWindow(L"ClipboardOwnerWindowClass",
+ L"ClipboardOwnerWindow",
+ 0, 0, 0, 0, 0,
+ HWND_MESSAGE,
+ 0, 0, 0);
+ }
+ return clipboard_owner_;
+}