/* * 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 #include #include 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(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(fsPathMaxLengthExcludingExtension, lastComponent.length()); CopyMemory(fsPathBuffer, lastComponent.characters(), len * sizeof(UChar)); } else { len = min(fsPathMaxLengthExcludingExtension, url.length()); CopyMemory(fsPathBuffer, url.characters(), len * sizeof(UChar)); } fsPathBuffer[len] = 0; pathRemoveBadFSCharacters(fsPathBuffer, len); } if (!extension) return String(static_cast(fsPathBuffer)); if (!isLink && usedURL) { PathRenameExtension(fsPathBuffer, extension); return String(static_cast(fsPathBuffer)); } String result(static_cast(fsPathBuffer)); result += String(static_cast(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 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 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 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 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& 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 ClipboardWin::types() const { HashSet results; if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) return results; if (!m_dataObject) return results; COMPtr 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 ClipboardWin::files() const { RefPtr 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(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(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(""); 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 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 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 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