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 //-------------------------------------------------------------------------
119 nsresult
nsClipboard::CreateNativeDataObject(nsITransferable
* aTransferable
,
120 IDataObject
** aDataObj
,
122 if (nullptr == aTransferable
) {
123 return NS_ERROR_FAILURE
;
126 // Create our native DataObject that implements
127 // the OLE IDataObject interface
128 nsDataObj
* dataObj
= new nsDataObj(uri
);
131 return NS_ERROR_OUT_OF_MEMORY
;
136 // Now set it up with all the right data flavors & enums
137 nsresult res
= SetupNativeDataObject(aTransferable
, dataObj
);
146 //-------------------------------------------------------------------------
147 nsresult
nsClipboard::SetupNativeDataObject(nsITransferable
* aTransferable
,
148 IDataObject
* aDataObj
) {
149 if (nullptr == aTransferable
|| nullptr == aDataObj
) {
150 return NS_ERROR_FAILURE
;
153 nsDataObj
* dObj
= static_cast<nsDataObj
*>(aDataObj
);
155 // Now give the Transferable to the DataObject
156 // for getting the data out of it
157 dObj
->SetTransferable(aTransferable
);
159 // Get the transferable list of data flavors
160 nsTArray
<nsCString
> flavors
;
161 aTransferable
->FlavorsTransferableCanExport(flavors
);
163 // Walk through flavors that contain data and register them
164 // into the DataObj as supported flavors
165 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
166 nsCString
& flavorStr
= flavors
[i
];
168 // When putting data onto the clipboard, we want to maintain kHTMLMime
169 // ("text/html") and not map it to CF_HTML here since this will be done
171 UINT format
= GetFormat(flavorStr
.get(), false);
173 // Now tell the native IDataObject about both our mime type and
174 // the native data format
176 SET_FORMATETC(fe
, format
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
);
177 dObj
->AddDataFlavor(flavorStr
.get(), &fe
);
179 // Do various things internal to the implementation, like map one
180 // flavor to another or add additional flavors based on what's required
181 // for the win32 impl.
182 if (flavorStr
.EqualsLiteral(kUnicodeMime
)) {
183 // if we find text/unicode, also advertise text/plain (which we will
184 // convert on our own in nsDataObj::GetText().
186 SET_FORMATETC(textFE
, CF_TEXT
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
);
187 dObj
->AddDataFlavor(kTextMime
, &textFE
);
188 } else if (flavorStr
.EqualsLiteral(kHTMLMime
)) {
189 // if we find text/html, also advertise win32's html flavor (which we will
190 // convert on our own in nsDataObj::GetText().
192 SET_FORMATETC(htmlFE
, GetHtmlClipboardFormat(), 0, DVASPECT_CONTENT
, -1,
194 dObj
->AddDataFlavor(kHTMLMime
, &htmlFE
);
195 } else if (flavorStr
.EqualsLiteral(kURLMime
)) {
196 // if we're a url, in addition to also being text, we need to register
197 // the "file" flavors so that the win32 shell knows to create an internet
198 // shortcut when it sees one of these beasts.
199 FORMATETC shortcutFE
;
200 SET_FORMATETC(shortcutFE
,
201 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA
), 0,
202 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
203 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
204 SET_FORMATETC(shortcutFE
,
205 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW
), 0,
206 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
207 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
208 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_FILECONTENTS
),
209 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
210 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
211 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_INETURLA
), 0,
212 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
213 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
214 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_INETURLW
), 0,
215 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
216 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
217 } else if (flavorStr
.EqualsLiteral(kPNGImageMime
) ||
218 flavorStr
.EqualsLiteral(kJPEGImageMime
) ||
219 flavorStr
.EqualsLiteral(kJPGImageMime
) ||
220 flavorStr
.EqualsLiteral(kGIFImageMime
) ||
221 flavorStr
.EqualsLiteral(kNativeImageMime
)) {
222 // if we're an image, register the native bitmap flavor
225 SET_FORMATETC(imageFE
, CF_DIBV5
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
226 dObj
->AddDataFlavor(flavorStr
.get(), &imageFE
);
228 SET_FORMATETC(imageFE
, CF_DIB
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
229 dObj
->AddDataFlavor(flavorStr
.get(), &imageFE
);
230 } else if (flavorStr
.EqualsLiteral(kFilePromiseMime
)) {
231 // if we're a file promise flavor, also register the
232 // CFSTR_PREFERREDDROPEFFECT format. The data object
233 // returns a value of DROPEFFECTS_MOVE to the drop target
234 // when it asks for the value of this format. This causes
235 // the file to be moved from the temporary location instead
236 // of being copied. The right thing to do here is to call
237 // SetData() on the data object and set the value of this format
238 // to DROPEFFECTS_MOVE on this particular data object. But,
239 // since all the other clipboard formats follow the model of setting
240 // data on the data object only when the drop object calls GetData(),
241 // I am leaving this format's value hard coded in the data object.
242 // We can change this if other consumers of this format get added to this
243 // codebase and they need different values.
244 FORMATETC shortcutFE
;
245 SET_FORMATETC(shortcutFE
,
246 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
), 0,
247 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
248 dObj
->AddDataFlavor(kFilePromiseMime
, &shortcutFE
);
256 void nsClipboard::OleSetClipboardResultToString(HRESULT aHres
,
257 nsACString
& aResult
) {
262 case CLIPBRD_E_CANT_OPEN
:
263 aResult
= "CLIPBRD_E_CANT_OPEN";
265 case CLIPBRD_E_CANT_EMPTY
:
266 aResult
= "CLIPBRD_E_CANT_EMPTY";
268 case CLIPBRD_E_CANT_CLOSE
:
269 aResult
= "CLIPBRD_E_CANT_CLOSE";
271 case CLIPBRD_E_CANT_SET
:
272 aResult
= "CLIPBRD_E_CANT_SET";
275 // Explicit template instantiaton, because otherwise the call is
277 constexpr int kRadix
= 16;
278 aResult
= IntToCString
<int32_t>(aHres
, kRadix
);
284 void nsClipboard::LogOleSetClipboardResult(const HRESULT aHres
) {
285 if (MOZ_LOG_TEST(gWin32ClipboardLog
, LogLevel::Debug
)) {
286 nsAutoCString hresString
;
287 OleSetClipboardResultToString(aHres
, hresString
);
288 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
289 ("OleSetClipboard result: %s", hresString
.get()));
293 //-------------------------------------------------------------------------
294 NS_IMETHODIMP
nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard
) {
295 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
297 if (aWhichClipboard
!= kGlobalClipboard
) {
298 return NS_ERROR_FAILURE
;
301 mIgnoreEmptyNotification
= true;
303 // make sure we have a good transferable
304 if (nullptr == mTransferable
) {
305 return NS_ERROR_FAILURE
;
308 IDataObject
* dataObj
;
309 if (NS_SUCCEEDED(CreateNativeDataObject(mTransferable
, &dataObj
,
310 nullptr))) { // this add refs dataObj
311 LogOleSetClipboardResult(::OleSetClipboard(dataObj
));
314 // Clear the native clipboard
315 LogOleSetClipboardResult(::OleSetClipboard(nullptr));
318 mIgnoreEmptyNotification
= false;
323 //-------------------------------------------------------------------------
324 nsresult
nsClipboard::GetGlobalData(HGLOBAL aHGBL
, void** aData
,
326 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Verbose
, ("%s", __FUNCTION__
));
328 // Allocate a new memory buffer and copy the data from global memory.
329 // Recall that win98 allocates to nearest DWORD boundary. As a safety
330 // precaution, allocate an extra 3 bytes (but don't report them in |aLen|!)
331 // and null them out to ensure that all of our NS_strlen calls will succeed.
332 // NS_strlen operates on char16_t, so we need 3 NUL bytes to ensure it finds
333 // a full NUL char16_t when |*aLen| is odd.
334 nsresult result
= NS_ERROR_FAILURE
;
335 if (aHGBL
!= nullptr) {
336 LPSTR lpStr
= (LPSTR
)GlobalLock(aHGBL
);
337 CheckedInt
<uint32_t> allocSize
=
338 CheckedInt
<uint32_t>(GlobalSize(aHGBL
)) + 3;
339 if (!allocSize
.isValid()) {
340 return NS_ERROR_INVALID_ARG
;
342 char* data
= static_cast<char*>(malloc(allocSize
.value()));
344 uint32_t size
= allocSize
.value() - 3;
345 memcpy(data
, lpStr
, size
);
346 // null terminate for safety
347 data
[size
] = data
[size
+ 1] = data
[size
+ 2] = '\0';
356 // We really shouldn't ever get here
363 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
, nullptr,
365 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
366 (LPWSTR
)&lpMsgBuf
, 0, nullptr);
368 // Display the string.
369 MessageBoxW(nullptr, (LPCWSTR
)lpMsgBuf
, L
"GetLastError",
370 MB_OK
| MB_ICONINFORMATION
);
379 //-------------------------------------------------------------------------
380 nsresult
nsClipboard::GetNativeDataOffClipboard(nsIWidget
* aWidget
,
381 UINT
/*aIndex*/, UINT aFormat
,
382 void** aData
, uint32_t* aLen
) {
383 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
384 ("%s: overload taking nsIWidget*.", __FUNCTION__
));
387 nsresult result
= NS_ERROR_FAILURE
;
389 HWND nativeWin
= nullptr;
390 if (::OpenClipboard(nativeWin
)) {
391 hglb
= ::GetClipboardData(aFormat
);
392 result
= GetGlobalData(hglb
, aData
, aLen
);
398 static void DisplayErrCode(HRESULT hres
) {
399 if (hres
== E_INVALIDARG
) {
400 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("E_INVALIDARG\n"));
401 } else if (hres
== E_UNEXPECTED
) {
402 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("E_UNEXPECTED\n"));
403 } else if (hres
== E_OUTOFMEMORY
) {
404 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("E_OUTOFMEMORY\n"));
405 } else if (hres
== DV_E_LINDEX
) {
406 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_LINDEX\n"));
407 } else if (hres
== DV_E_FORMATETC
) {
408 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_FORMATETC\n"));
409 } else if (hres
== DV_E_TYMED
) {
410 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_TYMED\n"));
411 } else if (hres
== DV_E_DVASPECT
) {
412 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_DVASPECT\n"));
413 } else if (hres
== OLE_E_NOTRUNNING
) {
414 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("OLE_E_NOTRUNNING\n"));
415 } else if (hres
== STG_E_MEDIUMFULL
) {
416 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("STG_E_MEDIUMFULL\n"));
417 } else if (hres
== DV_E_CLIPFORMAT
) {
418 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("DV_E_CLIPFORMAT\n"));
419 } else if (hres
== S_OK
) {
420 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
, ("S_OK\n"));
422 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
,
423 ("****** DisplayErrCode 0x%X\n", hres
));
427 //-------------------------------------------------------------------------
428 static HRESULT
FillSTGMedium(IDataObject
* aDataObject
, UINT aFormat
,
429 LPFORMATETC pFE
, LPSTGMEDIUM pSTM
, DWORD aTymed
) {
430 SET_FORMATETC(*pFE
, aFormat
, 0, DVASPECT_CONTENT
, -1, aTymed
);
432 // Starting by querying for the data to see if we can get it as from global
434 HRESULT hres
= S_FALSE
;
435 hres
= aDataObject
->QueryGetData(pFE
);
436 DisplayErrCode(hres
);
438 hres
= aDataObject
->GetData(pFE
, pSTM
);
439 DisplayErrCode(hres
);
444 //-------------------------------------------------------------------------
445 // If aFormat is CF_DIBV5, aMIMEImageFormat must be a type for which we have
446 // an image encoder (e.g. image/png).
447 // For other values of aFormat, it is OK to pass null for aMIMEImageFormat.
448 nsresult
nsClipboard::GetNativeDataOffClipboard(IDataObject
* aDataObject
,
449 UINT aIndex
, UINT aFormat
,
450 const char* aMIMEImageFormat
,
451 void** aData
, uint32_t* aLen
) {
452 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
453 ("%s: overload taking IDataObject*.", __FUNCTION__
));
455 nsresult result
= NS_ERROR_FAILURE
;
463 UINT format
= aFormat
;
464 HRESULT hres
= S_FALSE
;
466 // XXX at the moment we only support global memory transfers
467 // It is here where we will add support for native images
471 hres
= FillSTGMedium(aDataObject
, format
, &fe
, &stm
, TYMED_HGLOBAL
);
473 // Currently this is only handling TYMED_HGLOBAL data
474 // For Text, Dibs, Files, and generic data (like HTML)
476 static CLIPFORMAT fileDescriptorFlavorA
=
477 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA
);
478 static CLIPFORMAT fileDescriptorFlavorW
=
479 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW
);
480 static CLIPFORMAT fileFlavor
=
481 ::RegisterClipboardFormat(CFSTR_FILECONTENTS
);
482 static CLIPFORMAT preferredDropEffect
=
483 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
);
486 case TYMED_HGLOBAL
: {
487 switch (fe
.cfFormat
) {
489 // Get the data out of the global data handle. The size we
490 // return should not include the null because the other
491 // platforms don't use nulls, so just return the length we get
492 // back from strlen(), since we know CF_TEXT is null
493 // terminated. Recall that GetGlobalData() returns the size of
494 // the allocated buffer, not the size of the data (on 98, these
495 // are not the same) so we can't use that.
496 uint32_t allocLen
= 0;
497 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
498 *aLen
= strlen(reinterpret_cast<char*>(*aData
));
503 case CF_UNICODETEXT
: {
504 // Get the data out of the global data handle. The size we
505 // return should not include the null because the other
506 // platforms don't use nulls, so just return the length we get
507 // back from strlen(), since we know CF_UNICODETEXT is null
508 // terminated. Recall that GetGlobalData() returns the size of
509 // the allocated buffer, not the size of the data (on 98, these
510 // are not the same) so we can't use that.
511 uint32_t allocLen
= 0;
512 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
513 *aLen
= NS_strlen(reinterpret_cast<char16_t
*>(*aData
)) * 2;
519 if (aMIMEImageFormat
) {
520 uint32_t allocLen
= 0;
521 const char* clipboardData
;
522 if (NS_SUCCEEDED(GetGlobalData(
523 stm
.hGlobal
, (void**)&clipboardData
, &allocLen
))) {
524 nsCOMPtr
<imgIContainer
> container
;
525 nsCOMPtr
<imgITools
> imgTools
=
526 do_CreateInstance("@mozilla.org/image/tools;1");
527 result
= imgTools
->DecodeImageFromBuffer(
528 clipboardData
, allocLen
,
529 nsLiteralCString(IMAGE_BMP_MS_CLIPBOARD
),
530 getter_AddRefs(container
));
531 if (NS_FAILED(result
)) {
535 nsAutoCString mimeType
;
536 if (strcmp(aMIMEImageFormat
, kJPGImageMime
) == 0) {
537 mimeType
.Assign(IMAGE_JPEG
);
539 mimeType
.Assign(aMIMEImageFormat
);
542 nsCOMPtr
<nsIInputStream
> inputStream
;
543 result
= imgTools
->EncodeImage(container
, mimeType
, u
""_ns
,
544 getter_AddRefs(inputStream
));
545 if (NS_FAILED(result
)) {
550 result
= NS_ERROR_FAILURE
;
554 *aData
= inputStream
.forget().take();
555 *aLen
= sizeof(nsIInputStream
*);
561 // in the case of a file drop, multiple files are stashed within a
562 // single data object. In order to match mozilla's D&D apis, we
563 // just pull out the file at the requested index, pretending as
564 // if there really are multiple drag items.
565 HDROP dropFiles
= (HDROP
)GlobalLock(stm
.hGlobal
);
567 UINT numFiles
= ::DragQueryFileW(dropFiles
, 0xFFFFFFFF, nullptr, 0);
568 NS_ASSERTION(numFiles
> 0,
569 "File drop flavor, but no files...hmmmm");
570 NS_ASSERTION(aIndex
< numFiles
,
571 "Asked for a file index out of range of list");
574 ::DragQueryFileW(dropFiles
, aIndex
, nullptr, 0);
575 wchar_t* buffer
= reinterpret_cast<wchar_t*>(
576 moz_xmalloc((fileNameLen
+ 1) * sizeof(wchar_t)));
577 ::DragQueryFileW(dropFiles
, aIndex
, buffer
, fileNameLen
+ 1);
579 *aLen
= fileNameLen
* sizeof(char16_t
);
582 GlobalUnlock(stm
.hGlobal
);
587 if (fe
.cfFormat
== fileDescriptorFlavorA
||
588 fe
.cfFormat
== fileDescriptorFlavorW
||
589 fe
.cfFormat
== fileFlavor
) {
591 "Mozilla doesn't yet understand how to read this type of "
594 // Get the data out of the global data handle. The size we
595 // return should not include the null because the other
596 // platforms don't use nulls, so just return the length we get
597 // back from strlen(), since we know CF_UNICODETEXT is null
598 // terminated. Recall that GetGlobalData() returns the size of
599 // the allocated buffer, not the size of the data (on 98, these
600 // are not the same) so we can't use that.
602 // NOTE: we are assuming that anything that falls into this
603 // default case is unicode. As we start to get more
604 // kinds of binary data, this may become an incorrect
605 // assumption. Stay tuned.
606 uint32_t allocLen
= 0;
607 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
608 if (fe
.cfFormat
== GetHtmlClipboardFormat()) {
609 // CF_HTML is actually UTF8, not unicode, so disregard the
610 // assumption above. We have to check the header for the
611 // actual length, and we'll do that in FindPlatformHTML().
612 // For now, return the allocLen. This case is mostly to
613 // ensure we don't try to call strlen on the buffer.
615 } else if (fe
.cfFormat
== GetCustomClipboardFormat()) {
618 } else if (fe
.cfFormat
== preferredDropEffect
) {
619 // As per the MSDN doc entitled: "Shell Clipboard Formats"
620 // CFSTR_PREFERREDDROPEFFECT should return a DWORD
622 // http://msdn.microsoft.com/en-us/library/bb776902(v=vs.85).aspx
624 allocLen
== sizeof(DWORD
),
625 "CFSTR_PREFERREDDROPEFFECT should return a DWORD");
628 *aLen
= NS_strlen(reinterpret_cast<char16_t
*>(*aData
)) *
640 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
,
641 ("*********************** TYMED_GDI\n"));
649 ReleaseStgMedium(&stm
);
655 //-------------------------------------------------------------------------
656 nsresult
nsClipboard::GetDataFromDataObject(IDataObject
* aDataObject
,
657 UINT anIndex
, nsIWidget
* aWindow
,
658 nsITransferable
* aTransferable
) {
659 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
661 // make sure we have a good transferable
662 if (!aTransferable
) {
663 return NS_ERROR_INVALID_ARG
;
666 nsresult res
= NS_ERROR_FAILURE
;
668 // get flavor list that includes all flavors that can be written (including
669 // ones obtained through conversion)
670 nsTArray
<nsCString
> flavors
;
671 res
= aTransferable
->FlavorsTransferableCanImport(flavors
);
672 if (NS_FAILED(res
)) {
673 return NS_ERROR_FAILURE
;
676 // Walk through flavors and see which flavor is on the clipboard them on the
678 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
679 nsCString
& flavorStr
= flavors
[i
];
680 UINT format
= GetFormat(flavorStr
.get());
682 // Try to get the data using the desired flavor. This might fail, but all is
684 void* data
= nullptr;
685 uint32_t dataLen
= 0;
686 bool dataFound
= false;
687 if (nullptr != aDataObject
) {
688 if (NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject
, anIndex
, format
,
689 flavorStr
.get(), &data
,
693 } else if (nullptr != aWindow
) {
694 if (NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow
, anIndex
, format
,
700 // This is our second chance to try to find some data, having not found it
701 // when directly asking for the flavor. Let's try digging around in other
702 // flavors to help satisfy our craving for data.
704 if (flavorStr
.EqualsLiteral(kUnicodeMime
)) {
706 FindUnicodeFromPlainText(aDataObject
, anIndex
, &data
, &dataLen
);
707 } else if (flavorStr
.EqualsLiteral(kURLMime
)) {
708 // drags from other windows apps expose the native
709 // CFSTR_INETURL{A,W} flavor
710 dataFound
= FindURLFromNativeURL(aDataObject
, anIndex
, &data
, &dataLen
);
713 FindURLFromLocalFile(aDataObject
, anIndex
, &data
, &dataLen
);
716 } // if we try one last ditch effort to find our data
718 // Hopefully by this point we've found it and can go about our business
720 nsCOMPtr
<nsISupports
> genericDataWrapper
;
721 if (flavorStr
.EqualsLiteral(kFileMime
)) {
722 // we have a file path in |data|. Create an nsLocalFile object.
723 nsDependentString
filepath(reinterpret_cast<char16_t
*>(data
));
724 nsCOMPtr
<nsIFile
> file
;
726 NS_NewLocalFile(filepath
, false, getter_AddRefs(file
)))) {
727 genericDataWrapper
= do_QueryInterface(file
);
730 } else if (flavorStr
.EqualsLiteral(kNativeHTMLMime
)) {
732 // the editor folks want CF_HTML exactly as it's on the clipboard, no
733 // conversions, no fancy stuff. Pull it off the clipboard, stuff it into
734 // a wrapper and hand it back to them.
735 if (FindPlatformHTML(aDataObject
, anIndex
, &data
, &dummy
, &dataLen
)) {
736 nsPrimitiveHelpers::CreatePrimitiveForData(
737 flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
));
740 continue; // something wrong with this flavor, keep looking for other
744 } else if (flavorStr
.EqualsLiteral(kHTMLMime
)) {
745 uint32_t startOfData
= 0;
746 // The JS folks want CF_HTML exactly as it is on the clipboard, but
747 // minus the CF_HTML header index information.
748 // It also needs to be converted to UTF16 and have linebreaks changed.
749 if (FindPlatformHTML(aDataObject
, anIndex
, &data
, &startOfData
,
751 dataLen
-= startOfData
;
752 nsPrimitiveHelpers::CreatePrimitiveForCFHTML(
753 static_cast<char*>(data
) + startOfData
, &dataLen
,
754 getter_AddRefs(genericDataWrapper
));
757 continue; // something wrong with this flavor, keep looking for other
761 } else if (flavorStr
.EqualsLiteral(kJPEGImageMime
) ||
762 flavorStr
.EqualsLiteral(kJPGImageMime
) ||
763 flavorStr
.EqualsLiteral(kPNGImageMime
)) {
764 nsIInputStream
* imageStream
= reinterpret_cast<nsIInputStream
*>(data
);
765 genericDataWrapper
= do_QueryInterface(imageStream
);
766 NS_IF_RELEASE(imageStream
);
768 // Treat custom types as a string of bytes.
769 if (!flavorStr
.EqualsLiteral(kCustomTypesMime
)) {
770 // we probably have some form of text. The DOM only wants LF, so
771 // convert from Win32 line endings to DOM line endings.
772 int32_t signedLen
= static_cast<int32_t>(dataLen
);
773 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr
, &data
,
777 if (flavorStr
.EqualsLiteral(kRTFMime
)) {
778 // RTF on Windows is known to sometimes deliver an extra null byte.
779 if (dataLen
> 0 && static_cast<char*>(data
)[dataLen
- 1] == '\0') {
785 nsPrimitiveHelpers::CreatePrimitiveForData(
786 flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
));
790 NS_ASSERTION(genericDataWrapper
,
791 "About to put null data into the transferable");
792 aTransferable
->SetTransferData(flavorStr
.get(), genericDataWrapper
);
795 // we found one, get out of the loop
806 // Someone asked for the OS CF_HTML flavor. We give it back to them exactly
809 bool nsClipboard ::FindPlatformHTML(IDataObject
* inDataObject
, UINT inIndex
,
810 void** outData
, uint32_t* outStartOfData
,
811 uint32_t* outDataLen
) {
812 // Reference: MSDN doc entitled "HTML Clipboard Format"
813 // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
814 // CF_HTML is UTF8, not unicode. We also can't rely on it being
815 // null-terminated so we have to check the CF_HTML header for the correct
816 // length. The length we return is the bytecount from the beginning of the
817 // selected data to the end of the selected data, without the null
818 // termination. Because it's UTF8, we're guaranteed the header is ASCII.
820 if (!outData
|| !*outData
) {
824 char version
[8] = {0};
825 int32_t startOfData
= 0;
826 int32_t endOfData
= 0;
828 sscanf((char*)*outData
, "Version:%7s\nStartHTML:%d\nEndHTML:%d", version
,
829 &startOfData
, &endOfData
);
831 if (numFound
!= 3 || startOfData
< -1 || endOfData
< -1) {
835 // Fixup the start and end markers if they have no context (set to -1)
836 if (startOfData
== -1) {
839 if (endOfData
== -1) {
840 endOfData
= *outDataLen
;
843 // Make sure we were passed sane values within our buffer size.
844 // (Note that we've handled all cases of negative endOfData above, so we can
845 // safely cast it to be unsigned here.)
846 if (!endOfData
|| startOfData
>= endOfData
||
847 static_cast<uint32_t>(endOfData
) > *outDataLen
) {
851 // We want to return the buffer not offset by startOfData because it will be
852 // parsed out later (probably by HTMLEditor::ParseCFHTML) when it is still
853 // in CF_HTML format.
855 // We return the byte offset from the start of the data buffer to where the
856 // HTML data starts. The caller might want to extract the HTML only.
857 *outStartOfData
= startOfData
;
858 *outDataLen
= endOfData
;
863 // FindUnicodeFromPlainText
865 // we are looking for text/unicode and we failed to find it on the clipboard
866 // first, try again with text/plain. If that is present, convert it to unicode.
868 bool nsClipboard ::FindUnicodeFromPlainText(IDataObject
* inDataObject
,
869 UINT inIndex
, void** outData
,
870 uint32_t* outDataLen
) {
871 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
873 // we are looking for text/unicode and we failed to find it on the clipboard
874 // first, try again with text/plain. If that is present, convert it to
877 GetNativeDataOffClipboard(inDataObject
, inIndex
, GetFormat(kTextMime
),
878 nullptr, outData
, outDataLen
);
879 if (NS_FAILED(rv
) || !*outData
) {
883 const char* castedText
= static_cast<char*>(*outData
);
885 rv
= NS_CopyNativeToUnicode(nsDependentCSubstring(castedText
, *outDataLen
),
891 // out with the old, in with the new
893 *outData
= ToNewUnicode(tmp
);
894 *outDataLen
= tmp
.Length() * sizeof(char16_t
);
898 } // FindUnicodeFromPlainText
901 // FindURLFromLocalFile
903 // we are looking for a URL and couldn't find it, try again with looking for
904 // a local file. If we have one, it may either be a normal file or an internet
905 // shortcut. In both cases, however, we can get a URL (it will be a file:// url
906 // in the local file case).
908 bool nsClipboard ::FindURLFromLocalFile(IDataObject
* inDataObject
, UINT inIndex
,
909 void** outData
, uint32_t* outDataLen
) {
910 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
912 bool dataFound
= false;
914 nsresult loadResult
=
915 GetNativeDataOffClipboard(inDataObject
, inIndex
, GetFormat(kFileMime
),
916 nullptr, outData
, outDataLen
);
917 if (NS_SUCCEEDED(loadResult
) && *outData
) {
918 // we have a file path in |data|. Is it an internet shortcut or a normal
920 const nsDependentString
filepath(static_cast<char16_t
*>(*outData
));
921 nsCOMPtr
<nsIFile
> file
;
922 nsresult rv
= NS_NewLocalFile(filepath
, true, getter_AddRefs(file
));
928 if (IsInternetShortcut(filepath
)) {
931 ResolveShortcut(file
, url
);
932 if (!url
.IsEmpty()) {
933 // convert it to unicode and pass it out
934 NS_ConvertUTF8toUTF16
urlString(url
);
935 // the internal mozilla URL format, text/x-moz-url, contains
936 // URL\ntitle. We can guess the title from the file's name.
938 file
->GetLeafName(title
);
939 // We rely on IsInternetShortcut check that file has a .url extension.
940 title
.SetLength(title
.Length() - 4);
941 if (title
.IsEmpty()) {
944 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ title
);
946 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
951 // we have a normal file, use some Necko objects to get our file path
952 nsAutoCString urlSpec
;
953 NS_GetURLSpecFromFile(file
, urlSpec
);
955 // convert it to unicode and pass it out
957 *outData
= UTF8ToNewUnicode(urlSpec
);
959 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
961 } // else regular file
965 } // FindURLFromLocalFile
968 // FindURLFromNativeURL
970 // we are looking for a URL and couldn't find it using our internal
971 // URL flavor, so look for it using the native URL flavor,
972 // CF_INETURLSTRW (We don't handle CF_INETURLSTRA currently)
974 bool nsClipboard ::FindURLFromNativeURL(IDataObject
* inDataObject
, UINT inIndex
,
975 void** outData
, uint32_t* outDataLen
) {
976 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
978 bool dataFound
= false;
980 void* tempOutData
= nullptr;
981 uint32_t tempDataLen
= 0;
983 nsresult loadResult
= GetNativeDataOffClipboard(
984 inDataObject
, inIndex
, ::RegisterClipboardFormat(CFSTR_INETURLW
), nullptr,
985 &tempOutData
, &tempDataLen
);
986 if (NS_SUCCEEDED(loadResult
) && tempOutData
) {
987 nsDependentString
urlString(static_cast<char16_t
*>(tempOutData
));
988 // the internal mozilla URL format, text/x-moz-url, contains
989 // URL\ntitle. Since we don't actually have a title here,
990 // just repeat the URL to fake it.
991 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ urlString
);
993 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
997 loadResult
= GetNativeDataOffClipboard(
998 inDataObject
, inIndex
, ::RegisterClipboardFormat(CFSTR_INETURLA
),
999 nullptr, &tempOutData
, &tempDataLen
);
1000 if (NS_SUCCEEDED(loadResult
) && tempOutData
) {
1001 // CFSTR_INETURLA is (currently) equal to CFSTR_SHELLURL which is equal to
1002 // CF_TEXT which is by definition ANSI encoded.
1003 nsCString urlUnescapedA
;
1005 NS_UnescapeURL(static_cast<char*>(tempOutData
), tempDataLen
,
1006 esc_OnlyNonASCII
| esc_SkipControl
, urlUnescapedA
);
1010 NS_CopyNativeToUnicode(urlUnescapedA
, urlString
);
1012 NS_CopyNativeToUnicode(
1013 nsDependentCString(static_cast<char*>(tempOutData
), tempDataLen
),
1017 // the internal mozilla URL format, text/x-moz-url, contains
1018 // URL\ntitle. Since we don't actually have a title here,
1019 // just repeat the URL to fake it.
1020 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ urlString
);
1022 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
1029 } // FindURLFromNativeURL
1034 void nsClipboard ::ResolveShortcut(nsIFile
* aFile
, nsACString
& outURL
) {
1035 nsCOMPtr
<nsIFileProtocolHandler
> fph
;
1036 nsresult rv
= NS_GetFileProtocolHandler(getter_AddRefs(fph
));
1037 if (NS_FAILED(rv
)) {
1041 nsCOMPtr
<nsIURI
> uri
;
1042 rv
= fph
->ReadURLFile(aFile
, getter_AddRefs(uri
));
1043 if (NS_FAILED(rv
)) {
1047 uri
->GetSpec(outURL
);
1048 } // ResolveShortcut
1051 // IsInternetShortcut
1053 // A file is an Internet Shortcut if it ends with .URL
1055 bool nsClipboard ::IsInternetShortcut(const nsAString
& inFileName
) {
1056 return StringEndsWith(inFileName
, u
".url"_ns
,
1057 nsCaseInsensitiveStringComparator
);
1058 } // IsInternetShortcut
1060 //-------------------------------------------------------------------------
1062 nsClipboard::GetNativeClipboardData(nsITransferable
* aTransferable
,
1063 int32_t aWhichClipboard
) {
1064 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
1065 ("%s aWhichClipboard=%i", __FUNCTION__
, aWhichClipboard
));
1067 // make sure we have a good transferable
1068 if (!aTransferable
|| aWhichClipboard
!= kGlobalClipboard
) {
1069 return NS_ERROR_FAILURE
;
1074 // This makes sure we can use the OLE functionality for the clipboard
1075 IDataObject
* dataObj
;
1076 if (S_OK
== ::OleGetClipboard(&dataObj
)) {
1077 // Use OLE IDataObject for clipboard operations
1078 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Verbose
,
1079 ("%s: use OLE IDataObject.", __FUNCTION__
));
1080 res
= GetDataFromDataObject(dataObj
, 0, nullptr, aTransferable
);
1083 // do it the old manual way
1084 res
= GetDataFromDataObject(nullptr, 0, mWindow
, aTransferable
);
1090 nsClipboard::EmptyClipboard(int32_t aWhichClipboard
) {
1091 // Some programs such as ZoneAlarm monitor clipboard usage and then open the
1092 // clipboard to scan it. If we i) empty and then ii) set data, then the
1093 // 'set data' can sometimes fail with access denied becacuse another program
1094 // has the clipboard open. So to avoid this race condition for OpenClipboard
1095 // we do not empty the clipboard when we're setting it.
1096 if (aWhichClipboard
== kGlobalClipboard
&& !mEmptyingForSetData
) {
1097 LogOleSetClipboardResult(::OleSetClipboard(nullptr));
1099 return nsBaseClipboard::EmptyClipboard(aWhichClipboard
);
1102 //-------------------------------------------------------------------------
1103 NS_IMETHODIMP
nsClipboard::HasDataMatchingFlavors(
1104 const nsTArray
<nsCString
>& aFlavorList
, int32_t aWhichClipboard
,
1107 if (aWhichClipboard
!= kGlobalClipboard
) {
1111 for (auto& flavor
: aFlavorList
) {
1113 if (flavor
.EqualsLiteral(kTextMime
)) {
1115 "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode "
1120 UINT format
= GetFormat(flavor
.get());
1121 if (IsClipboardFormatAvailable(format
)) {
1125 // We haven't found the exact flavor the client asked for, but maybe we
1126 // can still find it from something else that's on the clipboard...
1127 if (flavor
.EqualsLiteral(kUnicodeMime
)) {
1128 // client asked for unicode and it wasn't present, check if we have
1129 // CF_TEXT. We'll handle the actual data substitution in the data
1131 if (IsClipboardFormatAvailable(GetFormat(kTextMime
))) {