widget/windows/nsDataObj.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/windows/nsDataObj.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2140 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/ArrayUtils.h"
    1.10 +
    1.11 +#include <ole2.h>
    1.12 +#include <shlobj.h>
    1.13 +
    1.14 +#include "nsDataObj.h"
    1.15 +#include "nsClipboard.h"
    1.16 +#include "nsReadableUtils.h"
    1.17 +#include "nsITransferable.h"
    1.18 +#include "nsISupportsPrimitives.h"
    1.19 +#include "IEnumFE.h"
    1.20 +#include "nsPrimitiveHelpers.h"
    1.21 +#include "nsXPIDLString.h"
    1.22 +#include "nsImageClipboard.h"
    1.23 +#include "nsCRT.h"
    1.24 +#include "nsPrintfCString.h"
    1.25 +#include "nsIStringBundle.h"
    1.26 +#include "nsEscape.h"
    1.27 +#include "nsIURL.h"
    1.28 +#include "nsNetUtil.h"
    1.29 +#include "nsXPCOMStrings.h"
    1.30 +#include "nscore.h"
    1.31 +#include "nsDirectoryServiceDefs.h"
    1.32 +#include "nsITimer.h"
    1.33 +#include "nsThreadUtils.h"
    1.34 +#include "mozilla/Preferences.h"
    1.35 +
    1.36 +#include "WinUtils.h"
    1.37 +#include "mozilla/LazyIdleThread.h"
    1.38 +#include "mozilla/WindowsVersion.h"
    1.39 +#include <algorithm>
    1.40 +
    1.41 +
    1.42 +using namespace mozilla;
    1.43 +using namespace mozilla::widget;
    1.44 +
    1.45 +#define DEFAULT_THREAD_TIMEOUT_MS 30000
    1.46 +
    1.47 +NS_IMPL_ISUPPORTS(nsDataObj::CStream, nsIStreamListener)
    1.48 +
    1.49 +//-----------------------------------------------------------------------------
    1.50 +// CStream implementation
    1.51 +nsDataObj::CStream::CStream() :
    1.52 +  mChannelRead(false),
    1.53 +  mStreamRead(0)
    1.54 +{
    1.55 +}
    1.56 +
    1.57 +//-----------------------------------------------------------------------------
    1.58 +nsDataObj::CStream::~CStream()
    1.59 +{
    1.60 +}
    1.61 +
    1.62 +//-----------------------------------------------------------------------------
    1.63 +// helper - initializes the stream
    1.64 +nsresult nsDataObj::CStream::Init(nsIURI *pSourceURI)
    1.65 +{
    1.66 +  nsresult rv;
    1.67 +  rv = NS_NewChannel(getter_AddRefs(mChannel), pSourceURI,
    1.68 +                     nullptr, nullptr, nullptr,
    1.69 +                     nsIRequest::LOAD_FROM_CACHE);
    1.70 +  NS_ENSURE_SUCCESS(rv, rv);
    1.71 +  rv = mChannel->AsyncOpen(this, nullptr);
    1.72 +  NS_ENSURE_SUCCESS(rv, rv);
    1.73 +  return NS_OK;
    1.74 +}
    1.75 +
    1.76 +//-----------------------------------------------------------------------------
    1.77 +// IUnknown's QueryInterface, nsISupport's AddRef and Release are shared by
    1.78 +// IUnknown and nsIStreamListener.
    1.79 +STDMETHODIMP nsDataObj::CStream::QueryInterface(REFIID refiid, void** ppvResult)
    1.80 +{
    1.81 +  *ppvResult = nullptr;
    1.82 +  if (IID_IUnknown == refiid ||
    1.83 +      refiid == IID_IStream)
    1.84 +
    1.85 +  {
    1.86 +    *ppvResult = this;
    1.87 +  }
    1.88 +
    1.89 +  if (nullptr != *ppvResult)
    1.90 +  {
    1.91 +    ((LPUNKNOWN)*ppvResult)->AddRef();
    1.92 +    return S_OK;
    1.93 +  }
    1.94 +
    1.95 +  return E_NOINTERFACE;
    1.96 +}
    1.97 +
    1.98 +// nsIStreamListener implementation
    1.99 +NS_IMETHODIMP
   1.100 +nsDataObj::CStream::OnDataAvailable(nsIRequest *aRequest,
   1.101 +                                    nsISupports *aContext,
   1.102 +                                    nsIInputStream *aInputStream,
   1.103 +                                    uint64_t aOffset, // offset within the stream
   1.104 +                                    uint32_t aCount) // bytes available on this call
   1.105 +{
   1.106 +    // Extend the write buffer for the incoming data.
   1.107 +    uint8_t* buffer = mChannelData.AppendElements(aCount);
   1.108 +    if (buffer == nullptr)
   1.109 +      return NS_ERROR_OUT_OF_MEMORY;
   1.110 +    NS_ASSERTION((mChannelData.Length() == (aOffset + aCount)),
   1.111 +      "stream length mismatch w/write buffer");
   1.112 +
   1.113 +    // Read() may not return aCount on a single call, so loop until we've
   1.114 +    // accumulated all the data OnDataAvailable has promised.
   1.115 +    nsresult rv;
   1.116 +    uint32_t odaBytesReadTotal = 0;
   1.117 +    do {
   1.118 +      uint32_t bytesReadByCall = 0;
   1.119 +      rv = aInputStream->Read((char*)(buffer + odaBytesReadTotal),
   1.120 +                              aCount, &bytesReadByCall);
   1.121 +      odaBytesReadTotal += bytesReadByCall;
   1.122 +    } while (aCount < odaBytesReadTotal && NS_SUCCEEDED(rv));
   1.123 +    return rv;
   1.124 +}
   1.125 +
   1.126 +NS_IMETHODIMP nsDataObj::CStream::OnStartRequest(nsIRequest *aRequest,
   1.127 +                                                 nsISupports *aContext)
   1.128 +{
   1.129 +    mChannelResult = NS_OK;
   1.130 +    return NS_OK;
   1.131 +}
   1.132 +
   1.133 +NS_IMETHODIMP nsDataObj::CStream::OnStopRequest(nsIRequest *aRequest,
   1.134 +                                                nsISupports *aContext,
   1.135 +                                                nsresult aStatusCode)
   1.136 +{
   1.137 +    mChannelRead = true;
   1.138 +    mChannelResult = aStatusCode;
   1.139 +    return NS_OK;
   1.140 +}
   1.141 +
   1.142 +// Pumps thread messages while waiting for the async listener operation to
   1.143 +// complete. Failing this call will fail the stream incall from Windows
   1.144 +// and cancel the operation.
   1.145 +nsresult nsDataObj::CStream::WaitForCompletion()
   1.146 +{
   1.147 +  // We are guaranteed OnStopRequest will get called, so this should be ok.
   1.148 +  while (!mChannelRead) {
   1.149 +    // Pump messages
   1.150 +    NS_ProcessNextEvent(nullptr, true);
   1.151 +  }
   1.152 +
   1.153 +  if (!mChannelData.Length())
   1.154 +    mChannelResult = NS_ERROR_FAILURE;
   1.155 +
   1.156 +  return mChannelResult;
   1.157 +}
   1.158 +
   1.159 +//-----------------------------------------------------------------------------
   1.160 +// IStream
   1.161 +STDMETHODIMP nsDataObj::CStream::Clone(IStream** ppStream)
   1.162 +{
   1.163 +  return E_NOTIMPL;
   1.164 +}
   1.165 +
   1.166 +//-----------------------------------------------------------------------------
   1.167 +STDMETHODIMP nsDataObj::CStream::Commit(DWORD dwFrags)
   1.168 +{
   1.169 +  return E_NOTIMPL;
   1.170 +}
   1.171 +
   1.172 +//-----------------------------------------------------------------------------
   1.173 +STDMETHODIMP nsDataObj::CStream::CopyTo(IStream* pDestStream,
   1.174 +                                        ULARGE_INTEGER nBytesToCopy,
   1.175 +                                        ULARGE_INTEGER* nBytesRead,
   1.176 +                                        ULARGE_INTEGER* nBytesWritten)
   1.177 +{
   1.178 +  return E_NOTIMPL;
   1.179 +}
   1.180 +
   1.181 +//-----------------------------------------------------------------------------
   1.182 +STDMETHODIMP nsDataObj::CStream::LockRegion(ULARGE_INTEGER nStart,
   1.183 +                                            ULARGE_INTEGER nBytes,
   1.184 +                                            DWORD dwFlags)
   1.185 +{
   1.186 +  return E_NOTIMPL;
   1.187 +}
   1.188 +
   1.189 +//-----------------------------------------------------------------------------
   1.190 +STDMETHODIMP nsDataObj::CStream::Read(void* pvBuffer,
   1.191 +                                      ULONG nBytesToRead,
   1.192 +                                      ULONG* nBytesRead)
   1.193 +{
   1.194 +  // Wait for the write into our buffer to complete via the stream listener.
   1.195 +  // We can't respond to this by saying "call us back later".
   1.196 +  if (NS_FAILED(WaitForCompletion()))
   1.197 +    return E_FAIL;
   1.198 +
   1.199 +  // Bytes left for Windows to read out of our buffer
   1.200 +  ULONG bytesLeft = mChannelData.Length() - mStreamRead;
   1.201 +  // Let Windows know what we will hand back, usually this is the entire buffer
   1.202 +  *nBytesRead = std::min(bytesLeft, nBytesToRead);
   1.203 +  // Copy the buffer data over
   1.204 +  memcpy(pvBuffer, ((char*)mChannelData.Elements() + mStreamRead), *nBytesRead);
   1.205 +  // Update our bytes read tracking
   1.206 +  mStreamRead += *nBytesRead;
   1.207 +  return S_OK;
   1.208 +}
   1.209 +
   1.210 +//-----------------------------------------------------------------------------
   1.211 +STDMETHODIMP nsDataObj::CStream::Revert(void)
   1.212 +{
   1.213 +  return E_NOTIMPL;
   1.214 +}
   1.215 +
   1.216 +//-----------------------------------------------------------------------------
   1.217 +STDMETHODIMP nsDataObj::CStream::Seek(LARGE_INTEGER nMove,
   1.218 +                                      DWORD dwOrigin,
   1.219 +                                      ULARGE_INTEGER* nNewPos)
   1.220 +{
   1.221 +  if (nNewPos == nullptr)
   1.222 +    return STG_E_INVALIDPOINTER;
   1.223 +
   1.224 +  if (nMove.LowPart == 0 && nMove.HighPart == 0 &&
   1.225 +      (dwOrigin == STREAM_SEEK_SET || dwOrigin == STREAM_SEEK_CUR)) { 
   1.226 +    nNewPos->LowPart = 0;
   1.227 +    nNewPos->HighPart = 0;
   1.228 +    return S_OK;
   1.229 +  }
   1.230 +
   1.231 +  return E_NOTIMPL;
   1.232 +}
   1.233 +
   1.234 +//-----------------------------------------------------------------------------
   1.235 +STDMETHODIMP nsDataObj::CStream::SetSize(ULARGE_INTEGER nNewSize)
   1.236 +{
   1.237 +  return E_NOTIMPL;
   1.238 +}
   1.239 +
   1.240 +//-----------------------------------------------------------------------------
   1.241 +STDMETHODIMP nsDataObj::CStream::Stat(STATSTG* statstg, DWORD dwFlags)
   1.242 +{
   1.243 +  if (statstg == nullptr)
   1.244 +    return STG_E_INVALIDPOINTER;
   1.245 +
   1.246 +  if (!mChannel || NS_FAILED(WaitForCompletion()))
   1.247 +    return E_FAIL;
   1.248 +
   1.249 +  memset((void*)statstg, 0, sizeof(STATSTG));
   1.250 +
   1.251 +  if (dwFlags != STATFLAG_NONAME) 
   1.252 +  {
   1.253 +    nsCOMPtr<nsIURI> sourceURI;
   1.254 +    if (NS_FAILED(mChannel->GetURI(getter_AddRefs(sourceURI)))) {
   1.255 +      return E_FAIL;
   1.256 +    }
   1.257 +
   1.258 +    nsAutoCString strFileName;
   1.259 +    nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
   1.260 +    sourceURL->GetFileName(strFileName);
   1.261 +
   1.262 +    if (strFileName.IsEmpty())
   1.263 +      return E_FAIL;
   1.264 +
   1.265 +    NS_UnescapeURL(strFileName);
   1.266 +    NS_ConvertUTF8toUTF16 wideFileName(strFileName);
   1.267 +
   1.268 +    uint32_t nMaxNameLength = (wideFileName.Length()*2) + 2;
   1.269 +    void * retBuf = CoTaskMemAlloc(nMaxNameLength); // freed by caller
   1.270 +    if (!retBuf) 
   1.271 +      return STG_E_INSUFFICIENTMEMORY;
   1.272 +
   1.273 +    ZeroMemory(retBuf, nMaxNameLength);
   1.274 +    memcpy(retBuf, wideFileName.get(), wideFileName.Length()*2);
   1.275 +    statstg->pwcsName = (LPOLESTR)retBuf;
   1.276 +  }
   1.277 +
   1.278 +  SYSTEMTIME st;
   1.279 +
   1.280 +  statstg->type = STGTY_STREAM;
   1.281 +
   1.282 +  GetSystemTime(&st);
   1.283 +  SystemTimeToFileTime((const SYSTEMTIME*)&st, (LPFILETIME)&statstg->mtime);
   1.284 +  statstg->ctime = statstg->atime = statstg->mtime;
   1.285 +
   1.286 +  statstg->cbSize.LowPart = (DWORD)mChannelData.Length();
   1.287 +  statstg->grfMode = STGM_READ;
   1.288 +  statstg->grfLocksSupported = LOCK_ONLYONCE;
   1.289 +  statstg->clsid = CLSID_NULL;
   1.290 +
   1.291 +  return S_OK;
   1.292 +}
   1.293 +
   1.294 +//-----------------------------------------------------------------------------
   1.295 +STDMETHODIMP nsDataObj::CStream::UnlockRegion(ULARGE_INTEGER nStart,
   1.296 +                                              ULARGE_INTEGER nBytes,
   1.297 +                                              DWORD dwFlags)
   1.298 +{
   1.299 +  return E_NOTIMPL;
   1.300 +}
   1.301 +
   1.302 +//-----------------------------------------------------------------------------
   1.303 +STDMETHODIMP nsDataObj::CStream::Write(const void* pvBuffer,
   1.304 +                                       ULONG nBytesToRead,
   1.305 +                                       ULONG* nBytesRead)
   1.306 +{
   1.307 +  return E_NOTIMPL;
   1.308 +}
   1.309 +
   1.310 +//-----------------------------------------------------------------------------
   1.311 +HRESULT nsDataObj::CreateStream(IStream **outStream)
   1.312 +{
   1.313 +  NS_ENSURE_TRUE(outStream, E_INVALIDARG);
   1.314 +
   1.315 +  nsresult rv = NS_ERROR_FAILURE;
   1.316 +  nsAutoString wideFileName;
   1.317 +  nsCOMPtr<nsIURI> sourceURI;
   1.318 +  HRESULT res;
   1.319 +
   1.320 +  res = GetDownloadDetails(getter_AddRefs(sourceURI),
   1.321 +                           wideFileName);
   1.322 +  if(FAILED(res))
   1.323 +    return res;
   1.324 +
   1.325 +  nsDataObj::CStream *pStream = new nsDataObj::CStream();
   1.326 +  NS_ENSURE_TRUE(pStream, E_OUTOFMEMORY);
   1.327 +
   1.328 +  pStream->AddRef();
   1.329 +
   1.330 +  rv = pStream->Init(sourceURI);
   1.331 +  if (NS_FAILED(rv))
   1.332 +  {
   1.333 +    pStream->Release();
   1.334 +    return E_FAIL;
   1.335 +  }
   1.336 +  *outStream = pStream;
   1.337 +
   1.338 +  return S_OK;
   1.339 +}
   1.340 +
   1.341 +static GUID CLSID_nsDataObj =
   1.342 +	{ 0x1bba7640, 0xdf52, 0x11cf, { 0x82, 0x7b, 0, 0xa0, 0x24, 0x3a, 0xe5, 0x05 } };
   1.343 +
   1.344 +/* 
   1.345 + * deliberately not using MAX_PATH. This is because on platforms < XP
   1.346 + * a file created with a long filename may be mishandled by the shell
   1.347 + * resulting in it not being able to be deleted or moved. 
   1.348 + * See bug 250392 for more details.
   1.349 + */
   1.350 +#define NS_MAX_FILEDESCRIPTOR 128 + 1
   1.351 +
   1.352 +/*
   1.353 + * Class nsDataObj
   1.354 + */
   1.355 +
   1.356 +//-----------------------------------------------------
   1.357 +// construction 
   1.358 +//-----------------------------------------------------
   1.359 +nsDataObj::nsDataObj(nsIURI * uri)
   1.360 +  : m_cRef(0), mTransferable(nullptr),
   1.361 +    mIsAsyncMode(FALSE), mIsInOperation(FALSE)
   1.362 +{
   1.363 +  mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, 
   1.364 +                                 NS_LITERAL_CSTRING("nsDataObj"),
   1.365 +                                 LazyIdleThread::ManualShutdown);
   1.366 +  m_enumFE = new CEnumFormatEtc();
   1.367 +  m_enumFE->AddRef();
   1.368 +
   1.369 +  if (uri) {
   1.370 +    // A URI was obtained, so pass this through to the DataObject
   1.371 +    // so it can create a SourceURL for CF_HTML flavour
   1.372 +    uri->GetSpec(mSourceURL);
   1.373 +  }
   1.374 +}
   1.375 +//-----------------------------------------------------
   1.376 +// destruction
   1.377 +//-----------------------------------------------------
   1.378 +nsDataObj::~nsDataObj()
   1.379 +{
   1.380 +  NS_IF_RELEASE(mTransferable);
   1.381 +
   1.382 +  mDataFlavors.Clear();
   1.383 +
   1.384 +  m_enumFE->Release();
   1.385 +
   1.386 +  // Free arbitrary system formats
   1.387 +  for (uint32_t idx = 0; idx < mDataEntryList.Length(); idx++) {
   1.388 +      CoTaskMemFree(mDataEntryList[idx]->fe.ptd);
   1.389 +      ReleaseStgMedium(&mDataEntryList[idx]->stgm);
   1.390 +      CoTaskMemFree(mDataEntryList[idx]);
   1.391 +  }
   1.392 +}
   1.393 +
   1.394 +
   1.395 +//-----------------------------------------------------
   1.396 +// IUnknown interface methods - see inknown.h for documentation
   1.397 +//-----------------------------------------------------
   1.398 +STDMETHODIMP nsDataObj::QueryInterface(REFIID riid, void** ppv)
   1.399 +{
   1.400 +	*ppv=nullptr;
   1.401 +
   1.402 +	if ( (IID_IUnknown == riid) || (IID_IDataObject	== riid) ) {
   1.403 +		*ppv = this;
   1.404 +		AddRef();
   1.405 +		return S_OK;
   1.406 +  } else if (IID_IAsyncOperation == riid) {
   1.407 +    *ppv = static_cast<IAsyncOperation*>(this);
   1.408 +    AddRef();
   1.409 +    return S_OK;
   1.410 +  }
   1.411 +
   1.412 +	return E_NOINTERFACE;
   1.413 +}
   1.414 +
   1.415 +//-----------------------------------------------------
   1.416 +STDMETHODIMP_(ULONG) nsDataObj::AddRef()
   1.417 +{
   1.418 +	++m_cRef;
   1.419 +	NS_LOG_ADDREF(this, m_cRef, "nsDataObj", sizeof(*this));
   1.420 +	return m_cRef;
   1.421 +}
   1.422 +
   1.423 +
   1.424 +//-----------------------------------------------------
   1.425 +STDMETHODIMP_(ULONG) nsDataObj::Release()
   1.426 +{
   1.427 +	--m_cRef;
   1.428 +	
   1.429 +	NS_LOG_RELEASE(this, m_cRef, "nsDataObj");
   1.430 +	if (0 != m_cRef)
   1.431 +		return m_cRef;
   1.432 +
   1.433 +  // We have released our last ref on this object and need to delete the
   1.434 +  // temp file. External app acting as drop target may still need to open the
   1.435 +  // temp file. Addref a timer so it can delay deleting file and destroying
   1.436 +  // this object. Delete file anyway and destroy this obj if there's a problem.
   1.437 +  if (mCachedTempFile) {
   1.438 +    nsresult rv;
   1.439 +    mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
   1.440 +    if (NS_SUCCEEDED(rv)) {
   1.441 +      mTimer->InitWithFuncCallback(nsDataObj::RemoveTempFile, this,
   1.442 +                                   500, nsITimer::TYPE_ONE_SHOT);
   1.443 +      return AddRef();
   1.444 +    }
   1.445 +    mCachedTempFile->Remove(false);
   1.446 +    mCachedTempFile = nullptr;
   1.447 +  }
   1.448 +
   1.449 +	delete this;
   1.450 +
   1.451 +	return 0;
   1.452 +}
   1.453 +
   1.454 +//-----------------------------------------------------
   1.455 +BOOL nsDataObj::FormatsMatch(const FORMATETC& source, const FORMATETC& target) const
   1.456 +{
   1.457 +  if ((source.cfFormat == target.cfFormat) &&
   1.458 +      (source.dwAspect & target.dwAspect) &&
   1.459 +      (source.tymed & target.tymed)) {
   1.460 +    return TRUE;
   1.461 +  } else {
   1.462 +    return FALSE;
   1.463 +  }
   1.464 +}
   1.465 +
   1.466 +//-----------------------------------------------------
   1.467 +// IDataObject methods
   1.468 +//-----------------------------------------------------
   1.469 +STDMETHODIMP nsDataObj::GetData(LPFORMATETC aFormat, LPSTGMEDIUM pSTM)
   1.470 +{
   1.471 +  if (!mTransferable)
   1.472 +    return DV_E_FORMATETC;
   1.473 +
   1.474 +  uint32_t dfInx = 0;
   1.475 +
   1.476 +  static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA ); 
   1.477 +  static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW ); 
   1.478 +  static CLIPFORMAT uniformResourceLocatorA = ::RegisterClipboardFormat( CFSTR_INETURLA );
   1.479 +  static CLIPFORMAT uniformResourceLocatorW = ::RegisterClipboardFormat( CFSTR_INETURLW );
   1.480 +  static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS ); 
   1.481 +  static CLIPFORMAT PreferredDropEffect = ::RegisterClipboardFormat( CFSTR_PREFERREDDROPEFFECT );
   1.482 +
   1.483 +  // Arbitrary system formats are used for image feedback during drag
   1.484 +  // and drop. We are responsible for storing these internally during
   1.485 +  // drag operations.
   1.486 +  LPDATAENTRY pde;
   1.487 +  if (LookupArbitraryFormat(aFormat, &pde, FALSE)) {
   1.488 +    return CopyMediumData(pSTM, &pde->stgm, aFormat, FALSE)
   1.489 +           ? S_OK : E_UNEXPECTED;
   1.490 +  }
   1.491 +
   1.492 +  // Firefox internal formats
   1.493 +  ULONG count;
   1.494 +  FORMATETC fe;
   1.495 +  m_enumFE->Reset();
   1.496 +  while (NOERROR == m_enumFE->Next(1, &fe, &count)
   1.497 +         && dfInx < mDataFlavors.Length()) {
   1.498 +    nsCString& df = mDataFlavors.ElementAt(dfInx);
   1.499 +    if (FormatsMatch(fe, *aFormat)) {
   1.500 +      pSTM->pUnkForRelease = nullptr;     // caller is responsible for deleting this data
   1.501 +      CLIPFORMAT format = aFormat->cfFormat;
   1.502 +      switch(format) {
   1.503 +
   1.504 +      // Someone is asking for plain or unicode text
   1.505 +      case CF_TEXT:
   1.506 +      case CF_UNICODETEXT:
   1.507 +      return GetText(df, *aFormat, *pSTM);
   1.508 +
   1.509 +      // Some 3rd party apps that receive drag and drop files from the browser
   1.510 +      // window require support for this.
   1.511 +      case CF_HDROP:
   1.512 +        return GetFile(*aFormat, *pSTM);
   1.513 +
   1.514 +      // Someone is asking for an image
   1.515 +      case CF_DIBV5:
   1.516 +      case CF_DIB:
   1.517 +        return GetDib(df, *aFormat, *pSTM);
   1.518 +
   1.519 +      default:
   1.520 +        if ( format == fileDescriptorFlavorA )
   1.521 +          return GetFileDescriptor ( *aFormat, *pSTM, false );
   1.522 +        if ( format == fileDescriptorFlavorW )
   1.523 +          return GetFileDescriptor ( *aFormat, *pSTM, true);
   1.524 +        if ( format == uniformResourceLocatorA )
   1.525 +          return GetUniformResourceLocator( *aFormat, *pSTM, false);
   1.526 +        if ( format == uniformResourceLocatorW )
   1.527 +          return GetUniformResourceLocator( *aFormat, *pSTM, true);
   1.528 +        if ( format == fileFlavor )
   1.529 +          return GetFileContents ( *aFormat, *pSTM );
   1.530 +        if ( format == PreferredDropEffect )
   1.531 +          return GetPreferredDropEffect( *aFormat, *pSTM );
   1.532 +        //PR_LOG(gWindowsLog, PR_LOG_ALWAYS, 
   1.533 +        //       ("***** nsDataObj::GetData - Unknown format %u\n", format));
   1.534 +        return GetText(df, *aFormat, *pSTM);
   1.535 +      } //switch
   1.536 +    } // if
   1.537 +    dfInx++;
   1.538 +  } // while
   1.539 +
   1.540 +  return DATA_E_FORMATETC;
   1.541 +}
   1.542 +
   1.543 +//-----------------------------------------------------
   1.544 +STDMETHODIMP nsDataObj::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
   1.545 +{
   1.546 +  return E_FAIL;
   1.547 +}
   1.548 +
   1.549 +
   1.550 +//-----------------------------------------------------
   1.551 +// Other objects querying to see if we support a 
   1.552 +// particular format
   1.553 +//-----------------------------------------------------
   1.554 +STDMETHODIMP nsDataObj::QueryGetData(LPFORMATETC pFE)
   1.555 +{
   1.556 +  // Arbitrary system formats are used for image feedback during drag
   1.557 +  // and drop. We are responsible for storing these internally during
   1.558 +  // drag operations.
   1.559 +  LPDATAENTRY pde;
   1.560 +  if (LookupArbitraryFormat(pFE, &pde, FALSE))
   1.561 +    return S_OK;
   1.562 +
   1.563 +  // Firefox internal formats
   1.564 +  ULONG count;
   1.565 +  FORMATETC fe;
   1.566 +  m_enumFE->Reset();
   1.567 +  while (NOERROR == m_enumFE->Next(1, &fe, &count)) {
   1.568 +    if (fe.cfFormat == pFE->cfFormat) {
   1.569 +      return S_OK;
   1.570 +    }
   1.571 +  }
   1.572 +  return E_FAIL;
   1.573 +}
   1.574 +
   1.575 +//-----------------------------------------------------
   1.576 +STDMETHODIMP nsDataObj::GetCanonicalFormatEtc
   1.577 +	 (LPFORMATETC pFEIn, LPFORMATETC pFEOut)
   1.578 +{
   1.579 +  return E_FAIL;
   1.580 +}
   1.581 +
   1.582 +//-----------------------------------------------------
   1.583 +STDMETHODIMP nsDataObj::SetData(LPFORMATETC aFormat, LPSTGMEDIUM aMedium, BOOL shouldRel)
   1.584 +{
   1.585 +  // Arbitrary system formats are used for image feedback during drag
   1.586 +  // and drop. We are responsible for storing these internally during
   1.587 +  // drag operations.
   1.588 +  LPDATAENTRY pde;
   1.589 +  if (LookupArbitraryFormat(aFormat, &pde, TRUE)) {
   1.590 +    // Release the old data the lookup handed us for this format. This
   1.591 +    // may have been set in CopyMediumData when we originally stored the
   1.592 +    // data.
   1.593 +    if (pde->stgm.tymed) {
   1.594 +      ReleaseStgMedium(&pde->stgm);
   1.595 +      memset(&pde->stgm, 0, sizeof(STGMEDIUM));
   1.596 +    }
   1.597 +
   1.598 +    bool result = true;
   1.599 +    if (shouldRel) {
   1.600 +      // If shouldRel is TRUE, the data object called owns the storage medium
   1.601 +      // after the call returns. Store the incoming data in our data array for
   1.602 +      // release when we are destroyed. This is the common case with arbitrary
   1.603 +      // data from explorer.
   1.604 +      pde->stgm = *aMedium;
   1.605 +    } else {
   1.606 +      // Copy the incoming data into our data array. (AFAICT, this never gets
   1.607 +      // called with arbitrary formats for drag images.)
   1.608 +      result = CopyMediumData(&pde->stgm, aMedium, aFormat, TRUE);
   1.609 +    }
   1.610 +    pde->fe.tymed = pde->stgm.tymed;
   1.611 +
   1.612 +    return result ? S_OK : DV_E_TYMED;
   1.613 +  }
   1.614 +
   1.615 +  if (shouldRel)
   1.616 +    ReleaseStgMedium(aMedium);
   1.617 +
   1.618 +  return S_OK;
   1.619 +}
   1.620 +
   1.621 +bool
   1.622 +nsDataObj::LookupArbitraryFormat(FORMATETC *aFormat, LPDATAENTRY *aDataEntry, BOOL aAddorUpdate)
   1.623 +{
   1.624 +  *aDataEntry = nullptr;
   1.625 +
   1.626 +  if (aFormat->ptd != nullptr)
   1.627 +    return false;
   1.628 +
   1.629 +  // See if it's already in our list. If so return the data entry.
   1.630 +  for (uint32_t idx = 0; idx < mDataEntryList.Length(); idx++) {
   1.631 +    if (mDataEntryList[idx]->fe.cfFormat == aFormat->cfFormat &&
   1.632 +        mDataEntryList[idx]->fe.dwAspect == aFormat->dwAspect &&
   1.633 +        mDataEntryList[idx]->fe.lindex == aFormat->lindex) {
   1.634 +      if (aAddorUpdate || (mDataEntryList[idx]->fe.tymed & aFormat->tymed)) {
   1.635 +        // If the caller requests we update, or if the 
   1.636 +        // medium type matches, return the entry. 
   1.637 +        *aDataEntry = mDataEntryList[idx];
   1.638 +        return true;
   1.639 +      } else {
   1.640 +        // Medium does not match, not found.
   1.641 +        return false;
   1.642 +      }
   1.643 +    }
   1.644 +  }
   1.645 +
   1.646 +  if (!aAddorUpdate)
   1.647 +    return false;
   1.648 +
   1.649 +  // Add another entry to mDataEntryList
   1.650 +  LPDATAENTRY dataEntry = (LPDATAENTRY)CoTaskMemAlloc(sizeof(DATAENTRY));
   1.651 +  if (!dataEntry)
   1.652 +    return false;
   1.653 +  
   1.654 +  dataEntry->fe = *aFormat;
   1.655 +  *aDataEntry = dataEntry;
   1.656 +  memset(&dataEntry->stgm, 0, sizeof(STGMEDIUM));
   1.657 +
   1.658 +  // Add this to our IEnumFORMATETC impl. so we can return it when
   1.659 +  // it's requested.
   1.660 +  m_enumFE->AddFormatEtc(aFormat);
   1.661 +
   1.662 +  // Store a copy internally in the arbitrary formats array.
   1.663 +  mDataEntryList.AppendElement(dataEntry);
   1.664 +
   1.665 +  return true;
   1.666 +}
   1.667 +
   1.668 +bool
   1.669 +nsDataObj::CopyMediumData(STGMEDIUM *aMediumDst, STGMEDIUM *aMediumSrc, LPFORMATETC aFormat, BOOL aSetData)
   1.670 +{
   1.671 +  STGMEDIUM stgmOut = *aMediumSrc;
   1.672 +  
   1.673 +  switch (stgmOut.tymed) {
   1.674 +    case TYMED_ISTREAM:
   1.675 +      stgmOut.pstm->AddRef();
   1.676 +    break;
   1.677 +    case TYMED_ISTORAGE:
   1.678 +      stgmOut.pstg->AddRef();
   1.679 +    break;
   1.680 +    case TYMED_HGLOBAL:
   1.681 +      if (!aMediumSrc->pUnkForRelease) {
   1.682 +        if (aSetData) {
   1.683 +          if (aMediumSrc->tymed != TYMED_HGLOBAL)
   1.684 +            return false;
   1.685 +          stgmOut.hGlobal = OleDuplicateData(aMediumSrc->hGlobal, aFormat->cfFormat, 0);
   1.686 +          if (!stgmOut.hGlobal)
   1.687 +            return false;
   1.688 +        } else {
   1.689 +          // We are returning this data from LookupArbitraryFormat, indicate to the
   1.690 +          // shell we hold it and will free it.
   1.691 +          stgmOut.pUnkForRelease = static_cast<IDataObject*>(this);
   1.692 +        }
   1.693 +      }
   1.694 +    break;
   1.695 +    default:
   1.696 +      return false;
   1.697 +  }
   1.698 +
   1.699 +  if (stgmOut.pUnkForRelease)
   1.700 +    stgmOut.pUnkForRelease->AddRef();
   1.701 +
   1.702 +  *aMediumDst = stgmOut;
   1.703 +
   1.704 +  return true;
   1.705 +}
   1.706 +
   1.707 +//-----------------------------------------------------
   1.708 +STDMETHODIMP nsDataObj::EnumFormatEtc(DWORD dwDir, LPENUMFORMATETC *ppEnum)
   1.709 +{
   1.710 +  switch (dwDir) {
   1.711 +    case DATADIR_GET:
   1.712 +      m_enumFE->Clone(ppEnum);
   1.713 +      break;
   1.714 +    case DATADIR_SET:
   1.715 +      // fall through
   1.716 +    default:
   1.717 +      *ppEnum = nullptr;
   1.718 +  } // switch
   1.719 +
   1.720 +  if (nullptr == *ppEnum)
   1.721 +    return E_FAIL;
   1.722 +
   1.723 +  (*ppEnum)->Reset();
   1.724 +  // Clone already AddRefed the result so don't addref it again.
   1.725 +  return NOERROR;
   1.726 +}
   1.727 +
   1.728 +//-----------------------------------------------------
   1.729 +STDMETHODIMP nsDataObj::DAdvise(LPFORMATETC pFE, DWORD dwFlags,
   1.730 +										            LPADVISESINK pIAdviseSink, DWORD* pdwConn)
   1.731 +{
   1.732 +  return E_FAIL;
   1.733 +}
   1.734 +
   1.735 +
   1.736 +//-----------------------------------------------------
   1.737 +STDMETHODIMP nsDataObj::DUnadvise(DWORD dwConn)
   1.738 +{
   1.739 +  return E_FAIL;
   1.740 +}
   1.741 +
   1.742 +//-----------------------------------------------------
   1.743 +STDMETHODIMP nsDataObj::EnumDAdvise(LPENUMSTATDATA *ppEnum)
   1.744 +{
   1.745 +  return E_FAIL;
   1.746 +}
   1.747 +
   1.748 +// IAsyncOperation methods
   1.749 +STDMETHODIMP nsDataObj::EndOperation(HRESULT hResult,
   1.750 +                                     IBindCtx *pbcReserved,
   1.751 +                                     DWORD dwEffects)
   1.752 +{
   1.753 +  mIsInOperation = FALSE;
   1.754 +  return S_OK;
   1.755 +}
   1.756 +
   1.757 +STDMETHODIMP nsDataObj::GetAsyncMode(BOOL *pfIsOpAsync)
   1.758 +{
   1.759 +  *pfIsOpAsync = mIsAsyncMode;
   1.760 +
   1.761 +  return S_OK;
   1.762 +}
   1.763 +
   1.764 +STDMETHODIMP nsDataObj::InOperation(BOOL *pfInAsyncOp)
   1.765 +{
   1.766 +  *pfInAsyncOp = mIsInOperation;
   1.767 +
   1.768 +  return S_OK;
   1.769 +}
   1.770 +
   1.771 +STDMETHODIMP nsDataObj::SetAsyncMode(BOOL fDoOpAsync)
   1.772 +{
   1.773 +  mIsAsyncMode = fDoOpAsync;
   1.774 +  return S_OK;
   1.775 +}
   1.776 +
   1.777 +STDMETHODIMP nsDataObj::StartOperation(IBindCtx *pbcReserved)
   1.778 +{
   1.779 +  mIsInOperation = TRUE;
   1.780 +  return S_OK;
   1.781 +}
   1.782 +
   1.783 +//-----------------------------------------------------
   1.784 +// GetData and SetData helper functions
   1.785 +//-----------------------------------------------------
   1.786 +HRESULT nsDataObj::AddSetFormat(FORMATETC& aFE)
   1.787 +{
   1.788 +  return S_OK;
   1.789 +}
   1.790 +
   1.791 +//-----------------------------------------------------
   1.792 +HRESULT nsDataObj::AddGetFormat(FORMATETC& aFE)
   1.793 +{
   1.794 +  return S_OK;
   1.795 +}
   1.796 +
   1.797 +//
   1.798 +// GetDIB
   1.799 +//
   1.800 +// Someone is asking for a bitmap. The data in the transferable will be a straight
   1.801 +// imgIContainer, so just QI it.
   1.802 +//
   1.803 +HRESULT 
   1.804 +nsDataObj::GetDib(const nsACString& inFlavor,
   1.805 +                  FORMATETC &aFormat,
   1.806 +                  STGMEDIUM & aSTG)
   1.807 +{
   1.808 +  ULONG result = E_FAIL;
   1.809 +  uint32_t len = 0;
   1.810 +  nsCOMPtr<nsISupports> genericDataWrapper;
   1.811 +  mTransferable->GetTransferData(PromiseFlatCString(inFlavor).get(), getter_AddRefs(genericDataWrapper), &len);
   1.812 +  nsCOMPtr<imgIContainer> image ( do_QueryInterface(genericDataWrapper) );
   1.813 +  if ( !image ) {
   1.814 +    // Check if the image was put in an nsISupportsInterfacePointer wrapper.
   1.815 +    // This might not be necessary any more, but could be useful for backwards
   1.816 +    // compatibility.
   1.817 +    nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
   1.818 +    if ( ptr ) {
   1.819 +      nsCOMPtr<nsISupports> supports;
   1.820 +      ptr->GetData(getter_AddRefs(supports));
   1.821 +      image = do_QueryInterface(supports);
   1.822 +    }
   1.823 +  }
   1.824 +  
   1.825 +  if ( image ) {
   1.826 +    // use the |nsImageToClipboard| helper class to build up a bitmap. We now own
   1.827 +    // the bits, and pass them back to the OS in |aSTG|.
   1.828 +    nsImageToClipboard converter(image, aFormat.cfFormat == CF_DIBV5);
   1.829 +    HANDLE bits = nullptr;
   1.830 +    nsresult rv = converter.GetPicture ( &bits );
   1.831 +    if ( NS_SUCCEEDED(rv) && bits ) {
   1.832 +      aSTG.hGlobal = bits;
   1.833 +      aSTG.tymed = TYMED_HGLOBAL;
   1.834 +      result = S_OK;
   1.835 +    }
   1.836 +  } // if we have an image
   1.837 +  else  
   1.838 +    NS_WARNING ( "Definitely not an image on clipboard" );
   1.839 +	return result;
   1.840 +}
   1.841 +
   1.842 +
   1.843 +
   1.844 +//
   1.845 +// GetFileDescriptor
   1.846 +//
   1.847 +
   1.848 +HRESULT 
   1.849 +nsDataObj :: GetFileDescriptor ( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode )
   1.850 +{
   1.851 +  HRESULT res = S_OK;
   1.852 +  
   1.853 +  // How we handle this depends on if we're dealing with an internet
   1.854 +  // shortcut, since those are done under the covers.
   1.855 +  if (IsFlavourPresent(kFilePromiseMime) ||
   1.856 +      IsFlavourPresent(kFileMime))
   1.857 +  {
   1.858 +    if (aIsUnicode)
   1.859 +      return GetFileDescriptor_IStreamW(aFE, aSTG);
   1.860 +    else
   1.861 +      return GetFileDescriptor_IStreamA(aFE, aSTG);
   1.862 +  }
   1.863 +  else if (IsFlavourPresent(kURLMime))
   1.864 +  {
   1.865 +    if ( aIsUnicode )
   1.866 +      res = GetFileDescriptorInternetShortcutW ( aFE, aSTG );
   1.867 +    else
   1.868 +      res = GetFileDescriptorInternetShortcutA ( aFE, aSTG );
   1.869 +  }
   1.870 +  else
   1.871 +    NS_WARNING ( "Not yet implemented\n" );
   1.872 +  
   1.873 +	return res;
   1.874 +} // GetFileDescriptor
   1.875 +
   1.876 +
   1.877 +//
   1.878 +HRESULT 
   1.879 +nsDataObj :: GetFileContents ( FORMATETC& aFE, STGMEDIUM& aSTG )
   1.880 +{
   1.881 +  HRESULT res = S_OK;
   1.882 +  
   1.883 +  // How we handle this depends on if we're dealing with an internet
   1.884 +  // shortcut, since those are done under the covers.
   1.885 +  if (IsFlavourPresent(kFilePromiseMime) ||
   1.886 +      IsFlavourPresent(kFileMime))
   1.887 +    return GetFileContents_IStream(aFE, aSTG);
   1.888 +  else if (IsFlavourPresent(kURLMime))
   1.889 +    return GetFileContentsInternetShortcut ( aFE, aSTG );
   1.890 +  else
   1.891 +    NS_WARNING ( "Not yet implemented\n" );
   1.892 +
   1.893 +	return res;
   1.894 +	
   1.895 +} // GetFileContents
   1.896 +
   1.897 +// 
   1.898 +// Given a unicode string, we ensure that it contains only characters which are valid within
   1.899 +// the file system. Remove all forbidden characters from the name, and completely disallow 
   1.900 +// any title that starts with a forbidden name and extension (e.g. "nul" is invalid, but 
   1.901 +// "nul." and "nul.txt" are also invalid and will cause problems).
   1.902 +//
   1.903 +// It would seem that this is more functionality suited to being in nsIFile.
   1.904 +//
   1.905 +static void
   1.906 +MangleTextToValidFilename(nsString & aText)
   1.907 +{
   1.908 +  static const char* forbiddenNames[] = {
   1.909 +    "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", 
   1.910 +    "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
   1.911 +    "CON", "PRN", "AUX", "NUL", "CLOCK$"
   1.912 +  };
   1.913 +
   1.914 +  aText.StripChars(FILE_PATH_SEPARATOR  FILE_ILLEGAL_CHARACTERS);
   1.915 +  aText.CompressWhitespace(true, true);
   1.916 +  uint32_t nameLen;
   1.917 +  for (size_t n = 0; n < ArrayLength(forbiddenNames); ++n) {
   1.918 +    nameLen = (uint32_t) strlen(forbiddenNames[n]);
   1.919 +    if (aText.EqualsIgnoreCase(forbiddenNames[n], nameLen)) {
   1.920 +      // invalid name is either the entire string, or a prefix with a period
   1.921 +      if (aText.Length() == nameLen || aText.CharAt(nameLen) == char16_t('.')) {
   1.922 +        aText.Truncate();
   1.923 +        break;
   1.924 +      }
   1.925 +    }
   1.926 +  }
   1.927 +}
   1.928 +
   1.929 +// 
   1.930 +// Given a unicode string, convert it down to a valid local charset filename
   1.931 +// with the supplied extension. This ensures that we do not cut MBCS characters
   1.932 +// in the middle.
   1.933 +//
   1.934 +// It would seem that this is more functionality suited to being in nsIFile.
   1.935 +//
   1.936 +static bool
   1.937 +CreateFilenameFromTextA(nsString & aText, const char * aExtension, 
   1.938 +                         char * aFilename, uint32_t aFilenameLen)
   1.939 +{
   1.940 +  // ensure that the supplied name doesn't have invalid characters. If 
   1.941 +  // a valid mangled filename couldn't be created then it will leave the
   1.942 +  // text empty.
   1.943 +  MangleTextToValidFilename(aText);
   1.944 +  if (aText.IsEmpty())
   1.945 +    return false;
   1.946 +
   1.947 +  // repeatably call WideCharToMultiByte as long as the title doesn't fit in the buffer 
   1.948 +  // available to us. Continually reduce the length of the source title until the MBCS
   1.949 +  // version will fit in the buffer with room for the supplied extension. Doing it this
   1.950 +  // way ensures that even in MBCS environments there will be a valid MBCS filename of
   1.951 +  // the correct length.
   1.952 +  int maxUsableFilenameLen = aFilenameLen - strlen(aExtension) - 1; // space for ext + null byte
   1.953 +  int currLen, textLen = (int) std::min(aText.Length(), aFilenameLen);
   1.954 +  char defaultChar = '_';
   1.955 +  do {
   1.956 +    currLen = WideCharToMultiByte(CP_ACP, 
   1.957 +      WC_COMPOSITECHECK|WC_DEFAULTCHAR,
   1.958 +      aText.get(), textLen--, aFilename, maxUsableFilenameLen, &defaultChar, nullptr);
   1.959 +  }
   1.960 +  while (currLen == 0 && textLen > 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
   1.961 +  if (currLen > 0 && textLen > 0) {
   1.962 +    strcpy(&aFilename[currLen], aExtension);
   1.963 +    return true;
   1.964 +  }
   1.965 +  else {
   1.966 +    // empty names aren't permitted
   1.967 +    return false;
   1.968 +  }
   1.969 +}
   1.970 +
   1.971 +static bool
   1.972 +CreateFilenameFromTextW(nsString & aText, const wchar_t * aExtension, 
   1.973 +                         wchar_t * aFilename, uint32_t aFilenameLen)
   1.974 +{
   1.975 +  // ensure that the supplied name doesn't have invalid characters. If 
   1.976 +  // a valid mangled filename couldn't be created then it will leave the
   1.977 +  // text empty.
   1.978 +  MangleTextToValidFilename(aText);
   1.979 +  if (aText.IsEmpty())
   1.980 +    return false;
   1.981 +
   1.982 +  const int extensionLen = wcslen(aExtension);
   1.983 +  if (aText.Length() + extensionLen + 1 > aFilenameLen)
   1.984 +    aText.Truncate(aFilenameLen - extensionLen - 1);
   1.985 +  wcscpy(&aFilename[0], aText.get());
   1.986 +  wcscpy(&aFilename[aText.Length()], aExtension);
   1.987 +  return true;
   1.988 +}
   1.989 +
   1.990 +#define PAGEINFO_PROPERTIES "chrome://navigator/locale/pageInfo.properties"
   1.991 +
   1.992 +static bool
   1.993 +GetLocalizedString(const char16_t * aName, nsXPIDLString & aString)
   1.994 +{
   1.995 +  nsCOMPtr<nsIStringBundleService> stringService =
   1.996 +    mozilla::services::GetStringBundleService();
   1.997 +  if (!stringService)
   1.998 +    return false;
   1.999 +
  1.1000 +  nsCOMPtr<nsIStringBundle> stringBundle;
  1.1001 +  nsresult rv = stringService->CreateBundle(PAGEINFO_PROPERTIES,
  1.1002 +                                            getter_AddRefs(stringBundle));
  1.1003 +  if (NS_FAILED(rv))
  1.1004 +    return false;
  1.1005 +
  1.1006 +  rv = stringBundle->GetStringFromName(aName, getter_Copies(aString));
  1.1007 +  return NS_SUCCEEDED(rv);
  1.1008 +}
  1.1009 +
  1.1010 +//
  1.1011 +// GetFileDescriptorInternetShortcut
  1.1012 +//
  1.1013 +// Create the special format for an internet shortcut and build up the data
  1.1014 +// structures the shell is expecting.
  1.1015 +//
  1.1016 +HRESULT
  1.1017 +nsDataObj :: GetFileDescriptorInternetShortcutA ( FORMATETC& aFE, STGMEDIUM& aSTG )
  1.1018 +{
  1.1019 +  // get the title of the shortcut
  1.1020 +  nsAutoString title;
  1.1021 +  if ( NS_FAILED(ExtractShortcutTitle(title)) )
  1.1022 +    return E_OUTOFMEMORY;
  1.1023 +
  1.1024 +  HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORA));
  1.1025 +  if (!fileGroupDescHandle)
  1.1026 +    return E_OUTOFMEMORY;
  1.1027 +
  1.1028 +  LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(::GlobalLock(fileGroupDescHandle));
  1.1029 +  if (!fileGroupDescA) {
  1.1030 +    ::GlobalFree(fileGroupDescHandle);
  1.1031 +    return E_OUTOFMEMORY;
  1.1032 +  }
  1.1033 +
  1.1034 +  // get a valid filename in the following order: 1) from the page title, 
  1.1035 +  // 2) localized string for an untitled page, 3) just use "Untitled.URL"
  1.1036 +  if (!CreateFilenameFromTextA(title, ".URL", 
  1.1037 +                               fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
  1.1038 +    nsXPIDLString untitled;
  1.1039 +    if (!GetLocalizedString(MOZ_UTF16("noPageTitle"), untitled) ||
  1.1040 +        !CreateFilenameFromTextA(untitled, ".URL", 
  1.1041 +                                 fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
  1.1042 +      strcpy(fileGroupDescA->fgd[0].cFileName, "Untitled.URL");
  1.1043 +    }
  1.1044 +  }
  1.1045 +
  1.1046 +  // one file in the file block
  1.1047 +  fileGroupDescA->cItems = 1;
  1.1048 +  fileGroupDescA->fgd[0].dwFlags = FD_LINKUI;
  1.1049 +
  1.1050 +  ::GlobalUnlock( fileGroupDescHandle );
  1.1051 +  aSTG.hGlobal = fileGroupDescHandle;
  1.1052 +  aSTG.tymed = TYMED_HGLOBAL;
  1.1053 +
  1.1054 +  return S_OK;
  1.1055 +} // GetFileDescriptorInternetShortcutA
  1.1056 +
  1.1057 +HRESULT
  1.1058 +nsDataObj :: GetFileDescriptorInternetShortcutW ( FORMATETC& aFE, STGMEDIUM& aSTG )
  1.1059 +{
  1.1060 +  // get the title of the shortcut
  1.1061 +  nsAutoString title;
  1.1062 +  if ( NS_FAILED(ExtractShortcutTitle(title)) )
  1.1063 +    return E_OUTOFMEMORY;
  1.1064 +
  1.1065 +  HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
  1.1066 +  if (!fileGroupDescHandle)
  1.1067 +    return E_OUTOFMEMORY;
  1.1068 +
  1.1069 +  LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(::GlobalLock(fileGroupDescHandle));
  1.1070 +  if (!fileGroupDescW) {
  1.1071 +    ::GlobalFree(fileGroupDescHandle);
  1.1072 +    return E_OUTOFMEMORY;
  1.1073 +  }
  1.1074 +
  1.1075 +  // get a valid filename in the following order: 1) from the page title, 
  1.1076 +  // 2) localized string for an untitled page, 3) just use "Untitled.URL"
  1.1077 +  if (!CreateFilenameFromTextW(title, L".URL",
  1.1078 +                               fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
  1.1079 +    nsXPIDLString untitled;
  1.1080 +    if (!GetLocalizedString(MOZ_UTF16("noPageTitle"), untitled) ||
  1.1081 +        !CreateFilenameFromTextW(untitled, L".URL",
  1.1082 +                                 fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
  1.1083 +      wcscpy(fileGroupDescW->fgd[0].cFileName, L"Untitled.URL");
  1.1084 +    }
  1.1085 +  }
  1.1086 +
  1.1087 +  // one file in the file block
  1.1088 +  fileGroupDescW->cItems = 1;
  1.1089 +  fileGroupDescW->fgd[0].dwFlags = FD_LINKUI;
  1.1090 +
  1.1091 +  ::GlobalUnlock( fileGroupDescHandle );
  1.1092 +  aSTG.hGlobal = fileGroupDescHandle;
  1.1093 +  aSTG.tymed = TYMED_HGLOBAL;
  1.1094 +
  1.1095 +  return S_OK;
  1.1096 +} // GetFileDescriptorInternetShortcutW
  1.1097 +
  1.1098 +
  1.1099 +//
  1.1100 +// GetFileContentsInternetShortcut
  1.1101 +//
  1.1102 +// Create the special format for an internet shortcut and build up the data
  1.1103 +// structures the shell is expecting.
  1.1104 +//
  1.1105 +HRESULT
  1.1106 +nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG )
  1.1107 +{
  1.1108 +  static const char * kShellIconPref = "browser.shell.shortcutFavicons";
  1.1109 +  nsAutoString url;
  1.1110 +  if ( NS_FAILED(ExtractShortcutURL(url)) )
  1.1111 +    return E_OUTOFMEMORY;
  1.1112 +
  1.1113 +  // will need to change if we ever support iDNS
  1.1114 +  nsAutoCString asciiUrl;
  1.1115 +  LossyCopyUTF16toASCII(url, asciiUrl);
  1.1116 +
  1.1117 +  nsCOMPtr<nsIFile> icoFile;
  1.1118 +  nsCOMPtr<nsIURI> aUri;
  1.1119 +  NS_NewURI(getter_AddRefs(aUri), url);
  1.1120 +
  1.1121 +  const char *shortcutFormatStr;
  1.1122 +  int totalLen;
  1.1123 +  nsCString path;
  1.1124 +  if (!Preferences::GetBool(kShellIconPref, true) ||
  1.1125 +      !IsVistaOrLater()) {
  1.1126 +    shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n";
  1.1127 +    const int formatLen = strlen(shortcutFormatStr) - 2;  // don't include %s
  1.1128 +    totalLen = formatLen + asciiUrl.Length();  // don't include null character
  1.1129 +  } else {
  1.1130 +    nsCOMPtr<nsIFile> icoFile;
  1.1131 +    nsCOMPtr<nsIURI> aUri;
  1.1132 +    NS_NewURI(getter_AddRefs(aUri), url);
  1.1133 +
  1.1134 +    nsAutoString aUriHash;
  1.1135 +
  1.1136 +    mozilla::widget::FaviconHelper::ObtainCachedIconFile(aUri, aUriHash, mIOThread, true);
  1.1137 +
  1.1138 +    nsresult rv = mozilla::widget::FaviconHelper::GetOutputIconPath(aUri, icoFile, true);
  1.1139 +    NS_ENSURE_SUCCESS(rv, E_FAIL);
  1.1140 +    rv = icoFile->GetNativePath(path);
  1.1141 +    NS_ENSURE_SUCCESS(rv, E_FAIL);
  1.1142 +
  1.1143 +    shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n"
  1.1144 +                        "IDList=\r\nHotKey=0\r\nIconFile=%s\r\n"
  1.1145 +                        "IconIndex=0\r\n";
  1.1146 +    const int formatLen = strlen(shortcutFormatStr) - 2 * 2; // no %s twice
  1.1147 +    totalLen = formatLen + asciiUrl.Length() +
  1.1148 +               path.Length(); // we don't want a null character on the end
  1.1149 +  }
  1.1150 +
  1.1151 +  // create a global memory area and build up the file contents w/in it
  1.1152 +  HGLOBAL hGlobalMemory = ::GlobalAlloc(GMEM_SHARE, totalLen);
  1.1153 +  if ( !hGlobalMemory )
  1.1154 +    return E_OUTOFMEMORY;
  1.1155 +
  1.1156 +  char* contents = reinterpret_cast<char*>(::GlobalLock(hGlobalMemory));
  1.1157 +  if ( !contents ) {
  1.1158 +    ::GlobalFree( hGlobalMemory );
  1.1159 +    return E_OUTOFMEMORY;
  1.1160 +  }
  1.1161 +    
  1.1162 +  //NOTE: we intentionally use the Microsoft version of snprintf here because it does NOT null 
  1.1163 +  // terminate strings which reach the maximum size of the buffer. Since we know that the 
  1.1164 +  // formatted length here is totalLen, this call to _snprintf will format the string into 
  1.1165 +  // the buffer without appending the null character.
  1.1166 +
  1.1167 +  if (!Preferences::GetBool(kShellIconPref, true)) {
  1.1168 +    _snprintf(contents, totalLen, shortcutFormatStr, asciiUrl.get());
  1.1169 +  } else {
  1.1170 +    _snprintf(contents, totalLen, shortcutFormatStr, asciiUrl.get(), path.get());
  1.1171 +  }
  1.1172 +
  1.1173 +  ::GlobalUnlock(hGlobalMemory);
  1.1174 +  aSTG.hGlobal = hGlobalMemory;
  1.1175 +  aSTG.tymed = TYMED_HGLOBAL;
  1.1176 +
  1.1177 +  return S_OK;
  1.1178 +} // GetFileContentsInternetShortcut
  1.1179 +
  1.1180 +// check if specified flavour is present in the transferable
  1.1181 +bool nsDataObj :: IsFlavourPresent(const char *inFlavour)
  1.1182 +{
  1.1183 +  bool retval = false;
  1.1184 +  NS_ENSURE_TRUE(mTransferable, false);
  1.1185 +  
  1.1186 +  // get the list of flavors available in the transferable
  1.1187 +  nsCOMPtr<nsISupportsArray> flavorList;
  1.1188 +  mTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
  1.1189 +  NS_ENSURE_TRUE(flavorList, false);
  1.1190 +
  1.1191 +  // try to find requested flavour
  1.1192 +  uint32_t cnt;
  1.1193 +  flavorList->Count(&cnt);
  1.1194 +  for (uint32_t i = 0; i < cnt; ++i) {
  1.1195 +    nsCOMPtr<nsISupports> genericFlavor;
  1.1196 +    flavorList->GetElementAt (i, getter_AddRefs(genericFlavor));
  1.1197 +    nsCOMPtr<nsISupportsCString> currentFlavor (do_QueryInterface(genericFlavor));
  1.1198 +    if (currentFlavor) {
  1.1199 +      nsAutoCString flavorStr;
  1.1200 +      currentFlavor->GetData(flavorStr);
  1.1201 +      if (flavorStr.Equals(inFlavour)) {
  1.1202 +        retval = true;         // found it!
  1.1203 +        break;
  1.1204 +      }
  1.1205 +    }
  1.1206 +  } // for each flavor
  1.1207 +
  1.1208 +  return retval;
  1.1209 +}
  1.1210 +
  1.1211 +HRESULT nsDataObj::GetPreferredDropEffect ( FORMATETC& aFE, STGMEDIUM& aSTG )
  1.1212 +{
  1.1213 +  HRESULT res = S_OK;
  1.1214 +  aSTG.tymed = TYMED_HGLOBAL;
  1.1215 +  aSTG.pUnkForRelease = nullptr;    
  1.1216 +  HGLOBAL hGlobalMemory = nullptr;
  1.1217 +  hGlobalMemory = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
  1.1218 +  if (hGlobalMemory) {
  1.1219 +    DWORD* pdw = (DWORD*) GlobalLock(hGlobalMemory);
  1.1220 +    // The PreferredDropEffect clipboard format is only registered if a drag/drop
  1.1221 +    // of an image happens from Mozilla to the desktop.  We want its value
  1.1222 +    // to be DROPEFFECT_MOVE in that case so that the file is moved from the
  1.1223 +    // temporary location, not copied.
  1.1224 +    // This value should, ideally, be set on the data object via SetData() but 
  1.1225 +    // our IDataObject implementation doesn't implement SetData.  It adds data
  1.1226 +    // to the data object lazily only when the drop target asks for it.
  1.1227 +    *pdw = (DWORD) DROPEFFECT_MOVE;
  1.1228 +    GlobalUnlock(hGlobalMemory);
  1.1229 +  }
  1.1230 +  else {
  1.1231 +    res = E_OUTOFMEMORY;
  1.1232 +  }
  1.1233 +  aSTG.hGlobal = hGlobalMemory;
  1.1234 +  return res;
  1.1235 +}
  1.1236 +
  1.1237 +//-----------------------------------------------------
  1.1238 +HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGMEDIUM& aSTG)
  1.1239 +{
  1.1240 +  void* data = nullptr;
  1.1241 +  uint32_t   len;
  1.1242 +  
  1.1243 +  // if someone asks for text/plain, look up text/unicode instead in the transferable.
  1.1244 +  const char* flavorStr;
  1.1245 +  const nsPromiseFlatCString& flat = PromiseFlatCString(aDataFlavor);
  1.1246 +  if ( aDataFlavor.Equals("text/plain") )
  1.1247 +    flavorStr = kUnicodeMime;
  1.1248 +  else
  1.1249 +    flavorStr = flat.get();
  1.1250 +
  1.1251 +  // NOTE: CreateDataFromPrimitive creates new memory, that needs to be deleted
  1.1252 +  nsCOMPtr<nsISupports> genericDataWrapper;
  1.1253 +  mTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &len);
  1.1254 +  if ( !len )
  1.1255 +    return E_FAIL;
  1.1256 +  nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr, genericDataWrapper, &data, len );
  1.1257 +  if ( !data )
  1.1258 +    return E_FAIL;
  1.1259 +
  1.1260 +  HGLOBAL     hGlobalMemory = nullptr;
  1.1261 +
  1.1262 +  aSTG.tymed          = TYMED_HGLOBAL;
  1.1263 +  aSTG.pUnkForRelease = nullptr;
  1.1264 +
  1.1265 +  // We play games under the hood and advertise flavors that we know we
  1.1266 +  // can support, only they require a bit of conversion or munging of the data.
  1.1267 +  // Do that here.
  1.1268 +  //
  1.1269 +  // The transferable gives us data that is null-terminated, but this isn't reflected in
  1.1270 +  // the |len| parameter. Windoze apps expect this null to be there so bump our data buffer
  1.1271 +  // by the appropriate size to account for the null (one char for CF_TEXT, one char16_t for
  1.1272 +  // CF_UNICODETEXT).
  1.1273 +  DWORD allocLen = (DWORD)len;
  1.1274 +  if ( aFE.cfFormat == CF_TEXT ) {
  1.1275 +    // Someone is asking for text/plain; convert the unicode (assuming it's present)
  1.1276 +    // to text with the correct platform encoding.
  1.1277 +    char* plainTextData = nullptr;
  1.1278 +    char16_t* castedUnicode = reinterpret_cast<char16_t*>(data);
  1.1279 +    int32_t plainTextLen = 0;
  1.1280 +    nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText ( castedUnicode, len / 2, &plainTextData, &plainTextLen );
  1.1281 +   
  1.1282 +    // replace the unicode data with our plaintext data. Recall that |plainTextLen| doesn't include
  1.1283 +    // the null in the length.
  1.1284 +    nsMemory::Free(data);
  1.1285 +    if ( plainTextData ) {
  1.1286 +      data = plainTextData;
  1.1287 +      allocLen = plainTextLen + sizeof(char);
  1.1288 +    }
  1.1289 +    else {
  1.1290 +      NS_WARNING ( "Oh no, couldn't convert unicode to plain text" );
  1.1291 +      return S_OK;
  1.1292 +    }
  1.1293 +  }
  1.1294 +  else if ( aFE.cfFormat == nsClipboard::CF_HTML ) {
  1.1295 +    // Someone is asking for win32's HTML flavor. Convert our html fragment
  1.1296 +    // from unicode to UTF-8 then put it into a format specified by msft.
  1.1297 +    NS_ConvertUTF16toUTF8 converter ( reinterpret_cast<char16_t*>(data) );
  1.1298 +    char* utf8HTML = nullptr;
  1.1299 +    nsresult rv = BuildPlatformHTML ( converter.get(), &utf8HTML );      // null terminates
  1.1300 +    
  1.1301 +    nsMemory::Free(data);
  1.1302 +    if ( NS_SUCCEEDED(rv) && utf8HTML ) {
  1.1303 +      // replace the unicode data with our HTML data. Don't forget the null.
  1.1304 +      data = utf8HTML;
  1.1305 +      allocLen = strlen(utf8HTML) + sizeof(char);
  1.1306 +    }
  1.1307 +    else {
  1.1308 +      NS_WARNING ( "Oh no, couldn't convert to HTML" );
  1.1309 +      return S_OK;
  1.1310 +    }
  1.1311 +  }
  1.1312 +  else {
  1.1313 +    // we assume that any data that isn't caught above is unicode. This may
  1.1314 +    // be an erroneous assumption, but is true so far.
  1.1315 +    allocLen += sizeof(char16_t);
  1.1316 +  }
  1.1317 +
  1.1318 +  hGlobalMemory = (HGLOBAL)GlobalAlloc(GMEM_MOVEABLE, allocLen);
  1.1319 +
  1.1320 +  // Copy text to Global Memory Area
  1.1321 +  if ( hGlobalMemory ) {
  1.1322 +    char* dest = reinterpret_cast<char*>(GlobalLock(hGlobalMemory));
  1.1323 +    char* source = reinterpret_cast<char*>(data);
  1.1324 +    memcpy ( dest, source, allocLen );                         // copies the null as well
  1.1325 +    GlobalUnlock(hGlobalMemory);
  1.1326 +  }
  1.1327 +  aSTG.hGlobal = hGlobalMemory;
  1.1328 +
  1.1329 +  // Now, delete the memory that was created by CreateDataFromPrimitive (or our text/plain data)
  1.1330 +  nsMemory::Free(data);
  1.1331 +
  1.1332 +  return S_OK;
  1.1333 +}
  1.1334 +
  1.1335 +//-----------------------------------------------------
  1.1336 +HRESULT nsDataObj::GetFile(FORMATETC& aFE, STGMEDIUM& aSTG)
  1.1337 +{
  1.1338 +  uint32_t dfInx = 0;
  1.1339 +  ULONG count;
  1.1340 +  FORMATETC fe;
  1.1341 +  m_enumFE->Reset();
  1.1342 +  while (NOERROR == m_enumFE->Next(1, &fe, &count)
  1.1343 +    && dfInx < mDataFlavors.Length()) {
  1.1344 +      if (mDataFlavors[dfInx].EqualsLiteral(kNativeImageMime))
  1.1345 +        return DropImage(aFE, aSTG);
  1.1346 +      if (mDataFlavors[dfInx].EqualsLiteral(kFileMime))
  1.1347 +        return DropFile(aFE, aSTG);
  1.1348 +      if (mDataFlavors[dfInx].EqualsLiteral(kFilePromiseMime))
  1.1349 +        return DropTempFile(aFE, aSTG);
  1.1350 +      dfInx++;
  1.1351 +  }
  1.1352 +  return E_FAIL;
  1.1353 +}
  1.1354 +
  1.1355 +HRESULT nsDataObj::DropFile(FORMATETC& aFE, STGMEDIUM& aSTG)
  1.1356 +{
  1.1357 +  nsresult rv;
  1.1358 +  uint32_t len = 0;
  1.1359 +  nsCOMPtr<nsISupports> genericDataWrapper;
  1.1360 +
  1.1361 +  mTransferable->GetTransferData(kFileMime, getter_AddRefs(genericDataWrapper),
  1.1362 +                                 &len);
  1.1363 +  nsCOMPtr<nsIFile> file ( do_QueryInterface(genericDataWrapper) );
  1.1364 +
  1.1365 +  if (!file)
  1.1366 +  {
  1.1367 +    nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
  1.1368 +    if (ptr) {
  1.1369 +      nsCOMPtr<nsISupports> supports;
  1.1370 +      ptr->GetData(getter_AddRefs(supports));
  1.1371 +      file = do_QueryInterface(supports);
  1.1372 +    }
  1.1373 +  }
  1.1374 +
  1.1375 +  if (!file)
  1.1376 +    return E_FAIL;
  1.1377 +
  1.1378 +  aSTG.tymed = TYMED_HGLOBAL;
  1.1379 +  aSTG.pUnkForRelease = nullptr;
  1.1380 +
  1.1381 +  nsAutoString path;
  1.1382 +  rv = file->GetPath(path);
  1.1383 +  if (NS_FAILED(rv))
  1.1384 +    return E_FAIL;
  1.1385 +
  1.1386 +  uint32_t allocLen = path.Length() + 2;
  1.1387 +  HGLOBAL hGlobalMemory = nullptr;
  1.1388 +  char16_t *dest;
  1.1389 +
  1.1390 +  hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) +
  1.1391 +                                             allocLen * sizeof(char16_t));
  1.1392 +  if (!hGlobalMemory)
  1.1393 +    return E_FAIL;
  1.1394 +
  1.1395 +  DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
  1.1396 +
  1.1397 +  // First, populate the drop file structure
  1.1398 +  pDropFile->pFiles = sizeof(DROPFILES); //Offset to start of file name string
  1.1399 +  pDropFile->fNC    = 0;
  1.1400 +  pDropFile->pt.x   = 0;
  1.1401 +  pDropFile->pt.y   = 0;
  1.1402 +  pDropFile->fWide  = TRUE;
  1.1403 +
  1.1404 +  // Copy the filename right after the DROPFILES structure
  1.1405 +  dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
  1.1406 +  memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t));
  1.1407 +
  1.1408 +  // Two null characters are needed at the end of the file name.
  1.1409 +  // Lookup the CF_HDROP shell clipboard format for more info.
  1.1410 +  // Add the second null character right after the first one.
  1.1411 +  dest[allocLen - 1] = L'\0';
  1.1412 +
  1.1413 +  GlobalUnlock(hGlobalMemory);
  1.1414 +
  1.1415 +  aSTG.hGlobal = hGlobalMemory;
  1.1416 +
  1.1417 +  return S_OK;
  1.1418 +}
  1.1419 +
  1.1420 +HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
  1.1421 +{
  1.1422 +  nsresult rv;
  1.1423 +  if (!mCachedTempFile) {
  1.1424 +    uint32_t len = 0;
  1.1425 +    nsCOMPtr<nsISupports> genericDataWrapper;
  1.1426 +
  1.1427 +    mTransferable->GetTransferData(kNativeImageMime, getter_AddRefs(genericDataWrapper), &len);
  1.1428 +    nsCOMPtr<imgIContainer> image(do_QueryInterface(genericDataWrapper));
  1.1429 +
  1.1430 +    if (!image) {
  1.1431 +      // Check if the image was put in an nsISupportsInterfacePointer wrapper.
  1.1432 +      // This might not be necessary any more, but could be useful for backwards
  1.1433 +      // compatibility.
  1.1434 +      nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
  1.1435 +      if (ptr) {
  1.1436 +        nsCOMPtr<nsISupports> supports;
  1.1437 +        ptr->GetData(getter_AddRefs(supports));
  1.1438 +        image = do_QueryInterface(supports);
  1.1439 +      }
  1.1440 +    }
  1.1441 +
  1.1442 +    if (!image) 
  1.1443 +      return E_FAIL;
  1.1444 +
  1.1445 +    // Use the clipboard helper class to build up a memory bitmap.
  1.1446 +    nsImageToClipboard converter(image);
  1.1447 +    HANDLE bits = nullptr;
  1.1448 +    rv = converter.GetPicture(&bits); // Clipboard routines return a global handle we own.
  1.1449 +
  1.1450 +    if (NS_FAILED(rv) || !bits)
  1.1451 +      return E_FAIL;
  1.1452 +
  1.1453 +    // We now own these bits!
  1.1454 +    uint32_t bitmapSize = GlobalSize(bits);
  1.1455 +    if (!bitmapSize) {
  1.1456 +      GlobalFree(bits);
  1.1457 +      return E_FAIL;
  1.1458 +    }
  1.1459 +
  1.1460 +    // Save the bitmap to a temporary location.      
  1.1461 +    nsCOMPtr<nsIFile> dropFile;
  1.1462 +    rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
  1.1463 +    if (!dropFile) {
  1.1464 +      GlobalFree(bits);
  1.1465 +      return E_FAIL;
  1.1466 +    }
  1.1467 +
  1.1468 +    // Filename must be random so as not to confuse apps like
  1.1469 +    // Photoshop which handle multiple drags into a single window.
  1.1470 +    char buf[13];
  1.1471 +    nsCString filename;
  1.1472 +    NS_MakeRandomString(buf, 8);
  1.1473 +    memcpy(buf+8, ".bmp", 5);
  1.1474 +    filename.Append(nsDependentCString(buf, 12));
  1.1475 +    dropFile->AppendNative(filename);
  1.1476 +    rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
  1.1477 +    if (NS_FAILED(rv)) { 
  1.1478 +      GlobalFree(bits);
  1.1479 +      return E_FAIL;
  1.1480 +    }
  1.1481 +
  1.1482 +    // Cache the temp file so we can delete it later and so
  1.1483 +    // it doesn't get recreated over and over on multiple calls
  1.1484 +    // which does occur from windows shell.
  1.1485 +    dropFile->Clone(getter_AddRefs(mCachedTempFile));
  1.1486 +
  1.1487 +    // Write the data to disk.
  1.1488 +    nsCOMPtr<nsIOutputStream> outStream;
  1.1489 +    rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
  1.1490 +    if (NS_FAILED(rv)) { 
  1.1491 +      GlobalFree(bits);
  1.1492 +      return E_FAIL;
  1.1493 +    }
  1.1494 +
  1.1495 +    char * bm = (char *)GlobalLock(bits);
  1.1496 +
  1.1497 +    BITMAPFILEHEADER	fileHdr;
  1.1498 +    BITMAPINFOHEADER *bmpHdr = (BITMAPINFOHEADER*)bm;
  1.1499 +
  1.1500 +    fileHdr.bfType        = ((WORD) ('M' << 8) | 'B');
  1.1501 +    fileHdr.bfSize        = GlobalSize (bits) + sizeof(fileHdr);
  1.1502 +    fileHdr.bfReserved1   = 0;
  1.1503 +    fileHdr.bfReserved2   = 0;
  1.1504 +    fileHdr.bfOffBits     = (DWORD) (sizeof(fileHdr) + bmpHdr->biSize);
  1.1505 +
  1.1506 +    uint32_t writeCount = 0;
  1.1507 +    if (NS_FAILED(outStream->Write((const char *)&fileHdr, sizeof(fileHdr), &writeCount)) ||
  1.1508 +        NS_FAILED(outStream->Write((const char *)bm, bitmapSize, &writeCount)))
  1.1509 +      rv = NS_ERROR_FAILURE;
  1.1510 +
  1.1511 +    outStream->Close();
  1.1512 +
  1.1513 +    GlobalUnlock(bits);
  1.1514 +    GlobalFree(bits);
  1.1515 +
  1.1516 +    if (NS_FAILED(rv))
  1.1517 +      return E_FAIL;
  1.1518 +  }
  1.1519 +  
  1.1520 +  // Pass the file name back to the drop target so that it can access the file.
  1.1521 +  nsAutoString path;
  1.1522 +  rv = mCachedTempFile->GetPath(path);
  1.1523 +  if (NS_FAILED(rv))
  1.1524 +    return E_FAIL;
  1.1525 +
  1.1526 +  // Two null characters are needed to terminate the file name list.
  1.1527 +  HGLOBAL hGlobalMemory = nullptr;
  1.1528 +
  1.1529 +  uint32_t allocLen = path.Length() + 2;
  1.1530 +
  1.1531 +  aSTG.tymed = TYMED_HGLOBAL;
  1.1532 +  aSTG.pUnkForRelease = nullptr;
  1.1533 +
  1.1534 +  hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(char16_t));
  1.1535 +  if (!hGlobalMemory)
  1.1536 +    return E_FAIL;
  1.1537 +
  1.1538 +  DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
  1.1539 +
  1.1540 +  // First, populate the drop file structure.
  1.1541 +  pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array.
  1.1542 +  pDropFile->fNC    = 0;
  1.1543 +  pDropFile->pt.x   = 0;
  1.1544 +  pDropFile->pt.y   = 0;
  1.1545 +  pDropFile->fWide  = TRUE;
  1.1546 +
  1.1547 +  // Copy the filename right after the DROPFILES structure.
  1.1548 +  char16_t* dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
  1.1549 +  memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); // Copies the null character in path as well.
  1.1550 +
  1.1551 +  // Two null characters are needed at the end of the file name.  
  1.1552 +  // Lookup the CF_HDROP shell clipboard format for more info.
  1.1553 +  // Add the second null character right after the first one.
  1.1554 +  dest[allocLen - 1] = L'\0';
  1.1555 +
  1.1556 +  GlobalUnlock(hGlobalMemory);
  1.1557 +
  1.1558 +  aSTG.hGlobal = hGlobalMemory;
  1.1559 +
  1.1560 +  return S_OK;
  1.1561 +}
  1.1562 +
  1.1563 +HRESULT nsDataObj::DropTempFile(FORMATETC& aFE, STGMEDIUM& aSTG)
  1.1564 +{
  1.1565 +  nsresult rv;
  1.1566 +  if (!mCachedTempFile) {
  1.1567 +    // Tempfile will need a temporary location.      
  1.1568 +    nsCOMPtr<nsIFile> dropFile;
  1.1569 +    rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
  1.1570 +    if (!dropFile)
  1.1571 +      return E_FAIL;
  1.1572 +
  1.1573 +    // Filename must be random
  1.1574 +    nsCString filename;
  1.1575 +    nsAutoString wideFileName;
  1.1576 +    nsCOMPtr<nsIURI> sourceURI;
  1.1577 +    HRESULT res;
  1.1578 +    res = GetDownloadDetails(getter_AddRefs(sourceURI),
  1.1579 +      wideFileName);
  1.1580 +    if (FAILED(res))
  1.1581 +      return res;
  1.1582 +    NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, filename);
  1.1583 +
  1.1584 +    dropFile->AppendNative(filename);
  1.1585 +    rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
  1.1586 +    if (NS_FAILED(rv))
  1.1587 +      return E_FAIL;
  1.1588 +
  1.1589 +    // Cache the temp file so we can delete it later and so
  1.1590 +    // it doesn't get recreated over and over on multiple calls
  1.1591 +    // which does occur from windows shell.
  1.1592 +    dropFile->Clone(getter_AddRefs(mCachedTempFile));
  1.1593 +
  1.1594 +    // Write the data to disk.
  1.1595 +    nsCOMPtr<nsIOutputStream> outStream;
  1.1596 +    rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
  1.1597 +    if (NS_FAILED(rv))
  1.1598 +      return E_FAIL;
  1.1599 +
  1.1600 +    IStream *pStream = nullptr;
  1.1601 +    nsDataObj::CreateStream(&pStream);
  1.1602 +    NS_ENSURE_TRUE(pStream, E_FAIL);
  1.1603 +
  1.1604 +    char buffer[512];
  1.1605 +    ULONG readCount = 0;
  1.1606 +    uint32_t writeCount = 0;
  1.1607 +    while (1) {
  1.1608 +      HRESULT hres = pStream->Read(buffer, sizeof(buffer), &readCount);
  1.1609 +      if (FAILED(hres))
  1.1610 +        return E_FAIL;
  1.1611 +      if (readCount == 0)
  1.1612 +        break;
  1.1613 +      rv = outStream->Write(buffer, readCount, &writeCount);
  1.1614 +      if (NS_FAILED(rv))
  1.1615 +        return E_FAIL;
  1.1616 +    }
  1.1617 +    outStream->Close();
  1.1618 +    pStream->Release();
  1.1619 +  }
  1.1620 +
  1.1621 +  // Pass the file name back to the drop target so that it can access the file.
  1.1622 +  nsAutoString path;
  1.1623 +  rv = mCachedTempFile->GetPath(path);
  1.1624 +  if (NS_FAILED(rv))
  1.1625 +    return E_FAIL;
  1.1626 +
  1.1627 +  uint32_t allocLen = path.Length() + 2;
  1.1628 +
  1.1629 +  // Two null characters are needed to terminate the file name list.
  1.1630 +  HGLOBAL hGlobalMemory = nullptr;
  1.1631 +
  1.1632 +  aSTG.tymed = TYMED_HGLOBAL;
  1.1633 +  aSTG.pUnkForRelease = nullptr;
  1.1634 +
  1.1635 +  hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(char16_t));
  1.1636 +  if (!hGlobalMemory)
  1.1637 +    return E_FAIL;
  1.1638 +
  1.1639 +  DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
  1.1640 +
  1.1641 +  // First, populate the drop file structure.
  1.1642 +  pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array.
  1.1643 +  pDropFile->fNC    = 0;
  1.1644 +  pDropFile->pt.x   = 0;
  1.1645 +  pDropFile->pt.y   = 0;
  1.1646 +  pDropFile->fWide  = TRUE;
  1.1647 +
  1.1648 +  // Copy the filename right after the DROPFILES structure.
  1.1649 +  char16_t* dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
  1.1650 +  memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); // Copies the null character in path as well.
  1.1651 +
  1.1652 +  // Two null characters are needed at the end of the file name.  
  1.1653 +  // Lookup the CF_HDROP shell clipboard format for more info.
  1.1654 +  // Add the second null character right after the first one.
  1.1655 +  dest[allocLen - 1] = L'\0';
  1.1656 +
  1.1657 +  GlobalUnlock(hGlobalMemory);
  1.1658 +
  1.1659 +  aSTG.hGlobal = hGlobalMemory;
  1.1660 +
  1.1661 +  return S_OK;
  1.1662 +}
  1.1663 +
  1.1664 +//-----------------------------------------------------
  1.1665 +HRESULT nsDataObj::GetMetafilePict(FORMATETC&, STGMEDIUM&)
  1.1666 +{
  1.1667 +	return E_NOTIMPL;
  1.1668 +}
  1.1669 +
  1.1670 +//-----------------------------------------------------
  1.1671 +HRESULT nsDataObj::SetBitmap(FORMATETC&, STGMEDIUM&)
  1.1672 +{
  1.1673 +	return E_NOTIMPL;
  1.1674 +}
  1.1675 +
  1.1676 +//-----------------------------------------------------
  1.1677 +HRESULT nsDataObj::SetDib(FORMATETC&, STGMEDIUM&)
  1.1678 +{
  1.1679 +	return E_FAIL;
  1.1680 +}
  1.1681 +
  1.1682 +//-----------------------------------------------------
  1.1683 +HRESULT nsDataObj::SetText  (FORMATETC& aFE, STGMEDIUM& aSTG)
  1.1684 +{
  1.1685 +	return E_FAIL;
  1.1686 +}
  1.1687 +
  1.1688 +//-----------------------------------------------------
  1.1689 +HRESULT nsDataObj::SetMetafilePict(FORMATETC&, STGMEDIUM&)
  1.1690 +{
  1.1691 +	return E_FAIL;
  1.1692 +}
  1.1693 +
  1.1694 +
  1.1695 +
  1.1696 +//-----------------------------------------------------
  1.1697 +//-----------------------------------------------------
  1.1698 +CLSID nsDataObj::GetClassID() const
  1.1699 +{
  1.1700 +	return CLSID_nsDataObj;
  1.1701 +}
  1.1702 +
  1.1703 +//-----------------------------------------------------
  1.1704 +// Registers the DataFlavor/FE pair.
  1.1705 +//-----------------------------------------------------
  1.1706 +void nsDataObj::AddDataFlavor(const char* aDataFlavor, LPFORMATETC aFE)
  1.1707 +{
  1.1708 +  // These two lists are the mapping to and from data flavors and FEs.
  1.1709 +  // Later, OLE will tell us it needs a certain type of FORMATETC (text, unicode, etc)
  1.1710 +  // unicode, etc), so we will look up the data flavor that corresponds to
  1.1711 +  // the FE and then ask the transferable for that type of data.
  1.1712 +  mDataFlavors.AppendElement(aDataFlavor);
  1.1713 +  m_enumFE->AddFormatEtc(aFE);
  1.1714 +}
  1.1715 +
  1.1716 +//-----------------------------------------------------
  1.1717 +// Sets the transferable object
  1.1718 +//-----------------------------------------------------
  1.1719 +void nsDataObj::SetTransferable(nsITransferable * aTransferable)
  1.1720 +{
  1.1721 +    NS_IF_RELEASE(mTransferable);
  1.1722 +
  1.1723 +  mTransferable = aTransferable;
  1.1724 +  if (nullptr == mTransferable) {
  1.1725 +    return;
  1.1726 +  }
  1.1727 +
  1.1728 +  NS_ADDREF(mTransferable);
  1.1729 +
  1.1730 +  return;
  1.1731 +}
  1.1732 +
  1.1733 +
  1.1734 +//
  1.1735 +// ExtractURL
  1.1736 +//
  1.1737 +// Roots around in the transferable for the appropriate flavor that indicates
  1.1738 +// a url and pulls out the url portion of the data. Used mostly for creating
  1.1739 +// internet shortcuts on the desktop. The url flavor is of the format:
  1.1740 +//
  1.1741 +//   <url> <linefeed> <page title>
  1.1742 +//
  1.1743 +nsresult
  1.1744 +nsDataObj :: ExtractShortcutURL ( nsString & outURL )
  1.1745 +{
  1.1746 +  NS_ASSERTION ( mTransferable, "We don't have a good transferable" );
  1.1747 +  nsresult rv = NS_ERROR_FAILURE;
  1.1748 +  
  1.1749 +  uint32_t len = 0;
  1.1750 +  nsCOMPtr<nsISupports> genericURL;
  1.1751 +  if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
  1.1752 +    nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
  1.1753 +    if ( urlObject ) {
  1.1754 +      nsAutoString url;
  1.1755 +      urlObject->GetData ( url );
  1.1756 +      outURL = url;
  1.1757 +
  1.1758 +      // find the first linefeed in the data, that's where the url ends. trunc the 
  1.1759 +      // result string at that point.
  1.1760 +      int32_t lineIndex = outURL.FindChar ( '\n' );
  1.1761 +      NS_ASSERTION ( lineIndex > 0, "Format for url flavor is <url> <linefeed> <page title>" );
  1.1762 +      if ( lineIndex > 0 ) {
  1.1763 +        outURL.Truncate ( lineIndex );
  1.1764 +        rv = NS_OK;    
  1.1765 +      }
  1.1766 +    }
  1.1767 +  } else if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLDataMime, getter_AddRefs(genericURL), &len)) ||
  1.1768 +              NS_SUCCEEDED(mTransferable->GetTransferData(kURLPrivateMime, getter_AddRefs(genericURL), &len)) ) {
  1.1769 +    nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
  1.1770 +    if ( urlObject ) {
  1.1771 +      nsAutoString url;
  1.1772 +      urlObject->GetData ( url );
  1.1773 +      outURL = url;
  1.1774 +
  1.1775 +      rv = NS_OK;    
  1.1776 +    }
  1.1777 +
  1.1778 +  }  // if found flavor
  1.1779 +  
  1.1780 +  return rv;
  1.1781 +
  1.1782 +} // ExtractShortcutURL
  1.1783 +
  1.1784 +
  1.1785 +//
  1.1786 +// ExtractShortcutTitle
  1.1787 +//
  1.1788 +// Roots around in the transferable for the appropriate flavor that indicates
  1.1789 +// a url and pulls out the title portion of the data. Used mostly for creating
  1.1790 +// internet shortcuts on the desktop. The url flavor is of the format:
  1.1791 +//
  1.1792 +//   <url> <linefeed> <page title>
  1.1793 +//
  1.1794 +nsresult
  1.1795 +nsDataObj :: ExtractShortcutTitle ( nsString & outTitle )
  1.1796 +{
  1.1797 +  NS_ASSERTION ( mTransferable, "We'd don't have a good transferable" );
  1.1798 +  nsresult rv = NS_ERROR_FAILURE;
  1.1799 +  
  1.1800 +  uint32_t len = 0;
  1.1801 +  nsCOMPtr<nsISupports> genericURL;
  1.1802 +  if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
  1.1803 +    nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
  1.1804 +    if ( urlObject ) {
  1.1805 +      nsAutoString url;
  1.1806 +      urlObject->GetData ( url );
  1.1807 +
  1.1808 +      // find the first linefeed in the data, that's where the url ends. we want
  1.1809 +      // everything after that linefeed. FindChar() returns -1 if we can't find
  1.1810 +      int32_t lineIndex = url.FindChar ( '\n' );
  1.1811 +      NS_ASSERTION ( lineIndex != -1, "Format for url flavor is <url> <linefeed> <page title>" );
  1.1812 +      if ( lineIndex != -1 ) {
  1.1813 +        url.Mid ( outTitle, lineIndex + 1, (len/2) - (lineIndex + 1) );
  1.1814 +        rv = NS_OK;    
  1.1815 +      }
  1.1816 +    }
  1.1817 +  } // if found flavor
  1.1818 +  
  1.1819 +  return rv;
  1.1820 +
  1.1821 +} // ExtractShortcutTitle
  1.1822 +
  1.1823 +
  1.1824 +//
  1.1825 +// BuildPlatformHTML
  1.1826 +//
  1.1827 +// Munge our HTML data to win32's CF_HTML spec. Basically, put the requisite
  1.1828 +// header information on it. This will null terminate |outPlatformHTML|. See
  1.1829 +//  http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
  1.1830 +// for details.
  1.1831 +//
  1.1832 +// We assume that |inOurHTML| is already a fragment (ie, doesn't have <HTML>
  1.1833 +// or <BODY> tags). We'll wrap the fragment with them to make other apps
  1.1834 +// happy.
  1.1835 +//
  1.1836 +nsresult 
  1.1837 +nsDataObj :: BuildPlatformHTML ( const char* inOurHTML, char** outPlatformHTML ) 
  1.1838 +{
  1.1839 +  *outPlatformHTML = nullptr;
  1.1840 +
  1.1841 +  nsDependentCString inHTMLString(inOurHTML);
  1.1842 +  const char* const numPlaceholder  = "00000000";
  1.1843 +  const char* const startHTMLPrefix = "Version:0.9\r\nStartHTML:";
  1.1844 +  const char* const endHTMLPrefix   = "\r\nEndHTML:";
  1.1845 +  const char* const startFragPrefix = "\r\nStartFragment:";
  1.1846 +  const char* const endFragPrefix   = "\r\nEndFragment:";
  1.1847 +  const char* const startSourceURLPrefix = "\r\nSourceURL:";
  1.1848 +  const char* const endFragTrailer  = "\r\n";
  1.1849 +
  1.1850 +  // Do we already have mSourceURL from a drag?
  1.1851 +  if (mSourceURL.IsEmpty()) {
  1.1852 +    nsAutoString url;
  1.1853 +    ExtractShortcutURL(url);
  1.1854 +
  1.1855 +    AppendUTF16toUTF8(url, mSourceURL);
  1.1856 +  }
  1.1857 +
  1.1858 +  const int32_t kSourceURLLength    = mSourceURL.Length();
  1.1859 +  const int32_t kNumberLength       = strlen(numPlaceholder);
  1.1860 +
  1.1861 +  const int32_t kTotalHeaderLen     = strlen(startHTMLPrefix) +
  1.1862 +                                      strlen(endHTMLPrefix) +
  1.1863 +                                      strlen(startFragPrefix) + 
  1.1864 +                                      strlen(endFragPrefix) + 
  1.1865 +                                      strlen(endFragTrailer) +
  1.1866 +                                      (kSourceURLLength > 0 ? strlen(startSourceURLPrefix) : 0) +
  1.1867 +                                      kSourceURLLength +
  1.1868 +                                      (4 * kNumberLength);
  1.1869 +
  1.1870 +  NS_NAMED_LITERAL_CSTRING(htmlHeaderString, "<html><body>\r\n");
  1.1871 +
  1.1872 +  NS_NAMED_LITERAL_CSTRING(fragmentHeaderString, "<!--StartFragment-->");
  1.1873 +
  1.1874 +  nsDependentCString trailingString(
  1.1875 +      "<!--EndFragment-->\r\n"
  1.1876 +      "</body>\r\n"
  1.1877 +      "</html>");
  1.1878 +
  1.1879 +  // calculate the offsets
  1.1880 +  int32_t startHTMLOffset = kTotalHeaderLen;
  1.1881 +  int32_t startFragOffset = startHTMLOffset
  1.1882 +                              + htmlHeaderString.Length()
  1.1883 +			      + fragmentHeaderString.Length();
  1.1884 +
  1.1885 +  int32_t endFragOffset   = startFragOffset
  1.1886 +                              + inHTMLString.Length();
  1.1887 +
  1.1888 +  int32_t endHTMLOffset   = endFragOffset
  1.1889 +                              + trailingString.Length();
  1.1890 +
  1.1891 +  // now build the final version
  1.1892 +  nsCString clipboardString;
  1.1893 +  clipboardString.SetCapacity(endHTMLOffset);
  1.1894 +
  1.1895 +  clipboardString.Append(startHTMLPrefix);
  1.1896 +  clipboardString.Append(nsPrintfCString("%08u", startHTMLOffset));
  1.1897 +
  1.1898 +  clipboardString.Append(endHTMLPrefix);  
  1.1899 +  clipboardString.Append(nsPrintfCString("%08u", endHTMLOffset));
  1.1900 +
  1.1901 +  clipboardString.Append(startFragPrefix);
  1.1902 +  clipboardString.Append(nsPrintfCString("%08u", startFragOffset));
  1.1903 +
  1.1904 +  clipboardString.Append(endFragPrefix);
  1.1905 +  clipboardString.Append(nsPrintfCString("%08u", endFragOffset));
  1.1906 +
  1.1907 +  if (kSourceURLLength > 0) {
  1.1908 +    clipboardString.Append(startSourceURLPrefix);
  1.1909 +    clipboardString.Append(mSourceURL);
  1.1910 +  }
  1.1911 +
  1.1912 +  clipboardString.Append(endFragTrailer);
  1.1913 +
  1.1914 +  clipboardString.Append(htmlHeaderString);
  1.1915 +  clipboardString.Append(fragmentHeaderString);
  1.1916 +  clipboardString.Append(inHTMLString);
  1.1917 +  clipboardString.Append(trailingString);
  1.1918 +
  1.1919 +  *outPlatformHTML = ToNewCString(clipboardString);
  1.1920 +  if (!*outPlatformHTML)
  1.1921 +    return NS_ERROR_OUT_OF_MEMORY;
  1.1922 +
  1.1923 +  return NS_OK;
  1.1924 +}
  1.1925 +
  1.1926 +HRESULT 
  1.1927 +nsDataObj :: GetUniformResourceLocator( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode )
  1.1928 +{
  1.1929 +  HRESULT res = S_OK;
  1.1930 +  if (IsFlavourPresent(kURLMime)) {
  1.1931 +    if ( aIsUnicode )
  1.1932 +      res = ExtractUniformResourceLocatorW( aFE, aSTG );
  1.1933 +    else
  1.1934 +      res = ExtractUniformResourceLocatorA( aFE, aSTG );
  1.1935 +  }
  1.1936 +  else
  1.1937 +    NS_WARNING ("Not yet implemented\n");
  1.1938 +  return res;
  1.1939 +}
  1.1940 +
  1.1941 +HRESULT
  1.1942 +nsDataObj::ExtractUniformResourceLocatorA(FORMATETC& aFE, STGMEDIUM& aSTG )
  1.1943 +{
  1.1944 +  HRESULT result = S_OK;
  1.1945 +
  1.1946 +  nsAutoString url;
  1.1947 +  if (NS_FAILED(ExtractShortcutURL(url)))
  1.1948 +    return E_OUTOFMEMORY;
  1.1949 +
  1.1950 +  NS_LossyConvertUTF16toASCII asciiUrl(url);
  1.1951 +  const int totalLen = asciiUrl.Length() + 1;
  1.1952 +  HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
  1.1953 +  if (!hGlobalMemory)
  1.1954 +    return E_OUTOFMEMORY;
  1.1955 +
  1.1956 +  char* contents = reinterpret_cast<char*>(GlobalLock(hGlobalMemory));
  1.1957 +  if (!contents) {
  1.1958 +    GlobalFree(hGlobalMemory);
  1.1959 +    return E_OUTOFMEMORY;
  1.1960 +  }
  1.1961 +
  1.1962 +  strcpy(contents, asciiUrl.get());
  1.1963 +  GlobalUnlock(hGlobalMemory);
  1.1964 +  aSTG.hGlobal = hGlobalMemory;
  1.1965 +  aSTG.tymed = TYMED_HGLOBAL;
  1.1966 +
  1.1967 +  return result;
  1.1968 +}
  1.1969 +
  1.1970 +HRESULT
  1.1971 +nsDataObj::ExtractUniformResourceLocatorW(FORMATETC& aFE, STGMEDIUM& aSTG )
  1.1972 +{
  1.1973 +  HRESULT result = S_OK;
  1.1974 +
  1.1975 +  nsAutoString url;
  1.1976 +  if (NS_FAILED(ExtractShortcutURL(url)))
  1.1977 +    return E_OUTOFMEMORY;
  1.1978 +
  1.1979 +  const int totalLen = (url.Length() + 1) * sizeof(char16_t);
  1.1980 +  HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
  1.1981 +  if (!hGlobalMemory)
  1.1982 +    return E_OUTOFMEMORY;
  1.1983 +
  1.1984 +  wchar_t* contents = reinterpret_cast<wchar_t*>(GlobalLock(hGlobalMemory));
  1.1985 +  if (!contents) {
  1.1986 +    GlobalFree(hGlobalMemory);
  1.1987 +    return E_OUTOFMEMORY;
  1.1988 +  }
  1.1989 +
  1.1990 +  wcscpy(contents, url.get());
  1.1991 +  GlobalUnlock(hGlobalMemory);
  1.1992 +  aSTG.hGlobal = hGlobalMemory;
  1.1993 +  aSTG.tymed = TYMED_HGLOBAL;
  1.1994 +
  1.1995 +  return result;
  1.1996 +}
  1.1997 +
  1.1998 +
  1.1999 +// Gets the filename from the kFilePromiseURLMime flavour
  1.2000 +HRESULT nsDataObj::GetDownloadDetails(nsIURI **aSourceURI,
  1.2001 +                                      nsAString &aFilename)
  1.2002 +{
  1.2003 +  *aSourceURI = nullptr;
  1.2004 +
  1.2005 +  NS_ENSURE_TRUE(mTransferable, E_FAIL);
  1.2006 +
  1.2007 +  // get the URI from the kFilePromiseURLMime flavor
  1.2008 +  nsCOMPtr<nsISupports> urlPrimitive;
  1.2009 +  uint32_t dataSize = 0;
  1.2010 +  mTransferable->GetTransferData(kFilePromiseURLMime, getter_AddRefs(urlPrimitive), &dataSize);
  1.2011 +  nsCOMPtr<nsISupportsString> srcUrlPrimitive = do_QueryInterface(urlPrimitive);
  1.2012 +  NS_ENSURE_TRUE(srcUrlPrimitive, E_FAIL);
  1.2013 +  
  1.2014 +  nsAutoString srcUri;
  1.2015 +  srcUrlPrimitive->GetData(srcUri);
  1.2016 +  if (srcUri.IsEmpty())
  1.2017 +    return E_FAIL;
  1.2018 +  nsCOMPtr<nsIURI> sourceURI;
  1.2019 +  NS_NewURI(getter_AddRefs(sourceURI), srcUri);
  1.2020 +
  1.2021 +  nsAutoString srcFileName;
  1.2022 +  nsCOMPtr<nsISupports> fileNamePrimitive;
  1.2023 +  mTransferable->GetTransferData(kFilePromiseDestFilename, getter_AddRefs(fileNamePrimitive), &dataSize);
  1.2024 +  nsCOMPtr<nsISupportsString> srcFileNamePrimitive = do_QueryInterface(fileNamePrimitive);
  1.2025 +  if (srcFileNamePrimitive) {
  1.2026 +    srcFileNamePrimitive->GetData(srcFileName);
  1.2027 +  } else {
  1.2028 +    nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
  1.2029 +    if (!sourceURL)
  1.2030 +      return E_FAIL;
  1.2031 +    
  1.2032 +    nsAutoCString urlFileName;
  1.2033 +    sourceURL->GetFileName(urlFileName);
  1.2034 +    NS_UnescapeURL(urlFileName);
  1.2035 +    CopyUTF8toUTF16(urlFileName, srcFileName);
  1.2036 +  }
  1.2037 +  if (srcFileName.IsEmpty())
  1.2038 +    return E_FAIL;
  1.2039 +
  1.2040 +  // make the name safe for the filesystem
  1.2041 +  MangleTextToValidFilename(srcFileName);
  1.2042 +
  1.2043 +  sourceURI.swap(*aSourceURI);
  1.2044 +  aFilename = srcFileName;
  1.2045 +  return S_OK;
  1.2046 +}
  1.2047 +
  1.2048 +HRESULT nsDataObj::GetFileDescriptor_IStreamA(FORMATETC& aFE, STGMEDIUM& aSTG)
  1.2049 +{
  1.2050 +  HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
  1.2051 +  NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY);
  1.2052 +
  1.2053 +  LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(GlobalLock(fileGroupDescHandle));
  1.2054 +  if (!fileGroupDescA) {
  1.2055 +    ::GlobalFree(fileGroupDescHandle);
  1.2056 +    return E_OUTOFMEMORY;
  1.2057 +  }
  1.2058 +
  1.2059 +  nsAutoString wideFileName;
  1.2060 +  HRESULT res;
  1.2061 +  nsCOMPtr<nsIURI> sourceURI;
  1.2062 +  res = GetDownloadDetails(getter_AddRefs(sourceURI), wideFileName);
  1.2063 +  if (FAILED(res))
  1.2064 +  {
  1.2065 +    ::GlobalFree(fileGroupDescHandle);
  1.2066 +    return res;
  1.2067 +  }
  1.2068 +
  1.2069 +  nsAutoCString nativeFileName;
  1.2070 +  NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, nativeFileName);
  1.2071 +  
  1.2072 +  strncpy(fileGroupDescA->fgd[0].cFileName, nativeFileName.get(), NS_MAX_FILEDESCRIPTOR - 1);
  1.2073 +  fileGroupDescA->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0';
  1.2074 +
  1.2075 +  // one file in the file block
  1.2076 +  fileGroupDescA->cItems = 1;
  1.2077 +  fileGroupDescA->fgd[0].dwFlags = FD_PROGRESSUI;
  1.2078 +
  1.2079 +  GlobalUnlock( fileGroupDescHandle );
  1.2080 +  aSTG.hGlobal = fileGroupDescHandle;
  1.2081 +  aSTG.tymed = TYMED_HGLOBAL;
  1.2082 +
  1.2083 +  return S_OK;
  1.2084 +}
  1.2085 +
  1.2086 +HRESULT nsDataObj::GetFileDescriptor_IStreamW(FORMATETC& aFE, STGMEDIUM& aSTG)
  1.2087 +{
  1.2088 +  HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
  1.2089 +  NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY);
  1.2090 +
  1.2091 +  LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(GlobalLock(fileGroupDescHandle));
  1.2092 +  if (!fileGroupDescW) {
  1.2093 +    ::GlobalFree(fileGroupDescHandle);
  1.2094 +    return E_OUTOFMEMORY;
  1.2095 +  }
  1.2096 +
  1.2097 +  nsAutoString wideFileName;
  1.2098 +  HRESULT res;
  1.2099 +  nsCOMPtr<nsIURI> sourceURI;
  1.2100 +  res = GetDownloadDetails(getter_AddRefs(sourceURI),
  1.2101 +                           wideFileName);
  1.2102 +  if (FAILED(res))
  1.2103 +  {
  1.2104 +    ::GlobalFree(fileGroupDescHandle);
  1.2105 +    return res;
  1.2106 +  }
  1.2107 +
  1.2108 +  wcsncpy(fileGroupDescW->fgd[0].cFileName, wideFileName.get(), NS_MAX_FILEDESCRIPTOR - 1);
  1.2109 +  fileGroupDescW->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0';
  1.2110 +  // one file in the file block
  1.2111 +  fileGroupDescW->cItems = 1;
  1.2112 +  fileGroupDescW->fgd[0].dwFlags = FD_PROGRESSUI;
  1.2113 +
  1.2114 +  GlobalUnlock(fileGroupDescHandle);
  1.2115 +  aSTG.hGlobal = fileGroupDescHandle;
  1.2116 +  aSTG.tymed = TYMED_HGLOBAL;
  1.2117 +
  1.2118 +  return S_OK;
  1.2119 +}
  1.2120 +
  1.2121 +HRESULT nsDataObj::GetFileContents_IStream(FORMATETC& aFE, STGMEDIUM& aSTG)
  1.2122 +{
  1.2123 +  IStream *pStream = nullptr;
  1.2124 +
  1.2125 +  nsDataObj::CreateStream(&pStream);
  1.2126 +  NS_ENSURE_TRUE(pStream, E_FAIL);
  1.2127 +
  1.2128 +  aSTG.tymed = TYMED_ISTREAM;
  1.2129 +  aSTG.pstm = pStream;
  1.2130 +  aSTG.pUnkForRelease = nullptr;
  1.2131 +
  1.2132 +  return S_OK;
  1.2133 +}
  1.2134 +
  1.2135 +void nsDataObj::RemoveTempFile(nsITimer* aTimer, void* aClosure)
  1.2136 +{
  1.2137 +  nsDataObj *timedDataObj = static_cast<nsDataObj *>(aClosure);
  1.2138 +  if (timedDataObj->mCachedTempFile) {
  1.2139 +    timedDataObj->mCachedTempFile->Remove(false);
  1.2140 +    timedDataObj->mCachedTempFile = nullptr;
  1.2141 +  }
  1.2142 +  timedDataObj->Release();
  1.2143 +}

mercurial