Bug 1317322 - Part 2: Fix shutdown leak when win32 holds nsDataObj with temp file until shutdown, r=jimm

MozReview-Commit-ID: 90obWnqRSWk
This commit is contained in:
Michael Layzell 2017-02-24 14:38:09 -05:00
Родитель 165c0a9513
Коммит add4f2919d
3 изменённых файлов: 81 добавлений и 21 удалений

Просмотреть файл

@ -65,7 +65,7 @@ nsClipboard::nsClipboard() : nsBaseClipboard()
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1");
if (observerService) {
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
observerService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, PR_FALSE);
}
}

Просмотреть файл

@ -441,6 +441,82 @@ STDMETHODIMP_(ULONG) nsDataObj::AddRef()
return m_cRef;
}
namespace {
class RemoveTempFileHelper : public nsIObserver
{
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;
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
mTimer->Init(this, 500, nsITimer::TYPE_ONE_SHOT);
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1");
if (NS_WARN_IF(!observerService)) {
mTimer->Cancel();
mTimer = nullptr;
return;
}
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
}
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
private:
~RemoveTempFileHelper()
{
if (mTempFile) {
mTempFile->Remove(false);
}
}
nsCOMPtr<nsIFile> mTempFile;
nsCOMPtr<nsITimer> mTimer;
};
NS_IMPL_ISUPPORTS(RemoveTempFileHelper, nsIObserver);
NS_IMETHODIMP
RemoveTempFileHelper::Observe(nsISupports* aSubject, const char* 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;
}
} // namespace
//-----------------------------------------------------
STDMETHODIMP_(ULONG) nsDataObj::Release()
@ -454,17 +530,12 @@ STDMETHODIMP_(ULONG) nsDataObj::Release()
// 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.
// this object.
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);
RefPtr<RemoveTempFileHelper> helper =
new RemoveTempFileHelper(mCachedTempFile);
mCachedTempFile = nullptr;
helper->Attach();
}
delete this;
@ -2151,13 +2222,3 @@ HRESULT nsDataObj::GetFileContents_IStream(FORMATETC& aFE, STGMEDIUM& aSTG)
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();
}

Просмотреть файл

@ -289,7 +289,6 @@ protected:
bool LookupArbitraryFormat(FORMATETC *aFormat, LPDATAENTRY *aDataEntry, BOOL aAddorUpdate);
bool CopyMediumData(STGMEDIUM *aMediumDst, STGMEDIUM *aMediumSrc, LPFORMATETC aFormat, BOOL aSetData);
static void RemoveTempFile(nsITimer* aTimer, void* aClosure);
};