/* -*- 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/. */
//----------------------------------------------------------------------------- // 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, nsIInputStream* aInputStream,
uint64_t aOffset, // offset within the stream
uint32_t aCount) // bytes available on this call
{ // If we've been asked to read zero bytes, call `Read` once, just to ensure // any side-effects take place, and return immediately. if (aCount == 0) { char buffer[1] = {0};
uint32_t bytesReadByCall = 0;
nsresult rv = aInputStream->Read(buffer, 0, &bytesReadByCall);
MOZ_ASSERT(bytesReadByCall == 0); return rv;
}
// Extend the write buffer for the incoming data.
size_t oldLength = mChannelData.Length(); char* buffer = reinterpret_cast<char*>(mChannelData.AppendElements(aCount, fallible)); if (!buffer) { return NS_ERROR_OUT_OF_MEMORY;
}
MOZ_ASSERT(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.
uint32_t bytesRead = 0; while (bytesRead < aCount) {
uint32_t bytesReadByCall = 0;
nsresult rv = aInputStream->Read(buffer + bytesRead, aCount - bytesRead,
&bytesReadByCall);
bytesRead += bytesReadByCall;
if (bytesReadByCall == 0) { // A `bytesReadByCall` of zero indicates EOF without failure... but we // were promised `aCount` elements and haven't gotten them. Return a // generic failure.
rv = NS_ERROR_FAILURE;
}
if (NS_FAILED(rv)) { // Drop any trailing uninitialized elements before erroring out.
mChannelData.RemoveElementsAt(oldLength + bytesRead, aCount - bytesRead); return rv;
}
} 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.
SpinEventLoopUntil("widget:nsDataObj::CStream::WaitForCompletion"_ns,
[&]() { return mChannelRead; });
if (!mChannelData.Length()) mChannelResult = NS_ERROR_FAILURE;
//-----------------------------------------------------------------------------
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;
}
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 dataPrincipal from the transferable and add it to the new // channel.
nsCOMPtr<nsIPrincipal> requestingPrincipal =
mTransferable->GetDataPrincipal();
MOZ_ASSERT(requestingPrincipal, "can not create channel without a principal");
// Note that the cookieJarSettings could be null if the data object is for the // image copy. We will fix this in Bug 1690532.
nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
mTransferable->GetCookieJarSettings();
// The referrer is optional.
nsCOMPtr<nsIReferrerInfo> referrerInfo = mTransferable->GetReferrerInfo();
if (nullptr != *ppvResult) {
((LPUNKNOWN)*ppvResult)->AddRef(); return S_OK;
}
return E_NOINTERFACE;
}
void nsDataObj::CMemStream::WaitForCompletion() { if (!mEvent) { // We are not waiting for obtaining the icon cache. return;
} if (!NS_IsMainThread()) {
mEvent->Wait(INFINITE);
} else { // We should not block the main thread.
mEvent->Signal();
} // mEvent will always be in the signaled state here.
}
//----------------------------------------------------------------------------- // IStream
STDMETHODIMP nsDataObj::CMemStream::Read(void* pvBuffer, ULONG nBytesToRead,
ULONG* nBytesRead) { // Wait until the event is signaled.
WaitForCompletion();
// Bytes left for Windows to read out of our buffer
ULONG bytesLeft = mTotalLength - 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, contents + mStreamRead, *nBytesRead); // Update our bytes read tracking
mStreamRead += *nBytesRead;
//----------------------------------------------------- // construction //-----------------------------------------------------
nsDataObj::nsDataObj(nsIURI* uri)
: m_cRef(0),
mTransferable(nullptr),
mIsAsyncMode(FALSE),
mIsInOperation(FALSE) {
mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, "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() {
mTransferable = nullptr;
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;
// When the first reference is taken, hold our own internal reference. if (m_cRef == 1) {
mKeepAlive = this;
}
return m_cRef;
}
namespace { class RemoveTempFileHelper final : public nsIObserver, public nsINamed { public: explicit RemoveTempFileHelper(nsIFile* aTempFile) : mTempFile(aTempFile) {
MOZ_ASSERT(mTempFile);
}
// The attach method is seperate from the constructor as we may be addref-ing // ourself, and we want to be sure someone has a strong reference to us. void Attach() { // We need to listen to both the xpcom shutdown message and our timer, and // fire when the first of either of these two messages is received.
nsresult rv;
rv = NS_NewTimerWithObserver(getter_AddRefs(mTimer), this, 500,
nsITimer::TYPE_ONE_SHOT); if (NS_WARN_IF(NS_FAILED(rv))) { return;
}
NS_IMETHODIMP
RemoveTempFileHelper::Observe(nsISupports* aSubject, constchar* aTopic, const char16_t* aData) { // Let's be careful and make sure that we don't die immediately
RefPtr<RemoveTempFileHelper> grip = this;
// Make sure that we aren't called again by destroying references to ourself.
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1"); if (observerService) {
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
}
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
// Remove the tempfile if (mTempFile) {
mTempFile->Remove(false);
mTempFile = nullptr;
} return NS_OK;
}
// If we hold the last reference, submit release of it to the main thread. if (m_cRef == 1 && mKeepAlive) {
NS_ReleaseOnMainThread("nsDataObj release", mKeepAlive.forget(), true);
}
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. if (mCachedTempFile) {
RefPtr<RemoveTempFileHelper> helper = new RemoveTempFileHelper(mCachedTempFile);
mCachedTempFile = nullptr;
helper->Attach();
}
// In case the destructor ever AddRef/Releases, ensure we don't delete twice // or take mKeepAlive as another reference.
m_cRef = 1;
// 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 const& df = mDataFlavors.ElementAt(dfInx); if (FormatsMatch(fe, *aFormat)) {
pSTM->pUnkForRelease =
nullptr; // caller is responsible for deleting this data
CLIPFORMAT const format = aFormat->cfFormat;
// compile-time-constant format indicators: 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, DibType::Bmp);
default: /* fallthrough */;
} // switch
// non-compile-time-constant format indicators: if (format == imagePNGFormat) return GetDib(df, *aFormat, *pSTM, DibType::Png); 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, LogLevel::Info, // ("***** nsDataObj::GetData - Unknown format %u\n", format)); return GetText(df, *aFormat, *pSTM);
} // if
dfInx++;
} // while
//----------------------------------------------------- // 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;
//-----------------------------------------------------
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;
// 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]; returntrue;
} else { // Medium does not match, not found. returnfalse;
}
}
}
if (!aAddorUpdate) returnfalse;
// Add another entry to mDataEntryList
LPDATAENTRY dataEntry = (LPDATAENTRY)CoTaskMemAlloc(sizeof(DATAENTRY)); if (!dataEntry) returnfalse;
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) returnfalse;
stgmOut.hGlobal =
OleDuplicateData(aMediumSrc->hGlobal, aFormat->cfFormat, 0); if (!stgmOut.hGlobal) returnfalse;
} 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: returnfalse;
}
if (stgmOut.pUnkForRelease) stgmOut.pUnkForRelease->AddRef();
*aMediumDst = stgmOut;
returntrue;
}
//-----------------------------------------------------
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;
}
// // 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, DibType aDibType) {
nsCOMPtr<nsISupports> genericDataWrapper; if (NS_FAILED(
mTransferable->GetTransferData(PromiseFlatCString(inFlavor).get(),
getter_AddRefs(genericDataWrapper)))) { return E_FAIL;
}
nsCOMPtr<imgIContainer> image = do_QueryInterface(genericDataWrapper); if (!image) { return E_FAIL;
}
// 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);
} elseif (IsFlavourPresent(kURLMime)) { if (aIsUnicode)
res = GetFileDescriptorInternetShortcutW(aFE, aSTG); else
res = GetFileDescriptorInternetShortcutA(aFE, aSTG);
} else
NS_WARNING("Not yet implemented\n");
// 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); elseif (IsFlavourPresent(kURLMime)) return GetFileContentsInternetShortcut(aFE, aSTG); else
NS_WARNING("Not yet implemented\n");
return res;
} // GetFileContents
// Ensure that the supplied name doesn't have invalid characters. staticvoid ValidateFilename(nsString& aFilename, bool isShortcut) {
nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1"); if (NS_WARN_IF(!mimeService)) {
aFilename.Truncate(); return;
}
// // Given a unicode string, convert it to a valid local charset filename // and append the .url extension to be used for a shortcut file. // 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. // staticbool CreateURLFilenameFromTextA(nsAutoString& aText, char* aFilename) { if (aText.IsEmpty()) { returnfalse;
}
aText.AppendLiteral(".url");
ValidateFilename(aText, true); if (aText.IsEmpty()) { returnfalse;
}
// ValidateFilename should already be checking the filename length, but do // an extra check to verify for the local code page that the converted text // doesn't go over MAX_PATH and just return false if it does. char defaultChar = '_'; int currLen = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
aText.get(), -1, aFilename, MAX_PATH,
&defaultChar, nullptr); return currLen != 0;
}
// Wide character version of CreateURLFilenameFromTextA staticbool CreateURLFilenameFromTextW(nsAutoString& aText, wchar_t* aFilename) { if (aText.IsEmpty()) { returnfalse;
}
aText.AppendLiteral(".url");
ValidateFilename(aText, true); if (aText.IsEmpty() || aText.Length() >= MAX_PATH) { returnfalse;
}
// // 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;
// 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 (!CreateURLFilenameFromTextA(title, fileGroupDescA->fgd[0].cFileName)) {
nsAutoString untitled; if (!GetLocalizedString("noPageTitle", untitled) ||
!CreateURLFilenameFromTextA(untitled,
fileGroupDescA->fgd[0].cFileName)) {
strcpy(fileGroupDescA->fgd[0].cFileName, "Untitled.url");
}
}
// one file in the file block
fileGroupDescA->cItems = 1;
fileGroupDescA->fgd[0].dwFlags = FD_LINKUI;
HRESULT
nsDataObj ::GetFileDescriptorInternetShortcutW(FORMATETC& aFE,
STGMEDIUM& aSTG) { // get the title of the shortcut
nsAutoString title; if (NS_FAILED(ExtractShortcutTitle(title))) 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 (!CreateURLFilenameFromTextW(title, fileGroupDescW->fgd[0].cFileName)) {
nsAutoString untitled; if (!GetLocalizedString("noPageTitle", untitled) ||
!CreateURLFilenameFromTextW(untitled,
fileGroupDescW->fgd[0].cFileName)) {
wcscpy(fileGroupDescW->fgd[0].cFileName, L"Untitled.url");
}
}
// one file in the file block
fileGroupDescW->cItems = 1;
fileGroupDescW->fgd[0].dwFlags = FD_LINKUI;
// // 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) { staticconstchar* kShellIconPref = "browser.shell.shortcutFavicons";
nsAutoString url; if (NS_FAILED(ExtractShortcutURL(url))) return E_OUTOFMEMORY;
constchar* shortcutFormatStr; int totalLen;
nsCString asciiPath; if (!Preferences::GetBool(kShellIconPref, true)) {
shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n"; constint formatLen = strlen(shortcutFormatStr) - 2; // don't include %s
totalLen = formatLen + asciiUrl.Length(); // don't include null character
} else {
nsCOMPtr<nsIFile> icoFile;
nsAutoString aUriHash;
event = new AutoCloseEvent(); if (!event->IsInited()) { return E_FAIL;
}
RefPtr<AutoSetEvent> e = new AutoSetEvent(WrapNotNull(event));
mozilla::widget::FaviconHelper::ObtainCachedIconFile(
aUri, aUriHash, mIOThread, true,
NS_NewRunnableFunction( "FaviconHelper::RefreshDesktop", [e = std::move(e)] { if (e->IsWaiting()) { // Unblock IStream:::Read.
e->Signal();
} else { // We could not wait until the favicon was available. We have // to refresh to refect the favicon.
SendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE,
SPI_SETNONCLIENTMETRICS, 0);
}
}));
if (IsAsciiNullTerminated(static_cast<const char16_t*>(path.get()))) {
LossyCopyUTF16toASCII(path, asciiPath);
shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n" "IDList=\r\nHotKey=0\r\nIconFile=%s\r\n" "IconIndex=0\r\n";
} else { int len =
WideCharToMultiByte(CP_UTF7, 0, char16ptr_t(path.BeginReading()),
path.Length(), nullptr, 0, nullptr, nullptr);
NS_ENSURE_TRUE(len > 0, E_FAIL);
asciiPath.SetLength(len);
WideCharToMultiByte(CP_UTF7, 0, char16ptr_t(path.BeginReading()),
path.Length(), asciiPath.BeginWriting(), len, nullptr,
nullptr);
shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n" "IDList=\r\nHotKey=0\r\nIconIndex=0\r\n" "[InternetShortcut.W]\r\nIconFile=%s\r\n";
} constint formatLen = strlen(shortcutFormatStr) - 2 * 2; // no %s twice
totalLen = formatLen + asciiUrl.Length() +
asciiPath.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
nsAutoGlobalMem globalMem(nsHGLOBAL(::GlobalAlloc(GMEM_SHARE, totalLen))); if (!globalMem) return E_OUTOFMEMORY;
char* contents = reinterpret_cast<char*>(::GlobalLock(globalMem.get())); if (!contents) { 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 (aFE.tymed & TYMED_ISTREAM) { if (!mIsInOperation) { // The drop target didn't initiate an async operation. // We can't block CMemStream::Read.
event = nullptr;
}
RefPtr<IStream> stream = new CMemStream(globalMem.disown(), totalLen, event.forget());
stream.forget(&aSTG.pstm);
aSTG.tymed = TYMED_ISTREAM;
} else { if (event && event->IsInited()) {
event->Signal(); // We can't block reading the global memory
}
aSTG.hGlobal = globalMem.disown();
aSTG.tymed = TYMED_HGLOBAL;
}
return S_OK;
} // GetFileContentsInternetShortcut
// check if specified flavour is present in the transferable bool nsDataObj ::IsFlavourPresent(constchar* inFlavour) { bool retval = false;
NS_ENSURE_TRUE(mTransferable, false);
// get the list of flavors available in the transferable
nsTArray<nsCString> flavors;
nsresult rv = mTransferable->FlavorsTransferableCanExport(flavors);
NS_ENSURE_SUCCESS(rv, false);
// try to find requested flavour for (uint32_t i = 0; i < flavors.Length(); ++i) { if (flavors[i].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;
}
// 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;
}
} elseif (aFE.cfFormat == nsClipboard::GetHtmlClipboardFormat()) { // 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;
}
} elseif (aFE.cfFormat != nsClipboard::GetCustomClipboardFormat()) { // 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);
}
// 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);
// 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';
// Select the image encoding. // // If we get here, the negotiation phase selected "CF_HDROP"... which // unfortunately means "file", rather than something more useful like "file // of type XYZ". As of 2024, though, it seems that pretty much everything in // the ecosystem understands PNG, so we just default to that (with a config // pref to enable fallback to BMP for older recipients).
nsCString extension; if (StaticPrefs::clipboard_copy_image_file_as_png()) {
extension = ".png"_ns;
rv = imgTools->EncodeImage(image, nsLiteralCString(IMAGE_PNG), u""_ns,
getter_AddRefs(inputStream));
} else {
extension = ".bmp"_ns;
rv = imgTools->EncodeImage(image, nsLiteralCString(IMAGE_BMP),
u"bpp=32;version=3"_ns,
getter_AddRefs(inputStream));
} if (NS_FAILED(rv) || !inputStream) { return E_FAIL;
}
nsCOMPtr<imgIEncoder> encoder = do_QueryInterface(inputStream); if (!encoder) { 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) { 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[9];
NS_MakeRandomString(buf, 8);
nsCString filename{buf};
filename.Append(extension);
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;
}
uint32_t written = 0;
rv = outStream->Write(src, size, &written); if (NS_FAILED(rv) || written != size) { return E_FAIL;
}
outStream->Close();
}
// 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;
// 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_CopyUnicodeToNative(wideFileName, 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;
// 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;
// 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;
}
//----------------------------------------------------- // Registers the DataFlavor/FE pair. //----------------------------------------------------- void nsDataObj::AddDataFlavor(constchar* 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);
}
// // 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;
nsCOMPtr<nsISupports> genericURL; if (NS_SUCCEEDED(mTransferable->GetTransferData(
kURLMime, getter_AddRefs(genericURL)))) {
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 "); if (lineIndex > 0) {
outURL.Truncate(lineIndex);
rv = NS_OK;
}
}
} elseif (NS_SUCCEEDED(mTransferable->GetTransferData(
kURLDataMime, getter_AddRefs(genericURL))) ||
NS_SUCCEEDED(mTransferable->GetTransferData(
kURLPrivateMime, getter_AddRefs(genericURL)))) {
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;
nsCOMPtr<nsISupports> genericURL; if (NS_SUCCEEDED(mTransferable->GetTransferData(
kURLMime, getter_AddRefs(genericURL)))) {
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 "); if (lineIndex != -1) {
url.Mid(outTitle, lineIndex + 1, url.Length() - (lineIndex + 1));
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.67 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.