widget/windows/nsDataObj.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 }

mercurial