1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=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 "ActorsParent.h"
9 #include "mozIStorageConnection.h"
10 #include "mozIStorageService.h"
11 #include "nsIObjectInputStream.h"
12 #include "nsIObjectOutputStream.h"
14 #include "nsIFileStreams.h"
15 #include "nsIObserverService.h"
16 #include "nsIPermissionManager.h"
17 #include "nsIPrincipal.h"
18 #include "nsIRunnable.h"
19 #include "nsISimpleEnumerator.h"
20 #include "nsIScriptObjectPrincipal.h"
21 #include "nsIScriptSecurityManager.h"
22 #include "nsISupportsPrimitives.h"
25 #include "nsPIDOMWindow.h"
28 #include "GeckoProfiler.h"
29 #include "mozilla/Atomics.h"
30 #include "mozilla/BasePrincipal.h"
31 #include "mozilla/CondVar.h"
32 #include "mozilla/Telemetry.h"
33 #include "mozilla/dom/PContent.h"
34 #include "mozilla/dom/asmjscache/AsmJSCache.h"
35 #include "mozilla/dom/cache/QuotaClient.h"
36 #include "mozilla/dom/indexedDB/ActorsParent.h"
37 #include "mozilla/dom/localstorage/ActorsParent.h"
38 #include "mozilla/dom/quota/PQuotaParent.h"
39 #include "mozilla/dom/quota/PQuotaRequestParent.h"
40 #include "mozilla/dom/quota/PQuotaUsageRequestParent.h"
41 #include "mozilla/dom/simpledb/ActorsParent.h"
42 #include "mozilla/dom/StorageActivityService.h"
43 #include "mozilla/dom/StorageDBUpdater.h"
44 #include "mozilla/ipc/BackgroundParent.h"
45 #include "mozilla/ipc/BackgroundUtils.h"
46 #include "mozilla/IntegerRange.h"
47 #include "mozilla/Mutex.h"
48 #include "mozilla/Preferences.h"
49 #include "mozilla/Services.h"
50 #include "mozilla/StaticPtr.h"
51 #include "mozilla/TextUtils.h"
52 #include "mozilla/Telemetry.h"
53 #include "mozilla/TypeTraits.h"
54 #include "mozilla/Unused.h"
55 #include "mozStorageCID.h"
56 #include "mozStorageHelper.h"
57 #include "nsAppDirectoryServiceDefs.h"
58 #include "nsComponentManagerUtils.h"
59 #include "nsAboutProtocolUtils.h"
60 #include "nsCharSeparatedTokenizer.h"
61 #include "nsContentUtils.h"
62 #include "nsCRTGlue.h"
63 #include "nsDirectoryServiceUtils.h"
65 #include "nsNetUtil.h"
66 #include "nsPrintfCString.h"
67 #include "nsScriptSecurityManager.h"
68 #include "nsThreadUtils.h"
69 #include "nsXULAppAPI.h"
71 #include "xpcpublic.h"
73 #include "OriginScope.h"
74 #include "QuotaManager.h"
75 #include "QuotaManagerService.h"
76 #include "QuotaObject.h"
77 #include "UsageInfo.h"
79 #define DISABLE_ASSERTS_FOR_FUZZING 0
81 #if DISABLE_ASSERTS_FOR_FUZZING
82 # define ASSERT_UNLESS_FUZZING(...) \
86 # define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
89 #define UNKNOWN_FILE_WARNING(_leafName) \
90 QM_WARNING("Something (%s) in the directory that doesn't belong!", \
91 NS_ConvertUTF16toUTF8(_leafName).get())
93 // The amount of time, in milliseconds, that our IO thread will stay alive
94 // after the last event it processes.
95 #define DEFAULT_THREAD_TIMEOUT_MS 30000
97 // The amount of time, in milliseconds, that we will wait for active storage
98 // transactions on shutdown before aborting them.
99 #define DEFAULT_SHUTDOWN_TIMER_MS 30000
101 // Preference that users can set to override temporary storage smart limit
103 #define PREF_FIXED_LIMIT "dom.quotaManager.temporaryStorage.fixedLimit"
104 #define PREF_CHUNK_SIZE "dom.quotaManager.temporaryStorage.chunkSize"
106 // Preference that is used to enable testing features
107 #define PREF_TESTING_FEATURES "dom.quotaManager.testing"
109 // profile-before-change, when we need to shut down quota manager
110 #define PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID "profile-before-change-qm"
113 #define MB *1024ULL KB
114 #define GB *1024ULL MB
120 using namespace mozilla::ipc
;
122 // We want profiles to be platform-independent so we always need to replace
123 // the same characters on every platform. Windows has the most extensive set
124 // of illegal characters so we use its FILE_ILLEGAL_CHARACTERS and
125 // FILE_PATH_SEPARATOR.
126 const char QuotaManager::kReplaceChars
[] = CONTROL_CHARACTERS
"/:*?\"<>|\\";
130 /*******************************************************************************
132 ******************************************************************************/
134 const uint32_t kSQLitePageSizeOverride
= 512;
136 const uint32_t kHackyDowngradeMajorStorageVersion
= 2;
137 const uint32_t kHackyDowngradeMinorStorageVersion
= 1;
139 // Important version history:
140 // - Bug 1290481 bumped our schema from major.minor 2.0 to 3.0 in Firefox 57
141 // which caused Firefox 57 release concerns because the major schema upgrade
142 // means anyone downgrading to Firefox 56 will experience a non-operational
143 // QuotaManager and all of its clients.
144 // - Bug 1404344 got very concerned about that and so we decided to effectively
145 // rename 3.0 to 2.1, effective in Firefox 57. This works because post
146 // storage.sqlite v1.0, QuotaManager doesn't care about minor storage version
147 // increases. It also works because all the upgrade did was give the DOM
148 // Cache API QuotaClient an opportunity to create its newly added .padding
149 // files during initialization/upgrade, which isn't functionally necessary as
150 // that can be done on demand.
152 // Major storage version. Bump for backwards-incompatible changes.
153 // (The next major version should be 4 to distinguish from the Bug 1290481
155 const uint32_t kMajorStorageVersion
= 2;
157 // Minor storage version. Bump for backwards-compatible changes.
158 const uint32_t kMinorStorageVersion
= 1;
160 // The storage version we store in the SQLite database is a (signed) 32-bit
161 // integer. The major version is left-shifted 16 bits so the max value is
162 // 0xFFFF. The minor version occupies the lower 16 bits and its max is 0xFFFF.
163 static_assert(kMajorStorageVersion
<= 0xFFFF,
164 "Major version needs to fit in 16 bits.");
165 static_assert(kMinorStorageVersion
<= 0xFFFF,
166 "Minor version needs to fit in 16 bits.");
168 const int32_t kStorageVersion
=
169 int32_t((kMajorStorageVersion
<< 16) + kMinorStorageVersion
);
171 // See comments above about why these are a thing.
172 const int32_t kHackyPreDowngradeStorageVersion
= int32_t((3 << 16) + 0);
173 const int32_t kHackyPostDowngradeStorageVersion
= int32_t((2 << 16) + 1);
175 static_assert(static_cast<uint32_t>(StorageType::Persistent
) ==
176 static_cast<uint32_t>(PERSISTENCE_TYPE_PERSISTENT
),
177 "Enum values should match.");
179 static_assert(static_cast<uint32_t>(StorageType::Temporary
) ==
180 static_cast<uint32_t>(PERSISTENCE_TYPE_TEMPORARY
),
181 "Enum values should match.");
183 static_assert(static_cast<uint32_t>(StorageType::Default
) ==
184 static_cast<uint32_t>(PERSISTENCE_TYPE_DEFAULT
),
185 "Enum values should match.");
187 const char kChromeOrigin
[] = "chrome";
188 const char kAboutHomeOriginPrefix
[] = "moz-safe-about:home";
189 const char kIndexedDBOriginPrefix
[] = "indexeddb://";
190 const char kResourceOriginPrefix
[] = "resource://";
192 #define INDEXEDDB_DIRECTORY_NAME "indexedDB"
193 #define STORAGE_DIRECTORY_NAME "storage"
194 #define PERSISTENT_DIRECTORY_NAME "persistent"
195 #define PERMANENT_DIRECTORY_NAME "permanent"
196 #define TEMPORARY_DIRECTORY_NAME "temporary"
197 #define DEFAULT_DIRECTORY_NAME "default"
200 kNoAppId
= nsIScriptSecurityManager::NO_APP_ID
,
201 kUnknownAppId
= nsIScriptSecurityManager::UNKNOWN_APP_ID
204 #define STORAGE_FILE_NAME "storage.sqlite"
206 // The name of the file that we use to load/save the last access time of an
208 // XXX We should get rid of old metadata files at some point, bug 1343576.
209 #define METADATA_FILE_NAME ".metadata"
210 #define METADATA_TMP_FILE_NAME ".metadata-tmp"
211 #define METADATA_V2_FILE_NAME ".metadata-v2"
212 #define METADATA_V2_TMP_FILE_NAME ".metadata-v2-tmp"
214 #define WEB_APPS_STORE_FILE_NAME "webappsstore.sqlite"
215 #define LS_ARCHIVE_FILE_NAME "ls-archive.sqlite"
216 #define LS_ARCHIVE_TMP_FILE_NAME "ls-archive-tmp.sqlite"
218 /******************************************************************************
220 ******************************************************************************/
222 int32_t MakeStorageVersion(uint32_t aMajorStorageVersion
,
223 uint32_t aMinorStorageVersion
) {
224 return int32_t((aMajorStorageVersion
<< 16) + aMinorStorageVersion
);
227 uint32_t GetMajorStorageVersion(int32_t aStorageVersion
) {
228 return uint32_t(aStorageVersion
>> 16);
231 nsresult
CreateTables(mozIStorageConnection
* aConnection
) {
232 AssertIsOnIOThread();
233 MOZ_ASSERT(aConnection
);
235 // The database doesn't have any tables for now. It's only used for storage
237 // However, this is the place where any future tables should be created.
243 int32_t storageVersion
;
244 rv
= aConnection
->GetSchemaVersion(&storageVersion
);
245 if (NS_WARN_IF(NS_FAILED(rv
))) {
249 MOZ_ASSERT(storageVersion
== 0);
253 rv
= aConnection
->SetSchemaVersion(kStorageVersion
);
254 if (NS_WARN_IF(NS_FAILED(rv
))) {
261 nsresult
CreateWebAppsStoreConnection(nsIFile
* aWebAppsStoreFile
,
262 mozIStorageService
* aStorageService
,
263 mozIStorageConnection
** aConnection
) {
264 AssertIsOnIOThread();
265 MOZ_ASSERT(aWebAppsStoreFile
);
266 MOZ_ASSERT(aStorageService
);
267 MOZ_ASSERT(aConnection
);
269 // Check if the old database exists at all.
271 nsresult rv
= aWebAppsStoreFile
->Exists(&exists
);
272 if (NS_WARN_IF(NS_FAILED(rv
))) {
277 // webappsstore.sqlite doesn't exist, return a null connection.
278 *aConnection
= nullptr;
283 rv
= aWebAppsStoreFile
->IsDirectory(&isDirectory
);
284 if (NS_WARN_IF(NS_FAILED(rv
))) {
289 QM_WARNING("webappsstore.sqlite is not a file!");
290 *aConnection
= nullptr;
294 nsCOMPtr
<mozIStorageConnection
> connection
;
295 rv
= aStorageService
->OpenUnsharedDatabase(aWebAppsStoreFile
,
296 getter_AddRefs(connection
));
297 if (rv
== NS_ERROR_FILE_CORRUPTED
) {
298 // Don't throw an error, leave a corrupted webappsstore database as it is.
299 *aConnection
= nullptr;
302 if (NS_WARN_IF(NS_FAILED(rv
))) {
306 rv
= StorageDBUpdater::Update(connection
);
308 // Don't throw an error, leave a non-updateable webappsstore database as
310 *aConnection
= nullptr;
314 connection
.forget(aConnection
);
318 /******************************************************************************
319 * Quota manager class declarations
320 ******************************************************************************/
324 class DirectoryLockImpl final
: public DirectoryLock
{
325 RefPtr
<QuotaManager
> mQuotaManager
;
327 const Nullable
<PersistenceType
> mPersistenceType
;
328 const nsCString mGroup
;
329 const OriginScope mOriginScope
;
330 const Nullable
<Client::Type
> mClientType
;
331 RefPtr
<OpenDirectoryListener
> mOpenListener
;
333 nsTArray
<DirectoryLockImpl
*> mBlocking
;
334 nsTArray
<DirectoryLockImpl
*> mBlockedOn
;
336 const bool mExclusive
;
338 // Internal quota manager operations use this flag to prevent directory lock
339 // registraction/unregistration from updating origin access time, etc.
340 const bool mInternal
;
345 DirectoryLockImpl(QuotaManager
* aQuotaManager
,
346 const Nullable
<PersistenceType
>& aPersistenceType
,
347 const nsACString
& aGroup
, const OriginScope
& aOriginScope
,
348 const Nullable
<Client::Type
>& aClientType
, bool aExclusive
,
349 bool aInternal
, OpenDirectoryListener
* aOpenListener
);
351 void AssertIsOnOwningThread() const
359 const Nullable
<PersistenceType
>& GetPersistenceType() const {
360 return mPersistenceType
;
363 const nsACString
& GetGroup() const { return mGroup
; }
365 const OriginScope
& GetOriginScope() const { return mOriginScope
; }
367 const Nullable
<Client::Type
>& GetClientType() const { return mClientType
; }
369 bool IsInternal() const { return mInternal
; }
371 bool ShouldUpdateLockTable() {
373 mPersistenceType
.Value() != PERSISTENCE_TYPE_PERSISTENT
;
376 // Test whether this DirectoryLock needs to wait for the given lock.
377 bool MustWaitFor(const DirectoryLockImpl
& aLock
);
379 void AddBlockingLock(DirectoryLockImpl
* aLock
) {
380 AssertIsOnOwningThread();
382 mBlocking
.AppendElement(aLock
);
385 const nsTArray
<DirectoryLockImpl
*>& GetBlockedOnLocks() { return mBlockedOn
; }
387 void AddBlockedOnLock(DirectoryLockImpl
* aLock
) {
388 AssertIsOnOwningThread();
390 mBlockedOn
.AppendElement(aLock
);
393 void MaybeUnblock(DirectoryLockImpl
* aLock
) {
394 AssertIsOnOwningThread();
396 mBlockedOn
.RemoveElement(aLock
);
397 if (mBlockedOn
.IsEmpty()) {
398 NotifyOpenListener();
402 void NotifyOpenListener();
405 AssertIsOnOwningThread();
410 NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl
, override
)
413 ~DirectoryLockImpl();
416 class QuotaManager::CreateRunnable final
: public BackgroundThreadObject
,
418 nsCOMPtr
<nsIEventTarget
> mMainEventTarget
;
419 nsTArray
<nsCOMPtr
<nsIRunnable
>> mCallbacks
;
420 nsString mBaseDirPath
;
421 RefPtr
<QuotaManager
> mManager
;
422 nsresult mResultCode
;
435 explicit CreateRunnable(nsIEventTarget
* aMainEventTarget
)
436 : Runnable("dom::quota::QuotaManager::CreateRunnable"),
437 mMainEventTarget(aMainEventTarget
),
439 mState(State::Initial
) {
440 AssertIsOnBackgroundThread();
443 void AddCallback(nsIRunnable
* aCallback
) {
444 AssertIsOnOwningThread();
445 MOZ_ASSERT(aCallback
);
447 mCallbacks
.AppendElement(aCallback
);
455 nsresult
CreateManager();
457 nsresult
RegisterObserver();
459 void CallCallbacks();
461 State
GetNextState(nsCOMPtr
<nsIEventTarget
>& aThread
);
466 class QuotaManager::ShutdownRunnable final
: public Runnable
{
467 // Only touched on the main thread.
471 explicit ShutdownRunnable(bool& aDone
)
472 : Runnable("dom::quota::QuotaManager::ShutdownRunnable"), mDone(aDone
) {
473 MOZ_ASSERT(NS_IsMainThread());
477 ~ShutdownRunnable() {}
482 class QuotaManager::ShutdownObserver final
: public nsIObserver
{
483 nsCOMPtr
<nsIEventTarget
> mBackgroundThread
;
486 explicit ShutdownObserver(nsIEventTarget
* aBackgroundThread
)
487 : mBackgroundThread(aBackgroundThread
) {
488 MOZ_ASSERT(NS_IsMainThread());
495 ~ShutdownObserver() { MOZ_ASSERT(NS_IsMainThread()); }
500 /*******************************************************************************
501 * Local class declarations
502 ******************************************************************************/
506 class OriginInfo final
{
507 friend class GroupInfo
;
508 friend class QuotaManager
;
509 friend class QuotaObject
;
512 OriginInfo(GroupInfo
* aGroupInfo
, const nsACString
& aOrigin
, uint64_t aUsage
,
513 int64_t aAccessTime
, bool aPersisted
);
515 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo
)
517 int64_t LockedAccessTime() const {
518 AssertCurrentThreadOwnsQuotaMutex();
523 bool LockedPersisted() const {
524 AssertCurrentThreadOwnsQuotaMutex();
530 // Private destructor, to discourage deletion outside of Release():
532 MOZ_COUNT_DTOR(OriginInfo
);
534 MOZ_ASSERT(!mQuotaObjects
.Count());
537 void LockedDecreaseUsage(int64_t aSize
);
539 void LockedUpdateAccessTime(int64_t aAccessTime
) {
540 AssertCurrentThreadOwnsQuotaMutex();
542 mAccessTime
= aAccessTime
;
545 void LockedPersist();
547 nsDataHashtable
<nsStringHashKey
, QuotaObject
*> mQuotaObjects
;
549 GroupInfo
* mGroupInfo
;
550 const nsCString mOrigin
;
556 class OriginInfoLRUComparator
{
558 bool Equals(const OriginInfo
* a
, const OriginInfo
* b
) const {
559 return a
&& b
? a
->LockedAccessTime() == b
->LockedAccessTime()
560 : !a
&& !b
? true : false;
563 bool LessThan(const OriginInfo
* a
, const OriginInfo
* b
) const {
564 return a
&& b
? a
->LockedAccessTime() < b
->LockedAccessTime()
569 class GroupInfo final
{
570 friend class GroupInfoPair
;
571 friend class OriginInfo
;
572 friend class QuotaManager
;
573 friend class QuotaObject
;
576 GroupInfo(GroupInfoPair
* aGroupInfoPair
, PersistenceType aPersistenceType
,
577 const nsACString
& aGroup
)
578 : mGroupInfoPair(aGroupInfoPair
),
579 mPersistenceType(aPersistenceType
),
582 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
);
584 MOZ_COUNT_CTOR(GroupInfo
);
587 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo
)
590 // Private destructor, to discourage deletion outside of Release():
591 ~GroupInfo() { MOZ_COUNT_DTOR(GroupInfo
); }
593 already_AddRefed
<OriginInfo
> LockedGetOriginInfo(const nsACString
& aOrigin
);
595 void LockedAddOriginInfo(OriginInfo
* aOriginInfo
);
597 void LockedRemoveOriginInfo(const nsACString
& aOrigin
);
599 void LockedRemoveOriginInfos();
601 bool LockedHasOriginInfos() {
602 AssertCurrentThreadOwnsQuotaMutex();
604 return !mOriginInfos
.IsEmpty();
607 nsTArray
<RefPtr
<OriginInfo
>> mOriginInfos
;
609 GroupInfoPair
* mGroupInfoPair
;
610 PersistenceType mPersistenceType
;
615 class GroupInfoPair
{
616 friend class QuotaManager
;
617 friend class QuotaObject
;
620 GroupInfoPair() { MOZ_COUNT_CTOR(GroupInfoPair
); }
622 ~GroupInfoPair() { MOZ_COUNT_DTOR(GroupInfoPair
); }
625 already_AddRefed
<GroupInfo
> LockedGetGroupInfo(
626 PersistenceType aPersistenceType
) {
627 AssertCurrentThreadOwnsQuotaMutex();
628 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
);
630 RefPtr
<GroupInfo
> groupInfo
=
631 GetGroupInfoForPersistenceType(aPersistenceType
);
632 return groupInfo
.forget();
635 void LockedSetGroupInfo(PersistenceType aPersistenceType
,
636 GroupInfo
* aGroupInfo
) {
637 AssertCurrentThreadOwnsQuotaMutex();
638 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
);
640 RefPtr
<GroupInfo
>& groupInfo
=
641 GetGroupInfoForPersistenceType(aPersistenceType
);
642 groupInfo
= aGroupInfo
;
645 void LockedClearGroupInfo(PersistenceType aPersistenceType
) {
646 AssertCurrentThreadOwnsQuotaMutex();
647 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
);
649 RefPtr
<GroupInfo
>& groupInfo
=
650 GetGroupInfoForPersistenceType(aPersistenceType
);
654 bool LockedHasGroupInfos() {
655 AssertCurrentThreadOwnsQuotaMutex();
657 return mTemporaryStorageGroupInfo
|| mDefaultStorageGroupInfo
;
660 RefPtr
<GroupInfo
>& GetGroupInfoForPersistenceType(
661 PersistenceType aPersistenceType
);
663 RefPtr
<GroupInfo
> mTemporaryStorageGroupInfo
;
664 RefPtr
<GroupInfo
> mDefaultStorageGroupInfo
;
669 class CollectOriginsHelper final
: public Runnable
{
670 uint64_t mMinSizeToBeFreed
;
675 // The members below are protected by mMutex.
676 nsTArray
<RefPtr
<DirectoryLockImpl
>> mLocks
;
677 uint64_t mSizeToBeFreed
;
681 CollectOriginsHelper(mozilla::Mutex
& aMutex
, uint64_t aMinSizeToBeFreed
);
683 // Blocks the current thread until origins are collected on the main thread.
684 // The returned value contains an aggregate size of those origins.
685 int64_t BlockAndReturnOriginsForEviction(
686 nsTArray
<RefPtr
<DirectoryLockImpl
>>& aLocks
);
689 ~CollectOriginsHelper() {}
695 class OriginOperationBase
: public BackgroundThreadObject
, public Runnable
{
697 nsresult mResultCode
;
703 // Running initialization on the main thread.
706 // Running initialization on the owning thread.
709 // Running quota manager initialization on the owning thread.
710 State_CreatingQuotaManager
,
712 // Running on the owning thread in the listener for OpenDirectory.
713 State_DirectoryOpenPending
,
715 // Running on the IO thread.
716 State_DirectoryWorkOpen
,
718 // Running on the owning thread after all work is done.
719 State_UnblockingOpen
,
727 bool mActorDestroyed
;
730 bool mNeedsMainThreadInit
;
731 bool mNeedsQuotaManagerInit
;
734 void NoteActorDestroyed() {
735 AssertIsOnOwningThread();
737 mActorDestroyed
= true;
740 bool IsActorDestroyed() const {
741 AssertIsOnOwningThread();
743 return mActorDestroyed
;
747 explicit OriginOperationBase(
748 nsIEventTarget
* aOwningThread
= GetCurrentThreadEventTarget())
749 : BackgroundThreadObject(aOwningThread
),
750 Runnable("dom::quota::OriginOperationBase"),
752 mState(State_Initial
),
753 mActorDestroyed(false),
754 mNeedsMainThreadInit(false),
755 mNeedsQuotaManagerInit(false) {}
757 // Reference counted.
758 virtual ~OriginOperationBase() {
759 MOZ_ASSERT(mState
== State_Complete
);
760 MOZ_ASSERT(mActorDestroyed
);
764 State
GetState() const { return mState
; }
767 void SetState(State aState
) {
768 MOZ_ASSERT(mState
== State_Initial
);
772 void AdvanceState() {
775 mState
= State_Initializing
;
777 case State_Initializing
:
778 mState
= State_FinishingInit
;
780 case State_FinishingInit
:
781 mState
= State_CreatingQuotaManager
;
783 case State_CreatingQuotaManager
:
784 mState
= State_DirectoryOpenPending
;
786 case State_DirectoryOpenPending
:
787 mState
= State_DirectoryWorkOpen
;
789 case State_DirectoryWorkOpen
:
790 mState
= State_UnblockingOpen
;
792 case State_UnblockingOpen
:
793 mState
= State_Complete
;
796 MOZ_CRASH("Bad state!");
803 virtual nsresult
DoInitOnMainThread() { return NS_OK
; }
805 virtual void Open() = 0;
807 nsresult
DirectoryOpen();
809 virtual nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) = 0;
811 void Finish(nsresult aResult
);
813 virtual void UnblockOpen() = 0;
818 nsresult
InitOnMainThread();
820 nsresult
FinishInit();
822 nsresult
QuotaManagerOpen();
824 nsresult
DirectoryWork();
827 class FinalizeOriginEvictionOp
: public OriginOperationBase
{
828 nsTArray
<RefPtr
<DirectoryLockImpl
>> mLocks
;
831 FinalizeOriginEvictionOp(nsIEventTarget
* aBackgroundThread
,
832 nsTArray
<RefPtr
<DirectoryLockImpl
>>& aLocks
)
833 : OriginOperationBase(aBackgroundThread
) {
834 MOZ_ASSERT(!NS_IsMainThread());
836 mLocks
.SwapElements(aLocks
);
841 void RunOnIOThreadImmediately();
844 ~FinalizeOriginEvictionOp() {}
846 virtual void Open() override
;
848 virtual nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
850 virtual void UnblockOpen() override
;
853 class NormalOriginOperationBase
: public OriginOperationBase
,
854 public OpenDirectoryListener
{
855 RefPtr
<DirectoryLock
> mDirectoryLock
;
858 Nullable
<PersistenceType
> mPersistenceType
;
859 OriginScope mOriginScope
;
860 Nullable
<Client::Type
> mClientType
;
861 mozilla::Atomic
<bool> mCanceled
;
862 const bool mExclusive
;
865 void RunImmediately() {
866 MOZ_ASSERT(GetState() == State_Initial
);
868 MOZ_ALWAYS_SUCCEEDS(this->Run());
872 NormalOriginOperationBase(const Nullable
<PersistenceType
>& aPersistenceType
,
873 const OriginScope
& aOriginScope
, bool aExclusive
)
874 : mPersistenceType(aPersistenceType
),
875 mOriginScope(aOriginScope
),
876 mExclusive(aExclusive
) {
877 AssertIsOnOwningThread();
880 ~NormalOriginOperationBase() {}
883 // Need to declare refcounting unconditionally, because
884 // OpenDirectoryListener has pure-virtual refcounting.
885 NS_DECL_ISUPPORTS_INHERITED
887 virtual void Open() override
;
889 virtual void UnblockOpen() override
;
891 // OpenDirectoryListener overrides.
892 virtual void DirectoryLockAcquired(DirectoryLock
* aLock
) override
;
894 virtual void DirectoryLockFailed() override
;
896 // Used to send results before unblocking open.
897 virtual void SendResults() = 0;
900 class SaveOriginAccessTimeOp
: public NormalOriginOperationBase
{
904 SaveOriginAccessTimeOp(PersistenceType aPersistenceType
,
905 const nsACString
& aOrigin
, int64_t aTimestamp
)
906 : NormalOriginOperationBase(Nullable
<PersistenceType
>(aPersistenceType
),
907 OriginScope::FromOrigin(aOrigin
),
908 /* aExclusive */ false),
909 mTimestamp(aTimestamp
) {
910 AssertIsOnOwningThread();
914 ~SaveOriginAccessTimeOp() {}
916 virtual nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
918 virtual void SendResults() override
;
921 /*******************************************************************************
922 * Actor class declarations
923 ******************************************************************************/
925 class Quota final
: public PQuotaParent
{
927 bool mActorDestroyed
;
933 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::quota::Quota
)
938 void StartIdleMaintenance();
941 virtual void ActorDestroy(ActorDestroyReason aWhy
) override
;
943 virtual PQuotaUsageRequestParent
* AllocPQuotaUsageRequestParent(
944 const UsageRequestParams
& aParams
) override
;
946 virtual mozilla::ipc::IPCResult
RecvPQuotaUsageRequestConstructor(
947 PQuotaUsageRequestParent
* aActor
,
948 const UsageRequestParams
& aParams
) override
;
950 virtual bool DeallocPQuotaUsageRequestParent(
951 PQuotaUsageRequestParent
* aActor
) override
;
953 virtual PQuotaRequestParent
* AllocPQuotaRequestParent(
954 const RequestParams
& aParams
) override
;
956 virtual mozilla::ipc::IPCResult
RecvPQuotaRequestConstructor(
957 PQuotaRequestParent
* aActor
, const RequestParams
& aParams
) override
;
959 virtual bool DeallocPQuotaRequestParent(PQuotaRequestParent
* aActor
) override
;
961 virtual mozilla::ipc::IPCResult
RecvStartIdleMaintenance() override
;
963 virtual mozilla::ipc::IPCResult
RecvStopIdleMaintenance() override
;
966 class QuotaUsageRequestBase
: public NormalOriginOperationBase
,
967 public PQuotaUsageRequestParent
{
969 // May be overridden by subclasses if they need to perform work on the
970 // background thread before being run.
971 virtual bool Init(Quota
* aQuota
);
974 QuotaUsageRequestBase()
975 : NormalOriginOperationBase(Nullable
<PersistenceType
>(),
976 OriginScope::FromNull(),
977 /* aExclusive */ false) {}
979 nsresult
GetUsageForOrigin(QuotaManager
* aQuotaManager
,
980 PersistenceType aPersistenceType
,
981 const nsACString
& aGroup
,
982 const nsACString
& aOrigin
, UsageInfo
* aUsageInfo
);
984 // Subclasses use this override to set the IPDL response value.
985 virtual void GetResponse(UsageRequestResponse
& aResponse
) = 0;
988 void SendResults() override
;
991 void ActorDestroy(ActorDestroyReason aWhy
) override
;
993 mozilla::ipc::IPCResult
RecvCancel() final
;
996 class GetUsageOp final
: public QuotaUsageRequestBase
{
997 nsTArray
<OriginUsage
> mOriginUsages
;
998 nsDataHashtable
<nsCStringHashKey
, uint32_t> mOriginUsagesIndex
;
1003 explicit GetUsageOp(const UsageRequestParams
& aParams
);
1008 nsresult
TraverseRepository(QuotaManager
* aQuotaManager
,
1009 PersistenceType aPersistenceType
);
1011 nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
1013 void GetResponse(UsageRequestResponse
& aResponse
) override
;
1016 class GetOriginUsageOp final
: public QuotaUsageRequestBase
{
1017 // If mGetGroupUsage is false, we use mUsageInfo to record the origin usage
1018 // and the file usage. Otherwise, we use it to record the group usage and the
1020 UsageInfo mUsageInfo
;
1022 const OriginUsageParams mParams
;
1025 bool mGetGroupUsage
;
1028 explicit GetOriginUsageOp(const UsageRequestParams
& aParams
);
1030 MOZ_IS_CLASS_INIT
bool Init(Quota
* aQuota
) override
;
1033 ~GetOriginUsageOp() {}
1035 MOZ_IS_CLASS_INIT
virtual nsresult
DoInitOnMainThread() override
;
1037 virtual nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
1039 void GetResponse(UsageRequestResponse
& aResponse
) override
;
1042 class QuotaRequestBase
: public NormalOriginOperationBase
,
1043 public PQuotaRequestParent
{
1045 // May be overridden by subclasses if they need to perform work on the
1046 // background thread before being run.
1047 virtual bool Init(Quota
* aQuota
);
1050 explicit QuotaRequestBase(bool aExclusive
)
1051 : NormalOriginOperationBase(Nullable
<PersistenceType
>(),
1052 OriginScope::FromNull(), aExclusive
) {}
1054 // Subclasses use this override to set the IPDL response value.
1055 virtual void GetResponse(RequestResponse
& aResponse
) = 0;
1058 virtual void SendResults() override
;
1061 virtual void ActorDestroy(ActorDestroyReason aWhy
) override
;
1064 class InitOp final
: public QuotaRequestBase
{
1066 InitOp() : QuotaRequestBase(/* aExclusive */ false) {
1067 AssertIsOnOwningThread();
1073 nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
1075 void GetResponse(RequestResponse
& aResponse
) override
;
1078 class InitTemporaryStorageOp final
: public QuotaRequestBase
{
1080 InitTemporaryStorageOp() : QuotaRequestBase(/* aExclusive */ false) {
1081 AssertIsOnOwningThread();
1085 ~InitTemporaryStorageOp() {}
1087 nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
1089 void GetResponse(RequestResponse
& aResponse
) override
;
1092 class InitOriginOp final
: public QuotaRequestBase
{
1093 const InitOriginParams mParams
;
1099 explicit InitOriginOp(const RequestParams
& aParams
);
1101 bool Init(Quota
* aQuota
) override
;
1106 nsresult
DoInitOnMainThread() override
;
1108 nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
1110 void GetResponse(RequestResponse
& aResponse
) override
;
1113 class ResetOrClearOp final
: public QuotaRequestBase
{
1117 explicit ResetOrClearOp(bool aClear
)
1118 : QuotaRequestBase(/* aExclusive */ true), mClear(aClear
) {
1119 AssertIsOnOwningThread();
1123 ~ResetOrClearOp() {}
1125 void DeleteFiles(QuotaManager
* aQuotaManager
);
1127 virtual nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
1129 virtual void GetResponse(RequestResponse
& aResponse
) override
;
1132 class ClearRequestBase
: public QuotaRequestBase
{
1137 ClearRequestBase(bool aExclusive
, bool aClear
)
1138 : QuotaRequestBase(aExclusive
), mClear(aClear
) {
1139 AssertIsOnOwningThread();
1142 void DeleteFiles(QuotaManager
* aQuotaManager
,
1143 PersistenceType aPersistenceType
);
1145 nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
1148 class ClearOriginOp final
: public ClearRequestBase
{
1149 const ClearResetOriginParams mParams
;
1152 explicit ClearOriginOp(const RequestParams
& aParams
);
1154 bool Init(Quota
* aQuota
) override
;
1159 nsresult
DoInitOnMainThread() override
;
1161 void GetResponse(RequestResponse
& aResponse
) override
;
1164 class ClearDataOp final
: public ClearRequestBase
{
1165 const ClearDataParams mParams
;
1168 explicit ClearDataOp(const RequestParams
& aParams
);
1170 bool Init(Quota
* aQuota
) override
;
1175 nsresult
DoInitOnMainThread() override
;
1177 void GetResponse(RequestResponse
& aResponse
) override
;
1180 class PersistRequestBase
: public QuotaRequestBase
{
1181 const PrincipalInfo mPrincipalInfo
;
1188 bool Init(Quota
* aQuota
) override
;
1191 explicit PersistRequestBase(const PrincipalInfo
& aPrincipalInfo
);
1194 nsresult
DoInitOnMainThread() override
;
1197 class PersistedOp final
: public PersistRequestBase
{
1201 explicit PersistedOp(const RequestParams
& aParams
);
1206 nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
1208 void GetResponse(RequestResponse
& aResponse
) override
;
1211 class PersistOp final
: public PersistRequestBase
{
1213 explicit PersistOp(const RequestParams
& aParams
);
1218 nsresult
DoDirectoryWork(QuotaManager
* aQuotaManager
) override
;
1220 void GetResponse(RequestResponse
& aResponse
) override
;
1223 class StoragePressureRunnable final
: public Runnable
{
1224 const uint64_t mUsage
;
1227 explicit StoragePressureRunnable(uint64_t aUsage
)
1228 : Runnable("dom::quota::QuotaObject::StoragePressureRunnable"),
1232 ~StoragePressureRunnable() = default;
1237 /*******************************************************************************
1239 ******************************************************************************/
1241 template <typename T
, bool = mozilla::IsUnsigned
<T
>::value
>
1243 static void Assert(T aInt
) {
1244 static_assert(mozilla::IsIntegral
<T
>::value
, "Not an integer!");
1245 MOZ_ASSERT(aInt
>= 0);
1249 template <typename T
>
1250 struct IntChecker
<T
, true> {
1251 static void Assert(T aInt
) {
1252 static_assert(mozilla::IsIntegral
<T
>::value
, "Not an integer!");
1256 template <typename T
>
1257 void AssertNoOverflow(uint64_t aDest
, T aArg
) {
1258 IntChecker
<T
>::Assert(aDest
);
1259 IntChecker
<T
>::Assert(aArg
);
1260 MOZ_ASSERT(UINT64_MAX
- aDest
>= uint64_t(aArg
));
1263 template <typename T
, typename U
>
1264 void AssertNoUnderflow(T aDest
, U aArg
) {
1265 IntChecker
<T
>::Assert(aDest
);
1266 IntChecker
<T
>::Assert(aArg
);
1267 MOZ_ASSERT(uint64_t(aDest
) >= uint64_t(aArg
));
1270 inline bool IsDotFile(const nsAString
& aFileName
) {
1271 return QuotaManager::IsDotFile(aFileName
);
1274 inline bool IsOSMetadata(const nsAString
& aFileName
) {
1275 return QuotaManager::IsOSMetadata(aFileName
);
1278 bool IsOriginMetadata(const nsAString
& aFileName
) {
1279 return aFileName
.EqualsLiteral(METADATA_FILE_NAME
) ||
1280 aFileName
.EqualsLiteral(METADATA_V2_FILE_NAME
) ||
1281 IsOSMetadata(aFileName
);
1284 bool IsTempMetadata(const nsAString
& aFileName
) {
1285 return aFileName
.EqualsLiteral(METADATA_TMP_FILE_NAME
) ||
1286 aFileName
.EqualsLiteral(METADATA_V2_TMP_FILE_NAME
);
1291 BackgroundThreadObject::BackgroundThreadObject()
1292 : mOwningThread(GetCurrentThreadEventTarget()) {
1293 AssertIsOnOwningThread();
1296 BackgroundThreadObject::BackgroundThreadObject(nsIEventTarget
* aOwningThread
)
1297 : mOwningThread(aOwningThread
) {}
1301 void BackgroundThreadObject::AssertIsOnOwningThread() const {
1302 AssertIsOnBackgroundThread();
1303 MOZ_ASSERT(mOwningThread
);
1305 MOZ_ASSERT(NS_SUCCEEDED(mOwningThread
->IsOnCurrentThread(¤t
)));
1306 MOZ_ASSERT(current
);
1311 nsIEventTarget
* BackgroundThreadObject::OwningThread() const {
1312 MOZ_ASSERT(mOwningThread
);
1313 return mOwningThread
;
1316 bool IsOnIOThread() {
1317 QuotaManager
* quotaManager
= QuotaManager::Get();
1318 NS_ASSERTION(quotaManager
, "Must have a manager here!");
1321 return NS_SUCCEEDED(
1322 quotaManager
->IOThread()->IsOnCurrentThread(¤tThread
)) &&
1326 void AssertIsOnIOThread() {
1327 NS_ASSERTION(IsOnIOThread(), "Running on the wrong thread!");
1330 void AssertCurrentThreadOwnsQuotaMutex() {
1332 QuotaManager
* quotaManager
= QuotaManager::Get();
1333 NS_ASSERTION(quotaManager
, "Must have a manager here!");
1335 quotaManager
->AssertCurrentThreadOwnsQuotaMutex();
1339 void ReportInternalError(const char* aFile
, uint32_t aLine
, const char* aStr
) {
1340 // Get leaf of file path
1341 for (const char* p
= aFile
; *p
; ++p
) {
1342 if (*p
== '/' && *(p
+ 1)) {
1347 nsContentUtils::LogSimpleConsoleError(
1348 NS_ConvertUTF8toUTF16(
1349 nsPrintfCString("Quota %s: %s:%" PRIu32
, aStr
, aFile
, aLine
)),
1351 false /* Quota Manager is not active in private browsing mode */);
1356 StaticRefPtr
<QuotaManager
> gInstance
;
1357 bool gCreateFailed
= false;
1358 StaticRefPtr
<QuotaManager::CreateRunnable
> gCreateRunnable
;
1359 mozilla::Atomic
<bool> gShutdown(false);
1361 // Constants for temporary storage limit computing.
1362 static const int32_t kDefaultFixedLimitKB
= -1;
1363 static const uint32_t kDefaultChunkSizeKB
= 10 * 1024;
1364 int32_t gFixedLimitKB
= kDefaultFixedLimitKB
;
1365 uint32_t gChunkSizeKB
= kDefaultChunkSizeKB
;
1367 bool gTestingEnabled
= false;
1369 class StorageOperationBase
: public Runnable
{
1370 mozilla::Mutex mMutex
;
1371 mozilla::CondVar mCondVar
;
1372 nsresult mMainThreadResultCode
;
1378 nsTArray
<OriginProps
> mOriginProps
;
1380 nsCOMPtr
<nsIFile
> mDirectory
;
1382 const bool mPersistent
;
1385 StorageOperationBase(nsIFile
* aDirectory
, bool aPersistent
)
1386 : Runnable("dom::quota::StorageOperationBase"),
1387 mMutex("StorageOperationBase::mMutex"),
1388 mCondVar(mMutex
, "StorageOperationBase::mCondVar"),
1389 mMainThreadResultCode(NS_OK
),
1391 mDirectory(aDirectory
),
1392 mPersistent(aPersistent
) {
1393 AssertIsOnIOThread();
1397 virtual ~StorageOperationBase() {}
1399 nsresult
GetDirectoryMetadata(nsIFile
* aDirectory
, int64_t& aTimestamp
,
1400 nsACString
& aGroup
, nsACString
& aOrigin
,
1401 Nullable
<bool>& aIsApp
);
1403 // Upgrade helper to load the contents of ".metadata-v2" files from previous
1404 // schema versions. Although QuotaManager has a similar GetDirectoryMetadata2
1405 // method, it is only intended to read current version ".metadata-v2" files.
1406 // And unlike the old ".metadata" files, the ".metadata-v2" format can evolve
1407 // because our "storage.sqlite" lets us track the overall version of the
1408 // storage directory.
1409 nsresult
GetDirectoryMetadata2(nsIFile
* aDirectory
, int64_t& aTimestamp
,
1410 nsACString
& aSuffix
, nsACString
& aGroup
,
1411 nsACString
& aOrigin
, bool& aIsApp
);
1413 nsresult
RemoveObsoleteOrigin(const OriginProps
& aOriginProps
);
1415 nsresult
ProcessOriginDirectories();
1417 virtual nsresult
ProcessOriginDirectory(const OriginProps
& aOriginProps
) = 0;
1420 nsresult
RunOnMainThread();
1426 struct StorageOperationBase::OriginProps
{
1427 enum Type
{ eChrome
, eContent
, eObsolete
};
1429 nsCOMPtr
<nsIFile
> mDirectory
;
1432 OriginAttributes mAttrs
;
1440 bool mNeedsRestore2
;
1444 explicit OriginProps()
1447 mNeedsRestore(false),
1448 mNeedsRestore2(false),
1451 nsresult
Init(nsIFile
* aDirectory
);
1454 class MOZ_STACK_CLASS OriginParser final
{
1456 enum ResultType
{ InvalidOrigin
, ObsoleteOrigin
, ValidOrigin
};
1459 static bool IgnoreWhitespace(char16_t
/* aChar */) { return false; }
1461 typedef nsCCharSeparatedTokenizerTemplate
<IgnoreWhitespace
> Tokenizer
;
1463 enum SchemeType
{ eNone
, eFile
, eAbout
};
1466 eExpectingAppIdOrScheme
,
1467 eExpectingInMozBrowser
,
1469 eExpectingEmptyToken1
,
1470 eExpectingEmptyToken2
,
1471 eExpectingEmptyTokenOrUniversalFileOrigin
,
1474 eExpectingEmptyTokenOrDriveLetterOrPathnameComponent
,
1475 eExpectingEmptyTokenOrPathnameComponent
,
1477 // We transit from eExpectingHost to this state when we encounter a host
1478 // beginning with "[" which indicates an IPv6 literal. Because we mangle the
1479 // IPv6 ":" delimiter to be a "+", we will receive separate tokens for each
1480 // portion of the IPv6 address, including a final token that ends with "]".
1481 // (Note that we do not mangle "[" or "]".) Note that the URL spec
1482 // explicitly disclaims support for "<zone_id>" and so we don't have to deal
1484 eExpectingIPV6Token
,
1486 eHandledTrailingSeparator
1489 const nsCString mOrigin
;
1490 const OriginAttributes mOriginAttributes
;
1491 Tokenizer mTokenizer
;
1496 Nullable
<uint32_t> mPort
;
1497 nsTArray
<nsCString
> mPathnameComponents
;
1498 nsCString mHandledTokens
;
1500 SchemeType mSchemeType
;
1502 bool mInIsolatedMozBrowser
;
1503 bool mUniversalFileOrigin
;
1504 bool mMaybeDriveLetter
;
1507 // Number of group which a IPv6 address has. Should be less than 9.
1511 OriginParser(const nsACString
& aOrigin
,
1512 const OriginAttributes
& aOriginAttributes
)
1514 mOriginAttributes(aOriginAttributes
),
1515 mTokenizer(aOrigin
, '+'),
1519 mState(eExpectingAppIdOrScheme
),
1520 mInIsolatedMozBrowser(false),
1521 mUniversalFileOrigin(false),
1522 mMaybeDriveLetter(false),
1526 static ResultType
ParseOrigin(const nsACString
& aOrigin
, nsCString
& aSpec
,
1527 OriginAttributes
* aAttrs
);
1529 ResultType
Parse(nsACString
& aSpec
, OriginAttributes
* aAttrs
);
1532 void HandleScheme(const nsDependentCSubstring
& aToken
);
1534 void HandlePathnameComponent(const nsDependentCSubstring
& aToken
);
1536 void HandleToken(const nsDependentCSubstring
& aToken
);
1538 void HandleTrailingSeparator();
1541 class RepositoryOperationBase
: public StorageOperationBase
{
1543 RepositoryOperationBase(nsIFile
* aDirectory
, bool aPersistent
)
1544 : StorageOperationBase(aDirectory
, aPersistent
) {}
1546 nsresult
ProcessRepository();
1549 virtual ~RepositoryOperationBase() {}
1551 template <typename UpgradeMethod
>
1552 nsresult
MaybeUpgradeClients(const OriginProps
& aOriginsProps
,
1553 UpgradeMethod aMethod
);
1556 virtual nsresult
PrepareOriginDirectory(OriginProps
& aOriginProps
,
1557 bool* aRemoved
) = 0;
1560 class CreateOrUpgradeDirectoryMetadataHelper final
1561 : public RepositoryOperationBase
{
1562 nsCOMPtr
<nsIFile
> mPermanentStorageDir
;
1565 CreateOrUpgradeDirectoryMetadataHelper(nsIFile
* aDirectory
, bool aPersistent
)
1566 : RepositoryOperationBase(aDirectory
, aPersistent
) {}
1569 nsresult
MaybeUpgradeOriginDirectory(nsIFile
* aDirectory
);
1571 nsresult
PrepareOriginDirectory(OriginProps
& aOriginProps
,
1572 bool* aRemoved
) override
;
1574 nsresult
ProcessOriginDirectory(const OriginProps
& aOriginProps
) override
;
1577 class UpgradeStorageFrom0_0To1_0Helper final
: public RepositoryOperationBase
{
1579 UpgradeStorageFrom0_0To1_0Helper(nsIFile
* aDirectory
, bool aPersistent
)
1580 : RepositoryOperationBase(aDirectory
, aPersistent
) {}
1583 nsresult
PrepareOriginDirectory(OriginProps
& aOriginProps
,
1584 bool* aRemoved
) override
;
1586 nsresult
ProcessOriginDirectory(const OriginProps
& aOriginProps
) override
;
1589 class UpgradeStorageFrom1_0To2_0Helper final
: public RepositoryOperationBase
{
1591 UpgradeStorageFrom1_0To2_0Helper(nsIFile
* aDirectory
, bool aPersistent
)
1592 : RepositoryOperationBase(aDirectory
, aPersistent
) {}
1595 nsresult
MaybeRemoveMorgueDirectory(const OriginProps
& aOriginProps
);
1597 nsresult
MaybeRemoveAppsData(const OriginProps
& aOriginProps
, bool* aRemoved
);
1599 nsresult
MaybeStripObsoleteOriginAttributes(const OriginProps
& aOriginProps
,
1602 nsresult
PrepareOriginDirectory(OriginProps
& aOriginProps
,
1603 bool* aRemoved
) override
;
1605 nsresult
ProcessOriginDirectory(const OriginProps
& aOriginProps
) override
;
1608 class UpgradeStorageFrom2_0To2_1Helper final
: public RepositoryOperationBase
{
1610 UpgradeStorageFrom2_0To2_1Helper(nsIFile
* aDirectory
, bool aPersistent
)
1611 : RepositoryOperationBase(aDirectory
, aPersistent
) {}
1614 nsresult
PrepareOriginDirectory(OriginProps
& aOriginProps
,
1615 bool* aRemoved
) override
;
1617 nsresult
ProcessOriginDirectory(const OriginProps
& aOriginProps
) override
;
1620 class RestoreDirectoryMetadata2Helper final
: public StorageOperationBase
{
1622 RestoreDirectoryMetadata2Helper(nsIFile
* aDirectory
, bool aPersistent
)
1623 : StorageOperationBase(aDirectory
, aPersistent
) {}
1625 nsresult
RestoreMetadata2File();
1628 nsresult
ProcessOriginDirectory(const OriginProps
& aOriginProps
) override
;
1631 void SanitizeOriginString(nsCString
& aOrigin
) {
1633 NS_ASSERTION(!strcmp(QuotaManager::kReplaceChars
,
1634 FILE_ILLEGAL_CHARACTERS FILE_PATH_SEPARATOR
),
1635 "Illegal file characters have changed!");
1638 aOrigin
.ReplaceChar(QuotaManager::kReplaceChars
, '+');
1641 nsresult
CloneStoragePath(nsIFile
* aBaseDir
, const nsAString
& aStorageName
,
1642 nsAString
& aStoragePath
) {
1645 nsCOMPtr
<nsIFile
> storageDir
;
1646 rv
= aBaseDir
->Clone(getter_AddRefs(storageDir
));
1647 if (NS_WARN_IF(NS_FAILED(rv
))) {
1651 rv
= storageDir
->Append(aStorageName
);
1652 if (NS_WARN_IF(NS_FAILED(rv
))) {
1656 rv
= storageDir
->GetPath(aStoragePath
);
1657 if (NS_WARN_IF(NS_FAILED(rv
))) {
1664 int64_t GetLastModifiedTime(nsIFile
* aFile
, bool aPersistent
) {
1665 AssertIsOnIOThread();
1668 class MOZ_STACK_CLASS Helper final
{
1670 static nsresult
GetLastModifiedTime(nsIFile
* aFile
, int64_t* aTimestamp
) {
1671 AssertIsOnIOThread();
1673 MOZ_ASSERT(aTimestamp
);
1676 nsresult rv
= aFile
->IsDirectory(&isDirectory
);
1677 if (NS_WARN_IF(NS_FAILED(rv
))) {
1683 rv
= aFile
->GetLeafName(leafName
);
1684 if (NS_WARN_IF(NS_FAILED(rv
))) {
1688 if (IsOriginMetadata(leafName
) || IsTempMetadata(leafName
) ||
1689 IsDotFile(leafName
)) {
1694 rv
= aFile
->GetLastModifiedTime(×tamp
);
1695 if (NS_WARN_IF(NS_FAILED(rv
))) {
1699 // Need to convert from milliseconds to microseconds.
1700 MOZ_ASSERT((INT64_MAX
/ PR_USEC_PER_MSEC
) > timestamp
);
1701 timestamp
*= int64_t(PR_USEC_PER_MSEC
);
1703 if (timestamp
> *aTimestamp
) {
1704 *aTimestamp
= timestamp
;
1709 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
1710 rv
= aFile
->GetDirectoryEntries(getter_AddRefs(entries
));
1711 if (NS_WARN_IF(NS_FAILED(rv
))) {
1715 nsCOMPtr
<nsIFile
> file
;
1716 while (NS_SUCCEEDED((rv
= entries
->GetNextFile(getter_AddRefs(file
)))) &&
1718 rv
= GetLastModifiedTime(file
, aTimestamp
);
1719 if (NS_WARN_IF(NS_FAILED(rv
))) {
1723 if (NS_WARN_IF(NS_FAILED(rv
))) {
1735 int64_t timestamp
= INT64_MIN
;
1736 nsresult rv
= Helper::GetLastModifiedTime(aFile
, ×tamp
);
1737 if (NS_FAILED(rv
)) {
1738 timestamp
= PR_Now();
1744 nsresult
EnsureDirectory(nsIFile
* aDirectory
, bool* aCreated
) {
1745 AssertIsOnIOThread();
1747 nsresult rv
= aDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755);
1748 if (rv
== NS_ERROR_FILE_ALREADY_EXISTS
) {
1750 rv
= aDirectory
->IsDirectory(&isDirectory
);
1751 NS_ENSURE_SUCCESS(rv
, rv
);
1752 NS_ENSURE_TRUE(isDirectory
, NS_ERROR_UNEXPECTED
);
1756 NS_ENSURE_SUCCESS(rv
, rv
);
1764 enum FileFlag
{ kTruncateFileFlag
, kUpdateFileFlag
, kAppendFileFlag
};
1766 nsresult
GetOutputStream(nsIFile
* aFile
, FileFlag aFileFlag
,
1767 nsIOutputStream
** aStream
) {
1768 AssertIsOnIOThread();
1772 nsCOMPtr
<nsIOutputStream
> outputStream
;
1773 switch (aFileFlag
) {
1774 case kTruncateFileFlag
: {
1775 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(outputStream
), aFile
);
1776 if (NS_WARN_IF(NS_FAILED(rv
))) {
1783 case kUpdateFileFlag
: {
1785 rv
= aFile
->Exists(&exists
);
1786 if (NS_WARN_IF(NS_FAILED(rv
))) {
1795 nsCOMPtr
<nsIFileStream
> stream
;
1796 rv
= NS_NewLocalFileStream(getter_AddRefs(stream
), aFile
);
1797 if (NS_WARN_IF(NS_FAILED(rv
))) {
1801 outputStream
= do_QueryInterface(stream
);
1802 if (NS_WARN_IF(!outputStream
)) {
1803 return NS_ERROR_FAILURE
;
1809 case kAppendFileFlag
: {
1810 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(outputStream
), aFile
,
1811 PR_WRONLY
| PR_CREATE_FILE
| PR_APPEND
);
1812 if (NS_WARN_IF(NS_FAILED(rv
))) {
1820 MOZ_CRASH("Should never get here!");
1823 outputStream
.forget(aStream
);
1827 nsresult
GetBinaryOutputStream(nsIFile
* aFile
, FileFlag aFileFlag
,
1828 nsIBinaryOutputStream
** aStream
) {
1829 nsCOMPtr
<nsIOutputStream
> outputStream
;
1830 nsresult rv
= GetOutputStream(aFile
, aFileFlag
, getter_AddRefs(outputStream
));
1831 if (NS_WARN_IF(NS_FAILED(rv
))) {
1835 if (NS_WARN_IF(!outputStream
)) {
1836 return NS_ERROR_UNEXPECTED
;
1839 nsCOMPtr
<nsIObjectOutputStream
> objectOutputStream
=
1840 NS_NewObjectOutputStream(outputStream
);
1842 objectOutputStream
.forget(aStream
);
1846 void GetJarPrefix(uint32_t aAppId
, bool aInIsolatedMozBrowser
,
1847 nsACString
& aJarPrefix
) {
1848 MOZ_ASSERT(aAppId
!= nsIScriptSecurityManager::UNKNOWN_APP_ID
);
1850 if (aAppId
== nsIScriptSecurityManager::UNKNOWN_APP_ID
) {
1851 aAppId
= nsIScriptSecurityManager::NO_APP_ID
;
1854 aJarPrefix
.Truncate();
1857 if (aAppId
== nsIScriptSecurityManager::NO_APP_ID
&& !aInIsolatedMozBrowser
) {
1861 // aJarPrefix = appId + "+" + { 't', 'f' } + "+";
1862 aJarPrefix
.AppendInt(aAppId
);
1863 aJarPrefix
.Append('+');
1864 aJarPrefix
.Append(aInIsolatedMozBrowser
? 't' : 'f');
1865 aJarPrefix
.Append('+');
1868 nsresult
CreateDirectoryMetadata(nsIFile
* aDirectory
, int64_t aTimestamp
,
1869 const nsACString
& aSuffix
,
1870 const nsACString
& aGroup
,
1871 const nsACString
& aOrigin
) {
1872 AssertIsOnIOThread();
1874 OriginAttributes groupAttributes
;
1876 nsCString groupNoSuffix
;
1877 bool ok
= groupAttributes
.PopulateFromOrigin(aGroup
, groupNoSuffix
);
1879 return NS_ERROR_FAILURE
;
1882 nsCString groupPrefix
;
1883 GetJarPrefix(groupAttributes
.mAppId
, groupAttributes
.mInIsolatedMozBrowser
,
1886 nsCString group
= groupPrefix
+ groupNoSuffix
;
1888 OriginAttributes originAttributes
;
1890 nsCString originNoSuffix
;
1891 ok
= originAttributes
.PopulateFromOrigin(aOrigin
, originNoSuffix
);
1893 return NS_ERROR_FAILURE
;
1896 nsCString originPrefix
;
1897 GetJarPrefix(originAttributes
.mAppId
, originAttributes
.mInIsolatedMozBrowser
,
1900 nsCString origin
= originPrefix
+ originNoSuffix
;
1902 MOZ_ASSERT(groupPrefix
== originPrefix
);
1904 nsCOMPtr
<nsIFile
> file
;
1905 nsresult rv
= aDirectory
->Clone(getter_AddRefs(file
));
1906 if (NS_WARN_IF(NS_FAILED(rv
))) {
1910 rv
= file
->Append(NS_LITERAL_STRING(METADATA_TMP_FILE_NAME
));
1911 if (NS_WARN_IF(NS_FAILED(rv
))) {
1915 nsCOMPtr
<nsIBinaryOutputStream
> stream
;
1916 rv
= GetBinaryOutputStream(file
, kTruncateFileFlag
, getter_AddRefs(stream
));
1917 if (NS_WARN_IF(NS_FAILED(rv
))) {
1923 rv
= stream
->Write64(aTimestamp
);
1924 if (NS_WARN_IF(NS_FAILED(rv
))) {
1928 rv
= stream
->WriteStringZ(group
.get());
1929 if (NS_WARN_IF(NS_FAILED(rv
))) {
1933 rv
= stream
->WriteStringZ(origin
.get());
1934 if (NS_WARN_IF(NS_FAILED(rv
))) {
1938 // Currently unused (used to be isApp).
1939 rv
= stream
->WriteBoolean(false);
1940 if (NS_WARN_IF(NS_FAILED(rv
))) {
1944 rv
= stream
->Flush();
1945 if (NS_WARN_IF(NS_FAILED(rv
))) {
1949 rv
= stream
->Close();
1950 if (NS_WARN_IF(NS_FAILED(rv
))) {
1954 rv
= file
->RenameTo(nullptr, NS_LITERAL_STRING(METADATA_FILE_NAME
));
1955 if (NS_WARN_IF(NS_FAILED(rv
))) {
1962 nsresult
CreateDirectoryMetadata2(nsIFile
* aDirectory
, int64_t aTimestamp
,
1963 bool aPersisted
, const nsACString
& aSuffix
,
1964 const nsACString
& aGroup
,
1965 const nsACString
& aOrigin
) {
1966 AssertIsOnIOThread();
1967 MOZ_ASSERT(aDirectory
);
1969 nsCOMPtr
<nsIFile
> file
;
1970 nsresult rv
= aDirectory
->Clone(getter_AddRefs(file
));
1971 if (NS_WARN_IF(NS_FAILED(rv
))) {
1975 rv
= file
->Append(NS_LITERAL_STRING(METADATA_V2_TMP_FILE_NAME
));
1976 if (NS_WARN_IF(NS_FAILED(rv
))) {
1980 nsCOMPtr
<nsIBinaryOutputStream
> stream
;
1981 rv
= GetBinaryOutputStream(file
, kTruncateFileFlag
, getter_AddRefs(stream
));
1982 if (NS_WARN_IF(NS_FAILED(rv
))) {
1988 rv
= stream
->Write64(aTimestamp
);
1989 if (NS_WARN_IF(NS_FAILED(rv
))) {
1993 rv
= stream
->WriteBoolean(aPersisted
);
1994 if (NS_WARN_IF(NS_FAILED(rv
))) {
1999 rv
= stream
->Write32(0);
2000 if (NS_WARN_IF(NS_FAILED(rv
))) {
2005 rv
= stream
->Write32(0);
2006 if (NS_WARN_IF(NS_FAILED(rv
))) {
2010 // The suffix isn't used right now, but we might need it in future. It's
2011 // a bit of redundancy we can live with given how painful is to upgrade
2013 rv
= stream
->WriteStringZ(PromiseFlatCString(aSuffix
).get());
2014 if (NS_WARN_IF(NS_FAILED(rv
))) {
2018 rv
= stream
->WriteStringZ(PromiseFlatCString(aGroup
).get());
2019 if (NS_WARN_IF(NS_FAILED(rv
))) {
2023 rv
= stream
->WriteStringZ(PromiseFlatCString(aOrigin
).get());
2024 if (NS_WARN_IF(NS_FAILED(rv
))) {
2028 // Currently unused (used to be isApp).
2029 rv
= stream
->WriteBoolean(false);
2030 if (NS_WARN_IF(NS_FAILED(rv
))) {
2034 rv
= stream
->Flush();
2035 if (NS_WARN_IF(NS_FAILED(rv
))) {
2039 rv
= stream
->Close();
2040 if (NS_WARN_IF(NS_FAILED(rv
))) {
2044 rv
= file
->RenameTo(nullptr, NS_LITERAL_STRING(METADATA_V2_FILE_NAME
));
2045 if (NS_WARN_IF(NS_FAILED(rv
))) {
2052 nsresult
CreateDirectoryMetadataFiles(nsIFile
* aDirectory
, bool aPersisted
,
2053 const nsACString
& aSuffix
,
2054 const nsACString
& aGroup
,
2055 const nsACString
& aOrigin
,
2056 int64_t* aTimestamp
) {
2057 AssertIsOnIOThread();
2059 int64_t timestamp
= PR_Now();
2062 CreateDirectoryMetadata(aDirectory
, timestamp
, aSuffix
, aGroup
, aOrigin
);
2063 if (NS_WARN_IF(NS_FAILED(rv
))) {
2067 rv
= CreateDirectoryMetadata2(aDirectory
, timestamp
, aPersisted
, aSuffix
,
2069 if (NS_WARN_IF(NS_FAILED(rv
))) {
2074 *aTimestamp
= timestamp
;
2079 nsresult
GetBinaryInputStream(nsIFile
* aDirectory
, const nsAString
& aFilename
,
2080 nsIBinaryInputStream
** aStream
) {
2081 MOZ_ASSERT(!NS_IsMainThread());
2082 MOZ_ASSERT(aDirectory
);
2083 MOZ_ASSERT(aStream
);
2085 nsCOMPtr
<nsIFile
> file
;
2086 nsresult rv
= aDirectory
->Clone(getter_AddRefs(file
));
2087 if (NS_WARN_IF(NS_FAILED(rv
))) {
2091 rv
= file
->Append(aFilename
);
2092 if (NS_WARN_IF(NS_FAILED(rv
))) {
2096 nsCOMPtr
<nsIInputStream
> stream
;
2097 rv
= NS_NewLocalFileInputStream(getter_AddRefs(stream
), file
);
2098 if (NS_WARN_IF(NS_FAILED(rv
))) {
2102 nsCOMPtr
<nsIInputStream
> bufferedStream
;
2103 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
),
2104 stream
.forget(), 512);
2105 if (NS_WARN_IF(NS_FAILED(rv
))) {
2109 nsCOMPtr
<nsIBinaryInputStream
> binaryStream
=
2110 do_CreateInstance("@mozilla.org/binaryinputstream;1");
2111 if (NS_WARN_IF(!binaryStream
)) {
2112 return NS_ERROR_FAILURE
;
2115 rv
= binaryStream
->SetInputStream(bufferedStream
);
2116 if (NS_WARN_IF(NS_FAILED(rv
))) {
2120 binaryStream
.forget(aStream
);
2124 // This method computes and returns our best guess for the temporary storage
2125 // limit (in bytes), based on the amount of space users have free on their hard
2126 // drive and on given temporary storage usage (also in bytes).
2127 nsresult
GetTemporaryStorageLimit(nsIFile
* aDirectory
, uint64_t aCurrentUsage
,
2129 // Check for free space on device where temporary storage directory lives.
2130 int64_t bytesAvailable
;
2131 nsresult rv
= aDirectory
->GetDiskSpaceAvailable(&bytesAvailable
);
2132 NS_ENSURE_SUCCESS(rv
, rv
);
2134 NS_ASSERTION(bytesAvailable
>= 0, "Negative bytes available?!");
2136 uint64_t availableKB
=
2137 static_cast<uint64_t>((bytesAvailable
+ aCurrentUsage
) / 1024);
2139 // Grow/shrink in gChunkSizeKB units, deliberately, so that in the common case
2140 // we don't shrink temporary storage and evict origin data every time we
2142 availableKB
= (availableKB
/ gChunkSizeKB
) * gChunkSizeKB
;
2144 // Allow temporary storage to consume up to half the available space.
2145 uint64_t resultKB
= availableKB
* .50;
2147 *aLimit
= resultKB
* 1024;
2153 /*******************************************************************************
2154 * Exported functions
2155 ******************************************************************************/
2157 PQuotaParent
* AllocPQuotaParent() {
2158 AssertIsOnBackgroundThread();
2160 if (NS_WARN_IF(QuotaManager::IsShuttingDown())) {
2164 RefPtr
<Quota
> actor
= new Quota();
2166 return actor
.forget().take();
2169 bool DeallocPQuotaParent(PQuotaParent
* aActor
) {
2170 AssertIsOnBackgroundThread();
2173 RefPtr
<Quota
> actor
= dont_AddRef(static_cast<Quota
*>(aActor
));
2177 /*******************************************************************************
2179 ******************************************************************************/
2181 DirectoryLockImpl::DirectoryLockImpl(
2182 QuotaManager
* aQuotaManager
,
2183 const Nullable
<PersistenceType
>& aPersistenceType
, const nsACString
& aGroup
,
2184 const OriginScope
& aOriginScope
, const Nullable
<Client::Type
>& aClientType
,
2185 bool aExclusive
, bool aInternal
, OpenDirectoryListener
* aOpenListener
)
2186 : mQuotaManager(aQuotaManager
),
2187 mPersistenceType(aPersistenceType
),
2189 mOriginScope(aOriginScope
),
2190 mClientType(aClientType
),
2191 mOpenListener(aOpenListener
),
2192 mExclusive(aExclusive
),
2193 mInternal(aInternal
),
2194 mInvalidated(false) {
2195 AssertIsOnOwningThread();
2196 MOZ_ASSERT(aQuotaManager
);
2197 MOZ_ASSERT_IF(aOriginScope
.IsOrigin(), !aOriginScope
.GetOrigin().IsEmpty());
2198 MOZ_ASSERT_IF(!aInternal
, !aPersistenceType
.IsNull());
2199 MOZ_ASSERT_IF(!aInternal
,
2200 aPersistenceType
.Value() != PERSISTENCE_TYPE_INVALID
);
2201 MOZ_ASSERT_IF(!aInternal
, !aGroup
.IsEmpty());
2202 MOZ_ASSERT_IF(!aInternal
, aOriginScope
.IsOrigin());
2203 MOZ_ASSERT_IF(!aInternal
, !aClientType
.IsNull());
2204 MOZ_ASSERT_IF(!aInternal
, aClientType
.Value() < Client::TypeMax());
2205 MOZ_ASSERT_IF(!aInternal
, aOpenListener
);
2208 DirectoryLockImpl::~DirectoryLockImpl() {
2209 AssertIsOnOwningThread();
2210 MOZ_ASSERT(mQuotaManager
);
2212 for (DirectoryLockImpl
* blockingLock
: mBlocking
) {
2213 blockingLock
->MaybeUnblock(this);
2218 mQuotaManager
->UnregisterDirectoryLock(this);
2223 void DirectoryLockImpl::AssertIsOnOwningThread() const {
2224 MOZ_ASSERT(mQuotaManager
);
2225 mQuotaManager
->AssertIsOnOwningThread();
2230 bool DirectoryLockImpl::MustWaitFor(const DirectoryLockImpl
& aExistingLock
) {
2231 AssertIsOnOwningThread();
2233 // Waiting is never required if the ops in comparison represent shared locks.
2234 if (!aExistingLock
.mExclusive
&& !mExclusive
) {
2238 // If the persistence types don't overlap, the op can proceed.
2239 if (!aExistingLock
.mPersistenceType
.IsNull() && !mPersistenceType
.IsNull() &&
2240 aExistingLock
.mPersistenceType
.Value() != mPersistenceType
.Value()) {
2244 // If the origin scopes don't overlap, the op can proceed.
2245 bool match
= aExistingLock
.mOriginScope
.Matches(mOriginScope
);
2250 // If the client types don't overlap, the op can proceed.
2251 if (!aExistingLock
.mClientType
.IsNull() && !mClientType
.IsNull() &&
2252 aExistingLock
.mClientType
.Value() != mClientType
.Value()) {
2256 // Otherwise, when all attributes overlap (persistence type, origin scope and
2257 // client type) the op must wait.
2261 void DirectoryLockImpl::NotifyOpenListener() {
2262 AssertIsOnOwningThread();
2263 MOZ_ASSERT(mQuotaManager
);
2264 MOZ_ASSERT(mOpenListener
);
2267 mOpenListener
->DirectoryLockFailed();
2269 mOpenListener
->DirectoryLockAcquired(this);
2272 mOpenListener
= nullptr;
2274 mQuotaManager
->RemovePendingDirectoryLock(this);
2277 nsresult
QuotaManager::CreateRunnable::Init() {
2278 MOZ_ASSERT(NS_IsMainThread());
2279 MOZ_ASSERT(mState
== State::Initial
);
2283 nsCOMPtr
<nsIFile
> baseDir
;
2284 rv
= NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR
,
2285 getter_AddRefs(baseDir
));
2286 if (NS_FAILED(rv
)) {
2287 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
2288 getter_AddRefs(baseDir
));
2290 if (NS_WARN_IF(NS_FAILED(rv
))) {
2294 rv
= baseDir
->GetPath(mBaseDirPath
);
2295 if (NS_WARN_IF(NS_FAILED(rv
))) {
2299 Unused
<< NextGenLocalStorageEnabled();
2304 nsresult
QuotaManager::CreateRunnable::CreateManager() {
2305 AssertIsOnOwningThread();
2306 MOZ_ASSERT(mState
== State::CreatingManager
);
2308 mManager
= new QuotaManager();
2310 nsresult rv
= mManager
->Init(mBaseDirPath
);
2311 if (NS_WARN_IF(NS_FAILED(rv
))) {
2318 nsresult
QuotaManager::CreateRunnable::RegisterObserver() {
2319 MOZ_ASSERT(NS_IsMainThread());
2320 MOZ_ASSERT(mState
== State::RegisteringObserver
);
2322 if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB
, PREF_FIXED_LIMIT
,
2323 kDefaultFixedLimitKB
)) ||
2324 NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB
, PREF_CHUNK_SIZE
,
2325 kDefaultChunkSizeKB
))) {
2326 NS_WARNING("Unable to respond to temp storage pref changes!");
2329 if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled
,
2330 PREF_TESTING_FEATURES
, false))) {
2331 NS_WARNING("Unable to respond to testing pref changes!");
2334 nsCOMPtr
<nsIObserverService
> observerService
=
2335 mozilla::services::GetObserverService();
2336 if (NS_WARN_IF(!observerService
)) {
2337 return NS_ERROR_FAILURE
;
2340 nsCOMPtr
<nsIObserver
> observer
= new ShutdownObserver(mOwningThread
);
2342 nsresult rv
= observerService
->AddObserver(
2343 observer
, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID
, false);
2344 if (NS_WARN_IF(NS_FAILED(rv
))) {
2348 // This service has to be started on the main thread currently.
2349 nsCOMPtr
<mozIStorageService
> ss
=
2350 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID
, &rv
);
2351 if (NS_WARN_IF(NS_FAILED(rv
))) {
2355 QuotaManagerService
* qms
= QuotaManagerService::GetOrCreate();
2356 if (NS_WARN_IF(!qms
)) {
2360 qms
->NoteLiveManager(mManager
);
2362 for (RefPtr
<Client
>& client
: mManager
->mClients
) {
2363 client
->DidInitialize(mManager
);
2369 void QuotaManager::CreateRunnable::CallCallbacks() {
2370 AssertIsOnOwningThread();
2371 MOZ_ASSERT(mState
== State::CallingCallbacks
);
2373 gCreateRunnable
= nullptr;
2375 if (NS_FAILED(mResultCode
)) {
2376 gCreateFailed
= true;
2378 gInstance
= mManager
;
2383 nsTArray
<nsCOMPtr
<nsIRunnable
>> callbacks
;
2384 mCallbacks
.SwapElements(callbacks
);
2386 for (nsCOMPtr
<nsIRunnable
>& callback
: callbacks
) {
2387 Unused
<< callback
->Run();
2391 auto QuotaManager::CreateRunnable::GetNextState(
2392 nsCOMPtr
<nsIEventTarget
>& aThread
) -> State
{
2394 case State::Initial
:
2395 aThread
= mOwningThread
;
2396 return State::CreatingManager
;
2397 case State::CreatingManager
:
2398 if (mMainEventTarget
) {
2399 aThread
= mMainEventTarget
;
2401 aThread
= GetMainThreadEventTarget();
2403 return State::RegisteringObserver
;
2404 case State::RegisteringObserver
:
2405 aThread
= mOwningThread
;
2406 return State::CallingCallbacks
;
2407 case State::CallingCallbacks
:
2409 return State::Completed
;
2411 MOZ_CRASH("Bad state!");
2416 QuotaManager::CreateRunnable::Run() {
2420 case State::Initial
:
2424 case State::CreatingManager
:
2425 rv
= CreateManager();
2428 case State::RegisteringObserver
:
2429 rv
= RegisterObserver();
2432 case State::CallingCallbacks
:
2437 case State::Completed
:
2439 MOZ_CRASH("Bad state!");
2442 nsCOMPtr
<nsIEventTarget
> thread
;
2443 if (NS_WARN_IF(NS_FAILED(rv
))) {
2444 if (NS_SUCCEEDED(mResultCode
)) {
2448 mState
= State::CallingCallbacks
;
2449 thread
= mOwningThread
;
2451 mState
= GetNextState(thread
);
2455 MOZ_ALWAYS_SUCCEEDS(thread
->Dispatch(this, NS_DISPATCH_NORMAL
));
2462 QuotaManager::ShutdownRunnable::Run() {
2463 if (NS_IsMainThread()) {
2469 AssertIsOnBackgroundThread();
2471 RefPtr
<QuotaManager
> quotaManager
= gInstance
.get();
2473 quotaManager
->Shutdown();
2475 gInstance
= nullptr;
2478 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
2483 NS_IMPL_ISUPPORTS(QuotaManager::ShutdownObserver
, nsIObserver
)
2486 QuotaManager::ShutdownObserver::Observe(nsISupports
* aSubject
,
2488 const char16_t
* aData
) {
2489 MOZ_ASSERT(NS_IsMainThread());
2490 MOZ_ASSERT(!strcmp(aTopic
, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID
));
2491 MOZ_ASSERT(gInstance
);
2493 nsCOMPtr
<nsIObserverService
> observerService
=
2494 mozilla::services::GetObserverService();
2495 if (NS_WARN_IF(!observerService
)) {
2496 return NS_ERROR_FAILURE
;
2499 // Unregister ourselves from the observer service first to make sure the
2500 // nested event loop below will not cause re-entrancy issues.
2501 Unused
<< observerService
->RemoveObserver(
2502 this, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID
);
2504 QuotaManagerService
* qms
= QuotaManagerService::Get();
2507 qms
->NoteShuttingDownManager();
2509 for (RefPtr
<Client
>& client
: gInstance
->mClients
) {
2510 client
->WillShutdown();
2515 RefPtr
<ShutdownRunnable
> shutdownRunnable
= new ShutdownRunnable(done
);
2516 MOZ_ALWAYS_SUCCEEDS(
2517 mBackgroundThread
->Dispatch(shutdownRunnable
, NS_DISPATCH_NORMAL
));
2519 MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return done
; }));
2524 /*******************************************************************************
2526 ******************************************************************************/
2528 void QuotaObject::AddRef() {
2529 QuotaManager
* quotaManager
= QuotaManager::Get();
2530 if (!quotaManager
) {
2531 NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
2538 MutexAutoLock
lock(quotaManager
->mQuotaMutex
);
2543 void QuotaObject::Release() {
2544 QuotaManager
* quotaManager
= QuotaManager::Get();
2545 if (!quotaManager
) {
2546 NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
2548 nsrefcnt count
= --mRefCnt
;
2558 MutexAutoLock
lock(quotaManager
->mQuotaMutex
);
2567 mOriginInfo
->mQuotaObjects
.Remove(mPath
);
2574 bool QuotaObject::MaybeUpdateSize(int64_t aSize
, bool aTruncate
) {
2575 QuotaManager
* quotaManager
= QuotaManager::Get();
2576 MOZ_ASSERT(quotaManager
);
2578 MutexAutoLock
lock(quotaManager
->mQuotaMutex
);
2580 return LockedMaybeUpdateSize(aSize
, aTruncate
);
2583 bool QuotaObject::IncreaseSize(int64_t aDelta
) {
2584 MOZ_ASSERT(aDelta
>= 0);
2586 QuotaManager
* quotaManager
= QuotaManager::Get();
2587 MOZ_ASSERT(quotaManager
);
2589 MutexAutoLock
lock(quotaManager
->mQuotaMutex
);
2591 AssertNoOverflow(mSize
, aDelta
);
2592 int64_t size
= mSize
+ aDelta
;
2594 return LockedMaybeUpdateSize(size
, /* aTruncate */ false);
2597 void QuotaObject::DisableQuotaCheck() {
2598 QuotaManager
* quotaManager
= QuotaManager::Get();
2599 MOZ_ASSERT(quotaManager
);
2601 MutexAutoLock
lock(quotaManager
->mQuotaMutex
);
2603 mQuotaCheckDisabled
= true;
2606 void QuotaObject::EnableQuotaCheck() {
2607 QuotaManager
* quotaManager
= QuotaManager::Get();
2608 MOZ_ASSERT(quotaManager
);
2610 MutexAutoLock
lock(quotaManager
->mQuotaMutex
);
2612 mQuotaCheckDisabled
= false;
2615 bool QuotaObject::LockedMaybeUpdateSize(int64_t aSize
, bool aTruncate
) {
2616 QuotaManager
* quotaManager
= QuotaManager::Get();
2617 MOZ_ASSERT(quotaManager
);
2619 quotaManager
->mQuotaMutex
.AssertCurrentThreadOwns();
2621 if (mWritingDone
== false && mOriginInfo
) {
2622 mWritingDone
= true;
2623 StorageActivityService::SendActivity(mOriginInfo
->mOrigin
);
2626 if (mQuotaCheckDisabled
) {
2630 if (mSize
== aSize
) {
2639 GroupInfo
* groupInfo
= mOriginInfo
->mGroupInfo
;
2640 MOZ_ASSERT(groupInfo
);
2642 if (mSize
> aSize
) {
2644 const int64_t delta
= mSize
- aSize
;
2646 AssertNoUnderflow(quotaManager
->mTemporaryStorageUsage
, delta
);
2647 quotaManager
->mTemporaryStorageUsage
-= delta
;
2649 if (!mOriginInfo
->LockedPersisted()) {
2650 AssertNoUnderflow(groupInfo
->mUsage
, delta
);
2651 groupInfo
->mUsage
-= delta
;
2654 AssertNoUnderflow(mOriginInfo
->mUsage
, delta
);
2655 mOriginInfo
->mUsage
-= delta
;
2662 MOZ_ASSERT(mSize
< aSize
);
2664 RefPtr
<GroupInfo
> complementaryGroupInfo
=
2665 groupInfo
->mGroupInfoPair
->LockedGetGroupInfo(
2666 ComplementaryPersistenceType(groupInfo
->mPersistenceType
));
2668 uint64_t delta
= aSize
- mSize
;
2670 AssertNoOverflow(mOriginInfo
->mUsage
, delta
);
2671 uint64_t newUsage
= mOriginInfo
->mUsage
+ delta
;
2673 // Temporary storage has no limit for origin usage (there's a group and the
2674 // global limit though).
2676 uint64_t newGroupUsage
= groupInfo
->mUsage
;
2677 if (!mOriginInfo
->LockedPersisted()) {
2678 AssertNoOverflow(groupInfo
->mUsage
, delta
);
2679 newGroupUsage
+= delta
;
2681 uint64_t groupUsage
= groupInfo
->mUsage
;
2682 if (complementaryGroupInfo
) {
2683 AssertNoOverflow(groupUsage
, complementaryGroupInfo
->mUsage
);
2684 groupUsage
+= complementaryGroupInfo
->mUsage
;
2687 // Temporary storage has a hard limit for group usage (20 % of the global
2689 AssertNoOverflow(groupUsage
, delta
);
2690 if (groupUsage
+ delta
> quotaManager
->GetGroupLimit()) {
2695 AssertNoOverflow(quotaManager
->mTemporaryStorageUsage
, delta
);
2696 uint64_t newTemporaryStorageUsage
=
2697 quotaManager
->mTemporaryStorageUsage
+ delta
;
2699 if (newTemporaryStorageUsage
> quotaManager
->mTemporaryStorageLimit
) {
2700 // This will block the thread without holding the lock while waitting.
2702 AutoTArray
<RefPtr
<DirectoryLockImpl
>, 10> locks
;
2703 uint64_t sizeToBeFreed
;
2705 if (IsOnBackgroundThread()) {
2706 MutexAutoUnlock
autoUnlock(quotaManager
->mQuotaMutex
);
2708 sizeToBeFreed
= quotaManager
->CollectOriginsForEviction(delta
, locks
);
2711 quotaManager
->LockedCollectOriginsForEviction(delta
, locks
);
2714 if (!sizeToBeFreed
) {
2715 uint64_t usage
= quotaManager
->mTemporaryStorageUsage
;
2717 MutexAutoUnlock
autoUnlock(quotaManager
->mQuotaMutex
);
2719 quotaManager
->NotifyStoragePressure(usage
);
2724 NS_ASSERTION(sizeToBeFreed
>= delta
, "Huh?");
2727 MutexAutoUnlock
autoUnlock(quotaManager
->mQuotaMutex
);
2729 for (RefPtr
<DirectoryLockImpl
>& lock
: locks
) {
2730 MOZ_ASSERT(!lock
->GetPersistenceType().IsNull());
2731 MOZ_ASSERT(lock
->GetOriginScope().IsOrigin());
2732 MOZ_ASSERT(!lock
->GetOriginScope().GetOrigin().IsEmpty());
2734 quotaManager
->DeleteFilesForOrigin(lock
->GetPersistenceType().Value(),
2735 lock
->GetOriginScope().GetOrigin());
2741 NS_ASSERTION(mOriginInfo
, "How come?!");
2743 for (DirectoryLockImpl
* lock
: locks
) {
2744 MOZ_ASSERT(!lock
->GetPersistenceType().IsNull());
2745 MOZ_ASSERT(!lock
->GetGroup().IsEmpty());
2746 MOZ_ASSERT(lock
->GetOriginScope().IsOrigin());
2747 MOZ_ASSERT(!lock
->GetOriginScope().GetOrigin().IsEmpty());
2749 !(lock
->GetOriginScope().GetOrigin() == mOriginInfo
->mOrigin
&&
2750 lock
->GetPersistenceType().Value() == groupInfo
->mPersistenceType
),
2753 quotaManager
->LockedRemoveQuotaForOrigin(
2754 lock
->GetPersistenceType().Value(), lock
->GetGroup(),
2755 lock
->GetOriginScope().GetOrigin());
2758 // We unlocked and relocked several times so we need to recompute all the
2759 // essential variables and recheck the group limit.
2761 AssertNoUnderflow(aSize
, mSize
);
2762 delta
= aSize
- mSize
;
2764 AssertNoOverflow(mOriginInfo
->mUsage
, delta
);
2765 newUsage
= mOriginInfo
->mUsage
+ delta
;
2767 newGroupUsage
= groupInfo
->mUsage
;
2768 if (!mOriginInfo
->LockedPersisted()) {
2769 AssertNoOverflow(groupInfo
->mUsage
, delta
);
2770 newGroupUsage
+= delta
;
2772 uint64_t groupUsage
= groupInfo
->mUsage
;
2773 if (complementaryGroupInfo
) {
2774 AssertNoOverflow(groupUsage
, complementaryGroupInfo
->mUsage
);
2775 groupUsage
+= complementaryGroupInfo
->mUsage
;
2778 AssertNoOverflow(groupUsage
, delta
);
2779 if (groupUsage
+ delta
> quotaManager
->GetGroupLimit()) {
2780 // Unfortunately some other thread increased the group usage in the
2781 // meantime and we are not below the group limit anymore.
2783 // However, the origin eviction must be finalized in this case too.
2784 MutexAutoUnlock
autoUnlock(quotaManager
->mQuotaMutex
);
2786 quotaManager
->FinalizeOriginEviction(locks
);
2792 AssertNoOverflow(quotaManager
->mTemporaryStorageUsage
, delta
);
2793 newTemporaryStorageUsage
= quotaManager
->mTemporaryStorageUsage
+ delta
;
2796 newTemporaryStorageUsage
<= quotaManager
->mTemporaryStorageLimit
,
2799 // Ok, we successfully freed enough space and the operation can continue
2800 // without throwing the quota error.
2801 mOriginInfo
->mUsage
= newUsage
;
2802 if (!mOriginInfo
->LockedPersisted()) {
2803 groupInfo
->mUsage
= newGroupUsage
;
2805 quotaManager
->mTemporaryStorageUsage
= newTemporaryStorageUsage
;
2808 // Some other thread could increase the size in the meantime, but no more
2810 MOZ_ASSERT(mSize
< aSize
);
2813 // Finally, release IO thread only objects and allow next synchronized
2814 // ops for the evicted origins.
2815 MutexAutoUnlock
autoUnlock(quotaManager
->mQuotaMutex
);
2817 quotaManager
->FinalizeOriginEviction(locks
);
2822 mOriginInfo
->mUsage
= newUsage
;
2823 if (!mOriginInfo
->LockedPersisted()) {
2824 groupInfo
->mUsage
= newGroupUsage
;
2826 quotaManager
->mTemporaryStorageUsage
= newTemporaryStorageUsage
;
2833 /*******************************************************************************
2835 ******************************************************************************/
2837 QuotaManager::QuotaManager()
2838 : mQuotaMutex("QuotaManager.mQuotaMutex"),
2839 mTemporaryStorageLimit(0),
2840 mTemporaryStorageUsage(0),
2841 mTemporaryStorageInitialized(false),
2842 mStorageInitialized(false) {
2843 AssertIsOnOwningThread();
2844 MOZ_ASSERT(!gInstance
);
2847 QuotaManager::~QuotaManager() {
2848 AssertIsOnOwningThread();
2849 MOZ_ASSERT(!gInstance
|| gInstance
== this);
2852 void QuotaManager::GetOrCreate(nsIRunnable
* aCallback
,
2853 nsIEventTarget
* aMainEventTarget
) {
2854 AssertIsOnBackgroundThread();
2856 if (IsShuttingDown()) {
2857 MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!");
2861 if (gInstance
|| gCreateFailed
) {
2862 MOZ_ASSERT(!gCreateRunnable
);
2863 MOZ_ASSERT_IF(gCreateFailed
, !gInstance
);
2865 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(aCallback
));
2867 if (!gCreateRunnable
) {
2868 gCreateRunnable
= new CreateRunnable(aMainEventTarget
);
2869 if (aMainEventTarget
) {
2870 MOZ_ALWAYS_SUCCEEDS(
2871 aMainEventTarget
->Dispatch(gCreateRunnable
, NS_DISPATCH_NORMAL
));
2873 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(gCreateRunnable
));
2877 gCreateRunnable
->AddCallback(aCallback
);
2882 QuotaManager
* QuotaManager::Get() {
2883 // Does not return an owning reference.
2888 bool QuotaManager::IsShuttingDown() { return gShutdown
; }
2891 bool QuotaManager::IsOSMetadata(const nsAString
& aFileName
) {
2892 return aFileName
.EqualsLiteral(DSSTORE_FILE_NAME
) ||
2893 aFileName
.EqualsLiteral(DESKTOP_FILE_NAME
) ||
2894 aFileName
.LowerCaseEqualsLiteral(DESKTOP_INI_FILE_NAME
) ||
2895 aFileName
.EqualsLiteral(THUMBS_DB_FILE_NAME
);
2899 bool QuotaManager::IsDotFile(const nsAString
& aFileName
) {
2900 return aFileName
.First() == char16_t('.');
2903 auto QuotaManager::CreateDirectoryLock(
2904 const Nullable
<PersistenceType
>& aPersistenceType
, const nsACString
& aGroup
,
2905 const OriginScope
& aOriginScope
, const Nullable
<Client::Type
>& aClientType
,
2906 bool aExclusive
, bool aInternal
, OpenDirectoryListener
* aOpenListener
)
2907 -> already_AddRefed
<DirectoryLockImpl
> {
2908 AssertIsOnOwningThread();
2909 MOZ_ASSERT_IF(aOriginScope
.IsOrigin(), !aOriginScope
.GetOrigin().IsEmpty());
2910 MOZ_ASSERT_IF(!aInternal
, !aPersistenceType
.IsNull());
2911 MOZ_ASSERT_IF(!aInternal
,
2912 aPersistenceType
.Value() != PERSISTENCE_TYPE_INVALID
);
2913 MOZ_ASSERT_IF(!aInternal
, !aGroup
.IsEmpty());
2914 MOZ_ASSERT_IF(!aInternal
, aOriginScope
.IsOrigin());
2915 MOZ_ASSERT_IF(!aInternal
, !aClientType
.IsNull());
2916 MOZ_ASSERT_IF(!aInternal
, aClientType
.Value() < Client::TypeMax());
2917 MOZ_ASSERT_IF(!aInternal
, aOpenListener
);
2919 RefPtr
<DirectoryLockImpl
> lock
=
2920 new DirectoryLockImpl(this, aPersistenceType
, aGroup
, aOriginScope
,
2921 aClientType
, aExclusive
, aInternal
, aOpenListener
);
2923 mPendingDirectoryLocks
.AppendElement(lock
);
2925 // See if this lock needs to wait.
2926 bool blocked
= false;
2927 for (uint32_t index
= mDirectoryLocks
.Length(); index
> 0; index
--) {
2928 DirectoryLockImpl
* existingLock
= mDirectoryLocks
[index
- 1];
2929 if (lock
->MustWaitFor(*existingLock
)) {
2930 existingLock
->AddBlockingLock(lock
);
2931 lock
->AddBlockedOnLock(existingLock
);
2936 RegisterDirectoryLock(lock
);
2938 // Otherwise, notify the open listener immediately.
2940 lock
->NotifyOpenListener();
2943 return lock
.forget();
2946 auto QuotaManager::CreateDirectoryLockForEviction(
2947 PersistenceType aPersistenceType
, const nsACString
& aGroup
,
2948 const nsACString
& aOrigin
) -> already_AddRefed
<DirectoryLockImpl
> {
2949 AssertIsOnOwningThread();
2950 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_INVALID
);
2951 MOZ_ASSERT(!aOrigin
.IsEmpty());
2953 RefPtr
<DirectoryLockImpl
> lock
= new DirectoryLockImpl(
2954 this, Nullable
<PersistenceType
>(aPersistenceType
), aGroup
,
2955 OriginScope::FromOrigin(aOrigin
), Nullable
<Client::Type
>(),
2956 /* aExclusive */ true,
2957 /* aInternal */ true, nullptr);
2960 for (uint32_t index
= mDirectoryLocks
.Length(); index
> 0; index
--) {
2961 DirectoryLockImpl
* existingLock
= mDirectoryLocks
[index
- 1];
2962 MOZ_ASSERT(!lock
->MustWaitFor(*existingLock
));
2966 RegisterDirectoryLock(lock
);
2968 return lock
.forget();
2971 void QuotaManager::RegisterDirectoryLock(DirectoryLockImpl
* aLock
) {
2972 AssertIsOnOwningThread();
2975 mDirectoryLocks
.AppendElement(aLock
);
2977 if (aLock
->ShouldUpdateLockTable()) {
2978 const Nullable
<PersistenceType
>& persistenceType
=
2979 aLock
->GetPersistenceType();
2980 const OriginScope
& originScope
= aLock
->GetOriginScope();
2982 MOZ_ASSERT(!persistenceType
.IsNull());
2983 MOZ_ASSERT(!aLock
->GetGroup().IsEmpty());
2984 MOZ_ASSERT(originScope
.IsOrigin());
2985 MOZ_ASSERT(!originScope
.GetOrigin().IsEmpty());
2987 DirectoryLockTable
& directoryLockTable
=
2988 GetDirectoryLockTable(persistenceType
.Value());
2990 nsTArray
<DirectoryLockImpl
*>* array
;
2991 if (!directoryLockTable
.Get(originScope
.GetOrigin(), &array
)) {
2992 array
= new nsTArray
<DirectoryLockImpl
*>();
2993 directoryLockTable
.Put(originScope
.GetOrigin(), array
);
2995 if (!IsShuttingDown()) {
2996 UpdateOriginAccessTime(persistenceType
.Value(), aLock
->GetGroup(),
2997 originScope
.GetOrigin());
3000 array
->AppendElement(aLock
);
3004 void QuotaManager::UnregisterDirectoryLock(DirectoryLockImpl
* aLock
) {
3005 AssertIsOnOwningThread();
3007 MOZ_ALWAYS_TRUE(mDirectoryLocks
.RemoveElement(aLock
));
3009 if (aLock
->ShouldUpdateLockTable()) {
3010 const Nullable
<PersistenceType
>& persistenceType
=
3011 aLock
->GetPersistenceType();
3012 const OriginScope
& originScope
= aLock
->GetOriginScope();
3014 MOZ_ASSERT(!persistenceType
.IsNull());
3015 MOZ_ASSERT(!aLock
->GetGroup().IsEmpty());
3016 MOZ_ASSERT(originScope
.IsOrigin());
3017 MOZ_ASSERT(!originScope
.GetOrigin().IsEmpty());
3019 DirectoryLockTable
& directoryLockTable
=
3020 GetDirectoryLockTable(persistenceType
.Value());
3022 nsTArray
<DirectoryLockImpl
*>* array
;
3023 MOZ_ALWAYS_TRUE(directoryLockTable
.Get(originScope
.GetOrigin(), &array
));
3025 MOZ_ALWAYS_TRUE(array
->RemoveElement(aLock
));
3026 if (array
->IsEmpty()) {
3027 directoryLockTable
.Remove(originScope
.GetOrigin());
3029 if (!IsShuttingDown()) {
3030 UpdateOriginAccessTime(persistenceType
.Value(), aLock
->GetGroup(),
3031 originScope
.GetOrigin());
3037 void QuotaManager::RemovePendingDirectoryLock(DirectoryLockImpl
* aLock
) {
3038 AssertIsOnOwningThread();
3041 MOZ_ALWAYS_TRUE(mPendingDirectoryLocks
.RemoveElement(aLock
));
3044 uint64_t QuotaManager::CollectOriginsForEviction(
3045 uint64_t aMinSizeToBeFreed
, nsTArray
<RefPtr
<DirectoryLockImpl
>>& aLocks
) {
3046 AssertIsOnOwningThread();
3047 MOZ_ASSERT(aLocks
.IsEmpty());
3049 struct MOZ_STACK_CLASS Helper final
{
3050 static void GetInactiveOriginInfos(
3051 nsTArray
<RefPtr
<OriginInfo
>>& aOriginInfos
,
3052 nsTArray
<DirectoryLockImpl
*>& aLocks
,
3053 nsTArray
<OriginInfo
*>& aInactiveOriginInfos
) {
3054 for (OriginInfo
* originInfo
: aOriginInfos
) {
3055 MOZ_ASSERT(originInfo
->mGroupInfo
->mPersistenceType
!=
3056 PERSISTENCE_TYPE_PERSISTENT
);
3058 if (originInfo
->LockedPersisted()) {
3062 OriginScope originScope
= OriginScope::FromOrigin(originInfo
->mOrigin
);
3065 for (uint32_t j
= aLocks
.Length(); j
> 0; j
--) {
3066 DirectoryLockImpl
* lock
= aLocks
[j
- 1];
3067 if (originScope
.Matches(lock
->GetOriginScope())) {
3074 MOZ_ASSERT(!originInfo
->mQuotaObjects
.Count(),
3075 "Inactive origin shouldn't have open files!");
3076 aInactiveOriginInfos
.InsertElementSorted(originInfo
,
3077 OriginInfoLRUComparator());
3083 // Split locks into separate arrays and filter out locks for persistent
3084 // storage, they can't block us.
3085 nsTArray
<DirectoryLockImpl
*> temporaryStorageLocks
;
3086 nsTArray
<DirectoryLockImpl
*> defaultStorageLocks
;
3087 for (DirectoryLockImpl
* lock
: mDirectoryLocks
) {
3088 const Nullable
<PersistenceType
>& persistenceType
=
3089 lock
->GetPersistenceType();
3091 if (persistenceType
.IsNull()) {
3092 temporaryStorageLocks
.AppendElement(lock
);
3093 defaultStorageLocks
.AppendElement(lock
);
3094 } else if (persistenceType
.Value() == PERSISTENCE_TYPE_TEMPORARY
) {
3095 temporaryStorageLocks
.AppendElement(lock
);
3096 } else if (persistenceType
.Value() == PERSISTENCE_TYPE_DEFAULT
) {
3097 defaultStorageLocks
.AppendElement(lock
);
3099 MOZ_ASSERT(persistenceType
.Value() == PERSISTENCE_TYPE_PERSISTENT
);
3101 // Do nothing here, persistent origins don't need to be collected ever.
3105 nsTArray
<OriginInfo
*> inactiveOrigins
;
3107 // Enumerate and process inactive origins. This must be protected by the
3109 MutexAutoLock
lock(mQuotaMutex
);
3111 for (auto iter
= mGroupInfoPairs
.Iter(); !iter
.Done(); iter
.Next()) {
3112 GroupInfoPair
* pair
= iter
.UserData();
3114 MOZ_ASSERT(!iter
.Key().IsEmpty());
3117 RefPtr
<GroupInfo
> groupInfo
=
3118 pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY
);
3120 Helper::GetInactiveOriginInfos(groupInfo
->mOriginInfos
,
3121 temporaryStorageLocks
, inactiveOrigins
);
3124 groupInfo
= pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT
);
3126 Helper::GetInactiveOriginInfos(groupInfo
->mOriginInfos
,
3127 defaultStorageLocks
, inactiveOrigins
);
3132 // Make sure the array is sorted correctly.
3133 for (uint32_t index
= inactiveOrigins
.Length(); index
> 1; index
--) {
3134 MOZ_ASSERT(inactiveOrigins
[index
- 1]->mAccessTime
>=
3135 inactiveOrigins
[index
- 2]->mAccessTime
);
3139 // Create a list of inactive and the least recently used origins
3140 // whose aggregate size is greater or equals the minimal size to be freed.
3141 uint64_t sizeToBeFreed
= 0;
3142 for (uint32_t count
= inactiveOrigins
.Length(), index
= 0; index
< count
;
3144 if (sizeToBeFreed
>= aMinSizeToBeFreed
) {
3145 inactiveOrigins
.TruncateLength(index
);
3149 sizeToBeFreed
+= inactiveOrigins
[index
]->mUsage
;
3152 if (sizeToBeFreed
>= aMinSizeToBeFreed
) {
3153 // Success, add directory locks for these origins, so any other
3154 // operations for them will be delayed (until origin eviction is finalized).
3156 for (OriginInfo
* originInfo
: inactiveOrigins
) {
3157 RefPtr
<DirectoryLockImpl
> lock
= CreateDirectoryLockForEviction(
3158 originInfo
->mGroupInfo
->mPersistenceType
,
3159 originInfo
->mGroupInfo
->mGroup
, originInfo
->mOrigin
);
3160 aLocks
.AppendElement(lock
.forget());
3163 return sizeToBeFreed
;
3169 nsresult
QuotaManager::Init(const nsAString
& aBasePath
) {
3170 mBasePath
= aBasePath
;
3172 nsCOMPtr
<nsIFile
> baseDir
;
3173 nsresult rv
= NS_NewLocalFile(aBasePath
, false, getter_AddRefs(baseDir
));
3174 if (NS_WARN_IF(NS_FAILED(rv
))) {
3178 rv
= CloneStoragePath(baseDir
, NS_LITERAL_STRING(INDEXEDDB_DIRECTORY_NAME
),
3180 if (NS_WARN_IF(NS_FAILED(rv
))) {
3184 rv
= baseDir
->Append(NS_LITERAL_STRING(STORAGE_DIRECTORY_NAME
));
3185 if (NS_WARN_IF(NS_FAILED(rv
))) {
3189 rv
= baseDir
->GetPath(mStoragePath
);
3190 if (NS_WARN_IF(NS_FAILED(rv
))) {
3194 rv
= CloneStoragePath(baseDir
, NS_LITERAL_STRING(PERMANENT_DIRECTORY_NAME
),
3195 mPermanentStoragePath
);
3196 if (NS_WARN_IF(NS_FAILED(rv
))) {
3200 rv
= CloneStoragePath(baseDir
, NS_LITERAL_STRING(TEMPORARY_DIRECTORY_NAME
),
3201 mTemporaryStoragePath
);
3202 if (NS_WARN_IF(NS_FAILED(rv
))) {
3206 rv
= CloneStoragePath(baseDir
, NS_LITERAL_STRING(DEFAULT_DIRECTORY_NAME
),
3207 mDefaultStoragePath
);
3208 if (NS_WARN_IF(NS_FAILED(rv
))) {
3212 rv
= NS_NewNamedThread("QuotaManager IO", getter_AddRefs(mIOThread
));
3213 if (NS_WARN_IF(NS_FAILED(rv
))) {
3217 // Make a timer here to avoid potential failures later. We don't actually
3218 // initialize the timer until shutdown.
3219 mShutdownTimer
= NS_NewTimer();
3220 if (NS_WARN_IF(!mShutdownTimer
)) {
3221 return NS_ERROR_FAILURE
;
3224 static_assert(Client::IDB
== 0 && Client::ASMJS
== 1 &&
3225 Client::DOMCACHE
== 2 && Client::SDB
== 3 &&
3226 Client::LS
== 4 && Client::TYPE_MAX
== 5,
3227 "Fix the registration!");
3229 MOZ_ASSERT(mClients
.Capacity() == Client::TYPE_MAX
,
3230 "Should be using an auto array with correct capacity!");
3232 // Register clients.
3233 mClients
.AppendElement(indexedDB::CreateQuotaClient());
3234 mClients
.AppendElement(asmjscache::CreateClient());
3235 mClients
.AppendElement(cache::CreateQuotaClient());
3236 mClients
.AppendElement(simpledb::CreateQuotaClient());
3237 if (CachedNextGenLocalStorageEnabled()) {
3238 mClients
.AppendElement(localstorage::CreateQuotaClient());
3240 mClients
.SetLength(Client::TypeMax());
3246 void QuotaManager::Shutdown() {
3247 AssertIsOnOwningThread();
3249 // Setting this flag prevents the service from being recreated and prevents
3250 // further storagess from being created.
3251 if (gShutdown
.exchange(true)) {
3252 NS_ERROR("Shutdown more than once?!");
3255 StopIdleMaintenance();
3257 // Kick off the shutdown timer.
3258 MOZ_ALWAYS_SUCCEEDS(mShutdownTimer
->InitWithNamedFuncCallback(
3259 &ShutdownTimerCallback
, this, DEFAULT_SHUTDOWN_TIMER_MS
,
3260 nsITimer::TYPE_ONE_SHOT
, "QuotaManager::ShutdownTimerCallback"));
3262 // Each client will spin the event loop while we wait on all the threads
3263 // to close. Our timer may fire during that loop.
3264 for (uint32_t index
= 0; index
< uint32_t(Client::TypeMax()); index
++) {
3265 mClients
[index
]->ShutdownWorkThreads();
3268 // Cancel the timer regardless of whether it actually fired.
3269 if (NS_FAILED(mShutdownTimer
->Cancel())) {
3270 NS_WARNING("Failed to cancel shutdown timer!");
3273 // NB: It's very important that runnable is destroyed on this thread
3274 // (i.e. after we join the IO thread) because we can't release the
3275 // QuotaManager on the IO thread. This should probably use
3276 // NewNonOwningRunnableMethod ...
3277 RefPtr
<Runnable
> runnable
=
3278 NewRunnableMethod("dom::quota::QuotaManager::ReleaseIOThreadObjects",
3279 this, &QuotaManager::ReleaseIOThreadObjects
);
3280 MOZ_ASSERT(runnable
);
3282 // Give clients a chance to cleanup IO thread only objects.
3283 if (NS_FAILED(mIOThread
->Dispatch(runnable
, NS_DISPATCH_NORMAL
))) {
3284 NS_WARNING("Failed to dispatch runnable!");
3287 // Make sure to join with our IO thread.
3288 if (NS_FAILED(mIOThread
->Shutdown())) {
3289 NS_WARNING("Failed to shutdown IO thread!");
3292 for (RefPtr
<DirectoryLockImpl
>& lock
: mPendingDirectoryLocks
) {
3297 void QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType
,
3298 const nsACString
& aGroup
,
3299 const nsACString
& aOrigin
,
3300 uint64_t aUsageBytes
, int64_t aAccessTime
,
3302 AssertIsOnIOThread();
3303 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
);
3305 MutexAutoLock
lock(mQuotaMutex
);
3307 GroupInfoPair
* pair
;
3308 if (!mGroupInfoPairs
.Get(aGroup
, &pair
)) {
3309 pair
= new GroupInfoPair();
3310 mGroupInfoPairs
.Put(aGroup
, pair
);
3311 // The hashtable is now responsible to delete the GroupInfoPair.
3314 RefPtr
<GroupInfo
> groupInfo
= pair
->LockedGetGroupInfo(aPersistenceType
);
3316 groupInfo
= new GroupInfo(pair
, aPersistenceType
, aGroup
);
3317 pair
->LockedSetGroupInfo(aPersistenceType
, groupInfo
);
3320 RefPtr
<OriginInfo
> originInfo
=
3321 new OriginInfo(groupInfo
, aOrigin
, aUsageBytes
, aAccessTime
, aPersisted
);
3322 groupInfo
->LockedAddOriginInfo(originInfo
);
3325 void QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType
,
3326 const nsACString
& aGroup
,
3327 const nsACString
& aOrigin
,
3329 MOZ_ASSERT(!NS_IsMainThread());
3330 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
);
3332 MutexAutoLock
lock(mQuotaMutex
);
3334 GroupInfoPair
* pair
;
3335 if (!mGroupInfoPairs
.Get(aGroup
, &pair
)) {
3339 RefPtr
<GroupInfo
> groupInfo
= pair
->LockedGetGroupInfo(aPersistenceType
);
3344 RefPtr
<OriginInfo
> originInfo
= groupInfo
->LockedGetOriginInfo(aOrigin
);
3346 originInfo
->LockedDecreaseUsage(aSize
);
3350 void QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType
,
3351 const nsACString
& aGroup
,
3352 const nsACString
& aOrigin
) {
3353 AssertIsOnOwningThread();
3354 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
);
3356 MutexAutoLock
lock(mQuotaMutex
);
3358 GroupInfoPair
* pair
;
3359 if (!mGroupInfoPairs
.Get(aGroup
, &pair
)) {
3363 RefPtr
<GroupInfo
> groupInfo
= pair
->LockedGetGroupInfo(aPersistenceType
);
3368 RefPtr
<OriginInfo
> originInfo
= groupInfo
->LockedGetOriginInfo(aOrigin
);
3370 int64_t timestamp
= PR_Now();
3371 originInfo
->LockedUpdateAccessTime(timestamp
);
3373 MutexAutoUnlock
autoUnlock(mQuotaMutex
);
3375 RefPtr
<SaveOriginAccessTimeOp
> op
=
3376 new SaveOriginAccessTimeOp(aPersistenceType
, aOrigin
, timestamp
);
3378 op
->RunImmediately();
3382 void QuotaManager::RemoveQuota() {
3383 AssertIsOnIOThread();
3385 MutexAutoLock
lock(mQuotaMutex
);
3387 for (auto iter
= mGroupInfoPairs
.Iter(); !iter
.Done(); iter
.Next()) {
3388 nsAutoPtr
<GroupInfoPair
>& pair
= iter
.Data();
3390 MOZ_ASSERT(!iter
.Key().IsEmpty(), "Empty key!");
3391 MOZ_ASSERT(pair
, "Null pointer!");
3393 RefPtr
<GroupInfo
> groupInfo
=
3394 pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY
);
3396 groupInfo
->LockedRemoveOriginInfos();
3399 groupInfo
= pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT
);
3401 groupInfo
->LockedRemoveOriginInfos();
3407 NS_ASSERTION(mTemporaryStorageUsage
== 0, "Should be zero!");
3410 already_AddRefed
<QuotaObject
> QuotaManager::GetQuotaObject(
3411 PersistenceType aPersistenceType
, const nsACString
& aGroup
,
3412 const nsACString
& aOrigin
, nsIFile
* aFile
, int64_t aFileSize
,
3413 int64_t* aFileSizeOut
/* = nullptr */) {
3414 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3420 if (aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
3425 nsresult rv
= aFile
->GetPath(path
);
3426 NS_ENSURE_SUCCESS(rv
, nullptr);
3430 if (aFileSize
== -1) {
3432 rv
= aFile
->Exists(&exists
);
3433 NS_ENSURE_SUCCESS(rv
, nullptr);
3436 rv
= aFile
->GetFileSize(&fileSize
);
3437 NS_ENSURE_SUCCESS(rv
, nullptr);
3442 fileSize
= aFileSize
;
3445 // Re-escape our parameters above to make sure we get the right quota group.
3446 nsAutoCString group
;
3447 rv
= NS_EscapeURL(aGroup
, esc_Query
, group
, fallible
);
3448 NS_ENSURE_SUCCESS(rv
, nullptr);
3450 nsAutoCString origin
;
3451 rv
= NS_EscapeURL(aOrigin
, esc_Query
, origin
, fallible
);
3452 NS_ENSURE_SUCCESS(rv
, nullptr);
3454 RefPtr
<QuotaObject
> result
;
3456 MutexAutoLock
lock(mQuotaMutex
);
3458 GroupInfoPair
* pair
;
3459 if (!mGroupInfoPairs
.Get(group
, &pair
)) {
3463 RefPtr
<GroupInfo
> groupInfo
= pair
->LockedGetGroupInfo(aPersistenceType
);
3469 RefPtr
<OriginInfo
> originInfo
= groupInfo
->LockedGetOriginInfo(origin
);
3475 // We need this extra raw pointer because we can't assign to the smart
3476 // pointer directly since QuotaObject::AddRef would try to acquire the same
3478 QuotaObject
* quotaObject
;
3479 if (!originInfo
->mQuotaObjects
.Get(path
, "aObject
)) {
3480 // Create a new QuotaObject.
3481 quotaObject
= new QuotaObject(originInfo
, path
, fileSize
);
3483 // Put it to the hashtable. The hashtable is not responsible to delete
3485 originInfo
->mQuotaObjects
.Put(path
, quotaObject
);
3488 // Addref the QuotaObject and move the ownership to the result. This must
3489 // happen before we unlock!
3490 result
= quotaObject
->LockedAddRef();
3494 *aFileSizeOut
= fileSize
;
3497 // The caller becomes the owner of the QuotaObject, that is, the caller is
3498 // is responsible to delete it when the last reference is removed.
3499 return result
.forget();
3502 already_AddRefed
<QuotaObject
> QuotaManager::GetQuotaObject(
3503 PersistenceType aPersistenceType
, const nsACString
& aGroup
,
3504 const nsACString
& aOrigin
, const nsAString
& aPath
, int64_t aFileSize
,
3505 int64_t* aFileSizeOut
/* = nullptr */) {
3506 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3512 nsCOMPtr
<nsIFile
> file
;
3513 nsresult rv
= NS_NewLocalFile(aPath
, false, getter_AddRefs(file
));
3514 NS_ENSURE_SUCCESS(rv
, nullptr);
3516 return GetQuotaObject(aPersistenceType
, aGroup
, aOrigin
, file
, aFileSize
,
3520 Nullable
<bool> QuotaManager::OriginPersisted(const nsACString
& aGroup
,
3521 const nsACString
& aOrigin
) {
3522 AssertIsOnIOThread();
3524 MutexAutoLock
lock(mQuotaMutex
);
3526 RefPtr
<OriginInfo
> originInfo
=
3527 LockedGetOriginInfo(PERSISTENCE_TYPE_DEFAULT
, aGroup
, aOrigin
);
3529 return Nullable
<bool>(originInfo
->LockedPersisted());
3532 return Nullable
<bool>();
3535 void QuotaManager::PersistOrigin(const nsACString
& aGroup
,
3536 const nsACString
& aOrigin
) {
3537 AssertIsOnIOThread();
3539 MutexAutoLock
lock(mQuotaMutex
);
3541 RefPtr
<OriginInfo
> originInfo
=
3542 LockedGetOriginInfo(PERSISTENCE_TYPE_DEFAULT
, aGroup
, aOrigin
);
3543 if (originInfo
&& !originInfo
->LockedPersisted()) {
3544 originInfo
->LockedPersist();
3548 void QuotaManager::AbortOperationsForProcess(ContentParentId aContentParentId
) {
3549 AssertIsOnOwningThread();
3551 for (RefPtr
<Client
>& client
: mClients
) {
3552 client
->AbortOperationsForProcess(aContentParentId
);
3556 nsresult
QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType
,
3557 const nsACString
& aASCIIOrigin
,
3558 nsIFile
** aDirectory
) const {
3559 nsCOMPtr
<nsIFile
> directory
;
3560 nsresult rv
= NS_NewLocalFile(GetStoragePath(aPersistenceType
), false,
3561 getter_AddRefs(directory
));
3562 NS_ENSURE_SUCCESS(rv
, rv
);
3564 nsAutoCString
originSanitized(aASCIIOrigin
);
3565 SanitizeOriginString(originSanitized
);
3567 rv
= directory
->Append(NS_ConvertASCIItoUTF16(originSanitized
));
3568 NS_ENSURE_SUCCESS(rv
, rv
);
3570 directory
.forget(aDirectory
);
3574 nsresult
QuotaManager::RestoreDirectoryMetadata2(nsIFile
* aDirectory
,
3576 AssertIsOnIOThread();
3577 MOZ_ASSERT(aDirectory
);
3578 MOZ_ASSERT(mStorageInitialized
);
3580 RefPtr
<RestoreDirectoryMetadata2Helper
> helper
=
3581 new RestoreDirectoryMetadata2Helper(aDirectory
, aPersistent
);
3583 nsresult rv
= helper
->RestoreMetadata2File();
3584 if (NS_WARN_IF(NS_FAILED(rv
))) {
3591 nsresult
QuotaManager::GetDirectoryMetadata2(
3592 nsIFile
* aDirectory
, int64_t* aTimestamp
, bool* aPersisted
,
3593 nsACString
& aSuffix
, nsACString
& aGroup
, nsACString
& aOrigin
) {
3594 MOZ_ASSERT(!NS_IsMainThread());
3595 MOZ_ASSERT(aDirectory
);
3596 MOZ_ASSERT(aTimestamp
);
3597 MOZ_ASSERT(aPersisted
);
3598 MOZ_ASSERT(mStorageInitialized
);
3600 nsCOMPtr
<nsIBinaryInputStream
> binaryStream
;
3602 GetBinaryInputStream(aDirectory
, NS_LITERAL_STRING(METADATA_V2_FILE_NAME
),
3603 getter_AddRefs(binaryStream
));
3604 NS_ENSURE_SUCCESS(rv
, rv
);
3607 rv
= binaryStream
->Read64(×tamp
);
3608 NS_ENSURE_SUCCESS(rv
, rv
);
3611 rv
= binaryStream
->ReadBoolean(&persisted
);
3612 if (NS_WARN_IF(NS_FAILED(rv
))) {
3616 uint32_t reservedData1
;
3617 rv
= binaryStream
->Read32(&reservedData1
);
3618 if (NS_WARN_IF(NS_FAILED(rv
))) {
3622 uint32_t reservedData2
;
3623 rv
= binaryStream
->Read32(&reservedData2
);
3624 if (NS_WARN_IF(NS_FAILED(rv
))) {
3629 rv
= binaryStream
->ReadCString(suffix
);
3630 if (NS_WARN_IF(NS_FAILED(rv
))) {
3635 rv
= binaryStream
->ReadCString(group
);
3636 NS_ENSURE_SUCCESS(rv
, rv
);
3639 rv
= binaryStream
->ReadCString(origin
);
3640 NS_ENSURE_SUCCESS(rv
, rv
);
3642 // Currently unused (used to be isApp).
3644 rv
= binaryStream
->ReadBoolean(&dummy
);
3645 if (NS_WARN_IF(NS_FAILED(rv
))) {
3649 *aTimestamp
= timestamp
;
3650 *aPersisted
= persisted
;
3657 nsresult
QuotaManager::GetDirectoryMetadata2WithRestore(
3658 nsIFile
* aDirectory
, bool aPersistent
, int64_t* aTimestamp
,
3659 bool* aPersisted
, nsACString
& aSuffix
, nsACString
& aGroup
,
3660 nsACString
& aOrigin
, const bool aTelemetry
) {
3661 nsresult rv
= GetDirectoryMetadata2(aDirectory
, aTimestamp
, aPersisted
,
3662 aSuffix
, aGroup
, aOrigin
);
3663 if (NS_WARN_IF(NS_FAILED(rv
))) {
3664 rv
= RestoreDirectoryMetadata2(aDirectory
, aPersistent
);
3665 if (NS_WARN_IF(NS_FAILED(rv
))) {
3667 REPORT_TELEMETRY_INIT_ERR(kInternalError
, Rep_RestoreDirMeta
);
3672 rv
= GetDirectoryMetadata2(aDirectory
, aTimestamp
, aPersisted
, aSuffix
,
3674 if (NS_WARN_IF(NS_FAILED(rv
))) {
3676 REPORT_TELEMETRY_INIT_ERR(kExternalError
, Rep_GetDirMeta
);
3685 nsresult
QuotaManager::GetDirectoryMetadata2(nsIFile
* aDirectory
,
3686 int64_t* aTimestamp
,
3688 AssertIsOnIOThread();
3689 MOZ_ASSERT(aDirectory
);
3690 MOZ_ASSERT(aTimestamp
!= nullptr || aPersisted
!= nullptr);
3691 MOZ_ASSERT(mStorageInitialized
);
3693 nsCOMPtr
<nsIBinaryInputStream
> binaryStream
;
3695 GetBinaryInputStream(aDirectory
, NS_LITERAL_STRING(METADATA_V2_FILE_NAME
),
3696 getter_AddRefs(binaryStream
));
3697 if (NS_WARN_IF(NS_FAILED(rv
))) {
3702 rv
= binaryStream
->Read64(×tamp
);
3703 if (NS_WARN_IF(NS_FAILED(rv
))) {
3708 if (aPersisted
!= nullptr) {
3709 rv
= binaryStream
->ReadBoolean(&persisted
);
3710 if (NS_WARN_IF(NS_FAILED(rv
))) {
3715 if (aTimestamp
!= nullptr) {
3716 *aTimestamp
= timestamp
;
3718 if (aPersisted
!= nullptr) {
3719 *aPersisted
= persisted
;
3724 nsresult
QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile
* aDirectory
,
3726 int64_t* aTimestamp
,
3728 nsresult rv
= GetDirectoryMetadata2(aDirectory
, aTimestamp
, aPersisted
);
3729 if (NS_WARN_IF(NS_FAILED(rv
))) {
3730 rv
= RestoreDirectoryMetadata2(aDirectory
, aPersistent
);
3731 if (NS_WARN_IF(NS_FAILED(rv
))) {
3735 rv
= GetDirectoryMetadata2(aDirectory
, aTimestamp
, aPersisted
);
3736 if (NS_WARN_IF(NS_FAILED(rv
))) {
3744 nsresult
QuotaManager::InitializeRepository(PersistenceType aPersistenceType
) {
3745 MOZ_ASSERT(aPersistenceType
== PERSISTENCE_TYPE_TEMPORARY
||
3746 aPersistenceType
== PERSISTENCE_TYPE_DEFAULT
);
3748 nsCOMPtr
<nsIFile
> directory
;
3749 nsresult rv
= NS_NewLocalFile(GetStoragePath(aPersistenceType
), false,
3750 getter_AddRefs(directory
));
3751 if (NS_WARN_IF(NS_FAILED(rv
))) {
3752 REPORT_TELEMETRY_INIT_ERR(kExternalError
, Rep_NewLocalFile
);
3757 rv
= EnsureDirectory(directory
, &created
);
3758 if (NS_WARN_IF(NS_FAILED(rv
))) {
3759 REPORT_TELEMETRY_INIT_ERR(kExternalError
, Rep_EnsureDirectory
);
3763 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
3764 rv
= directory
->GetDirectoryEntries(getter_AddRefs(entries
));
3765 if (NS_WARN_IF(NS_FAILED(rv
))) {
3766 REPORT_TELEMETRY_INIT_ERR(kExternalError
, Rep_GetDirEntries
);
3770 // A keeper to defer the return only in Nightly, so that the telemetry data
3771 // for whole profile can be collected
3772 #ifdef NIGHTLY_BUILD
3773 nsresult statusKeeper
= NS_OK
;
3776 nsCOMPtr
<nsIFile
> childDirectory
;
3777 while (NS_SUCCEEDED(
3778 (rv
= entries
->GetNextFile(getter_AddRefs(childDirectory
)))) &&
3781 rv
= childDirectory
->IsDirectory(&isDirectory
);
3782 if (NS_WARN_IF(NS_FAILED(rv
))) {
3783 REPORT_TELEMETRY_INIT_ERR(kExternalError
, Rep_IsDirectory
);
3784 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
3785 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(rv
);
3790 rv
= childDirectory
->GetLeafName(leafName
);
3791 if (NS_WARN_IF(NS_FAILED(rv
))) {
3792 REPORT_TELEMETRY_INIT_ERR(kExternalError
, Rep_GetLeafName
);
3793 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
3794 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(rv
);
3797 if (IsOSMetadata(leafName
) || IsDotFile(leafName
)) {
3801 UNKNOWN_FILE_WARNING(leafName
);
3803 REPORT_TELEMETRY_INIT_ERR(kInternalError
, Rep_UnexpectedFile
);
3804 RECORD_IN_NIGHTLY(statusKeeper
, NS_ERROR_UNEXPECTED
);
3805 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(NS_ERROR_UNEXPECTED
);
3813 rv
= GetDirectoryMetadata2WithRestore(childDirectory
,
3814 /* aPersistent */ false, ×tamp
,
3815 &persisted
, suffix
, group
, origin
,
3816 /* aTelemetry */ true);
3817 if (NS_WARN_IF(NS_FAILED(rv
))) {
3818 // Error should have reported in GetDirectoryMetadata2WithRestore
3819 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
3820 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(rv
);
3823 rv
= InitializeOrigin(aPersistenceType
, group
, origin
, timestamp
, persisted
,
3825 if (NS_WARN_IF(NS_FAILED(rv
))) {
3826 // Error should have reported in InitializeOrigin
3827 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
3828 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(rv
);
3831 if (NS_WARN_IF(NS_FAILED(rv
))) {
3832 REPORT_TELEMETRY_INIT_ERR(kInternalError
, Rep_GetNextFile
);
3833 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
3834 #ifndef NIGHTLY_BUILD
3839 #ifdef NIGHTLY_BUILD
3840 if (NS_FAILED(statusKeeper
)) {
3841 return statusKeeper
;
3848 nsresult
QuotaManager::InitializeOrigin(PersistenceType aPersistenceType
,
3849 const nsACString
& aGroup
,
3850 const nsACString
& aOrigin
,
3851 int64_t aAccessTime
, bool aPersisted
,
3852 nsIFile
* aDirectory
) {
3853 AssertIsOnIOThread();
3857 bool trackQuota
= aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
;
3859 // We need to initialize directories of all clients if they exists and also
3860 // get the total usage to initialize the quota.
3861 nsAutoPtr
<UsageInfo
> usageInfo
;
3863 usageInfo
= new UsageInfo();
3866 // A keeper to defer the return only in Nightly, so that the telemetry data
3867 // for whole profile can be collected
3868 #ifdef NIGHTLY_BUILD
3869 nsresult statusKeeper
= NS_OK
;
3872 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
3873 rv
= aDirectory
->GetDirectoryEntries(getter_AddRefs(entries
));
3874 if (NS_WARN_IF(NS_FAILED(rv
))) {
3875 REPORT_TELEMETRY_INIT_ERR(kExternalError
, Ori_GetDirEntries
);
3879 nsCOMPtr
<nsIFile
> file
;
3880 while (NS_SUCCEEDED((rv
= entries
->GetNextFile(getter_AddRefs(file
)))) &&
3883 rv
= file
->IsDirectory(&isDirectory
);
3884 if (NS_WARN_IF(NS_FAILED(rv
))) {
3885 REPORT_TELEMETRY_INIT_ERR(kExternalError
, Ori_IsDirectory
);
3886 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
3887 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(rv
);
3891 rv
= file
->GetLeafName(leafName
);
3892 if (NS_WARN_IF(NS_FAILED(rv
))) {
3893 REPORT_TELEMETRY_INIT_ERR(kExternalError
, Ori_GetLeafName
);
3894 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
3895 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(rv
);
3899 if (IsOriginMetadata(leafName
)) {
3903 if (IsTempMetadata(leafName
)) {
3904 rv
= file
->Remove(/* recursive */ false);
3905 if (NS_WARN_IF(NS_FAILED(rv
))) {
3906 REPORT_TELEMETRY_INIT_ERR(kExternalError
, Ori_Remove
);
3907 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
3908 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(rv
);
3914 if (IsOSMetadata(leafName
) || IsDotFile(leafName
)) {
3918 UNKNOWN_FILE_WARNING(leafName
);
3919 REPORT_TELEMETRY_INIT_ERR(kInternalError
, Ori_UnexpectedFile
);
3920 RECORD_IN_NIGHTLY(statusKeeper
, NS_ERROR_UNEXPECTED
);
3921 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(NS_ERROR_UNEXPECTED
);
3924 Client::Type clientType
;
3925 rv
= Client::TypeFromText(leafName
, clientType
);
3926 if (NS_FAILED(rv
)) {
3927 UNKNOWN_FILE_WARNING(leafName
);
3928 REPORT_TELEMETRY_INIT_ERR(kInternalError
, Ori_UnexpectedClient
);
3929 RECORD_IN_NIGHTLY(statusKeeper
, NS_ERROR_UNEXPECTED
);
3930 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(NS_ERROR_UNEXPECTED
);
3933 Atomic
<bool> dummy(false);
3934 rv
= mClients
[clientType
]->InitOrigin(aPersistenceType
, aGroup
, aOrigin
,
3935 /* aCanceled */ dummy
, usageInfo
);
3936 if (NS_WARN_IF(NS_FAILED(rv
))) {
3937 // error should have reported in InitOrigin
3938 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
3939 CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(rv
);
3942 if (NS_WARN_IF(NS_FAILED(rv
))) {
3943 REPORT_TELEMETRY_INIT_ERR(kInternalError
, Ori_GetNextFile
);
3944 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
3945 #ifndef NIGHTLY_BUILD
3950 #ifdef NIGHTLY_BUILD
3951 if (NS_FAILED(statusKeeper
)) {
3952 return statusKeeper
;
3957 InitQuotaForOrigin(aPersistenceType
, aGroup
, aOrigin
,
3958 usageInfo
->TotalUsage(), aAccessTime
, aPersisted
);
3964 nsresult
QuotaManager::MaybeUpgradeIndexedDBDirectory() {
3965 AssertIsOnIOThread();
3967 nsCOMPtr
<nsIFile
> indexedDBDir
;
3969 NS_NewLocalFile(mIndexedDBPath
, false, getter_AddRefs(indexedDBDir
));
3970 NS_ENSURE_SUCCESS(rv
, rv
);
3973 rv
= indexedDBDir
->Exists(&exists
);
3974 NS_ENSURE_SUCCESS(rv
, rv
);
3977 // Nothing to upgrade.
3982 rv
= indexedDBDir
->IsDirectory(&isDirectory
);
3983 NS_ENSURE_SUCCESS(rv
, rv
);
3986 NS_WARNING("indexedDB entry is not a directory!");
3990 nsCOMPtr
<nsIFile
> persistentStorageDir
;
3991 rv
= NS_NewLocalFile(mStoragePath
, false,
3992 getter_AddRefs(persistentStorageDir
));
3993 NS_ENSURE_SUCCESS(rv
, rv
);
3995 rv
= persistentStorageDir
->Append(
3996 NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME
));
3997 NS_ENSURE_SUCCESS(rv
, rv
);
3999 rv
= persistentStorageDir
->Exists(&exists
);
4000 NS_ENSURE_SUCCESS(rv
, rv
);
4003 NS_WARNING("indexedDB directory shouldn't exist after the upgrade!");
4007 nsCOMPtr
<nsIFile
> storageDir
;
4008 rv
= persistentStorageDir
->GetParent(getter_AddRefs(storageDir
));
4009 NS_ENSURE_SUCCESS(rv
, rv
);
4011 // MoveTo() is atomic if the move happens on the same volume which should
4012 // be our case, so even if we crash in the middle of the operation nothing
4013 // breaks next time we try to initialize.
4014 // However there's a theoretical possibility that the indexedDB directory
4015 // is on different volume, but it should be rare enough that we don't have
4016 // to worry about it.
4017 rv
= indexedDBDir
->MoveTo(storageDir
,
4018 NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME
));
4019 NS_ENSURE_SUCCESS(rv
, rv
);
4024 nsresult
QuotaManager::MaybeUpgradePersistentStorageDirectory() {
4025 AssertIsOnIOThread();
4027 nsCOMPtr
<nsIFile
> persistentStorageDir
;
4028 nsresult rv
= NS_NewLocalFile(mStoragePath
, false,
4029 getter_AddRefs(persistentStorageDir
));
4030 if (NS_WARN_IF(NS_FAILED(rv
))) {
4034 rv
= persistentStorageDir
->Append(
4035 NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME
));
4036 if (NS_WARN_IF(NS_FAILED(rv
))) {
4041 rv
= persistentStorageDir
->Exists(&exists
);
4042 if (NS_WARN_IF(NS_FAILED(rv
))) {
4047 // Nothing to upgrade.
4052 rv
= persistentStorageDir
->IsDirectory(&isDirectory
);
4053 if (NS_WARN_IF(NS_FAILED(rv
))) {
4058 NS_WARNING("persistent entry is not a directory!");
4062 nsCOMPtr
<nsIFile
> defaultStorageDir
;
4063 rv
= NS_NewLocalFile(mDefaultStoragePath
, false,
4064 getter_AddRefs(defaultStorageDir
));
4065 if (NS_WARN_IF(NS_FAILED(rv
))) {
4069 rv
= defaultStorageDir
->Exists(&exists
);
4070 if (NS_WARN_IF(NS_FAILED(rv
))) {
4075 NS_WARNING("storage/persistent shouldn't exist after the upgrade!");
4079 // Create real metadata files for origin directories in persistent storage.
4080 RefPtr
<CreateOrUpgradeDirectoryMetadataHelper
> helper
=
4081 new CreateOrUpgradeDirectoryMetadataHelper(persistentStorageDir
,
4082 /* aPersistent */ true);
4084 rv
= helper
->ProcessRepository();
4085 if (NS_WARN_IF(NS_FAILED(rv
))) {
4089 // Upgrade metadata files for origin directories in temporary storage.
4090 nsCOMPtr
<nsIFile
> temporaryStorageDir
;
4091 rv
= NS_NewLocalFile(mTemporaryStoragePath
, false,
4092 getter_AddRefs(temporaryStorageDir
));
4093 if (NS_WARN_IF(NS_FAILED(rv
))) {
4097 rv
= temporaryStorageDir
->Exists(&exists
);
4098 if (NS_WARN_IF(NS_FAILED(rv
))) {
4103 rv
= temporaryStorageDir
->IsDirectory(&isDirectory
);
4104 if (NS_WARN_IF(NS_FAILED(rv
))) {
4109 NS_WARNING("temporary entry is not a directory!");
4114 new CreateOrUpgradeDirectoryMetadataHelper(temporaryStorageDir
,
4115 /* aPersistent */ false);
4117 rv
= helper
->ProcessRepository();
4118 if (NS_WARN_IF(NS_FAILED(rv
))) {
4123 // And finally rename persistent to default.
4124 rv
= persistentStorageDir
->RenameTo(
4125 nullptr, NS_LITERAL_STRING(DEFAULT_DIRECTORY_NAME
));
4126 if (NS_WARN_IF(NS_FAILED(rv
))) {
4133 nsresult
QuotaManager::MaybeRemoveOldDirectories() {
4134 AssertIsOnIOThread();
4136 nsCOMPtr
<nsIFile
> indexedDBDir
;
4138 NS_NewLocalFile(mIndexedDBPath
, false, getter_AddRefs(indexedDBDir
));
4139 if (NS_WARN_IF(NS_FAILED(rv
))) {
4144 rv
= indexedDBDir
->Exists(&exists
);
4145 if (NS_WARN_IF(NS_FAILED(rv
))) {
4150 QM_WARNING("Deleting old <profile>/indexedDB directory!");
4152 rv
= indexedDBDir
->Remove(/* aRecursive */ true);
4153 if (NS_WARN_IF(NS_FAILED(rv
))) {
4158 nsCOMPtr
<nsIFile
> persistentStorageDir
;
4159 rv
= NS_NewLocalFile(mStoragePath
, false,
4160 getter_AddRefs(persistentStorageDir
));
4161 if (NS_WARN_IF(NS_FAILED(rv
))) {
4165 rv
= persistentStorageDir
->Append(
4166 NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME
));
4167 if (NS_WARN_IF(NS_FAILED(rv
))) {
4171 rv
= persistentStorageDir
->Exists(&exists
);
4172 if (NS_WARN_IF(NS_FAILED(rv
))) {
4177 QM_WARNING("Deleting old <profile>/storage/persistent directory!");
4179 rv
= persistentStorageDir
->Remove(/* aRecursive */ true);
4180 if (NS_WARN_IF(NS_FAILED(rv
))) {
4188 template <typename Helper
>
4189 nsresult
QuotaManager::UpgradeStorage(const int32_t aOldVersion
,
4190 const int32_t aNewVersion
,
4191 mozIStorageConnection
* aConnection
) {
4192 AssertIsOnIOThread();
4193 MOZ_ASSERT(aNewVersion
> aOldVersion
);
4194 MOZ_ASSERT(aNewVersion
<= kStorageVersion
);
4195 MOZ_ASSERT(aConnection
);
4199 for (const PersistenceType persistenceType
: kAllPersistenceTypes
) {
4200 nsCOMPtr
<nsIFile
> directory
;
4201 rv
= NS_NewLocalFile(GetStoragePath(persistenceType
), false,
4202 getter_AddRefs(directory
));
4203 if (NS_WARN_IF(NS_FAILED(rv
))) {
4208 rv
= directory
->Exists(&exists
);
4209 if (NS_WARN_IF(NS_FAILED(rv
))) {
4217 bool persistent
= persistenceType
== PERSISTENCE_TYPE_PERSISTENT
;
4218 RefPtr
<RepositoryOperationBase
> helper
= new Helper(directory
, persistent
);
4219 rv
= helper
->ProcessRepository();
4220 if (NS_WARN_IF(NS_FAILED(rv
))) {
4227 int32_t storageVersion
;
4228 rv
= aConnection
->GetSchemaVersion(&storageVersion
);
4229 if (NS_WARN_IF(NS_FAILED(rv
))) {
4233 MOZ_ASSERT(storageVersion
== aOldVersion
);
4237 rv
= aConnection
->SetSchemaVersion(aNewVersion
);
4238 if (NS_WARN_IF(NS_FAILED(rv
))) {
4245 nsresult
QuotaManager::UpgradeStorageFrom0_0To1_0(
4246 mozIStorageConnection
* aConnection
) {
4247 AssertIsOnIOThread();
4248 MOZ_ASSERT(aConnection
);
4250 nsresult rv
= MaybeUpgradeIndexedDBDirectory();
4251 if (NS_WARN_IF(NS_FAILED(rv
))) {
4255 rv
= MaybeUpgradePersistentStorageDirectory();
4256 if (NS_WARN_IF(NS_FAILED(rv
))) {
4260 rv
= MaybeRemoveOldDirectories();
4261 if (NS_WARN_IF(NS_FAILED(rv
))) {
4265 rv
= UpgradeStorage
<UpgradeStorageFrom0_0To1_0Helper
>(
4266 0, MakeStorageVersion(1, 0), aConnection
);
4267 if (NS_WARN_IF(NS_FAILED(rv
))) {
4274 nsresult
QuotaManager::UpgradeStorageFrom1_0To2_0(
4275 mozIStorageConnection
* aConnection
) {
4276 AssertIsOnIOThread();
4277 MOZ_ASSERT(aConnection
);
4279 // The upgrade consists of a number of logically distinct bugs that
4280 // intentionally got fixed at the same time to trigger just one major
4284 // Morgue directory cleanup
4286 // The original bug that added "on demand" morgue cleanup is 1165119.
4289 // Morgue directories are removed from all origin directories during the
4290 // upgrade process. Origin initialization and usage calculation doesn't try
4291 // to remove morgue directories anymore.
4293 // [Downgrade-incompatible changes]:
4294 // Morgue directories can reappear if user runs an already upgraded profile
4295 // in an older version of Firefox. Morgue directories then prevent current
4296 // Firefox from initializing and using the storage.
4301 // The bug that removes isApp flags is 1311057.
4304 // Origin directories with appIds are removed during the upgrade process.
4306 // [Downgrade-incompatible changes]:
4307 // Origin directories with appIds can reappear if user runs an already
4308 // upgraded profile in an older version of Firefox. Origin directories with
4309 // appIds don't prevent current Firefox from initializing and using the
4310 // storage, but they wouldn't ever be removed again, potentially causing
4311 // problems once appId is removed from origin attributes.
4314 // Strip obsolete origin attributes
4316 // The bug that strips obsolete origin attributes is 1314361.
4319 // Origin directories with obsolete origin attributes are renamed and their
4320 // metadata files are updated during the upgrade process.
4322 // [Downgrade-incompatible changes]:
4323 // Origin directories with obsolete origin attributes can reappear if user
4324 // runs an already upgraded profile in an older version of Firefox. Origin
4325 // directories with obsolete origin attributes don't prevent current Firefox
4326 // from initializing and using the storage, but they wouldn't ever be upgraded
4327 // again, potentially causing problems in future.
4330 // File manager directory renaming (client specific)
4332 // The original bug that added "on demand" file manager directory renaming is
4336 // All file manager directories are renamed to contain the ".files" suffix.
4338 // [Downgrade-incompatible changes]:
4339 // File manager directories with the ".files" suffix prevent older versions of
4340 // Firefox from initializing and using the storage.
4341 // File manager directories without the ".files" suffix can appear if user
4342 // runs an already upgraded profile in an older version of Firefox. File
4343 // manager directories without the ".files" suffix then prevent current
4344 // Firefox from initializing and using the storage.
4346 nsresult rv
= UpgradeStorage
<UpgradeStorageFrom1_0To2_0Helper
>(
4347 MakeStorageVersion(1, 0), MakeStorageVersion(2, 0), aConnection
);
4348 if (NS_WARN_IF(NS_FAILED(rv
))) {
4355 nsresult
QuotaManager::UpgradeStorageFrom2_0To2_1(
4356 mozIStorageConnection
* aConnection
) {
4357 AssertIsOnIOThread();
4358 MOZ_ASSERT(aConnection
);
4360 // The upgrade is mainly to create a directory padding file in DOM Cache
4361 // directory to record the overall padding size of an origin.
4363 nsresult rv
= UpgradeStorage
<UpgradeStorageFrom2_0To2_1Helper
>(
4364 MakeStorageVersion(2, 0), MakeStorageVersion(2, 1), aConnection
);
4365 if (NS_WARN_IF(NS_FAILED(rv
))) {
4372 nsresult
QuotaManager::MaybeRemoveLocalStorageData() {
4373 AssertIsOnIOThread();
4374 MOZ_ASSERT(!CachedNextGenLocalStorageEnabled());
4376 // Cleanup the tmp file first, if there's any.
4377 nsCOMPtr
<nsIFile
> lsArchiveTmpFile
;
4379 NS_NewLocalFile(mStoragePath
, false, getter_AddRefs(lsArchiveTmpFile
));
4380 if (NS_WARN_IF(NS_FAILED(rv
))) {
4384 rv
= lsArchiveTmpFile
->Append(NS_LITERAL_STRING(LS_ARCHIVE_TMP_FILE_NAME
));
4385 if (NS_WARN_IF(NS_FAILED(rv
))) {
4390 rv
= lsArchiveTmpFile
->Exists(&exists
);
4391 if (NS_WARN_IF(NS_FAILED(rv
))) {
4396 rv
= lsArchiveTmpFile
->Remove(false);
4397 if (NS_WARN_IF(NS_FAILED(rv
))) {
4402 // Now check the real archive file.
4403 nsCOMPtr
<nsIFile
> lsArchiveFile
;
4404 rv
= NS_NewLocalFile(mStoragePath
, false, getter_AddRefs(lsArchiveFile
));
4405 if (NS_WARN_IF(NS_FAILED(rv
))) {
4409 rv
= lsArchiveFile
->Append(NS_LITERAL_STRING(LS_ARCHIVE_FILE_NAME
));
4410 if (NS_WARN_IF(NS_FAILED(rv
))) {
4414 rv
= lsArchiveFile
->Exists(&exists
);
4415 if (NS_WARN_IF(NS_FAILED(rv
))) {
4420 // If the ls archive doesn't exist then ls directories can't exist either.
4424 rv
= MaybeRemoveLocalStorageDirectories();
4425 if (NS_WARN_IF(NS_FAILED(rv
))) {
4429 // Finally remove the ls archive, so we don't have to check all origin
4430 // directories next time this method is called.
4431 rv
= lsArchiveFile
->Remove(false);
4432 if (NS_WARN_IF(NS_FAILED(rv
))) {
4439 nsresult
QuotaManager::MaybeRemoveLocalStorageDirectories() {
4440 AssertIsOnIOThread();
4442 nsCOMPtr
<nsIFile
> defaultStorageDir
;
4443 nsresult rv
= NS_NewLocalFile(mDefaultStoragePath
, false,
4444 getter_AddRefs(defaultStorageDir
));
4445 if (NS_WARN_IF(NS_FAILED(rv
))) {
4450 rv
= defaultStorageDir
->Exists(&exists
);
4451 if (NS_WARN_IF(NS_FAILED(rv
))) {
4459 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
4460 rv
= defaultStorageDir
->GetDirectoryEntries(getter_AddRefs(entries
));
4461 if (NS_WARN_IF(NS_FAILED(rv
))) {
4471 rv
= entries
->HasMoreElements(&hasMore
);
4472 if (NS_WARN_IF(NS_FAILED(rv
))) {
4480 nsCOMPtr
<nsISupports
> entry
;
4481 rv
= entries
->GetNext(getter_AddRefs(entry
));
4482 if (NS_WARN_IF(NS_FAILED(rv
))) {
4486 nsCOMPtr
<nsIFile
> originDir
= do_QueryInterface(entry
);
4487 MOZ_ASSERT(originDir
);
4489 rv
= originDir
->Exists(&exists
);
4490 if (NS_WARN_IF(NS_FAILED(rv
))) {
4497 rv
= originDir
->IsDirectory(&isDirectory
);
4498 if (NS_WARN_IF(NS_FAILED(rv
))) {
4504 rv
= originDir
->GetLeafName(leafName
);
4505 if (NS_WARN_IF(NS_FAILED(rv
))) {
4509 // Unknown files during upgrade are allowed. Just warn if we find them.
4510 if (!IsOSMetadata(leafName
)) {
4511 UNKNOWN_FILE_WARNING(leafName
);
4517 nsCOMPtr
<nsIFile
> lsDir
;
4518 rv
= originDir
->Clone(getter_AddRefs(lsDir
));
4519 if (NS_WARN_IF(NS_FAILED(rv
))) {
4523 rv
= lsDir
->Append(NS_LITERAL_STRING(LS_DIRECTORY_NAME
));
4524 if (NS_WARN_IF(NS_FAILED(rv
))) {
4528 rv
= lsDir
->Exists(&exists
);
4529 if (NS_WARN_IF(NS_FAILED(rv
))) {
4537 rv
= lsDir
->IsDirectory(&isDirectory
);
4538 if (NS_WARN_IF(NS_FAILED(rv
))) {
4543 QM_WARNING("ls entry is not a directory!");
4549 rv
= lsDir
->GetPath(path
);
4550 if (NS_WARN_IF(NS_FAILED(rv
))) {
4554 QM_WARNING("Deleting %s directory!", NS_ConvertUTF16toUTF8(path
).get());
4556 rv
= lsDir
->Remove(/* aRecursive */ true);
4557 if (NS_WARN_IF(NS_FAILED(rv
))) {
4565 nsresult
QuotaManager::MaybeCreateLocalStorageArchive() {
4566 AssertIsOnIOThread();
4567 MOZ_ASSERT(CachedNextGenLocalStorageEnabled());
4569 // Check if the archive was already successfully created.
4570 nsCOMPtr
<nsIFile
> lsArchiveFile
;
4572 NS_NewLocalFile(mStoragePath
, false, getter_AddRefs(lsArchiveFile
));
4573 if (NS_WARN_IF(NS_FAILED(rv
))) {
4577 rv
= lsArchiveFile
->Append(NS_LITERAL_STRING(LS_ARCHIVE_FILE_NAME
));
4578 if (NS_WARN_IF(NS_FAILED(rv
))) {
4583 rv
= lsArchiveFile
->Exists(&exists
);
4584 if (NS_WARN_IF(NS_FAILED(rv
))) {
4589 // ls-archive.sqlite already exists, nothing to create.
4593 // Get the storage service first, we will need it at multiple places.
4594 nsCOMPtr
<mozIStorageService
> ss
=
4595 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID
, &rv
);
4596 if (NS_WARN_IF(NS_FAILED(rv
))) {
4600 // Get the web apps store file.
4601 nsCOMPtr
<nsIFile
> webAppsStoreFile
;
4602 rv
= NS_NewLocalFile(mBasePath
, false, getter_AddRefs(webAppsStoreFile
));
4603 if (NS_WARN_IF(NS_FAILED(rv
))) {
4607 rv
= webAppsStoreFile
->Append(NS_LITERAL_STRING(WEB_APPS_STORE_FILE_NAME
));
4608 if (NS_WARN_IF(NS_FAILED(rv
))) {
4612 // Now check if the web apps store is useable.
4613 nsCOMPtr
<mozIStorageConnection
> connection
;
4614 rv
= CreateWebAppsStoreConnection(webAppsStoreFile
, ss
,
4615 getter_AddRefs(connection
));
4616 if (NS_WARN_IF(NS_FAILED(rv
))) {
4621 // Find out the journal mode.
4622 nsCOMPtr
<mozIStorageStatement
> stmt
;
4623 rv
= connection
->CreateStatement(NS_LITERAL_CSTRING("PRAGMA journal_mode;"),
4624 getter_AddRefs(stmt
));
4625 if (NS_WARN_IF(NS_FAILED(rv
))) {
4630 rv
= stmt
->ExecuteStep(&hasResult
);
4631 if (NS_WARN_IF(NS_FAILED(rv
))) {
4635 MOZ_ASSERT(hasResult
);
4637 nsCString journalMode
;
4638 rv
= stmt
->GetUTF8String(0, journalMode
);
4639 if (NS_WARN_IF(NS_FAILED(rv
))) {
4643 rv
= stmt
->Finalize();
4644 if (NS_WARN_IF(NS_FAILED(rv
))) {
4648 if (journalMode
.EqualsLiteral("wal")) {
4649 // We don't copy the WAL file, so make sure the old database is fully
4651 rv
= connection
->ExecuteSimpleSQL(
4652 NS_LITERAL_CSTRING("PRAGMA wal_checkpoint(TRUNCATE);"));
4653 if (NS_WARN_IF(NS_FAILED(rv
))) {
4658 // Explicitely close the connection before the old database is copied.
4659 rv
= connection
->Close();
4660 if (NS_WARN_IF(NS_FAILED(rv
))) {
4664 // Copy the old database. The database is copied from
4665 // <profile>/webappsstore.sqlite to
4666 // <profile>/storage/ls-archive-tmp.sqlite
4667 // We use a "-tmp" postfix since we are not done yet.
4668 nsCOMPtr
<nsIFile
> storageDir
;
4669 rv
= NS_NewLocalFile(mStoragePath
, false, getter_AddRefs(storageDir
));
4670 if (NS_WARN_IF(NS_FAILED(rv
))) {
4674 rv
= webAppsStoreFile
->CopyTo(storageDir
,
4675 NS_LITERAL_STRING(LS_ARCHIVE_TMP_FILE_NAME
));
4676 if (NS_WARN_IF(NS_FAILED(rv
))) {
4680 nsCOMPtr
<nsIFile
> lsArchiveTmpFile
;
4681 rv
= NS_NewLocalFile(mStoragePath
, false, getter_AddRefs(lsArchiveTmpFile
));
4682 if (NS_WARN_IF(NS_FAILED(rv
))) {
4686 rv
= lsArchiveTmpFile
->Append(NS_LITERAL_STRING(LS_ARCHIVE_TMP_FILE_NAME
));
4687 if (NS_WARN_IF(NS_FAILED(rv
))) {
4691 if (journalMode
.EqualsLiteral("wal")) {
4692 nsCOMPtr
<mozIStorageConnection
> lsArchiveTmpConnection
;
4693 rv
= ss
->OpenUnsharedDatabase(lsArchiveTmpFile
,
4694 getter_AddRefs(lsArchiveTmpConnection
));
4695 if (NS_WARN_IF(NS_FAILED(rv
))) {
4699 // The archive will only be used for lazy data migration. There won't be
4700 // any concurrent readers and writers that could benefit from Write-Ahead
4701 // Logging. So switch to a standard rollback journal. The standard
4702 // rollback journal also provides atomicity across multiple attached
4703 // databases which is import for the lazy data migration to work safely.
4704 rv
= lsArchiveTmpConnection
->ExecuteSimpleSQL(
4705 NS_LITERAL_CSTRING("PRAGMA journal_mode = DELETE;"));
4706 if (NS_WARN_IF(NS_FAILED(rv
))) {
4710 // The connection will be now implicitely closed (it's always safer to
4711 // close database connection before we manipulate underlying file)
4714 // Finally, rename ls-archive-tmp.sqlite to ls-archive.sqlite
4715 rv
= lsArchiveTmpFile
->MoveTo(nullptr,
4716 NS_LITERAL_STRING(LS_ARCHIVE_FILE_NAME
));
4717 if (NS_WARN_IF(NS_FAILED(rv
))) {
4721 // If webappsstore database is not useable, just create an empty archive.
4723 // Ensure the storage directory actually exists.
4724 nsCOMPtr
<nsIFile
> storageDirectory
;
4725 rv
= NS_NewLocalFile(GetStoragePath(), false,
4726 getter_AddRefs(storageDirectory
));
4727 if (NS_WARN_IF(NS_FAILED(rv
))) {
4732 rv
= EnsureDirectory(storageDirectory
, &dummy
);
4733 if (NS_WARN_IF(NS_FAILED(rv
))) {
4737 nsCOMPtr
<mozIStorageConnection
> lsArchiveConnection
;
4738 rv
= ss
->OpenUnsharedDatabase(lsArchiveFile
,
4739 getter_AddRefs(lsArchiveConnection
));
4740 if (NS_WARN_IF(NS_FAILED(rv
))) {
4744 rv
= StorageDBUpdater::Update(lsArchiveConnection
);
4745 if (NS_WARN_IF(NS_FAILED(rv
))) {
4755 void QuotaManager::AssertStorageIsInitialized() const {
4756 AssertIsOnIOThread();
4757 MOZ_ASSERT(mStorageInitialized
);
4762 nsresult
QuotaManager::EnsureStorageIsInitialized() {
4763 AssertIsOnIOThread();
4765 if (mStorageInitialized
) {
4769 nsCOMPtr
<nsIFile
> storageFile
;
4770 nsresult rv
= NS_NewLocalFile(mBasePath
, false, getter_AddRefs(storageFile
));
4771 if (NS_WARN_IF(NS_FAILED(rv
))) {
4775 rv
= storageFile
->Append(NS_LITERAL_STRING(STORAGE_FILE_NAME
));
4776 if (NS_WARN_IF(NS_FAILED(rv
))) {
4780 nsCOMPtr
<mozIStorageService
> ss
=
4781 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID
, &rv
);
4782 if (NS_WARN_IF(NS_FAILED(rv
))) {
4786 nsCOMPtr
<mozIStorageConnection
> connection
;
4787 rv
= ss
->OpenUnsharedDatabase(storageFile
, getter_AddRefs(connection
));
4788 if (rv
== NS_ERROR_FILE_CORRUPTED
) {
4789 // Nuke the database file.
4790 rv
= storageFile
->Remove(false);
4791 if (NS_WARN_IF(NS_FAILED(rv
))) {
4795 rv
= ss
->OpenUnsharedDatabase(storageFile
, getter_AddRefs(connection
));
4798 if (NS_WARN_IF(NS_FAILED(rv
))) {
4802 // We want extra durability for this important file.
4803 rv
= connection
->ExecuteSimpleSQL(
4804 NS_LITERAL_CSTRING("PRAGMA synchronous = EXTRA;"));
4805 if (NS_WARN_IF(NS_FAILED(rv
))) {
4809 // Check to make sure that the storage version is correct.
4810 int32_t storageVersion
;
4811 rv
= connection
->GetSchemaVersion(&storageVersion
);
4812 if (NS_WARN_IF(NS_FAILED(rv
))) {
4816 // Hacky downgrade logic!
4817 // If we see major.minor of 3.0, downgrade it to be 2.1.
4818 if (storageVersion
== kHackyPreDowngradeStorageVersion
) {
4819 storageVersion
= kHackyPostDowngradeStorageVersion
;
4820 rv
= connection
->SetSchemaVersion(storageVersion
);
4821 if (NS_WARN_IF(NS_FAILED(rv
))) {
4822 MOZ_ASSERT(false, "Downgrade didn't take.");
4827 if (GetMajorStorageVersion(storageVersion
) > kMajorStorageVersion
) {
4828 NS_WARNING("Unable to initialize storage, version is too high!");
4829 return NS_ERROR_FAILURE
;
4832 if (storageVersion
< kStorageVersion
) {
4833 const bool newDatabase
= !storageVersion
;
4835 nsCOMPtr
<nsIFile
> storageDir
;
4836 rv
= NS_NewLocalFile(mStoragePath
, false, getter_AddRefs(storageDir
));
4837 if (NS_WARN_IF(NS_FAILED(rv
))) {
4842 rv
= storageDir
->Exists(&exists
);
4843 if (NS_WARN_IF(NS_FAILED(rv
))) {
4848 nsCOMPtr
<nsIFile
> indexedDBDir
;
4849 rv
= NS_NewLocalFile(mIndexedDBPath
, false, getter_AddRefs(indexedDBDir
));
4850 if (NS_WARN_IF(NS_FAILED(rv
))) {
4854 rv
= indexedDBDir
->Exists(&exists
);
4855 if (NS_WARN_IF(NS_FAILED(rv
))) {
4860 const bool newDirectory
= !exists
;
4863 // Set the page size first.
4864 if (kSQLitePageSizeOverride
) {
4865 rv
= connection
->ExecuteSimpleSQL(nsPrintfCString(
4866 "PRAGMA page_size = %" PRIu32
";", kSQLitePageSizeOverride
));
4867 if (NS_WARN_IF(NS_FAILED(rv
))) {
4873 mozStorageTransaction
transaction(
4874 connection
, false, mozIStorageConnection::TRANSACTION_IMMEDIATE
);
4876 // An upgrade method can upgrade the database, the storage or both.
4877 // The upgrade loop below can only be avoided when there's no database and
4878 // no storage yet (e.g. new profile).
4879 if (newDatabase
&& newDirectory
) {
4880 rv
= CreateTables(connection
);
4881 if (NS_WARN_IF(NS_FAILED(rv
))) {
4885 MOZ_ASSERT(NS_SUCCEEDED(connection
->GetSchemaVersion(&storageVersion
)));
4886 MOZ_ASSERT(storageVersion
== kStorageVersion
);
4888 // This logic needs to change next time we change the storage!
4889 static_assert(kStorageVersion
== int32_t((2 << 16) + 1),
4890 "Upgrade function needed due to storage version increase.");
4892 while (storageVersion
!= kStorageVersion
) {
4893 if (storageVersion
== 0) {
4894 rv
= UpgradeStorageFrom0_0To1_0(connection
);
4895 } else if (storageVersion
== MakeStorageVersion(1, 0)) {
4896 rv
= UpgradeStorageFrom1_0To2_0(connection
);
4897 } else if (storageVersion
== MakeStorageVersion(2, 0)) {
4898 rv
= UpgradeStorageFrom2_0To2_1(connection
);
4901 "Unable to initialize storage, no upgrade path is "
4903 return NS_ERROR_FAILURE
;
4906 if (NS_WARN_IF(NS_FAILED(rv
))) {
4910 rv
= connection
->GetSchemaVersion(&storageVersion
);
4911 if (NS_WARN_IF(NS_FAILED(rv
))) {
4916 MOZ_ASSERT(storageVersion
== kStorageVersion
);
4919 rv
= transaction
.Commit();
4920 if (NS_WARN_IF(NS_FAILED(rv
))) {
4925 if (CachedNextGenLocalStorageEnabled()) {
4926 rv
= MaybeCreateLocalStorageArchive();
4928 rv
= MaybeRemoveLocalStorageData();
4930 if (NS_WARN_IF(NS_FAILED(rv
))) {
4934 mStorageInitialized
= true;
4939 void QuotaManager::OpenDirectory(PersistenceType aPersistenceType
,
4940 const nsACString
& aGroup
,
4941 const nsACString
& aOrigin
,
4942 Client::Type aClientType
, bool aExclusive
,
4943 OpenDirectoryListener
* aOpenListener
) {
4944 AssertIsOnOwningThread();
4946 RefPtr
<DirectoryLockImpl
> lock
= CreateDirectoryLock(
4947 Nullable
<PersistenceType
>(aPersistenceType
), aGroup
,
4948 OriginScope::FromOrigin(aOrigin
), Nullable
<Client::Type
>(aClientType
),
4949 aExclusive
, false, aOpenListener
);
4953 void QuotaManager::OpenDirectoryInternal(
4954 const Nullable
<PersistenceType
>& aPersistenceType
,
4955 const OriginScope
& aOriginScope
, const Nullable
<Client::Type
>& aClientType
,
4956 bool aExclusive
, OpenDirectoryListener
* aOpenListener
) {
4957 AssertIsOnOwningThread();
4959 RefPtr
<DirectoryLockImpl
> lock
= CreateDirectoryLock(
4960 aPersistenceType
, EmptyCString(), aOriginScope
,
4961 Nullable
<Client::Type
>(aClientType
), aExclusive
, true, aOpenListener
);
4968 // All the locks that block this new exclusive lock need to be invalidated.
4969 // We also need to notify clients to abort operations for them.
4970 AutoTArray
<nsAutoPtr
<nsTHashtable
<nsCStringHashKey
>>, Client::TYPE_MAX
>
4972 origins
.SetLength(Client::TypeMax());
4974 const nsTArray
<DirectoryLockImpl
*>& blockedOnLocks
=
4975 lock
->GetBlockedOnLocks();
4977 for (DirectoryLockImpl
* blockedOnLock
: blockedOnLocks
) {
4978 if (!blockedOnLock
->IsInternal()) {
4979 blockedOnLock
->Invalidate();
4981 MOZ_ASSERT(!blockedOnLock
->GetClientType().IsNull());
4982 Client::Type clientType
= blockedOnLock
->GetClientType().Value();
4983 MOZ_ASSERT(clientType
< Client::TypeMax());
4985 const OriginScope
& originScope
= blockedOnLock
->GetOriginScope();
4986 MOZ_ASSERT(originScope
.IsOrigin());
4987 MOZ_ASSERT(!originScope
.GetOrigin().IsEmpty());
4989 nsAutoPtr
<nsTHashtable
<nsCStringHashKey
>>& origin
= origins
[clientType
];
4991 origin
= new nsTHashtable
<nsCStringHashKey
>();
4993 origin
->PutEntry(originScope
.GetOrigin());
4997 for (uint32_t index
: IntegerRange(uint32_t(Client::TypeMax()))) {
4998 if (origins
[index
]) {
4999 for (auto iter
= origins
[index
]->Iter(); !iter
.Done(); iter
.Next()) {
5000 MOZ_ASSERT(mClients
[index
]);
5002 mClients
[index
]->AbortOperations(iter
.Get()->GetKey());
5008 nsresult
QuotaManager::EnsureOriginIsInitialized(
5009 PersistenceType aPersistenceType
, const nsACString
& aSuffix
,
5010 const nsACString
& aGroup
, const nsACString
& aOrigin
,
5011 bool aCreateIfNotExists
, nsIFile
** aDirectory
) {
5012 AssertIsOnIOThread();
5013 MOZ_ASSERT(aDirectory
);
5015 nsCOMPtr
<nsIFile
> directory
;
5017 nsresult rv
= EnsureOriginIsInitializedInternal(
5018 aPersistenceType
, aSuffix
, aGroup
, aOrigin
, aCreateIfNotExists
,
5019 getter_AddRefs(directory
), &created
);
5020 if (rv
== NS_ERROR_NOT_AVAILABLE
) {
5023 if (NS_WARN_IF(NS_FAILED(rv
))) {
5027 directory
.forget(aDirectory
);
5031 nsresult
QuotaManager::EnsureOriginIsInitializedInternal(
5032 PersistenceType aPersistenceType
, const nsACString
& aSuffix
,
5033 const nsACString
& aGroup
, const nsACString
& aOrigin
,
5034 bool aCreateIfNotExists
, nsIFile
** aDirectory
, bool* aCreated
) {
5035 AssertIsOnIOThread();
5036 MOZ_ASSERT(aDirectory
);
5037 MOZ_ASSERT(aCreated
);
5039 nsresult rv
= EnsureStorageIsInitialized();
5040 NS_ENSURE_SUCCESS(rv
, rv
);
5042 // Get directory for this origin and persistence type.
5043 nsCOMPtr
<nsIFile
> directory
;
5044 rv
= GetDirectoryForOrigin(aPersistenceType
, aOrigin
,
5045 getter_AddRefs(directory
));
5046 NS_ENSURE_SUCCESS(rv
, rv
);
5048 if (aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
5049 if (mInitializedOrigins
.Contains(aOrigin
)) {
5050 directory
.forget(aDirectory
);
5055 rv
= EnsureTemporaryStorageIsInitialized();
5056 if (NS_WARN_IF(NS_FAILED(rv
))) {
5062 rv
= EnsureOriginDirectory(directory
, aCreateIfNotExists
, &created
);
5063 if (rv
== NS_ERROR_NOT_AVAILABLE
) {
5066 if (NS_WARN_IF(NS_FAILED(rv
))) {
5071 if (aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
5073 rv
= CreateDirectoryMetadataFiles(directory
,
5074 /* aPersisted */ true, aSuffix
, aGroup
,
5075 aOrigin
, ×tamp
);
5076 if (NS_WARN_IF(NS_FAILED(rv
))) {
5080 rv
= GetDirectoryMetadata2WithRestore(directory
,
5081 /* aPersistent */ true, ×tamp
,
5082 /* aPersisted */ nullptr);
5083 if (NS_WARN_IF(NS_FAILED(rv
))) {
5087 MOZ_ASSERT(timestamp
<= PR_Now());
5090 rv
= InitializeOrigin(aPersistenceType
, aGroup
, aOrigin
, timestamp
,
5091 /* aPersisted */ true, directory
);
5092 NS_ENSURE_SUCCESS(rv
, rv
);
5094 mInitializedOrigins
.AppendElement(aOrigin
);
5095 } else if (created
) {
5096 rv
= CreateDirectoryMetadataFiles(directory
,
5097 /* aPersisted */ false, aSuffix
, aGroup
,
5098 aOrigin
, ×tamp
);
5099 if (NS_WARN_IF(NS_FAILED(rv
))) {
5103 // Don't need to traverse the directory, since it's empty.
5104 InitQuotaForOrigin(aPersistenceType
, aGroup
, aOrigin
,
5105 /* aUsageBytes */ 0, timestamp
,
5106 /* aPersisted */ false);
5109 directory
.forget(aDirectory
);
5110 *aCreated
= created
;
5114 nsresult
QuotaManager::EnsureTemporaryStorageIsInitialized() {
5115 AssertIsOnIOThread();
5116 MOZ_ASSERT(mStorageInitialized
);
5118 if (mTemporaryStorageInitialized
) {
5122 TimeStamp startTime
= TimeStamp::Now();
5124 // A keeper to defer the return only in Nightly, so that the telemetry data
5125 // for whole profile can be collected
5126 nsresult statusKeeper
= NS_OK
;
5128 nsresult rv
= InitializeRepository(PERSISTENCE_TYPE_DEFAULT
);
5129 if (NS_WARN_IF(NS_FAILED(rv
))) {
5130 RECORD_IN_NIGHTLY(statusKeeper
, rv
);
5132 #ifndef NIGHTLY_BUILD
5133 // We have to cleanup partially initialized quota.
5140 rv
= InitializeRepository(PERSISTENCE_TYPE_TEMPORARY
);
5141 if (NS_WARN_IF(NS_FAILED(rv
)) || NS_FAILED(statusKeeper
)) {
5142 // We have to cleanup partially initialized quota.
5145 #ifdef NIGHTLY_BUILD
5146 return NS_FAILED(statusKeeper
) ? statusKeeper
: rv
;
5152 Telemetry::AccumulateTimeDelta(Telemetry::QM_REPOSITORIES_INITIALIZATION_TIME
,
5153 startTime
, TimeStamp::Now());
5155 if (gFixedLimitKB
>= 0) {
5156 mTemporaryStorageLimit
= static_cast<uint64_t>(gFixedLimitKB
) * 1024;
5158 nsCOMPtr
<nsIFile
> storageDir
=
5159 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
);
5160 if (NS_WARN_IF(NS_FAILED(rv
))) {
5164 rv
= storageDir
->InitWithPath(GetStoragePath());
5165 if (NS_WARN_IF(NS_FAILED(rv
))) {
5169 rv
= GetTemporaryStorageLimit(storageDir
, mTemporaryStorageUsage
,
5170 &mTemporaryStorageLimit
);
5171 if (NS_WARN_IF(NS_FAILED(rv
))) {
5176 mTemporaryStorageInitialized
= true;
5178 CheckTemporaryStorageLimits();
5183 nsresult
QuotaManager::EnsureOriginDirectory(nsIFile
* aDirectory
,
5184 bool aCreateIfNotExists
,
5186 AssertIsOnIOThread();
5187 MOZ_ASSERT(aDirectory
);
5188 MOZ_ASSERT(aCreated
);
5191 nsresult rv
= aDirectory
->Exists(&exists
);
5192 if (NS_WARN_IF(NS_FAILED(rv
))) {
5197 if (!aCreateIfNotExists
) {
5198 return NS_ERROR_NOT_AVAILABLE
;
5202 rv
= aDirectory
->GetLeafName(leafName
);
5203 if (NS_WARN_IF(NS_FAILED(rv
))) {
5207 if (!leafName
.EqualsLiteral(kChromeOrigin
) &&
5208 !IsSanitizedOriginValid(NS_ConvertUTF16toUTF8(leafName
))) {
5210 "Preventing creation of a new origin directory which is not "
5211 "supported by our origin parser or is obsolete!");
5212 return NS_ERROR_FAILURE
;
5216 rv
= EnsureDirectory(aDirectory
, aCreated
);
5217 if (NS_WARN_IF(NS_FAILED(rv
))) {
5224 nsresult
QuotaManager::AboutToClearOrigins(
5225 const Nullable
<PersistenceType
>& aPersistenceType
,
5226 const OriginScope
& aOriginScope
,
5227 const Nullable
<Client::Type
>& aClientType
) {
5228 AssertIsOnIOThread();
5232 if (aClientType
.IsNull()) {
5233 for (uint32_t index
= 0; index
< uint32_t(Client::TypeMax()); index
++) {
5234 rv
= mClients
[index
]->AboutToClearOrigins(aPersistenceType
, aOriginScope
);
5235 if (NS_WARN_IF(NS_FAILED(rv
))) {
5240 rv
= mClients
[aClientType
.Value()]->AboutToClearOrigins(aPersistenceType
,
5242 if (NS_WARN_IF(NS_FAILED(rv
))) {
5250 void QuotaManager::OriginClearCompleted(
5251 PersistenceType aPersistenceType
, const nsACString
& aOrigin
,
5252 const Nullable
<Client::Type
>& aClientType
) {
5253 AssertIsOnIOThread();
5255 if (aClientType
.IsNull()) {
5256 if (aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
5257 mInitializedOrigins
.RemoveElement(aOrigin
);
5260 for (uint32_t index
= 0; index
< uint32_t(Client::TypeMax()); index
++) {
5261 mClients
[index
]->OnOriginClearCompleted(aPersistenceType
, aOrigin
);
5264 mClients
[aClientType
.Value()]->OnOriginClearCompleted(aPersistenceType
,
5269 void QuotaManager::ResetOrClearCompleted() {
5270 AssertIsOnIOThread();
5272 mInitializedOrigins
.Clear();
5273 mTemporaryStorageInitialized
= false;
5274 mStorageInitialized
= false;
5276 ReleaseIOThreadObjects();
5279 Client
* QuotaManager::GetClient(Client::Type aClientType
) {
5280 MOZ_ASSERT(aClientType
>= Client::IDB
);
5281 MOZ_ASSERT(aClientType
< Client::TypeMax());
5283 return mClients
.ElementAt(aClientType
);
5286 uint64_t QuotaManager::GetGroupLimit() const {
5287 MOZ_ASSERT(mTemporaryStorageInitialized
);
5289 // To avoid one group evicting all the rest, limit the amount any one group
5290 // can use to 20%. To prevent individual sites from using exorbitant amounts
5291 // of storage where there is a lot of free space, cap the group limit to 2GB.
5292 uint64_t x
= std::min
<uint64_t>(mTemporaryStorageLimit
* .20, 2 GB
);
5294 // In low-storage situations, make an exception (while not exceeding the total
5296 return std::min
<uint64_t>(mTemporaryStorageLimit
,
5297 std::max
<uint64_t>(x
, 10 MB
));
5300 void QuotaManager::GetGroupUsageAndLimit(const nsACString
& aGroup
,
5301 UsageInfo
* aUsageInfo
) {
5302 AssertIsOnIOThread();
5303 MOZ_ASSERT(aUsageInfo
);
5306 MutexAutoLock
lock(mQuotaMutex
);
5308 aUsageInfo
->SetLimit(GetGroupLimit());
5309 aUsageInfo
->ResetUsage();
5311 GroupInfoPair
* pair
;
5312 if (!mGroupInfoPairs
.Get(aGroup
, &pair
)) {
5316 // Calculate temporary group usage
5317 RefPtr
<GroupInfo
> temporaryGroupInfo
=
5318 pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY
);
5319 if (temporaryGroupInfo
) {
5320 aUsageInfo
->AppendToDatabaseUsage(temporaryGroupInfo
->mUsage
);
5323 // Calculate default group usage
5324 RefPtr
<GroupInfo
> defaultGroupInfo
=
5325 pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT
);
5326 if (defaultGroupInfo
) {
5327 aUsageInfo
->AppendToDatabaseUsage(defaultGroupInfo
->mUsage
);
5332 void QuotaManager::NotifyStoragePressure(uint64_t aUsage
) {
5333 mQuotaMutex
.AssertNotCurrentThreadOwns();
5335 RefPtr
<StoragePressureRunnable
> storagePressureRunnable
=
5336 new StoragePressureRunnable(aUsage
);
5338 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(storagePressureRunnable
));
5342 void QuotaManager::GetStorageId(PersistenceType aPersistenceType
,
5343 const nsACString
& aOrigin
,
5344 Client::Type aClientType
,
5345 nsACString
& aDatabaseId
) {
5347 str
.AppendInt(aPersistenceType
);
5349 str
.Append(aOrigin
);
5351 str
.AppendInt(aClientType
);
5357 nsresult
QuotaManager::GetInfoFromPrincipal(nsIPrincipal
* aPrincipal
,
5358 nsACString
* aSuffix
,
5360 nsACString
* aOrigin
) {
5361 MOZ_ASSERT(NS_IsMainThread());
5362 MOZ_ASSERT(aPrincipal
);
5364 if (nsContentUtils::IsSystemPrincipal(aPrincipal
)) {
5365 GetInfoForChrome(aSuffix
, aGroup
, aOrigin
);
5369 if (aPrincipal
->GetIsNullPrincipal()) {
5370 NS_WARNING("IndexedDB not supported from this principal!");
5371 return NS_ERROR_FAILURE
;
5375 nsresult rv
= aPrincipal
->GetOrigin(origin
);
5376 NS_ENSURE_SUCCESS(rv
, rv
);
5378 if (origin
.EqualsLiteral(kChromeOrigin
)) {
5379 NS_WARNING("Non-chrome principal can't use chrome origin!");
5380 return NS_ERROR_FAILURE
;
5384 aPrincipal
->OriginAttributesRef().CreateSuffix(suffix
);
5387 aSuffix
->Assign(suffix
);
5391 nsCString baseDomain
;
5392 rv
= aPrincipal
->GetBaseDomain(baseDomain
);
5393 if (NS_FAILED(rv
)) {
5394 // A hack for JetPack.
5396 nsCOMPtr
<nsIURI
> uri
;
5397 rv
= aPrincipal
->GetURI(getter_AddRefs(uri
));
5398 NS_ENSURE_SUCCESS(rv
, rv
);
5400 bool isIndexedDBURI
= false;
5401 rv
= uri
->SchemeIs("indexedDB", &isIndexedDBURI
);
5402 NS_ENSURE_SUCCESS(rv
, rv
);
5404 if (isIndexedDBURI
) {
5408 NS_ENSURE_SUCCESS(rv
, rv
);
5410 if (baseDomain
.IsEmpty()) {
5411 aGroup
->Assign(origin
);
5413 aGroup
->Assign(baseDomain
+ suffix
);
5418 aOrigin
->Assign(origin
);
5425 nsresult
QuotaManager::GetInfoFromWindow(nsPIDOMWindowOuter
* aWindow
,
5426 nsACString
* aSuffix
,
5428 nsACString
* aOrigin
) {
5429 MOZ_ASSERT(NS_IsMainThread());
5430 MOZ_ASSERT(aWindow
);
5432 nsCOMPtr
<nsIScriptObjectPrincipal
> sop
= do_QueryInterface(aWindow
);
5433 NS_ENSURE_TRUE(sop
, NS_ERROR_FAILURE
);
5435 nsCOMPtr
<nsIPrincipal
> principal
= sop
->GetPrincipal();
5436 NS_ENSURE_TRUE(principal
, NS_ERROR_FAILURE
);
5438 nsresult rv
= GetInfoFromPrincipal(principal
, aSuffix
, aGroup
, aOrigin
);
5439 NS_ENSURE_SUCCESS(rv
, rv
);
5445 void QuotaManager::GetInfoForChrome(nsACString
* aSuffix
, nsACString
* aGroup
,
5446 nsACString
* aOrigin
) {
5447 MOZ_ASSERT(NS_IsMainThread());
5448 MOZ_ASSERT(nsContentUtils::LegacyIsCallerChromeOrNativeCode());
5451 aSuffix
->Assign(EmptyCString());
5454 ChromeOrigin(*aGroup
);
5457 ChromeOrigin(*aOrigin
);
5462 bool QuotaManager::IsOriginInternal(const nsACString
& aOrigin
) {
5463 MOZ_ASSERT(!aOrigin
.IsEmpty());
5465 // The first prompt is not required for these origins.
5466 if (aOrigin
.EqualsLiteral(kChromeOrigin
) ||
5467 StringBeginsWith(aOrigin
, nsDependentCString(kAboutHomeOriginPrefix
)) ||
5468 StringBeginsWith(aOrigin
, nsDependentCString(kIndexedDBOriginPrefix
)) ||
5469 StringBeginsWith(aOrigin
, nsDependentCString(kResourceOriginPrefix
))) {
5477 void QuotaManager::ChromeOrigin(nsACString
& aOrigin
) {
5478 aOrigin
.AssignLiteral(kChromeOrigin
);
5482 bool QuotaManager::AreOriginsEqualOnDisk(nsACString
& aOrigin1
,
5483 nsACString
& aOrigin2
) {
5484 nsCString
origin1Sanitized(aOrigin1
);
5485 SanitizeOriginString(origin1Sanitized
);
5487 nsCString
origin2Sanitized(aOrigin2
);
5488 SanitizeOriginString(origin2Sanitized
);
5490 return origin1Sanitized
== origin2Sanitized
;
5494 bool QuotaManager::ParseOrigin(const nsACString
& aOrigin
, nsCString
& aSpec
,
5495 OriginAttributes
* aAttrs
) {
5498 if (aOrigin
.Equals(kChromeOrigin
)) {
5499 aSpec
= kChromeOrigin
;
5503 nsCString
sanitizedOrigin(aOrigin
);
5504 SanitizeOriginString(sanitizedOrigin
);
5506 OriginParser::ResultType result
=
5507 OriginParser::ParseOrigin(sanitizedOrigin
, aSpec
, aAttrs
);
5508 if (NS_WARN_IF(result
!= OriginParser::ValidOrigin
)) {
5515 uint64_t QuotaManager::LockedCollectOriginsForEviction(
5516 uint64_t aMinSizeToBeFreed
, nsTArray
<RefPtr
<DirectoryLockImpl
>>& aLocks
) {
5517 mQuotaMutex
.AssertCurrentThreadOwns();
5519 RefPtr
<CollectOriginsHelper
> helper
=
5520 new CollectOriginsHelper(mQuotaMutex
, aMinSizeToBeFreed
);
5522 // Unlock while calling out to XPCOM (code behind the dispatch method needs
5523 // to acquire its own lock which can potentially lead to a deadlock and it
5524 // also calls an observer that can do various stuff like IO, so it's better
5525 // to not hold our mutex while that happens).
5527 MutexAutoUnlock
autoUnlock(mQuotaMutex
);
5529 MOZ_ALWAYS_SUCCEEDS(mOwningThread
->Dispatch(helper
, NS_DISPATCH_NORMAL
));
5532 return helper
->BlockAndReturnOriginsForEviction(aLocks
);
5535 void QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType
,
5536 const nsACString
& aGroup
,
5537 const nsACString
& aOrigin
) {
5538 mQuotaMutex
.AssertCurrentThreadOwns();
5539 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
);
5541 GroupInfoPair
* pair
;
5542 mGroupInfoPairs
.Get(aGroup
, &pair
);
5548 RefPtr
<GroupInfo
> groupInfo
= pair
->LockedGetGroupInfo(aPersistenceType
);
5550 groupInfo
->LockedRemoveOriginInfo(aOrigin
);
5552 if (!groupInfo
->LockedHasOriginInfos()) {
5553 pair
->LockedClearGroupInfo(aPersistenceType
);
5555 if (!pair
->LockedHasGroupInfos()) {
5556 mGroupInfoPairs
.Remove(aGroup
);
5562 already_AddRefed
<OriginInfo
> QuotaManager::LockedGetOriginInfo(
5563 PersistenceType aPersistenceType
, const nsACString
& aGroup
,
5564 const nsACString
& aOrigin
) {
5565 mQuotaMutex
.AssertCurrentThreadOwns();
5566 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
);
5568 GroupInfoPair
* pair
;
5569 if (mGroupInfoPairs
.Get(aGroup
, &pair
)) {
5570 RefPtr
<GroupInfo
> groupInfo
= pair
->LockedGetGroupInfo(aPersistenceType
);
5572 return groupInfo
->LockedGetOriginInfo(aOrigin
);
5579 void QuotaManager::CheckTemporaryStorageLimits() {
5580 AssertIsOnIOThread();
5582 nsTArray
<OriginInfo
*> doomedOriginInfos
;
5584 MutexAutoLock
lock(mQuotaMutex
);
5586 for (auto iter
= mGroupInfoPairs
.Iter(); !iter
.Done(); iter
.Next()) {
5587 GroupInfoPair
* pair
= iter
.UserData();
5589 MOZ_ASSERT(!iter
.Key().IsEmpty(), "Empty key!");
5590 MOZ_ASSERT(pair
, "Null pointer!");
5592 uint64_t groupUsage
= 0;
5594 RefPtr
<GroupInfo
> temporaryGroupInfo
=
5595 pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY
);
5596 if (temporaryGroupInfo
) {
5597 groupUsage
+= temporaryGroupInfo
->mUsage
;
5600 RefPtr
<GroupInfo
> defaultGroupInfo
=
5601 pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT
);
5602 if (defaultGroupInfo
) {
5603 groupUsage
+= defaultGroupInfo
->mUsage
;
5606 if (groupUsage
> 0) {
5607 QuotaManager
* quotaManager
= QuotaManager::Get();
5608 MOZ_ASSERT(quotaManager
, "Shouldn't be null!");
5610 if (groupUsage
> quotaManager
->GetGroupLimit()) {
5611 nsTArray
<OriginInfo
*> originInfos
;
5612 if (temporaryGroupInfo
) {
5613 originInfos
.AppendElements(temporaryGroupInfo
->mOriginInfos
);
5615 if (defaultGroupInfo
) {
5616 originInfos
.AppendElements(defaultGroupInfo
->mOriginInfos
);
5618 originInfos
.Sort(OriginInfoLRUComparator());
5620 for (uint32_t i
= 0; i
< originInfos
.Length(); i
++) {
5621 OriginInfo
* originInfo
= originInfos
[i
];
5622 if (originInfo
->LockedPersisted()) {
5626 doomedOriginInfos
.AppendElement(originInfo
);
5627 groupUsage
-= originInfo
->mUsage
;
5629 if (groupUsage
<= quotaManager
->GetGroupLimit()) {
5638 for (uint32_t index
= 0; index
< doomedOriginInfos
.Length(); index
++) {
5639 usage
+= doomedOriginInfos
[index
]->mUsage
;
5642 if (mTemporaryStorageUsage
- usage
> mTemporaryStorageLimit
) {
5643 nsTArray
<OriginInfo
*> originInfos
;
5645 for (auto iter
= mGroupInfoPairs
.Iter(); !iter
.Done(); iter
.Next()) {
5646 GroupInfoPair
* pair
= iter
.UserData();
5648 MOZ_ASSERT(!iter
.Key().IsEmpty(), "Empty key!");
5649 MOZ_ASSERT(pair
, "Null pointer!");
5651 RefPtr
<GroupInfo
> groupInfo
=
5652 pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY
);
5654 originInfos
.AppendElements(groupInfo
->mOriginInfos
);
5657 groupInfo
= pair
->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT
);
5659 originInfos
.AppendElements(groupInfo
->mOriginInfos
);
5663 for (uint32_t index
= originInfos
.Length(); index
> 0; index
--) {
5664 if (doomedOriginInfos
.Contains(originInfos
[index
- 1]) ||
5665 originInfos
[index
- 1]->LockedPersisted()) {
5666 originInfos
.RemoveElementAt(index
- 1);
5670 originInfos
.Sort(OriginInfoLRUComparator());
5672 for (uint32_t i
= 0; i
< originInfos
.Length(); i
++) {
5673 if (mTemporaryStorageUsage
- usage
<= mTemporaryStorageLimit
) {
5674 originInfos
.TruncateLength(i
);
5678 usage
+= originInfos
[i
]->mUsage
;
5681 doomedOriginInfos
.AppendElements(originInfos
);
5685 for (uint32_t index
= 0; index
< doomedOriginInfos
.Length(); index
++) {
5686 OriginInfo
* doomedOriginInfo
= doomedOriginInfos
[index
];
5690 MutexAutoLock
lock(mQuotaMutex
);
5691 MOZ_ASSERT(!doomedOriginInfo
->LockedPersisted());
5695 DeleteFilesForOrigin(doomedOriginInfo
->mGroupInfo
->mPersistenceType
,
5696 doomedOriginInfo
->mOrigin
);
5699 nsTArray
<OriginParams
> doomedOrigins
;
5701 MutexAutoLock
lock(mQuotaMutex
);
5703 for (uint32_t index
= 0; index
< doomedOriginInfos
.Length(); index
++) {
5704 OriginInfo
* doomedOriginInfo
= doomedOriginInfos
[index
];
5706 PersistenceType persistenceType
=
5707 doomedOriginInfo
->mGroupInfo
->mPersistenceType
;
5708 nsCString group
= doomedOriginInfo
->mGroupInfo
->mGroup
;
5709 nsCString origin
= doomedOriginInfo
->mOrigin
;
5710 LockedRemoveQuotaForOrigin(persistenceType
, group
, origin
);
5713 doomedOriginInfos
[index
] = nullptr;
5716 doomedOrigins
.AppendElement(OriginParams(persistenceType
, origin
));
5720 for (const OriginParams
& doomedOrigin
: doomedOrigins
) {
5721 OriginClearCompleted(doomedOrigin
.mPersistenceType
, doomedOrigin
.mOrigin
,
5722 Nullable
<Client::Type
>());
5725 if (mTemporaryStorageUsage
> mTemporaryStorageLimit
) {
5726 // If disk space is still low after origin clear, notify storage pressure.
5727 NotifyStoragePressure(mTemporaryStorageUsage
);
5731 void QuotaManager::DeleteFilesForOrigin(PersistenceType aPersistenceType
,
5732 const nsACString
& aOrigin
) {
5733 nsCOMPtr
<nsIFile
> directory
;
5734 nsresult rv
= GetDirectoryForOrigin(aPersistenceType
, aOrigin
,
5735 getter_AddRefs(directory
));
5736 NS_ENSURE_SUCCESS_VOID(rv
);
5738 rv
= directory
->Remove(true);
5739 if (rv
!= NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
&&
5740 rv
!= NS_ERROR_FILE_NOT_FOUND
&& NS_FAILED(rv
)) {
5741 // This should never fail if we've closed all storage connections
5743 NS_ERROR("Failed to remove directory!");
5747 void QuotaManager::FinalizeOriginEviction(
5748 nsTArray
<RefPtr
<DirectoryLockImpl
>>& aLocks
) {
5749 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
5751 RefPtr
<FinalizeOriginEvictionOp
> op
=
5752 new FinalizeOriginEvictionOp(mOwningThread
, aLocks
);
5754 if (IsOnIOThread()) {
5755 op
->RunOnIOThreadImmediately();
5761 void QuotaManager::ShutdownTimerCallback(nsITimer
* aTimer
, void* aClosure
) {
5762 AssertIsOnBackgroundThread();
5764 auto quotaManager
= static_cast<QuotaManager
*>(aClosure
);
5765 MOZ_ASSERT(quotaManager
);
5768 "Some storage operations are taking longer than expected "
5769 "during shutdown and will be aborted!");
5771 // Abort all operations.
5772 for (RefPtr
<Client
>& client
: quotaManager
->mClients
) {
5773 client
->AbortOperations(VoidCString());
5777 auto QuotaManager::GetDirectoryLockTable(PersistenceType aPersistenceType
)
5778 -> DirectoryLockTable
& {
5779 switch (aPersistenceType
) {
5780 case PERSISTENCE_TYPE_TEMPORARY
:
5781 return mTemporaryDirectoryLockTable
;
5782 case PERSISTENCE_TYPE_DEFAULT
:
5783 return mDefaultDirectoryLockTable
;
5785 case PERSISTENCE_TYPE_PERSISTENT
:
5786 case PERSISTENCE_TYPE_INVALID
:
5788 MOZ_CRASH("Bad persistence type value!");
5792 bool QuotaManager::IsSanitizedOriginValid(const nsACString
& aSanitizedOrigin
) {
5793 AssertIsOnIOThread();
5794 MOZ_ASSERT(!aSanitizedOrigin
.Equals(kChromeOrigin
));
5797 if (auto entry
= mValidOrigins
.LookupForAdd(aSanitizedOrigin
)) {
5798 // We already parsed this sanitized origin string.
5799 valid
= entry
.Data();
5802 OriginAttributes attrs
;
5803 OriginParser::ResultType result
=
5804 OriginParser::ParseOrigin(aSanitizedOrigin
, spec
, &attrs
);
5806 valid
= result
== OriginParser::ValidOrigin
;
5807 entry
.OrInsert([valid
]() { return valid
; });
5813 /*******************************************************************************
5814 * Local class implementations
5815 ******************************************************************************/
5817 OriginInfo::OriginInfo(GroupInfo
* aGroupInfo
, const nsACString
& aOrigin
,
5818 uint64_t aUsage
, int64_t aAccessTime
, bool aPersisted
)
5819 : mGroupInfo(aGroupInfo
),
5822 mAccessTime(aAccessTime
),
5823 mPersisted(aPersisted
) {
5824 MOZ_ASSERT(aGroupInfo
);
5825 MOZ_ASSERT_IF(aPersisted
,
5826 aGroupInfo
->mPersistenceType
== PERSISTENCE_TYPE_DEFAULT
);
5828 MOZ_COUNT_CTOR(OriginInfo
);
5831 void OriginInfo::LockedDecreaseUsage(int64_t aSize
) {
5832 AssertCurrentThreadOwnsQuotaMutex();
5834 AssertNoUnderflow(mUsage
, aSize
);
5837 if (!LockedPersisted()) {
5838 AssertNoUnderflow(mGroupInfo
->mUsage
, aSize
);
5839 mGroupInfo
->mUsage
-= aSize
;
5842 QuotaManager
* quotaManager
= QuotaManager::Get();
5843 MOZ_ASSERT(quotaManager
);
5845 AssertNoUnderflow(quotaManager
->mTemporaryStorageUsage
, aSize
);
5846 quotaManager
->mTemporaryStorageUsage
-= aSize
;
5849 void OriginInfo::LockedPersist() {
5850 AssertCurrentThreadOwnsQuotaMutex();
5851 MOZ_ASSERT(mGroupInfo
->mPersistenceType
== PERSISTENCE_TYPE_DEFAULT
);
5852 MOZ_ASSERT(!mPersisted
);
5856 // Remove Usage from GroupInfo
5857 AssertNoUnderflow(mGroupInfo
->mUsage
, mUsage
);
5858 mGroupInfo
->mUsage
-= mUsage
;
5861 already_AddRefed
<OriginInfo
> GroupInfo::LockedGetOriginInfo(
5862 const nsACString
& aOrigin
) {
5863 AssertCurrentThreadOwnsQuotaMutex();
5865 for (RefPtr
<OriginInfo
>& originInfo
: mOriginInfos
) {
5866 if (originInfo
->mOrigin
== aOrigin
) {
5867 RefPtr
<OriginInfo
> result
= originInfo
;
5868 return result
.forget();
5875 void GroupInfo::LockedAddOriginInfo(OriginInfo
* aOriginInfo
) {
5876 AssertCurrentThreadOwnsQuotaMutex();
5878 NS_ASSERTION(!mOriginInfos
.Contains(aOriginInfo
),
5879 "Replacing an existing entry!");
5880 mOriginInfos
.AppendElement(aOriginInfo
);
5882 if (!aOriginInfo
->LockedPersisted()) {
5883 AssertNoOverflow(mUsage
, aOriginInfo
->mUsage
);
5884 mUsage
+= aOriginInfo
->mUsage
;
5887 QuotaManager
* quotaManager
= QuotaManager::Get();
5888 MOZ_ASSERT(quotaManager
);
5890 AssertNoOverflow(quotaManager
->mTemporaryStorageUsage
, aOriginInfo
->mUsage
);
5891 quotaManager
->mTemporaryStorageUsage
+= aOriginInfo
->mUsage
;
5894 void GroupInfo::LockedRemoveOriginInfo(const nsACString
& aOrigin
) {
5895 AssertCurrentThreadOwnsQuotaMutex();
5897 for (uint32_t index
= 0; index
< mOriginInfos
.Length(); index
++) {
5898 if (mOriginInfos
[index
]->mOrigin
== aOrigin
) {
5899 if (!mOriginInfos
[index
]->LockedPersisted()) {
5900 AssertNoUnderflow(mUsage
, mOriginInfos
[index
]->mUsage
);
5901 mUsage
-= mOriginInfos
[index
]->mUsage
;
5904 QuotaManager
* quotaManager
= QuotaManager::Get();
5905 MOZ_ASSERT(quotaManager
);
5907 AssertNoUnderflow(quotaManager
->mTemporaryStorageUsage
,
5908 mOriginInfos
[index
]->mUsage
);
5909 quotaManager
->mTemporaryStorageUsage
-= mOriginInfos
[index
]->mUsage
;
5911 mOriginInfos
.RemoveElementAt(index
);
5918 void GroupInfo::LockedRemoveOriginInfos() {
5919 AssertCurrentThreadOwnsQuotaMutex();
5921 QuotaManager
* quotaManager
= QuotaManager::Get();
5922 MOZ_ASSERT(quotaManager
);
5924 for (uint32_t index
= mOriginInfos
.Length(); index
> 0; index
--) {
5925 OriginInfo
* originInfo
= mOriginInfos
[index
- 1];
5927 if (!originInfo
->LockedPersisted()) {
5928 AssertNoUnderflow(mUsage
, originInfo
->mUsage
);
5929 mUsage
-= originInfo
->mUsage
;
5932 AssertNoUnderflow(quotaManager
->mTemporaryStorageUsage
, originInfo
->mUsage
);
5933 quotaManager
->mTemporaryStorageUsage
-= originInfo
->mUsage
;
5935 mOriginInfos
.RemoveElementAt(index
- 1);
5939 RefPtr
<GroupInfo
>& GroupInfoPair::GetGroupInfoForPersistenceType(
5940 PersistenceType aPersistenceType
) {
5941 switch (aPersistenceType
) {
5942 case PERSISTENCE_TYPE_TEMPORARY
:
5943 return mTemporaryStorageGroupInfo
;
5944 case PERSISTENCE_TYPE_DEFAULT
:
5945 return mDefaultStorageGroupInfo
;
5947 case PERSISTENCE_TYPE_PERSISTENT
:
5948 case PERSISTENCE_TYPE_INVALID
:
5950 MOZ_CRASH("Bad persistence type value!");
5954 CollectOriginsHelper::CollectOriginsHelper(mozilla::Mutex
& aMutex
,
5955 uint64_t aMinSizeToBeFreed
)
5956 : Runnable("dom::quota::CollectOriginsHelper"),
5957 mMinSizeToBeFreed(aMinSizeToBeFreed
),
5959 mCondVar(aMutex
, "CollectOriginsHelper::mCondVar"),
5962 MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
5963 mMutex
.AssertCurrentThreadOwns();
5966 int64_t CollectOriginsHelper::BlockAndReturnOriginsForEviction(
5967 nsTArray
<RefPtr
<DirectoryLockImpl
>>& aLocks
) {
5968 MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
5969 mMutex
.AssertCurrentThreadOwns();
5975 mLocks
.SwapElements(aLocks
);
5976 return mSizeToBeFreed
;
5980 CollectOriginsHelper::Run() {
5981 AssertIsOnBackgroundThread();
5983 QuotaManager
* quotaManager
= QuotaManager::Get();
5984 NS_ASSERTION(quotaManager
, "Shouldn't be null!");
5986 // We use extra stack vars here to avoid race detector warnings (the same
5987 // memory accessed with and without the lock held).
5988 nsTArray
<RefPtr
<DirectoryLockImpl
>> locks
;
5989 uint64_t sizeToBeFreed
=
5990 quotaManager
->CollectOriginsForEviction(mMinSizeToBeFreed
, locks
);
5992 MutexAutoLock
lock(mMutex
);
5994 NS_ASSERTION(mWaiting
, "Huh?!");
5996 mLocks
.SwapElements(locks
);
5997 mSizeToBeFreed
= sizeToBeFreed
;
6004 /*******************************************************************************
6005 * OriginOperationBase
6006 ******************************************************************************/
6009 OriginOperationBase::Run() {
6013 case State_Initial
: {
6018 case State_Initializing
: {
6019 rv
= InitOnMainThread();
6023 case State_FinishingInit
: {
6028 case State_CreatingQuotaManager
: {
6029 rv
= QuotaManagerOpen();
6033 case State_DirectoryOpenPending
: {
6034 rv
= DirectoryOpen();
6038 case State_DirectoryWorkOpen
: {
6039 rv
= DirectoryWork();
6043 case State_UnblockingOpen
: {
6049 MOZ_CRASH("Bad state!");
6052 if (NS_WARN_IF(NS_FAILED(rv
)) && mState
!= State_UnblockingOpen
) {
6059 nsresult
OriginOperationBase::DirectoryOpen() {
6060 AssertIsOnOwningThread();
6061 MOZ_ASSERT(mState
== State_DirectoryOpenPending
);
6063 QuotaManager
* quotaManager
= QuotaManager::Get();
6064 if (NS_WARN_IF(!quotaManager
)) {
6065 return NS_ERROR_FAILURE
;
6068 // Must set this before dispatching otherwise we will race with the IO thread.
6071 nsresult rv
= quotaManager
->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL
);
6072 if (NS_WARN_IF(NS_FAILED(rv
))) {
6073 return NS_ERROR_FAILURE
;
6079 void OriginOperationBase::Finish(nsresult aResult
) {
6080 if (NS_SUCCEEDED(mResultCode
)) {
6081 mResultCode
= aResult
;
6084 // Must set mState before dispatching otherwise we will race with the main
6086 mState
= State_UnblockingOpen
;
6088 MOZ_ALWAYS_SUCCEEDS(mOwningThread
->Dispatch(this, NS_DISPATCH_NORMAL
));
6091 nsresult
OriginOperationBase::Init() {
6092 AssertIsOnOwningThread();
6093 MOZ_ASSERT(mState
== State_Initial
);
6097 if (mNeedsMainThreadInit
) {
6098 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
6101 MOZ_ALWAYS_SUCCEEDS(Run());
6107 nsresult
OriginOperationBase::InitOnMainThread() {
6108 MOZ_ASSERT(NS_IsMainThread());
6109 MOZ_ASSERT(mState
== State_Initializing
);
6111 nsresult rv
= DoInitOnMainThread();
6112 if (NS_WARN_IF(NS_FAILED(rv
))) {
6118 MOZ_ALWAYS_SUCCEEDS(mOwningThread
->Dispatch(this, NS_DISPATCH_NORMAL
));
6123 nsresult
OriginOperationBase::FinishInit() {
6124 AssertIsOnOwningThread();
6125 MOZ_ASSERT(mState
== State_FinishingInit
);
6127 if (QuotaManager::IsShuttingDown()) {
6128 return NS_ERROR_FAILURE
;
6133 if (mNeedsQuotaManagerInit
&& !QuotaManager::Get()) {
6134 QuotaManager::GetOrCreate(this);
6142 nsresult
OriginOperationBase::QuotaManagerOpen() {
6143 AssertIsOnOwningThread();
6144 MOZ_ASSERT(mState
== State_CreatingQuotaManager
);
6146 if (NS_WARN_IF(!QuotaManager::Get())) {
6147 return NS_ERROR_FAILURE
;
6155 nsresult
OriginOperationBase::DirectoryWork() {
6156 AssertIsOnIOThread();
6157 MOZ_ASSERT(mState
== State_DirectoryWorkOpen
);
6159 QuotaManager
* quotaManager
= QuotaManager::Get();
6160 if (NS_WARN_IF(!quotaManager
)) {
6161 return NS_ERROR_FAILURE
;
6166 if (mNeedsQuotaManagerInit
) {
6167 rv
= quotaManager
->EnsureStorageIsInitialized();
6168 if (NS_WARN_IF(NS_FAILED(rv
))) {
6173 rv
= DoDirectoryWork(quotaManager
);
6174 if (NS_WARN_IF(NS_FAILED(rv
))) {
6178 // Must set mState before dispatching otherwise we will race with the owning
6182 MOZ_ALWAYS_SUCCEEDS(mOwningThread
->Dispatch(this, NS_DISPATCH_NORMAL
));
6187 void FinalizeOriginEvictionOp::Dispatch() {
6188 MOZ_ASSERT(!NS_IsMainThread());
6189 MOZ_ASSERT(GetState() == State_Initial
);
6191 SetState(State_DirectoryOpenPending
);
6193 MOZ_ALWAYS_SUCCEEDS(mOwningThread
->Dispatch(this, NS_DISPATCH_NORMAL
));
6196 void FinalizeOriginEvictionOp::RunOnIOThreadImmediately() {
6197 AssertIsOnIOThread();
6198 MOZ_ASSERT(GetState() == State_Initial
);
6200 SetState(State_DirectoryWorkOpen
);
6202 MOZ_ALWAYS_SUCCEEDS(this->Run());
6205 void FinalizeOriginEvictionOp::Open() { MOZ_CRASH("Shouldn't get here!"); }
6207 nsresult
FinalizeOriginEvictionOp::DoDirectoryWork(
6208 QuotaManager
* aQuotaManager
) {
6209 AssertIsOnIOThread();
6211 AUTO_PROFILER_LABEL("FinalizeOriginEvictionOp::DoDirectoryWork", OTHER
);
6213 for (RefPtr
<DirectoryLockImpl
>& lock
: mLocks
) {
6214 aQuotaManager
->OriginClearCompleted(lock
->GetPersistenceType().Value(),
6215 lock
->GetOriginScope().GetOrigin(),
6216 Nullable
<Client::Type
>());
6222 void FinalizeOriginEvictionOp::UnblockOpen() {
6223 AssertIsOnOwningThread();
6224 MOZ_ASSERT(GetState() == State_UnblockingOpen
);
6227 NoteActorDestroyed();
6235 NS_IMPL_ISUPPORTS_INHERITED0(NormalOriginOperationBase
, Runnable
)
6237 void NormalOriginOperationBase::Open() {
6238 AssertIsOnOwningThread();
6239 MOZ_ASSERT(GetState() == State_CreatingQuotaManager
);
6240 MOZ_ASSERT(QuotaManager::Get());
6244 QuotaManager::Get()->OpenDirectoryInternal(mPersistenceType
, mOriginScope
,
6245 mClientType
, mExclusive
, this);
6248 void NormalOriginOperationBase::UnblockOpen() {
6249 AssertIsOnOwningThread();
6250 MOZ_ASSERT(GetState() == State_UnblockingOpen
);
6254 mDirectoryLock
= nullptr;
6259 void NormalOriginOperationBase::DirectoryLockAcquired(DirectoryLock
* aLock
) {
6260 AssertIsOnOwningThread();
6262 MOZ_ASSERT(GetState() == State_DirectoryOpenPending
);
6263 MOZ_ASSERT(!mDirectoryLock
);
6265 mDirectoryLock
= aLock
;
6267 nsresult rv
= DirectoryOpen();
6268 if (NS_WARN_IF(NS_FAILED(rv
))) {
6274 void NormalOriginOperationBase::DirectoryLockFailed() {
6275 AssertIsOnOwningThread();
6276 MOZ_ASSERT(GetState() == State_DirectoryOpenPending
);
6277 MOZ_ASSERT(!mDirectoryLock
);
6279 Finish(NS_ERROR_FAILURE
);
6282 nsresult
SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager
* aQuotaManager
) {
6283 AssertIsOnIOThread();
6284 MOZ_ASSERT(!mPersistenceType
.IsNull());
6285 MOZ_ASSERT(mOriginScope
.IsOrigin());
6287 AUTO_PROFILER_LABEL("SaveOriginAccessTimeOp::DoDirectoryWork", OTHER
);
6289 nsCOMPtr
<nsIFile
> file
;
6290 nsresult rv
= aQuotaManager
->GetDirectoryForOrigin(
6291 mPersistenceType
.Value(), mOriginScope
.GetOrigin(), getter_AddRefs(file
));
6292 if (NS_WARN_IF(NS_FAILED(rv
))) {
6296 rv
= file
->Append(NS_LITERAL_STRING(METADATA_V2_FILE_NAME
));
6297 if (NS_WARN_IF(NS_FAILED(rv
))) {
6301 nsCOMPtr
<nsIBinaryOutputStream
> stream
;
6302 rv
= GetBinaryOutputStream(file
, kUpdateFileFlag
, getter_AddRefs(stream
));
6303 if (NS_WARN_IF(NS_FAILED(rv
))) {
6307 // The origin directory may not exist anymore.
6309 rv
= stream
->Write64(mTimestamp
);
6310 if (NS_WARN_IF(NS_FAILED(rv
))) {
6318 void SaveOriginAccessTimeOp::SendResults() {
6320 NoteActorDestroyed();
6325 StoragePressureRunnable::Run() {
6326 MOZ_ASSERT(NS_IsMainThread());
6328 nsCOMPtr
<nsIObserverService
> obsSvc
= mozilla::services::GetObserverService();
6329 if (NS_WARN_IF(!obsSvc
)) {
6330 return NS_ERROR_FAILURE
;
6333 nsCOMPtr
<nsISupportsPRUint64
> wrapper
=
6334 do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID
);
6335 if (NS_WARN_IF(!wrapper
)) {
6336 return NS_ERROR_FAILURE
;
6339 wrapper
->SetData(mUsage
);
6341 obsSvc
->NotifyObservers(wrapper
, "QuotaManager::StoragePressure", u
"");
6346 /*******************************************************************************
6348 ******************************************************************************/
6352 : mActorDestroyed(false)
6357 Quota::~Quota() { MOZ_ASSERT(mActorDestroyed
); }
6359 void Quota::StartIdleMaintenance() {
6360 AssertIsOnBackgroundThread();
6361 MOZ_ASSERT(!QuotaManager::IsShuttingDown());
6363 QuotaManager
* quotaManager
= QuotaManager::Get();
6364 if (NS_WARN_IF(!quotaManager
)) {
6368 quotaManager
->StartIdleMaintenance();
6371 void Quota::ActorDestroy(ActorDestroyReason aWhy
) {
6372 AssertIsOnBackgroundThread();
6374 MOZ_ASSERT(!mActorDestroyed
);
6375 mActorDestroyed
= true;
6379 PQuotaUsageRequestParent
* Quota::AllocPQuotaUsageRequestParent(
6380 const UsageRequestParams
& aParams
) {
6381 AssertIsOnBackgroundThread();
6382 MOZ_ASSERT(aParams
.type() != UsageRequestParams::T__None
);
6384 RefPtr
<QuotaUsageRequestBase
> actor
;
6386 switch (aParams
.type()) {
6387 case UsageRequestParams::TAllUsageParams
:
6388 actor
= new GetUsageOp(aParams
);
6391 case UsageRequestParams::TOriginUsageParams
:
6392 actor
= new GetOriginUsageOp(aParams
);
6396 MOZ_CRASH("Should never get here!");
6401 // Transfer ownership to IPDL.
6402 return actor
.forget().take();
6405 mozilla::ipc::IPCResult
Quota::RecvPQuotaUsageRequestConstructor(
6406 PQuotaUsageRequestParent
* aActor
, const UsageRequestParams
& aParams
) {
6407 AssertIsOnBackgroundThread();
6409 MOZ_ASSERT(aParams
.type() != UsageRequestParams::T__None
);
6411 auto* op
= static_cast<QuotaUsageRequestBase
*>(aActor
);
6413 if (NS_WARN_IF(!op
->Init(this))) {
6414 return IPC_FAIL_NO_REASON(this);
6417 op
->RunImmediately();
6421 bool Quota::DeallocPQuotaUsageRequestParent(PQuotaUsageRequestParent
* aActor
) {
6422 AssertIsOnBackgroundThread();
6425 // Transfer ownership back from IPDL.
6426 RefPtr
<QuotaUsageRequestBase
> actor
=
6427 dont_AddRef(static_cast<QuotaUsageRequestBase
*>(aActor
));
6431 PQuotaRequestParent
* Quota::AllocPQuotaRequestParent(
6432 const RequestParams
& aParams
) {
6433 AssertIsOnBackgroundThread();
6434 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
6436 if (aParams
.type() == RequestParams::TClearDataParams
) {
6437 PBackgroundParent
* actor
= Manager();
6440 if (BackgroundParent::IsOtherProcessActor(actor
)) {
6441 ASSERT_UNLESS_FUZZING();
6446 RefPtr
<QuotaRequestBase
> actor
;
6448 switch (aParams
.type()) {
6449 case RequestParams::TInitParams
:
6450 actor
= new InitOp();
6453 case RequestParams::TInitTemporaryStorageParams
:
6454 actor
= new InitTemporaryStorageOp();
6457 case RequestParams::TInitOriginParams
:
6458 actor
= new InitOriginOp(aParams
);
6461 case RequestParams::TClearOriginParams
:
6462 case RequestParams::TResetOriginParams
:
6463 actor
= new ClearOriginOp(aParams
);
6466 case RequestParams::TClearDataParams
:
6467 actor
= new ClearDataOp(aParams
);
6470 case RequestParams::TClearAllParams
:
6471 actor
= new ResetOrClearOp(/* aClear */ true);
6474 case RequestParams::TResetAllParams
:
6475 actor
= new ResetOrClearOp(/* aClear */ false);
6478 case RequestParams::TPersistedParams
:
6479 actor
= new PersistedOp(aParams
);
6482 case RequestParams::TPersistParams
:
6483 actor
= new PersistOp(aParams
);
6487 MOZ_CRASH("Should never get here!");
6492 // Transfer ownership to IPDL.
6493 return actor
.forget().take();
6496 mozilla::ipc::IPCResult
Quota::RecvPQuotaRequestConstructor(
6497 PQuotaRequestParent
* aActor
, const RequestParams
& aParams
) {
6498 AssertIsOnBackgroundThread();
6500 MOZ_ASSERT(aParams
.type() != RequestParams::T__None
);
6502 auto* op
= static_cast<QuotaRequestBase
*>(aActor
);
6504 if (NS_WARN_IF(!op
->Init(this))) {
6505 return IPC_FAIL_NO_REASON(this);
6508 op
->RunImmediately();
6512 bool Quota::DeallocPQuotaRequestParent(PQuotaRequestParent
* aActor
) {
6513 AssertIsOnBackgroundThread();
6516 // Transfer ownership back from IPDL.
6517 RefPtr
<QuotaRequestBase
> actor
=
6518 dont_AddRef(static_cast<QuotaRequestBase
*>(aActor
));
6522 mozilla::ipc::IPCResult
Quota::RecvStartIdleMaintenance() {
6523 AssertIsOnBackgroundThread();
6525 PBackgroundParent
* actor
= Manager();
6528 if (BackgroundParent::IsOtherProcessActor(actor
)) {
6529 ASSERT_UNLESS_FUZZING();
6530 return IPC_FAIL_NO_REASON(this);
6533 if (QuotaManager::IsShuttingDown()) {
6537 QuotaManager
* quotaManager
= QuotaManager::Get();
6538 if (!quotaManager
) {
6539 nsCOMPtr
<nsIRunnable
> callback
=
6540 NewRunnableMethod("dom::quota::Quota::StartIdleMaintenance", this,
6541 &Quota::StartIdleMaintenance
);
6543 QuotaManager::GetOrCreate(callback
);
6547 quotaManager
->StartIdleMaintenance();
6552 mozilla::ipc::IPCResult
Quota::RecvStopIdleMaintenance() {
6553 AssertIsOnBackgroundThread();
6555 PBackgroundParent
* actor
= Manager();
6558 if (BackgroundParent::IsOtherProcessActor(actor
)) {
6559 ASSERT_UNLESS_FUZZING();
6560 return IPC_FAIL_NO_REASON(this);
6563 if (QuotaManager::IsShuttingDown()) {
6567 QuotaManager
* quotaManager
= QuotaManager::Get();
6568 if (!quotaManager
) {
6572 quotaManager
->StopIdleMaintenance();
6577 bool QuotaUsageRequestBase::Init(Quota
* aQuota
) {
6578 AssertIsOnOwningThread();
6581 mNeedsQuotaManagerInit
= true;
6586 nsresult
QuotaUsageRequestBase::GetUsageForOrigin(
6587 QuotaManager
* aQuotaManager
, PersistenceType aPersistenceType
,
6588 const nsACString
& aGroup
, const nsACString
& aOrigin
,
6589 UsageInfo
* aUsageInfo
) {
6590 AssertIsOnIOThread();
6591 MOZ_ASSERT(aQuotaManager
);
6592 MOZ_ASSERT(aUsageInfo
);
6593 MOZ_ASSERT(aUsageInfo
->TotalUsage() == 0);
6595 nsCOMPtr
<nsIFile
> directory
;
6596 nsresult rv
= aQuotaManager
->GetDirectoryForOrigin(aPersistenceType
, aOrigin
,
6597 getter_AddRefs(directory
));
6598 NS_ENSURE_SUCCESS(rv
, rv
);
6601 rv
= directory
->Exists(&exists
);
6602 NS_ENSURE_SUCCESS(rv
, rv
);
6604 // If the directory exists then enumerate all the files inside, adding up
6605 // the sizes to get the final usage statistic.
6606 if (exists
&& !mCanceled
) {
6609 if (aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
6610 initialized
= aQuotaManager
->IsOriginInitialized(aOrigin
);
6612 initialized
= aQuotaManager
->IsTemporaryStorageInitialized();
6615 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
6616 rv
= directory
->GetDirectoryEntries(getter_AddRefs(entries
));
6617 NS_ENSURE_SUCCESS(rv
, rv
);
6619 nsCOMPtr
<nsIFile
> file
;
6620 while (NS_SUCCEEDED((rv
= entries
->GetNextFile(getter_AddRefs(file
)))) &&
6621 file
&& !mCanceled
) {
6623 rv
= file
->IsDirectory(&isDirectory
);
6624 if (NS_WARN_IF(NS_FAILED(rv
))) {
6629 rv
= file
->GetLeafName(leafName
);
6630 if (NS_WARN_IF(NS_FAILED(rv
))) {
6635 // We are maintaining existing behavior here (failing if the origin is
6636 // not yet initialized or just continuing otherwise).
6637 // This can possibly be used by developers to add temporary backups into
6638 // origin directories without losing get usage functionality.
6639 if (IsOriginMetadata(leafName
)) {
6643 if (IsTempMetadata(leafName
)) {
6645 rv
= file
->Remove(/* recursive */ false);
6646 if (NS_WARN_IF(NS_FAILED(rv
))) {
6654 if (IsOSMetadata(leafName
) || IsDotFile(leafName
)) {
6658 UNKNOWN_FILE_WARNING(leafName
);
6660 return NS_ERROR_UNEXPECTED
;
6665 Client::Type clientType
;
6666 rv
= Client::TypeFromText(leafName
, clientType
);
6667 if (NS_FAILED(rv
)) {
6668 UNKNOWN_FILE_WARNING(leafName
);
6670 return NS_ERROR_UNEXPECTED
;
6675 Client
* client
= aQuotaManager
->GetClient(clientType
);
6679 rv
= client
->GetUsageForOrigin(aPersistenceType
, aGroup
, aOrigin
,
6680 mCanceled
, aUsageInfo
);
6682 rv
= client
->InitOrigin(aPersistenceType
, aGroup
, aOrigin
, mCanceled
,
6685 NS_ENSURE_SUCCESS(rv
, rv
);
6692 void QuotaUsageRequestBase::SendResults() {
6693 AssertIsOnOwningThread();
6695 if (IsActorDestroyed()) {
6696 if (NS_SUCCEEDED(mResultCode
)) {
6697 mResultCode
= NS_ERROR_FAILURE
;
6701 mResultCode
= NS_ERROR_FAILURE
;
6704 UsageRequestResponse response
;
6706 if (NS_SUCCEEDED(mResultCode
)) {
6707 GetResponse(response
);
6709 response
= mResultCode
;
6712 Unused
<< PQuotaUsageRequestParent::Send__delete__(this, response
);
6716 void QuotaUsageRequestBase::ActorDestroy(ActorDestroyReason aWhy
) {
6717 AssertIsOnOwningThread();
6719 NoteActorDestroyed();
6722 mozilla::ipc::IPCResult
QuotaUsageRequestBase::RecvCancel() {
6723 AssertIsOnOwningThread();
6725 if (mCanceled
.exchange(true)) {
6726 NS_WARNING("Canceled more than once?!");
6727 return IPC_FAIL_NO_REASON(this);
6733 GetUsageOp::GetUsageOp(const UsageRequestParams
& aParams
)
6734 : mGetAll(aParams
.get_AllUsageParams().getAll()) {
6735 AssertIsOnOwningThread();
6736 MOZ_ASSERT(aParams
.type() == UsageRequestParams::TAllUsageParams
);
6739 nsresult
GetUsageOp::TraverseRepository(QuotaManager
* aQuotaManager
,
6740 PersistenceType aPersistenceType
) {
6741 AssertIsOnIOThread();
6742 MOZ_ASSERT(aQuotaManager
);
6744 nsCOMPtr
<nsIFile
> directory
;
6745 nsresult rv
= NS_NewLocalFile(aQuotaManager
->GetStoragePath(aPersistenceType
),
6746 false, getter_AddRefs(directory
));
6747 if (NS_WARN_IF(NS_FAILED(rv
))) {
6752 rv
= directory
->Exists(&exists
);
6753 if (NS_WARN_IF(NS_FAILED(rv
))) {
6761 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
6762 rv
= directory
->GetDirectoryEntries(getter_AddRefs(entries
));
6763 if (NS_WARN_IF(NS_FAILED(rv
))) {
6767 bool persistent
= aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
;
6769 nsCOMPtr
<nsIFile
> originDir
;
6770 while (NS_SUCCEEDED((rv
= entries
->GetNextFile(getter_AddRefs(originDir
)))) &&
6771 originDir
&& !mCanceled
) {
6773 rv
= originDir
->IsDirectory(&isDirectory
);
6774 if (NS_WARN_IF(NS_FAILED(rv
))) {
6780 rv
= originDir
->GetLeafName(leafName
);
6781 if (NS_WARN_IF(NS_FAILED(rv
))) {
6785 // Unknown files during getting usages are allowed. Just warn if we find
6787 if (!IsOSMetadata(leafName
)) {
6788 UNKNOWN_FILE_WARNING(leafName
);
6798 rv
= aQuotaManager
->GetDirectoryMetadata2WithRestore(
6799 originDir
, persistent
, ×tamp
, &persisted
, suffix
, group
, origin
);
6800 if (NS_WARN_IF(NS_FAILED(rv
))) {
6804 if (!mGetAll
&& aQuotaManager
->IsOriginInternal(origin
)) {
6808 OriginUsage
* originUsage
;
6810 // We can't store pointers to OriginUsage objects in the hashtable
6811 // since AppendElement() reallocates its internal array buffer as number
6812 // of elements grows.
6814 if (mOriginUsagesIndex
.Get(origin
, &index
)) {
6815 originUsage
= &mOriginUsages
[index
];
6817 index
= mOriginUsages
.Length();
6819 originUsage
= mOriginUsages
.AppendElement();
6821 originUsage
->origin() = origin
;
6822 originUsage
->persisted() = false;
6823 originUsage
->usage() = 0;
6825 mOriginUsagesIndex
.Put(origin
, index
);
6828 if (aPersistenceType
== PERSISTENCE_TYPE_DEFAULT
) {
6829 originUsage
->persisted() = persisted
;
6832 originUsage
->lastAccessed() = timestamp
;
6834 UsageInfo usageInfo
;
6835 rv
= GetUsageForOrigin(aQuotaManager
, aPersistenceType
, group
, origin
,
6837 if (NS_WARN_IF(NS_FAILED(rv
))) {
6841 originUsage
->usage() = originUsage
->usage() + usageInfo
.TotalUsage();
6843 if (NS_WARN_IF(NS_FAILED(rv
))) {
6850 nsresult
GetUsageOp::DoDirectoryWork(QuotaManager
* aQuotaManager
) {
6851 AssertIsOnIOThread();
6853 AUTO_PROFILER_LABEL("GetUsageOp::DoDirectoryWork", OTHER
);
6857 for (const PersistenceType type
: kAllPersistenceTypes
) {
6858 rv
= TraverseRepository(aQuotaManager
, type
);
6859 if (NS_WARN_IF(NS_FAILED(rv
))) {
6867 void GetUsageOp::GetResponse(UsageRequestResponse
& aResponse
) {
6868 AssertIsOnOwningThread();
6870 aResponse
= AllUsageResponse();
6872 if (!mOriginUsages
.IsEmpty()) {
6873 nsTArray
<OriginUsage
>& originUsages
=
6874 aResponse
.get_AllUsageResponse().originUsages();
6876 mOriginUsages
.SwapElements(originUsages
);
6880 GetOriginUsageOp::GetOriginUsageOp(const UsageRequestParams
& aParams
)
6881 : mParams(aParams
.get_OriginUsageParams()),
6882 mGetGroupUsage(aParams
.get_OriginUsageParams().getGroupUsage()) {
6883 AssertIsOnOwningThread();
6884 MOZ_ASSERT(aParams
.type() == UsageRequestParams::TOriginUsageParams
);
6887 bool GetOriginUsageOp::Init(Quota
* aQuota
) {
6888 AssertIsOnOwningThread();
6891 if (NS_WARN_IF(!QuotaUsageRequestBase::Init(aQuota
))) {
6895 mNeedsMainThreadInit
= true;
6900 nsresult
GetOriginUsageOp::DoInitOnMainThread() {
6901 MOZ_ASSERT(NS_IsMainThread());
6902 MOZ_ASSERT(GetState() == State_Initializing
);
6903 MOZ_ASSERT(mNeedsMainThreadInit
);
6905 const PrincipalInfo
& principalInfo
= mParams
.principalInfo();
6908 nsCOMPtr
<nsIPrincipal
> principal
=
6909 PrincipalInfoToPrincipal(principalInfo
, &rv
);
6910 if (NS_WARN_IF(NS_FAILED(rv
))) {
6914 // Figure out which origin we're dealing with.
6917 QuotaManager::GetInfoFromPrincipal(principal
, &mSuffix
, &mGroup
, &origin
);
6918 if (NS_WARN_IF(NS_FAILED(rv
))) {
6922 mOriginScope
.SetFromOrigin(origin
);
6927 nsresult
GetOriginUsageOp::DoDirectoryWork(QuotaManager
* aQuotaManager
) {
6928 AssertIsOnIOThread();
6929 MOZ_ASSERT(mUsageInfo
.TotalUsage() == 0);
6931 AUTO_PROFILER_LABEL("GetOriginUsageOp::DoDirectoryWork", OTHER
);
6935 if (mGetGroupUsage
) {
6936 // Ensure temporary storage is initialized first. It will initialize all
6937 // origins for temporary storage including origins belonging to our group by
6938 // traversing the repositories. EnsureStorageIsInitialized is needed before
6939 // EnsureTemporaryStorageIsInitialized.
6940 rv
= aQuotaManager
->EnsureStorageIsInitialized();
6941 if (NS_WARN_IF(NS_FAILED(rv
))) {
6945 rv
= aQuotaManager
->EnsureTemporaryStorageIsInitialized();
6946 if (NS_WARN_IF(NS_FAILED(rv
))) {
6950 // Get cached usage and limit (the method doesn't have to stat any files).
6951 aQuotaManager
->GetGroupUsageAndLimit(mGroup
, &mUsageInfo
);
6956 // Add all the persistent/temporary/default storage files we care about.
6957 for (const PersistenceType type
: kAllPersistenceTypes
) {
6958 UsageInfo usageInfo
;
6959 rv
= GetUsageForOrigin(aQuotaManager
, type
, mGroup
,
6960 mOriginScope
.GetOrigin(), &usageInfo
);
6961 if (NS_WARN_IF(NS_FAILED(rv
))) {
6965 mUsageInfo
.Append(usageInfo
);
6971 void GetOriginUsageOp::GetResponse(UsageRequestResponse
& aResponse
) {
6972 AssertIsOnOwningThread();
6974 OriginUsageResponse usageResponse
;
6976 // We'll get the group usage when mGetGroupUsage is true and get the
6977 // origin usage when mGetGroupUsage is false.
6978 usageResponse
.usage() = mUsageInfo
.TotalUsage();
6980 if (mGetGroupUsage
) {
6981 usageResponse
.limit() = mUsageInfo
.Limit();
6983 usageResponse
.fileUsage() = mUsageInfo
.FileUsage();
6986 aResponse
= usageResponse
;
6989 bool QuotaRequestBase::Init(Quota
* aQuota
) {
6990 AssertIsOnOwningThread();
6993 mNeedsQuotaManagerInit
= true;
6998 void QuotaRequestBase::SendResults() {
6999 AssertIsOnOwningThread();
7001 if (IsActorDestroyed()) {
7002 if (NS_SUCCEEDED(mResultCode
)) {
7003 mResultCode
= NS_ERROR_FAILURE
;
7006 RequestResponse response
;
7008 if (NS_SUCCEEDED(mResultCode
)) {
7009 GetResponse(response
);
7011 response
= mResultCode
;
7014 Unused
<< PQuotaRequestParent::Send__delete__(this, response
);
7018 void QuotaRequestBase::ActorDestroy(ActorDestroyReason aWhy
) {
7019 AssertIsOnOwningThread();
7021 NoteActorDestroyed();
7024 nsresult
InitOp::DoDirectoryWork(QuotaManager
* aQuotaManager
) {
7025 AssertIsOnIOThread();
7027 AUTO_PROFILER_LABEL("InitOp::DoDirectoryWork", OTHER
);
7029 aQuotaManager
->AssertStorageIsInitialized();
7034 void InitOp::GetResponse(RequestResponse
& aResponse
) {
7035 AssertIsOnOwningThread();
7037 aResponse
= InitResponse();
7040 nsresult
InitTemporaryStorageOp::DoDirectoryWork(QuotaManager
* aQuotaManager
) {
7041 AssertIsOnIOThread();
7043 AUTO_PROFILER_LABEL("InitTemporaryStorageOp::DoDirectoryWork", OTHER
);
7045 aQuotaManager
->AssertStorageIsInitialized();
7047 nsresult rv
= aQuotaManager
->EnsureTemporaryStorageIsInitialized();
7048 if (NS_WARN_IF(NS_FAILED(rv
))) {
7055 void InitTemporaryStorageOp::GetResponse(RequestResponse
& aResponse
) {
7056 AssertIsOnOwningThread();
7058 aResponse
= InitTemporaryStorageResponse();
7061 InitOriginOp::InitOriginOp(const RequestParams
& aParams
)
7062 : QuotaRequestBase(/* aExclusive */ false),
7063 mParams(aParams
.get_InitOriginParams()),
7065 AssertIsOnOwningThread();
7066 MOZ_ASSERT(aParams
.type() == RequestParams::TInitOriginParams
);
7069 bool InitOriginOp::Init(Quota
* aQuota
) {
7070 AssertIsOnOwningThread();
7073 if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota
))) {
7077 MOZ_ASSERT(mParams
.persistenceType() != PERSISTENCE_TYPE_INVALID
);
7079 mPersistenceType
.SetValue(mParams
.persistenceType());
7081 mNeedsMainThreadInit
= true;
7086 nsresult
InitOriginOp::DoInitOnMainThread() {
7087 MOZ_ASSERT(NS_IsMainThread());
7088 MOZ_ASSERT(GetState() == State_Initializing
);
7089 MOZ_ASSERT(mNeedsMainThreadInit
);
7091 const PrincipalInfo
& principalInfo
= mParams
.principalInfo();
7094 nsCOMPtr
<nsIPrincipal
> principal
=
7095 PrincipalInfoToPrincipal(principalInfo
, &rv
);
7096 if (NS_WARN_IF(NS_FAILED(rv
))) {
7100 // Figure out which origin we're dealing with.
7103 QuotaManager::GetInfoFromPrincipal(principal
, &mSuffix
, &mGroup
, &origin
);
7104 if (NS_WARN_IF(NS_FAILED(rv
))) {
7108 mOriginScope
.SetFromOrigin(origin
);
7113 nsresult
InitOriginOp::DoDirectoryWork(QuotaManager
* aQuotaManager
) {
7114 AssertIsOnIOThread();
7115 MOZ_ASSERT(!mPersistenceType
.IsNull());
7117 AUTO_PROFILER_LABEL("InitOriginOp::DoDirectoryWork", OTHER
);
7119 nsCOMPtr
<nsIFile
> directory
;
7121 nsresult rv
= aQuotaManager
->EnsureOriginIsInitializedInternal(
7122 mPersistenceType
.Value(), mSuffix
, mGroup
, mOriginScope
.GetOrigin(),
7123 /* aCreateIfNotExists */ true, getter_AddRefs(directory
), &created
);
7124 if (NS_WARN_IF(NS_FAILED(rv
))) {
7133 void InitOriginOp::GetResponse(RequestResponse
& aResponse
) {
7134 AssertIsOnOwningThread();
7136 InitOriginResponse response
;
7138 response
.created() = mCreated
;
7140 aResponse
= response
;
7143 void ResetOrClearOp::DeleteFiles(QuotaManager
* aQuotaManager
) {
7144 AssertIsOnIOThread();
7145 MOZ_ASSERT(aQuotaManager
);
7147 nsresult rv
= aQuotaManager
->AboutToClearOrigins(Nullable
<PersistenceType
>(),
7148 OriginScope::FromNull(),
7149 Nullable
<Client::Type
>());
7150 if (NS_WARN_IF(NS_FAILED(rv
))) {
7154 nsCOMPtr
<nsIFile
> directory
;
7155 rv
= NS_NewLocalFile(aQuotaManager
->GetStoragePath(), false,
7156 getter_AddRefs(directory
));
7157 if (NS_WARN_IF(NS_FAILED(rv
))) {
7161 rv
= directory
->Remove(true);
7162 if (rv
!= NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
&&
7163 rv
!= NS_ERROR_FILE_NOT_FOUND
&& NS_FAILED(rv
)) {
7164 // This should never fail if we've closed all storage connections
7166 MOZ_ASSERT(false, "Failed to remove storage directory!");
7169 nsCOMPtr
<nsIFile
> storageFile
;
7170 rv
= NS_NewLocalFile(aQuotaManager
->GetBasePath(), false,
7171 getter_AddRefs(storageFile
));
7172 if (NS_WARN_IF(NS_FAILED(rv
))) {
7176 rv
= storageFile
->Append(NS_LITERAL_STRING(STORAGE_FILE_NAME
));
7177 if (NS_WARN_IF(NS_FAILED(rv
))) {
7181 rv
= storageFile
->Remove(true);
7182 if (rv
!= NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
&&
7183 rv
!= NS_ERROR_FILE_NOT_FOUND
&& NS_FAILED(rv
)) {
7184 // This should never fail if we've closed the storage connection
7186 MOZ_ASSERT(false, "Failed to remove storage file!");
7190 nsresult
ResetOrClearOp::DoDirectoryWork(QuotaManager
* aQuotaManager
) {
7191 AssertIsOnIOThread();
7193 AUTO_PROFILER_LABEL("ResetOrClearOp::DoDirectoryWork", OTHER
);
7196 DeleteFiles(aQuotaManager
);
7199 aQuotaManager
->RemoveQuota();
7201 aQuotaManager
->ResetOrClearCompleted();
7206 void ResetOrClearOp::GetResponse(RequestResponse
& aResponse
) {
7207 AssertIsOnOwningThread();
7209 aResponse
= ClearAllResponse();
7211 aResponse
= ResetAllResponse();
7215 void ClearRequestBase::DeleteFiles(QuotaManager
* aQuotaManager
,
7216 PersistenceType aPersistenceType
) {
7217 AssertIsOnIOThread();
7218 MOZ_ASSERT(aQuotaManager
);
7220 nsresult rv
= aQuotaManager
->AboutToClearOrigins(
7221 Nullable
<PersistenceType
>(aPersistenceType
), mOriginScope
, mClientType
);
7222 if (NS_WARN_IF(NS_FAILED(rv
))) {
7226 nsCOMPtr
<nsIFile
> directory
;
7227 rv
= NS_NewLocalFile(aQuotaManager
->GetStoragePath(aPersistenceType
), false,
7228 getter_AddRefs(directory
));
7229 if (NS_WARN_IF(NS_FAILED(rv
))) {
7233 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
7235 NS_FAILED(directory
->GetDirectoryEntries(getter_AddRefs(entries
)))) ||
7240 OriginScope originScope
= mOriginScope
.Clone();
7241 if (originScope
.IsOrigin()) {
7242 nsCString
originSanitized(originScope
.GetOrigin());
7243 SanitizeOriginString(originSanitized
);
7244 originScope
.SetOrigin(originSanitized
);
7245 } else if (originScope
.IsPrefix()) {
7246 nsCString
originNoSuffixSanitized(originScope
.GetOriginNoSuffix());
7247 SanitizeOriginString(originNoSuffixSanitized
);
7248 originScope
.SetOriginNoSuffix(originNoSuffixSanitized
);
7251 nsCOMPtr
<nsIFile
> file
;
7252 while (NS_SUCCEEDED((rv
= entries
->GetNextFile(getter_AddRefs(file
)))) &&
7255 rv
= file
->IsDirectory(&isDirectory
);
7256 if (NS_WARN_IF(NS_FAILED(rv
))) {
7261 rv
= file
->GetLeafName(leafName
);
7262 if (NS_WARN_IF(NS_FAILED(rv
))) {
7267 // Unknown files during clearing are allowed. Just warn if we find them.
7268 if (!IsOSMetadata(leafName
)) {
7269 UNKNOWN_FILE_WARNING(leafName
);
7274 // Skip the origin directory if it doesn't match the pattern.
7275 if (!originScope
.Matches(
7276 OriginScope::FromOrigin(NS_ConvertUTF16toUTF8(leafName
)))) {
7280 bool persistent
= aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
;
7287 rv
= aQuotaManager
->GetDirectoryMetadata2WithRestore(
7288 file
, persistent
, ×tamp
, &persisted
, suffix
, group
, origin
);
7289 if (NS_WARN_IF(NS_FAILED(rv
))) {
7293 UsageInfo usageInfo
;
7295 if (!mClientType
.IsNull()) {
7296 Client::Type clientType
= mClientType
.Value();
7298 nsAutoString clientDirectoryName
;
7299 rv
= Client::TypeToText(clientType
, clientDirectoryName
);
7300 if (NS_WARN_IF(NS_FAILED(rv
))) {
7304 rv
= file
->Append(clientDirectoryName
);
7305 if (NS_WARN_IF(NS_FAILED(rv
))) {
7310 rv
= file
->Exists(&exists
);
7311 if (NS_WARN_IF(NS_FAILED(rv
))) {
7320 if (aPersistenceType
== PERSISTENCE_TYPE_PERSISTENT
) {
7321 initialized
= aQuotaManager
->IsOriginInitialized(origin
);
7323 initialized
= aQuotaManager
->IsTemporaryStorageInitialized();
7326 Client
* client
= aQuotaManager
->GetClient(clientType
);
7329 Atomic
<bool> dummy(false);
7331 rv
= client
->GetUsageForOrigin(aPersistenceType
, group
, origin
, dummy
,
7334 rv
= client
->InitOrigin(aPersistenceType
, group
, origin
, dummy
,
7337 if (NS_WARN_IF(NS_FAILED(rv
))) {
7342 for (uint32_t index
= 0; index
< 10; index
++) {
7343 // We can't guarantee that this will always succeed on Windows...
7344 if (NS_SUCCEEDED((rv
= file
->Remove(true)))) {
7348 NS_WARNING("Failed to remove directory, retrying after a short delay.");
7350 PR_Sleep(PR_MillisecondsToInterval(200));
7353 if (NS_FAILED(rv
)) {
7354 NS_WARNING("Failed to remove directory, giving up!");
7357 if (aPersistenceType
!= PERSISTENCE_TYPE_PERSISTENT
) {
7358 if (mClientType
.IsNull()) {
7359 aQuotaManager
->RemoveQuotaForOrigin(aPersistenceType
, group
, origin
);
7361 aQuotaManager
->DecreaseUsageForOrigin(aPersistenceType
, group
, origin
,
7362 usageInfo
.TotalUsage());
7366 aQuotaManager
->OriginClearCompleted(aPersistenceType
, origin
, mClientType
);
7370 nsresult
ClearRequestBase::DoDirectoryWork(QuotaManager
* aQuotaManager
) {
7371 AssertIsOnIOThread();
7373 AUTO_PROFILER_LABEL("ClearRequestBase::DoDirectoryWork", OTHER
);
7376 if (mPersistenceType
.IsNull()) {
7377 for (const PersistenceType type
: kAllPersistenceTypes
) {
7378 DeleteFiles(aQuotaManager
, type
);
7381 DeleteFiles(aQuotaManager
, mPersistenceType
.Value());
7388 ClearOriginOp::ClearOriginOp(const RequestParams
& aParams
)
7389 : ClearRequestBase(/* aExclusive */ true,
7390 aParams
.type() == RequestParams::TClearOriginParams
),
7391 mParams(aParams
.type() == RequestParams::TClearOriginParams
7392 ? aParams
.get_ClearOriginParams().commonParams()
7393 : aParams
.get_ResetOriginParams().commonParams())
7396 MOZ_ASSERT(aParams
.type() == RequestParams::TClearOriginParams
||
7397 aParams
.type() == RequestParams::TResetOriginParams
);
7400 bool ClearOriginOp::Init(Quota
* aQuota
) {
7401 AssertIsOnOwningThread();
7404 if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota
))) {
7408 if (mParams
.persistenceTypeIsExplicit()) {
7409 MOZ_ASSERT(mParams
.persistenceType() != PERSISTENCE_TYPE_INVALID
);
7411 mPersistenceType
.SetValue(mParams
.persistenceType());
7414 if (mParams
.clientTypeIsExplicit()) {
7415 MOZ_ASSERT(mParams
.clientType() != Client::TYPE_MAX
);
7417 mClientType
.SetValue(mParams
.clientType());
7420 mNeedsMainThreadInit
= true;
7425 nsresult
ClearOriginOp::DoInitOnMainThread() {
7426 MOZ_ASSERT(NS_IsMainThread());
7427 MOZ_ASSERT(GetState() == State_Initializing
);
7428 MOZ_ASSERT(mNeedsMainThreadInit
);
7430 const PrincipalInfo
& principalInfo
= mParams
.principalInfo();
7433 nsCOMPtr
<nsIPrincipal
> principal
=
7434 PrincipalInfoToPrincipal(principalInfo
, &rv
);
7435 if (NS_WARN_IF(NS_FAILED(rv
))) {
7439 // Figure out which origin we're dealing with.
7441 rv
= QuotaManager::GetInfoFromPrincipal(principal
, nullptr, nullptr, &origin
);
7442 if (NS_WARN_IF(NS_FAILED(rv
))) {
7446 if (mParams
.matchAll()) {
7447 mOriginScope
.SetFromPrefix(origin
);
7449 mOriginScope
.SetFromOrigin(origin
);
7455 void ClearOriginOp::GetResponse(RequestResponse
& aResponse
) {
7456 AssertIsOnOwningThread();
7459 aResponse
= ClearOriginResponse();
7461 aResponse
= ResetOriginResponse();
7465 ClearDataOp::ClearDataOp(const RequestParams
& aParams
)
7466 : ClearRequestBase(/* aExclusive */ true,
7469 MOZ_ASSERT(aParams
.type() == RequestParams::TClearDataParams
);
7472 bool ClearDataOp::Init(Quota
* aQuota
) {
7473 AssertIsOnOwningThread();
7476 if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota
))) {
7480 mNeedsMainThreadInit
= true;
7485 nsresult
ClearDataOp::DoInitOnMainThread() {
7486 MOZ_ASSERT(NS_IsMainThread());
7487 MOZ_ASSERT(GetState() == State_Initializing
);
7488 MOZ_ASSERT(mNeedsMainThreadInit
);
7490 mOriginScope
.SetFromJSONPattern(mParams
.pattern());
7495 void ClearDataOp::GetResponse(RequestResponse
& aResponse
) {
7496 AssertIsOnOwningThread();
7498 aResponse
= ClearDataResponse();
7501 PersistRequestBase::PersistRequestBase(const PrincipalInfo
& aPrincipalInfo
)
7502 : QuotaRequestBase(/* aExclusive */ false), mPrincipalInfo(aPrincipalInfo
) {
7503 AssertIsOnOwningThread();
7506 bool PersistRequestBase::Init(Quota
* aQuota
) {
7507 AssertIsOnOwningThread();
7510 if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota
))) {
7514 mPersistenceType
.SetValue(PERSISTENCE_TYPE_DEFAULT
);
7516 mNeedsMainThreadInit
= true;
7521 nsresult
PersistRequestBase::DoInitOnMainThread() {
7522 MOZ_ASSERT(NS_IsMainThread());
7523 MOZ_ASSERT(GetState() == State_Initializing
);
7524 MOZ_ASSERT(mNeedsMainThreadInit
);
7527 nsCOMPtr
<nsIPrincipal
> principal
=
7528 PrincipalInfoToPrincipal(mPrincipalInfo
, &rv
);
7529 if (NS_WARN_IF(NS_FAILED(rv
))) {
7533 // Figure out which origin we're dealing with.
7536 QuotaManager::GetInfoFromPrincipal(principal
, &mSuffix
, &mGroup
, &origin
);
7537 if (NS_WARN_IF(NS_FAILED(rv
))) {
7541 mOriginScope
.SetFromOrigin(origin
);
7546 PersistedOp::PersistedOp(const RequestParams
& aParams
)
7547 : PersistRequestBase(aParams
.get_PersistedParams().principalInfo()),
7549 MOZ_ASSERT(aParams
.type() == RequestParams::TPersistedParams
);
7552 nsresult
PersistedOp::DoDirectoryWork(QuotaManager
* aQuotaManager
) {
7553 AssertIsOnIOThread();
7554 MOZ_ASSERT(!mPersistenceType
.IsNull());
7555 MOZ_ASSERT(mPersistenceType
.Value() == PERSISTENCE_TYPE_DEFAULT
);
7556 MOZ_ASSERT(mOriginScope
.IsOrigin());
7558 AUTO_PROFILER_LABEL("PersistedOp::DoDirectoryWork", OTHER
);
7560 Nullable
<bool> persisted
=
7561 aQuotaManager
->OriginPersisted(mGroup
, mOriginScope
.GetOrigin());
7563 if (!persisted
.IsNull()) {
7564 mPersisted
= persisted
.Value();
7568 // If we get here, it means the origin hasn't been initialized yet.
7569 // Try to get the persisted flag from directory metadata on disk.
7571 nsCOMPtr
<nsIFile
> directory
;
7572 nsresult rv
= aQuotaManager
->GetDirectoryForOrigin(mPersistenceType
.Value(),
7573 mOriginScope
.GetOrigin(),
7574 getter_AddRefs(directory
));
7575 if (NS_WARN_IF(NS_FAILED(rv
))) {
7580 rv
= directory
->Exists(&exists
);
7581 if (NS_WARN_IF(NS_FAILED(rv
))) {
7586 // Get the persisted flag.
7588 rv
= aQuotaManager
->GetDirectoryMetadata2WithRestore(
7590 /* aPersistent */ false,
7591 /* aTimestamp */ nullptr, &persisted
);
7592 if (NS_WARN_IF(NS_FAILED(rv
))) {
7596 mPersisted
= persisted
;
7598 // The directory has not been created yet.
7605 void PersistedOp::GetResponse(RequestResponse
& aResponse
) {
7606 AssertIsOnOwningThread();
7608 PersistedResponse persistedResponse
;
7609 persistedResponse
.persisted() = mPersisted
;
7611 aResponse
= persistedResponse
;
7614 PersistOp::PersistOp(const RequestParams
& aParams
)
7615 : PersistRequestBase(aParams
.get_PersistParams().principalInfo()) {
7616 MOZ_ASSERT(aParams
.type() == RequestParams::TPersistParams
);
7619 nsresult
PersistOp::DoDirectoryWork(QuotaManager
* aQuotaManager
) {
7620 AssertIsOnIOThread();
7621 MOZ_ASSERT(!mPersistenceType
.IsNull());
7622 MOZ_ASSERT(mPersistenceType
.Value() == PERSISTENCE_TYPE_DEFAULT
);
7623 MOZ_ASSERT(mOriginScope
.IsOrigin());
7625 AUTO_PROFILER_LABEL("PersistOp::DoDirectoryWork", OTHER
);
7627 // Update directory metadata on disk first. Then, create/update the originInfo
7629 nsCOMPtr
<nsIFile
> directory
;
7630 nsresult rv
= aQuotaManager
->GetDirectoryForOrigin(mPersistenceType
.Value(),
7631 mOriginScope
.GetOrigin(),
7632 getter_AddRefs(directory
));
7633 if (NS_WARN_IF(NS_FAILED(rv
))) {
7638 rv
= aQuotaManager
->EnsureOriginDirectory(directory
,
7639 /* aCreateIfNotExists */ true,
7641 if (NS_WARN_IF(NS_FAILED(rv
))) {
7647 rv
= CreateDirectoryMetadataFiles(directory
,
7648 /* aPersisted */ true, mSuffix
, mGroup
,
7649 mOriginScope
.GetOrigin(), ×tamp
);
7650 if (NS_WARN_IF(NS_FAILED(rv
))) {
7654 // Directory metadata has been successfully created.
7655 // Create OriginInfo too if temporary storage was already initialized.
7656 if (aQuotaManager
->IsTemporaryStorageInitialized()) {
7657 aQuotaManager
->InitQuotaForOrigin(mPersistenceType
.Value(), mGroup
,
7658 mOriginScope
.GetOrigin(),
7659 /* aUsageBytes */ 0, timestamp
,
7660 /* aPersisted */ true);
7663 // Get the persisted flag (restore the metadata file if necessary).
7665 rv
= aQuotaManager
->GetDirectoryMetadata2WithRestore(
7667 /* aPersistent */ false,
7668 /* aTimestamp */ nullptr, &persisted
);
7669 if (NS_WARN_IF(NS_FAILED(rv
))) {
7674 nsCOMPtr
<nsIFile
> file
;
7675 nsresult rv
= directory
->Clone(getter_AddRefs(file
));
7676 if (NS_WARN_IF(NS_FAILED(rv
))) {
7680 rv
= file
->Append(NS_LITERAL_STRING(METADATA_V2_FILE_NAME
));
7681 if (NS_WARN_IF(NS_FAILED(rv
))) {
7685 nsCOMPtr
<nsIBinaryOutputStream
> stream
;
7686 rv
= GetBinaryOutputStream(file
, kUpdateFileFlag
, getter_AddRefs(stream
));
7687 if (NS_WARN_IF(NS_FAILED(rv
))) {
7693 // Update origin access time while we are here.
7694 rv
= stream
->Write64(PR_Now());
7695 if (NS_WARN_IF(NS_FAILED(rv
))) {
7699 // Set the persisted flag to true.
7700 rv
= stream
->WriteBoolean(true);
7701 if (NS_WARN_IF(NS_FAILED(rv
))) {
7706 // Directory metadata has been successfully updated.
7707 // Update OriginInfo too if temporary storage was already initialized.
7708 if (aQuotaManager
->IsTemporaryStorageInitialized()) {
7709 aQuotaManager
->PersistOrigin(mGroup
, mOriginScope
.GetOrigin());
7716 void PersistOp::GetResponse(RequestResponse
& aResponse
) {
7717 AssertIsOnOwningThread();
7719 aResponse
= PersistResponse();
7722 nsresult
StorageOperationBase::GetDirectoryMetadata(nsIFile
* aDirectory
,
7723 int64_t& aTimestamp
,
7725 nsACString
& aOrigin
,
7726 Nullable
<bool>& aIsApp
) {
7727 AssertIsOnIOThread();
7728 MOZ_ASSERT(aDirectory
);
7730 nsCOMPtr
<nsIBinaryInputStream
> binaryStream
;
7732 GetBinaryInputStream(aDirectory
, NS_LITERAL_STRING(METADATA_FILE_NAME
),
7733 getter_AddRefs(binaryStream
));
7734 if (NS_WARN_IF(NS_FAILED(rv
))) {
7739 rv
= binaryStream
->Read64(×tamp
);
7740 if (NS_WARN_IF(NS_FAILED(rv
))) {
7745 rv
= binaryStream
->ReadCString(group
);
7746 if (NS_WARN_IF(NS_FAILED(rv
))) {
7751 rv
= binaryStream
->ReadCString(origin
);
7752 if (NS_WARN_IF(NS_FAILED(rv
))) {
7756 Nullable
<bool> isApp
;
7758 if (NS_SUCCEEDED(binaryStream
->ReadBoolean(&value
))) {
7759 isApp
.SetValue(value
);
7762 aTimestamp
= timestamp
;
7765 aIsApp
= std::move(isApp
);
7769 nsresult
StorageOperationBase::GetDirectoryMetadata2(
7770 nsIFile
* aDirectory
, int64_t& aTimestamp
, nsACString
& aSuffix
,
7771 nsACString
& aGroup
, nsACString
& aOrigin
, bool& aIsApp
) {
7772 AssertIsOnIOThread();
7773 MOZ_ASSERT(aDirectory
);
7775 nsCOMPtr
<nsIBinaryInputStream
> binaryStream
;
7777 GetBinaryInputStream(aDirectory
, NS_LITERAL_STRING(METADATA_V2_FILE_NAME
),
7778 getter_AddRefs(binaryStream
));
7779 if (NS_WARN_IF(NS_FAILED(rv
))) {
7784 rv
= binaryStream
->Read64(×tamp
);
7785 if (NS_WARN_IF(NS_FAILED(rv
))) {
7790 rv
= binaryStream
->ReadBoolean(&persisted
);
7791 if (NS_WARN_IF(NS_FAILED(rv
))) {
7795 uint32_t reservedData1
;
7796 rv
= binaryStream
->Read32(&reservedData1
);
7797 if (NS_WARN_IF(NS_FAILED(rv
))) {
7801 uint32_t reservedData2
;
7802 rv
= binaryStream
->Read32(&reservedData2
);
7803 if (NS_WARN_IF(NS_FAILED(rv
))) {
7808 rv
= binaryStream
->ReadCString(suffix
);
7809 if (NS_WARN_IF(NS_FAILED(rv
))) {
7814 rv
= binaryStream
->ReadCString(group
);
7815 if (NS_WARN_IF(NS_FAILED(rv
))) {
7820 rv
= binaryStream
->ReadCString(origin
);
7821 if (NS_WARN_IF(NS_FAILED(rv
))) {
7826 rv
= binaryStream
->ReadBoolean(&isApp
);
7827 if (NS_WARN_IF(NS_FAILED(rv
))) {
7831 aTimestamp
= timestamp
;
7839 nsresult
StorageOperationBase::RemoveObsoleteOrigin(
7840 const OriginProps
& aOriginProps
) {
7841 AssertIsOnIOThread();
7842 MOZ_ASSERT(aOriginProps
.mDirectory
);
7845 "Deleting obsolete %s directory that is no longer a legal "
7847 NS_ConvertUTF16toUTF8(aOriginProps
.mLeafName
).get());
7849 nsresult rv
= aOriginProps
.mDirectory
->Remove(/* recursive */ true);
7850 if (NS_WARN_IF(NS_FAILED(rv
))) {
7857 nsresult
StorageOperationBase::ProcessOriginDirectories() {
7858 AssertIsOnIOThread();
7859 MOZ_ASSERT(!mOriginProps
.IsEmpty());
7861 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
7864 mozilla::MutexAutoLock
autolock(mMutex
);
7870 if (NS_WARN_IF(NS_FAILED(mMainThreadResultCode
))) {
7871 return mMainThreadResultCode
;
7874 // Verify that the bounce to the main thread didn't start the shutdown
7876 if (NS_WARN_IF(QuotaManager::IsShuttingDown())) {
7877 return NS_ERROR_FAILURE
;
7882 // Don't try to upgrade obsolete origins, remove them right after we detect
7884 for (auto& originProps
: mOriginProps
) {
7885 if (originProps
.mType
== OriginProps::eObsolete
) {
7886 MOZ_ASSERT(originProps
.mSuffix
.IsEmpty());
7887 MOZ_ASSERT(originProps
.mGroup
.IsEmpty());
7888 MOZ_ASSERT(originProps
.mOrigin
.IsEmpty());
7890 rv
= RemoveObsoleteOrigin(originProps
);
7892 MOZ_ASSERT(!originProps
.mGroup
.IsEmpty());
7893 MOZ_ASSERT(!originProps
.mOrigin
.IsEmpty());
7895 rv
= ProcessOriginDirectory(originProps
);
7897 if (NS_WARN_IF(NS_FAILED(rv
))) {
7905 nsresult
StorageOperationBase::RunOnMainThread() {
7906 MOZ_ASSERT(NS_IsMainThread());
7907 MOZ_ASSERT(!mOriginProps
.IsEmpty());
7911 for (uint32_t count
= mOriginProps
.Length(), index
= 0; index
< count
;
7913 OriginProps
& originProps
= mOriginProps
[index
];
7915 switch (originProps
.mType
) {
7916 case OriginProps::eChrome
: {
7917 QuotaManager::GetInfoForChrome(
7918 &originProps
.mSuffix
, &originProps
.mGroup
, &originProps
.mOrigin
);
7922 case OriginProps::eContent
: {
7923 nsCOMPtr
<nsIURI
> uri
;
7924 rv
= NS_NewURI(getter_AddRefs(uri
), originProps
.mSpec
);
7925 if (NS_WARN_IF(NS_FAILED(rv
))) {
7929 nsCOMPtr
<nsIPrincipal
> principal
=
7930 BasePrincipal::CreateCodebasePrincipal(uri
, originProps
.mAttrs
);
7931 if (NS_WARN_IF(!principal
)) {
7932 return NS_ERROR_FAILURE
;
7935 rv
= QuotaManager::GetInfoFromPrincipal(principal
, &originProps
.mSuffix
,
7936 &originProps
.mGroup
,
7937 &originProps
.mOrigin
);
7938 if (NS_WARN_IF(NS_FAILED(rv
))) {
7945 case OriginProps::eObsolete
: {
7946 // There's no way to get info for obsolete origins.
7951 MOZ_CRASH("Bad type!");
7959 StorageOperationBase::Run() {
7960 MOZ_ASSERT(NS_IsMainThread());
7962 nsresult rv
= RunOnMainThread();
7963 if (NS_WARN_IF(NS_FAILED(rv
))) {
7964 mMainThreadResultCode
= rv
;
7967 MutexAutoLock
lock(mMutex
);
7968 MOZ_ASSERT(mWaiting
);
7976 nsresult
StorageOperationBase::OriginProps::Init(nsIFile
* aDirectory
) {
7977 AssertIsOnIOThread();
7978 MOZ_ASSERT(aDirectory
);
7981 nsresult rv
= aDirectory
->GetLeafName(leafName
);
7982 if (NS_WARN_IF(NS_FAILED(rv
))) {
7986 if (leafName
.EqualsLiteral(kChromeOrigin
)) {
7987 // XXX We can remove this special handling once origin parser supports it
7989 mDirectory
= aDirectory
;
7990 mLeafName
= leafName
;
7991 mSpec
= kChromeOrigin
;
7993 } else if (leafName
.EqualsLiteral("moz-safe-about+++home")) {
7994 // XXX We can remove this special handling once origin parser supports it
7997 // This directory was accidentally created by a buggy nightly and can be
8000 mDirectory
= aDirectory
;
8001 mLeafName
= leafName
;
8005 OriginAttributes attrs
;
8006 OriginParser::ResultType result
= OriginParser::ParseOrigin(
8007 NS_ConvertUTF16toUTF8(leafName
), spec
, &attrs
);
8008 if (NS_WARN_IF(result
== OriginParser::InvalidOrigin
)) {
8009 return NS_ERROR_FAILURE
;
8012 mDirectory
= aDirectory
;
8013 mLeafName
= leafName
;
8016 mType
= result
== OriginParser::ObsoleteOrigin
? eObsolete
: eContent
;
8023 auto OriginParser::ParseOrigin(const nsACString
& aOrigin
, nsCString
& aSpec
,
8024 OriginAttributes
* aAttrs
) -> ResultType
{
8025 MOZ_ASSERT(!aOrigin
.IsEmpty());
8028 OriginAttributes originAttributes
;
8030 nsCString originNoSuffix
;
8031 bool ok
= originAttributes
.PopulateFromOrigin(aOrigin
, originNoSuffix
);
8033 return InvalidOrigin
;
8036 OriginParser
parser(originNoSuffix
, originAttributes
);
8037 return parser
.Parse(aSpec
, aAttrs
);
8040 auto OriginParser::Parse(nsACString
& aSpec
, OriginAttributes
* aAttrs
)
8044 while (mTokenizer
.hasMoreTokens()) {
8045 const nsDependentCSubstring
& token
= mTokenizer
.nextToken();
8053 if (!mHandledTokens
.IsEmpty()) {
8054 mHandledTokens
.AppendLiteral(", ");
8056 mHandledTokens
.Append('\'');
8057 mHandledTokens
.Append(token
);
8058 mHandledTokens
.Append('\'');
8061 if (!mError
&& mTokenizer
.separatorAfterCurrentToken()) {
8062 HandleTrailingSeparator();
8066 QM_WARNING("Origin '%s' failed to parse, handled tokens: %s", mOrigin
.get(),
8067 mHandledTokens
.get());
8069 return InvalidOrigin
;
8072 MOZ_ASSERT(mState
== eComplete
|| mState
== eHandledTrailingSeparator
);
8074 // For IPv6 URL, it should at least have three groups.
8075 MOZ_ASSERT_IF(mIPGroup
> 0, mIPGroup
>= 3);
8077 if (mAppId
== kNoAppId
) {
8078 *aAttrs
= mOriginAttributes
;
8080 MOZ_ASSERT(mOriginAttributes
.mAppId
== kNoAppId
);
8082 *aAttrs
= OriginAttributes(mAppId
, mInIsolatedMozBrowser
);
8085 nsAutoCString
spec(mScheme
);
8087 if (mSchemeType
== eFile
) {
8088 spec
.AppendLiteral("://");
8090 if (mUniversalFileOrigin
) {
8091 MOZ_ASSERT(mPathnameComponents
.Length() == 1);
8093 spec
.Append(mPathnameComponents
[0]);
8095 for (uint32_t count
= mPathnameComponents
.Length(), index
= 0;
8096 index
< count
; index
++) {
8098 spec
.Append(mPathnameComponents
[index
]);
8107 if (mSchemeType
== eAbout
) {
8110 spec
.AppendLiteral("://");
8115 if (!mPort
.IsNull()) {
8117 spec
.AppendInt(mPort
.Value());
8122 return mScheme
.EqualsLiteral("app") ? ObsoleteOrigin
: ValidOrigin
;
8125 void OriginParser::HandleScheme(const nsDependentCSubstring
& aToken
) {
8126 MOZ_ASSERT(!aToken
.IsEmpty());
8127 MOZ_ASSERT(mState
== eExpectingAppIdOrScheme
|| mState
== eExpectingScheme
);
8129 bool isAbout
= false;
8130 bool isFile
= false;
8131 if (aToken
.EqualsLiteral("http") || aToken
.EqualsLiteral("https") ||
8132 (isAbout
= aToken
.EqualsLiteral("about") ||
8133 aToken
.EqualsLiteral("moz-safe-about")) ||
8134 aToken
.EqualsLiteral("indexeddb") ||
8135 (isFile
= aToken
.EqualsLiteral("file")) || aToken
.EqualsLiteral("app") ||
8136 aToken
.EqualsLiteral("resource") ||
8137 aToken
.EqualsLiteral("moz-extension")) {
8141 mSchemeType
= eAbout
;
8142 mState
= eExpectingHost
;
8145 mSchemeType
= eFile
;
8147 mState
= eExpectingEmptyToken1
;
8153 QM_WARNING("'%s' is not a valid scheme!", nsCString(aToken
).get());
8158 void OriginParser::HandlePathnameComponent(
8159 const nsDependentCSubstring
& aToken
) {
8160 MOZ_ASSERT(!aToken
.IsEmpty());
8161 MOZ_ASSERT(mState
== eExpectingEmptyTokenOrDriveLetterOrPathnameComponent
||
8162 mState
== eExpectingEmptyTokenOrPathnameComponent
);
8163 MOZ_ASSERT(mSchemeType
== eFile
);
8165 mPathnameComponents
.AppendElement(aToken
);
8167 mState
= mTokenizer
.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
8171 void OriginParser::HandleToken(const nsDependentCSubstring
& aToken
) {
8173 case eExpectingAppIdOrScheme
: {
8174 if (aToken
.IsEmpty()) {
8175 QM_WARNING("Expected an app id or scheme (not an empty string)!");
8181 if (IsAsciiDigit(aToken
.First())) {
8182 // nsDependentCSubstring doesn't provice ToInteger()
8183 nsCString
token(aToken
);
8186 uint32_t appId
= token
.ToInteger(&rv
);
8187 if (NS_SUCCEEDED(rv
)) {
8189 mState
= eExpectingInMozBrowser
;
8194 HandleScheme(aToken
);
8199 case eExpectingInMozBrowser
: {
8200 if (aToken
.Length() != 1) {
8201 QM_WARNING("'%d' is not a valid length for the inMozBrowser flag!",
8208 if (aToken
.First() == 't') {
8209 mInIsolatedMozBrowser
= true;
8210 } else if (aToken
.First() == 'f') {
8211 mInIsolatedMozBrowser
= false;
8213 QM_WARNING("'%s' is not a valid value for the inMozBrowser flag!",
8214 nsCString(aToken
).get());
8220 mState
= eExpectingScheme
;
8225 case eExpectingScheme
: {
8226 if (aToken
.IsEmpty()) {
8227 QM_WARNING("Expected a scheme (not an empty string)!");
8233 HandleScheme(aToken
);
8238 case eExpectingEmptyToken1
: {
8239 if (!aToken
.IsEmpty()) {
8240 QM_WARNING("Expected the first empty token!");
8246 mState
= eExpectingEmptyToken2
;
8251 case eExpectingEmptyToken2
: {
8252 if (!aToken
.IsEmpty()) {
8253 QM_WARNING("Expected the second empty token!");
8259 if (mSchemeType
== eFile
) {
8260 mState
= eExpectingEmptyTokenOrUniversalFileOrigin
;
8262 mState
= eExpectingHost
;
8268 case eExpectingEmptyTokenOrUniversalFileOrigin
: {
8269 MOZ_ASSERT(mSchemeType
== eFile
);
8271 if (aToken
.IsEmpty()) {
8272 mState
= mTokenizer
.hasMoreTokens()
8273 ? eExpectingEmptyTokenOrDriveLetterOrPathnameComponent
8279 if (aToken
.EqualsLiteral("UNIVERSAL_FILE_URI_ORIGIN")) {
8280 mUniversalFileOrigin
= true;
8282 mPathnameComponents
.AppendElement(aToken
);
8290 "Expected the third empty token or "
8291 "UNIVERSAL_FILE_URI_ORIGIN!");
8297 case eExpectingHost
: {
8298 if (aToken
.IsEmpty()) {
8299 QM_WARNING("Expected a host (not an empty string)!");
8307 if (aToken
.First() == '[') {
8308 MOZ_ASSERT(mIPGroup
== 0);
8311 mState
= eExpectingIPV6Token
;
8313 MOZ_ASSERT(mTokenizer
.hasMoreTokens());
8317 mState
= mTokenizer
.hasMoreTokens() ? eExpectingPort
: eComplete
;
8322 case eExpectingPort
: {
8323 MOZ_ASSERT(mSchemeType
== eNone
);
8325 if (aToken
.IsEmpty()) {
8326 QM_WARNING("Expected a port (not an empty string)!");
8332 // nsDependentCSubstring doesn't provice ToInteger()
8333 nsCString
token(aToken
);
8336 uint32_t port
= token
.ToInteger(&rv
);
8337 if (NS_SUCCEEDED(rv
)) {
8338 mPort
.SetValue() = port
;
8340 QM_WARNING("'%s' is not a valid port number!", token
.get());
8351 case eExpectingEmptyTokenOrDriveLetterOrPathnameComponent
: {
8352 MOZ_ASSERT(mSchemeType
== eFile
);
8354 if (aToken
.IsEmpty()) {
8355 mPathnameComponents
.AppendElement(EmptyCString());
8357 mState
= mTokenizer
.hasMoreTokens()
8358 ? eExpectingEmptyTokenOrPathnameComponent
8364 if (aToken
.Length() == 1 && IsAsciiAlpha(aToken
.First())) {
8365 mMaybeDriveLetter
= true;
8367 mPathnameComponents
.AppendElement(aToken
);
8369 mState
= mTokenizer
.hasMoreTokens()
8370 ? eExpectingEmptyTokenOrPathnameComponent
8376 HandlePathnameComponent(aToken
);
8381 case eExpectingEmptyTokenOrPathnameComponent
: {
8382 MOZ_ASSERT(mSchemeType
== eFile
);
8384 if (aToken
.IsEmpty()) {
8385 if (mMaybeDriveLetter
) {
8386 MOZ_ASSERT(mPathnameComponents
.Length() == 1);
8388 nsCString
& pathnameComponent
= mPathnameComponents
[0];
8389 pathnameComponent
.Append(':');
8391 mMaybeDriveLetter
= false;
8393 mPathnameComponents
.AppendElement(EmptyCString());
8396 mState
= mTokenizer
.hasMoreTokens()
8397 ? eExpectingEmptyTokenOrPathnameComponent
8403 HandlePathnameComponent(aToken
);
8408 case eExpectingIPV6Token
: {
8409 // A safe check for preventing infinity recursion.
8410 if (++mIPGroup
> 8) {
8415 mHost
.AppendLiteral(":");
8416 mHost
.Append(aToken
);
8417 if (!aToken
.IsEmpty() && aToken
.Last() == ']') {
8418 mState
= mTokenizer
.hasMoreTokens() ? eExpectingPort
: eComplete
;
8425 MOZ_CRASH("Should never get here!");
8429 void OriginParser::HandleTrailingSeparator() {
8430 MOZ_ASSERT(mState
== eComplete
);
8431 MOZ_ASSERT(mSchemeType
== eFile
);
8433 mPathnameComponents
.AppendElement(EmptyCString());
8435 mState
= eHandledTrailingSeparator
;
8438 nsresult
RepositoryOperationBase::ProcessRepository() {
8439 AssertIsOnIOThread();
8441 DebugOnly
<bool> exists
;
8442 MOZ_ASSERT(NS_SUCCEEDED(mDirectory
->Exists(&exists
)));
8445 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
8446 nsresult rv
= mDirectory
->GetDirectoryEntries(getter_AddRefs(entries
));
8447 if (NS_WARN_IF(NS_FAILED(rv
))) {
8452 nsCOMPtr
<nsIFile
> originDir
;
8453 rv
= entries
->GetNextFile(getter_AddRefs(originDir
));
8454 if (NS_WARN_IF(NS_FAILED(rv
))) {
8463 rv
= originDir
->IsDirectory(&isDirectory
);
8464 if (NS_WARN_IF(NS_FAILED(rv
))) {
8470 rv
= originDir
->GetLeafName(leafName
);
8471 if (NS_WARN_IF(NS_FAILED(rv
))) {
8475 // Unknown files during upgrade are allowed. Just warn if we find them.
8476 if (!IsOSMetadata(leafName
)) {
8477 UNKNOWN_FILE_WARNING(leafName
);
8482 OriginProps originProps
;
8483 rv
= originProps
.Init(originDir
);
8484 if (NS_WARN_IF(NS_FAILED(rv
))) {
8488 if (originProps
.mType
!= OriginProps::eObsolete
) {
8490 rv
= PrepareOriginDirectory(originProps
, &removed
);
8491 if (NS_WARN_IF(NS_FAILED(rv
))) {
8499 mOriginProps
.AppendElement(std::move(originProps
));
8502 if (mOriginProps
.IsEmpty()) {
8506 rv
= ProcessOriginDirectories();
8507 if (NS_WARN_IF(NS_FAILED(rv
))) {
8514 template <typename UpgradeMethod
>
8515 nsresult
RepositoryOperationBase::MaybeUpgradeClients(
8516 const OriginProps
& aOriginProps
, UpgradeMethod aMethod
) {
8517 AssertIsOnIOThread();
8518 MOZ_ASSERT(aOriginProps
.mDirectory
);
8519 MOZ_ASSERT(aMethod
);
8521 QuotaManager
* quotaManager
= QuotaManager::Get();
8522 MOZ_ASSERT(quotaManager
);
8524 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
8526 aOriginProps
.mDirectory
->GetDirectoryEntries(getter_AddRefs(entries
));
8527 if (NS_WARN_IF(NS_FAILED(rv
))) {
8532 nsCOMPtr
<nsIFile
> file
;
8533 rv
= entries
->GetNextFile(getter_AddRefs(file
));
8534 if (NS_WARN_IF(NS_FAILED(rv
))) {
8543 rv
= file
->IsDirectory(&isDirectory
);
8544 if (NS_WARN_IF(NS_FAILED(rv
))) {
8549 rv
= file
->GetLeafName(leafName
);
8550 if (NS_WARN_IF(NS_FAILED(rv
))) {
8555 // Unknown files during upgrade are allowed. Just warn if we find them.
8556 if (!IsOriginMetadata(leafName
) && !IsTempMetadata(leafName
)) {
8557 UNKNOWN_FILE_WARNING(leafName
);
8562 Client::Type clientType
;
8563 rv
= Client::TypeFromText(leafName
, clientType
);
8564 if (NS_FAILED(rv
)) {
8565 UNKNOWN_FILE_WARNING(leafName
);
8569 Client
* client
= quotaManager
->GetClient(clientType
);
8572 rv
= (client
->*aMethod
)(file
);
8573 if (NS_WARN_IF(NS_FAILED(rv
))) {
8581 nsresult
CreateOrUpgradeDirectoryMetadataHelper::MaybeUpgradeOriginDirectory(
8582 nsIFile
* aDirectory
) {
8583 AssertIsOnIOThread();
8584 MOZ_ASSERT(aDirectory
);
8586 nsCOMPtr
<nsIFile
> metadataFile
;
8587 nsresult rv
= aDirectory
->Clone(getter_AddRefs(metadataFile
));
8588 if (NS_WARN_IF(NS_FAILED(rv
))) {
8592 rv
= metadataFile
->Append(NS_LITERAL_STRING(METADATA_FILE_NAME
));
8593 if (NS_WARN_IF(NS_FAILED(rv
))) {
8598 rv
= metadataFile
->Exists(&exists
);
8599 if (NS_WARN_IF(NS_FAILED(rv
))) {
8604 // Directory structure upgrade needed.
8605 // Move all files to IDB specific directory.
8607 nsString idbDirectoryName
;
8608 rv
= Client::TypeToText(Client::IDB
, idbDirectoryName
);
8609 if (NS_WARN_IF(NS_FAILED(rv
))) {
8613 nsCOMPtr
<nsIFile
> idbDirectory
;
8614 rv
= aDirectory
->Clone(getter_AddRefs(idbDirectory
));
8615 if (NS_WARN_IF(NS_FAILED(rv
))) {
8619 rv
= idbDirectory
->Append(idbDirectoryName
);
8620 if (NS_WARN_IF(NS_FAILED(rv
))) {
8624 rv
= idbDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755);
8625 if (rv
== NS_ERROR_FILE_ALREADY_EXISTS
) {
8626 NS_WARNING("IDB directory already exists!");
8629 rv
= idbDirectory
->IsDirectory(&isDirectory
);
8630 if (NS_WARN_IF(NS_FAILED(rv
))) {
8634 if (NS_WARN_IF(!isDirectory
)) {
8635 return NS_ERROR_UNEXPECTED
;
8638 if (NS_WARN_IF(NS_FAILED(rv
))) {
8643 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
8644 rv
= aDirectory
->GetDirectoryEntries(getter_AddRefs(entries
));
8645 if (NS_WARN_IF(NS_FAILED(rv
))) {
8649 nsCOMPtr
<nsIFile
> file
;
8650 while (NS_SUCCEEDED((rv
= entries
->GetNextFile(getter_AddRefs(file
)))) &&
8653 rv
= file
->GetLeafName(leafName
);
8654 if (NS_WARN_IF(NS_FAILED(rv
))) {
8658 if (!leafName
.Equals(idbDirectoryName
)) {
8659 rv
= file
->MoveTo(idbDirectory
, EmptyString());
8660 if (NS_WARN_IF(NS_FAILED(rv
))) {
8666 rv
= metadataFile
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644);
8667 if (NS_WARN_IF(NS_FAILED(rv
))) {
8675 nsresult
CreateOrUpgradeDirectoryMetadataHelper::PrepareOriginDirectory(
8676 OriginProps
& aOriginProps
, bool* aRemoved
) {
8677 AssertIsOnIOThread();
8678 MOZ_ASSERT(aOriginProps
.mDirectory
);
8679 MOZ_ASSERT(aRemoved
);
8684 rv
= MaybeUpgradeOriginDirectory(aOriginProps
.mDirectory
);
8685 if (NS_WARN_IF(NS_FAILED(rv
))) {
8689 bool persistent
= QuotaManager::IsOriginInternal(aOriginProps
.mSpec
);
8690 aOriginProps
.mTimestamp
=
8691 GetLastModifiedTime(aOriginProps
.mDirectory
, persistent
);
8696 Nullable
<bool> isApp
;
8697 rv
= GetDirectoryMetadata(aOriginProps
.mDirectory
, timestamp
, group
, origin
,
8699 if (NS_FAILED(rv
)) {
8700 aOriginProps
.mTimestamp
=
8701 GetLastModifiedTime(aOriginProps
.mDirectory
, mPersistent
);
8702 aOriginProps
.mNeedsRestore
= true;
8703 } else if (!isApp
.IsNull()) {
8704 aOriginProps
.mIgnore
= true;
8712 nsresult
CreateOrUpgradeDirectoryMetadataHelper::ProcessOriginDirectory(
8713 const OriginProps
& aOriginProps
) {
8714 AssertIsOnIOThread();
8719 rv
= CreateDirectoryMetadata(aOriginProps
.mDirectory
,
8720 aOriginProps
.mTimestamp
, aOriginProps
.mSuffix
,
8721 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
8722 if (NS_WARN_IF(NS_FAILED(rv
))) {
8726 // Move internal origins to new persistent storage.
8727 if (QuotaManager::IsOriginInternal(aOriginProps
.mSpec
)) {
8728 if (!mPermanentStorageDir
) {
8729 QuotaManager
* quotaManager
= QuotaManager::Get();
8730 MOZ_ASSERT(quotaManager
);
8732 const nsString
& permanentStoragePath
=
8733 quotaManager
->GetStoragePath(PERSISTENCE_TYPE_PERSISTENT
);
8735 rv
= NS_NewLocalFile(permanentStoragePath
, false,
8736 getter_AddRefs(mPermanentStorageDir
));
8737 if (NS_WARN_IF(NS_FAILED(rv
))) {
8743 rv
= aOriginProps
.mDirectory
->GetLeafName(leafName
);
8744 if (NS_WARN_IF(NS_FAILED(rv
))) {
8748 nsCOMPtr
<nsIFile
> newDirectory
;
8749 rv
= mPermanentStorageDir
->Clone(getter_AddRefs(newDirectory
));
8750 if (NS_WARN_IF(NS_FAILED(rv
))) {
8754 rv
= newDirectory
->Append(leafName
);
8755 if (NS_WARN_IF(NS_FAILED(rv
))) {
8760 rv
= newDirectory
->Exists(&exists
);
8761 if (NS_WARN_IF(NS_FAILED(rv
))) {
8766 QM_WARNING("Found %s in storage/persistent and storage/permanent !",
8767 NS_ConvertUTF16toUTF8(leafName
).get());
8769 rv
= aOriginProps
.mDirectory
->Remove(/* recursive */ true);
8771 rv
= aOriginProps
.mDirectory
->MoveTo(mPermanentStorageDir
,
8774 if (NS_WARN_IF(NS_FAILED(rv
))) {
8778 } else if (aOriginProps
.mNeedsRestore
) {
8779 rv
= CreateDirectoryMetadata(aOriginProps
.mDirectory
,
8780 aOriginProps
.mTimestamp
, aOriginProps
.mSuffix
,
8781 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
8782 if (NS_WARN_IF(NS_FAILED(rv
))) {
8785 } else if (!aOriginProps
.mIgnore
) {
8786 nsCOMPtr
<nsIFile
> file
;
8787 rv
= aOriginProps
.mDirectory
->Clone(getter_AddRefs(file
));
8788 if (NS_WARN_IF(NS_FAILED(rv
))) {
8792 rv
= file
->Append(NS_LITERAL_STRING(METADATA_FILE_NAME
));
8793 if (NS_WARN_IF(NS_FAILED(rv
))) {
8797 nsCOMPtr
<nsIBinaryOutputStream
> stream
;
8798 rv
= GetBinaryOutputStream(file
, kAppendFileFlag
, getter_AddRefs(stream
));
8799 if (NS_WARN_IF(NS_FAILED(rv
))) {
8805 // Currently unused (used to be isApp).
8806 rv
= stream
->WriteBoolean(false);
8807 if (NS_WARN_IF(NS_FAILED(rv
))) {
8815 nsresult
UpgradeStorageFrom0_0To1_0Helper::PrepareOriginDirectory(
8816 OriginProps
& aOriginProps
, bool* aRemoved
) {
8817 AssertIsOnIOThread();
8818 MOZ_ASSERT(aOriginProps
.mDirectory
);
8819 MOZ_ASSERT(aRemoved
);
8824 Nullable
<bool> isApp
;
8825 nsresult rv
= GetDirectoryMetadata(aOriginProps
.mDirectory
, timestamp
, group
,
8827 if (NS_FAILED(rv
) || isApp
.IsNull()) {
8828 aOriginProps
.mTimestamp
=
8829 GetLastModifiedTime(aOriginProps
.mDirectory
, mPersistent
);
8830 aOriginProps
.mNeedsRestore
= true;
8832 aOriginProps
.mTimestamp
= timestamp
;
8839 nsresult
UpgradeStorageFrom0_0To1_0Helper::ProcessOriginDirectory(
8840 const OriginProps
& aOriginProps
) {
8841 AssertIsOnIOThread();
8845 if (aOriginProps
.mNeedsRestore
) {
8846 rv
= CreateDirectoryMetadata(aOriginProps
.mDirectory
,
8847 aOriginProps
.mTimestamp
, aOriginProps
.mSuffix
,
8848 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
8849 if (NS_WARN_IF(NS_FAILED(rv
))) {
8855 CreateDirectoryMetadata2(aOriginProps
.mDirectory
, aOriginProps
.mTimestamp
,
8856 /* aPersisted */ false, aOriginProps
.mSuffix
,
8857 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
8858 if (NS_WARN_IF(NS_FAILED(rv
))) {
8863 rv
= aOriginProps
.mDirectory
->GetLeafName(oldName
);
8864 if (NS_WARN_IF(NS_FAILED(rv
))) {
8868 nsAutoCString
originSanitized(aOriginProps
.mOrigin
);
8869 SanitizeOriginString(originSanitized
);
8871 NS_ConvertASCIItoUTF16
newName(originSanitized
);
8873 if (!oldName
.Equals(newName
)) {
8874 rv
= aOriginProps
.mDirectory
->RenameTo(nullptr, newName
);
8875 if (NS_WARN_IF(NS_FAILED(rv
))) {
8883 nsresult
UpgradeStorageFrom1_0To2_0Helper::MaybeRemoveMorgueDirectory(
8884 const OriginProps
& aOriginProps
) {
8885 AssertIsOnIOThread();
8886 MOZ_ASSERT(aOriginProps
.mDirectory
);
8888 // The Cache API was creating top level morgue directories by accident for
8889 // a short time in nightly. This unfortunately prevents all storage from
8890 // working. So recover these profiles permanently by removing these corrupt
8891 // directories as part of this upgrade.
8893 nsCOMPtr
<nsIFile
> morgueDir
;
8894 nsresult rv
= aOriginProps
.mDirectory
->Clone(getter_AddRefs(morgueDir
));
8895 if (NS_WARN_IF(NS_FAILED(rv
))) {
8899 rv
= morgueDir
->Append(NS_LITERAL_STRING("morgue"));
8900 if (NS_WARN_IF(NS_FAILED(rv
))) {
8905 rv
= morgueDir
->Exists(&exists
);
8906 if (NS_WARN_IF(NS_FAILED(rv
))) {
8911 QM_WARNING("Deleting accidental morgue directory!");
8913 rv
= morgueDir
->Remove(/* recursive */ true);
8914 if (NS_WARN_IF(NS_FAILED(rv
))) {
8922 nsresult
UpgradeStorageFrom1_0To2_0Helper::MaybeRemoveAppsData(
8923 const OriginProps
& aOriginProps
, bool* aRemoved
) {
8924 AssertIsOnIOThread();
8926 // XXX This will need to be reworked as part of bug 1320404 (appId is
8927 // going to be removed from origin attributes).
8928 if (aOriginProps
.mAttrs
.mAppId
!= kNoAppId
&&
8929 aOriginProps
.mAttrs
.mAppId
!= kUnknownAppId
) {
8930 nsresult rv
= RemoveObsoleteOrigin(aOriginProps
);
8931 if (NS_WARN_IF(NS_FAILED(rv
))) {
8943 nsresult
UpgradeStorageFrom1_0To2_0Helper::MaybeStripObsoleteOriginAttributes(
8944 const OriginProps
& aOriginProps
, bool* aStripped
) {
8945 AssertIsOnIOThread();
8946 MOZ_ASSERT(aOriginProps
.mDirectory
);
8948 const nsAString
& oldLeafName
= aOriginProps
.mLeafName
;
8950 nsCString
originSanitized(aOriginProps
.mOrigin
);
8951 SanitizeOriginString(originSanitized
);
8953 NS_ConvertUTF8toUTF16
newLeafName(originSanitized
);
8955 if (oldLeafName
== newLeafName
) {
8960 nsresult rv
= CreateDirectoryMetadata(
8961 aOriginProps
.mDirectory
, aOriginProps
.mTimestamp
, aOriginProps
.mSuffix
,
8962 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
8963 if (NS_WARN_IF(NS_FAILED(rv
))) {
8968 CreateDirectoryMetadata2(aOriginProps
.mDirectory
, aOriginProps
.mTimestamp
,
8969 /* aPersisted */ false, aOriginProps
.mSuffix
,
8970 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
8971 if (NS_WARN_IF(NS_FAILED(rv
))) {
8975 nsCOMPtr
<nsIFile
> newFile
;
8976 rv
= aOriginProps
.mDirectory
->GetParent(getter_AddRefs(newFile
));
8977 if (NS_WARN_IF(NS_FAILED(rv
))) {
8981 rv
= newFile
->Append(newLeafName
);
8982 if (NS_WARN_IF(NS_FAILED(rv
))) {
8987 rv
= newFile
->Exists(&exists
);
8988 if (NS_WARN_IF(NS_FAILED(rv
))) {
8994 "Can't rename %s directory, %s directory already exists, "
8996 NS_ConvertUTF16toUTF8(oldLeafName
).get(),
8997 NS_ConvertUTF16toUTF8(newLeafName
).get());
8999 rv
= aOriginProps
.mDirectory
->Remove(/* recursive */ true);
9001 rv
= aOriginProps
.mDirectory
->RenameTo(nullptr, newLeafName
);
9003 if (NS_WARN_IF(NS_FAILED(rv
))) {
9011 nsresult
UpgradeStorageFrom1_0To2_0Helper::PrepareOriginDirectory(
9012 OriginProps
& aOriginProps
, bool* aRemoved
) {
9013 AssertIsOnIOThread();
9014 MOZ_ASSERT(aOriginProps
.mDirectory
);
9015 MOZ_ASSERT(aRemoved
);
9017 nsresult rv
= MaybeRemoveMorgueDirectory(aOriginProps
);
9018 if (NS_WARN_IF(NS_FAILED(rv
))) {
9022 rv
= MaybeUpgradeClients(aOriginProps
, &Client::UpgradeStorageFrom1_0To2_0
);
9023 if (NS_WARN_IF(NS_FAILED(rv
))) {
9028 rv
= MaybeRemoveAppsData(aOriginProps
, &removed
);
9029 if (NS_WARN_IF(NS_FAILED(rv
))) {
9040 Nullable
<bool> isApp
;
9041 rv
= GetDirectoryMetadata(aOriginProps
.mDirectory
, timestamp
, group
, origin
,
9043 if (NS_FAILED(rv
) || isApp
.IsNull()) {
9044 aOriginProps
.mNeedsRestore
= true;
9048 rv
= GetDirectoryMetadata2(aOriginProps
.mDirectory
, timestamp
, suffix
, group
,
9049 origin
, isApp
.SetValue());
9050 if (NS_FAILED(rv
)) {
9051 aOriginProps
.mTimestamp
=
9052 GetLastModifiedTime(aOriginProps
.mDirectory
, mPersistent
);
9053 aOriginProps
.mNeedsRestore2
= true;
9055 aOriginProps
.mTimestamp
= timestamp
;
9062 nsresult
UpgradeStorageFrom1_0To2_0Helper::ProcessOriginDirectory(
9063 const OriginProps
& aOriginProps
) {
9064 AssertIsOnIOThread();
9067 nsresult rv
= MaybeStripObsoleteOriginAttributes(aOriginProps
, &stripped
);
9068 if (NS_WARN_IF(NS_FAILED(rv
))) {
9075 if (aOriginProps
.mNeedsRestore
) {
9076 rv
= CreateDirectoryMetadata(aOriginProps
.mDirectory
,
9077 aOriginProps
.mTimestamp
, aOriginProps
.mSuffix
,
9078 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
9079 if (NS_WARN_IF(NS_FAILED(rv
))) {
9084 if (aOriginProps
.mNeedsRestore2
) {
9085 rv
= CreateDirectoryMetadata2(aOriginProps
.mDirectory
,
9086 aOriginProps
.mTimestamp
,
9087 /* aPersisted */ false, aOriginProps
.mSuffix
,
9088 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
9089 if (NS_WARN_IF(NS_FAILED(rv
))) {
9097 nsresult
UpgradeStorageFrom2_0To2_1Helper::PrepareOriginDirectory(
9098 OriginProps
& aOriginProps
, bool* aRemoved
) {
9099 AssertIsOnIOThread();
9100 MOZ_ASSERT(aOriginProps
.mDirectory
);
9101 MOZ_ASSERT(aRemoved
);
9104 MaybeUpgradeClients(aOriginProps
, &Client::UpgradeStorageFrom2_0To2_1
);
9105 if (NS_WARN_IF(NS_FAILED(rv
))) {
9112 Nullable
<bool> isApp
;
9113 rv
= GetDirectoryMetadata(aOriginProps
.mDirectory
, timestamp
, group
, origin
,
9115 if (NS_FAILED(rv
) || isApp
.IsNull()) {
9116 aOriginProps
.mNeedsRestore
= true;
9120 rv
= GetDirectoryMetadata2(aOriginProps
.mDirectory
, timestamp
, suffix
, group
,
9121 origin
, isApp
.SetValue());
9122 if (NS_FAILED(rv
)) {
9123 aOriginProps
.mTimestamp
=
9124 GetLastModifiedTime(aOriginProps
.mDirectory
, mPersistent
);
9125 aOriginProps
.mNeedsRestore2
= true;
9127 aOriginProps
.mTimestamp
= timestamp
;
9134 nsresult
UpgradeStorageFrom2_0To2_1Helper::ProcessOriginDirectory(
9135 const OriginProps
& aOriginProps
) {
9136 AssertIsOnIOThread();
9140 if (aOriginProps
.mNeedsRestore
) {
9141 rv
= CreateDirectoryMetadata(aOriginProps
.mDirectory
,
9142 aOriginProps
.mTimestamp
, aOriginProps
.mSuffix
,
9143 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
9144 if (NS_WARN_IF(NS_FAILED(rv
))) {
9149 if (aOriginProps
.mNeedsRestore2
) {
9150 rv
= CreateDirectoryMetadata2(aOriginProps
.mDirectory
,
9151 aOriginProps
.mTimestamp
,
9152 /* aPersisted */ false, aOriginProps
.mSuffix
,
9153 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
9154 if (NS_WARN_IF(NS_FAILED(rv
))) {
9162 nsresult
RestoreDirectoryMetadata2Helper::RestoreMetadata2File() {
9163 AssertIsOnIOThread();
9167 OriginProps originProps
;
9168 rv
= originProps
.Init(mDirectory
);
9169 if (NS_WARN_IF(NS_FAILED(rv
))) {
9173 originProps
.mTimestamp
= GetLastModifiedTime(mDirectory
, mPersistent
);
9175 mOriginProps
.AppendElement(std::move(originProps
));
9177 rv
= ProcessOriginDirectories();
9178 if (NS_WARN_IF(NS_FAILED(rv
))) {
9185 nsresult
RestoreDirectoryMetadata2Helper::ProcessOriginDirectory(
9186 const OriginProps
& aOriginProps
) {
9187 AssertIsOnIOThread();
9189 // We don't have any approach to restore aPersisted, so reset it to false.
9191 CreateDirectoryMetadata2(aOriginProps
.mDirectory
, aOriginProps
.mTimestamp
,
9192 /* aPersisted */ false, aOriginProps
.mSuffix
,
9193 aOriginProps
.mGroup
, aOriginProps
.mOrigin
);
9194 if (NS_WARN_IF(NS_FAILED(rv
))) {
9201 } // namespace quota
9203 } // namespace mozilla