summaryrefslogtreecommitdiff
path: root/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp')
-rw-r--r--Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp841
1 files changed, 841 insertions, 0 deletions
diff --git a/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp b/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp
new file mode 100644
index 000000000..7fdc6b8dd
--- /dev/null
+++ b/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp
@@ -0,0 +1,841 @@
+/*
+ * Copyright (C) 2007, 2008 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+#include "config.h"
+#include "ClipboardUtilitiesWin.h"
+
+#include "DocumentFragment.h"
+#include "URL.h"
+#include "TextEncoding.h"
+#include "markup.h"
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <wininet.h> // for INTERNET_MAX_URL_LENGTH
+#include <wtf/StringExtras.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+
+
+#if USE(CF)
+#include <CoreFoundation/CoreFoundation.h>
+#include <wtf/RetainPtr.h>
+#endif
+
+namespace WebCore {
+
+#if USE(CF)
+FORMATETC* cfHDropFormat()
+{
+ static FORMATETC urlFormat = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &urlFormat;
+}
+
+static bool urlFromPath(CFStringRef path, String& url)
+{
+ if (!path)
+ return false;
+
+ RetainPtr<CFURLRef> cfURL = adoptCF(CFURLCreateWithFileSystemPath(0, path, kCFURLWindowsPathStyle, false));
+ if (!cfURL)
+ return false;
+
+ url = CFURLGetString(cfURL.get());
+
+ // Work around <rdar://problem/6708300>, where CFURLCreateWithFileSystemPath makes URLs with "localhost".
+ if (url.startsWith("file://localhost/"))
+ url.remove(7, 9);
+
+ return true;
+}
+#endif
+
+static bool getDataMapItem(const DragDataMap* dataObject, FORMATETC* format, String& item)
+{
+ DragDataMap::const_iterator found = dataObject->find(format->cfFormat);
+ if (found == dataObject->end())
+ return false;
+ item = found->value[0];
+ return true;
+}
+
+static bool getWebLocData(IDataObject* dataObject, String& url, String* title)
+{
+ bool succeeded = false;
+#if USE(CF)
+ WCHAR filename[MAX_PATH];
+ WCHAR urlBuffer[INTERNET_MAX_URL_LENGTH];
+
+ STGMEDIUM medium;
+ if (FAILED(dataObject->GetData(cfHDropFormat(), &medium)))
+ return false;
+
+ HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
+
+ if (!hdrop)
+ return false;
+
+ if (!DragQueryFileW(hdrop, 0, filename, WTF_ARRAY_LENGTH(filename)))
+ goto exit;
+
+ if (_wcsicmp(PathFindExtensionW(filename), L".url"))
+ goto exit;
+
+ if (!GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, urlBuffer, WTF_ARRAY_LENGTH(urlBuffer), filename))
+ goto exit;
+
+ if (title) {
+ PathRemoveExtension(filename);
+ *title = String((UChar*)filename);
+ }
+
+ url = String((UChar*)urlBuffer);
+ succeeded = true;
+
+exit:
+ // Free up memory.
+ DragFinish(hdrop);
+ GlobalUnlock(medium.hGlobal);
+#endif
+ return succeeded;
+}
+
+static bool getWebLocData(const DragDataMap* dataObject, String& url, String* title)
+{
+#if USE(CF)
+ WCHAR filename[MAX_PATH];
+ WCHAR urlBuffer[INTERNET_MAX_URL_LENGTH];
+
+ if (!dataObject->contains(cfHDropFormat()->cfFormat))
+ return false;
+
+ wcscpy(filename, dataObject->get(cfHDropFormat()->cfFormat)[0].charactersWithNullTermination().data());
+ if (_wcsicmp(PathFindExtensionW(filename), L".url"))
+ return false;
+
+ if (!GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, urlBuffer, WTF_ARRAY_LENGTH(urlBuffer), filename))
+ return false;
+
+ if (title) {
+ PathRemoveExtension(filename);
+ *title = filename;
+ }
+
+ url = urlBuffer;
+ return true;
+#else
+ return false;
+#endif
+}
+
+static String extractURL(const String &inURL, String* title)
+{
+ String url = inURL;
+ int splitLoc = url.find('\n');
+ if (splitLoc > 0) {
+ if (title)
+ *title = url.substring(splitLoc+1);
+ url.truncate(splitLoc);
+ } else if (title)
+ *title = url;
+ return url;
+}
+
+// Firefox text/html
+static FORMATETC* texthtmlFormat()
+{
+ static UINT cf = RegisterClipboardFormat(L"text/html");
+ static FORMATETC texthtmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &texthtmlFormat;
+}
+
+HGLOBAL createGlobalData(const URL& url, const String& title)
+{
+ String mutableURL(url.string());
+ String mutableTitle(title);
+ SIZE_T size = mutableURL.length() + mutableTitle.length() + 2; // +1 for "\n" and +1 for null terminator
+ HGLOBAL cbData = ::GlobalAlloc(GPTR, size * sizeof(UChar));
+
+ if (cbData) {
+ PWSTR buffer = static_cast<PWSTR>(GlobalLock(cbData));
+ _snwprintf(buffer, size, L"%s\n%s", mutableURL.charactersWithNullTermination().data(), mutableTitle.charactersWithNullTermination().data());
+ GlobalUnlock(cbData);
+ }
+ return cbData;
+}
+
+HGLOBAL createGlobalData(const String& str)
+{
+ HGLOBAL vm = ::GlobalAlloc(GPTR, (str.length() + 1) * sizeof(UChar));
+ if (!vm)
+ return 0;
+ UChar* buffer = static_cast<UChar*>(GlobalLock(vm));
+ StringView(str).getCharactersWithUpconvert(buffer);
+ buffer[str.length()] = 0;
+ GlobalUnlock(vm);
+ return vm;
+}
+
+HGLOBAL createGlobalData(const Vector<char>& vector)
+{
+ HGLOBAL vm = ::GlobalAlloc(GPTR, vector.size() + 1);
+ if (!vm)
+ return 0;
+ char* buffer = static_cast<char*>(GlobalLock(vm));
+ memcpy(buffer, vector.data(), vector.size());
+ buffer[vector.size()] = 0;
+ GlobalUnlock(vm);
+ return vm;
+}
+
+static String getFullCFHTML(IDataObject* data)
+{
+ STGMEDIUM store;
+ if (SUCCEEDED(data->GetData(htmlFormat(), &store))) {
+ // MS HTML Format parsing
+ char* data = static_cast<char*>(GlobalLock(store.hGlobal));
+ SIZE_T dataSize = ::GlobalSize(store.hGlobal);
+ String cfhtml(UTF8Encoding().decode(data, dataSize));
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ return cfhtml;
+ }
+ return String();
+}
+
+static void append(Vector<char>& vector, const char* string)
+{
+ vector.append(string, strlen(string));
+}
+
+static void append(Vector<char>& vector, const CString& string)
+{
+ vector.append(string.data(), string.length());
+}
+
+// Find the markup between "<!--StartFragment -->" and "<!--EndFragment -->", accounting for browser quirks.
+static String extractMarkupFromCFHTML(const String& cfhtml)
+{
+ unsigned markupStart = cfhtml.find("<html", 0, false);
+ unsigned tagStart = cfhtml.find("startfragment", markupStart, false);
+ unsigned fragmentStart = cfhtml.find('>', tagStart) + 1;
+ unsigned tagEnd = cfhtml.find("endfragment", fragmentStart, false);
+ unsigned fragmentEnd = cfhtml.reverseFind('<', tagEnd);
+ return cfhtml.substring(fragmentStart, fragmentEnd - fragmentStart).stripWhiteSpace();
+}
+
+// Documentation for the CF_HTML format is available at http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
+void markupToCFHTML(const String& markup, const String& srcURL, Vector<char>& result)
+{
+ if (markup.isEmpty())
+ return;
+
+ #define MAX_DIGITS 10
+ #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
+ #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
+ #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
+
+ const char* header = "Version:0.9\n"
+ "StartHTML:" NUMBER_FORMAT "\n"
+ "EndHTML:" NUMBER_FORMAT "\n"
+ "StartFragment:" NUMBER_FORMAT "\n"
+ "EndFragment:" NUMBER_FORMAT "\n";
+ const char* sourceURLPrefix = "SourceURL:";
+
+ const char* startMarkup = "<HTML>\n<BODY>\n<!--StartFragment-->\n";
+ const char* endMarkup = "\n<!--EndFragment-->\n</BODY>\n</HTML>";
+
+ CString sourceURLUTF8 = srcURL == blankURL() ? "" : srcURL.utf8();
+ CString markupUTF8 = markup.utf8();
+
+ // calculate offsets
+ unsigned startHTMLOffset = strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_DIGITS * 4;
+ if (sourceURLUTF8.length())
+ startHTMLOffset += strlen(sourceURLPrefix) + sourceURLUTF8.length() + 1;
+ unsigned startFragmentOffset = startHTMLOffset + strlen(startMarkup);
+ unsigned endFragmentOffset = startFragmentOffset + markupUTF8.length();
+ unsigned endHTMLOffset = endFragmentOffset + strlen(endMarkup);
+
+ unsigned headerBufferLength = startHTMLOffset + 1; // + 1 for '\0' terminator.
+ char* headerBuffer = (char*)malloc(headerBufferLength);
+ snprintf(headerBuffer, headerBufferLength, header, startHTMLOffset, endHTMLOffset, startFragmentOffset, endFragmentOffset);
+ append(result, CString(headerBuffer));
+ free(headerBuffer);
+ if (sourceURLUTF8.length()) {
+ append(result, sourceURLPrefix);
+ append(result, sourceURLUTF8);
+ result.append('\n');
+ }
+ append(result, startMarkup);
+ append(result, markupUTF8);
+ append(result, endMarkup);
+
+ #undef MAX_DIGITS
+ #undef MAKE_NUMBER_FORMAT_1
+ #undef MAKE_NUMBER_FORMAT_2
+ #undef NUMBER_FORMAT
+}
+
+void replaceNewlinesWithWindowsStyleNewlines(String& str)
+{
+ DEPRECATED_DEFINE_STATIC_LOCAL(String, windowsNewline, (ASCIILiteral("\r\n")));
+ StringBuilder result;
+ for (unsigned index = 0; index < str.length(); ++index) {
+ if (str[index] != '\n' || (index > 0 && str[index - 1] == '\r'))
+ result.append(str[index]);
+ else
+ result.append(windowsNewline);
+ }
+ str = result.toString();
+}
+
+void replaceNBSPWithSpace(String& str)
+{
+ static const UChar NonBreakingSpaceCharacter = 0xA0;
+ static const UChar SpaceCharacter = ' ';
+ str.replace(NonBreakingSpaceCharacter, SpaceCharacter);
+}
+
+FORMATETC* urlWFormat()
+{
+ static UINT cf = RegisterClipboardFormat(L"UniformResourceLocatorW");
+ static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &urlFormat;
+}
+
+FORMATETC* urlFormat()
+{
+ static UINT cf = RegisterClipboardFormat(L"UniformResourceLocator");
+ static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &urlFormat;
+}
+
+FORMATETC* plainTextFormat()
+{
+ static FORMATETC textFormat = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &textFormat;
+}
+
+FORMATETC* plainTextWFormat()
+{
+ static FORMATETC textFormat = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &textFormat;
+}
+
+FORMATETC* filenameWFormat()
+{
+ static UINT cf = RegisterClipboardFormat(L"FileNameW");
+ static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &urlFormat;
+}
+
+FORMATETC* filenameFormat()
+{
+ static UINT cf = RegisterClipboardFormat(L"FileName");
+ static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &urlFormat;
+}
+
+// MSIE HTML Format
+FORMATETC* htmlFormat()
+{
+ static UINT cf = RegisterClipboardFormat(L"HTML Format");
+ static FORMATETC htmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &htmlFormat;
+}
+
+FORMATETC* smartPasteFormat()
+{
+ static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format");
+ static FORMATETC htmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &htmlFormat;
+}
+
+FORMATETC* fileDescriptorFormat()
+{
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
+ static FORMATETC fileDescriptorFormat = { cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ return &fileDescriptorFormat;
+}
+
+FORMATETC* fileContentFormatZero()
+{
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
+ static FORMATETC fileContentFormat = { cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL };
+ return &fileContentFormat;
+}
+
+void getFileDescriptorData(IDataObject* dataObject, int& size, String& pathname)
+{
+ STGMEDIUM store;
+ size = 0;
+ if (FAILED(dataObject->GetData(fileDescriptorFormat(), &store)))
+ return;
+
+ FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(store.hGlobal));
+ size = fgd->fgd[0].nFileSizeLow;
+ pathname = fgd->fgd[0].cFileName;
+
+ GlobalUnlock(store.hGlobal);
+ ::ReleaseStgMedium(&store);
+}
+
+void getFileContentData(IDataObject* dataObject, int size, void* dataBlob)
+{
+ STGMEDIUM store;
+ if (FAILED(dataObject->GetData(fileContentFormatZero(), &store)))
+ return;
+ void* data = GlobalLock(store.hGlobal);
+ ::CopyMemory(dataBlob, data, size);
+
+ GlobalUnlock(store.hGlobal);
+ ::ReleaseStgMedium(&store);
+}
+
+void setFileDescriptorData(IDataObject* dataObject, int size, const String& passedPathname)
+{
+ String pathname = passedPathname;
+
+ STGMEDIUM medium = { 0 };
+ medium.tymed = TYMED_HGLOBAL;
+
+ medium.hGlobal = ::GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
+ if (!medium.hGlobal)
+ return;
+
+ FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(medium.hGlobal));
+ ::ZeroMemory(fgd, sizeof(FILEGROUPDESCRIPTOR));
+ fgd->cItems = 1;
+ fgd->fgd[0].dwFlags = FD_FILESIZE;
+ fgd->fgd[0].nFileSizeLow = size;
+
+ int maxSize = std::min<int>(pathname.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName));
+ CopyMemory(fgd->fgd[0].cFileName, pathname.charactersWithNullTermination().data(), maxSize * sizeof(UChar));
+ GlobalUnlock(medium.hGlobal);
+
+ dataObject->SetData(fileDescriptorFormat(), &medium, TRUE);
+}
+
+void setFileContentData(IDataObject* dataObject, int size, void* dataBlob)
+{
+ STGMEDIUM medium = { 0 };
+ medium.tymed = TYMED_HGLOBAL;
+
+ medium.hGlobal = ::GlobalAlloc(GPTR, size);
+ if (!medium.hGlobal)
+ return;
+ void* fileContents = GlobalLock(medium.hGlobal);
+ ::CopyMemory(fileContents, dataBlob, size);
+ GlobalUnlock(medium.hGlobal);
+
+ dataObject->SetData(fileContentFormatZero(), &medium, TRUE);
+}
+
+String getURL(IDataObject* dataObject, DragData::FilenameConversionPolicy filenamePolicy, String* title)
+{
+ STGMEDIUM store;
+ String url;
+ if (getWebLocData(dataObject, url, title))
+ return url;
+
+ if (SUCCEEDED(dataObject->GetData(urlWFormat(), &store))) {
+ // URL using Unicode
+ UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal));
+ url = extractURL(String(data), title);
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ } else if (SUCCEEDED(dataObject->GetData(urlFormat(), &store))) {
+ // URL using ASCII
+ char* data = static_cast<char*>(GlobalLock(store.hGlobal));
+ url = extractURL(String(data), title);
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ }
+#if USE(CF)
+ else if (filenamePolicy == DragData::ConvertFilenames) {
+ if (SUCCEEDED(dataObject->GetData(filenameWFormat(), &store))) {
+ // file using unicode
+ wchar_t* data = static_cast<wchar_t*>(GlobalLock(store.hGlobal));
+ if (data && data[0] && (PathFileExists(data) || PathIsUNC(data))) {
+ RetainPtr<CFStringRef> pathAsCFString = adoptCF(CFStringCreateWithCharacters(kCFAllocatorDefault, (const UniChar*)data, wcslen(data)));
+ if (urlFromPath(pathAsCFString.get(), url) && title)
+ *title = url;
+ }
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ } else if (SUCCEEDED(dataObject->GetData(filenameFormat(), &store))) {
+ // filename using ascii
+ char* data = static_cast<char*>(GlobalLock(store.hGlobal));
+ if (data && data[0] && (PathFileExistsA(data) || PathIsUNCA(data))) {
+ RetainPtr<CFStringRef> pathAsCFString = adoptCF(CFStringCreateWithCString(kCFAllocatorDefault, data, kCFStringEncodingASCII));
+ if (urlFromPath(pathAsCFString.get(), url) && title)
+ *title = url;
+ }
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ }
+ }
+#endif
+ return url;
+}
+
+String getURL(const DragDataMap* data, DragData::FilenameConversionPolicy filenamePolicy, String* title)
+{
+ String url;
+
+ if (getWebLocData(data, url, title))
+ return url;
+ if (getDataMapItem(data, urlWFormat(), url))
+ return extractURL(url, title);
+ if (getDataMapItem(data, urlFormat(), url))
+ return extractURL(url, title);
+#if USE(CF)
+ if (filenamePolicy != DragData::ConvertFilenames)
+ return url;
+
+ String stringData;
+ if (!getDataMapItem(data, filenameWFormat(), stringData))
+ getDataMapItem(data, filenameFormat(), stringData);
+
+ if (stringData.isEmpty() || (!PathFileExists(stringData.charactersWithNullTermination().data()) && !PathIsUNC(stringData.charactersWithNullTermination().data())))
+ return url;
+ RetainPtr<CFStringRef> pathAsCFString = adoptCF(CFStringCreateWithCharacters(kCFAllocatorDefault, (const UniChar *)stringData.charactersWithNullTermination().data(), wcslen(stringData.charactersWithNullTermination().data())));
+ if (urlFromPath(pathAsCFString.get(), url) && title)
+ *title = url;
+#endif
+ return url;
+}
+
+String getPlainText(IDataObject* dataObject)
+{
+ STGMEDIUM store;
+ String text;
+ if (SUCCEEDED(dataObject->GetData(plainTextWFormat(), &store))) {
+ // Unicode text
+ UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal));
+ text = String(data);
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ } else if (SUCCEEDED(dataObject->GetData(plainTextFormat(), &store))) {
+ // ASCII text
+ char* data = static_cast<char*>(GlobalLock(store.hGlobal));
+ text = String(data);
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ } else {
+ // FIXME: Originally, we called getURL() here because dragging and dropping files doesn't
+ // populate the drag with text data. Per https://bugs.webkit.org/show_bug.cgi?id=38826, this
+ // is undesirable, so maybe this line can be removed.
+ text = getURL(dataObject, DragData::DoNotConvertFilenames);
+ }
+ return text;
+}
+
+String getPlainText(const DragDataMap* data)
+{
+ String text;
+
+ if (getDataMapItem(data, plainTextWFormat(), text))
+ return text;
+ if (getDataMapItem(data, plainTextFormat(), text))
+ return text;
+ return getURL(data, DragData::DoNotConvertFilenames);
+}
+
+String getTextHTML(IDataObject* data)
+{
+ STGMEDIUM store;
+ String html;
+ if (SUCCEEDED(data->GetData(texthtmlFormat(), &store))) {
+ UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal));
+ html = String(data);
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ }
+ return html;
+}
+
+String getTextHTML(const DragDataMap* data)
+{
+ String text;
+ getDataMapItem(data, texthtmlFormat(), text);
+ return text;
+}
+
+String getCFHTML(IDataObject* data)
+{
+ String cfhtml = getFullCFHTML(data);
+ if (!cfhtml.isEmpty())
+ return extractMarkupFromCFHTML(cfhtml);
+ return String();
+}
+
+String getCFHTML(const DragDataMap* dataMap)
+{
+ String cfhtml;
+ getDataMapItem(dataMap, htmlFormat(), cfhtml);
+ return extractMarkupFromCFHTML(cfhtml);
+}
+
+PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*)
+{
+ // FIXME: We should be able to create fragments from files
+ return 0;
+}
+
+PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const DragDataMap*)
+{
+ // FIXME: We should be able to create fragments from files
+ return 0;
+}
+
+bool containsFilenames(const IDataObject*)
+{
+ // FIXME: We'll want to update this once we can produce fragments from files
+ return false;
+}
+
+bool containsFilenames(const DragDataMap*)
+{
+ // FIXME: We'll want to update this once we can produce fragments from files
+ return false;
+}
+
+// Convert a String containing CF_HTML formatted text to a DocumentFragment
+PassRefPtr<DocumentFragment> fragmentFromCFHTML(Document* doc, const String& cfhtml)
+{
+ // obtain baseURL if present
+ String srcURLStr("sourceURL:");
+ String srcURL;
+ unsigned lineStart = cfhtml.find(srcURLStr, 0, false);
+ if (lineStart != -1) {
+ unsigned srcEnd = cfhtml.find("\n", lineStart, false);
+ unsigned srcStart = lineStart+srcURLStr.length();
+ String rawSrcURL = cfhtml.substring(srcStart, srcEnd-srcStart);
+ replaceNBSPWithSpace(rawSrcURL);
+ srcURL = rawSrcURL.stripWhiteSpace();
+ }
+
+ String markup = extractMarkupFromCFHTML(cfhtml);
+ return createFragmentFromMarkup(*doc, markup, srcURL, DisallowScriptingAndPluginContent);
+}
+
+PassRefPtr<DocumentFragment> fragmentFromHTML(Document* doc, IDataObject* data)
+{
+ if (!doc || !data)
+ return 0;
+
+ String cfhtml = getFullCFHTML(data);
+ if (!cfhtml.isEmpty()) {
+ if (RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(doc, cfhtml))
+ return fragment.release();
+ }
+
+ String html = getTextHTML(data);
+ String srcURL;
+ if (!html.isEmpty())
+ return createFragmentFromMarkup(*doc, html, srcURL, DisallowScriptingAndPluginContent);
+
+ return 0;
+}
+
+PassRefPtr<DocumentFragment> fragmentFromHTML(Document* document, const DragDataMap* data)
+{
+ if (!document || !data || data->isEmpty())
+ return 0;
+
+ String stringData;
+ if (getDataMapItem(data, htmlFormat(), stringData)) {
+ if (RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(document, stringData))
+ return fragment.release();
+ }
+
+ String srcURL;
+ if (getDataMapItem(data, texthtmlFormat(), stringData))
+ return createFragmentFromMarkup(*document, stringData, srcURL, DisallowScriptingAndPluginContent);
+
+ return 0;
+}
+
+bool containsHTML(IDataObject* data)
+{
+ return SUCCEEDED(data->QueryGetData(texthtmlFormat())) || SUCCEEDED(data->QueryGetData(htmlFormat()));
+}
+
+bool containsHTML(const DragDataMap* data)
+{
+ return data->contains(texthtmlFormat()->cfFormat) || data->contains(htmlFormat()->cfFormat);
+}
+
+typedef void (*GetStringFunction)(IDataObject*, FORMATETC*, Vector<String>&);
+typedef void (*SetStringFunction)(IDataObject*, FORMATETC*, const Vector<String>&);
+
+struct ClipboardDataItem {
+ GetStringFunction getString;
+ SetStringFunction setString;
+ FORMATETC* format;
+
+ ClipboardDataItem(FORMATETC* format, GetStringFunction getString, SetStringFunction setString): format(format), getString(getString), setString(setString) { }
+};
+
+typedef HashMap<UINT, ClipboardDataItem*> ClipboardFormatMap;
+
+// Getter functions.
+
+template<typename T> void getStringData(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings)
+{
+ STGMEDIUM store;
+ if (FAILED(data->GetData(format, &store)))
+ return;
+ dataStrings.append(String(static_cast<T*>(GlobalLock(store.hGlobal)), ::GlobalSize(store.hGlobal) / sizeof(T)));
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+}
+
+void getUtf8Data(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings)
+{
+ STGMEDIUM store;
+ if (FAILED(data->GetData(format, &store)))
+ return;
+ dataStrings.append(String(UTF8Encoding().decode(static_cast<char*>(GlobalLock(store.hGlobal)), GlobalSize(store.hGlobal))));
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+}
+
+#if USE(CF)
+void getCFData(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings)
+{
+ STGMEDIUM store;
+ if (FAILED(data->GetData(format, &store)))
+ return;
+
+ HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(store.hGlobal));
+ if (!hdrop)
+ return;
+
+ WCHAR filename[MAX_PATH];
+ UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0);
+ for (UINT i = 0; i < fileCount; i++) {
+ if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename)))
+ continue;
+ dataStrings.append(static_cast<UChar*>(filename));
+ }
+
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+}
+#endif
+
+// Setter functions.
+
+void setUCharData(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings)
+{
+ STGMEDIUM medium = {0};
+ medium.tymed = TYMED_HGLOBAL;
+
+ medium.hGlobal = createGlobalData(dataStrings.first());
+ if (!medium.hGlobal)
+ return;
+ data->SetData(format, &medium, FALSE);
+ ::GlobalFree(medium.hGlobal);
+}
+
+void setUtf8Data(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings)
+{
+ STGMEDIUM medium = {0};
+ medium.tymed = TYMED_HGLOBAL;
+
+ CString charString = dataStrings.first().utf8();
+ size_t stringLength = charString.length();
+ medium.hGlobal = ::GlobalAlloc(GPTR, stringLength + 1);
+ if (!medium.hGlobal)
+ return;
+ char* buffer = static_cast<char*>(GlobalLock(medium.hGlobal));
+ memcpy(buffer, charString.data(), stringLength);
+ buffer[stringLength] = 0;
+ GlobalUnlock(medium.hGlobal);
+ data->SetData(format, &medium, FALSE);
+ ::GlobalFree(medium.hGlobal);
+}
+
+#if USE(CF)
+void setCFData(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings)
+{
+ STGMEDIUM medium = {0};
+ medium.tymed = TYMED_HGLOBAL;
+
+ SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (dataStrings.first().length() + 2));
+ medium.hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize);
+ if (!medium.hGlobal)
+ return;
+
+ DROPFILES* dropFiles = reinterpret_cast<DROPFILES *>(GlobalLock(medium.hGlobal));
+ dropFiles->pFiles = sizeof(DROPFILES);
+ dropFiles->fWide = TRUE;
+ String filename = dataStrings.first();
+ wcscpy(reinterpret_cast<LPWSTR>(dropFiles + 1), filename.charactersWithNullTermination().data());
+ GlobalUnlock(medium.hGlobal);
+ data->SetData(format, &medium, FALSE);
+ ::GlobalFree(medium.hGlobal);
+}
+#endif
+
+static const ClipboardFormatMap& getClipboardMap()
+{
+ static ClipboardFormatMap formatMap;
+ if (formatMap.isEmpty()) {
+ formatMap.add(htmlFormat()->cfFormat, new ClipboardDataItem(htmlFormat(), getUtf8Data, setUtf8Data));
+ formatMap.add(texthtmlFormat()->cfFormat, new ClipboardDataItem(texthtmlFormat(), getStringData<UChar>, setUCharData));
+ formatMap.add(plainTextFormat()->cfFormat, new ClipboardDataItem(plainTextFormat(), getStringData<char>, setUtf8Data));
+ formatMap.add(plainTextWFormat()->cfFormat, new ClipboardDataItem(plainTextWFormat(), getStringData<UChar>, setUCharData));
+#if USE(CF)
+ formatMap.add(cfHDropFormat()->cfFormat, new ClipboardDataItem(cfHDropFormat(), getCFData, setCFData));
+#endif
+ formatMap.add(filenameFormat()->cfFormat, new ClipboardDataItem(filenameFormat(), getStringData<char>, setUtf8Data));
+ formatMap.add(filenameWFormat()->cfFormat, new ClipboardDataItem(filenameWFormat(), getStringData<UChar>, setUCharData));
+ formatMap.add(urlFormat()->cfFormat, new ClipboardDataItem(urlFormat(), getStringData<char>, setUtf8Data));
+ formatMap.add(urlWFormat()->cfFormat, new ClipboardDataItem(urlWFormat(), getStringData<UChar>, setUCharData));
+ }
+ return formatMap;
+}
+
+void getClipboardData(IDataObject* dataObject, FORMATETC* format, Vector<String>& dataStrings)
+{
+ const ClipboardFormatMap& formatMap = getClipboardMap();
+ ClipboardFormatMap::const_iterator found = formatMap.find(format->cfFormat);
+ if (found == formatMap.end())
+ return;
+ found->value->getString(dataObject, found->value->format, dataStrings);
+}
+
+void setClipboardData(IDataObject* dataObject, UINT format, const Vector<String>& dataStrings)
+{
+ const ClipboardFormatMap& formatMap = getClipboardMap();
+ ClipboardFormatMap::const_iterator found = formatMap.find(format);
+ if (found == formatMap.end())
+ return;
+ found->value->setString(dataObject, found->value->format, dataStrings);
+}
+
+} // namespace WebCore