|
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 "mozilla/ArrayUtils.h" |
|
7 |
|
8 #include <ole2.h> |
|
9 #include <shlobj.h> |
|
10 |
|
11 #include "nsDataObj.h" |
|
12 #include "nsClipboard.h" |
|
13 #include "nsReadableUtils.h" |
|
14 #include "nsITransferable.h" |
|
15 #include "nsISupportsPrimitives.h" |
|
16 #include "IEnumFE.h" |
|
17 #include "nsPrimitiveHelpers.h" |
|
18 #include "nsXPIDLString.h" |
|
19 #include "nsImageClipboard.h" |
|
20 #include "nsCRT.h" |
|
21 #include "nsPrintfCString.h" |
|
22 #include "nsIStringBundle.h" |
|
23 #include "nsEscape.h" |
|
24 #include "nsIURL.h" |
|
25 #include "nsNetUtil.h" |
|
26 #include "nsXPCOMStrings.h" |
|
27 #include "nscore.h" |
|
28 #include "nsDirectoryServiceDefs.h" |
|
29 #include "nsITimer.h" |
|
30 #include "nsThreadUtils.h" |
|
31 #include "mozilla/Preferences.h" |
|
32 |
|
33 #include "WinUtils.h" |
|
34 #include "mozilla/LazyIdleThread.h" |
|
35 #include "mozilla/WindowsVersion.h" |
|
36 #include <algorithm> |
|
37 |
|
38 |
|
39 using namespace mozilla; |
|
40 using namespace mozilla::widget; |
|
41 |
|
42 #define DEFAULT_THREAD_TIMEOUT_MS 30000 |
|
43 |
|
44 NS_IMPL_ISUPPORTS(nsDataObj::CStream, nsIStreamListener) |
|
45 |
|
46 //----------------------------------------------------------------------------- |
|
47 // CStream implementation |
|
48 nsDataObj::CStream::CStream() : |
|
49 mChannelRead(false), |
|
50 mStreamRead(0) |
|
51 { |
|
52 } |
|
53 |
|
54 //----------------------------------------------------------------------------- |
|
55 nsDataObj::CStream::~CStream() |
|
56 { |
|
57 } |
|
58 |
|
59 //----------------------------------------------------------------------------- |
|
60 // helper - initializes the stream |
|
61 nsresult nsDataObj::CStream::Init(nsIURI *pSourceURI) |
|
62 { |
|
63 nsresult rv; |
|
64 rv = NS_NewChannel(getter_AddRefs(mChannel), pSourceURI, |
|
65 nullptr, nullptr, nullptr, |
|
66 nsIRequest::LOAD_FROM_CACHE); |
|
67 NS_ENSURE_SUCCESS(rv, rv); |
|
68 rv = mChannel->AsyncOpen(this, nullptr); |
|
69 NS_ENSURE_SUCCESS(rv, rv); |
|
70 return NS_OK; |
|
71 } |
|
72 |
|
73 //----------------------------------------------------------------------------- |
|
74 // IUnknown's QueryInterface, nsISupport's AddRef and Release are shared by |
|
75 // IUnknown and nsIStreamListener. |
|
76 STDMETHODIMP nsDataObj::CStream::QueryInterface(REFIID refiid, void** ppvResult) |
|
77 { |
|
78 *ppvResult = nullptr; |
|
79 if (IID_IUnknown == refiid || |
|
80 refiid == IID_IStream) |
|
81 |
|
82 { |
|
83 *ppvResult = this; |
|
84 } |
|
85 |
|
86 if (nullptr != *ppvResult) |
|
87 { |
|
88 ((LPUNKNOWN)*ppvResult)->AddRef(); |
|
89 return S_OK; |
|
90 } |
|
91 |
|
92 return E_NOINTERFACE; |
|
93 } |
|
94 |
|
95 // nsIStreamListener implementation |
|
96 NS_IMETHODIMP |
|
97 nsDataObj::CStream::OnDataAvailable(nsIRequest *aRequest, |
|
98 nsISupports *aContext, |
|
99 nsIInputStream *aInputStream, |
|
100 uint64_t aOffset, // offset within the stream |
|
101 uint32_t aCount) // bytes available on this call |
|
102 { |
|
103 // Extend the write buffer for the incoming data. |
|
104 uint8_t* buffer = mChannelData.AppendElements(aCount); |
|
105 if (buffer == nullptr) |
|
106 return NS_ERROR_OUT_OF_MEMORY; |
|
107 NS_ASSERTION((mChannelData.Length() == (aOffset + aCount)), |
|
108 "stream length mismatch w/write buffer"); |
|
109 |
|
110 // Read() may not return aCount on a single call, so loop until we've |
|
111 // accumulated all the data OnDataAvailable has promised. |
|
112 nsresult rv; |
|
113 uint32_t odaBytesReadTotal = 0; |
|
114 do { |
|
115 uint32_t bytesReadByCall = 0; |
|
116 rv = aInputStream->Read((char*)(buffer + odaBytesReadTotal), |
|
117 aCount, &bytesReadByCall); |
|
118 odaBytesReadTotal += bytesReadByCall; |
|
119 } while (aCount < odaBytesReadTotal && NS_SUCCEEDED(rv)); |
|
120 return rv; |
|
121 } |
|
122 |
|
123 NS_IMETHODIMP nsDataObj::CStream::OnStartRequest(nsIRequest *aRequest, |
|
124 nsISupports *aContext) |
|
125 { |
|
126 mChannelResult = NS_OK; |
|
127 return NS_OK; |
|
128 } |
|
129 |
|
130 NS_IMETHODIMP nsDataObj::CStream::OnStopRequest(nsIRequest *aRequest, |
|
131 nsISupports *aContext, |
|
132 nsresult aStatusCode) |
|
133 { |
|
134 mChannelRead = true; |
|
135 mChannelResult = aStatusCode; |
|
136 return NS_OK; |
|
137 } |
|
138 |
|
139 // Pumps thread messages while waiting for the async listener operation to |
|
140 // complete. Failing this call will fail the stream incall from Windows |
|
141 // and cancel the operation. |
|
142 nsresult nsDataObj::CStream::WaitForCompletion() |
|
143 { |
|
144 // We are guaranteed OnStopRequest will get called, so this should be ok. |
|
145 while (!mChannelRead) { |
|
146 // Pump messages |
|
147 NS_ProcessNextEvent(nullptr, true); |
|
148 } |
|
149 |
|
150 if (!mChannelData.Length()) |
|
151 mChannelResult = NS_ERROR_FAILURE; |
|
152 |
|
153 return mChannelResult; |
|
154 } |
|
155 |
|
156 //----------------------------------------------------------------------------- |
|
157 // IStream |
|
158 STDMETHODIMP nsDataObj::CStream::Clone(IStream** ppStream) |
|
159 { |
|
160 return E_NOTIMPL; |
|
161 } |
|
162 |
|
163 //----------------------------------------------------------------------------- |
|
164 STDMETHODIMP nsDataObj::CStream::Commit(DWORD dwFrags) |
|
165 { |
|
166 return E_NOTIMPL; |
|
167 } |
|
168 |
|
169 //----------------------------------------------------------------------------- |
|
170 STDMETHODIMP nsDataObj::CStream::CopyTo(IStream* pDestStream, |
|
171 ULARGE_INTEGER nBytesToCopy, |
|
172 ULARGE_INTEGER* nBytesRead, |
|
173 ULARGE_INTEGER* nBytesWritten) |
|
174 { |
|
175 return E_NOTIMPL; |
|
176 } |
|
177 |
|
178 //----------------------------------------------------------------------------- |
|
179 STDMETHODIMP nsDataObj::CStream::LockRegion(ULARGE_INTEGER nStart, |
|
180 ULARGE_INTEGER nBytes, |
|
181 DWORD dwFlags) |
|
182 { |
|
183 return E_NOTIMPL; |
|
184 } |
|
185 |
|
186 //----------------------------------------------------------------------------- |
|
187 STDMETHODIMP nsDataObj::CStream::Read(void* pvBuffer, |
|
188 ULONG nBytesToRead, |
|
189 ULONG* nBytesRead) |
|
190 { |
|
191 // Wait for the write into our buffer to complete via the stream listener. |
|
192 // We can't respond to this by saying "call us back later". |
|
193 if (NS_FAILED(WaitForCompletion())) |
|
194 return E_FAIL; |
|
195 |
|
196 // Bytes left for Windows to read out of our buffer |
|
197 ULONG bytesLeft = mChannelData.Length() - mStreamRead; |
|
198 // Let Windows know what we will hand back, usually this is the entire buffer |
|
199 *nBytesRead = std::min(bytesLeft, nBytesToRead); |
|
200 // Copy the buffer data over |
|
201 memcpy(pvBuffer, ((char*)mChannelData.Elements() + mStreamRead), *nBytesRead); |
|
202 // Update our bytes read tracking |
|
203 mStreamRead += *nBytesRead; |
|
204 return S_OK; |
|
205 } |
|
206 |
|
207 //----------------------------------------------------------------------------- |
|
208 STDMETHODIMP nsDataObj::CStream::Revert(void) |
|
209 { |
|
210 return E_NOTIMPL; |
|
211 } |
|
212 |
|
213 //----------------------------------------------------------------------------- |
|
214 STDMETHODIMP nsDataObj::CStream::Seek(LARGE_INTEGER nMove, |
|
215 DWORD dwOrigin, |
|
216 ULARGE_INTEGER* nNewPos) |
|
217 { |
|
218 if (nNewPos == nullptr) |
|
219 return STG_E_INVALIDPOINTER; |
|
220 |
|
221 if (nMove.LowPart == 0 && nMove.HighPart == 0 && |
|
222 (dwOrigin == STREAM_SEEK_SET || dwOrigin == STREAM_SEEK_CUR)) { |
|
223 nNewPos->LowPart = 0; |
|
224 nNewPos->HighPart = 0; |
|
225 return S_OK; |
|
226 } |
|
227 |
|
228 return E_NOTIMPL; |
|
229 } |
|
230 |
|
231 //----------------------------------------------------------------------------- |
|
232 STDMETHODIMP nsDataObj::CStream::SetSize(ULARGE_INTEGER nNewSize) |
|
233 { |
|
234 return E_NOTIMPL; |
|
235 } |
|
236 |
|
237 //----------------------------------------------------------------------------- |
|
238 STDMETHODIMP nsDataObj::CStream::Stat(STATSTG* statstg, DWORD dwFlags) |
|
239 { |
|
240 if (statstg == nullptr) |
|
241 return STG_E_INVALIDPOINTER; |
|
242 |
|
243 if (!mChannel || NS_FAILED(WaitForCompletion())) |
|
244 return E_FAIL; |
|
245 |
|
246 memset((void*)statstg, 0, sizeof(STATSTG)); |
|
247 |
|
248 if (dwFlags != STATFLAG_NONAME) |
|
249 { |
|
250 nsCOMPtr<nsIURI> sourceURI; |
|
251 if (NS_FAILED(mChannel->GetURI(getter_AddRefs(sourceURI)))) { |
|
252 return E_FAIL; |
|
253 } |
|
254 |
|
255 nsAutoCString strFileName; |
|
256 nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI); |
|
257 sourceURL->GetFileName(strFileName); |
|
258 |
|
259 if (strFileName.IsEmpty()) |
|
260 return E_FAIL; |
|
261 |
|
262 NS_UnescapeURL(strFileName); |
|
263 NS_ConvertUTF8toUTF16 wideFileName(strFileName); |
|
264 |
|
265 uint32_t nMaxNameLength = (wideFileName.Length()*2) + 2; |
|
266 void * retBuf = CoTaskMemAlloc(nMaxNameLength); // freed by caller |
|
267 if (!retBuf) |
|
268 return STG_E_INSUFFICIENTMEMORY; |
|
269 |
|
270 ZeroMemory(retBuf, nMaxNameLength); |
|
271 memcpy(retBuf, wideFileName.get(), wideFileName.Length()*2); |
|
272 statstg->pwcsName = (LPOLESTR)retBuf; |
|
273 } |
|
274 |
|
275 SYSTEMTIME st; |
|
276 |
|
277 statstg->type = STGTY_STREAM; |
|
278 |
|
279 GetSystemTime(&st); |
|
280 SystemTimeToFileTime((const SYSTEMTIME*)&st, (LPFILETIME)&statstg->mtime); |
|
281 statstg->ctime = statstg->atime = statstg->mtime; |
|
282 |
|
283 statstg->cbSize.LowPart = (DWORD)mChannelData.Length(); |
|
284 statstg->grfMode = STGM_READ; |
|
285 statstg->grfLocksSupported = LOCK_ONLYONCE; |
|
286 statstg->clsid = CLSID_NULL; |
|
287 |
|
288 return S_OK; |
|
289 } |
|
290 |
|
291 //----------------------------------------------------------------------------- |
|
292 STDMETHODIMP nsDataObj::CStream::UnlockRegion(ULARGE_INTEGER nStart, |
|
293 ULARGE_INTEGER nBytes, |
|
294 DWORD dwFlags) |
|
295 { |
|
296 return E_NOTIMPL; |
|
297 } |
|
298 |
|
299 //----------------------------------------------------------------------------- |
|
300 STDMETHODIMP nsDataObj::CStream::Write(const void* pvBuffer, |
|
301 ULONG nBytesToRead, |
|
302 ULONG* nBytesRead) |
|
303 { |
|
304 return E_NOTIMPL; |
|
305 } |
|
306 |
|
307 //----------------------------------------------------------------------------- |
|
308 HRESULT nsDataObj::CreateStream(IStream **outStream) |
|
309 { |
|
310 NS_ENSURE_TRUE(outStream, E_INVALIDARG); |
|
311 |
|
312 nsresult rv = NS_ERROR_FAILURE; |
|
313 nsAutoString wideFileName; |
|
314 nsCOMPtr<nsIURI> sourceURI; |
|
315 HRESULT res; |
|
316 |
|
317 res = GetDownloadDetails(getter_AddRefs(sourceURI), |
|
318 wideFileName); |
|
319 if(FAILED(res)) |
|
320 return res; |
|
321 |
|
322 nsDataObj::CStream *pStream = new nsDataObj::CStream(); |
|
323 NS_ENSURE_TRUE(pStream, E_OUTOFMEMORY); |
|
324 |
|
325 pStream->AddRef(); |
|
326 |
|
327 rv = pStream->Init(sourceURI); |
|
328 if (NS_FAILED(rv)) |
|
329 { |
|
330 pStream->Release(); |
|
331 return E_FAIL; |
|
332 } |
|
333 *outStream = pStream; |
|
334 |
|
335 return S_OK; |
|
336 } |
|
337 |
|
338 static GUID CLSID_nsDataObj = |
|
339 { 0x1bba7640, 0xdf52, 0x11cf, { 0x82, 0x7b, 0, 0xa0, 0x24, 0x3a, 0xe5, 0x05 } }; |
|
340 |
|
341 /* |
|
342 * deliberately not using MAX_PATH. This is because on platforms < XP |
|
343 * a file created with a long filename may be mishandled by the shell |
|
344 * resulting in it not being able to be deleted or moved. |
|
345 * See bug 250392 for more details. |
|
346 */ |
|
347 #define NS_MAX_FILEDESCRIPTOR 128 + 1 |
|
348 |
|
349 /* |
|
350 * Class nsDataObj |
|
351 */ |
|
352 |
|
353 //----------------------------------------------------- |
|
354 // construction |
|
355 //----------------------------------------------------- |
|
356 nsDataObj::nsDataObj(nsIURI * uri) |
|
357 : m_cRef(0), mTransferable(nullptr), |
|
358 mIsAsyncMode(FALSE), mIsInOperation(FALSE) |
|
359 { |
|
360 mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, |
|
361 NS_LITERAL_CSTRING("nsDataObj"), |
|
362 LazyIdleThread::ManualShutdown); |
|
363 m_enumFE = new CEnumFormatEtc(); |
|
364 m_enumFE->AddRef(); |
|
365 |
|
366 if (uri) { |
|
367 // A URI was obtained, so pass this through to the DataObject |
|
368 // so it can create a SourceURL for CF_HTML flavour |
|
369 uri->GetSpec(mSourceURL); |
|
370 } |
|
371 } |
|
372 //----------------------------------------------------- |
|
373 // destruction |
|
374 //----------------------------------------------------- |
|
375 nsDataObj::~nsDataObj() |
|
376 { |
|
377 NS_IF_RELEASE(mTransferable); |
|
378 |
|
379 mDataFlavors.Clear(); |
|
380 |
|
381 m_enumFE->Release(); |
|
382 |
|
383 // Free arbitrary system formats |
|
384 for (uint32_t idx = 0; idx < mDataEntryList.Length(); idx++) { |
|
385 CoTaskMemFree(mDataEntryList[idx]->fe.ptd); |
|
386 ReleaseStgMedium(&mDataEntryList[idx]->stgm); |
|
387 CoTaskMemFree(mDataEntryList[idx]); |
|
388 } |
|
389 } |
|
390 |
|
391 |
|
392 //----------------------------------------------------- |
|
393 // IUnknown interface methods - see inknown.h for documentation |
|
394 //----------------------------------------------------- |
|
395 STDMETHODIMP nsDataObj::QueryInterface(REFIID riid, void** ppv) |
|
396 { |
|
397 *ppv=nullptr; |
|
398 |
|
399 if ( (IID_IUnknown == riid) || (IID_IDataObject == riid) ) { |
|
400 *ppv = this; |
|
401 AddRef(); |
|
402 return S_OK; |
|
403 } else if (IID_IAsyncOperation == riid) { |
|
404 *ppv = static_cast<IAsyncOperation*>(this); |
|
405 AddRef(); |
|
406 return S_OK; |
|
407 } |
|
408 |
|
409 return E_NOINTERFACE; |
|
410 } |
|
411 |
|
412 //----------------------------------------------------- |
|
413 STDMETHODIMP_(ULONG) nsDataObj::AddRef() |
|
414 { |
|
415 ++m_cRef; |
|
416 NS_LOG_ADDREF(this, m_cRef, "nsDataObj", sizeof(*this)); |
|
417 return m_cRef; |
|
418 } |
|
419 |
|
420 |
|
421 //----------------------------------------------------- |
|
422 STDMETHODIMP_(ULONG) nsDataObj::Release() |
|
423 { |
|
424 --m_cRef; |
|
425 |
|
426 NS_LOG_RELEASE(this, m_cRef, "nsDataObj"); |
|
427 if (0 != m_cRef) |
|
428 return m_cRef; |
|
429 |
|
430 // We have released our last ref on this object and need to delete the |
|
431 // temp file. External app acting as drop target may still need to open the |
|
432 // temp file. Addref a timer so it can delay deleting file and destroying |
|
433 // this object. Delete file anyway and destroy this obj if there's a problem. |
|
434 if (mCachedTempFile) { |
|
435 nsresult rv; |
|
436 mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); |
|
437 if (NS_SUCCEEDED(rv)) { |
|
438 mTimer->InitWithFuncCallback(nsDataObj::RemoveTempFile, this, |
|
439 500, nsITimer::TYPE_ONE_SHOT); |
|
440 return AddRef(); |
|
441 } |
|
442 mCachedTempFile->Remove(false); |
|
443 mCachedTempFile = nullptr; |
|
444 } |
|
445 |
|
446 delete this; |
|
447 |
|
448 return 0; |
|
449 } |
|
450 |
|
451 //----------------------------------------------------- |
|
452 BOOL nsDataObj::FormatsMatch(const FORMATETC& source, const FORMATETC& target) const |
|
453 { |
|
454 if ((source.cfFormat == target.cfFormat) && |
|
455 (source.dwAspect & target.dwAspect) && |
|
456 (source.tymed & target.tymed)) { |
|
457 return TRUE; |
|
458 } else { |
|
459 return FALSE; |
|
460 } |
|
461 } |
|
462 |
|
463 //----------------------------------------------------- |
|
464 // IDataObject methods |
|
465 //----------------------------------------------------- |
|
466 STDMETHODIMP nsDataObj::GetData(LPFORMATETC aFormat, LPSTGMEDIUM pSTM) |
|
467 { |
|
468 if (!mTransferable) |
|
469 return DV_E_FORMATETC; |
|
470 |
|
471 uint32_t dfInx = 0; |
|
472 |
|
473 static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA ); |
|
474 static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW ); |
|
475 static CLIPFORMAT uniformResourceLocatorA = ::RegisterClipboardFormat( CFSTR_INETURLA ); |
|
476 static CLIPFORMAT uniformResourceLocatorW = ::RegisterClipboardFormat( CFSTR_INETURLW ); |
|
477 static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS ); |
|
478 static CLIPFORMAT PreferredDropEffect = ::RegisterClipboardFormat( CFSTR_PREFERREDDROPEFFECT ); |
|
479 |
|
480 // Arbitrary system formats are used for image feedback during drag |
|
481 // and drop. We are responsible for storing these internally during |
|
482 // drag operations. |
|
483 LPDATAENTRY pde; |
|
484 if (LookupArbitraryFormat(aFormat, &pde, FALSE)) { |
|
485 return CopyMediumData(pSTM, &pde->stgm, aFormat, FALSE) |
|
486 ? S_OK : E_UNEXPECTED; |
|
487 } |
|
488 |
|
489 // Firefox internal formats |
|
490 ULONG count; |
|
491 FORMATETC fe; |
|
492 m_enumFE->Reset(); |
|
493 while (NOERROR == m_enumFE->Next(1, &fe, &count) |
|
494 && dfInx < mDataFlavors.Length()) { |
|
495 nsCString& df = mDataFlavors.ElementAt(dfInx); |
|
496 if (FormatsMatch(fe, *aFormat)) { |
|
497 pSTM->pUnkForRelease = nullptr; // caller is responsible for deleting this data |
|
498 CLIPFORMAT format = aFormat->cfFormat; |
|
499 switch(format) { |
|
500 |
|
501 // Someone is asking for plain or unicode text |
|
502 case CF_TEXT: |
|
503 case CF_UNICODETEXT: |
|
504 return GetText(df, *aFormat, *pSTM); |
|
505 |
|
506 // Some 3rd party apps that receive drag and drop files from the browser |
|
507 // window require support for this. |
|
508 case CF_HDROP: |
|
509 return GetFile(*aFormat, *pSTM); |
|
510 |
|
511 // Someone is asking for an image |
|
512 case CF_DIBV5: |
|
513 case CF_DIB: |
|
514 return GetDib(df, *aFormat, *pSTM); |
|
515 |
|
516 default: |
|
517 if ( format == fileDescriptorFlavorA ) |
|
518 return GetFileDescriptor ( *aFormat, *pSTM, false ); |
|
519 if ( format == fileDescriptorFlavorW ) |
|
520 return GetFileDescriptor ( *aFormat, *pSTM, true); |
|
521 if ( format == uniformResourceLocatorA ) |
|
522 return GetUniformResourceLocator( *aFormat, *pSTM, false); |
|
523 if ( format == uniformResourceLocatorW ) |
|
524 return GetUniformResourceLocator( *aFormat, *pSTM, true); |
|
525 if ( format == fileFlavor ) |
|
526 return GetFileContents ( *aFormat, *pSTM ); |
|
527 if ( format == PreferredDropEffect ) |
|
528 return GetPreferredDropEffect( *aFormat, *pSTM ); |
|
529 //PR_LOG(gWindowsLog, PR_LOG_ALWAYS, |
|
530 // ("***** nsDataObj::GetData - Unknown format %u\n", format)); |
|
531 return GetText(df, *aFormat, *pSTM); |
|
532 } //switch |
|
533 } // if |
|
534 dfInx++; |
|
535 } // while |
|
536 |
|
537 return DATA_E_FORMATETC; |
|
538 } |
|
539 |
|
540 //----------------------------------------------------- |
|
541 STDMETHODIMP nsDataObj::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM) |
|
542 { |
|
543 return E_FAIL; |
|
544 } |
|
545 |
|
546 |
|
547 //----------------------------------------------------- |
|
548 // Other objects querying to see if we support a |
|
549 // particular format |
|
550 //----------------------------------------------------- |
|
551 STDMETHODIMP nsDataObj::QueryGetData(LPFORMATETC pFE) |
|
552 { |
|
553 // Arbitrary system formats are used for image feedback during drag |
|
554 // and drop. We are responsible for storing these internally during |
|
555 // drag operations. |
|
556 LPDATAENTRY pde; |
|
557 if (LookupArbitraryFormat(pFE, &pde, FALSE)) |
|
558 return S_OK; |
|
559 |
|
560 // Firefox internal formats |
|
561 ULONG count; |
|
562 FORMATETC fe; |
|
563 m_enumFE->Reset(); |
|
564 while (NOERROR == m_enumFE->Next(1, &fe, &count)) { |
|
565 if (fe.cfFormat == pFE->cfFormat) { |
|
566 return S_OK; |
|
567 } |
|
568 } |
|
569 return E_FAIL; |
|
570 } |
|
571 |
|
572 //----------------------------------------------------- |
|
573 STDMETHODIMP nsDataObj::GetCanonicalFormatEtc |
|
574 (LPFORMATETC pFEIn, LPFORMATETC pFEOut) |
|
575 { |
|
576 return E_FAIL; |
|
577 } |
|
578 |
|
579 //----------------------------------------------------- |
|
580 STDMETHODIMP nsDataObj::SetData(LPFORMATETC aFormat, LPSTGMEDIUM aMedium, BOOL shouldRel) |
|
581 { |
|
582 // Arbitrary system formats are used for image feedback during drag |
|
583 // and drop. We are responsible for storing these internally during |
|
584 // drag operations. |
|
585 LPDATAENTRY pde; |
|
586 if (LookupArbitraryFormat(aFormat, &pde, TRUE)) { |
|
587 // Release the old data the lookup handed us for this format. This |
|
588 // may have been set in CopyMediumData when we originally stored the |
|
589 // data. |
|
590 if (pde->stgm.tymed) { |
|
591 ReleaseStgMedium(&pde->stgm); |
|
592 memset(&pde->stgm, 0, sizeof(STGMEDIUM)); |
|
593 } |
|
594 |
|
595 bool result = true; |
|
596 if (shouldRel) { |
|
597 // If shouldRel is TRUE, the data object called owns the storage medium |
|
598 // after the call returns. Store the incoming data in our data array for |
|
599 // release when we are destroyed. This is the common case with arbitrary |
|
600 // data from explorer. |
|
601 pde->stgm = *aMedium; |
|
602 } else { |
|
603 // Copy the incoming data into our data array. (AFAICT, this never gets |
|
604 // called with arbitrary formats for drag images.) |
|
605 result = CopyMediumData(&pde->stgm, aMedium, aFormat, TRUE); |
|
606 } |
|
607 pde->fe.tymed = pde->stgm.tymed; |
|
608 |
|
609 return result ? S_OK : DV_E_TYMED; |
|
610 } |
|
611 |
|
612 if (shouldRel) |
|
613 ReleaseStgMedium(aMedium); |
|
614 |
|
615 return S_OK; |
|
616 } |
|
617 |
|
618 bool |
|
619 nsDataObj::LookupArbitraryFormat(FORMATETC *aFormat, LPDATAENTRY *aDataEntry, BOOL aAddorUpdate) |
|
620 { |
|
621 *aDataEntry = nullptr; |
|
622 |
|
623 if (aFormat->ptd != nullptr) |
|
624 return false; |
|
625 |
|
626 // See if it's already in our list. If so return the data entry. |
|
627 for (uint32_t idx = 0; idx < mDataEntryList.Length(); idx++) { |
|
628 if (mDataEntryList[idx]->fe.cfFormat == aFormat->cfFormat && |
|
629 mDataEntryList[idx]->fe.dwAspect == aFormat->dwAspect && |
|
630 mDataEntryList[idx]->fe.lindex == aFormat->lindex) { |
|
631 if (aAddorUpdate || (mDataEntryList[idx]->fe.tymed & aFormat->tymed)) { |
|
632 // If the caller requests we update, or if the |
|
633 // medium type matches, return the entry. |
|
634 *aDataEntry = mDataEntryList[idx]; |
|
635 return true; |
|
636 } else { |
|
637 // Medium does not match, not found. |
|
638 return false; |
|
639 } |
|
640 } |
|
641 } |
|
642 |
|
643 if (!aAddorUpdate) |
|
644 return false; |
|
645 |
|
646 // Add another entry to mDataEntryList |
|
647 LPDATAENTRY dataEntry = (LPDATAENTRY)CoTaskMemAlloc(sizeof(DATAENTRY)); |
|
648 if (!dataEntry) |
|
649 return false; |
|
650 |
|
651 dataEntry->fe = *aFormat; |
|
652 *aDataEntry = dataEntry; |
|
653 memset(&dataEntry->stgm, 0, sizeof(STGMEDIUM)); |
|
654 |
|
655 // Add this to our IEnumFORMATETC impl. so we can return it when |
|
656 // it's requested. |
|
657 m_enumFE->AddFormatEtc(aFormat); |
|
658 |
|
659 // Store a copy internally in the arbitrary formats array. |
|
660 mDataEntryList.AppendElement(dataEntry); |
|
661 |
|
662 return true; |
|
663 } |
|
664 |
|
665 bool |
|
666 nsDataObj::CopyMediumData(STGMEDIUM *aMediumDst, STGMEDIUM *aMediumSrc, LPFORMATETC aFormat, BOOL aSetData) |
|
667 { |
|
668 STGMEDIUM stgmOut = *aMediumSrc; |
|
669 |
|
670 switch (stgmOut.tymed) { |
|
671 case TYMED_ISTREAM: |
|
672 stgmOut.pstm->AddRef(); |
|
673 break; |
|
674 case TYMED_ISTORAGE: |
|
675 stgmOut.pstg->AddRef(); |
|
676 break; |
|
677 case TYMED_HGLOBAL: |
|
678 if (!aMediumSrc->pUnkForRelease) { |
|
679 if (aSetData) { |
|
680 if (aMediumSrc->tymed != TYMED_HGLOBAL) |
|
681 return false; |
|
682 stgmOut.hGlobal = OleDuplicateData(aMediumSrc->hGlobal, aFormat->cfFormat, 0); |
|
683 if (!stgmOut.hGlobal) |
|
684 return false; |
|
685 } else { |
|
686 // We are returning this data from LookupArbitraryFormat, indicate to the |
|
687 // shell we hold it and will free it. |
|
688 stgmOut.pUnkForRelease = static_cast<IDataObject*>(this); |
|
689 } |
|
690 } |
|
691 break; |
|
692 default: |
|
693 return false; |
|
694 } |
|
695 |
|
696 if (stgmOut.pUnkForRelease) |
|
697 stgmOut.pUnkForRelease->AddRef(); |
|
698 |
|
699 *aMediumDst = stgmOut; |
|
700 |
|
701 return true; |
|
702 } |
|
703 |
|
704 //----------------------------------------------------- |
|
705 STDMETHODIMP nsDataObj::EnumFormatEtc(DWORD dwDir, LPENUMFORMATETC *ppEnum) |
|
706 { |
|
707 switch (dwDir) { |
|
708 case DATADIR_GET: |
|
709 m_enumFE->Clone(ppEnum); |
|
710 break; |
|
711 case DATADIR_SET: |
|
712 // fall through |
|
713 default: |
|
714 *ppEnum = nullptr; |
|
715 } // switch |
|
716 |
|
717 if (nullptr == *ppEnum) |
|
718 return E_FAIL; |
|
719 |
|
720 (*ppEnum)->Reset(); |
|
721 // Clone already AddRefed the result so don't addref it again. |
|
722 return NOERROR; |
|
723 } |
|
724 |
|
725 //----------------------------------------------------- |
|
726 STDMETHODIMP nsDataObj::DAdvise(LPFORMATETC pFE, DWORD dwFlags, |
|
727 LPADVISESINK pIAdviseSink, DWORD* pdwConn) |
|
728 { |
|
729 return E_FAIL; |
|
730 } |
|
731 |
|
732 |
|
733 //----------------------------------------------------- |
|
734 STDMETHODIMP nsDataObj::DUnadvise(DWORD dwConn) |
|
735 { |
|
736 return E_FAIL; |
|
737 } |
|
738 |
|
739 //----------------------------------------------------- |
|
740 STDMETHODIMP nsDataObj::EnumDAdvise(LPENUMSTATDATA *ppEnum) |
|
741 { |
|
742 return E_FAIL; |
|
743 } |
|
744 |
|
745 // IAsyncOperation methods |
|
746 STDMETHODIMP nsDataObj::EndOperation(HRESULT hResult, |
|
747 IBindCtx *pbcReserved, |
|
748 DWORD dwEffects) |
|
749 { |
|
750 mIsInOperation = FALSE; |
|
751 return S_OK; |
|
752 } |
|
753 |
|
754 STDMETHODIMP nsDataObj::GetAsyncMode(BOOL *pfIsOpAsync) |
|
755 { |
|
756 *pfIsOpAsync = mIsAsyncMode; |
|
757 |
|
758 return S_OK; |
|
759 } |
|
760 |
|
761 STDMETHODIMP nsDataObj::InOperation(BOOL *pfInAsyncOp) |
|
762 { |
|
763 *pfInAsyncOp = mIsInOperation; |
|
764 |
|
765 return S_OK; |
|
766 } |
|
767 |
|
768 STDMETHODIMP nsDataObj::SetAsyncMode(BOOL fDoOpAsync) |
|
769 { |
|
770 mIsAsyncMode = fDoOpAsync; |
|
771 return S_OK; |
|
772 } |
|
773 |
|
774 STDMETHODIMP nsDataObj::StartOperation(IBindCtx *pbcReserved) |
|
775 { |
|
776 mIsInOperation = TRUE; |
|
777 return S_OK; |
|
778 } |
|
779 |
|
780 //----------------------------------------------------- |
|
781 // GetData and SetData helper functions |
|
782 //----------------------------------------------------- |
|
783 HRESULT nsDataObj::AddSetFormat(FORMATETC& aFE) |
|
784 { |
|
785 return S_OK; |
|
786 } |
|
787 |
|
788 //----------------------------------------------------- |
|
789 HRESULT nsDataObj::AddGetFormat(FORMATETC& aFE) |
|
790 { |
|
791 return S_OK; |
|
792 } |
|
793 |
|
794 // |
|
795 // GetDIB |
|
796 // |
|
797 // Someone is asking for a bitmap. The data in the transferable will be a straight |
|
798 // imgIContainer, so just QI it. |
|
799 // |
|
800 HRESULT |
|
801 nsDataObj::GetDib(const nsACString& inFlavor, |
|
802 FORMATETC &aFormat, |
|
803 STGMEDIUM & aSTG) |
|
804 { |
|
805 ULONG result = E_FAIL; |
|
806 uint32_t len = 0; |
|
807 nsCOMPtr<nsISupports> genericDataWrapper; |
|
808 mTransferable->GetTransferData(PromiseFlatCString(inFlavor).get(), getter_AddRefs(genericDataWrapper), &len); |
|
809 nsCOMPtr<imgIContainer> image ( do_QueryInterface(genericDataWrapper) ); |
|
810 if ( !image ) { |
|
811 // Check if the image was put in an nsISupportsInterfacePointer wrapper. |
|
812 // This might not be necessary any more, but could be useful for backwards |
|
813 // compatibility. |
|
814 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper)); |
|
815 if ( ptr ) { |
|
816 nsCOMPtr<nsISupports> supports; |
|
817 ptr->GetData(getter_AddRefs(supports)); |
|
818 image = do_QueryInterface(supports); |
|
819 } |
|
820 } |
|
821 |
|
822 if ( image ) { |
|
823 // use the |nsImageToClipboard| helper class to build up a bitmap. We now own |
|
824 // the bits, and pass them back to the OS in |aSTG|. |
|
825 nsImageToClipboard converter(image, aFormat.cfFormat == CF_DIBV5); |
|
826 HANDLE bits = nullptr; |
|
827 nsresult rv = converter.GetPicture ( &bits ); |
|
828 if ( NS_SUCCEEDED(rv) && bits ) { |
|
829 aSTG.hGlobal = bits; |
|
830 aSTG.tymed = TYMED_HGLOBAL; |
|
831 result = S_OK; |
|
832 } |
|
833 } // if we have an image |
|
834 else |
|
835 NS_WARNING ( "Definitely not an image on clipboard" ); |
|
836 return result; |
|
837 } |
|
838 |
|
839 |
|
840 |
|
841 // |
|
842 // GetFileDescriptor |
|
843 // |
|
844 |
|
845 HRESULT |
|
846 nsDataObj :: GetFileDescriptor ( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode ) |
|
847 { |
|
848 HRESULT res = S_OK; |
|
849 |
|
850 // How we handle this depends on if we're dealing with an internet |
|
851 // shortcut, since those are done under the covers. |
|
852 if (IsFlavourPresent(kFilePromiseMime) || |
|
853 IsFlavourPresent(kFileMime)) |
|
854 { |
|
855 if (aIsUnicode) |
|
856 return GetFileDescriptor_IStreamW(aFE, aSTG); |
|
857 else |
|
858 return GetFileDescriptor_IStreamA(aFE, aSTG); |
|
859 } |
|
860 else if (IsFlavourPresent(kURLMime)) |
|
861 { |
|
862 if ( aIsUnicode ) |
|
863 res = GetFileDescriptorInternetShortcutW ( aFE, aSTG ); |
|
864 else |
|
865 res = GetFileDescriptorInternetShortcutA ( aFE, aSTG ); |
|
866 } |
|
867 else |
|
868 NS_WARNING ( "Not yet implemented\n" ); |
|
869 |
|
870 return res; |
|
871 } // GetFileDescriptor |
|
872 |
|
873 |
|
874 // |
|
875 HRESULT |
|
876 nsDataObj :: GetFileContents ( FORMATETC& aFE, STGMEDIUM& aSTG ) |
|
877 { |
|
878 HRESULT res = S_OK; |
|
879 |
|
880 // How we handle this depends on if we're dealing with an internet |
|
881 // shortcut, since those are done under the covers. |
|
882 if (IsFlavourPresent(kFilePromiseMime) || |
|
883 IsFlavourPresent(kFileMime)) |
|
884 return GetFileContents_IStream(aFE, aSTG); |
|
885 else if (IsFlavourPresent(kURLMime)) |
|
886 return GetFileContentsInternetShortcut ( aFE, aSTG ); |
|
887 else |
|
888 NS_WARNING ( "Not yet implemented\n" ); |
|
889 |
|
890 return res; |
|
891 |
|
892 } // GetFileContents |
|
893 |
|
894 // |
|
895 // Given a unicode string, we ensure that it contains only characters which are valid within |
|
896 // the file system. Remove all forbidden characters from the name, and completely disallow |
|
897 // any title that starts with a forbidden name and extension (e.g. "nul" is invalid, but |
|
898 // "nul." and "nul.txt" are also invalid and will cause problems). |
|
899 // |
|
900 // It would seem that this is more functionality suited to being in nsIFile. |
|
901 // |
|
902 static void |
|
903 MangleTextToValidFilename(nsString & aText) |
|
904 { |
|
905 static const char* forbiddenNames[] = { |
|
906 "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", |
|
907 "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", |
|
908 "CON", "PRN", "AUX", "NUL", "CLOCK$" |
|
909 }; |
|
910 |
|
911 aText.StripChars(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS); |
|
912 aText.CompressWhitespace(true, true); |
|
913 uint32_t nameLen; |
|
914 for (size_t n = 0; n < ArrayLength(forbiddenNames); ++n) { |
|
915 nameLen = (uint32_t) strlen(forbiddenNames[n]); |
|
916 if (aText.EqualsIgnoreCase(forbiddenNames[n], nameLen)) { |
|
917 // invalid name is either the entire string, or a prefix with a period |
|
918 if (aText.Length() == nameLen || aText.CharAt(nameLen) == char16_t('.')) { |
|
919 aText.Truncate(); |
|
920 break; |
|
921 } |
|
922 } |
|
923 } |
|
924 } |
|
925 |
|
926 // |
|
927 // Given a unicode string, convert it down to a valid local charset filename |
|
928 // with the supplied extension. This ensures that we do not cut MBCS characters |
|
929 // in the middle. |
|
930 // |
|
931 // It would seem that this is more functionality suited to being in nsIFile. |
|
932 // |
|
933 static bool |
|
934 CreateFilenameFromTextA(nsString & aText, const char * aExtension, |
|
935 char * aFilename, uint32_t aFilenameLen) |
|
936 { |
|
937 // ensure that the supplied name doesn't have invalid characters. If |
|
938 // a valid mangled filename couldn't be created then it will leave the |
|
939 // text empty. |
|
940 MangleTextToValidFilename(aText); |
|
941 if (aText.IsEmpty()) |
|
942 return false; |
|
943 |
|
944 // repeatably call WideCharToMultiByte as long as the title doesn't fit in the buffer |
|
945 // available to us. Continually reduce the length of the source title until the MBCS |
|
946 // version will fit in the buffer with room for the supplied extension. Doing it this |
|
947 // way ensures that even in MBCS environments there will be a valid MBCS filename of |
|
948 // the correct length. |
|
949 int maxUsableFilenameLen = aFilenameLen - strlen(aExtension) - 1; // space for ext + null byte |
|
950 int currLen, textLen = (int) std::min(aText.Length(), aFilenameLen); |
|
951 char defaultChar = '_'; |
|
952 do { |
|
953 currLen = WideCharToMultiByte(CP_ACP, |
|
954 WC_COMPOSITECHECK|WC_DEFAULTCHAR, |
|
955 aText.get(), textLen--, aFilename, maxUsableFilenameLen, &defaultChar, nullptr); |
|
956 } |
|
957 while (currLen == 0 && textLen > 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER); |
|
958 if (currLen > 0 && textLen > 0) { |
|
959 strcpy(&aFilename[currLen], aExtension); |
|
960 return true; |
|
961 } |
|
962 else { |
|
963 // empty names aren't permitted |
|
964 return false; |
|
965 } |
|
966 } |
|
967 |
|
968 static bool |
|
969 CreateFilenameFromTextW(nsString & aText, const wchar_t * aExtension, |
|
970 wchar_t * aFilename, uint32_t aFilenameLen) |
|
971 { |
|
972 // ensure that the supplied name doesn't have invalid characters. If |
|
973 // a valid mangled filename couldn't be created then it will leave the |
|
974 // text empty. |
|
975 MangleTextToValidFilename(aText); |
|
976 if (aText.IsEmpty()) |
|
977 return false; |
|
978 |
|
979 const int extensionLen = wcslen(aExtension); |
|
980 if (aText.Length() + extensionLen + 1 > aFilenameLen) |
|
981 aText.Truncate(aFilenameLen - extensionLen - 1); |
|
982 wcscpy(&aFilename[0], aText.get()); |
|
983 wcscpy(&aFilename[aText.Length()], aExtension); |
|
984 return true; |
|
985 } |
|
986 |
|
987 #define PAGEINFO_PROPERTIES "chrome://navigator/locale/pageInfo.properties" |
|
988 |
|
989 static bool |
|
990 GetLocalizedString(const char16_t * aName, nsXPIDLString & aString) |
|
991 { |
|
992 nsCOMPtr<nsIStringBundleService> stringService = |
|
993 mozilla::services::GetStringBundleService(); |
|
994 if (!stringService) |
|
995 return false; |
|
996 |
|
997 nsCOMPtr<nsIStringBundle> stringBundle; |
|
998 nsresult rv = stringService->CreateBundle(PAGEINFO_PROPERTIES, |
|
999 getter_AddRefs(stringBundle)); |
|
1000 if (NS_FAILED(rv)) |
|
1001 return false; |
|
1002 |
|
1003 rv = stringBundle->GetStringFromName(aName, getter_Copies(aString)); |
|
1004 return NS_SUCCEEDED(rv); |
|
1005 } |
|
1006 |
|
1007 // |
|
1008 // GetFileDescriptorInternetShortcut |
|
1009 // |
|
1010 // Create the special format for an internet shortcut and build up the data |
|
1011 // structures the shell is expecting. |
|
1012 // |
|
1013 HRESULT |
|
1014 nsDataObj :: GetFileDescriptorInternetShortcutA ( FORMATETC& aFE, STGMEDIUM& aSTG ) |
|
1015 { |
|
1016 // get the title of the shortcut |
|
1017 nsAutoString title; |
|
1018 if ( NS_FAILED(ExtractShortcutTitle(title)) ) |
|
1019 return E_OUTOFMEMORY; |
|
1020 |
|
1021 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORA)); |
|
1022 if (!fileGroupDescHandle) |
|
1023 return E_OUTOFMEMORY; |
|
1024 |
|
1025 LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(::GlobalLock(fileGroupDescHandle)); |
|
1026 if (!fileGroupDescA) { |
|
1027 ::GlobalFree(fileGroupDescHandle); |
|
1028 return E_OUTOFMEMORY; |
|
1029 } |
|
1030 |
|
1031 // get a valid filename in the following order: 1) from the page title, |
|
1032 // 2) localized string for an untitled page, 3) just use "Untitled.URL" |
|
1033 if (!CreateFilenameFromTextA(title, ".URL", |
|
1034 fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { |
|
1035 nsXPIDLString untitled; |
|
1036 if (!GetLocalizedString(MOZ_UTF16("noPageTitle"), untitled) || |
|
1037 !CreateFilenameFromTextA(untitled, ".URL", |
|
1038 fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { |
|
1039 strcpy(fileGroupDescA->fgd[0].cFileName, "Untitled.URL"); |
|
1040 } |
|
1041 } |
|
1042 |
|
1043 // one file in the file block |
|
1044 fileGroupDescA->cItems = 1; |
|
1045 fileGroupDescA->fgd[0].dwFlags = FD_LINKUI; |
|
1046 |
|
1047 ::GlobalUnlock( fileGroupDescHandle ); |
|
1048 aSTG.hGlobal = fileGroupDescHandle; |
|
1049 aSTG.tymed = TYMED_HGLOBAL; |
|
1050 |
|
1051 return S_OK; |
|
1052 } // GetFileDescriptorInternetShortcutA |
|
1053 |
|
1054 HRESULT |
|
1055 nsDataObj :: GetFileDescriptorInternetShortcutW ( FORMATETC& aFE, STGMEDIUM& aSTG ) |
|
1056 { |
|
1057 // get the title of the shortcut |
|
1058 nsAutoString title; |
|
1059 if ( NS_FAILED(ExtractShortcutTitle(title)) ) |
|
1060 return E_OUTOFMEMORY; |
|
1061 |
|
1062 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW)); |
|
1063 if (!fileGroupDescHandle) |
|
1064 return E_OUTOFMEMORY; |
|
1065 |
|
1066 LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(::GlobalLock(fileGroupDescHandle)); |
|
1067 if (!fileGroupDescW) { |
|
1068 ::GlobalFree(fileGroupDescHandle); |
|
1069 return E_OUTOFMEMORY; |
|
1070 } |
|
1071 |
|
1072 // get a valid filename in the following order: 1) from the page title, |
|
1073 // 2) localized string for an untitled page, 3) just use "Untitled.URL" |
|
1074 if (!CreateFilenameFromTextW(title, L".URL", |
|
1075 fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { |
|
1076 nsXPIDLString untitled; |
|
1077 if (!GetLocalizedString(MOZ_UTF16("noPageTitle"), untitled) || |
|
1078 !CreateFilenameFromTextW(untitled, L".URL", |
|
1079 fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { |
|
1080 wcscpy(fileGroupDescW->fgd[0].cFileName, L"Untitled.URL"); |
|
1081 } |
|
1082 } |
|
1083 |
|
1084 // one file in the file block |
|
1085 fileGroupDescW->cItems = 1; |
|
1086 fileGroupDescW->fgd[0].dwFlags = FD_LINKUI; |
|
1087 |
|
1088 ::GlobalUnlock( fileGroupDescHandle ); |
|
1089 aSTG.hGlobal = fileGroupDescHandle; |
|
1090 aSTG.tymed = TYMED_HGLOBAL; |
|
1091 |
|
1092 return S_OK; |
|
1093 } // GetFileDescriptorInternetShortcutW |
|
1094 |
|
1095 |
|
1096 // |
|
1097 // GetFileContentsInternetShortcut |
|
1098 // |
|
1099 // Create the special format for an internet shortcut and build up the data |
|
1100 // structures the shell is expecting. |
|
1101 // |
|
1102 HRESULT |
|
1103 nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG ) |
|
1104 { |
|
1105 static const char * kShellIconPref = "browser.shell.shortcutFavicons"; |
|
1106 nsAutoString url; |
|
1107 if ( NS_FAILED(ExtractShortcutURL(url)) ) |
|
1108 return E_OUTOFMEMORY; |
|
1109 |
|
1110 // will need to change if we ever support iDNS |
|
1111 nsAutoCString asciiUrl; |
|
1112 LossyCopyUTF16toASCII(url, asciiUrl); |
|
1113 |
|
1114 nsCOMPtr<nsIFile> icoFile; |
|
1115 nsCOMPtr<nsIURI> aUri; |
|
1116 NS_NewURI(getter_AddRefs(aUri), url); |
|
1117 |
|
1118 const char *shortcutFormatStr; |
|
1119 int totalLen; |
|
1120 nsCString path; |
|
1121 if (!Preferences::GetBool(kShellIconPref, true) || |
|
1122 !IsVistaOrLater()) { |
|
1123 shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n"; |
|
1124 const int formatLen = strlen(shortcutFormatStr) - 2; // don't include %s |
|
1125 totalLen = formatLen + asciiUrl.Length(); // don't include null character |
|
1126 } else { |
|
1127 nsCOMPtr<nsIFile> icoFile; |
|
1128 nsCOMPtr<nsIURI> aUri; |
|
1129 NS_NewURI(getter_AddRefs(aUri), url); |
|
1130 |
|
1131 nsAutoString aUriHash; |
|
1132 |
|
1133 mozilla::widget::FaviconHelper::ObtainCachedIconFile(aUri, aUriHash, mIOThread, true); |
|
1134 |
|
1135 nsresult rv = mozilla::widget::FaviconHelper::GetOutputIconPath(aUri, icoFile, true); |
|
1136 NS_ENSURE_SUCCESS(rv, E_FAIL); |
|
1137 rv = icoFile->GetNativePath(path); |
|
1138 NS_ENSURE_SUCCESS(rv, E_FAIL); |
|
1139 |
|
1140 shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n" |
|
1141 "IDList=\r\nHotKey=0\r\nIconFile=%s\r\n" |
|
1142 "IconIndex=0\r\n"; |
|
1143 const int formatLen = strlen(shortcutFormatStr) - 2 * 2; // no %s twice |
|
1144 totalLen = formatLen + asciiUrl.Length() + |
|
1145 path.Length(); // we don't want a null character on the end |
|
1146 } |
|
1147 |
|
1148 // create a global memory area and build up the file contents w/in it |
|
1149 HGLOBAL hGlobalMemory = ::GlobalAlloc(GMEM_SHARE, totalLen); |
|
1150 if ( !hGlobalMemory ) |
|
1151 return E_OUTOFMEMORY; |
|
1152 |
|
1153 char* contents = reinterpret_cast<char*>(::GlobalLock(hGlobalMemory)); |
|
1154 if ( !contents ) { |
|
1155 ::GlobalFree( hGlobalMemory ); |
|
1156 return E_OUTOFMEMORY; |
|
1157 } |
|
1158 |
|
1159 //NOTE: we intentionally use the Microsoft version of snprintf here because it does NOT null |
|
1160 // terminate strings which reach the maximum size of the buffer. Since we know that the |
|
1161 // formatted length here is totalLen, this call to _snprintf will format the string into |
|
1162 // the buffer without appending the null character. |
|
1163 |
|
1164 if (!Preferences::GetBool(kShellIconPref, true)) { |
|
1165 _snprintf(contents, totalLen, shortcutFormatStr, asciiUrl.get()); |
|
1166 } else { |
|
1167 _snprintf(contents, totalLen, shortcutFormatStr, asciiUrl.get(), path.get()); |
|
1168 } |
|
1169 |
|
1170 ::GlobalUnlock(hGlobalMemory); |
|
1171 aSTG.hGlobal = hGlobalMemory; |
|
1172 aSTG.tymed = TYMED_HGLOBAL; |
|
1173 |
|
1174 return S_OK; |
|
1175 } // GetFileContentsInternetShortcut |
|
1176 |
|
1177 // check if specified flavour is present in the transferable |
|
1178 bool nsDataObj :: IsFlavourPresent(const char *inFlavour) |
|
1179 { |
|
1180 bool retval = false; |
|
1181 NS_ENSURE_TRUE(mTransferable, false); |
|
1182 |
|
1183 // get the list of flavors available in the transferable |
|
1184 nsCOMPtr<nsISupportsArray> flavorList; |
|
1185 mTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList)); |
|
1186 NS_ENSURE_TRUE(flavorList, false); |
|
1187 |
|
1188 // try to find requested flavour |
|
1189 uint32_t cnt; |
|
1190 flavorList->Count(&cnt); |
|
1191 for (uint32_t i = 0; i < cnt; ++i) { |
|
1192 nsCOMPtr<nsISupports> genericFlavor; |
|
1193 flavorList->GetElementAt (i, getter_AddRefs(genericFlavor)); |
|
1194 nsCOMPtr<nsISupportsCString> currentFlavor (do_QueryInterface(genericFlavor)); |
|
1195 if (currentFlavor) { |
|
1196 nsAutoCString flavorStr; |
|
1197 currentFlavor->GetData(flavorStr); |
|
1198 if (flavorStr.Equals(inFlavour)) { |
|
1199 retval = true; // found it! |
|
1200 break; |
|
1201 } |
|
1202 } |
|
1203 } // for each flavor |
|
1204 |
|
1205 return retval; |
|
1206 } |
|
1207 |
|
1208 HRESULT nsDataObj::GetPreferredDropEffect ( FORMATETC& aFE, STGMEDIUM& aSTG ) |
|
1209 { |
|
1210 HRESULT res = S_OK; |
|
1211 aSTG.tymed = TYMED_HGLOBAL; |
|
1212 aSTG.pUnkForRelease = nullptr; |
|
1213 HGLOBAL hGlobalMemory = nullptr; |
|
1214 hGlobalMemory = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD)); |
|
1215 if (hGlobalMemory) { |
|
1216 DWORD* pdw = (DWORD*) GlobalLock(hGlobalMemory); |
|
1217 // The PreferredDropEffect clipboard format is only registered if a drag/drop |
|
1218 // of an image happens from Mozilla to the desktop. We want its value |
|
1219 // to be DROPEFFECT_MOVE in that case so that the file is moved from the |
|
1220 // temporary location, not copied. |
|
1221 // This value should, ideally, be set on the data object via SetData() but |
|
1222 // our IDataObject implementation doesn't implement SetData. It adds data |
|
1223 // to the data object lazily only when the drop target asks for it. |
|
1224 *pdw = (DWORD) DROPEFFECT_MOVE; |
|
1225 GlobalUnlock(hGlobalMemory); |
|
1226 } |
|
1227 else { |
|
1228 res = E_OUTOFMEMORY; |
|
1229 } |
|
1230 aSTG.hGlobal = hGlobalMemory; |
|
1231 return res; |
|
1232 } |
|
1233 |
|
1234 //----------------------------------------------------- |
|
1235 HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGMEDIUM& aSTG) |
|
1236 { |
|
1237 void* data = nullptr; |
|
1238 uint32_t len; |
|
1239 |
|
1240 // if someone asks for text/plain, look up text/unicode instead in the transferable. |
|
1241 const char* flavorStr; |
|
1242 const nsPromiseFlatCString& flat = PromiseFlatCString(aDataFlavor); |
|
1243 if ( aDataFlavor.Equals("text/plain") ) |
|
1244 flavorStr = kUnicodeMime; |
|
1245 else |
|
1246 flavorStr = flat.get(); |
|
1247 |
|
1248 // NOTE: CreateDataFromPrimitive creates new memory, that needs to be deleted |
|
1249 nsCOMPtr<nsISupports> genericDataWrapper; |
|
1250 mTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &len); |
|
1251 if ( !len ) |
|
1252 return E_FAIL; |
|
1253 nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr, genericDataWrapper, &data, len ); |
|
1254 if ( !data ) |
|
1255 return E_FAIL; |
|
1256 |
|
1257 HGLOBAL hGlobalMemory = nullptr; |
|
1258 |
|
1259 aSTG.tymed = TYMED_HGLOBAL; |
|
1260 aSTG.pUnkForRelease = nullptr; |
|
1261 |
|
1262 // We play games under the hood and advertise flavors that we know we |
|
1263 // can support, only they require a bit of conversion or munging of the data. |
|
1264 // Do that here. |
|
1265 // |
|
1266 // The transferable gives us data that is null-terminated, but this isn't reflected in |
|
1267 // the |len| parameter. Windoze apps expect this null to be there so bump our data buffer |
|
1268 // by the appropriate size to account for the null (one char for CF_TEXT, one char16_t for |
|
1269 // CF_UNICODETEXT). |
|
1270 DWORD allocLen = (DWORD)len; |
|
1271 if ( aFE.cfFormat == CF_TEXT ) { |
|
1272 // Someone is asking for text/plain; convert the unicode (assuming it's present) |
|
1273 // to text with the correct platform encoding. |
|
1274 char* plainTextData = nullptr; |
|
1275 char16_t* castedUnicode = reinterpret_cast<char16_t*>(data); |
|
1276 int32_t plainTextLen = 0; |
|
1277 nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText ( castedUnicode, len / 2, &plainTextData, &plainTextLen ); |
|
1278 |
|
1279 // replace the unicode data with our plaintext data. Recall that |plainTextLen| doesn't include |
|
1280 // the null in the length. |
|
1281 nsMemory::Free(data); |
|
1282 if ( plainTextData ) { |
|
1283 data = plainTextData; |
|
1284 allocLen = plainTextLen + sizeof(char); |
|
1285 } |
|
1286 else { |
|
1287 NS_WARNING ( "Oh no, couldn't convert unicode to plain text" ); |
|
1288 return S_OK; |
|
1289 } |
|
1290 } |
|
1291 else if ( aFE.cfFormat == nsClipboard::CF_HTML ) { |
|
1292 // Someone is asking for win32's HTML flavor. Convert our html fragment |
|
1293 // from unicode to UTF-8 then put it into a format specified by msft. |
|
1294 NS_ConvertUTF16toUTF8 converter ( reinterpret_cast<char16_t*>(data) ); |
|
1295 char* utf8HTML = nullptr; |
|
1296 nsresult rv = BuildPlatformHTML ( converter.get(), &utf8HTML ); // null terminates |
|
1297 |
|
1298 nsMemory::Free(data); |
|
1299 if ( NS_SUCCEEDED(rv) && utf8HTML ) { |
|
1300 // replace the unicode data with our HTML data. Don't forget the null. |
|
1301 data = utf8HTML; |
|
1302 allocLen = strlen(utf8HTML) + sizeof(char); |
|
1303 } |
|
1304 else { |
|
1305 NS_WARNING ( "Oh no, couldn't convert to HTML" ); |
|
1306 return S_OK; |
|
1307 } |
|
1308 } |
|
1309 else { |
|
1310 // we assume that any data that isn't caught above is unicode. This may |
|
1311 // be an erroneous assumption, but is true so far. |
|
1312 allocLen += sizeof(char16_t); |
|
1313 } |
|
1314 |
|
1315 hGlobalMemory = (HGLOBAL)GlobalAlloc(GMEM_MOVEABLE, allocLen); |
|
1316 |
|
1317 // Copy text to Global Memory Area |
|
1318 if ( hGlobalMemory ) { |
|
1319 char* dest = reinterpret_cast<char*>(GlobalLock(hGlobalMemory)); |
|
1320 char* source = reinterpret_cast<char*>(data); |
|
1321 memcpy ( dest, source, allocLen ); // copies the null as well |
|
1322 GlobalUnlock(hGlobalMemory); |
|
1323 } |
|
1324 aSTG.hGlobal = hGlobalMemory; |
|
1325 |
|
1326 // Now, delete the memory that was created by CreateDataFromPrimitive (or our text/plain data) |
|
1327 nsMemory::Free(data); |
|
1328 |
|
1329 return S_OK; |
|
1330 } |
|
1331 |
|
1332 //----------------------------------------------------- |
|
1333 HRESULT nsDataObj::GetFile(FORMATETC& aFE, STGMEDIUM& aSTG) |
|
1334 { |
|
1335 uint32_t dfInx = 0; |
|
1336 ULONG count; |
|
1337 FORMATETC fe; |
|
1338 m_enumFE->Reset(); |
|
1339 while (NOERROR == m_enumFE->Next(1, &fe, &count) |
|
1340 && dfInx < mDataFlavors.Length()) { |
|
1341 if (mDataFlavors[dfInx].EqualsLiteral(kNativeImageMime)) |
|
1342 return DropImage(aFE, aSTG); |
|
1343 if (mDataFlavors[dfInx].EqualsLiteral(kFileMime)) |
|
1344 return DropFile(aFE, aSTG); |
|
1345 if (mDataFlavors[dfInx].EqualsLiteral(kFilePromiseMime)) |
|
1346 return DropTempFile(aFE, aSTG); |
|
1347 dfInx++; |
|
1348 } |
|
1349 return E_FAIL; |
|
1350 } |
|
1351 |
|
1352 HRESULT nsDataObj::DropFile(FORMATETC& aFE, STGMEDIUM& aSTG) |
|
1353 { |
|
1354 nsresult rv; |
|
1355 uint32_t len = 0; |
|
1356 nsCOMPtr<nsISupports> genericDataWrapper; |
|
1357 |
|
1358 mTransferable->GetTransferData(kFileMime, getter_AddRefs(genericDataWrapper), |
|
1359 &len); |
|
1360 nsCOMPtr<nsIFile> file ( do_QueryInterface(genericDataWrapper) ); |
|
1361 |
|
1362 if (!file) |
|
1363 { |
|
1364 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper)); |
|
1365 if (ptr) { |
|
1366 nsCOMPtr<nsISupports> supports; |
|
1367 ptr->GetData(getter_AddRefs(supports)); |
|
1368 file = do_QueryInterface(supports); |
|
1369 } |
|
1370 } |
|
1371 |
|
1372 if (!file) |
|
1373 return E_FAIL; |
|
1374 |
|
1375 aSTG.tymed = TYMED_HGLOBAL; |
|
1376 aSTG.pUnkForRelease = nullptr; |
|
1377 |
|
1378 nsAutoString path; |
|
1379 rv = file->GetPath(path); |
|
1380 if (NS_FAILED(rv)) |
|
1381 return E_FAIL; |
|
1382 |
|
1383 uint32_t allocLen = path.Length() + 2; |
|
1384 HGLOBAL hGlobalMemory = nullptr; |
|
1385 char16_t *dest; |
|
1386 |
|
1387 hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + |
|
1388 allocLen * sizeof(char16_t)); |
|
1389 if (!hGlobalMemory) |
|
1390 return E_FAIL; |
|
1391 |
|
1392 DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory); |
|
1393 |
|
1394 // First, populate the drop file structure |
|
1395 pDropFile->pFiles = sizeof(DROPFILES); //Offset to start of file name string |
|
1396 pDropFile->fNC = 0; |
|
1397 pDropFile->pt.x = 0; |
|
1398 pDropFile->pt.y = 0; |
|
1399 pDropFile->fWide = TRUE; |
|
1400 |
|
1401 // Copy the filename right after the DROPFILES structure |
|
1402 dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles); |
|
1403 memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); |
|
1404 |
|
1405 // Two null characters are needed at the end of the file name. |
|
1406 // Lookup the CF_HDROP shell clipboard format for more info. |
|
1407 // Add the second null character right after the first one. |
|
1408 dest[allocLen - 1] = L'\0'; |
|
1409 |
|
1410 GlobalUnlock(hGlobalMemory); |
|
1411 |
|
1412 aSTG.hGlobal = hGlobalMemory; |
|
1413 |
|
1414 return S_OK; |
|
1415 } |
|
1416 |
|
1417 HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG) |
|
1418 { |
|
1419 nsresult rv; |
|
1420 if (!mCachedTempFile) { |
|
1421 uint32_t len = 0; |
|
1422 nsCOMPtr<nsISupports> genericDataWrapper; |
|
1423 |
|
1424 mTransferable->GetTransferData(kNativeImageMime, getter_AddRefs(genericDataWrapper), &len); |
|
1425 nsCOMPtr<imgIContainer> image(do_QueryInterface(genericDataWrapper)); |
|
1426 |
|
1427 if (!image) { |
|
1428 // Check if the image was put in an nsISupportsInterfacePointer wrapper. |
|
1429 // This might not be necessary any more, but could be useful for backwards |
|
1430 // compatibility. |
|
1431 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper)); |
|
1432 if (ptr) { |
|
1433 nsCOMPtr<nsISupports> supports; |
|
1434 ptr->GetData(getter_AddRefs(supports)); |
|
1435 image = do_QueryInterface(supports); |
|
1436 } |
|
1437 } |
|
1438 |
|
1439 if (!image) |
|
1440 return E_FAIL; |
|
1441 |
|
1442 // Use the clipboard helper class to build up a memory bitmap. |
|
1443 nsImageToClipboard converter(image); |
|
1444 HANDLE bits = nullptr; |
|
1445 rv = converter.GetPicture(&bits); // Clipboard routines return a global handle we own. |
|
1446 |
|
1447 if (NS_FAILED(rv) || !bits) |
|
1448 return E_FAIL; |
|
1449 |
|
1450 // We now own these bits! |
|
1451 uint32_t bitmapSize = GlobalSize(bits); |
|
1452 if (!bitmapSize) { |
|
1453 GlobalFree(bits); |
|
1454 return E_FAIL; |
|
1455 } |
|
1456 |
|
1457 // Save the bitmap to a temporary location. |
|
1458 nsCOMPtr<nsIFile> dropFile; |
|
1459 rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile)); |
|
1460 if (!dropFile) { |
|
1461 GlobalFree(bits); |
|
1462 return E_FAIL; |
|
1463 } |
|
1464 |
|
1465 // Filename must be random so as not to confuse apps like |
|
1466 // Photoshop which handle multiple drags into a single window. |
|
1467 char buf[13]; |
|
1468 nsCString filename; |
|
1469 NS_MakeRandomString(buf, 8); |
|
1470 memcpy(buf+8, ".bmp", 5); |
|
1471 filename.Append(nsDependentCString(buf, 12)); |
|
1472 dropFile->AppendNative(filename); |
|
1473 rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660); |
|
1474 if (NS_FAILED(rv)) { |
|
1475 GlobalFree(bits); |
|
1476 return E_FAIL; |
|
1477 } |
|
1478 |
|
1479 // Cache the temp file so we can delete it later and so |
|
1480 // it doesn't get recreated over and over on multiple calls |
|
1481 // which does occur from windows shell. |
|
1482 dropFile->Clone(getter_AddRefs(mCachedTempFile)); |
|
1483 |
|
1484 // Write the data to disk. |
|
1485 nsCOMPtr<nsIOutputStream> outStream; |
|
1486 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile); |
|
1487 if (NS_FAILED(rv)) { |
|
1488 GlobalFree(bits); |
|
1489 return E_FAIL; |
|
1490 } |
|
1491 |
|
1492 char * bm = (char *)GlobalLock(bits); |
|
1493 |
|
1494 BITMAPFILEHEADER fileHdr; |
|
1495 BITMAPINFOHEADER *bmpHdr = (BITMAPINFOHEADER*)bm; |
|
1496 |
|
1497 fileHdr.bfType = ((WORD) ('M' << 8) | 'B'); |
|
1498 fileHdr.bfSize = GlobalSize (bits) + sizeof(fileHdr); |
|
1499 fileHdr.bfReserved1 = 0; |
|
1500 fileHdr.bfReserved2 = 0; |
|
1501 fileHdr.bfOffBits = (DWORD) (sizeof(fileHdr) + bmpHdr->biSize); |
|
1502 |
|
1503 uint32_t writeCount = 0; |
|
1504 if (NS_FAILED(outStream->Write((const char *)&fileHdr, sizeof(fileHdr), &writeCount)) || |
|
1505 NS_FAILED(outStream->Write((const char *)bm, bitmapSize, &writeCount))) |
|
1506 rv = NS_ERROR_FAILURE; |
|
1507 |
|
1508 outStream->Close(); |
|
1509 |
|
1510 GlobalUnlock(bits); |
|
1511 GlobalFree(bits); |
|
1512 |
|
1513 if (NS_FAILED(rv)) |
|
1514 return E_FAIL; |
|
1515 } |
|
1516 |
|
1517 // Pass the file name back to the drop target so that it can access the file. |
|
1518 nsAutoString path; |
|
1519 rv = mCachedTempFile->GetPath(path); |
|
1520 if (NS_FAILED(rv)) |
|
1521 return E_FAIL; |
|
1522 |
|
1523 // Two null characters are needed to terminate the file name list. |
|
1524 HGLOBAL hGlobalMemory = nullptr; |
|
1525 |
|
1526 uint32_t allocLen = path.Length() + 2; |
|
1527 |
|
1528 aSTG.tymed = TYMED_HGLOBAL; |
|
1529 aSTG.pUnkForRelease = nullptr; |
|
1530 |
|
1531 hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(char16_t)); |
|
1532 if (!hGlobalMemory) |
|
1533 return E_FAIL; |
|
1534 |
|
1535 DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory); |
|
1536 |
|
1537 // First, populate the drop file structure. |
|
1538 pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array. |
|
1539 pDropFile->fNC = 0; |
|
1540 pDropFile->pt.x = 0; |
|
1541 pDropFile->pt.y = 0; |
|
1542 pDropFile->fWide = TRUE; |
|
1543 |
|
1544 // Copy the filename right after the DROPFILES structure. |
|
1545 char16_t* dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles); |
|
1546 memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); // Copies the null character in path as well. |
|
1547 |
|
1548 // Two null characters are needed at the end of the file name. |
|
1549 // Lookup the CF_HDROP shell clipboard format for more info. |
|
1550 // Add the second null character right after the first one. |
|
1551 dest[allocLen - 1] = L'\0'; |
|
1552 |
|
1553 GlobalUnlock(hGlobalMemory); |
|
1554 |
|
1555 aSTG.hGlobal = hGlobalMemory; |
|
1556 |
|
1557 return S_OK; |
|
1558 } |
|
1559 |
|
1560 HRESULT nsDataObj::DropTempFile(FORMATETC& aFE, STGMEDIUM& aSTG) |
|
1561 { |
|
1562 nsresult rv; |
|
1563 if (!mCachedTempFile) { |
|
1564 // Tempfile will need a temporary location. |
|
1565 nsCOMPtr<nsIFile> dropFile; |
|
1566 rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile)); |
|
1567 if (!dropFile) |
|
1568 return E_FAIL; |
|
1569 |
|
1570 // Filename must be random |
|
1571 nsCString filename; |
|
1572 nsAutoString wideFileName; |
|
1573 nsCOMPtr<nsIURI> sourceURI; |
|
1574 HRESULT res; |
|
1575 res = GetDownloadDetails(getter_AddRefs(sourceURI), |
|
1576 wideFileName); |
|
1577 if (FAILED(res)) |
|
1578 return res; |
|
1579 NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, filename); |
|
1580 |
|
1581 dropFile->AppendNative(filename); |
|
1582 rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660); |
|
1583 if (NS_FAILED(rv)) |
|
1584 return E_FAIL; |
|
1585 |
|
1586 // Cache the temp file so we can delete it later and so |
|
1587 // it doesn't get recreated over and over on multiple calls |
|
1588 // which does occur from windows shell. |
|
1589 dropFile->Clone(getter_AddRefs(mCachedTempFile)); |
|
1590 |
|
1591 // Write the data to disk. |
|
1592 nsCOMPtr<nsIOutputStream> outStream; |
|
1593 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile); |
|
1594 if (NS_FAILED(rv)) |
|
1595 return E_FAIL; |
|
1596 |
|
1597 IStream *pStream = nullptr; |
|
1598 nsDataObj::CreateStream(&pStream); |
|
1599 NS_ENSURE_TRUE(pStream, E_FAIL); |
|
1600 |
|
1601 char buffer[512]; |
|
1602 ULONG readCount = 0; |
|
1603 uint32_t writeCount = 0; |
|
1604 while (1) { |
|
1605 HRESULT hres = pStream->Read(buffer, sizeof(buffer), &readCount); |
|
1606 if (FAILED(hres)) |
|
1607 return E_FAIL; |
|
1608 if (readCount == 0) |
|
1609 break; |
|
1610 rv = outStream->Write(buffer, readCount, &writeCount); |
|
1611 if (NS_FAILED(rv)) |
|
1612 return E_FAIL; |
|
1613 } |
|
1614 outStream->Close(); |
|
1615 pStream->Release(); |
|
1616 } |
|
1617 |
|
1618 // Pass the file name back to the drop target so that it can access the file. |
|
1619 nsAutoString path; |
|
1620 rv = mCachedTempFile->GetPath(path); |
|
1621 if (NS_FAILED(rv)) |
|
1622 return E_FAIL; |
|
1623 |
|
1624 uint32_t allocLen = path.Length() + 2; |
|
1625 |
|
1626 // Two null characters are needed to terminate the file name list. |
|
1627 HGLOBAL hGlobalMemory = nullptr; |
|
1628 |
|
1629 aSTG.tymed = TYMED_HGLOBAL; |
|
1630 aSTG.pUnkForRelease = nullptr; |
|
1631 |
|
1632 hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(char16_t)); |
|
1633 if (!hGlobalMemory) |
|
1634 return E_FAIL; |
|
1635 |
|
1636 DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory); |
|
1637 |
|
1638 // First, populate the drop file structure. |
|
1639 pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array. |
|
1640 pDropFile->fNC = 0; |
|
1641 pDropFile->pt.x = 0; |
|
1642 pDropFile->pt.y = 0; |
|
1643 pDropFile->fWide = TRUE; |
|
1644 |
|
1645 // Copy the filename right after the DROPFILES structure. |
|
1646 char16_t* dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles); |
|
1647 memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); // Copies the null character in path as well. |
|
1648 |
|
1649 // Two null characters are needed at the end of the file name. |
|
1650 // Lookup the CF_HDROP shell clipboard format for more info. |
|
1651 // Add the second null character right after the first one. |
|
1652 dest[allocLen - 1] = L'\0'; |
|
1653 |
|
1654 GlobalUnlock(hGlobalMemory); |
|
1655 |
|
1656 aSTG.hGlobal = hGlobalMemory; |
|
1657 |
|
1658 return S_OK; |
|
1659 } |
|
1660 |
|
1661 //----------------------------------------------------- |
|
1662 HRESULT nsDataObj::GetMetafilePict(FORMATETC&, STGMEDIUM&) |
|
1663 { |
|
1664 return E_NOTIMPL; |
|
1665 } |
|
1666 |
|
1667 //----------------------------------------------------- |
|
1668 HRESULT nsDataObj::SetBitmap(FORMATETC&, STGMEDIUM&) |
|
1669 { |
|
1670 return E_NOTIMPL; |
|
1671 } |
|
1672 |
|
1673 //----------------------------------------------------- |
|
1674 HRESULT nsDataObj::SetDib(FORMATETC&, STGMEDIUM&) |
|
1675 { |
|
1676 return E_FAIL; |
|
1677 } |
|
1678 |
|
1679 //----------------------------------------------------- |
|
1680 HRESULT nsDataObj::SetText (FORMATETC& aFE, STGMEDIUM& aSTG) |
|
1681 { |
|
1682 return E_FAIL; |
|
1683 } |
|
1684 |
|
1685 //----------------------------------------------------- |
|
1686 HRESULT nsDataObj::SetMetafilePict(FORMATETC&, STGMEDIUM&) |
|
1687 { |
|
1688 return E_FAIL; |
|
1689 } |
|
1690 |
|
1691 |
|
1692 |
|
1693 //----------------------------------------------------- |
|
1694 //----------------------------------------------------- |
|
1695 CLSID nsDataObj::GetClassID() const |
|
1696 { |
|
1697 return CLSID_nsDataObj; |
|
1698 } |
|
1699 |
|
1700 //----------------------------------------------------- |
|
1701 // Registers the DataFlavor/FE pair. |
|
1702 //----------------------------------------------------- |
|
1703 void nsDataObj::AddDataFlavor(const char* aDataFlavor, LPFORMATETC aFE) |
|
1704 { |
|
1705 // These two lists are the mapping to and from data flavors and FEs. |
|
1706 // Later, OLE will tell us it needs a certain type of FORMATETC (text, unicode, etc) |
|
1707 // unicode, etc), so we will look up the data flavor that corresponds to |
|
1708 // the FE and then ask the transferable for that type of data. |
|
1709 mDataFlavors.AppendElement(aDataFlavor); |
|
1710 m_enumFE->AddFormatEtc(aFE); |
|
1711 } |
|
1712 |
|
1713 //----------------------------------------------------- |
|
1714 // Sets the transferable object |
|
1715 //----------------------------------------------------- |
|
1716 void nsDataObj::SetTransferable(nsITransferable * aTransferable) |
|
1717 { |
|
1718 NS_IF_RELEASE(mTransferable); |
|
1719 |
|
1720 mTransferable = aTransferable; |
|
1721 if (nullptr == mTransferable) { |
|
1722 return; |
|
1723 } |
|
1724 |
|
1725 NS_ADDREF(mTransferable); |
|
1726 |
|
1727 return; |
|
1728 } |
|
1729 |
|
1730 |
|
1731 // |
|
1732 // ExtractURL |
|
1733 // |
|
1734 // Roots around in the transferable for the appropriate flavor that indicates |
|
1735 // a url and pulls out the url portion of the data. Used mostly for creating |
|
1736 // internet shortcuts on the desktop. The url flavor is of the format: |
|
1737 // |
|
1738 // <url> <linefeed> <page title> |
|
1739 // |
|
1740 nsresult |
|
1741 nsDataObj :: ExtractShortcutURL ( nsString & outURL ) |
|
1742 { |
|
1743 NS_ASSERTION ( mTransferable, "We don't have a good transferable" ); |
|
1744 nsresult rv = NS_ERROR_FAILURE; |
|
1745 |
|
1746 uint32_t len = 0; |
|
1747 nsCOMPtr<nsISupports> genericURL; |
|
1748 if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) { |
|
1749 nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) ); |
|
1750 if ( urlObject ) { |
|
1751 nsAutoString url; |
|
1752 urlObject->GetData ( url ); |
|
1753 outURL = url; |
|
1754 |
|
1755 // find the first linefeed in the data, that's where the url ends. trunc the |
|
1756 // result string at that point. |
|
1757 int32_t lineIndex = outURL.FindChar ( '\n' ); |
|
1758 NS_ASSERTION ( lineIndex > 0, "Format for url flavor is <url> <linefeed> <page title>" ); |
|
1759 if ( lineIndex > 0 ) { |
|
1760 outURL.Truncate ( lineIndex ); |
|
1761 rv = NS_OK; |
|
1762 } |
|
1763 } |
|
1764 } else if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLDataMime, getter_AddRefs(genericURL), &len)) || |
|
1765 NS_SUCCEEDED(mTransferable->GetTransferData(kURLPrivateMime, getter_AddRefs(genericURL), &len)) ) { |
|
1766 nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) ); |
|
1767 if ( urlObject ) { |
|
1768 nsAutoString url; |
|
1769 urlObject->GetData ( url ); |
|
1770 outURL = url; |
|
1771 |
|
1772 rv = NS_OK; |
|
1773 } |
|
1774 |
|
1775 } // if found flavor |
|
1776 |
|
1777 return rv; |
|
1778 |
|
1779 } // ExtractShortcutURL |
|
1780 |
|
1781 |
|
1782 // |
|
1783 // ExtractShortcutTitle |
|
1784 // |
|
1785 // Roots around in the transferable for the appropriate flavor that indicates |
|
1786 // a url and pulls out the title portion of the data. Used mostly for creating |
|
1787 // internet shortcuts on the desktop. The url flavor is of the format: |
|
1788 // |
|
1789 // <url> <linefeed> <page title> |
|
1790 // |
|
1791 nsresult |
|
1792 nsDataObj :: ExtractShortcutTitle ( nsString & outTitle ) |
|
1793 { |
|
1794 NS_ASSERTION ( mTransferable, "We'd don't have a good transferable" ); |
|
1795 nsresult rv = NS_ERROR_FAILURE; |
|
1796 |
|
1797 uint32_t len = 0; |
|
1798 nsCOMPtr<nsISupports> genericURL; |
|
1799 if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) { |
|
1800 nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) ); |
|
1801 if ( urlObject ) { |
|
1802 nsAutoString url; |
|
1803 urlObject->GetData ( url ); |
|
1804 |
|
1805 // find the first linefeed in the data, that's where the url ends. we want |
|
1806 // everything after that linefeed. FindChar() returns -1 if we can't find |
|
1807 int32_t lineIndex = url.FindChar ( '\n' ); |
|
1808 NS_ASSERTION ( lineIndex != -1, "Format for url flavor is <url> <linefeed> <page title>" ); |
|
1809 if ( lineIndex != -1 ) { |
|
1810 url.Mid ( outTitle, lineIndex + 1, (len/2) - (lineIndex + 1) ); |
|
1811 rv = NS_OK; |
|
1812 } |
|
1813 } |
|
1814 } // if found flavor |
|
1815 |
|
1816 return rv; |
|
1817 |
|
1818 } // ExtractShortcutTitle |
|
1819 |
|
1820 |
|
1821 // |
|
1822 // BuildPlatformHTML |
|
1823 // |
|
1824 // Munge our HTML data to win32's CF_HTML spec. Basically, put the requisite |
|
1825 // header information on it. This will null terminate |outPlatformHTML|. See |
|
1826 // http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp |
|
1827 // for details. |
|
1828 // |
|
1829 // We assume that |inOurHTML| is already a fragment (ie, doesn't have <HTML> |
|
1830 // or <BODY> tags). We'll wrap the fragment with them to make other apps |
|
1831 // happy. |
|
1832 // |
|
1833 nsresult |
|
1834 nsDataObj :: BuildPlatformHTML ( const char* inOurHTML, char** outPlatformHTML ) |
|
1835 { |
|
1836 *outPlatformHTML = nullptr; |
|
1837 |
|
1838 nsDependentCString inHTMLString(inOurHTML); |
|
1839 const char* const numPlaceholder = "00000000"; |
|
1840 const char* const startHTMLPrefix = "Version:0.9\r\nStartHTML:"; |
|
1841 const char* const endHTMLPrefix = "\r\nEndHTML:"; |
|
1842 const char* const startFragPrefix = "\r\nStartFragment:"; |
|
1843 const char* const endFragPrefix = "\r\nEndFragment:"; |
|
1844 const char* const startSourceURLPrefix = "\r\nSourceURL:"; |
|
1845 const char* const endFragTrailer = "\r\n"; |
|
1846 |
|
1847 // Do we already have mSourceURL from a drag? |
|
1848 if (mSourceURL.IsEmpty()) { |
|
1849 nsAutoString url; |
|
1850 ExtractShortcutURL(url); |
|
1851 |
|
1852 AppendUTF16toUTF8(url, mSourceURL); |
|
1853 } |
|
1854 |
|
1855 const int32_t kSourceURLLength = mSourceURL.Length(); |
|
1856 const int32_t kNumberLength = strlen(numPlaceholder); |
|
1857 |
|
1858 const int32_t kTotalHeaderLen = strlen(startHTMLPrefix) + |
|
1859 strlen(endHTMLPrefix) + |
|
1860 strlen(startFragPrefix) + |
|
1861 strlen(endFragPrefix) + |
|
1862 strlen(endFragTrailer) + |
|
1863 (kSourceURLLength > 0 ? strlen(startSourceURLPrefix) : 0) + |
|
1864 kSourceURLLength + |
|
1865 (4 * kNumberLength); |
|
1866 |
|
1867 NS_NAMED_LITERAL_CSTRING(htmlHeaderString, "<html><body>\r\n"); |
|
1868 |
|
1869 NS_NAMED_LITERAL_CSTRING(fragmentHeaderString, "<!--StartFragment-->"); |
|
1870 |
|
1871 nsDependentCString trailingString( |
|
1872 "<!--EndFragment-->\r\n" |
|
1873 "</body>\r\n" |
|
1874 "</html>"); |
|
1875 |
|
1876 // calculate the offsets |
|
1877 int32_t startHTMLOffset = kTotalHeaderLen; |
|
1878 int32_t startFragOffset = startHTMLOffset |
|
1879 + htmlHeaderString.Length() |
|
1880 + fragmentHeaderString.Length(); |
|
1881 |
|
1882 int32_t endFragOffset = startFragOffset |
|
1883 + inHTMLString.Length(); |
|
1884 |
|
1885 int32_t endHTMLOffset = endFragOffset |
|
1886 + trailingString.Length(); |
|
1887 |
|
1888 // now build the final version |
|
1889 nsCString clipboardString; |
|
1890 clipboardString.SetCapacity(endHTMLOffset); |
|
1891 |
|
1892 clipboardString.Append(startHTMLPrefix); |
|
1893 clipboardString.Append(nsPrintfCString("%08u", startHTMLOffset)); |
|
1894 |
|
1895 clipboardString.Append(endHTMLPrefix); |
|
1896 clipboardString.Append(nsPrintfCString("%08u", endHTMLOffset)); |
|
1897 |
|
1898 clipboardString.Append(startFragPrefix); |
|
1899 clipboardString.Append(nsPrintfCString("%08u", startFragOffset)); |
|
1900 |
|
1901 clipboardString.Append(endFragPrefix); |
|
1902 clipboardString.Append(nsPrintfCString("%08u", endFragOffset)); |
|
1903 |
|
1904 if (kSourceURLLength > 0) { |
|
1905 clipboardString.Append(startSourceURLPrefix); |
|
1906 clipboardString.Append(mSourceURL); |
|
1907 } |
|
1908 |
|
1909 clipboardString.Append(endFragTrailer); |
|
1910 |
|
1911 clipboardString.Append(htmlHeaderString); |
|
1912 clipboardString.Append(fragmentHeaderString); |
|
1913 clipboardString.Append(inHTMLString); |
|
1914 clipboardString.Append(trailingString); |
|
1915 |
|
1916 *outPlatformHTML = ToNewCString(clipboardString); |
|
1917 if (!*outPlatformHTML) |
|
1918 return NS_ERROR_OUT_OF_MEMORY; |
|
1919 |
|
1920 return NS_OK; |
|
1921 } |
|
1922 |
|
1923 HRESULT |
|
1924 nsDataObj :: GetUniformResourceLocator( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode ) |
|
1925 { |
|
1926 HRESULT res = S_OK; |
|
1927 if (IsFlavourPresent(kURLMime)) { |
|
1928 if ( aIsUnicode ) |
|
1929 res = ExtractUniformResourceLocatorW( aFE, aSTG ); |
|
1930 else |
|
1931 res = ExtractUniformResourceLocatorA( aFE, aSTG ); |
|
1932 } |
|
1933 else |
|
1934 NS_WARNING ("Not yet implemented\n"); |
|
1935 return res; |
|
1936 } |
|
1937 |
|
1938 HRESULT |
|
1939 nsDataObj::ExtractUniformResourceLocatorA(FORMATETC& aFE, STGMEDIUM& aSTG ) |
|
1940 { |
|
1941 HRESULT result = S_OK; |
|
1942 |
|
1943 nsAutoString url; |
|
1944 if (NS_FAILED(ExtractShortcutURL(url))) |
|
1945 return E_OUTOFMEMORY; |
|
1946 |
|
1947 NS_LossyConvertUTF16toASCII asciiUrl(url); |
|
1948 const int totalLen = asciiUrl.Length() + 1; |
|
1949 HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen); |
|
1950 if (!hGlobalMemory) |
|
1951 return E_OUTOFMEMORY; |
|
1952 |
|
1953 char* contents = reinterpret_cast<char*>(GlobalLock(hGlobalMemory)); |
|
1954 if (!contents) { |
|
1955 GlobalFree(hGlobalMemory); |
|
1956 return E_OUTOFMEMORY; |
|
1957 } |
|
1958 |
|
1959 strcpy(contents, asciiUrl.get()); |
|
1960 GlobalUnlock(hGlobalMemory); |
|
1961 aSTG.hGlobal = hGlobalMemory; |
|
1962 aSTG.tymed = TYMED_HGLOBAL; |
|
1963 |
|
1964 return result; |
|
1965 } |
|
1966 |
|
1967 HRESULT |
|
1968 nsDataObj::ExtractUniformResourceLocatorW(FORMATETC& aFE, STGMEDIUM& aSTG ) |
|
1969 { |
|
1970 HRESULT result = S_OK; |
|
1971 |
|
1972 nsAutoString url; |
|
1973 if (NS_FAILED(ExtractShortcutURL(url))) |
|
1974 return E_OUTOFMEMORY; |
|
1975 |
|
1976 const int totalLen = (url.Length() + 1) * sizeof(char16_t); |
|
1977 HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen); |
|
1978 if (!hGlobalMemory) |
|
1979 return E_OUTOFMEMORY; |
|
1980 |
|
1981 wchar_t* contents = reinterpret_cast<wchar_t*>(GlobalLock(hGlobalMemory)); |
|
1982 if (!contents) { |
|
1983 GlobalFree(hGlobalMemory); |
|
1984 return E_OUTOFMEMORY; |
|
1985 } |
|
1986 |
|
1987 wcscpy(contents, url.get()); |
|
1988 GlobalUnlock(hGlobalMemory); |
|
1989 aSTG.hGlobal = hGlobalMemory; |
|
1990 aSTG.tymed = TYMED_HGLOBAL; |
|
1991 |
|
1992 return result; |
|
1993 } |
|
1994 |
|
1995 |
|
1996 // Gets the filename from the kFilePromiseURLMime flavour |
|
1997 HRESULT nsDataObj::GetDownloadDetails(nsIURI **aSourceURI, |
|
1998 nsAString &aFilename) |
|
1999 { |
|
2000 *aSourceURI = nullptr; |
|
2001 |
|
2002 NS_ENSURE_TRUE(mTransferable, E_FAIL); |
|
2003 |
|
2004 // get the URI from the kFilePromiseURLMime flavor |
|
2005 nsCOMPtr<nsISupports> urlPrimitive; |
|
2006 uint32_t dataSize = 0; |
|
2007 mTransferable->GetTransferData(kFilePromiseURLMime, getter_AddRefs(urlPrimitive), &dataSize); |
|
2008 nsCOMPtr<nsISupportsString> srcUrlPrimitive = do_QueryInterface(urlPrimitive); |
|
2009 NS_ENSURE_TRUE(srcUrlPrimitive, E_FAIL); |
|
2010 |
|
2011 nsAutoString srcUri; |
|
2012 srcUrlPrimitive->GetData(srcUri); |
|
2013 if (srcUri.IsEmpty()) |
|
2014 return E_FAIL; |
|
2015 nsCOMPtr<nsIURI> sourceURI; |
|
2016 NS_NewURI(getter_AddRefs(sourceURI), srcUri); |
|
2017 |
|
2018 nsAutoString srcFileName; |
|
2019 nsCOMPtr<nsISupports> fileNamePrimitive; |
|
2020 mTransferable->GetTransferData(kFilePromiseDestFilename, getter_AddRefs(fileNamePrimitive), &dataSize); |
|
2021 nsCOMPtr<nsISupportsString> srcFileNamePrimitive = do_QueryInterface(fileNamePrimitive); |
|
2022 if (srcFileNamePrimitive) { |
|
2023 srcFileNamePrimitive->GetData(srcFileName); |
|
2024 } else { |
|
2025 nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI); |
|
2026 if (!sourceURL) |
|
2027 return E_FAIL; |
|
2028 |
|
2029 nsAutoCString urlFileName; |
|
2030 sourceURL->GetFileName(urlFileName); |
|
2031 NS_UnescapeURL(urlFileName); |
|
2032 CopyUTF8toUTF16(urlFileName, srcFileName); |
|
2033 } |
|
2034 if (srcFileName.IsEmpty()) |
|
2035 return E_FAIL; |
|
2036 |
|
2037 // make the name safe for the filesystem |
|
2038 MangleTextToValidFilename(srcFileName); |
|
2039 |
|
2040 sourceURI.swap(*aSourceURI); |
|
2041 aFilename = srcFileName; |
|
2042 return S_OK; |
|
2043 } |
|
2044 |
|
2045 HRESULT nsDataObj::GetFileDescriptor_IStreamA(FORMATETC& aFE, STGMEDIUM& aSTG) |
|
2046 { |
|
2047 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW)); |
|
2048 NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY); |
|
2049 |
|
2050 LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(GlobalLock(fileGroupDescHandle)); |
|
2051 if (!fileGroupDescA) { |
|
2052 ::GlobalFree(fileGroupDescHandle); |
|
2053 return E_OUTOFMEMORY; |
|
2054 } |
|
2055 |
|
2056 nsAutoString wideFileName; |
|
2057 HRESULT res; |
|
2058 nsCOMPtr<nsIURI> sourceURI; |
|
2059 res = GetDownloadDetails(getter_AddRefs(sourceURI), wideFileName); |
|
2060 if (FAILED(res)) |
|
2061 { |
|
2062 ::GlobalFree(fileGroupDescHandle); |
|
2063 return res; |
|
2064 } |
|
2065 |
|
2066 nsAutoCString nativeFileName; |
|
2067 NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, nativeFileName); |
|
2068 |
|
2069 strncpy(fileGroupDescA->fgd[0].cFileName, nativeFileName.get(), NS_MAX_FILEDESCRIPTOR - 1); |
|
2070 fileGroupDescA->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0'; |
|
2071 |
|
2072 // one file in the file block |
|
2073 fileGroupDescA->cItems = 1; |
|
2074 fileGroupDescA->fgd[0].dwFlags = FD_PROGRESSUI; |
|
2075 |
|
2076 GlobalUnlock( fileGroupDescHandle ); |
|
2077 aSTG.hGlobal = fileGroupDescHandle; |
|
2078 aSTG.tymed = TYMED_HGLOBAL; |
|
2079 |
|
2080 return S_OK; |
|
2081 } |
|
2082 |
|
2083 HRESULT nsDataObj::GetFileDescriptor_IStreamW(FORMATETC& aFE, STGMEDIUM& aSTG) |
|
2084 { |
|
2085 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW)); |
|
2086 NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY); |
|
2087 |
|
2088 LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(GlobalLock(fileGroupDescHandle)); |
|
2089 if (!fileGroupDescW) { |
|
2090 ::GlobalFree(fileGroupDescHandle); |
|
2091 return E_OUTOFMEMORY; |
|
2092 } |
|
2093 |
|
2094 nsAutoString wideFileName; |
|
2095 HRESULT res; |
|
2096 nsCOMPtr<nsIURI> sourceURI; |
|
2097 res = GetDownloadDetails(getter_AddRefs(sourceURI), |
|
2098 wideFileName); |
|
2099 if (FAILED(res)) |
|
2100 { |
|
2101 ::GlobalFree(fileGroupDescHandle); |
|
2102 return res; |
|
2103 } |
|
2104 |
|
2105 wcsncpy(fileGroupDescW->fgd[0].cFileName, wideFileName.get(), NS_MAX_FILEDESCRIPTOR - 1); |
|
2106 fileGroupDescW->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0'; |
|
2107 // one file in the file block |
|
2108 fileGroupDescW->cItems = 1; |
|
2109 fileGroupDescW->fgd[0].dwFlags = FD_PROGRESSUI; |
|
2110 |
|
2111 GlobalUnlock(fileGroupDescHandle); |
|
2112 aSTG.hGlobal = fileGroupDescHandle; |
|
2113 aSTG.tymed = TYMED_HGLOBAL; |
|
2114 |
|
2115 return S_OK; |
|
2116 } |
|
2117 |
|
2118 HRESULT nsDataObj::GetFileContents_IStream(FORMATETC& aFE, STGMEDIUM& aSTG) |
|
2119 { |
|
2120 IStream *pStream = nullptr; |
|
2121 |
|
2122 nsDataObj::CreateStream(&pStream); |
|
2123 NS_ENSURE_TRUE(pStream, E_FAIL); |
|
2124 |
|
2125 aSTG.tymed = TYMED_ISTREAM; |
|
2126 aSTG.pstm = pStream; |
|
2127 aSTG.pUnkForRelease = nullptr; |
|
2128 |
|
2129 return S_OK; |
|
2130 } |
|
2131 |
|
2132 void nsDataObj::RemoveTempFile(nsITimer* aTimer, void* aClosure) |
|
2133 { |
|
2134 nsDataObj *timedDataObj = static_cast<nsDataObj *>(aClosure); |
|
2135 if (timedDataObj->mCachedTempFile) { |
|
2136 timedDataObj->mCachedTempFile->Remove(false); |
|
2137 timedDataObj->mCachedTempFile = nullptr; |
|
2138 } |
|
2139 timedDataObj->Release(); |
|
2140 } |