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