--- a/content/base/public/File.h
+++ b/content/base/public/File.h
@@ -101,17 +101,17 @@ public:
const nsAString& aContentType);
static already_AddRefed<File>
CreateTemporaryFileBlob(nsISupports* aParent, PRFileDesc* aFD,
uint64_t aStartPos, uint64_t aLength,
const nsAString& aContentType);
static already_AddRefed<File>
- CreateFromFile(nsISupports* aParent, nsIFile* aFile);
+ CreateFromFile(nsISupports* aParent, nsIFile* aFile, bool aTemporary = false);
static already_AddRefed<File>
CreateFromFile(nsISupports* aParent, const nsAString& aContentType,
uint64_t aLength, nsIFile* aFile,
indexedDB::FileInfo* aFileInfo);
static already_AddRefed<File>
CreateFromFile(nsISupports* aParent, const nsAString& aName,
@@ -626,33 +626,35 @@ private:
};
class FileImplFile : public FileImplBase
{
public:
NS_DECL_ISUPPORTS_INHERITED
// Create as a file
- explicit FileImplFile(nsIFile* aFile)
+ explicit FileImplFile(nsIFile* aFile, bool aTemporary = false)
: FileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
, mFile(aFile)
, mWholeFile(true)
, mStoredFile(false)
+ , mIsTemporary(aTemporary)
{
NS_ASSERTION(mFile, "must have file");
// Lazily get the content type and size
mContentType.SetIsVoid(true);
mFile->GetLeafName(mName);
}
FileImplFile(nsIFile* aFile, indexedDB::FileInfo* aFileInfo)
: FileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
, mFile(aFile)
, mWholeFile(true)
, mStoredFile(true)
+ , mIsTemporary(false)
{
NS_ASSERTION(mFile, "must have file");
NS_ASSERTION(aFileInfo, "must have file info");
// Lazily get the content type and size
mContentType.SetIsVoid(true);
mFile->GetLeafName(mName);
mFileInfos.AppendElement(aFileInfo);
@@ -660,76 +662,82 @@ public:
// Create as a file
FileImplFile(const nsAString& aName, const nsAString& aContentType,
uint64_t aLength, nsIFile* aFile)
: FileImplBase(aName, aContentType, aLength, UINT64_MAX)
, mFile(aFile)
, mWholeFile(true)
, mStoredFile(false)
+ , mIsTemporary(false)
{
NS_ASSERTION(mFile, "must have file");
}
FileImplFile(const nsAString& aName, const nsAString& aContentType,
uint64_t aLength, nsIFile* aFile,
uint64_t aLastModificationDate)
: FileImplBase(aName, aContentType, aLength, aLastModificationDate)
, mFile(aFile)
, mWholeFile(true)
, mStoredFile(false)
+ , mIsTemporary(false)
{
NS_ASSERTION(mFile, "must have file");
}
// Create as a file with custom name
FileImplFile(nsIFile* aFile, const nsAString& aName,
const nsAString& aContentType)
: FileImplBase(aName, aContentType, UINT64_MAX, UINT64_MAX)
, mFile(aFile)
, mWholeFile(true)
, mStoredFile(false)
+ , mIsTemporary(false)
{
NS_ASSERTION(mFile, "must have file");
if (aContentType.IsEmpty()) {
// Lazily get the content type and size
mContentType.SetIsVoid(true);
}
}
// Create as a stored file
FileImplFile(const nsAString& aName, const nsAString& aContentType,
uint64_t aLength, nsIFile* aFile,
indexedDB::FileInfo* aFileInfo)
: FileImplBase(aName, aContentType, aLength, UINT64_MAX)
, mFile(aFile)
, mWholeFile(true)
, mStoredFile(true)
+ , mIsTemporary(false)
{
NS_ASSERTION(mFile, "must have file");
mFileInfos.AppendElement(aFileInfo);
}
// Create as a stored blob
FileImplFile(const nsAString& aContentType, uint64_t aLength,
nsIFile* aFile, indexedDB::FileInfo* aFileInfo)
: FileImplBase(aContentType, aLength)
, mFile(aFile)
, mWholeFile(true)
, mStoredFile(true)
+ , mIsTemporary(false)
{
NS_ASSERTION(mFile, "must have file");
mFileInfos.AppendElement(aFileInfo);
}
// Create as a file to be later initialized
FileImplFile()
: FileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
, mWholeFile(true)
, mStoredFile(false)
+ , mIsTemporary(false)
{
// Lazily get the content type and size
mContentType.SetIsVoid(true);
mName.SetIsVoid(true);
}
// Overrides
virtual uint64_t GetSize(ErrorResult& aRv) MOZ_OVERRIDE;
@@ -737,26 +745,37 @@ public:
virtual int64_t GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE;
virtual void GetMozFullPathInternal(nsAString& aFullPath,
ErrorResult& aRv) MOZ_OVERRIDE;
virtual nsresult GetInternalStream(nsIInputStream**) MOZ_OVERRIDE;
void SetPath(const nsAString& aFullPath);
protected:
- virtual ~FileImplFile() {}
+ virtual ~FileImplFile() {
+ if (mFile && mIsTemporary) {
+ // Ignore errors if any, not much we can do. Clean-up will be done by
+ // https://mxr.mozilla.org/mozilla-central/source/xpcom/io/nsAnonymousTemporaryFile.cpp?rev=6c1c7e45c902#127
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ mFile->Remove(false);
+ NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to remove temporary DOMFile.");
+ }
+ }
private:
// Create slice
FileImplFile(const FileImplFile* aOther, uint64_t aStart,
uint64_t aLength, const nsAString& aContentType)
: FileImplBase(aContentType, aOther->mStart + aStart, aLength)
, mFile(aOther->mFile)
, mWholeFile(false)
, mStoredFile(aOther->mStoredFile)
+ , mIsTemporary(false)
{
NS_ASSERTION(mFile, "must have file");
mImmutable = aOther->mImmutable;
if (mStoredFile) {
indexedDB::FileInfo* fileInfo;
using indexedDB::IndexedDatabaseManager;
@@ -785,16 +804,17 @@ private:
virtual bool IsWholeFile() const MOZ_OVERRIDE
{
return mWholeFile;
}
nsCOMPtr<nsIFile> mFile;
bool mWholeFile;
bool mStoredFile;
+ bool mIsTemporary;
};
class FileList MOZ_FINAL : public nsIDOMFileList,
public nsWrapperCache
{
~FileList() {}
public:
--- a/content/base/src/File.cpp
+++ b/content/base/src/File.cpp
@@ -226,19 +226,19 @@ File::CreateTemporaryFileBlob(nsISupport
const nsAString& aContentType)
{
nsRefPtr<File> file = new File(aParent,
new FileImplTemporaryFileBlob(aFD, aStartPos, aLength, aContentType));
return file.forget();
}
/* static */ already_AddRefed<File>
-File::CreateFromFile(nsISupports* aParent, nsIFile* aFile)
+File::CreateFromFile(nsISupports* aParent, nsIFile* aFile, bool aTemporary)
{
- nsRefPtr<File> file = new File(aParent, new FileImplFile(aFile));
+ nsRefPtr<File> file = new File(aParent, new FileImplFile(aFile, aTemporary));
return file.forget();
}
/* static */ already_AddRefed<File>
File::CreateFromFile(nsISupports* aParent, const nsAString& aContentType,
uint64_t aLength, nsIFile* aFile,
indexedDB::FileInfo* aFileInfo)
{
new file mode 100644
--- /dev/null
+++ b/content/base/test/chrome/test_fileconstructor_tempfile.xul
@@ -0,0 +1,93 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=982874
+
+Tests building a DOMFile with the "temporary" option and checks that
+the underlying file is removed when the DOMFile is gc'ed.
+-->
+<window title="Mozilla Bug 982874"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=982874">
+ Mozilla Bug 982874</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 982874 **/
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+let Cu = Components.utils;
+
+SimpleTest.waitForExplicitFinish();
+
+function cleanup(tmp) {
+ // Force cycle and garbage collection and check that we removed the file.
+ for (let i = 0; i < 10; i++) {
+ Cu.forceCC();
+ Cu.forceGC();
+ }
+ if (tmp.exists()) {
+ ok(false, "Failed to remove temporary file!");
+ } else {
+ ok(true, "Temporary file removed when gc-ing the DOMFile");
+ }
+ SimpleTest.finish();
+}
+
+try {
+ // Create a file in $TMPDIR/mozilla-temp-files
+ let tmp = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("TmpD", Ci.nsIFile);
+ tmp.append("mozilla-temp-files");
+ tmp.append("test.txt");
+ tmp.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
+
+ // Add some content to the file.
+ let fileData = "I'm a temporary file!";
+ let outStream = Cc["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream);
+ outStream.init(tmp, 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0666, 0);
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ // Create a scoped DOMFile so the gc will happily get rid of it.
+ {
+ let dirfile = new File(tmp, { temporary: true });
+ ok(true, "Temporary File() created");
+ var reader = new FileReader();
+ reader.readAsArrayBuffer(dirfile);
+ reader.onload = function(event) {
+ let buffer = event.target.result;
+ ok(buffer.byteLength > 0,
+ "Blob size should be > 0 : " + buffer.byteLength);
+ cleanup(tmp);
+ }
+ }
+} catch (e) {
+ ok(false, "Unable to create the File() object : " + e);
+ SimpleTest.finish();
+}
+]]>
+</script>
+
+</window>