gecko-dev/widget/windows/nsDataObj.cpp
Wes Kocher 4e9f80ed2e Backed out 14 changesets (bug 1165515) for b2g mochitest-6 permafail CLOSED TREE
Backed out changeset 9b97e2aa2ed9 (bug 1165515)
Backed out changeset 150606c022a2 (bug 1165515)
Backed out changeset 4e875a488349 (bug 1165515)
Backed out changeset 467e7feeb546 (bug 1165515)
Backed out changeset d6b6cc373197 (bug 1165515)
Backed out changeset 0615265b593c (bug 1165515)
Backed out changeset fafd1dce9f08 (bug 1165515)
Backed out changeset d1df869245f9 (bug 1165515)
Backed out changeset 6876a7c63611 (bug 1165515)
Backed out changeset b7841c94a9a3 (bug 1165515)
Backed out changeset e5e3617f7c73 (bug 1165515)
Backed out changeset 39be3db95978 (bug 1165515)
Backed out changeset 0ec74176f8de (bug 1165515)
Backed out changeset 5b928dd10d71 (bug 1165515)
2015-06-01 17:57:58 -07:00

2161 lines
66 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
#include <ole2.h>
#include <shlobj.h>
#include "nsDataObj.h"
#include "nsClipboard.h"
#include "nsReadableUtils.h"
#include "nsITransferable.h"
#include "nsISupportsPrimitives.h"
#include "IEnumFE.h"
#include "nsPrimitiveHelpers.h"
#include "nsXPIDLString.h"
#include "nsImageClipboard.h"
#include "nsCRT.h"
#include "nsPrintfCString.h"
#include "nsIStringBundle.h"
#include "nsEscape.h"
#include "nsIURL.h"
#include "nsNetUtil.h"
#include "nsXPCOMStrings.h"
#include "nscore.h"
#include "nsDirectoryServiceDefs.h"
#include "nsITimer.h"
#include "nsThreadUtils.h"
#include "mozilla/Preferences.h"
#include "nsIContentPolicy.h"
#include "nsContentUtils.h"
#include "nsINode.h"
#include "WinUtils.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/WindowsVersion.h"
#include <algorithm>
using namespace mozilla;
using namespace mozilla::widget;
#define DEFAULT_THREAD_TIMEOUT_MS 30000
NS_IMPL_ISUPPORTS(nsDataObj::CStream, nsIStreamListener)
//-----------------------------------------------------------------------------
// CStream implementation
nsDataObj::CStream::CStream() :
mChannelRead(false),
mStreamRead(0)
{
}
//-----------------------------------------------------------------------------
nsDataObj::CStream::~CStream()
{
}
//-----------------------------------------------------------------------------
// helper - initializes the stream
nsresult nsDataObj::CStream::Init(nsIURI *pSourceURI,
nsINode* aRequestingNode)
{
// we can not create a channel without a requestingNode
if (!aRequestingNode) {
return NS_ERROR_FAILURE;
}
nsresult rv;
rv = NS_NewChannel(getter_AddRefs(mChannel),
pSourceURI,
aRequestingNode,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER,
nullptr, // loadGroup
nullptr, // aCallbacks
nsIRequest::LOAD_FROM_CACHE);
NS_ENSURE_SUCCESS(rv, rv);
rv = mChannel->AsyncOpen(this, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
//-----------------------------------------------------------------------------
// IUnknown's QueryInterface, nsISupport's AddRef and Release are shared by
// IUnknown and nsIStreamListener.
STDMETHODIMP nsDataObj::CStream::QueryInterface(REFIID refiid, void** ppvResult)
{
*ppvResult = nullptr;
if (IID_IUnknown == refiid ||
refiid == IID_IStream)
{
*ppvResult = this;
}
if (nullptr != *ppvResult)
{
((LPUNKNOWN)*ppvResult)->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
// nsIStreamListener implementation
NS_IMETHODIMP
nsDataObj::CStream::OnDataAvailable(nsIRequest *aRequest,
nsISupports *aContext,
nsIInputStream *aInputStream,
uint64_t aOffset, // offset within the stream
uint32_t aCount) // bytes available on this call
{
// Extend the write buffer for the incoming data.
uint8_t* buffer = mChannelData.AppendElements(aCount);
if (buffer == nullptr)
return NS_ERROR_OUT_OF_MEMORY;
NS_ASSERTION((mChannelData.Length() == (aOffset + aCount)),
"stream length mismatch w/write buffer");
// Read() may not return aCount on a single call, so loop until we've
// accumulated all the data OnDataAvailable has promised.
nsresult rv;
uint32_t odaBytesReadTotal = 0;
do {
uint32_t bytesReadByCall = 0;
rv = aInputStream->Read((char*)(buffer + odaBytesReadTotal),
aCount, &bytesReadByCall);
odaBytesReadTotal += bytesReadByCall;
} while (aCount < odaBytesReadTotal && NS_SUCCEEDED(rv));
return rv;
}
NS_IMETHODIMP nsDataObj::CStream::OnStartRequest(nsIRequest *aRequest,
nsISupports *aContext)
{
mChannelResult = NS_OK;
return NS_OK;
}
NS_IMETHODIMP nsDataObj::CStream::OnStopRequest(nsIRequest *aRequest,
nsISupports *aContext,
nsresult aStatusCode)
{
mChannelRead = true;
mChannelResult = aStatusCode;
return NS_OK;
}
// Pumps thread messages while waiting for the async listener operation to
// complete. Failing this call will fail the stream incall from Windows
// and cancel the operation.
nsresult nsDataObj::CStream::WaitForCompletion()
{
// We are guaranteed OnStopRequest will get called, so this should be ok.
while (!mChannelRead) {
// Pump messages
NS_ProcessNextEvent(nullptr, true);
}
if (!mChannelData.Length())
mChannelResult = NS_ERROR_FAILURE;
return mChannelResult;
}
//-----------------------------------------------------------------------------
// IStream
STDMETHODIMP nsDataObj::CStream::Clone(IStream** ppStream)
{
return E_NOTIMPL;
}
//-----------------------------------------------------------------------------
STDMETHODIMP nsDataObj::CStream::Commit(DWORD dwFrags)
{
return E_NOTIMPL;
}
//-----------------------------------------------------------------------------
STDMETHODIMP nsDataObj::CStream::CopyTo(IStream* pDestStream,
ULARGE_INTEGER nBytesToCopy,
ULARGE_INTEGER* nBytesRead,
ULARGE_INTEGER* nBytesWritten)
{
return E_NOTIMPL;
}
//-----------------------------------------------------------------------------
STDMETHODIMP nsDataObj::CStream::LockRegion(ULARGE_INTEGER nStart,
ULARGE_INTEGER nBytes,
DWORD dwFlags)
{
return E_NOTIMPL;
}
//-----------------------------------------------------------------------------
STDMETHODIMP nsDataObj::CStream::Read(void* pvBuffer,
ULONG nBytesToRead,
ULONG* nBytesRead)
{
// Wait for the write into our buffer to complete via the stream listener.
// We can't respond to this by saying "call us back later".
if (NS_FAILED(WaitForCompletion()))
return E_FAIL;
// Bytes left for Windows to read out of our buffer
ULONG bytesLeft = mChannelData.Length() - mStreamRead;
// Let Windows know what we will hand back, usually this is the entire buffer
*nBytesRead = std::min(bytesLeft, nBytesToRead);
// Copy the buffer data over
memcpy(pvBuffer, ((char*)mChannelData.Elements() + mStreamRead), *nBytesRead);
// Update our bytes read tracking
mStreamRead += *nBytesRead;
return S_OK;
}
//-----------------------------------------------------------------------------
STDMETHODIMP nsDataObj::CStream::Revert(void)
{
return E_NOTIMPL;
}
//-----------------------------------------------------------------------------
STDMETHODIMP nsDataObj::CStream::Seek(LARGE_INTEGER nMove,
DWORD dwOrigin,
ULARGE_INTEGER* nNewPos)
{
if (nNewPos == nullptr)
return STG_E_INVALIDPOINTER;
if (nMove.LowPart == 0 && nMove.HighPart == 0 &&
(dwOrigin == STREAM_SEEK_SET || dwOrigin == STREAM_SEEK_CUR)) {
nNewPos->LowPart = 0;
nNewPos->HighPart = 0;
return S_OK;
}
return E_NOTIMPL;
}
//-----------------------------------------------------------------------------
STDMETHODIMP nsDataObj::CStream::SetSize(ULARGE_INTEGER nNewSize)
{
return E_NOTIMPL;
}
//-----------------------------------------------------------------------------
STDMETHODIMP nsDataObj::CStream::Stat(STATSTG* statstg, DWORD dwFlags)
{
if (statstg == nullptr)
return STG_E_INVALIDPOINTER;
if (!mChannel || NS_FAILED(WaitForCompletion()))
return E_FAIL;
memset((void*)statstg, 0, sizeof(STATSTG));
if (dwFlags != STATFLAG_NONAME)
{
nsCOMPtr<nsIURI> sourceURI;
if (NS_FAILED(mChannel->GetURI(getter_AddRefs(sourceURI)))) {
return E_FAIL;
}
nsAutoCString strFileName;
nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
sourceURL->GetFileName(strFileName);
if (strFileName.IsEmpty())
return E_FAIL;
NS_UnescapeURL(strFileName);
NS_ConvertUTF8toUTF16 wideFileName(strFileName);
uint32_t nMaxNameLength = (wideFileName.Length()*2) + 2;
void * retBuf = CoTaskMemAlloc(nMaxNameLength); // freed by caller
if (!retBuf)
return STG_E_INSUFFICIENTMEMORY;
ZeroMemory(retBuf, nMaxNameLength);
memcpy(retBuf, wideFileName.get(), wideFileName.Length()*2);
statstg->pwcsName = (LPOLESTR)retBuf;
}
SYSTEMTIME st;
statstg->type = STGTY_STREAM;
GetSystemTime(&st);
SystemTimeToFileTime((const SYSTEMTIME*)&st, (LPFILETIME)&statstg->mtime);
statstg->ctime = statstg->atime = statstg->mtime;
statstg->cbSize.LowPart = (DWORD)mChannelData.Length();
statstg->grfMode = STGM_READ;
statstg->grfLocksSupported = LOCK_ONLYONCE;
statstg->clsid = CLSID_NULL;
return S_OK;
}
//-----------------------------------------------------------------------------
STDMETHODIMP nsDataObj::CStream::UnlockRegion(ULARGE_INTEGER nStart,
ULARGE_INTEGER nBytes,
DWORD dwFlags)
{
return E_NOTIMPL;
}
//-----------------------------------------------------------------------------
STDMETHODIMP nsDataObj::CStream::Write(const void* pvBuffer,
ULONG nBytesToRead,
ULONG* nBytesRead)
{
return E_NOTIMPL;
}
//-----------------------------------------------------------------------------
HRESULT nsDataObj::CreateStream(IStream **outStream)
{
NS_ENSURE_TRUE(outStream, E_INVALIDARG);
nsresult rv = NS_ERROR_FAILURE;
nsAutoString wideFileName;
nsCOMPtr<nsIURI> sourceURI;
HRESULT res;
res = GetDownloadDetails(getter_AddRefs(sourceURI),
wideFileName);
if(FAILED(res))
return res;
nsDataObj::CStream *pStream = new nsDataObj::CStream();
NS_ENSURE_TRUE(pStream, E_OUTOFMEMORY);
pStream->AddRef();
// query the requestingNode from the transferable and add it to the new channel
nsCOMPtr<nsIDOMNode> requestingDomNode;
mTransferable->GetRequestingNode(getter_AddRefs(requestingDomNode));
nsCOMPtr<nsINode> requestingNode = do_QueryInterface(requestingDomNode);
MOZ_ASSERT(requestingNode, "can not create channel without a node");
rv = pStream->Init(sourceURI, requestingNode);
if (NS_FAILED(rv))
{
pStream->Release();
return E_FAIL;
}
*outStream = pStream;
return S_OK;
}
static GUID CLSID_nsDataObj =
{ 0x1bba7640, 0xdf52, 0x11cf, { 0x82, 0x7b, 0, 0xa0, 0x24, 0x3a, 0xe5, 0x05 } };
/*
* deliberately not using MAX_PATH. This is because on platforms < XP
* a file created with a long filename may be mishandled by the shell
* resulting in it not being able to be deleted or moved.
* See bug 250392 for more details.
*/
#define NS_MAX_FILEDESCRIPTOR 128 + 1
/*
* Class nsDataObj
*/
//-----------------------------------------------------
// construction
//-----------------------------------------------------
nsDataObj::nsDataObj(nsIURI * uri)
: m_cRef(0), mTransferable(nullptr),
mIsAsyncMode(FALSE), mIsInOperation(FALSE)
{
mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
NS_LITERAL_CSTRING("nsDataObj"),
LazyIdleThread::ManualShutdown);
m_enumFE = new CEnumFormatEtc();
m_enumFE->AddRef();
if (uri) {
// A URI was obtained, so pass this through to the DataObject
// so it can create a SourceURL for CF_HTML flavour
uri->GetSpec(mSourceURL);
}
}
//-----------------------------------------------------
// destruction
//-----------------------------------------------------
nsDataObj::~nsDataObj()
{
NS_IF_RELEASE(mTransferable);
mDataFlavors.Clear();
m_enumFE->Release();
// Free arbitrary system formats
for (uint32_t idx = 0; idx < mDataEntryList.Length(); idx++) {
CoTaskMemFree(mDataEntryList[idx]->fe.ptd);
ReleaseStgMedium(&mDataEntryList[idx]->stgm);
CoTaskMemFree(mDataEntryList[idx]);
}
}
//-----------------------------------------------------
// IUnknown interface methods - see inknown.h for documentation
//-----------------------------------------------------
STDMETHODIMP nsDataObj::QueryInterface(REFIID riid, void** ppv)
{
*ppv=nullptr;
if ( (IID_IUnknown == riid) || (IID_IDataObject == riid) ) {
*ppv = this;
AddRef();
return S_OK;
} else if (IID_IAsyncOperation == riid) {
*ppv = static_cast<IAsyncOperation*>(this);
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
//-----------------------------------------------------
STDMETHODIMP_(ULONG) nsDataObj::AddRef()
{
++m_cRef;
NS_LOG_ADDREF(this, m_cRef, "nsDataObj", sizeof(*this));
return m_cRef;
}
//-----------------------------------------------------
STDMETHODIMP_(ULONG) nsDataObj::Release()
{
--m_cRef;
NS_LOG_RELEASE(this, m_cRef, "nsDataObj");
if (0 != m_cRef)
return m_cRef;
// We have released our last ref on this object and need to delete the
// temp file. External app acting as drop target may still need to open the
// temp file. Addref a timer so it can delay deleting file and destroying
// this object. Delete file anyway and destroy this obj if there's a problem.
if (mCachedTempFile) {
nsresult rv;
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
mTimer->InitWithFuncCallback(nsDataObj::RemoveTempFile, this,
500, nsITimer::TYPE_ONE_SHOT);
return AddRef();
}
mCachedTempFile->Remove(false);
mCachedTempFile = nullptr;
}
delete this;
return 0;
}
//-----------------------------------------------------
BOOL nsDataObj::FormatsMatch(const FORMATETC& source, const FORMATETC& target) const
{
if ((source.cfFormat == target.cfFormat) &&
(source.dwAspect & target.dwAspect) &&
(source.tymed & target.tymed)) {
return TRUE;
} else {
return FALSE;
}
}
//-----------------------------------------------------
// IDataObject methods
//-----------------------------------------------------
STDMETHODIMP nsDataObj::GetData(LPFORMATETC aFormat, LPSTGMEDIUM pSTM)
{
if (!mTransferable)
return DV_E_FORMATETC;
uint32_t dfInx = 0;
static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA );
static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW );
static CLIPFORMAT uniformResourceLocatorA = ::RegisterClipboardFormat( CFSTR_INETURLA );
static CLIPFORMAT uniformResourceLocatorW = ::RegisterClipboardFormat( CFSTR_INETURLW );
static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS );
static CLIPFORMAT PreferredDropEffect = ::RegisterClipboardFormat( CFSTR_PREFERREDDROPEFFECT );
// Arbitrary system formats are used for image feedback during drag
// and drop. We are responsible for storing these internally during
// drag operations.
LPDATAENTRY pde;
if (LookupArbitraryFormat(aFormat, &pde, FALSE)) {
return CopyMediumData(pSTM, &pde->stgm, aFormat, FALSE)
? S_OK : E_UNEXPECTED;
}
// Firefox internal formats
ULONG count;
FORMATETC fe;
m_enumFE->Reset();
while (NOERROR == m_enumFE->Next(1, &fe, &count)
&& dfInx < mDataFlavors.Length()) {
nsCString& df = mDataFlavors.ElementAt(dfInx);
if (FormatsMatch(fe, *aFormat)) {
pSTM->pUnkForRelease = nullptr; // caller is responsible for deleting this data
CLIPFORMAT format = aFormat->cfFormat;
switch(format) {
// Someone is asking for plain or unicode text
case CF_TEXT:
case CF_UNICODETEXT:
return GetText(df, *aFormat, *pSTM);
// Some 3rd party apps that receive drag and drop files from the browser
// window require support for this.
case CF_HDROP:
return GetFile(*aFormat, *pSTM);
// Someone is asking for an image
case CF_DIBV5:
case CF_DIB:
return GetDib(df, *aFormat, *pSTM);
default:
if ( format == fileDescriptorFlavorA )
return GetFileDescriptor ( *aFormat, *pSTM, false );
if ( format == fileDescriptorFlavorW )
return GetFileDescriptor ( *aFormat, *pSTM, true);
if ( format == uniformResourceLocatorA )
return GetUniformResourceLocator( *aFormat, *pSTM, false);
if ( format == uniformResourceLocatorW )
return GetUniformResourceLocator( *aFormat, *pSTM, true);
if ( format == fileFlavor )
return GetFileContents ( *aFormat, *pSTM );
if ( format == PreferredDropEffect )
return GetPreferredDropEffect( *aFormat, *pSTM );
//MOZ_LOG(gWindowsLog, PR_LOG_ALWAYS,
// ("***** nsDataObj::GetData - Unknown format %u\n", format));
return GetText(df, *aFormat, *pSTM);
} //switch
} // if
dfInx++;
} // while
return DATA_E_FORMATETC;
}
//-----------------------------------------------------
STDMETHODIMP nsDataObj::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
{
return E_FAIL;
}
//-----------------------------------------------------
// Other objects querying to see if we support a
// particular format
//-----------------------------------------------------
STDMETHODIMP nsDataObj::QueryGetData(LPFORMATETC pFE)
{
// Arbitrary system formats are used for image feedback during drag
// and drop. We are responsible for storing these internally during
// drag operations.
LPDATAENTRY pde;
if (LookupArbitraryFormat(pFE, &pde, FALSE))
return S_OK;
// Firefox internal formats
ULONG count;
FORMATETC fe;
m_enumFE->Reset();
while (NOERROR == m_enumFE->Next(1, &fe, &count)) {
if (fe.cfFormat == pFE->cfFormat) {
return S_OK;
}
}
return E_FAIL;
}
//-----------------------------------------------------
STDMETHODIMP nsDataObj::GetCanonicalFormatEtc
(LPFORMATETC pFEIn, LPFORMATETC pFEOut)
{
return E_NOTIMPL;
}
//-----------------------------------------------------
STDMETHODIMP nsDataObj::SetData(LPFORMATETC aFormat, LPSTGMEDIUM aMedium, BOOL shouldRel)
{
// Arbitrary system formats are used for image feedback during drag
// and drop. We are responsible for storing these internally during
// drag operations.
LPDATAENTRY pde;
if (LookupArbitraryFormat(aFormat, &pde, TRUE)) {
// Release the old data the lookup handed us for this format. This
// may have been set in CopyMediumData when we originally stored the
// data.
if (pde->stgm.tymed) {
ReleaseStgMedium(&pde->stgm);
memset(&pde->stgm, 0, sizeof(STGMEDIUM));
}
bool result = true;
if (shouldRel) {
// If shouldRel is TRUE, the data object called owns the storage medium
// after the call returns. Store the incoming data in our data array for
// release when we are destroyed. This is the common case with arbitrary
// data from explorer.
pde->stgm = *aMedium;
} else {
// Copy the incoming data into our data array. (AFAICT, this never gets
// called with arbitrary formats for drag images.)
result = CopyMediumData(&pde->stgm, aMedium, aFormat, TRUE);
}
pde->fe.tymed = pde->stgm.tymed;
return result ? S_OK : DV_E_TYMED;
}
if (shouldRel)
ReleaseStgMedium(aMedium);
return S_OK;
}
bool
nsDataObj::LookupArbitraryFormat(FORMATETC *aFormat, LPDATAENTRY *aDataEntry, BOOL aAddorUpdate)
{
*aDataEntry = nullptr;
if (aFormat->ptd != nullptr)
return false;
// See if it's already in our list. If so return the data entry.
for (uint32_t idx = 0; idx < mDataEntryList.Length(); idx++) {
if (mDataEntryList[idx]->fe.cfFormat == aFormat->cfFormat &&
mDataEntryList[idx]->fe.dwAspect == aFormat->dwAspect &&
mDataEntryList[idx]->fe.lindex == aFormat->lindex) {
if (aAddorUpdate || (mDataEntryList[idx]->fe.tymed & aFormat->tymed)) {
// If the caller requests we update, or if the
// medium type matches, return the entry.
*aDataEntry = mDataEntryList[idx];
return true;
} else {
// Medium does not match, not found.
return false;
}
}
}
if (!aAddorUpdate)
return false;
// Add another entry to mDataEntryList
LPDATAENTRY dataEntry = (LPDATAENTRY)CoTaskMemAlloc(sizeof(DATAENTRY));
if (!dataEntry)
return false;
dataEntry->fe = *aFormat;
*aDataEntry = dataEntry;
memset(&dataEntry->stgm, 0, sizeof(STGMEDIUM));
// Add this to our IEnumFORMATETC impl. so we can return it when
// it's requested.
m_enumFE->AddFormatEtc(aFormat);
// Store a copy internally in the arbitrary formats array.
mDataEntryList.AppendElement(dataEntry);
return true;
}
bool
nsDataObj::CopyMediumData(STGMEDIUM *aMediumDst, STGMEDIUM *aMediumSrc, LPFORMATETC aFormat, BOOL aSetData)
{
STGMEDIUM stgmOut = *aMediumSrc;
switch (stgmOut.tymed) {
case TYMED_ISTREAM:
stgmOut.pstm->AddRef();
break;
case TYMED_ISTORAGE:
stgmOut.pstg->AddRef();
break;
case TYMED_HGLOBAL:
if (!aMediumSrc->pUnkForRelease) {
if (aSetData) {
if (aMediumSrc->tymed != TYMED_HGLOBAL)
return false;
stgmOut.hGlobal = OleDuplicateData(aMediumSrc->hGlobal, aFormat->cfFormat, 0);
if (!stgmOut.hGlobal)
return false;
} else {
// We are returning this data from LookupArbitraryFormat, indicate to the
// shell we hold it and will free it.
stgmOut.pUnkForRelease = static_cast<IDataObject*>(this);
}
}
break;
default:
return false;
}
if (stgmOut.pUnkForRelease)
stgmOut.pUnkForRelease->AddRef();
*aMediumDst = stgmOut;
return true;
}
//-----------------------------------------------------
STDMETHODIMP nsDataObj::EnumFormatEtc(DWORD dwDir, LPENUMFORMATETC *ppEnum)
{
switch (dwDir) {
case DATADIR_GET:
m_enumFE->Clone(ppEnum);
break;
case DATADIR_SET:
// fall through
default:
*ppEnum = nullptr;
} // switch
if (nullptr == *ppEnum)
return E_FAIL;
(*ppEnum)->Reset();
// Clone already AddRefed the result so don't addref it again.
return NOERROR;
}
//-----------------------------------------------------
STDMETHODIMP nsDataObj::DAdvise(LPFORMATETC pFE, DWORD dwFlags,
LPADVISESINK pIAdviseSink, DWORD* pdwConn)
{
return OLE_E_ADVISENOTSUPPORTED;
}
//-----------------------------------------------------
STDMETHODIMP nsDataObj::DUnadvise(DWORD dwConn)
{
return OLE_E_ADVISENOTSUPPORTED;
}
//-----------------------------------------------------
STDMETHODIMP nsDataObj::EnumDAdvise(LPENUMSTATDATA *ppEnum)
{
return OLE_E_ADVISENOTSUPPORTED;
}
// IAsyncOperation methods
STDMETHODIMP nsDataObj::EndOperation(HRESULT hResult,
IBindCtx *pbcReserved,
DWORD dwEffects)
{
mIsInOperation = FALSE;
return S_OK;
}
STDMETHODIMP nsDataObj::GetAsyncMode(BOOL *pfIsOpAsync)
{
*pfIsOpAsync = mIsAsyncMode;
return S_OK;
}
STDMETHODIMP nsDataObj::InOperation(BOOL *pfInAsyncOp)
{
*pfInAsyncOp = mIsInOperation;
return S_OK;
}
STDMETHODIMP nsDataObj::SetAsyncMode(BOOL fDoOpAsync)
{
mIsAsyncMode = fDoOpAsync;
return S_OK;
}
STDMETHODIMP nsDataObj::StartOperation(IBindCtx *pbcReserved)
{
mIsInOperation = TRUE;
return S_OK;
}
//-----------------------------------------------------
// GetData and SetData helper functions
//-----------------------------------------------------
HRESULT nsDataObj::AddSetFormat(FORMATETC& aFE)
{
return S_OK;
}
//-----------------------------------------------------
HRESULT nsDataObj::AddGetFormat(FORMATETC& aFE)
{
return S_OK;
}
//
// GetDIB
//
// Someone is asking for a bitmap. The data in the transferable will be a straight
// imgIContainer, so just QI it.
//
HRESULT
nsDataObj::GetDib(const nsACString& inFlavor,
FORMATETC &aFormat,
STGMEDIUM & aSTG)
{
ULONG result = E_FAIL;
uint32_t len = 0;
nsCOMPtr<nsISupports> genericDataWrapper;
mTransferable->GetTransferData(PromiseFlatCString(inFlavor).get(), getter_AddRefs(genericDataWrapper), &len);
nsCOMPtr<imgIContainer> image ( do_QueryInterface(genericDataWrapper) );
if ( !image ) {
// Check if the image was put in an nsISupportsInterfacePointer wrapper.
// This might not be necessary any more, but could be useful for backwards
// compatibility.
nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
if ( ptr ) {
nsCOMPtr<nsISupports> supports;
ptr->GetData(getter_AddRefs(supports));
image = do_QueryInterface(supports);
}
}
if ( image ) {
// use the |nsImageToClipboard| helper class to build up a bitmap. We now own
// the bits, and pass them back to the OS in |aSTG|.
nsImageToClipboard converter(image, aFormat.cfFormat == CF_DIBV5);
HANDLE bits = nullptr;
nsresult rv = converter.GetPicture ( &bits );
if ( NS_SUCCEEDED(rv) && bits ) {
aSTG.hGlobal = bits;
aSTG.tymed = TYMED_HGLOBAL;
result = S_OK;
}
} // if we have an image
else
NS_WARNING ( "Definitely not an image on clipboard" );
return result;
}
//
// GetFileDescriptor
//
HRESULT
nsDataObj :: GetFileDescriptor ( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode )
{
HRESULT res = S_OK;
// How we handle this depends on if we're dealing with an internet
// shortcut, since those are done under the covers.
if (IsFlavourPresent(kFilePromiseMime) ||
IsFlavourPresent(kFileMime))
{
if (aIsUnicode)
return GetFileDescriptor_IStreamW(aFE, aSTG);
else
return GetFileDescriptor_IStreamA(aFE, aSTG);
}
else if (IsFlavourPresent(kURLMime))
{
if ( aIsUnicode )
res = GetFileDescriptorInternetShortcutW ( aFE, aSTG );
else
res = GetFileDescriptorInternetShortcutA ( aFE, aSTG );
}
else
NS_WARNING ( "Not yet implemented\n" );
return res;
} // GetFileDescriptor
//
HRESULT
nsDataObj :: GetFileContents ( FORMATETC& aFE, STGMEDIUM& aSTG )
{
HRESULT res = S_OK;
// How we handle this depends on if we're dealing with an internet
// shortcut, since those are done under the covers.
if (IsFlavourPresent(kFilePromiseMime) ||
IsFlavourPresent(kFileMime))
return GetFileContents_IStream(aFE, aSTG);
else if (IsFlavourPresent(kURLMime))
return GetFileContentsInternetShortcut ( aFE, aSTG );
else
NS_WARNING ( "Not yet implemented\n" );
return res;
} // GetFileContents
//
// Given a unicode string, we ensure that it contains only characters which are valid within
// the file system. Remove all forbidden characters from the name, and completely disallow
// any title that starts with a forbidden name and extension (e.g. "nul" is invalid, but
// "nul." and "nul.txt" are also invalid and will cause problems).
//
// It would seem that this is more functionality suited to being in nsIFile.
//
static void
MangleTextToValidFilename(nsString & aText)
{
static const char* forbiddenNames[] = {
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
"CON", "PRN", "AUX", "NUL", "CLOCK$"
};
aText.StripChars(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS);
aText.CompressWhitespace(true, true);
uint32_t nameLen;
for (size_t n = 0; n < ArrayLength(forbiddenNames); ++n) {
nameLen = (uint32_t) strlen(forbiddenNames[n]);
if (aText.EqualsIgnoreCase(forbiddenNames[n], nameLen)) {
// invalid name is either the entire string, or a prefix with a period
if (aText.Length() == nameLen || aText.CharAt(nameLen) == char16_t('.')) {
aText.Truncate();
break;
}
}
}
}
//
// Given a unicode string, convert it down to a valid local charset filename
// with the supplied extension. This ensures that we do not cut MBCS characters
// in the middle.
//
// It would seem that this is more functionality suited to being in nsIFile.
//
static bool
CreateFilenameFromTextA(nsString & aText, const char * aExtension,
char * aFilename, uint32_t aFilenameLen)
{
// ensure that the supplied name doesn't have invalid characters. If
// a valid mangled filename couldn't be created then it will leave the
// text empty.
MangleTextToValidFilename(aText);
if (aText.IsEmpty())
return false;
// repeatably call WideCharToMultiByte as long as the title doesn't fit in the buffer
// available to us. Continually reduce the length of the source title until the MBCS
// version will fit in the buffer with room for the supplied extension. Doing it this
// way ensures that even in MBCS environments there will be a valid MBCS filename of
// the correct length.
int maxUsableFilenameLen = aFilenameLen - strlen(aExtension) - 1; // space for ext + null byte
int currLen, textLen = (int) std::min(aText.Length(), aFilenameLen);
char defaultChar = '_';
do {
currLen = WideCharToMultiByte(CP_ACP,
WC_COMPOSITECHECK|WC_DEFAULTCHAR,
aText.get(), textLen--, aFilename, maxUsableFilenameLen, &defaultChar, nullptr);
}
while (currLen == 0 && textLen > 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
if (currLen > 0 && textLen > 0) {
strcpy(&aFilename[currLen], aExtension);
return true;
}
else {
// empty names aren't permitted
return false;
}
}
static bool
CreateFilenameFromTextW(nsString & aText, const wchar_t * aExtension,
wchar_t * aFilename, uint32_t aFilenameLen)
{
// ensure that the supplied name doesn't have invalid characters. If
// a valid mangled filename couldn't be created then it will leave the
// text empty.
MangleTextToValidFilename(aText);
if (aText.IsEmpty())
return false;
const int extensionLen = wcslen(aExtension);
if (aText.Length() + extensionLen + 1 > aFilenameLen)
aText.Truncate(aFilenameLen - extensionLen - 1);
wcscpy(&aFilename[0], aText.get());
wcscpy(&aFilename[aText.Length()], aExtension);
return true;
}
#define PAGEINFO_PROPERTIES "chrome://navigator/locale/pageInfo.properties"
static bool
GetLocalizedString(const char16_t * aName, nsXPIDLString & aString)
{
nsCOMPtr<nsIStringBundleService> stringService =
mozilla::services::GetStringBundleService();
if (!stringService)
return false;
nsCOMPtr<nsIStringBundle> stringBundle;
nsresult rv = stringService->CreateBundle(PAGEINFO_PROPERTIES,
getter_AddRefs(stringBundle));
if (NS_FAILED(rv))
return false;
rv = stringBundle->GetStringFromName(aName, getter_Copies(aString));
return NS_SUCCEEDED(rv);
}
//
// GetFileDescriptorInternetShortcut
//
// Create the special format for an internet shortcut and build up the data
// structures the shell is expecting.
//
HRESULT
nsDataObj :: GetFileDescriptorInternetShortcutA ( FORMATETC& aFE, STGMEDIUM& aSTG )
{
// get the title of the shortcut
nsAutoString title;
if ( NS_FAILED(ExtractShortcutTitle(title)) )
return E_OUTOFMEMORY;
HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORA));
if (!fileGroupDescHandle)
return E_OUTOFMEMORY;
LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(::GlobalLock(fileGroupDescHandle));
if (!fileGroupDescA) {
::GlobalFree(fileGroupDescHandle);
return E_OUTOFMEMORY;
}
// get a valid filename in the following order: 1) from the page title,
// 2) localized string for an untitled page, 3) just use "Untitled.URL"
if (!CreateFilenameFromTextA(title, ".URL",
fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
nsXPIDLString untitled;
if (!GetLocalizedString(MOZ_UTF16("noPageTitle"), untitled) ||
!CreateFilenameFromTextA(untitled, ".URL",
fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
strcpy(fileGroupDescA->fgd[0].cFileName, "Untitled.URL");
}
}
// one file in the file block
fileGroupDescA->cItems = 1;
fileGroupDescA->fgd[0].dwFlags = FD_LINKUI;
::GlobalUnlock( fileGroupDescHandle );
aSTG.hGlobal = fileGroupDescHandle;
aSTG.tymed = TYMED_HGLOBAL;
return S_OK;
} // GetFileDescriptorInternetShortcutA
HRESULT
nsDataObj :: GetFileDescriptorInternetShortcutW ( FORMATETC& aFE, STGMEDIUM& aSTG )
{
// get the title of the shortcut
nsAutoString title;
if ( NS_FAILED(ExtractShortcutTitle(title)) )
return E_OUTOFMEMORY;
HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
if (!fileGroupDescHandle)
return E_OUTOFMEMORY;
LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(::GlobalLock(fileGroupDescHandle));
if (!fileGroupDescW) {
::GlobalFree(fileGroupDescHandle);
return E_OUTOFMEMORY;
}
// get a valid filename in the following order: 1) from the page title,
// 2) localized string for an untitled page, 3) just use "Untitled.URL"
if (!CreateFilenameFromTextW(title, L".URL",
fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
nsXPIDLString untitled;
if (!GetLocalizedString(MOZ_UTF16("noPageTitle"), untitled) ||
!CreateFilenameFromTextW(untitled, L".URL",
fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
wcscpy(fileGroupDescW->fgd[0].cFileName, L"Untitled.URL");
}
}
// one file in the file block
fileGroupDescW->cItems = 1;
fileGroupDescW->fgd[0].dwFlags = FD_LINKUI;
::GlobalUnlock( fileGroupDescHandle );
aSTG.hGlobal = fileGroupDescHandle;
aSTG.tymed = TYMED_HGLOBAL;
return S_OK;
} // GetFileDescriptorInternetShortcutW
//
// GetFileContentsInternetShortcut
//
// Create the special format for an internet shortcut and build up the data
// structures the shell is expecting.
//
HRESULT
nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG )
{
static const char * kShellIconPref = "browser.shell.shortcutFavicons";
nsAutoString url;
if ( NS_FAILED(ExtractShortcutURL(url)) )
return E_OUTOFMEMORY;
// will need to change if we ever support iDNS
nsAutoCString asciiUrl;
LossyCopyUTF16toASCII(url, asciiUrl);
nsCOMPtr<nsIFile> icoFile;
nsCOMPtr<nsIURI> aUri;
NS_NewURI(getter_AddRefs(aUri), url);
const char *shortcutFormatStr;
int totalLen;
nsCString path;
if (!Preferences::GetBool(kShellIconPref, true) ||
!IsVistaOrLater()) {
shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n";
const int formatLen = strlen(shortcutFormatStr) - 2; // don't include %s
totalLen = formatLen + asciiUrl.Length(); // don't include null character
} else {
nsCOMPtr<nsIFile> icoFile;
nsCOMPtr<nsIURI> aUri;
NS_NewURI(getter_AddRefs(aUri), url);
nsAutoString aUriHash;
mozilla::widget::FaviconHelper::ObtainCachedIconFile(aUri, aUriHash, mIOThread, true);
nsresult rv = mozilla::widget::FaviconHelper::GetOutputIconPath(aUri, icoFile, true);
NS_ENSURE_SUCCESS(rv, E_FAIL);
rv = icoFile->GetNativePath(path);
NS_ENSURE_SUCCESS(rv, E_FAIL);
shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n"
"IDList=\r\nHotKey=0\r\nIconFile=%s\r\n"
"IconIndex=0\r\n";
const int formatLen = strlen(shortcutFormatStr) - 2 * 2; // no %s twice
totalLen = formatLen + asciiUrl.Length() +
path.Length(); // we don't want a null character on the end
}
// create a global memory area and build up the file contents w/in it
HGLOBAL hGlobalMemory = ::GlobalAlloc(GMEM_SHARE, totalLen);
if ( !hGlobalMemory )
return E_OUTOFMEMORY;
char* contents = reinterpret_cast<char*>(::GlobalLock(hGlobalMemory));
if ( !contents ) {
::GlobalFree( hGlobalMemory );
return E_OUTOFMEMORY;
}
//NOTE: we intentionally use the Microsoft version of snprintf here because it does NOT null
// terminate strings which reach the maximum size of the buffer. Since we know that the
// formatted length here is totalLen, this call to _snprintf will format the string into
// the buffer without appending the null character.
if (!Preferences::GetBool(kShellIconPref, true)) {
_snprintf(contents, totalLen, shortcutFormatStr, asciiUrl.get());
} else {
_snprintf(contents, totalLen, shortcutFormatStr, asciiUrl.get(), path.get());
}
::GlobalUnlock(hGlobalMemory);
aSTG.hGlobal = hGlobalMemory;
aSTG.tymed = TYMED_HGLOBAL;
return S_OK;
} // GetFileContentsInternetShortcut
// check if specified flavour is present in the transferable
bool nsDataObj :: IsFlavourPresent(const char *inFlavour)
{
bool retval = false;
NS_ENSURE_TRUE(mTransferable, false);
// get the list of flavors available in the transferable
nsCOMPtr<nsISupportsArray> flavorList;
mTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
NS_ENSURE_TRUE(flavorList, false);
// try to find requested flavour
uint32_t cnt;
flavorList->Count(&cnt);
for (uint32_t i = 0; i < cnt; ++i) {
nsCOMPtr<nsISupports> genericFlavor;
flavorList->GetElementAt (i, getter_AddRefs(genericFlavor));
nsCOMPtr<nsISupportsCString> currentFlavor (do_QueryInterface(genericFlavor));
if (currentFlavor) {
nsAutoCString flavorStr;
currentFlavor->GetData(flavorStr);
if (flavorStr.Equals(inFlavour)) {
retval = true; // found it!
break;
}
}
} // for each flavor
return retval;
}
HRESULT nsDataObj::GetPreferredDropEffect ( FORMATETC& aFE, STGMEDIUM& aSTG )
{
HRESULT res = S_OK;
aSTG.tymed = TYMED_HGLOBAL;
aSTG.pUnkForRelease = nullptr;
HGLOBAL hGlobalMemory = nullptr;
hGlobalMemory = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
if (hGlobalMemory) {
DWORD* pdw = (DWORD*) GlobalLock(hGlobalMemory);
// The PreferredDropEffect clipboard format is only registered if a drag/drop
// of an image happens from Mozilla to the desktop. We want its value
// to be DROPEFFECT_MOVE in that case so that the file is moved from the
// temporary location, not copied.
// This value should, ideally, be set on the data object via SetData() but
// our IDataObject implementation doesn't implement SetData. It adds data
// to the data object lazily only when the drop target asks for it.
*pdw = (DWORD) DROPEFFECT_MOVE;
GlobalUnlock(hGlobalMemory);
}
else {
res = E_OUTOFMEMORY;
}
aSTG.hGlobal = hGlobalMemory;
return res;
}
//-----------------------------------------------------
HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGMEDIUM& aSTG)
{
void* data = nullptr;
uint32_t len;
// if someone asks for text/plain, look up text/unicode instead in the transferable.
const char* flavorStr;
const nsPromiseFlatCString& flat = PromiseFlatCString(aDataFlavor);
if (aDataFlavor.EqualsLiteral("text/plain"))
flavorStr = kUnicodeMime;
else
flavorStr = flat.get();
// NOTE: CreateDataFromPrimitive creates new memory, that needs to be deleted
nsCOMPtr<nsISupports> genericDataWrapper;
mTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &len);
if ( !len )
return E_FAIL;
nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr, genericDataWrapper, &data, len );
if ( !data )
return E_FAIL;
HGLOBAL hGlobalMemory = nullptr;
aSTG.tymed = TYMED_HGLOBAL;
aSTG.pUnkForRelease = nullptr;
// We play games under the hood and advertise flavors that we know we
// can support, only they require a bit of conversion or munging of the data.
// Do that here.
//
// The transferable gives us data that is null-terminated, but this isn't reflected in
// the |len| parameter. Windoze apps expect this null to be there so bump our data buffer
// by the appropriate size to account for the null (one char for CF_TEXT, one char16_t for
// CF_UNICODETEXT).
DWORD allocLen = (DWORD)len;
if ( aFE.cfFormat == CF_TEXT ) {
// Someone is asking for text/plain; convert the unicode (assuming it's present)
// to text with the correct platform encoding.
size_t bufferSize = sizeof(char)*(len + 2);
char* plainTextData = static_cast<char*>(moz_xmalloc(bufferSize));
char16_t* castedUnicode = reinterpret_cast<char16_t*>(data);
int32_t plainTextLen = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)castedUnicode, len / 2 + 1, plainTextData, bufferSize, NULL, NULL);
// replace the unicode data with our plaintext data. Recall that |plainTextLen| doesn't include
// the null in the length.
free(data);
if ( plainTextLen ) {
data = plainTextData;
allocLen = plainTextLen;
}
else {
free(plainTextData);
NS_WARNING ( "Oh no, couldn't convert unicode to plain text" );
return S_OK;
}
}
else if ( aFE.cfFormat == nsClipboard::CF_HTML ) {
// Someone is asking for win32's HTML flavor. Convert our html fragment
// from unicode to UTF-8 then put it into a format specified by msft.
NS_ConvertUTF16toUTF8 converter ( reinterpret_cast<char16_t*>(data) );
char* utf8HTML = nullptr;
nsresult rv = BuildPlatformHTML ( converter.get(), &utf8HTML ); // null terminates
free(data);
if ( NS_SUCCEEDED(rv) && utf8HTML ) {
// replace the unicode data with our HTML data. Don't forget the null.
data = utf8HTML;
allocLen = strlen(utf8HTML) + sizeof(char);
}
else {
NS_WARNING ( "Oh no, couldn't convert to HTML" );
return S_OK;
}
}
else {
// we assume that any data that isn't caught above is unicode. This may
// be an erroneous assumption, but is true so far.
allocLen += sizeof(char16_t);
}
hGlobalMemory = (HGLOBAL)GlobalAlloc(GMEM_MOVEABLE, allocLen);
// Copy text to Global Memory Area
if ( hGlobalMemory ) {
char* dest = reinterpret_cast<char*>(GlobalLock(hGlobalMemory));
char* source = reinterpret_cast<char*>(data);
memcpy ( dest, source, allocLen ); // copies the null as well
GlobalUnlock(hGlobalMemory);
}
aSTG.hGlobal = hGlobalMemory;
// Now, delete the memory that was created by CreateDataFromPrimitive (or our text/plain data)
free(data);
return S_OK;
}
//-----------------------------------------------------
HRESULT nsDataObj::GetFile(FORMATETC& aFE, STGMEDIUM& aSTG)
{
uint32_t dfInx = 0;
ULONG count;
FORMATETC fe;
m_enumFE->Reset();
while (NOERROR == m_enumFE->Next(1, &fe, &count)
&& dfInx < mDataFlavors.Length()) {
if (mDataFlavors[dfInx].EqualsLiteral(kNativeImageMime))
return DropImage(aFE, aSTG);
if (mDataFlavors[dfInx].EqualsLiteral(kFileMime))
return DropFile(aFE, aSTG);
if (mDataFlavors[dfInx].EqualsLiteral(kFilePromiseMime))
return DropTempFile(aFE, aSTG);
dfInx++;
}
return E_FAIL;
}
HRESULT nsDataObj::DropFile(FORMATETC& aFE, STGMEDIUM& aSTG)
{
nsresult rv;
uint32_t len = 0;
nsCOMPtr<nsISupports> genericDataWrapper;
mTransferable->GetTransferData(kFileMime, getter_AddRefs(genericDataWrapper),
&len);
nsCOMPtr<nsIFile> file ( do_QueryInterface(genericDataWrapper) );
if (!file)
{
nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
if (ptr) {
nsCOMPtr<nsISupports> supports;
ptr->GetData(getter_AddRefs(supports));
file = do_QueryInterface(supports);
}
}
if (!file)
return E_FAIL;
aSTG.tymed = TYMED_HGLOBAL;
aSTG.pUnkForRelease = nullptr;
nsAutoString path;
rv = file->GetPath(path);
if (NS_FAILED(rv))
return E_FAIL;
uint32_t allocLen = path.Length() + 2;
HGLOBAL hGlobalMemory = nullptr;
char16_t *dest;
hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) +
allocLen * sizeof(char16_t));
if (!hGlobalMemory)
return E_FAIL;
DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
// First, populate the drop file structure
pDropFile->pFiles = sizeof(DROPFILES); //Offset to start of file name string
pDropFile->fNC = 0;
pDropFile->pt.x = 0;
pDropFile->pt.y = 0;
pDropFile->fWide = TRUE;
// Copy the filename right after the DROPFILES structure
dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t));
// Two null characters are needed at the end of the file name.
// Lookup the CF_HDROP shell clipboard format for more info.
// Add the second null character right after the first one.
dest[allocLen - 1] = L'\0';
GlobalUnlock(hGlobalMemory);
aSTG.hGlobal = hGlobalMemory;
return S_OK;
}
HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
{
nsresult rv;
if (!mCachedTempFile) {
uint32_t len = 0;
nsCOMPtr<nsISupports> genericDataWrapper;
mTransferable->GetTransferData(kNativeImageMime, getter_AddRefs(genericDataWrapper), &len);
nsCOMPtr<imgIContainer> image(do_QueryInterface(genericDataWrapper));
if (!image) {
// Check if the image was put in an nsISupportsInterfacePointer wrapper.
// This might not be necessary any more, but could be useful for backwards
// compatibility.
nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
if (ptr) {
nsCOMPtr<nsISupports> supports;
ptr->GetData(getter_AddRefs(supports));
image = do_QueryInterface(supports);
}
}
if (!image)
return E_FAIL;
// Use the clipboard helper class to build up a memory bitmap.
nsImageToClipboard converter(image);
HANDLE bits = nullptr;
rv = converter.GetPicture(&bits); // Clipboard routines return a global handle we own.
if (NS_FAILED(rv) || !bits)
return E_FAIL;
// We now own these bits!
uint32_t bitmapSize = GlobalSize(bits);
if (!bitmapSize) {
GlobalFree(bits);
return E_FAIL;
}
// Save the bitmap to a temporary location.
nsCOMPtr<nsIFile> dropFile;
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
if (!dropFile) {
GlobalFree(bits);
return E_FAIL;
}
// Filename must be random so as not to confuse apps like
// Photoshop which handle multiple drags into a single window.
char buf[13];
nsCString filename;
NS_MakeRandomString(buf, 8);
memcpy(buf+8, ".bmp", 5);
filename.Append(nsDependentCString(buf, 12));
dropFile->AppendNative(filename);
rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
if (NS_FAILED(rv)) {
GlobalFree(bits);
return E_FAIL;
}
// Cache the temp file so we can delete it later and so
// it doesn't get recreated over and over on multiple calls
// which does occur from windows shell.
dropFile->Clone(getter_AddRefs(mCachedTempFile));
// Write the data to disk.
nsCOMPtr<nsIOutputStream> outStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
if (NS_FAILED(rv)) {
GlobalFree(bits);
return E_FAIL;
}
char * bm = (char *)GlobalLock(bits);
BITMAPFILEHEADER fileHdr;
BITMAPINFOHEADER *bmpHdr = (BITMAPINFOHEADER*)bm;
fileHdr.bfType = ((WORD) ('M' << 8) | 'B');
fileHdr.bfSize = GlobalSize (bits) + sizeof(fileHdr);
fileHdr.bfReserved1 = 0;
fileHdr.bfReserved2 = 0;
fileHdr.bfOffBits = (DWORD) (sizeof(fileHdr) + bmpHdr->biSize);
uint32_t writeCount = 0;
if (NS_FAILED(outStream->Write((const char *)&fileHdr, sizeof(fileHdr), &writeCount)) ||
NS_FAILED(outStream->Write((const char *)bm, bitmapSize, &writeCount)))
rv = NS_ERROR_FAILURE;
outStream->Close();
GlobalUnlock(bits);
GlobalFree(bits);
if (NS_FAILED(rv))
return E_FAIL;
}
// Pass the file name back to the drop target so that it can access the file.
nsAutoString path;
rv = mCachedTempFile->GetPath(path);
if (NS_FAILED(rv))
return E_FAIL;
// Two null characters are needed to terminate the file name list.
HGLOBAL hGlobalMemory = nullptr;
uint32_t allocLen = path.Length() + 2;
aSTG.tymed = TYMED_HGLOBAL;
aSTG.pUnkForRelease = nullptr;
hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(char16_t));
if (!hGlobalMemory)
return E_FAIL;
DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
// First, populate the drop file structure.
pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array.
pDropFile->fNC = 0;
pDropFile->pt.x = 0;
pDropFile->pt.y = 0;
pDropFile->fWide = TRUE;
// Copy the filename right after the DROPFILES structure.
char16_t* dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); // Copies the null character in path as well.
// Two null characters are needed at the end of the file name.
// Lookup the CF_HDROP shell clipboard format for more info.
// Add the second null character right after the first one.
dest[allocLen - 1] = L'\0';
GlobalUnlock(hGlobalMemory);
aSTG.hGlobal = hGlobalMemory;
return S_OK;
}
HRESULT nsDataObj::DropTempFile(FORMATETC& aFE, STGMEDIUM& aSTG)
{
nsresult rv;
if (!mCachedTempFile) {
// Tempfile will need a temporary location.
nsCOMPtr<nsIFile> dropFile;
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
if (!dropFile)
return E_FAIL;
// Filename must be random
nsCString filename;
nsAutoString wideFileName;
nsCOMPtr<nsIURI> sourceURI;
HRESULT res;
res = GetDownloadDetails(getter_AddRefs(sourceURI),
wideFileName);
if (FAILED(res))
return res;
NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, filename);
dropFile->AppendNative(filename);
rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
if (NS_FAILED(rv))
return E_FAIL;
// Cache the temp file so we can delete it later and so
// it doesn't get recreated over and over on multiple calls
// which does occur from windows shell.
dropFile->Clone(getter_AddRefs(mCachedTempFile));
// Write the data to disk.
nsCOMPtr<nsIOutputStream> outStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
if (NS_FAILED(rv))
return E_FAIL;
IStream *pStream = nullptr;
nsDataObj::CreateStream(&pStream);
NS_ENSURE_TRUE(pStream, E_FAIL);
char buffer[512];
ULONG readCount = 0;
uint32_t writeCount = 0;
while (1) {
HRESULT hres = pStream->Read(buffer, sizeof(buffer), &readCount);
if (FAILED(hres))
return E_FAIL;
if (readCount == 0)
break;
rv = outStream->Write(buffer, readCount, &writeCount);
if (NS_FAILED(rv))
return E_FAIL;
}
outStream->Close();
pStream->Release();
}
// Pass the file name back to the drop target so that it can access the file.
nsAutoString path;
rv = mCachedTempFile->GetPath(path);
if (NS_FAILED(rv))
return E_FAIL;
uint32_t allocLen = path.Length() + 2;
// Two null characters are needed to terminate the file name list.
HGLOBAL hGlobalMemory = nullptr;
aSTG.tymed = TYMED_HGLOBAL;
aSTG.pUnkForRelease = nullptr;
hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(char16_t));
if (!hGlobalMemory)
return E_FAIL;
DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
// First, populate the drop file structure.
pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array.
pDropFile->fNC = 0;
pDropFile->pt.x = 0;
pDropFile->pt.y = 0;
pDropFile->fWide = TRUE;
// Copy the filename right after the DROPFILES structure.
char16_t* dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); // Copies the null character in path as well.
// Two null characters are needed at the end of the file name.
// Lookup the CF_HDROP shell clipboard format for more info.
// Add the second null character right after the first one.
dest[allocLen - 1] = L'\0';
GlobalUnlock(hGlobalMemory);
aSTG.hGlobal = hGlobalMemory;
return S_OK;
}
//-----------------------------------------------------
HRESULT nsDataObj::GetMetafilePict(FORMATETC&, STGMEDIUM&)
{
return E_NOTIMPL;
}
//-----------------------------------------------------
HRESULT nsDataObj::SetBitmap(FORMATETC&, STGMEDIUM&)
{
return E_NOTIMPL;
}
//-----------------------------------------------------
HRESULT nsDataObj::SetDib(FORMATETC&, STGMEDIUM&)
{
return E_FAIL;
}
//-----------------------------------------------------
HRESULT nsDataObj::SetText (FORMATETC& aFE, STGMEDIUM& aSTG)
{
return E_FAIL;
}
//-----------------------------------------------------
HRESULT nsDataObj::SetMetafilePict(FORMATETC&, STGMEDIUM&)
{
return E_FAIL;
}
//-----------------------------------------------------
//-----------------------------------------------------
CLSID nsDataObj::GetClassID() const
{
return CLSID_nsDataObj;
}
//-----------------------------------------------------
// Registers the DataFlavor/FE pair.
//-----------------------------------------------------
void nsDataObj::AddDataFlavor(const char* aDataFlavor, LPFORMATETC aFE)
{
// These two lists are the mapping to and from data flavors and FEs.
// Later, OLE will tell us it needs a certain type of FORMATETC (text, unicode, etc)
// unicode, etc), so we will look up the data flavor that corresponds to
// the FE and then ask the transferable for that type of data.
mDataFlavors.AppendElement(aDataFlavor);
m_enumFE->AddFormatEtc(aFE);
}
//-----------------------------------------------------
// Sets the transferable object
//-----------------------------------------------------
void nsDataObj::SetTransferable(nsITransferable * aTransferable)
{
NS_IF_RELEASE(mTransferable);
mTransferable = aTransferable;
if (nullptr == mTransferable) {
return;
}
NS_ADDREF(mTransferable);
return;
}
//
// ExtractURL
//
// Roots around in the transferable for the appropriate flavor that indicates
// a url and pulls out the url portion of the data. Used mostly for creating
// internet shortcuts on the desktop. The url flavor is of the format:
//
// <url> <linefeed> <page title>
//
nsresult
nsDataObj :: ExtractShortcutURL ( nsString & outURL )
{
NS_ASSERTION ( mTransferable, "We don't have a good transferable" );
nsresult rv = NS_ERROR_FAILURE;
uint32_t len = 0;
nsCOMPtr<nsISupports> genericURL;
if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
if ( urlObject ) {
nsAutoString url;
urlObject->GetData ( url );
outURL = url;
// find the first linefeed in the data, that's where the url ends. trunc the
// result string at that point.
int32_t lineIndex = outURL.FindChar ( '\n' );
NS_ASSERTION ( lineIndex > 0, "Format for url flavor is <url> <linefeed> <page title>" );
if ( lineIndex > 0 ) {
outURL.Truncate ( lineIndex );
rv = NS_OK;
}
}
} else if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLDataMime, getter_AddRefs(genericURL), &len)) ||
NS_SUCCEEDED(mTransferable->GetTransferData(kURLPrivateMime, getter_AddRefs(genericURL), &len)) ) {
nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
if ( urlObject ) {
nsAutoString url;
urlObject->GetData ( url );
outURL = url;
rv = NS_OK;
}
} // if found flavor
return rv;
} // ExtractShortcutURL
//
// ExtractShortcutTitle
//
// Roots around in the transferable for the appropriate flavor that indicates
// a url and pulls out the title portion of the data. Used mostly for creating
// internet shortcuts on the desktop. The url flavor is of the format:
//
// <url> <linefeed> <page title>
//
nsresult
nsDataObj :: ExtractShortcutTitle ( nsString & outTitle )
{
NS_ASSERTION ( mTransferable, "We'd don't have a good transferable" );
nsresult rv = NS_ERROR_FAILURE;
uint32_t len = 0;
nsCOMPtr<nsISupports> genericURL;
if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
if ( urlObject ) {
nsAutoString url;
urlObject->GetData ( url );
// find the first linefeed in the data, that's where the url ends. we want
// everything after that linefeed. FindChar() returns -1 if we can't find
int32_t lineIndex = url.FindChar ( '\n' );
NS_ASSERTION ( lineIndex != -1, "Format for url flavor is <url> <linefeed> <page title>" );
if ( lineIndex != -1 ) {
url.Mid ( outTitle, lineIndex + 1, (len/2) - (lineIndex + 1) );
rv = NS_OK;
}
}
} // if found flavor
return rv;
} // ExtractShortcutTitle
//
// BuildPlatformHTML
//
// Munge our HTML data to win32's CF_HTML spec. Basically, put the requisite
// header information on it. This will null terminate |outPlatformHTML|. See
// http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
// for details.
//
// We assume that |inOurHTML| is already a fragment (ie, doesn't have <HTML>
// or <BODY> tags). We'll wrap the fragment with them to make other apps
// happy.
//
nsresult
nsDataObj :: BuildPlatformHTML ( const char* inOurHTML, char** outPlatformHTML )
{
*outPlatformHTML = nullptr;
nsDependentCString inHTMLString(inOurHTML);
const char* const numPlaceholder = "00000000";
const char* const startHTMLPrefix = "Version:0.9\r\nStartHTML:";
const char* const endHTMLPrefix = "\r\nEndHTML:";
const char* const startFragPrefix = "\r\nStartFragment:";
const char* const endFragPrefix = "\r\nEndFragment:";
const char* const startSourceURLPrefix = "\r\nSourceURL:";
const char* const endFragTrailer = "\r\n";
// Do we already have mSourceURL from a drag?
if (mSourceURL.IsEmpty()) {
nsAutoString url;
ExtractShortcutURL(url);
AppendUTF16toUTF8(url, mSourceURL);
}
const int32_t kSourceURLLength = mSourceURL.Length();
const int32_t kNumberLength = strlen(numPlaceholder);
const int32_t kTotalHeaderLen = strlen(startHTMLPrefix) +
strlen(endHTMLPrefix) +
strlen(startFragPrefix) +
strlen(endFragPrefix) +
strlen(endFragTrailer) +
(kSourceURLLength > 0 ? strlen(startSourceURLPrefix) : 0) +
kSourceURLLength +
(4 * kNumberLength);
NS_NAMED_LITERAL_CSTRING(htmlHeaderString, "<html><body>\r\n");
NS_NAMED_LITERAL_CSTRING(fragmentHeaderString, "<!--StartFragment-->");
nsDependentCString trailingString(
"<!--EndFragment-->\r\n"
"</body>\r\n"
"</html>");
// calculate the offsets
int32_t startHTMLOffset = kTotalHeaderLen;
int32_t startFragOffset = startHTMLOffset
+ htmlHeaderString.Length()
+ fragmentHeaderString.Length();
int32_t endFragOffset = startFragOffset
+ inHTMLString.Length();
int32_t endHTMLOffset = endFragOffset
+ trailingString.Length();
// now build the final version
nsCString clipboardString;
clipboardString.SetCapacity(endHTMLOffset);
clipboardString.Append(startHTMLPrefix);
clipboardString.Append(nsPrintfCString("%08u", startHTMLOffset));
clipboardString.Append(endHTMLPrefix);
clipboardString.Append(nsPrintfCString("%08u", endHTMLOffset));
clipboardString.Append(startFragPrefix);
clipboardString.Append(nsPrintfCString("%08u", startFragOffset));
clipboardString.Append(endFragPrefix);
clipboardString.Append(nsPrintfCString("%08u", endFragOffset));
if (kSourceURLLength > 0) {
clipboardString.Append(startSourceURLPrefix);
clipboardString.Append(mSourceURL);
}
clipboardString.Append(endFragTrailer);
clipboardString.Append(htmlHeaderString);
clipboardString.Append(fragmentHeaderString);
clipboardString.Append(inHTMLString);
clipboardString.Append(trailingString);
*outPlatformHTML = ToNewCString(clipboardString);
if (!*outPlatformHTML)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
HRESULT
nsDataObj :: GetUniformResourceLocator( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode )
{
HRESULT res = S_OK;
if (IsFlavourPresent(kURLMime)) {
if ( aIsUnicode )
res = ExtractUniformResourceLocatorW( aFE, aSTG );
else
res = ExtractUniformResourceLocatorA( aFE, aSTG );
}
else
NS_WARNING ("Not yet implemented\n");
return res;
}
HRESULT
nsDataObj::ExtractUniformResourceLocatorA(FORMATETC& aFE, STGMEDIUM& aSTG )
{
HRESULT result = S_OK;
nsAutoString url;
if (NS_FAILED(ExtractShortcutURL(url)))
return E_OUTOFMEMORY;
NS_LossyConvertUTF16toASCII asciiUrl(url);
const int totalLen = asciiUrl.Length() + 1;
HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
if (!hGlobalMemory)
return E_OUTOFMEMORY;
char* contents = reinterpret_cast<char*>(GlobalLock(hGlobalMemory));
if (!contents) {
GlobalFree(hGlobalMemory);
return E_OUTOFMEMORY;
}
strcpy(contents, asciiUrl.get());
GlobalUnlock(hGlobalMemory);
aSTG.hGlobal = hGlobalMemory;
aSTG.tymed = TYMED_HGLOBAL;
return result;
}
HRESULT
nsDataObj::ExtractUniformResourceLocatorW(FORMATETC& aFE, STGMEDIUM& aSTG )
{
HRESULT result = S_OK;
nsAutoString url;
if (NS_FAILED(ExtractShortcutURL(url)))
return E_OUTOFMEMORY;
const int totalLen = (url.Length() + 1) * sizeof(char16_t);
HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
if (!hGlobalMemory)
return E_OUTOFMEMORY;
wchar_t* contents = reinterpret_cast<wchar_t*>(GlobalLock(hGlobalMemory));
if (!contents) {
GlobalFree(hGlobalMemory);
return E_OUTOFMEMORY;
}
wcscpy(contents, url.get());
GlobalUnlock(hGlobalMemory);
aSTG.hGlobal = hGlobalMemory;
aSTG.tymed = TYMED_HGLOBAL;
return result;
}
// Gets the filename from the kFilePromiseURLMime flavour
HRESULT nsDataObj::GetDownloadDetails(nsIURI **aSourceURI,
nsAString &aFilename)
{
*aSourceURI = nullptr;
NS_ENSURE_TRUE(mTransferable, E_FAIL);
// get the URI from the kFilePromiseURLMime flavor
nsCOMPtr<nsISupports> urlPrimitive;
uint32_t dataSize = 0;
mTransferable->GetTransferData(kFilePromiseURLMime, getter_AddRefs(urlPrimitive), &dataSize);
nsCOMPtr<nsISupportsString> srcUrlPrimitive = do_QueryInterface(urlPrimitive);
NS_ENSURE_TRUE(srcUrlPrimitive, E_FAIL);
nsAutoString srcUri;
srcUrlPrimitive->GetData(srcUri);
if (srcUri.IsEmpty())
return E_FAIL;
nsCOMPtr<nsIURI> sourceURI;
NS_NewURI(getter_AddRefs(sourceURI), srcUri);
nsAutoString srcFileName;
nsCOMPtr<nsISupports> fileNamePrimitive;
mTransferable->GetTransferData(kFilePromiseDestFilename, getter_AddRefs(fileNamePrimitive), &dataSize);
nsCOMPtr<nsISupportsString> srcFileNamePrimitive = do_QueryInterface(fileNamePrimitive);
if (srcFileNamePrimitive) {
srcFileNamePrimitive->GetData(srcFileName);
} else {
nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
if (!sourceURL)
return E_FAIL;
nsAutoCString urlFileName;
sourceURL->GetFileName(urlFileName);
NS_UnescapeURL(urlFileName);
CopyUTF8toUTF16(urlFileName, srcFileName);
}
if (srcFileName.IsEmpty())
return E_FAIL;
// make the name safe for the filesystem
MangleTextToValidFilename(srcFileName);
sourceURI.swap(*aSourceURI);
aFilename = srcFileName;
return S_OK;
}
HRESULT nsDataObj::GetFileDescriptor_IStreamA(FORMATETC& aFE, STGMEDIUM& aSTG)
{
HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY);
LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(GlobalLock(fileGroupDescHandle));
if (!fileGroupDescA) {
::GlobalFree(fileGroupDescHandle);
return E_OUTOFMEMORY;
}
nsAutoString wideFileName;
HRESULT res;
nsCOMPtr<nsIURI> sourceURI;
res = GetDownloadDetails(getter_AddRefs(sourceURI), wideFileName);
if (FAILED(res))
{
::GlobalFree(fileGroupDescHandle);
return res;
}
nsAutoCString nativeFileName;
NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, nativeFileName);
strncpy(fileGroupDescA->fgd[0].cFileName, nativeFileName.get(), NS_MAX_FILEDESCRIPTOR - 1);
fileGroupDescA->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0';
// one file in the file block
fileGroupDescA->cItems = 1;
fileGroupDescA->fgd[0].dwFlags = FD_PROGRESSUI;
GlobalUnlock( fileGroupDescHandle );
aSTG.hGlobal = fileGroupDescHandle;
aSTG.tymed = TYMED_HGLOBAL;
return S_OK;
}
HRESULT nsDataObj::GetFileDescriptor_IStreamW(FORMATETC& aFE, STGMEDIUM& aSTG)
{
HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY);
LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(GlobalLock(fileGroupDescHandle));
if (!fileGroupDescW) {
::GlobalFree(fileGroupDescHandle);
return E_OUTOFMEMORY;
}
nsAutoString wideFileName;
HRESULT res;
nsCOMPtr<nsIURI> sourceURI;
res = GetDownloadDetails(getter_AddRefs(sourceURI),
wideFileName);
if (FAILED(res))
{
::GlobalFree(fileGroupDescHandle);
return res;
}
wcsncpy(fileGroupDescW->fgd[0].cFileName, wideFileName.get(), NS_MAX_FILEDESCRIPTOR - 1);
fileGroupDescW->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0';
// one file in the file block
fileGroupDescW->cItems = 1;
fileGroupDescW->fgd[0].dwFlags = FD_PROGRESSUI;
GlobalUnlock(fileGroupDescHandle);
aSTG.hGlobal = fileGroupDescHandle;
aSTG.tymed = TYMED_HGLOBAL;
return S_OK;
}
HRESULT nsDataObj::GetFileContents_IStream(FORMATETC& aFE, STGMEDIUM& aSTG)
{
IStream *pStream = nullptr;
nsDataObj::CreateStream(&pStream);
NS_ENSURE_TRUE(pStream, E_FAIL);
aSTG.tymed = TYMED_ISTREAM;
aSTG.pstm = pStream;
aSTG.pUnkForRelease = nullptr;
return S_OK;
}
void nsDataObj::RemoveTempFile(nsITimer* aTimer, void* aClosure)
{
nsDataObj *timedDataObj = static_cast<nsDataObj *>(aClosure);
if (timedDataObj->mCachedTempFile) {
timedDataObj->mCachedTempFile->Remove(false);
timedDataObj->mCachedTempFile = nullptr;
}
timedDataObj->Release();
}