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 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Verbose
, ("%s", __FUNCTION__
));
287 // Allocate a new memory buffer and copy the data from global memory.
288 // Recall that win98 allocates to nearest DWORD boundary. As a safety
289 // precaution, allocate an extra 3 bytes (but don't report them in |aLen|!)
290 // and null them out to ensure that all of our NS_strlen calls will succeed.
291 // NS_strlen operates on char16_t, so we need 3 NUL bytes to ensure it finds
292 // a full NUL char16_t when |*aLen| is odd.
293 nsresult result
= NS_ERROR_FAILURE
;
294 if (aHGBL
!= nullptr) {
295 LPSTR lpStr
= (LPSTR
)GlobalLock(aHGBL
);
296 CheckedInt
<uint32_t> allocSize
=
297 CheckedInt
<uint32_t>(GlobalSize(aHGBL
)) + 3;
298 if (!allocSize
.isValid()) {
299 return NS_ERROR_INVALID_ARG
;
301 char* data
= static_cast<char*>(malloc(allocSize
.value()));
303 uint32_t size
= allocSize
.value() - 3;
304 memcpy(data
, lpStr
, size
);
305 // null terminate for safety
306 data
[size
] = data
[size
+ 1] = data
[size
+ 2] = '\0';
315 // We really shouldn't ever get here
322 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
, nullptr,
324 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
325 (LPWSTR
)&lpMsgBuf
, 0, nullptr);
327 // Display the string.
328 MessageBoxW(nullptr, (LPCWSTR
)lpMsgBuf
, L
"GetLastError",
329 MB_OK
| MB_ICONINFORMATION
);
338 //-------------------------------------------------------------------------
339 nsresult
nsClipboard::GetNativeDataOffClipboard(nsIWidget
* aWidget
,
340 UINT
/*aIndex*/, UINT aFormat
,
341 void** aData
, uint32_t* aLen
) {
342 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
343 ("%s: overload taking nsIWidget*.", __FUNCTION__
));
346 nsresult result
= NS_ERROR_FAILURE
;
348 HWND nativeWin
= nullptr;
349 if (::OpenClipboard(nativeWin
)) {
350 hglb
= ::GetClipboardData(aFormat
);
351 result
= GetGlobalData(hglb
, aData
, aLen
);
357 static void DisplayErrCode(HRESULT hres
) {
358 if (hres
== E_INVALIDARG
) {
359 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("E_INVALIDARG\n"));
360 } else if (hres
== E_UNEXPECTED
) {
361 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("E_UNEXPECTED\n"));
362 } else if (hres
== E_OUTOFMEMORY
) {
363 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("E_OUTOFMEMORY\n"));
364 } else if (hres
== DV_E_LINDEX
) {
365 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_LINDEX\n"));
366 } else if (hres
== DV_E_FORMATETC
) {
367 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_FORMATETC\n"));
368 } else if (hres
== DV_E_TYMED
) {
369 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_TYMED\n"));
370 } else if (hres
== DV_E_DVASPECT
) {
371 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_DVASPECT\n"));
372 } else if (hres
== OLE_E_NOTRUNNING
) {
373 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("OLE_E_NOTRUNNING\n"));
374 } else if (hres
== STG_E_MEDIUMFULL
) {
375 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("STG_E_MEDIUMFULL\n"));
376 } else if (hres
== DV_E_CLIPFORMAT
) {
377 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_CLIPFORMAT\n"));
378 } else if (hres
== S_OK
) {
379 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("S_OK\n"));
381 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
,
382 ("****** DisplayErrCode 0x%X\n", hres
));
386 //-------------------------------------------------------------------------
387 static HRESULT
FillSTGMedium(IDataObject
* aDataObject
, UINT aFormat
,
388 LPFORMATETC pFE
, LPSTGMEDIUM pSTM
, DWORD aTymed
) {
389 SET_FORMATETC(*pFE
, aFormat
, 0, DVASPECT_CONTENT
, -1, aTymed
);
391 // Starting by querying for the data to see if we can get it as from global
393 HRESULT hres
= S_FALSE
;
394 hres
= aDataObject
->QueryGetData(pFE
);
395 DisplayErrCode(hres
);
397 hres
= aDataObject
->GetData(pFE
, pSTM
);
398 DisplayErrCode(hres
);
403 //-------------------------------------------------------------------------
404 // If aFormat is CF_DIBV5, aMIMEImageFormat must be a type for which we have
405 // an image encoder (e.g. image/png).
406 // For other values of aFormat, it is OK to pass null for aMIMEImageFormat.
407 nsresult
nsClipboard::GetNativeDataOffClipboard(IDataObject
* aDataObject
,
408 UINT aIndex
, UINT aFormat
,
409 const char* aMIMEImageFormat
,
410 void** aData
, uint32_t* aLen
) {
411 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
412 ("%s: overload taking IDataObject*.", __FUNCTION__
));
414 nsresult result
= NS_ERROR_FAILURE
;
422 UINT format
= aFormat
;
423 HRESULT hres
= S_FALSE
;
425 // XXX at the moment we only support global memory transfers
426 // It is here where we will add support for native images
430 hres
= FillSTGMedium(aDataObject
, format
, &fe
, &stm
, TYMED_HGLOBAL
);
432 // Currently this is only handling TYMED_HGLOBAL data
433 // For Text, Dibs, Files, and generic data (like HTML)
435 static CLIPFORMAT fileDescriptorFlavorA
=
436 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA
);
437 static CLIPFORMAT fileDescriptorFlavorW
=
438 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW
);
439 static CLIPFORMAT fileFlavor
=
440 ::RegisterClipboardFormat(CFSTR_FILECONTENTS
);
441 static CLIPFORMAT preferredDropEffect
=
442 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
);
445 case TYMED_HGLOBAL
: {
446 switch (fe
.cfFormat
) {
448 // Get the data out of the global data handle. The size we
449 // return should not include the null because the other
450 // platforms don't use nulls, so just return the length we get
451 // back from strlen(), since we know CF_TEXT is null
452 // terminated. Recall that GetGlobalData() returns the size of
453 // the allocated buffer, not the size of the data (on 98, these
454 // are not the same) so we can't use that.
455 uint32_t allocLen
= 0;
456 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
457 *aLen
= strlen(reinterpret_cast<char*>(*aData
));
462 case CF_UNICODETEXT
: {
463 // Get the data out of the global data handle. The size we
464 // return should not include the null because the other
465 // platforms don't use nulls, so just return the length we get
466 // back from strlen(), since we know CF_UNICODETEXT is null
467 // terminated. Recall that GetGlobalData() returns the size of
468 // the allocated buffer, not the size of the data (on 98, these
469 // are not the same) so we can't use that.
470 uint32_t allocLen
= 0;
471 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
472 *aLen
= NS_strlen(reinterpret_cast<char16_t
*>(*aData
)) * 2;
478 if (aMIMEImageFormat
) {
479 uint32_t allocLen
= 0;
480 const char* clipboardData
;
481 if (NS_SUCCEEDED(GetGlobalData(
482 stm
.hGlobal
, (void**)&clipboardData
, &allocLen
))) {
483 nsCOMPtr
<imgIContainer
> container
;
484 nsCOMPtr
<imgITools
> imgTools
=
485 do_CreateInstance("@mozilla.org/image/tools;1");
486 result
= imgTools
->DecodeImageFromBuffer(
487 clipboardData
, allocLen
,
488 nsLiteralCString(IMAGE_BMP_MS_CLIPBOARD
),
489 getter_AddRefs(container
));
490 if (NS_FAILED(result
)) {
494 nsAutoCString mimeType
;
495 if (strcmp(aMIMEImageFormat
, kJPGImageMime
) == 0) {
496 mimeType
.Assign(IMAGE_JPEG
);
498 mimeType
.Assign(aMIMEImageFormat
);
501 nsCOMPtr
<nsIInputStream
> inputStream
;
502 result
= imgTools
->EncodeImage(container
, mimeType
, u
""_ns
,
503 getter_AddRefs(inputStream
));
504 if (NS_FAILED(result
)) {
509 result
= NS_ERROR_FAILURE
;
513 *aData
= inputStream
.forget().take();
514 *aLen
= sizeof(nsIInputStream
*);
520 // in the case of a file drop, multiple files are stashed within a
521 // single data object. In order to match mozilla's D&D apis, we
522 // just pull out the file at the requested index, pretending as
523 // if there really are multiple drag items.
524 HDROP dropFiles
= (HDROP
)GlobalLock(stm
.hGlobal
);
526 UINT numFiles
= ::DragQueryFileW(dropFiles
, 0xFFFFFFFF, nullptr, 0);
527 NS_ASSERTION(numFiles
> 0,
528 "File drop flavor, but no files...hmmmm");
529 NS_ASSERTION(aIndex
< numFiles
,
530 "Asked for a file index out of range of list");
533 ::DragQueryFileW(dropFiles
, aIndex
, nullptr, 0);
534 wchar_t* buffer
= reinterpret_cast<wchar_t*>(
535 moz_xmalloc((fileNameLen
+ 1) * sizeof(wchar_t)));
536 ::DragQueryFileW(dropFiles
, aIndex
, buffer
, fileNameLen
+ 1);
538 *aLen
= fileNameLen
* sizeof(char16_t
);
541 GlobalUnlock(stm
.hGlobal
);
546 if (fe
.cfFormat
== fileDescriptorFlavorA
||
547 fe
.cfFormat
== fileDescriptorFlavorW
||
548 fe
.cfFormat
== fileFlavor
) {
550 "Mozilla doesn't yet understand how to read this type of "
553 // Get the data out of the global data handle. The size we
554 // return should not include the null because the other
555 // platforms don't use nulls, so just return the length we get
556 // back from strlen(), since we know CF_UNICODETEXT is null
557 // terminated. Recall that GetGlobalData() returns the size of
558 // the allocated buffer, not the size of the data (on 98, these
559 // are not the same) so we can't use that.
561 // NOTE: we are assuming that anything that falls into this
562 // default case is unicode. As we start to get more
563 // kinds of binary data, this may become an incorrect
564 // assumption. Stay tuned.
565 uint32_t allocLen
= 0;
566 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
567 if (fe
.cfFormat
== GetHtmlClipboardFormat()) {
568 // CF_HTML is actually UTF8, not unicode, so disregard the
569 // assumption above. We have to check the header for the
570 // actual length, and we'll do that in FindPlatformHTML().
571 // For now, return the allocLen. This case is mostly to
572 // ensure we don't try to call strlen on the buffer.
574 } else if (fe
.cfFormat
== GetCustomClipboardFormat()) {
577 } else if (fe
.cfFormat
== preferredDropEffect
) {
578 // As per the MSDN doc entitled: "Shell Clipboard Formats"
579 // CFSTR_PREFERREDDROPEFFECT should return a DWORD
581 // http://msdn.microsoft.com/en-us/library/bb776902(v=vs.85).aspx
583 allocLen
== sizeof(DWORD
),
584 "CFSTR_PREFERREDDROPEFFECT should return a DWORD");
587 *aLen
= NS_strlen(reinterpret_cast<char16_t
*>(*aData
)) *
599 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
,
600 ("*********************** TYMED_GDI\n"));
608 ReleaseStgMedium(&stm
);
614 //-------------------------------------------------------------------------
615 nsresult
nsClipboard::GetDataFromDataObject(IDataObject
* aDataObject
,
616 UINT anIndex
, nsIWidget
* aWindow
,
617 nsITransferable
* aTransferable
) {
618 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
620 // make sure we have a good transferable
621 if (!aTransferable
) {
622 return NS_ERROR_INVALID_ARG
;
625 nsresult res
= NS_ERROR_FAILURE
;
627 // get flavor list that includes all flavors that can be written (including
628 // ones obtained through conversion)
629 nsTArray
<nsCString
> flavors
;
630 res
= aTransferable
->FlavorsTransferableCanImport(flavors
);
631 if (NS_FAILED(res
)) {
632 return NS_ERROR_FAILURE
;
635 // Walk through flavors and see which flavor is on the clipboard them on the
637 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
638 nsCString
& flavorStr
= flavors
[i
];
639 UINT format
= GetFormat(flavorStr
.get());
641 // Try to get the data using the desired flavor. This might fail, but all is
643 void* data
= nullptr;
644 uint32_t dataLen
= 0;
645 bool dataFound
= false;
646 if (nullptr != aDataObject
) {
647 if (NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject
, anIndex
, format
,
648 flavorStr
.get(), &data
,
652 } else if (nullptr != aWindow
) {
653 if (NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow
, anIndex
, format
,
659 // This is our second chance to try to find some data, having not found it
660 // when directly asking for the flavor. Let's try digging around in other
661 // flavors to help satisfy our craving for data.
663 if (flavorStr
.EqualsLiteral(kUnicodeMime
)) {
665 FindUnicodeFromPlainText(aDataObject
, anIndex
, &data
, &dataLen
);
666 } else if (flavorStr
.EqualsLiteral(kURLMime
)) {
667 // drags from other windows apps expose the native
668 // CFSTR_INETURL{A,W} flavor
669 dataFound
= FindURLFromNativeURL(aDataObject
, anIndex
, &data
, &dataLen
);
672 FindURLFromLocalFile(aDataObject
, anIndex
, &data
, &dataLen
);
675 } // if we try one last ditch effort to find our data
677 // Hopefully by this point we've found it and can go about our business
679 nsCOMPtr
<nsISupports
> genericDataWrapper
;
680 if (flavorStr
.EqualsLiteral(kFileMime
)) {
681 // we have a file path in |data|. Create an nsLocalFile object.
682 nsDependentString
filepath(reinterpret_cast<char16_t
*>(data
));
683 nsCOMPtr
<nsIFile
> file
;
685 NS_NewLocalFile(filepath
, false, getter_AddRefs(file
)))) {
686 genericDataWrapper
= do_QueryInterface(file
);
689 } else if (flavorStr
.EqualsLiteral(kNativeHTMLMime
)) {
691 // the editor folks want CF_HTML exactly as it's on the clipboard, no
692 // conversions, no fancy stuff. Pull it off the clipboard, stuff it into
693 // a wrapper and hand it back to them.
694 if (FindPlatformHTML(aDataObject
, anIndex
, &data
, &dummy
, &dataLen
)) {
695 nsPrimitiveHelpers::CreatePrimitiveForData(
696 flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
));
699 continue; // something wrong with this flavor, keep looking for other
703 } else if (flavorStr
.EqualsLiteral(kHTMLMime
)) {
704 uint32_t startOfData
= 0;
705 // The JS folks want CF_HTML exactly as it is on the clipboard, but
706 // minus the CF_HTML header index information.
707 // It also needs to be converted to UTF16 and have linebreaks changed.
708 if (FindPlatformHTML(aDataObject
, anIndex
, &data
, &startOfData
,
710 dataLen
-= startOfData
;
711 nsPrimitiveHelpers::CreatePrimitiveForCFHTML(
712 static_cast<char*>(data
) + startOfData
, &dataLen
,
713 getter_AddRefs(genericDataWrapper
));
716 continue; // something wrong with this flavor, keep looking for other
720 } else if (flavorStr
.EqualsLiteral(kJPEGImageMime
) ||
721 flavorStr
.EqualsLiteral(kJPGImageMime
) ||
722 flavorStr
.EqualsLiteral(kPNGImageMime
)) {
723 nsIInputStream
* imageStream
= reinterpret_cast<nsIInputStream
*>(data
);
724 genericDataWrapper
= do_QueryInterface(imageStream
);
725 NS_IF_RELEASE(imageStream
);
727 // Treat custom types as a string of bytes.
728 if (!flavorStr
.EqualsLiteral(kCustomTypesMime
)) {
729 // we probably have some form of text. The DOM only wants LF, so
730 // convert from Win32 line endings to DOM line endings.
731 int32_t signedLen
= static_cast<int32_t>(dataLen
);
732 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr
, &data
,
736 if (flavorStr
.EqualsLiteral(kRTFMime
)) {
737 // RTF on Windows is known to sometimes deliver an extra null byte.
738 if (dataLen
> 0 && static_cast<char*>(data
)[dataLen
- 1] == '\0') {
744 nsPrimitiveHelpers::CreatePrimitiveForData(
745 flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
));
749 NS_ASSERTION(genericDataWrapper
,
750 "About to put null data into the transferable");
751 aTransferable
->SetTransferData(flavorStr
.get(), genericDataWrapper
);
754 // we found one, get out of the loop
765 // Someone asked for the OS CF_HTML flavor. We give it back to them exactly
768 bool nsClipboard ::FindPlatformHTML(IDataObject
* inDataObject
, UINT inIndex
,
769 void** outData
, uint32_t* outStartOfData
,
770 uint32_t* outDataLen
) {
771 // Reference: MSDN doc entitled "HTML Clipboard Format"
772 // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
773 // CF_HTML is UTF8, not unicode. We also can't rely on it being
774 // null-terminated so we have to check the CF_HTML header for the correct
775 // length. The length we return is the bytecount from the beginning of the
776 // selected data to the end of the selected data, without the null
777 // termination. Because it's UTF8, we're guaranteed the header is ASCII.
779 if (!outData
|| !*outData
) {
783 char version
[8] = {0};
784 int32_t startOfData
= 0;
785 int32_t endOfData
= 0;
787 sscanf((char*)*outData
, "Version:%7s\nStartHTML:%d\nEndHTML:%d", version
,
788 &startOfData
, &endOfData
);
790 if (numFound
!= 3 || startOfData
< -1 || endOfData
< -1) {
794 // Fixup the start and end markers if they have no context (set to -1)
795 if (startOfData
== -1) {
798 if (endOfData
== -1) {
799 endOfData
= *outDataLen
;
802 // Make sure we were passed sane values within our buffer size.
803 // (Note that we've handled all cases of negative endOfData above, so we can
804 // safely cast it to be unsigned here.)
805 if (!endOfData
|| startOfData
>= endOfData
||
806 static_cast<uint32_t>(endOfData
) > *outDataLen
) {
810 // We want to return the buffer not offset by startOfData because it will be
811 // parsed out later (probably by HTMLEditor::ParseCFHTML) when it is still
812 // in CF_HTML format.
814 // We return the byte offset from the start of the data buffer to where the
815 // HTML data starts. The caller might want to extract the HTML only.
816 *outStartOfData
= startOfData
;
817 *outDataLen
= endOfData
;
822 // FindUnicodeFromPlainText
824 // we are looking for text/unicode and we failed to find it on the clipboard
825 // first, try again with text/plain. If that is present, convert it to unicode.
827 bool nsClipboard ::FindUnicodeFromPlainText(IDataObject
* inDataObject
,
828 UINT inIndex
, void** outData
,
829 uint32_t* outDataLen
) {
830 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
832 // we are looking for text/unicode and we failed to find it on the clipboard
833 // first, try again with text/plain. If that is present, convert it to
836 GetNativeDataOffClipboard(inDataObject
, inIndex
, GetFormat(kTextMime
),
837 nullptr, outData
, outDataLen
);
838 if (NS_FAILED(rv
) || !*outData
) {
842 const char* castedText
= static_cast<char*>(*outData
);
844 rv
= NS_CopyNativeToUnicode(nsDependentCSubstring(castedText
, *outDataLen
),
850 // out with the old, in with the new
852 *outData
= ToNewUnicode(tmp
);
853 *outDataLen
= tmp
.Length() * sizeof(char16_t
);
857 } // FindUnicodeFromPlainText
860 // FindURLFromLocalFile
862 // we are looking for a URL and couldn't find it, try again with looking for
863 // a local file. If we have one, it may either be a normal file or an internet
864 // shortcut. In both cases, however, we can get a URL (it will be a file:// url
865 // in the local file case).
867 bool nsClipboard ::FindURLFromLocalFile(IDataObject
* inDataObject
, UINT inIndex
,
868 void** outData
, uint32_t* outDataLen
) {
869 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
871 bool dataFound
= false;
873 nsresult loadResult
=
874 GetNativeDataOffClipboard(inDataObject
, inIndex
, GetFormat(kFileMime
),
875 nullptr, outData
, outDataLen
);
876 if (NS_SUCCEEDED(loadResult
) && *outData
) {
877 // we have a file path in |data|. Is it an internet shortcut or a normal
879 const nsDependentString
filepath(static_cast<char16_t
*>(*outData
));
880 nsCOMPtr
<nsIFile
> file
;
881 nsresult rv
= NS_NewLocalFile(filepath
, true, getter_AddRefs(file
));
887 if (IsInternetShortcut(filepath
)) {
890 ResolveShortcut(file
, url
);
891 if (!url
.IsEmpty()) {
892 // convert it to unicode and pass it out
893 NS_ConvertUTF8toUTF16
urlString(url
);
894 // the internal mozilla URL format, text/x-moz-url, contains
895 // URL\ntitle. We can guess the title from the file's name.
897 file
->GetLeafName(title
);
898 // We rely on IsInternetShortcut check that file has a .url extension.
899 title
.SetLength(title
.Length() - 4);
900 if (title
.IsEmpty()) {
903 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ title
);
905 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
910 // we have a normal file, use some Necko objects to get our file path
911 nsAutoCString urlSpec
;
912 NS_GetURLSpecFromFile(file
, urlSpec
);
914 // convert it to unicode and pass it out
916 *outData
= UTF8ToNewUnicode(urlSpec
);
918 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
920 } // else regular file
924 } // FindURLFromLocalFile
927 // FindURLFromNativeURL
929 // we are looking for a URL and couldn't find it using our internal
930 // URL flavor, so look for it using the native URL flavor,
931 // CF_INETURLSTRW (We don't handle CF_INETURLSTRA currently)
933 bool nsClipboard ::FindURLFromNativeURL(IDataObject
* inDataObject
, UINT inIndex
,
934 void** outData
, uint32_t* outDataLen
) {
935 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
937 bool dataFound
= false;
939 void* tempOutData
= nullptr;
940 uint32_t tempDataLen
= 0;
942 nsresult loadResult
= GetNativeDataOffClipboard(
943 inDataObject
, inIndex
, ::RegisterClipboardFormat(CFSTR_INETURLW
), nullptr,
944 &tempOutData
, &tempDataLen
);
945 if (NS_SUCCEEDED(loadResult
) && tempOutData
) {
946 nsDependentString
urlString(static_cast<char16_t
*>(tempOutData
));
947 // the internal mozilla URL format, text/x-moz-url, contains
948 // URL\ntitle. Since we don't actually have a title here,
949 // just repeat the URL to fake it.
950 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ urlString
);
952 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
956 loadResult
= GetNativeDataOffClipboard(
957 inDataObject
, inIndex
, ::RegisterClipboardFormat(CFSTR_INETURLA
),
958 nullptr, &tempOutData
, &tempDataLen
);
959 if (NS_SUCCEEDED(loadResult
) && tempOutData
) {
960 // CFSTR_INETURLA is (currently) equal to CFSTR_SHELLURL which is equal to
961 // CF_TEXT which is by definition ANSI encoded.
962 nsCString urlUnescapedA
;
964 NS_UnescapeURL(static_cast<char*>(tempOutData
), tempDataLen
,
965 esc_OnlyNonASCII
| esc_SkipControl
, urlUnescapedA
);
969 NS_CopyNativeToUnicode(urlUnescapedA
, urlString
);
971 NS_CopyNativeToUnicode(
972 nsDependentCString(static_cast<char*>(tempOutData
), tempDataLen
),
976 // the internal mozilla URL format, text/x-moz-url, contains
977 // URL\ntitle. Since we don't actually have a title here,
978 // just repeat the URL to fake it.
979 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ urlString
);
981 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
988 } // FindURLFromNativeURL
993 void nsClipboard ::ResolveShortcut(nsIFile
* aFile
, nsACString
& outURL
) {
994 nsCOMPtr
<nsIFileProtocolHandler
> fph
;
995 nsresult rv
= NS_GetFileProtocolHandler(getter_AddRefs(fph
));
1000 nsCOMPtr
<nsIURI
> uri
;
1001 rv
= fph
->ReadURLFile(aFile
, getter_AddRefs(uri
));
1002 if (NS_FAILED(rv
)) {
1006 uri
->GetSpec(outURL
);
1007 } // ResolveShortcut
1010 // IsInternetShortcut
1012 // A file is an Internet Shortcut if it ends with .URL
1014 bool nsClipboard ::IsInternetShortcut(const nsAString
& inFileName
) {
1015 return StringEndsWith(inFileName
, u
".url"_ns
,
1016 nsCaseInsensitiveStringComparator
);
1017 } // IsInternetShortcut
1019 //-------------------------------------------------------------------------
1021 nsClipboard::GetNativeClipboardData(nsITransferable
* aTransferable
,
1022 int32_t aWhichClipboard
) {
1023 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
1024 ("%s aWhichClipboard=%i", __FUNCTION__
, aWhichClipboard
));
1026 // make sure we have a good transferable
1027 if (!aTransferable
|| aWhichClipboard
!= kGlobalClipboard
) {
1028 return NS_ERROR_FAILURE
;
1033 // This makes sure we can use the OLE functionality for the clipboard
1034 IDataObject
* dataObj
;
1035 if (S_OK
== ::OleGetClipboard(&dataObj
)) {
1036 // Use OLE IDataObject for clipboard operations
1037 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Verbose
,
1038 ("%s: use OLE IDataObject.", __FUNCTION__
));
1039 res
= GetDataFromDataObject(dataObj
, 0, nullptr, aTransferable
);
1042 // do it the old manual way
1043 res
= GetDataFromDataObject(nullptr, 0, mWindow
, aTransferable
);
1049 nsClipboard::EmptyClipboard(int32_t aWhichClipboard
) {
1050 // Some programs such as ZoneAlarm monitor clipboard usage and then open the
1051 // clipboard to scan it. If we i) empty and then ii) set data, then the
1052 // 'set data' can sometimes fail with access denied becacuse another program
1053 // has the clipboard open. So to avoid this race condition for OpenClipboard
1054 // we do not empty the clipboard when we're setting it.
1055 if (aWhichClipboard
== kGlobalClipboard
&& !mEmptyingForSetData
) {
1056 OleSetClipboard(nullptr);
1058 return nsBaseClipboard::EmptyClipboard(aWhichClipboard
);
1061 //-------------------------------------------------------------------------
1062 NS_IMETHODIMP
nsClipboard::HasDataMatchingFlavors(
1063 const nsTArray
<nsCString
>& aFlavorList
, int32_t aWhichClipboard
,
1066 if (aWhichClipboard
!= kGlobalClipboard
) {
1070 for (auto& flavor
: aFlavorList
) {
1072 if (flavor
.EqualsLiteral(kTextMime
)) {
1074 "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode "
1079 UINT format
= GetFormat(flavor
.get());
1080 if (IsClipboardFormatAvailable(format
)) {
1084 // We haven't found the exact flavor the client asked for, but maybe we
1085 // can still find it from something else that's on the clipboard...
1086 if (flavor
.EqualsLiteral(kUnicodeMime
)) {
1087 // client asked for unicode and it wasn't present, check if we have
1088 // CF_TEXT. We'll handle the actual data substitution in the data
1090 if (IsClipboardFormatAvailable(GetFormat(kTextMime
))) {