Bug 785662 - Add NS_OpenAnonymousTemporaryFile(), and use it in nsMediaCache::Init(). r=roc
authorChris Pearce <cpearce@mozilla.com>
Tue, 25 Sep 2012 12:50:30 +1200
changeset 109310 20b379c30cfc3367aff77c1e611b21cc2d1c413e
parent 109309 a060cc08dd636e34d266a1bdde9f0db809bebb72
child 109311 e31bf74c3a405a0df4d0cdad48373c75a4b5904c
push id23619
push useremorley@mozilla.com
push dateFri, 05 Oct 2012 10:54:02 +0000
treeherdermozilla-central@3b458f4e0f42 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs785662
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 785662 - Add NS_OpenAnonymousTemporaryFile(), and use it in nsMediaCache::Init(). r=roc
content/media/nsMediaCache.cpp
xpcom/build/nsXPComInit.cpp
xpcom/io/Makefile.in
xpcom/io/nsAnonymousTemporaryFile.cpp
xpcom/io/nsAnonymousTemporaryFile.h
xpcom/io/nsIFile.idl
xpcom/io/nsMediaCacheRemover.cpp
--- a/content/media/nsMediaCache.cpp
+++ b/content/media/nsMediaCache.cpp
@@ -3,29 +3,27 @@
 /* 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/ReentrantMonitor.h"
 #include "mozilla/XPCOM.h"
 
 #include "nsMediaCache.h"
-#include "nsDirectoryServiceUtils.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsXULAppAPI.h"
 #include "nsNetUtil.h"
 #include "prio.h"
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
 #include "MediaResource.h"
 #include "nsMathUtils.h"
 #include "prlog.h"
 #include "mozilla/Preferences.h"
 #include "FileBlockCache.h"
 #include "mozilla/Attributes.h"
+#include "nsAnonymousTemporaryFile.h"
 
 using namespace mozilla;
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gMediaCacheLog;
 #define LOG(type, msg) PR_LOG(gMediaCacheLog, type, msg)
 #else
 #define LOG(type, msg)
@@ -514,57 +512,18 @@ nsMediaCacheStream::BlockList::NotifyBlo
 }
 
 nsresult
 nsMediaCache::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ASSERTION(!mFileCache, "Cache file already open?");
 
-  // In single process Gecko, store the media cache in the profile directory
-  // so that multiple users can use separate media caches concurrently.
-  // In multi-process Gecko, there is no profile dir, so just store it in the
-  // system temp directory instead.
-  nsresult rv;
-  nsCOMPtr<nsIFile> tmpFile;
-  const char* dir = (XRE_GetProcessType() == GeckoProcessType_Content) ?
-    NS_OS_TEMP_DIR : NS_APP_USER_PROFILE_LOCAL_50_DIR;
-  rv = NS_GetSpecialDirectory(dir, getter_AddRefs(tmpFile));
-  NS_ENSURE_SUCCESS(rv,rv);
-
-  // We put the media cache file in
-  // ${TempDir}/mozilla-media-cache/media_cache
-  rv = tmpFile->AppendNative(nsDependentCString("mozilla-media-cache"));
-  NS_ENSURE_SUCCESS(rv,rv);
-
-  rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
-  if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
-    // Ensure the permissions are 0700. If not, we won't be able to create,
-    // read to and write from the media cache file in its subdirectory on
-    // non-Windows platforms.
-    uint32_t perms;
-    rv = tmpFile->GetPermissions(&perms);
-    NS_ENSURE_SUCCESS(rv,rv);
-    if (perms != 0700) {
-      rv = tmpFile->SetPermissions(0700);
-      NS_ENSURE_SUCCESS(rv,rv);
-    }
-  } else {
-    NS_ENSURE_SUCCESS(rv,rv);
-  }
-
-  rv = tmpFile->AppendNative(nsDependentCString("media_cache"));
-  NS_ENSURE_SUCCESS(rv,rv);
-
-  rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700);
-  NS_ENSURE_SUCCESS(rv,rv);
-
   PRFileDesc* fileDesc = nullptr;
-  rv = tmpFile->OpenNSPRFileDesc(PR_RDWR | nsIFile::DELETE_ON_CLOSE,
-                                 PR_IRWXU, &fileDesc);
+  nsresult rv = NS_OpenAnonymousTemporaryFile(&fileDesc);
   NS_ENSURE_SUCCESS(rv,rv);
 
   mFileCache = new FileBlockCache();
   rv = mFileCache->Open(fileDesc);
   NS_ENSURE_SUCCESS(rv,rv);
 
 #ifdef PR_LOGGING
   if (!gMediaCacheLog) {
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -136,17 +136,17 @@ static BrowserProcessSubThread* sIOThrea
 // We hook into this function locally to create and register the registry
 // Since noone outside xpcom needs to know about this and nsRegistry.cpp
 // does not have a local include file, we are putting this definition
 // here rather than in nsIRegistry.h
 extern nsresult NS_RegistryGetFactory(nsIFactory** aFactory);
 extern nsresult NS_CategoryManagerGetFactory( nsIFactory** );
 
 #ifdef XP_WIN
-extern nsresult ScheduleMediaCacheRemover();
+extern nsresult CreateAnonTempFileRemover();
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsProcess)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsIDImpl)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsStringImpl)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsCStringImpl)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRBoolImpl)
@@ -459,17 +459,17 @@ NS_InitXPCOM2(nsIServiceManager* *result
     mozilla::scache::StartupCache::GetSingleton();
     mozilla::AvailableMemoryTracker::Activate();
 
     // Notify observers of xpcom autoregistration start
     NS_CreateServicesFromCategory(NS_XPCOM_STARTUP_CATEGORY, 
                                   nullptr,
                                   NS_XPCOM_STARTUP_OBSERVER_ID);
 #ifdef XP_WIN
-    ScheduleMediaCacheRemover();
+    CreateAnonTempFileRemover();
 #endif
 
     mozilla::MapsMemoryReporter::Init();
 
     mozilla::Telemetry::Init();
 
     mozilla::HangMonitor::Startup();
 
--- a/xpcom/io/Makefile.in
+++ b/xpcom/io/Makefile.in
@@ -21,16 +21,17 @@ LIBXUL_LIBRARY  = 1
 ifdef GNU_CC 
 ifneq ($(OS_ARCH), Darwin)
 MODULE_OPTIMIZE_FLAGS = $(MOZ_OPTIMIZE_FLAGS) -fno-strict-aliasing
 endif
 endif
 
 CPPSRCS		= \
 		Base64.cpp \
+		nsAnonymousTemporaryFile.cpp \
 		nsAppFileLocationProvider.cpp \
 		nsBinaryStream.cpp \
 		nsDirectoryService.cpp \
 		nsEscape.cpp \
 		nsInputStreamTee.cpp \
 		nsLinebreakConverter.cpp \
 		nsLocalFileCommon.cpp \
 		nsMultiplexInputStream.cpp \
@@ -53,23 +54,24 @@ CMMSRCS		+= \
 		CocoaFileUtils.mm \
 		$(NULL)
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
 CPPSRCS		+= nsLocalFileOS2.cpp
 else
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-CPPSRCS		+= nsLocalFileWin.cpp nsMediaCacheRemover.cpp
+CPPSRCS		+= nsLocalFileWin.cpp
 else
 CPPSRCS		+= nsLocalFileUnix.cpp
 endif # windows
 endif # OS2
 
 EXPORTS		= \
+		nsAnonymousTemporaryFile.h \
 		nsAppDirectoryServiceDefs.h \
 		nsDirectoryService.h \
 		nsDirectoryServiceAtomList.h \
 		nsEscape.h \
 		nsLinebreakConverter.h \
 		nsLocalFile.h \
 		nsMultiplexInputStream.h \
 		nsNativeCharsetUtils.h \
rename from xpcom/io/nsMediaCacheRemover.cpp
rename to xpcom/io/nsAnonymousTemporaryFile.cpp
--- a/xpcom/io/nsMediaCacheRemover.cpp
+++ b/xpcom/io/nsAnonymousTemporaryFile.cpp
@@ -1,118 +1,240 @@
+/* -*- 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/. */
 
-#ifndef XP_WIN
-#error nsMediaCacheRemover only needed on Windows.
-#endif
 
+#include "nsAnonymousTemporaryFile.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsXULAppAPI.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsAppDirectoryServiceDefs.h"
+
+#ifdef XP_WIN
 #include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
 #include "nsIIdleService.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIFile.h"
-#include "nsAppDirectoryServiceDefs.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsXULAppAPI.h"
-#include "nsString.h"
 #include "nsAutoPtr.h"
 #include "nsITimer.h"
+#include "nsCRT.h"
+
+using namespace mozilla;
+#endif
+
+
+// We store the temp files in the system temp dir.
+//
+// On Windows systems in particular we use a sub-directory of the temp
+// directory, because:
+//   1. DELETE_ON_CLOSE is unreliable on Windows, in particular if we power
+//      cycle (and perhaps if we crash) the files are not deleted. We store
+//      the temporary files in a known sub-dir so that we can find and delete
+//      them easily and quickly.
+//   2. On Windows NT the system temp dir is in the user's $HomeDir/AppData,
+//      so we can be sure the user always has write privileges to that directory;
+//      if the sub-dir for our temp files was in some shared location and
+//      was created by a privileged user, it's possible that other users
+//      wouldn't have write access to that sub-dir. (Non-Windows systems
+//      don't store their temp files in a sub-dir, so this isn't an issue on
+//      those platforms).
+//   3. Content processes can access the system temp dir
+//      (NS_GetSpecialDirectory fails on NS_APP_USER_PROFILE_LOCAL_50_DIR
+//      for content process for example, which is where we previously stored
+//      temp files on Windows). This argument applies to all platforms, not
+//      just Windows.
+static nsresult
+GetTempDir(nsIFile** aTempDir)
+{
+  NS_ENSURE_ARG(aTempDir);
+  nsCOMPtr<nsIFile> tmpFile;
+  nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile));
+  NS_ENSURE_SUCCESS(rv,rv);
+
+#ifdef XP_WIN
+  // On windows DELETE_ON_CLOSE is unreliable, so we store temporary files
+  // in a subdir of the temp dir and delete that in an idle service observer
+  // to ensure it's been cleared.
+  rv = tmpFile->AppendNative(nsDependentCString("mozilla-temp-files"));
+  NS_ENSURE_SUCCESS(rv,rv);    
+  rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
+  NS_ENSURE_TRUE(rv == NS_ERROR_FILE_ALREADY_EXISTS || NS_SUCCEEDED(rv), rv);
+#endif
+ 
+  tmpFile.forget(aTempDir);
+
+  return NS_OK;
+}
+
+nsresult
+NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc)
+{
+  NS_ENSURE_ARG(aOutFileDesc);
+  nsresult rv;
+
+  nsCOMPtr<nsIFile> tmpFile;
+  rv = GetTempDir(getter_AddRefs(tmpFile));
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  // Give the temp file a name with a random element. CreateUnique will also
+  // append a counter to the name if it encounters a name collision. Adding
+  // a random element to the name reduces the likelihood of a name collision,
+  // so that CreateUnique() doesn't end up trying a lot of name variants in
+  // its "try appending an incrementing counter" loop, as file IO can be
+  // expensive on some mobile flash drives.
+  nsAutoCString name("mozilla-temp-");
+  name.AppendInt(rand());
+
+  rv = tmpFile->AppendNative(name);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  rv = tmpFile->OpenNSPRFileDesc(PR_RDWR | nsIFile::DELETE_ON_CLOSE,
+                                 PR_IRWXU, aOutFileDesc);
+
+  return rv;    
+}
+
+#ifdef XP_WIN
+
+// On Windows we have an idle service observer that runs some time after
+// startup and deletes any stray anonymous temporary files...
 
 // Duration of idle time before we'll get a callback whereupon we attempt to
-// remove any stray and unused media cache temp files.
-#define TEMP_FILE_IDLE_TIME 30
+// remove any stray and unused anonymous temp files.
+#define TEMP_FILE_IDLE_TIME_S 30
 
-// The nsMediaCacheRemover is created in a timer, which sets an idle observer.
+// The nsAnonTempFileRemover is created in a timer, which sets an idle observer.
 // This is expiration time (in ms) which initial timer is set for (3 minutes).
-#define SCHEDULE_TIMEOUT 3 * 60 * 1000
+#define SCHEDULE_TIMEOUT_MS 3 * 60 * 1000
+
+#define XPCOM_SHUTDOWN_TOPIC "xpcom-shutdown"
 
 // This class adds itself as an idle observer. When the application has
 // been idle for about 30 seconds we'll get a notification, whereupon we'll
-// attempt to delete ${TempDir}/mozilla-media-cache/. This is to ensure all
-// media cache temp files which were supposed to be deleted on application
-// exit were actually deleted as they may not be if we previously crashed.
-// See bug 572579. This is only needed on some versions of Windows,
+// attempt to delete ${TempDir}/mozilla-temp-files/. This is to ensure all
+// temp files that were supposed to be deleted on application exit were actually
+// deleted, as they may not be if we previously crashed. See bugs 572579 and
+// 785662. This is only needed on some versions of Windows,
 // nsIFile::DELETE_ON_CLOSE works on other platforms.
-class nsMediaCacheRemover : public nsIObserver {
+// This class adds itself as a shutdown observer so that it can cancel the
+// idle observer and its timer on shutdown. Note: the observer and idle
+// services hold references to instances of this object, and those references
+// are what keep this object alive.
+class nsAnonTempFileRemover : public nsIObserver {
 public:
   NS_DECL_ISUPPORTS
 
-  nsMediaCacheRemover() {
-    MOZ_COUNT_CTOR(nsMediaCacheRemover);
+  nsAnonTempFileRemover() {
+    MOZ_COUNT_CTOR(nsAnonTempFileRemover);
+  }
+
+  ~nsAnonTempFileRemover() {
+    MOZ_COUNT_DTOR(nsAnonTempFileRemover);
+  }
+
+  nsresult Init() {
+    // We add the idle observer in a timer, so that the app has enough
+    // time to start up before we add the idle observer. If we register the
+    // idle observer too early, it will be registered before the fake idle
+    // service is installed when running in xpcshell, and this interferes with
+    // the fake idle service, causing xpcshell-test failures.
+    mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    NS_ENSURE_TRUE(mTimer != nullptr, NS_ERROR_FAILURE);
+    nsresult rv = mTimer->Init(this,
+                         SCHEDULE_TIMEOUT_MS,
+                         nsITimer::TYPE_ONE_SHOT);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Register shutdown observer so we can cancel the timer if we shutdown before
+    // the timer runs.
+    nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
+    NS_ENSURE_TRUE(obsSrv != nullptr, NS_ERROR_FAILURE);
+    return obsSrv->AddObserver(this, XPCOM_SHUTDOWN_TOPIC, false);
   }
 
-  ~nsMediaCacheRemover() {
-    MOZ_COUNT_DTOR(nsMediaCacheRemover);
+  void Cleanup() {
+    // Cancel timer.
+    if (mTimer) {
+      mTimer->Cancel();
+      mTimer = nullptr;
+    }    
+    // Remove idle service observer.
+    nsCOMPtr<nsIIdleService> idleSvc =
+      do_GetService("@mozilla.org/widget/idleservice;1");
+    if (idleSvc) {
+      idleSvc->RemoveIdleObserver(this, TEMP_FILE_IDLE_TIME_S);    
+    }
+    // Remove shutdown observer.
+    nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
+    if (obsSrv) {
+      obsSrv->RemoveObserver(this, XPCOM_SHUTDOWN_TOPIC);
+    }
   }
-
-  NS_IMETHODIMP Observe(nsISupports *subject,
-                        const char *topic,
-                        const PRUnichar *data)
+  
+  NS_IMETHODIMP Observe(nsISupports *aSubject,
+                        const char *aTopic,
+                        const PRUnichar *aData)
   {
-    if (strcmp(topic, "idle") == 0) {
+    if (nsCRT::strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0 &&
+        NS_FAILED(RegisterIdleObserver())) {
+      Cleanup();
+    } else if (nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE) == 0) {
       // The user has been idle for a while, clean up the temp files.
       // The idle service will drop its reference to this object after
       // we exit, destroying this object.
-      RemoveMediaCacheFiles();
+      RemoveAnonTempFileFiles();
+      Cleanup();
+    } else if (nsCRT::strcmp(aTopic, XPCOM_SHUTDOWN_TOPIC) == 0) {
+      Cleanup();
     }
     return NS_OK;
   }
 
   nsresult RegisterIdleObserver() {
     // Add this as an idle observer. When we've been idle for 
-    // TEMP_FILE_IDLE_TIME seconds, we'll get a notification, and we'll then
-    // try to delete any stray media cache temp files.
+    // TEMP_FILE_IDLE_TIME_S seconds, we'll get a notification, and we'll then
+    // try to delete any stray temp files.
     nsCOMPtr<nsIIdleService> idleSvc =
       do_GetService("@mozilla.org/widget/idleservice;1");
     if (!idleSvc)
       return NS_ERROR_FAILURE;
-    return idleSvc->AddIdleObserver(this, TEMP_FILE_IDLE_TIME);
+    return idleSvc->AddIdleObserver(this, TEMP_FILE_IDLE_TIME_S);
   }
 
-  void RemoveMediaCacheFiles() {
-    nsCOMPtr<nsIIdleService> idleSvc =
-      do_GetService("@mozilla.org/widget/idleservice;1");
-    if (idleSvc)
-      idleSvc->RemoveIdleObserver(this, TEMP_FILE_IDLE_TIME);
-
+  void RemoveAnonTempFileFiles() {
     nsCOMPtr<nsIFile> tmpDir;
-    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
-                                         getter_AddRefs(tmpDir));
-    if (NS_FAILED(rv))
-      return;
-
-    NS_ABORT_IF_FALSE(XRE_GetProcessType() == GeckoProcessType_Default,
-                      "Need to update media cache file location");
-
-    rv = tmpDir->AppendNative(nsDependentCString("mozilla-media-cache"));
-    if (NS_FAILED(rv))
-      return;
+    nsresult rv = GetTempDir(getter_AddRefs(tmpDir));
+    NS_ENSURE_SUCCESS(rv,);
 
     // Remove the directory recursively.  
     tmpDir->Remove(true);
   }
+  
+private:
+  nsCOMPtr<nsITimer> mTimer;
 };
 
-NS_IMPL_ISUPPORTS1(nsMediaCacheRemover, nsIObserver)
+NS_IMPL_ISUPPORTS1(nsAnonTempFileRemover, nsIObserver)
 
-void CreateMediaCacheRemover(nsITimer* aTimer, void* aClosure) {
-  // Create a new nsMediaCacheRemover, and register it as an idle observer.
-  // If it is successfully registered as an idle observer, its owning reference
-  // will be held by the idle service, otherwise it will be destroyed by the
-  // refptr here when it goes out of scope.
-  nsRefPtr<nsMediaCacheRemover> t = new nsMediaCacheRemover();
-  t->RegisterIdleObserver();
+nsresult CreateAnonTempFileRemover() {
+  // Create a temp file remover. If Init() succeeds, the temp file remover is kept
+  // alive by a reference held by the observer service, since the temp file remover
+  // is a shutdown observer. We only create the temp file remover if we're running
+  // in the main process; there's no point in doing the temp file removal multiple
+  // times per startup.
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    return NS_OK;
+  }
+  nsRefPtr<nsAnonTempFileRemover> tempRemover = new nsAnonTempFileRemover();
+  return tempRemover->Init();
 }
 
-nsresult ScheduleMediaCacheRemover() {
-  // We create the nsMediaCacheRemover in a timer, so that the app has enough
-  // time to start up before we add the idle observer. If we register the
-  // idle observer too early, it will be registered before the fake idle
-  // service is installed when running in xpcshell, and this interferes with
-  // the fake idle service, causing xpcshell-test failures.
-  nsCOMPtr<nsITimer> t = do_CreateInstance(NS_TIMER_CONTRACTID);
-  nsresult res = t->InitWithFuncCallback(CreateMediaCacheRemover,
-                                         0,
-                                         SCHEDULE_TIMEOUT,
-                                         nsITimer::TYPE_ONE_SHOT);
-  return res;
-}
+#endif
+
new file mode 100644
--- /dev/null
+++ b/xpcom/io/nsAnonymousTemporaryFile.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#pragma once
+
+#include "prio.h"
+#include "nscore.h"
+
+/**
+ * OpenAnonymousTemporaryFile
+ *
+ * Creates and opens a temporary file which has a random name. Callers have no
+ * control over the file name, and the file is opened in a temporary location
+ * which is appropriate for the platform.
+ *
+ * Upon success, aOutFileDesc contains an opened handle to the temporary file.
+ * The caller is responsible for closing the file when they're finished with it.
+ *
+ * The file will be deleted when the file handle is closed. On non-Windows
+ * platforms the file will be unlinked before this function returns. On Windows
+ * the OS supplied delete-on-close mechanism is unreliable if the application
+ * crashes or the computer power cycles unexpectedly, so unopened temporary
+ * files are purged at some time after application startup.
+ *
+ */ 
+nsresult
+NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc);
+
--- a/xpcom/io/nsIFile.idl
+++ b/xpcom/io/nsIFile.idl
@@ -339,30 +339,39 @@ interface nsIFile : nsISupports
      *
      *  This attribute will determine if the nsLocalFile will auto
      *  resolve symbolic links.  By default, this value will be false
      *  on all non unix systems.  On unix, this attribute is effectively
      *  a noop.  
      */
     attribute boolean followLinks;  
 
+    /**
+     * Flag for openNSPRFileDesc(), to hint to the OS that the file will be
+     * read sequentially with agressive readahead.
+     */
     const unsigned long OS_READAHEAD = 0x40000000;
+    
+    /**
+     * Flag for openNSPRFileDesc(). Deprecated and unreliable!
+     * Instead use NS_OpenAnonymousTemporaryFile() to create a temporary
+     * file which will be deleted upon close!
+     */
     const unsigned long DELETE_ON_CLOSE = 0x80000000;
 
     /**
      * Return the result of PR_Open on the file.  The caller is
      * responsible for calling PR_Close on the result.
      *
      * @param flags the PR_Open flags from prio.h, plus optionally
      * OS_READAHEAD or DELETE_ON_CLOSE. OS_READAHEAD is a hint to the
      * OS that the file will be read sequentially with agressive
-     * readahead. DELETE_ON_CLOSE may be implemented by removing the
-     * file (by path name) immediately after opening it, so beware of
-     * possible races; the file should be exclusively owned by this
-     * process.
+     * readahead. DELETE_ON_CLOSE is unreliable on Windows and is deprecated.
+     * Instead use NS_OpenAnonymousTemporaryFile() to create a temporary
+     * file which will be deleted upon close.
      */
     [noscript] PRFileDescStar openNSPRFileDesc(in long flags, in long mode);
 
     /**
      * Return the result of fopen on the file.  The caller is
      * responsible for calling fclose on the result.
      */
     [noscript] FILE           openANSIFileDesc(in string mode);