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
19 # include "mozilla/a11y/Compatibility.h"
21 #include "mozilla/Logging.h"
22 #include "mozilla/StaticPrefs_clipboard.h"
23 #include "nsArrayUtils.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsDataObj.h"
28 #include "nsNativeCharsetUtils.h"
29 #include "nsIInputStream.h"
30 #include "nsITransferable.h"
32 #include "nsReadableUtils.h"
33 #include "nsUnicharUtils.h"
34 #include "nsPrimitiveHelpers.h"
35 #include "nsIWidget.h"
36 #include "nsWidgetsCID.h"
38 #include "nsNetUtil.h"
39 #include "nsIFileProtocolHandler.h"
41 #include "nsIObserverService.h"
42 #include "nsMimeTypes.h"
43 #include "imgITools.h"
45 using mozilla::LogLevel
;
47 static mozilla::LazyLogModule
gWin32ClipboardLog("nsClipboard");
50 UINT
nsClipboard::GetHtmlClipboardFormat() {
51 static UINT format
= ::RegisterClipboardFormatW(L
"HTML Format");
56 UINT
nsClipboard::GetCustomClipboardFormat() {
58 ::RegisterClipboardFormatW(L
"application/x-moz-custom-clipdata");
62 //-------------------------------------------------------------------------
64 // nsClipboard constructor
66 //-------------------------------------------------------------------------
67 nsClipboard::nsClipboard() : nsBaseClipboard() {
70 // Register for a shutdown notification so that we can flush data
71 // to the OS clipboard.
72 nsCOMPtr
<nsIObserverService
> observerService
=
73 do_GetService("@mozilla.org/observer-service;1");
74 if (observerService
) {
75 observerService
->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID
,
80 //-------------------------------------------------------------------------
81 // nsClipboard destructor
82 //-------------------------------------------------------------------------
83 nsClipboard::~nsClipboard() {}
85 NS_IMPL_ISUPPORTS_INHERITED(nsClipboard
, nsBaseClipboard
, nsIObserver
)
88 nsClipboard::Observe(nsISupports
* aSubject
, const char* aTopic
,
89 const char16_t
* aData
) {
90 // This will be called on shutdown.
91 ::OleFlushClipboard();
97 //-------------------------------------------------------------------------
98 UINT
nsClipboard::GetFormat(const char* aMimeStr
, bool aMapHTMLMime
) {
101 if (strcmp(aMimeStr
, kTextMime
) == 0) {
102 format
= CF_UNICODETEXT
;
103 } else if (strcmp(aMimeStr
, kRTFMime
) == 0) {
104 format
= ::RegisterClipboardFormat(L
"Rich Text Format");
105 } else if (strcmp(aMimeStr
, kJPEGImageMime
) == 0 ||
106 strcmp(aMimeStr
, kJPGImageMime
) == 0 ||
107 strcmp(aMimeStr
, kPNGImageMime
) == 0) {
109 } else if (strcmp(aMimeStr
, kFileMime
) == 0 ||
110 strcmp(aMimeStr
, kFilePromiseMime
) == 0) {
112 } else if ((strcmp(aMimeStr
, kNativeHTMLMime
) == 0) ||
113 (aMapHTMLMime
&& strcmp(aMimeStr
, kHTMLMime
) == 0)) {
114 format
= GetHtmlClipboardFormat();
115 } else if (strcmp(aMimeStr
, kCustomTypesMime
) == 0) {
116 format
= GetCustomClipboardFormat();
118 format
= ::RegisterClipboardFormatW(NS_ConvertASCIItoUTF16(aMimeStr
).get());
124 //-------------------------------------------------------------------------
126 nsresult
nsClipboard::CreateNativeDataObject(
127 nsITransferable
* aTransferable
, IDataObject
** aDataObj
, nsIURI
* aUri
,
128 MightNeedToFlush
* aMightNeedToFlush
) {
129 MOZ_ASSERT(aTransferable
);
130 if (!aTransferable
) {
131 return NS_ERROR_FAILURE
;
134 // Create our native DataObject that implements the OLE IDataObject interface
135 RefPtr
<nsDataObj
> dataObj
= new nsDataObj(aUri
);
137 // Now set it up with all the right data flavors & enums
139 SetupNativeDataObject(aTransferable
, dataObj
, aMightNeedToFlush
);
140 if (NS_SUCCEEDED(res
)) {
141 dataObj
.forget(aDataObj
);
146 static nsresult
StoreValueInDataObject(nsDataObj
* aObj
,
147 LPCWSTR aClipboardFormat
, DWORD value
) {
148 HGLOBAL hGlobalMemory
= ::GlobalAlloc(GMEM_MOVEABLE
, sizeof(DWORD
));
149 if (!hGlobalMemory
) {
150 return NS_ERROR_OUT_OF_MEMORY
;
152 DWORD
* pdw
= (DWORD
*)::GlobalLock(hGlobalMemory
);
154 ::GlobalUnlock(hGlobalMemory
);
157 stg
.tymed
= TYMED_HGLOBAL
;
158 stg
.pUnkForRelease
= nullptr;
159 stg
.hGlobal
= hGlobalMemory
;
162 SET_FORMATETC(fe
, ::RegisterClipboardFormat(aClipboardFormat
), 0,
163 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
164 aObj
->SetData(&fe
, &stg
, TRUE
);
169 //-------------------------------------------------------------------------
170 nsresult
nsClipboard::SetupNativeDataObject(
171 nsITransferable
* aTransferable
, IDataObject
* aDataObj
,
172 MightNeedToFlush
* aMightNeedToFlush
) {
173 MOZ_ASSERT(aTransferable
);
174 MOZ_ASSERT(aDataObj
);
175 if (!aTransferable
|| !aDataObj
) {
176 return NS_ERROR_FAILURE
;
179 auto* dObj
= static_cast<nsDataObj
*>(aDataObj
);
180 if (aMightNeedToFlush
) {
181 *aMightNeedToFlush
= MightNeedToFlush::No
;
184 // Now give the Transferable to the DataObject
185 // for getting the data out of it
186 dObj
->SetTransferable(aTransferable
);
188 // Get the transferable list of data flavors
189 nsTArray
<nsCString
> flavors
;
190 aTransferable
->FlavorsTransferableCanExport(flavors
);
192 // Walk through flavors that contain data and register them
193 // into the DataObj as supported flavors
194 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
195 nsCString
& flavorStr
= flavors
[i
];
197 // When putting data onto the clipboard, we want to maintain kHTMLMime
198 // ("text/html") and not map it to CF_HTML here since this will be done
200 UINT format
= GetFormat(flavorStr
.get(), false);
202 // Now tell the native IDataObject about both our mime type and
203 // the native data format
205 SET_FORMATETC(fe
, format
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
);
206 dObj
->AddDataFlavor(flavorStr
.get(), &fe
);
208 // Do various things internal to the implementation, like map one
209 // flavor to another or add additional flavors based on what's required
210 // for the win32 impl.
211 if (flavorStr
.EqualsLiteral(kTextMime
)) {
212 // if we find text/plain, also add CF_TEXT, but we can add it for
213 // text/plain as well.
215 SET_FORMATETC(textFE
, CF_TEXT
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
);
216 dObj
->AddDataFlavor(kTextMime
, &textFE
);
217 if (aMightNeedToFlush
) {
218 *aMightNeedToFlush
= MightNeedToFlush::Yes
;
220 } else if (flavorStr
.EqualsLiteral(kHTMLMime
)) {
221 // if we find text/html, also advertise win32's html flavor (which we will
222 // convert on our own in nsDataObj::GetText().
224 SET_FORMATETC(htmlFE
, GetHtmlClipboardFormat(), 0, DVASPECT_CONTENT
, -1,
226 dObj
->AddDataFlavor(kHTMLMime
, &htmlFE
);
227 } else if (flavorStr
.EqualsLiteral(kURLMime
)) {
228 // if we're a url, in addition to also being text, we need to register
229 // the "file" flavors so that the win32 shell knows to create an internet
230 // shortcut when it sees one of these beasts.
231 FORMATETC shortcutFE
;
232 SET_FORMATETC(shortcutFE
,
233 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA
), 0,
234 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
235 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
236 SET_FORMATETC(shortcutFE
,
237 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW
), 0,
238 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
239 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
240 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_FILECONTENTS
),
241 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
242 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
243 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_INETURLA
), 0,
244 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
245 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
246 SET_FORMATETC(shortcutFE
, ::RegisterClipboardFormat(CFSTR_INETURLW
), 0,
247 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
248 dObj
->AddDataFlavor(kURLMime
, &shortcutFE
);
249 } else if (flavorStr
.EqualsLiteral(kPNGImageMime
) ||
250 flavorStr
.EqualsLiteral(kJPEGImageMime
) ||
251 flavorStr
.EqualsLiteral(kJPGImageMime
) ||
252 flavorStr
.EqualsLiteral(kGIFImageMime
) ||
253 flavorStr
.EqualsLiteral(kNativeImageMime
)) {
254 // if we're an image, register the native bitmap flavor
257 SET_FORMATETC(imageFE
, CF_DIBV5
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
258 dObj
->AddDataFlavor(flavorStr
.get(), &imageFE
);
260 SET_FORMATETC(imageFE
, CF_DIB
, 0, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
261 dObj
->AddDataFlavor(flavorStr
.get(), &imageFE
);
262 } else if (flavorStr
.EqualsLiteral(kFilePromiseMime
)) {
263 // if we're a file promise flavor, also register the
264 // CFSTR_PREFERREDDROPEFFECT format. The data object
265 // returns a value of DROPEFFECTS_MOVE to the drop target
266 // when it asks for the value of this format. This causes
267 // the file to be moved from the temporary location instead
268 // of being copied. The right thing to do here is to call
269 // SetData() on the data object and set the value of this format
270 // to DROPEFFECTS_MOVE on this particular data object. But,
271 // since all the other clipboard formats follow the model of setting
272 // data on the data object only when the drop object calls GetData(),
273 // I am leaving this format's value hard coded in the data object.
274 // We can change this if other consumers of this format get added to this
275 // codebase and they need different values.
276 FORMATETC shortcutFE
;
277 SET_FORMATETC(shortcutFE
,
278 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
), 0,
279 DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
)
280 dObj
->AddDataFlavor(kFilePromiseMime
, &shortcutFE
);
284 if (!StaticPrefs::clipboard_copyPrivateDataToClipboardCloudOrHistory()) {
285 // Let Clipboard know that data is sensitive and must not be copied to
286 // the Cloud Clipboard, Clipboard History and similar.
287 // https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats#cloud-clipboard-and-clipboard-history-formats
288 if (aTransferable
->GetIsPrivateData()) {
290 StoreValueInDataObject(dObj
, TEXT("CanUploadToCloudClipboard"), 0);
291 NS_ENSURE_SUCCESS(rv
, rv
);
293 StoreValueInDataObject(dObj
, TEXT("CanIncludeInClipboardHistory"), 0);
294 NS_ENSURE_SUCCESS(rv
, rv
);
295 rv
= StoreValueInDataObject(
296 dObj
, TEXT("ExcludeClipboardContentFromMonitorProcessing"), 0);
297 NS_ENSURE_SUCCESS(rv
, rv
);
304 // See methods listed at
305 // <https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-idataobject#methods>.
306 static void IDataObjectMethodResultToString(const HRESULT aHres
,
307 nsACString
& aResult
) {
310 aResult
= "E_INVALIDARG";
313 aResult
= "E_UNEXPECTED";
316 aResult
= "E_OUTOFMEMORY";
319 aResult
= "DV_E_LINDEX";
322 aResult
= "DV_E_FORMATETC";
325 aResult
= "DV_E_TYMED";
328 aResult
= "DV_E_DVASPECT";
330 case OLE_E_NOTRUNNING
:
331 aResult
= "OLE_E_NOTRUNNING";
333 case STG_E_MEDIUMFULL
:
334 aResult
= "STG_E_MEDIUMFULL";
336 case DV_E_CLIPFORMAT
:
337 aResult
= "DV_E_CLIPFORMAT";
343 // Explicit template instantiaton, because otherwise the call is
345 constexpr int kRadix
= 16;
346 aResult
= IntToCString
<int32_t>(aHres
, kRadix
);
352 // <https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-olegetclipboard>.
353 static void OleGetClipboardResultToString(const HRESULT aHres
,
354 nsACString
& aResult
) {
359 case CLIPBRD_E_CANT_OPEN
:
360 aResult
= "CLIPBRD_E_CANT_OPEN";
362 case CLIPBRD_E_CANT_CLOSE
:
363 aResult
= "CLIPBRD_E_CANT_CLOSE";
366 // Explicit template instantiaton, because otherwise the call is
368 constexpr int kRadix
= 16;
369 aResult
= IntToCString
<int32_t>(aHres
, kRadix
);
375 // <https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-olegetclipboard>.
376 static void LogOleGetClipboardResult(const HRESULT aHres
) {
377 if (MOZ_LOG_TEST(gWin32ClipboardLog
, LogLevel::Debug
)) {
378 nsAutoCString hresString
;
379 OleGetClipboardResultToString(aHres
, hresString
);
380 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
381 ("OleGetClipboard result: %s", hresString
.get()));
386 // <https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-olesetclipboard>.
387 static void OleSetClipboardResultToString(HRESULT aHres
, nsACString
& aResult
) {
392 case CLIPBRD_E_CANT_OPEN
:
393 aResult
= "CLIPBRD_E_CANT_OPEN";
395 case CLIPBRD_E_CANT_EMPTY
:
396 aResult
= "CLIPBRD_E_CANT_EMPTY";
398 case CLIPBRD_E_CANT_CLOSE
:
399 aResult
= "CLIPBRD_E_CANT_CLOSE";
401 case CLIPBRD_E_CANT_SET
:
402 aResult
= "CLIPBRD_E_CANT_SET";
405 // Explicit template instantiaton, because otherwise the call is
407 constexpr int kRadix
= 16;
408 aResult
= IntToCString
<int32_t>(aHres
, kRadix
);
414 // <https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-olesetclipboard>.
415 static void LogOleSetClipboardResult(const HRESULT aHres
) {
416 if (MOZ_LOG_TEST(gWin32ClipboardLog
, LogLevel::Debug
)) {
417 nsAutoCString hresString
;
418 OleSetClipboardResultToString(aHres
, hresString
);
419 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
420 ("OleSetClipboard result: %s", hresString
.get()));
424 template <typename Function
, typename LogFunction
, typename
... Args
>
425 static HRESULT
RepeatedlyTry(Function aFunction
, LogFunction aLogFunction
,
427 // These are magic values based on local testing. They are chosen not higher
428 // to avoid jank (<https://developer.mozilla.org/en-US/docs/Glossary/Jank>).
429 // When changing them, be careful.
430 static constexpr int kNumberOfTries
= 3;
431 static constexpr int kDelayInMs
= 3;
434 for (int i
= 0; i
< kNumberOfTries
; ++i
) {
435 hres
= aFunction(aArgs
...);
442 std::this_thread::sleep_for(std::chrono::milliseconds(kDelayInMs
));
448 // Other apps can block access to the clipboard. This repeatedly
449 // calls `::OleSetClipboard` for a fixed number of times and should be called
450 // instead of `::OleSetClipboard`.
451 static void RepeatedlyTryOleSetClipboard(IDataObject
* aDataObj
) {
452 RepeatedlyTry(::OleSetClipboard
, LogOleSetClipboardResult
, aDataObj
);
455 //-------------------------------------------------------------------------
456 NS_IMETHODIMP
nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard
) {
457 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
459 if (aWhichClipboard
!= kGlobalClipboard
) {
460 return NS_ERROR_FAILURE
;
463 // make sure we have a good transferable
464 if (!mTransferable
) {
465 return NS_ERROR_FAILURE
;
469 a11y::Compatibility::SuppressA11yForClipboardCopy();
472 RefPtr
<IDataObject
> dataObj
;
473 auto mightNeedToFlush
= MightNeedToFlush::No
;
474 if (NS_SUCCEEDED(CreateNativeDataObject(mTransferable
,
475 getter_AddRefs(dataObj
), nullptr,
476 &mightNeedToFlush
))) {
477 RepeatedlyTryOleSetClipboard(dataObj
);
479 const bool doFlush
= [&] {
480 switch (StaticPrefs::widget_windows_sync_clipboard_flush()) {
486 // Bug 1774285: Windows Suggested Actions (introduced in Windows 11
487 // 22H2) walks the entire a11y tree using UIA if something is placed
488 // on the clipboard using delayed rendering. (The OLE clipboard always
489 // uses delayed rendering.) This a11y tree walk causes an unacceptable
490 // hang, particularly when the a11y cache is disabled. We choose the
491 // lesser of the two performance/memory evils here and force immediate
493 return mightNeedToFlush
== MightNeedToFlush::Yes
&&
494 NeedsWindows11SuggestedActionsWorkaround();
498 RepeatedlyTry(::OleFlushClipboard
, [](HRESULT
) {});
501 // Clear the native clipboard
502 RepeatedlyTryOleSetClipboard(nullptr);
508 //-------------------------------------------------------------------------
509 nsresult
nsClipboard::GetGlobalData(HGLOBAL aHGBL
, void** aData
,
511 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Verbose
, ("%s", __FUNCTION__
));
513 // Allocate a new memory buffer and copy the data from global memory.
514 // Recall that win98 allocates to nearest DWORD boundary. As a safety
515 // precaution, allocate an extra 3 bytes (but don't report them in |aLen|!)
516 // and null them out to ensure that all of our NS_strlen calls will succeed.
517 // NS_strlen operates on char16_t, so we need 3 NUL bytes to ensure it finds
518 // a full NUL char16_t when |*aLen| is odd.
519 nsresult result
= NS_ERROR_FAILURE
;
520 if (aHGBL
!= nullptr) {
521 LPSTR lpStr
= (LPSTR
)GlobalLock(aHGBL
);
522 mozilla::CheckedInt
<uint32_t> allocSize
=
523 mozilla::CheckedInt
<uint32_t>(GlobalSize(aHGBL
)) + 3;
524 if (!allocSize
.isValid()) {
525 return NS_ERROR_INVALID_ARG
;
527 char* data
= static_cast<char*>(malloc(allocSize
.value()));
529 uint32_t size
= allocSize
.value() - 3;
530 memcpy(data
, lpStr
, size
);
531 // null terminate for safety
532 data
[size
] = data
[size
+ 1] = data
[size
+ 2] = '\0';
541 // We really shouldn't ever get here
548 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
, nullptr,
550 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
551 (LPWSTR
)&lpMsgBuf
, 0, nullptr);
553 // Display the string.
554 MessageBoxW(nullptr, (LPCWSTR
)lpMsgBuf
, L
"GetLastError",
555 MB_OK
| MB_ICONINFORMATION
);
564 //-------------------------------------------------------------------------
565 nsresult
nsClipboard::GetNativeDataOffClipboard(nsIWidget
* aWidget
,
566 UINT
/*aIndex*/, UINT aFormat
,
567 void** aData
, uint32_t* aLen
) {
568 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
569 ("%s: overload taking nsIWidget*.", __FUNCTION__
));
572 nsresult result
= NS_ERROR_FAILURE
;
574 HWND nativeWin
= nullptr;
575 if (::OpenClipboard(nativeWin
)) {
576 hglb
= ::GetClipboardData(aFormat
);
577 result
= GetGlobalData(hglb
, aData
, aLen
);
583 // See methods listed at
584 // <https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-idataobject#methods>.
585 static void LogIDataObjectMethodResult(const HRESULT aHres
,
586 const nsCString
& aMethodName
) {
587 if (MOZ_LOG_TEST(gWin32ClipboardLog
, LogLevel::Debug
)) {
588 nsAutoCString hresString
;
589 IDataObjectMethodResultToString(aHres
, hresString
);
591 gWin32ClipboardLog
, LogLevel::Debug
,
592 ("IDataObject::%s result: %s", aMethodName
.get(), hresString
.get()));
596 // Other apps can block access to the clipboard. This repeatedly calls
597 // `GetData` for a fixed number of times and should be called instead of
599 // <https://docs.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-idataobject-getdata>.
600 // While Microsoft's documentation doesn't include `CLIPBRD_E_CANT_OPEN`
601 // explicitly, it allows it implicitly and in local experiments it was indeed
603 static HRESULT
RepeatedlyTryGetData(IDataObject
& aDataObject
, LPFORMATETC pFE
,
605 return RepeatedlyTry(
606 [&aDataObject
, &pFE
, &pSTM
]() { return aDataObject
.GetData(pFE
, pSTM
); },
607 std::bind(LogIDataObjectMethodResult
, std::placeholders::_1
,
611 //-------------------------------------------------------------------------
613 HRESULT
nsClipboard::FillSTGMedium(IDataObject
* aDataObject
, UINT aFormat
,
614 LPFORMATETC pFE
, LPSTGMEDIUM pSTM
,
616 SET_FORMATETC(*pFE
, aFormat
, 0, DVASPECT_CONTENT
, -1, aTymed
);
618 // Starting by querying for the data to see if we can get it as from global
620 HRESULT hres
= S_FALSE
;
621 hres
= aDataObject
->QueryGetData(pFE
);
622 LogIDataObjectMethodResult(hres
, "QueryGetData"_ns
);
624 hres
= RepeatedlyTryGetData(*aDataObject
, pFE
, pSTM
);
629 //-------------------------------------------------------------------------
630 // If aFormat is CF_DIBV5, aMIMEImageFormat must be a type for which we have
631 // an image encoder (e.g. image/png).
632 // For other values of aFormat, it is OK to pass null for aMIMEImageFormat.
633 nsresult
nsClipboard::GetNativeDataOffClipboard(IDataObject
* aDataObject
,
634 UINT aIndex
, UINT aFormat
,
635 const char* aMIMEImageFormat
,
636 void** aData
, uint32_t* aLen
) {
637 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
638 ("%s: overload taking IDataObject*.", __FUNCTION__
));
640 nsresult result
= NS_ERROR_FAILURE
;
648 UINT format
= aFormat
;
649 HRESULT hres
= S_FALSE
;
651 // XXX at the moment we only support global memory transfers
652 // It is here where we will add support for native images
656 hres
= FillSTGMedium(aDataObject
, format
, &fe
, &stm
, TYMED_HGLOBAL
);
658 // Currently this is only handling TYMED_HGLOBAL data
659 // For Text, Dibs, Files, and generic data (like HTML)
661 static CLIPFORMAT fileDescriptorFlavorA
=
662 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA
);
663 static CLIPFORMAT fileDescriptorFlavorW
=
664 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW
);
665 static CLIPFORMAT fileFlavor
=
666 ::RegisterClipboardFormat(CFSTR_FILECONTENTS
);
667 static CLIPFORMAT preferredDropEffect
=
668 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
);
671 case TYMED_HGLOBAL
: {
672 switch (fe
.cfFormat
) {
674 // Get the data out of the global data handle. The size we
675 // return should not include the null because the other
676 // platforms don't use nulls, so just return the length we get
677 // back from strlen(), since we know CF_TEXT is null
678 // terminated. Recall that GetGlobalData() returns the size of
679 // the allocated buffer, not the size of the data (on 98, these
680 // are not the same) so we can't use that.
681 uint32_t allocLen
= 0;
682 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
683 *aLen
= strlen(reinterpret_cast<char*>(*aData
));
688 case CF_UNICODETEXT
: {
689 // Get the data out of the global data handle. The size we
690 // return should not include the null because the other
691 // platforms don't use nulls, so just return the length we get
692 // back from strlen(), since we know CF_UNICODETEXT is null
693 // terminated. Recall that GetGlobalData() returns the size of
694 // the allocated buffer, not the size of the data (on 98, these
695 // are not the same) so we can't use that.
696 uint32_t allocLen
= 0;
697 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
698 *aLen
= NS_strlen(reinterpret_cast<char16_t
*>(*aData
)) * 2;
704 if (aMIMEImageFormat
) {
705 uint32_t allocLen
= 0;
706 const char* clipboardData
;
707 if (NS_SUCCEEDED(GetGlobalData(
708 stm
.hGlobal
, (void**)&clipboardData
, &allocLen
))) {
709 nsCOMPtr
<imgIContainer
> container
;
710 nsCOMPtr
<imgITools
> imgTools
=
711 do_CreateInstance("@mozilla.org/image/tools;1");
712 result
= imgTools
->DecodeImageFromBuffer(
713 clipboardData
, allocLen
,
714 nsLiteralCString(IMAGE_BMP_MS_CLIPBOARD
),
715 getter_AddRefs(container
));
716 if (NS_FAILED(result
)) {
720 nsAutoCString mimeType
;
721 if (strcmp(aMIMEImageFormat
, kJPGImageMime
) == 0) {
722 mimeType
.Assign(IMAGE_JPEG
);
724 mimeType
.Assign(aMIMEImageFormat
);
727 nsCOMPtr
<nsIInputStream
> inputStream
;
728 result
= imgTools
->EncodeImage(container
, mimeType
, u
""_ns
,
729 getter_AddRefs(inputStream
));
730 if (NS_FAILED(result
)) {
735 result
= NS_ERROR_FAILURE
;
739 *aData
= inputStream
.forget().take();
740 *aLen
= sizeof(nsIInputStream
*);
746 // in the case of a file drop, multiple files are stashed within a
747 // single data object. In order to match mozilla's D&D apis, we
748 // just pull out the file at the requested index, pretending as
749 // if there really are multiple drag items.
750 HDROP dropFiles
= (HDROP
)GlobalLock(stm
.hGlobal
);
752 UINT numFiles
= ::DragQueryFileW(dropFiles
, 0xFFFFFFFF, nullptr, 0);
753 NS_ASSERTION(numFiles
> 0,
754 "File drop flavor, but no files...hmmmm");
755 NS_ASSERTION(aIndex
< numFiles
,
756 "Asked for a file index out of range of list");
759 ::DragQueryFileW(dropFiles
, aIndex
, nullptr, 0);
760 wchar_t* buffer
= reinterpret_cast<wchar_t*>(
761 moz_xmalloc((fileNameLen
+ 1) * sizeof(wchar_t)));
762 ::DragQueryFileW(dropFiles
, aIndex
, buffer
, fileNameLen
+ 1);
764 *aLen
= fileNameLen
* sizeof(char16_t
);
767 GlobalUnlock(stm
.hGlobal
);
772 if (fe
.cfFormat
== fileDescriptorFlavorA
||
773 fe
.cfFormat
== fileDescriptorFlavorW
||
774 fe
.cfFormat
== fileFlavor
) {
776 "Mozilla doesn't yet understand how to read this type of "
779 // Get the data out of the global data handle. The size we
780 // return should not include the null because the other
781 // platforms don't use nulls, so just return the length we get
782 // back from strlen(), since we know CF_UNICODETEXT is null
783 // terminated. Recall that GetGlobalData() returns the size of
784 // the allocated buffer, not the size of the data (on 98, these
785 // are not the same) so we can't use that.
787 // NOTE: we are assuming that anything that falls into this
788 // default case is unicode. As we start to get more
789 // kinds of binary data, this may become an incorrect
790 // assumption. Stay tuned.
791 uint32_t allocLen
= 0;
792 if (NS_SUCCEEDED(GetGlobalData(stm
.hGlobal
, aData
, &allocLen
))) {
793 if (fe
.cfFormat
== GetHtmlClipboardFormat()) {
794 // CF_HTML is actually UTF8, not unicode, so disregard the
795 // assumption above. We have to check the header for the
796 // actual length, and we'll do that in FindPlatformHTML().
797 // For now, return the allocLen. This case is mostly to
798 // ensure we don't try to call strlen on the buffer.
800 } else if (fe
.cfFormat
== GetCustomClipboardFormat()) {
803 } else if (fe
.cfFormat
== preferredDropEffect
) {
804 // As per the MSDN doc entitled: "Shell Clipboard Formats"
805 // CFSTR_PREFERREDDROPEFFECT should return a DWORD
807 // http://msdn.microsoft.com/en-us/library/bb776902(v=vs.85).aspx
809 allocLen
== sizeof(DWORD
),
810 "CFSTR_PREFERREDDROPEFFECT should return a DWORD");
813 *aLen
= NS_strlen(reinterpret_cast<char16_t
*>(*aData
)) *
825 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Info
,
826 ("*********************** TYMED_GDI\n"));
834 ReleaseStgMedium(&stm
);
840 //-------------------------------------------------------------------------
841 nsresult
nsClipboard::GetDataFromDataObject(IDataObject
* aDataObject
,
842 UINT anIndex
, nsIWidget
* aWindow
,
843 nsITransferable
* aTransferable
) {
844 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
846 // make sure we have a good transferable
847 if (!aTransferable
) {
848 return NS_ERROR_INVALID_ARG
;
851 nsresult res
= NS_ERROR_FAILURE
;
853 // get flavor list that includes all flavors that can be written (including
854 // ones obtained through conversion)
855 nsTArray
<nsCString
> flavors
;
856 res
= aTransferable
->FlavorsTransferableCanImport(flavors
);
857 if (NS_FAILED(res
)) {
858 return NS_ERROR_FAILURE
;
861 // Walk through flavors and see which flavor is on the clipboard them on the
863 for (uint32_t i
= 0; i
< flavors
.Length(); i
++) {
864 nsCString
& flavorStr
= flavors
[i
];
865 UINT format
= GetFormat(flavorStr
.get());
867 // Try to get the data using the desired flavor. This might fail, but all is
869 void* data
= nullptr;
870 uint32_t dataLen
= 0;
871 bool dataFound
= false;
872 if (nullptr != aDataObject
) {
873 if (NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject
, anIndex
, format
,
874 flavorStr
.get(), &data
,
878 } else if (nullptr != aWindow
) {
879 if (NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow
, anIndex
, format
,
885 // This is our second chance to try to find some data, having not found it
886 // when directly asking for the flavor. Let's try digging around in other
887 // flavors to help satisfy our craving for data.
889 if (flavorStr
.EqualsLiteral(kTextMime
)) {
891 FindUnicodeFromPlainText(aDataObject
, anIndex
, &data
, &dataLen
);
892 } else if (flavorStr
.EqualsLiteral(kURLMime
)) {
893 // drags from other windows apps expose the native
894 // CFSTR_INETURL{A,W} flavor
895 dataFound
= FindURLFromNativeURL(aDataObject
, anIndex
, &data
, &dataLen
);
898 FindURLFromLocalFile(aDataObject
, anIndex
, &data
, &dataLen
);
901 } // if we try one last ditch effort to find our data
903 // Hopefully by this point we've found it and can go about our business
905 nsCOMPtr
<nsISupports
> genericDataWrapper
;
906 if (flavorStr
.EqualsLiteral(kFileMime
)) {
907 // we have a file path in |data|. Create an nsLocalFile object.
908 nsDependentString
filepath(reinterpret_cast<char16_t
*>(data
));
909 nsCOMPtr
<nsIFile
> file
;
911 NS_NewLocalFile(filepath
, false, getter_AddRefs(file
)))) {
912 genericDataWrapper
= do_QueryInterface(file
);
915 } else if (flavorStr
.EqualsLiteral(kNativeHTMLMime
)) {
917 // the editor folks want CF_HTML exactly as it's on the clipboard, no
918 // conversions, no fancy stuff. Pull it off the clipboard, stuff it into
919 // a wrapper and hand it back to them.
920 if (FindPlatformHTML(aDataObject
, anIndex
, &data
, &dummy
, &dataLen
)) {
921 nsPrimitiveHelpers::CreatePrimitiveForData(
922 flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
));
925 continue; // something wrong with this flavor, keep looking for other
929 } else if (flavorStr
.EqualsLiteral(kHTMLMime
)) {
930 uint32_t startOfData
= 0;
931 // The JS folks want CF_HTML exactly as it is on the clipboard, but
932 // minus the CF_HTML header index information.
933 // It also needs to be converted to UTF16 and have linebreaks changed.
934 if (FindPlatformHTML(aDataObject
, anIndex
, &data
, &startOfData
,
936 dataLen
-= startOfData
;
937 nsPrimitiveHelpers::CreatePrimitiveForCFHTML(
938 static_cast<char*>(data
) + startOfData
, &dataLen
,
939 getter_AddRefs(genericDataWrapper
));
942 continue; // something wrong with this flavor, keep looking for other
946 } else if (flavorStr
.EqualsLiteral(kJPEGImageMime
) ||
947 flavorStr
.EqualsLiteral(kJPGImageMime
) ||
948 flavorStr
.EqualsLiteral(kPNGImageMime
)) {
949 nsIInputStream
* imageStream
= reinterpret_cast<nsIInputStream
*>(data
);
950 genericDataWrapper
= do_QueryInterface(imageStream
);
951 NS_IF_RELEASE(imageStream
);
953 // Treat custom types as a string of bytes.
954 if (!flavorStr
.EqualsLiteral(kCustomTypesMime
)) {
955 bool isRTF
= flavorStr
.EqualsLiteral(kRTFMime
);
956 // we probably have some form of text. The DOM only wants LF, so
957 // convert from Win32 line endings to DOM line endings.
958 int32_t signedLen
= static_cast<int32_t>(dataLen
);
959 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(isRTF
, &data
,
964 // RTF on Windows is known to sometimes deliver an extra null byte.
965 if (dataLen
> 0 && static_cast<char*>(data
)[dataLen
- 1] == '\0') {
971 nsPrimitiveHelpers::CreatePrimitiveForData(
972 flavorStr
, data
, dataLen
, getter_AddRefs(genericDataWrapper
));
976 NS_ASSERTION(genericDataWrapper
,
977 "About to put null data into the transferable");
978 aTransferable
->SetTransferData(flavorStr
.get(), genericDataWrapper
);
981 // we found one, get out of the loop
992 // Someone asked for the OS CF_HTML flavor. We give it back to them exactly
995 bool nsClipboard ::FindPlatformHTML(IDataObject
* inDataObject
, UINT inIndex
,
996 void** outData
, uint32_t* outStartOfData
,
997 uint32_t* outDataLen
) {
998 // Reference: MSDN doc entitled "HTML Clipboard Format"
999 // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
1000 // CF_HTML is UTF8, not unicode. We also can't rely on it being
1001 // null-terminated so we have to check the CF_HTML header for the correct
1002 // length. The length we return is the bytecount from the beginning of the
1003 // selected data to the end of the selected data, without the null
1004 // termination. Because it's UTF8, we're guaranteed the header is ASCII.
1006 if (!outData
|| !*outData
) {
1010 char version
[8] = {0};
1011 int32_t startOfData
= 0;
1012 int32_t endOfData
= 0;
1014 sscanf((char*)*outData
, "Version:%7s\nStartHTML:%d\nEndHTML:%d", version
,
1015 &startOfData
, &endOfData
);
1017 if (numFound
!= 3 || startOfData
< -1 || endOfData
< -1) {
1021 // Fixup the start and end markers if they have no context (set to -1)
1022 if (startOfData
== -1) {
1025 if (endOfData
== -1) {
1026 endOfData
= *outDataLen
;
1029 // Make sure we were passed sane values within our buffer size.
1030 // (Note that we've handled all cases of negative endOfData above, so we can
1031 // safely cast it to be unsigned here.)
1032 if (!endOfData
|| startOfData
>= endOfData
||
1033 static_cast<uint32_t>(endOfData
) > *outDataLen
) {
1037 // We want to return the buffer not offset by startOfData because it will be
1038 // parsed out later (probably by HTMLEditor::ParseCFHTML) when it is still
1039 // in CF_HTML format.
1041 // We return the byte offset from the start of the data buffer to where the
1042 // HTML data starts. The caller might want to extract the HTML only.
1043 *outStartOfData
= startOfData
;
1044 *outDataLen
= endOfData
;
1049 // FindUnicodeFromPlainText
1051 // Looks for CF_TEXT on the clipboard and converts it into an UTF-16 string
1052 // if present. Returns this string in outData, and its length in outDataLen.
1053 // XXXndeakin Windows converts between CF_UNICODE and CF_TEXT automatically
1054 // so it doesn't seem like this is actually needed.
1056 bool nsClipboard ::FindUnicodeFromPlainText(IDataObject
* inDataObject
,
1057 UINT inIndex
, void** outData
,
1058 uint32_t* outDataLen
) {
1059 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
1061 // We are looking for text/plain and we failed to find it on the clipboard
1062 // first, so try again with CF_TEXT. If that is present, convert it to
1064 nsresult rv
= GetNativeDataOffClipboard(inDataObject
, inIndex
, CF_TEXT
,
1065 nullptr, outData
, outDataLen
);
1066 if (NS_FAILED(rv
) || !*outData
) {
1070 const char* castedText
= static_cast<char*>(*outData
);
1072 rv
= NS_CopyNativeToUnicode(nsDependentCSubstring(castedText
, *outDataLen
),
1074 if (NS_FAILED(rv
)) {
1078 // out with the old, in with the new
1080 *outData
= ToNewUnicode(tmp
);
1081 *outDataLen
= tmp
.Length() * sizeof(char16_t
);
1085 } // FindUnicodeFromPlainText
1088 // FindURLFromLocalFile
1090 // we are looking for a URL and couldn't find it, try again with looking for
1091 // a local file. If we have one, it may either be a normal file or an internet
1092 // shortcut. In both cases, however, we can get a URL (it will be a file:// url
1093 // in the local file case).
1095 bool nsClipboard ::FindURLFromLocalFile(IDataObject
* inDataObject
, UINT inIndex
,
1096 void** outData
, uint32_t* outDataLen
) {
1097 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
1099 bool dataFound
= false;
1101 nsresult loadResult
=
1102 GetNativeDataOffClipboard(inDataObject
, inIndex
, GetFormat(kFileMime
),
1103 nullptr, outData
, outDataLen
);
1104 if (NS_SUCCEEDED(loadResult
) && *outData
) {
1105 // we have a file path in |data|. Is it an internet shortcut or a normal
1107 const nsDependentString
filepath(static_cast<char16_t
*>(*outData
));
1108 nsCOMPtr
<nsIFile
> file
;
1109 nsresult rv
= NS_NewLocalFile(filepath
, true, getter_AddRefs(file
));
1110 if (NS_FAILED(rv
)) {
1115 if (IsInternetShortcut(filepath
)) {
1118 ResolveShortcut(file
, url
);
1119 if (!url
.IsEmpty()) {
1120 // convert it to unicode and pass it out
1121 NS_ConvertUTF8toUTF16
urlString(url
);
1122 // the internal mozilla URL format, text/x-moz-url, contains
1123 // URL\ntitle. We can guess the title from the file's name.
1125 file
->GetLeafName(title
);
1126 // We rely on IsInternetShortcut check that file has a .url extension.
1127 title
.SetLength(title
.Length() - 4);
1128 if (title
.IsEmpty()) {
1131 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ title
);
1133 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
1138 // we have a normal file, use some Necko objects to get our file path
1139 nsAutoCString urlSpec
;
1140 NS_GetURLSpecFromFile(file
, urlSpec
);
1142 // convert it to unicode and pass it out
1144 *outData
= UTF8ToNewUnicode(urlSpec
);
1146 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
1148 } // else regular file
1152 } // FindURLFromLocalFile
1155 // FindURLFromNativeURL
1157 // we are looking for a URL and couldn't find it using our internal
1158 // URL flavor, so look for it using the native URL flavor,
1159 // CF_INETURLSTRW (We don't handle CF_INETURLSTRA currently)
1161 bool nsClipboard ::FindURLFromNativeURL(IDataObject
* inDataObject
, UINT inIndex
,
1162 void** outData
, uint32_t* outDataLen
) {
1163 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
, ("%s", __FUNCTION__
));
1165 bool dataFound
= false;
1167 void* tempOutData
= nullptr;
1168 uint32_t tempDataLen
= 0;
1170 nsresult loadResult
= GetNativeDataOffClipboard(
1171 inDataObject
, inIndex
, ::RegisterClipboardFormat(CFSTR_INETURLW
), nullptr,
1172 &tempOutData
, &tempDataLen
);
1173 if (NS_SUCCEEDED(loadResult
) && tempOutData
) {
1174 nsDependentString
urlString(static_cast<char16_t
*>(tempOutData
));
1175 // the internal mozilla URL format, text/x-moz-url, contains
1176 // URL\ntitle. Since we don't actually have a title here,
1177 // just repeat the URL to fake it.
1178 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ urlString
);
1180 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
1184 loadResult
= GetNativeDataOffClipboard(
1185 inDataObject
, inIndex
, ::RegisterClipboardFormat(CFSTR_INETURLA
),
1186 nullptr, &tempOutData
, &tempDataLen
);
1187 if (NS_SUCCEEDED(loadResult
) && tempOutData
) {
1188 // CFSTR_INETURLA is (currently) equal to CFSTR_SHELLURL which is equal to
1189 // CF_TEXT which is by definition ANSI encoded.
1190 nsCString urlUnescapedA
;
1192 NS_UnescapeURL(static_cast<char*>(tempOutData
), tempDataLen
,
1193 esc_OnlyNonASCII
| esc_SkipControl
, urlUnescapedA
);
1197 NS_CopyNativeToUnicode(urlUnescapedA
, urlString
);
1199 NS_CopyNativeToUnicode(
1200 nsDependentCString(static_cast<char*>(tempOutData
), tempDataLen
),
1204 // the internal mozilla URL format, text/x-moz-url, contains
1205 // URL\ntitle. Since we don't actually have a title here,
1206 // just repeat the URL to fake it.
1207 *outData
= ToNewUnicode(urlString
+ u
"\n"_ns
+ urlString
);
1209 NS_strlen(static_cast<char16_t
*>(*outData
)) * sizeof(char16_t
);
1216 } // FindURLFromNativeURL
1218 // Other apps can block access to the clipboard. This repeatedly
1219 // calls `::OleGetClipboard` for a fixed number of times and should be called
1220 // instead of `::OleGetClipboard`.
1221 static HRESULT
RepeatedlyTryOleGetClipboard(IDataObject
** aDataObj
) {
1222 return RepeatedlyTry(::OleGetClipboard
, LogOleGetClipboardResult
, aDataObj
);
1228 void nsClipboard ::ResolveShortcut(nsIFile
* aFile
, nsACString
& outURL
) {
1229 nsCOMPtr
<nsIFileProtocolHandler
> fph
;
1230 nsresult rv
= NS_GetFileProtocolHandler(getter_AddRefs(fph
));
1231 if (NS_FAILED(rv
)) {
1235 nsCOMPtr
<nsIURI
> uri
;
1236 rv
= fph
->ReadURLFile(aFile
, getter_AddRefs(uri
));
1237 if (NS_FAILED(rv
)) {
1241 uri
->GetSpec(outURL
);
1242 } // ResolveShortcut
1245 // IsInternetShortcut
1247 // A file is an Internet Shortcut if it ends with .URL
1249 bool nsClipboard ::IsInternetShortcut(const nsAString
& inFileName
) {
1250 return StringEndsWith(inFileName
, u
".url"_ns
,
1251 nsCaseInsensitiveStringComparator
);
1252 } // IsInternetShortcut
1254 //-------------------------------------------------------------------------
1256 nsClipboard::GetNativeClipboardData(nsITransferable
* aTransferable
,
1257 int32_t aWhichClipboard
) {
1258 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Debug
,
1259 ("%s aWhichClipboard=%i", __FUNCTION__
, aWhichClipboard
));
1261 // make sure we have a good transferable
1262 if (!aTransferable
|| aWhichClipboard
!= kGlobalClipboard
) {
1263 return NS_ERROR_FAILURE
;
1268 // This makes sure we can use the OLE functionality for the clipboard
1269 IDataObject
* dataObj
;
1270 if (S_OK
== RepeatedlyTryOleGetClipboard(&dataObj
)) {
1271 // Use OLE IDataObject for clipboard operations
1272 MOZ_LOG(gWin32ClipboardLog
, LogLevel::Verbose
,
1273 ("%s: use OLE IDataObject.", __FUNCTION__
));
1274 res
= GetDataFromDataObject(dataObj
, 0, nullptr, aTransferable
);
1277 // do it the old manual way
1278 res
= GetDataFromDataObject(nullptr, 0, mWindow
, aTransferable
);
1284 nsClipboard::EmptyClipboard(int32_t aWhichClipboard
) {
1285 // Some programs such as ZoneAlarm monitor clipboard usage and then open the
1286 // clipboard to scan it. If we i) empty and then ii) set data, then the
1287 // 'set data' can sometimes fail with access denied becacuse another program
1288 // has the clipboard open. So to avoid this race condition for OpenClipboard
1289 // we do not empty the clipboard when we're setting it.
1290 if (aWhichClipboard
== kGlobalClipboard
&& !mEmptyingForSetData
) {
1291 RepeatedlyTryOleSetClipboard(nullptr);
1293 return nsBaseClipboard::EmptyClipboard(aWhichClipboard
);
1296 //-------------------------------------------------------------------------
1297 NS_IMETHODIMP
nsClipboard::HasDataMatchingFlavors(
1298 const nsTArray
<nsCString
>& aFlavorList
, int32_t aWhichClipboard
,
1301 if (aWhichClipboard
!= kGlobalClipboard
) {
1305 for (auto& flavor
: aFlavorList
) {
1306 UINT format
= GetFormat(flavor
.get());
1307 if (IsClipboardFormatAvailable(format
)) {