summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/win/ClipboardUtilitiesWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/win/ClipboardUtilitiesWin.cpp')
-rw-r--r--WebCore/platform/win/ClipboardUtilitiesWin.cpp452
1 files changed, 452 insertions, 0 deletions
diff --git a/WebCore/platform/win/ClipboardUtilitiesWin.cpp b/WebCore/platform/win/ClipboardUtilitiesWin.cpp
new file mode 100644
index 0000000..3762a1a
--- /dev/null
+++ b/WebCore/platform/win/ClipboardUtilitiesWin.cpp
@@ -0,0 +1,452 @@
+/*
+ * 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 COMPUTER, 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 COMPUTER, 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 "CString.h"
+#include "DocumentFragment.h"
+#include "KURL.h"
+#include "PlatformString.h"
+#include "TextEncoding.h"
+#include "markup.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <wtf/RetainPtr.h>
+#include <shlwapi.h>
+#include <wininet.h> // for INTERNET_MAX_URL_LENGTH
+
+namespace WebCore {
+
+FORMATETC* cfHDropFormat()
+{
+ static FORMATETC urlFormat = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &urlFormat;
+}
+
+static bool getWebLocData(IDataObject* dataObject, String& url, String* title)
+{
+ bool succeeded = false;
+ WCHAR filename[MAX_PATH];
+ WCHAR urlBuffer[INTERNET_MAX_URL_LENGTH];
+
+ STGMEDIUM medium;
+ if (FAILED(dataObject->GetData(cfHDropFormat(), &medium)))
+ return false;
+
+ HDROP hdrop = (HDROP)GlobalLock(medium.hGlobal);
+
+ if (!hdrop)
+ return false;
+
+ if (!DragQueryFileW(hdrop, 0, filename, ARRAYSIZE(filename)))
+ goto exit;
+
+ if (_wcsicmp(PathFindExtensionW(filename), L".url"))
+ goto exit;
+
+ if (!GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, urlBuffer, ARRAYSIZE(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);
+ return succeeded;
+}
+
+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 KURL& 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 = (PWSTR)::GlobalLock(cbData);
+ swprintf_s(buffer, size, L"%s\n%s", mutableURL.charactersWithNullTermination(), mutableTitle.charactersWithNullTermination());
+ ::GlobalUnlock(cbData);
+ }
+ return cbData;
+}
+
+HGLOBAL createGlobalData(const String& str)
+{
+ HGLOBAL globalData = ::GlobalAlloc(GPTR, (str.length() + 1) * sizeof(UChar));
+ if (!globalData)
+ return 0;
+ UChar* buffer = static_cast<UChar*>(::GlobalLock(globalData));
+ memcpy(buffer, str.characters(), str.length() * sizeof(UChar));
+ buffer[str.length()] = 0;
+ ::GlobalUnlock(globalData);
+ return globalData;
+}
+
+HGLOBAL createGlobalData(const Vector<char>& vector)
+{
+ HGLOBAL globalData = ::GlobalAlloc(GPTR, vector.size() + 1);
+ if (!globalData)
+ return 0;
+ char* buffer = static_cast<char*>(::GlobalLock(globalData));
+ memcpy(buffer, vector.data(), vector.size());
+ buffer[vector.size()] = 0;
+ ::GlobalUnlock(globalData);
+ return globalData;
+}
+
+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());
+}
+
+// Documentation for the CF_HTML format is available at http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
+void markupToCF_HTML(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);
+
+ append(result, String::format(header, startHTMLOffset, endHTMLOffset, startFragmentOffset, endFragmentOffset).utf8());
+ 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
+}
+
+String urlToMarkup(const KURL& url, const String& title)
+{
+ Vector<UChar> markup;
+ append(markup, "<a href=\"");
+ append(markup, url.string());
+ append(markup, "\">");
+ append(markup, title);
+ append(markup, "</a>");
+ return String::adopt(markup);
+}
+
+void replaceNewlinesWithWindowsStyleNewlines(String& str)
+{
+ static const UChar Newline = '\n';
+ static const char* const WindowsNewline("\r\n");
+ str.replace(Newline, WindowsNewline);
+}
+
+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;
+}
+
+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 = String(CFURLGetString(cfURL.get()));
+ return true;
+}
+
+String getURL(IDataObject* dataObject, bool& success, String* title)
+{
+ STGMEDIUM store;
+ String url;
+ success = false;
+ if (getWebLocData(dataObject, url, title)) {
+ success = true;
+ return url;
+ } else if (SUCCEEDED(dataObject->GetData(urlWFormat(), &store))) {
+ //URL using unicode
+ UChar* data = (UChar*)GlobalLock(store.hGlobal);
+ url = extractURL(String(data), title);
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ success = true;
+ } else if (SUCCEEDED(dataObject->GetData(urlFormat(), &store))) {
+ //URL using ascii
+ char* data = (char*)GlobalLock(store.hGlobal);
+ url = extractURL(String(data), title);
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ success = true;
+ } else if (SUCCEEDED(dataObject->GetData(filenameWFormat(), &store))) {
+ //file using unicode
+ wchar_t* data = (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)) {
+ if (title)
+ *title = url;
+ success = true;
+ }
+ }
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ } else if (SUCCEEDED(dataObject->GetData(filenameFormat(), &store))) {
+ //filename using ascii
+ char* data = (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)) {
+ if (title)
+ *title = url;
+ success = true;
+ }
+ }
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ }
+ return url;
+}
+
+String getPlainText(IDataObject* dataObject, bool& success)
+{
+ STGMEDIUM store;
+ String text;
+ success = false;
+ if (SUCCEEDED(dataObject->GetData(plainTextWFormat(), &store))) {
+ //unicode text
+ UChar* data = (UChar*)GlobalLock(store.hGlobal);
+ text = String(data);
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ success = true;
+ } else if (SUCCEEDED(dataObject->GetData(plainTextFormat(), &store))) {
+ //ascii text
+ char* data = (char*)GlobalLock(store.hGlobal);
+ text = String(data);
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ success = true;
+ } else {
+ //If a file is dropped on the window, it does not provide either of the
+ //plain text formats, so here we try to forcibly get a url.
+ text = getURL(dataObject, success);
+ success = true;
+ }
+ return text;
+}
+
+PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*)
+{
+ //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;
+}
+
+//Convert a String containing CF_HTML formatted text to a DocumentFragment
+PassRefPtr<DocumentFragment> fragmentFromCF_HTML(Document* doc, const String& cf_html)
+{
+ // obtain baseURL if present
+ String srcURLStr("sourceURL:");
+ String srcURL;
+ unsigned lineStart = cf_html.find(srcURLStr, 0, false);
+ if (lineStart != -1) {
+ unsigned srcEnd = cf_html.find("\n", lineStart, false);
+ unsigned srcStart = lineStart+srcURLStr.length();
+ String rawSrcURL = cf_html.substring(srcStart, srcEnd-srcStart);
+ replaceNBSPWithSpace(rawSrcURL);
+ srcURL = rawSrcURL.stripWhiteSpace();
+ }
+
+ // find the markup between "<!--StartFragment -->" and "<!--EndFragment -->", accounting for browser quirks
+ unsigned markupStart = cf_html.find("<html", 0, false);
+ unsigned tagStart = cf_html.find("startfragment", markupStart, false);
+ unsigned fragmentStart = cf_html.find('>', tagStart) + 1;
+ unsigned tagEnd = cf_html.find("endfragment", fragmentStart, false);
+ unsigned fragmentEnd = cf_html.reverseFind('<', tagEnd);
+ String markup = cf_html.substring(fragmentStart, fragmentEnd - fragmentStart).stripWhiteSpace();
+
+ return createFragmentFromMarkup(doc, markup, srcURL);
+}
+
+
+PassRefPtr<DocumentFragment> fragmentFromHTML(Document* doc, IDataObject* data)
+{
+ if (!doc || !data)
+ return 0;
+
+ STGMEDIUM store;
+ String html;
+ String srcURL;
+ if (SUCCEEDED(data->GetData(htmlFormat(), &store))) {
+ //MS HTML Format parsing
+ char* data = (char*)GlobalLock(store.hGlobal);
+ SIZE_T dataSize = ::GlobalSize(store.hGlobal);
+ String cf_html(UTF8Encoding().decode(data, dataSize));
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ if (PassRefPtr<DocumentFragment> fragment = fragmentFromCF_HTML(doc, cf_html))
+ return fragment;
+ }
+ if (SUCCEEDED(data->GetData(texthtmlFormat(), &store))) {
+ //raw html
+ UChar* data = (UChar*)GlobalLock(store.hGlobal);
+ html = String(data);
+ GlobalUnlock(store.hGlobal);
+ ReleaseStgMedium(&store);
+ return createFragmentFromMarkup(doc, html, srcURL);
+ }
+
+ return 0;
+}
+
+bool containsHTML(IDataObject* data)
+{
+ return SUCCEEDED(data->QueryGetData(texthtmlFormat())) || SUCCEEDED(data->QueryGetData(htmlFormat()));
+}
+
+} // namespace WebCore