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
14 #include "nsArrayUtils.h"
16 #include "nsDataObj.h"
18 #include "nsNativeCharsetUtils.h"
19 #include "nsITransferable.h"
22 #include "nsReadableUtils.h"
23 #include "nsUnicharUtils.h"
24 #include "nsPrimitiveHelpers.h"
25 #include "nsIWidget.h"
26 #include "nsWidgetsCID.h"
28 #include "nsNetUtil.h"
29 #include "nsIFileProtocolHandler.h"
31 #include "nsIObserverService.h"
32 #include "nsMimeTypes.h"
33 #include "imgITools.h"
35 using mozilla::LogLevel
;
37 static mozilla::LazyLogModule
gWin32ClipboardLog("nsClipboard");
40 UINT
nsClipboard::GetHtmlClipboardFormat() {
41 static UINT format
= ::RegisterClipboardFormatW(L
"HTML Format");
46 UINT
nsClipboard::GetCustomClipboardFormat() {
48 ::RegisterClipboardFormatW(L
"application/x-moz-custom-clipdata");
52 //-------------------------------------------------------------------------
54 // nsClipboard constructor
56 //-------------------------------------------------------------------------
57 nsClipboard::nsClipboard() : nsBaseClipboard() {
58 mIgnoreEmptyNotification
= false;
61 // Register for a shutdown notification so that we can flush data
62 // to the OS clipboard.
63 nsCOMPtr
<nsIObserverService
> observerService
=
64 do_GetService("@mozilla.org/observer-service;1");
65 if (observerService
) {
66 observerService
->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID
,
71 //-------------------------------------------------------------------------
72 // nsClipboard destructor
73 //-------------------------------------------------------------------------
74 nsClipboard::~nsClipboard() {}
76 NS_IMPL_ISUPPORTS_INHERITED(nsClipboard
, nsBaseClipboard
, nsIObserver
)
79 nsClipboard::Observe(nsISupports
* aSubject
, const char* aTopic
,
80 const char16_t
* aData
) {
81 // This will be called on shutdown.
82 ::OleFlushClipboard();
88 //-------------------------------------------------------------------------
89 UINT
nsClipboard::GetFormat(const char* aMimeStr
, bool aMapHTMLMime
) {
92 if (strcmp(aMimeStr
, kTextMime
) == 0) {
94 } else if (strcmp(aMimeStr
, kUnicodeMime
) == 0) {
95 format
= CF_UNICODETEXT
;
96 } else if (strcmp(aMimeStr
, kRTFMime
) == 0) {
97 format
= ::RegisterClipboardFormat(L
"Rich Text Format");
98 } else if (strcmp(aMimeStr
, kJPEGImageMime
) == 0 ||
99 strcmp(aMimeStr
, kJPGImageMime
) == 0 ||
100 strcmp(aMimeStr
, kPNGImageMime
) == 0) {
102 } else if (strcmp(aMimeStr
, kFileMime
) == 0 ||
103 strcmp(aMimeStr
, kFilePromiseMime
) == 0) {
105 } else if ((strcmp(aMimeStr
, kNativeHTMLMime
) == 0) ||
106 (aMapHTMLMime
&& strcmp(aMimeStr
, kHTMLMime
) == 0)) {
107 format
= GetHtmlClipboardFormat();
108 } else if (strcmp(aMimeStr
, kCustomTypesMime
) == 0) {
109 format
= GetCustomClipboardFormat();
111 format
= ::RegisterClipboardFormatW(NS_ConvertASCIItoUTF16(aMimeStr
).get());
117 //-------------------------------------------------------------------------
118 nsresult
nsClipboard::CreateNativeDataObject(nsITransferable
* aTransferable
,
119 IDataObject
** aDataObj
,
121 if (nullptr == aTransferable
) {
122 return NS_ERROR_FAILURE
;
125 // Create our native DataObject that implements
126 // the OLE IDataObject interface
127 nsDataObj
* dataObj
= new nsDataObj(uri
);
130 return NS_ERROR_OUT_OF_MEMORY
;
135 // Now set it up with all the right data flavors & enums
136 nsresult res
= SetupNativeDataObject(aTransferable
, dataObj
);
145 //-------------------------------------------------------------------------
146 nsresult
nsClipboard::SetupNativeDataObject(nsITransferable
* aTransferable
,
147 IDataObject
* aDataObj
) {
148 if (nullptr == aTransferable
|| nullptr == aDataObj
) {
149 return NS_ERROR_FAILURE
;
152 nsDataObj
* dObj
= static_cast<nsDataObj
*>(aDataObj
);
154 // Now give the Transferable to the DataObject
155 // for getting the data out of it
156 dObj
->SetTransferable(aTransferable
);
158 // Get the transferable list of data flavors
159 nsTArray
<nsCString
> flavors
;
160 aTransferable
->FlavorsTransferableCanExport(flavors
);
162 // Walk through flavors that contain data and register them
163 // into the DataObj as supported flavors
164 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
165 nsCString
& flavorStr
= flavors
[i
];
167 // When putting data onto the clipboard, we want to maintain kHTMLMime
168 // ("text/html") and not map it to CF_HTML here since this will be done
170 UINT format
= GetFormat(flavorStr
.get(), false);
172 // Now tell the native IDataObject about both our mime type and
173 // the native data format
175 SET_FORMATETC(fe
, format
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
);
176 dObj
->AddDataFlavor(flavorStr
.get(), &fe
);
178 // Do various things internal to the implementation, like map one
179 // flavor to another or add additional flavors based on what's required
180 // for the win32 impl.
181 if (flavorStr
.EqualsLiteral(kUnicodeMime
)) {
182 // if we find text/unicode, also advertise text/plain (which we will
183 // convert on our own in nsDataObj::GetText().
185 SET_FORMATETC(textFE
, CF_TEXT
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
);
186 dObj
->AddDataFlavor(kTextMime
, &textFE
);
187 } else if (flavorStr
.EqualsLiteral(kHTMLMime
)) {
188 // if we find text/html, also advertise win32's html flavor (which we will
189 // convert on our own in nsDataObj::GetText().
191 SET_FORMATETC(htmlFE
, GetHtmlClipboardFormat(), 0, DVASPECT_CONTENT
, -1,
193 dObj
->AddDataFlavor(kHTMLMime
, &htmlFE
);
194 } else if (flavorStr
.EqualsLiteral(kURLMime
)) {
195 // if we're a url, in addition to also being text, we need to register
196 // the "file" flavors so that the win32 shell knows to create an internet
197 // shortcut when it sees one of these beasts.
198 FORMATETC shortcutFE
;
199 SET_FORMATETC(shortcutFE
,
200 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA
), 0,
201 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
202 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
203 SET_FORMATETC(shortcutFE
,
204 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW
), 0,
205 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
206 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
207 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_FILECONTENTS
),
208 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
209 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
210 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_INETURLA
), 0,
211 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
212 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
213 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_INETURLW
), 0,
214 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
215 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
216 } else if (flavorStr
.EqualsLiteral(kPNGImageMime
) ||
217 flavorStr
.EqualsLiteral(kJPEGImageMime
) ||
218 flavorStr
.EqualsLiteral(kJPGImageMime
) ||
219 flavorStr
.EqualsLiteral(kGIFImageMime
) ||
220 flavorStr
.EqualsLiteral(kNativeImageMime
)) {
221 // if we're an image, register the native bitmap flavor
224 SET_FORMATETC(imageFE
, CF_DIBV5
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
225 dObj
->AddDataFlavor(flavorStr
.get(), &imageFE
);
227 SET_FORMATETC(imageFE
, CF_DIB
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
228 dObj
->AddDataFlavor(flavorStr
.get(), &imageFE
);
229 } else if (flavorStr
.EqualsLiteral(kFilePromiseMime
)) {
230 // if we're a file promise flavor, also register the
231 // CFSTR_PREFERREDDROPEFFECT format. The data object
232 // returns a value of DROPEFFECTS_MOVE to the drop target
233 // when it asks for the value of this format. This causes
234 // the file to be moved from the temporary location instead
235 // of being copied. The right thing to do here is to call
236 // SetData() on the data object and set the value of this format
237 // to DROPEFFECTS_MOVE on this particular data object. But,
238 // since all the other clipboard formats follow the model of setting
239 // data on the data object only when the drop object calls GetData(),
240 // I am leaving this format's value hard coded in the data object.
241 // We can change this if other consumers of this format get added to this
242 // codebase and they need different values.
243 FORMATETC shortcutFE
;
244 SET_FORMATETC(shortcutFE
,
245 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
), 0,
246 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
247 dObj
->AddDataFlavor(kFilePromiseMime
, &shortcutFE
);
254 //-------------------------------------------------------------------------
255 NS_IMETHODIMP
nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard
) {
256 if (aWhichClipboard
!= kGlobalClipboard
) {
257 return NS_ERROR_FAILURE
;
260 mIgnoreEmptyNotification
= true;
262 // make sure we have a good transferable
263 if (nullptr == mTransferable
) {
264 return NS_ERROR_FAILURE
;
267 IDataObject
* dataObj
;
268 if (NS_SUCCEEDED(CreateNativeDataObject(mTransferable
, &dataObj
,
269 nullptr))) { // this add refs dataObj
270 ::OleSetClipboard(dataObj
);
273 // Clear the native clipboard
274 ::OleSetClipboard(nullptr);
277 mIgnoreEmptyNotification
= false;
282 //-------------------------------------------------------------------------
283 nsresult
nsClipboard::GetGlobalData(HGLOBAL aHGBL
, void** aData
,
285 // Allocate a new memory buffer and copy the data from global memory.
286 // Recall that win98 allocates to nearest DWORD boundary. As a safety
287 // precaution, allocate an extra 3 bytes (but don't report them in |aLen|!)
288 // and null them out to ensure that all of our NS_strlen calls will succeed.
289 // NS_strlen operates on char16_t, so we need 3 NUL bytes to ensure it finds
290 // a full NUL char16_t when |*aLen| is odd.
291 nsresult result
= NS_ERROR_FAILURE
;
292 if (aHGBL
!= nullptr) {
293 LPSTR lpStr
= (LPSTR
)GlobalLock(aHGBL
);
294 CheckedInt
<uint32_t> allocSize
=
295 CheckedInt
<uint32_t>(GlobalSize(aHGBL
)) + 3;
296 if (!allocSize
.isValid()) {
297 return NS_ERROR_INVALID_ARG
;
299 char* data
= static_cast<char*>(malloc(allocSize
.value()));
301 uint32_t size
= allocSize
.value() - 3;
302 memcpy(data
, lpStr
, size
);
303 // null terminate for safety
304 data
[size
] = data
[size
+ 1] = data
[size
+ 2] = '\0';
313 // We really shouldn't ever get here
320 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
, nullptr,
322 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
323 (LPWSTR
)&lpMsgBuf
, 0, nullptr);
325 // Display the string.
326 MessageBoxW(nullptr, (LPCWSTR
)lpMsgBuf
, L
"GetLastError",
327 MB_OK
| MB_ICONINFORMATION
);
336 //-------------------------------------------------------------------------
337 nsresult
nsClipboard::GetNativeDataOffClipboard(nsIWidget
* aWidget
,
338 UINT
/*aIndex*/, UINT aFormat
,
339 void** aData
, uint32_t* aLen
) {
341 nsresult result
= NS_ERROR_FAILURE
;
343 HWND nativeWin
= nullptr;
344 if (::OpenClipboard(nativeWin
)) {
345 hglb
= ::GetClipboardData(aFormat
);
346 result
= GetGlobalData(hglb
, aData
, aLen
);
352 static void DisplayErrCode(HRESULT hres
) {
353 if (hres
== E_INVALIDARG
) {
354 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("E_INVALIDARG\n"));
355 } else if (hres
== E_UNEXPECTED
) {
356 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("E_UNEXPECTED\n"));
357 } else if (hres
== E_OUTOFMEMORY
) {
358 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("E_OUTOFMEMORY\n"));
359 } else if (hres
== DV_E_LINDEX
) {
360 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_LINDEX\n"));
361 } else if (hres
== DV_E_FORMATETC
) {
362 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_FORMATETC\n"));
363 } else if (hres
== DV_E_TYMED
) {
364 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_TYMED\n"));
365 } else if (hres
== DV_E_DVASPECT
) {
366 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_DVASPECT\n"));
367 } else if (hres
== OLE_E_NOTRUNNING
) {
368 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("OLE_E_NOTRUNNING\n"));
369 } else if (hres
== STG_E_MEDIUMFULL
) {
370 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("STG_E_MEDIUMFULL\n"));
371 } else if (hres
== DV_E_CLIPFORMAT
) {
372 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_CLIPFORMAT\n"));
373 } else if (hres
== S_OK
) {
374 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("S_OK\n"));
376 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
,
377 ("****** DisplayErrCode 0x%X\n", hres
));
381 //-------------------------------------------------------------------------
382 static HRESULT
FillSTGMedium(IDataObject
* aDataObject
, UINT aFormat
,
383 LPFORMATETC pFE
, LPSTGMEDIUM pSTM
, DWORD aTymed
) {
384 SET_FORMATETC(*pFE
, aFormat
, 0, DVASPECT_CONTENT
, -1, aTymed
);
386 // Starting by querying for the data to see if we can get it as from global
388 HRESULT hres
= S_FALSE
;
389 hres
= aDataObject
->QueryGetData(pFE
);
390 DisplayErrCode(hres
);
392 hres
= aDataObject
->GetData(pFE
, pSTM
);
393 DisplayErrCode(hres
);
398 //-------------------------------------------------------------------------
399 // If aFormat is CF_DIBV5, aMIMEImageFormat must be a type for which we have
400 // an image encoder (e.g. image/png).
401 // For other values of aFormat, it is OK to pass null for aMIMEImageFormat.
402 nsresult
nsClipboard::GetNativeDataOffClipboard(IDataObject
* aDataObject
,
403 UINT aIndex
, UINT aFormat
,
404 const char* aMIMEImageFormat
,
405 void** aData
, uint32_t* aLen
) {
406 nsresult result
= NS_ERROR_FAILURE
;
414 UINT format
= aFormat
;
415 HRESULT hres
= S_FALSE
;
417 // XXX at the moment we only support global memory transfers
418 // It is here where we will add support for native images
422 hres
= FillSTGMedium(aDataObject
, format
, &fe
, &stm
, TYMED_HGLOBAL
);
424 // Currently this is only handling TYMED_HGLOBAL data
425 // For Text, Dibs, Files, and generic data (like HTML)
427 static CLIPFORMAT fileDescriptorFlavorA
=
428 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA
);
429 static CLIPFORMAT fileDescriptorFlavorW
=
430 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW
);
431 static CLIPFORMAT fileFlavor
=
432 ::RegisterClipboardFormat(CFSTR_FILECONTENTS
);
433 static CLIPFORMAT preferredDropEffect
=
434 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
);
437 case TYMED_HGLOBAL
: {
438 switch (fe
.cfFormat
) {
440 // Get the data out of the global data handle. The size we
441 // return should not include the null because the other
442 // platforms don't use nulls, so just return the length we get
443 // back from strlen(), since we know CF_TEXT is null
444 // terminated. Recall that GetGlobalData() returns the size of
445 // the allocated buffer, not the size of the data (on 98, these
446 // are not the same) so we can't use that.
447 uint32_t allocLen
= 0;
448 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
449 *aLen
= strlen(reinterpret_cast<char*>(*aData
));
454 case CF_UNICODETEXT
: {
455 // Get the data out of the global data handle. The size we
456 // return should not include the null because the other
457 // platforms don't use nulls, so just return the length we get
458 // back from strlen(), since we know CF_UNICODETEXT is null
459 // terminated. Recall that GetGlobalData() returns the size of
460 // the allocated buffer, not the size of the data (on 98, these
461 // are not the same) so we can't use that.
462 uint32_t allocLen
= 0;
463 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
464 *aLen
= NS_strlen(reinterpret_cast<char16_t
*>(*aData
)) * 2;
470 if (aMIMEImageFormat
) {
471 uint32_t allocLen
= 0;
472 const char* clipboardData
;
473 if (NS_SUCCEEDED(GetGlobalData(
474 stm
.hGlobal
, (void**)&clipboardData
, &allocLen
))) {
475 nsCOMPtr
<imgIContainer
> container
;
476 nsCOMPtr
<imgITools
> imgTools
=
477 do_CreateInstance("@mozilla.org/image/tools;1");
478 result
= imgTools
->DecodeImageFromBuffer(
479 clipboardData
, allocLen
,
480 nsLiteralCString(IMAGE_BMP_MS_CLIPBOARD
),
481 getter_AddRefs(container
));
482 if (NS_FAILED(result
)) {
486 nsAutoCString mimeType
;
487 if (strcmp(aMIMEImageFormat
, kJPGImageMime
) == 0) {
488 mimeType
.Assign(IMAGE_JPEG
);
490 mimeType
.Assign(aMIMEImageFormat
);
493 nsCOMPtr
<nsIInputStream
> inputStream
;
494 result
= imgTools
->EncodeImage(container
, mimeType
, u
""_ns
,
495 getter_AddRefs(inputStream
));
496 if (NS_FAILED(result
)) {
501 result
= NS_ERROR_FAILURE
;
505 *aData
= inputStream
.forget().take();
506 *aLen
= sizeof(nsIInputStream
*);
512 // in the case of a file drop, multiple files are stashed within a
513 // single data object. In order to match mozilla's D&D apis, we
514 // just pull out the file at the requested index, pretending as
515 // if there really are multiple drag items.
516 HDROP dropFiles
= (HDROP
)GlobalLock(stm
.hGlobal
);
518 UINT numFiles
= ::DragQueryFileW(dropFiles
, 0xFFFFFFFF, nullptr, 0);
519 NS_ASSERTION(numFiles
> 0,
520 "File drop flavor, but no files...hmmmm");
521 NS_ASSERTION(aIndex
< numFiles
,
522 "Asked for a file index out of range of list");
525 ::DragQueryFileW(dropFiles
, aIndex
, nullptr, 0);
526 wchar_t* buffer
= reinterpret_cast<wchar_t*>(
527 moz_xmalloc((fileNameLen
+ 1) * sizeof(wchar_t)));
528 ::DragQueryFileW(dropFiles
, aIndex
, buffer
, fileNameLen
+ 1);
530 *aLen
= fileNameLen
* sizeof(char16_t
);
533 GlobalUnlock(stm
.hGlobal
);
538 if (fe
.cfFormat
== fileDescriptorFlavorA
||
539 fe
.cfFormat
== fileDescriptorFlavorW
||
540 fe
.cfFormat
== fileFlavor
) {
542 "Mozilla doesn't yet understand how to read this type of "
545 // Get the data out of the global data handle. The size we
546 // return should not include the null because the other
547 // platforms don't use nulls, so just return the length we get
548 // back from strlen(), since we know CF_UNICODETEXT is null
549 // terminated. Recall that GetGlobalData() returns the size of
550 // the allocated buffer, not the size of the data (on 98, these
551 // are not the same) so we can't use that.
553 // NOTE: we are assuming that anything that falls into this
554 // default case is unicode. As we start to get more
555 // kinds of binary data, this may become an incorrect
556 // assumption. Stay tuned.
557 uint32_t allocLen
= 0;
558 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
559 if (fe
.cfFormat
== GetHtmlClipboardFormat()) {
560 // CF_HTML is actually UTF8, not unicode, so disregard the
561 // assumption above. We have to check the header for the
562 // actual length, and we'll do that in FindPlatformHTML().
563 // For now, return the allocLen. This case is mostly to
564 // ensure we don't try to call strlen on the buffer.
566 } else if (fe
.cfFormat
== GetCustomClipboardFormat()) {
569 } else if (fe
.cfFormat
== preferredDropEffect
) {
570 // As per the MSDN doc entitled: "Shell Clipboard Formats"
571 // CFSTR_PREFERREDDROPEFFECT should return a DWORD
573 // http://msdn.microsoft.com/en-us/library/bb776902(v=vs.85).aspx
575 allocLen
== sizeof(DWORD
),
576 "CFSTR_PREFERREDDROPEFFECT should return a DWORD");
579 *aLen
= NS_strlen(reinterpret_cast<char16_t
*>(*aData
)) *
591 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
,
592 ("*********************** TYMED_GDI\n"));
600 ReleaseStgMedium(&stm
);
606 //-------------------------------------------------------------------------
607 nsresult
nsClipboard::GetDataFromDataObject(IDataObject
* aDataObject
,
608 UINT anIndex
, nsIWidget
* aWindow
,
609 nsITransferable
* aTransferable
) {
610 // make sure we have a good transferable
611 if (!aTransferable
) {
612 return NS_ERROR_INVALID_ARG
;
615 nsresult res
= NS_ERROR_FAILURE
;
617 // get flavor list that includes all flavors that can be written (including
618 // ones obtained through conversion)
619 nsTArray
<nsCString
> flavors
;
620 res
= aTransferable
->FlavorsTransferableCanImport(flavors
);
621 if (NS_FAILED(res
)) {
622 return NS_ERROR_FAILURE
;
625 // Walk through flavors and see which flavor is on the clipboard them on the
627 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
628 nsCString
& flavorStr
= flavors
[i
];
629 UINT format
= GetFormat(flavorStr
.get());
631 // Try to get the data using the desired flavor. This might fail, but all is
633 void* data
= nullptr;
634 uint32_t dataLen
= 0;
635 bool dataFound
= false;
636 if (nullptr != aDataObject
) {
637 if (NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject
, anIndex
, format
,
638 flavorStr
.get(), &data
,
642 } else if (nullptr != aWindow
) {
643 if (NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow
, anIndex
, format
,
649 // This is our second chance to try to find some data, having not found it
650 // when directly asking for the flavor. Let's try digging around in other
651 // flavors to help satisfy our craving for data.
653 if (flavorStr
.EqualsLiteral(kUnicodeMime
)) {
655 FindUnicodeFromPlainText(aDataObject
, anIndex
, &data
, &dataLen
);
656 } else if (flavorStr
.EqualsLiteral(kURLMime
)) {
657 // drags from other windows apps expose the native
658 // CFSTR_INETURL{A,W} flavor
659 dataFound
= FindURLFromNativeURL(aDataObject
, anIndex
, &data
, &dataLen
);
662 FindURLFromLocalFile(aDataObject
, anIndex
, &data
, &dataLen
);
665 } // if we try one last ditch effort to find our data
667 // Hopefully by this point we've found it and can go about our business
669 nsCOMPtr
<nsISupports
> genericDataWrapper
;
670 if (flavorStr
.EqualsLiteral(kFileMime
)) {
671 // we have a file path in |data|. Create an nsLocalFile object.
672 nsDependentString
filepath(reinterpret_cast<char16_t
*>(data
));
673 nsCOMPtr
<nsIFile
> file
;
675 NS_NewLocalFile(filepath
, false, getter_AddRefs(file
)))) {
676 genericDataWrapper
= do_QueryInterface(file
);
679 } else if (flavorStr
.EqualsLiteral(kNativeHTMLMime
)) {
681 // the editor folks want CF_HTML exactly as it's on the clipboard, no
682 // conversions, no fancy stuff. Pull it off the clipboard, stuff it into
683 // a wrapper and hand it back to them.
684 if (FindPlatformHTML(aDataObject
, anIndex
, &data
, &dummy
, &dataLen
)) {
685 nsPrimitiveHelpers::CreatePrimitiveForData(
686 flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
));
689 continue; // something wrong with this flavor, keep looking for other
693 } else if (flavorStr
.EqualsLiteral(kHTMLMime
)) {
694 uint32_t startOfData
= 0;
695 // The JS folks want CF_HTML exactly as it is on the clipboard, but
696 // minus the CF_HTML header index information.
697 // It also needs to be converted to UTF16 and have linebreaks changed.
698 if (FindPlatformHTML(aDataObject
, anIndex
, &data
, &startOfData
,
700 dataLen
-= startOfData
;
701 nsPrimitiveHelpers::CreatePrimitiveForCFHTML(
702 static_cast<char*>(data
) + startOfData
, &dataLen
,
703 getter_AddRefs(genericDataWrapper
));
706 continue; // something wrong with this flavor, keep looking for other
710 } else if (flavorStr
.EqualsLiteral(kJPEGImageMime
) ||
711 flavorStr
.EqualsLiteral(kJPGImageMime
) ||
712 flavorStr
.EqualsLiteral(kPNGImageMime
)) {
713 nsIInputStream
* imageStream
= reinterpret_cast<nsIInputStream
*>(data
);
714 genericDataWrapper
= do_QueryInterface(imageStream
);
715 NS_IF_RELEASE(imageStream
);
717 // Treat custom types as a string of bytes.
718 if (!flavorStr
.EqualsLiteral(kCustomTypesMime
)) {
719 // we probably have some form of text. The DOM only wants LF, so
720 // convert from Win32 line endings to DOM line endings.
721 int32_t signedLen
= static_cast<int32_t>(dataLen
);
722 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr
, &data
,
726 if (flavorStr
.EqualsLiteral(kRTFMime
)) {
727 // RTF on Windows is known to sometimes deliver an extra null byte.
728 if (dataLen
> 0 && static_cast<char*>(data
)[dataLen
- 1] == '\0') {
734 nsPrimitiveHelpers::CreatePrimitiveForData(
735 flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
));
739 NS_ASSERTION(genericDataWrapper
,
740 "About to put null data into the transferable");
741 aTransferable
->SetTransferData(flavorStr
.get(), genericDataWrapper
);
744 // we found one, get out of the loop
755 // Someone asked for the OS CF_HTML flavor. We give it back to them exactly
758 bool nsClipboard ::FindPlatformHTML(IDataObject
* inDataObject
, UINT inIndex
,
759 void** outData
, uint32_t* outStartOfData
,
760 uint32_t* outDataLen
) {
761 // Reference: MSDN doc entitled "HTML Clipboard Format"
762 // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
763 // CF_HTML is UTF8, not unicode. We also can't rely on it being
764 // null-terminated so we have to check the CF_HTML header for the correct
765 // length. The length we return is the bytecount from the beginning of the
766 // selected data to the end of the selected data, without the null
767 // termination. Because it's UTF8, we're guaranteed the header is ASCII.
769 if (!outData
|| !*outData
) {
773 char version
[8] = {0};
774 int32_t startOfData
= 0;
775 int32_t endOfData
= 0;
777 sscanf((char*)*outData
, "Version:%7s\nStartHTML:%d\nEndHTML:%d", version
,
778 &startOfData
, &endOfData
);
780 if (numFound
!= 3 || startOfData
< -1 || endOfData
< -1) {
784 // Fixup the start and end markers if they have no context (set to -1)
785 if (startOfData
== -1) {
788 if (endOfData
== -1) {
789 endOfData
= *outDataLen
;
792 // Make sure we were passed sane values within our buffer size.
793 // (Note that we've handled all cases of negative endOfData above, so we can
794 // safely cast it to be unsigned here.)
795 if (!endOfData
|| startOfData
>= endOfData
||
796 static_cast<uint32_t>(endOfData
) > *outDataLen
) {
800 // We want to return the buffer not offset by startOfData because it will be
801 // parsed out later (probably by HTMLEditor::ParseCFHTML) when it is still
802 // in CF_HTML format.
804 // We return the byte offset from the start of the data buffer to where the
805 // HTML data starts. The caller might want to extract the HTML only.
806 *outStartOfData
= startOfData
;
807 *outDataLen
= endOfData
;
812 // FindUnicodeFromPlainText
814 // we are looking for text/unicode and we failed to find it on the clipboard
815 // first, try again with text/plain. If that is present, convert it to unicode.
817 bool nsClipboard ::FindUnicodeFromPlainText(IDataObject
* inDataObject
,
818 UINT inIndex
, void** outData
,
819 uint32_t* outDataLen
) {
820 // we are looking for text/unicode and we failed to find it on the clipboard
821 // first, try again with text/plain. If that is present, convert it to
824 GetNativeDataOffClipboard(inDataObject
, inIndex
, GetFormat(kTextMime
),
825 nullptr, outData
, outDataLen
);
826 if (NS_FAILED(rv
) || !*outData
) {
830 const char* castedText
= static_cast<char*>(*outData
);
832 rv
= NS_CopyNativeToUnicode(nsDependentCSubstring(castedText
, *outDataLen
),
838 // out with the old, in with the new
840 *outData
= ToNewUnicode(tmp
);
841 *outDataLen
= tmp
.Length() * sizeof(char16_t
);
845 } // FindUnicodeFromPlainText
848 // FindURLFromLocalFile
850 // we are looking for a URL and couldn't find it, try again with looking for
851 // a local file. If we have one, it may either be a normal file or an internet
852 // shortcut. In both cases, however, we can get a URL (it will be a file:// url
853 // in the local file case).
855 bool nsClipboard ::FindURLFromLocalFile(IDataObject
* inDataObject
, UINT inIndex
,
856 void** outData
, uint32_t* outDataLen
) {
857 bool dataFound
= false;
859 nsresult loadResult
=
860 GetNativeDataOffClipboard(inDataObject
, inIndex
, GetFormat(kFileMime
),
861 nullptr, outData
, outDataLen
);
862 if (NS_SUCCEEDED(loadResult
) && *outData
) {
863 // we have a file path in |data|. Is it an internet shortcut or a normal
865 const nsDependentString
filepath(static_cast<char16_t
*>(*outData
));
866 nsCOMPtr
<nsIFile
> file
;
867 nsresult rv
= NS_NewLocalFile(filepath
, true, getter_AddRefs(file
));
873 if (IsInternetShortcut(filepath
)) {
876 ResolveShortcut(file
, url
);
877 if (!url
.IsEmpty()) {
878 // convert it to unicode and pass it out
879 NS_ConvertUTF8toUTF16
urlString(url
);
880 // the internal mozilla URL format, text/x-moz-url, contains
881 // URL\ntitle. We can guess the title from the file's name.
883 file
->GetLeafName(title
);
884 // We rely on IsInternetShortcut check that file has a .url extension.
885 title
.SetLength(title
.Length() - 4);
886 if (title
.IsEmpty()) {
889 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ title
);
891 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
896 // we have a normal file, use some Necko objects to get our file path
897 nsAutoCString urlSpec
;
898 NS_GetURLSpecFromFile(file
, urlSpec
);
900 // convert it to unicode and pass it out
902 *outData
= UTF8ToNewUnicode(urlSpec
);
904 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
906 } // else regular file
910 } // FindURLFromLocalFile
913 // FindURLFromNativeURL
915 // we are looking for a URL and couldn't find it using our internal
916 // URL flavor, so look for it using the native URL flavor,
917 // CF_INETURLSTRW (We don't handle CF_INETURLSTRA currently)
919 bool nsClipboard ::FindURLFromNativeURL(IDataObject
* inDataObject
, UINT inIndex
,
920 void** outData
, uint32_t* outDataLen
) {
921 bool dataFound
= false;
923 void* tempOutData
= nullptr;
924 uint32_t tempDataLen
= 0;
926 nsresult loadResult
= GetNativeDataOffClipboard(
927 inDataObject
, inIndex
, ::RegisterClipboardFormat(CFSTR_INETURLW
), nullptr,
928 &tempOutData
, &tempDataLen
);
929 if (NS_SUCCEEDED(loadResult
) && tempOutData
) {
930 nsDependentString
urlString(static_cast<char16_t
*>(tempOutData
));
931 // the internal mozilla URL format, text/x-moz-url, contains
932 // URL\ntitle. Since we don't actually have a title here,
933 // just repeat the URL to fake it.
934 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ urlString
);
936 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
940 loadResult
= GetNativeDataOffClipboard(
941 inDataObject
, inIndex
, ::RegisterClipboardFormat(CFSTR_INETURLA
),
942 nullptr, &tempOutData
, &tempDataLen
);
943 if (NS_SUCCEEDED(loadResult
) && tempOutData
) {
944 // CFSTR_INETURLA is (currently) equal to CFSTR_SHELLURL which is equal to
945 // CF_TEXT which is by definition ANSI encoded.
946 nsCString urlUnescapedA
;
948 NS_UnescapeURL(static_cast<char*>(tempOutData
), tempDataLen
,
949 esc_OnlyNonASCII
| esc_SkipControl
, urlUnescapedA
);
953 NS_CopyNativeToUnicode(urlUnescapedA
, urlString
);
955 NS_CopyNativeToUnicode(
956 nsDependentCString(static_cast<char*>(tempOutData
), tempDataLen
),
960 // the internal mozilla URL format, text/x-moz-url, contains
961 // URL\ntitle. Since we don't actually have a title here,
962 // just repeat the URL to fake it.
963 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ urlString
);
965 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
972 } // FindURLFromNativeURL
977 void nsClipboard ::ResolveShortcut(nsIFile
* aFile
, nsACString
& outURL
) {
978 nsCOMPtr
<nsIFileProtocolHandler
> fph
;
979 nsresult rv
= NS_GetFileProtocolHandler(getter_AddRefs(fph
));
984 nsCOMPtr
<nsIURI
> uri
;
985 rv
= fph
->ReadURLFile(aFile
, getter_AddRefs(uri
));
990 uri
->GetSpec(outURL
);
994 // IsInternetShortcut
996 // A file is an Internet Shortcut if it ends with .URL
998 bool nsClipboard ::IsInternetShortcut(const nsAString
& inFileName
) {
999 return StringEndsWith(inFileName
, u
".url"_ns
,
1000 nsCaseInsensitiveStringComparator
);
1001 } // IsInternetShortcut
1003 //-------------------------------------------------------------------------
1005 nsClipboard::GetNativeClipboardData(nsITransferable
* aTransferable
,
1006 int32_t aWhichClipboard
) {
1007 // make sure we have a good transferable
1008 if (!aTransferable
|| aWhichClipboard
!= kGlobalClipboard
) {
1009 return NS_ERROR_FAILURE
;
1014 // This makes sure we can use the OLE functionality for the clipboard
1015 IDataObject
* dataObj
;
1016 if (S_OK
== ::OleGetClipboard(&dataObj
)) {
1017 // Use OLE IDataObject for clipboard operations
1018 res
= GetDataFromDataObject(dataObj
, 0, nullptr, aTransferable
);
1021 // do it the old manual way
1022 res
= GetDataFromDataObject(nullptr, 0, mWindow
, aTransferable
);
1028 nsClipboard::EmptyClipboard(int32_t aWhichClipboard
) {
1029 // Some programs such as ZoneAlarm monitor clipboard usage and then open the
1030 // clipboard to scan it. If we i) empty and then ii) set data, then the
1031 // 'set data' can sometimes fail with access denied becacuse another program
1032 // has the clipboard open. So to avoid this race condition for OpenClipboard
1033 // we do not empty the clipboard when we're setting it.
1034 if (aWhichClipboard
== kGlobalClipboard
&& !mEmptyingForSetData
) {
1035 OleSetClipboard(nullptr);
1037 return nsBaseClipboard::EmptyClipboard(aWhichClipboard
);
1040 //-------------------------------------------------------------------------
1041 NS_IMETHODIMP
nsClipboard::HasDataMatchingFlavors(
1042 const nsTArray
<nsCString
>& aFlavorList
, int32_t aWhichClipboard
,
1045 if (aWhichClipboard
!= kGlobalClipboard
) {
1049 for (auto& flavor
: aFlavorList
) {
1051 if (flavor
.EqualsLiteral(kTextMime
)) {
1053 "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode "
1058 UINT format
= GetFormat(flavor
.get());
1059 if (IsClipboardFormatAvailable(format
)) {
1063 // We haven't found the exact flavor the client asked for, but maybe we
1064 // can still find it from something else that's on the clipboard...
1065 if (flavor
.EqualsLiteral(kUnicodeMime
)) {
1066 // client asked for unicode and it wasn't present, check if we have
1067 // CF_TEXT. We'll handle the actual data substitution in the data
1069 if (IsClipboardFormatAvailable(GetFormat(kTextMime
))) {