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"
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"
23 #include "nsIUsageCallback.h"
24 #include "nsPIDOMWindow.h"
27 #include "GeckoProfiler.h"
28 #include "mozilla/Atomics.h"
29 #include "mozilla/CondVar.h"
30 #include "mozilla/dom/asmjscache/AsmJSCache.h"
31 #include "mozilla/dom/FileService.h"
32 #include "mozilla/dom/indexedDB/Client.h"
33 #include "mozilla/Mutex.h"
34 #include "mozilla/LazyIdleThread.h"
35 #include "mozilla/Preferences.h"
36 #include "mozilla/Services.h"
37 #include "nsAppDirectoryServiceDefs.h"
38 #include "nsComponentManagerUtils.h"
39 #include "nsAboutProtocolUtils.h"
40 #include "nsContentUtils.h"
41 #include "nsCRTGlue.h"
42 #include "nsDirectoryServiceUtils.h"
43 #include "nsNetUtil.h"
44 #include "nsScriptSecurityManager.h"
45 #include "nsThreadUtils.h"
46 #include "nsXULAppAPI.h"
47 #include "xpcpublic.h"
49 #include "AcquireListener.h"
50 #include "CheckQuotaHelper.h"
51 #include "OriginCollection.h"
52 #include "OriginOrPatternString.h"
53 #include "QuotaObject.h"
54 #include "StorageMatcher.h"
55 #include "UsageInfo.h"
56 #include "Utilities.h"
58 // The amount of time, in milliseconds, that our IO thread will stay alive
59 // after the last event it processes.
60 #define DEFAULT_THREAD_TIMEOUT_MS 30000
62 // The amount of time, in milliseconds, that we will wait for active storage
63 // transactions on shutdown before aborting them.
64 #define DEFAULT_SHUTDOWN_TIMER_MS 30000
66 // Preference that users can set to override DEFAULT_QUOTA_MB
67 #define PREF_STORAGE_QUOTA "dom.indexedDB.warningQuota"
69 // Preference that users can set to override temporary storage smart limit
71 #define PREF_FIXED_LIMIT "dom.quotaManager.temporaryStorage.fixedLimit"
72 #define PREF_CHUNK_SIZE "dom.quotaManager.temporaryStorage.chunkSize"
74 // Preference that is used to enable testing features
75 #define PREF_TESTING_FEATURES "dom.quotaManager.testing"
77 // profile-before-change, when we need to shut down quota manager
78 #define PROFILE_BEFORE_CHANGE_OBSERVER_ID "profile-before-change"
80 // The name of the file that we use to load/save the last access time of an
82 #define METADATA_FILE_NAME ".metadata"
84 #define PERMISSION_DEFAUT_PERSISTENT_STORAGE "default-persistent-storage"
87 #define MB * 1024ULL KB
88 #define GB * 1024ULL MB
91 using namespace mozilla::dom
;
92 using mozilla::dom::FileService
;
95 static_cast<uint32_t>(StorageType::Persistent
) ==
96 static_cast<uint32_t>(PERSISTENCE_TYPE_PERSISTENT
),
97 "Enum values should match.");
100 static_cast<uint32_t>(StorageType::Temporary
) ==
101 static_cast<uint32_t>(PERSISTENCE_TYPE_TEMPORARY
),
102 "Enum values should match.");
104 BEGIN_QUOTA_NAMESPACE
106 // A struct that contains the information corresponding to a pending or
107 // running operation that requires synchronization (e.g. opening a db,
108 // clearing dbs for an origin, etc).
109 struct SynchronizedOp
111 SynchronizedOp(const OriginOrPatternString
& aOriginOrPattern
,
112 Nullable
<PersistenceType
> aPersistenceType
,
113 const nsACString
& aId
);
117 // Test whether this SynchronizedOp needs to wait for the given op.
119 MustWaitFor(const SynchronizedOp
& aOp
);
122 DelayRunnable(nsIRunnable
* aRunnable
);
125 DispatchDelayedRunnables();
127 const OriginOrPatternString mOriginOrPattern
;
128 Nullable
<PersistenceType
> mPersistenceType
;
130 nsRefPtr
<AcquireListener
> mListener
;
131 nsTArray
<nsCOMPtr
<nsIRunnable
> > mDelayedRunnables
;
132 ArrayCluster
<nsIOfflineStorage
*> mStorages
;
135 class CollectOriginsHelper MOZ_FINAL
: public nsRunnable
138 CollectOriginsHelper(mozilla::Mutex
& aMutex
, uint64_t aMinSizeToBeFreed
);
143 // Blocks the current thread until origins are collected on the main thread.
144 // The returned value contains an aggregate size of those origins.
146 BlockAndReturnOriginsForEviction(nsTArray
<OriginInfo
*>& aOriginInfos
);
149 ~CollectOriginsHelper()
152 uint64_t mMinSizeToBeFreed
;
154 mozilla::Mutex
& mMutex
;
155 mozilla::CondVar mCondVar
;
157 // The members below are protected by mMutex.
158 nsTArray
<OriginInfo
*> mOriginInfos
;
159 uint64_t mSizeToBeFreed
;
163 // Responsible for clearing the storage files for a particular origin on the
164 // IO thread. Created when nsIQuotaManager::ClearStoragesForURI is called.
165 // Runs three times, first on the main thread, next on the IO thread, and then
166 // finally again on the main thread. While on the IO thread the runnable will
167 // actually remove the origin's storage files and the directory that contains
168 // them before dispatching itself back to the main thread. When back on the main
169 // thread the runnable will notify the QuotaManager that the job has been
171 class OriginClearRunnable MOZ_FINAL
: public nsRunnable
,
172 public AcquireListener
178 // Running on the main thread in the callback for OpenAllowed.
181 // Running on the IO thread.
184 // Running on the main thread after all work is done.
189 NS_DECL_ISUPPORTS_INHERITED
191 OriginClearRunnable(const OriginOrPatternString
& aOriginOrPattern
,
192 Nullable
<PersistenceType
> aPersistenceType
)
193 : mOriginOrPattern(aOriginOrPattern
),
194 mPersistenceType(aPersistenceType
),
195 mCallbackState(Pending
)
201 // AcquireListener override
203 OnExclusiveAccessAcquired() MOZ_OVERRIDE
;
208 switch (mCallbackState
) {
210 mCallbackState
= OpenAllowed
;
216 mCallbackState
= Complete
;
219 NS_NOTREACHED("Can't advance past Complete!");
224 InvalidateOpenedStorages(nsTArray
<nsCOMPtr
<nsIOfflineStorage
> >& aStorages
,
228 DeleteFiles(QuotaManager
* aQuotaManager
,
229 PersistenceType aPersistenceType
);
232 ~OriginClearRunnable() {}
234 OriginOrPatternString mOriginOrPattern
;
235 Nullable
<PersistenceType
> mPersistenceType
;
236 CallbackState mCallbackState
;
239 // Responsible for calculating the amount of space taken up by storages of a
240 // certain origin. Created when nsIQuotaManager::GetUsageForURI is called.
241 // May be canceled with nsIQuotaRequest::Cancel. Runs three times, first
242 // on the main thread, next on the IO thread, and then finally again on the main
243 // thread. While on the IO thread the runnable will calculate the size of all
244 // files in the origin's directory before dispatching itself back to the main
245 // thread. When on the main thread the runnable will call the callback and then
246 // notify the QuotaManager that the job has been completed.
247 class AsyncUsageRunnable MOZ_FINAL
: public UsageInfo
,
249 public nsIQuotaRequest
255 // Running on the main thread in the callback for OpenAllowed.
258 // Running on the IO thread.
261 // Running on the main thread after all work is done.
264 // Running on the main thread after skipping the work
269 NS_DECL_ISUPPORTS_INHERITED
270 NS_DECL_NSIQUOTAREQUEST
272 AsyncUsageRunnable(uint32_t aAppId
,
273 bool aInMozBrowserOnly
,
274 const nsACString
& aGroup
,
275 const OriginOrPatternString
& aOrigin
,
277 nsIUsageCallback
* aCallback
);
285 switch (mCallbackState
) {
287 mCallbackState
= OpenAllowed
;
293 mCallbackState
= Complete
;
296 NS_NOTREACHED("Can't advance past Complete!");
304 ~AsyncUsageRunnable() {}
306 // Run calls the RunInternal method and makes sure that we always dispatch
307 // to the main thread in case of an error.
312 AddToUsage(QuotaManager
* aQuotaManager
,
313 PersistenceType aPersistenceType
);
315 nsCOMPtr
<nsIURI
> mURI
;
316 nsCOMPtr
<nsIUsageCallback
> mCallback
;
319 OriginOrPatternString mOrigin
;
320 CallbackState mCallbackState
;
321 bool mInMozBrowserOnly
;
324 class ResetOrClearRunnable MOZ_FINAL
: public nsRunnable
,
325 public AcquireListener
331 // Running on the main thread in the callback for OpenAllowed.
334 // Running on the IO thread.
337 // Running on the main thread after all work is done.
342 NS_DECL_ISUPPORTS_INHERITED
344 ResetOrClearRunnable(bool aClear
)
345 : mCallbackState(Pending
),
352 // AcquireListener override
354 OnExclusiveAccessAcquired() MOZ_OVERRIDE
;
359 switch (mCallbackState
) {
361 mCallbackState
= OpenAllowed
;
367 mCallbackState
= Complete
;
370 NS_NOTREACHED("Can't advance past Complete!");
375 InvalidateOpenedStorages(nsTArray
<nsCOMPtr
<nsIOfflineStorage
> >& aStorages
,
379 DeleteFiles(QuotaManager
* aQuotaManager
,
380 PersistenceType aPersistenceType
);
383 ~ResetOrClearRunnable() {}
385 CallbackState mCallbackState
;
389 // Responsible for finalizing eviction of certian origins (storage files have
390 // been already cleared, we just need to release IO thread only objects and
391 // allow next synchronized ops for evicted origins). Created when
392 // QuotaManager::FinalizeOriginEviction is called. Runs three times, first
393 // on the main thread, next on the IO thread, and then finally again on the main
394 // thread. While on the IO thread the runnable will release IO thread only
395 // objects before dispatching itself back to the main thread. When back on the
396 // main thread the runnable will call QuotaManager::AllowNextSynchronizedOp.
397 // The runnable can also run in a shortened mode (runs only twice).
398 class FinalizeOriginEvictionRunnable MOZ_FINAL
: public nsRunnable
404 // Running on the main thread in the callback for OpenAllowed.
407 // Running on the IO thread.
410 // Running on the main thread after IO work is done.
415 FinalizeOriginEvictionRunnable(nsTArray
<nsCString
>& aOrigins
)
416 : mCallbackState(Pending
)
418 mOrigins
.SwapElements(aOrigins
);
427 switch (mCallbackState
) {
429 mCallbackState
= OpenAllowed
;
435 mCallbackState
= Complete
;
438 MOZ_ASSERT_UNREACHABLE("Can't advance past Complete!");
449 CallbackState mCallbackState
;
450 nsTArray
<nsCString
> mOrigins
;
456 QuotaManager
* quotaManager
= QuotaManager::Get();
457 NS_ASSERTION(quotaManager
, "Must have a manager here!");
460 return NS_SUCCEEDED(quotaManager
->IOThread()->
461 IsOnCurrentThread(¤tThread
)) && currentThread
;
467 NS_ASSERTION(IsOnIOThread(), "Running on the wrong thread!");
471 AssertCurrentThreadOwnsQuotaMutex()
474 QuotaManager
* quotaManager
= QuotaManager::Get();
475 NS_ASSERTION(quotaManager
, "Must have a manager here!");
477 quotaManager
->AssertCurrentThreadOwnsQuotaMutex();
485 // Amount of space that storages may use by default in megabytes.
486 static const int32_t kDefaultQuotaMB
= 50;
489 QuotaManager
* gInstance
= nullptr;
490 mozilla::Atomic
<bool> gShutdown(false);
492 int32_t gStorageQuotaMB
= kDefaultQuotaMB
;
494 // Constants for temporary storage limit computing.
495 static const int32_t kDefaultFixedLimitKB
= -1;
496 static const uint32_t kDefaultChunkSizeKB
= 10 * 1024;
497 int32_t gFixedLimitKB
= kDefaultFixedLimitKB
;
498 uint32_t gChunkSizeKB
= kDefaultChunkSizeKB
;
500 bool gTestingEnabled
= false;
502 // A callback runnable used by the TransactionPool when it's safe to proceed
503 // with a SetVersion/DeleteDatabase/etc.
504 class WaitForTransactionsToFinishRunnable MOZ_FINAL
: public nsRunnable
507 WaitForTransactionsToFinishRunnable(SynchronizedOp
* aOp
)
508 : mOp(aOp
), mCountdown(1)
510 NS_ASSERTION(mOp
, "Why don't we have a runnable?");
511 NS_ASSERTION(mOp
->mStorages
.IsEmpty(), "We're here too early!");
512 NS_ASSERTION(mOp
->mListener
,
513 "What are we supposed to do when we're done?");
514 NS_ASSERTION(mCountdown
, "Wrong countdown!");
527 // The QuotaManager holds this alive.
532 class WaitForFileHandlesToFinishRunnable MOZ_FINAL
: public nsRunnable
535 WaitForFileHandlesToFinishRunnable()
552 class SaveOriginAccessTimeRunnable MOZ_FINAL
: public nsRunnable
555 SaveOriginAccessTimeRunnable(const nsACString
& aOrigin
, int64_t aTimestamp
)
556 : mOrigin(aOrigin
), mTimestamp(aTimestamp
)
567 struct MOZ_STACK_CLASS RemoveQuotaInfo
569 RemoveQuotaInfo(PersistenceType aPersistenceType
, const nsACString
& aPattern
)
570 : persistenceType(aPersistenceType
), pattern(aPattern
)
573 PersistenceType persistenceType
;
577 struct MOZ_STACK_CLASS InactiveOriginsInfo
579 InactiveOriginsInfo(OriginCollection
& aCollection
,
580 nsTArray
<OriginInfo
*>& aOrigins
)
581 : collection(aCollection
), origins(aOrigins
)
584 OriginCollection
& collection
;
585 nsTArray
<OriginInfo
*>& origins
;
591 return XRE_GetProcessType() == GeckoProcessType_Default
;
595 SanitizeOriginString(nsCString
& aOrigin
)
597 // We want profiles to be platform-independent so we always need to replace
598 // the same characters on every platform. Windows has the most extensive set
599 // of illegal characters so we use its FILE_ILLEGAL_CHARACTERS and
600 // FILE_PATH_SEPARATOR.
601 static const char kReplaceChars
[] = CONTROL_CHARACTERS
"/:*?\"<>|\\";
604 NS_ASSERTION(!strcmp(kReplaceChars
,
605 FILE_ILLEGAL_CHARACTERS FILE_PATH_SEPARATOR
),
606 "Illegal file characters have changed!");
609 aOrigin
.ReplaceChar(kReplaceChars
, '+');
613 EnsureDirectory(nsIFile
* aDirectory
, bool* aCreated
)
615 AssertIsOnIOThread();
617 nsresult rv
= aDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755);
618 if (rv
== NS_ERROR_FILE_ALREADY_EXISTS
) {
620 rv
= aDirectory
->IsDirectory(&isDirectory
);
621 NS_ENSURE_SUCCESS(rv
, rv
);
622 NS_ENSURE_TRUE(isDirectory
, NS_ERROR_UNEXPECTED
);
627 NS_ENSURE_SUCCESS(rv
, rv
);
636 CreateDirectoryUpgradeStamp(nsIFile
* aDirectory
)
638 AssertIsOnIOThread();
640 nsCOMPtr
<nsIFile
> metadataFile
;
641 nsresult rv
= aDirectory
->Clone(getter_AddRefs(metadataFile
));
642 NS_ENSURE_SUCCESS(rv
, rv
);
644 rv
= metadataFile
->Append(NS_LITERAL_STRING(METADATA_FILE_NAME
));
645 NS_ENSURE_SUCCESS(rv
, rv
);
647 rv
= metadataFile
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644);
648 NS_ENSURE_SUCCESS(rv
, rv
);
654 GetDirectoryMetadataStream(nsIFile
* aDirectory
, bool aUpdate
,
655 nsIBinaryOutputStream
** aStream
)
657 AssertIsOnIOThread();
659 nsCOMPtr
<nsIFile
> metadataFile
;
660 nsresult rv
= aDirectory
->Clone(getter_AddRefs(metadataFile
));
661 NS_ENSURE_SUCCESS(rv
, rv
);
663 rv
= metadataFile
->Append(NS_LITERAL_STRING(METADATA_FILE_NAME
));
664 NS_ENSURE_SUCCESS(rv
, rv
);
666 nsCOMPtr
<nsIOutputStream
> outputStream
;
669 rv
= metadataFile
->Exists(&exists
);
670 NS_ENSURE_SUCCESS(rv
, rv
);
677 nsCOMPtr
<nsIFileStream
> stream
;
678 rv
= NS_NewLocalFileStream(getter_AddRefs(stream
), metadataFile
);
679 NS_ENSURE_SUCCESS(rv
, rv
);
681 outputStream
= do_QueryInterface(stream
);
682 NS_ENSURE_TRUE(outputStream
, NS_ERROR_FAILURE
);
685 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(outputStream
),
687 NS_ENSURE_SUCCESS(rv
, rv
);
690 nsCOMPtr
<nsIBinaryOutputStream
> binaryStream
=
691 do_CreateInstance("@mozilla.org/binaryoutputstream;1");
692 NS_ENSURE_TRUE(binaryStream
, NS_ERROR_FAILURE
);
694 rv
= binaryStream
->SetOutputStream(outputStream
);
695 NS_ENSURE_SUCCESS(rv
, rv
);
697 binaryStream
.forget(aStream
);
702 CreateDirectoryMetadata(nsIFile
* aDirectory
, int64_t aTimestamp
,
703 const nsACString
& aGroup
, const nsACString
& aOrigin
)
705 AssertIsOnIOThread();
707 nsCOMPtr
<nsIBinaryOutputStream
> stream
;
709 GetDirectoryMetadataStream(aDirectory
, false, getter_AddRefs(stream
));
710 NS_ENSURE_SUCCESS(rv
, rv
);
712 NS_ASSERTION(stream
, "This shouldn't be null!");
714 rv
= stream
->Write64(aTimestamp
);
715 NS_ENSURE_SUCCESS(rv
, rv
);
717 rv
= stream
->WriteStringZ(PromiseFlatCString(aGroup
).get());
718 NS_ENSURE_SUCCESS(rv
, rv
);
720 rv
= stream
->WriteStringZ(PromiseFlatCString(aOrigin
).get());
721 NS_ENSURE_SUCCESS(rv
, rv
);
727 GetDirectoryMetadata(nsIFile
* aDirectory
, int64_t* aTimestamp
,
728 nsACString
& aGroup
, nsACString
& aOrigin
)
730 AssertIsOnIOThread();
732 nsCOMPtr
<nsIFile
> metadataFile
;
733 nsresult rv
= aDirectory
->Clone(getter_AddRefs(metadataFile
));
734 NS_ENSURE_SUCCESS(rv
, rv
);
736 rv
= metadataFile
->Append(NS_LITERAL_STRING(METADATA_FILE_NAME
));
737 NS_ENSURE_SUCCESS(rv
, rv
);
739 nsCOMPtr
<nsIInputStream
> stream
;
740 rv
= NS_NewLocalFileInputStream(getter_AddRefs(stream
), metadataFile
);
741 NS_ENSURE_SUCCESS(rv
, rv
);
743 nsCOMPtr
<nsIInputStream
> bufferedStream
;
744 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
), stream
, 512);
745 NS_ENSURE_SUCCESS(rv
, rv
);
747 nsCOMPtr
<nsIBinaryInputStream
> binaryStream
=
748 do_CreateInstance("@mozilla.org/binaryinputstream;1");
749 NS_ENSURE_TRUE(binaryStream
, NS_ERROR_FAILURE
);
751 rv
= binaryStream
->SetInputStream(bufferedStream
);
752 NS_ENSURE_SUCCESS(rv
, rv
);
755 rv
= binaryStream
->Read64(×tamp
);
756 NS_ENSURE_SUCCESS(rv
, rv
);
759 rv
= binaryStream
->ReadCString(group
);
760 NS_ENSURE_SUCCESS(rv
, rv
);
763 rv
= binaryStream
->ReadCString(origin
);
764 NS_ENSURE_SUCCESS(rv
, rv
);
766 *aTimestamp
= timestamp
;
773 MaybeUpgradeOriginDirectory(nsIFile
* aDirectory
)
775 AssertIsOnIOThread();
776 NS_ASSERTION(aDirectory
, "Null pointer!");
778 nsCOMPtr
<nsIFile
> metadataFile
;
779 nsresult rv
= aDirectory
->Clone(getter_AddRefs(metadataFile
));
780 NS_ENSURE_SUCCESS(rv
, rv
);
782 rv
= metadataFile
->Append(NS_LITERAL_STRING(METADATA_FILE_NAME
));
783 NS_ENSURE_SUCCESS(rv
, rv
);
786 rv
= metadataFile
->Exists(&exists
);
787 NS_ENSURE_SUCCESS(rv
, rv
);
790 // Directory structure upgrade needed.
791 // Move all files to IDB specific directory.
793 nsString idbDirectoryName
;
794 rv
= Client::TypeToText(Client::IDB
, idbDirectoryName
);
795 NS_ENSURE_SUCCESS(rv
, rv
);
797 nsCOMPtr
<nsIFile
> idbDirectory
;
798 rv
= aDirectory
->Clone(getter_AddRefs(idbDirectory
));
799 NS_ENSURE_SUCCESS(rv
, rv
);
801 rv
= idbDirectory
->Append(idbDirectoryName
);
802 NS_ENSURE_SUCCESS(rv
, rv
);
804 rv
= idbDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755);
805 if (rv
== NS_ERROR_FILE_ALREADY_EXISTS
) {
806 NS_WARNING("IDB directory already exists!");
809 rv
= idbDirectory
->IsDirectory(&isDirectory
);
810 NS_ENSURE_SUCCESS(rv
, rv
);
811 NS_ENSURE_TRUE(isDirectory
, NS_ERROR_UNEXPECTED
);
814 NS_ENSURE_SUCCESS(rv
, rv
);
817 nsCOMPtr
<nsISimpleEnumerator
> entries
;
818 rv
= aDirectory
->GetDirectoryEntries(getter_AddRefs(entries
));
819 NS_ENSURE_SUCCESS(rv
, rv
);
822 while (NS_SUCCEEDED((rv
= entries
->HasMoreElements(&hasMore
))) && hasMore
) {
823 nsCOMPtr
<nsISupports
> entry
;
824 rv
= entries
->GetNext(getter_AddRefs(entry
));
825 NS_ENSURE_SUCCESS(rv
, rv
);
827 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(entry
);
828 NS_ENSURE_TRUE(file
, NS_NOINTERFACE
);
831 rv
= file
->GetLeafName(leafName
);
832 NS_ENSURE_SUCCESS(rv
, rv
);
834 if (!leafName
.Equals(idbDirectoryName
)) {
835 rv
= file
->MoveTo(idbDirectory
, EmptyString());
836 NS_ENSURE_SUCCESS(rv
, rv
);
840 rv
= metadataFile
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644);
841 NS_ENSURE_SUCCESS(rv
, rv
);
847 // This method computes and returns our best guess for the temporary storage
848 // limit (in bytes), based on the amount of space users have free on their hard
849 // drive and on given temporary storage usage (also in bytes).
851 GetTemporaryStorageLimit(nsIFile
* aDirectory
, uint64_t aCurrentUsage
,
854 // Check for free space on device where temporary storage directory lives.
855 int64_t bytesAvailable
;
856 nsresult rv
= aDirectory
->GetDiskSpaceAvailable(&bytesAvailable
);
857 NS_ENSURE_SUCCESS(rv
, rv
);
859 NS_ASSERTION(bytesAvailable
>= 0, "Negative bytes available?!");
861 uint64_t availableKB
=
862 static_cast<uint64_t>((bytesAvailable
+ aCurrentUsage
) / 1024);
864 // Grow/shrink in gChunkSizeKB units, deliberately, so that in the common case
865 // we don't shrink temporary storage and evict origin data every time we
867 availableKB
= (availableKB
/ gChunkSizeKB
) * gChunkSizeKB
;
869 // Allow temporary storage to consume up to half the available space.
870 uint64_t resultKB
= availableKB
* .50;
872 *aLimit
= resultKB
* 1024;
876 } // anonymous namespace
878 QuotaManager::QuotaManager()
879 : mCurrentWindowIndex(BAD_TLS_INDEX
),
880 mQuotaMutex("QuotaManager.mQuotaMutex"),
881 mTemporaryStorageLimit(0),
882 mTemporaryStorageUsage(0),
883 mTemporaryStorageInitialized(false),
884 mStorageAreaInitialized(false)
886 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
887 NS_ASSERTION(!gInstance
, "More than one instance!");
890 QuotaManager::~QuotaManager()
892 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
893 NS_ASSERTION(!gInstance
|| gInstance
== this, "Different instances!");
899 QuotaManager::GetOrCreate()
901 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
903 if (IsShuttingDown()) {
904 NS_ERROR("Calling GetOrCreate() after shutdown!");
909 nsRefPtr
<QuotaManager
> instance(new QuotaManager());
911 nsresult rv
= instance
->Init();
912 NS_ENSURE_SUCCESS(rv
, nullptr);
914 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
915 NS_ENSURE_TRUE(obs
, nullptr);
917 // We need this callback to know when to shut down all our threads.
918 rv
= obs
->AddObserver(instance
, PROFILE_BEFORE_CHANGE_OBSERVER_ID
, false);
919 NS_ENSURE_SUCCESS(rv
, nullptr);
921 // The observer service will hold our last reference, don't AddRef here.
922 gInstance
= instance
;
932 // Does not return an owning reference.
938 QuotaManager::FactoryCreate()
940 // Returns a raw pointer that carries an owning reference! Lame, but the
941 // singleton factory macros force this.
942 QuotaManager
* quotaManager
= GetOrCreate();
943 NS_IF_ADDREF(quotaManager
);
949 QuotaManager::IsShuttingDown()
957 // We need a thread-local to hold the current window.
958 NS_ASSERTION(mCurrentWindowIndex
== BAD_TLS_INDEX
, "Huh?");
960 if (PR_NewThreadPrivateIndex(&mCurrentWindowIndex
, nullptr) != PR_SUCCESS
) {
961 NS_ERROR("PR_NewThreadPrivateIndex failed, QuotaManager disabled");
962 mCurrentWindowIndex
= BAD_TLS_INDEX
;
963 return NS_ERROR_FAILURE
;
967 if (IsMainProcess()) {
968 nsCOMPtr
<nsIFile
> baseDir
;
969 rv
= NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR
,
970 getter_AddRefs(baseDir
));
972 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
973 getter_AddRefs(baseDir
));
975 NS_ENSURE_SUCCESS(rv
, rv
);
977 nsCOMPtr
<nsIFile
> indexedDBDir
;
978 rv
= baseDir
->Clone(getter_AddRefs(indexedDBDir
));
979 NS_ENSURE_SUCCESS(rv
, rv
);
981 rv
= indexedDBDir
->Append(NS_LITERAL_STRING("indexedDB"));
982 NS_ENSURE_SUCCESS(rv
, rv
);
984 rv
= indexedDBDir
->GetPath(mIndexedDBPath
);
985 NS_ENSURE_SUCCESS(rv
, rv
);
987 rv
= baseDir
->Append(NS_LITERAL_STRING("storage"));
988 NS_ENSURE_SUCCESS(rv
, rv
);
990 nsCOMPtr
<nsIFile
> persistentStorageDir
;
991 rv
= baseDir
->Clone(getter_AddRefs(persistentStorageDir
));
992 NS_ENSURE_SUCCESS(rv
, rv
);
994 rv
= persistentStorageDir
->Append(NS_LITERAL_STRING("persistent"));
995 NS_ENSURE_SUCCESS(rv
, rv
);
997 rv
= persistentStorageDir
->GetPath(mPersistentStoragePath
);
998 NS_ENSURE_SUCCESS(rv
, rv
);
1000 nsCOMPtr
<nsIFile
> temporaryStorageDir
;
1001 rv
= baseDir
->Clone(getter_AddRefs(temporaryStorageDir
));
1002 NS_ENSURE_SUCCESS(rv
, rv
);
1004 rv
= temporaryStorageDir
->Append(NS_LITERAL_STRING("temporary"));
1005 NS_ENSURE_SUCCESS(rv
, rv
);
1007 rv
= temporaryStorageDir
->GetPath(mTemporaryStoragePath
);
1008 NS_ENSURE_SUCCESS(rv
, rv
);
1010 // Make a lazy thread for any IO we need (like clearing or enumerating the
1011 // contents of storage directories).
1012 mIOThread
= new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS
,
1013 NS_LITERAL_CSTRING("Storage I/O"),
1014 LazyIdleThread::ManualShutdown
);
1016 // Make a timer here to avoid potential failures later. We don't actually
1017 // initialize the timer until shutdown.
1018 mShutdownTimer
= do_CreateInstance(NS_TIMER_CONTRACTID
);
1019 NS_ENSURE_TRUE(mShutdownTimer
, NS_ERROR_FAILURE
);
1022 if (NS_FAILED(Preferences::AddIntVarCache(&gStorageQuotaMB
,
1024 kDefaultQuotaMB
))) {
1025 NS_WARNING("Unable to respond to quota pref changes!");
1028 if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB
, PREF_FIXED_LIMIT
,
1029 kDefaultFixedLimitKB
)) ||
1030 NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB
,
1032 kDefaultChunkSizeKB
))) {
1033 NS_WARNING("Unable to respond to temp storage pref changes!");
1036 if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled
,
1037 PREF_TESTING_FEATURES
, false))) {
1038 NS_WARNING("Unable to respond to testing pref changes!");
1041 static_assert(Client::IDB
== 0 && Client::ASMJS
== 1 && Client::TYPE_MAX
== 2,
1042 "Fix the registration!");
1044 NS_ASSERTION(mClients
.Capacity() == Client::TYPE_MAX
,
1045 "Should be using an auto array with correct capacity!");
1047 // Register IndexedDB
1048 mClients
.AppendElement(new indexedDB::Client());
1049 mClients
.AppendElement(asmjscache::CreateClient());
1055 QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType
,
1056 const nsACString
& aGroup
,
1057 const nsACString
& aOrigin
,
1058 uint64_t aLimitBytes
,
1059 uint64_t aUsageBytes
,
1060 int64_t aAccessTime
)
1062 AssertIsOnIOThread();
1063 MOZ_ASSERT(aLimitBytes
> 0 ||
1064 aPersistenceType
== PERSISTENCE_TYPE_TEMPORARY
);
1065 MOZ_ASSERT(aUsageBytes
<= aLimitBytes
||
1066 aPersistenceType
== PERSISTENCE_TYPE_TEMPORARY
);
1068 MutexAutoLock
lock(mQuotaMutex
);
1070 GroupInfoPair
* pair
;
1071 if (!mGroupInfoPairs
.Get(aGroup
, &pair
)) {
1072 pair
= new GroupInfoPair();
1073 mGroupInfoPairs
.Put(aGroup
, pair
);
1074 // The hashtable is now responsible to delete the GroupInfoPair.
1077 nsRefPtr
<GroupInfo
> groupInfo
= pair
->LockedGetGroupInfo(aPersistenceType
);
1079 groupInfo
= new GroupInfo(aPersistenceType
, aGroup
);
1080 pair
->LockedSetGroupInfo(groupInfo
);
1083 nsRefPtr
<OriginInfo
> originInfo
=
1084 new OriginInfo(groupInfo
, aOrigin
, aLimitBytes
, aUsageBytes
, aAccessTime
);
1085 groupInfo
->LockedAddOriginInfo(originInfo
);
1089 QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType
,
1090 const nsACString
& aGroup
,
1091 const nsACString
& aOrigin
,
1094 AssertIsOnIOThread();
1096 MutexAutoLock
lock(mQuotaMutex
);
1098 GroupInfoPair
* pair
;
1099 if (!mGroupInfoPairs
.Get(aGroup
, &pair
)) {
1103 nsRefPtr
<GroupInfo
> groupInfo
= pair
->LockedGetGroupInfo(aPersistenceType
);
1108 nsRefPtr
<OriginInfo
> originInfo
= groupInfo
->LockedGetOriginInfo(aOrigin
);
1110 originInfo
->LockedDecreaseUsage(aSize
);
1115 QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType
,
1116 const nsACString
& aGroup
,
1117 const nsACString
& aOrigin
)
1119 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1121 MutexAutoLock
lock(mQuotaMutex
);
1123 GroupInfoPair
* pair
;
1124 if (!mGroupInfoPairs
.Get(aGroup
, &pair
)) {
1128 nsRefPtr
<GroupInfo
> groupInfo
=
1129 pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY
);
1134 nsRefPtr
<OriginInfo
> originInfo
= groupInfo
->LockedGetOriginInfo(aOrigin
);
1136 int64_t timestamp
= PR_Now();
1137 originInfo
->LockedUpdateAccessTime(timestamp
);
1139 if (!groupInfo
->IsForTemporaryStorage()) {
1143 MutexAutoUnlock
autoUnlock(mQuotaMutex
);
1145 SaveOriginAccessTime(aOrigin
, timestamp
);
1151 QuotaManager::RemoveQuotaCallback(const nsACString
& aKey
,
1152 nsAutoPtr
<GroupInfoPair
>& aValue
,
1155 NS_ASSERTION(!aKey
.IsEmpty(), "Empty key!");
1156 NS_ASSERTION(aValue
, "Null pointer!");
1158 nsRefPtr
<GroupInfo
> groupInfo
=
1159 aValue
->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY
);
1161 groupInfo
->LockedRemoveOriginInfos();
1164 return PL_DHASH_REMOVE
;
1168 QuotaManager::RemoveQuota()
1170 MutexAutoLock
lock(mQuotaMutex
);
1172 mGroupInfoPairs
.Enumerate(RemoveQuotaCallback
, nullptr);
1174 NS_ASSERTION(mTemporaryStorageUsage
== 0, "Should be zero!");
1179 QuotaManager::RemoveQuotaForPersistenceTypeCallback(
1180 const nsACString
& aKey
,
1181 nsAutoPtr
<GroupInfoPair
>& aValue
,
1184 NS_ASSERTION(!aKey
.IsEmpty(), "Empty key!");
1185 NS_ASSERTION(aValue
, "Null pointer!");
1186 NS_ASSERTION(aUserArg
, "Null pointer!");
1188 PersistenceType
& persistenceType
= *static_cast<PersistenceType
*>(aUserArg
);
1190 if (persistenceType
== PERSISTENCE_TYPE_TEMPORARY
) {
1191 nsRefPtr
<GroupInfo
> groupInfo
=
1192 aValue
->LockedGetGroupInfo(persistenceType
);
1194 groupInfo
->LockedRemoveOriginInfos();
1198 aValue
->LockedClearGroupInfo(persistenceType
);
1200 return aValue
->LockedHasGroupInfos() ? PL_DHASH_NEXT
: PL_DHASH_REMOVE
;
1204 QuotaManager::RemoveQuotaForPersistenceType(PersistenceType aPersistenceType
)
1206 MutexAutoLock
lock(mQuotaMutex
);
1208 mGroupInfoPairs
.Enumerate(RemoveQuotaForPersistenceTypeCallback
,
1211 NS_ASSERTION(aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
||
1212 mTemporaryStorageUsage
== 0, "Should be zero!");
1217 QuotaManager::RemoveQuotaForPatternCallback(const nsACString
& aKey
,
1218 nsAutoPtr
<GroupInfoPair
>& aValue
,
1221 NS_ASSERTION(!aKey
.IsEmpty(), "Empty key!");
1222 NS_ASSERTION(aValue
, "Null pointer!");
1223 NS_ASSERTION(aUserArg
, "Null pointer!");
1225 RemoveQuotaInfo
* info
= static_cast<RemoveQuotaInfo
*>(aUserArg
);
1227 nsRefPtr
<GroupInfo
> groupInfo
=
1228 aValue
->LockedGetGroupInfo(info
->persistenceType
);
1230 groupInfo
->LockedRemoveOriginInfosForPattern(info
->pattern
);
1232 if (!groupInfo
->LockedHasOriginInfos()) {
1233 aValue
->LockedClearGroupInfo(info
->persistenceType
);
1235 if (!aValue
->LockedHasGroupInfos()) {
1236 return PL_DHASH_REMOVE
;
1241 return PL_DHASH_NEXT
;
1245 QuotaManager::RemoveQuotaForPattern(PersistenceType aPersistenceType
,
1246 const nsACString
& aPattern
)
1248 NS_ASSERTION(!aPattern
.IsEmpty(), "Empty pattern!");
1250 RemoveQuotaInfo
info(aPersistenceType
, aPattern
);
1252 MutexAutoLock
lock(mQuotaMutex
);
1254 mGroupInfoPairs
.Enumerate(RemoveQuotaForPatternCallback
, &info
);
1257 already_AddRefed
<QuotaObject
>
1258 QuotaManager::GetQuotaObject(PersistenceType aPersistenceType
,
1259 const nsACString
& aGroup
,
1260 const nsACString
& aOrigin
,
1263 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
1266 nsresult rv
= aFile
->GetPath(path
);
1267 NS_ENSURE_SUCCESS(rv
, nullptr);
1272 rv
= aFile
->Exists(&exists
);
1273 NS_ENSURE_SUCCESS(rv
, nullptr);
1276 rv
= aFile
->GetFileSize(&fileSize
);
1277 NS_ENSURE_SUCCESS(rv
, nullptr);
1283 nsRefPtr
<QuotaObject
> result
;
1285 MutexAutoLock
lock(mQuotaMutex
);
1287 GroupInfoPair
* pair
;
1288 if (!mGroupInfoPairs
.Get(aGroup
, &pair
)) {
1292 nsRefPtr
<GroupInfo
> groupInfo
= pair
->LockedGetGroupInfo(aPersistenceType
);
1298 nsRefPtr
<OriginInfo
> originInfo
= groupInfo
->LockedGetOriginInfo(aOrigin
);
1304 // We need this extra raw pointer because we can't assign to the smart
1305 // pointer directly since QuotaObject::AddRef would try to acquire the same
1307 QuotaObject
* quotaObject
;
1308 if (!originInfo
->mQuotaObjects
.Get(path
, "aObject
)) {
1309 // Create a new QuotaObject.
1310 quotaObject
= new QuotaObject(originInfo
, path
, fileSize
);
1312 // Put it to the hashtable. The hashtable is not responsible to delete
1314 originInfo
->mQuotaObjects
.Put(path
, quotaObject
);
1317 // Addref the QuotaObject and move the ownership to the result. This must
1318 // happen before we unlock!
1319 result
= quotaObject
->LockedAddRef();
1322 // The caller becomes the owner of the QuotaObject, that is, the caller is
1323 // is responsible to delete it when the last reference is removed.
1324 return result
.forget();
1327 already_AddRefed
<QuotaObject
>
1328 QuotaManager::GetQuotaObject(PersistenceType aPersistenceType
,
1329 const nsACString
& aGroup
,
1330 const nsACString
& aOrigin
,
1331 const nsAString
& aPath
)
1334 nsCOMPtr
<nsIFile
> file
= do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
);
1335 NS_ENSURE_SUCCESS(rv
, nullptr);
1337 rv
= file
->InitWithPath(aPath
);
1338 NS_ENSURE_SUCCESS(rv
, nullptr);
1340 return GetQuotaObject(aPersistenceType
, aGroup
, aOrigin
, file
);
1344 QuotaManager::RegisterStorage(nsIOfflineStorage
* aStorage
)
1346 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1347 NS_ASSERTION(aStorage
, "Null pointer!");
1349 // Don't allow any new storages to be created after shutdown.
1350 if (IsShuttingDown()) {
1354 // Add this storage to its origin info if it exists, create it otherwise.
1355 const nsACString
& origin
= aStorage
->Origin();
1356 ArrayCluster
<nsIOfflineStorage
*>* cluster
;
1357 if (!mLiveStorages
.Get(origin
, &cluster
)) {
1358 cluster
= new ArrayCluster
<nsIOfflineStorage
*>();
1359 mLiveStorages
.Put(origin
, cluster
);
1361 UpdateOriginAccessTime(aStorage
->Type(), aStorage
->Group(), origin
);
1363 (*cluster
)[aStorage
->GetClient()->GetType()].AppendElement(aStorage
);
1369 QuotaManager::UnregisterStorage(nsIOfflineStorage
* aStorage
)
1371 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1372 NS_ASSERTION(aStorage
, "Null pointer!");
1374 // Remove this storage from its origin array, maybe remove the array if it
1376 const nsACString
& origin
= aStorage
->Origin();
1377 ArrayCluster
<nsIOfflineStorage
*>* cluster
;
1378 if (mLiveStorages
.Get(origin
, &cluster
) &&
1379 (*cluster
)[aStorage
->GetClient()->GetType()].RemoveElement(aStorage
)) {
1380 if (cluster
->IsEmpty()) {
1381 mLiveStorages
.Remove(origin
);
1383 UpdateOriginAccessTime(aStorage
->Type(), aStorage
->Group(), origin
);
1387 NS_ERROR("Didn't know anything about this storage!");
1391 QuotaManager::OnStorageClosed(nsIOfflineStorage
* aStorage
)
1393 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1394 NS_ASSERTION(aStorage
, "Null pointer!");
1396 // Check through the list of SynchronizedOps to see if any are waiting for
1397 // this storage to close before proceeding.
1398 SynchronizedOp
* op
=
1399 FindSynchronizedOp(aStorage
->Origin(),
1400 Nullable
<PersistenceType
>(aStorage
->Type()),
1403 Client::Type clientType
= aStorage
->GetClient()->GetType();
1405 // This storage is in the scope of this SynchronizedOp. Remove it
1406 // from the list if necessary.
1407 if (op
->mStorages
[clientType
].RemoveElement(aStorage
)) {
1408 // Now set up the helper if there are no more live storages.
1409 NS_ASSERTION(op
->mListener
,
1410 "How did we get rid of the listener before removing the "
1412 if (op
->mStorages
[clientType
].IsEmpty()) {
1413 // At this point, all storages are closed, so no new transactions
1414 // can be started. There may, however, still be outstanding
1415 // transactions that have not completed. We need to wait for those
1416 // before we dispatch the helper.
1417 if (NS_FAILED(RunSynchronizedOp(aStorage
, op
))) {
1418 NS_WARNING("Failed to run synchronized op!");
1426 QuotaManager::AbortCloseStoragesForWindow(nsPIDOMWindow
* aWindow
)
1428 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1429 NS_ASSERTION(aWindow
, "Null pointer!");
1431 FileService
* service
= FileService::Get();
1433 StorageMatcher
<ArrayCluster
<nsIOfflineStorage
*> > liveStorages
;
1434 liveStorages
.Find(mLiveStorages
);
1436 for (uint32_t i
= 0; i
< Client::TYPE_MAX
; i
++) {
1437 nsRefPtr
<Client
>& client
= mClients
[i
];
1438 bool utilized
= service
&& client
->IsFileServiceUtilized();
1439 bool activated
= client
->IsTransactionServiceActivated();
1441 nsTArray
<nsIOfflineStorage
*>& array
= liveStorages
[i
];
1442 for (uint32_t j
= 0; j
< array
.Length(); j
++) {
1443 nsIOfflineStorage
*& storage
= array
[j
];
1445 if (storage
->IsOwned(aWindow
)) {
1446 if (NS_FAILED(storage
->Close())) {
1447 NS_WARNING("Failed to close storage for dying window!");
1451 service
->AbortFileHandlesForStorage(storage
);
1455 client
->AbortTransactionsForStorage(storage
);
1463 QuotaManager::HasOpenTransactions(nsPIDOMWindow
* aWindow
)
1465 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1466 NS_ASSERTION(aWindow
, "Null pointer!");
1468 FileService
* service
= FileService::Get();
1470 nsAutoPtr
<StorageMatcher
<ArrayCluster
<nsIOfflineStorage
*> > > liveStorages
;
1472 for (uint32_t i
= 0; i
< Client::TYPE_MAX
; i
++) {
1473 nsRefPtr
<Client
>& client
= mClients
[i
];
1474 bool utilized
= service
&& client
->IsFileServiceUtilized();
1475 bool activated
= client
->IsTransactionServiceActivated();
1477 if (utilized
|| activated
) {
1478 if (!liveStorages
) {
1479 liveStorages
= new StorageMatcher
<ArrayCluster
<nsIOfflineStorage
*> >();
1480 liveStorages
->Find(mLiveStorages
);
1483 nsTArray
<nsIOfflineStorage
*>& storages
= liveStorages
->ArrayAt(i
);
1484 for (uint32_t j
= 0; j
< storages
.Length(); j
++) {
1485 nsIOfflineStorage
*& storage
= storages
[j
];
1487 if (storage
->IsOwned(aWindow
) &&
1488 ((utilized
&& service
->HasFileHandlesForStorage(storage
)) ||
1489 (activated
&& client
->HasTransactionsForStorage(storage
)))) {
1500 QuotaManager::WaitForOpenAllowed(const OriginOrPatternString
& aOriginOrPattern
,
1501 Nullable
<PersistenceType
> aPersistenceType
,
1502 const nsACString
& aId
, nsIRunnable
* aRunnable
)
1504 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1505 NS_ASSERTION(!aOriginOrPattern
.IsEmpty() || aOriginOrPattern
.IsNull(),
1507 NS_ASSERTION(aRunnable
, "Null pointer!");
1509 nsAutoPtr
<SynchronizedOp
> op(new SynchronizedOp(aOriginOrPattern
,
1510 aPersistenceType
, aId
));
1512 // See if this runnable needs to wait.
1513 bool delayed
= false;
1514 for (uint32_t index
= mSynchronizedOps
.Length(); index
> 0; index
--) {
1515 nsAutoPtr
<SynchronizedOp
>& existingOp
= mSynchronizedOps
[index
- 1];
1516 if (op
->MustWaitFor(*existingOp
)) {
1517 existingOp
->DelayRunnable(aRunnable
);
1523 // Otherwise, dispatch it immediately.
1525 nsresult rv
= NS_DispatchToCurrentThread(aRunnable
);
1526 NS_ENSURE_SUCCESS(rv
, rv
);
1529 // Adding this to the synchronized ops list will block any additional
1530 // ops from proceeding until this one is done.
1531 mSynchronizedOps
.AppendElement(op
.forget());
1537 QuotaManager::AddSynchronizedOp(const OriginOrPatternString
& aOriginOrPattern
,
1538 Nullable
<PersistenceType
> aPersistenceType
)
1540 nsAutoPtr
<SynchronizedOp
> op(new SynchronizedOp(aOriginOrPattern
,
1545 for (uint32_t index
= mSynchronizedOps
.Length(); index
> 0; index
--) {
1546 nsAutoPtr
<SynchronizedOp
>& existingOp
= mSynchronizedOps
[index
- 1];
1547 NS_ASSERTION(!op
->MustWaitFor(*existingOp
), "What?");
1551 mSynchronizedOps
.AppendElement(op
.forget());
1555 QuotaManager::AllowNextSynchronizedOp(
1556 const OriginOrPatternString
& aOriginOrPattern
,
1557 Nullable
<PersistenceType
> aPersistenceType
,
1558 const nsACString
& aId
)
1560 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1561 NS_ASSERTION(!aOriginOrPattern
.IsEmpty() || aOriginOrPattern
.IsNull(),
1562 "Empty origin/pattern!");
1564 uint32_t count
= mSynchronizedOps
.Length();
1565 for (uint32_t index
= 0; index
< count
; index
++) {
1566 nsAutoPtr
<SynchronizedOp
>& op
= mSynchronizedOps
[index
];
1567 if (op
->mOriginOrPattern
.IsOrigin() == aOriginOrPattern
.IsOrigin() &&
1568 op
->mOriginOrPattern
== aOriginOrPattern
&&
1569 op
->mPersistenceType
== aPersistenceType
) {
1570 if (op
->mId
== aId
) {
1571 NS_ASSERTION(op
->mStorages
.IsEmpty(), "How did this happen?");
1573 op
->DispatchDelayedRunnables();
1575 mSynchronizedOps
.RemoveElementAt(index
);
1579 // If one or the other is for an origin clear, we should have matched
1580 // solely on origin.
1581 NS_ASSERTION(!op
->mId
.IsEmpty() && !aId
.IsEmpty(),
1582 "Why didn't we match earlier?");
1586 NS_NOTREACHED("Why didn't we find a SynchronizedOp?");
1590 QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType
,
1591 const nsACString
& aASCIIOrigin
,
1592 nsIFile
** aDirectory
) const
1595 nsCOMPtr
<nsIFile
> directory
=
1596 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
);
1597 NS_ENSURE_SUCCESS(rv
, rv
);
1599 rv
= directory
->InitWithPath(GetStoragePath(aPersistenceType
));
1600 NS_ENSURE_SUCCESS(rv
, rv
);
1602 nsAutoCString
originSanitized(aASCIIOrigin
);
1603 SanitizeOriginString(originSanitized
);
1605 rv
= directory
->Append(NS_ConvertASCIItoUTF16(originSanitized
));
1606 NS_ENSURE_SUCCESS(rv
, rv
);
1608 directory
.forget(aDirectory
);
1613 QuotaManager::InitializeOrigin(PersistenceType aPersistenceType
,
1614 const nsACString
& aGroup
,
1615 const nsACString
& aOrigin
,
1617 int64_t aAccessTime
,
1618 nsIFile
* aDirectory
)
1620 AssertIsOnIOThread();
1624 bool temporaryStorage
= aPersistenceType
== PERSISTENCE_TYPE_TEMPORARY
;
1625 if (!temporaryStorage
) {
1626 rv
= MaybeUpgradeOriginDirectory(aDirectory
);
1627 NS_ENSURE_SUCCESS(rv
, rv
);
1630 // We need to initialize directories of all clients if they exists and also
1631 // get the total usage to initialize the quota.
1632 nsAutoPtr
<UsageInfo
> usageInfo
;
1634 usageInfo
= new UsageInfo();
1637 nsCOMPtr
<nsISimpleEnumerator
> entries
;
1638 rv
= aDirectory
->GetDirectoryEntries(getter_AddRefs(entries
));
1639 NS_ENSURE_SUCCESS(rv
, rv
);
1642 while (NS_SUCCEEDED((rv
= entries
->HasMoreElements(&hasMore
))) && hasMore
) {
1643 nsCOMPtr
<nsISupports
> entry
;
1644 rv
= entries
->GetNext(getter_AddRefs(entry
));
1645 NS_ENSURE_SUCCESS(rv
, rv
);
1647 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(entry
);
1648 NS_ENSURE_TRUE(file
, NS_NOINTERFACE
);
1651 rv
= file
->GetLeafName(leafName
);
1652 NS_ENSURE_SUCCESS(rv
, rv
);
1654 if (leafName
.EqualsLiteral(METADATA_FILE_NAME
) ||
1655 leafName
.EqualsLiteral(DSSTORE_FILE_NAME
)) {
1660 rv
= file
->IsDirectory(&isDirectory
);
1661 NS_ENSURE_SUCCESS(rv
, rv
);
1664 NS_WARNING("Unknown file found!");
1665 return NS_ERROR_UNEXPECTED
;
1668 Client::Type clientType
;
1669 rv
= Client::TypeFromText(leafName
, clientType
);
1670 if (NS_FAILED(rv
)) {
1671 NS_WARNING("Unknown directory found!");
1672 return NS_ERROR_UNEXPECTED
;
1675 rv
= mClients
[clientType
]->InitOrigin(aPersistenceType
, aGroup
, aOrigin
,
1677 NS_ENSURE_SUCCESS(rv
, rv
);
1681 uint64_t quotaMaxBytes
;
1682 uint64_t totalUsageBytes
= usageInfo
->TotalUsage();
1684 if (temporaryStorage
) {
1685 // Temporary storage has no limit for origin usage (there's a group and
1686 // the global limit though).
1690 quotaMaxBytes
= GetStorageQuotaMB() * 1024 * 1024;
1691 if (totalUsageBytes
> quotaMaxBytes
) {
1692 NS_WARNING("Origin is already using more storage than allowed!");
1693 return NS_ERROR_UNEXPECTED
;
1697 InitQuotaForOrigin(aPersistenceType
, aGroup
, aOrigin
, quotaMaxBytes
,
1698 totalUsageBytes
, aAccessTime
);
1705 QuotaManager::MaybeUpgradeIndexedDBDirectory()
1707 AssertIsOnIOThread();
1709 if (mStorageAreaInitialized
) {
1715 nsCOMPtr
<nsIFile
> indexedDBDir
=
1716 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
);
1717 NS_ENSURE_SUCCESS(rv
, rv
);
1719 rv
= indexedDBDir
->InitWithPath(mIndexedDBPath
);
1720 NS_ENSURE_SUCCESS(rv
, rv
);
1723 rv
= indexedDBDir
->Exists(&exists
);
1724 NS_ENSURE_SUCCESS(rv
, rv
);
1727 // Nothing to upgrade.
1728 mStorageAreaInitialized
= true;
1734 rv
= indexedDBDir
->IsDirectory(&isDirectory
);
1735 NS_ENSURE_SUCCESS(rv
, rv
);
1738 NS_WARNING("indexedDB entry is not a directory!");
1742 nsCOMPtr
<nsIFile
> persistentStorageDir
=
1743 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
);
1744 NS_ENSURE_SUCCESS(rv
, rv
);
1746 rv
= persistentStorageDir
->InitWithPath(mPersistentStoragePath
);
1747 NS_ENSURE_SUCCESS(rv
, rv
);
1749 rv
= persistentStorageDir
->Exists(&exists
);
1750 NS_ENSURE_SUCCESS(rv
, rv
);
1753 NS_WARNING("indexedDB directory shouldn't exist after the upgrade!");
1757 nsCOMPtr
<nsIFile
> storageDir
;
1758 rv
= persistentStorageDir
->GetParent(getter_AddRefs(storageDir
));
1759 NS_ENSURE_SUCCESS(rv
, rv
);
1761 nsString persistentStorageName
;
1762 rv
= persistentStorageDir
->GetLeafName(persistentStorageName
);
1763 NS_ENSURE_SUCCESS(rv
, rv
);
1765 // MoveTo() is atomic if the move happens on the same volume which should
1766 // be our case, so even if we crash in the middle of the operation nothing
1767 // breaks next time we try to initialize.
1768 // However there's a theoretical possibility that the indexedDB directory
1769 // is on different volume, but it should be rare enough that we don't have
1770 // to worry about it.
1771 rv
= indexedDBDir
->MoveTo(storageDir
, persistentStorageName
);
1772 NS_ENSURE_SUCCESS(rv
, rv
);
1774 mStorageAreaInitialized
= true;
1780 QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType
,
1781 const nsACString
& aGroup
,
1782 const nsACString
& aOrigin
,
1784 nsIFile
** aDirectory
)
1786 AssertIsOnIOThread();
1788 nsresult rv
= MaybeUpgradeIndexedDBDirectory();
1789 NS_ENSURE_SUCCESS(rv
, rv
);
1791 // Get directory for this origin and persistence type.
1792 nsCOMPtr
<nsIFile
> directory
;
1793 rv
= GetDirectoryForOrigin(aPersistenceType
, aOrigin
,
1794 getter_AddRefs(directory
));
1795 NS_ENSURE_SUCCESS(rv
, rv
);
1797 if (aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
1798 if (mInitializedOrigins
.Contains(aOrigin
)) {
1799 NS_ADDREF(*aDirectory
= directory
);
1804 rv
= EnsureDirectory(directory
, &created
);
1805 NS_ENSURE_SUCCESS(rv
, rv
);
1808 rv
= CreateDirectoryUpgradeStamp(directory
);
1809 NS_ENSURE_SUCCESS(rv
, rv
);
1812 rv
= InitializeOrigin(aPersistenceType
, aGroup
, aOrigin
, aTrackQuota
, 0,
1814 NS_ENSURE_SUCCESS(rv
, rv
);
1816 mInitializedOrigins
.AppendElement(aOrigin
);
1818 directory
.forget(aDirectory
);
1822 NS_ASSERTION(aPersistenceType
== PERSISTENCE_TYPE_TEMPORARY
, "Huh?");
1823 NS_ASSERTION(aTrackQuota
, "Huh?");
1825 if (!mTemporaryStorageInitialized
) {
1826 nsCOMPtr
<nsIFile
> parentDirectory
;
1827 rv
= directory
->GetParent(getter_AddRefs(parentDirectory
));
1828 NS_ENSURE_SUCCESS(rv
, rv
);
1831 rv
= EnsureDirectory(parentDirectory
, &created
);
1832 NS_ENSURE_SUCCESS(rv
, rv
);
1834 nsCOMPtr
<nsISimpleEnumerator
> entries
;
1835 rv
= parentDirectory
->GetDirectoryEntries(getter_AddRefs(entries
));
1836 NS_ENSURE_SUCCESS(rv
, rv
);
1839 while (NS_SUCCEEDED((rv
= entries
->HasMoreElements(&hasMore
))) && hasMore
) {
1840 nsCOMPtr
<nsISupports
> entry
;
1841 rv
= entries
->GetNext(getter_AddRefs(entry
));
1842 NS_ENSURE_SUCCESS(rv
, rv
);
1844 nsCOMPtr
<nsIFile
> childDirectory
= do_QueryInterface(entry
);
1845 NS_ENSURE_TRUE(childDirectory
, NS_NOINTERFACE
);
1848 rv
= childDirectory
->IsDirectory(&isDirectory
);
1849 NS_ENSURE_SUCCESS(rv
, rv
);
1850 NS_ENSURE_TRUE(isDirectory
, NS_ERROR_UNEXPECTED
);
1855 rv
= GetDirectoryMetadata(childDirectory
, ×tamp
, group
, origin
);
1856 NS_ENSURE_SUCCESS(rv
, rv
);
1858 rv
= InitializeOrigin(aPersistenceType
, group
, origin
, aTrackQuota
,
1859 timestamp
, childDirectory
);
1860 if (NS_FAILED(rv
)) {
1861 NS_WARNING("Failed to initialize origin!");
1863 // We have to cleanup partially initialized quota for temporary storage.
1864 RemoveQuotaForPersistenceType(aPersistenceType
);
1870 if (gFixedLimitKB
>= 0) {
1871 mTemporaryStorageLimit
= gFixedLimitKB
* 1024;
1874 rv
= GetTemporaryStorageLimit(parentDirectory
, mTemporaryStorageUsage
,
1875 &mTemporaryStorageLimit
);
1876 NS_ENSURE_SUCCESS(rv
, rv
);
1879 mTemporaryStorageInitialized
= true;
1881 CheckTemporaryStorageLimits();
1885 rv
= EnsureDirectory(directory
, &created
);
1886 NS_ENSURE_SUCCESS(rv
, rv
);
1889 int64_t timestamp
= PR_Now();
1891 rv
= CreateDirectoryMetadata(directory
, timestamp
, aGroup
, aOrigin
);
1892 NS_ENSURE_SUCCESS(rv
, rv
);
1894 rv
= InitializeOrigin(aPersistenceType
, aGroup
, aOrigin
, aTrackQuota
,
1895 timestamp
, directory
);
1896 NS_ENSURE_SUCCESS(rv
, rv
);
1899 directory
.forget(aDirectory
);
1904 QuotaManager::OriginClearCompleted(
1905 PersistenceType aPersistenceType
,
1906 const OriginOrPatternString
& aOriginOrPattern
)
1908 AssertIsOnIOThread();
1910 if (aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
1911 if (aOriginOrPattern
.IsOrigin()) {
1912 mInitializedOrigins
.RemoveElement(aOriginOrPattern
);
1915 for (uint32_t index
= mInitializedOrigins
.Length(); index
> 0; index
--) {
1916 if (PatternMatchesOrigin(aOriginOrPattern
,
1917 mInitializedOrigins
[index
- 1])) {
1918 mInitializedOrigins
.RemoveElementAt(index
- 1);
1924 for (uint32_t index
= 0; index
< Client::TYPE_MAX
; index
++) {
1925 mClients
[index
]->OnOriginClearCompleted(aPersistenceType
, aOriginOrPattern
);
1930 QuotaManager::ResetOrClearCompleted()
1932 AssertIsOnIOThread();
1934 mInitializedOrigins
.Clear();
1935 mTemporaryStorageInitialized
= false;
1937 ReleaseIOThreadObjects();
1940 already_AddRefed
<mozilla::dom::quota::Client
>
1941 QuotaManager::GetClient(Client::Type aClientType
)
1943 nsRefPtr
<Client
> client
= mClients
.SafeElementAt(aClientType
);
1944 return client
.forget();
1948 QuotaManager::GetGroupLimit() const
1950 MOZ_ASSERT(mTemporaryStorageInitialized
);
1952 // To avoid one group evicting all the rest, limit the amount any one group
1953 // can use to 20%. To prevent individual sites from using exorbitant amounts
1954 // of storage where there is a lot of free space, cap the group limit to 2GB.
1955 uint64_t x
= std::min
<uint64_t>(mTemporaryStorageLimit
* .20, 2 GB
);
1957 // In low-storage situations, make an exception (while not exceeding the total
1959 return std::min
<uint64_t>(mTemporaryStorageLimit
,
1960 std::max
<uint64_t>(x
, 10 MB
));
1965 QuotaManager::GetStorageQuotaMB()
1967 return uint32_t(std::max(gStorageQuotaMB
, 0));
1972 QuotaManager::GetStorageId(PersistenceType aPersistenceType
,
1973 const nsACString
& aOrigin
,
1974 Client::Type aClientType
,
1975 const nsAString
& aName
,
1976 nsACString
& aDatabaseId
)
1979 str
.AppendInt(aPersistenceType
);
1981 str
.Append(aOrigin
);
1983 str
.AppendInt(aClientType
);
1985 str
.Append(NS_ConvertUTF16toUTF8(aName
));
1992 QuotaManager::GetInfoFromURI(nsIURI
* aURI
,
1996 nsACString
* aASCIIOrigin
,
1997 StoragePrivilege
* aPrivilege
,
1998 PersistenceType
* aDefaultPersistenceType
)
2000 NS_ASSERTION(aURI
, "Null uri!");
2002 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
2003 NS_ENSURE_TRUE(secMan
, NS_ERROR_FAILURE
);
2005 nsCOMPtr
<nsIPrincipal
> principal
;
2006 nsresult rv
= secMan
->GetAppCodebasePrincipal(aURI
, aAppId
, aInMozBrowser
,
2007 getter_AddRefs(principal
));
2008 NS_ENSURE_SUCCESS(rv
, rv
);
2010 rv
= GetInfoFromPrincipal(principal
, aGroup
, aASCIIOrigin
, aPrivilege
,
2011 aDefaultPersistenceType
);
2012 NS_ENSURE_SUCCESS(rv
, rv
);
2018 TryGetInfoForAboutURI(nsIPrincipal
* aPrincipal
,
2020 nsACString
& aASCIIOrigin
,
2021 StoragePrivilege
* aPrivilege
,
2022 PersistenceType
* aDefaultPersistenceType
)
2024 NS_ASSERTION(aPrincipal
, "Don't hand me a null principal!");
2026 nsCOMPtr
<nsIURI
> uri
;
2027 nsresult rv
= aPrincipal
->GetURI(getter_AddRefs(uri
));
2028 NS_ENSURE_SUCCESS(rv
, rv
);
2030 return NS_ERROR_NOT_AVAILABLE
;
2034 rv
= uri
->SchemeIs("about", &isAbout
);
2035 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && isAbout
, NS_ERROR_FAILURE
);
2037 nsCOMPtr
<nsIAboutModule
> module
;
2038 rv
= NS_GetAboutModule(uri
, getter_AddRefs(module
));
2039 NS_ENSURE_SUCCESS(rv
, rv
);
2041 nsCOMPtr
<nsIURI
> inner
= NS_GetInnermostURI(uri
);
2042 NS_ENSURE_TRUE(inner
, NS_ERROR_FAILURE
);
2044 nsAutoString postfix
;
2045 rv
= module
->GetIndexedDBOriginPostfix(uri
, postfix
);
2046 NS_ENSURE_SUCCESS(rv
, rv
);
2049 if (DOMStringIsNull(postfix
)) {
2050 rv
= inner
->GetSpec(origin
);
2051 NS_ENSURE_SUCCESS(rv
, rv
);
2053 nsAutoCString scheme
;
2054 rv
= inner
->GetScheme(scheme
);
2055 NS_ENSURE_SUCCESS(rv
, rv
);
2057 origin
= scheme
+ NS_LITERAL_CSTRING(":") + NS_ConvertUTF16toUTF8(postfix
);
2060 ToLowerCase(origin
);
2061 aGroup
.Assign(origin
);
2062 aASCIIOrigin
.Assign(origin
);
2065 *aPrivilege
= Content
;
2068 if (aDefaultPersistenceType
) {
2069 *aDefaultPersistenceType
= PERSISTENCE_TYPE_PERSISTENT
;
2077 QuotaManager::GetInfoFromPrincipal(nsIPrincipal
* aPrincipal
,
2079 nsACString
* aASCIIOrigin
,
2080 StoragePrivilege
* aPrivilege
,
2081 PersistenceType
* aDefaultPersistenceType
)
2083 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2084 NS_ASSERTION(aPrincipal
, "Don't hand me a null principal!");
2086 if (aGroup
&& aASCIIOrigin
) {
2087 nsresult rv
= TryGetInfoForAboutURI(aPrincipal
, *aGroup
, *aASCIIOrigin
,
2088 aPrivilege
, aDefaultPersistenceType
);
2089 if (NS_SUCCEEDED(rv
)) {
2094 if (nsContentUtils::IsSystemPrincipal(aPrincipal
)) {
2095 GetInfoForChrome(aGroup
, aASCIIOrigin
, aPrivilege
, aDefaultPersistenceType
);
2099 bool isNullPrincipal
;
2100 nsresult rv
= aPrincipal
->GetIsNullPrincipal(&isNullPrincipal
);
2101 NS_ENSURE_SUCCESS(rv
, rv
);
2103 if (isNullPrincipal
) {
2104 NS_WARNING("IndexedDB not supported from this principal!");
2105 return NS_ERROR_FAILURE
;
2109 rv
= aPrincipal
->GetOrigin(getter_Copies(origin
));
2110 NS_ENSURE_SUCCESS(rv
, rv
);
2112 if (origin
.EqualsLiteral("chrome")) {
2113 NS_WARNING("Non-chrome principal can't use chrome origin!");
2114 return NS_ERROR_FAILURE
;
2117 nsCString jarPrefix
;
2118 if (aGroup
|| aASCIIOrigin
) {
2119 rv
= aPrincipal
->GetJarPrefix(jarPrefix
);
2120 NS_ENSURE_SUCCESS(rv
, rv
);
2124 nsCString baseDomain
;
2125 rv
= aPrincipal
->GetBaseDomain(baseDomain
);
2126 if (NS_FAILED(rv
)) {
2127 // A hack for JetPack.
2129 nsCOMPtr
<nsIURI
> uri
;
2130 rv
= aPrincipal
->GetURI(getter_AddRefs(uri
));
2131 NS_ENSURE_SUCCESS(rv
, rv
);
2133 bool isIndexedDBURI
= false;
2134 rv
= uri
->SchemeIs("indexedDB", &isIndexedDBURI
);
2135 NS_ENSURE_SUCCESS(rv
, rv
);
2137 if (isIndexedDBURI
) {
2141 NS_ENSURE_SUCCESS(rv
, rv
);
2143 if (baseDomain
.IsEmpty()) {
2144 aGroup
->Assign(jarPrefix
+ origin
);
2147 aGroup
->Assign(jarPrefix
+ baseDomain
);
2152 aASCIIOrigin
->Assign(jarPrefix
+ origin
);
2156 *aPrivilege
= Content
;
2159 if (aDefaultPersistenceType
) {
2160 *aDefaultPersistenceType
= PERSISTENCE_TYPE_PERSISTENT
;
2168 QuotaManager::GetInfoFromWindow(nsPIDOMWindow
* aWindow
,
2170 nsACString
* aASCIIOrigin
,
2171 StoragePrivilege
* aPrivilege
,
2172 PersistenceType
* aDefaultPersistenceType
)
2174 NS_ASSERTION(NS_IsMainThread(),
2175 "We're about to touch a window off the main thread!");
2176 NS_ASSERTION(aWindow
, "Don't hand me a null window!");
2178 nsCOMPtr
<nsIScriptObjectPrincipal
> sop
= do_QueryInterface(aWindow
);
2179 NS_ENSURE_TRUE(sop
, NS_ERROR_FAILURE
);
2181 nsCOMPtr
<nsIPrincipal
> principal
= sop
->GetPrincipal();
2182 NS_ENSURE_TRUE(principal
, NS_ERROR_FAILURE
);
2184 nsresult rv
= GetInfoFromPrincipal(principal
, aGroup
, aASCIIOrigin
,
2185 aPrivilege
, aDefaultPersistenceType
);
2186 NS_ENSURE_SUCCESS(rv
, rv
);
2193 QuotaManager::GetInfoForChrome(nsACString
* aGroup
,
2194 nsACString
* aASCIIOrigin
,
2195 StoragePrivilege
* aPrivilege
,
2196 PersistenceType
* aDefaultPersistenceType
)
2198 NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
2200 static const char kChromeOrigin
[] = "chrome";
2203 aGroup
->AssignLiteral(kChromeOrigin
);
2206 aASCIIOrigin
->AssignLiteral(kChromeOrigin
);
2209 *aPrivilege
= Chrome
;
2211 if (aDefaultPersistenceType
) {
2212 *aDefaultPersistenceType
= PERSISTENCE_TYPE_PERSISTENT
;
2216 NS_IMPL_ISUPPORTS(QuotaManager
, nsIQuotaManager
, nsIObserver
)
2219 QuotaManager::GetUsageForURI(nsIURI
* aURI
,
2220 nsIUsageCallback
* aCallback
,
2222 bool aInMozBrowserOnly
,
2223 uint8_t aOptionalArgCount
,
2224 nsIQuotaRequest
** _retval
)
2226 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2228 NS_ENSURE_ARG_POINTER(aURI
);
2229 NS_ENSURE_ARG_POINTER(aCallback
);
2231 // This only works from the main process.
2232 NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE
);
2234 if (!aOptionalArgCount
) {
2235 aAppId
= nsIScriptSecurityManager::NO_APP_ID
;
2238 // Figure out which origin we're dealing with.
2241 nsresult rv
= GetInfoFromURI(aURI
, aAppId
, aInMozBrowserOnly
, &group
, &origin
,
2243 NS_ENSURE_SUCCESS(rv
, rv
);
2245 OriginOrPatternString oops
= OriginOrPatternString::FromOrigin(origin
);
2247 nsRefPtr
<AsyncUsageRunnable
> runnable
=
2248 new AsyncUsageRunnable(aAppId
, aInMozBrowserOnly
, group
, oops
, aURI
,
2251 // Put the computation runnable in the queue.
2252 rv
= WaitForOpenAllowed(oops
, Nullable
<PersistenceType
>(), EmptyCString(),
2254 NS_ENSURE_SUCCESS(rv
, rv
);
2256 runnable
->AdvanceState();
2258 runnable
.forget(_retval
);
2263 QuotaManager::Clear()
2265 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2267 if (!gTestingEnabled
) {
2268 NS_WARNING("Testing features are not enabled!");
2272 OriginOrPatternString oops
= OriginOrPatternString::FromNull();
2274 nsRefPtr
<ResetOrClearRunnable
> runnable
= new ResetOrClearRunnable(true);
2276 // Put the clear runnable in the queue.
2278 WaitForOpenAllowed(oops
, Nullable
<PersistenceType
>(), EmptyCString(),
2280 NS_ENSURE_SUCCESS(rv
, rv
);
2282 runnable
->AdvanceState();
2284 // Give the runnable some help by invalidating any storages in the way.
2285 StorageMatcher
<nsAutoTArray
<nsIOfflineStorage
*, 20> > matches
;
2286 matches
.Find(mLiveStorages
);
2288 for (uint32_t index
= 0; index
< matches
.Length(); index
++) {
2289 // We need to grab references to any live storages here to prevent them
2290 // from dying while we invalidate them.
2291 nsCOMPtr
<nsIOfflineStorage
> storage
= matches
[index
];
2292 storage
->Invalidate();
2295 // After everything has been invalidated the helper should be dispatched to
2296 // the end of the event queue.
2301 QuotaManager::ClearStoragesForURI(nsIURI
* aURI
,
2303 bool aInMozBrowserOnly
,
2304 const nsACString
& aPersistenceType
,
2305 uint8_t aOptionalArgCount
)
2307 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2309 NS_ENSURE_ARG_POINTER(aURI
);
2311 Nullable
<PersistenceType
> persistenceType
;
2313 NullablePersistenceTypeFromText(aPersistenceType
, &persistenceType
);
2314 if (NS_WARN_IF(NS_FAILED(rv
))) {
2315 return NS_ERROR_INVALID_ARG
;
2318 // This only works from the main process.
2319 NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE
);
2321 if (!aOptionalArgCount
) {
2322 aAppId
= nsIScriptSecurityManager::NO_APP_ID
;
2325 // Figure out which origin we're dealing with.
2327 rv
= GetInfoFromURI(aURI
, aAppId
, aInMozBrowserOnly
, nullptr, &origin
,
2329 NS_ENSURE_SUCCESS(rv
, rv
);
2331 nsAutoCString pattern
;
2332 GetOriginPatternString(aAppId
, aInMozBrowserOnly
, origin
, pattern
);
2334 // If there is a pending or running clear operation for this origin, return
2336 if (IsClearOriginPending(pattern
, persistenceType
)) {
2340 OriginOrPatternString oops
= OriginOrPatternString::FromPattern(pattern
);
2342 // Queue up the origin clear runnable.
2343 nsRefPtr
<OriginClearRunnable
> runnable
=
2344 new OriginClearRunnable(oops
, persistenceType
);
2346 rv
= WaitForOpenAllowed(oops
, persistenceType
, EmptyCString(), runnable
);
2347 NS_ENSURE_SUCCESS(rv
, rv
);
2349 runnable
->AdvanceState();
2351 // Give the runnable some help by invalidating any storages in the way.
2352 StorageMatcher
<nsAutoTArray
<nsIOfflineStorage
*, 20> > matches
;
2353 matches
.Find(mLiveStorages
, pattern
);
2355 for (uint32_t index
= 0; index
< matches
.Length(); index
++) {
2356 if (persistenceType
.IsNull() ||
2357 matches
[index
]->Type() == persistenceType
.Value()) {
2358 // We need to grab references to any live storages here to prevent them
2359 // from dying while we invalidate them.
2360 nsCOMPtr
<nsIOfflineStorage
> storage
= matches
[index
];
2361 storage
->Invalidate();
2365 // After everything has been invalidated the helper should be dispatched to
2366 // the end of the event queue.
2371 QuotaManager::Reset()
2373 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2375 if (!gTestingEnabled
) {
2376 NS_WARNING("Testing features are not enabled!");
2380 OriginOrPatternString oops
= OriginOrPatternString::FromNull();
2382 nsRefPtr
<ResetOrClearRunnable
> runnable
= new ResetOrClearRunnable(false);
2384 // Put the reset runnable in the queue.
2386 WaitForOpenAllowed(oops
, Nullable
<PersistenceType
>(), EmptyCString(),
2388 NS_ENSURE_SUCCESS(rv
, rv
);
2390 runnable
->AdvanceState();
2392 // Give the runnable some help by invalidating any storages in the way.
2393 StorageMatcher
<nsAutoTArray
<nsIOfflineStorage
*, 20> > matches
;
2394 matches
.Find(mLiveStorages
);
2396 for (uint32_t index
= 0; index
< matches
.Length(); index
++) {
2397 // We need to grab references to any live storages here to prevent them
2398 // from dying while we invalidate them.
2399 nsCOMPtr
<nsIOfflineStorage
> storage
= matches
[index
];
2400 storage
->Invalidate();
2403 // After everything has been invalidated the helper should be dispatched to
2404 // the end of the event queue.
2409 QuotaManager::Observe(nsISupports
* aSubject
,
2411 const char16_t
* aData
)
2413 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2415 if (!strcmp(aTopic
, PROFILE_BEFORE_CHANGE_OBSERVER_ID
)) {
2416 // Setting this flag prevents the service from being recreated and prevents
2417 // further storagess from being created.
2418 if (gShutdown
.exchange(true)) {
2419 NS_ERROR("Shutdown more than once?!");
2422 if (IsMainProcess()) {
2423 FileService
* service
= FileService::Get();
2425 // This should only wait for storages registered in this manager
2426 // to complete. Other storages may still have running file handles.
2427 // If the necko service (thread pool) gets the shutdown notification
2428 // first then the sync loop won't be processed at all, otherwise it will
2429 // lock the main thread until all storages registered in this manager
2432 nsTArray
<uint32_t> indexes
;
2433 for (uint32_t index
= 0; index
< Client::TYPE_MAX
; index
++) {
2434 if (mClients
[index
]->IsFileServiceUtilized()) {
2435 indexes
.AppendElement(index
);
2439 StorageMatcher
<nsTArray
<nsCOMPtr
<nsIOfflineStorage
>>> liveStorages
;
2440 liveStorages
.Find(mLiveStorages
, &indexes
);
2442 if (!liveStorages
.IsEmpty()) {
2443 nsRefPtr
<WaitForFileHandlesToFinishRunnable
> runnable
=
2444 new WaitForFileHandlesToFinishRunnable();
2446 service
->WaitForStoragesToComplete(liveStorages
, runnable
);
2448 nsIThread
* thread
= NS_GetCurrentThread();
2449 while (runnable
->IsBusy()) {
2450 if (!NS_ProcessNextEvent(thread
)) {
2451 NS_ERROR("Failed to process next event!");
2458 // Kick off the shutdown timer.
2459 if (NS_FAILED(mShutdownTimer
->Init(this, DEFAULT_SHUTDOWN_TIMER_MS
,
2460 nsITimer::TYPE_ONE_SHOT
))) {
2461 NS_WARNING("Failed to initialize shutdown timer!");
2464 // Each client will spin the event loop while we wait on all the threads
2465 // to close. Our timer may fire during that loop.
2466 for (uint32_t index
= 0; index
< Client::TYPE_MAX
; index
++) {
2467 mClients
[index
]->ShutdownTransactionService();
2470 // Cancel the timer regardless of whether it actually fired.
2471 if (NS_FAILED(mShutdownTimer
->Cancel())) {
2472 NS_WARNING("Failed to cancel shutdown timer!");
2475 // Give clients a chance to cleanup IO thread only objects.
2476 nsCOMPtr
<nsIRunnable
> runnable
=
2477 NS_NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects
);
2479 NS_WARNING("Failed to create runnable!");
2482 if (NS_FAILED(mIOThread
->Dispatch(runnable
, NS_DISPATCH_NORMAL
))) {
2483 NS_WARNING("Failed to dispatch runnable!");
2486 // Make sure to join with our IO thread.
2487 if (NS_FAILED(mIOThread
->Shutdown())) {
2488 NS_WARNING("Failed to shutdown IO thread!");
2495 if (!strcmp(aTopic
, NS_TIMER_CALLBACK_TOPIC
)) {
2496 NS_ASSERTION(IsMainProcess(), "Should only happen in the main process!");
2498 NS_WARNING("Some storage operations are taking longer than expected "
2499 "during shutdown and will be aborted!");
2501 // Grab all live storages, for all origins.
2502 StorageMatcher
<nsAutoTArray
<nsIOfflineStorage
*, 50> > liveStorages
;
2503 liveStorages
.Find(mLiveStorages
);
2505 // Invalidate them all.
2506 if (!liveStorages
.IsEmpty()) {
2507 uint32_t count
= liveStorages
.Length();
2508 for (uint32_t index
= 0; index
< count
; index
++) {
2509 liveStorages
[index
]->Invalidate();
2516 if (!strcmp(aTopic
, TOPIC_WEB_APP_CLEAR_DATA
)) {
2517 nsCOMPtr
<mozIApplicationClearPrivateDataParams
> params
=
2518 do_QueryInterface(aSubject
);
2519 NS_ENSURE_TRUE(params
, NS_ERROR_UNEXPECTED
);
2522 nsresult rv
= params
->GetAppId(&appId
);
2523 NS_ENSURE_SUCCESS(rv
, rv
);
2526 rv
= params
->GetBrowserOnly(&browserOnly
);
2527 NS_ENSURE_SUCCESS(rv
, rv
);
2529 rv
= ClearStoragesForApp(appId
, browserOnly
);
2530 NS_ENSURE_SUCCESS(rv
, rv
);
2535 NS_NOTREACHED("Unknown topic!");
2536 return NS_ERROR_UNEXPECTED
;
2540 QuotaManager::SetCurrentWindowInternal(nsPIDOMWindow
* aWindow
)
2542 NS_ASSERTION(mCurrentWindowIndex
!= BAD_TLS_INDEX
,
2543 "Should have a valid TLS storage index!");
2546 NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex
),
2547 "Somebody forgot to clear the current window!");
2548 PR_SetThreadPrivate(mCurrentWindowIndex
, aWindow
);
2551 // We cannot assert PR_GetThreadPrivate(mCurrentWindowIndex) here because
2552 // there are some cases where we did not already have a window.
2553 PR_SetThreadPrivate(mCurrentWindowIndex
, nullptr);
2558 QuotaManager::CancelPromptsForWindowInternal(nsPIDOMWindow
* aWindow
)
2560 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2562 nsRefPtr
<CheckQuotaHelper
> helper
;
2564 MutexAutoLock
autoLock(mQuotaMutex
);
2566 if (mCheckQuotaHelpers
.Get(aWindow
, getter_AddRefs(helper
))) {
2572 QuotaManager::LockedQuotaIsLifted()
2574 mQuotaMutex
.AssertCurrentThreadOwns();
2576 NS_ASSERTION(mCurrentWindowIndex
!= BAD_TLS_INDEX
,
2577 "Should have a valid TLS storage index!");
2579 nsPIDOMWindow
* window
=
2580 static_cast<nsPIDOMWindow
*>(PR_GetThreadPrivate(mCurrentWindowIndex
));
2582 // Quota is not enforced in chrome contexts (e.g. for components and JSMs)
2583 // so we must have a window here.
2584 NS_ASSERTION(window
, "Why don't we have a Window here?");
2586 bool createdHelper
= false;
2588 nsRefPtr
<CheckQuotaHelper
> helper
;
2589 if (!mCheckQuotaHelpers
.Get(window
, getter_AddRefs(helper
))) {
2590 helper
= new CheckQuotaHelper(window
, mQuotaMutex
);
2591 createdHelper
= true;
2593 mCheckQuotaHelpers
.Put(window
, helper
);
2595 // Unlock while calling out to XPCOM (code behind the dispatch method needs
2596 // to acquire its own lock which can potentially lead to a deadlock and it
2597 // also calls an observer that can do various stuff like IO, so it's better
2598 // to not hold our mutex while that happens).
2600 MutexAutoUnlock
autoUnlock(mQuotaMutex
);
2602 nsresult rv
= NS_DispatchToMainThread(helper
);
2603 NS_ENSURE_SUCCESS(rv
, false);
2606 // Relocked. If any other threads hit the quota limit on the same Window,
2607 // they are using the helper we created here and are now blocking in
2608 // PromptAndReturnQuotaDisabled.
2611 bool result
= helper
->PromptAndReturnQuotaIsDisabled();
2613 // If this thread created the helper and added it to the hash, this thread
2615 if (createdHelper
) {
2616 mCheckQuotaHelpers
.Remove(window
);
2623 QuotaManager::LockedCollectOriginsForEviction(
2624 uint64_t aMinSizeToBeFreed
,
2625 nsTArray
<OriginInfo
*>& aOriginInfos
)
2627 mQuotaMutex
.AssertCurrentThreadOwns();
2629 nsRefPtr
<CollectOriginsHelper
> helper
=
2630 new CollectOriginsHelper(mQuotaMutex
, aMinSizeToBeFreed
);
2632 // Unlock while calling out to XPCOM (see the detailed comment in
2633 // LockedQuotaIsLifted)
2635 MutexAutoUnlock
autoUnlock(mQuotaMutex
);
2637 if (NS_FAILED(NS_DispatchToMainThread(helper
))) {
2638 NS_WARNING("Failed to dispatch to the main thread!");
2642 return helper
->BlockAndReturnOriginsForEviction(aOriginInfos
);
2646 QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType
,
2647 const nsACString
& aGroup
,
2648 const nsACString
& aOrigin
)
2650 mQuotaMutex
.AssertCurrentThreadOwns();
2652 GroupInfoPair
* pair
;
2653 mGroupInfoPairs
.Get(aGroup
, &pair
);
2659 nsRefPtr
<GroupInfo
> groupInfo
= pair
->LockedGetGroupInfo(aPersistenceType
);
2661 groupInfo
->LockedRemoveOriginInfo(aOrigin
);
2663 if (!groupInfo
->LockedHasOriginInfos()) {
2664 pair
->LockedClearGroupInfo(aPersistenceType
);
2666 if (!pair
->LockedHasGroupInfos()) {
2667 mGroupInfoPairs
.Remove(aGroup
);
2674 QuotaManager::AcquireExclusiveAccess(const nsACString
& aPattern
,
2675 Nullable
<PersistenceType
> aPersistenceType
,
2676 nsIOfflineStorage
* aStorage
,
2677 AcquireListener
* aListener
,
2678 WaitingOnStoragesCallback aCallback
,
2681 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2682 NS_ASSERTION(aListener
, "Need a listener!");
2684 // Find the right SynchronizedOp.
2685 SynchronizedOp
* op
=
2686 FindSynchronizedOp(aPattern
, aPersistenceType
,
2687 aStorage
? aStorage
->Id() : EmptyCString());
2689 NS_ASSERTION(op
, "We didn't find a SynchronizedOp?");
2690 NS_ASSERTION(!op
->mListener
, "SynchronizedOp already has a listener?!?");
2692 nsTArray
<nsCOMPtr
<nsIOfflineStorage
> > liveStorages
;
2695 // We need to wait for the storages to go away.
2696 // Hold on to all storage objects that represent the same storage file
2697 // (except the one that is requesting this version change).
2699 Client::Type clientType
= aStorage
->GetClient()->GetType();
2701 StorageMatcher
<nsAutoTArray
<nsIOfflineStorage
*, 20> > matches
;
2702 matches
.Find(mLiveStorages
, aPattern
, clientType
);
2704 if (!matches
.IsEmpty()) {
2705 // Grab all storages that are not yet closed but whose storage id match
2706 // the one we're looking for.
2707 for (uint32_t index
= 0; index
< matches
.Length(); index
++) {
2708 nsIOfflineStorage
*& storage
= matches
[index
];
2709 if (!storage
->IsClosed() &&
2710 storage
!= aStorage
&&
2711 storage
->Id() == aStorage
->Id() &&
2712 (aPersistenceType
.IsNull() ||
2713 aPersistenceType
.Value() == storage
->Type())) {
2714 liveStorages
.AppendElement(storage
);
2719 if (!liveStorages
.IsEmpty()) {
2720 NS_ASSERTION(op
->mStorages
[clientType
].IsEmpty(),
2721 "How do we already have storages here?");
2722 op
->mStorages
[clientType
].AppendElements(liveStorages
);
2726 StorageMatcher
<ArrayCluster
<nsIOfflineStorage
*> > matches
;
2727 if (aPattern
.IsVoid()) {
2728 matches
.Find(mLiveStorages
);
2731 matches
.Find(mLiveStorages
, aPattern
);
2734 NS_ASSERTION(op
->mStorages
.IsEmpty(),
2735 "How do we already have storages here?");
2737 // We want *all* storages that match the given persistence type, even those
2738 // that are closed, when we're going to clear the origin.
2739 if (!matches
.IsEmpty()) {
2740 for (uint32_t i
= 0; i
< Client::TYPE_MAX
; i
++) {
2741 nsTArray
<nsIOfflineStorage
*>& storages
= matches
.ArrayAt(i
);
2742 for (uint32_t j
= 0; j
< storages
.Length(); j
++) {
2743 nsIOfflineStorage
* storage
= storages
[j
];
2744 if (aPersistenceType
.IsNull() ||
2745 aPersistenceType
.Value() == storage
->Type()) {
2746 liveStorages
.AppendElement(storage
);
2747 op
->mStorages
[i
].AppendElement(storage
);
2754 op
->mListener
= aListener
;
2756 if (!liveStorages
.IsEmpty()) {
2757 // Give our callback the storages so it can decide what to do with them.
2758 aCallback(liveStorages
, aClosure
);
2760 NS_ASSERTION(liveStorages
.IsEmpty(),
2761 "Should have done something with the array!");
2764 // Wait for those storages to close.
2769 // If we're trying to open a storage and nothing blocks it, or if we're
2770 // clearing an origin, then go ahead and schedule the op.
2771 nsresult rv
= RunSynchronizedOp(aStorage
, op
);
2772 NS_ENSURE_SUCCESS(rv
, rv
);
2778 QuotaManager::RunSynchronizedOp(nsIOfflineStorage
* aStorage
,
2779 SynchronizedOp
* aOp
)
2781 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2782 NS_ASSERTION(aOp
, "Null pointer!");
2783 NS_ASSERTION(aOp
->mListener
, "No listener on this op!");
2784 NS_ASSERTION(!aStorage
||
2785 aOp
->mStorages
[aStorage
->GetClient()->GetType()].IsEmpty(),
2786 "This op isn't ready to run!");
2788 ArrayCluster
<nsIOfflineStorage
*> storages
;
2790 uint32_t startIndex
;
2794 Client::Type clientType
= aStorage
->GetClient()->GetType();
2796 storages
[clientType
].AppendElement(aStorage
);
2798 startIndex
= clientType
;
2799 endIndex
= clientType
+ 1;
2802 aOp
->mStorages
.SwapElements(storages
);
2805 endIndex
= Client::TYPE_MAX
;
2808 nsRefPtr
<WaitForTransactionsToFinishRunnable
> runnable
=
2809 new WaitForTransactionsToFinishRunnable(aOp
);
2811 // Ask the file service to call us back when it's done with this storage.
2812 FileService
* service
= FileService::Get();
2815 // Have to copy here in case a transaction service needs a list too.
2816 nsTArray
<nsCOMPtr
<nsIOfflineStorage
>> array
;
2818 for (uint32_t index
= startIndex
; index
< endIndex
; index
++) {
2819 if (!storages
[index
].IsEmpty() &&
2820 mClients
[index
]->IsFileServiceUtilized()) {
2821 array
.AppendElements(storages
[index
]);
2825 if (!array
.IsEmpty()) {
2828 service
->WaitForStoragesToComplete(array
, runnable
);
2832 // Ask each transaction service to call us back when they're done with this
2834 for (uint32_t index
= startIndex
; index
< endIndex
; index
++) {
2835 nsRefPtr
<Client
>& client
= mClients
[index
];
2836 if (!storages
[index
].IsEmpty() && client
->IsTransactionServiceActivated()) {
2839 client
->WaitForStoragesToComplete(storages
[index
], runnable
);
2843 nsresult rv
= runnable
->Run();
2844 NS_ENSURE_SUCCESS(rv
, rv
);
2850 QuotaManager::FindSynchronizedOp(const nsACString
& aPattern
,
2851 Nullable
<PersistenceType
> aPersistenceType
,
2852 const nsACString
& aId
)
2854 for (uint32_t index
= 0; index
< mSynchronizedOps
.Length(); index
++) {
2855 const nsAutoPtr
<SynchronizedOp
>& currentOp
= mSynchronizedOps
[index
];
2856 if (PatternMatchesOrigin(aPattern
, currentOp
->mOriginOrPattern
) &&
2857 (currentOp
->mPersistenceType
.IsNull() ||
2858 currentOp
->mPersistenceType
== aPersistenceType
) &&
2859 (currentOp
->mId
.IsEmpty() || currentOp
->mId
== aId
)) {
2868 QuotaManager::ClearStoragesForApp(uint32_t aAppId
, bool aBrowserOnly
)
2870 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2871 NS_ASSERTION(aAppId
!= nsIScriptSecurityManager::UNKNOWN_APP_ID
,
2874 // This only works from the main process.
2875 NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE
);
2877 nsAutoCString pattern
;
2878 GetOriginPatternStringMaybeIgnoreBrowser(aAppId
, aBrowserOnly
, pattern
);
2880 // Clear both temporary and persistent storages.
2881 Nullable
<PersistenceType
> persistenceType
;
2883 // If there is a pending or running clear operation for this app, return
2885 if (IsClearOriginPending(pattern
, persistenceType
)) {
2889 OriginOrPatternString oops
= OriginOrPatternString::FromPattern(pattern
);
2891 // Queue up the origin clear runnable.
2892 nsRefPtr
<OriginClearRunnable
> runnable
=
2893 new OriginClearRunnable(oops
, persistenceType
);
2896 WaitForOpenAllowed(oops
, persistenceType
, EmptyCString(), runnable
);
2897 NS_ENSURE_SUCCESS(rv
, rv
);
2899 runnable
->AdvanceState();
2901 // Give the runnable some help by invalidating any storages in the way.
2902 StorageMatcher
<nsAutoTArray
<nsIOfflineStorage
*, 20> > matches
;
2903 matches
.Find(mLiveStorages
, pattern
);
2905 for (uint32_t index
= 0; index
< matches
.Length(); index
++) {
2906 // We need to grab references here to prevent the storage from dying while
2907 // we invalidate it.
2908 nsCOMPtr
<nsIOfflineStorage
> storage
= matches
[index
];
2909 storage
->Invalidate();
2917 QuotaManager::GetOriginsExceedingGroupLimit(const nsACString
& aKey
,
2918 GroupInfoPair
* aValue
,
2921 NS_ASSERTION(!aKey
.IsEmpty(), "Empty key!");
2922 NS_ASSERTION(aValue
, "Null pointer!");
2924 nsRefPtr
<GroupInfo
> groupInfo
=
2925 aValue
->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY
);
2927 QuotaManager
* quotaManager
= QuotaManager::Get();
2928 NS_ASSERTION(quotaManager
, "Shouldn't be null!");
2930 if (groupInfo
->mUsage
> quotaManager
->GetGroupLimit()) {
2931 nsTArray
<OriginInfo
*>* doomedOriginInfos
=
2932 static_cast<nsTArray
<OriginInfo
*>*>(aUserArg
);
2934 nsTArray
<nsRefPtr
<OriginInfo
> >& originInfos
= groupInfo
->mOriginInfos
;
2935 originInfos
.Sort(OriginInfoLRUComparator());
2937 uint64_t usage
= groupInfo
->mUsage
;
2938 for (uint32_t i
= 0; i
< originInfos
.Length(); i
++) {
2939 OriginInfo
* originInfo
= originInfos
[i
];
2941 doomedOriginInfos
->AppendElement(originInfo
);
2942 usage
-= originInfo
->mUsage
;
2944 if (usage
<= quotaManager
->GetGroupLimit()) {
2951 return PL_DHASH_NEXT
;
2956 QuotaManager::GetAllTemporaryStorageOrigins(const nsACString
& aKey
,
2957 GroupInfoPair
* aValue
,
2960 NS_ASSERTION(!aKey
.IsEmpty(), "Empty key!");
2961 NS_ASSERTION(aValue
, "Null pointer!");
2963 nsRefPtr
<GroupInfo
> groupInfo
=
2964 aValue
->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY
);
2966 nsTArray
<OriginInfo
*>* originInfos
=
2967 static_cast<nsTArray
<OriginInfo
*>*>(aUserArg
);
2969 originInfos
->AppendElements(groupInfo
->mOriginInfos
);
2972 return PL_DHASH_NEXT
;
2976 QuotaManager::CheckTemporaryStorageLimits()
2978 AssertIsOnIOThread();
2980 nsTArray
<OriginInfo
*> doomedOriginInfos
;
2982 MutexAutoLock
lock(mQuotaMutex
);
2984 mGroupInfoPairs
.EnumerateRead(GetOriginsExceedingGroupLimit
,
2985 &doomedOriginInfos
);
2988 for (uint32_t index
= 0; index
< doomedOriginInfos
.Length(); index
++) {
2989 usage
+= doomedOriginInfos
[index
]->mUsage
;
2992 if (mTemporaryStorageUsage
- usage
> mTemporaryStorageLimit
) {
2993 nsTArray
<OriginInfo
*> originInfos
;
2995 mGroupInfoPairs
.EnumerateRead(GetAllTemporaryStorageOrigins
,
2998 for (uint32_t index
= originInfos
.Length(); index
> 0; index
--) {
2999 if (doomedOriginInfos
.Contains(originInfos
[index
- 1])) {
3000 originInfos
.RemoveElementAt(index
- 1);
3004 originInfos
.Sort(OriginInfoLRUComparator());
3006 for (uint32_t i
= 0; i
< originInfos
.Length(); i
++) {
3007 if (mTemporaryStorageUsage
- usage
<= mTemporaryStorageLimit
) {
3008 originInfos
.TruncateLength(i
);
3012 usage
+= originInfos
[i
]->mUsage
;
3015 doomedOriginInfos
.AppendElements(originInfos
);
3019 for (uint32_t index
= 0; index
< doomedOriginInfos
.Length(); index
++) {
3020 DeleteTemporaryFilesForOrigin(doomedOriginInfos
[index
]->mOrigin
);
3023 nsTArray
<nsCString
> doomedOrigins
;
3025 MutexAutoLock
lock(mQuotaMutex
);
3027 for (uint32_t index
= 0; index
< doomedOriginInfos
.Length(); index
++) {
3028 OriginInfo
* doomedOriginInfo
= doomedOriginInfos
[index
];
3030 nsCString group
= doomedOriginInfo
->mGroupInfo
->mGroup
;
3031 nsCString origin
= doomedOriginInfo
->mOrigin
;
3032 LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY
, group
, origin
);
3035 doomedOriginInfos
[index
] = nullptr;
3038 doomedOrigins
.AppendElement(origin
);
3042 for (uint32_t index
= 0; index
< doomedOrigins
.Length(); index
++) {
3043 OriginClearCompleted(
3044 PERSISTENCE_TYPE_TEMPORARY
,
3045 OriginOrPatternString::FromOrigin(doomedOrigins
[index
]));
3051 QuotaManager::AddTemporaryStorageOrigins(
3052 const nsACString
& aKey
,
3053 ArrayCluster
<nsIOfflineStorage
*>* aValue
,
3056 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3057 NS_ASSERTION(!aKey
.IsEmpty(), "Empty key!");
3058 NS_ASSERTION(aValue
, "Null pointer!");
3059 NS_ASSERTION(aUserArg
, "Null pointer!");
3061 OriginCollection
& collection
= *static_cast<OriginCollection
*>(aUserArg
);
3063 if (collection
.ContainsOrigin(aKey
)) {
3064 return PL_DHASH_NEXT
;
3067 for (uint32_t i
= 0; i
< Client::TYPE_MAX
; i
++) {
3068 nsTArray
<nsIOfflineStorage
*>& array
= (*aValue
)[i
];
3069 for (uint32_t j
= 0; j
< array
.Length(); j
++) {
3070 nsIOfflineStorage
*& storage
= array
[j
];
3071 if (storage
->Type() == PERSISTENCE_TYPE_TEMPORARY
) {
3072 collection
.AddOrigin(aKey
);
3073 return PL_DHASH_NEXT
;
3078 return PL_DHASH_NEXT
;
3083 QuotaManager::GetInactiveTemporaryStorageOrigins(const nsACString
& aKey
,
3084 GroupInfoPair
* aValue
,
3087 NS_ASSERTION(!aKey
.IsEmpty(), "Empty key!");
3088 NS_ASSERTION(aValue
, "Null pointer!");
3089 NS_ASSERTION(aUserArg
, "Null pointer!");
3091 nsRefPtr
<GroupInfo
> groupInfo
=
3092 aValue
->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY
);
3094 InactiveOriginsInfo
* info
= static_cast<InactiveOriginsInfo
*>(aUserArg
);
3096 nsTArray
<nsRefPtr
<OriginInfo
> >& originInfos
= groupInfo
->mOriginInfos
;
3098 for (uint32_t i
= 0; i
< originInfos
.Length(); i
++) {
3099 OriginInfo
* originInfo
= originInfos
[i
];
3101 if (!info
->collection
.ContainsOrigin(originInfo
->mOrigin
)) {
3102 NS_ASSERTION(!originInfo
->mQuotaObjects
.Count(),
3103 "Inactive origin shouldn't have open files!");
3104 info
->origins
.AppendElement(originInfo
);
3109 return PL_DHASH_NEXT
;
3113 QuotaManager::CollectOriginsForEviction(uint64_t aMinSizeToBeFreed
,
3114 nsTArray
<OriginInfo
*>& aOriginInfos
)
3116 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3118 // Collect active origins first.
3119 OriginCollection originCollection
;
3121 // Add patterns and origins that have running or pending synchronized ops.
3122 // (add patterns first to reduce redundancy in the origin collection).
3124 for (index
= 0; index
< mSynchronizedOps
.Length(); index
++) {
3125 nsAutoPtr
<SynchronizedOp
>& op
= mSynchronizedOps
[index
];
3126 if (op
->mPersistenceType
.IsNull() ||
3127 op
->mPersistenceType
.Value() == PERSISTENCE_TYPE_TEMPORARY
) {
3128 if (op
->mOriginOrPattern
.IsPattern() &&
3129 !originCollection
.ContainsPattern(op
->mOriginOrPattern
)) {
3130 originCollection
.AddPattern(op
->mOriginOrPattern
);
3135 for (index
= 0; index
< mSynchronizedOps
.Length(); index
++) {
3136 nsAutoPtr
<SynchronizedOp
>& op
= mSynchronizedOps
[index
];
3137 if (op
->mPersistenceType
.IsNull() ||
3138 op
->mPersistenceType
.Value() == PERSISTENCE_TYPE_TEMPORARY
) {
3139 if (op
->mOriginOrPattern
.IsOrigin() &&
3140 !originCollection
.ContainsOrigin(op
->mOriginOrPattern
)) {
3141 originCollection
.AddOrigin(op
->mOriginOrPattern
);
3146 // Add origins that have live temporary storages.
3147 mLiveStorages
.EnumerateRead(AddTemporaryStorageOrigins
, &originCollection
);
3149 // Enumerate inactive origins. This must be protected by the mutex.
3150 nsTArray
<OriginInfo
*> inactiveOrigins
;
3152 InactiveOriginsInfo
info(originCollection
, inactiveOrigins
);
3153 MutexAutoLock
lock(mQuotaMutex
);
3154 mGroupInfoPairs
.EnumerateRead(GetInactiveTemporaryStorageOrigins
, &info
);
3157 // We now have a list of all inactive origins. So it's safe to sort the list
3158 // and calculate available size without holding the lock.
3160 // Sort by the origin access time.
3161 inactiveOrigins
.Sort(OriginInfoLRUComparator());
3163 // Create a list of inactive and the least recently used origins
3164 // whose aggregate size is greater or equals the minimal size to be freed.
3165 uint64_t sizeToBeFreed
= 0;
3166 for(index
= 0; index
< inactiveOrigins
.Length(); index
++) {
3167 if (sizeToBeFreed
>= aMinSizeToBeFreed
) {
3168 inactiveOrigins
.TruncateLength(index
);
3172 sizeToBeFreed
+= inactiveOrigins
[index
]->mUsage
;
3175 if (sizeToBeFreed
>= aMinSizeToBeFreed
) {
3176 // Success, add synchronized ops for these origins, so any other
3177 // operations for them will be delayed (until origin eviction is finalized).
3179 for(index
= 0; index
< inactiveOrigins
.Length(); index
++) {
3180 OriginOrPatternString oops
=
3181 OriginOrPatternString::FromOrigin(inactiveOrigins
[index
]->mOrigin
);
3183 AddSynchronizedOp(oops
,
3184 Nullable
<PersistenceType
>(PERSISTENCE_TYPE_TEMPORARY
));
3187 inactiveOrigins
.SwapElements(aOriginInfos
);
3188 return sizeToBeFreed
;
3195 QuotaManager::DeleteTemporaryFilesForOrigin(const nsACString
& aOrigin
)
3197 nsCOMPtr
<nsIFile
> directory
;
3198 nsresult rv
= GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY
, aOrigin
,
3199 getter_AddRefs(directory
));
3200 NS_ENSURE_SUCCESS_VOID(rv
);
3202 rv
= directory
->Remove(true);
3203 if (rv
!= NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
&&
3204 rv
!= NS_ERROR_FILE_NOT_FOUND
&& NS_FAILED(rv
)) {
3205 // This should never fail if we've closed all storage connections
3207 NS_ERROR("Failed to remove directory!");
3212 QuotaManager::FinalizeOriginEviction(nsTArray
<nsCString
>& aOrigins
)
3214 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3216 nsRefPtr
<FinalizeOriginEvictionRunnable
> runnable
=
3217 new FinalizeOriginEvictionRunnable(aOrigins
);
3219 nsresult rv
= IsOnIOThread() ? runnable
->RunImmediately()
3220 : runnable
->Dispatch();
3221 NS_ENSURE_SUCCESS_VOID(rv
);
3225 QuotaManager::SaveOriginAccessTime(const nsACString
& aOrigin
,
3228 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3230 if (QuotaManager::IsShuttingDown()) {
3234 nsRefPtr
<SaveOriginAccessTimeRunnable
> runnable
=
3235 new SaveOriginAccessTimeRunnable(aOrigin
, aTimestamp
);
3237 if (NS_FAILED(mIOThread
->Dispatch(runnable
, NS_DISPATCH_NORMAL
))) {
3238 NS_WARNING("Failed to dispatch runnable!");
3243 QuotaManager::GetOriginPatternString(uint32_t aAppId
,
3244 MozBrowserPatternFlag aBrowserFlag
,
3245 const nsACString
& aOrigin
,
3246 nsAutoCString
& _retval
)
3248 NS_ASSERTION(aAppId
!= nsIScriptSecurityManager::UNKNOWN_APP_ID
,
3250 NS_ASSERTION(aOrigin
.IsEmpty() || aBrowserFlag
!= IgnoreMozBrowser
,
3253 if (aOrigin
.IsEmpty()) {
3256 _retval
.AppendInt(aAppId
);
3257 _retval
.Append('+');
3259 if (aBrowserFlag
!= IgnoreMozBrowser
) {
3260 if (aBrowserFlag
== MozBrowser
) {
3261 _retval
.Append('t');
3264 _retval
.Append('f');
3266 _retval
.Append('+');
3273 if (aAppId
!= nsIScriptSecurityManager::NO_APP_ID
||
3274 aBrowserFlag
== MozBrowser
) {
3275 nsAutoCString pattern
;
3276 GetOriginPatternString(aAppId
, aBrowserFlag
, EmptyCString(), pattern
);
3277 NS_ASSERTION(PatternMatchesOrigin(pattern
, aOrigin
),
3278 "Origin doesn't match parameters!");
3285 SynchronizedOp::SynchronizedOp(const OriginOrPatternString
& aOriginOrPattern
,
3286 Nullable
<PersistenceType
> aPersistenceType
,
3287 const nsACString
& aId
)
3288 : mOriginOrPattern(aOriginOrPattern
), mPersistenceType(aPersistenceType
),
3291 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3292 MOZ_COUNT_CTOR(SynchronizedOp
);
3295 SynchronizedOp::~SynchronizedOp()
3297 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3298 MOZ_COUNT_DTOR(SynchronizedOp
);
3302 SynchronizedOp::MustWaitFor(const SynchronizedOp
& aExistingOp
)
3304 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3306 if (aExistingOp
.mOriginOrPattern
.IsNull() || mOriginOrPattern
.IsNull()) {
3312 if (aExistingOp
.mOriginOrPattern
.IsOrigin()) {
3313 if (mOriginOrPattern
.IsOrigin()) {
3314 match
= aExistingOp
.mOriginOrPattern
.Equals(mOriginOrPattern
);
3317 match
= PatternMatchesOrigin(mOriginOrPattern
, aExistingOp
.mOriginOrPattern
);
3320 else if (mOriginOrPattern
.IsOrigin()) {
3321 match
= PatternMatchesOrigin(aExistingOp
.mOriginOrPattern
, mOriginOrPattern
);
3324 match
= PatternMatchesOrigin(mOriginOrPattern
, aExistingOp
.mOriginOrPattern
) ||
3325 PatternMatchesOrigin(aExistingOp
.mOriginOrPattern
, mOriginOrPattern
);
3328 // If the origins don't match, the second can proceed.
3333 // If the origins match but the persistence types are different, the second
3335 if (!aExistingOp
.mPersistenceType
.IsNull() && !mPersistenceType
.IsNull() &&
3336 aExistingOp
.mPersistenceType
.Value() != mPersistenceType
.Value()) {
3340 // If the origins and the ids match, the second must wait.
3341 if (aExistingOp
.mId
== mId
) {
3345 // Waiting is required if either one corresponds to an origin clearing
3347 if (aExistingOp
.mId
.IsEmpty() || mId
.IsEmpty()) {
3351 // Otherwise, things for the same origin but different storages can proceed
3357 SynchronizedOp::DelayRunnable(nsIRunnable
* aRunnable
)
3359 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3360 NS_ASSERTION(mDelayedRunnables
.IsEmpty() || mId
.IsEmpty(),
3361 "Only ClearOrigin operations can delay multiple runnables!");
3363 mDelayedRunnables
.AppendElement(aRunnable
);
3367 SynchronizedOp::DispatchDelayedRunnables()
3369 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3370 NS_ASSERTION(!mListener
, "Any listener should be gone by now!");
3372 uint32_t count
= mDelayedRunnables
.Length();
3373 for (uint32_t index
= 0; index
< count
; index
++) {
3374 NS_DispatchToCurrentThread(mDelayedRunnables
[index
]);
3377 mDelayedRunnables
.Clear();
3380 CollectOriginsHelper::CollectOriginsHelper(mozilla::Mutex
& aMutex
,
3381 uint64_t aMinSizeToBeFreed
)
3382 : mMinSizeToBeFreed(aMinSizeToBeFreed
),
3384 mCondVar(aMutex
, "CollectOriginsHelper::mCondVar"),
3388 MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
3389 mMutex
.AssertCurrentThreadOwns();
3393 CollectOriginsHelper::BlockAndReturnOriginsForEviction(
3394 nsTArray
<OriginInfo
*>& aOriginInfos
)
3396 MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
3397 mMutex
.AssertCurrentThreadOwns();
3403 mOriginInfos
.SwapElements(aOriginInfos
);
3404 return mSizeToBeFreed
;
3408 CollectOriginsHelper::Run()
3410 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
3412 QuotaManager
* quotaManager
= QuotaManager::Get();
3413 NS_ASSERTION(quotaManager
, "Shouldn't be null!");
3415 // We use extra stack vars here to avoid race detector warnings (the same
3416 // memory accessed with and without the lock held).
3417 nsTArray
<OriginInfo
*> originInfos
;
3418 uint64_t sizeToBeFreed
=
3419 quotaManager
->CollectOriginsForEviction(mMinSizeToBeFreed
, originInfos
);
3421 MutexAutoLock
lock(mMutex
);
3423 NS_ASSERTION(mWaiting
, "Huh?!");
3425 mOriginInfos
.SwapElements(originInfos
);
3426 mSizeToBeFreed
= sizeToBeFreed
;
3434 OriginClearRunnable::OnExclusiveAccessAcquired()
3436 QuotaManager
* quotaManager
= QuotaManager::Get();
3437 NS_ASSERTION(quotaManager
, "This should never fail!");
3439 nsresult rv
= quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
);
3440 NS_ENSURE_SUCCESS(rv
, rv
);
3447 OriginClearRunnable::InvalidateOpenedStorages(
3448 nsTArray
<nsCOMPtr
<nsIOfflineStorage
> >& aStorages
,
3451 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3453 nsTArray
<nsCOMPtr
<nsIOfflineStorage
> > storages
;
3454 storages
.SwapElements(aStorages
);
3456 for (uint32_t index
= 0; index
< storages
.Length(); index
++) {
3457 storages
[index
]->Invalidate();
3462 OriginClearRunnable::DeleteFiles(QuotaManager
* aQuotaManager
,
3463 PersistenceType aPersistenceType
)
3465 AssertIsOnIOThread();
3466 NS_ASSERTION(aQuotaManager
, "Don't pass me null!");
3470 nsCOMPtr
<nsIFile
> directory
=
3471 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
);
3472 NS_ENSURE_SUCCESS_VOID(rv
);
3474 rv
= directory
->InitWithPath(aQuotaManager
->GetStoragePath(aPersistenceType
));
3475 NS_ENSURE_SUCCESS_VOID(rv
);
3477 nsCOMPtr
<nsISimpleEnumerator
> entries
;
3478 if (NS_FAILED(directory
->GetDirectoryEntries(getter_AddRefs(entries
))) ||
3483 nsCString
originSanitized(mOriginOrPattern
);
3484 SanitizeOriginString(originSanitized
);
3487 while (NS_SUCCEEDED((rv
= entries
->HasMoreElements(&hasMore
))) && hasMore
) {
3488 nsCOMPtr
<nsISupports
> entry
;
3489 rv
= entries
->GetNext(getter_AddRefs(entry
));
3490 NS_ENSURE_SUCCESS_VOID(rv
);
3492 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(entry
);
3493 NS_ASSERTION(file
, "Don't know what this is!");
3496 rv
= file
->IsDirectory(&isDirectory
);
3497 NS_ENSURE_SUCCESS_VOID(rv
);
3500 NS_WARNING("Something in the IndexedDB directory that doesn't belong!");
3505 rv
= file
->GetLeafName(leafName
);
3506 NS_ENSURE_SUCCESS_VOID(rv
);
3508 // Skip storages for other apps.
3509 if (!PatternMatchesOrigin(originSanitized
,
3510 NS_ConvertUTF16toUTF8(leafName
))) {
3514 if (NS_FAILED(file
->Remove(true))) {
3515 // This should never fail if we've closed all storage connections
3517 NS_ERROR("Failed to remove directory!");
3521 aQuotaManager
->RemoveQuotaForPattern(aPersistenceType
, mOriginOrPattern
);
3523 aQuotaManager
->OriginClearCompleted(aPersistenceType
, mOriginOrPattern
);
3526 NS_IMPL_ISUPPORTS_INHERITED0(OriginClearRunnable
, nsRunnable
)
3529 OriginClearRunnable::Run()
3531 PROFILER_LABEL("OriginClearRunnable", "Run",
3532 js::ProfileEntry::Category::OTHER
);
3534 QuotaManager
* quotaManager
= QuotaManager::Get();
3535 NS_ASSERTION(quotaManager
, "This should never fail!");
3537 switch (mCallbackState
) {
3539 NS_NOTREACHED("Should never get here without being dispatched!");
3540 return NS_ERROR_UNEXPECTED
;
3544 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3548 // Now we have to wait until the thread pool is done with all of the
3549 // storages we care about.
3551 quotaManager
->AcquireExclusiveAccess(mOriginOrPattern
, mPersistenceType
,
3552 this, InvalidateOpenedStorages
,
3554 NS_ENSURE_SUCCESS(rv
, rv
);
3560 AssertIsOnIOThread();
3564 if (mPersistenceType
.IsNull()) {
3565 DeleteFiles(quotaManager
, PERSISTENCE_TYPE_PERSISTENT
);
3566 DeleteFiles(quotaManager
, PERSISTENCE_TYPE_TEMPORARY
);
3568 DeleteFiles(quotaManager
, mPersistenceType
.Value());
3571 // Now dispatch back to the main thread.
3572 if (NS_FAILED(NS_DispatchToMainThread(this))) {
3573 NS_WARNING("Failed to dispatch to main thread!");
3574 return NS_ERROR_FAILURE
;
3581 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3583 // Tell the QuotaManager that we're done.
3584 quotaManager
->AllowNextSynchronizedOp(mOriginOrPattern
, mPersistenceType
,
3591 NS_ERROR("Unknown state value!");
3592 return NS_ERROR_UNEXPECTED
;
3595 NS_NOTREACHED("Should never get here!");
3596 return NS_ERROR_UNEXPECTED
;
3599 AsyncUsageRunnable::AsyncUsageRunnable(uint32_t aAppId
,
3600 bool aInMozBrowserOnly
,
3601 const nsACString
& aGroup
,
3602 const OriginOrPatternString
& aOrigin
,
3604 nsIUsageCallback
* aCallback
)
3606 mCallback(aCallback
),
3610 mCallbackState(Pending
),
3611 mInMozBrowserOnly(aInMozBrowserOnly
)
3613 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3614 NS_ASSERTION(aURI
, "Null pointer!");
3615 NS_ASSERTION(!aGroup
.IsEmpty(), "Empty group!");
3616 NS_ASSERTION(aOrigin
.IsOrigin(), "Expect origin only here!");
3617 NS_ASSERTION(!aOrigin
.IsEmpty(), "Empty origin!");
3618 NS_ASSERTION(aCallback
, "Null pointer!");
3622 AsyncUsageRunnable::TakeShortcut()
3624 NS_ASSERTION(mCallbackState
== Pending
, "Huh?");
3626 nsresult rv
= NS_DispatchToCurrentThread(this);
3627 NS_ENSURE_SUCCESS(rv
, rv
);
3629 mCallbackState
= Shortcut
;
3634 AsyncUsageRunnable::RunInternal()
3636 QuotaManager
* quotaManager
= QuotaManager::Get();
3637 NS_ASSERTION(quotaManager
, "This should never fail!");
3641 switch (mCallbackState
) {
3643 NS_NOTREACHED("Should never get here without being dispatched!");
3644 return NS_ERROR_UNEXPECTED
;
3648 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3652 rv
= quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
);
3653 if (NS_FAILED(rv
)) {
3654 NS_WARNING("Failed to dispatch to the IO thread!");
3661 AssertIsOnIOThread();
3665 // Add all the persistent storage files we care about.
3666 rv
= AddToUsage(quotaManager
, PERSISTENCE_TYPE_PERSISTENT
);
3667 NS_ENSURE_SUCCESS(rv
, rv
);
3669 // Add all the temporary storage files we care about.
3670 rv
= AddToUsage(quotaManager
, PERSISTENCE_TYPE_TEMPORARY
);
3671 NS_ENSURE_SUCCESS(rv
, rv
);
3673 // Run dispatches us back to the main thread.
3677 case Complete
: // Fall through
3679 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3681 // Call the callback unless we were canceled.
3683 mCallback
->OnUsageResult(mURI
, TotalUsage(), FileUsage(), mAppId
,
3689 mCallback
= nullptr;
3691 // And tell the QuotaManager that we're done.
3692 if (mCallbackState
== Complete
) {
3693 quotaManager
->AllowNextSynchronizedOp(mOrigin
,
3694 Nullable
<PersistenceType
>(),
3702 NS_ERROR("Unknown state value!");
3703 return NS_ERROR_UNEXPECTED
;
3706 NS_NOTREACHED("Should never get here!");
3707 return NS_ERROR_UNEXPECTED
;
3711 AsyncUsageRunnable::AddToUsage(QuotaManager
* aQuotaManager
,
3712 PersistenceType aPersistenceType
)
3714 AssertIsOnIOThread();
3716 nsCOMPtr
<nsIFile
> directory
;
3717 nsresult rv
= aQuotaManager
->GetDirectoryForOrigin(aPersistenceType
, mOrigin
,
3718 getter_AddRefs(directory
));
3719 NS_ENSURE_SUCCESS(rv
, rv
);
3722 rv
= directory
->Exists(&exists
);
3723 NS_ENSURE_SUCCESS(rv
, rv
);
3725 // If the directory exists then enumerate all the files inside, adding up
3726 // the sizes to get the final usage statistic.
3727 if (exists
&& !mCanceled
) {
3730 if (aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
3731 initialized
= aQuotaManager
->mInitializedOrigins
.Contains(mOrigin
);
3734 rv
= MaybeUpgradeOriginDirectory(directory
);
3735 NS_ENSURE_SUCCESS(rv
, rv
);
3739 NS_ASSERTION(aPersistenceType
== PERSISTENCE_TYPE_TEMPORARY
, "Huh?");
3740 initialized
= aQuotaManager
->mTemporaryStorageInitialized
;
3743 nsCOMPtr
<nsISimpleEnumerator
> entries
;
3744 rv
= directory
->GetDirectoryEntries(getter_AddRefs(entries
));
3745 NS_ENSURE_SUCCESS(rv
, rv
);
3748 while (NS_SUCCEEDED((rv
= entries
->HasMoreElements(&hasMore
))) &&
3749 hasMore
&& !mCanceled
) {
3750 nsCOMPtr
<nsISupports
> entry
;
3751 rv
= entries
->GetNext(getter_AddRefs(entry
));
3752 NS_ENSURE_SUCCESS(rv
, rv
);
3754 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(entry
);
3755 NS_ENSURE_TRUE(file
, NS_NOINTERFACE
);
3758 rv
= file
->GetLeafName(leafName
);
3759 NS_ENSURE_SUCCESS(rv
, rv
);
3761 if (leafName
.EqualsLiteral(METADATA_FILE_NAME
) ||
3762 leafName
.EqualsLiteral(DSSTORE_FILE_NAME
)) {
3768 rv
= file
->IsDirectory(&isDirectory
);
3769 NS_ENSURE_SUCCESS(rv
, rv
);
3772 NS_WARNING("Unknown file found!");
3773 return NS_ERROR_UNEXPECTED
;
3777 Client::Type clientType
;
3778 rv
= Client::TypeFromText(leafName
, clientType
);
3779 if (NS_FAILED(rv
)) {
3780 NS_WARNING("Unknown directory found!");
3782 return NS_ERROR_UNEXPECTED
;
3787 nsRefPtr
<Client
>& client
= aQuotaManager
->mClients
[clientType
];
3790 rv
= client
->GetUsageForOrigin(aPersistenceType
, mGroup
, mOrigin
, this);
3793 rv
= client
->InitOrigin(aPersistenceType
, mGroup
, mOrigin
, this);
3795 NS_ENSURE_SUCCESS(rv
, rv
);
3802 NS_IMPL_ISUPPORTS_INHERITED(AsyncUsageRunnable
, nsRunnable
, nsIQuotaRequest
)
3805 AsyncUsageRunnable::Run()
3807 PROFILER_LABEL("Quota", "AsyncUsageRunnable::Run",
3808 js::ProfileEntry::Category::OTHER
);
3810 nsresult rv
= RunInternal();
3812 if (!NS_IsMainThread()) {
3813 if (NS_FAILED(rv
)) {
3817 if (NS_FAILED(NS_DispatchToMainThread(this))) {
3818 NS_WARNING("Failed to dispatch to main thread!");
3826 AsyncUsageRunnable::Cancel()
3828 if (mCanceled
.exchange(true)) {
3829 NS_WARNING("Canceled more than once?!");
3830 return NS_ERROR_UNEXPECTED
;
3837 ResetOrClearRunnable::OnExclusiveAccessAcquired()
3839 QuotaManager
* quotaManager
= QuotaManager::Get();
3840 NS_ASSERTION(quotaManager
, "This should never fail!");
3842 nsresult rv
= quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
);
3843 NS_ENSURE_SUCCESS(rv
, rv
);
3850 ResetOrClearRunnable::InvalidateOpenedStorages(
3851 nsTArray
<nsCOMPtr
<nsIOfflineStorage
> >& aStorages
,
3854 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3856 nsTArray
<nsCOMPtr
<nsIOfflineStorage
> > storages
;
3857 storages
.SwapElements(aStorages
);
3859 for (uint32_t index
= 0; index
< storages
.Length(); index
++) {
3860 storages
[index
]->Invalidate();
3865 ResetOrClearRunnable::DeleteFiles(QuotaManager
* aQuotaManager
,
3866 PersistenceType aPersistenceType
)
3868 AssertIsOnIOThread();
3869 NS_ASSERTION(aQuotaManager
, "Don't pass me null!");
3873 nsCOMPtr
<nsIFile
> directory
=
3874 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
);
3875 NS_ENSURE_SUCCESS_VOID(rv
);
3877 rv
= directory
->InitWithPath(aQuotaManager
->GetStoragePath(aPersistenceType
));
3878 NS_ENSURE_SUCCESS_VOID(rv
);
3880 rv
= directory
->Remove(true);
3881 if (rv
!= NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
&&
3882 rv
!= NS_ERROR_FILE_NOT_FOUND
&& NS_FAILED(rv
)) {
3883 // This should never fail if we've closed all storage connections
3885 NS_ERROR("Failed to remove directory!");
3889 NS_IMPL_ISUPPORTS_INHERITED0(ResetOrClearRunnable
, nsRunnable
)
3892 ResetOrClearRunnable::Run()
3894 QuotaManager
* quotaManager
= QuotaManager::Get();
3895 NS_ASSERTION(quotaManager
, "This should never fail!");
3897 switch (mCallbackState
) {
3899 NS_NOTREACHED("Should never get here without being dispatched!");
3900 return NS_ERROR_UNEXPECTED
;
3904 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3908 // Now we have to wait until the thread pool is done with all of the
3909 // storages we care about.
3911 quotaManager
->AcquireExclusiveAccess(NullCString(),
3912 Nullable
<PersistenceType
>(), this,
3913 InvalidateOpenedStorages
, nullptr);
3914 NS_ENSURE_SUCCESS(rv
, rv
);
3920 AssertIsOnIOThread();
3925 DeleteFiles(quotaManager
, PERSISTENCE_TYPE_PERSISTENT
);
3926 DeleteFiles(quotaManager
, PERSISTENCE_TYPE_TEMPORARY
);
3929 quotaManager
->RemoveQuota();
3930 quotaManager
->ResetOrClearCompleted();
3932 // Now dispatch back to the main thread.
3933 if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL
))) {
3934 NS_WARNING("Failed to dispatch to main thread!");
3935 return NS_ERROR_FAILURE
;
3942 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3944 // Tell the QuotaManager that we're done.
3945 quotaManager
->AllowNextSynchronizedOp(OriginOrPatternString::FromNull(),
3946 Nullable
<PersistenceType
>(),
3953 NS_ERROR("Unknown state value!");
3954 return NS_ERROR_UNEXPECTED
;
3957 NS_NOTREACHED("Should never get here!");
3958 return NS_ERROR_UNEXPECTED
;
3962 FinalizeOriginEvictionRunnable::Run()
3964 QuotaManager
* quotaManager
= QuotaManager::Get();
3965 NS_ASSERTION(quotaManager
, "This should never fail!");
3969 switch (mCallbackState
) {
3971 NS_NOTREACHED("Should never get here without being dispatched!");
3972 return NS_ERROR_UNEXPECTED
;
3976 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3980 rv
= quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
);
3981 if (NS_FAILED(rv
)) {
3982 NS_WARNING("Failed to dispatch to the IO thread!");
3989 AssertIsOnIOThread();
3993 for (uint32_t index
= 0; index
< mOrigins
.Length(); index
++) {
3994 quotaManager
->OriginClearCompleted(
3995 PERSISTENCE_TYPE_TEMPORARY
,
3996 OriginOrPatternString::FromOrigin(mOrigins
[index
]));
3999 if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL
))) {
4000 NS_WARNING("Failed to dispatch to main thread!");
4001 return NS_ERROR_FAILURE
;
4008 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4010 for (uint32_t index
= 0; index
< mOrigins
.Length(); index
++) {
4011 quotaManager
->AllowNextSynchronizedOp(
4012 OriginOrPatternString::FromOrigin(mOrigins
[index
]),
4013 Nullable
<PersistenceType
>(PERSISTENCE_TYPE_TEMPORARY
),
4021 NS_ERROR("Unknown state value!");
4022 return NS_ERROR_UNEXPECTED
;
4025 NS_NOTREACHED("Should never get here!");
4026 return NS_ERROR_UNEXPECTED
;
4030 FinalizeOriginEvictionRunnable::Dispatch()
4032 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
4033 NS_ASSERTION(mCallbackState
== Pending
, "Huh?");
4035 mCallbackState
= OpenAllowed
;
4036 return NS_DispatchToMainThread(this);
4040 FinalizeOriginEvictionRunnable::RunImmediately()
4042 AssertIsOnIOThread();
4043 NS_ASSERTION(mCallbackState
== Pending
, "Huh?");
4045 mCallbackState
= IO
;
4050 WaitForTransactionsToFinishRunnable::Run()
4052 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4053 NS_ASSERTION(mOp
, "Null op!");
4054 NS_ASSERTION(mOp
->mListener
, "Nothing to run!");
4055 NS_ASSERTION(mCountdown
, "Wrong countdown!");
4061 // Don't hold the listener alive longer than necessary.
4062 nsRefPtr
<AcquireListener
> listener
;
4063 listener
.swap(mOp
->mListener
);
4067 nsresult rv
= listener
->OnExclusiveAccessAcquired();
4068 NS_ENSURE_SUCCESS(rv
, rv
);
4070 // The listener is responsible for calling
4071 // QuotaManager::AllowNextSynchronizedOp.
4076 WaitForFileHandlesToFinishRunnable::Run()
4078 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4086 SaveOriginAccessTimeRunnable::Run()
4088 AssertIsOnIOThread();
4090 QuotaManager
* quotaManager
= QuotaManager::Get();
4091 NS_ASSERTION(quotaManager
, "This should never fail!");
4093 nsCOMPtr
<nsIFile
> directory
;
4095 quotaManager
->GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY
, mOrigin
,
4096 getter_AddRefs(directory
));
4097 NS_ENSURE_SUCCESS(rv
, rv
);
4099 nsCOMPtr
<nsIBinaryOutputStream
> stream
;
4100 rv
= GetDirectoryMetadataStream(directory
, true, getter_AddRefs(stream
));
4101 NS_ENSURE_SUCCESS(rv
, rv
);
4103 // The origin directory may not exist anymore.
4105 rv
= stream
->Write64(mTimestamp
);
4106 NS_ENSURE_SUCCESS(rv
, rv
);