summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/win/ClipboardWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/win/ClipboardWin.cpp')
-rw-r--r--WebCore/platform/win/ClipboardWin.cpp767
1 files changed, 767 insertions, 0 deletions
diff --git a/WebCore/platform/win/ClipboardWin.cpp b/WebCore/platform/win/ClipboardWin.cpp
new file mode 100644
index 0000000..129d881
--- /dev/null
+++ b/WebCore/platform/win/ClipboardWin.cpp
@@ -0,0 +1,767 @@
+/*
+ * Copyright (C) 2006, 2007 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 "ClipboardWin.h"
+
+#include "CString.h"
+#include "CachedImage.h"
+#include "ClipboardUtilitiesWin.h"
+#include "Document.h"
+#include "DragData.h"
+#include "Editor.h"
+#include "Element.h"
+#include "EventHandler.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
+#include "HTMLNames.h"
+#include "Image.h"
+#include "MIMETypeRegistry.h"
+#include "Page.h"
+#include "Pasteboard.h"
+#include "PlatformMouseEvent.h"
+#include "PlatformString.h"
+#include "Range.h"
+#include "RenderImage.h"
+#include "ResourceResponse.h"
+#include "StringHash.h"
+#include "WCDataObject.h"
+#include "csshelper.h"
+#include "markup.h"
+
+#include <shlwapi.h>
+#include <wininet.h>
+
+#include <wtf/RefPtr.h>
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+// format string for
+static const char szShellDotUrlTemplate[] = "[InternetShortcut]\r\nURL=%s\r\n";
+
+// We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft
+// see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3
+
+enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText };
+
+static ClipboardDataType clipboardTypeFromMIMEType(const String& type)
+{
+ String qType = type.stripWhiteSpace().lower();
+
+ // two special cases for IE compatibility
+ if (qType == "text" || qType == "text/plain" || qType.startsWith("text/plain;"))
+ return ClipboardDataTypeText;
+ if (qType == "url" || qType == "text/uri-list")
+ return ClipboardDataTypeURL;
+
+ return ClipboardDataTypeNone;
+}
+
+static inline FORMATETC* fileDescriptorFormat()
+{
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
+ static FORMATETC fileDescriptorFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &fileDescriptorFormat;
+}
+
+static inline FORMATETC* fileContentFormatZero()
+{
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
+ static FORMATETC fileContentFormat = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
+ return &fileContentFormat;
+}
+
+static inline void pathRemoveBadFSCharacters(PWSTR psz, size_t length)
+{
+ size_t writeTo = 0;
+ size_t readFrom = 0;
+ while (readFrom < length) {
+ UINT type = PathGetCharType(psz[readFrom]);
+ if (psz[readFrom] == 0 || type & (GCT_LFNCHAR | GCT_SHORTCHAR)) {
+ psz[writeTo++] = psz[readFrom];
+ }
+
+ readFrom++;
+ }
+ psz[writeTo] = 0;
+}
+
+static String filesystemPathFromUrlOrTitle(const String& url, const String& title, TCHAR* extension, bool isLink)
+{
+ bool usedURL = false;
+ WCHAR fsPathBuffer[MAX_PATH + 1];
+ fsPathBuffer[0] = 0;
+ int extensionLen = extension ? lstrlen(extension) : 0;
+
+ if (!title.isEmpty()) {
+ size_t len = min<size_t>(title.length(), MAX_PATH - extensionLen);
+ CopyMemory(fsPathBuffer, title.characters(), len * sizeof(UChar));
+ fsPathBuffer[len] = 0;
+ pathRemoveBadFSCharacters(fsPathBuffer, len);
+ }
+
+ if (!lstrlen(fsPathBuffer)) {
+ DWORD len = MAX_PATH;
+ String nullTermURL = url;
+ usedURL = true;
+ if (UrlIsFileUrl((LPCWSTR)nullTermURL.charactersWithNullTermination())
+ && SUCCEEDED(PathCreateFromUrl((LPCWSTR)nullTermURL.charactersWithNullTermination(), fsPathBuffer, &len, 0))) {
+ // When linking to a file URL we can trivially find the file name
+ PWSTR fn = PathFindFileName(fsPathBuffer);
+ if (fn && fn != fsPathBuffer)
+ lstrcpyn(fsPathBuffer, fn, lstrlen(fn) + 1);
+ } else {
+ // The filename for any content based drag should be the last element of
+ // the path. If we can't find it, or we're coming up with the name for a link
+ // we just use the entire url.
+ KURL kurl(url);
+ String lastComponent;
+ if (!isLink && !(lastComponent = kurl.lastPathComponent()).isEmpty()) {
+ len = min<DWORD>(MAX_PATH, lastComponent.length());
+ CopyMemory(fsPathBuffer, lastComponent.characters(), len * sizeof(UChar));
+ } else {
+ len = min<DWORD>(MAX_PATH, nullTermURL.length());
+ CopyMemory(fsPathBuffer, nullTermURL.characters(), len * sizeof(UChar));
+ }
+ fsPathBuffer[len] = 0;
+ pathRemoveBadFSCharacters(fsPathBuffer, len);
+ }
+ }
+
+ if (!extension)
+ return String((UChar*)fsPathBuffer);
+
+ if (!isLink && usedURL) {
+ PathRenameExtension(fsPathBuffer, extension);
+ return String((UChar*)fsPathBuffer);
+ }
+
+ String result((UChar*)fsPathBuffer);
+ result += String((UChar*)extension);
+ return result;
+}
+
+static HGLOBAL createGlobalURLContent(const String& url, int estimatedFileSize)
+{
+ HRESULT hr = S_OK;
+ HGLOBAL memObj = 0;
+
+ char* fileContents;
+ char ansiUrl[INTERNET_MAX_URL_LENGTH + 1];
+ // Used to generate the buffer. This is null terminated whereas the fileContents won't be.
+ char contentGenerationBuffer[INTERNET_MAX_URL_LENGTH + ARRAYSIZE(szShellDotUrlTemplate) + 1];
+
+ if (estimatedFileSize > 0 && estimatedFileSize > ARRAYSIZE(contentGenerationBuffer))
+ return 0;
+
+ int ansiUrlSize = ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)url.characters(), url.length(), ansiUrl, ARRAYSIZE(ansiUrl) - 1, 0, 0);
+ if (!ansiUrlSize)
+ return 0;
+
+ ansiUrl[ansiUrlSize] = 0;
+
+ int fileSize = (int) (ansiUrlSize+strlen(szShellDotUrlTemplate)-2); // -2 to remove the %s
+ ASSERT(estimatedFileSize < 0 || fileSize == estimatedFileSize);
+
+ memObj = GlobalAlloc(GPTR, fileSize);
+ if (!memObj)
+ return 0;
+
+ fileContents = (PSTR)GlobalLock(memObj);
+
+ sprintf_s(contentGenerationBuffer, ARRAYSIZE(contentGenerationBuffer), szShellDotUrlTemplate, ansiUrl);
+ CopyMemory(fileContents, contentGenerationBuffer, fileSize);
+
+ GlobalUnlock(memObj);
+
+ return memObj;
+}
+
+static HGLOBAL createGlobalImageFileContent(SharedBuffer* data)
+{
+ HGLOBAL memObj = GlobalAlloc(GPTR, data->size());
+ if (!memObj)
+ return 0;
+
+ char* fileContents = (PSTR)GlobalLock(memObj);
+
+ CopyMemory(fileContents, data->data(), data->size());
+
+ GlobalUnlock(memObj);
+
+ return memObj;
+}
+
+static HGLOBAL createGlobalHDropContent(const KURL& url, String& fileName, SharedBuffer* data)
+{
+ if (fileName.isEmpty() || !data )
+ return 0;
+
+ WCHAR filePath[MAX_PATH];
+
+ if (url.isLocalFile()) {
+ String localPath = url.path();
+ // windows does not enjoy a leading slash on paths
+ if (localPath[0] == '/')
+ localPath = localPath.substring(1);
+ LPCTSTR localPathStr = localPath.charactersWithNullTermination();
+ if (wcslen(localPathStr) + 1 < MAX_PATH)
+ wcscpy_s(filePath, MAX_PATH, localPathStr);
+ else
+ return 0;
+ } else {
+ WCHAR tempPath[MAX_PATH];
+ WCHAR extension[MAX_PATH];
+ if (!::GetTempPath(ARRAYSIZE(tempPath), tempPath))
+ return 0;
+ if (!::PathAppend(tempPath, fileName.charactersWithNullTermination()))
+ return 0;
+ LPCWSTR foundExtension = ::PathFindExtension(tempPath);
+ if (foundExtension) {
+ if (wcscpy_s(extension, MAX_PATH, foundExtension))
+ return 0;
+ } else
+ *extension = 0;
+ ::PathRemoveExtension(tempPath);
+ for (int i = 1; i < 10000; i++) {
+ if (swprintf_s(filePath, MAX_PATH, TEXT("%s-%d%s"), tempPath, i, extension) == -1)
+ return 0;
+ if (!::PathFileExists(filePath))
+ break;
+ }
+ HANDLE tempFileHandle = CreateFile(filePath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
+ if (tempFileHandle == INVALID_HANDLE_VALUE)
+ return 0;
+
+ // Write the data to this temp file.
+ DWORD written;
+ BOOL tempWriteSucceeded = WriteFile(tempFileHandle, data->data(), data->size(), &written, 0);
+ CloseHandle(tempFileHandle);
+ if (!tempWriteSucceeded)
+ return 0;
+ }
+
+ SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (wcslen(filePath) + 2));
+ HGLOBAL memObj = GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize);
+ if (!memObj)
+ return 0;
+
+ DROPFILES* dropFiles = (DROPFILES*) GlobalLock(memObj);
+ dropFiles->pFiles = sizeof(DROPFILES);
+ dropFiles->fWide = TRUE;
+ wcscpy((LPWSTR)(dropFiles + 1), filePath);
+ GlobalUnlock(memObj);
+
+ return memObj;
+}
+
+static HGLOBAL createGlobalUrlFileDescriptor(const String& url, const String& title, int& /*out*/ estimatedSize)
+{
+ HRESULT hr = S_OK;
+ HGLOBAL memObj = 0;
+ String fsPath;
+ memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
+ if (!memObj)
+ return 0;
+
+ FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj);
+ memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR));
+ fgd->cItems = 1;
+ fgd->fgd[0].dwFlags = FD_FILESIZE;
+ int fileSize = ::WideCharToMultiByte(CP_ACP, 0, url.characters(), url.length(), 0, 0, 0, 0);
+ fileSize += strlen(szShellDotUrlTemplate) - 2; // -2 is for getting rid of %s in the template string
+ fgd->fgd[0].nFileSizeLow = fileSize;
+ estimatedSize = fileSize;
+ fsPath = filesystemPathFromUrlOrTitle(url, title, L".URL", true);
+
+ if (fsPath.length() <= 0) {
+ GlobalUnlock(memObj);
+ GlobalFree(memObj);
+ return 0;
+ }
+
+ int maxSize = min(fsPath.length(), ARRAYSIZE(fgd->fgd[0].cFileName));
+ CopyMemory(fgd->fgd[0].cFileName, (LPCWSTR)fsPath.characters(), maxSize * sizeof(UChar));
+ GlobalUnlock(memObj);
+
+ return memObj;
+}
+
+
+static HGLOBAL createGlobalImageFileDescriptor(const String& url, const String& title, CachedImage* image)
+{
+ ASSERT_ARG(image, image);
+ ASSERT(image->image()->data());
+
+ HRESULT hr = S_OK;
+ HGLOBAL memObj = 0;
+ String fsPath;
+ memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
+ if (!memObj)
+ return 0;
+
+ FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj);
+ memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR));
+ fgd->cItems = 1;
+ fgd->fgd[0].dwFlags = FD_FILESIZE;
+ fgd->fgd[0].nFileSizeLow = image->image()->data()->size();
+
+ String extension(".");
+ extension += WebCore::MIMETypeRegistry::getPreferredExtensionForMIMEType(image->response().mimeType());
+ const String& preferredTitle = title.isEmpty() ? image->response().suggestedFilename() : title;
+ fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, extension.length() ? (TCHAR*)extension.charactersWithNullTermination() : 0, false);
+
+ if (fsPath.length() <= 0) {
+ GlobalUnlock(memObj);
+ GlobalFree(memObj);
+ return 0;
+ }
+
+ int maxSize = min(fsPath.length(), ARRAYSIZE(fgd->fgd[0].cFileName));
+ CopyMemory(fgd->fgd[0].cFileName, (LPCWSTR)fsPath.characters(), maxSize * sizeof(UChar));
+ GlobalUnlock(memObj);
+
+ return memObj;
+}
+
+
+// writeFileToDataObject takes ownership of fileDescriptor and fileContent
+static HRESULT writeFileToDataObject(IDataObject* dataObject, HGLOBAL fileDescriptor, HGLOBAL fileContent, HGLOBAL hDropContent)
+{
+ HRESULT hr = S_OK;
+ FORMATETC* fe;
+ STGMEDIUM medium = {0};
+ medium.tymed = TYMED_HGLOBAL;
+
+ if (!fileDescriptor || !fileContent)
+ goto exit;
+
+ // Descriptor
+ fe = fileDescriptorFormat();
+
+ medium.hGlobal = fileDescriptor;
+
+ if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE)))
+ goto exit;
+
+ // Contents
+ fe = fileContentFormatZero();
+ medium.hGlobal = fileContent;
+ if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE)))
+ goto exit;
+
+ // HDROP
+ if (hDropContent) {
+ medium.hGlobal = hDropContent;
+ hr = dataObject->SetData(cfHDropFormat(), &medium, TRUE);
+ }
+
+exit:
+ if (FAILED(hr)) {
+ if (fileDescriptor)
+ GlobalFree(fileDescriptor);
+ if (fileContent)
+ GlobalFree(fileContent);
+ if (hDropContent)
+ GlobalFree(hDropContent);
+ }
+ return hr;
+}
+
+ClipboardWin::ClipboardWin(bool isForDragging, IDataObject* dataObject, ClipboardAccessPolicy policy)
+ : Clipboard(policy, isForDragging)
+ , m_dataObject(dataObject)
+ , m_writableDataObject(0)
+{
+}
+
+ClipboardWin::ClipboardWin(bool isForDragging, WCDataObject* dataObject, ClipboardAccessPolicy policy)
+ : Clipboard(policy, isForDragging)
+ , m_dataObject(dataObject)
+ , m_writableDataObject(dataObject)
+{
+}
+
+ClipboardWin::~ClipboardWin()
+{
+}
+
+static bool writeURL(WCDataObject *data, const KURL& url, String title, bool withPlainText, bool withHTML)
+{
+ ASSERT(data);
+
+ if (url.isEmpty())
+ return false;
+
+ if (title.isEmpty()) {
+ title = url.lastPathComponent();
+ if (title.isEmpty())
+ title = url.host();
+ }
+
+ STGMEDIUM medium = {0};
+ medium.tymed = TYMED_HGLOBAL;
+
+ medium.hGlobal = createGlobalData(url, title);
+ bool success = false;
+ if (medium.hGlobal && FAILED(data->SetData(urlWFormat(), &medium, TRUE)))
+ ::GlobalFree(medium.hGlobal);
+ else
+ success = true;
+
+ if (withHTML) {
+ Vector<char> cfhtmlData;
+ markupToCF_HTML(urlToMarkup(url, title), "", cfhtmlData);
+ medium.hGlobal = createGlobalData(cfhtmlData);
+ if (medium.hGlobal && FAILED(data->SetData(htmlFormat(), &medium, TRUE)))
+ ::GlobalFree(medium.hGlobal);
+ else
+ success = true;
+ }
+
+ if (withPlainText) {
+ medium.hGlobal = createGlobalData(url.string());
+ if (medium.hGlobal && FAILED(data->SetData(plainTextWFormat(), &medium, TRUE)))
+ ::GlobalFree(medium.hGlobal);
+ else
+ success = true;
+ }
+
+ return success;
+}
+
+void ClipboardWin::clearData(const String& type)
+{
+ //FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
+ ASSERT(isForDragging());
+ if (policy() != ClipboardWritable || !m_writableDataObject)
+ return;
+
+ ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
+
+ if (dataType == ClipboardDataTypeURL) {
+ m_writableDataObject->clearData(urlWFormat()->cfFormat);
+ m_writableDataObject->clearData(urlFormat()->cfFormat);
+ }
+ if (dataType == ClipboardDataTypeText) {
+ m_writableDataObject->clearData(plainTextFormat()->cfFormat);
+ m_writableDataObject->clearData(plainTextWFormat()->cfFormat);
+ }
+
+}
+
+void ClipboardWin::clearAllData()
+{
+ //FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
+ ASSERT(isForDragging());
+ if (policy() != ClipboardWritable)
+ return;
+
+ m_writableDataObject = 0;
+ WCDataObject::createInstance(&m_writableDataObject);
+ m_dataObject = m_writableDataObject;
+}
+
+String ClipboardWin::getData(const String& type, bool& success) const
+{
+ success = false;
+ if (policy() != ClipboardReadable || !m_dataObject) {
+ return "";
+ }
+
+ ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
+ if (dataType == ClipboardDataTypeText)
+ return getPlainText(m_dataObject.get(), success);
+ else if (dataType == ClipboardDataTypeURL)
+ return getURL(m_dataObject.get(), success);
+
+ return "";
+}
+
+bool ClipboardWin::setData(const String& type, const String& data)
+{
+ // FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
+ ASSERT(isForDragging());
+ if (policy() != ClipboardWritable || !m_writableDataObject)
+ return false;
+
+ ClipboardDataType winType = clipboardTypeFromMIMEType(type);
+
+ if (winType == ClipboardDataTypeURL)
+ return WebCore::writeURL(m_writableDataObject.get(), KURL(data), String(), false, true);
+
+ if (winType == ClipboardDataTypeText) {
+ STGMEDIUM medium = {0};
+ medium.tymed = TYMED_HGLOBAL;
+ medium.hGlobal = createGlobalData(data);
+ if (!medium.hGlobal)
+ return false;
+
+ if (FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE))) {
+ ::GlobalFree(medium.hGlobal);
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static void addMimeTypesForFormat(HashSet<String>& results, FORMATETC& format)
+{
+ // URL and Text are provided for compatibility with IE's model
+ if (format.cfFormat == urlFormat()->cfFormat || format.cfFormat == urlWFormat()->cfFormat) {
+ results.add("URL");
+ results.add("text/uri-list");
+ }
+
+ if (format.cfFormat == plainTextWFormat()->cfFormat || format.cfFormat == plainTextFormat()->cfFormat) {
+ results.add("Text");
+ results.add("text/plain");
+ }
+}
+
+// extensions beyond IE's API
+HashSet<String> ClipboardWin::types() const
+{
+ HashSet<String> results;
+ if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
+ return results;
+
+ if (!m_dataObject)
+ return results;
+
+ COMPtr<IEnumFORMATETC> itr;
+
+ if (FAILED(m_dataObject->EnumFormatEtc(0, &itr)))
+ return results;
+
+ if (!itr)
+ return results;
+
+ FORMATETC data;
+
+ while (SUCCEEDED(itr->Next(1, &data, 0))) {
+ addMimeTypesForFormat(results, data);
+ }
+
+ return results;
+}
+
+void ClipboardWin::setDragImage(CachedImage* image, Node *node, const IntPoint &loc)
+{
+ if (policy() != ClipboardImageWritable && policy() != ClipboardWritable)
+ return;
+
+ if (m_dragImage)
+ m_dragImage->removeClient(this);
+ m_dragImage = image;
+ if (m_dragImage)
+ m_dragImage->addClient(this);
+
+ m_dragLoc = loc;
+ m_dragImageElement = node;
+}
+
+void ClipboardWin::setDragImage(CachedImage* img, const IntPoint &loc)
+{
+ setDragImage(img, 0, loc);
+}
+
+void ClipboardWin::setDragImageElement(Node *node, const IntPoint &loc)
+{
+ setDragImage(0, node, loc);
+}
+
+DragImageRef ClipboardWin::createDragImage(IntPoint& loc) const
+{
+ HBITMAP result = 0;
+ //FIXME: Need to be able to draw element <rdar://problem/5015942>
+ if (m_dragImage) {
+ result = createDragImageFromImage(m_dragImage->image());
+ loc = m_dragLoc;
+ }
+ return result;
+}
+
+static String imageToMarkup(const String& url)
+{
+ String markup("<img src=\"");
+ markup.append(url);
+ markup.append("\"/>");
+ return markup;
+}
+
+static CachedImage* getCachedImage(Element* element)
+{
+ // Attempt to pull CachedImage from element
+ ASSERT(element);
+ RenderObject* renderer = element->renderer();
+ if (!renderer || !renderer->isImage())
+ return 0;
+
+ RenderImage* image = static_cast<RenderImage*>(renderer);
+ if (image->cachedImage() && !image->cachedImage()->errorOccurred())
+ return image->cachedImage();
+
+ return 0;
+}
+
+static void writeImageToDataObject(IDataObject* dataObject, Element* element, const KURL& url)
+{
+ // Shove image data into a DataObject for use as a file
+ CachedImage* cachedImage = getCachedImage(element);
+ if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded())
+ return;
+
+ SharedBuffer* imageBuffer = cachedImage->image()->data();
+ if (!imageBuffer || !imageBuffer->size())
+ return;
+
+ HGLOBAL imageFileDescriptor = createGlobalImageFileDescriptor(url.string(), element->getAttribute(altAttr), cachedImage);
+ if (!imageFileDescriptor)
+ return;
+
+ HGLOBAL imageFileContent = createGlobalImageFileContent(imageBuffer);
+ if (!imageFileContent) {
+ GlobalFree(imageFileDescriptor);
+ return;
+ }
+
+ String fileName = cachedImage->response().suggestedFilename();
+ HGLOBAL hDropContent = createGlobalHDropContent(url, fileName, imageBuffer);
+ if (!hDropContent) {
+ GlobalFree(hDropContent);
+ return;
+ }
+
+ writeFileToDataObject(dataObject, imageFileDescriptor, imageFileContent, hDropContent);
+}
+
+void ClipboardWin::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame)
+{
+ // Order is important here for Explorer's sake
+ if (!m_writableDataObject)
+ return;
+ WebCore::writeURL(m_writableDataObject.get(), url, title, true, false);
+
+ writeImageToDataObject(m_writableDataObject.get(), element, url);
+
+ AtomicString imageURL = element->getAttribute(srcAttr);
+ if (imageURL.isEmpty())
+ return;
+
+ String fullURL = frame->document()->completeURL(parseURL(imageURL)).string();
+ if (fullURL.isEmpty())
+ return;
+ STGMEDIUM medium = {0};
+ medium.tymed = TYMED_HGLOBAL;
+ ExceptionCode ec = 0;
+
+ // Put img tag on the clipboard referencing the image
+ Vector<char> data;
+ markupToCF_HTML(imageToMarkup(fullURL), "", data);
+ medium.hGlobal = createGlobalData(data);
+ if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
+ ::GlobalFree(medium.hGlobal);
+}
+
+void ClipboardWin::writeURL(const KURL& kurl, const String& titleStr, Frame*)
+{
+ if (!m_writableDataObject)
+ return;
+ WebCore::writeURL(m_writableDataObject.get(), kurl, titleStr, true, true);
+
+ int estimatedSize = 0;
+ String url = kurl.string();
+
+ HGLOBAL urlFileDescriptor = createGlobalUrlFileDescriptor(url, titleStr, estimatedSize);
+ if (!urlFileDescriptor)
+ return;
+ HGLOBAL urlFileContent = createGlobalURLContent(url, estimatedSize);
+ if (!urlFileContent) {
+ GlobalFree(urlFileDescriptor);
+ return;
+ }
+ writeFileToDataObject(m_writableDataObject.get(), urlFileDescriptor, urlFileContent, 0);
+}
+
+void ClipboardWin::writeRange(Range* selectedRange, Frame* frame)
+{
+ ASSERT(selectedRange);
+ if (!m_writableDataObject)
+ return;
+
+ STGMEDIUM medium = {0};
+ medium.tymed = TYMED_HGLOBAL;
+ ExceptionCode ec = 0;
+
+ Vector<char> data;
+ markupToCF_HTML(createMarkup(selectedRange, 0, AnnotateForInterchange),
+ selectedRange->startContainer(ec)->document()->url().string(), data);
+ medium.hGlobal = createGlobalData(data);
+ if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
+ ::GlobalFree(medium.hGlobal);
+
+ String str = frame->selectedText();
+ replaceNewlinesWithWindowsStyleNewlines(str);
+ replaceNBSPWithSpace(str);
+ medium.hGlobal = createGlobalData(str);
+ if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
+ ::GlobalFree(medium.hGlobal);
+
+ medium.hGlobal = 0;
+ if (frame->editor()->canSmartCopyOrDelete())
+ m_writableDataObject->SetData(smartPasteFormat(), &medium, TRUE);
+}
+
+bool ClipboardWin::hasData()
+{
+ if (!m_dataObject)
+ return false;
+
+ COMPtr<IEnumFORMATETC> itr;
+ if (FAILED(m_dataObject->EnumFormatEtc(0, &itr)))
+ return false;
+
+ if (!itr)
+ return false;
+
+ FORMATETC data;
+
+ if (SUCCEEDED(itr->Next(1, &data, 0))) {
+ // There is at least one item in the IDataObject
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace WebCore