Add code controlled by a persist flag to delete files and dirs if saveDocument returns with an error code. b=129332 r=brade@netscape.com sr=jaggernaut@netscape.com

This commit is contained in:
locka%iol.ie 2003-05-30 11:42:25 +00:00
Родитель 2ede35f2c3
Коммит 4ad2187d3d
3 изменённых файлов: 174 добавлений и 8 удалений

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

@ -66,6 +66,13 @@ interface nsIWebBrowserPersist : nsISupports
const unsigned long PERSIST_FLAGS_DONT_CHANGE_FILENAMES = 2048;
/** Fail on broken inline links */
const unsigned long PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS = 4096;
/**
* Automatically cleanup after a failed or cancelled operation, deleting all
* created files and directories. This flag does nothing for failed upload
* operations to remote servers.
*/
const unsigned long PERSIST_FLAGS_CLEANUP_ON_FAILURE = 8192;
/**
* Flags governing how data is fetched and saved from the network.

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

@ -40,6 +40,7 @@
#include "nsEscape.h"
#include "nsUnicharUtils.h"
#include "nsCRT.h"
#include "nsSupportsArray.h"
#include "nsCExternalHandlerService.h"
@ -156,6 +157,15 @@ struct UploadData
}
};
struct CleanupData
{
nsCOMPtr<nsILocalFile> mFile;
// Snapshot of what the file actually is at the time of creation so that if
// it transmutes into something else later on it can be ignored. For example,
// catch files that turn into dirs or vice versa.
PRPackedBool mIsDirectory;
};
// Maximum file length constant. The max file name length is
// volume / server dependent but it is difficult to obtain
// that information. Instead this constant is a reasonable value that
@ -212,7 +222,7 @@ nsWebBrowserPersist::nsWebBrowserPersist() :
nsWebBrowserPersist::~nsWebBrowserPersist()
{
CleanUp();
Cleanup();
}
//*****************************************************************************
@ -1515,17 +1525,33 @@ nsresult nsWebBrowserPersist::SaveDocumentInternal(
{
if (localDataPath)
{
localDataPath->Create(nsILocalFile::DIRECTORY_TYPE, 0755);
PRBool exists = PR_FALSE;
PRBool isDirectory = PR_FALSE;
PRBool haveDir = PR_FALSE;
localDataPath->Exists(&exists);
localDataPath->IsDirectory(&isDirectory);
if (!exists || !isDirectory)
if (exists)
{
localDataPath->IsDirectory(&haveDir);
}
if (!haveDir && NS_SUCCEEDED(localDataPath->Create(nsILocalFile::DIRECTORY_TYPE, 0755)))
{
haveDir = PR_TRUE;
}
if (!haveDir)
{
EndDownload(NS_ERROR_FAILURE);
mCurrentBaseURI = oldBaseURI;
return NS_ERROR_FAILURE;
}
if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE)
{
// Add to list of things to delete later if all goes wrong
CleanupData *cleanupData = new CleanupData;
NS_ENSURE_TRUE(cleanupData, NS_ERROR_OUT_OF_MEMORY);
cleanupData->mFile = localDataPath;
cleanupData->mIsDirectory = PR_TRUE;
mCleanupList.AppendElement(cleanupData);
}
}
}
@ -1643,7 +1669,7 @@ nsresult nsWebBrowserPersist::SaveDocuments()
return rv;
}
void nsWebBrowserPersist::CleanUp()
void nsWebBrowserPersist::Cleanup()
{
mURIMap.Enumerate(EnumCleanupURIMap, this);
mURIMap.Reset();
@ -1658,9 +1684,124 @@ void nsWebBrowserPersist::CleanUp()
delete docData;
}
mDocList.Clear();
for (i = 0; i < mCleanupList.Count(); i++)
{
CleanupData *cleanupData = (CleanupData *) mCleanupList.ElementAt(i);
delete cleanupData;
}
mCleanupList.Clear();
mFilenameList.Clear();
}
void nsWebBrowserPersist::CleanupLocalFiles()
{
// Two passes, the first pass cleans up files, the second pass tests
// for and then deletes empty directories. Directories that are not
// empty after the first pass must contain files from something else
// and are not deleted.
int pass;
for (pass = 0; pass < 2; pass++)
{
PRUint32 i;
for (i = 0; i < mCleanupList.Count(); i++)
{
CleanupData *cleanupData = (CleanupData *) mCleanupList.ElementAt(i);
nsCOMPtr<nsILocalFile> file = cleanupData->mFile;
// Test if the dir / file exists (something in an earlier loop
// may have already removed it)
PRBool exists = PR_FALSE;
file->Exists(&exists);
if (!exists)
continue;
// Test if the file has changed in between creation and deletion
// in some way that means it should be ignored
PRBool isDirectory = PR_FALSE;
file->IsDirectory(&isDirectory);
if (isDirectory != cleanupData->mIsDirectory)
continue; // A file has become a dir or vice versa !
if (pass == 0 && !isDirectory)
{
file->Remove(PR_FALSE);
}
else if (pass == 1 && isDirectory) // Directory
{
// Directories are more complicated. Enumerate through
// children looking for files. Any files created by the
// persist object would have been deleted by the first
// pass so if there are any there at this stage, the dir
// cannot be deleted because it has someone else's files
// in it. Empty child dirs are deleted but they must be
// recursed through to ensure they are actually empty.
PRBool isEmptyDirectory = PR_TRUE;
nsSupportsArray dirStack;
PRUint32 stackSize = 0;
// Push the top level enum onto the stack
nsCOMPtr<nsISimpleEnumerator> pos;
if (NS_SUCCEEDED(file->GetDirectoryEntries(getter_AddRefs(pos))))
dirStack.AppendElement(pos);
while (isEmptyDirectory &&
NS_SUCCEEDED(dirStack.Count(&stackSize)) && stackSize > 0)
{
// Pop the last element
nsCOMPtr<nsISimpleEnumerator> curPos;
dirStack.GetElementAt(stackSize - 1, getter_AddRefs(curPos));
dirStack.RemoveElementAt(stackSize - 1);
// Test if the enumerator has any more files in it
PRBool hasMoreElements = PR_FALSE;
curPos->HasMoreElements(&hasMoreElements);
if (!hasMoreElements)
{
continue;
}
// Child files automatically make this code drop out,
// while child dirs keep the loop going.
nsCOMPtr<nsISupports> child;
curPos->GetNext(getter_AddRefs(child));
NS_ASSERTION(child, "No child element, but hasMoreElements says otherwise");
if (!child)
continue;
nsCOMPtr<nsILocalFile> childAsFile = do_QueryInterface(child);
NS_ASSERTION(childAsFile, "This should be a file but isn't");
PRBool childIsSymlink = PR_FALSE;
childAsFile->IsSymlink(&childIsSymlink);
PRBool childIsDir = PR_FALSE;
childAsFile->IsDirectory(&childIsDir);
if (!childIsDir || childIsSymlink)
{
// Some kind of file or symlink which means dir
// is not empty so just drop out.
isEmptyDirectory = PR_FALSE;
break;
}
// Push parent enumerator followed by child enumerator
nsCOMPtr<nsISimpleEnumerator> childPos;
childAsFile->GetDirectoryEntries(getter_AddRefs(childPos));
dirStack.AppendElement(curPos);
if (childPos)
dirStack.AppendElement(childPos);
}
dirStack.Clear();
// If after all that walking the dir is deemed empty, delete it
if (isEmptyDirectory)
{
file->Remove(PR_TRUE);
}
}
}
}
}
nsresult
nsWebBrowserPersist::CalculateUniqueFilename(nsIURI *aURI)
{
@ -2043,6 +2184,16 @@ nsWebBrowserPersist::MakeOutputStreamFromFile(
NS_ENSURE_SUCCESS(CallQueryInterface(fileOutputStream, aOutputStream), NS_ERROR_FAILURE);
if (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE)
{
// Add to cleanup list in event of failure
CleanupData *cleanupData = new CleanupData;
NS_ENSURE_TRUE(cleanupData, NS_ERROR_OUT_OF_MEMORY);
cleanupData->mFile = aFile;
cleanupData->mIsDirectory = PR_FALSE;
mCleanupList.AppendElement(cleanupData);
}
return NS_OK;
}
@ -2069,9 +2220,15 @@ nsWebBrowserPersist::EndDownload(nsresult aResult)
mPersistResult = aResult;
}
// Do file cleanup if required
if (NS_FAILED(aResult) && (mPersistFlags & PERSIST_FLAGS_CLEANUP_ON_FAILURE))
{
CleanupLocalFiles();
}
// Cleanup the channels
mCompleted = PR_TRUE;
CleanUp();
Cleanup();
}
/* Hack class to get access to nsISupportsKey's protected mKey member */

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

@ -89,7 +89,8 @@ protected:
// Private members
private:
void CleanUp();
void Cleanup();
void CleanupLocalFiles();
nsresult GetValidURIFromObject(nsISupports *aObject, nsIURI **aURI) const;
nsresult GetLocalFileFromURI(nsIURI *aURI, nsILocalFile **aLocalFile) const;
nsresult AppendPathToURI(nsIURI *aURI, const nsAString & aPath) const;
@ -177,6 +178,7 @@ private:
nsHashtable mUploadList;
nsHashtable mURIMap;
nsVoidArray mDocList;
nsVoidArray mCleanupList;
nsCStringArray mFilenameList;
PRPackedBool mFirstAndOnlyUse;
PRPackedBool mCancel;