diff options
author | avi@google.com <avi@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-12 16:25:23 +0000 |
---|---|---|
committer | avi@google.com <avi@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-12 16:25:23 +0000 |
commit | 2b05317b8aa8edb15c794f5d389e71b1a50c331d (patch) | |
tree | 1bddb2c1799823c7b68ca0a2c57eabc19eed2733 /base/clipboard.cc | |
parent | b6e09acf8ced26198871626c76bb5a3741cc51f1 (diff) | |
download | chromium_src-2b05317b8aa8edb15c794f5d389e71b1a50c331d.zip chromium_src-2b05317b8aa8edb15c794f5d389e71b1a50c331d.tar.gz chromium_src-2b05317b8aa8edb15c794f5d389e71b1a50c331d.tar.bz2 |
Basic implementation of the clipboard on the Mac.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@717 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/clipboard.cc')
-rw-r--r-- | base/clipboard.cc | 658 |
1 files changed, 0 insertions, 658 deletions
diff --git a/base/clipboard.cc b/base/clipboard.cc deleted file mode 100644 index b85ec00..0000000 --- a/base/clipboard.cc +++ /dev/null @@ -1,658 +0,0 @@ -// Copyright 2008, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Many of these functions are based on those found in -// webkit/port/platform/PasteboardWin.cpp - -#include <shlobj.h> -#include <shellapi.h> - -#include "base/clipboard.h" - -#include "base/clipboard_util.h" -#include "base/logging.h" -#include "base/string_util.h" - -namespace { - -// A small object to ensure we close the clipboard after opening it. -class ClipboardLock { - public: - ClipboardLock() : we_own_the_lock_(false) { } - - ~ClipboardLock() { - if (we_own_the_lock_) - Release(); - } - - bool Acquire(HWND owner) { - // We shouldn't be calling this if we already own the clipbard lock. - DCHECK(!we_own_the_lock_); - - // We already have the lock. We don't want to stomp on the other use. - if (we_own_the_lock_) - return false; - - const int kMaxAttemptsToOpenClipboard = 5; - - // Attempt to acquire the clipboard lock. This may fail if another process - // currently holds the 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 lock contention. - - for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) { - if (::OpenClipboard(owner)) { - we_own_the_lock_ = true; - return we_own_the_lock_; - } - - // Having failed, we yeild our timeslice to other processes. ::Yield seems - // to be insufficient here, so we sleep for 5 ms. - if (attempts < (kMaxAttemptsToOpenClipboard - 1)) - ::Sleep(5); - } - - // We failed to acquire the clipboard. - return false; - } - - void Release() { - // We should only be calling this if we already own the clipbard lock. - DCHECK(we_own_the_lock_); - - // We we don't have the lock, there is nothing to release. - if (!we_own_the_lock_) - return; - - ::CloseClipboard(); - we_own_the_lock_ = false; - } - - private: - bool we_own_the_lock_; -}; - -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() { - // 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); - - clipboard_owner_ = ::CreateWindow(L"ClipboardOwnerWindowClass", - L"ClipboardOwnerWindow", - 0, 0, 0, 0, 0, - HWND_MESSAGE, - 0, 0, 0); -} - -Clipboard::~Clipboard() { - ::DestroyWindow(clipboard_owner_); - clipboard_owner_ = NULL; -} - -void Clipboard::Clear() const { - // Acquire the clipboard. - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - ::EmptyClipboard(); -} - -void Clipboard::WriteText(const std::wstring& text) const { - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - HGLOBAL glob = CreateGlobalData(text); - if (glob && !::SetClipboardData(CF_UNICODETEXT, glob)) - ::GlobalFree(glob); -} - -void Clipboard::WriteHTML(const std::wstring& markup, - const std::string& url) const { - // Acquire the clipboard. - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - std::string html_fragment; - MarkupToHTMLClipboardFormat(markup, url, &html_fragment); - HGLOBAL glob = CreateGlobalData(html_fragment); - if (glob && !::SetClipboardData(ClipboardUtil::GetHtmlFormat()->cfFormat, - glob)) { - ::GlobalFree(glob); - } -} - -void Clipboard::WriteBookmark(const std::wstring& title, - const std::string& url) const { - // Acquire the clipboard. - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - std::wstring bookmark(title); - bookmark.append(1, L'\n'); - bookmark.append(UTF8ToWide(url)); - HGLOBAL glob = CreateGlobalData(bookmark); - if (glob && !::SetClipboardData(ClipboardUtil::GetUrlWFormat()->cfFormat, - glob)) { - ::GlobalFree(glob); - } -} - -void Clipboard::WriteHyperlink(const std::wstring& title, - const std::string& url) const { - // Write as a bookmark. - WriteBookmark(title, url); - - // Build the HTML link. - std::wstring link(L"<a href=\""); - link.append(UTF8ToWide(url)); - link.append(L"\">"); - link.append(title); - link.append(L"</a>"); - - // Write as an HTML link. - WriteHTML(link, std::string()); -} - -void Clipboard::WriteWebSmartPaste() const { - // Acquire the clipboard. - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - SetClipboardData(ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat, NULL); -} - -void Clipboard::WriteBitmap(const void* pixels, const gfx::Size& size) const { - 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, pixels, 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 SharedMemory& bitmap, - const gfx::Size& size) const { - // 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 the vertical orientation - 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) const { - // Acquire the clipboard. - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - // 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}; - ::AlphaBlend(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); - - // Actually write the bitmap to the clipboard - ::SetClipboardData(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::WriteFile(const std::wstring& file) const { - std::vector<std::wstring> files; - files.push_back(file); - WriteFiles(files); -} - -void Clipboard::WriteFiles(const std::vector<std::wstring>& files) const { - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - // Calculate the amount of space we'll need store the strings: require - // NULL terminator between strings, and double null terminator at the end. - size_t bytes = sizeof(DROPFILES); - for (size_t i = 0; i < files.size(); ++i) - bytes += (files[i].length() + 1) * sizeof(wchar_t); - bytes += sizeof(wchar_t); - - HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes); - if (!hdata) - return; - - DROPFILES* drop_files = static_cast<DROPFILES*>(::GlobalLock(hdata)); - drop_files->pFiles = sizeof(DROPFILES); - drop_files->fWide = TRUE; - BYTE* data = reinterpret_cast<BYTE*>(drop_files) + sizeof(DROPFILES); - - // Copy the strings stored in 'files' with proper NULL separation. - wchar_t* data_pos = reinterpret_cast<wchar_t*>(data); - for (size_t i = 0; i < files.size(); ++i) { - size_t offset = files[i].length() + 1; - memcpy(data_pos, files[i].c_str(), offset * sizeof(wchar_t)); - data_pos += offset; - } - data_pos[0] = L'\0'; // Double NULL termination after the last string. - - ::GlobalUnlock(hdata); - if (!::SetClipboardData(CF_HDROP, hdata)) - ::GlobalFree(hdata); -} - -bool Clipboard::IsFormatAvailable(unsigned int format) const { - return ::IsClipboardFormatAvailable(format) != FALSE; -} - -void Clipboard::ReadText(std::wstring* result) const { - if (!result) { - NOTREACHED(); - return; - } - - result->clear(); - - // Acquire the clipboard. - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - HANDLE data = ::GetClipboardData(CF_UNICODETEXT); - if (!data) - return; - - result->assign(static_cast<const wchar_t*>(::GlobalLock(data))); - ::GlobalUnlock(data); -} - -void Clipboard::ReadAsciiText(std::string* result) const { - if (!result) { - NOTREACHED(); - return; - } - - result->clear(); - - // Acquire the clipboard. - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - HANDLE data = ::GetClipboardData(CF_TEXT); - if (!data) - return; - - result->assign(static_cast<const char*>(::GlobalLock(data))); - ::GlobalUnlock(data); -} - -void Clipboard::ReadHTML(std::wstring* markup, std::string* src_url) const { - if (markup) - markup->clear(); - - if (src_url) - src_url->clear(); - - // Acquire the clipboard. - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - HANDLE data = ::GetClipboardData(ClipboardUtil::GetHtmlFormat()->cfFormat); - if (!data) - return; - - std::string html_fragment(static_cast<const char*>(::GlobalLock(data))); - ::GlobalUnlock(data); - - ParseHTMLClipboardFormat(html_fragment, markup, src_url); -} - -void Clipboard::ReadBookmark(std::wstring* title, std::string* url) const { - if (title) - title->clear(); - - if (url) - url->clear(); - - // Acquire the clipboard. - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - return; - - HANDLE data = ::GetClipboardData(ClipboardUtil::GetUrlWFormat()->cfFormat); - if (!data) - return; - - std::wstring bookmark(static_cast<const wchar_t*>(::GlobalLock(data))); - ::GlobalUnlock(data); - - ParseBookmarkClipboardFormat(bookmark, title, url); -} - -// Read a file in HDROP format from the clipboard. -void Clipboard::ReadFile(std::wstring* file) const { - if (!file) { - NOTREACHED(); - return; - } - - file->clear(); - std::vector<std::wstring> files; - ReadFiles(&files); - - // Take the first file, if available. - if (!files.empty()) - file->assign(files[0]); -} - -// Read a set of files in HDROP format from the clipboard. -void Clipboard::ReadFiles(std::vector<std::wstring>* files) const { - if (!files) { - NOTREACHED(); - return; - } - - files->clear(); - - ClipboardLock lock; - if (!lock.Acquire(clipboard_owner_)) - 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(file); - } - } -} - -// static -void Clipboard::MarkupToHTMLClipboardFormat(const std::wstring& markup, - const std::string& src_url, - std::string* html_fragment) { - DCHECK(html_fragment); - // Documentation for the CF_HTML format is available at - // http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp - - if (markup.empty()) { - html_fragment->clear(); - return; - } - - std::string markup_utf8 = WideToUTF8(markup); - - html_fragment->assign("Version:0.9"); - - std::string start_html("\nStartHTML:"); - std::string end_html("\nEndHTML:"); - std::string start_fragment("\nStartFragment:"); - std::string end_fragment("\nEndFragment:"); - std::string source_url("\nSourceURL:"); - - bool has_source_url = !src_url.empty() && - !StartsWithASCII(src_url, "about:", false); - if (has_source_url) - source_url.append(src_url); - - std::string start_markup("\n<HTML>\n<BODY>\n<!--StartFragment-->\n"); - std::string end_markup("\n<!--EndFragment-->\n</BODY>\n</HTML>"); - - // calculate offsets - const size_t kMaxDigits = 10; // number of digits in UINT_MAX in base 10 - - size_t start_html_offset, start_fragment_offset; - size_t end_fragment_offset, end_html_offset; - - start_html_offset = html_fragment->length() + - start_html.length() + end_html.length() + - start_fragment.length() + end_fragment.length() + - (has_source_url ? source_url.length() : 0) + - (4*kMaxDigits); - - start_fragment_offset = start_html_offset + start_markup.length(); - end_fragment_offset = start_fragment_offset + markup_utf8.length(); - end_html_offset = end_fragment_offset + end_markup.length(); - - // fill in needed data - start_html.append(StringPrintf("%010u", start_html_offset)); - end_html.append(StringPrintf("%010u", end_html_offset)); - start_fragment.append(StringPrintf("%010u", start_fragment_offset)); - end_fragment.append(StringPrintf("%010u", end_fragment_offset)); - start_markup.append(markup_utf8); - - // create full html_fragment string from the fragments - html_fragment->append(start_html); - html_fragment->append(end_html); - html_fragment->append(start_fragment); - html_fragment->append(end_fragment); - if (has_source_url) - html_fragment->append(source_url); - html_fragment->append(start_markup); - html_fragment->append(end_markup); -} - -// static -void Clipboard::ParseHTMLClipboardFormat(const std::string& html_frag, - std::wstring* markup, - std::string* src_url) { - if (src_url) { - // Obtain SourceURL, if present - std::string src_url_str("SourceURL:"); - size_t line_start = html_frag.find(src_url_str, 0); - if (line_start != std::string::npos) { - size_t src_start = line_start+src_url_str.length(); - size_t src_end = html_frag.find("\n", line_start); - - if (src_end != std::string::npos) - *src_url = html_frag.substr(src_start, src_end - src_start); - } - } - - if (markup) { - // Find the markup between "<!--StartFragment -->" and - // "<!--EndFragment -->", accounting for browser quirks - size_t markup_start = html_frag.find('<', 0); - size_t tag_start = html_frag.find("StartFragment", markup_start); - size_t frag_start = html_frag.find('>', tag_start) + 1; - // Here we do something slightly differently than WebKit. Webkit does a - // forward find for EndFragment, but that seems to be a bug if the html - // fragment actually includes the string "EndFragment" - size_t tag_end = html_frag.rfind("EndFragment", std::string::npos); - size_t frag_end = html_frag.rfind('<', tag_end); - - TrimWhitespace(UTF8ToWide(html_frag.substr(frag_start, - frag_end - frag_start)), - TRIM_ALL, markup); - } -} - -// static -void Clipboard::ParseBookmarkClipboardFormat(const std::wstring& bookmark, - std::wstring* title, - std::string* url) { - const wchar_t* const kDelim = L"\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 != std::wstring::npos) - *url = WideToUTF8(bookmark.substr(url_start, std::wstring::npos)); - } -} |