widget/windows/nsClipboard.cpp

branch
TOR_BUG_9701
changeset 10
ac0c01689b40
equal deleted inserted replaced
-1:000000000000 0:934f409e61b5
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/. */
5
6 #include "nsClipboard.h"
7 #include <ole2.h>
8 #include <shlobj.h>
9 #include <intshcut.h>
10
11 // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
12 #include <shellapi.h>
13
14 #include "nsCOMPtr.h"
15 #include "nsDataObj.h"
16 #include "nsIClipboardOwner.h"
17 #include "nsString.h"
18 #include "nsNativeCharsetUtils.h"
19 #include "nsIFormatConverter.h"
20 #include "nsITransferable.h"
21 #include "nsCOMPtr.h"
22 #include "nsXPCOM.h"
23 #include "nsISupportsPrimitives.h"
24 #include "nsXPIDLString.h"
25 #include "nsReadableUtils.h"
26 #include "nsUnicharUtils.h"
27 #include "nsPrimitiveHelpers.h"
28 #include "nsImageClipboard.h"
29 #include "nsIWidget.h"
30 #include "nsIComponentManager.h"
31 #include "nsWidgetsCID.h"
32 #include "nsCRT.h"
33 #include "nsNetUtil.h"
34 #include "nsEscape.h"
35 #include "nsIObserverService.h"
36
37 #ifdef PR_LOGGING
38 PRLogModuleInfo* gWin32ClipboardLog = nullptr;
39 #endif
40
41 // oddly, this isn't in the MSVC headers anywhere.
42 UINT nsClipboard::CF_HTML = ::RegisterClipboardFormatW(L"HTML Format");
43
44
45 //-------------------------------------------------------------------------
46 //
47 // nsClipboard constructor
48 //
49 //-------------------------------------------------------------------------
50 nsClipboard::nsClipboard() : nsBaseClipboard()
51 {
52 #ifdef PR_LOGGING
53 if (!gWin32ClipboardLog) {
54 gWin32ClipboardLog = PR_NewLogModule("nsClipboard");
55 }
56 #endif
57
58 mIgnoreEmptyNotification = false;
59 mWindow = nullptr;
60
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_SHUTDOWN_OBSERVER_ID, PR_FALSE);
67 }
68
69 //-------------------------------------------------------------------------
70 // nsClipboard destructor
71 //-------------------------------------------------------------------------
72 nsClipboard::~nsClipboard()
73 {
74
75 }
76
77 NS_IMPL_ISUPPORTS_INHERITED(nsClipboard, nsBaseClipboard, nsIObserver)
78
79 NS_IMETHODIMP
80 nsClipboard::Observe(nsISupports *aSubject, const char *aTopic,
81 const char16_t *aData)
82 {
83 // This will be called on shutdown.
84 ::OleFlushClipboard();
85 ::CloseClipboard();
86
87 return NS_OK;
88 }
89
90 //-------------------------------------------------------------------------
91 UINT nsClipboard::GetFormat(const char* aMimeStr)
92 {
93 UINT format;
94
95 if (strcmp(aMimeStr, kTextMime) == 0)
96 format = CF_TEXT;
97 else if (strcmp(aMimeStr, kUnicodeMime) == 0)
98 format = CF_UNICODETEXT;
99 else if (strcmp(aMimeStr, kJPEGImageMime) == 0 ||
100 strcmp(aMimeStr, kJPGImageMime) == 0 ||
101 strcmp(aMimeStr, kPNGImageMime) == 0)
102 format = CF_DIBV5;
103 else if (strcmp(aMimeStr, kFileMime) == 0 ||
104 strcmp(aMimeStr, kFilePromiseMime) == 0)
105 format = CF_HDROP;
106 else if (strcmp(aMimeStr, kNativeHTMLMime) == 0)
107 format = CF_HTML;
108 else
109 format = ::RegisterClipboardFormatW(NS_ConvertASCIItoUTF16(aMimeStr).get());
110
111 return format;
112 }
113
114 //-------------------------------------------------------------------------
115 nsresult nsClipboard::CreateNativeDataObject(nsITransferable * aTransferable, IDataObject ** aDataObj, nsIURI * uri)
116 {
117 if (nullptr == aTransferable) {
118 return NS_ERROR_FAILURE;
119 }
120
121 // Create our native DataObject that implements
122 // the OLE IDataObject interface
123 nsDataObj * dataObj = new nsDataObj(uri);
124
125 if (!dataObj)
126 return NS_ERROR_OUT_OF_MEMORY;
127
128 dataObj->AddRef();
129
130 // Now set it up with all the right data flavors & enums
131 nsresult res = SetupNativeDataObject(aTransferable, dataObj);
132 if (NS_OK == res) {
133 *aDataObj = dataObj;
134 } else {
135 delete dataObj;
136 }
137 return res;
138 }
139
140 //-------------------------------------------------------------------------
141 nsresult nsClipboard::SetupNativeDataObject(nsITransferable * aTransferable, IDataObject * aDataObj)
142 {
143 if (nullptr == aTransferable || nullptr == aDataObj) {
144 return NS_ERROR_FAILURE;
145 }
146
147 nsDataObj * dObj = static_cast<nsDataObj *>(aDataObj);
148
149 // Now give the Transferable to the DataObject
150 // for getting the data out of it
151 dObj->SetTransferable(aTransferable);
152
153 // Get the transferable list of data flavors
154 nsCOMPtr<nsISupportsArray> dfList;
155 aTransferable->FlavorsTransferableCanExport(getter_AddRefs(dfList));
156
157 // Walk through flavors that contain data and register them
158 // into the DataObj as supported flavors
159 uint32_t i;
160 uint32_t cnt;
161 dfList->Count(&cnt);
162 for (i=0;i<cnt;i++) {
163 nsCOMPtr<nsISupports> genericFlavor;
164 dfList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
165 nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
166 if ( currentFlavor ) {
167 nsXPIDLCString flavorStr;
168 currentFlavor->ToString(getter_Copies(flavorStr));
169 UINT format = GetFormat(flavorStr);
170
171 // Now tell the native IDataObject about both our mime type and
172 // the native data format
173 FORMATETC fe;
174 SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
175 dObj->AddDataFlavor(flavorStr, &fe);
176
177 // Do various things internal to the implementation, like map one
178 // flavor to another or add additional flavors based on what's required
179 // for the win32 impl.
180 if ( strcmp(flavorStr, kUnicodeMime) == 0 ) {
181 // if we find text/unicode, also advertise text/plain (which we will convert
182 // on our own in nsDataObj::GetText().
183 FORMATETC textFE;
184 SET_FORMATETC(textFE, CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
185 dObj->AddDataFlavor(kTextMime, &textFE);
186 }
187 else if ( strcmp(flavorStr, kHTMLMime) == 0 ) {
188 // if we find text/html, also advertise win32's html flavor (which we will convert
189 // on our own in nsDataObj::GetText().
190 FORMATETC htmlFE;
191 SET_FORMATETC(htmlFE, CF_HTML, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
192 dObj->AddDataFlavor(kHTMLMime, &htmlFE);
193 }
194 else if ( strcmp(flavorStr, kURLMime) == 0 ) {
195 // if we're a url, in addition to also being text, we need to register
196 // the "file" flavors so that the win32 shell knows to create an internet
197 // shortcut when it sees one of these beasts.
198 FORMATETC shortcutFE;
199 SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
200 dObj->AddDataFlavor(kURLMime, &shortcutFE);
201 SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
202 dObj->AddDataFlavor(kURLMime, &shortcutFE);
203 SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
204 dObj->AddDataFlavor(kURLMime, &shortcutFE);
205 SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLA), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
206 dObj->AddDataFlavor(kURLMime, &shortcutFE);
207 SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLW), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
208 dObj->AddDataFlavor(kURLMime, &shortcutFE);
209 }
210 else if ( strcmp(flavorStr, kPNGImageMime) == 0 || strcmp(flavorStr, kJPEGImageMime) == 0 ||
211 strcmp(flavorStr, kJPGImageMime) == 0 || strcmp(flavorStr, kGIFImageMime) == 0 ||
212 strcmp(flavorStr, kNativeImageMime) == 0 ) {
213 // if we're an image, register the native bitmap flavor
214 FORMATETC imageFE;
215 // Add DIBv5
216 SET_FORMATETC(imageFE, CF_DIBV5, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
217 dObj->AddDataFlavor(flavorStr, &imageFE);
218 // Add DIBv3
219 SET_FORMATETC(imageFE, CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
220 dObj->AddDataFlavor(flavorStr, &imageFE);
221 }
222 else if ( strcmp(flavorStr, kFilePromiseMime) == 0 ) {
223 // if we're a file promise flavor, also register the
224 // CFSTR_PREFERREDDROPEFFECT format. The data object
225 // returns a value of DROPEFFECTS_MOVE to the drop target
226 // when it asks for the value of this format. This causes
227 // the file to be moved from the temporary location instead
228 // of being copied. The right thing to do here is to call
229 // SetData() on the data object and set the value of this format
230 // to DROPEFFECTS_MOVE on this particular data object. But,
231 // since all the other clipboard formats follow the model of setting
232 // data on the data object only when the drop object calls GetData(),
233 // I am leaving this format's value hard coded in the data object.
234 // We can change this if other consumers of this format get added to this
235 // codebase and they need different values.
236 FORMATETC shortcutFE;
237 SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
238 dObj->AddDataFlavor(kFilePromiseMime, &shortcutFE);
239 }
240 }
241 }
242
243 return NS_OK;
244 }
245
246 //-------------------------------------------------------------------------
247 NS_IMETHODIMP nsClipboard::SetNativeClipboardData ( int32_t aWhichClipboard )
248 {
249 if ( aWhichClipboard != kGlobalClipboard )
250 return NS_ERROR_FAILURE;
251
252 mIgnoreEmptyNotification = true;
253
254 // make sure we have a good transferable
255 if (nullptr == mTransferable) {
256 return NS_ERROR_FAILURE;
257 }
258
259 IDataObject * dataObj;
260 if ( NS_SUCCEEDED(CreateNativeDataObject(mTransferable, &dataObj, nullptr)) ) { // this add refs dataObj
261 ::OleSetClipboard(dataObj);
262 dataObj->Release();
263 } else {
264 // Clear the native clipboard
265 ::OleSetClipboard(nullptr);
266 }
267
268 mIgnoreEmptyNotification = false;
269
270 return NS_OK;
271 }
272
273
274 //-------------------------------------------------------------------------
275 nsresult nsClipboard::GetGlobalData(HGLOBAL aHGBL, void ** aData, uint32_t * aLen)
276 {
277 // Allocate a new memory buffer and copy the data from global memory.
278 // Recall that win98 allocates to nearest DWORD boundary. As a safety
279 // precaution, allocate an extra 2 bytes (but don't report them!) and
280 // null them out to ensure that all of our strlen calls will succeed.
281 nsresult result = NS_ERROR_FAILURE;
282 if (aHGBL != nullptr) {
283 LPSTR lpStr = (LPSTR) GlobalLock(aHGBL);
284 DWORD allocSize = GlobalSize(aHGBL);
285 char* data = static_cast<char*>(nsMemory::Alloc(allocSize + sizeof(char16_t)));
286 if ( data ) {
287 memcpy ( data, lpStr, allocSize );
288 data[allocSize] = data[allocSize + 1] = '\0'; // null terminate for safety
289
290 GlobalUnlock(aHGBL);
291 *aData = data;
292 *aLen = allocSize;
293
294 result = NS_OK;
295 }
296 } else {
297 #ifdef MOZ_METRO
298 return result;
299 #endif
300 // We really shouldn't ever get here
301 // but just in case
302 *aData = nullptr;
303 *aLen = 0;
304 LPVOID lpMsgBuf;
305
306 FormatMessageW(
307 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
308 nullptr,
309 GetLastError(),
310 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
311 (LPWSTR) &lpMsgBuf,
312 0,
313 nullptr
314 );
315
316 // Display the string.
317 MessageBoxW( nullptr, (LPCWSTR) lpMsgBuf, L"GetLastError",
318 MB_OK | MB_ICONINFORMATION );
319
320 // Free the buffer.
321 LocalFree( lpMsgBuf );
322 }
323
324 return result;
325 }
326
327 //-------------------------------------------------------------------------
328 nsresult nsClipboard::GetNativeDataOffClipboard(nsIWidget * aWidget, UINT /*aIndex*/, UINT aFormat, void ** aData, uint32_t * aLen)
329 {
330 HGLOBAL hglb;
331 nsresult result = NS_ERROR_FAILURE;
332
333 HWND nativeWin = nullptr;
334 if (::OpenClipboard(nativeWin)) {
335 hglb = ::GetClipboardData(aFormat);
336 result = GetGlobalData(hglb, aData, aLen);
337 ::CloseClipboard();
338 }
339 return result;
340 }
341
342 static void DisplayErrCode(HRESULT hres)
343 {
344 #if defined(DEBUG_rods) || defined(DEBUG_pinkerton)
345 if (hres == E_INVALIDARG) {
346 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("E_INVALIDARG\n"));
347 } else
348 if (hres == E_UNEXPECTED) {
349 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("E_UNEXPECTED\n"));
350 } else
351 if (hres == E_OUTOFMEMORY) {
352 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("E_OUTOFMEMORY\n"));
353 } else
354 if (hres == DV_E_LINDEX ) {
355 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_LINDEX\n"));
356 } else
357 if (hres == DV_E_FORMATETC) {
358 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_FORMATETC\n"));
359 } else
360 if (hres == DV_E_TYMED) {
361 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_TYMED\n"));
362 } else
363 if (hres == DV_E_DVASPECT) {
364 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_DVASPECT\n"));
365 } else
366 if (hres == OLE_E_NOTRUNNING) {
367 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("OLE_E_NOTRUNNING\n"));
368 } else
369 if (hres == STG_E_MEDIUMFULL) {
370 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("STG_E_MEDIUMFULL\n"));
371 } else
372 if (hres == DV_E_CLIPFORMAT) {
373 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_CLIPFORMAT\n"));
374 } else
375 if (hres == S_OK) {
376 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("S_OK\n"));
377 } else {
378 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS,
379 ("****** DisplayErrCode 0x%X\n", hres));
380 }
381 #endif
382 }
383
384 //-------------------------------------------------------------------------
385 static HRESULT FillSTGMedium(IDataObject * aDataObject, UINT aFormat, LPFORMATETC pFE, LPSTGMEDIUM pSTM, DWORD aTymed)
386 {
387 SET_FORMATETC(*pFE, aFormat, 0, DVASPECT_CONTENT, -1, aTymed);
388
389 // Starting by querying for the data to see if we can get it as from global memory
390 HRESULT hres = S_FALSE;
391 hres = aDataObject->QueryGetData(pFE);
392 DisplayErrCode(hres);
393 if (S_OK == hres) {
394 hres = aDataObject->GetData(pFE, pSTM);
395 DisplayErrCode(hres);
396 }
397 return hres;
398 }
399
400
401 //-------------------------------------------------------------------------
402 // If aFormat is CF_DIBV5, aMIMEImageFormat must be a type for which we have
403 // an image encoder (e.g. image/png).
404 // For other values of aFormat, it is OK to pass null for aMIMEImageFormat.
405 nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject * aDataObject, UINT aIndex, UINT aFormat, const char * aMIMEImageFormat, void ** aData, uint32_t * aLen)
406 {
407 nsresult result = NS_ERROR_FAILURE;
408 *aData = nullptr;
409 *aLen = 0;
410
411 if ( !aDataObject )
412 return result;
413
414 UINT format = aFormat;
415 HRESULT hres = S_FALSE;
416
417 // XXX at the moment we only support global memory transfers
418 // It is here where we will add support for native images
419 // and IStream
420 FORMATETC fe;
421 STGMEDIUM stm;
422 hres = FillSTGMedium(aDataObject, format, &fe, &stm, TYMED_HGLOBAL);
423
424 // Currently this is only handling TYMED_HGLOBAL data
425 // For Text, Dibs, Files, and generic data (like HTML)
426 if (S_OK == hres) {
427 static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA );
428 static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW );
429 static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS );
430 static CLIPFORMAT preferredDropEffect = ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
431
432 switch (stm.tymed) {
433 case TYMED_HGLOBAL:
434 {
435 switch (fe.cfFormat) {
436 case CF_TEXT:
437 {
438 // Get the data out of the global data handle. The size we return
439 // should not include the null because the other platforms don't
440 // use nulls, so just return the length we get back from strlen(),
441 // since we know CF_TEXT is null terminated. Recall that GetGlobalData()
442 // returns the size of the allocated buffer, not the size of the data
443 // (on 98, these are not the same) so we can't use that.
444 uint32_t allocLen = 0;
445 if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) {
446 *aLen = strlen ( reinterpret_cast<char*>(*aData) );
447 result = NS_OK;
448 }
449 } break;
450
451 case CF_UNICODETEXT:
452 {
453 // Get the data out of the global data handle. The size we return
454 // should not include the null because the other platforms don't
455 // use nulls, so just return the length we get back from strlen(),
456 // since we know CF_UNICODETEXT is null terminated. Recall that GetGlobalData()
457 // returns the size of the allocated buffer, not the size of the data
458 // (on 98, these are not the same) so we can't use that.
459 uint32_t allocLen = 0;
460 if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) {
461 *aLen = NS_strlen(reinterpret_cast<char16_t*>(*aData)) * 2;
462 result = NS_OK;
463 }
464 } break;
465
466 case CF_DIBV5:
467 if (aMIMEImageFormat)
468 {
469 uint32_t allocLen = 0;
470 unsigned char * clipboardData;
471 if (NS_SUCCEEDED(GetGlobalData(stm.hGlobal, (void **)&clipboardData, &allocLen)))
472 {
473 nsImageFromClipboard converter;
474 nsIInputStream * inputStream;
475 converter.GetEncodedImageStream(clipboardData, aMIMEImageFormat, &inputStream); // addrefs for us, don't release
476 if ( inputStream ) {
477 *aData = inputStream;
478 *aLen = sizeof(nsIInputStream*);
479 result = NS_OK;
480 }
481 }
482 } break;
483
484 case CF_HDROP :
485 {
486 // in the case of a file drop, multiple files are stashed within a
487 // single data object. In order to match mozilla's D&D apis, we
488 // just pull out the file at the requested index, pretending as
489 // if there really are multiple drag items.
490 HDROP dropFiles = (HDROP) GlobalLock(stm.hGlobal);
491
492 UINT numFiles = ::DragQueryFileW(dropFiles, 0xFFFFFFFF, nullptr, 0);
493 NS_ASSERTION ( numFiles > 0, "File drop flavor, but no files...hmmmm" );
494 NS_ASSERTION ( aIndex < numFiles, "Asked for a file index out of range of list" );
495 if (numFiles > 0) {
496 UINT fileNameLen = ::DragQueryFileW(dropFiles, aIndex, nullptr, 0);
497 wchar_t* buffer = reinterpret_cast<wchar_t*>(nsMemory::Alloc((fileNameLen + 1) * sizeof(wchar_t)));
498 if ( buffer ) {
499 ::DragQueryFileW(dropFiles, aIndex, buffer, fileNameLen + 1);
500 *aData = buffer;
501 *aLen = fileNameLen * sizeof(char16_t);
502 result = NS_OK;
503 }
504 else
505 result = NS_ERROR_OUT_OF_MEMORY;
506 }
507 GlobalUnlock (stm.hGlobal) ;
508
509 } break;
510
511 default: {
512 if ( fe.cfFormat == fileDescriptorFlavorA || fe.cfFormat == fileDescriptorFlavorW || fe.cfFormat == fileFlavor ) {
513 NS_WARNING ( "Mozilla doesn't yet understand how to read this type of file flavor" );
514 }
515 else
516 {
517 // Get the data out of the global data handle. The size we return
518 // should not include the null because the other platforms don't
519 // use nulls, so just return the length we get back from strlen(),
520 // since we know CF_UNICODETEXT is null terminated. Recall that GetGlobalData()
521 // returns the size of the allocated buffer, not the size of the data
522 // (on 98, these are not the same) so we can't use that.
523 //
524 // NOTE: we are assuming that anything that falls into this default case
525 // is unicode. As we start to get more kinds of binary data, this
526 // may become an incorrect assumption. Stay tuned.
527 uint32_t allocLen = 0;
528 if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) {
529 if ( fe.cfFormat == CF_HTML ) {
530 // CF_HTML is actually UTF8, not unicode, so disregard the assumption
531 // above. We have to check the header for the actual length, and we'll
532 // do that in FindPlatformHTML(). For now, return the allocLen. This
533 // case is mostly to ensure we don't try to call strlen on the buffer.
534 *aLen = allocLen;
535 } else if (fe.cfFormat == preferredDropEffect) {
536 // As per the MSDN doc entitled: "Shell Clipboard Formats"
537 // CFSTR_PREFERREDDROPEFFECT should return a DWORD
538 // Reference: http://msdn.microsoft.com/en-us/library/bb776902(v=vs.85).aspx
539 NS_ASSERTION(allocLen == sizeof(DWORD),
540 "CFSTR_PREFERREDDROPEFFECT should return a DWORD");
541 *aLen = allocLen;
542 } else {
543 *aLen = NS_strlen(reinterpret_cast<char16_t*>(*aData)) *
544 sizeof(char16_t);
545 }
546 result = NS_OK;
547 }
548 }
549 } break;
550 } // switch
551 } break;
552
553 case TYMED_GDI:
554 {
555 #ifdef DEBUG
556 PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS,
557 ("*********************** TYMED_GDI\n"));
558 #endif
559 } break;
560
561 default:
562 break;
563 } //switch
564
565 ReleaseStgMedium(&stm);
566 }
567
568 return result;
569 }
570
571
572 //-------------------------------------------------------------------------
573 nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject,
574 UINT anIndex,
575 nsIWidget * aWindow,
576 nsITransferable * aTransferable)
577 {
578 // make sure we have a good transferable
579 if ( !aTransferable )
580 return NS_ERROR_INVALID_ARG;
581
582 nsresult res = NS_ERROR_FAILURE;
583
584 // get flavor list that includes all flavors that can be written (including ones
585 // obtained through conversion)
586 nsCOMPtr<nsISupportsArray> flavorList;
587 res = aTransferable->FlavorsTransferableCanImport ( getter_AddRefs(flavorList) );
588 if ( NS_FAILED(res) )
589 return NS_ERROR_FAILURE;
590
591 // Walk through flavors and see which flavor is on the clipboard them on the native clipboard,
592 uint32_t i;
593 uint32_t cnt;
594 flavorList->Count(&cnt);
595 for (i=0;i<cnt;i++) {
596 nsCOMPtr<nsISupports> genericFlavor;
597 flavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
598 nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
599 if ( currentFlavor ) {
600 nsXPIDLCString flavorStr;
601 currentFlavor->ToString(getter_Copies(flavorStr));
602 UINT format = GetFormat(flavorStr);
603
604 // Try to get the data using the desired flavor. This might fail, but all is
605 // not lost.
606 void* data = nullptr;
607 uint32_t dataLen = 0;
608 bool dataFound = false;
609 if (nullptr != aDataObject) {
610 if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject, anIndex, format, flavorStr, &data, &dataLen)) )
611 dataFound = true;
612 }
613 else if (nullptr != aWindow) {
614 if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow, anIndex, format, &data, &dataLen)) )
615 dataFound = true;
616 }
617
618 // This is our second chance to try to find some data, having not found it
619 // when directly asking for the flavor. Let's try digging around in other
620 // flavors to help satisfy our craving for data.
621 if ( !dataFound ) {
622 if ( strcmp(flavorStr, kUnicodeMime) == 0 )
623 dataFound = FindUnicodeFromPlainText ( aDataObject, anIndex, &data, &dataLen );
624 else if ( strcmp(flavorStr, kURLMime) == 0 ) {
625 // drags from other windows apps expose the native
626 // CFSTR_INETURL{A,W} flavor
627 dataFound = FindURLFromNativeURL ( aDataObject, anIndex, &data, &dataLen );
628 if ( !dataFound )
629 dataFound = FindURLFromLocalFile ( aDataObject, anIndex, &data, &dataLen );
630 }
631 } // if we try one last ditch effort to find our data
632
633 // Hopefully by this point we've found it and can go about our business
634 if ( dataFound ) {
635 nsCOMPtr<nsISupports> genericDataWrapper;
636 if ( strcmp(flavorStr, kFileMime) == 0 ) {
637 // we have a file path in |data|. Create an nsLocalFile object.
638 nsDependentString filepath(reinterpret_cast<char16_t*>(data));
639 nsCOMPtr<nsIFile> file;
640 if ( NS_SUCCEEDED(NS_NewLocalFile(filepath, false, getter_AddRefs(file))) )
641 genericDataWrapper = do_QueryInterface(file);
642 nsMemory::Free(data);
643 }
644 else if ( strcmp(flavorStr, kNativeHTMLMime) == 0) {
645 // the editor folks want CF_HTML exactly as it's on the clipboard, no conversions,
646 // no fancy stuff. Pull it off the clipboard, stuff it into a wrapper and hand
647 // it back to them.
648 if ( FindPlatformHTML(aDataObject, anIndex, &data, &dataLen) )
649 nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper) );
650 else
651 {
652 nsMemory::Free(data);
653 continue; // something wrong with this flavor, keep looking for other data
654 }
655 nsMemory::Free(data);
656 }
657 else if ( strcmp(flavorStr, kJPEGImageMime) == 0 ||
658 strcmp(flavorStr, kJPGImageMime) == 0 ||
659 strcmp(flavorStr, kPNGImageMime) == 0) {
660 nsIInputStream * imageStream = reinterpret_cast<nsIInputStream*>(data);
661 genericDataWrapper = do_QueryInterface(imageStream);
662 NS_IF_RELEASE(imageStream);
663 }
664 else {
665 // we probably have some form of text. The DOM only wants LF, so convert from Win32 line
666 // endings to DOM line endings.
667 int32_t signedLen = static_cast<int32_t>(dataLen);
668 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks ( flavorStr, &data, &signedLen );
669 dataLen = signedLen;
670
671 nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper) );
672 nsMemory::Free(data);
673 }
674
675 NS_ASSERTION ( genericDataWrapper, "About to put null data into the transferable" );
676 aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLen);
677 res = NS_OK;
678
679 // we found one, get out of the loop
680 break;
681 }
682
683 }
684 } // foreach flavor
685
686 return res;
687
688 }
689
690
691
692 //
693 // FindPlatformHTML
694 //
695 // Someone asked for the OS CF_HTML flavor. We give it back to them exactly as-is.
696 //
697 bool
698 nsClipboard :: FindPlatformHTML ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen )
699 {
700 // Reference: MSDN doc entitled "HTML Clipboard Format"
701 // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
702 // CF_HTML is UTF8, not unicode. We also can't rely on it being null-terminated
703 // so we have to check the CF_HTML header for the correct length.
704 // The length we return is the bytecount from the beginning of the selected data to the end
705 // of the selected data, without the null termination. Because it's UTF8, we're guaranteed
706 // the header is ASCII.
707
708 if (!outData || !*outData) {
709 return false;
710 }
711
712 char version[8] = { 0 };
713 int32_t startOfData = 0;
714 int32_t endOfData = 0;
715 int numFound = sscanf((char*)*outData, "Version:%7s\nStartHTML:%d\nEndHTML:%d",
716 version, &startOfData, &endOfData);
717
718 if (numFound != 3 || startOfData < -1 || endOfData < -1) {
719 return false;
720 }
721
722 // Fixup the start and end markers if they have no context (set to -1)
723 if (startOfData == -1) {
724 startOfData = 0;
725 }
726 if (endOfData == -1) {
727 endOfData = *outDataLen;
728 }
729
730 // Make sure we were passed sane values within our buffer size.
731 // (Note that we've handled all cases of negative endOfData above, so we can
732 // safely cast it to be unsigned here.)
733 if (!endOfData || startOfData >= endOfData ||
734 static_cast<uint32_t>(endOfData) > *outDataLen) {
735 return false;
736 }
737
738 // We want to return the buffer not offset by startOfData because it will be
739 // parsed out later (probably by nsHTMLEditor::ParseCFHTML) when it is still
740 // in CF_HTML format.
741 *outDataLen = endOfData;
742 return true;
743 }
744
745
746 //
747 // FindUnicodeFromPlainText
748 //
749 // we are looking for text/unicode and we failed to find it on the clipboard first,
750 // try again with text/plain. If that is present, convert it to unicode.
751 //
752 bool
753 nsClipboard :: FindUnicodeFromPlainText ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen )
754 {
755 bool dataFound = false;
756
757 // we are looking for text/unicode and we failed to find it on the clipboard first,
758 // try again with text/plain. If that is present, convert it to unicode.
759 nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kTextMime), nullptr, outData, outDataLen);
760 if ( NS_SUCCEEDED(loadResult) && *outData ) {
761 const char* castedText = reinterpret_cast<char*>(*outData);
762 char16_t* convertedText = nullptr;
763 int32_t convertedTextLen = 0;
764 nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode ( castedText, *outDataLen,
765 &convertedText, &convertedTextLen );
766 if ( convertedText ) {
767 // out with the old, in with the new
768 nsMemory::Free(*outData);
769 *outData = convertedText;
770 *outDataLen = convertedTextLen * sizeof(char16_t);
771 dataFound = true;
772 }
773 } // if plain text data on clipboard
774
775 return dataFound;
776
777 } // FindUnicodeFromPlainText
778
779
780 //
781 // FindURLFromLocalFile
782 //
783 // we are looking for a URL and couldn't find it, try again with looking for
784 // a local file. If we have one, it may either be a normal file or an internet shortcut.
785 // In both cases, however, we can get a URL (it will be a file:// url in the
786 // local file case).
787 //
788 bool
789 nsClipboard :: FindURLFromLocalFile ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen )
790 {
791 bool dataFound = false;
792
793 nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kFileMime), nullptr, outData, outDataLen);
794 if ( NS_SUCCEEDED(loadResult) && *outData ) {
795 // we have a file path in |data|. Is it an internet shortcut or a normal file?
796 const nsDependentString filepath(static_cast<char16_t*>(*outData));
797 nsCOMPtr<nsIFile> file;
798 nsresult rv = NS_NewLocalFile(filepath, true, getter_AddRefs(file));
799 if (NS_FAILED(rv)) {
800 nsMemory::Free(*outData);
801 return dataFound;
802 }
803
804 if ( IsInternetShortcut(filepath) ) {
805 nsMemory::Free(*outData);
806 nsAutoCString url;
807 ResolveShortcut( file, url );
808 if ( !url.IsEmpty() ) {
809 // convert it to unicode and pass it out
810 nsDependentString urlString(UTF8ToNewUnicode(url));
811 // the internal mozilla URL format, text/x-moz-url, contains
812 // URL\ntitle. We can guess the title from the file's name.
813 nsAutoString title;
814 file->GetLeafName(title);
815 // We rely on IsInternetShortcut check that file has a .url extension.
816 title.SetLength(title.Length() - 4);
817 if (title.IsEmpty())
818 title = urlString;
819 *outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + title);
820 *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
821
822 dataFound = true;
823 }
824 }
825 else {
826 // we have a normal file, use some Necko objects to get our file path
827 nsAutoCString urlSpec;
828 NS_GetURLSpecFromFile(file, urlSpec);
829
830 // convert it to unicode and pass it out
831 nsMemory::Free(*outData);
832 *outData = UTF8ToNewUnicode(urlSpec);
833 *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
834 dataFound = true;
835 } // else regular file
836 }
837
838 return dataFound;
839 } // FindURLFromLocalFile
840
841 //
842 // FindURLFromNativeURL
843 //
844 // we are looking for a URL and couldn't find it using our internal
845 // URL flavor, so look for it using the native URL flavor,
846 // CF_INETURLSTRW (We don't handle CF_INETURLSTRA currently)
847 //
848 bool
849 nsClipboard :: FindURLFromNativeURL ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen )
850 {
851 bool dataFound = false;
852
853 void* tempOutData = nullptr;
854 uint32_t tempDataLen = 0;
855
856 nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, ::RegisterClipboardFormat(CFSTR_INETURLW), nullptr, &tempOutData, &tempDataLen);
857 if ( NS_SUCCEEDED(loadResult) && tempOutData ) {
858 nsDependentString urlString(static_cast<char16_t*>(tempOutData));
859 // the internal mozilla URL format, text/x-moz-url, contains
860 // URL\ntitle. Since we don't actually have a title here,
861 // just repeat the URL to fake it.
862 *outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + urlString);
863 *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
864 nsMemory::Free(tempOutData);
865 dataFound = true;
866 }
867 else {
868 loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, ::RegisterClipboardFormat(CFSTR_INETURLA), nullptr, &tempOutData, &tempDataLen);
869 if ( NS_SUCCEEDED(loadResult) && tempOutData ) {
870 // CFSTR_INETURLA is (currently) equal to CFSTR_SHELLURL which is equal to CF_TEXT
871 // which is by definition ANSI encoded.
872 nsCString urlUnescapedA;
873 bool unescaped = NS_UnescapeURL(static_cast<char*>(tempOutData), tempDataLen, esc_OnlyNonASCII | esc_SkipControl, urlUnescapedA);
874
875 nsString urlString;
876 if (unescaped)
877 NS_CopyNativeToUnicode(urlUnescapedA, urlString);
878 else
879 NS_CopyNativeToUnicode(nsDependentCString(static_cast<char*>(tempOutData), tempDataLen), urlString);
880
881 // the internal mozilla URL format, text/x-moz-url, contains
882 // URL\ntitle. Since we don't actually have a title here,
883 // just repeat the URL to fake it.
884 *outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + urlString);
885 *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
886 nsMemory::Free(tempOutData);
887 dataFound = true;
888 }
889 }
890
891 return dataFound;
892 } // FindURLFromNativeURL
893
894 //
895 // ResolveShortcut
896 //
897 void
898 nsClipboard :: ResolveShortcut ( nsIFile* aFile, nsACString& outURL )
899 {
900 nsCOMPtr<nsIFileProtocolHandler> fph;
901 nsresult rv = NS_GetFileProtocolHandler(getter_AddRefs(fph));
902 if (NS_FAILED(rv))
903 return;
904
905 nsCOMPtr<nsIURI> uri;
906 rv = fph->ReadURLFile(aFile, getter_AddRefs(uri));
907 if (NS_FAILED(rv))
908 return;
909
910 uri->GetSpec(outURL);
911 } // ResolveShortcut
912
913
914 //
915 // IsInternetShortcut
916 //
917 // A file is an Internet Shortcut if it ends with .URL
918 //
919 bool
920 nsClipboard :: IsInternetShortcut ( const nsAString& inFileName )
921 {
922 return StringEndsWith(inFileName, NS_LITERAL_STRING(".url"), nsCaseInsensitiveStringComparator());
923 } // IsInternetShortcut
924
925
926 //-------------------------------------------------------------------------
927 NS_IMETHODIMP
928 nsClipboard::GetNativeClipboardData ( nsITransferable * aTransferable, int32_t aWhichClipboard )
929 {
930 // make sure we have a good transferable
931 if ( !aTransferable || aWhichClipboard != kGlobalClipboard )
932 return NS_ERROR_FAILURE;
933
934 nsresult res;
935
936 // This makes sure we can use the OLE functionality for the clipboard
937 IDataObject * dataObj;
938 if (S_OK == ::OleGetClipboard(&dataObj)) {
939 // Use OLE IDataObject for clipboard operations
940 res = GetDataFromDataObject(dataObj, 0, nullptr, aTransferable);
941 dataObj->Release();
942 }
943 else {
944 // do it the old manual way
945 res = GetDataFromDataObject(nullptr, 0, mWindow, aTransferable);
946 }
947 return res;
948
949 }
950
951 NS_IMETHODIMP
952 nsClipboard::EmptyClipboard(int32_t aWhichClipboard)
953 {
954 // Some programs such as ZoneAlarm monitor clipboard usage and then open the
955 // clipboard to scan it. If we i) empty and then ii) set data, then the
956 // 'set data' can sometimes fail with access denied becacuse another program
957 // has the clipboard open. So to avoid this race condition for OpenClipboard
958 // we do not empty the clipboard when we're setting it.
959 if (aWhichClipboard == kGlobalClipboard && !mEmptyingForSetData) {
960 OleSetClipboard(nullptr);
961 }
962 return nsBaseClipboard::EmptyClipboard(aWhichClipboard);
963 }
964
965 //-------------------------------------------------------------------------
966 NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(const char** aFlavorList,
967 uint32_t aLength,
968 int32_t aWhichClipboard,
969 bool *_retval)
970 {
971 *_retval = false;
972 if (aWhichClipboard != kGlobalClipboard || !aFlavorList)
973 return NS_OK;
974
975 for (uint32_t i = 0;i < aLength; ++i) {
976 #ifdef DEBUG
977 if (strcmp(aFlavorList[i], kTextMime) == 0)
978 NS_WARNING ( "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD" );
979 #endif
980
981 UINT format = GetFormat(aFlavorList[i]);
982 if (IsClipboardFormatAvailable(format)) {
983 *_retval = true;
984 break;
985 }
986 else {
987 // We haven't found the exact flavor the client asked for, but maybe we can
988 // still find it from something else that's on the clipboard...
989 if (strcmp(aFlavorList[i], kUnicodeMime) == 0) {
990 // client asked for unicode and it wasn't present, check if we have CF_TEXT.
991 // We'll handle the actual data substitution in the data object.
992 if (IsClipboardFormatAvailable(GetFormat(kTextMime)))
993 *_retval = true;
994 }
995 }
996 }
997
998 return NS_OK;
999 }

mercurial