Bug 968101 - Remove directories with low priority on background, r=honzab
authorMichal Novotny <michal.novotny@gmail.com>
Wed, 19 Feb 2014 13:04:06 +0100
changeset 191181 8c60c6fa67c9323bb4476e46d8784218ee4dd2fd
parent 191180 6f21473b58c6afe24f6861825c0f1ab3cb5ee25a
child 191182 c486ad9a89acd41b4261285b91762f9cc5a7abaa
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab
bugs968101
milestone30.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 968101 - Remove directories with low priority on background, r=honzab
netwerk/cache2/CacheFileIOManager.cpp
netwerk/cache2/CacheFileIOManager.h
toolkit/components/places/tests/expiration/xpcshell.ini
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -9,16 +9,19 @@
 #include "CacheHashUtils.h"
 #include "CacheStorageService.h"
 #include "CacheIndex.h"
 #include "CacheFileUtils.h"
 #include "nsThreadUtils.h"
 #include "CacheFile.h"
 #include "CacheObserver.h"
 #include "nsIFile.h"
+#include "nsITimer.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIDirectoryEnumerator.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/DebugOnly.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "private/pprio.h"
 #include "mozilla/VisualEventTracer.h"
 
 // include files for ftruncate (or equivalent)
@@ -31,35 +34,40 @@
 #else
 // XXX add necessary include file for ftruncate (or equivalent)
 #endif
 
 
 namespace mozilla {
 namespace net {
 
-#define kOpenHandlesLimit   64
-#define kMetadataWriteDelay 5000
-#define kEvictionLoopLimit  40       // in milliseconds
+#define kOpenHandlesLimit      64
+#define kMetadataWriteDelay    5000
+#define kEvictionLoopLimit     40      // in milliseconds
+#define kRemoveTrashStartDelay 60000   // in milliseconds
+#define kRemoveTrashLoopLimit  40      // in milliseconds
 
 bool
 CacheFileHandle::DispatchRelease()
 {
-  if (CacheFileIOManager::IsOnIOThreadOrCeased())
+  if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
     return false;
+  }
 
   nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
-  if (!ioTarget)
+  if (!ioTarget) {
     return false;
+  }
 
   nsRefPtr<nsRunnableMethod<CacheFileHandle, nsrefcnt, false> > event =
     NS_NewNonOwningRunnableMethod(this, &CacheFileHandle::Release);
   nsresult rv = ioTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
-  if (NS_FAILED(rv))
+  if (NS_FAILED(rv)) {
     return false;
+  }
 
   return true;
 }
 
 NS_IMPL_ADDREF(CacheFileHandle)
 NS_IMETHODIMP_(nsrefcnt)
 CacheFileHandle::Release()
 {
@@ -140,18 +148,17 @@ CacheFileHandle::Log()
 
   if (!mHash) {
     // special file
     LOG(("CacheFileHandle::Log() [this=%p, hash=nullptr, isDoomed=%d, "
          "priority=%d, closed=%d, invalid=%d, "
          "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
          this, mIsDoomed, mPriority, mClosed, mInvalid,
          mFileExists, mFileSize, leafName.get(), mKey.get()));
-  }
-  else {
+  } else {
     LOG(("CacheFileHandle::Log() [this=%p, hash=%08x%08x%08x%08x%08x, "
          "isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
          "fileExists=%d, fileSize=%lld, leafName=%s, key=%s]",
          this, LOGSHA1(mHash), mIsDoomed, mPriority, mClosed,
          mInvalid, mFileExists, mFileSize, leafName.get(), mKey.get()));
   }
 }
 
@@ -164,18 +171,17 @@ CacheFileHandle::FileSizeInK()
   size64 += 0x3FF;
   size64 >>= 10;
 
   uint32_t size;
   if (size64 >> 32) {
     NS_WARNING("CacheFileHandle::FileSizeInK() - FileSize is too large, "
                "truncating to PR_UINT32_MAX");
     size = PR_UINT32_MAX;
-  }
-  else {
+  } else {
     size = static_cast<uint32_t>(size64);
   }
 
   return size;
 }
 
 /******************************************************************************
  *  CacheFileHandles::HandleHashKey
@@ -200,18 +206,19 @@ CacheFileHandles::HandleHashKey::RemoveH
 }
 
 already_AddRefed<CacheFileHandle>
 CacheFileHandles::HandleHashKey::GetNewestHandle()
 {
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
 
   nsRefPtr<CacheFileHandle> handle;
-  if (mHandles.Length())
+  if (mHandles.Length()) {
     handle = mHandles[0];
+  }
 
   return handle.forget();
 }
 
 void
 CacheFileHandles::HandleHashKey::GetHandles(nsTArray<nsRefPtr<CacheFileHandle> > &aResult)
 {
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
@@ -284,18 +291,19 @@ CacheFileHandles::GetHandle(const SHA1Su
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (handle->IsDoomed()) {
     LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
          "found doomed handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
 
     // If the consumer doesn't want doomed handles, exit with NOT_AVAIL.
-    if (!aReturnDoomed)
+    if (!aReturnDoomed) {
       return NS_ERROR_NOT_AVAILABLE;
+    }
   } else {
     LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
          "found handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
   }
 
   handle.forget(_retval);
   return NS_OK;
 }
@@ -335,18 +343,19 @@ CacheFileHandles::NewHandle(const SHA1Su
 }
 
 void
 CacheFileHandles::RemoveHandle(CacheFileHandle *aHandle)
 {
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   MOZ_ASSERT(aHandle);
 
-  if (!aHandle)
+  if (!aHandle) {
     return;
+  }
 
 #ifdef DEBUG_HANDLES
   LOG(("CacheFileHandles::RemoveHandle() [handle=%p, hash=%08x%08x%08x%08x%08x]"
        , aHandle, LOGSHA1(aHandle->Hash())));
 #endif
 
   // find hash entry for key
   HandleHashKey *entry = mTable.GetEntry(*aHandle->Hash());
@@ -487,41 +496,42 @@ public:
         SHA1Sum sum;
         sum.update(mKey.BeginReading(), mKey.Length());
         sum.finish(mHash);
       }
 
       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this),
                             "net::cache::open-background");
       if (NS_SUCCEEDED(mRV)) {
-        if (!mIOMan)
+        if (!mIOMan) {
           mRV = NS_ERROR_NOT_INITIALIZED;
-        else {
-          if (mFlags & CacheFileIOManager::SPECIAL_FILE)
+        } else {
+          if (mFlags & CacheFileIOManager::SPECIAL_FILE) {
             mRV = mIOMan->OpenSpecialFileInternal(mKey, mFlags,
                                                   getter_AddRefs(mHandle));
-          else
+          } else {
             mRV = mIOMan->OpenFileInternal(&mHash, mFlags,
                                            getter_AddRefs(mHandle));
+          }
           mIOMan = nullptr;
           if (mHandle) {
             MOZ_EVENT_TRACER_NAME_OBJECT(mHandle.get(), mKey.get());
-            if (mHandle->Key().IsEmpty())
+            if (mHandle->Key().IsEmpty()) {
               mHandle->Key() = mKey;
+            }
           }
         }
       }
       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::open-background");
 
       MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::open-result");
       nsCOMPtr<nsIEventTarget> target;
       mTarget.swap(target);
       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
-    }
-    else {
+    } else {
       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::open-result");
       mCallback->OnFileOpened(mHandle, mRV);
       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::open-result");
     }
     return NS_OK;
   }
 
 protected:
@@ -557,32 +567,33 @@ public:
   {
     MOZ_COUNT_DTOR(ReadEvent);
   }
 
   NS_IMETHOD Run()
   {
     if (mTarget) {
       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::read-background");
-      if (mHandle->IsClosed())
+      if (mHandle->IsClosed()) {
         mRV = NS_ERROR_NOT_INITIALIZED;
-      else
+      } else {
         mRV = CacheFileIOManager::gInstance->ReadInternal(
           mHandle, mOffset, mBuf, mCount);
+      }
       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::read-background");
 
       MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::read-result");
       nsCOMPtr<nsIEventTarget> target;
       mTarget.swap(target);
       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
-    }
-    else {
+    } else {
       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::read-result");
-      if (mCallback)
+      if (mCallback) {
         mCallback->OnDataRead(mHandle, mBuf, mRV);
+      }
       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::read-result");
     }
     return NS_OK;
   }
 
 protected:
   nsRefPtr<CacheFileHandle>     mHandle;
   int64_t                       mOffset;
@@ -620,33 +631,33 @@ public:
       free(const_cast<char *>(mBuf));
     }
   }
 
   NS_IMETHOD Run()
   {
     if (mTarget) {
       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-background");
-      if (mHandle->IsClosed())
+      if (mHandle->IsClosed()) {
         mRV = NS_ERROR_NOT_INITIALIZED;
-      else
+      } else {
         mRV = CacheFileIOManager::gInstance->WriteInternal(
           mHandle, mOffset, mBuf, mCount, mValidate);
+      }
       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-background");
 
       MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::write-result");
       nsCOMPtr<nsIEventTarget> target;
       mTarget.swap(target);
       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
-    }
-    else {
+    } else {
       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-result");
-      if (mCallback)
+      if (mCallback) {
         mCallback->OnDataWritten(mHandle, mBuf, mRV);
-      else {
+      } else {
         free(const_cast<char *>(mBuf));
         mBuf = nullptr;
       }
       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-result");
     }
     return NS_OK;
   }
 
@@ -680,31 +691,32 @@ public:
   {
     MOZ_COUNT_DTOR(DoomFileEvent);
   }
 
   NS_IMETHOD Run()
   {
     if (mTarget) {
       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
-      if (mHandle->IsClosed())
+      if (mHandle->IsClosed()) {
         mRV = NS_ERROR_NOT_INITIALIZED;
-      else
+      } else {
         mRV = CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
+      }
       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::doom-background");
 
       MOZ_EVENT_TRACER_WAIT(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
       nsCOMPtr<nsIEventTarget> target;
       mTarget.swap(target);
       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
-    }
-    else {
+    } else {
       MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
-      if (mCallback)
+      if (mCallback) {
         mCallback->OnFileDoomed(mHandle, mRV);
+      }
       MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::doom-result");
     }
     return NS_OK;
   }
 
 protected:
   nsCOMPtr<CacheFileIOListener> mCallback;
   nsCOMPtr<nsIEventTarget>      mTarget;
@@ -733,30 +745,30 @@ public:
   ~DoomFileByKeyEvent()
   {
     MOZ_COUNT_DTOR(DoomFileByKeyEvent);
   }
 
   NS_IMETHOD Run()
   {
     if (mTarget) {
-      if (!mIOMan)
+      if (!mIOMan) {
         mRV = NS_ERROR_NOT_INITIALIZED;
-      else {
+      } else {
         mRV = mIOMan->DoomFileByKeyInternal(&mHash);
         mIOMan = nullptr;
       }
 
       nsCOMPtr<nsIEventTarget> target;
       mTarget.swap(target);
       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
-    }
-    else {
-      if (mCallback)
+    } else {
+      if (mCallback) {
         mCallback->OnFileDoomed(nullptr, mRV);
+      }
     }
     return NS_OK;
   }
 
 protected:
   SHA1Sum::Hash                 mHash;
   nsCOMPtr<CacheFileIOListener> mCallback;
   nsCOMPtr<nsIEventTarget>      mTarget;
@@ -774,18 +786,19 @@ public:
 
   ~ReleaseNSPRHandleEvent()
   {
     MOZ_COUNT_DTOR(ReleaseNSPRHandleEvent);
   }
 
   NS_IMETHOD Run()
   {
-    if (mHandle->mFD && !mHandle->IsClosed())
+    if (mHandle->mFD && !mHandle->IsClosed()) {
       CacheFileIOManager::gInstance->ReleaseNSPRHandleInternal(mHandle);
+    }
 
     return NS_OK;
   }
 
 protected:
   nsRefPtr<CacheFileHandle>     mHandle;
 };
 
@@ -806,29 +819,30 @@ public:
   ~TruncateSeekSetEOFEvent()
   {
     MOZ_COUNT_DTOR(TruncateSeekSetEOFEvent);
   }
 
   NS_IMETHOD Run()
   {
     if (mTarget) {
-      if (mHandle->IsClosed())
+      if (mHandle->IsClosed()) {
         mRV = NS_ERROR_NOT_INITIALIZED;
-      else
+      } else {
         mRV = CacheFileIOManager::gInstance->TruncateSeekSetEOFInternal(
           mHandle, mTruncatePos, mEOFPos);
+      }
 
       nsCOMPtr<nsIEventTarget> target;
       mTarget.swap(target);
       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
-    }
-    else {
-      if (mCallback)
+    } else {
+      if (mCallback) {
         mCallback->OnEOFSet(mHandle, mRV);
+      }
     }
     return NS_OK;
   }
 
 protected:
   nsRefPtr<CacheFileHandle>     mHandle;
   int64_t                       mTruncatePos;
   int64_t                       mEOFPos;
@@ -853,29 +867,30 @@ public:
   ~RenameFileEvent()
   {
     MOZ_COUNT_DTOR(RenameFileEvent);
   }
 
   NS_IMETHOD Run()
   {
     if (mTarget) {
-      if (mHandle->IsClosed())
+      if (mHandle->IsClosed()) {
         mRV = NS_ERROR_NOT_INITIALIZED;
-      else
+      } else {
         mRV = CacheFileIOManager::gInstance->RenameFileInternal(mHandle,
                                                                 mNewName);
+      }
 
       nsCOMPtr<nsIEventTarget> target;
       mTarget.swap(target);
       target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
-    }
-    else {
-      if (mCallback)
+    } else {
+      if (mCallback) {
         mCallback->OnFileRenamed(mHandle, mRV);
+      }
     }
     return NS_OK;
   }
 
 protected:
   nsRefPtr<CacheFileHandle>     mHandle;
   nsCString                     mNewName;
   nsCOMPtr<CacheFileIOListener> mCallback;
@@ -897,18 +912,19 @@ public:
 
   ~InitIndexEntryEvent()
   {
     MOZ_COUNT_DTOR(InitIndexEntryEvent);
   }
 
   NS_IMETHOD Run()
   {
-    if (mHandle->IsClosed() || mHandle->IsDoomed())
+    if (mHandle->IsClosed() || mHandle->IsDoomed()) {
       return NS_OK;
+    }
 
     CacheIndex::InitEntry(mHandle->Hash(), mAppId, mAnonymous, mInBrowser);
 
     // We cannot set the filesize before we init the entry. If we're opening
     // an existing entry file, frecency and expiration time will be set after
     // parsing the entry file, but we must set the filesize here since nobody is
     // going to set it if there is no write to the file.
     uint32_t sizeInK = mHandle->FileSizeInK();
@@ -945,18 +961,19 @@ public:
 
   ~UpdateIndexEntryEvent()
   {
     MOZ_COUNT_DTOR(UpdateIndexEntryEvent);
   }
 
   NS_IMETHOD Run()
   {
-    if (mHandle->IsClosed() || mHandle->IsDoomed())
+    if (mHandle->IsClosed() || mHandle->IsDoomed()) {
       return NS_OK;
+    }
 
     CacheIndex::UpdateEntry(mHandle->Hash(),
                             mHasFrecency ? &mFrecency : nullptr,
                             mHasExpirationTime ? &mExpirationTime : nullptr,
                             nullptr);
     return NS_OK;
   }
 
@@ -1017,16 +1034,17 @@ public:
 CacheFileIOManager * CacheFileIOManager::gInstance = nullptr;
 
 NS_IMPL_ISUPPORTS1(CacheFileIOManager, nsITimerCallback)
 
 CacheFileIOManager::CacheFileIOManager()
   : mShuttingDown(false)
   , mTreeCreated(false)
   , mOverLimitEvicting(false)
+  , mRemovingTrashDirs(false)
 {
   LOG(("CacheFileIOManager::CacheFileIOManager [this=%p]", this));
   MOZ_COUNT_CTOR(CacheFileIOManager);
   MOZ_ASSERT(!gInstance, "multiple CacheFileIOManager instances!");
 }
 
 CacheFileIOManager::~CacheFileIOManager()
 {
@@ -1036,18 +1054,19 @@ CacheFileIOManager::~CacheFileIOManager(
 
 nsresult
 CacheFileIOManager::Init()
 {
   LOG(("CacheFileIOManager::Init()"));
 
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (gInstance)
+  if (gInstance) {
     return NS_ERROR_ALREADY_INITIALIZED;
+  }
 
   nsRefPtr<CacheFileIOManager> ioMan = new CacheFileIOManager();
 
   nsresult rv = ioMan->InitInternal();
   NS_ENSURE_SUCCESS(rv, rv);
 
   ioMan.swap(gInstance);
   return NS_OK;
@@ -1059,28 +1078,31 @@ CacheFileIOManager::InitInternal()
   nsresult rv;
 
   mIOThread = new CacheIOThread();
 
   rv = mIOThread->Init();
   MOZ_ASSERT(NS_SUCCEEDED(rv), "Can't create background thread");
   NS_ENSURE_SUCCESS(rv, rv);
 
+  mStartTime = TimeStamp::NowLoRes();
+
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::Shutdown()
 {
   LOG(("CacheFileIOManager::Shutdown() [gInstance=%p]", gInstance));
 
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (!gInstance)
+  if (!gInstance) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_V2> shutdownTimer;
 
   CacheIndex::PreShutdown();
 
   ShutdownMetadataWriteScheduling();
 
   {
@@ -1093,18 +1115,19 @@ CacheFileIOManager::Shutdown()
     rv = gInstance->mIOThread->Dispatch(ev, CacheIOThread::CLOSE);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     condVar.Wait();
   }
 
   MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0);
   MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0);
 
-  if (gInstance->mIOThread)
+  if (gInstance->mIOThread) {
     gInstance->mIOThread->Shutdown();
+  }
 
   CacheIndex::Shutdown();
 
   nsRefPtr<CacheFileIOManager> ioMan;
   ioMan.swap(gInstance);
 
   return NS_OK;
 }
@@ -1142,28 +1165,35 @@ CacheFileIOManager::ShutdownInternal()
     }
 
     if (!h->IsSpecialFile() && !h->mIsDoomed &&
         (h->mInvalid || !h->mFileExists)) {
       CacheIndex::RemoveEntry(h->Hash());
     }
 
     // Remove the handle from mHandles/mSpecialHandles
-    if (h->IsSpecialFile())
+    if (h->IsSpecialFile()) {
       mSpecialHandles.RemoveElement(h);
-    else
+    } else {
       mHandles.RemoveHandle(h);
+    }
   }
 
   // Assert the table is empty. When we are here, no new handles can be added
   // and handles will no longer remove them self from this table and we don't 
   // want to keep invalid handles here. Also, there is no lookup after this 
   // point to happen.
   MOZ_ASSERT(mHandles.HandleCount() == 0);
 
+  // Release trash directory enumerator
+  if (mTrashDirEnumerator) {
+    mTrashDirEnumerator->Close();
+    mTrashDirEnumerator = nullptr;
+  }
+
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::OnProfile()
 {
   LOG(("CacheFileIOManager::OnProfile() [gInstance=%p]", gInstance));
 
@@ -1209,62 +1239,67 @@ CacheFileIOManager::OnProfile()
   return NS_OK;
 }
 
 // static
 already_AddRefed<nsIEventTarget>
 CacheFileIOManager::IOTarget()
 {
   nsCOMPtr<nsIEventTarget> target;
-  if (gInstance && gInstance->mIOThread)
+  if (gInstance && gInstance->mIOThread) {
     target = gInstance->mIOThread->Target();
+  }
 
   return target.forget();
 }
 
 // static
 already_AddRefed<CacheIOThread>
 CacheFileIOManager::IOThread()
 {
   nsRefPtr<CacheIOThread> thread;
-  if (gInstance)
+  if (gInstance) {
     thread = gInstance->mIOThread;
+  }
 
   return thread.forget();
 }
 
 // static
 bool
 CacheFileIOManager::IsOnIOThread()
 {
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
-  if (ioMan && ioMan->mIOThread)
+  if (ioMan && ioMan->mIOThread) {
     return ioMan->mIOThread->IsCurrentThread();
+  }
 
   return false;
 }
 
 // static
 bool
 CacheFileIOManager::IsOnIOThreadOrCeased()
 {
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
-  if (ioMan && ioMan->mIOThread)
+  if (ioMan && ioMan->mIOThread) {
     return ioMan->mIOThread->IsCurrentThread();
+  }
 
   // Ceased...
   return true;
 }
 
 // static
 bool
 CacheFileIOManager::IsShutdown()
 {
-  if (!gInstance)
+  if (!gInstance) {
     return true;
+  }
   return gInstance->mShuttingDown;
 }
 
 // static
 nsresult
 CacheFileIOManager::ScheduleMetadataWrite(CacheFile * aFile)
 {
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
@@ -1395,18 +1430,19 @@ CacheFileIOManager::OpenFile(const nsACS
                              CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]",
        PromiseFlatCString(aKey).get(), aFlags, aCallback));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (!ioMan)
+  if (!ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   bool priority = aFlags & CacheFileIOManager::PRIORITY;
   nsRefPtr<OpenFileEvent> ev = new OpenFileEvent(aKey, aFlags, aCallback);
   rv = ioMan->mIOThread->Dispatch(ev, priority
     ? CacheIOThread::OPEN_PRIORITY
     : CacheIOThread::OPEN);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1420,18 +1456,19 @@ CacheFileIOManager::OpenFileInternal(con
 {
   LOG(("CacheFileIOManager::OpenFileInternal() [hash=%08x%08x%08x%08x%08x, "
        "flags=%d]", LOGSHA1(aHash), aFlags));
 
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
 
   nsresult rv;
 
-  if (mShuttingDown)
+  if (mShuttingDown) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   if (!mTreeCreated) {
     rv = CreateCacheTree();
     if (NS_FAILED(rv)) return rv;
   }
 
   nsCOMPtr<nsIFile> file;
   rv = GetFile(aHash, getter_AddRefs(file));
@@ -1476,31 +1513,31 @@ CacheFileIOManager::OpenFileInternal(con
     handle.swap(*_retval);
     return NS_OK;
   }
 
   bool exists;
   rv = file->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN)
+  if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
     return NS_ERROR_NOT_AVAILABLE;
+  }
 
   rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, getter_AddRefs(handle));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (exists) {
     rv = file->GetFileSize(&handle->mFileSize);
     NS_ENSURE_SUCCESS(rv, rv);
 
     handle->mFileExists = true;
 
     CacheIndex::EnsureEntryExists(aHash);
-  }
-  else {
+  } else {
     handle->mFileSize = 0;
 
     CacheIndex::AddEntry(aHash);
   }
 
   handle->mFile.swap(file);
   handle.swap(*_retval);
   return NS_OK;
@@ -1513,18 +1550,19 @@ CacheFileIOManager::OpenSpecialFileInter
 {
   LOG(("CacheFileIOManager::OpenSpecialFileInternal() [key=%s, flags=%d]",
        PromiseFlatCString(aKey).get(), aFlags));
 
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
 
   nsresult rv;
 
-  if (mShuttingDown)
+  if (mShuttingDown) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   if (!mTreeCreated) {
     rv = CreateCacheTree();
     if (NS_FAILED(rv)) return rv;
   }
 
   nsCOMPtr<nsIFile> file;
   rv = GetSpecialFile(aKey, getter_AddRefs(file));
@@ -1571,29 +1609,29 @@ CacheFileIOManager::OpenSpecialFileInter
     handle.swap(*_retval);
     return NS_OK;
   }
 
   bool exists;
   rv = file->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN)
+  if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
     return NS_ERROR_NOT_AVAILABLE;
+  }
 
   handle = new CacheFileHandle(aKey, aFlags & PRIORITY);
   mSpecialHandles.AppendElement(handle);
 
   if (exists) {
     rv = file->GetFileSize(&handle->mFileSize);
     NS_ENSURE_SUCCESS(rv, rv);
 
     handle->mFileExists = true;
-  }
-  else {
+  } else {
     handle->mFileSize = 0;
   }
 
   handle->mFile.swap(file);
   handle.swap(*_retval);
   return NS_OK;
 }
 
@@ -1618,37 +1656,39 @@ CacheFileIOManager::CloseHandleInternal(
   }
 
   if (!aHandle->IsSpecialFile() && !aHandle->mIsDoomed &&
       (aHandle->mInvalid || !aHandle->mFileExists)) {
     CacheIndex::RemoveEntry(aHandle->Hash());
   }
 
   // Remove the handle from hashtable
-  if (aHandle->IsSpecialFile())
+  if (aHandle->IsSpecialFile()) {
     mSpecialHandles.RemoveElement(aHandle);
-  else if (!mShuttingDown) // Don't touch after shutdown
+  } else if (!mShuttingDown) { // Don't touch after shutdown
     mHandles.RemoveHandle(aHandle);
+  }
 
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::Read(CacheFileHandle *aHandle, int64_t aOffset,
                          char *aBuf, int32_t aCount,
                          CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::Read() [handle=%p, offset=%lld, count=%d, "
        "listener=%p]", aHandle, aOffset, aCount, aCallback));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (aHandle->IsClosed() || !ioMan)
+  if (aHandle->IsClosed() || !ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   nsRefPtr<ReadEvent> ev = new ReadEvent(aHandle, aOffset, aBuf, aCount,
                                          aCallback);
   rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
     ? CacheIOThread::READ_PRIORITY
     : CacheIOThread::READ);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1667,52 +1707,54 @@ CacheFileIOManager::ReadInternal(CacheFi
   if (!aHandle->mFileExists) {
     NS_WARNING("Trying to read from non-existent file");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (!aHandle->mFD) {
     rv = OpenNSPRHandle(aHandle);
     NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else {
+  } else {
     NSPRHandleUsed(aHandle);
   }
 
   // Check again, OpenNSPRHandle could figure out the file was gone.
   if (!aHandle->mFileExists) {
     NS_WARNING("Trying to read from non-existent file");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
-  if (offset == -1)
+  if (offset == -1) {
     return NS_ERROR_FAILURE;
+  }
 
   int32_t bytesRead = PR_Read(aHandle->mFD, aBuf, aCount);
-  if (bytesRead != aCount)
+  if (bytesRead != aCount) {
     return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset,
                           const char *aBuf, int32_t aCount, bool aValidate,
                           CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::Write() [handle=%p, offset=%lld, count=%d, "
        "validate=%d, listener=%p]", aHandle, aOffset, aCount, aValidate,
        aCallback));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (aHandle->IsClosed() || !ioMan)
+  if (aHandle->IsClosed() || !ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   nsRefPtr<WriteEvent> ev = new WriteEvent(aHandle, aOffset, aBuf, aCount,
                                            aValidate, aCallback);
   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
@@ -1730,67 +1772,70 @@ CacheFileIOManager::WriteInternal(CacheF
   if (!aHandle->mFileExists) {
     rv = CreateFile(aHandle);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!aHandle->mFD) {
     rv = OpenNSPRHandle(aHandle);
     NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else {
+  } else {
     NSPRHandleUsed(aHandle);
   }
 
   // Check again, OpenNSPRHandle could figure out the file was gone.
   if (!aHandle->mFileExists) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Write invalidates the entry by default
   aHandle->mInvalid = true;
 
   int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
-  if (offset == -1)
+  if (offset == -1) {
     return NS_ERROR_FAILURE;
+  }
 
   int32_t bytesWritten = PR_Write(aHandle->mFD, aBuf, aCount);
 
   if (bytesWritten != -1 && aHandle->mFileSize < aOffset+bytesWritten) {
     aHandle->mFileSize = aOffset+bytesWritten;
 
     if (!aHandle->IsDoomed() && !aHandle->IsSpecialFile()) {
       uint32_t size = aHandle->FileSizeInK();
       CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, &size);
       EvictIfOverLimitInternal();
     }
   }
 
-  if (bytesWritten != aCount)
+  if (bytesWritten != aCount) {
     return NS_ERROR_FAILURE;
+  }
 
   // Write was successful and this write validates the entry (i.e. metadata)
-  if (aValidate)
+  if (aValidate) {
     aHandle->mInvalid = false;
+  }
 
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::DoomFile(CacheFileHandle *aHandle,
                              CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::DoomFile() [handle=%p, listener=%p]",
        aHandle, aCallback));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (aHandle->IsClosed() || !ioMan)
+  if (aHandle->IsClosed() || !ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   nsRefPtr<DoomFileEvent> ev = new DoomFileEvent(aHandle, aCallback);
   rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
     ? CacheIOThread::OPEN_PRIORITY
     : CacheIOThread::OPEN);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
@@ -1799,23 +1844,25 @@ CacheFileIOManager::DoomFile(CacheFileHa
 nsresult
 CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle)
 {
   LOG(("CacheFileIOManager::DoomFileInternal() [handle=%p]", aHandle));
   aHandle->Log();
 
   nsresult rv;
 
-  if (aHandle->IsDoomed())
+  if (aHandle->IsDoomed()) {
     return NS_OK;
+  }
 
   if (aHandle->mFileExists) {
     // we need to move the current file to the doomed directory
-    if (aHandle->mFD)
+    if (aHandle->mFD) {
       ReleaseNSPRHandleInternal(aHandle);
+    }
 
     // find unused filename
     nsCOMPtr<nsIFile> file;
     rv = GetDoomedFile(getter_AddRefs(file));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIFile> parentDir;
     rv = file->GetParent(getter_AddRefs(parentDir));
@@ -1825,25 +1872,25 @@ CacheFileIOManager::DoomFileInternal(Cac
     rv = file->GetNativeLeafName(leafName);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = aHandle->mFile->MoveToNative(parentDir, leafName);
     if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
       LOG(("  file already removed under our hands"));
       aHandle->mFileExists = false;
       rv = NS_OK;
-    }
-    else {
+    } else {
       NS_ENSURE_SUCCESS(rv, rv);
       aHandle->mFile.swap(file);
     }
   }
 
-  if (!aHandle->IsSpecialFile())
+  if (!aHandle->IsSpecialFile()) {
     CacheIndex::RemoveEntry(aHandle->Hash());
+  }
 
   aHandle->mIsDoomed = true;
 
   if (!aHandle->IsSpecialFile()) {
     nsRefPtr<CacheStorageService> storageService = CacheStorageService::Self();
     if (storageService) {
       nsAutoCString url;
       nsCOMPtr<nsILoadContextInfo> info;
@@ -1863,18 +1910,19 @@ CacheFileIOManager::DoomFileByKey(const 
                                   CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::DoomFileByKey() [key=%s, listener=%p]",
        PromiseFlatCString(aKey).get(), aCallback));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (!ioMan)
+  if (!ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   nsRefPtr<DoomFileByKeyEvent> ev = new DoomFileByKeyEvent(aKey, aCallback);
   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::OPEN);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -1883,46 +1931,50 @@ CacheFileIOManager::DoomFileByKeyInterna
 {
   LOG(("CacheFileIOManager::DoomFileByKeyInternal() [hash=%08x%08x%08x%08x%08x]"
        , LOGSHA1(aHash)));
 
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
 
   nsresult rv;
 
-  if (mShuttingDown)
+  if (mShuttingDown) {
     return NS_ERROR_NOT_INITIALIZED;
-
-  if (!mCacheDirectory)
+  }
+
+  if (!mCacheDirectory) {
     return NS_ERROR_FILE_INVALID_PATH;
+  }
 
   // Find active handle
   nsRefPtr<CacheFileHandle> handle;
   mHandles.GetHandle(aHash, true, getter_AddRefs(handle));
 
   if (handle) {
     handle->Log();
 
-    if (handle->IsDoomed())
+    if (handle->IsDoomed()) {
       return NS_OK;
+    }
 
     return DoomFileInternal(handle);
   }
 
   // There is no handle for this file, delete the file if exists
   nsCOMPtr<nsIFile> file;
   rv = GetFile(aHash, getter_AddRefs(file));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool exists;
   rv = file->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!exists)
+  if (!exists) {
     return NS_ERROR_NOT_AVAILABLE;
+  }
 
   LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file from "
        "disk"));
   rv = file->Remove(false);
   if (NS_FAILED(rv)) {
     NS_WARNING("Cannot remove old entry from the disk");
     LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file failed. "
          "[rv=0x%08x]", rv));
@@ -1936,18 +1988,19 @@ CacheFileIOManager::DoomFileByKeyInterna
 nsresult
 CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle *aHandle)
 {
   LOG(("CacheFileIOManager::ReleaseNSPRHandle() [handle=%p]", aHandle));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (aHandle->IsClosed() || !ioMan)
+  if (aHandle->IsClosed() || !ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   nsRefPtr<ReleaseNSPRHandleEvent> ev = new ReleaseNSPRHandleEvent(aHandle);
   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::CLOSE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -1975,53 +2028,56 @@ CacheFileIOManager::TruncateSeekSetEOF(C
                                        CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::TruncateSeekSetEOF() [handle=%p, truncatePos=%lld, "
        "EOFPos=%lld, listener=%p]", aHandle, aTruncatePos, aEOFPos, aCallback));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (aHandle->IsClosed() || !ioMan)
+  if (aHandle->IsClosed() || !ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   nsRefPtr<TruncateSeekSetEOFEvent> ev = new TruncateSeekSetEOFEvent(
                                            aHandle, aTruncatePos, aEOFPos,
                                            aCallback);
   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 // static
 void CacheFileIOManager::GetCacheDirectory(nsIFile** result)
 {
   *result = nullptr;
 
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
-  if (!ioMan)
+  if (!ioMan) {
     return;
+  }
 
   nsCOMPtr<nsIFile> file = ioMan->mCacheDirectory;
   file.forget(result);
 }
 
 static nsresult
 TruncFile(PRFileDesc *aFD, uint32_t aEOF)
 {
 #if defined(XP_UNIX)
   if (ftruncate(PR_FileDesc2NativeHandle(aFD), aEOF) != 0) {
     NS_ERROR("ftruncate failed");
     return NS_ERROR_FAILURE;
   }
 #elif defined(XP_WIN)
   int32_t cnt = PR_Seek(aFD, aEOF, PR_SEEK_SET);
-  if (cnt == -1)
+  if (cnt == -1) {
     return NS_ERROR_FAILURE;
+  }
   if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(aFD))) {
     NS_ERROR("SetEndOfFile failed");
     return NS_ERROR_FAILURE;
   }
 #else
   MOZ_ASSERT(false, "Not implemented!");
 #endif
 
@@ -2041,18 +2097,17 @@ CacheFileIOManager::TruncateSeekSetEOFIn
   if (!aHandle->mFileExists) {
     rv = CreateFile(aHandle);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!aHandle->mFD) {
     rv = OpenNSPRHandle(aHandle);
     NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else {
+  } else {
     NSPRHandleUsed(aHandle);
   }
 
   // Check again, OpenNSPRHandle could figure out the file was gone.
   if (!aHandle->mFileExists) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -2074,21 +2129,23 @@ CacheFileIOManager::RenameFile(CacheFile
                                CacheFileIOListener *aCallback)
 {
   LOG(("CacheFileIOManager::RenameFile() [handle=%p, newName=%s, listener=%p]",
        aHandle, PromiseFlatCString(aNewName).get(), aCallback));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (aHandle->IsClosed() || !ioMan)
+  if (aHandle->IsClosed() || !ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
-
-  if (!aHandle->IsSpecialFile())
+  }
+
+  if (!aHandle->IsSpecialFile()) {
     return NS_ERROR_UNEXPECTED;
+  }
 
   nsRefPtr<RenameFileEvent> ev = new RenameFileEvent(aHandle, aNewName,
                                                      aCallback);
   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
@@ -2099,18 +2156,19 @@ CacheFileIOManager::RenameFileInternal(C
 {
   LOG(("CacheFileIOManager::RenameFileInternal() [handle=%p, newName=%s]",
        aHandle, PromiseFlatCString(aNewName).get()));
 
   nsresult rv;
 
   MOZ_ASSERT(aHandle->IsSpecialFile());
 
-  if (aHandle->IsDoomed())
+  if (aHandle->IsDoomed()) {
     return NS_ERROR_NOT_AVAILABLE;
+  }
 
   // Doom old handle if it exists and is not doomed
   for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
     if (!mSpecialHandles[i]->IsDoomed() &&
         mSpecialHandles[i]->Key() == aNewName) {
       MOZ_ASSERT(aHandle != mSpecialHandles[i]);
       rv = DoomFileInternal(mSpecialHandles[i]);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -2137,36 +2195,38 @@ CacheFileIOManager::RenameFileInternal(C
     }
   }
 
   if (!aHandle->FileExists()) {
     aHandle->mKey = aNewName;
     return NS_OK;
   }
 
-  if (aHandle->mFD)
+  if (aHandle->mFD) {
     ReleaseNSPRHandleInternal(aHandle);
+  }
 
   rv = aHandle->mFile->MoveToNative(nullptr, aNewName);
   NS_ENSURE_SUCCESS(rv, rv);
 
   aHandle->mKey = aNewName;
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::EvictIfOverLimit()
 {
   LOG(("CacheFileIOManager::EvictIfOverLimit()"));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (!ioMan)
+  if (!ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   nsCOMPtr<nsIRunnable> ev;
   ev = NS_NewRunnableMethod(ioMan,
                             &CacheFileIOManager::EvictIfOverLimitInternal);
 
   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::EVICT);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2177,18 +2237,19 @@ nsresult
 CacheFileIOManager::EvictIfOverLimitInternal()
 {
   LOG(("CacheFileIOManager::EvictIfOverLimitInternal()"));
 
   nsresult rv;
 
   MOZ_ASSERT(mIOThread->IsCurrentThread());
 
-  if (mShuttingDown)
+  if (mShuttingDown) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   if (mOverLimitEvicting) {
     LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Eviction already "
          "running."));
     return NS_OK;
   }
 
   uint32_t cacheUsage;
@@ -2219,25 +2280,28 @@ CacheFileIOManager::EvictIfOverLimitInte
 
 nsresult
 CacheFileIOManager::OverLimitEvictionInternal()
 {
   LOG(("CacheFileIOManager::OverLimitEvictionInternal()"));
 
   nsresult rv;
 
+  MOZ_ASSERT(mIOThread->IsCurrentThread());
+
   // mOverLimitEvicting is accessed only on IO thread, so we can set it to false
   // here and set ti to true again once we dispatch another event that will
   // continue with the eviction. The reason why we do so is that we can fail
   // early anywhere in this method and the variable will contain a correct
   // value. Otherwise we would need to set it to false on every failing place.
   mOverLimitEvicting = false;
 
-  if (mShuttingDown)
+  if (mShuttingDown) {
     return NS_ERROR_NOT_INITIALIZED;
+  }
 
   TimeStamp start;
 
   while (true) {
     uint32_t cacheUsage;
     rv = CacheIndex::GetCacheSize(&cacheUsage);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2266,27 +2330,25 @@ CacheFileIOManager::OverLimitEvictionInt
     uint32_t cnt;
     static uint32_t consecutiveFailures = 0;
     rv = CacheIndex::GetEntryForEviction(&hash, &cnt);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = DoomFileByKeyInternal(&hash);
     if (NS_SUCCEEDED(rv)) {
       consecutiveFailures = 0;
-    }
-    else if (rv == NS_ERROR_NOT_AVAILABLE) {
+    } else if (rv == NS_ERROR_NOT_AVAILABLE) {
       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
            "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
       // TODO index is outdated, start update
 
       // Make sure index won't return the same entry again
       CacheIndex::RemoveEntry(&hash);
       consecutiveFailures = 0;
-    }
-    else {
+    } else {
       // This shouldn't normally happen, but the eviction must not fail
       // completely if we ever encounter this problem.
       NS_WARNING("CacheFileIOManager::OverLimitEvictionInternal() - Unexpected "
                  "failure of DoomFileByKeyInternal()");
 
       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
            "DoomFileByKeyInternal() failed. [rv=0x%08x]", rv));
 
@@ -2323,32 +2385,365 @@ CacheFileIOManager::OverLimitEvictionInt
   rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mOverLimitEvicting = true;
   return NS_OK;
 }
 
 nsresult
+CacheFileIOManager::TrashDirectory(nsIFile *aFile)
+{
+#ifdef PR_LOGGING
+  nsAutoCString path;
+  aFile->GetNativePath(path);
+#endif
+  LOG(("CacheFileIOManager::TrashDirectory() [file=%s]", path.get()));
+
+  nsresult rv;
+
+  MOZ_ASSERT(mIOThread->IsCurrentThread());
+  MOZ_ASSERT(mCacheDirectory);
+
+  // When the directory is empty, it is cheaper to remove it directly instead of
+  // using the trash mechanism.
+  bool isEmpty;
+  rv = IsEmptyDirectory(aFile, &isEmpty);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (isEmpty) {
+    rv = aFile->Remove(false);
+    LOG(("CacheFileIOManager::TrashDirectory() - Directory removed [rv=0x%08x]",
+         rv));
+    return rv;
+  }
+
+#ifdef DEBUG
+  nsCOMPtr<nsIFile> dirCheck;
+  rv = aFile->GetParent(getter_AddRefs(dirCheck));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool equals = false;
+  rv = dirCheck->Equals(mCacheDirectory, &equals);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  MOZ_ASSERT(equals);
+#endif
+
+  nsCOMPtr<nsIFile> dir, trash;
+  nsAutoCString leaf;
+
+  rv = aFile->Clone(getter_AddRefs(dir));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aFile->Clone(getter_AddRefs(trash));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  srand(static_cast<unsigned>(PR_Now()));
+  while (true) {
+    leaf = kTrashDir;
+    leaf.AppendInt(rand());
+    rv = trash->SetNativeLeafName(leaf);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool exists;
+    if (NS_SUCCEEDED(trash->Exists(&exists)) && !exists) {
+      break;
+    }
+  }
+
+  LOG(("CacheFileIOManager::TrashDirectory() - Renaming directory [leaf=%s]",
+       leaf.get()));
+
+  rv = dir->MoveToNative(nullptr, leaf);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  StartRemovingTrash();
+  return NS_OK;
+}
+
+void
+CacheFileIOManager::OnTrashTimer(nsITimer *aTimer, void *aClosure)
+{
+  LOG(("CacheFileIOManager::OnTrashTimer() [timer=%p, closure=%p]", aTimer,
+       aClosure));
+
+  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
+
+  if (!ioMan) {
+    return;
+  }
+
+  ioMan->mTrashTimer = nullptr;
+  ioMan->StartRemovingTrash();
+}
+
+nsresult
+CacheFileIOManager::StartRemovingTrash()
+{
+  LOG(("CacheFileIOManager::StartRemovingTrash()"));
+
+  nsresult rv;
+
+  MOZ_ASSERT(mIOThread->IsCurrentThread());
+
+  if (mShuttingDown) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if (!mCacheDirectory) {
+    return NS_ERROR_FILE_INVALID_PATH;
+  }
+
+  if (mTrashTimer) {
+    LOG(("CacheFileIOManager::StartRemovingTrash() - Trash timer exists."));
+    return NS_OK;
+  }
+
+  if (mRemovingTrashDirs) {
+    LOG(("CacheFileIOManager::StartRemovingTrash() - Trash removing in "
+         "progress."));
+    return NS_OK;
+  }
+
+  uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
+  if (elapsed < kRemoveTrashStartDelay) {
+    nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
+    MOZ_ASSERT(ioTarget);
+
+    rv = timer->SetTarget(ioTarget);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = timer->InitWithFuncCallback(CacheFileIOManager::OnTrashTimer, nullptr,
+                                     kRemoveTrashStartDelay - elapsed,
+                                     nsITimer::TYPE_ONE_SHOT);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mTrashTimer.swap(timer);
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIRunnable> ev;
+  ev = NS_NewRunnableMethod(this,
+                            &CacheFileIOManager::RemoveTrashInternal);
+
+  rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mRemovingTrashDirs = true;
+  return NS_OK;
+}
+
+nsresult
+CacheFileIOManager::RemoveTrashInternal()
+{
+  LOG(("CacheFileIOManager::RemoveTrashInternal()"));
+
+  nsresult rv;
+
+  MOZ_ASSERT(mIOThread->IsCurrentThread());
+
+  if (mShuttingDown) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  MOZ_ASSERT(!mTrashTimer);
+  MOZ_ASSERT(mRemovingTrashDirs);
+
+  if (!mTreeCreated) {
+    rv = CreateCacheTree();
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  // mRemovingTrashDirs is accessed only on IO thread, so we can drop the flag
+  // here and set it again once we dispatch a continuation event. By doing so,
+  // we don't have to drop the flag on any possible early return.
+  mRemovingTrashDirs = false;
+
+  TimeStamp start;
+
+  while (true) {
+    if (start.IsNull()) {
+      start = TimeStamp::NowLoRes();
+    } else {
+      static TimeDuration const kLimit = TimeDuration::FromMilliseconds(
+                                           kRemoveTrashLoopLimit);
+      TimeDuration elapsed = TimeStamp::NowLoRes() - start;
+      if (elapsed >= kLimit) {
+        LOG(("CacheFileIOManager::RemoveTrashInternal() - Breaking loop after "
+             "%u ms.", static_cast<uint32_t>(elapsed.ToMilliseconds())));
+        break;
+      }
+    }
+
+    // Find some trash directory
+    if (!mTrashDir) {
+      MOZ_ASSERT(!mTrashDirEnumerator);
+
+      rv = FindTrashDirToRemove();
+      if (rv == NS_ERROR_NOT_AVAILABLE) {
+        LOG(("CacheFileIOManager::RemoveTrashInternal() - No trash directory "
+             "found."));
+        return NS_OK;
+      }
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsISimpleEnumerator> enumerator;
+      rv = mTrashDir->GetDirectoryEntries(getter_AddRefs(enumerator));
+      if (NS_SUCCEEDED(rv)) {
+        mTrashDirEnumerator = do_QueryInterface(enumerator, &rv);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      continue; // check elapsed time
+    }
+
+    // We null out mTrashDirEnumerator once we remove all files in the
+    // directory, so remove the trash directory if we don't have enumerator.
+    if (!mTrashDirEnumerator) {
+      rv = mTrashDir->Remove(false);
+      if (NS_FAILED(rv)) {
+        // There is no reason why removing an empty directory should fail, but
+        // if it does, we should continue and try to remove all other trash
+        // directories.
+        nsAutoCString leafName;
+        mTrashDir->GetNativeLeafName(leafName);
+        mFailedTrashDirs.AppendElement(leafName);
+        LOG(("CacheFileIOManager::RemoveTrashInternal() - Cannot remove "
+             "trashdir. [name=%s]", leafName.get()));
+      }
+
+      mTrashDir = nullptr;
+      continue; // check elapsed time
+    }
+
+    nsCOMPtr<nsIFile> file;
+    rv = mTrashDirEnumerator->GetNextFile(getter_AddRefs(file));
+    if (!file) {
+      mTrashDirEnumerator->Close();
+      mTrashDirEnumerator = nullptr;
+      continue; // check elapsed time
+    } else {
+      bool isDir = false;
+      file->IsDirectory(&isDir);
+      if (isDir) {
+        NS_WARNING("Found a directory in a trash directory! It will be removed "
+                   "recursively, but this can block IO thread for a while!");
+#ifdef PR_LOGGING
+        nsAutoCString path;
+        file->GetNativePath(path);
+#endif
+        LOG(("CacheFileIOManager::RemoveTrashInternal() - Found a directory in a trash "
+            "directory! It will be removed recursively, but this can block IO "
+            "thread for a while! [file=%s]", path.get()));
+      }
+      file->Remove(isDir);
+    }
+  }
+
+  nsCOMPtr<nsIRunnable> ev;
+  ev = NS_NewRunnableMethod(this,
+                            &CacheFileIOManager::RemoveTrashInternal);
+
+  rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mRemovingTrashDirs = true;
+  return NS_OK;
+}
+
+nsresult
+CacheFileIOManager::FindTrashDirToRemove()
+{
+  LOG(("CacheFileIOManager::FindTrashDirToRemove()"));
+
+  nsresult rv;
+
+  MOZ_ASSERT(mIOThread->IsCurrentThread());
+
+  nsCOMPtr<nsISimpleEnumerator> iter;
+  rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(iter));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool more;
+  nsCOMPtr<nsISupports> elem;
+
+  while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
+    rv = iter->GetNext(getter_AddRefs(elem));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
+    if (!file) {
+      continue;
+    }
+
+    bool isDir = false;
+    file->IsDirectory(&isDir);
+    if (!isDir) {
+      continue;
+    }
+
+    nsAutoCString leafName;
+    rv = file->GetNativeLeafName(leafName);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    if (leafName.Length() < strlen(kTrashDir)) {
+      continue;
+    }
+
+    if (!StringBeginsWith(leafName, NS_LITERAL_CSTRING(kTrashDir))) {
+      continue;
+    }
+
+    if (mFailedTrashDirs.Contains(leafName)) {
+      continue;
+    }
+
+    LOG(("CacheFileIOManager::FindTrashDirToRemove() - Returning directory %s",
+         leafName.get()));
+
+    mTrashDir = file;
+    return NS_OK;
+  }
+
+  // When we're here we've tried to delete all trash directories. Clear
+  // mFailedTrashDirs so we will try to delete them again when we start removing
+  // trash directories next time.
+  mFailedTrashDirs.Clear();
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult
 CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
                                    uint32_t         aAppId,
                                    bool             aAnonymous,
                                    bool             aInBrowser)
 {
   LOG(("CacheFileIOManager::InitIndexEntry() [handle=%p, appId=%u, anonymous=%d"
        ", inBrowser=%d]", aHandle, aAppId, aAnonymous, aInBrowser));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (aHandle->IsClosed() || !ioMan)
+  if (aHandle->IsClosed() || !ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
-
-  if (aHandle->IsSpecialFile())
+  }
+
+  if (aHandle->IsSpecialFile()) {
     return NS_ERROR_UNEXPECTED;
+  }
 
   nsRefPtr<InitIndexEntryEvent> ev =
     new InitIndexEntryEvent(aHandle, aAppId, aAnonymous, aInBrowser);
   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
@@ -2361,21 +2756,23 @@ CacheFileIOManager::UpdateIndexEntry(Cac
   LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
        "expirationTime=%s]", aHandle,
        aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
        aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : ""));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (aHandle->IsClosed() || !ioMan)
+  if (aHandle->IsClosed() || !ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
-
-  if (aHandle->IsSpecialFile())
+  }
+
+  if (aHandle->IsSpecialFile()) {
     return NS_ERROR_UNEXPECTED;
+  }
 
   nsRefPtr<UpdateIndexEntryEvent> ev =
     new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime);
   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::WRITE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
@@ -2384,21 +2781,23 @@ nsresult
 CacheFileIOManager::EnumerateEntryFiles(EEnumerateMode aMode,
                                         CacheEntriesEnumerator** aEnumerator)
 {
   LOG(("CacheFileIOManager::EnumerateEntryFiles(%d)", aMode));
 
   nsresult rv;
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
 
-  if (!ioMan)
+  if (!ioMan) {
     return NS_ERROR_NOT_INITIALIZED;
-
-  if (!ioMan->mCacheDirectory)
+  }
+
+  if (!ioMan->mCacheDirectory) {
     return NS_ERROR_FILE_NOT_FOUND;
+  }
 
   nsCOMPtr<nsIFile> file;
   rv = ioMan->mCacheDirectory->Clone(getter_AddRefs(file));
   NS_ENSURE_SUCCESS(rv, rv);
 
   switch (aMode) {
   case ENTRIES:
     rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
@@ -2465,35 +2864,38 @@ CacheFileIOManager::HashToStr(const SHA1
     _retval.Append(hexChars[(*aHash)[i] >> 4]);
     _retval.Append(hexChars[(*aHash)[i] & 0xF]);
   }
 }
 
 nsresult
 CacheFileIOManager::StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval)
 {
-  if (aHash.Length() != 2*sizeof(SHA1Sum::Hash))
+  if (aHash.Length() != 2*sizeof(SHA1Sum::Hash)) {
     return NS_ERROR_INVALID_ARG;
+  }
 
   for (uint32_t i=0 ; i<aHash.Length() ; i++) {
     uint8_t value;
 
-    if (aHash[i] >= '0' && aHash[i] <= '9')
+    if (aHash[i] >= '0' && aHash[i] <= '9') {
       value = aHash[i] - '0';
-    else if (aHash[i] >= 'A' && aHash[i] <= 'F')
+    } else if (aHash[i] >= 'A' && aHash[i] <= 'F') {
       value = aHash[i] - 'A' + 10;
-    else if (aHash[i] >= 'a' && aHash[i] <= 'f')
+    } else if (aHash[i] >= 'a' && aHash[i] <= 'f') {
       value = aHash[i] - 'a' + 10;
-    else
+    } else {
       return NS_ERROR_INVALID_ARG;
-
-    if (i%2 == 0)
+    }
+
+    if (i%2 == 0) {
       (reinterpret_cast<uint8_t *>(_retval))[i/2] = value << 4;
-    else
+    } else {
       (reinterpret_cast<uint8_t *>(_retval))[i/2] += value;
+    }
   }
 
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval)
 {
@@ -2549,86 +2951,139 @@ CacheFileIOManager::GetDoomedFile(nsIFil
   uint32_t iter=0;
   while (true) {
     iter++;
     leafName.AppendInt(rand());
     rv = file->SetNativeLeafName(leafName);
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool exists;
-    if (NS_SUCCEEDED(file->Exists(&exists)) && !exists)
+    if (NS_SUCCEEDED(file->Exists(&exists)) && !exists) {
       break;
+    }
 
     leafName.Truncate();
   }
 
 //  Telemetry::Accumulate(Telemetry::DISK_CACHE_GETDOOMEDFILE_ITERATIONS, iter);
 
   file.swap(*_retval);
   return NS_OK;
 }
 
 nsresult
-CacheFileIOManager::CheckAndCreateDir(nsIFile *aFile, const char *aDir)
+CacheFileIOManager::IsEmptyDirectory(nsIFile *aFile, bool *_retval)
+{
+  MOZ_ASSERT(mIOThread->IsCurrentThread());
+
+  nsresult rv;
+
+  nsCOMPtr<nsISimpleEnumerator> enumerator;
+  rv = aFile->GetDirectoryEntries(getter_AddRefs(enumerator));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool hasMoreElements = false;
+  rv = enumerator->HasMoreElements(&hasMoreElements);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *_retval = !hasMoreElements;
+  return NS_OK;
+}
+
+nsresult
+CacheFileIOManager::CheckAndCreateDir(nsIFile *aFile, const char *aDir,
+                                      bool aEnsureEmptyDir)
 {
   nsresult rv;
-  bool exists;
 
   nsCOMPtr<nsIFile> file;
   if (!aDir) {
     file = aFile;
   } else {
     nsAutoCString dir(aDir);
     rv = aFile->Clone(getter_AddRefs(file));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = file->AppendNative(dir);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  bool exists = false;
   rv = file->Exists(&exists);
-  if (NS_SUCCEEDED(rv) && !exists)
+  if (NS_SUCCEEDED(rv) && exists) {
+    bool isDirectory = false;
+    rv = file->IsDirectory(&isDirectory);
+    if (NS_FAILED(rv) || !isDirectory) {
+      // Try to remove the file
+      rv = file->Remove(false);
+      if (NS_SUCCEEDED(rv)) {
+        exists = false;
+      }
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (aEnsureEmptyDir && NS_SUCCEEDED(rv) && exists) {
+    bool isEmpty;
+    rv = IsEmptyDirectory(file, &isEmpty);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!isEmpty) {
+      rv = TrashDirectory(file);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      exists = false;
+    }
+  }
+
+  if (NS_SUCCEEDED(rv) && !exists) {
     rv = file->Create(nsIFile::DIRECTORY_TYPE, 0700);
+  }
   if (NS_FAILED(rv)) {
     NS_WARNING("Cannot create directory");
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::CreateCacheTree()
 {
+  MOZ_ASSERT(mIOThread->IsCurrentThread());
   MOZ_ASSERT(!mTreeCreated);
 
-  if (!mCacheDirectory)
+  if (!mCacheDirectory) {
     return NS_ERROR_FILE_INVALID_PATH;
+  }
 
   nsresult rv;
 
   // ensure parent directory exists
   nsCOMPtr<nsIFile> parentDir;
   rv = mCacheDirectory->GetParent(getter_AddRefs(parentDir));
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = CheckAndCreateDir(parentDir, nullptr);
+  rv = CheckAndCreateDir(parentDir, nullptr, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // ensure cache directory exists
-  rv = CheckAndCreateDir(mCacheDirectory, nullptr);
+  rv = CheckAndCreateDir(mCacheDirectory, nullptr, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // ensure entries directory exists
-  rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir);
+  rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // ensure doomed directory exists
-  rv = CheckAndCreateDir(mCacheDirectory, kDoomedDir);
+  rv = CheckAndCreateDir(mCacheDirectory, kDoomedDir, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mTreeCreated = true;
+
+  StartRemovingTrash();
+
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate)
 {
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
   MOZ_ASSERT(!aHandle->mFD);
@@ -2646,18 +3101,17 @@ CacheFileIOManager::OpenNSPRHandle(Cache
   }
 
   if (aCreate) {
     rv = aHandle->mFile->OpenNSPRFileDesc(
            PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, &aHandle->mFD);
     NS_ENSURE_SUCCESS(rv, rv);
 
     aHandle->mFileExists = true;
-  }
-  else {
+  } else {
     rv = aHandle->mFile->OpenNSPRFileDesc(PR_RDWR, 0600, &aHandle->mFD);
     if (NS_ERROR_FILE_NOT_FOUND == rv) {
       LOG(("  file doesn't exists"));
       aHandle->mFileExists = false;
       return DoomFileInternal(aHandle);
     }
     NS_ENSURE_SUCCESS(rv, rv);
   }
--- a/netwerk/cache2/CacheFileIOManager.h
+++ b/netwerk/cache2/CacheFileIOManager.h
@@ -6,34 +6,38 @@
 #define CacheFileIOManager__h__
 
 #include "CacheIOThread.h"
 #include "CacheEntriesEnumerator.h"
 #include "nsIEventTarget.h"
 #include "nsITimer.h"
 #include "nsCOMPtr.h"
 #include "mozilla/SHA1.h"
+#include "mozilla/TimeStamp.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsTHashtable.h"
 #include "prio.h"
 
 //#define DEBUG_HANDLES 1
 
 class nsIFile;
+class nsITimer;
+class nsIDirectoryEnumerator;
 
 namespace mozilla {
 namespace net {
 
 #ifdef DEBUG_HANDLES
 class CacheFileHandlesEntry;
 #endif
 
 const char kEntriesDir[] = "entries";
 const char kDoomedDir[]  = "doomed";
+const char kTrashDir[]   = "trash";
 
 
 class CacheFileHandle : public nsISupports
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   bool DispatchRelease();
 
@@ -298,40 +302,54 @@ private:
   nsresult ReleaseNSPRHandleInternal(CacheFileHandle *aHandle);
   nsresult TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
                                       int64_t aTruncatePos, int64_t aEOFPos);
   nsresult RenameFileInternal(CacheFileHandle *aHandle,
                               const nsACString &aNewName);
   nsresult EvictIfOverLimitInternal();
   nsresult OverLimitEvictionInternal();
 
+  nsresult TrashDirectory(nsIFile *aFile);
+  static void OnTrashTimer(nsITimer *aTimer, void *aClosure);
+  nsresult StartRemovingTrash();
+  nsresult RemoveTrashInternal();
+  nsresult FindTrashDirToRemove();
+
   nsresult CreateFile(CacheFileHandle *aHandle);
   static void HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval);
   static nsresult StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval);
   nsresult GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval);
   nsresult GetSpecialFile(const nsACString &aKey, nsIFile **_retval);
   nsresult GetDoomedFile(nsIFile **_retval);
-  nsresult CheckAndCreateDir(nsIFile *aFile, const char *aDir);
+  nsresult IsEmptyDirectory(nsIFile *aFile, bool *_retval);
+  nsresult CheckAndCreateDir(nsIFile *aFile, const char *aDir,
+                             bool aEnsureEmptyDir);
   nsresult CreateCacheTree();
   nsresult OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate = false);
   void     NSPRHandleUsed(CacheFileHandle *aHandle);
 
   nsresult ScheduleMetadataWriteInternal(CacheFile * aFile);
   nsresult UnscheduleMetadataWriteInternal(CacheFile * aFile);
   nsresult ShutdownMetadataWriteSchedulingInternal();
 
   static CacheFileIOManager           *gInstance;
+  TimeStamp                            mStartTime;
   bool                                 mShuttingDown;
   nsRefPtr<CacheIOThread>              mIOThread;
   nsCOMPtr<nsIFile>                    mCacheDirectory;
   bool                                 mTreeCreated;
   CacheFileHandles                     mHandles;
   nsTArray<CacheFileHandle *>          mHandlesByLastUsed;
   nsTArray<nsRefPtr<CacheFileHandle> > mSpecialHandles;
   nsTArray<nsRefPtr<CacheFile> >       mScheduledMetadataWrites;
   nsCOMPtr<nsITimer>                   mMetadataWritesTimer;
   bool                                 mOverLimitEvicting;
+  bool                                 mRemovingTrashDirs;
+  nsCOMPtr<nsITimer>                   mTrashTimer;
+  nsCOMPtr<nsIFile>                    mTrashDir;
+  nsCOMPtr<nsIDirectoryEnumerator>     mTrashDirEnumerator;
+  nsTArray<nsCString>                  mFailedTrashDirs;
 };
 
 } // net
 } // mozilla
 
 #endif
--- a/toolkit/components/places/tests/expiration/xpcshell.ini
+++ b/toolkit/components/places/tests/expiration/xpcshell.ini
@@ -11,10 +11,12 @@ skip-if = os == "android"
 [test_annos_expire_session.js]
 [test_debug_expiration.js]
 [test_idle_daily.js]
 [test_notifications.js]
 [test_notifications_onDeleteURI.js]
 [test_notifications_onDeleteVisits.js]
 [test_outdated_analyze.js]
 [test_pref_interval.js]
+# Crashes when timer is used on non-main thread due to JS implemetation in this test
+skip-if = "JS implementation of nsITimer"
 [test_pref_maxpages.js]
 [test_removeAllPages.js]