/*
* 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 "FileList.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameView.h"
#include "HTMLNames.h"
#include "Image.h"
#include "MIMETypeRegistry.h"
#include "NotImplemented.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)
{
static const size_t fsPathMaxLengthExcludingNullTerminator = MAX_PATH - 1;
bool usedURL = false;
WCHAR fsPathBuffer[MAX_PATH];
fsPathBuffer[0] = 0;
int extensionLen = extension ? lstrlen(extension) : 0;
int fsPathMaxLengthExcludingExtension = fsPathMaxLengthExcludingNullTerminator - extensionLen;
if (!title.isEmpty()) {
size_t len = min<size_t>(title.length(), fsPathMaxLengthExcludingExtension);
CopyMemory(fsPathBuffer, title.characters(), len * sizeof(UChar));
fsPathBuffer[len] = 0;
pathRemoveBadFSCharacters(fsPathBuffer, len);
}
if (!lstrlen(fsPathBuffer)) {
KURL kurl(ParsedURLString, url);
usedURL = true;
// The filename for any content based drag or file url 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.
DWORD len = fsPathMaxLengthExcludingExtension;
String lastComponent = kurl.lastPathComponent();
if (kurl.isLocalFile() || (!isLink && !lastComponent.isEmpty())) {
len = min<DWORD>(fsPathMaxLengthExcludingExtension, lastComponent.length());
CopyMemory(fsPathBuffer, lastComponent.characters(), len * sizeof(UChar));
} else {
len = min<DWORD>(fsPathMaxLengthExcludingExtension, url.length());
CopyMemory(fsPathBuffer, url.characters(), len * sizeof(UChar));
}
fsPathBuffer[len] = 0;
pathRemoveBadFSCharacters(fsPathBuffer, len);
}
if (!extension)
return String(static_cast<UChar*>(fsPathBuffer));
if (!isLink && usedURL) {
PathRenameExtension(fsPathBuffer, extension);
return String(static_cast<UChar*>(fsPathBuffer));
}
String result(static_cast<UChar*>(fsPathBuffer));
result += String(static_cast<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();
const String& preferredTitle = title.isEmpty() ? image->response().suggestedFilename() : title;
String extension = image->image()->filenameExtension();
if (extension.isEmpty()) {
// Do not continue processing in the rare and unusual case where a decoded image is not able
// to provide a filename extension. Something tricky (like a bait-n-switch) is going on
return 0;
}
extension.insert(".", 0);
fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, (TCHAR*)extension.charactersWithNullTermination(), 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(ParsedURLString, 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, const 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(DATADIR_GET, &itr)))
return results;
if (!itr)
return results;
FORMATETC data;
// IEnumFORMATETC::Next returns S_FALSE if there are no more items.
while (itr->Next(1, &data, 0) == S_OK) {
addMimeTypesForFormat(results, data);
}
return results;
}
PassRefPtr<FileList> ClipboardWin::files() const
{
RefPtr<FileList> files = FileList::create();
if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
return files.release();
if (!m_dataObject)
return files.release();
STGMEDIUM medium;
if (FAILED(m_dataObject->GetData(cfHDropFormat(), &medium)))
return files.release();
HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(medium.hGlobal));
if (!hdrop)
return files.release();
WCHAR filename[MAX_PATH];
UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0);
for (UINT i = 0; i < fileCount; i++) {
if (!DragQueryFileW(hdrop, i, filename, ARRAYSIZE(filename)))
continue;
files->append(File::create(reinterpret_cast<UChar*>(filename)));
}
GlobalUnlock(medium.hGlobal);
ReleaseStgMedium(&medium);
return files.release();
}
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;
if (m_dragImage) {
result = createDragImageFromImage(m_dragImage->image());
loc = m_dragLoc;
} else if (m_dragImageElement) {
Node* node = m_dragImageElement.get();
result = node->document()->frame()->nodeImage(node);
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 = toRenderImage(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(deprecatedParseURL(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);
}
void ClipboardWin::writePlainText(const String& text)
{
if (!m_writableDataObject)
return;
STGMEDIUM medium = {0};
medium.tymed = TYMED_HGLOBAL;
ExceptionCode ec = 0;
String str = text;
replaceNewlinesWithWindowsStyleNewlines(str);
replaceNBSPWithSpace(str);
medium.hGlobal = createGlobalData(str);
if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
::GlobalFree(medium.hGlobal);
medium.hGlobal = 0;
}
bool ClipboardWin::hasData()
{
if (!m_dataObject)
return false;
COMPtr<IEnumFORMATETC> itr;
if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr)))
return false;
if (!itr)
return false;
FORMATETC data;
// IEnumFORMATETC::Next returns S_FALSE if there are no more items.
if (itr->Next(1, &data, 0) == S_OK) {
// There is at least one item in the IDataObject
return true;
}
return false;
}
void ClipboardWin::setExternalDataObject(IDataObject *dataObject)
{
ASSERT(isForDragging());
m_writableDataObject = 0;
m_dataObject = dataObject;
}
} // namespace WebCore