1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsClipboard.h"
11 // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
15 #include "nsDataObj.h"
16 #include "nsIClipboardOwner.h"
18 #include "nsNativeCharsetUtils.h"
19 #include "nsIFormatConverter.h"
20 #include "nsITransferable.h"
23 #include "nsISupportsPrimitives.h"
24 #include "nsXPIDLString.h"
25 #include "nsReadableUtils.h"
26 #include "nsUnicharUtils.h"
27 #include "nsPrimitiveHelpers.h"
28 #include "nsImageClipboard.h"
29 #include "nsIWidget.h"
30 #include "nsIComponentManager.h"
31 #include "nsWidgetsCID.h"
33 #include "nsNetUtil.h"
37 PRLogModuleInfo
* gWin32ClipboardLog
= nullptr;
40 // oddly, this isn't in the MSVC headers anywhere.
41 UINT
nsClipboard::CF_HTML
= ::RegisterClipboardFormatW(L
"HTML Format");
44 //-------------------------------------------------------------------------
46 // nsClipboard constructor
48 //-------------------------------------------------------------------------
49 nsClipboard::nsClipboard() : nsBaseClipboard()
52 if (!gWin32ClipboardLog
) {
53 gWin32ClipboardLog
= PR_NewLogModule("nsClipboard");
57 mIgnoreEmptyNotification
= false;
61 //-------------------------------------------------------------------------
62 // nsClipboard destructor
63 //-------------------------------------------------------------------------
64 nsClipboard::~nsClipboard()
69 //-------------------------------------------------------------------------
70 UINT
nsClipboard::GetFormat(const char* aMimeStr
)
74 if (strcmp(aMimeStr
, kTextMime
) == 0)
76 else if (strcmp(aMimeStr
, kUnicodeMime
) == 0)
77 format
= CF_UNICODETEXT
;
78 else if (strcmp(aMimeStr
, kJPEGImageMime
) == 0 ||
79 strcmp(aMimeStr
, kJPGImageMime
) == 0 ||
80 strcmp(aMimeStr
, kPNGImageMime
) == 0)
82 else if (strcmp(aMimeStr
, kFileMime
) == 0 ||
83 strcmp(aMimeStr
, kFilePromiseMime
) == 0)
85 else if (strcmp(aMimeStr
, kNativeHTMLMime
) == 0)
88 format
= ::RegisterClipboardFormatW(NS_ConvertASCIItoUTF16(aMimeStr
).get());
93 //-------------------------------------------------------------------------
94 nsresult
nsClipboard::CreateNativeDataObject(nsITransferable
* aTransferable
, IDataObject
** aDataObj
, nsIURI
* uri
)
96 if (nullptr == aTransferable
) {
97 return NS_ERROR_FAILURE
;
100 // Create our native DataObject that implements
101 // the OLE IDataObject interface
102 nsDataObj
* dataObj
= new nsDataObj(uri
);
105 return NS_ERROR_OUT_OF_MEMORY
;
109 // Now set it up with all the right data flavors & enums
110 nsresult res
= SetupNativeDataObject(aTransferable
, dataObj
);
119 //-------------------------------------------------------------------------
120 nsresult
nsClipboard::SetupNativeDataObject(nsITransferable
* aTransferable
, IDataObject
* aDataObj
)
122 if (nullptr == aTransferable
|| nullptr == aDataObj
) {
123 return NS_ERROR_FAILURE
;
126 nsDataObj
* dObj
= static_cast<nsDataObj
*>(aDataObj
);
128 // Now give the Transferable to the DataObject
129 // for getting the data out of it
130 dObj
->SetTransferable(aTransferable
);
132 // Get the transferable list of data flavors
133 nsCOMPtr
<nsISupportsArray
> dfList
;
134 aTransferable
->FlavorsTransferableCanExport(getter_AddRefs(dfList
));
136 // Walk through flavors that contain data and register them
137 // into the DataObj as supported flavors
141 for (i
=0;i
<cnt
;i
++) {
142 nsCOMPtr
<nsISupports
> genericFlavor
;
143 dfList
->GetElementAt ( i
, getter_AddRefs(genericFlavor
) );
144 nsCOMPtr
<nsISupportsCString
> currentFlavor ( do_QueryInterface(genericFlavor
) );
145 if ( currentFlavor
) {
146 nsXPIDLCString flavorStr
;
147 currentFlavor
->ToString(getter_Copies(flavorStr
));
148 UINT format
= GetFormat(flavorStr
);
150 // Now tell the native IDataObject about both our mime type and
151 // the native data format
153 SET_FORMATETC(fe
, format
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
);
154 dObj
->AddDataFlavor(flavorStr
, &fe
);
156 // Do various things internal to the implementation, like map one
157 // flavor to another or add additional flavors based on what's required
158 // for the win32 impl.
159 if ( strcmp(flavorStr
, kUnicodeMime
) == 0 ) {
160 // if we find text/unicode, also advertise text/plain (which we will convert
161 // on our own in nsDataObj::GetText().
163 SET_FORMATETC(textFE
, CF_TEXT
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
);
164 dObj
->AddDataFlavor(kTextMime
, &textFE
);
166 else if ( strcmp(flavorStr
, kHTMLMime
) == 0 ) {
167 // if we find text/html, also advertise win32's html flavor (which we will convert
168 // on our own in nsDataObj::GetText().
170 SET_FORMATETC(htmlFE
, CF_HTML
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
);
171 dObj
->AddDataFlavor(kHTMLMime
, &htmlFE
);
173 else if ( strcmp(flavorStr
, kURLMime
) == 0 ) {
174 // if we're a url, in addition to also being text, we need to register
175 // the "file" flavors so that the win32 shell knows to create an internet
176 // shortcut when it sees one of these beasts.
177 FORMATETC shortcutFE
;
178 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA
), 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
179 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
180 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW
), 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
181 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
182 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_FILECONTENTS
), 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
183 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
184 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_INETURLA
), 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
185 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
186 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_INETURLW
), 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
187 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
189 else if ( strcmp(flavorStr
, kPNGImageMime
) == 0 || strcmp(flavorStr
, kJPEGImageMime
) == 0 ||
190 strcmp(flavorStr
, kJPGImageMime
) == 0 || strcmp(flavorStr
, kGIFImageMime
) == 0 ||
191 strcmp(flavorStr
, kNativeImageMime
) == 0 ) {
192 // if we're an image, register the native bitmap flavor
195 SET_FORMATETC(imageFE
, CF_DIBV5
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
196 dObj
->AddDataFlavor(flavorStr
, &imageFE
);
198 SET_FORMATETC(imageFE
, CF_DIB
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
199 dObj
->AddDataFlavor(flavorStr
, &imageFE
);
201 else if ( strcmp(flavorStr
, kFilePromiseMime
) == 0 ) {
202 // if we're a file promise flavor, also register the
203 // CFSTR_PREFERREDDROPEFFECT format. The data object
204 // returns a value of DROPEFFECTS_MOVE to the drop target
205 // when it asks for the value of this format. This causes
206 // the file to be moved from the temporary location instead
207 // of being copied. The right thing to do here is to call
208 // SetData() on the data object and set the value of this format
209 // to DROPEFFECTS_MOVE on this particular data object. But,
210 // since all the other clipboard formats follow the model of setting
211 // data on the data object only when the drop object calls GetData(),
212 // I am leaving this format's value hard coded in the data object.
213 // We can change this if other consumers of this format get added to this
214 // codebase and they need different values.
215 FORMATETC shortcutFE
;
216 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
), 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
217 dObj
->AddDataFlavor(kFilePromiseMime
, &shortcutFE
);
225 //-------------------------------------------------------------------------
226 NS_IMETHODIMP
nsClipboard::SetNativeClipboardData ( int32_t aWhichClipboard
)
228 if ( aWhichClipboard
!= kGlobalClipboard
)
229 return NS_ERROR_FAILURE
;
231 mIgnoreEmptyNotification
= true;
233 // make sure we have a good transferable
234 if (nullptr == mTransferable
) {
235 return NS_ERROR_FAILURE
;
238 IDataObject
* dataObj
;
239 if ( NS_SUCCEEDED(CreateNativeDataObject(mTransferable
, &dataObj
, NULL
)) ) { // this add refs dataObj
240 ::OleSetClipboard(dataObj
);
243 // Clear the native clipboard
244 ::OleSetClipboard(NULL
);
247 mIgnoreEmptyNotification
= false;
253 //-------------------------------------------------------------------------
254 nsresult
nsClipboard::GetGlobalData(HGLOBAL aHGBL
, void ** aData
, uint32_t * aLen
)
256 // Allocate a new memory buffer and copy the data from global memory.
257 // Recall that win98 allocates to nearest DWORD boundary. As a safety
258 // precaution, allocate an extra 2 bytes (but don't report them!) and
259 // null them out to ensure that all of our strlen calls will succeed.
260 nsresult result
= NS_ERROR_FAILURE
;
262 LPSTR lpStr
= (LPSTR
) GlobalLock(aHGBL
);
263 DWORD allocSize
= GlobalSize(aHGBL
);
264 char* data
= static_cast<char*>(nsMemory::Alloc(allocSize
+ sizeof(PRUnichar
)));
266 memcpy ( data
, lpStr
, allocSize
);
267 data
[allocSize
] = data
[allocSize
+ 1] = '\0'; // null terminate for safety
279 // We really shouldn't ever get here
286 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
289 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
295 // Display the string.
296 MessageBoxW( NULL
, (LPCWSTR
) lpMsgBuf
, L
"GetLastError", MB_OK
|MB_ICONINFORMATION
);
299 LocalFree( lpMsgBuf
);
305 //-------------------------------------------------------------------------
306 nsresult
nsClipboard::GetNativeDataOffClipboard(nsIWidget
* aWidget
, UINT
/*aIndex*/, UINT aFormat
, void ** aData
, uint32_t * aLen
)
309 nsresult result
= NS_ERROR_FAILURE
;
311 HWND nativeWin
= nullptr;
312 if (::OpenClipboard(nativeWin
)) {
313 hglb
= ::GetClipboardData(aFormat
);
314 result
= GetGlobalData(hglb
, aData
, aLen
);
320 static void DisplayErrCode(HRESULT hres
)
322 #if defined(DEBUG_rods) || defined(DEBUG_pinkerton)
323 if (hres
== E_INVALIDARG
) {
324 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("E_INVALIDARG\n"));
326 if (hres
== E_UNEXPECTED
) {
327 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("E_UNEXPECTED\n"));
329 if (hres
== E_OUTOFMEMORY
) {
330 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("E_OUTOFMEMORY\n"));
332 if (hres
== DV_E_LINDEX
) {
333 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("DV_E_LINDEX\n"));
335 if (hres
== DV_E_FORMATETC
) {
336 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("DV_E_FORMATETC\n"));
338 if (hres
== DV_E_TYMED
) {
339 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("DV_E_TYMED\n"));
341 if (hres
== DV_E_DVASPECT
) {
342 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("DV_E_DVASPECT\n"));
344 if (hres
== OLE_E_NOTRUNNING
) {
345 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("OLE_E_NOTRUNNING\n"));
347 if (hres
== STG_E_MEDIUMFULL
) {
348 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("STG_E_MEDIUMFULL\n"));
350 if (hres
== DV_E_CLIPFORMAT
) {
351 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("DV_E_CLIPFORMAT\n"));
354 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
, ("S_OK\n"));
356 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
,
357 ("****** DisplayErrCode 0x%X\n", hres
));
362 //-------------------------------------------------------------------------
363 static HRESULT
FillSTGMedium(IDataObject
* aDataObject
, UINT aFormat
, LPFORMATETC pFE
, LPSTGMEDIUM pSTM
, DWORD aTymed
)
365 SET_FORMATETC(*pFE
, aFormat
, 0, DVASPECT_CONTENT
, -1, aTymed
);
367 // Starting by querying for the data to see if we can get it as from global memory
368 HRESULT hres
= S_FALSE
;
369 hres
= aDataObject
->QueryGetData(pFE
);
370 DisplayErrCode(hres
);
372 hres
= aDataObject
->GetData(pFE
, pSTM
);
373 DisplayErrCode(hres
);
379 //-------------------------------------------------------------------------
380 // If aFormat is CF_DIBV5, aMIMEImageFormat must be a type for which we have
381 // an image encoder (e.g. image/png).
382 // For other values of aFormat, it is OK to pass null for aMIMEImageFormat.
383 nsresult
nsClipboard::GetNativeDataOffClipboard(IDataObject
* aDataObject
, UINT aIndex
, UINT aFormat
, const char * aMIMEImageFormat
, void ** aData
, uint32_t * aLen
)
385 nsresult result
= NS_ERROR_FAILURE
;
392 UINT format
= aFormat
;
393 HRESULT hres
= S_FALSE
;
395 // XXX at the moment we only support global memory transfers
396 // It is here where we will add support for native images
400 hres
= FillSTGMedium(aDataObject
, format
, &fe
, &stm
, TYMED_HGLOBAL
);
402 // Currently this is only handling TYMED_HGLOBAL data
403 // For Text, Dibs, Files, and generic data (like HTML)
405 static CLIPFORMAT fileDescriptorFlavorA
= ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA
);
406 static CLIPFORMAT fileDescriptorFlavorW
= ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW
);
407 static CLIPFORMAT fileFlavor
= ::RegisterClipboardFormat( CFSTR_FILECONTENTS
);
408 static CLIPFORMAT preferredDropEffect
= ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
);
413 switch (fe
.cfFormat
) {
416 // Get the data out of the global data handle. The size we return
417 // should not include the null because the other platforms don't
418 // use nulls, so just return the length we get back from strlen(),
419 // since we know CF_TEXT is null terminated. Recall that GetGlobalData()
420 // returns the size of the allocated buffer, not the size of the data
421 // (on 98, these are not the same) so we can't use that.
422 uint32_t allocLen
= 0;
423 if ( NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
)) ) {
424 *aLen
= strlen ( reinterpret_cast<char*>(*aData
) );
431 // Get the data out of the global data handle. The size we return
432 // should not include the null because the other platforms don't
433 // use nulls, so just return the length we get back from strlen(),
434 // since we know CF_UNICODETEXT is null terminated. Recall that GetGlobalData()
435 // returns the size of the allocated buffer, not the size of the data
436 // (on 98, these are not the same) so we can't use that.
437 uint32_t allocLen
= 0;
438 if ( NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
)) ) {
439 *aLen
= NS_strlen(reinterpret_cast<PRUnichar
*>(*aData
)) * 2;
445 if (aMIMEImageFormat
)
447 uint32_t allocLen
= 0;
448 unsigned char * clipboardData
;
449 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, (void **)&clipboardData
, &allocLen
)))
451 nsImageFromClipboard converter
;
452 nsIInputStream
* inputStream
;
453 converter
.GetEncodedImageStream(clipboardData
, aMIMEImageFormat
, &inputStream
); // addrefs for us, don't release
455 *aData
= inputStream
;
456 *aLen
= sizeof(nsIInputStream
*);
464 // in the case of a file drop, multiple files are stashed within a
465 // single data object. In order to match mozilla's D&D apis, we
466 // just pull out the file at the requested index, pretending as
467 // if there really are multiple drag items.
468 HDROP dropFiles
= (HDROP
) GlobalLock(stm
.hGlobal
);
470 UINT numFiles
= ::DragQueryFileW(dropFiles
, 0xFFFFFFFF, NULL
, 0);
471 NS_ASSERTION ( numFiles
> 0, "File drop flavor, but no files...hmmmm" );
472 NS_ASSERTION ( aIndex
< numFiles
, "Asked for a file index out of range of list" );
474 UINT fileNameLen
= ::DragQueryFileW(dropFiles
, aIndex
, nullptr, 0);
475 PRUnichar
* buffer
= reinterpret_cast<PRUnichar
*>(nsMemory::Alloc((fileNameLen
+ 1) * sizeof(PRUnichar
)));
477 ::DragQueryFileW(dropFiles
, aIndex
, buffer
, fileNameLen
+ 1);
479 *aLen
= fileNameLen
* sizeof(PRUnichar
);
483 result
= NS_ERROR_OUT_OF_MEMORY
;
485 GlobalUnlock (stm
.hGlobal
) ;
490 if ( fe
.cfFormat
== fileDescriptorFlavorA
|| fe
.cfFormat
== fileDescriptorFlavorW
|| fe
.cfFormat
== fileFlavor
) {
491 NS_WARNING ( "Mozilla doesn't yet understand how to read this type of file flavor" );
495 // Get the data out of the global data handle. The size we return
496 // should not include the null because the other platforms don't
497 // use nulls, so just return the length we get back from strlen(),
498 // since we know CF_UNICODETEXT is null terminated. Recall that GetGlobalData()
499 // returns the size of the allocated buffer, not the size of the data
500 // (on 98, these are not the same) so we can't use that.
502 // NOTE: we are assuming that anything that falls into this default case
503 // is unicode. As we start to get more kinds of binary data, this
504 // may become an incorrect assumption. Stay tuned.
505 uint32_t allocLen
= 0;
506 if ( NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
)) ) {
507 if ( fe
.cfFormat
== CF_HTML
) {
508 // CF_HTML is actually UTF8, not unicode, so disregard the assumption
509 // above. We have to check the header for the actual length, and we'll
510 // do that in FindPlatformHTML(). For now, return the allocLen. This
511 // case is mostly to ensure we don't try to call strlen on the buffer.
513 } else if (fe
.cfFormat
== preferredDropEffect
) {
514 // As per the MSDN doc entitled: "Shell Clipboard Formats"
515 // CFSTR_PREFERREDDROPEFFECT should return a DWORD
516 // Reference: http://msdn.microsoft.com/en-us/library/bb776902(v=vs.85).aspx
517 NS_ASSERTION(allocLen
== sizeof(DWORD
),
518 "CFSTR_PREFERREDDROPEFFECT should return a DWORD");
521 *aLen
= NS_strlen(reinterpret_cast<PRUnichar
*>(*aData
)) *
534 PR_LOG(gWin32ClipboardLog
, PR_LOG_ALWAYS
,
535 ("*********************** TYMED_GDI\n"));
543 ReleaseStgMedium(&stm
);
550 //-------------------------------------------------------------------------
551 nsresult
nsClipboard::GetDataFromDataObject(IDataObject
* aDataObject
,
554 nsITransferable
* aTransferable
)
556 // make sure we have a good transferable
557 if ( !aTransferable
)
558 return NS_ERROR_INVALID_ARG
;
560 nsresult res
= NS_ERROR_FAILURE
;
562 // get flavor list that includes all flavors that can be written (including ones
563 // obtained through conversion)
564 nsCOMPtr
<nsISupportsArray
> flavorList
;
565 res
= aTransferable
->FlavorsTransferableCanImport ( getter_AddRefs(flavorList
) );
566 if ( NS_FAILED(res
) )
567 return NS_ERROR_FAILURE
;
569 // Walk through flavors and see which flavor is on the clipboard them on the native clipboard,
572 flavorList
->Count(&cnt
);
573 for (i
=0;i
<cnt
;i
++) {
574 nsCOMPtr
<nsISupports
> genericFlavor
;
575 flavorList
->GetElementAt ( i
, getter_AddRefs(genericFlavor
) );
576 nsCOMPtr
<nsISupportsCString
> currentFlavor ( do_QueryInterface(genericFlavor
) );
577 if ( currentFlavor
) {
578 nsXPIDLCString flavorStr
;
579 currentFlavor
->ToString(getter_Copies(flavorStr
));
580 UINT format
= GetFormat(flavorStr
);
582 // Try to get the data using the desired flavor. This might fail, but all is
584 void* data
= nullptr;
585 uint32_t dataLen
= 0;
586 bool dataFound
= false;
587 if (nullptr != aDataObject
) {
588 if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject
, anIndex
, format
, flavorStr
, &data
, &dataLen
)) )
591 else if (nullptr != aWindow
) {
592 if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow
, anIndex
, format
, &data
, &dataLen
)) )
596 // This is our second chance to try to find some data, having not found it
597 // when directly asking for the flavor. Let's try digging around in other
598 // flavors to help satisfy our craving for data.
600 if ( strcmp(flavorStr
, kUnicodeMime
) == 0 )
601 dataFound
= FindUnicodeFromPlainText ( aDataObject
, anIndex
, &data
, &dataLen
);
602 else if ( strcmp(flavorStr
, kURLMime
) == 0 ) {
603 // drags from other windows apps expose the native
604 // CFSTR_INETURL{A,W} flavor
605 dataFound
= FindURLFromNativeURL ( aDataObject
, anIndex
, &data
, &dataLen
);
607 dataFound
= FindURLFromLocalFile ( aDataObject
, anIndex
, &data
, &dataLen
);
609 } // if we try one last ditch effort to find our data
611 // Hopefully by this point we've found it and can go about our business
613 nsCOMPtr
<nsISupports
> genericDataWrapper
;
614 if ( strcmp(flavorStr
, kFileMime
) == 0 ) {
615 // we have a file path in |data|. Create an nsLocalFile object.
616 nsDependentString
filepath(reinterpret_cast<PRUnichar
*>(data
));
617 nsCOMPtr
<nsIFile
> file
;
618 if ( NS_SUCCEEDED(NS_NewLocalFile(filepath
, false, getter_AddRefs(file
))) )
619 genericDataWrapper
= do_QueryInterface(file
);
620 nsMemory::Free(data
);
622 else if ( strcmp(flavorStr
, kNativeHTMLMime
) == 0) {
623 // the editor folks want CF_HTML exactly as it's on the clipboard, no conversions,
624 // no fancy stuff. Pull it off the clipboard, stuff it into a wrapper and hand
626 if ( FindPlatformHTML(aDataObject
, anIndex
, &data
, &dataLen
) )
627 nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
) );
630 nsMemory::Free(data
);
631 continue; // something wrong with this flavor, keep looking for other data
633 nsMemory::Free(data
);
635 else if ( strcmp(flavorStr
, kJPEGImageMime
) == 0 ||
636 strcmp(flavorStr
, kJPGImageMime
) == 0 ||
637 strcmp(flavorStr
, kPNGImageMime
) == 0) {
638 nsIInputStream
* imageStream
= reinterpret_cast<nsIInputStream
*>(data
);
639 genericDataWrapper
= do_QueryInterface(imageStream
);
640 NS_IF_RELEASE(imageStream
);
643 // we probably have some form of text. The DOM only wants LF, so convert from Win32 line
644 // endings to DOM line endings.
645 int32_t signedLen
= static_cast<int32_t>(dataLen
);
646 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks ( flavorStr
, &data
, &signedLen
);
649 nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
) );
650 nsMemory::Free(data
);
653 NS_ASSERTION ( genericDataWrapper
, "About to put null data into the transferable" );
654 aTransferable
->SetTransferData(flavorStr
, genericDataWrapper
, dataLen
);
657 // we found one, get out of the loop
673 // Someone asked for the OS CF_HTML flavor. We give it back to them exactly as-is.
676 nsClipboard :: FindPlatformHTML ( IDataObject
* inDataObject
, UINT inIndex
, void** outData
, uint32_t* outDataLen
)
678 // Reference: MSDN doc entitled "HTML Clipboard Format"
679 // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
680 // CF_HTML is UTF8, not unicode. We also can't rely on it being null-terminated
681 // so we have to check the CF_HTML header for the correct length.
682 // The length we return is the bytecount from the beginning of the selected data to the end
683 // of the selected data, without the null termination. Because it's UTF8, we're guaranteed
684 // the header is ASCII.
686 if (!outData
|| !*outData
) {
690 char version
[8] = { 0 };
691 int32_t startOfData
= 0;
692 int32_t endOfData
= 0;
693 int numFound
= sscanf((char*)*outData
, "Version:%7s\nStartHTML:%d\nEndHTML:%d",
694 version
, &startOfData
, &endOfData
);
696 if (numFound
!= 3 || startOfData
< -1 || endOfData
< -1) {
700 // Fixup the start and end markers if they have no context (set to -1)
701 if (startOfData
== -1) {
704 if (endOfData
== -1) {
705 endOfData
= *outDataLen
;
708 // Make sure we were passed sane values within our buffer size.
709 if (!endOfData
|| startOfData
>= endOfData
||
710 endOfData
> *outDataLen
) {
714 // We want to return the buffer not offset by startOfData because it will be
715 // parsed out later (probably by nsHTMLEditor::ParseCFHTML) when it is still
716 // in CF_HTML format.
717 *outDataLen
= endOfData
;
723 // FindUnicodeFromPlainText
725 // we are looking for text/unicode and we failed to find it on the clipboard first,
726 // try again with text/plain. If that is present, convert it to unicode.
729 nsClipboard :: FindUnicodeFromPlainText ( IDataObject
* inDataObject
, UINT inIndex
, void** outData
, uint32_t* outDataLen
)
731 bool dataFound
= false;
733 // we are looking for text/unicode and we failed to find it on the clipboard first,
734 // try again with text/plain. If that is present, convert it to unicode.
735 nsresult loadResult
= GetNativeDataOffClipboard(inDataObject
, inIndex
, GetFormat(kTextMime
), nullptr, outData
, outDataLen
);
736 if ( NS_SUCCEEDED(loadResult
) && *outData
) {
737 const char* castedText
= reinterpret_cast<char*>(*outData
);
738 PRUnichar
* convertedText
= nullptr;
739 int32_t convertedTextLen
= 0;
740 nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode ( castedText
, *outDataLen
,
741 &convertedText
, &convertedTextLen
);
742 if ( convertedText
) {
743 // out with the old, in with the new
744 nsMemory::Free(*outData
);
745 *outData
= convertedText
;
746 *outDataLen
= convertedTextLen
* sizeof(PRUnichar
);
749 } // if plain text data on clipboard
753 } // FindUnicodeFromPlainText
757 // FindURLFromLocalFile
759 // we are looking for a URL and couldn't find it, try again with looking for
760 // a local file. If we have one, it may either be a normal file or an internet shortcut.
761 // In both cases, however, we can get a URL (it will be a file:// url in the
765 nsClipboard :: FindURLFromLocalFile ( IDataObject
* inDataObject
, UINT inIndex
, void** outData
, uint32_t* outDataLen
)
767 bool dataFound
= false;
769 nsresult loadResult
= GetNativeDataOffClipboard(inDataObject
, inIndex
, GetFormat(kFileMime
), nullptr, outData
, outDataLen
);
770 if ( NS_SUCCEEDED(loadResult
) && *outData
) {
771 // we have a file path in |data|. Is it an internet shortcut or a normal file?
772 const nsDependentString
filepath(static_cast<PRUnichar
*>(*outData
));
773 nsCOMPtr
<nsIFile
> file
;
774 nsresult rv
= NS_NewLocalFile(filepath
, true, getter_AddRefs(file
));
776 nsMemory::Free(*outData
);
780 if ( IsInternetShortcut(filepath
) ) {
781 nsMemory::Free(*outData
);
783 ResolveShortcut( file
, url
);
784 if ( !url
.IsEmpty() ) {
785 // convert it to unicode and pass it out
786 nsDependentString
urlString(UTF8ToNewUnicode(url
));
787 // the internal mozilla URL format, text/x-moz-url, contains
788 // URL\ntitle. We can guess the title from the file's name.
790 file
->GetLeafName(title
);
791 // We rely on IsInternetShortcut check that file has a .url extension.
792 title
.SetLength(title
.Length() - 4);
795 *outData
= ToNewUnicode(urlString
+ NS_LITERAL_STRING("\n") + title
);
796 *outDataLen
= NS_strlen(static_cast<PRUnichar
*>(*outData
)) * sizeof(PRUnichar
);
802 // we have a normal file, use some Necko objects to get our file path
803 nsAutoCString urlSpec
;
804 NS_GetURLSpecFromFile(file
, urlSpec
);
806 // convert it to unicode and pass it out
807 nsMemory::Free(*outData
);
808 *outData
= UTF8ToNewUnicode(urlSpec
);
809 *outDataLen
= NS_strlen(static_cast<PRUnichar
*>(*outData
)) * sizeof(PRUnichar
);
811 } // else regular file
815 } // FindURLFromLocalFile
818 // FindURLFromNativeURL
820 // we are looking for a URL and couldn't find it using our internal
821 // URL flavor, so look for it using the native URL flavor,
822 // CF_INETURLSTRW (We don't handle CF_INETURLSTRA currently)
825 nsClipboard :: FindURLFromNativeURL ( IDataObject
* inDataObject
, UINT inIndex
, void** outData
, uint32_t* outDataLen
)
827 bool dataFound
= false;
829 void* tempOutData
= nullptr;
830 uint32_t tempDataLen
= 0;
832 nsresult loadResult
= GetNativeDataOffClipboard(inDataObject
, inIndex
, ::RegisterClipboardFormat(CFSTR_INETURLW
), nullptr, &tempOutData
, &tempDataLen
);
833 if ( NS_SUCCEEDED(loadResult
) && tempOutData
) {
834 nsDependentString
urlString(static_cast<PRUnichar
*>(tempOutData
));
835 // the internal mozilla URL format, text/x-moz-url, contains
836 // URL\ntitle. Since we don't actually have a title here,
837 // just repeat the URL to fake it.
838 *outData
= ToNewUnicode(urlString
+ NS_LITERAL_STRING("\n") + urlString
);
839 *outDataLen
= NS_strlen(static_cast<PRUnichar
*>(*outData
)) * sizeof(PRUnichar
);
840 nsMemory::Free(tempOutData
);
844 loadResult
= GetNativeDataOffClipboard(inDataObject
, inIndex
, ::RegisterClipboardFormat(CFSTR_INETURLA
), nullptr, &tempOutData
, &tempDataLen
);
845 if ( NS_SUCCEEDED(loadResult
) && tempOutData
) {
846 // CFSTR_INETURLA is (currently) equal to CFSTR_SHELLURL which is equal to CF_TEXT
847 // which is by definition ANSI encoded.
848 nsCString urlUnescapedA
;
849 bool unescaped
= NS_UnescapeURL(static_cast<char*>(tempOutData
), tempDataLen
, esc_OnlyNonASCII
| esc_SkipControl
, urlUnescapedA
);
853 NS_CopyNativeToUnicode(urlUnescapedA
, urlString
);
855 NS_CopyNativeToUnicode(nsDependentCString(static_cast<char*>(tempOutData
), tempDataLen
), urlString
);
857 // the internal mozilla URL format, text/x-moz-url, contains
858 // URL\ntitle. Since we don't actually have a title here,
859 // just repeat the URL to fake it.
860 *outData
= ToNewUnicode(urlString
+ NS_LITERAL_STRING("\n") + urlString
);
861 *outDataLen
= NS_strlen(static_cast<PRUnichar
*>(*outData
)) * sizeof(PRUnichar
);
862 nsMemory::Free(tempOutData
);
868 } // FindURLFromNativeURL
874 nsClipboard :: ResolveShortcut ( nsIFile
* aFile
, nsACString
& outURL
)
876 nsCOMPtr
<nsIFileProtocolHandler
> fph
;
877 nsresult rv
= NS_GetFileProtocolHandler(getter_AddRefs(fph
));
881 nsCOMPtr
<nsIURI
> uri
;
882 rv
= fph
->ReadURLFile(aFile
, getter_AddRefs(uri
));
886 uri
->GetSpec(outURL
);
891 // IsInternetShortcut
893 // A file is an Internet Shortcut if it ends with .URL
896 nsClipboard :: IsInternetShortcut ( const nsAString
& inFileName
)
898 return StringEndsWith(inFileName
, NS_LITERAL_STRING(".url"), nsCaseInsensitiveStringComparator());
899 } // IsInternetShortcut
902 //-------------------------------------------------------------------------
904 nsClipboard::GetNativeClipboardData ( nsITransferable
* aTransferable
, int32_t aWhichClipboard
)
906 // make sure we have a good transferable
907 if ( !aTransferable
|| aWhichClipboard
!= kGlobalClipboard
)
908 return NS_ERROR_FAILURE
;
912 // This makes sure we can use the OLE functionality for the clipboard
913 IDataObject
* dataObj
;
914 if (S_OK
== ::OleGetClipboard(&dataObj
)) {
915 // Use OLE IDataObject for clipboard operations
916 res
= GetDataFromDataObject(dataObj
, 0, nullptr, aTransferable
);
920 // do it the old manual way
921 res
= GetDataFromDataObject(nullptr, 0, mWindow
, aTransferable
);
928 nsClipboard::EmptyClipboard(int32_t aWhichClipboard
)
930 if (::OpenClipboard(nullptr)) {
934 return nsBaseClipboard::EmptyClipboard(aWhichClipboard
);
937 //-------------------------------------------------------------------------
938 NS_IMETHODIMP
nsClipboard::HasDataMatchingFlavors(const char** aFlavorList
,
940 int32_t aWhichClipboard
,
944 if (aWhichClipboard
!= kGlobalClipboard
|| !aFlavorList
)
947 for (uint32_t i
= 0;i
< aLength
; ++i
) {
949 if (strcmp(aFlavorList
[i
], kTextMime
) == 0)
950 NS_WARNING ( "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD" );
953 UINT format
= GetFormat(aFlavorList
[i
]);
954 if (IsClipboardFormatAvailable(format
)) {
959 // We haven't found the exact flavor the client asked for, but maybe we can
960 // still find it from something else that's on the clipboard...
961 if (strcmp(aFlavorList
[i
], kUnicodeMime
) == 0) {
962 // client asked for unicode and it wasn't present, check if we have CF_TEXT.
963 // We'll handle the actual data substitution in the data object.
964 if (IsClipboardFormatAvailable(GetFormat(kTextMime
)))