Bug 1793629 - Implement attention indicator for the unified extensions button, r...
[gecko.git] / widget / windows / nsClipboard.cpp
blobf3974bcfc7787127237d5b2c4a1a7240b8114d48
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"
8 #include <shlobj.h>
9 #include <intshcut.h>
11 // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
12 #include <shellapi.h>
14 #include <functional>
15 #include <thread>
16 #include <chrono>
18 #include "mozilla/Logging.h"
19 #include "mozilla/StaticPrefs_clipboard.h"
20 #include "nsArrayUtils.h"
21 #include "nsCOMPtr.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsDataObj.h"
24 #include "nsString.h"
25 #include "nsNativeCharsetUtils.h"
26 #include "nsIInputStream.h"
27 #include "nsITransferable.h"
28 #include "nsXPCOM.h"
29 #include "nsReadableUtils.h"
30 #include "nsUnicharUtils.h"
31 #include "nsPrimitiveHelpers.h"
32 #include "nsIWidget.h"
33 #include "nsWidgetsCID.h"
34 #include "nsCRT.h"
35 #include "nsNetUtil.h"
36 #include "nsIFileProtocolHandler.h"
37 #include "nsEscape.h"
38 #include "nsIObserverService.h"
39 #include "nsMimeTypes.h"
40 #include "imgITools.h"
42 using mozilla::LogLevel;
44 static mozilla::LazyLogModule gWin32ClipboardLog("nsClipboard");
46 /* static */
47 UINT nsClipboard::GetHtmlClipboardFormat() {
48 static UINT format = ::RegisterClipboardFormatW(L"HTML Format");
49 return format;
52 /* static */
53 UINT nsClipboard::GetCustomClipboardFormat() {
54 static UINT format =
55 ::RegisterClipboardFormatW(L"application/x-moz-custom-clipdata");
56 return format;
59 //-------------------------------------------------------------------------
61 // nsClipboard constructor
63 //-------------------------------------------------------------------------
64 nsClipboard::nsClipboard() : nsBaseClipboard() {
65 mWindow = nullptr;
67 // Register for a shutdown notification so that we can flush data
68 // to the OS clipboard.
69 nsCOMPtr<nsIObserverService> observerService =
70 do_GetService("@mozilla.org/observer-service;1");
71 if (observerService) {
72 observerService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
73 PR_FALSE);
77 //-------------------------------------------------------------------------
78 // nsClipboard destructor
79 //-------------------------------------------------------------------------
80 nsClipboard::~nsClipboard() {}
82 NS_IMPL_ISUPPORTS_INHERITED(nsClipboard, nsBaseClipboard, nsIObserver)
84 NS_IMETHODIMP
85 nsClipboard::Observe(nsISupports* aSubject, const char* aTopic,
86 const char16_t* aData) {
87 // This will be called on shutdown.
88 ::OleFlushClipboard();
89 ::CloseClipboard();
91 return NS_OK;
94 //-------------------------------------------------------------------------
95 UINT nsClipboard::GetFormat(const char* aMimeStr, bool aMapHTMLMime) {
96 UINT format;
98 if (strcmp(aMimeStr, kTextMime) == 0) {
99 format = CF_TEXT;
100 } else if (strcmp(aMimeStr, kUnicodeMime) == 0) {
101 format = CF_UNICODETEXT;
102 } else if (strcmp(aMimeStr, kRTFMime) == 0) {
103 format = ::RegisterClipboardFormat(L"Rich Text Format");
104 } else if (strcmp(aMimeStr, kJPEGImageMime) == 0 ||
105 strcmp(aMimeStr, kJPGImageMime) == 0 ||
106 strcmp(aMimeStr, kPNGImageMime) == 0) {
107 format = CF_DIBV5;
108 } else if (strcmp(aMimeStr, kFileMime) == 0 ||
109 strcmp(aMimeStr, kFilePromiseMime) == 0) {
110 format = CF_HDROP;
111 } else if ((strcmp(aMimeStr, kNativeHTMLMime) == 0) ||
112 (aMapHTMLMime && strcmp(aMimeStr, kHTMLMime) == 0)) {
113 format = GetHtmlClipboardFormat();
114 } else if (strcmp(aMimeStr, kCustomTypesMime) == 0) {
115 format = GetCustomClipboardFormat();
116 } else {
117 format = ::RegisterClipboardFormatW(NS_ConvertASCIItoUTF16(aMimeStr).get());
120 return format;
123 //-------------------------------------------------------------------------
124 // static
125 nsresult nsClipboard::CreateNativeDataObject(nsITransferable* aTransferable,
126 IDataObject** aDataObj,
127 nsIURI* uri) {
128 if (nullptr == aTransferable) {
129 return NS_ERROR_FAILURE;
132 // Create our native DataObject that implements
133 // the OLE IDataObject interface
134 nsDataObj* dataObj = new nsDataObj(uri);
136 if (!dataObj) {
137 return NS_ERROR_OUT_OF_MEMORY;
140 dataObj->AddRef();
142 // Now set it up with all the right data flavors & enums
143 nsresult res = SetupNativeDataObject(aTransferable, dataObj);
144 if (NS_OK == res) {
145 *aDataObj = dataObj;
146 } else {
147 dataObj->Release();
149 return res;
152 static nsresult StoreValueInDataObject(nsDataObj* aObj,
153 LPCWSTR aClipboardFormat, DWORD value) {
154 HGLOBAL hGlobalMemory = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
155 if (!hGlobalMemory) {
156 return NS_ERROR_OUT_OF_MEMORY;
158 DWORD* pdw = (DWORD*)::GlobalLock(hGlobalMemory);
159 *pdw = value;
160 ::GlobalUnlock(hGlobalMemory);
162 STGMEDIUM stg;
163 stg.tymed = TYMED_HGLOBAL;
164 stg.pUnkForRelease = nullptr;
165 stg.hGlobal = hGlobalMemory;
167 FORMATETC fe;
168 SET_FORMATETC(fe, ::RegisterClipboardFormat(aClipboardFormat), 0,
169 DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
170 aObj->SetData(&fe, &stg, TRUE);
172 return NS_OK;
175 //-------------------------------------------------------------------------
176 nsresult nsClipboard::SetupNativeDataObject(nsITransferable* aTransferable,
177 IDataObject* aDataObj) {
178 if (nullptr == aTransferable || nullptr == aDataObj) {
179 return NS_ERROR_FAILURE;
182 nsDataObj* dObj = static_cast<nsDataObj*>(aDataObj);
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
199 // below.
200 UINT format = GetFormat(flavorStr.get(), false);
202 // Now tell the native IDataObject about both our mime type and
203 // the native data format
204 FORMATETC fe;
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(kUnicodeMime)) {
212 // if we find text/unicode, also advertise text/plain (which we will
213 // convert on our own in nsDataObj::GetText().
214 FORMATETC textFE;
215 SET_FORMATETC(textFE, CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
216 dObj->AddDataFlavor(kTextMime, &textFE);
217 } else if (flavorStr.EqualsLiteral(kHTMLMime)) {
218 // if we find text/html, also advertise win32's html flavor (which we will
219 // convert on our own in nsDataObj::GetText().
220 FORMATETC htmlFE;
221 SET_FORMATETC(htmlFE, GetHtmlClipboardFormat(), 0, DVASPECT_CONTENT, -1,
222 TYMED_HGLOBAL);
223 dObj->AddDataFlavor(kHTMLMime, &htmlFE);
224 } else if (flavorStr.EqualsLiteral(kURLMime)) {
225 // if we're a url, in addition to also being text, we need to register
226 // the "file" flavors so that the win32 shell knows to create an internet
227 // shortcut when it sees one of these beasts.
228 FORMATETC shortcutFE;
229 SET_FORMATETC(shortcutFE,
230 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA), 0,
231 DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
232 dObj->AddDataFlavor(kURLMime, &shortcutFE);
233 SET_FORMATETC(shortcutFE,
234 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW), 0,
235 DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
236 dObj->AddDataFlavor(kURLMime, &shortcutFE);
237 SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILECONTENTS),
238 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
239 dObj->AddDataFlavor(kURLMime, &shortcutFE);
240 SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLA), 0,
241 DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
242 dObj->AddDataFlavor(kURLMime, &shortcutFE);
243 SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLW), 0,
244 DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
245 dObj->AddDataFlavor(kURLMime, &shortcutFE);
246 } else if (flavorStr.EqualsLiteral(kPNGImageMime) ||
247 flavorStr.EqualsLiteral(kJPEGImageMime) ||
248 flavorStr.EqualsLiteral(kJPGImageMime) ||
249 flavorStr.EqualsLiteral(kGIFImageMime) ||
250 flavorStr.EqualsLiteral(kNativeImageMime)) {
251 // if we're an image, register the native bitmap flavor
252 FORMATETC imageFE;
253 // Add DIBv5
254 SET_FORMATETC(imageFE, CF_DIBV5, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
255 dObj->AddDataFlavor(flavorStr.get(), &imageFE);
256 // Add DIBv3
257 SET_FORMATETC(imageFE, CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
258 dObj->AddDataFlavor(flavorStr.get(), &imageFE);
259 } else if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
260 // if we're a file promise flavor, also register the
261 // CFSTR_PREFERREDDROPEFFECT format. The data object
262 // returns a value of DROPEFFECTS_MOVE to the drop target
263 // when it asks for the value of this format. This causes
264 // the file to be moved from the temporary location instead
265 // of being copied. The right thing to do here is to call
266 // SetData() on the data object and set the value of this format
267 // to DROPEFFECTS_MOVE on this particular data object. But,
268 // since all the other clipboard formats follow the model of setting
269 // data on the data object only when the drop object calls GetData(),
270 // I am leaving this format's value hard coded in the data object.
271 // We can change this if other consumers of this format get added to this
272 // codebase and they need different values.
273 FORMATETC shortcutFE;
274 SET_FORMATETC(shortcutFE,
275 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT), 0,
276 DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
277 dObj->AddDataFlavor(kFilePromiseMime, &shortcutFE);
281 if (!StaticPrefs::clipboard_copyPrivateDataToClipboardCloudOrHistory()) {
282 // Let Clipboard know that data is sensitive and must not be copied to
283 // the Cloud Clipboard, Clipboard History and similar.
284 // https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats#cloud-clipboard-and-clipboard-history-formats
285 if (aTransferable->GetIsPrivateData()) {
286 nsresult rv =
287 StoreValueInDataObject(dObj, TEXT("CanUploadToCloudClipboard"), 0);
288 NS_ENSURE_SUCCESS(rv, rv);
289 rv =
290 StoreValueInDataObject(dObj, TEXT("CanIncludeInClipboardHistory"), 0);
291 NS_ENSURE_SUCCESS(rv, rv);
292 rv = StoreValueInDataObject(
293 dObj, TEXT("ExcludeClipboardContentFromMonitorProcessing"), 0);
294 NS_ENSURE_SUCCESS(rv, rv);
298 return NS_OK;
301 // See methods listed at
302 // <https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-idataobject#methods>.
303 static void IDataObjectMethodResultToString(const HRESULT aHres,
304 nsACString& aResult) {
305 switch (aHres) {
306 case E_INVALIDARG:
307 aResult = "E_INVALIDARG";
308 break;
309 case E_UNEXPECTED:
310 aResult = "E_UNEXPECTED";
311 break;
312 case E_OUTOFMEMORY:
313 aResult = "E_OUTOFMEMORY";
314 break;
315 case DV_E_LINDEX:
316 aResult = "DV_E_LINDEX";
317 break;
318 case DV_E_FORMATETC:
319 aResult = "DV_E_FORMATETC";
320 break;
321 case DV_E_TYMED:
322 aResult = "DV_E_TYMED";
323 break;
324 case DV_E_DVASPECT:
325 aResult = "DV_E_DVASPECT";
326 break;
327 case OLE_E_NOTRUNNING:
328 aResult = "OLE_E_NOTRUNNING";
329 break;
330 case STG_E_MEDIUMFULL:
331 aResult = "STG_E_MEDIUMFULL";
332 break;
333 case DV_E_CLIPFORMAT:
334 aResult = "DV_E_CLIPFORMAT";
335 break;
336 case S_OK:
337 aResult = "S_OK";
338 break;
339 default:
340 // Explicit template instantiaton, because otherwise the call is
341 // ambiguous.
342 constexpr int kRadix = 16;
343 aResult = IntToCString<int32_t>(aHres, kRadix);
344 break;
348 // See
349 // <https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-olegetclipboard>.
350 static void OleGetClipboardResultToString(const HRESULT aHres,
351 nsACString& aResult) {
352 switch (aHres) {
353 case S_OK:
354 aResult = "S_OK";
355 break;
356 case CLIPBRD_E_CANT_OPEN:
357 aResult = "CLIPBRD_E_CANT_OPEN";
358 break;
359 case CLIPBRD_E_CANT_CLOSE:
360 aResult = "CLIPBRD_E_CANT_CLOSE";
361 break;
362 default:
363 // Explicit template instantiaton, because otherwise the call is
364 // ambiguous.
365 constexpr int kRadix = 16;
366 aResult = IntToCString<int32_t>(aHres, kRadix);
367 break;
371 // See
372 // <https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-olegetclipboard>.
373 static void LogOleGetClipboardResult(const HRESULT aHres) {
374 if (MOZ_LOG_TEST(gWin32ClipboardLog, LogLevel::Debug)) {
375 nsAutoCString hresString;
376 OleGetClipboardResultToString(aHres, hresString);
377 MOZ_LOG(gWin32ClipboardLog, LogLevel::Debug,
378 ("OleGetClipboard result: %s", hresString.get()));
382 // See
383 // <https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-olesetclipboard>.
384 static void OleSetClipboardResultToString(HRESULT aHres, nsACString& aResult) {
385 switch (aHres) {
386 case S_OK:
387 aResult = "S_OK";
388 break;
389 case CLIPBRD_E_CANT_OPEN:
390 aResult = "CLIPBRD_E_CANT_OPEN";
391 break;
392 case CLIPBRD_E_CANT_EMPTY:
393 aResult = "CLIPBRD_E_CANT_EMPTY";
394 break;
395 case CLIPBRD_E_CANT_CLOSE:
396 aResult = "CLIPBRD_E_CANT_CLOSE";
397 break;
398 case CLIPBRD_E_CANT_SET:
399 aResult = "CLIPBRD_E_CANT_SET";
400 break;
401 default:
402 // Explicit template instantiaton, because otherwise the call is
403 // ambiguous.
404 constexpr int kRadix = 16;
405 aResult = IntToCString<int32_t>(aHres, kRadix);
406 break;
410 // See
411 // <https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-olesetclipboard>.
412 static void LogOleSetClipboardResult(const HRESULT aHres) {
413 if (MOZ_LOG_TEST(gWin32ClipboardLog, LogLevel::Debug)) {
414 nsAutoCString hresString;
415 OleSetClipboardResultToString(aHres, hresString);
416 MOZ_LOG(gWin32ClipboardLog, LogLevel::Debug,
417 ("OleSetClipboard result: %s", hresString.get()));
421 template <typename Function, typename LogFunction, typename... Args>
422 static HRESULT RepeatedlyTry(Function aFunction, LogFunction aLogFunction,
423 Args... aArgs) {
424 // These are magic values based on local testing. They are chosen not higher
425 // to avoid jank (<https://developer.mozilla.org/en-US/docs/Glossary/Jank>).
426 // When changing them, be careful.
427 static constexpr int kNumberOfTries = 3;
428 static constexpr int kDelayInMs = 3;
430 HRESULT hres;
431 for (int i = 0; i < kNumberOfTries; ++i) {
432 hres = aFunction(aArgs...);
433 aLogFunction(hres);
435 if (hres == S_OK) {
436 break;
439 std::this_thread::sleep_for(std::chrono::milliseconds(kDelayInMs));
442 return hres;
445 // Other apps can block access to the clipboard. This repeatedly
446 // calls `::OleSetClipboard` for a fixed number of times and should be called
447 // instead of `::OleSetClipboard`.
448 static void RepeatedlyTryOleSetClipboard(IDataObject* aDataObj) {
449 RepeatedlyTry(::OleSetClipboard, LogOleSetClipboardResult, aDataObj);
452 //-------------------------------------------------------------------------
453 NS_IMETHODIMP nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard) {
454 MOZ_LOG(gWin32ClipboardLog, LogLevel::Debug, ("%s", __FUNCTION__));
456 if (aWhichClipboard != kGlobalClipboard) {
457 return NS_ERROR_FAILURE;
460 // make sure we have a good transferable
461 if (nullptr == mTransferable) {
462 return NS_ERROR_FAILURE;
465 IDataObject* dataObj;
466 if (NS_SUCCEEDED(CreateNativeDataObject(mTransferable, &dataObj,
467 nullptr))) { // this add refs dataObj
468 RepeatedlyTryOleSetClipboard(dataObj);
469 dataObj->Release();
470 } else {
471 // Clear the native clipboard
472 RepeatedlyTryOleSetClipboard(nullptr);
475 return NS_OK;
478 //-------------------------------------------------------------------------
479 nsresult nsClipboard::GetGlobalData(HGLOBAL aHGBL, void** aData,
480 uint32_t* aLen) {
481 MOZ_LOG(gWin32ClipboardLog, LogLevel::Verbose, ("%s", __FUNCTION__));
483 // Allocate a new memory buffer and copy the data from global memory.
484 // Recall that win98 allocates to nearest DWORD boundary. As a safety
485 // precaution, allocate an extra 3 bytes (but don't report them in |aLen|!)
486 // and null them out to ensure that all of our NS_strlen calls will succeed.
487 // NS_strlen operates on char16_t, so we need 3 NUL bytes to ensure it finds
488 // a full NUL char16_t when |*aLen| is odd.
489 nsresult result = NS_ERROR_FAILURE;
490 if (aHGBL != nullptr) {
491 LPSTR lpStr = (LPSTR)GlobalLock(aHGBL);
492 mozilla::CheckedInt<uint32_t> allocSize =
493 mozilla::CheckedInt<uint32_t>(GlobalSize(aHGBL)) + 3;
494 if (!allocSize.isValid()) {
495 return NS_ERROR_INVALID_ARG;
497 char* data = static_cast<char*>(malloc(allocSize.value()));
498 if (data) {
499 uint32_t size = allocSize.value() - 3;
500 memcpy(data, lpStr, size);
501 // null terminate for safety
502 data[size] = data[size + 1] = data[size + 2] = '\0';
504 GlobalUnlock(aHGBL);
505 *aData = data;
506 *aLen = size;
508 result = NS_OK;
510 } else {
511 // We really shouldn't ever get here
512 // but just in case
513 *aData = nullptr;
514 *aLen = 0;
515 LPVOID lpMsgBuf;
517 FormatMessageW(
518 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr,
519 GetLastError(),
520 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
521 (LPWSTR)&lpMsgBuf, 0, nullptr);
523 // Display the string.
524 MessageBoxW(nullptr, (LPCWSTR)lpMsgBuf, L"GetLastError",
525 MB_OK | MB_ICONINFORMATION);
527 // Free the buffer.
528 LocalFree(lpMsgBuf);
531 return result;
534 //-------------------------------------------------------------------------
535 nsresult nsClipboard::GetNativeDataOffClipboard(nsIWidget* aWidget,
536 UINT /*aIndex*/, UINT aFormat,
537 void** aData, uint32_t* aLen) {
538 MOZ_LOG(gWin32ClipboardLog, LogLevel::Debug,
539 ("%s: overload taking nsIWidget*.", __FUNCTION__));
541 HGLOBAL hglb;
542 nsresult result = NS_ERROR_FAILURE;
544 HWND nativeWin = nullptr;
545 if (::OpenClipboard(nativeWin)) {
546 hglb = ::GetClipboardData(aFormat);
547 result = GetGlobalData(hglb, aData, aLen);
548 ::CloseClipboard();
550 return result;
553 // See methods listed at
554 // <https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-idataobject#methods>.
555 static void LogIDataObjectMethodResult(const HRESULT aHres,
556 const nsCString& aMethodName) {
557 if (MOZ_LOG_TEST(gWin32ClipboardLog, LogLevel::Debug)) {
558 nsAutoCString hresString;
559 IDataObjectMethodResultToString(aHres, hresString);
560 MOZ_LOG(
561 gWin32ClipboardLog, LogLevel::Debug,
562 ("IDataObject::%s result: %s", aMethodName.get(), hresString.get()));
566 // Other apps can block access to the clipboard. This repeatedly calls
567 // `GetData` for a fixed number of times and should be called instead of
568 // `GetData`. See
569 // <https://docs.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-idataobject-getdata>.
570 // While Microsoft's documentation doesn't include `CLIPBRD_E_CANT_OPEN`
571 // explicitly, it allows it implicitly and in local experiments it was indeed
572 // returned.
573 static HRESULT RepeatedlyTryGetData(IDataObject& aDataObject, LPFORMATETC pFE,
574 LPSTGMEDIUM pSTM) {
575 return RepeatedlyTry(
576 [&aDataObject, &pFE, &pSTM]() { return aDataObject.GetData(pFE, pSTM); },
577 std::bind(LogIDataObjectMethodResult, std::placeholders::_1,
578 "GetData"_ns));
581 //-------------------------------------------------------------------------
582 // static
583 HRESULT nsClipboard::FillSTGMedium(IDataObject* aDataObject, UINT aFormat,
584 LPFORMATETC pFE, LPSTGMEDIUM pSTM,
585 DWORD aTymed) {
586 SET_FORMATETC(*pFE, aFormat, 0, DVASPECT_CONTENT, -1, aTymed);
588 // Starting by querying for the data to see if we can get it as from global
589 // memory
590 HRESULT hres = S_FALSE;
591 hres = aDataObject->QueryGetData(pFE);
592 LogIDataObjectMethodResult(hres, "QueryGetData"_ns);
593 if (S_OK == hres) {
594 hres = RepeatedlyTryGetData(*aDataObject, pFE, pSTM);
596 return hres;
599 //-------------------------------------------------------------------------
600 // If aFormat is CF_DIBV5, aMIMEImageFormat must be a type for which we have
601 // an image encoder (e.g. image/png).
602 // For other values of aFormat, it is OK to pass null for aMIMEImageFormat.
603 nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject* aDataObject,
604 UINT aIndex, UINT aFormat,
605 const char* aMIMEImageFormat,
606 void** aData, uint32_t* aLen) {
607 MOZ_LOG(gWin32ClipboardLog, LogLevel::Debug,
608 ("%s: overload taking IDataObject*.", __FUNCTION__));
610 nsresult result = NS_ERROR_FAILURE;
611 *aData = nullptr;
612 *aLen = 0;
614 if (!aDataObject) {
615 return result;
618 UINT format = aFormat;
619 HRESULT hres = S_FALSE;
621 // XXX at the moment we only support global memory transfers
622 // It is here where we will add support for native images
623 // and IStream
624 FORMATETC fe;
625 STGMEDIUM stm;
626 hres = FillSTGMedium(aDataObject, format, &fe, &stm, TYMED_HGLOBAL);
628 // Currently this is only handling TYMED_HGLOBAL data
629 // For Text, Dibs, Files, and generic data (like HTML)
630 if (S_OK == hres) {
631 static CLIPFORMAT fileDescriptorFlavorA =
632 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
633 static CLIPFORMAT fileDescriptorFlavorW =
634 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
635 static CLIPFORMAT fileFlavor =
636 ::RegisterClipboardFormat(CFSTR_FILECONTENTS);
637 static CLIPFORMAT preferredDropEffect =
638 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
640 switch (stm.tymed) {
641 case TYMED_HGLOBAL: {
642 switch (fe.cfFormat) {
643 case CF_TEXT: {
644 // Get the data out of the global data handle. The size we
645 // return should not include the null because the other
646 // platforms don't use nulls, so just return the length we get
647 // back from strlen(), since we know CF_TEXT is null
648 // terminated. Recall that GetGlobalData() returns the size of
649 // the allocated buffer, not the size of the data (on 98, these
650 // are not the same) so we can't use that.
651 uint32_t allocLen = 0;
652 if (NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen))) {
653 *aLen = strlen(reinterpret_cast<char*>(*aData));
654 result = NS_OK;
656 } break;
658 case CF_UNICODETEXT: {
659 // Get the data out of the global data handle. The size we
660 // return should not include the null because the other
661 // platforms don't use nulls, so just return the length we get
662 // back from strlen(), since we know CF_UNICODETEXT is null
663 // terminated. Recall that GetGlobalData() returns the size of
664 // the allocated buffer, not the size of the data (on 98, these
665 // are not the same) so we can't use that.
666 uint32_t allocLen = 0;
667 if (NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen))) {
668 *aLen = NS_strlen(reinterpret_cast<char16_t*>(*aData)) * 2;
669 result = NS_OK;
671 } break;
673 case CF_DIBV5:
674 if (aMIMEImageFormat) {
675 uint32_t allocLen = 0;
676 const char* clipboardData;
677 if (NS_SUCCEEDED(GetGlobalData(
678 stm.hGlobal, (void**)&clipboardData, &allocLen))) {
679 nsCOMPtr<imgIContainer> container;
680 nsCOMPtr<imgITools> imgTools =
681 do_CreateInstance("@mozilla.org/image/tools;1");
682 result = imgTools->DecodeImageFromBuffer(
683 clipboardData, allocLen,
684 nsLiteralCString(IMAGE_BMP_MS_CLIPBOARD),
685 getter_AddRefs(container));
686 if (NS_FAILED(result)) {
687 break;
690 nsAutoCString mimeType;
691 if (strcmp(aMIMEImageFormat, kJPGImageMime) == 0) {
692 mimeType.Assign(IMAGE_JPEG);
693 } else {
694 mimeType.Assign(aMIMEImageFormat);
697 nsCOMPtr<nsIInputStream> inputStream;
698 result = imgTools->EncodeImage(container, mimeType, u""_ns,
699 getter_AddRefs(inputStream));
700 if (NS_FAILED(result)) {
701 break;
704 if (!inputStream) {
705 result = NS_ERROR_FAILURE;
706 break;
709 *aData = inputStream.forget().take();
710 *aLen = sizeof(nsIInputStream*);
713 break;
715 case CF_HDROP: {
716 // in the case of a file drop, multiple files are stashed within a
717 // single data object. In order to match mozilla's D&D apis, we
718 // just pull out the file at the requested index, pretending as
719 // if there really are multiple drag items.
720 HDROP dropFiles = (HDROP)GlobalLock(stm.hGlobal);
722 UINT numFiles = ::DragQueryFileW(dropFiles, 0xFFFFFFFF, nullptr, 0);
723 NS_ASSERTION(numFiles > 0,
724 "File drop flavor, but no files...hmmmm");
725 NS_ASSERTION(aIndex < numFiles,
726 "Asked for a file index out of range of list");
727 if (numFiles > 0) {
728 UINT fileNameLen =
729 ::DragQueryFileW(dropFiles, aIndex, nullptr, 0);
730 wchar_t* buffer = reinterpret_cast<wchar_t*>(
731 moz_xmalloc((fileNameLen + 1) * sizeof(wchar_t)));
732 ::DragQueryFileW(dropFiles, aIndex, buffer, fileNameLen + 1);
733 *aData = buffer;
734 *aLen = fileNameLen * sizeof(char16_t);
735 result = NS_OK;
737 GlobalUnlock(stm.hGlobal);
739 } break;
741 default: {
742 if (fe.cfFormat == fileDescriptorFlavorA ||
743 fe.cfFormat == fileDescriptorFlavorW ||
744 fe.cfFormat == fileFlavor) {
745 NS_WARNING(
746 "Mozilla doesn't yet understand how to read this type of "
747 "file flavor");
748 } else {
749 // Get the data out of the global data handle. The size we
750 // return should not include the null because the other
751 // platforms don't use nulls, so just return the length we get
752 // back from strlen(), since we know CF_UNICODETEXT is null
753 // terminated. Recall that GetGlobalData() returns the size of
754 // the allocated buffer, not the size of the data (on 98, these
755 // are not the same) so we can't use that.
757 // NOTE: we are assuming that anything that falls into this
758 // default case is unicode. As we start to get more
759 // kinds of binary data, this may become an incorrect
760 // assumption. Stay tuned.
761 uint32_t allocLen = 0;
762 if (NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen))) {
763 if (fe.cfFormat == GetHtmlClipboardFormat()) {
764 // CF_HTML is actually UTF8, not unicode, so disregard the
765 // assumption above. We have to check the header for the
766 // actual length, and we'll do that in FindPlatformHTML().
767 // For now, return the allocLen. This case is mostly to
768 // ensure we don't try to call strlen on the buffer.
769 *aLen = allocLen;
770 } else if (fe.cfFormat == GetCustomClipboardFormat()) {
771 // Binary data
772 *aLen = allocLen;
773 } else if (fe.cfFormat == preferredDropEffect) {
774 // As per the MSDN doc entitled: "Shell Clipboard Formats"
775 // CFSTR_PREFERREDDROPEFFECT should return a DWORD
776 // Reference:
777 // http://msdn.microsoft.com/en-us/library/bb776902(v=vs.85).aspx
778 NS_ASSERTION(
779 allocLen == sizeof(DWORD),
780 "CFSTR_PREFERREDDROPEFFECT should return a DWORD");
781 *aLen = allocLen;
782 } else {
783 *aLen = NS_strlen(reinterpret_cast<char16_t*>(*aData)) *
784 sizeof(char16_t);
786 result = NS_OK;
789 } break;
790 } // switch
791 } break;
793 case TYMED_GDI: {
794 #ifdef DEBUG
795 MOZ_LOG(gWin32ClipboardLog, LogLevel::Info,
796 ("*********************** TYMED_GDI\n"));
797 #endif
798 } break;
800 default:
801 break;
802 } // switch
804 ReleaseStgMedium(&stm);
807 return result;
810 //-------------------------------------------------------------------------
811 nsresult nsClipboard::GetDataFromDataObject(IDataObject* aDataObject,
812 UINT anIndex, nsIWidget* aWindow,
813 nsITransferable* aTransferable) {
814 MOZ_LOG(gWin32ClipboardLog, LogLevel::Debug, ("%s", __FUNCTION__));
816 // make sure we have a good transferable
817 if (!aTransferable) {
818 return NS_ERROR_INVALID_ARG;
821 nsresult res = NS_ERROR_FAILURE;
823 // get flavor list that includes all flavors that can be written (including
824 // ones obtained through conversion)
825 nsTArray<nsCString> flavors;
826 res = aTransferable->FlavorsTransferableCanImport(flavors);
827 if (NS_FAILED(res)) {
828 return NS_ERROR_FAILURE;
831 // Walk through flavors and see which flavor is on the clipboard them on the
832 // native clipboard,
833 for (uint32_t i = 0; i < flavors.Length(); i++) {
834 nsCString& flavorStr = flavors[i];
835 UINT format = GetFormat(flavorStr.get());
837 // Try to get the data using the desired flavor. This might fail, but all is
838 // not lost.
839 void* data = nullptr;
840 uint32_t dataLen = 0;
841 bool dataFound = false;
842 if (nullptr != aDataObject) {
843 if (NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject, anIndex, format,
844 flavorStr.get(), &data,
845 &dataLen))) {
846 dataFound = true;
848 } else if (nullptr != aWindow) {
849 if (NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow, anIndex, format,
850 &data, &dataLen))) {
851 dataFound = true;
855 // This is our second chance to try to find some data, having not found it
856 // when directly asking for the flavor. Let's try digging around in other
857 // flavors to help satisfy our craving for data.
858 if (!dataFound) {
859 if (flavorStr.EqualsLiteral(kUnicodeMime)) {
860 dataFound =
861 FindUnicodeFromPlainText(aDataObject, anIndex, &data, &dataLen);
862 } else if (flavorStr.EqualsLiteral(kURLMime)) {
863 // drags from other windows apps expose the native
864 // CFSTR_INETURL{A,W} flavor
865 dataFound = FindURLFromNativeURL(aDataObject, anIndex, &data, &dataLen);
866 if (!dataFound) {
867 dataFound =
868 FindURLFromLocalFile(aDataObject, anIndex, &data, &dataLen);
871 } // if we try one last ditch effort to find our data
873 // Hopefully by this point we've found it and can go about our business
874 if (dataFound) {
875 nsCOMPtr<nsISupports> genericDataWrapper;
876 if (flavorStr.EqualsLiteral(kFileMime)) {
877 // we have a file path in |data|. Create an nsLocalFile object.
878 nsDependentString filepath(reinterpret_cast<char16_t*>(data));
879 nsCOMPtr<nsIFile> file;
880 if (NS_SUCCEEDED(
881 NS_NewLocalFile(filepath, false, getter_AddRefs(file)))) {
882 genericDataWrapper = do_QueryInterface(file);
884 free(data);
885 } else if (flavorStr.EqualsLiteral(kNativeHTMLMime)) {
886 uint32_t dummy;
887 // the editor folks want CF_HTML exactly as it's on the clipboard, no
888 // conversions, no fancy stuff. Pull it off the clipboard, stuff it into
889 // a wrapper and hand it back to them.
890 if (FindPlatformHTML(aDataObject, anIndex, &data, &dummy, &dataLen)) {
891 nsPrimitiveHelpers::CreatePrimitiveForData(
892 flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper));
893 } else {
894 free(data);
895 continue; // something wrong with this flavor, keep looking for other
896 // data
898 free(data);
899 } else if (flavorStr.EqualsLiteral(kHTMLMime)) {
900 uint32_t startOfData = 0;
901 // The JS folks want CF_HTML exactly as it is on the clipboard, but
902 // minus the CF_HTML header index information.
903 // It also needs to be converted to UTF16 and have linebreaks changed.
904 if (FindPlatformHTML(aDataObject, anIndex, &data, &startOfData,
905 &dataLen)) {
906 dataLen -= startOfData;
907 nsPrimitiveHelpers::CreatePrimitiveForCFHTML(
908 static_cast<char*>(data) + startOfData, &dataLen,
909 getter_AddRefs(genericDataWrapper));
910 } else {
911 free(data);
912 continue; // something wrong with this flavor, keep looking for other
913 // data
915 free(data);
916 } else if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
917 flavorStr.EqualsLiteral(kJPGImageMime) ||
918 flavorStr.EqualsLiteral(kPNGImageMime)) {
919 nsIInputStream* imageStream = reinterpret_cast<nsIInputStream*>(data);
920 genericDataWrapper = do_QueryInterface(imageStream);
921 NS_IF_RELEASE(imageStream);
922 } else {
923 // Treat custom types as a string of bytes.
924 if (!flavorStr.EqualsLiteral(kCustomTypesMime)) {
925 // we probably have some form of text. The DOM only wants LF, so
926 // convert from Win32 line endings to DOM line endings.
927 int32_t signedLen = static_cast<int32_t>(dataLen);
928 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &data,
929 &signedLen);
930 dataLen = signedLen;
932 if (flavorStr.EqualsLiteral(kRTFMime)) {
933 // RTF on Windows is known to sometimes deliver an extra null byte.
934 if (dataLen > 0 && static_cast<char*>(data)[dataLen - 1] == '\0') {
935 dataLen--;
940 nsPrimitiveHelpers::CreatePrimitiveForData(
941 flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper));
942 free(data);
945 NS_ASSERTION(genericDataWrapper,
946 "About to put null data into the transferable");
947 aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper);
948 res = NS_OK;
950 // we found one, get out of the loop
951 break;
953 } // foreach flavor
955 return res;
959 // FindPlatformHTML
961 // Someone asked for the OS CF_HTML flavor. We give it back to them exactly
962 // as-is.
964 bool nsClipboard ::FindPlatformHTML(IDataObject* inDataObject, UINT inIndex,
965 void** outData, uint32_t* outStartOfData,
966 uint32_t* outDataLen) {
967 // Reference: MSDN doc entitled "HTML Clipboard Format"
968 // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
969 // CF_HTML is UTF8, not unicode. We also can't rely on it being
970 // null-terminated so we have to check the CF_HTML header for the correct
971 // length. The length we return is the bytecount from the beginning of the
972 // selected data to the end of the selected data, without the null
973 // termination. Because it's UTF8, we're guaranteed the header is ASCII.
975 if (!outData || !*outData) {
976 return false;
979 char version[8] = {0};
980 int32_t startOfData = 0;
981 int32_t endOfData = 0;
982 int numFound =
983 sscanf((char*)*outData, "Version:%7s\nStartHTML:%d\nEndHTML:%d", version,
984 &startOfData, &endOfData);
986 if (numFound != 3 || startOfData < -1 || endOfData < -1) {
987 return false;
990 // Fixup the start and end markers if they have no context (set to -1)
991 if (startOfData == -1) {
992 startOfData = 0;
994 if (endOfData == -1) {
995 endOfData = *outDataLen;
998 // Make sure we were passed sane values within our buffer size.
999 // (Note that we've handled all cases of negative endOfData above, so we can
1000 // safely cast it to be unsigned here.)
1001 if (!endOfData || startOfData >= endOfData ||
1002 static_cast<uint32_t>(endOfData) > *outDataLen) {
1003 return false;
1006 // We want to return the buffer not offset by startOfData because it will be
1007 // parsed out later (probably by HTMLEditor::ParseCFHTML) when it is still
1008 // in CF_HTML format.
1010 // We return the byte offset from the start of the data buffer to where the
1011 // HTML data starts. The caller might want to extract the HTML only.
1012 *outStartOfData = startOfData;
1013 *outDataLen = endOfData;
1014 return true;
1018 // FindUnicodeFromPlainText
1020 // we are looking for text/unicode and we failed to find it on the clipboard
1021 // first, try again with text/plain. If that is present, convert it to unicode.
1023 bool nsClipboard ::FindUnicodeFromPlainText(IDataObject* inDataObject,
1024 UINT inIndex, void** outData,
1025 uint32_t* outDataLen) {
1026 MOZ_LOG(gWin32ClipboardLog, LogLevel::Debug, ("%s", __FUNCTION__));
1028 // we are looking for text/unicode and we failed to find it on the clipboard
1029 // first, try again with text/plain. If that is present, convert it to
1030 // unicode.
1031 nsresult rv =
1032 GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kTextMime),
1033 nullptr, outData, outDataLen);
1034 if (NS_FAILED(rv) || !*outData) {
1035 return false;
1038 const char* castedText = static_cast<char*>(*outData);
1039 nsAutoString tmp;
1040 rv = NS_CopyNativeToUnicode(nsDependentCSubstring(castedText, *outDataLen),
1041 tmp);
1042 if (NS_FAILED(rv)) {
1043 return false;
1046 // out with the old, in with the new
1047 free(*outData);
1048 *outData = ToNewUnicode(tmp);
1049 *outDataLen = tmp.Length() * sizeof(char16_t);
1051 return true;
1053 } // FindUnicodeFromPlainText
1056 // FindURLFromLocalFile
1058 // we are looking for a URL and couldn't find it, try again with looking for
1059 // a local file. If we have one, it may either be a normal file or an internet
1060 // shortcut. In both cases, however, we can get a URL (it will be a file:// url
1061 // in the local file case).
1063 bool nsClipboard ::FindURLFromLocalFile(IDataObject* inDataObject, UINT inIndex,
1064 void** outData, uint32_t* outDataLen) {
1065 MOZ_LOG(gWin32ClipboardLog, LogLevel::Debug, ("%s", __FUNCTION__));
1067 bool dataFound = false;
1069 nsresult loadResult =
1070 GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kFileMime),
1071 nullptr, outData, outDataLen);
1072 if (NS_SUCCEEDED(loadResult) && *outData) {
1073 // we have a file path in |data|. Is it an internet shortcut or a normal
1074 // file?
1075 const nsDependentString filepath(static_cast<char16_t*>(*outData));
1076 nsCOMPtr<nsIFile> file;
1077 nsresult rv = NS_NewLocalFile(filepath, true, getter_AddRefs(file));
1078 if (NS_FAILED(rv)) {
1079 free(*outData);
1080 return dataFound;
1083 if (IsInternetShortcut(filepath)) {
1084 free(*outData);
1085 nsAutoCString url;
1086 ResolveShortcut(file, url);
1087 if (!url.IsEmpty()) {
1088 // convert it to unicode and pass it out
1089 NS_ConvertUTF8toUTF16 urlString(url);
1090 // the internal mozilla URL format, text/x-moz-url, contains
1091 // URL\ntitle. We can guess the title from the file's name.
1092 nsAutoString title;
1093 file->GetLeafName(title);
1094 // We rely on IsInternetShortcut check that file has a .url extension.
1095 title.SetLength(title.Length() - 4);
1096 if (title.IsEmpty()) {
1097 title = urlString;
1099 *outData = ToNewUnicode(urlString + u"\n"_ns + title);
1100 *outDataLen =
1101 NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
1103 dataFound = true;
1105 } else {
1106 // we have a normal file, use some Necko objects to get our file path
1107 nsAutoCString urlSpec;
1108 NS_GetURLSpecFromFile(file, urlSpec);
1110 // convert it to unicode and pass it out
1111 free(*outData);
1112 *outData = UTF8ToNewUnicode(urlSpec);
1113 *outDataLen =
1114 NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
1115 dataFound = true;
1116 } // else regular file
1119 return dataFound;
1120 } // FindURLFromLocalFile
1123 // FindURLFromNativeURL
1125 // we are looking for a URL and couldn't find it using our internal
1126 // URL flavor, so look for it using the native URL flavor,
1127 // CF_INETURLSTRW (We don't handle CF_INETURLSTRA currently)
1129 bool nsClipboard ::FindURLFromNativeURL(IDataObject* inDataObject, UINT inIndex,
1130 void** outData, uint32_t* outDataLen) {
1131 MOZ_LOG(gWin32ClipboardLog, LogLevel::Debug, ("%s", __FUNCTION__));
1133 bool dataFound = false;
1135 void* tempOutData = nullptr;
1136 uint32_t tempDataLen = 0;
1138 nsresult loadResult = GetNativeDataOffClipboard(
1139 inDataObject, inIndex, ::RegisterClipboardFormat(CFSTR_INETURLW), nullptr,
1140 &tempOutData, &tempDataLen);
1141 if (NS_SUCCEEDED(loadResult) && tempOutData) {
1142 nsDependentString urlString(static_cast<char16_t*>(tempOutData));
1143 // the internal mozilla URL format, text/x-moz-url, contains
1144 // URL\ntitle. Since we don't actually have a title here,
1145 // just repeat the URL to fake it.
1146 *outData = ToNewUnicode(urlString + u"\n"_ns + urlString);
1147 *outDataLen =
1148 NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
1149 free(tempOutData);
1150 dataFound = true;
1151 } else {
1152 loadResult = GetNativeDataOffClipboard(
1153 inDataObject, inIndex, ::RegisterClipboardFormat(CFSTR_INETURLA),
1154 nullptr, &tempOutData, &tempDataLen);
1155 if (NS_SUCCEEDED(loadResult) && tempOutData) {
1156 // CFSTR_INETURLA is (currently) equal to CFSTR_SHELLURL which is equal to
1157 // CF_TEXT which is by definition ANSI encoded.
1158 nsCString urlUnescapedA;
1159 bool unescaped =
1160 NS_UnescapeURL(static_cast<char*>(tempOutData), tempDataLen,
1161 esc_OnlyNonASCII | esc_SkipControl, urlUnescapedA);
1163 nsString urlString;
1164 if (unescaped) {
1165 NS_CopyNativeToUnicode(urlUnescapedA, urlString);
1166 } else {
1167 NS_CopyNativeToUnicode(
1168 nsDependentCString(static_cast<char*>(tempOutData), tempDataLen),
1169 urlString);
1172 // the internal mozilla URL format, text/x-moz-url, contains
1173 // URL\ntitle. Since we don't actually have a title here,
1174 // just repeat the URL to fake it.
1175 *outData = ToNewUnicode(urlString + u"\n"_ns + urlString);
1176 *outDataLen =
1177 NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
1178 free(tempOutData);
1179 dataFound = true;
1183 return dataFound;
1184 } // FindURLFromNativeURL
1186 // Other apps can block access to the clipboard. This repeatedly
1187 // calls `::OleGetClipboard` for a fixed number of times and should be called
1188 // instead of `::OleGetClipboard`.
1189 static HRESULT RepeatedlyTryOleGetClipboard(IDataObject** aDataObj) {
1190 return RepeatedlyTry(::OleGetClipboard, LogOleGetClipboardResult, aDataObj);
1194 // ResolveShortcut
1196 void nsClipboard ::ResolveShortcut(nsIFile* aFile, nsACString& outURL) {
1197 nsCOMPtr<nsIFileProtocolHandler> fph;
1198 nsresult rv = NS_GetFileProtocolHandler(getter_AddRefs(fph));
1199 if (NS_FAILED(rv)) {
1200 return;
1203 nsCOMPtr<nsIURI> uri;
1204 rv = fph->ReadURLFile(aFile, getter_AddRefs(uri));
1205 if (NS_FAILED(rv)) {
1206 return;
1209 uri->GetSpec(outURL);
1210 } // ResolveShortcut
1213 // IsInternetShortcut
1215 // A file is an Internet Shortcut if it ends with .URL
1217 bool nsClipboard ::IsInternetShortcut(const nsAString& inFileName) {
1218 return StringEndsWith(inFileName, u".url"_ns,
1219 nsCaseInsensitiveStringComparator);
1220 } // IsInternetShortcut
1222 //-------------------------------------------------------------------------
1223 NS_IMETHODIMP
1224 nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable,
1225 int32_t aWhichClipboard) {
1226 MOZ_LOG(gWin32ClipboardLog, LogLevel::Debug,
1227 ("%s aWhichClipboard=%i", __FUNCTION__, aWhichClipboard));
1229 // make sure we have a good transferable
1230 if (!aTransferable || aWhichClipboard != kGlobalClipboard) {
1231 return NS_ERROR_FAILURE;
1234 nsresult res;
1236 // This makes sure we can use the OLE functionality for the clipboard
1237 IDataObject* dataObj;
1238 if (S_OK == RepeatedlyTryOleGetClipboard(&dataObj)) {
1239 // Use OLE IDataObject for clipboard operations
1240 MOZ_LOG(gWin32ClipboardLog, LogLevel::Verbose,
1241 ("%s: use OLE IDataObject.", __FUNCTION__));
1242 res = GetDataFromDataObject(dataObj, 0, nullptr, aTransferable);
1243 dataObj->Release();
1244 } else {
1245 // do it the old manual way
1246 res = GetDataFromDataObject(nullptr, 0, mWindow, aTransferable);
1248 return res;
1251 NS_IMETHODIMP
1252 nsClipboard::EmptyClipboard(int32_t aWhichClipboard) {
1253 // Some programs such as ZoneAlarm monitor clipboard usage and then open the
1254 // clipboard to scan it. If we i) empty and then ii) set data, then the
1255 // 'set data' can sometimes fail with access denied becacuse another program
1256 // has the clipboard open. So to avoid this race condition for OpenClipboard
1257 // we do not empty the clipboard when we're setting it.
1258 if (aWhichClipboard == kGlobalClipboard && !mEmptyingForSetData) {
1259 RepeatedlyTryOleSetClipboard(nullptr);
1261 return nsBaseClipboard::EmptyClipboard(aWhichClipboard);
1264 //-------------------------------------------------------------------------
1265 NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(
1266 const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard,
1267 bool* _retval) {
1268 *_retval = false;
1269 if (aWhichClipboard != kGlobalClipboard) {
1270 return NS_OK;
1273 for (auto& flavor : aFlavorList) {
1274 #ifdef DEBUG
1275 if (flavor.EqualsLiteral(kTextMime)) {
1276 NS_WARNING(
1277 "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode "
1278 "INSTEAD");
1280 #endif
1282 UINT format = GetFormat(flavor.get());
1283 if (IsClipboardFormatAvailable(format)) {
1284 *_retval = true;
1285 break;
1286 } else {
1287 // We haven't found the exact flavor the client asked for, but maybe we
1288 // can still find it from something else that's on the clipboard...
1289 if (flavor.EqualsLiteral(kUnicodeMime)) {
1290 // client asked for unicode and it wasn't present, check if we have
1291 // CF_TEXT. We'll handle the actual data substitution in the data
1292 // object.
1293 if (IsClipboardFormatAvailable(GetFormat(kTextMime))) {
1294 *_retval = true;
1295 break;
1301 return NS_OK;