dom/quota/QuotaManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "QuotaManager.h"
     9 #include "mozIApplicationClearPrivateDataParams.h"
    10 #include "nsIBinaryInputStream.h"
    11 #include "nsIBinaryOutputStream.h"
    12 #include "nsIFile.h"
    13 #include "nsIObserverService.h"
    14 #include "nsIOfflineStorage.h"
    15 #include "nsIPrincipal.h"
    16 #include "nsIQuotaRequest.h"
    17 #include "nsIRunnable.h"
    18 #include "nsISimpleEnumerator.h"
    19 #include "nsIScriptObjectPrincipal.h"
    20 #include "nsIScriptSecurityManager.h"
    21 #include "nsITimer.h"
    22 #include "nsIURI.h"
    23 #include "nsIUsageCallback.h"
    25 #include <algorithm>
    26 #include "GeckoProfiler.h"
    27 #include "mozilla/Atomics.h"
    28 #include "mozilla/CondVar.h"
    29 #include "mozilla/dom/asmjscache/AsmJSCache.h"
    30 #include "mozilla/dom/file/FileService.h"
    31 #include "mozilla/dom/indexedDB/Client.h"
    32 #include "mozilla/Mutex.h"
    33 #include "mozilla/LazyIdleThread.h"
    34 #include "mozilla/Preferences.h"
    35 #include "mozilla/Services.h"
    36 #include "nsAppDirectoryServiceDefs.h"
    37 #include "nsComponentManagerUtils.h"
    38 #include "nsContentUtils.h"
    39 #include "nsCRTGlue.h"
    40 #include "nsDirectoryServiceUtils.h"
    41 #include "nsNetUtil.h"
    42 #include "nsScriptSecurityManager.h"
    43 #include "nsThreadUtils.h"
    44 #include "nsXULAppAPI.h"
    45 #include "xpcpublic.h"
    47 #include "AcquireListener.h"
    48 #include "CheckQuotaHelper.h"
    49 #include "OriginCollection.h"
    50 #include "OriginOrPatternString.h"
    51 #include "QuotaObject.h"
    52 #include "StorageMatcher.h"
    53 #include "UsageInfo.h"
    54 #include "Utilities.h"
    56 // The amount of time, in milliseconds, that our IO thread will stay alive
    57 // after the last event it processes.
    58 #define DEFAULT_THREAD_TIMEOUT_MS 30000
    60 // The amount of time, in milliseconds, that we will wait for active storage
    61 // transactions on shutdown before aborting them.
    62 #define DEFAULT_SHUTDOWN_TIMER_MS 30000
    64 // Preference that users can set to override DEFAULT_QUOTA_MB
    65 #define PREF_STORAGE_QUOTA "dom.indexedDB.warningQuota"
    67 // Preference that users can set to override temporary storage smart limit
    68 // calculation.
    69 #define PREF_FIXED_LIMIT "dom.quotaManager.temporaryStorage.fixedLimit"
    70 #define PREF_CHUNK_SIZE "dom.quotaManager.temporaryStorage.chunkSize"
    72 // Preference that is used to enable testing features
    73 #define PREF_TESTING_FEATURES "dom.quotaManager.testing"
    75 // profile-before-change, when we need to shut down quota manager
    76 #define PROFILE_BEFORE_CHANGE_OBSERVER_ID "profile-before-change"
    78 // The name of the file that we use to load/save the last access time of an
    79 // origin.
    80 #define METADATA_FILE_NAME ".metadata"
    82 #define PERMISSION_DEFAUT_PERSISTENT_STORAGE "default-persistent-storage"
    84 #define KB * 1024ULL
    85 #define MB * 1024ULL KB
    86 #define GB * 1024ULL MB
    88 USING_QUOTA_NAMESPACE
    89 using namespace mozilla::dom;
    90 using mozilla::dom::file::FileService;
    92 static_assert(
    93   static_cast<uint32_t>(StorageType::Persistent) ==
    94   static_cast<uint32_t>(PERSISTENCE_TYPE_PERSISTENT),
    95   "Enum values should match.");
    97 static_assert(
    98   static_cast<uint32_t>(StorageType::Temporary) ==
    99   static_cast<uint32_t>(PERSISTENCE_TYPE_TEMPORARY),
   100   "Enum values should match.");
   102 BEGIN_QUOTA_NAMESPACE
   104 // A struct that contains the information corresponding to a pending or
   105 // running operation that requires synchronization (e.g. opening a db,
   106 // clearing dbs for an origin, etc).
   107 struct SynchronizedOp
   108 {
   109   SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
   110                  Nullable<PersistenceType> aPersistenceType,
   111                  const nsACString& aId);
   113   ~SynchronizedOp();
   115   // Test whether this SynchronizedOp needs to wait for the given op.
   116   bool
   117   MustWaitFor(const SynchronizedOp& aOp);
   119   void
   120   DelayRunnable(nsIRunnable* aRunnable);
   122   void
   123   DispatchDelayedRunnables();
   125   const OriginOrPatternString mOriginOrPattern;
   126   Nullable<PersistenceType> mPersistenceType;
   127   nsCString mId;
   128   nsRefPtr<AcquireListener> mListener;
   129   nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
   130   ArrayCluster<nsIOfflineStorage*> mStorages;
   131 };
   133 class CollectOriginsHelper MOZ_FINAL : public nsRunnable
   134 {
   135 public:
   136   CollectOriginsHelper(mozilla::Mutex& aMutex, uint64_t aMinSizeToBeFreed);
   138   NS_IMETHOD
   139   Run();
   141   // Blocks the current thread until origins are collected on the main thread.
   142   // The returned value contains an aggregate size of those origins.
   143   int64_t
   144   BlockAndReturnOriginsForEviction(nsTArray<OriginInfo*>& aOriginInfos);
   146 private:
   147   ~CollectOriginsHelper()
   148   { }
   150   uint64_t mMinSizeToBeFreed;
   152   mozilla::Mutex& mMutex;
   153   mozilla::CondVar mCondVar;
   155   // The members below are protected by mMutex.
   156   nsTArray<OriginInfo*> mOriginInfos;
   157   uint64_t mSizeToBeFreed;
   158   bool mWaiting;
   159 };
   161 // Responsible for clearing the storage files for a particular origin on the
   162 // IO thread. Created when nsIQuotaManager::ClearStoragesForURI is called.
   163 // Runs three times, first on the main thread, next on the IO thread, and then
   164 // finally again on the main thread. While on the IO thread the runnable will
   165 // actually remove the origin's storage files and the directory that contains
   166 // them before dispatching itself back to the main thread. When back on the main
   167 // thread the runnable will notify the QuotaManager that the job has been
   168 // completed.
   169 class OriginClearRunnable MOZ_FINAL : public nsRunnable,
   170                                       public AcquireListener
   171 {
   172   enum CallbackState {
   173     // Not yet run.
   174     Pending = 0,
   176     // Running on the main thread in the callback for OpenAllowed.
   177     OpenAllowed,
   179     // Running on the IO thread.
   180     IO,
   182     // Running on the main thread after all work is done.
   183     Complete
   184   };
   186 public:
   187   NS_DECL_ISUPPORTS_INHERITED
   189   OriginClearRunnable(const OriginOrPatternString& aOriginOrPattern,
   190                       Nullable<PersistenceType> aPersistenceType)
   191   : mOriginOrPattern(aOriginOrPattern),
   192     mPersistenceType(aPersistenceType),
   193     mCallbackState(Pending)
   194   { }
   196   NS_IMETHOD
   197   Run();
   199   // AcquireListener override
   200   virtual nsresult
   201   OnExclusiveAccessAcquired() MOZ_OVERRIDE;
   203   void
   204   AdvanceState()
   205   {
   206     switch (mCallbackState) {
   207       case Pending:
   208         mCallbackState = OpenAllowed;
   209         return;
   210       case OpenAllowed:
   211         mCallbackState = IO;
   212         return;
   213       case IO:
   214         mCallbackState = Complete;
   215         return;
   216       default:
   217         NS_NOTREACHED("Can't advance past Complete!");
   218     }
   219   }
   221   static void
   222   InvalidateOpenedStorages(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
   223                            void* aClosure);
   225   void
   226   DeleteFiles(QuotaManager* aQuotaManager,
   227               PersistenceType aPersistenceType);
   229 private:
   230   OriginOrPatternString mOriginOrPattern;
   231   Nullable<PersistenceType> mPersistenceType;
   232   CallbackState mCallbackState;
   233 };
   235 // Responsible for calculating the amount of space taken up by storages of a
   236 // certain origin. Created when nsIQuotaManager::GetUsageForURI is called.
   237 // May be canceled with nsIQuotaRequest::Cancel. Runs three times, first
   238 // on the main thread, next on the IO thread, and then finally again on the main
   239 // thread. While on the IO thread the runnable will calculate the size of all
   240 // files in the origin's directory before dispatching itself back to the main
   241 // thread. When on the main thread the runnable will call the callback and then
   242 // notify the QuotaManager that the job has been completed.
   243 class AsyncUsageRunnable MOZ_FINAL : public UsageInfo,
   244                                      public nsRunnable,
   245                                      public nsIQuotaRequest
   246 {
   247   enum CallbackState {
   248     // Not yet run.
   249     Pending = 0,
   251     // Running on the main thread in the callback for OpenAllowed.
   252     OpenAllowed,
   254     // Running on the IO thread.
   255     IO,
   257     // Running on the main thread after all work is done.
   258     Complete,
   260     // Running on the main thread after skipping the work
   261     Shortcut
   262   };
   264 public:
   265   NS_DECL_ISUPPORTS_INHERITED
   266   NS_DECL_NSIQUOTAREQUEST
   268   AsyncUsageRunnable(uint32_t aAppId,
   269                      bool aInMozBrowserOnly,
   270                      const nsACString& aGroup,
   271                      const OriginOrPatternString& aOrigin,
   272                      nsIURI* aURI,
   273                      nsIUsageCallback* aCallback);
   275   NS_IMETHOD
   276   Run();
   278   void
   279   AdvanceState()
   280   {
   281     switch (mCallbackState) {
   282       case Pending:
   283         mCallbackState = OpenAllowed;
   284         return;
   285       case OpenAllowed:
   286         mCallbackState = IO;
   287         return;
   288       case IO:
   289         mCallbackState = Complete;
   290         return;
   291       default:
   292         NS_NOTREACHED("Can't advance past Complete!");
   293     }
   294   }
   296   nsresult
   297   TakeShortcut();
   299 private:
   300   // Run calls the RunInternal method and makes sure that we always dispatch
   301   // to the main thread in case of an error.
   302   inline nsresult
   303   RunInternal();
   305   nsresult
   306   AddToUsage(QuotaManager* aQuotaManager,
   307              PersistenceType aPersistenceType);
   309   nsCOMPtr<nsIURI> mURI;
   310   nsCOMPtr<nsIUsageCallback> mCallback;
   311   uint32_t mAppId;
   312   nsCString mGroup;
   313   OriginOrPatternString mOrigin;
   314   CallbackState mCallbackState;
   315   bool mInMozBrowserOnly;
   316 };
   318 class ResetOrClearRunnable MOZ_FINAL : public nsRunnable,
   319                                        public AcquireListener
   320 {
   321   enum CallbackState {
   322     // Not yet run.
   323     Pending = 0,
   325     // Running on the main thread in the callback for OpenAllowed.
   326     OpenAllowed,
   328     // Running on the IO thread.
   329     IO,
   331     // Running on the main thread after all work is done.
   332     Complete
   333   };
   335 public:
   336   NS_DECL_ISUPPORTS_INHERITED
   338   ResetOrClearRunnable(bool aClear)
   339   : mCallbackState(Pending),
   340     mClear(aClear)
   341   { }
   343   NS_IMETHOD
   344   Run();
   346   // AcquireListener override
   347   virtual nsresult
   348   OnExclusiveAccessAcquired() MOZ_OVERRIDE;
   350   void
   351   AdvanceState()
   352   {
   353     switch (mCallbackState) {
   354       case Pending:
   355         mCallbackState = OpenAllowed;
   356         return;
   357       case OpenAllowed:
   358         mCallbackState = IO;
   359         return;
   360       case IO:
   361         mCallbackState = Complete;
   362         return;
   363       default:
   364         NS_NOTREACHED("Can't advance past Complete!");
   365     }
   366   }
   368   static void
   369   InvalidateOpenedStorages(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
   370                            void* aClosure);
   372   void
   373   DeleteFiles(QuotaManager* aQuotaManager,
   374               PersistenceType aPersistenceType);
   376 private:
   377   CallbackState mCallbackState;
   378   bool mClear;
   379 };
   381 // Responsible for finalizing eviction of certian origins (storage files have
   382 // been already cleared, we just need to release IO thread only objects and
   383 // allow next synchronized ops for evicted origins). Created when
   384 // QuotaManager::FinalizeOriginEviction is called. Runs three times, first
   385 // on the main thread, next on the IO thread, and then finally again on the main
   386 // thread. While on the IO thread the runnable will release IO thread only
   387 // objects before dispatching itself back to the main thread. When back on the
   388 // main thread the runnable will call QuotaManager::AllowNextSynchronizedOp.
   389 // The runnable can also run in a shortened mode (runs only twice).
   390 class FinalizeOriginEvictionRunnable MOZ_FINAL : public nsRunnable
   391 {
   392   enum CallbackState {
   393     // Not yet run.
   394     Pending = 0,
   396     // Running on the main thread in the callback for OpenAllowed.
   397     OpenAllowed,
   399     // Running on the IO thread.
   400     IO,
   402     // Running on the main thread after IO work is done.
   403     Complete
   404   };
   406 public:
   407   FinalizeOriginEvictionRunnable(nsTArray<nsCString>& aOrigins)
   408   : mCallbackState(Pending)
   409   {
   410     mOrigins.SwapElements(aOrigins);
   411   }
   413   NS_IMETHOD
   414   Run();
   416   void
   417   AdvanceState()
   418   {
   419     switch (mCallbackState) {
   420       case Pending:
   421         mCallbackState = OpenAllowed;
   422         return;
   423       case OpenAllowed:
   424         mCallbackState = IO;
   425         return;
   426       case IO:
   427         mCallbackState = Complete;
   428         return;
   429       default:
   430         MOZ_ASSUME_UNREACHABLE("Can't advance past Complete!");
   431     }
   432   }
   434   nsresult
   435   Dispatch();
   437   nsresult
   438   RunImmediately();
   440 private:
   441   CallbackState mCallbackState;
   442   nsTArray<nsCString> mOrigins;
   443 };
   445 bool
   446 IsOnIOThread()
   447 {
   448   QuotaManager* quotaManager = QuotaManager::Get();
   449   NS_ASSERTION(quotaManager, "Must have a manager here!");
   451   bool currentThread;
   452   return NS_SUCCEEDED(quotaManager->IOThread()->
   453                       IsOnCurrentThread(&currentThread)) && currentThread;
   454 }
   456 void
   457 AssertIsOnIOThread()
   458 {
   459   NS_ASSERTION(IsOnIOThread(), "Running on the wrong thread!");
   460 }
   462 void
   463 AssertCurrentThreadOwnsQuotaMutex()
   464 {
   465 #ifdef DEBUG
   466   QuotaManager* quotaManager = QuotaManager::Get();
   467   NS_ASSERTION(quotaManager, "Must have a manager here!");
   469   quotaManager->AssertCurrentThreadOwnsQuotaMutex();
   470 #endif
   471 }
   473 END_QUOTA_NAMESPACE
   475 namespace {
   477 // Amount of space that storages may use by default in megabytes.
   478 static const int32_t  kDefaultQuotaMB =             50;
   481 QuotaManager* gInstance = nullptr;
   482 mozilla::Atomic<bool> gShutdown(false);
   484 int32_t gStorageQuotaMB = kDefaultQuotaMB;
   486 // Constants for temporary storage limit computing.
   487 static const int32_t kDefaultFixedLimitKB = -1;
   488 static const uint32_t kDefaultChunkSizeKB = 10 * 1024;
   489 int32_t gFixedLimitKB = kDefaultFixedLimitKB;
   490 uint32_t gChunkSizeKB = kDefaultChunkSizeKB;
   492 bool gTestingEnabled = false;
   494 // A callback runnable used by the TransactionPool when it's safe to proceed
   495 // with a SetVersion/DeleteDatabase/etc.
   496 class WaitForTransactionsToFinishRunnable MOZ_FINAL : public nsRunnable
   497 {
   498 public:
   499   WaitForTransactionsToFinishRunnable(SynchronizedOp* aOp)
   500   : mOp(aOp), mCountdown(1)
   501   {
   502     NS_ASSERTION(mOp, "Why don't we have a runnable?");
   503     NS_ASSERTION(mOp->mStorages.IsEmpty(), "We're here too early!");
   504     NS_ASSERTION(mOp->mListener,
   505                  "What are we supposed to do when we're done?");
   506     NS_ASSERTION(mCountdown, "Wrong countdown!");
   507   }
   509   NS_IMETHOD
   510   Run();
   512   void
   513   AddRun()
   514   {
   515     mCountdown++;
   516   }
   518 private:
   519   // The QuotaManager holds this alive.
   520   SynchronizedOp* mOp;
   521   uint32_t mCountdown;
   522 };
   524 class WaitForLockedFilesToFinishRunnable MOZ_FINAL : public nsRunnable
   525 {
   526 public:
   527   WaitForLockedFilesToFinishRunnable()
   528   : mBusy(true)
   529   { }
   531   NS_IMETHOD
   532   Run();
   534   bool
   535   IsBusy() const
   536   {
   537     return mBusy;
   538   }
   540 private:
   541   bool mBusy;
   542 };
   544 class SaveOriginAccessTimeRunnable MOZ_FINAL : public nsRunnable
   545 {
   546 public:
   547   SaveOriginAccessTimeRunnable(const nsACString& aOrigin, int64_t aTimestamp)
   548   : mOrigin(aOrigin), mTimestamp(aTimestamp)
   549   { }
   551   NS_IMETHOD
   552   Run();
   554 private:
   555   nsCString mOrigin;
   556   int64_t mTimestamp;
   557 };
   559 struct MOZ_STACK_CLASS RemoveQuotaInfo
   560 {
   561   RemoveQuotaInfo(PersistenceType aPersistenceType, const nsACString& aPattern)
   562   : persistenceType(aPersistenceType), pattern(aPattern)
   563   { }
   565   PersistenceType persistenceType;
   566   nsCString pattern;
   567 };
   569 struct MOZ_STACK_CLASS InactiveOriginsInfo
   570 {
   571   InactiveOriginsInfo(OriginCollection& aCollection,
   572                       nsTArray<OriginInfo*>& aOrigins)
   573   : collection(aCollection), origins(aOrigins)
   574   { }
   576   OriginCollection& collection;
   577   nsTArray<OriginInfo*>& origins;
   578 };
   580 bool
   581 IsMainProcess()
   582 {
   583   return XRE_GetProcessType() == GeckoProcessType_Default;
   584 }
   586 void
   587 SanitizeOriginString(nsCString& aOrigin)
   588 {
   589   // We want profiles to be platform-independent so we always need to replace
   590   // the same characters on every platform. Windows has the most extensive set
   591   // of illegal characters so we use its FILE_ILLEGAL_CHARACTERS and
   592   // FILE_PATH_SEPARATOR.
   593   static const char kReplaceChars[] = CONTROL_CHARACTERS "/:*?\"<>|\\";
   595 #ifdef XP_WIN
   596   NS_ASSERTION(!strcmp(kReplaceChars,
   597                        FILE_ILLEGAL_CHARACTERS FILE_PATH_SEPARATOR),
   598                "Illegal file characters have changed!");
   599 #endif
   601   aOrigin.ReplaceChar(kReplaceChars, '+');
   602 }
   604 nsresult
   605 EnsureDirectory(nsIFile* aDirectory, bool* aCreated)
   606 {
   607   AssertIsOnIOThread();
   609   nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
   610   if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
   611     bool isDirectory;
   612     rv = aDirectory->IsDirectory(&isDirectory);
   613     NS_ENSURE_SUCCESS(rv, rv);
   614     NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
   616     *aCreated = false;
   617   }
   618   else {
   619     NS_ENSURE_SUCCESS(rv, rv);
   621     *aCreated = true;
   622   }
   624   return NS_OK;
   625 }
   627 nsresult
   628 CreateDirectoryUpgradeStamp(nsIFile* aDirectory)
   629 {
   630   AssertIsOnIOThread();
   632   nsCOMPtr<nsIFile> metadataFile;
   633   nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
   634   NS_ENSURE_SUCCESS(rv, rv);
   636   rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
   637   NS_ENSURE_SUCCESS(rv, rv);
   639   rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
   640   NS_ENSURE_SUCCESS(rv, rv);
   642   return NS_OK;
   643 }
   645 nsresult
   646 GetDirectoryMetadataStream(nsIFile* aDirectory, bool aUpdate,
   647                            nsIBinaryOutputStream** aStream)
   648 {
   649   AssertIsOnIOThread();
   651   nsCOMPtr<nsIFile> metadataFile;
   652   nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
   653   NS_ENSURE_SUCCESS(rv, rv);
   655   rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
   656   NS_ENSURE_SUCCESS(rv, rv);
   658   nsCOMPtr<nsIOutputStream> outputStream;
   659   if (aUpdate) {
   660     bool exists;
   661     rv = metadataFile->Exists(&exists);
   662     NS_ENSURE_SUCCESS(rv, rv);
   664     if (!exists) {
   665       *aStream = nullptr;
   666       return NS_OK;
   667     }
   669     nsCOMPtr<nsIFileStream> stream;
   670     rv = NS_NewLocalFileStream(getter_AddRefs(stream), metadataFile);
   671     NS_ENSURE_SUCCESS(rv, rv);
   673     outputStream = do_QueryInterface(stream);
   674     NS_ENSURE_TRUE(outputStream, NS_ERROR_FAILURE);
   675   }
   676   else {
   677     rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
   678                                      metadataFile);
   679     NS_ENSURE_SUCCESS(rv, rv);
   680   }
   682   nsCOMPtr<nsIBinaryOutputStream> binaryStream =
   683     do_CreateInstance("@mozilla.org/binaryoutputstream;1");
   684   NS_ENSURE_TRUE(binaryStream, NS_ERROR_FAILURE);
   686   rv = binaryStream->SetOutputStream(outputStream);
   687   NS_ENSURE_SUCCESS(rv, rv);
   689   binaryStream.forget(aStream);
   690   return NS_OK;
   691 }
   693 nsresult
   694 CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
   695                         const nsACString& aGroup, const nsACString& aOrigin)
   696 {
   697   AssertIsOnIOThread();
   699   nsCOMPtr<nsIBinaryOutputStream> stream;
   700   nsresult rv =
   701     GetDirectoryMetadataStream(aDirectory, false, getter_AddRefs(stream));
   702   NS_ENSURE_SUCCESS(rv, rv);
   704   NS_ASSERTION(stream, "This shouldn't be null!");
   706   rv = stream->Write64(aTimestamp);
   707   NS_ENSURE_SUCCESS(rv, rv);
   709   rv = stream->WriteStringZ(PromiseFlatCString(aGroup).get());
   710   NS_ENSURE_SUCCESS(rv, rv);
   712   rv = stream->WriteStringZ(PromiseFlatCString(aOrigin).get());
   713   NS_ENSURE_SUCCESS(rv, rv);
   715   return NS_OK;
   716 }
   718 nsresult
   719 GetDirectoryMetadata(nsIFile* aDirectory, int64_t* aTimestamp,
   720                      nsACString& aGroup, nsACString& aOrigin)
   721 {
   722   AssertIsOnIOThread();
   724   nsCOMPtr<nsIFile> metadataFile;
   725   nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
   726   NS_ENSURE_SUCCESS(rv, rv);
   728   rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
   729   NS_ENSURE_SUCCESS(rv, rv);
   731   nsCOMPtr<nsIInputStream> stream;
   732   rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), metadataFile);
   733   NS_ENSURE_SUCCESS(rv, rv);
   735   nsCOMPtr<nsIInputStream> bufferedStream;
   736   rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, 512);
   737   NS_ENSURE_SUCCESS(rv, rv);
   739   nsCOMPtr<nsIBinaryInputStream> binaryStream =
   740     do_CreateInstance("@mozilla.org/binaryinputstream;1");
   741   NS_ENSURE_TRUE(binaryStream, NS_ERROR_FAILURE);
   743   rv = binaryStream->SetInputStream(bufferedStream);
   744   NS_ENSURE_SUCCESS(rv, rv);
   746   uint64_t timestamp;
   747   rv = binaryStream->Read64(&timestamp);
   748   NS_ENSURE_SUCCESS(rv, rv);
   750   nsCString group;
   751   rv = binaryStream->ReadCString(group);
   752   NS_ENSURE_SUCCESS(rv, rv);
   754   nsCString origin;
   755   rv = binaryStream->ReadCString(origin);
   756   NS_ENSURE_SUCCESS(rv, rv);
   758   *aTimestamp = timestamp;
   759   aGroup = group;
   760   aOrigin = origin;
   761   return NS_OK;
   762 }
   764 nsresult
   765 MaybeUpgradeOriginDirectory(nsIFile* aDirectory)
   766 {
   767   AssertIsOnIOThread();
   768   NS_ASSERTION(aDirectory, "Null pointer!");
   770   nsCOMPtr<nsIFile> metadataFile;
   771   nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
   772   NS_ENSURE_SUCCESS(rv, rv);
   774   rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
   775   NS_ENSURE_SUCCESS(rv, rv);
   777   bool exists;
   778   rv = metadataFile->Exists(&exists);
   779   NS_ENSURE_SUCCESS(rv, rv);
   781   if (!exists) {
   782     // Directory structure upgrade needed.
   783     // Move all files to IDB specific directory.
   785     nsString idbDirectoryName;
   786     rv = Client::TypeToText(Client::IDB, idbDirectoryName);
   787     NS_ENSURE_SUCCESS(rv, rv);
   789     nsCOMPtr<nsIFile> idbDirectory;
   790     rv = aDirectory->Clone(getter_AddRefs(idbDirectory));
   791     NS_ENSURE_SUCCESS(rv, rv);
   793     rv = idbDirectory->Append(idbDirectoryName);
   794     NS_ENSURE_SUCCESS(rv, rv);
   796     rv = idbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
   797     if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
   798       NS_WARNING("IDB directory already exists!");
   800       bool isDirectory;
   801       rv = idbDirectory->IsDirectory(&isDirectory);
   802       NS_ENSURE_SUCCESS(rv, rv);
   803       NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
   804     }
   805     else {
   806       NS_ENSURE_SUCCESS(rv, rv);
   807     }
   809     nsCOMPtr<nsISimpleEnumerator> entries;
   810     rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
   811     NS_ENSURE_SUCCESS(rv, rv);
   813     bool hasMore;
   814     while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
   815       nsCOMPtr<nsISupports> entry;
   816       rv = entries->GetNext(getter_AddRefs(entry));
   817       NS_ENSURE_SUCCESS(rv, rv);
   819       nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
   820       NS_ENSURE_TRUE(file, NS_NOINTERFACE);
   822       nsString leafName;
   823       rv = file->GetLeafName(leafName);
   824       NS_ENSURE_SUCCESS(rv, rv);
   826       if (!leafName.Equals(idbDirectoryName)) {
   827         rv = file->MoveTo(idbDirectory, EmptyString());
   828         NS_ENSURE_SUCCESS(rv, rv);
   829       }
   830     }
   832     rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
   833     NS_ENSURE_SUCCESS(rv, rv);
   834   }
   836   return NS_OK;
   837 }
   839 // This method computes and returns our best guess for the temporary storage
   840 // limit (in bytes), based on the amount of space users have free on their hard
   841 // drive and on given temporary storage usage (also in bytes).
   842 nsresult
   843 GetTemporaryStorageLimit(nsIFile* aDirectory, uint64_t aCurrentUsage,
   844                          uint64_t* aLimit)
   845 {
   846   // Check for free space on device where temporary storage directory lives.
   847   int64_t bytesAvailable;
   848   nsresult rv = aDirectory->GetDiskSpaceAvailable(&bytesAvailable);
   849   NS_ENSURE_SUCCESS(rv, rv);
   851   NS_ASSERTION(bytesAvailable >= 0, "Negative bytes available?!");
   853   uint64_t availableKB =
   854     static_cast<uint64_t>((bytesAvailable + aCurrentUsage) / 1024);
   856   // Grow/shrink in gChunkSizeKB units, deliberately, so that in the common case
   857   // we don't shrink temporary storage and evict origin data every time we
   858   // initialize.
   859   availableKB = (availableKB / gChunkSizeKB) * gChunkSizeKB;
   861   // Allow temporary storage to consume up to half the available space.
   862   uint64_t resultKB = availableKB * .50;
   864   *aLimit = resultKB * 1024;
   865   return NS_OK;
   866 }
   868 } // anonymous namespace
   870 QuotaManager::QuotaManager()
   871 : mCurrentWindowIndex(BAD_TLS_INDEX),
   872   mQuotaMutex("QuotaManager.mQuotaMutex"),
   873   mTemporaryStorageLimit(0),
   874   mTemporaryStorageUsage(0),
   875   mTemporaryStorageInitialized(false),
   876   mStorageAreaInitialized(false)
   877 {
   878   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   879   NS_ASSERTION(!gInstance, "More than one instance!");
   880 }
   882 QuotaManager::~QuotaManager()
   883 {
   884   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   885   NS_ASSERTION(!gInstance || gInstance == this, "Different instances!");
   886   gInstance = nullptr;
   887 }
   889 // static
   890 QuotaManager*
   891 QuotaManager::GetOrCreate()
   892 {
   893   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   895   if (IsShuttingDown()) {
   896     NS_ERROR("Calling GetOrCreate() after shutdown!");
   897     return nullptr;
   898   }
   900   if (!gInstance) {
   901     nsRefPtr<QuotaManager> instance(new QuotaManager());
   903     nsresult rv = instance->Init();
   904     NS_ENSURE_SUCCESS(rv, nullptr);
   906     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   907     NS_ENSURE_TRUE(obs, nullptr);
   909     // We need this callback to know when to shut down all our threads.
   910     rv = obs->AddObserver(instance, PROFILE_BEFORE_CHANGE_OBSERVER_ID, false);
   911     NS_ENSURE_SUCCESS(rv, nullptr);
   913     // The observer service will hold our last reference, don't AddRef here.
   914     gInstance = instance;
   915   }
   917   return gInstance;
   918 }
   920 // static
   921 QuotaManager*
   922 QuotaManager::Get()
   923 {
   924   // Does not return an owning reference.
   925   return gInstance;
   926 }
   928 // static
   929 QuotaManager*
   930 QuotaManager::FactoryCreate()
   931 {
   932   // Returns a raw pointer that carries an owning reference! Lame, but the
   933   // singleton factory macros force this.
   934   QuotaManager* quotaManager = GetOrCreate();
   935   NS_IF_ADDREF(quotaManager);
   936   return quotaManager;
   937 }
   939 // static
   940 bool
   941 QuotaManager::IsShuttingDown()
   942 {
   943   return gShutdown;
   944 }
   946 nsresult
   947 QuotaManager::Init()
   948 {
   949   // We need a thread-local to hold the current window.
   950   NS_ASSERTION(mCurrentWindowIndex == BAD_TLS_INDEX, "Huh?");
   952   if (PR_NewThreadPrivateIndex(&mCurrentWindowIndex, nullptr) != PR_SUCCESS) {
   953     NS_ERROR("PR_NewThreadPrivateIndex failed, QuotaManager disabled");
   954     mCurrentWindowIndex = BAD_TLS_INDEX;
   955     return NS_ERROR_FAILURE;
   956   }
   958   nsresult rv;
   959   if (IsMainProcess()) {
   960     nsCOMPtr<nsIFile> baseDir;
   961     rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
   962                                 getter_AddRefs(baseDir));
   963     if (NS_FAILED(rv)) {
   964       rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   965                                   getter_AddRefs(baseDir));
   966     }
   967     NS_ENSURE_SUCCESS(rv, rv);
   969     nsCOMPtr<nsIFile> indexedDBDir;
   970     rv = baseDir->Clone(getter_AddRefs(indexedDBDir));
   971     NS_ENSURE_SUCCESS(rv, rv);
   973     rv = indexedDBDir->Append(NS_LITERAL_STRING("indexedDB"));
   974     NS_ENSURE_SUCCESS(rv, rv);
   976     rv = indexedDBDir->GetPath(mIndexedDBPath);
   977     NS_ENSURE_SUCCESS(rv, rv);
   979     rv = baseDir->Append(NS_LITERAL_STRING("storage"));
   980     NS_ENSURE_SUCCESS(rv, rv);
   982     nsCOMPtr<nsIFile> persistentStorageDir;
   983     rv = baseDir->Clone(getter_AddRefs(persistentStorageDir));
   984     NS_ENSURE_SUCCESS(rv, rv);
   986     rv = persistentStorageDir->Append(NS_LITERAL_STRING("persistent"));
   987     NS_ENSURE_SUCCESS(rv, rv);
   989     rv = persistentStorageDir->GetPath(mPersistentStoragePath);
   990     NS_ENSURE_SUCCESS(rv, rv);
   992     nsCOMPtr<nsIFile> temporaryStorageDir;
   993     rv = baseDir->Clone(getter_AddRefs(temporaryStorageDir));
   994     NS_ENSURE_SUCCESS(rv, rv);
   996     rv = temporaryStorageDir->Append(NS_LITERAL_STRING("temporary"));
   997     NS_ENSURE_SUCCESS(rv, rv);
   999     rv = temporaryStorageDir->GetPath(mTemporaryStoragePath);
  1000     NS_ENSURE_SUCCESS(rv, rv);
  1002     // Make a lazy thread for any IO we need (like clearing or enumerating the
  1003     // contents of storage directories).
  1004     mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
  1005                                    NS_LITERAL_CSTRING("Storage I/O"),
  1006                                    LazyIdleThread::ManualShutdown);
  1008     // Make a timer here to avoid potential failures later. We don't actually
  1009     // initialize the timer until shutdown.
  1010     mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
  1011     NS_ENSURE_TRUE(mShutdownTimer, NS_ERROR_FAILURE);
  1014   if (NS_FAILED(Preferences::AddIntVarCache(&gStorageQuotaMB,
  1015                                             PREF_STORAGE_QUOTA,
  1016                                             kDefaultQuotaMB))) {
  1017     NS_WARNING("Unable to respond to quota pref changes!");
  1020   if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB, PREF_FIXED_LIMIT,
  1021                                             kDefaultFixedLimitKB)) ||
  1022       NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB,
  1023                                              PREF_CHUNK_SIZE,
  1024                                              kDefaultChunkSizeKB))) {
  1025     NS_WARNING("Unable to respond to temp storage pref changes!");
  1028   if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled,
  1029                                              PREF_TESTING_FEATURES, false))) {
  1030     NS_WARNING("Unable to respond to testing pref changes!");
  1033   static_assert(Client::IDB == 0 && Client::ASMJS == 1 && Client::TYPE_MAX == 2,
  1034                 "Fix the registration!");
  1036   NS_ASSERTION(mClients.Capacity() == Client::TYPE_MAX,
  1037                "Should be using an auto array with correct capacity!");
  1039   // Register IndexedDB
  1040   mClients.AppendElement(new indexedDB::Client());
  1041   mClients.AppendElement(asmjscache::CreateClient());
  1043   return NS_OK;
  1046 void
  1047 QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType,
  1048                                  const nsACString& aGroup,
  1049                                  const nsACString& aOrigin,
  1050                                  uint64_t aLimitBytes,
  1051                                  uint64_t aUsageBytes,
  1052                                  int64_t aAccessTime)
  1054   AssertIsOnIOThread();
  1055   MOZ_ASSERT(aLimitBytes > 0 ||
  1056              aPersistenceType == PERSISTENCE_TYPE_TEMPORARY);
  1057   MOZ_ASSERT(aUsageBytes <= aLimitBytes ||
  1058              aPersistenceType == PERSISTENCE_TYPE_TEMPORARY);
  1060   MutexAutoLock lock(mQuotaMutex);
  1062   GroupInfoPair* pair;
  1063   if (!mGroupInfoPairs.Get(aGroup, &pair)) {
  1064     pair = new GroupInfoPair();
  1065     mGroupInfoPairs.Put(aGroup, pair);
  1066     // The hashtable is now responsible to delete the GroupInfoPair.
  1069   nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
  1070   if (!groupInfo) {
  1071     groupInfo = new GroupInfo(aPersistenceType, aGroup);
  1072     pair->LockedSetGroupInfo(groupInfo);
  1075   nsRefPtr<OriginInfo> originInfo =
  1076     new OriginInfo(groupInfo, aOrigin, aLimitBytes, aUsageBytes, aAccessTime);
  1077   groupInfo->LockedAddOriginInfo(originInfo);
  1080 void
  1081 QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType,
  1082                                      const nsACString& aGroup,
  1083                                      const nsACString& aOrigin,
  1084                                      int64_t aSize)
  1086   AssertIsOnIOThread();
  1088   MutexAutoLock lock(mQuotaMutex);
  1090   GroupInfoPair* pair;
  1091   if (!mGroupInfoPairs.Get(aGroup, &pair)) {
  1092     return;
  1095   nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
  1096   if (!groupInfo) {
  1097     return;
  1100   nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
  1101   if (originInfo) {
  1102     originInfo->LockedDecreaseUsage(aSize);
  1106 void
  1107 QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType,
  1108                                      const nsACString& aGroup,
  1109                                      const nsACString& aOrigin)
  1111   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1113   MutexAutoLock lock(mQuotaMutex);
  1115   GroupInfoPair* pair;
  1116   if (!mGroupInfoPairs.Get(aGroup, &pair)) {
  1117     return;
  1120   nsRefPtr<GroupInfo> groupInfo =
  1121     pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
  1122   if (!groupInfo) {
  1123     return;
  1126   nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
  1127   if (originInfo) {
  1128     int64_t timestamp = PR_Now();
  1129     originInfo->LockedUpdateAccessTime(timestamp);
  1131     if (!groupInfo->IsForTemporaryStorage()) {
  1132       return;
  1135     MutexAutoUnlock autoUnlock(mQuotaMutex);
  1137     SaveOriginAccessTime(aOrigin, timestamp);
  1141 // static
  1142 PLDHashOperator
  1143 QuotaManager::RemoveQuotaCallback(const nsACString& aKey,
  1144                                   nsAutoPtr<GroupInfoPair>& aValue,
  1145                                   void* aUserArg)
  1147   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  1148   NS_ASSERTION(aValue, "Null pointer!");
  1150   nsRefPtr<GroupInfo> groupInfo =
  1151     aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
  1152   if (groupInfo) {
  1153     groupInfo->LockedRemoveOriginInfos();
  1156   return PL_DHASH_REMOVE;
  1159 void
  1160 QuotaManager::RemoveQuota()
  1162   MutexAutoLock lock(mQuotaMutex);
  1164   mGroupInfoPairs.Enumerate(RemoveQuotaCallback, nullptr);
  1166   NS_ASSERTION(mTemporaryStorageUsage == 0, "Should be zero!");
  1169 // static
  1170 PLDHashOperator
  1171 QuotaManager::RemoveQuotaForPersistenceTypeCallback(
  1172                                                const nsACString& aKey,
  1173                                                nsAutoPtr<GroupInfoPair>& aValue,
  1174                                                void* aUserArg)
  1176   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  1177   NS_ASSERTION(aValue, "Null pointer!");
  1178   NS_ASSERTION(aUserArg, "Null pointer!");
  1180   PersistenceType& persistenceType = *static_cast<PersistenceType*>(aUserArg);
  1182   if (persistenceType == PERSISTENCE_TYPE_TEMPORARY) {
  1183     nsRefPtr<GroupInfo> groupInfo =
  1184       aValue->LockedGetGroupInfo(persistenceType);
  1185     if (groupInfo) {
  1186       groupInfo->LockedRemoveOriginInfos();
  1190   aValue->LockedClearGroupInfo(persistenceType);
  1192   return aValue->LockedHasGroupInfos() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
  1195 void
  1196 QuotaManager::RemoveQuotaForPersistenceType(PersistenceType aPersistenceType)
  1198   MutexAutoLock lock(mQuotaMutex);
  1200   mGroupInfoPairs.Enumerate(RemoveQuotaForPersistenceTypeCallback,
  1201                             &aPersistenceType);
  1203   NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_PERSISTENT ||
  1204                mTemporaryStorageUsage == 0, "Should be zero!");
  1207 // static
  1208 PLDHashOperator
  1209 QuotaManager::RemoveQuotaForPatternCallback(const nsACString& aKey,
  1210                                             nsAutoPtr<GroupInfoPair>& aValue,
  1211                                             void* aUserArg)
  1213   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  1214   NS_ASSERTION(aValue, "Null pointer!");
  1215   NS_ASSERTION(aUserArg, "Null pointer!");
  1217   RemoveQuotaInfo* info = static_cast<RemoveQuotaInfo*>(aUserArg);
  1219   nsRefPtr<GroupInfo> groupInfo =
  1220     aValue->LockedGetGroupInfo(info->persistenceType);
  1221   if (groupInfo) {
  1222     groupInfo->LockedRemoveOriginInfosForPattern(info->pattern);
  1224     if (!groupInfo->LockedHasOriginInfos()) {
  1225       aValue->LockedClearGroupInfo(info->persistenceType);
  1227       if (!aValue->LockedHasGroupInfos()) {
  1228         return PL_DHASH_REMOVE;
  1233   return PL_DHASH_NEXT;
  1236 void
  1237 QuotaManager::RemoveQuotaForPattern(PersistenceType aPersistenceType,
  1238                                     const nsACString& aPattern)
  1240   NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!");
  1242   RemoveQuotaInfo info(aPersistenceType, aPattern);
  1244   MutexAutoLock lock(mQuotaMutex);
  1246   mGroupInfoPairs.Enumerate(RemoveQuotaForPatternCallback, &info);
  1249 already_AddRefed<QuotaObject>
  1250 QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
  1251                              const nsACString& aGroup,
  1252                              const nsACString& aOrigin,
  1253                              nsIFile* aFile)
  1255   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
  1257   nsString path;
  1258   nsresult rv = aFile->GetPath(path);
  1259   NS_ENSURE_SUCCESS(rv, nullptr);
  1261   int64_t fileSize;
  1263   bool exists;
  1264   rv = aFile->Exists(&exists);
  1265   NS_ENSURE_SUCCESS(rv, nullptr);
  1267   if (exists) {
  1268     rv = aFile->GetFileSize(&fileSize);
  1269     NS_ENSURE_SUCCESS(rv, nullptr);
  1271   else {
  1272     fileSize = 0;
  1275   nsRefPtr<QuotaObject> result;
  1277     MutexAutoLock lock(mQuotaMutex);
  1279     GroupInfoPair* pair;
  1280     if (!mGroupInfoPairs.Get(aGroup, &pair)) {
  1281       return nullptr;
  1284     nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
  1286     if (!groupInfo) {
  1287       return nullptr;
  1290     nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
  1292     if (!originInfo) {
  1293       return nullptr;
  1296     // We need this extra raw pointer because we can't assign to the smart
  1297     // pointer directly since QuotaObject::AddRef would try to acquire the same
  1298     // mutex.
  1299     QuotaObject* quotaObject;
  1300     if (!originInfo->mQuotaObjects.Get(path, &quotaObject)) {
  1301       // Create a new QuotaObject.
  1302       quotaObject = new QuotaObject(originInfo, path, fileSize);
  1304       // Put it to the hashtable. The hashtable is not responsible to delete
  1305       // the QuotaObject.
  1306       originInfo->mQuotaObjects.Put(path, quotaObject);
  1309     // Addref the QuotaObject and move the ownership to the result. This must
  1310     // happen before we unlock!
  1311     result = quotaObject->LockedAddRef();
  1314   // The caller becomes the owner of the QuotaObject, that is, the caller is
  1315   // is responsible to delete it when the last reference is removed.
  1316   return result.forget();
  1319 already_AddRefed<QuotaObject>
  1320 QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
  1321                              const nsACString& aGroup,
  1322                              const nsACString& aOrigin,
  1323                              const nsAString& aPath)
  1325   nsresult rv;
  1326   nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  1327   NS_ENSURE_SUCCESS(rv, nullptr);
  1329   rv = file->InitWithPath(aPath);
  1330   NS_ENSURE_SUCCESS(rv, nullptr);
  1332   return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file);
  1335 bool
  1336 QuotaManager::RegisterStorage(nsIOfflineStorage* aStorage)
  1338   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1339   NS_ASSERTION(aStorage, "Null pointer!");
  1341   // Don't allow any new storages to be created after shutdown.
  1342   if (IsShuttingDown()) {
  1343     return false;
  1346   // Add this storage to its origin info if it exists, create it otherwise.
  1347   const nsACString& origin = aStorage->Origin();
  1348   ArrayCluster<nsIOfflineStorage*>* cluster;
  1349   if (!mLiveStorages.Get(origin, &cluster)) {
  1350     cluster = new ArrayCluster<nsIOfflineStorage*>();
  1351     mLiveStorages.Put(origin, cluster);
  1353     UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin);
  1355   (*cluster)[aStorage->GetClient()->GetType()].AppendElement(aStorage);
  1357   return true;
  1360 void
  1361 QuotaManager::UnregisterStorage(nsIOfflineStorage* aStorage)
  1363   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1364   NS_ASSERTION(aStorage, "Null pointer!");
  1366   // Remove this storage from its origin array, maybe remove the array if it
  1367   // is then empty.
  1368   const nsACString& origin = aStorage->Origin();
  1369   ArrayCluster<nsIOfflineStorage*>* cluster;
  1370   if (mLiveStorages.Get(origin, &cluster) &&
  1371       (*cluster)[aStorage->GetClient()->GetType()].RemoveElement(aStorage)) {
  1372     if (cluster->IsEmpty()) {
  1373       mLiveStorages.Remove(origin);
  1375       UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin);
  1377     return;
  1379   NS_ERROR("Didn't know anything about this storage!");
  1382 void
  1383 QuotaManager::OnStorageClosed(nsIOfflineStorage* aStorage)
  1385   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1386   NS_ASSERTION(aStorage, "Null pointer!");
  1388   // Check through the list of SynchronizedOps to see if any are waiting for
  1389   // this storage to close before proceeding.
  1390   SynchronizedOp* op =
  1391     FindSynchronizedOp(aStorage->Origin(),
  1392                        Nullable<PersistenceType>(aStorage->Type()),
  1393                        aStorage->Id());
  1394   if (op) {
  1395     Client::Type clientType = aStorage->GetClient()->GetType();
  1397     // This storage is in the scope of this SynchronizedOp.  Remove it
  1398     // from the list if necessary.
  1399     if (op->mStorages[clientType].RemoveElement(aStorage)) {
  1400       // Now set up the helper if there are no more live storages.
  1401       NS_ASSERTION(op->mListener,
  1402                    "How did we get rid of the listener before removing the "
  1403                     "last storage?");
  1404       if (op->mStorages[clientType].IsEmpty()) {
  1405         // At this point, all storages are closed, so no new transactions
  1406         // can be started.  There may, however, still be outstanding
  1407         // transactions that have not completed.  We need to wait for those
  1408         // before we dispatch the helper.
  1409         if (NS_FAILED(RunSynchronizedOp(aStorage, op))) {
  1410           NS_WARNING("Failed to run synchronized op!");
  1417 void
  1418 QuotaManager::AbortCloseStoragesForWindow(nsPIDOMWindow* aWindow)
  1420   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1421   NS_ASSERTION(aWindow, "Null pointer!");
  1423   FileService* service = FileService::Get();
  1425   StorageMatcher<ArrayCluster<nsIOfflineStorage*> > liveStorages;
  1426   liveStorages.Find(mLiveStorages);
  1428   for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
  1429     nsRefPtr<Client>& client = mClients[i];
  1430     bool utilized = service && client->IsFileServiceUtilized();
  1431     bool activated = client->IsTransactionServiceActivated();
  1433     nsTArray<nsIOfflineStorage*>& array = liveStorages[i];
  1434     for (uint32_t j = 0; j < array.Length(); j++) {
  1435       nsIOfflineStorage*& storage = array[j];
  1437       if (storage->IsOwned(aWindow)) {
  1438         if (NS_FAILED(storage->Close())) {
  1439           NS_WARNING("Failed to close storage for dying window!");
  1442         if (utilized) {
  1443           service->AbortLockedFilesForStorage(storage);
  1446         if (activated) {
  1447           client->AbortTransactionsForStorage(storage);
  1454 bool
  1455 QuotaManager::HasOpenTransactions(nsPIDOMWindow* aWindow)
  1457   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1458   NS_ASSERTION(aWindow, "Null pointer!");
  1460   FileService* service = FileService::Get();
  1462   nsAutoPtr<StorageMatcher<ArrayCluster<nsIOfflineStorage*> > > liveStorages;
  1464   for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
  1465     nsRefPtr<Client>& client = mClients[i];
  1466     bool utilized = service && client->IsFileServiceUtilized();
  1467     bool activated = client->IsTransactionServiceActivated();
  1469     if (utilized || activated) {
  1470       if (!liveStorages) {
  1471         liveStorages = new StorageMatcher<ArrayCluster<nsIOfflineStorage*> >();
  1472         liveStorages->Find(mLiveStorages);
  1475       nsTArray<nsIOfflineStorage*>& storages = liveStorages->ArrayAt(i);
  1476       for (uint32_t j = 0; j < storages.Length(); j++) {
  1477         nsIOfflineStorage*& storage = storages[j];
  1479         if (storage->IsOwned(aWindow) &&
  1480             ((utilized && service->HasLockedFilesForStorage(storage)) ||
  1481              (activated && client->HasTransactionsForStorage(storage)))) {
  1482           return true;
  1488   return false;
  1491 nsresult
  1492 QuotaManager::WaitForOpenAllowed(const OriginOrPatternString& aOriginOrPattern,
  1493                                  Nullable<PersistenceType> aPersistenceType,
  1494                                  const nsACString& aId, nsIRunnable* aRunnable)
  1496   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1497   NS_ASSERTION(!aOriginOrPattern.IsEmpty() || aOriginOrPattern.IsNull(),
  1498                "Empty pattern!");
  1499   NS_ASSERTION(aRunnable, "Null pointer!");
  1501   nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern,
  1502                                                   aPersistenceType, aId));
  1504   // See if this runnable needs to wait.
  1505   bool delayed = false;
  1506   for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) {
  1507     nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
  1508     if (op->MustWaitFor(*existingOp)) {
  1509       existingOp->DelayRunnable(aRunnable);
  1510       delayed = true;
  1511       break;
  1515   // Otherwise, dispatch it immediately.
  1516   if (!delayed) {
  1517     nsresult rv = NS_DispatchToCurrentThread(aRunnable);
  1518     NS_ENSURE_SUCCESS(rv, rv);
  1521   // Adding this to the synchronized ops list will block any additional
  1522   // ops from proceeding until this one is done.
  1523   mSynchronizedOps.AppendElement(op.forget());
  1525   return NS_OK;
  1528 void
  1529 QuotaManager::AddSynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
  1530                                 Nullable<PersistenceType> aPersistenceType)
  1532   nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern,
  1533                                                   aPersistenceType,
  1534                                                   EmptyCString()));
  1536 #ifdef DEBUG
  1537   for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) {
  1538     nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
  1539     NS_ASSERTION(!op->MustWaitFor(*existingOp), "What?");
  1541 #endif
  1543   mSynchronizedOps.AppendElement(op.forget());
  1546 void
  1547 QuotaManager::AllowNextSynchronizedOp(
  1548                                   const OriginOrPatternString& aOriginOrPattern,
  1549                                   Nullable<PersistenceType> aPersistenceType,
  1550                                   const nsACString& aId)
  1552   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  1553   NS_ASSERTION(!aOriginOrPattern.IsEmpty() || aOriginOrPattern.IsNull(),
  1554                "Empty origin/pattern!");
  1556   uint32_t count = mSynchronizedOps.Length();
  1557   for (uint32_t index = 0; index < count; index++) {
  1558     nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
  1559     if (op->mOriginOrPattern.IsOrigin() == aOriginOrPattern.IsOrigin() &&
  1560         op->mOriginOrPattern == aOriginOrPattern &&
  1561         op->mPersistenceType == aPersistenceType) {
  1562       if (op->mId == aId) {
  1563         NS_ASSERTION(op->mStorages.IsEmpty(), "How did this happen?");
  1565         op->DispatchDelayedRunnables();
  1567         mSynchronizedOps.RemoveElementAt(index);
  1568         return;
  1571       // If one or the other is for an origin clear, we should have matched
  1572       // solely on origin.
  1573       NS_ASSERTION(!op->mId.IsEmpty() && !aId.IsEmpty(),
  1574                    "Why didn't we match earlier?");
  1578   NS_NOTREACHED("Why didn't we find a SynchronizedOp?");
  1581 nsresult
  1582 QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType,
  1583                                     const nsACString& aASCIIOrigin,
  1584                                     nsIFile** aDirectory) const
  1586   nsresult rv;
  1587   nsCOMPtr<nsIFile> directory =
  1588     do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  1589   NS_ENSURE_SUCCESS(rv, rv);
  1591   rv = directory->InitWithPath(GetStoragePath(aPersistenceType));
  1592   NS_ENSURE_SUCCESS(rv, rv);
  1594   nsAutoCString originSanitized(aASCIIOrigin);
  1595   SanitizeOriginString(originSanitized);
  1597   rv = directory->Append(NS_ConvertASCIItoUTF16(originSanitized));
  1598   NS_ENSURE_SUCCESS(rv, rv);
  1600   directory.forget(aDirectory);
  1601   return NS_OK;
  1604 nsresult
  1605 QuotaManager::InitializeOrigin(PersistenceType aPersistenceType,
  1606                                const nsACString& aGroup,
  1607                                const nsACString& aOrigin,
  1608                                bool aTrackQuota,
  1609                                int64_t aAccessTime,
  1610                                nsIFile* aDirectory)
  1612   AssertIsOnIOThread();
  1614   nsresult rv;
  1616   bool temporaryStorage = aPersistenceType == PERSISTENCE_TYPE_TEMPORARY;
  1617   if (!temporaryStorage) {
  1618     rv = MaybeUpgradeOriginDirectory(aDirectory);
  1619     NS_ENSURE_SUCCESS(rv, rv);
  1622   // We need to initialize directories of all clients if they exists and also
  1623   // get the total usage to initialize the quota.
  1624   nsAutoPtr<UsageInfo> usageInfo;
  1625   if (aTrackQuota) {
  1626     usageInfo = new UsageInfo();
  1629   nsCOMPtr<nsISimpleEnumerator> entries;
  1630   rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
  1631   NS_ENSURE_SUCCESS(rv, rv);
  1633   bool hasMore;
  1634   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
  1635     nsCOMPtr<nsISupports> entry;
  1636     rv = entries->GetNext(getter_AddRefs(entry));
  1637     NS_ENSURE_SUCCESS(rv, rv);
  1639     nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
  1640     NS_ENSURE_TRUE(file, NS_NOINTERFACE);
  1642     nsString leafName;
  1643     rv = file->GetLeafName(leafName);
  1644     NS_ENSURE_SUCCESS(rv, rv);
  1646     if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
  1647         leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
  1648       continue;
  1651     bool isDirectory;
  1652     rv = file->IsDirectory(&isDirectory);
  1653     NS_ENSURE_SUCCESS(rv, rv);
  1655     if (!isDirectory) {
  1656       NS_WARNING("Unknown file found!");
  1657       return NS_ERROR_UNEXPECTED;
  1660     Client::Type clientType;
  1661     rv = Client::TypeFromText(leafName, clientType);
  1662     if (NS_FAILED(rv)) {
  1663       NS_WARNING("Unknown directory found!");
  1664       return NS_ERROR_UNEXPECTED;
  1667     rv = mClients[clientType]->InitOrigin(aPersistenceType, aGroup, aOrigin,
  1668                                           usageInfo);
  1669     NS_ENSURE_SUCCESS(rv, rv);
  1672   if (aTrackQuota) {
  1673     uint64_t quotaMaxBytes;
  1674     uint64_t totalUsageBytes = usageInfo->TotalUsage();
  1676     if (temporaryStorage) {
  1677       // Temporary storage has no limit for origin usage (there's a group and
  1678       // the global limit though).
  1679       quotaMaxBytes = 0;
  1681     else {
  1682       quotaMaxBytes = GetStorageQuotaMB() * 1024 * 1024;
  1683       if (totalUsageBytes > quotaMaxBytes) {
  1684         NS_WARNING("Origin is already using more storage than allowed!");
  1685         return NS_ERROR_UNEXPECTED;
  1689     InitQuotaForOrigin(aPersistenceType, aGroup, aOrigin, quotaMaxBytes,
  1690                        totalUsageBytes, aAccessTime);
  1693   return NS_OK;
  1696 nsresult
  1697 QuotaManager::MaybeUpgradeIndexedDBDirectory()
  1699   AssertIsOnIOThread();
  1701   if (mStorageAreaInitialized) {
  1702     return NS_OK;
  1705   nsresult rv;
  1707   nsCOMPtr<nsIFile> indexedDBDir =
  1708     do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  1709   NS_ENSURE_SUCCESS(rv, rv);
  1711   rv = indexedDBDir->InitWithPath(mIndexedDBPath);
  1712   NS_ENSURE_SUCCESS(rv, rv);
  1714   bool exists;
  1715   rv = indexedDBDir->Exists(&exists);
  1716   NS_ENSURE_SUCCESS(rv, rv);
  1718   if (!exists) {
  1719     // Nothing to upgrade.
  1720     mStorageAreaInitialized = true;
  1722     return NS_OK;
  1725   bool isDirectory;
  1726   rv = indexedDBDir->IsDirectory(&isDirectory);
  1727   NS_ENSURE_SUCCESS(rv, rv);
  1729   if (!isDirectory) {
  1730     NS_WARNING("indexedDB entry is not a directory!");
  1731     return NS_OK;
  1734   nsCOMPtr<nsIFile> persistentStorageDir =
  1735     do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  1736   NS_ENSURE_SUCCESS(rv, rv);
  1738   rv = persistentStorageDir->InitWithPath(mPersistentStoragePath);
  1739   NS_ENSURE_SUCCESS(rv, rv);
  1741   rv = persistentStorageDir->Exists(&exists);
  1742   NS_ENSURE_SUCCESS(rv, rv);
  1744   if (exists) {
  1745     NS_WARNING("indexedDB directory shouldn't exist after the upgrade!");
  1746     return NS_OK;
  1749   nsCOMPtr<nsIFile> storageDir;
  1750   rv = persistentStorageDir->GetParent(getter_AddRefs(storageDir));
  1751   NS_ENSURE_SUCCESS(rv, rv);
  1753   nsString persistentStorageName;
  1754   rv = persistentStorageDir->GetLeafName(persistentStorageName);
  1755   NS_ENSURE_SUCCESS(rv, rv);
  1757   // MoveTo() is atomic if the move happens on the same volume which should
  1758   // be our case, so even if we crash in the middle of the operation nothing
  1759   // breaks next time we try to initialize.
  1760   // However there's a theoretical possibility that the indexedDB directory
  1761   // is on different volume, but it should be rare enough that we don't have
  1762   // to worry about it.
  1763   rv = indexedDBDir->MoveTo(storageDir, persistentStorageName);
  1764   NS_ENSURE_SUCCESS(rv, rv);
  1766   mStorageAreaInitialized = true;
  1768   return NS_OK;
  1771 nsresult
  1772 QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType,
  1773                                         const nsACString& aGroup,
  1774                                         const nsACString& aOrigin,
  1775                                         bool aTrackQuota,
  1776                                         nsIFile** aDirectory)
  1778   AssertIsOnIOThread();
  1780   nsresult rv = MaybeUpgradeIndexedDBDirectory();
  1781   NS_ENSURE_SUCCESS(rv, rv);
  1783   // Get directory for this origin and persistence type.
  1784   nsCOMPtr<nsIFile> directory;
  1785   rv = GetDirectoryForOrigin(aPersistenceType, aOrigin,
  1786                              getter_AddRefs(directory));
  1787   NS_ENSURE_SUCCESS(rv, rv);
  1789   if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
  1790     if (mInitializedOrigins.Contains(aOrigin)) {
  1791       NS_ADDREF(*aDirectory = directory);
  1792       return NS_OK;
  1795     bool created;
  1796     rv = EnsureDirectory(directory, &created);
  1797     NS_ENSURE_SUCCESS(rv, rv);
  1799     if (created) {
  1800       rv = CreateDirectoryUpgradeStamp(directory);
  1801       NS_ENSURE_SUCCESS(rv, rv);
  1804     rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aTrackQuota, 0,
  1805                           directory);
  1806     NS_ENSURE_SUCCESS(rv, rv);
  1808     mInitializedOrigins.AppendElement(aOrigin);
  1810     directory.forget(aDirectory);
  1811     return NS_OK;
  1814   NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
  1815   NS_ASSERTION(aTrackQuota, "Huh?");
  1817   if (!mTemporaryStorageInitialized) {
  1818     nsCOMPtr<nsIFile> parentDirectory;
  1819     rv = directory->GetParent(getter_AddRefs(parentDirectory));
  1820     NS_ENSURE_SUCCESS(rv, rv);
  1822     bool created;
  1823     rv = EnsureDirectory(parentDirectory, &created);
  1824     NS_ENSURE_SUCCESS(rv, rv);
  1826     nsCOMPtr<nsISimpleEnumerator> entries;
  1827     rv = parentDirectory->GetDirectoryEntries(getter_AddRefs(entries));
  1828     NS_ENSURE_SUCCESS(rv, rv);
  1830     bool hasMore;
  1831     while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
  1832       nsCOMPtr<nsISupports> entry;
  1833       rv = entries->GetNext(getter_AddRefs(entry));
  1834       NS_ENSURE_SUCCESS(rv, rv);
  1836       nsCOMPtr<nsIFile> childDirectory = do_QueryInterface(entry);
  1837       NS_ENSURE_TRUE(childDirectory, NS_NOINTERFACE);
  1839       bool isDirectory;
  1840       rv = childDirectory->IsDirectory(&isDirectory);
  1841       NS_ENSURE_SUCCESS(rv, rv);
  1842       NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
  1844       int64_t timestamp;
  1845       nsCString group;
  1846       nsCString origin;
  1847       rv = GetDirectoryMetadata(childDirectory, &timestamp, group, origin);
  1848       NS_ENSURE_SUCCESS(rv, rv);
  1850       rv = InitializeOrigin(aPersistenceType, group, origin, aTrackQuota,
  1851                             timestamp, childDirectory);
  1852       if (NS_FAILED(rv)) {
  1853         NS_WARNING("Failed to initialize origin!");
  1855         // We have to cleanup partially initialized quota for temporary storage.
  1856         RemoveQuotaForPersistenceType(aPersistenceType);
  1858         return rv;
  1862     if (gFixedLimitKB >= 0) {
  1863       mTemporaryStorageLimit = gFixedLimitKB * 1024;
  1865     else {
  1866       rv = GetTemporaryStorageLimit(parentDirectory, mTemporaryStorageUsage,
  1867                                     &mTemporaryStorageLimit);
  1868       NS_ENSURE_SUCCESS(rv, rv);
  1871     mTemporaryStorageInitialized = true;
  1873     CheckTemporaryStorageLimits();
  1876   bool created;
  1877   rv = EnsureDirectory(directory, &created);
  1878   NS_ENSURE_SUCCESS(rv, rv);
  1880   if (created) {
  1881     int64_t timestamp = PR_Now();
  1883     rv = CreateDirectoryMetadata(directory, timestamp, aGroup, aOrigin);
  1884     NS_ENSURE_SUCCESS(rv, rv);
  1886     rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aTrackQuota,
  1887                           timestamp, directory);
  1888     NS_ENSURE_SUCCESS(rv, rv);
  1891   directory.forget(aDirectory);
  1892   return NS_OK;
  1895 void
  1896 QuotaManager::OriginClearCompleted(
  1897                                   PersistenceType aPersistenceType,
  1898                                   const OriginOrPatternString& aOriginOrPattern)
  1900   AssertIsOnIOThread();
  1902   if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
  1903     if (aOriginOrPattern.IsOrigin()) {
  1904       mInitializedOrigins.RemoveElement(aOriginOrPattern);
  1906     else {
  1907       for (uint32_t index = mInitializedOrigins.Length(); index > 0; index--) {
  1908         if (PatternMatchesOrigin(aOriginOrPattern,
  1909                                  mInitializedOrigins[index - 1])) {
  1910           mInitializedOrigins.RemoveElementAt(index - 1);
  1916   for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
  1917     mClients[index]->OnOriginClearCompleted(aPersistenceType, aOriginOrPattern);
  1921 void
  1922 QuotaManager::ResetOrClearCompleted()
  1924   AssertIsOnIOThread();
  1926   mInitializedOrigins.Clear();
  1927   mTemporaryStorageInitialized = false;
  1929   ReleaseIOThreadObjects();
  1932 already_AddRefed<mozilla::dom::quota::Client>
  1933 QuotaManager::GetClient(Client::Type aClientType)
  1935   nsRefPtr<Client> client = mClients.SafeElementAt(aClientType);
  1936   return client.forget();
  1939 uint64_t
  1940 QuotaManager::GetGroupLimit() const
  1942   MOZ_ASSERT(mTemporaryStorageInitialized);
  1944   // To avoid one group evicting all the rest, limit the amount any one group
  1945   // can use to 20%. To prevent individual sites from using exorbitant amounts
  1946   // of storage where there is a lot of free space, cap the group limit to 2GB.
  1947   uint64_t x = std::min<uint64_t>(mTemporaryStorageLimit * .20, 2 GB);
  1949   // In low-storage situations, make an exception (while not exceeding the total
  1950   // storage limit).
  1951   return std::min<uint64_t>(mTemporaryStorageLimit,
  1952                             std::max<uint64_t>(x, 10 MB));
  1955 // static
  1956 uint32_t
  1957 QuotaManager::GetStorageQuotaMB()
  1959   return uint32_t(std::max(gStorageQuotaMB, 0));
  1962 // static
  1963 void
  1964 QuotaManager::GetStorageId(PersistenceType aPersistenceType,
  1965                            const nsACString& aOrigin,
  1966                            Client::Type aClientType,
  1967                            const nsAString& aName,
  1968                            nsACString& aDatabaseId)
  1970   nsAutoCString str;
  1971   str.AppendInt(aPersistenceType);
  1972   str.Append('*');
  1973   str.Append(aOrigin);
  1974   str.Append('*');
  1975   str.AppendInt(aClientType);
  1976   str.Append('*');
  1977   str.Append(NS_ConvertUTF16toUTF8(aName));
  1979   aDatabaseId = str;
  1982 // static
  1983 nsresult
  1984 QuotaManager::GetInfoFromURI(nsIURI* aURI,
  1985                              uint32_t aAppId,
  1986                              bool aInMozBrowser,
  1987                              nsACString* aGroup,
  1988                              nsACString* aASCIIOrigin,
  1989                              StoragePrivilege* aPrivilege,
  1990                              PersistenceType* aDefaultPersistenceType)
  1992   NS_ASSERTION(aURI, "Null uri!");
  1994   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
  1995   NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
  1997   nsCOMPtr<nsIPrincipal> principal;
  1998   nsresult rv = secMan->GetAppCodebasePrincipal(aURI, aAppId, aInMozBrowser,
  1999                                                 getter_AddRefs(principal));
  2000   NS_ENSURE_SUCCESS(rv, rv);
  2002   rv = GetInfoFromPrincipal(principal, aGroup, aASCIIOrigin, aPrivilege,
  2003                             aDefaultPersistenceType);
  2004   NS_ENSURE_SUCCESS(rv, rv);
  2006   return NS_OK;
  2009 // static
  2010 nsresult
  2011 QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
  2012                                    nsACString* aGroup,
  2013                                    nsACString* aASCIIOrigin,
  2014                                    StoragePrivilege* aPrivilege,
  2015                                    PersistenceType* aDefaultPersistenceType)
  2017   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2018   NS_ASSERTION(aPrincipal, "Don't hand me a null principal!");
  2020   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
  2021     GetInfoForChrome(aGroup, aASCIIOrigin, aPrivilege, aDefaultPersistenceType);
  2022     return NS_OK;
  2025   bool isNullPrincipal;
  2026   nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
  2027   NS_ENSURE_SUCCESS(rv, rv);
  2029   if (isNullPrincipal) {
  2030     NS_WARNING("IndexedDB not supported from this principal!");
  2031     return NS_ERROR_FAILURE;
  2034   nsCString origin;
  2035   rv = aPrincipal->GetOrigin(getter_Copies(origin));
  2036   NS_ENSURE_SUCCESS(rv, rv);
  2038   if (origin.EqualsLiteral("chrome")) {
  2039     NS_WARNING("Non-chrome principal can't use chrome origin!");
  2040     return NS_ERROR_FAILURE;
  2043   nsCString jarPrefix;
  2044   if (aGroup || aASCIIOrigin) {
  2045     rv = aPrincipal->GetJarPrefix(jarPrefix);
  2046     NS_ENSURE_SUCCESS(rv, rv);
  2049   if (aGroup) {
  2050     nsCString baseDomain;
  2051     rv = aPrincipal->GetBaseDomain(baseDomain);
  2052     if (NS_FAILED(rv)) {
  2053       // A hack for JetPack.
  2055       nsCOMPtr<nsIURI> uri;
  2056       rv = aPrincipal->GetURI(getter_AddRefs(uri));
  2057       NS_ENSURE_SUCCESS(rv, rv);
  2059       bool isIndexedDBURI = false;
  2060       rv = uri->SchemeIs("indexedDB", &isIndexedDBURI);
  2061       NS_ENSURE_SUCCESS(rv, rv);
  2063       if (isIndexedDBURI) {
  2064         rv = NS_OK;
  2067     NS_ENSURE_SUCCESS(rv, rv);
  2069     if (baseDomain.IsEmpty()) {
  2070       aGroup->Assign(jarPrefix + origin);
  2072     else {
  2073       aGroup->Assign(jarPrefix + baseDomain);
  2077   if (aASCIIOrigin) {
  2078     aASCIIOrigin->Assign(jarPrefix + origin);
  2081   if (aPrivilege) {
  2082     *aPrivilege = Content;
  2085   if (aDefaultPersistenceType) {
  2086     *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
  2089   return NS_OK;
  2092 // static
  2093 nsresult
  2094 QuotaManager::GetInfoFromWindow(nsPIDOMWindow* aWindow,
  2095                                 nsACString* aGroup,
  2096                                 nsACString* aASCIIOrigin,
  2097                                 StoragePrivilege* aPrivilege,
  2098                                 PersistenceType* aDefaultPersistenceType)
  2100   NS_ASSERTION(NS_IsMainThread(),
  2101                "We're about to touch a window off the main thread!");
  2102   NS_ASSERTION(aWindow, "Don't hand me a null window!");
  2104   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
  2105   NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
  2107   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
  2108   NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
  2110   nsresult rv = GetInfoFromPrincipal(principal, aGroup, aASCIIOrigin,
  2111                                      aPrivilege, aDefaultPersistenceType);
  2112   NS_ENSURE_SUCCESS(rv, rv);
  2114   return NS_OK;
  2117 // static
  2118 void
  2119 QuotaManager::GetInfoForChrome(nsACString* aGroup,
  2120                                nsACString* aASCIIOrigin,
  2121                                StoragePrivilege* aPrivilege,
  2122                                PersistenceType* aDefaultPersistenceType)
  2124   NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
  2126   static const char kChromeOrigin[] = "chrome";
  2128   if (aGroup) {
  2129     aGroup->AssignLiteral(kChromeOrigin);
  2131   if (aASCIIOrigin) {
  2132     aASCIIOrigin->AssignLiteral(kChromeOrigin);
  2134   if (aPrivilege) {
  2135     *aPrivilege = Chrome;
  2137   if (aDefaultPersistenceType) {
  2138     *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
  2142 NS_IMPL_ISUPPORTS(QuotaManager, nsIQuotaManager, nsIObserver)
  2144 NS_IMETHODIMP
  2145 QuotaManager::GetUsageForURI(nsIURI* aURI,
  2146                              nsIUsageCallback* aCallback,
  2147                              uint32_t aAppId,
  2148                              bool aInMozBrowserOnly,
  2149                              uint8_t aOptionalArgCount,
  2150                              nsIQuotaRequest** _retval)
  2152   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2154   NS_ENSURE_ARG_POINTER(aURI);
  2155   NS_ENSURE_ARG_POINTER(aCallback);
  2157   // This only works from the main process.
  2158   NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
  2160   if (!aOptionalArgCount) {
  2161     aAppId = nsIScriptSecurityManager::NO_APP_ID;
  2164   // Figure out which origin we're dealing with.
  2165   nsCString group;
  2166   nsCString origin;
  2167   nsresult rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, &group, &origin,
  2168                                nullptr, nullptr);
  2169   NS_ENSURE_SUCCESS(rv, rv);
  2171   OriginOrPatternString oops = OriginOrPatternString::FromOrigin(origin);
  2173   nsRefPtr<AsyncUsageRunnable> runnable =
  2174     new AsyncUsageRunnable(aAppId, aInMozBrowserOnly, group, oops, aURI,
  2175                            aCallback);
  2177   // Put the computation runnable in the queue.
  2178   rv = WaitForOpenAllowed(oops, Nullable<PersistenceType>(), EmptyCString(),
  2179                           runnable);
  2180   NS_ENSURE_SUCCESS(rv, rv);
  2182   runnable->AdvanceState();
  2184   runnable.forget(_retval);
  2185   return NS_OK;
  2188 NS_IMETHODIMP
  2189 QuotaManager::Clear()
  2191   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2193   if (!gTestingEnabled) {
  2194     NS_WARNING("Testing features are not enabled!");
  2195     return NS_OK;
  2198   OriginOrPatternString oops = OriginOrPatternString::FromNull();
  2200   nsRefPtr<ResetOrClearRunnable> runnable = new ResetOrClearRunnable(true);
  2202   // Put the clear runnable in the queue.
  2203   nsresult rv =
  2204     WaitForOpenAllowed(oops, Nullable<PersistenceType>(), EmptyCString(),
  2205                        runnable);
  2206   NS_ENSURE_SUCCESS(rv, rv);
  2208   runnable->AdvanceState();
  2210   // Give the runnable some help by invalidating any storages in the way.
  2211   StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
  2212   matches.Find(mLiveStorages);
  2214   for (uint32_t index = 0; index < matches.Length(); index++) {
  2215     // We need to grab references to any live storages here to prevent them
  2216     // from dying while we invalidate them.
  2217     nsCOMPtr<nsIOfflineStorage> storage = matches[index];
  2218     storage->Invalidate();
  2221   // After everything has been invalidated the helper should be dispatched to
  2222   // the end of the event queue.
  2223   return NS_OK;
  2226 NS_IMETHODIMP
  2227 QuotaManager::ClearStoragesForURI(nsIURI* aURI,
  2228                                   uint32_t aAppId,
  2229                                   bool aInMozBrowserOnly,
  2230                                   const nsACString& aPersistenceType,
  2231                                   uint8_t aOptionalArgCount)
  2233   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2235   NS_ENSURE_ARG_POINTER(aURI);
  2237   // This only works from the main process.
  2238   NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
  2240   if (!aOptionalArgCount) {
  2241     aAppId = nsIScriptSecurityManager::NO_APP_ID;
  2244   // Figure out which origin we're dealing with.
  2245   nsCString origin;
  2246   nsresult rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, nullptr, &origin,
  2247                                nullptr, nullptr);
  2248   NS_ENSURE_SUCCESS(rv, rv);
  2250   nsAutoCString pattern;
  2251   GetOriginPatternString(aAppId, aInMozBrowserOnly, origin, pattern);
  2253   Nullable<PersistenceType> persistenceType;
  2254   rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
  2255   NS_ENSURE_SUCCESS(rv, rv);
  2257   // If there is a pending or running clear operation for this origin, return
  2258   // immediately.
  2259   if (IsClearOriginPending(pattern, persistenceType)) {
  2260     return NS_OK;
  2263   OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern);
  2265   // Queue up the origin clear runnable.
  2266   nsRefPtr<OriginClearRunnable> runnable =
  2267     new OriginClearRunnable(oops, persistenceType);
  2269   rv = WaitForOpenAllowed(oops, persistenceType, EmptyCString(), runnable);
  2270   NS_ENSURE_SUCCESS(rv, rv);
  2272   runnable->AdvanceState();
  2274   // Give the runnable some help by invalidating any storages in the way.
  2275   StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
  2276   matches.Find(mLiveStorages, pattern);
  2278   for (uint32_t index = 0; index < matches.Length(); index++) {
  2279     if (persistenceType.IsNull() ||
  2280         matches[index]->Type() == persistenceType.Value()) {
  2281       // We need to grab references to any live storages here to prevent them
  2282       // from dying while we invalidate them.
  2283       nsCOMPtr<nsIOfflineStorage> storage = matches[index];
  2284       storage->Invalidate();
  2288   // After everything has been invalidated the helper should be dispatched to
  2289   // the end of the event queue.
  2290   return NS_OK;
  2293 NS_IMETHODIMP
  2294 QuotaManager::Reset()
  2296   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2298   if (!gTestingEnabled) {
  2299     NS_WARNING("Testing features are not enabled!");
  2300     return NS_OK;
  2303   OriginOrPatternString oops = OriginOrPatternString::FromNull();
  2305   nsRefPtr<ResetOrClearRunnable> runnable = new ResetOrClearRunnable(false);
  2307   // Put the reset runnable in the queue.
  2308   nsresult rv =
  2309     WaitForOpenAllowed(oops, Nullable<PersistenceType>(), EmptyCString(),
  2310                        runnable);
  2311   NS_ENSURE_SUCCESS(rv, rv);
  2313   runnable->AdvanceState();
  2315   // Give the runnable some help by invalidating any storages in the way.
  2316   StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
  2317   matches.Find(mLiveStorages);
  2319   for (uint32_t index = 0; index < matches.Length(); index++) {
  2320     // We need to grab references to any live storages here to prevent them
  2321     // from dying while we invalidate them.
  2322     nsCOMPtr<nsIOfflineStorage> storage = matches[index];
  2323     storage->Invalidate();
  2326   // After everything has been invalidated the helper should be dispatched to
  2327   // the end of the event queue.
  2328   return NS_OK;
  2331 NS_IMETHODIMP
  2332 QuotaManager::Observe(nsISupports* aSubject,
  2333                       const char* aTopic,
  2334                       const char16_t* aData)
  2336   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2338   if (!strcmp(aTopic, PROFILE_BEFORE_CHANGE_OBSERVER_ID)) {
  2339     // Setting this flag prevents the service from being recreated and prevents
  2340     // further storagess from being created.
  2341     if (gShutdown.exchange(true)) {
  2342       NS_ERROR("Shutdown more than once?!");
  2345     if (IsMainProcess()) {
  2346       FileService* service = FileService::Get();
  2347       if (service) {
  2348         // This should only wait for storages registered in this manager
  2349         // to complete. Other storages may still have running locked files.
  2350         // If the necko service (thread pool) gets the shutdown notification
  2351         // first then the sync loop won't be processed at all, otherwise it will
  2352         // lock the main thread until all storages registered in this manager
  2353         // are finished.
  2355         nsTArray<uint32_t> indexes;
  2356         for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
  2357           if (mClients[index]->IsFileServiceUtilized()) {
  2358             indexes.AppendElement(index);
  2362         StorageMatcher<nsTArray<nsCOMPtr<nsIFileStorage> > > liveStorages;
  2363         liveStorages.Find(mLiveStorages, &indexes);
  2365         if (!liveStorages.IsEmpty()) {
  2366           nsRefPtr<WaitForLockedFilesToFinishRunnable> runnable =
  2367             new WaitForLockedFilesToFinishRunnable();
  2369           service->WaitForStoragesToComplete(liveStorages, runnable);
  2371           nsIThread* thread = NS_GetCurrentThread();
  2372           while (runnable->IsBusy()) {
  2373             if (!NS_ProcessNextEvent(thread)) {
  2374               NS_ERROR("Failed to process next event!");
  2375               break;
  2381       // Kick off the shutdown timer.
  2382       if (NS_FAILED(mShutdownTimer->Init(this, DEFAULT_SHUTDOWN_TIMER_MS,
  2383                                          nsITimer::TYPE_ONE_SHOT))) {
  2384         NS_WARNING("Failed to initialize shutdown timer!");
  2387       // Each client will spin the event loop while we wait on all the threads
  2388       // to close. Our timer may fire during that loop.
  2389       for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
  2390         mClients[index]->ShutdownTransactionService();
  2393       // Cancel the timer regardless of whether it actually fired.
  2394       if (NS_FAILED(mShutdownTimer->Cancel())) {
  2395         NS_WARNING("Failed to cancel shutdown timer!");
  2398       // Give clients a chance to cleanup IO thread only objects.
  2399       nsCOMPtr<nsIRunnable> runnable =
  2400         NS_NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects);
  2401       if (!runnable) {
  2402         NS_WARNING("Failed to create runnable!");
  2405       if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
  2406         NS_WARNING("Failed to dispatch runnable!");
  2409       // Make sure to join with our IO thread.
  2410       if (NS_FAILED(mIOThread->Shutdown())) {
  2411         NS_WARNING("Failed to shutdown IO thread!");
  2415     return NS_OK;
  2418   if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
  2419     NS_ASSERTION(IsMainProcess(), "Should only happen in the main process!");
  2421     NS_WARNING("Some storage operations are taking longer than expected "
  2422                "during shutdown and will be aborted!");
  2424     // Grab all live storages, for all origins.
  2425     StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 50> > liveStorages;
  2426     liveStorages.Find(mLiveStorages);
  2428     // Invalidate them all.
  2429     if (!liveStorages.IsEmpty()) {
  2430       uint32_t count = liveStorages.Length();
  2431       for (uint32_t index = 0; index < count; index++) {
  2432         liveStorages[index]->Invalidate();
  2436     return NS_OK;
  2439   if (!strcmp(aTopic, TOPIC_WEB_APP_CLEAR_DATA)) {
  2440     nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
  2441       do_QueryInterface(aSubject);
  2442     NS_ENSURE_TRUE(params, NS_ERROR_UNEXPECTED);
  2444     uint32_t appId;
  2445     nsresult rv = params->GetAppId(&appId);
  2446     NS_ENSURE_SUCCESS(rv, rv);
  2448     bool browserOnly;
  2449     rv = params->GetBrowserOnly(&browserOnly);
  2450     NS_ENSURE_SUCCESS(rv, rv);
  2452     rv = ClearStoragesForApp(appId, browserOnly);
  2453     NS_ENSURE_SUCCESS(rv, rv);
  2455     return NS_OK;
  2458   NS_NOTREACHED("Unknown topic!");
  2459   return NS_ERROR_UNEXPECTED;
  2462 void
  2463 QuotaManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
  2465   NS_ASSERTION(mCurrentWindowIndex != BAD_TLS_INDEX,
  2466                "Should have a valid TLS storage index!");
  2468   if (aWindow) {
  2469     NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex),
  2470                  "Somebody forgot to clear the current window!");
  2471     PR_SetThreadPrivate(mCurrentWindowIndex, aWindow);
  2473   else {
  2474     // We cannot assert PR_GetThreadPrivate(mCurrentWindowIndex) here because
  2475     // there are some cases where we did not already have a window.
  2476     PR_SetThreadPrivate(mCurrentWindowIndex, nullptr);
  2480 void
  2481 QuotaManager::CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow)
  2483   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2485   nsRefPtr<CheckQuotaHelper> helper;
  2487   MutexAutoLock autoLock(mQuotaMutex);
  2489   if (mCheckQuotaHelpers.Get(aWindow, getter_AddRefs(helper))) {
  2490     helper->Cancel();
  2494 bool
  2495 QuotaManager::LockedQuotaIsLifted()
  2497   mQuotaMutex.AssertCurrentThreadOwns();
  2499   NS_ASSERTION(mCurrentWindowIndex != BAD_TLS_INDEX,
  2500                "Should have a valid TLS storage index!");
  2502   nsPIDOMWindow* window =
  2503     static_cast<nsPIDOMWindow*>(PR_GetThreadPrivate(mCurrentWindowIndex));
  2505   // Quota is not enforced in chrome contexts (e.g. for components and JSMs)
  2506   // so we must have a window here.
  2507   NS_ASSERTION(window, "Why don't we have a Window here?");
  2509   bool createdHelper = false;
  2511   nsRefPtr<CheckQuotaHelper> helper;
  2512   if (!mCheckQuotaHelpers.Get(window, getter_AddRefs(helper))) {
  2513     helper = new CheckQuotaHelper(window, mQuotaMutex);
  2514     createdHelper = true;
  2516     mCheckQuotaHelpers.Put(window, helper);
  2518     // Unlock while calling out to XPCOM (code behind the dispatch method needs
  2519     // to acquire its own lock which can potentially lead to a deadlock and it
  2520     // also calls an observer that can do various stuff like IO, so it's better
  2521     // to not hold our mutex while that happens).
  2523       MutexAutoUnlock autoUnlock(mQuotaMutex);
  2525       nsresult rv = NS_DispatchToMainThread(helper);
  2526       NS_ENSURE_SUCCESS(rv, false);
  2529     // Relocked.  If any other threads hit the quota limit on the same Window,
  2530     // they are using the helper we created here and are now blocking in
  2531     // PromptAndReturnQuotaDisabled.
  2534   bool result = helper->PromptAndReturnQuotaIsDisabled();
  2536   // If this thread created the helper and added it to the hash, this thread
  2537   // must remove it.
  2538   if (createdHelper) {
  2539     mCheckQuotaHelpers.Remove(window);
  2542   return result;
  2545 uint64_t
  2546 QuotaManager::LockedCollectOriginsForEviction(
  2547                                             uint64_t aMinSizeToBeFreed,
  2548                                             nsTArray<OriginInfo*>& aOriginInfos)
  2550   mQuotaMutex.AssertCurrentThreadOwns();
  2552   nsRefPtr<CollectOriginsHelper> helper =
  2553     new CollectOriginsHelper(mQuotaMutex, aMinSizeToBeFreed);
  2555   // Unlock while calling out to XPCOM (see the detailed comment in
  2556   // LockedQuotaIsLifted)
  2558     MutexAutoUnlock autoUnlock(mQuotaMutex);
  2560     if (NS_FAILED(NS_DispatchToMainThread(helper))) {
  2561       NS_WARNING("Failed to dispatch to the main thread!");
  2565   return helper->BlockAndReturnOriginsForEviction(aOriginInfos);
  2568 void
  2569 QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
  2570                                          const nsACString& aGroup,
  2571                                          const nsACString& aOrigin)
  2573   mQuotaMutex.AssertCurrentThreadOwns();
  2575   GroupInfoPair* pair;
  2576   mGroupInfoPairs.Get(aGroup, &pair);
  2578   if (!pair) {
  2579     return;
  2582   nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
  2583   if (groupInfo) {
  2584     groupInfo->LockedRemoveOriginInfo(aOrigin);
  2586     if (!groupInfo->LockedHasOriginInfos()) {
  2587       pair->LockedClearGroupInfo(aPersistenceType);
  2589       if (!pair->LockedHasGroupInfos()) {
  2590         mGroupInfoPairs.Remove(aGroup);
  2596 nsresult
  2597 QuotaManager::AcquireExclusiveAccess(const nsACString& aPattern,
  2598                                      Nullable<PersistenceType> aPersistenceType,
  2599                                      nsIOfflineStorage* aStorage,
  2600                                      AcquireListener* aListener,
  2601                                      WaitingOnStoragesCallback aCallback,
  2602                                      void* aClosure)
  2604   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2605   NS_ASSERTION(aListener, "Need a listener!");
  2607   // Find the right SynchronizedOp.
  2608   SynchronizedOp* op =
  2609     FindSynchronizedOp(aPattern, aPersistenceType,
  2610                        aStorage ? aStorage->Id() : EmptyCString());
  2612   NS_ASSERTION(op, "We didn't find a SynchronizedOp?");
  2613   NS_ASSERTION(!op->mListener, "SynchronizedOp already has a listener?!?");
  2615   nsTArray<nsCOMPtr<nsIOfflineStorage> > liveStorages;
  2617   if (aStorage) {
  2618     // We need to wait for the storages to go away.
  2619     // Hold on to all storage objects that represent the same storage file
  2620     // (except the one that is requesting this version change).
  2622     Client::Type clientType = aStorage->GetClient()->GetType();
  2624     StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
  2625     matches.Find(mLiveStorages, aPattern, clientType);
  2627     if (!matches.IsEmpty()) {
  2628       // Grab all storages that are not yet closed but whose storage id match
  2629       // the one we're looking for.
  2630       for (uint32_t index = 0; index < matches.Length(); index++) {
  2631         nsIOfflineStorage*& storage = matches[index];
  2632         if (!storage->IsClosed() &&
  2633             storage != aStorage &&
  2634             storage->Id() == aStorage->Id()) {
  2635           liveStorages.AppendElement(storage);
  2640     if (!liveStorages.IsEmpty()) {
  2641       NS_ASSERTION(op->mStorages[clientType].IsEmpty(),
  2642                    "How do we already have storages here?");
  2643       op->mStorages[clientType].AppendElements(liveStorages);
  2646   else {
  2647     StorageMatcher<ArrayCluster<nsIOfflineStorage*> > matches;
  2648     if (aPattern.IsVoid()) {
  2649       matches.Find(mLiveStorages);
  2651     else {
  2652       matches.Find(mLiveStorages, aPattern);
  2655     if (!matches.IsEmpty()) {
  2656       // We want *all* storages, even those that are closed, when we're going to
  2657       // clear the origin.
  2658       matches.AppendElementsTo(liveStorages);
  2660       NS_ASSERTION(op->mStorages.IsEmpty(),
  2661                    "How do we already have storages here?");
  2662       matches.SwapElements(op->mStorages);
  2666   op->mListener = aListener;
  2668   if (!liveStorages.IsEmpty()) {
  2669     // Give our callback the storages so it can decide what to do with them.
  2670     aCallback(liveStorages, aClosure);
  2672     NS_ASSERTION(liveStorages.IsEmpty(),
  2673                  "Should have done something with the array!");
  2675     if (aStorage) {
  2676       // Wait for those storages to close.
  2677       return NS_OK;
  2681   // If we're trying to open a storage and nothing blocks it, or if we're
  2682   // clearing an origin, then go ahead and schedule the op.
  2683   nsresult rv = RunSynchronizedOp(aStorage, op);
  2684   NS_ENSURE_SUCCESS(rv, rv);
  2686   return NS_OK;
  2689 nsresult
  2690 QuotaManager::RunSynchronizedOp(nsIOfflineStorage* aStorage,
  2691                                 SynchronizedOp* aOp)
  2693   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2694   NS_ASSERTION(aOp, "Null pointer!");
  2695   NS_ASSERTION(aOp->mListener, "No listener on this op!");
  2696   NS_ASSERTION(!aStorage ||
  2697                aOp->mStorages[aStorage->GetClient()->GetType()].IsEmpty(),
  2698                "This op isn't ready to run!");
  2700   ArrayCluster<nsIOfflineStorage*> storages;
  2702   uint32_t startIndex;
  2703   uint32_t endIndex;
  2705   if (aStorage) {
  2706     Client::Type clientType = aStorage->GetClient()->GetType();
  2708     storages[clientType].AppendElement(aStorage);
  2710     startIndex = clientType;
  2711     endIndex = clientType + 1;
  2713   else {
  2714     aOp->mStorages.SwapElements(storages);
  2716     startIndex = 0;
  2717     endIndex = Client::TYPE_MAX;
  2720   nsRefPtr<WaitForTransactionsToFinishRunnable> runnable =
  2721     new WaitForTransactionsToFinishRunnable(aOp);
  2723   // Ask the file service to call us back when it's done with this storage.
  2724   FileService* service = FileService::Get();
  2726   if (service) {
  2727     // Have to copy here in case a transaction service needs a list too.
  2728     nsTArray<nsCOMPtr<nsIFileStorage> > array;
  2730     for (uint32_t index = startIndex; index < endIndex; index++)  {
  2731       if (!storages[index].IsEmpty() &&
  2732           mClients[index]->IsFileServiceUtilized()) {
  2733         array.AppendElements(storages[index]);
  2737     if (!array.IsEmpty()) {
  2738       runnable->AddRun();
  2740       service->WaitForStoragesToComplete(array, runnable);
  2744   // Ask each transaction service to call us back when they're done with this
  2745   // storage.
  2746   for (uint32_t index = startIndex; index < endIndex; index++)  {
  2747     nsRefPtr<Client>& client = mClients[index];
  2748     if (!storages[index].IsEmpty() && client->IsTransactionServiceActivated()) {
  2749       runnable->AddRun();
  2751       client->WaitForStoragesToComplete(storages[index], runnable);
  2755   nsresult rv = runnable->Run();
  2756   NS_ENSURE_SUCCESS(rv, rv);
  2758   return NS_OK;
  2761 SynchronizedOp*
  2762 QuotaManager::FindSynchronizedOp(const nsACString& aPattern,
  2763                                  Nullable<PersistenceType> aPersistenceType,
  2764                                  const nsACString& aId)
  2766   for (uint32_t index = 0; index < mSynchronizedOps.Length(); index++) {
  2767     const nsAutoPtr<SynchronizedOp>& currentOp = mSynchronizedOps[index];
  2768     if (PatternMatchesOrigin(aPattern, currentOp->mOriginOrPattern) &&
  2769         (currentOp->mPersistenceType.IsNull() ||
  2770          currentOp->mPersistenceType == aPersistenceType) &&
  2771         (currentOp->mId.IsEmpty() || currentOp->mId == aId)) {
  2772       return currentOp;
  2776   return nullptr;
  2779 nsresult
  2780 QuotaManager::ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly)
  2782   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2783   NS_ASSERTION(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
  2784                "Bad appId!");
  2786   // This only works from the main process.
  2787   NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
  2789   nsAutoCString pattern;
  2790   GetOriginPatternStringMaybeIgnoreBrowser(aAppId, aBrowserOnly, pattern);
  2792   // Clear both temporary and persistent storages.
  2793   Nullable<PersistenceType> persistenceType;
  2795   // If there is a pending or running clear operation for this app, return
  2796   // immediately.
  2797   if (IsClearOriginPending(pattern, persistenceType)) {
  2798     return NS_OK;
  2801   OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern);
  2803   // Queue up the origin clear runnable.
  2804   nsRefPtr<OriginClearRunnable> runnable =
  2805     new OriginClearRunnable(oops, persistenceType);
  2807   nsresult rv =
  2808     WaitForOpenAllowed(oops, persistenceType, EmptyCString(), runnable);
  2809   NS_ENSURE_SUCCESS(rv, rv);
  2811   runnable->AdvanceState();
  2813   // Give the runnable some help by invalidating any storages in the way.
  2814   StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
  2815   matches.Find(mLiveStorages, pattern);
  2817   for (uint32_t index = 0; index < matches.Length(); index++) {
  2818     // We need to grab references here to prevent the storage from dying while
  2819     // we invalidate it.
  2820     nsCOMPtr<nsIOfflineStorage> storage = matches[index];
  2821     storage->Invalidate();
  2824   return NS_OK;
  2827 // static
  2828 PLDHashOperator
  2829 QuotaManager::GetOriginsExceedingGroupLimit(const nsACString& aKey,
  2830                                             GroupInfoPair* aValue,
  2831                                             void* aUserArg)
  2833   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  2834   NS_ASSERTION(aValue, "Null pointer!");
  2836   nsRefPtr<GroupInfo> groupInfo =
  2837     aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
  2838   if (groupInfo) {
  2839     QuotaManager* quotaManager = QuotaManager::Get();
  2840     NS_ASSERTION(quotaManager, "Shouldn't be null!");
  2842     if (groupInfo->mUsage > quotaManager->GetGroupLimit()) {
  2843       nsTArray<OriginInfo*>* doomedOriginInfos =
  2844         static_cast<nsTArray<OriginInfo*>*>(aUserArg);
  2846       nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos;
  2847       originInfos.Sort(OriginInfoLRUComparator());
  2849       uint64_t usage = groupInfo->mUsage;
  2850       for (uint32_t i = 0; i < originInfos.Length(); i++) {
  2851         OriginInfo* originInfo = originInfos[i];
  2853         doomedOriginInfos->AppendElement(originInfo);
  2854         usage -= originInfo->mUsage;
  2856         if (usage <= quotaManager->GetGroupLimit()) {
  2857           break;
  2863   return PL_DHASH_NEXT;
  2866 // static
  2867 PLDHashOperator
  2868 QuotaManager::GetAllTemporaryStorageOrigins(const nsACString& aKey,
  2869                                             GroupInfoPair* aValue,
  2870                                             void* aUserArg)
  2872   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  2873   NS_ASSERTION(aValue, "Null pointer!");
  2875   nsRefPtr<GroupInfo> groupInfo =
  2876     aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
  2877   if (groupInfo) {
  2878     nsTArray<OriginInfo*>* originInfos =
  2879       static_cast<nsTArray<OriginInfo*>*>(aUserArg);
  2881     originInfos->AppendElements(groupInfo->mOriginInfos);
  2884   return PL_DHASH_NEXT;
  2887 void
  2888 QuotaManager::CheckTemporaryStorageLimits()
  2890   AssertIsOnIOThread();
  2892   nsTArray<OriginInfo*> doomedOriginInfos;
  2894     MutexAutoLock lock(mQuotaMutex);
  2896     mGroupInfoPairs.EnumerateRead(GetOriginsExceedingGroupLimit,
  2897                                   &doomedOriginInfos);
  2899     uint64_t usage = 0;
  2900     for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
  2901       usage += doomedOriginInfos[index]->mUsage;
  2904     if (mTemporaryStorageUsage - usage > mTemporaryStorageLimit) {
  2905       nsTArray<OriginInfo*> originInfos;
  2907       mGroupInfoPairs.EnumerateRead(GetAllTemporaryStorageOrigins,
  2908                                     &originInfos);
  2910       for (uint32_t index = originInfos.Length(); index > 0; index--) {
  2911         if (doomedOriginInfos.Contains(originInfos[index - 1])) {
  2912           originInfos.RemoveElementAt(index - 1);
  2916       originInfos.Sort(OriginInfoLRUComparator());
  2918       for (uint32_t i = 0; i < originInfos.Length(); i++) {
  2919         if (mTemporaryStorageUsage - usage <= mTemporaryStorageLimit) {
  2920           originInfos.TruncateLength(i);
  2921           break;
  2924         usage += originInfos[i]->mUsage;
  2927       doomedOriginInfos.AppendElements(originInfos);
  2931   for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
  2932     DeleteTemporaryFilesForOrigin(doomedOriginInfos[index]->mOrigin);
  2935   nsTArray<nsCString> doomedOrigins;
  2937     MutexAutoLock lock(mQuotaMutex);
  2939     for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
  2940       OriginInfo* doomedOriginInfo = doomedOriginInfos[index];
  2942       nsCString group = doomedOriginInfo->mGroupInfo->mGroup;
  2943       nsCString origin = doomedOriginInfo->mOrigin;
  2944       LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY, group, origin);
  2946 #ifdef DEBUG
  2947       doomedOriginInfos[index] = nullptr;
  2948 #endif
  2950       doomedOrigins.AppendElement(origin);
  2954   for (uint32_t index = 0; index < doomedOrigins.Length(); index++) {
  2955     OriginClearCompleted(
  2956                        PERSISTENCE_TYPE_TEMPORARY,
  2957                        OriginOrPatternString::FromOrigin(doomedOrigins[index]));
  2961 // static
  2962 PLDHashOperator
  2963 QuotaManager::AddTemporaryStorageOrigins(
  2964                                        const nsACString& aKey,
  2965                                        ArrayCluster<nsIOfflineStorage*>* aValue,
  2966                                        void* aUserArg)
  2968   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  2969   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  2970   NS_ASSERTION(aValue, "Null pointer!");
  2971   NS_ASSERTION(aUserArg, "Null pointer!");
  2973   OriginCollection& collection = *static_cast<OriginCollection*>(aUserArg);
  2975   if (collection.ContainsOrigin(aKey)) {
  2976     return PL_DHASH_NEXT;
  2979   for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
  2980     nsTArray<nsIOfflineStorage*>& array = (*aValue)[i];
  2981     for (uint32_t j = 0; j < array.Length(); j++) {
  2982       nsIOfflineStorage*& storage = array[j];
  2983       if (storage->Type() == PERSISTENCE_TYPE_TEMPORARY) {
  2984         collection.AddOrigin(aKey);
  2985         return PL_DHASH_NEXT;
  2990   return PL_DHASH_NEXT;
  2993 // static
  2994 PLDHashOperator
  2995 QuotaManager::GetInactiveTemporaryStorageOrigins(const nsACString& aKey,
  2996                                                  GroupInfoPair* aValue,
  2997                                                  void* aUserArg)
  2999   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
  3000   NS_ASSERTION(aValue, "Null pointer!");
  3001   NS_ASSERTION(aUserArg, "Null pointer!");
  3003   nsRefPtr<GroupInfo> groupInfo =
  3004     aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
  3005   if (groupInfo) {
  3006     InactiveOriginsInfo* info = static_cast<InactiveOriginsInfo*>(aUserArg);
  3008     nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos;
  3010     for (uint32_t i = 0; i < originInfos.Length(); i++) {
  3011       OriginInfo* originInfo = originInfos[i];
  3013       if (!info->collection.ContainsOrigin(originInfo->mOrigin)) {
  3014         NS_ASSERTION(!originInfo->mQuotaObjects.Count(),
  3015                      "Inactive origin shouldn't have open files!");
  3016         info->origins.AppendElement(originInfo);
  3021   return PL_DHASH_NEXT;
  3024 uint64_t
  3025 QuotaManager::CollectOriginsForEviction(uint64_t aMinSizeToBeFreed,
  3026                                         nsTArray<OriginInfo*>& aOriginInfos)
  3028   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3030   // Collect active origins first.
  3031   OriginCollection originCollection;
  3033   // Add patterns and origins that have running or pending synchronized ops.
  3034   // (add patterns first to reduce redundancy in the origin collection).
  3035   uint32_t index;
  3036   for (index = 0; index < mSynchronizedOps.Length(); index++) {
  3037     nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
  3038     if (op->mPersistenceType.IsNull() ||
  3039         op->mPersistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) {
  3040       if (op->mOriginOrPattern.IsPattern() &&
  3041           !originCollection.ContainsPattern(op->mOriginOrPattern)) {
  3042         originCollection.AddPattern(op->mOriginOrPattern);
  3047   for (index = 0; index < mSynchronizedOps.Length(); index++) {
  3048     nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
  3049     if (op->mPersistenceType.IsNull() ||
  3050         op->mPersistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) {
  3051       if (op->mOriginOrPattern.IsOrigin() &&
  3052           !originCollection.ContainsOrigin(op->mOriginOrPattern)) {
  3053         originCollection.AddOrigin(op->mOriginOrPattern);
  3058   // Add origins that have live temporary storages.
  3059   mLiveStorages.EnumerateRead(AddTemporaryStorageOrigins, &originCollection);
  3061   // Enumerate inactive origins. This must be protected by the mutex.
  3062   nsTArray<OriginInfo*> inactiveOrigins;
  3064     InactiveOriginsInfo info(originCollection, inactiveOrigins);
  3065     MutexAutoLock lock(mQuotaMutex);
  3066     mGroupInfoPairs.EnumerateRead(GetInactiveTemporaryStorageOrigins, &info);
  3069   // We now have a list of all inactive origins. So it's safe to sort the list
  3070   // and calculate available size without holding the lock.
  3072   // Sort by the origin access time.
  3073   inactiveOrigins.Sort(OriginInfoLRUComparator());
  3075   // Create a list of inactive and the least recently used origins
  3076   // whose aggregate size is greater or equals the minimal size to be freed.
  3077   uint64_t sizeToBeFreed = 0;
  3078   for(index = 0; index < inactiveOrigins.Length(); index++) {
  3079     if (sizeToBeFreed >= aMinSizeToBeFreed) {
  3080       inactiveOrigins.TruncateLength(index);
  3081       break;
  3084     sizeToBeFreed += inactiveOrigins[index]->mUsage;
  3087   if (sizeToBeFreed >= aMinSizeToBeFreed) {
  3088     // Success, add synchronized ops for these origins, so any other
  3089     // operations for them will be delayed (until origin eviction is finalized).
  3091     for(index = 0; index < inactiveOrigins.Length(); index++) {
  3092       OriginOrPatternString oops =
  3093         OriginOrPatternString::FromOrigin(inactiveOrigins[index]->mOrigin);
  3095       AddSynchronizedOp(oops,
  3096                         Nullable<PersistenceType>(PERSISTENCE_TYPE_TEMPORARY));
  3099     inactiveOrigins.SwapElements(aOriginInfos);
  3100     return sizeToBeFreed;
  3103   return 0;
  3106 void
  3107 QuotaManager::DeleteTemporaryFilesForOrigin(const nsACString& aOrigin)
  3109   nsCOMPtr<nsIFile> directory;
  3110   nsresult rv = GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY, aOrigin,
  3111                                       getter_AddRefs(directory));
  3112   NS_ENSURE_SUCCESS_VOID(rv);
  3114   rv = directory->Remove(true);
  3115   if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
  3116       rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
  3117     // This should never fail if we've closed all storage connections
  3118     // correctly...
  3119     NS_ERROR("Failed to remove directory!");
  3123 void
  3124 QuotaManager::FinalizeOriginEviction(nsTArray<nsCString>& aOrigins)
  3126   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
  3128   nsRefPtr<FinalizeOriginEvictionRunnable> runnable =
  3129     new FinalizeOriginEvictionRunnable(aOrigins);
  3131   nsresult rv = IsOnIOThread() ? runnable->RunImmediately()
  3132                                : runnable->Dispatch();
  3133   NS_ENSURE_SUCCESS_VOID(rv);
  3136 void
  3137 QuotaManager::SaveOriginAccessTime(const nsACString& aOrigin,
  3138                                    int64_t aTimestamp)
  3140   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3142   if (QuotaManager::IsShuttingDown()) {
  3143     return;
  3146   nsRefPtr<SaveOriginAccessTimeRunnable> runnable =
  3147     new SaveOriginAccessTimeRunnable(aOrigin, aTimestamp);
  3149   if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
  3150     NS_WARNING("Failed to dispatch runnable!");
  3154 void
  3155 QuotaManager::GetOriginPatternString(uint32_t aAppId,
  3156                                      MozBrowserPatternFlag aBrowserFlag,
  3157                                      const nsACString& aOrigin,
  3158                                      nsAutoCString& _retval)
  3160   NS_ASSERTION(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
  3161                "Bad appId!");
  3162   NS_ASSERTION(aOrigin.IsEmpty() || aBrowserFlag != IgnoreMozBrowser,
  3163                "Bad args!");
  3165   if (aOrigin.IsEmpty()) {
  3166     _retval.Truncate();
  3168     _retval.AppendInt(aAppId);
  3169     _retval.Append('+');
  3171     if (aBrowserFlag != IgnoreMozBrowser) {
  3172       if (aBrowserFlag == MozBrowser) {
  3173         _retval.Append('t');
  3175       else {
  3176         _retval.Append('f');
  3178       _retval.Append('+');
  3181     return;
  3184 #ifdef DEBUG
  3185   if (aAppId != nsIScriptSecurityManager::NO_APP_ID ||
  3186       aBrowserFlag == MozBrowser) {
  3187     nsAutoCString pattern;
  3188     GetOriginPatternString(aAppId, aBrowserFlag, EmptyCString(), pattern);
  3189     NS_ASSERTION(PatternMatchesOrigin(pattern, aOrigin),
  3190                  "Origin doesn't match parameters!");
  3192 #endif
  3194   _retval = aOrigin;
  3197 SynchronizedOp::SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
  3198                                Nullable<PersistenceType> aPersistenceType,
  3199                                const nsACString& aId)
  3200 : mOriginOrPattern(aOriginOrPattern), mPersistenceType(aPersistenceType),
  3201   mId(aId)
  3203   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3204   MOZ_COUNT_CTOR(SynchronizedOp);
  3207 SynchronizedOp::~SynchronizedOp()
  3209   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3210   MOZ_COUNT_DTOR(SynchronizedOp);
  3213 bool
  3214 SynchronizedOp::MustWaitFor(const SynchronizedOp& aExistingOp)
  3216   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3218   if (aExistingOp.mOriginOrPattern.IsNull() || mOriginOrPattern.IsNull()) {
  3219     return true;
  3222   bool match;
  3224   if (aExistingOp.mOriginOrPattern.IsOrigin()) {
  3225     if (mOriginOrPattern.IsOrigin()) {
  3226       match = aExistingOp.mOriginOrPattern.Equals(mOriginOrPattern);
  3228     else {
  3229       match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern);
  3232   else if (mOriginOrPattern.IsOrigin()) {
  3233     match = PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
  3235   else {
  3236     match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern) ||
  3237             PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
  3240   // If the origins don't match, the second can proceed.
  3241   if (!match) {
  3242     return false;
  3245   // If the origins match but the persistence types are different, the second
  3246   // can proceed.
  3247   if (!aExistingOp.mPersistenceType.IsNull() && !mPersistenceType.IsNull() &&
  3248       aExistingOp.mPersistenceType.Value() != mPersistenceType.Value()) {
  3249     return false;
  3252   // If the origins and the ids match, the second must wait.
  3253   if (aExistingOp.mId == mId) {
  3254     return true;
  3257   // Waiting is required if either one corresponds to an origin clearing
  3258   // (an empty Id).
  3259   if (aExistingOp.mId.IsEmpty() || mId.IsEmpty()) {
  3260     return true;
  3263   // Otherwise, things for the same origin but different storages can proceed
  3264   // independently.
  3265   return false;
  3268 void
  3269 SynchronizedOp::DelayRunnable(nsIRunnable* aRunnable)
  3271   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3272   NS_ASSERTION(mDelayedRunnables.IsEmpty() || mId.IsEmpty(),
  3273                "Only ClearOrigin operations can delay multiple runnables!");
  3275   mDelayedRunnables.AppendElement(aRunnable);
  3278 void
  3279 SynchronizedOp::DispatchDelayedRunnables()
  3281   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3282   NS_ASSERTION(!mListener, "Any listener should be gone by now!");
  3284   uint32_t count = mDelayedRunnables.Length();
  3285   for (uint32_t index = 0; index < count; index++) {
  3286     NS_DispatchToCurrentThread(mDelayedRunnables[index]);
  3289   mDelayedRunnables.Clear();
  3292 CollectOriginsHelper::CollectOriginsHelper(mozilla::Mutex& aMutex,
  3293                                            uint64_t aMinSizeToBeFreed)
  3294 : mMinSizeToBeFreed(aMinSizeToBeFreed),
  3295   mMutex(aMutex),
  3296   mCondVar(aMutex, "CollectOriginsHelper::mCondVar"),
  3297   mSizeToBeFreed(0),
  3298   mWaiting(true)
  3300   MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
  3301   mMutex.AssertCurrentThreadOwns();
  3304 int64_t
  3305 CollectOriginsHelper::BlockAndReturnOriginsForEviction(
  3306                                             nsTArray<OriginInfo*>& aOriginInfos)
  3308   MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
  3309   mMutex.AssertCurrentThreadOwns();
  3311   while (mWaiting) {
  3312     mCondVar.Wait();
  3315   mOriginInfos.SwapElements(aOriginInfos);
  3316   return mSizeToBeFreed;
  3319 NS_IMETHODIMP
  3320 CollectOriginsHelper::Run()
  3322   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
  3324   QuotaManager* quotaManager = QuotaManager::Get();
  3325   NS_ASSERTION(quotaManager, "Shouldn't be null!");
  3327   // We use extra stack vars here to avoid race detector warnings (the same
  3328   // memory accessed with and without the lock held).
  3329   nsTArray<OriginInfo*> originInfos;
  3330   uint64_t sizeToBeFreed =
  3331     quotaManager->CollectOriginsForEviction(mMinSizeToBeFreed, originInfos);
  3333   MutexAutoLock lock(mMutex);
  3335   NS_ASSERTION(mWaiting, "Huh?!");
  3337   mOriginInfos.SwapElements(originInfos);
  3338   mSizeToBeFreed = sizeToBeFreed;
  3339   mWaiting = false;
  3340   mCondVar.Notify();
  3342   return NS_OK;
  3345 nsresult
  3346 OriginClearRunnable::OnExclusiveAccessAcquired()
  3348   QuotaManager* quotaManager = QuotaManager::Get();
  3349   NS_ASSERTION(quotaManager, "This should never fail!");
  3351   nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
  3352   NS_ENSURE_SUCCESS(rv, rv);
  3354   return NS_OK;
  3357 // static
  3358 void
  3359 OriginClearRunnable::InvalidateOpenedStorages(
  3360                               nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
  3361                               void* aClosure)
  3363   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3365   nsTArray<nsCOMPtr<nsIOfflineStorage> > storages;
  3366   storages.SwapElements(aStorages);
  3368   for (uint32_t index = 0; index < storages.Length(); index++) {
  3369     storages[index]->Invalidate();
  3373 void
  3374 OriginClearRunnable::DeleteFiles(QuotaManager* aQuotaManager,
  3375                                  PersistenceType aPersistenceType)
  3377   AssertIsOnIOThread();
  3378   NS_ASSERTION(aQuotaManager, "Don't pass me null!");
  3380   nsresult rv;
  3382   nsCOMPtr<nsIFile> directory =
  3383     do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  3384   NS_ENSURE_SUCCESS_VOID(rv);
  3386   rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
  3387   NS_ENSURE_SUCCESS_VOID(rv);
  3389   nsCOMPtr<nsISimpleEnumerator> entries;
  3390   if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(entries))) ||
  3391       !entries) {
  3392     return;
  3395   nsCString originSanitized(mOriginOrPattern);
  3396   SanitizeOriginString(originSanitized);
  3398   bool hasMore;
  3399   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
  3400     nsCOMPtr<nsISupports> entry;
  3401     rv = entries->GetNext(getter_AddRefs(entry));
  3402     NS_ENSURE_SUCCESS_VOID(rv);
  3404     nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
  3405     NS_ASSERTION(file, "Don't know what this is!");
  3407     bool isDirectory;
  3408     rv = file->IsDirectory(&isDirectory);
  3409     NS_ENSURE_SUCCESS_VOID(rv);
  3411     if (!isDirectory) {
  3412       NS_WARNING("Something in the IndexedDB directory that doesn't belong!");
  3413       continue;
  3416     nsString leafName;
  3417     rv = file->GetLeafName(leafName);
  3418     NS_ENSURE_SUCCESS_VOID(rv);
  3420     // Skip storages for other apps.
  3421     if (!PatternMatchesOrigin(originSanitized,
  3422                               NS_ConvertUTF16toUTF8(leafName))) {
  3423       continue;
  3426     if (NS_FAILED(file->Remove(true))) {
  3427       // This should never fail if we've closed all storage connections
  3428       // correctly...
  3429       NS_ERROR("Failed to remove directory!");
  3433   aQuotaManager->RemoveQuotaForPattern(aPersistenceType, mOriginOrPattern);
  3435   aQuotaManager->OriginClearCompleted(aPersistenceType, mOriginOrPattern);
  3438 NS_IMPL_ISUPPORTS_INHERITED0(OriginClearRunnable, nsRunnable)
  3440 NS_IMETHODIMP
  3441 OriginClearRunnable::Run()
  3443   PROFILER_LABEL("Quota", "OriginClearRunnable::Run");
  3445   QuotaManager* quotaManager = QuotaManager::Get();
  3446   NS_ASSERTION(quotaManager, "This should never fail!");
  3448   switch (mCallbackState) {
  3449     case Pending: {
  3450       NS_NOTREACHED("Should never get here without being dispatched!");
  3451       return NS_ERROR_UNEXPECTED;
  3454     case OpenAllowed: {
  3455       NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3457       AdvanceState();
  3459       // Now we have to wait until the thread pool is done with all of the
  3460       // storages we care about.
  3461       nsresult rv =
  3462         quotaManager->AcquireExclusiveAccess(mOriginOrPattern, mPersistenceType,
  3463                                              this, InvalidateOpenedStorages,
  3464                                              nullptr);
  3465       NS_ENSURE_SUCCESS(rv, rv);
  3467       return NS_OK;
  3470     case IO: {
  3471       AssertIsOnIOThread();
  3473       AdvanceState();
  3475       if (mPersistenceType.IsNull()) {
  3476         DeleteFiles(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
  3477         DeleteFiles(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
  3478       } else {
  3479         DeleteFiles(quotaManager, mPersistenceType.Value());
  3482       // Now dispatch back to the main thread.
  3483       if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
  3484         NS_WARNING("Failed to dispatch to main thread!");
  3485         return NS_ERROR_FAILURE;
  3488       return NS_OK;
  3491     case Complete: {
  3492       NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3494       // Tell the QuotaManager that we're done.
  3495       quotaManager->AllowNextSynchronizedOp(mOriginOrPattern, mPersistenceType,
  3496                                             EmptyCString());
  3498       return NS_OK;
  3501     default:
  3502       NS_ERROR("Unknown state value!");
  3503       return NS_ERROR_UNEXPECTED;
  3506   NS_NOTREACHED("Should never get here!");
  3507   return NS_ERROR_UNEXPECTED;
  3510 AsyncUsageRunnable::AsyncUsageRunnable(uint32_t aAppId,
  3511                                        bool aInMozBrowserOnly,
  3512                                        const nsACString& aGroup,
  3513                                        const OriginOrPatternString& aOrigin,
  3514                                        nsIURI* aURI,
  3515                                        nsIUsageCallback* aCallback)
  3516 : mURI(aURI),
  3517   mCallback(aCallback),
  3518   mAppId(aAppId),
  3519   mGroup(aGroup),
  3520   mOrigin(aOrigin),
  3521   mCallbackState(Pending),
  3522   mInMozBrowserOnly(aInMozBrowserOnly)
  3524   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3525   NS_ASSERTION(aURI, "Null pointer!");
  3526   NS_ASSERTION(!aGroup.IsEmpty(), "Empty group!");
  3527   NS_ASSERTION(aOrigin.IsOrigin(), "Expect origin only here!");
  3528   NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
  3529   NS_ASSERTION(aCallback, "Null pointer!");
  3532 nsresult
  3533 AsyncUsageRunnable::TakeShortcut()
  3535   NS_ASSERTION(mCallbackState == Pending, "Huh?");
  3537   nsresult rv = NS_DispatchToCurrentThread(this);
  3538   NS_ENSURE_SUCCESS(rv, rv);
  3540   mCallbackState = Shortcut;
  3541   return NS_OK;
  3544 nsresult
  3545 AsyncUsageRunnable::RunInternal()
  3547   QuotaManager* quotaManager = QuotaManager::Get();
  3548   NS_ASSERTION(quotaManager, "This should never fail!");
  3550   nsresult rv;
  3552   switch (mCallbackState) {
  3553     case Pending: {
  3554       NS_NOTREACHED("Should never get here without being dispatched!");
  3555       return NS_ERROR_UNEXPECTED;
  3558     case OpenAllowed: {
  3559       NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3561       AdvanceState();
  3563       rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
  3564       if (NS_FAILED(rv)) {
  3565         NS_WARNING("Failed to dispatch to the IO thread!");
  3568       return NS_OK;
  3571     case IO: {
  3572       AssertIsOnIOThread();
  3574       AdvanceState();
  3576       // Add all the persistent storage files we care about.
  3577       rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
  3578       NS_ENSURE_SUCCESS(rv, rv);
  3580       // Add all the temporary storage files we care about.
  3581       rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
  3582       NS_ENSURE_SUCCESS(rv, rv);
  3584       // Run dispatches us back to the main thread.
  3585       return NS_OK;
  3588     case Complete: // Fall through
  3589     case Shortcut: {
  3590       NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3592       // Call the callback unless we were canceled.
  3593       if (!mCanceled) {
  3594         mCallback->OnUsageResult(mURI, TotalUsage(), FileUsage(), mAppId,
  3595                                  mInMozBrowserOnly);
  3598       // Clean up.
  3599       mURI = nullptr;
  3600       mCallback = nullptr;
  3602       // And tell the QuotaManager that we're done.
  3603       if (mCallbackState == Complete) {
  3604         quotaManager->AllowNextSynchronizedOp(mOrigin,
  3605                                               Nullable<PersistenceType>(),
  3606                                               EmptyCString());
  3609       return NS_OK;
  3612     default:
  3613       NS_ERROR("Unknown state value!");
  3614       return NS_ERROR_UNEXPECTED;
  3617   NS_NOTREACHED("Should never get here!");
  3618   return NS_ERROR_UNEXPECTED;
  3621 nsresult
  3622 AsyncUsageRunnable::AddToUsage(QuotaManager* aQuotaManager,
  3623                                PersistenceType aPersistenceType)
  3625   AssertIsOnIOThread();
  3627   nsCOMPtr<nsIFile> directory;
  3628   nsresult rv = aQuotaManager->GetDirectoryForOrigin(aPersistenceType, mOrigin,
  3629                                                      getter_AddRefs(directory));
  3630   NS_ENSURE_SUCCESS(rv, rv);
  3632   bool exists;
  3633   rv = directory->Exists(&exists);
  3634   NS_ENSURE_SUCCESS(rv, rv);
  3636   // If the directory exists then enumerate all the files inside, adding up
  3637   // the sizes to get the final usage statistic.
  3638   if (exists && !mCanceled) {
  3639     bool initialized;
  3641     if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
  3642       initialized = aQuotaManager->mInitializedOrigins.Contains(mOrigin);
  3644       if (!initialized) {
  3645         rv = MaybeUpgradeOriginDirectory(directory);
  3646         NS_ENSURE_SUCCESS(rv, rv);
  3649     else {
  3650       NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
  3651       initialized = aQuotaManager->mTemporaryStorageInitialized;
  3654     nsCOMPtr<nsISimpleEnumerator> entries;
  3655     rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
  3656     NS_ENSURE_SUCCESS(rv, rv);
  3658     bool hasMore;
  3659     while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
  3660            hasMore && !mCanceled) {
  3661       nsCOMPtr<nsISupports> entry;
  3662       rv = entries->GetNext(getter_AddRefs(entry));
  3663       NS_ENSURE_SUCCESS(rv, rv);
  3665       nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
  3666       NS_ENSURE_TRUE(file, NS_NOINTERFACE);
  3668       nsString leafName;
  3669       rv = file->GetLeafName(leafName);
  3670       NS_ENSURE_SUCCESS(rv, rv);
  3672       if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
  3673           leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
  3674         continue;
  3677       if (!initialized) {
  3678         bool isDirectory;
  3679         rv = file->IsDirectory(&isDirectory);
  3680         NS_ENSURE_SUCCESS(rv, rv);
  3682         if (!isDirectory) {
  3683           NS_WARNING("Unknown file found!");
  3684           return NS_ERROR_UNEXPECTED;
  3688       Client::Type clientType;
  3689       rv = Client::TypeFromText(leafName, clientType);
  3690       if (NS_FAILED(rv)) {
  3691         NS_WARNING("Unknown directory found!");
  3692         if (!initialized) {
  3693           return NS_ERROR_UNEXPECTED;
  3695         continue;
  3698       nsRefPtr<Client>& client = aQuotaManager->mClients[clientType];
  3700       if (initialized) {
  3701         rv = client->GetUsageForOrigin(aPersistenceType, mGroup, mOrigin, this);
  3703       else {
  3704         rv = client->InitOrigin(aPersistenceType, mGroup, mOrigin, this);
  3706       NS_ENSURE_SUCCESS(rv, rv);
  3710   return NS_OK;
  3713 NS_IMPL_ISUPPORTS_INHERITED(AsyncUsageRunnable, nsRunnable, nsIQuotaRequest)
  3715 NS_IMETHODIMP
  3716 AsyncUsageRunnable::Run()
  3718   PROFILER_LABEL("Quota", "AsyncUsageRunnable::Run");
  3720   nsresult rv = RunInternal();
  3722   if (!NS_IsMainThread()) {
  3723     if (NS_FAILED(rv)) {
  3724       ResetUsage();
  3727     if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
  3728       NS_WARNING("Failed to dispatch to main thread!");
  3732   return NS_OK;
  3735 NS_IMETHODIMP
  3736 AsyncUsageRunnable::Cancel()
  3738   if (mCanceled.exchange(true)) {
  3739     NS_WARNING("Canceled more than once?!");
  3740     return NS_ERROR_UNEXPECTED;
  3743   return NS_OK;
  3746 nsresult
  3747 ResetOrClearRunnable::OnExclusiveAccessAcquired()
  3749   QuotaManager* quotaManager = QuotaManager::Get();
  3750   NS_ASSERTION(quotaManager, "This should never fail!");
  3752   nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
  3753   NS_ENSURE_SUCCESS(rv, rv);
  3755   return NS_OK;
  3758 // static
  3759 void
  3760 ResetOrClearRunnable::InvalidateOpenedStorages(
  3761                               nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
  3762                               void* aClosure)
  3764   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3766   nsTArray<nsCOMPtr<nsIOfflineStorage> > storages;
  3767   storages.SwapElements(aStorages);
  3769   for (uint32_t index = 0; index < storages.Length(); index++) {
  3770     storages[index]->Invalidate();
  3774 void
  3775 ResetOrClearRunnable::DeleteFiles(QuotaManager* aQuotaManager,
  3776                                   PersistenceType aPersistenceType)
  3778   AssertIsOnIOThread();
  3779   NS_ASSERTION(aQuotaManager, "Don't pass me null!");
  3781   nsresult rv;
  3783   nsCOMPtr<nsIFile> directory =
  3784     do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
  3785   NS_ENSURE_SUCCESS_VOID(rv);
  3787   rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
  3788   NS_ENSURE_SUCCESS_VOID(rv);
  3790   rv = directory->Remove(true);
  3791   if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
  3792       rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
  3793     // This should never fail if we've closed all storage connections
  3794     // correctly...
  3795     NS_ERROR("Failed to remove directory!");
  3799 NS_IMPL_ISUPPORTS_INHERITED0(ResetOrClearRunnable, nsRunnable)
  3801 NS_IMETHODIMP
  3802 ResetOrClearRunnable::Run()
  3804   QuotaManager* quotaManager = QuotaManager::Get();
  3805   NS_ASSERTION(quotaManager, "This should never fail!");
  3807   switch (mCallbackState) {
  3808     case Pending: {
  3809       NS_NOTREACHED("Should never get here without being dispatched!");
  3810       return NS_ERROR_UNEXPECTED;
  3813     case OpenAllowed: {
  3814       NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3816       AdvanceState();
  3818       // Now we have to wait until the thread pool is done with all of the
  3819       // storages we care about.
  3820       nsresult rv =
  3821         quotaManager->AcquireExclusiveAccess(NullCString(),
  3822                                              Nullable<PersistenceType>(), this,
  3823                                              InvalidateOpenedStorages, nullptr);
  3824       NS_ENSURE_SUCCESS(rv, rv);
  3826       return NS_OK;
  3829     case IO: {
  3830       AssertIsOnIOThread();
  3832       AdvanceState();
  3834       if (mClear) {
  3835         DeleteFiles(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
  3836         DeleteFiles(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
  3839       quotaManager->RemoveQuota();
  3840       quotaManager->ResetOrClearCompleted();
  3842       // Now dispatch back to the main thread.
  3843       if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
  3844         NS_WARNING("Failed to dispatch to main thread!");
  3845         return NS_ERROR_FAILURE;
  3848       return NS_OK;
  3851     case Complete: {
  3852       NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3854       // Tell the QuotaManager that we're done.
  3855       quotaManager->AllowNextSynchronizedOp(OriginOrPatternString::FromNull(),
  3856                                             Nullable<PersistenceType>(),
  3857                                             EmptyCString());
  3859       return NS_OK;
  3862     default:
  3863       NS_ERROR("Unknown state value!");
  3864       return NS_ERROR_UNEXPECTED;
  3867   NS_NOTREACHED("Should never get here!");
  3868   return NS_ERROR_UNEXPECTED;
  3871 NS_IMETHODIMP
  3872 FinalizeOriginEvictionRunnable::Run()
  3874   QuotaManager* quotaManager = QuotaManager::Get();
  3875   NS_ASSERTION(quotaManager, "This should never fail!");
  3877   nsresult rv;
  3879   switch (mCallbackState) {
  3880     case Pending: {
  3881       NS_NOTREACHED("Should never get here without being dispatched!");
  3882       return NS_ERROR_UNEXPECTED;
  3885     case OpenAllowed: {
  3886       NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3888       AdvanceState();
  3890       rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
  3891       if (NS_FAILED(rv)) {
  3892         NS_WARNING("Failed to dispatch to the IO thread!");
  3895       return NS_OK;
  3898     case IO: {
  3899       AssertIsOnIOThread();
  3901       AdvanceState();
  3903       for (uint32_t index = 0; index < mOrigins.Length(); index++) {
  3904         quotaManager->OriginClearCompleted(
  3905                             PERSISTENCE_TYPE_TEMPORARY,
  3906                             OriginOrPatternString::FromOrigin(mOrigins[index]));
  3909       if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
  3910         NS_WARNING("Failed to dispatch to main thread!");
  3911         return NS_ERROR_FAILURE;
  3914       return NS_OK;
  3917     case Complete: {
  3918       NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3920       for (uint32_t index = 0; index < mOrigins.Length(); index++) {
  3921         quotaManager->AllowNextSynchronizedOp(
  3922                           OriginOrPatternString::FromOrigin(mOrigins[index]),
  3923                           Nullable<PersistenceType>(PERSISTENCE_TYPE_TEMPORARY),
  3924                           EmptyCString());
  3927       return NS_OK;
  3930     default:
  3931       NS_ERROR("Unknown state value!");
  3932       return NS_ERROR_UNEXPECTED;
  3935   NS_NOTREACHED("Should never get here!");
  3936   return NS_ERROR_UNEXPECTED;
  3939 nsresult
  3940 FinalizeOriginEvictionRunnable::Dispatch()
  3942   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
  3943   NS_ASSERTION(mCallbackState == Pending, "Huh?");
  3945   mCallbackState = OpenAllowed;
  3946   return NS_DispatchToMainThread(this);
  3949 nsresult
  3950 FinalizeOriginEvictionRunnable::RunImmediately()
  3952   AssertIsOnIOThread();
  3953   NS_ASSERTION(mCallbackState == Pending, "Huh?");
  3955   mCallbackState = IO;
  3956   return this->Run();
  3959 NS_IMETHODIMP
  3960 WaitForTransactionsToFinishRunnable::Run()
  3962   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3963   NS_ASSERTION(mOp, "Null op!");
  3964   NS_ASSERTION(mOp->mListener, "Nothing to run!");
  3965   NS_ASSERTION(mCountdown, "Wrong countdown!");
  3967   if (--mCountdown) {
  3968     return NS_OK;
  3971   // Don't hold the listener alive longer than necessary.
  3972   nsRefPtr<AcquireListener> listener;
  3973   listener.swap(mOp->mListener);
  3975   mOp = nullptr;
  3977   nsresult rv = listener->OnExclusiveAccessAcquired();
  3978   NS_ENSURE_SUCCESS(rv, rv);
  3980   // The listener is responsible for calling
  3981   // QuotaManager::AllowNextSynchronizedOp.
  3982   return NS_OK;
  3985 NS_IMETHODIMP
  3986 WaitForLockedFilesToFinishRunnable::Run()
  3988   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  3990   mBusy = false;
  3992   return NS_OK;
  3995 NS_IMETHODIMP
  3996 SaveOriginAccessTimeRunnable::Run()
  3998   AssertIsOnIOThread();
  4000   QuotaManager* quotaManager = QuotaManager::Get();
  4001   NS_ASSERTION(quotaManager, "This should never fail!");
  4003   nsCOMPtr<nsIFile> directory;
  4004   nsresult rv =
  4005     quotaManager->GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY, mOrigin,
  4006                                         getter_AddRefs(directory));
  4007   NS_ENSURE_SUCCESS(rv, rv);
  4009   nsCOMPtr<nsIBinaryOutputStream> stream;
  4010   rv = GetDirectoryMetadataStream(directory, true, getter_AddRefs(stream));
  4011   NS_ENSURE_SUCCESS(rv, rv);
  4013   // The origin directory may not exist anymore.
  4014   if (stream) {
  4015     rv = stream->Write64(mTimestamp);
  4016     NS_ENSURE_SUCCESS(rv, rv);
  4019   return NS_OK;

mercurial