2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "ClipboardWin.h"
29 #include "CachedImage.h"
30 #include "ClipboardUtilitiesWin.h"
35 #include "EventHandler.h"
38 #include "FrameLoader.h"
39 #include "FrameView.h"
40 #include "HTMLNames.h"
41 #include "HTMLParserIdioms.h"
43 #include "MIMETypeRegistry.h"
44 #include "NotImplemented.h"
46 #include "Pasteboard.h"
47 #include "PlatformMouseEvent.h"
49 #include "RenderImage.h"
50 #include "ResourceResponse.h"
51 #include "SharedBuffer.h"
52 #include "WCDataObject.h"
56 #include <wtf/RefPtr.h>
57 #include <wtf/text/CString.h>
58 #include <wtf/text/WTFString.h>
59 #include <wtf/text/StringHash.h>
65 using namespace HTMLNames;
67 // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft
68 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3
70 enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText, ClipboardDataTypeTextHTML };
72 static ClipboardDataType clipboardTypeFromMIMEType(const String& type)
74 String qType = type.stripWhiteSpace().lower();
76 // two special cases for IE compatibility
77 if (qType == "text" || qType == "text/plain" || qType.startsWith("text/plain;"))
78 return ClipboardDataTypeText;
79 if (qType == "url" || qType == "text/uri-list")
80 return ClipboardDataTypeURL;
81 if (qType == "text/html")
82 return ClipboardDataTypeTextHTML;
84 return ClipboardDataTypeNone;
88 static inline void pathRemoveBadFSCharacters(PWSTR psz, size_t length)
92 while (readFrom < length) {
93 UINT type = PathGetCharType(psz[readFrom]);
94 if (!psz[readFrom] || type & (GCT_LFNCHAR | GCT_SHORTCHAR))
95 psz[writeTo++] = psz[readFrom];
103 static String filesystemPathFromUrlOrTitle(const String& url, const String& title, const UChar* extension, bool isLink)
109 static const size_t fsPathMaxLengthExcludingNullTerminator = MAX_PATH - 1;
110 bool usedURL = false;
111 WCHAR fsPathBuffer[MAX_PATH];
113 int extensionLen = extension ? lstrlen(extension) : 0;
114 int fsPathMaxLengthExcludingExtension = fsPathMaxLengthExcludingNullTerminator - extensionLen;
116 if (!title.isEmpty()) {
117 size_t len = min<size_t>(title.length(), fsPathMaxLengthExcludingExtension);
118 CopyMemory(fsPathBuffer, title.characters(), len * sizeof(UChar));
119 fsPathBuffer[len] = 0;
120 pathRemoveBadFSCharacters(fsPathBuffer, len);
123 if (!lstrlen(fsPathBuffer)) {
124 KURL kurl(ParsedURLString, url);
126 // The filename for any content based drag or file url should be the last element of
127 // the path. If we can't find it, or we're coming up with the name for a link
128 // we just use the entire url.
129 DWORD len = fsPathMaxLengthExcludingExtension;
130 String lastComponent = kurl.lastPathComponent();
131 if (kurl.isLocalFile() || (!isLink && !lastComponent.isEmpty())) {
132 len = min<DWORD>(fsPathMaxLengthExcludingExtension, lastComponent.length());
133 CopyMemory(fsPathBuffer, lastComponent.characters(), len * sizeof(UChar));
135 len = min<DWORD>(fsPathMaxLengthExcludingExtension, url.length());
136 CopyMemory(fsPathBuffer, url.characters(), len * sizeof(UChar));
138 fsPathBuffer[len] = 0;
139 pathRemoveBadFSCharacters(fsPathBuffer, len);
143 return String(static_cast<UChar*>(fsPathBuffer));
145 if (!isLink && usedURL) {
146 PathRenameExtension(fsPathBuffer, extension);
147 return String(static_cast<UChar*>(fsPathBuffer));
150 String result(static_cast<UChar*>(fsPathBuffer));
151 result += String(extension);
156 static HGLOBAL createGlobalImageFileContent(SharedBuffer* data)
158 HGLOBAL memObj = GlobalAlloc(GPTR, data->size());
162 char* fileContents = (PSTR)GlobalLock(memObj);
164 CopyMemory(fileContents, data->data(), data->size());
166 GlobalUnlock(memObj);
171 static HGLOBAL createGlobalHDropContent(const KURL& url, String& fileName, SharedBuffer* data)
173 if (fileName.isEmpty() || !data)
176 WCHAR filePath[MAX_PATH];
178 if (url.isLocalFile()) {
179 String localPath = url.path();
180 // windows does not enjoy a leading slash on paths
181 if (localPath[0] == '/')
182 localPath = localPath.substring(1);
183 LPCWSTR localPathStr = localPath.charactersWithNullTermination();
184 if (wcslen(localPathStr) + 1 < MAX_PATH)
185 wcscpy_s(filePath, MAX_PATH, localPathStr);
193 WCHAR tempPath[MAX_PATH];
194 WCHAR extension[MAX_PATH];
195 if (!::GetTempPath(WTF_ARRAY_LENGTH(tempPath), tempPath))
197 if (!::PathAppend(tempPath, fileName.charactersWithNullTermination()))
199 LPCWSTR foundExtension = ::PathFindExtension(tempPath);
200 if (foundExtension) {
201 if (wcscpy_s(extension, MAX_PATH, foundExtension))
205 ::PathRemoveExtension(tempPath);
206 for (int i = 1; i < 10000; i++) {
207 if (swprintf_s(filePath, MAX_PATH, TEXT("%s-%d%s"), tempPath, i, extension) == -1)
209 if (!::PathFileExists(filePath))
212 HANDLE tempFileHandle = CreateFile(filePath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
213 if (tempFileHandle == INVALID_HANDLE_VALUE)
216 // Write the data to this temp file.
218 BOOL tempWriteSucceeded = WriteFile(tempFileHandle, data->data(), data->size(), &written, 0);
219 CloseHandle(tempFileHandle);
220 if (!tempWriteSucceeded)
225 SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (wcslen(filePath) + 2));
226 HGLOBAL memObj = GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize);
230 DROPFILES* dropFiles = (DROPFILES*) GlobalLock(memObj);
231 dropFiles->pFiles = sizeof(DROPFILES);
232 dropFiles->fWide = TRUE;
233 wcscpy((LPWSTR)(dropFiles + 1), filePath);
234 GlobalUnlock(memObj);
239 static HGLOBAL createGlobalImageFileDescriptor(const String& url, const String& title, CachedImage* image)
241 ASSERT_ARG(image, image);
242 ASSERT(image->image()->data());
247 memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
251 FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj);
252 memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR));
254 fgd->fgd[0].dwFlags = FD_FILESIZE;
255 fgd->fgd[0].nFileSizeLow = image->image()->data()->size();
257 const String& preferredTitle = title.isEmpty() ? image->response().suggestedFilename() : title;
258 String extension = image->image()->filenameExtension();
259 if (extension.isEmpty()) {
260 // Do not continue processing in the rare and unusual case where a decoded image is not able
261 // to provide a filename extension. Something tricky (like a bait-n-switch) is going on
264 extension.insert(".", 0);
265 fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, extension.charactersWithNullTermination(), false);
267 if (fsPath.length() <= 0) {
268 GlobalUnlock(memObj);
273 int maxSize = min(fsPath.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName));
274 CopyMemory(fgd->fgd[0].cFileName, (LPCWSTR)fsPath.characters(), maxSize * sizeof(UChar));
275 GlobalUnlock(memObj);
281 // writeFileToDataObject takes ownership of fileDescriptor and fileContent
282 static HRESULT writeFileToDataObject(IDataObject* dataObject, HGLOBAL fileDescriptor, HGLOBAL fileContent, HGLOBAL hDropContent)
286 STGMEDIUM medium = {0};
287 medium.tymed = TYMED_HGLOBAL;
289 if (!fileDescriptor || !fileContent)
293 fe = fileDescriptorFormat();
295 medium.hGlobal = fileDescriptor;
297 if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE)))
301 fe = fileContentFormatZero();
302 medium.hGlobal = fileContent;
303 if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE)))
309 medium.hGlobal = hDropContent;
310 hr = dataObject->SetData(cfHDropFormat(), &medium, TRUE);
317 GlobalFree(fileDescriptor);
319 GlobalFree(fileContent);
321 GlobalFree(hDropContent);
326 PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData* dragData, Frame* frame)
328 if (dragData->platformData())
329 return ClipboardWin::create(DragAndDrop, dragData->platformData(), policy, frame);
330 return ClipboardWin::create(DragAndDrop, dragData->dragDataMap(), policy, frame);
333 ClipboardWin::ClipboardWin(ClipboardType clipboardType, IDataObject* dataObject, ClipboardAccessPolicy policy, Frame* frame)
334 : Clipboard(policy, clipboardType)
335 , m_dataObject(dataObject)
336 , m_writableDataObject(0)
341 ClipboardWin::ClipboardWin(ClipboardType clipboardType, WCDataObject* dataObject, ClipboardAccessPolicy policy, Frame* frame)
342 : Clipboard(policy, clipboardType)
343 , m_dataObject(dataObject)
344 , m_writableDataObject(dataObject)
349 ClipboardWin::ClipboardWin(ClipboardType clipboardType, const DragDataMap& dataMap, ClipboardAccessPolicy policy, Frame* frame)
350 : Clipboard(policy, clipboardType)
352 , m_writableDataObject(0)
354 , m_dragDataMap(dataMap)
358 ClipboardWin::~ClipboardWin()
362 static bool writeURL(WCDataObject *data, const KURL& url, String title, bool withPlainText, bool withHTML)
369 if (title.isEmpty()) {
370 title = url.lastPathComponent();
375 STGMEDIUM medium = {0};
376 medium.tymed = TYMED_HGLOBAL;
378 medium.hGlobal = createGlobalData(url, title);
379 bool success = false;
380 if (medium.hGlobal && FAILED(data->SetData(urlWFormat(), &medium, TRUE)))
381 ::GlobalFree(medium.hGlobal);
386 Vector<char> cfhtmlData;
387 markupToCFHTML(urlToMarkup(url, title), "", cfhtmlData);
388 medium.hGlobal = createGlobalData(cfhtmlData);
389 if (medium.hGlobal && FAILED(data->SetData(htmlFormat(), &medium, TRUE)))
390 ::GlobalFree(medium.hGlobal);
396 medium.hGlobal = createGlobalData(url.string());
397 if (medium.hGlobal && FAILED(data->SetData(plainTextWFormat(), &medium, TRUE)))
398 ::GlobalFree(medium.hGlobal);
406 void ClipboardWin::clearData(const String& type)
408 // FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
409 ASSERT(isForDragAndDrop());
410 if (policy() != ClipboardWritable || !m_writableDataObject)
413 ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
415 if (dataType == ClipboardDataTypeURL) {
416 m_writableDataObject->clearData(urlWFormat()->cfFormat);
417 m_writableDataObject->clearData(urlFormat()->cfFormat);
419 if (dataType == ClipboardDataTypeText) {
420 m_writableDataObject->clearData(plainTextFormat()->cfFormat);
421 m_writableDataObject->clearData(plainTextWFormat()->cfFormat);
426 void ClipboardWin::clearAllData()
428 // FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
429 ASSERT(isForDragAndDrop());
430 if (policy() != ClipboardWritable)
433 m_writableDataObject = 0;
434 WCDataObject::createInstance(&m_writableDataObject);
435 m_dataObject = m_writableDataObject;
438 String ClipboardWin::getData(const String& type, bool& success) const
441 if (policy() != ClipboardReadable || (!m_dataObject && m_dragDataMap.isEmpty()))
444 ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
445 if (dataType == ClipboardDataTypeText)
446 return m_dataObject ? getPlainText(m_dataObject.get(), success) : getPlainText(&m_dragDataMap);
447 if (dataType == ClipboardDataTypeURL)
448 return m_dataObject ? getURL(m_dataObject.get(), DragData::DoNotConvertFilenames, success) : getURL(&m_dragDataMap, DragData::DoNotConvertFilenames);
449 else if (dataType == ClipboardDataTypeTextHTML) {
450 String data = m_dataObject ? getTextHTML(m_dataObject.get(), success) : getTextHTML(&m_dragDataMap);
453 return m_dataObject ? getCFHTML(m_dataObject.get(), success) : getCFHTML(&m_dragDataMap);
459 bool ClipboardWin::setData(const String& type, const String& data)
461 // FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
462 ASSERT(isForDragAndDrop());
463 if (policy() != ClipboardWritable || !m_writableDataObject)
466 ClipboardDataType winType = clipboardTypeFromMIMEType(type);
468 if (winType == ClipboardDataTypeURL)
469 return WebCore::writeURL(m_writableDataObject.get(), KURL(ParsedURLString, data), String(), false, true);
471 if (winType == ClipboardDataTypeText) {
472 STGMEDIUM medium = {0};
473 medium.tymed = TYMED_HGLOBAL;
474 medium.hGlobal = createGlobalData(data);
478 if (FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE))) {
479 ::GlobalFree(medium.hGlobal);
488 static void addMimeTypesForFormat(HashSet<String>& results, const FORMATETC& format)
490 // URL and Text are provided for compatibility with IE's model
491 if (format.cfFormat == urlFormat()->cfFormat || format.cfFormat == urlWFormat()->cfFormat) {
493 results.add("text/uri-list");
496 if (format.cfFormat == plainTextWFormat()->cfFormat || format.cfFormat == plainTextFormat()->cfFormat) {
498 results.add("text/plain");
502 // extensions beyond IE's API
503 HashSet<String> ClipboardWin::types() const
505 HashSet<String> results;
506 if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
509 if (!m_dataObject && m_dragDataMap.isEmpty())
513 COMPtr<IEnumFORMATETC> itr;
515 if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr)))
523 // IEnumFORMATETC::Next returns S_FALSE if there are no more items.
524 while (itr->Next(1, &data, 0) == S_OK)
525 addMimeTypesForFormat(results, data);
527 for (DragDataMap::const_iterator it = m_dragDataMap.begin(); it != m_dragDataMap.end(); ++it) {
529 data.cfFormat = (*it).first;
530 addMimeTypesForFormat(results, data);
537 PassRefPtr<FileList> ClipboardWin::files() const
543 RefPtr<FileList> files = FileList::create();
544 if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
545 return files.release();
547 if (!m_dataObject && m_dragDataMap.isEmpty())
548 return files.release();
552 if (FAILED(m_dataObject->GetData(cfHDropFormat(), &medium)))
553 return files.release();
555 HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(medium.hGlobal));
557 return files.release();
559 WCHAR filename[MAX_PATH];
560 UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0);
561 for (UINT i = 0; i < fileCount; i++) {
562 if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename)))
564 files->append(File::create(reinterpret_cast<UChar*>(filename)));
567 GlobalUnlock(medium.hGlobal);
568 ReleaseStgMedium(&medium);
569 return files.release();
571 if (!m_dragDataMap.contains(cfHDropFormat()->cfFormat))
572 return files.release();
573 Vector<String> filesVector = m_dragDataMap.get(cfHDropFormat()->cfFormat);
574 for (Vector<String>::iterator it = filesVector.begin(); it != filesVector.end(); ++it)
575 files->append(File::create((*it).characters()));
576 return files.release();
580 void ClipboardWin::setDragImage(CachedImage* image, Node *node, const IntPoint &loc)
582 if (policy() != ClipboardImageWritable && policy() != ClipboardWritable)
586 m_dragImage->removeClient(this);
589 m_dragImage->addClient(this);
592 m_dragImageElement = node;
595 void ClipboardWin::setDragImage(CachedImage* img, const IntPoint &loc)
597 setDragImage(img, 0, loc);
600 void ClipboardWin::setDragImageElement(Node *node, const IntPoint &loc)
602 setDragImage(0, node, loc);
605 DragImageRef ClipboardWin::createDragImage(IntPoint& loc) const
609 result = createDragImageFromImage(m_dragImage->image());
611 } else if (m_dragImageElement) {
612 Node* node = m_dragImageElement.get();
613 result = node->document()->frame()->nodeImage(node);
619 static CachedImage* getCachedImage(Element* element)
621 // Attempt to pull CachedImage from element
623 RenderObject* renderer = element->renderer();
624 if (!renderer || !renderer->isImage())
627 RenderImage* image = toRenderImage(renderer);
628 if (image->cachedImage() && !image->cachedImage()->errorOccurred())
629 return image->cachedImage();
634 static void writeImageToDataObject(IDataObject* dataObject, Element* element, const KURL& url)
636 // Shove image data into a DataObject for use as a file
637 CachedImage* cachedImage = getCachedImage(element);
638 if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded())
641 SharedBuffer* imageBuffer = cachedImage->image()->data();
642 if (!imageBuffer || !imageBuffer->size())
645 HGLOBAL imageFileDescriptor = createGlobalImageFileDescriptor(url.string(), element->getAttribute(altAttr), cachedImage);
646 if (!imageFileDescriptor)
649 HGLOBAL imageFileContent = createGlobalImageFileContent(imageBuffer);
650 if (!imageFileContent) {
651 GlobalFree(imageFileDescriptor);
655 String fileName = cachedImage->response().suggestedFilename();
656 HGLOBAL hDropContent = createGlobalHDropContent(url, fileName, imageBuffer);
658 GlobalFree(hDropContent);
662 writeFileToDataObject(dataObject, imageFileDescriptor, imageFileContent, hDropContent);
665 void ClipboardWin::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame)
667 // Order is important here for Explorer's sake
668 if (!m_writableDataObject)
670 WebCore::writeURL(m_writableDataObject.get(), url, title, true, false);
672 writeImageToDataObject(m_writableDataObject.get(), element, url);
674 AtomicString imageURL = element->getAttribute(srcAttr);
675 if (imageURL.isEmpty())
678 KURL fullURL = frame->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL));
679 if (fullURL.isEmpty())
681 STGMEDIUM medium = {0};
682 medium.tymed = TYMED_HGLOBAL;
684 // Put img tag on the clipboard referencing the image
686 markupToCFHTML(createMarkup(element, IncludeNode, 0, ResolveAllURLs), "", data);
687 medium.hGlobal = createGlobalData(data);
688 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
689 ::GlobalFree(medium.hGlobal);
692 void ClipboardWin::writeURL(const KURL& kurl, const String& titleStr, Frame*)
694 if (!m_writableDataObject)
696 WebCore::writeURL(m_writableDataObject.get(), kurl, titleStr, true, true);
698 String url = kurl.string();
699 ASSERT(url.containsOnlyASCII()); // KURL::string() is URL encoded.
701 String fsPath = filesystemPathFromUrlOrTitle(url, titleStr, L".URL", true);
702 String contentString("[InternetShortcut]\r\nURL=" + url + "\r\n");
703 CString content = contentString.latin1();
705 if (fsPath.length() <= 0)
708 HGLOBAL urlFileDescriptor = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
709 if (!urlFileDescriptor)
712 HGLOBAL urlFileContent = GlobalAlloc(GPTR, content.length());
713 if (!urlFileContent) {
714 GlobalFree(urlFileDescriptor);
718 FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(urlFileDescriptor));
719 ZeroMemory(fgd, sizeof(FILEGROUPDESCRIPTOR));
721 fgd->fgd[0].dwFlags = FD_FILESIZE;
722 fgd->fgd[0].nFileSizeLow = content.length();
724 unsigned maxSize = min(fsPath.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName));
725 CopyMemory(fgd->fgd[0].cFileName, fsPath.characters(), maxSize * sizeof(UChar));
726 GlobalUnlock(urlFileDescriptor);
728 char* fileContents = static_cast<char*>(GlobalLock(urlFileContent));
729 CopyMemory(fileContents, content.data(), content.length());
730 GlobalUnlock(urlFileContent);
732 writeFileToDataObject(m_writableDataObject.get(), urlFileDescriptor, urlFileContent, 0);
735 void ClipboardWin::writeRange(Range* selectedRange, Frame* frame)
737 ASSERT(selectedRange);
738 if (!m_writableDataObject)
741 STGMEDIUM medium = {0};
742 medium.tymed = TYMED_HGLOBAL;
743 ExceptionCode ec = 0;
746 markupToCFHTML(createMarkup(selectedRange, 0, AnnotateForInterchange),
747 selectedRange->startContainer(ec)->document()->url().string(), data);
748 medium.hGlobal = createGlobalData(data);
749 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
750 ::GlobalFree(medium.hGlobal);
752 String str = frame->editor()->selectedText();
753 replaceNewlinesWithWindowsStyleNewlines(str);
754 replaceNBSPWithSpace(str);
755 medium.hGlobal = createGlobalData(str);
756 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
757 ::GlobalFree(medium.hGlobal);
760 if (frame->editor()->canSmartCopyOrDelete())
761 m_writableDataObject->SetData(smartPasteFormat(), &medium, TRUE);
764 void ClipboardWin::writePlainText(const String& text)
766 if (!m_writableDataObject)
769 STGMEDIUM medium = {0};
770 medium.tymed = TYMED_HGLOBAL;
773 replaceNewlinesWithWindowsStyleNewlines(str);
774 replaceNBSPWithSpace(str);
775 medium.hGlobal = createGlobalData(str);
776 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
777 ::GlobalFree(medium.hGlobal);
782 bool ClipboardWin::hasData()
784 if (!m_dataObject && m_dragDataMap.isEmpty())
788 COMPtr<IEnumFORMATETC> itr;
789 if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr)))
797 // IEnumFORMATETC::Next returns S_FALSE if there are no more items.
798 if (itr->Next(1, &data, 0) == S_OK) {
799 // There is at least one item in the IDataObject
805 return !m_dragDataMap.isEmpty();
808 void ClipboardWin::setExternalDataObject(IDataObject *dataObject)
810 ASSERT(isForDragAndDrop());
812 m_writableDataObject = 0;
813 m_dataObject = dataObject;
816 } // namespace WebCore