From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- dom/quota/QuotaManager.h | 707 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 707 insertions(+) create mode 100644 dom/quota/QuotaManager.h (limited to 'dom/quota/QuotaManager.h') diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h new file mode 100644 index 0000000000..380a60c865 --- /dev/null +++ b/dom/quota/QuotaManager.h @@ -0,0 +1,707 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_quota_quotamanager_h__ +#define mozilla_dom_quota_quotamanager_h__ + +#include +#include +#include "Client.h" +#include "ErrorList.h" +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/Assertions.h" +#include "mozilla/InitializedOnce.h" +#include "mozilla/MozPromise.h" +#include "mozilla/Mutex.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Result.h" +#include "mozilla/dom/Nullable.h" +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/dom/quota/Assertions.h" +#include "mozilla/dom/quota/CommonMetadata.h" +#include "mozilla/dom/quota/ForwardDecls.h" +#include "mozilla/dom/quota/InitializationTypes.h" +#include "mozilla/dom/quota/PersistenceType.h" +#include "mozilla/dom/quota/QuotaCommon.h" +#include "nsCOMPtr.h" +#include "nsClassHashtable.h" +#include "nsTHashMap.h" +#include "nsDebug.h" +#include "nsHashKeys.h" +#include "nsISupports.h" +#include "nsStringFwd.h" +#include "nsTArray.h" +#include "nsTStringRepr.h" +#include "nscore.h" +#include "prenv.h" + +#define QUOTA_MANAGER_CONTRACTID "@mozilla.org/dom/quota/manager;1" + +class mozIStorageConnection; +class nsIEventTarget; +class nsIFile; +class nsIPrincipal; +class nsIRunnable; +class nsIThread; +class nsITimer; +class nsPIDOMWindowOuter; + +namespace mozilla { + +class OriginAttributes; + +namespace ipc { + +class PrincipalInfo; + +} // namespace ipc + +} // namespace mozilla + +namespace mozilla::dom::quota { + +class CanonicalQuotaObject; +class ClientUsageArray; +class ClientDirectoryLock; +class DirectoryLockImpl; +class GroupInfo; +class GroupInfoPair; +class OriginDirectoryLock; +class OriginInfo; +class OriginScope; +class QuotaObject; +class UniversalDirectoryLock; + +class QuotaManager final : public BackgroundThreadObject { + friend class CanonicalQuotaObject; + friend class DirectoryLockImpl; + friend class GroupInfo; + friend class OriginInfo; + + using PrincipalInfo = mozilla::ipc::PrincipalInfo; + using DirectoryLockTable = + nsClassHashtable>>; + + class Observer; + + public: + QuotaManager(const nsAString& aBasePath, const nsAString& aStorageName); + + NS_INLINE_DECL_REFCOUNTING(QuotaManager) + + static nsresult Initialize(); + + static bool IsRunningXPCShellTests() { + static bool kRunningXPCShellTests = + !!PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR"); + return kRunningXPCShellTests; + } + + static bool IsRunningGTests() { + static bool kRunningGTests = !!PR_GetEnv("MOZ_RUN_GTEST"); + return kRunningGTests; + } + + static const char kReplaceChars[]; + static const char16_t kReplaceChars16[]; + + static Result>, nsresult> GetOrCreate(); + + static Result EnsureCreated(); + + // Returns a non-owning reference. + static QuotaManager* Get(); + + // Use only in gtests! + static nsIObserver* GetObserver(); + + // Returns true if we've begun the shutdown process. + static bool IsShuttingDown(); + + static void ShutdownInstance(); + + // Use only in gtests! + static void Reset(); + + static bool IsOSMetadata(const nsAString& aFileName); + + static bool IsDotFile(const nsAString& aFileName); + + bool IsOriginInitialized(const nsACString& aOrigin) const { + AssertIsOnIOThread(); + + return mInitializedOrigins.Contains(aOrigin); + } + + bool IsTemporaryStorageInitialized() const { + AssertIsOnIOThread(); + + return mTemporaryStorageInitialized; + } + + /** + * For initialization of an origin where the directory already exists. This is + * used by EnsureTemporaryStorageIsInitialized/InitializeRepository once it + * has tallied origin usage by calling each of the QuotaClient InitOrigin + * methods. + */ + void InitQuotaForOrigin(const FullOriginMetadata& aFullOriginMetadata, + const ClientUsageArray& aClientUsages, + uint64_t aUsageBytes); + + /** + * For use in special-cases like LSNG where we need to be able to know that + * there is no data stored for an origin. LSNG knows that there is 0 usage for + * its storage of an origin and wants to make sure there is a QuotaObject + * tracking this. This method will create a non-persisted, 0-usage, + * mDirectoryExists=false OriginInfo if there isn't already an OriginInfo. If + * an OriginInfo already exists, it will be left as-is, because that implies a + * different client has usages for the origin (and there's no need to add + * LSNG's 0 usage to the QuotaObject). + */ + void EnsureQuotaForOrigin(const OriginMetadata& aOriginMetadata); + + /** + * For use when creating an origin directory. It's possible that origin usage + * is already being tracked due to a call to EnsureQuotaForOrigin, and in that + * case we need to update the existing OriginInfo rather than create a new + * one. + * + * @return last access time of the origin. + */ + int64_t NoteOriginDirectoryCreated(const OriginMetadata& aOriginMetadata, + bool aPersisted); + + // XXX clients can use QuotaObject instead of calling this method directly. + void DecreaseUsageForClient(const ClientMetadata& aClientMetadata, + int64_t aSize); + + void ResetUsageForClient(const ClientMetadata& aClientMetadata); + + UsageInfo GetUsageForClient(PersistenceType aPersistenceType, + const OriginMetadata& aOriginMetadata, + Client::Type aClientType); + + void UpdateOriginAccessTime(PersistenceType aPersistenceType, + const OriginMetadata& aOriginMetadata); + + void RemoveQuota(); + + void RemoveQuotaForRepository(PersistenceType aPersistenceType) { + MutexAutoLock lock(mQuotaMutex); + LockedRemoveQuotaForRepository(aPersistenceType); + } + + void RemoveQuotaForOrigin(PersistenceType aPersistenceType, + const OriginMetadata& aOriginMetadata) { + MutexAutoLock lock(mQuotaMutex); + LockedRemoveQuotaForOrigin(aOriginMetadata); + } + + nsresult LoadQuota(); + + void UnloadQuota(); + + already_AddRefed GetQuotaObject( + PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata, + Client::Type aClientType, nsIFile* aFile, int64_t aFileSize = -1, + int64_t* aFileSizeOut = nullptr); + + already_AddRefed GetQuotaObject( + PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata, + Client::Type aClientType, const nsAString& aPath, int64_t aFileSize = -1, + int64_t* aFileSizeOut = nullptr); + + already_AddRefed GetQuotaObject(const int64_t aDirectoryLockId, + const nsAString& aPath); + + Nullable OriginPersisted(const OriginMetadata& aOriginMetadata); + + void PersistOrigin(const OriginMetadata& aOriginMetadata); + + using DirectoryLockIdTableArray = + AutoTArray; + void AbortOperationsForLocks(const DirectoryLockIdTableArray& aLockIds); + + // Called when a process is being shot down. Aborts any running operations + // for the given process. + void AbortOperationsForProcess(ContentParentId aContentParentId); + + Result, nsresult> GetOriginDirectory( + const OriginMetadata& aOriginMetadata) const; + + nsresult RestoreDirectoryMetadata2(nsIFile* aDirectory); + + // XXX Remove aPersistenceType argument once the persistence type is stored + // in the metadata file. + Result LoadFullOriginMetadata( + nsIFile* aDirectory, PersistenceType aPersistenceType); + + Result LoadFullOriginMetadataWithRestore( + nsIFile* aDirectory); + + Result GetOriginMetadata(nsIFile* aDirectory); + + // This is the main entry point into the QuotaManager API. + // Any storage API implementation (quota client) that participates in + // centralized quota and storage handling should call this method to get + // a directory lock which will protect client's files from being deleted + // while they are still in use. + // After a lock is acquired, client is notified via the open listener's + // method DirectoryLockAcquired. If the lock couldn't be acquired, client + // gets DirectoryLockFailed notification. + // A lock is a reference counted object and at the time DirectoryLockAcquired + // is called, quota manager holds just one strong reference to it which is + // then immediatelly cleared by quota manager. So it's up to client to add + // a new reference in order to keep the lock alive. + // Unlocking is simply done by dropping all references to the lock object. + // In other words, protection which the lock represents dies with the lock + // object itself. + RefPtr CreateDirectoryLock( + PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata, + Client::Type aClientType, bool aExclusive); + + // XXX RemoveMe once bug 1170279 gets fixed. + RefPtr CreateDirectoryLockInternal( + const Nullable& aPersistenceType, + const OriginScope& aOriginScope, + const Nullable& aClientType, bool aExclusive); + + // Collect inactive and the least recently used origins. + uint64_t CollectOriginsForEviction( + uint64_t aMinSizeToBeFreed, + nsTArray>& aLocks); + + /** + * Helper method to invoke the provided predicate on all "pending" OriginInfo + * instances. These are origins for which the origin directory has not yet + * been created but for which quota is already being tracked. This happens, + * for example, for the LocalStorage client where an origin that previously + * was not using LocalStorage can start issuing writes which it buffers until + * eventually flushing them. We defer creating the origin directory for as + * long as possible in that case, so the directory won't exist. Logic that + * would otherwise only consult the filesystem also needs to use this method. + */ + template + void CollectPendingOriginsForListing(P aPredicate); + + bool IsStorageInitialized() const { + AssertIsOnIOThread(); + return static_cast(mStorageConnection); + } + + void AssertStorageIsInitialized() const +#ifdef DEBUG + ; +#else + { + } +#endif + + nsresult EnsureStorageIsInitialized(); + + // Returns a pair of an nsIFile object referring to the directory, and a bool + // indicating whether the directory was newly created. + Result, bool>, nsresult> + EnsurePersistentOriginIsInitialized(const OriginMetadata& aOriginMetadata); + + // Returns a pair of an nsIFile object referring to the directory, and a bool + // indicating whether the directory was newly created. + Result, bool>, nsresult> + EnsureTemporaryOriginIsInitialized(PersistenceType aPersistenceType, + const OriginMetadata& aOriginMetadata); + + nsresult EnsureTemporaryStorageIsInitialized(); + + RefPtr ClearPrivateRepository(); + + RefPtr ShutdownStorage(); + + void ShutdownStorageInternal(); + + // Returns a bool indicating whether the directory was newly created. + Result EnsureOriginDirectory(nsIFile& aDirectory); + + nsresult AboutToClearOrigins( + const Nullable& aPersistenceType, + const OriginScope& aOriginScope, + const Nullable& aClientType); + + void OriginClearCompleted(PersistenceType aPersistenceType, + const nsACString& aOrigin, + const Nullable& aClientType); + + void RepositoryClearCompleted(PersistenceType aPersistenceType); + + void StartIdleMaintenance() { + AssertIsOnOwningThread(); + + for (const auto& client : *mClients) { + client->StartIdleMaintenance(); + } + } + + void StopIdleMaintenance() { + AssertIsOnOwningThread(); + + for (const auto& client : *mClients) { + client->StopIdleMaintenance(); + } + } + + void AssertCurrentThreadOwnsQuotaMutex() { + mQuotaMutex.AssertCurrentThreadOwns(); + } + + nsIThread* IOThread() { return mIOThread->get(); } + + Client* GetClient(Client::Type aClientType); + + const AutoTArray& AllClientTypes(); + + const nsString& GetBasePath() const { return mBasePath; } + + const nsString& GetStorageName() const { return mStorageName; } + + const nsString& GetStoragePath() const { return *mStoragePath; } + + const nsString& GetStoragePath(PersistenceType aPersistenceType) const { + if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + return *mPermanentStoragePath; + } + + if (aPersistenceType == PERSISTENCE_TYPE_TEMPORARY) { + return *mTemporaryStoragePath; + } + + if (aPersistenceType == PERSISTENCE_TYPE_DEFAULT) { + return *mDefaultStoragePath; + } + + MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_PRIVATE); + + return *mPrivateStoragePath; + } + + uint64_t GetGroupLimit() const; + + std::pair GetUsageAndLimitForEstimate( + const OriginMetadata& aOriginMetadata); + + uint64_t GetOriginUsage(const PrincipalMetadata& aPrincipalMetadata); + + Maybe GetFullOriginMetadata( + const OriginMetadata& aOriginMetadata); + + void NotifyStoragePressure(uint64_t aUsage); + + // Record a quota client shutdown step, if shutting down. + // Assumes that the QuotaManager singleton is alive. + static void MaybeRecordQuotaClientShutdownStep( + const Client::Type aClientType, const nsACString& aStepDescription); + + // Record a quota client shutdown step, if shutting down. + // Checks if the QuotaManager singleton is alive. + static void SafeMaybeRecordQuotaClientShutdownStep( + Client::Type aClientType, const nsACString& aStepDescription); + + // Record a quota manager shutdown step, use only if shutdown is active. + void RecordQuotaManagerShutdownStep(const nsACString& aStepDescription); + + // Record a quota manager shutdown step, if shutting down. + void MaybeRecordQuotaManagerShutdownStep(const nsACString& aStepDescription); + + template + void MaybeRecordQuotaManagerShutdownStepWith(F&& aFunc); + + static void GetStorageId(PersistenceType aPersistenceType, + const nsACString& aOrigin, Client::Type aClientType, + nsACString& aDatabaseId); + + static bool IsPrincipalInfoValid(const PrincipalInfo& aPrincipalInfo); + + Result GetInfoFromValidatedPrincipalInfo( + const PrincipalInfo& aPrincipalInfo); + + static nsAutoCString GetOriginFromValidatedPrincipalInfo( + const PrincipalInfo& aPrincipalInfo); + + static Result GetInfoFromPrincipal( + nsIPrincipal* aPrincipal); + + static Result GetInfoFromWindow( + nsPIDOMWindowOuter* aWindow); + + static Result GetOriginFromPrincipal( + nsIPrincipal* aPrincipal); + + static Result GetOriginFromWindow( + nsPIDOMWindowOuter* aWindow); + + static nsLiteralCString GetOriginForChrome(); + + static PrincipalMetadata GetInfoForChrome(); + + static bool IsOriginInternal(const nsACString& aOrigin); + + static bool AreOriginsEqualOnDisk(const nsACString& aOrigin1, + const nsACString& aOrigin2); + + static Result ParseOrigin(const nsACString& aOrigin); + + static void InvalidateQuotaCache(); + + private: + virtual ~QuotaManager(); + + nsresult Init(); + + void Shutdown(); + + void RegisterDirectoryLock(DirectoryLockImpl& aLock); + + void UnregisterDirectoryLock(DirectoryLockImpl& aLock); + + void AddPendingDirectoryLock(DirectoryLockImpl& aLock); + + void RemovePendingDirectoryLock(DirectoryLockImpl& aLock); + + uint64_t LockedCollectOriginsForEviction( + uint64_t aMinSizeToBeFreed, + nsTArray>& aLocks); + + void LockedRemoveQuotaForRepository(PersistenceType aPersistenceType); + + void LockedRemoveQuotaForOrigin(const OriginMetadata& aOriginMetadata); + + already_AddRefed LockedGetOrCreateGroupInfo( + PersistenceType aPersistenceType, const nsACString& aSuffix, + const nsACString& aGroup); + + already_AddRefed LockedGetOriginInfo( + PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata); + + nsresult UpgradeFromIndexedDBDirectoryToPersistentStorageDirectory( + nsIFile* aIndexedDBDir); + + nsresult UpgradeFromPersistentStorageDirectoryToDefaultStorageDirectory( + nsIFile* aPersistentStorageDir); + + nsresult MaybeUpgradeToDefaultStorageDirectory(nsIFile& aStorageFile); + + template + nsresult UpgradeStorage(const int32_t aOldVersion, const int32_t aNewVersion, + mozIStorageConnection* aConnection); + + nsresult UpgradeStorageFrom0_0To1_0(mozIStorageConnection* aConnection); + + nsresult UpgradeStorageFrom1_0To2_0(mozIStorageConnection* aConnection); + + nsresult UpgradeStorageFrom2_0To2_1(mozIStorageConnection* aConnection); + + nsresult UpgradeStorageFrom2_1To2_2(mozIStorageConnection* aConnection); + + nsresult UpgradeStorageFrom2_2To2_3(mozIStorageConnection* aConnection); + + nsresult MaybeCreateOrUpgradeStorage(mozIStorageConnection& aConnection); + + OkOrErr MaybeRemoveLocalStorageArchiveTmpFile(); + + nsresult MaybeRemoveLocalStorageDataAndArchive(nsIFile& aLsArchiveFile); + + nsresult MaybeRemoveLocalStorageDirectories(); + + Result CopyLocalStorageArchiveFromWebAppsStore( + nsIFile& aLsArchiveFile) const; + + Result, nsresult> + CreateLocalStorageArchiveConnection(nsIFile& aLsArchiveFile) const; + + Result, nsresult> + RecopyLocalStorageArchiveFromWebAppsStore(nsIFile& aLsArchiveFile); + + Result, nsresult> + DowngradeLocalStorageArchive(nsIFile& aLsArchiveFile); + + Result, nsresult> + UpgradeLocalStorageArchiveFromLessThan4To4(nsIFile& aLsArchiveFile); + + /* + nsresult UpgradeLocalStorageArchiveFrom4To5(); + */ + + Result MaybeCreateOrUpgradeLocalStorageArchive( + nsIFile& aLsArchiveFile); + + Result CreateEmptyLocalStorageArchive( + nsIFile& aLsArchiveFile) const; + + template + nsresult InitializeRepository(PersistenceType aPersistenceType, + OriginFunc&& aOriginFunc); + + nsresult InitializeOrigin(PersistenceType aPersistenceType, + const OriginMetadata& aOriginMetadata, + int64_t aAccessTime, bool aPersisted, + nsIFile* aDirectory); + + using OriginInfosFlatTraversable = + nsTArray>>; + + using OriginInfosNestedTraversable = + nsTArray>>>; + + OriginInfosNestedTraversable GetOriginInfosExceedingGroupLimit() const; + + OriginInfosNestedTraversable GetOriginInfosExceedingGlobalLimit() const; + + void ClearOrigins(const OriginInfosNestedTraversable& aDoomedOriginInfos); + + void CleanupTemporaryStorage(); + + void DeleteOriginDirectory(const OriginMetadata& aOriginMetadata); + + void FinalizeOriginEviction(nsTArray>&& aLocks); + + Result ArchiveOrigins( + const nsTArray& aFullOriginMetadatas); + + void ReleaseIOThreadObjects() { + AssertIsOnIOThread(); + + for (Client::Type type : AllClientTypes()) { + (*mClients)[type]->ReleaseIOThreadObjects(); + } + } + + DirectoryLockTable& GetDirectoryLockTable(PersistenceType aPersistenceType); + + bool IsSanitizedOriginValid(const nsACString& aSanitizedOrigin); + + Result EnsureStorageOriginFromOrigin( + const nsACString& aOrigin); + + Result GetOriginFromStorageOrigin( + const nsACString& aStorageOrigin); + + int64_t GenerateDirectoryLockId(); + + bool ShutdownStarted() const; + + void RecordShutdownStep(Maybe aClientType, + const nsACString& aStepDescription); + + template + auto ExecuteInitialization(Initialization aInitialization, Func&& aFunc) + -> std::invoke_result_t&>; + + template + auto ExecuteInitialization(Initialization aInitialization, + const nsACString& aContext, Func&& aFunc) + -> std::invoke_result_t&>; + + template + auto ExecuteOriginInitialization(const nsACString& aOrigin, + const OriginInitialization aInitialization, + const nsACString& aContext, Func&& aFunc) + -> std::invoke_result_t&>; + + template + static void MaybeInsertNonPersistedOriginInfos( + Iterator aDest, const RefPtr& aTemporaryGroupInfo, + const RefPtr& aDefaultGroupInfo, + const RefPtr& aPrivateGroupInfo); + + template + static OriginInfosFlatTraversable CollectLRUOriginInfosUntil( + Collect&& aCollect, Pred&& aPred); + + // Thread on which IO is performed. + LazyInitializedOnceNotNull> mIOThread; + + nsCOMPtr mStorageConnection; + + EnumeratedArray mShutdownSteps; + LazyInitializedOnce mShutdownStartedAt; + + // Accesses to mQuotaManagerShutdownSteps must be protected by mQuotaMutex. + nsCString mQuotaManagerShutdownSteps; + + mutable mozilla::Mutex mQuotaMutex MOZ_UNANNOTATED; + + nsClassHashtable mGroupInfoPairs; + + // Maintains a list of directory locks that are queued. + nsTArray> mPendingDirectoryLocks; + + // Maintains a list of directory locks that are acquired or queued. It can be + // accessed on the owning (PBackground) thread only. + nsTArray> mDirectoryLocks; + + // Only modifed on the owning thread, but read on multiple threads. Therefore + // all modifications (including those on the owning thread) and all reads off + // the owning thread must be protected by mQuotaMutex. In other words, only + // reads on the owning thread don't have to be protected by mQuotaMutex. + nsTHashMap> + mDirectoryLockIdTable; + + // Directory lock tables that are used to update origin access time. + DirectoryLockTable mTemporaryDirectoryLockTable; + DirectoryLockTable mDefaultDirectoryLockTable; + DirectoryLockTable mPrivateDirectoryLockTable; + + // A list of all successfully initialized persistent origins. This list isn't + // protected by any mutex but it is only ever touched on the IO thread. + nsTArray mInitializedOrigins; + + // A hash table that is used to cache origin parser results for given + // sanitized origin strings. This hash table isn't protected by any mutex but + // it is only ever touched on the IO thread. + nsTHashMap mValidOrigins; + + // These maps are protected by mQuotaMutex. + nsTHashMap mOriginToStorageOriginMap; + nsTHashMap mStorageOriginToOriginMap; + + // This array is populated at initialization time and then never modified, so + // it can be iterated on any thread. + LazyInitializedOnce, Client::TYPE_MAX>> + mClients; + + using ClientTypesArray = AutoTArray; + LazyInitializedOnce mAllClientTypes; + LazyInitializedOnce mAllClientTypesExceptLS; + + // This object isn't protected by any mutex but it is only ever touched on + // the IO thread. + InitializationInfo mInitializationInfo; + + const nsString mBasePath; + const nsString mStorageName; + LazyInitializedOnce mIndexedDBPath; + LazyInitializedOnce mStoragePath; + LazyInitializedOnce mStorageArchivesPath; + LazyInitializedOnce mPermanentStoragePath; + LazyInitializedOnce mTemporaryStoragePath; + LazyInitializedOnce mDefaultStoragePath; + LazyInitializedOnce mPrivateStoragePath; + + MozPromiseHolder mShutdownStoragePromiseHolder; + + uint64_t mTemporaryStorageLimit; + uint64_t mTemporaryStorageUsage; + int64_t mNextDirectoryLockId; + bool mTemporaryStorageInitialized; + bool mCacheUsable; + bool mShuttingDownStorage; +}; + +} // namespace mozilla::dom::quota + +#endif /* mozilla_dom_quota_quotamanager_h__ */ -- cgit v1.2.3