summaryrefslogtreecommitdiffstats
path: root/base/clipboard_win.cc
diff options
context:
space:
mode:
authoravi@google.com <avi@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-12 16:25:23 +0000
committeravi@google.com <avi@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-12 16:25:23 +0000
commit2b05317b8aa8edb15c794f5d389e71b1a50c331d (patch)
tree1bddb2c1799823c7b68ca0a2c57eabc19eed2733 /base/clipboard_win.cc
parentb6e09acf8ced26198871626c76bb5a3741cc51f1 (diff)
downloadchromium_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_win.cc')
-rw-r--r--base/clipboard_win.cc658
1 files changed, 658 insertions, 0 deletions
diff --git a/base/clipboard_win.cc b/base/clipboard_win.cc
new file mode 100644
index 0000000..b5b5bf2
--- /dev/null
+++ b/base/clipboard_win.cc
@@ -0,0 +1,658 @@
+// 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() {
+ // Acquire the clipboard.
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ ::EmptyClipboard();
+}
+
+void Clipboard::WriteText(const std::wstring& text) {
+ 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) {
+ // 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) {
+ // 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) {
+ // 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() {
+ // 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) {
+ 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) {
+ // 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) {
+ // 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) {
+ std::vector<std::wstring> files;
+ files.push_back(file);
+ WriteFiles(files);
+}
+
+void Clipboard::WriteFiles(const std::vector<std::wstring>& files) {
+ 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));
+ }
+}