diff options
Diffstat (limited to 'dom/quota/OriginOperations.cpp')
-rw-r--r-- | dom/quota/OriginOperations.cpp | 2684 |
1 files changed, 2684 insertions, 0 deletions
diff --git a/dom/quota/OriginOperations.cpp b/dom/quota/OriginOperations.cpp new file mode 100644 index 0000000000..8eb6626780 --- /dev/null +++ b/dom/quota/OriginOperations.cpp @@ -0,0 +1,2684 @@ +/* -*- 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/. */ + +#include "OriginOperations.h" + +#include <algorithm> +#include <cstdint> +#include <utility> + +#include "ErrorList.h" +#include "FileUtils.h" +#include "GroupInfo.h" +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/Atomics.h" +#include "mozilla/Maybe.h" +#include "mozilla/NotNull.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Result.h" +#include "mozilla/ResultExtensions.h" +#include "mozilla/dom/Nullable.h" +#include "mozilla/dom/quota/CommonMetadata.h" +#include "mozilla/dom/quota/Client.h" +#include "mozilla/dom/quota/Constants.h" +#include "mozilla/dom/quota/DirectoryLock.h" +#include "mozilla/dom/quota/PersistenceType.h" +#include "mozilla/dom/quota/PQuota.h" +#include "mozilla/dom/quota/PQuotaRequest.h" +#include "mozilla/dom/quota/PQuotaUsageRequest.h" +#include "mozilla/dom/quota/OriginScope.h" +#include "mozilla/dom/quota/QuotaCommon.h" +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/dom/quota/QuotaManagerImpl.h" +#include "mozilla/dom/quota/ResultExtensions.h" +#include "mozilla/dom/quota/StreamUtils.h" +#include "mozilla/dom/quota/UsageInfo.h" +#include "mozilla/fallible.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "NormalOriginOperationBase.h" +#include "nsCOMPtr.h" +#include "nsTHashMap.h" +#include "nsDebug.h" +#include "nsError.h" +#include "nsHashKeys.h" +#include "nsIBinaryOutputStream.h" +#include "nsIFile.h" +#include "nsIObjectOutputStream.h" +#include "nsIOutputStream.h" +#include "nsLiteralString.h" +#include "nsPrintfCString.h" +#include "nsString.h" +#include "nsTArray.h" +#include "OriginInfo.h" +#include "OriginOperationBase.h" +#include "QuotaRequestBase.h" +#include "QuotaUsageRequestBase.h" +#include "ResolvableNormalOriginOp.h" +#include "prthread.h" +#include "prtime.h" + +namespace mozilla::dom::quota { + +using namespace mozilla::ipc; + +template <class Base> +class OpenStorageDirectoryHelper : public Base { + protected: + OpenStorageDirectoryHelper(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const char* aName) + : Base(std::move(aQuotaManager), aName) {} + + RefPtr<BoolPromise> OpenStorageDirectory( + const Nullable<PersistenceType>& aPersistenceType, + const OriginScope& aOriginScope, + const Nullable<Client::Type>& aClientType, bool aExclusive); + + RefPtr<UniversalDirectoryLock> mDirectoryLock; +}; + +class FinalizeOriginEvictionOp : public OriginOperationBase { + nsTArray<RefPtr<OriginDirectoryLock>> mLocks; + + public: + FinalizeOriginEvictionOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + nsTArray<RefPtr<OriginDirectoryLock>>&& aLocks) + : OriginOperationBase(std::move(aQuotaManager), + "dom::quota::FinalizeOriginEvictionOp"), + mLocks(std::move(aLocks)) { + AssertIsOnOwningThread(); + } + + private: + ~FinalizeOriginEvictionOp() = default; + + virtual RefPtr<BoolPromise> Open() override; + + virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + virtual void UnblockOpen() override; +}; + +class SaveOriginAccessTimeOp + : public OpenStorageDirectoryHelper<NormalOriginOperationBase> { + const OriginMetadata mOriginMetadata; + int64_t mTimestamp; + + public: + SaveOriginAccessTimeOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const OriginMetadata& aOriginMetadata, + int64_t aTimestamp) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), + "dom::quota::SaveOriginAccessTimeOp"), + mOriginMetadata(aOriginMetadata), + mTimestamp(aTimestamp) { + AssertIsOnOwningThread(); + } + + private: + ~SaveOriginAccessTimeOp() = default; + + RefPtr<BoolPromise> OpenDirectory() override; + + virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + virtual void SendResults() override; + + void CloseDirectory() override; +}; + +class ClearPrivateRepositoryOp + : public OpenStorageDirectoryHelper<ResolvableNormalOriginOp<bool>> { + public: + explicit ClearPrivateRepositoryOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), + "dom::quota::ClearPrivateRepositoryOp") { + AssertIsOnOwningThread(); + } + + private: + ~ClearPrivateRepositoryOp() = default; + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override { return true; } + + void CloseDirectory() override; +}; + +class ShutdownStorageOp : public ResolvableNormalOriginOp<bool> { + RefPtr<UniversalDirectoryLock> mDirectoryLock; + + public: + explicit ShutdownStorageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) + : ResolvableNormalOriginOp(std::move(aQuotaManager), + "dom::quota::ShutdownStorageOp") { + AssertIsOnOwningThread(); + } + + private: + ~ShutdownStorageOp() = default; + +#ifdef DEBUG + nsresult DirectoryOpen() override; +#endif + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override { return true; } + + void CloseDirectory() override; +}; + +// A mix-in class to simplify operations that need to process every origin in +// one or more repositories. Sub-classes should call TraverseRepository in their +// DoDirectoryWork and implement a ProcessOrigin method for their per-origin +// logic. +class TraverseRepositoryHelper { + public: + TraverseRepositoryHelper() = default; + + protected: + virtual ~TraverseRepositoryHelper() = default; + + // If ProcessOrigin returns an error, TraverseRepository will immediately + // terminate and return the received error code to its caller. + nsresult TraverseRepository(QuotaManager& aQuotaManager, + PersistenceType aPersistenceType); + + private: + virtual const Atomic<bool>& GetIsCanceledFlag() = 0; + + virtual nsresult ProcessOrigin(QuotaManager& aQuotaManager, + nsIFile& aOriginDir, const bool aPersistent, + const PersistenceType aPersistenceType) = 0; +}; + +class GetUsageOp final + : public OpenStorageDirectoryHelper<QuotaUsageRequestBase>, + public TraverseRepositoryHelper { + nsTArray<OriginUsage> mOriginUsages; + nsTHashMap<nsCStringHashKey, uint32_t> mOriginUsagesIndex; + + bool mGetAll; + + public: + GetUsageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const UsageRequestParams& aParams); + + private: + ~GetUsageOp() = default; + + void ProcessOriginInternal(QuotaManager* aQuotaManager, + const PersistenceType aPersistenceType, + const nsACString& aOrigin, + const int64_t aTimestamp, const bool aPersisted, + const uint64_t aUsage); + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + const Atomic<bool>& GetIsCanceledFlag() override; + + nsresult ProcessOrigin(QuotaManager& aQuotaManager, nsIFile& aOriginDir, + const bool aPersistent, + const PersistenceType aPersistenceType) override; + + void GetResponse(UsageRequestResponse& aResponse) override; + + void CloseDirectory() override; +}; + +class GetOriginUsageOp final + : public OpenStorageDirectoryHelper<QuotaUsageRequestBase> { + const OriginUsageParams mParams; + PrincipalMetadata mPrincipalMetadata; + UsageInfo mUsageInfo; + bool mFromMemory; + + public: + GetOriginUsageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const UsageRequestParams& aParams); + + private: + ~GetOriginUsageOp() = default; + + nsresult DoInit(QuotaManager& aQuotaManager) override; + + RefPtr<BoolPromise> OpenDirectory() override; + + virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + void GetResponse(UsageRequestResponse& aResponse) override; + + void CloseDirectory() override; +}; + +class StorageNameOp final : public QuotaRequestBase { + nsString mName; + + public: + explicit StorageNameOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager); + + private: + ~StorageNameOp() = default; + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + void GetResponse(RequestResponse& aResponse) override; + + void CloseDirectory() override; +}; + +class InitializedRequestBase : public ResolvableNormalOriginOp<bool> { + protected: + bool mInitialized; + + InitializedRequestBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const char* aName); + + private: + RefPtr<BoolPromise> OpenDirectory() override; + + void CloseDirectory() override; +}; + +class StorageInitializedOp final : public InitializedRequestBase { + public: + explicit StorageInitializedOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) + : InitializedRequestBase(std::move(aQuotaManager), + "dom::quota::StorageInitializedOp") {} + + private: + ~StorageInitializedOp() = default; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override; +}; + +class TemporaryStorageInitializedOp final : public InitializedRequestBase { + public: + explicit TemporaryStorageInitializedOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) + : InitializedRequestBase(std::move(aQuotaManager), + "dom::quota::StorageInitializedOp") {} + + private: + ~TemporaryStorageInitializedOp() = default; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override; +}; + +class InitOp final : public ResolvableNormalOriginOp<bool> { + RefPtr<UniversalDirectoryLock> mDirectoryLock; + + public: + InitOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + RefPtr<UniversalDirectoryLock> aDirectoryLock); + + private: + ~InitOp() = default; + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override; + + void CloseDirectory() override; +}; + +class InitTemporaryStorageOp final : public ResolvableNormalOriginOp<bool> { + RefPtr<UniversalDirectoryLock> mDirectoryLock; + + public: + InitTemporaryStorageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + RefPtr<UniversalDirectoryLock> aDirectoryLock); + + private: + ~InitTemporaryStorageOp() = default; + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override; + + void CloseDirectory() override; +}; + +class InitializeOriginRequestBase : public QuotaRequestBase { + protected: + const PrincipalInfo mPrincipalInfo; + PrincipalMetadata mPrincipalMetadata; + RefPtr<UniversalDirectoryLock> mDirectoryLock; + const PersistenceType mPersistenceType; + bool mCreated; + + InitializeOriginRequestBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const char* aName, + PersistenceType aPersistenceType, + const PrincipalInfo& aPrincipalInfo); + + nsresult DoInit(QuotaManager& aQuotaManager) override; + + private: + RefPtr<BoolPromise> OpenDirectory() override; + + void CloseDirectory() override; +}; + +class InitializePersistentOriginOp final : public InitializeOriginRequestBase { + public: + InitializePersistentOriginOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams); + + private: + ~InitializePersistentOriginOp() = default; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + void GetResponse(RequestResponse& aResponse) override; +}; + +class InitializeTemporaryOriginOp final : public InitializeOriginRequestBase { + public: + InitializeTemporaryOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams); + + private: + ~InitializeTemporaryOriginOp() = default; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + void GetResponse(RequestResponse& aResponse) override; +}; + +class InitializeClientBase : public ResolvableNormalOriginOp<bool> { + protected: + const PrincipalInfo mPrincipalInfo; + ClientMetadata mClientMetadata; + RefPtr<UniversalDirectoryLock> mDirectoryLock; + const PersistenceType mPersistenceType; + const Client::Type mClientType; + bool mCreated; + + InitializeClientBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const char* aName, PersistenceType aPersistenceType, + const PrincipalInfo& aPrincipalInfo, + Client::Type aClientType); + + nsresult DoInit(QuotaManager& aQuotaManager) override; + + private: + RefPtr<BoolPromise> OpenDirectory() override; + + void CloseDirectory() override; +}; + +class InitializePersistentClientOp : public InitializeClientBase { + public: + InitializePersistentClientOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const PrincipalInfo& aPrincipalInfo, Client::Type aClientType); + + private: + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override; +}; + +class InitializeTemporaryClientOp : public InitializeClientBase { + public: + InitializeTemporaryClientOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + PersistenceType aPersistenceType, + const PrincipalInfo& aPrincipalInfo, + Client::Type aClientType); + + private: + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override; +}; + +class GetFullOriginMetadataOp + : public OpenStorageDirectoryHelper<QuotaRequestBase> { + const GetFullOriginMetadataParams mParams; + // XXX Consider wrapping with LazyInitializedOnce + OriginMetadata mOriginMetadata; + Maybe<FullOriginMetadata> mMaybeFullOriginMetadata; + + public: + GetFullOriginMetadataOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const GetFullOriginMetadataParams& aParams); + + private: + nsresult DoInit(QuotaManager& aQuotaManager) override; + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + void GetResponse(RequestResponse& aResponse) override; + + void CloseDirectory() override; +}; + +class ClearStorageOp final + : public OpenStorageDirectoryHelper<ResolvableNormalOriginOp<bool>> { + public: + explicit ClearStorageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager); + + private: + ~ClearStorageOp() = default; + + void DeleteFiles(QuotaManager& aQuotaManager); + + void DeleteStorageFile(QuotaManager& aQuotaManager); + + RefPtr<BoolPromise> OpenDirectory() override; + + virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override; + + void CloseDirectory() override; +}; + +class ClearRequestBase + : public OpenStorageDirectoryHelper<ResolvableNormalOriginOp<bool>> { + protected: + ClearRequestBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const char* aName) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), aName) { + AssertIsOnOwningThread(); + } + + void DeleteFiles(QuotaManager& aQuotaManager, + const OriginMetadata& aOriginMetadata, + const Nullable<Client::Type>& aClientType); + + void DeleteFiles(QuotaManager& aQuotaManager, + PersistenceType aPersistenceType, + const OriginScope& aOriginScope, + const Nullable<Client::Type>& aClientType); + + private: + template <typename FileCollector> + void DeleteFilesInternal(QuotaManager& aQuotaManager, + PersistenceType aPersistenceType, + const OriginScope& aOriginScope, + const Nullable<Client::Type>& aClientType, + const FileCollector& aFileCollector); +}; + +class ClearOriginOp final : public ClearRequestBase { + const PrincipalInfo mPrincipalInfo; + PrincipalMetadata mPrincipalMetadata; + const Nullable<PersistenceType> mPersistenceType; + const Nullable<Client::Type> mClientType; + + public: + ClearOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const mozilla::Maybe<PersistenceType>& aPersistenceType, + const PrincipalInfo& aPrincipalInfo, + const mozilla::Maybe<Client::Type>& aClientType); + + private: + ~ClearOriginOp() = default; + + nsresult DoInit(QuotaManager& aQuotaManager) override; + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override; + + void CloseDirectory() override; +}; + +class ClearStoragesForOriginPrefixOp final + : public OpenStorageDirectoryHelper<ClearRequestBase> { + const nsCString mPrefix; + const Nullable<PersistenceType> mPersistenceType; + + public: + ClearStoragesForOriginPrefixOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const Maybe<PersistenceType>& aPersistenceType, + const PrincipalInfo& aPrincipalInfo); + + private: + ~ClearStoragesForOriginPrefixOp() = default; + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override; + + void CloseDirectory() override; +}; + +class ClearDataOp final : public ClearRequestBase { + const OriginAttributesPattern mPattern; + + public: + ClearDataOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const OriginAttributesPattern& aPattern); + + private: + ~ClearDataOp() = default; + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + bool GetResolveValue() override; + + void CloseDirectory() override; +}; + +class ResetOriginOp final : public QuotaRequestBase { + nsCString mOrigin; + RefPtr<UniversalDirectoryLock> mDirectoryLock; + Nullable<PersistenceType> mPersistenceType; + Nullable<Client::Type> mClientType; + + public: + ResetOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams); + + private: + ~ResetOriginOp() = default; + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + void GetResponse(RequestResponse& aResponse) override; + + void CloseDirectory() override; +}; + +class PersistRequestBase : public OpenStorageDirectoryHelper<QuotaRequestBase> { + const PrincipalInfo mPrincipalInfo; + + protected: + PrincipalMetadata mPrincipalMetadata; + + protected: + PersistRequestBase(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const PrincipalInfo& aPrincipalInfo); + + nsresult DoInit(QuotaManager& aQuotaManager) override; + + private: + RefPtr<BoolPromise> OpenDirectory() override; + + void CloseDirectory() override; +}; + +class PersistedOp final : public PersistRequestBase { + bool mPersisted; + + public: + PersistedOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams); + + private: + ~PersistedOp() = default; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + void GetResponse(RequestResponse& aResponse) override; +}; + +class PersistOp final : public PersistRequestBase { + public: + PersistOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams); + + private: + ~PersistOp() = default; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + void GetResponse(RequestResponse& aResponse) override; +}; + +class EstimateOp final : public OpenStorageDirectoryHelper<QuotaRequestBase> { + const EstimateParams mParams; + OriginMetadata mOriginMetadata; + std::pair<uint64_t, uint64_t> mUsageAndLimit; + + public: + EstimateOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const EstimateParams& aParams); + + private: + ~EstimateOp() = default; + + nsresult DoInit(QuotaManager& aQuotaManager) override; + + RefPtr<BoolPromise> OpenDirectory() override; + + virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + void GetResponse(RequestResponse& aResponse) override; + + void CloseDirectory() override; +}; + +class ListOriginsOp final : public OpenStorageDirectoryHelper<QuotaRequestBase>, + public TraverseRepositoryHelper { + // XXX Bug 1521541 will make each origin has it's own state. + nsTArray<nsCString> mOrigins; + + public: + explicit ListOriginsOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager); + + private: + ~ListOriginsOp() = default; + + RefPtr<BoolPromise> OpenDirectory() override; + + nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; + + const Atomic<bool>& GetIsCanceledFlag() override; + + nsresult ProcessOrigin(QuotaManager& aQuotaManager, nsIFile& aOriginDir, + const bool aPersistent, + const PersistenceType aPersistenceType) override; + + void GetResponse(RequestResponse& aResponse) override; + + void CloseDirectory() override; +}; + +RefPtr<OriginOperationBase> CreateFinalizeOriginEvictionOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + nsTArray<RefPtr<OriginDirectoryLock>>&& aLocks) { + return MakeRefPtr<FinalizeOriginEvictionOp>(std::move(aQuotaManager), + std::move(aLocks)); +} + +RefPtr<NormalOriginOperationBase> CreateSaveOriginAccessTimeOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const OriginMetadata& aOriginMetadata, int64_t aTimestamp) { + return MakeRefPtr<SaveOriginAccessTimeOp>(std::move(aQuotaManager), + aOriginMetadata, aTimestamp); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateClearPrivateRepositoryOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) { + return MakeRefPtr<ClearPrivateRepositoryOp>(std::move(aQuotaManager)); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateShutdownStorageOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) { + return MakeRefPtr<ShutdownStorageOp>(std::move(aQuotaManager)); +} + +RefPtr<QuotaUsageRequestBase> CreateGetUsageOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const UsageRequestParams& aParams) { + return MakeRefPtr<GetUsageOp>(std::move(aQuotaManager), aParams); +} + +RefPtr<QuotaUsageRequestBase> CreateGetOriginUsageOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const UsageRequestParams& aParams) { + return MakeRefPtr<GetOriginUsageOp>(std::move(aQuotaManager), aParams); +} + +RefPtr<QuotaRequestBase> CreateStorageNameOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) { + return MakeRefPtr<StorageNameOp>(std::move(aQuotaManager)); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateStorageInitializedOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) { + return MakeRefPtr<StorageInitializedOp>(std::move(aQuotaManager)); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateTemporaryStorageInitializedOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) { + return MakeRefPtr<TemporaryStorageInitializedOp>(std::move(aQuotaManager)); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateInitOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + RefPtr<UniversalDirectoryLock> aDirectoryLock) { + return MakeRefPtr<InitOp>(std::move(aQuotaManager), + std::move(aDirectoryLock)); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateInitTemporaryStorageOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + RefPtr<UniversalDirectoryLock> aDirectoryLock) { + return MakeRefPtr<InitTemporaryStorageOp>(std::move(aQuotaManager), + std::move(aDirectoryLock)); +} + +RefPtr<QuotaRequestBase> CreateInitializePersistentOriginOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams) { + return MakeRefPtr<InitializePersistentOriginOp>(std::move(aQuotaManager), + aParams); +} + +RefPtr<QuotaRequestBase> CreateInitializeTemporaryOriginOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams) { + return MakeRefPtr<InitializeTemporaryOriginOp>(std::move(aQuotaManager), + aParams); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateInitializePersistentClientOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const mozilla::ipc::PrincipalInfo& aPrincipalInfo, + const Client::Type aClientType) { + return MakeRefPtr<InitializePersistentClientOp>(std::move(aQuotaManager), + aPrincipalInfo, aClientType); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateInitializeTemporaryClientOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, + const Client::Type aClientType) { + return MakeRefPtr<InitializeTemporaryClientOp>( + std::move(aQuotaManager), aPersistenceType, aPrincipalInfo, aClientType); +} + +RefPtr<QuotaRequestBase> CreateGetFullOriginMetadataOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const GetFullOriginMetadataParams& aParams) { + return MakeRefPtr<GetFullOriginMetadataOp>(std::move(aQuotaManager), aParams); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateClearStorageOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) { + return MakeRefPtr<ClearStorageOp>(std::move(aQuotaManager)); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateClearOriginOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const Maybe<PersistenceType>& aPersistenceType, + const PrincipalInfo& aPrincipalInfo, + const Maybe<Client::Type>& aClientType) { + return MakeRefPtr<ClearOriginOp>(std::move(aQuotaManager), aPersistenceType, + aPrincipalInfo, aClientType); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateClearStoragesForOriginPrefixOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const Maybe<PersistenceType>& aPersistenceType, + const PrincipalInfo& aPrincipalInfo) { + return MakeRefPtr<ClearStoragesForOriginPrefixOp>( + std::move(aQuotaManager), aPersistenceType, aPrincipalInfo); +} + +RefPtr<ResolvableNormalOriginOp<bool>> CreateClearDataOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const OriginAttributesPattern& aPattern) { + return MakeRefPtr<ClearDataOp>(std::move(aQuotaManager), aPattern); +} + +RefPtr<QuotaRequestBase> CreateResetOriginOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams) { + return MakeRefPtr<ResetOriginOp>(std::move(aQuotaManager), aParams); +} + +RefPtr<QuotaRequestBase> CreatePersistedOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams) { + return MakeRefPtr<PersistedOp>(std::move(aQuotaManager), aParams); +} + +RefPtr<QuotaRequestBase> CreatePersistOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams) { + return MakeRefPtr<PersistOp>(std::move(aQuotaManager), aParams); +} + +RefPtr<QuotaRequestBase> CreateEstimateOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const EstimateParams& aParams) { + return MakeRefPtr<EstimateOp>(std::move(aQuotaManager), aParams); +} + +RefPtr<QuotaRequestBase> CreateListOriginsOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) { + return MakeRefPtr<ListOriginsOp>(std::move(aQuotaManager)); +} + +template <class Base> +RefPtr<BoolPromise> OpenStorageDirectoryHelper<Base>::OpenStorageDirectory( + const Nullable<PersistenceType>& aPersistenceType, + const OriginScope& aOriginScope, const Nullable<Client::Type>& aClientType, + bool aExclusive) { + return Base::mQuotaManager + ->OpenStorageDirectory(aPersistenceType, aOriginScope, aClientType, + aExclusive) + ->Then(GetCurrentSerialEventTarget(), __func__, + [self = RefPtr(this)]( + UniversalDirectoryLockPromise::ResolveOrRejectValue&& aValue) { + if (aValue.IsReject()) { + return BoolPromise::CreateAndReject(aValue.RejectValue(), + __func__); + } + + self->mDirectoryLock = std::move(aValue.ResolveValue()); + + return BoolPromise::CreateAndResolve(true, __func__); + }); +} + +RefPtr<BoolPromise> FinalizeOriginEvictionOp::Open() { + AssertIsOnOwningThread(); + MOZ_ASSERT(!mLocks.IsEmpty()); + + return BoolPromise::CreateAndResolve(true, __func__); +} + +nsresult FinalizeOriginEvictionOp::DoDirectoryWork( + QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("FinalizeOriginEvictionOp::DoDirectoryWork", OTHER); + + for (const auto& lock : mLocks) { + aQuotaManager.OriginClearCompleted( + lock->GetPersistenceType(), lock->Origin(), Nullable<Client::Type>()); + } + + return NS_OK; +} + +void FinalizeOriginEvictionOp::UnblockOpen() { + AssertIsOnOwningThread(); + +#ifdef DEBUG + NoteActorDestroyed(); +#endif + + mLocks.Clear(); +} + +RefPtr<BoolPromise> SaveOriginAccessTimeOp::OpenDirectory() { + AssertIsOnOwningThread(); + + return OpenStorageDirectory( + Nullable<PersistenceType>(mOriginMetadata.mPersistenceType), + OriginScope::FromOrigin(mOriginMetadata.mOrigin), + Nullable<Client::Type>(), + /* aExclusive */ false); +} + +nsresult SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + + AUTO_PROFILER_LABEL("SaveOriginAccessTimeOp::DoDirectoryWork", OTHER); + + QM_TRY(MOZ_TO_RESULT(!QuotaManager::IsShuttingDown()), NS_ERROR_ABORT); + + QM_TRY_INSPECT(const auto& file, + aQuotaManager.GetOriginDirectory(mOriginMetadata)); + + // The origin directory might not exist + // anymore, because it was deleted by a clear operation. + QM_TRY_INSPECT(const bool& exists, MOZ_TO_RESULT_INVOKE_MEMBER(file, Exists)); + + if (exists) { + QM_TRY(MOZ_TO_RESULT(file->Append(nsLiteralString(METADATA_V2_FILE_NAME)))); + + QM_TRY_INSPECT(const auto& stream, + GetBinaryOutputStream(*file, FileFlag::Update)); + MOZ_ASSERT(stream); + + QM_TRY(MOZ_TO_RESULT(stream->Write64(mTimestamp))); + } + + return NS_OK; +} + +void SaveOriginAccessTimeOp::SendResults() { +#ifdef DEBUG + NoteActorDestroyed(); +#endif +} + +void SaveOriginAccessTimeOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +RefPtr<BoolPromise> ClearPrivateRepositoryOp::OpenDirectory() { + AssertIsOnOwningThread(); + + return OpenStorageDirectory( + Nullable<PersistenceType>(PERSISTENCE_TYPE_PRIVATE), + OriginScope::FromNull(), Nullable<Client::Type>(), + /* aExclusive */ true); +} + +nsresult ClearPrivateRepositoryOp::DoDirectoryWork( + QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + + AUTO_PROFILER_LABEL("ClearPrivateRepositoryOp::DoDirectoryWork", OTHER); + + QM_TRY_INSPECT( + const auto& directory, + QM_NewLocalFile(aQuotaManager.GetStoragePath(PERSISTENCE_TYPE_PRIVATE))); + + nsresult rv = directory->Remove(true); + if (rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) { + // This should never fail if we've closed all storage connections + // correctly... + MOZ_ASSERT(false, "Failed to remove directory!"); + } + + aQuotaManager.RemoveQuotaForRepository(PERSISTENCE_TYPE_PRIVATE); + + aQuotaManager.RepositoryClearCompleted(PERSISTENCE_TYPE_PRIVATE); + + return NS_OK; +} + +void ClearPrivateRepositoryOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +RefPtr<BoolPromise> ShutdownStorageOp::OpenDirectory() { + AssertIsOnOwningThread(); + + // Clear directory lock tables (which also saves origin access time) before + // acquiring the exclusive lock below. Otherwise, saving of origin access + // time would be scheduled after storage shutdown and that would initialize + // storage again in the end. + mQuotaManager->ClearDirectoryLockTables(); + + mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal( + Nullable<PersistenceType>(), OriginScope::FromNull(), + Nullable<Client::Type>(), + /* aExclusive */ true); + + return mDirectoryLock->Acquire(); +} + +#ifdef DEBUG +nsresult ShutdownStorageOp::DirectoryOpen() { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mDirectoryLock); + mDirectoryLock->AssertIsAcquiredExclusively(); + + return NormalOriginOperationBase::DirectoryOpen(); +} +#endif + +nsresult ShutdownStorageOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("ShutdownStorageOp::DoDirectoryWork", OTHER); + + aQuotaManager.MaybeRecordQuotaManagerShutdownStep( + "ShutdownStorageOp::DoDirectoryWork -> ShutdownStorageInternal."_ns); + + aQuotaManager.ShutdownStorageInternal(); + + return NS_OK; +} + +void ShutdownStorageOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +nsresult TraverseRepositoryHelper::TraverseRepository( + QuotaManager& aQuotaManager, PersistenceType aPersistenceType) { + AssertIsOnIOThread(); + + QM_TRY_INSPECT( + const auto& directory, + QM_NewLocalFile(aQuotaManager.GetStoragePath(aPersistenceType))); + + QM_TRY_INSPECT(const bool& exists, + MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists)); + + if (!exists) { + return NS_OK; + } + + QM_TRY(CollectEachFileAtomicCancelable( + *directory, GetIsCanceledFlag(), + [this, aPersistenceType, &aQuotaManager, + persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT]( + const nsCOMPtr<nsIFile>& originDir) -> Result<Ok, nsresult> { + QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*originDir)); + + switch (dirEntryKind) { + case nsIFileKind::ExistsAsDirectory: + QM_TRY(MOZ_TO_RESULT(ProcessOrigin(aQuotaManager, *originDir, + persistent, aPersistenceType))); + break; + + case nsIFileKind::ExistsAsFile: { + QM_TRY_INSPECT(const auto& leafName, + MOZ_TO_RESULT_INVOKE_MEMBER_TYPED( + nsAutoString, originDir, GetLeafName)); + + // Unknown files during getting usages are allowed. Just warn if we + // find them. + if (!IsOSMetadata(leafName)) { + UNKNOWN_FILE_WARNING(leafName); + } + + break; + } + + case nsIFileKind::DoesNotExist: + // Ignore files that got removed externally while iterating. + break; + } + + return Ok{}; + })); + + return NS_OK; +} + +GetUsageOp::GetUsageOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const UsageRequestParams& aParams) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), + "dom::quota::GetUsageOp"), + mGetAll(aParams.get_AllUsageParams().getAll()) { + AssertIsOnOwningThread(); + MOZ_ASSERT(aParams.type() == UsageRequestParams::TAllUsageParams); +} + +void GetUsageOp::ProcessOriginInternal(QuotaManager* aQuotaManager, + const PersistenceType aPersistenceType, + const nsACString& aOrigin, + const int64_t aTimestamp, + const bool aPersisted, + const uint64_t aUsage) { + if (!mGetAll && aQuotaManager->IsOriginInternal(aOrigin)) { + return; + } + + // We can't store pointers to OriginUsage objects in the hashtable + // since AppendElement() reallocates its internal array buffer as number + // of elements grows. + const auto& originUsage = + mOriginUsagesIndex.WithEntryHandle(aOrigin, [&](auto&& entry) { + if (entry) { + return WrapNotNullUnchecked(&mOriginUsages[entry.Data()]); + } + + entry.Insert(mOriginUsages.Length()); + + return mOriginUsages.EmplaceBack(nsCString{aOrigin}, false, 0, 0); + }); + + if (aPersistenceType == PERSISTENCE_TYPE_DEFAULT) { + originUsage->persisted() = aPersisted; + } + + originUsage->usage() = originUsage->usage() + aUsage; + + originUsage->lastAccessed() = + std::max<int64_t>(originUsage->lastAccessed(), aTimestamp); +} + +const Atomic<bool>& GetUsageOp::GetIsCanceledFlag() { + AssertIsOnIOThread(); + + return mCanceled; +} + +// XXX Remove aPersistent +// XXX Remove aPersistenceType once GetUsageForOrigin uses the persistence +// type from OriginMetadata +nsresult GetUsageOp::ProcessOrigin(QuotaManager& aQuotaManager, + nsIFile& aOriginDir, const bool aPersistent, + const PersistenceType aPersistenceType) { + AssertIsOnIOThread(); + + QM_TRY_UNWRAP(auto maybeMetadata, + QM_OR_ELSE_WARN_IF( + // Expression + aQuotaManager.LoadFullOriginMetadataWithRestore(&aOriginDir) + .map([](auto metadata) -> Maybe<FullOriginMetadata> { + return Some(std::move(metadata)); + }), + // Predicate. + IsSpecificError<NS_ERROR_MALFORMED_URI>, + // Fallback. + ErrToDefaultOk<Maybe<FullOriginMetadata>>)); + + if (!maybeMetadata) { + // Unknown directories during getting usage are allowed. Just warn if we + // find them. + QM_TRY_INSPECT(const auto& leafName, + MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsAutoString, aOriginDir, + GetLeafName)); + + UNKNOWN_FILE_WARNING(leafName); + return NS_OK; + } + + auto metadata = maybeMetadata.extract(); + + QM_TRY_INSPECT(const auto& usageInfo, + GetUsageForOrigin(aQuotaManager, aPersistenceType, metadata)); + + ProcessOriginInternal(&aQuotaManager, aPersistenceType, metadata.mOrigin, + metadata.mLastAccessTime, metadata.mPersisted, + usageInfo.TotalUsage().valueOr(0)); + + return NS_OK; +} + +RefPtr<BoolPromise> GetUsageOp::OpenDirectory() { + AssertIsOnOwningThread(); + + return OpenStorageDirectory(Nullable<PersistenceType>(), + OriginScope::FromNull(), Nullable<Client::Type>(), + /* aExclusive */ false); +} + +nsresult GetUsageOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + + AUTO_PROFILER_LABEL("GetUsageOp::DoDirectoryWork", OTHER); + + nsresult rv; + + for (const PersistenceType type : kAllPersistenceTypes) { + rv = TraverseRepository(aQuotaManager, type); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + // TraverseRepository above only consulted the filesystem. We also need to + // consider origins which may have pending quota usage, such as buffered + // LocalStorage writes for an origin which didn't previously have any + // LocalStorage data. + + aQuotaManager.CollectPendingOriginsForListing( + [this, &aQuotaManager](const auto& originInfo) { + ProcessOriginInternal( + &aQuotaManager, originInfo->GetGroupInfo()->GetPersistenceType(), + originInfo->Origin(), originInfo->LockedAccessTime(), + originInfo->LockedPersisted(), originInfo->LockedUsage()); + }); + + return NS_OK; +} + +void GetUsageOp::GetResponse(UsageRequestResponse& aResponse) { + AssertIsOnOwningThread(); + + aResponse = AllUsageResponse(); + + aResponse.get_AllUsageResponse().originUsages() = std::move(mOriginUsages); +} + +void GetUsageOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +GetOriginUsageOp::GetOriginUsageOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const UsageRequestParams& aParams) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), + "dom::quota::GetOriginUsageOp"), + mParams(aParams.get_OriginUsageParams()) { + AssertIsOnOwningThread(); + MOZ_ASSERT(aParams.type() == UsageRequestParams::TOriginUsageParams); + + // Overwrite GetOriginUsageOp default values. + mFromMemory = mParams.fromMemory(); +} + +nsresult GetOriginUsageOp::DoInit(QuotaManager& aQuotaManager) { + AssertIsOnOwningThread(); + + QM_TRY_UNWRAP( + mPrincipalMetadata, + aQuotaManager.GetInfoFromValidatedPrincipalInfo(mParams.principalInfo())); + + mPrincipalMetadata.AssertInvariants(); + + return NS_OK; +} + +RefPtr<BoolPromise> GetOriginUsageOp::OpenDirectory() { + AssertIsOnOwningThread(); + + return OpenStorageDirectory( + Nullable<PersistenceType>(), + OriginScope::FromOrigin(mPrincipalMetadata.mOrigin), + Nullable<Client::Type>(), + /* aExclusive */ false); +} + +nsresult GetOriginUsageOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + MOZ_ASSERT(mUsageInfo.TotalUsage().isNothing()); + + AUTO_PROFILER_LABEL("GetOriginUsageOp::DoDirectoryWork", OTHER); + + if (mFromMemory) { + // Ensure temporary storage is initialized. If temporary storage hasn't been + // initialized yet, the method will initialize it by traversing the + // repositories for temporary and default storage (including our origin). + QM_TRY(MOZ_TO_RESULT( + aQuotaManager.EnsureTemporaryStorageIsInitializedInternal())); + + // Get cached usage (the method doesn't have to stat any files). File usage + // is not tracked in memory separately, so just add to the database usage. + mUsageInfo += DatabaseUsageType( + Some(aQuotaManager.GetOriginUsage(mPrincipalMetadata))); + + return NS_OK; + } + + // Add all the persistent/temporary/default storage files we care about. + for (const PersistenceType type : kAllPersistenceTypes) { + const OriginMetadata originMetadata = {mPrincipalMetadata, type}; + + auto usageInfoOrErr = + GetUsageForOrigin(aQuotaManager, type, originMetadata); + if (NS_WARN_IF(usageInfoOrErr.isErr())) { + return usageInfoOrErr.unwrapErr(); + } + + mUsageInfo += usageInfoOrErr.unwrap(); + } + + return NS_OK; +} + +void GetOriginUsageOp::GetResponse(UsageRequestResponse& aResponse) { + AssertIsOnOwningThread(); + + OriginUsageResponse usageResponse; + + usageResponse.usageInfo() = mUsageInfo; + + aResponse = usageResponse; +} + +void GetOriginUsageOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +StorageNameOp::StorageNameOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) + : QuotaRequestBase(std::move(aQuotaManager), "dom::quota::StorageNameOp") { + AssertIsOnOwningThread(); +} + +RefPtr<BoolPromise> StorageNameOp::OpenDirectory() { + AssertIsOnOwningThread(); + + return BoolPromise::CreateAndResolve(true, __func__); +} + +nsresult StorageNameOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("StorageNameOp::DoDirectoryWork", OTHER); + + mName = aQuotaManager.GetStorageName(); + + return NS_OK; +} + +void StorageNameOp::GetResponse(RequestResponse& aResponse) { + AssertIsOnOwningThread(); + + StorageNameResponse storageNameResponse; + + storageNameResponse.name() = mName; + + aResponse = storageNameResponse; +} + +void StorageNameOp::CloseDirectory() { AssertIsOnOwningThread(); } + +InitializedRequestBase::InitializedRequestBase( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, const char* aName) + : ResolvableNormalOriginOp(std::move(aQuotaManager), aName), + mInitialized(false) { + AssertIsOnOwningThread(); +} + +RefPtr<BoolPromise> InitializedRequestBase::OpenDirectory() { + AssertIsOnOwningThread(); + + return BoolPromise::CreateAndResolve(true, __func__); +} + +void InitializedRequestBase::CloseDirectory() { AssertIsOnOwningThread(); } + +nsresult StorageInitializedOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("StorageInitializedOp::DoDirectoryWork", OTHER); + + mInitialized = aQuotaManager.IsStorageInitializedInternal(); + + return NS_OK; +} + +bool StorageInitializedOp::GetResolveValue() { + AssertIsOnOwningThread(); + + return mInitialized; +} + +nsresult TemporaryStorageInitializedOp::DoDirectoryWork( + QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("TemporaryStorageInitializedOp::DoDirectoryWork", OTHER); + + mInitialized = aQuotaManager.IsTemporaryStorageInitializedInternal(); + + return NS_OK; +} + +bool TemporaryStorageInitializedOp::GetResolveValue() { + AssertIsOnOwningThread(); + + return mInitialized; +} + +InitOp::InitOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + RefPtr<UniversalDirectoryLock> aDirectoryLock) + : ResolvableNormalOriginOp(std::move(aQuotaManager), "dom::quota::InitOp"), + mDirectoryLock(std::move(aDirectoryLock)) { + AssertIsOnOwningThread(); + MOZ_ASSERT(mDirectoryLock); +} + +RefPtr<BoolPromise> InitOp::OpenDirectory() { + AssertIsOnOwningThread(); + MOZ_ASSERT(mDirectoryLock); + + return BoolPromise::CreateAndResolve(true, __func__); +} + +nsresult InitOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("InitOp::DoDirectoryWork", OTHER); + + QM_TRY(MOZ_TO_RESULT(aQuotaManager.EnsureStorageIsInitializedInternal())); + + return NS_OK; +} + +bool InitOp::GetResolveValue() { return true; } + +void InitOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +InitTemporaryStorageOp::InitTemporaryStorageOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + RefPtr<UniversalDirectoryLock> aDirectoryLock) + : ResolvableNormalOriginOp(std::move(aQuotaManager), + "dom::quota::InitTemporaryStorageOp"), + mDirectoryLock(std::move(aDirectoryLock)) { + AssertIsOnOwningThread(); +} + +RefPtr<BoolPromise> InitTemporaryStorageOp::OpenDirectory() { + AssertIsOnOwningThread(); + MOZ_ASSERT(mDirectoryLock); + + return BoolPromise::CreateAndResolve(true, __func__); +} + +nsresult InitTemporaryStorageOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("InitTemporaryStorageOp::DoDirectoryWork", OTHER); + + QM_TRY(OkIf(aQuotaManager.IsStorageInitializedInternal()), + NS_ERROR_NOT_INITIALIZED); + + QM_TRY(MOZ_TO_RESULT( + aQuotaManager.EnsureTemporaryStorageIsInitializedInternal())); + + return NS_OK; +} + +bool InitTemporaryStorageOp::GetResolveValue() { + AssertIsOnOwningThread(); + + return true; +} + +void InitTemporaryStorageOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +InitializeOriginRequestBase::InitializeOriginRequestBase( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, const char* aName, + const PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo) + : QuotaRequestBase(std::move(aQuotaManager), aName), + mPrincipalInfo(aPrincipalInfo), + mPersistenceType(aPersistenceType), + mCreated(false) { + AssertIsOnOwningThread(); +} + +nsresult InitializeOriginRequestBase::DoInit(QuotaManager& aQuotaManager) { + AssertIsOnOwningThread(); + + QM_TRY_UNWRAP( + mPrincipalMetadata, + aQuotaManager.GetInfoFromValidatedPrincipalInfo(mPrincipalInfo)); + + mPrincipalMetadata.AssertInvariants(); + + return NS_OK; +} + +RefPtr<BoolPromise> InitializeOriginRequestBase::OpenDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal( + Nullable<PersistenceType>(mPersistenceType), + OriginScope::FromOrigin(mPrincipalMetadata.mOrigin), + Nullable<Client::Type>(), /* aExclusive */ false); + + return mDirectoryLock->Acquire(); +} + +void InitializeOriginRequestBase::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +InitializePersistentOriginOp::InitializePersistentOriginOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams) + : InitializeOriginRequestBase( + std::move(aQuotaManager), "dom::quota::InitializePersistentOriginOp", + PERSISTENCE_TYPE_PERSISTENT, + aParams.get_InitializePersistentOriginParams().principalInfo()) { + AssertIsOnOwningThread(); + MOZ_ASSERT(aParams.type() == + RequestParams::TInitializePersistentOriginParams); +} + +nsresult InitializePersistentOriginOp::DoDirectoryWork( + QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("InitializePersistentOriginOp::DoDirectoryWork", OTHER); + + QM_TRY(OkIf(aQuotaManager.IsStorageInitializedInternal()), + NS_ERROR_NOT_INITIALIZED); + + QM_TRY_UNWRAP(mCreated, + (aQuotaManager + .EnsurePersistentOriginIsInitialized(OriginMetadata{ + mPrincipalMetadata, PERSISTENCE_TYPE_PERSISTENT}) + .map([](const auto& res) { return res.second; }))); + + return NS_OK; +} + +void InitializePersistentOriginOp::GetResponse(RequestResponse& aResponse) { + AssertIsOnOwningThread(); + + aResponse = InitializePersistentOriginResponse(mCreated); +} + +InitializeTemporaryOriginOp::InitializeTemporaryOriginOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams) + : InitializeOriginRequestBase( + std::move(aQuotaManager), "dom::quota::InitializeTemporaryOriginOp", + aParams.get_InitializeTemporaryOriginParams().persistenceType(), + aParams.get_InitializeTemporaryOriginParams().principalInfo()) { + AssertIsOnOwningThread(); + MOZ_ASSERT(aParams.type() == RequestParams::TInitializeTemporaryOriginParams); +} + +nsresult InitializeTemporaryOriginOp::DoDirectoryWork( + QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("InitializeTemporaryOriginOp::DoDirectoryWork", OTHER); + + QM_TRY(OkIf(aQuotaManager.IsStorageInitializedInternal()), + NS_ERROR_NOT_INITIALIZED); + + QM_TRY(OkIf(aQuotaManager.IsTemporaryStorageInitializedInternal()), + NS_ERROR_NOT_INITIALIZED); + + QM_TRY_UNWRAP(mCreated, + (aQuotaManager + .EnsureTemporaryOriginIsInitialized( + mPersistenceType, + OriginMetadata{mPrincipalMetadata, mPersistenceType}) + .map([](const auto& res) { return res.second; }))); + + return NS_OK; +} + +void InitializeTemporaryOriginOp::GetResponse(RequestResponse& aResponse) { + AssertIsOnOwningThread(); + + aResponse = InitializeTemporaryOriginResponse(mCreated); +} + +InitializeClientBase::InitializeClientBase( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, const char* aName, + const PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, + Client::Type aClientType) + : ResolvableNormalOriginOp(std::move(aQuotaManager), aName), + mPrincipalInfo(aPrincipalInfo), + mPersistenceType(aPersistenceType), + mClientType(aClientType), + mCreated(false) { + AssertIsOnOwningThread(); +} + +nsresult InitializeClientBase::DoInit(QuotaManager& aQuotaManager) { + AssertIsOnOwningThread(); + + QM_TRY_UNWRAP( + PrincipalMetadata principalMetadata, + aQuotaManager.GetInfoFromValidatedPrincipalInfo(mPrincipalInfo)); + + principalMetadata.AssertInvariants(); + + mClientMetadata = { + OriginMetadata{std::move(principalMetadata), mPersistenceType}, + mClientType}; + + return NS_OK; +} + +RefPtr<BoolPromise> InitializeClientBase::OpenDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal( + Nullable(mPersistenceType), + OriginScope::FromOrigin(mClientMetadata.mOrigin), + Nullable(mClientMetadata.mClientType), /* aExclusive */ false); + + return mDirectoryLock->Acquire(); +} + +void InitializeClientBase::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +InitializePersistentClientOp::InitializePersistentClientOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const PrincipalInfo& aPrincipalInfo, Client::Type aClientType) + : InitializeClientBase( + std::move(aQuotaManager), "dom::quota::InitializePersistentClientOp", + PERSISTENCE_TYPE_PERSISTENT, aPrincipalInfo, aClientType) { + AssertIsOnOwningThread(); +} + +nsresult InitializePersistentClientOp::DoDirectoryWork( + QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("InitializePersistentClientOp::DoDirectoryWork", OTHER); + + QM_TRY(MOZ_TO_RESULT(aQuotaManager.IsStorageInitializedInternal()), + NS_ERROR_FAILURE); + + QM_TRY( + MOZ_TO_RESULT(aQuotaManager.IsOriginInitialized(mClientMetadata.mOrigin)), + NS_ERROR_FAILURE); + + QM_TRY_UNWRAP( + mCreated, + (aQuotaManager.EnsurePersistentClientIsInitialized(mClientMetadata) + .map([](const auto& res) { return res.second; }))); + + return NS_OK; +} + +bool InitializePersistentClientOp::GetResolveValue() { + AssertIsOnOwningThread(); + + return mCreated; +} + +InitializeTemporaryClientOp::InitializeTemporaryClientOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, + Client::Type aClientType) + : InitializeClientBase(std::move(aQuotaManager), + "dom::quota::InitializeTemporaryClientOp", + aPersistenceType, aPrincipalInfo, aClientType) { + AssertIsOnOwningThread(); +} + +nsresult InitializeTemporaryClientOp::DoDirectoryWork( + QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("InitializeTemporaryClientOp::DoDirectoryWork", OTHER); + + QM_TRY(MOZ_TO_RESULT(aQuotaManager.IsStorageInitializedInternal()), + NS_ERROR_FAILURE); + + QM_TRY(MOZ_TO_RESULT(aQuotaManager.IsTemporaryStorageInitializedInternal()), + NS_ERROR_FAILURE); + + QM_TRY(MOZ_TO_RESULT( + aQuotaManager.IsTemporaryOriginInitialized(mClientMetadata)), + NS_ERROR_FAILURE); + + QM_TRY_UNWRAP( + mCreated, + (aQuotaManager.EnsureTemporaryClientIsInitialized(mClientMetadata) + .map([](const auto& res) { return res.second; }))); + + return NS_OK; +} + +bool InitializeTemporaryClientOp::GetResolveValue() { + AssertIsOnOwningThread(); + + return mCreated; +} + +GetFullOriginMetadataOp::GetFullOriginMetadataOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const GetFullOriginMetadataParams& aParams) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), + "dom::quota::GetFullOriginMetadataOp"), + mParams(aParams) { + AssertIsOnOwningThread(); +} + +nsresult GetFullOriginMetadataOp::DoInit(QuotaManager& aQuotaManager) { + AssertIsOnOwningThread(); + + QM_TRY_UNWRAP( + PrincipalMetadata principalMetadata, + aQuotaManager.GetInfoFromValidatedPrincipalInfo(mParams.principalInfo())); + + principalMetadata.AssertInvariants(); + + mOriginMetadata = {std::move(principalMetadata), mParams.persistenceType()}; + + return NS_OK; +} + +RefPtr<BoolPromise> GetFullOriginMetadataOp::OpenDirectory() { + AssertIsOnOwningThread(); + + return OpenStorageDirectory( + Nullable<PersistenceType>(mOriginMetadata.mPersistenceType), + OriginScope::FromOrigin(mOriginMetadata.mOrigin), + Nullable<Client::Type>(), + /* aExclusive */ false); +} + +nsresult GetFullOriginMetadataOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + + AUTO_PROFILER_LABEL("GetFullOriginMetadataOp::DoDirectoryWork", OTHER); + + // Ensure temporary storage is initialized. If temporary storage hasn't + // been initialized yet, the method will initialize it by traversing the + // repositories for temporary and default storage (including our origin). + QM_TRY(MOZ_TO_RESULT( + aQuotaManager.EnsureTemporaryStorageIsInitializedInternal())); + + // Get metadata cached in memory (the method doesn't have to stat any + // files). + mMaybeFullOriginMetadata = + aQuotaManager.GetFullOriginMetadata(mOriginMetadata); + + return NS_OK; +} + +void GetFullOriginMetadataOp::GetResponse(RequestResponse& aResponse) { + AssertIsOnOwningThread(); + + aResponse = GetFullOriginMetadataResponse(); + aResponse.get_GetFullOriginMetadataResponse().maybeFullOriginMetadata() = + std::move(mMaybeFullOriginMetadata); +} + +void GetFullOriginMetadataOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +ClearStorageOp::ClearStorageOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), + "dom::quota::ClearStorageOp") { + AssertIsOnOwningThread(); +} + +void ClearStorageOp::DeleteFiles(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + nsresult rv = aQuotaManager.AboutToClearOrigins(Nullable<PersistenceType>(), + OriginScope::FromNull(), + Nullable<Client::Type>()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + auto directoryOrErr = QM_NewLocalFile(aQuotaManager.GetStoragePath()); + if (NS_WARN_IF(directoryOrErr.isErr())) { + return; + } + + nsCOMPtr<nsIFile> directory = directoryOrErr.unwrap(); + + rv = directory->Remove(true); + if (rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) { + // This should never fail if we've closed all storage connections + // correctly... + MOZ_ASSERT(false, "Failed to remove storage directory!"); + } +} + +void ClearStorageOp::DeleteStorageFile(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + QM_TRY_INSPECT(const auto& storageFile, + QM_NewLocalFile(aQuotaManager.GetBasePath()), QM_VOID); + + QM_TRY(MOZ_TO_RESULT(storageFile->Append(aQuotaManager.GetStorageName() + + kSQLiteSuffix)), + QM_VOID); + + const nsresult rv = storageFile->Remove(true); + if (rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) { + // This should never fail if we've closed the storage connection + // correctly... + MOZ_ASSERT(false, "Failed to remove storage file!"); + } +} + +RefPtr<BoolPromise> ClearStorageOp::OpenDirectory() { + AssertIsOnOwningThread(); + + // Clear directory lock tables (which also saves origin access time) before + // acquiring the exclusive lock below. Otherwise, saving of origin access + // time would be scheduled after storage clearing and that would initialize + // storage again in the end. + mQuotaManager->ClearDirectoryLockTables(); + + return OpenStorageDirectory(Nullable<PersistenceType>(), + OriginScope::FromNull(), Nullable<Client::Type>(), + /* aExclusive */ true); +} + +nsresult ClearStorageOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + + AUTO_PROFILER_LABEL("ClearStorageOp::DoDirectoryWork", OTHER); + + DeleteFiles(aQuotaManager); + + aQuotaManager.RemoveQuota(); + + aQuotaManager.ShutdownStorageInternal(); + + DeleteStorageFile(aQuotaManager); + + return NS_OK; +} + +bool ClearStorageOp::GetResolveValue() { + AssertIsOnOwningThread(); + + return true; +} + +void ClearStorageOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager, + const OriginMetadata& aOriginMetadata, + const Nullable<Client::Type>& aClientType) { + AssertIsOnIOThread(); + + DeleteFilesInternal( + aQuotaManager, aOriginMetadata.mPersistenceType, + OriginScope::FromOrigin(aOriginMetadata.mOrigin), aClientType, + [&aQuotaManager, &aOriginMetadata]( + const std::function<Result<Ok, nsresult>(nsCOMPtr<nsIFile>&&)>& aBody) + -> Result<Ok, nsresult> { + QM_TRY_UNWRAP(auto directory, + aQuotaManager.GetOriginDirectory(aOriginMetadata)); + + QM_TRY_INSPECT(const bool& exists, + MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists)); + + if (!exists) { + return Ok{}; + } + + QM_TRY_RETURN(aBody(std::move(directory))); + }); +} + +void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager, + PersistenceType aPersistenceType, + const OriginScope& aOriginScope, + const Nullable<Client::Type>& aClientType) { + AssertIsOnIOThread(); + + DeleteFilesInternal( + aQuotaManager, aPersistenceType, aOriginScope, aClientType, + [&aQuotaManager, &aPersistenceType]( + const std::function<Result<Ok, nsresult>(nsCOMPtr<nsIFile>&&)>& aBody) + -> Result<Ok, nsresult> { + QM_TRY_INSPECT( + const auto& directory, + QM_NewLocalFile(aQuotaManager.GetStoragePath(aPersistenceType))); + + QM_TRY_INSPECT(const bool& exists, + MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists)); + + if (!exists) { + return Ok{}; + } + + QM_TRY_RETURN(CollectEachFile(*directory, aBody)); + }); +} + +template <typename FileCollector> +void ClearRequestBase::DeleteFilesInternal( + QuotaManager& aQuotaManager, PersistenceType aPersistenceType, + const OriginScope& aOriginScope, const Nullable<Client::Type>& aClientType, + const FileCollector& aFileCollector) { + AssertIsOnIOThread(); + + QM_TRY(MOZ_TO_RESULT(aQuotaManager.AboutToClearOrigins( + Nullable<PersistenceType>(aPersistenceType), aOriginScope, + aClientType)), + QM_VOID); + + nsTArray<nsCOMPtr<nsIFile>> directoriesForRemovalRetry; + + aQuotaManager.MaybeRecordQuotaManagerShutdownStep( + "ClearRequestBase: Starting deleting files"_ns); + + QM_TRY( + aFileCollector([&aClientType, &originScope = aOriginScope, + aPersistenceType, &aQuotaManager, + &directoriesForRemovalRetry](nsCOMPtr<nsIFile>&& file) + -> mozilla::Result<Ok, nsresult> { + QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*file)); + + QM_TRY_INSPECT( + const auto& leafName, + MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsAutoString, file, GetLeafName)); + + switch (dirEntryKind) { + case nsIFileKind::ExistsAsDirectory: { + QM_TRY_UNWRAP(auto maybeMetadata, + QM_OR_ELSE_WARN_IF( + // Expression + aQuotaManager.GetOriginMetadata(file).map( + [](auto metadata) -> Maybe<OriginMetadata> { + return Some(std::move(metadata)); + }), + // Predicate. + IsSpecificError<NS_ERROR_MALFORMED_URI>, + // Fallback. + ErrToDefaultOk<Maybe<OriginMetadata>>)); + + if (!maybeMetadata) { + // Unknown directories during clearing are allowed. Just + // warn if we find them. + UNKNOWN_FILE_WARNING(leafName); + break; + } + + auto metadata = maybeMetadata.extract(); + + MOZ_ASSERT(metadata.mPersistenceType == aPersistenceType); + + // Skip the origin directory if it doesn't match the pattern. + if (!originScope.Matches( + OriginScope::FromOrigin(metadata.mOrigin))) { + break; + } + + if (!aClientType.IsNull()) { + nsAutoString clientDirectoryName; + QM_TRY(OkIf(Client::TypeToText(aClientType.Value(), + clientDirectoryName, fallible)), + Err(NS_ERROR_FAILURE)); + + QM_TRY(MOZ_TO_RESULT(file->Append(clientDirectoryName))); + + QM_TRY_INSPECT(const bool& exists, + MOZ_TO_RESULT_INVOKE_MEMBER(file, Exists)); + + if (!exists) { + break; + } + } + + // We can't guarantee that this will always succeed on + // Windows... + QM_WARNONLY_TRY( + aQuotaManager.RemoveOriginDirectory(*file), [&](const auto&) { + directoriesForRemovalRetry.AppendElement(std::move(file)); + }); + + const bool initialized = + aPersistenceType == PERSISTENCE_TYPE_PERSISTENT + ? aQuotaManager.IsOriginInitialized(metadata.mOrigin) + : aQuotaManager.IsTemporaryStorageInitializedInternal(); + + // If it hasn't been initialized, we don't need to update the + // quota and notify the removing client. + if (!initialized) { + break; + } + + if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT) { + if (aClientType.IsNull()) { + aQuotaManager.RemoveQuotaForOrigin(aPersistenceType, metadata); + } else { + aQuotaManager.ResetUsageForClient( + ClientMetadata{metadata, aClientType.Value()}); + } + } + + aQuotaManager.OriginClearCompleted(aPersistenceType, + metadata.mOrigin, aClientType); + + break; + } + + case nsIFileKind::ExistsAsFile: { + // Unknown files during clearing are allowed. Just warn if we + // find them. + if (!IsOSMetadata(leafName)) { + UNKNOWN_FILE_WARNING(leafName); + } + + break; + } + + case nsIFileKind::DoesNotExist: + // Ignore files that got removed externally while iterating. + break; + } + + return Ok{}; + }), + QM_VOID); + + // Retry removing any directories that failed to be removed earlier now. + // + // XXX This will still block this operation. We might instead dispatch a + // runnable to our own thread for each retry round with a timer. We must + // ensure that the directory lock is upheld until we complete or give up + // though. + for (uint32_t index = 0; index < 10; index++) { + aQuotaManager.MaybeRecordQuotaManagerShutdownStepWith([index]() { + return nsPrintfCString( + "ClearRequestBase: Starting repeated directory removal #%d", index); + }); + + for (auto&& file : std::exchange(directoriesForRemovalRetry, + nsTArray<nsCOMPtr<nsIFile>>{})) { + QM_WARNONLY_TRY( + aQuotaManager.RemoveOriginDirectory(*file), + ([&directoriesForRemovalRetry, &file](const auto&) { + directoriesForRemovalRetry.AppendElement(std::move(file)); + })); + } + + aQuotaManager.MaybeRecordQuotaManagerShutdownStepWith([index]() { + return nsPrintfCString( + "ClearRequestBase: Completed repeated directory removal #%d", index); + }); + + if (directoriesForRemovalRetry.IsEmpty()) { + break; + } + + aQuotaManager.MaybeRecordQuotaManagerShutdownStepWith([index]() { + return nsPrintfCString("ClearRequestBase: Before sleep #%d", index); + }); + + PR_Sleep(PR_MillisecondsToInterval(200)); + + aQuotaManager.MaybeRecordQuotaManagerShutdownStepWith([index]() { + return nsPrintfCString("ClearRequestBase: After sleep #%d", index); + }); + } + + QM_WARNONLY_TRY(OkIf(directoriesForRemovalRetry.IsEmpty())); + + aQuotaManager.MaybeRecordQuotaManagerShutdownStep( + "ClearRequestBase: Completed deleting files"_ns); +} + +ClearOriginOp::ClearOriginOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const mozilla::Maybe<PersistenceType>& aPersistenceType, + const PrincipalInfo& aPrincipalInfo, + const mozilla::Maybe<Client::Type>& aClientType) + : ClearRequestBase(std::move(aQuotaManager), "dom::quota::ClearOriginOp"), + mPrincipalInfo(aPrincipalInfo), + mPersistenceType(aPersistenceType + ? Nullable<PersistenceType>(*aPersistenceType) + : Nullable<PersistenceType>()), + mClientType(aClientType ? Nullable<Client::Type>(*aClientType) + : Nullable<Client::Type>()) { + AssertIsOnOwningThread(); +} + +nsresult ClearOriginOp::DoInit(QuotaManager& aQuotaManager) { + AssertIsOnOwningThread(); + + QM_TRY_UNWRAP( + mPrincipalMetadata, + aQuotaManager.GetInfoFromValidatedPrincipalInfo(mPrincipalInfo)); + + mPrincipalMetadata.AssertInvariants(); + + return NS_OK; +} + +RefPtr<BoolPromise> ClearOriginOp::OpenDirectory() { + AssertIsOnOwningThread(); + + return OpenStorageDirectory( + mPersistenceType, OriginScope::FromOrigin(mPrincipalMetadata.mOrigin), + mClientType, + /* aExclusive */ true); +} + +nsresult ClearOriginOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + + AUTO_PROFILER_LABEL("ClearRequestBase::DoDirectoryWork", OTHER); + + if (mPersistenceType.IsNull()) { + for (const PersistenceType type : kAllPersistenceTypes) { + DeleteFiles(aQuotaManager, OriginMetadata(mPrincipalMetadata, type), + mClientType); + } + } else { + DeleteFiles(aQuotaManager, + OriginMetadata(mPrincipalMetadata, mPersistenceType.Value()), + mClientType); + } + + return NS_OK; +} + +bool ClearOriginOp::GetResolveValue() { + AssertIsOnOwningThread(); + + return true; +} + +void ClearOriginOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +ClearStoragesForOriginPrefixOp::ClearStoragesForOriginPrefixOp( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const Maybe<PersistenceType>& aPersistenceType, + const PrincipalInfo& aPrincipalInfo) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), + "dom::quota::ClearStoragesForOriginPrefixOp"), + mPrefix( + QuotaManager::GetOriginFromValidatedPrincipalInfo(aPrincipalInfo)), + mPersistenceType(aPersistenceType + ? Nullable<PersistenceType>(*aPersistenceType) + : Nullable<PersistenceType>()) { + AssertIsOnOwningThread(); +} + +RefPtr<BoolPromise> ClearStoragesForOriginPrefixOp::OpenDirectory() { + AssertIsOnOwningThread(); + + return OpenStorageDirectory(mPersistenceType, + OriginScope::FromPrefix(mPrefix), + Nullable<Client::Type>(), + /* aExclusive */ true); +} + +nsresult ClearStoragesForOriginPrefixOp::DoDirectoryWork( + QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("ClearStoragesForOriginPrefixOp::DoDirectoryWork", OTHER); + + if (mPersistenceType.IsNull()) { + for (const PersistenceType type : kAllPersistenceTypes) { + DeleteFiles(aQuotaManager, type, OriginScope::FromPrefix(mPrefix), + Nullable<Client::Type>()); + } + } else { + DeleteFiles(aQuotaManager, mPersistenceType.Value(), + OriginScope::FromPrefix(mPrefix), Nullable<Client::Type>()); + } + + return NS_OK; +} + +bool ClearStoragesForOriginPrefixOp::GetResolveValue() { + AssertIsOnOwningThread(); + + return true; +} + +void ClearStoragesForOriginPrefixOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +ClearDataOp::ClearDataOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const OriginAttributesPattern& aPattern) + : ClearRequestBase(std::move(aQuotaManager), "dom::quota::ClearDataOp"), + mPattern(aPattern) {} + +RefPtr<BoolPromise> ClearDataOp::OpenDirectory() { + AssertIsOnOwningThread(); + + return OpenStorageDirectory(Nullable<PersistenceType>(), + OriginScope::FromPattern(mPattern), + Nullable<Client::Type>(), + /* aExclusive */ true); +} + +nsresult ClearDataOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("ClearRequestBase::DoDirectoryWork", OTHER); + + for (const PersistenceType type : kAllPersistenceTypes) { + DeleteFiles(aQuotaManager, type, OriginScope::FromPattern(mPattern), + Nullable<Client::Type>()); + } + + return NS_OK; +} + +bool ClearDataOp::GetResolveValue() { + AssertIsOnOwningThread(); + + return true; +} + +void ClearDataOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +ResetOriginOp::ResetOriginOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams) + : QuotaRequestBase(std::move(aQuotaManager), "dom::quota::ResetOriginOp") { + AssertIsOnOwningThread(); + MOZ_ASSERT(aParams.type() == RequestParams::TResetOriginParams); + + const ClearResetOriginParams& params = + aParams.get_ResetOriginParams().commonParams(); + + mOrigin = + QuotaManager::GetOriginFromValidatedPrincipalInfo(params.principalInfo()); + + if (params.persistenceTypeIsExplicit()) { + mPersistenceType.SetValue(params.persistenceType()); + } + + if (params.clientTypeIsExplicit()) { + mClientType.SetValue(params.clientType()); + } +} + +RefPtr<BoolPromise> ResetOriginOp::OpenDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal( + mPersistenceType, OriginScope::FromOrigin(mOrigin), mClientType, + /* aExclusive */ true); + + return mDirectoryLock->Acquire(); +} + +nsresult ResetOriginOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + + AUTO_PROFILER_LABEL("ResetOriginOp::DoDirectoryWork", OTHER); + + // All the work is handled by NormalOriginOperationBase parent class. In + // this particular case, we just needed to acquire an exclusive directory + // lock and that's it. + + return NS_OK; +} + +void ResetOriginOp::GetResponse(RequestResponse& aResponse) { + AssertIsOnOwningThread(); + + aResponse = ResetOriginResponse(); +} + +void ResetOriginOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +PersistRequestBase::PersistRequestBase( + MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const PrincipalInfo& aPrincipalInfo) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), + "dom::quota::PersistRequestBase"), + mPrincipalInfo(aPrincipalInfo) { + AssertIsOnOwningThread(); +} + +nsresult PersistRequestBase::DoInit(QuotaManager& aQuotaManager) { + AssertIsOnOwningThread(); + + // Figure out which origin we're dealing with. + QM_TRY_UNWRAP( + mPrincipalMetadata, + aQuotaManager.GetInfoFromValidatedPrincipalInfo(mPrincipalInfo)); + + mPrincipalMetadata.AssertInvariants(); + + return NS_OK; +} + +RefPtr<BoolPromise> PersistRequestBase::OpenDirectory() { + AssertIsOnOwningThread(); + + return OpenStorageDirectory( + Nullable<PersistenceType>(PERSISTENCE_TYPE_DEFAULT), + OriginScope::FromOrigin(mPrincipalMetadata.mOrigin), + Nullable<Client::Type>(), + /* aExclusive */ false); +} + +void PersistRequestBase::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +PersistedOp::PersistedOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams) + : PersistRequestBase(std::move(aQuotaManager), + aParams.get_PersistedParams().principalInfo()), + mPersisted(false) { + MOZ_ASSERT(aParams.type() == RequestParams::TPersistedParams); +} + +nsresult PersistedOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + + AUTO_PROFILER_LABEL("PersistedOp::DoDirectoryWork", OTHER); + + const OriginMetadata originMetadata = {mPrincipalMetadata, + PERSISTENCE_TYPE_DEFAULT}; + + Nullable<bool> persisted = aQuotaManager.OriginPersisted(originMetadata); + + if (!persisted.IsNull()) { + mPersisted = persisted.Value(); + return NS_OK; + } + + // If we get here, it means the origin hasn't been initialized yet. + // Try to get the persisted flag from directory metadata on disk. + + QM_TRY_INSPECT(const auto& directory, + aQuotaManager.GetOriginDirectory(originMetadata)); + + QM_TRY_INSPECT(const bool& exists, + MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists)); + + if (exists) { + // Get the metadata. We only use the persisted flag. + QM_TRY_INSPECT(const auto& metadata, + aQuotaManager.LoadFullOriginMetadataWithRestore(directory)); + + mPersisted = metadata.mPersisted; + } else { + // The directory has not been created yet. + mPersisted = false; + } + + return NS_OK; +} + +void PersistedOp::GetResponse(RequestResponse& aResponse) { + AssertIsOnOwningThread(); + + PersistedResponse persistedResponse; + persistedResponse.persisted() = mPersisted; + + aResponse = persistedResponse; +} + +PersistOp::PersistOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const RequestParams& aParams) + : PersistRequestBase(std::move(aQuotaManager), + aParams.get_PersistParams().principalInfo()) { + MOZ_ASSERT(aParams.type() == RequestParams::TPersistParams); +} + +nsresult PersistOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + + const OriginMetadata originMetadata = {mPrincipalMetadata, + PERSISTENCE_TYPE_DEFAULT}; + + AUTO_PROFILER_LABEL("PersistOp::DoDirectoryWork", OTHER); + + // Update directory metadata on disk first. Then, create/update the + // originInfo if needed. + QM_TRY_INSPECT(const auto& directory, + aQuotaManager.GetOriginDirectory(originMetadata)); + + QM_TRY_INSPECT(const bool& created, + aQuotaManager.EnsureOriginDirectory(*directory)); + + if (created) { + int64_t timestamp; + + // Origin directory has been successfully created. + // Create OriginInfo too if temporary storage was already initialized. + if (aQuotaManager.IsTemporaryStorageInitializedInternal()) { + timestamp = aQuotaManager.NoteOriginDirectoryCreated( + originMetadata, /* aPersisted */ true); + } else { + timestamp = PR_Now(); + } + + QM_TRY(MOZ_TO_RESULT(QuotaManager::CreateDirectoryMetadata2( + *directory, timestamp, + /* aPersisted */ true, originMetadata))); + } else { + // Get the metadata (restore the metadata file if necessary). We only use + // the persisted flag. + QM_TRY_INSPECT(const auto& metadata, + aQuotaManager.LoadFullOriginMetadataWithRestore(directory)); + + if (!metadata.mPersisted) { + QM_TRY_INSPECT(const auto& file, + CloneFileAndAppend( + *directory, nsLiteralString(METADATA_V2_FILE_NAME))); + + QM_TRY_INSPECT(const auto& stream, + GetBinaryOutputStream(*file, FileFlag::Update)); + + MOZ_ASSERT(stream); + + // Update origin access time while we are here. + QM_TRY(MOZ_TO_RESULT(stream->Write64(PR_Now()))); + + // Set the persisted flag to true. + QM_TRY(MOZ_TO_RESULT(stream->WriteBoolean(true))); + } + + // Directory metadata has been successfully updated. + // Update OriginInfo too if temporary storage was already initialized. + if (aQuotaManager.IsTemporaryStorageInitializedInternal()) { + aQuotaManager.PersistOrigin(originMetadata); + } + } + + return NS_OK; +} + +void PersistOp::GetResponse(RequestResponse& aResponse) { + AssertIsOnOwningThread(); + + aResponse = PersistResponse(); +} + +EstimateOp::EstimateOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager, + const EstimateParams& aParams) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), + "dom::quota::EstimateOp"), + mParams(aParams) { + AssertIsOnOwningThread(); +} + +nsresult EstimateOp::DoInit(QuotaManager& aQuotaManager) { + AssertIsOnOwningThread(); + + QM_TRY_UNWRAP( + PrincipalMetadata principalMetadata, + aQuotaManager.GetInfoFromValidatedPrincipalInfo(mParams.principalInfo())); + + principalMetadata.AssertInvariants(); + + mOriginMetadata = {std::move(principalMetadata), PERSISTENCE_TYPE_DEFAULT}; + + return NS_OK; +} + +RefPtr<BoolPromise> EstimateOp::OpenDirectory() { + AssertIsOnOwningThread(); + + // XXX In theory, we should be locking entire group, not just one origin. + return OpenStorageDirectory( + Nullable<PersistenceType>(mOriginMetadata.mPersistenceType), + OriginScope::FromOrigin(mOriginMetadata.mOrigin), + Nullable<Client::Type>(), + /* aExclusive */ false); +} + +nsresult EstimateOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + + AUTO_PROFILER_LABEL("EstimateOp::DoDirectoryWork", OTHER); + + // Ensure temporary storage is initialized. If temporary storage hasn't been + // initialized yet, the method will initialize it by traversing the + // repositories for temporary and default storage (including origins + // belonging to our group). + QM_TRY(MOZ_TO_RESULT( + aQuotaManager.EnsureTemporaryStorageIsInitializedInternal())); + + // Get cached usage (the method doesn't have to stat any files). + mUsageAndLimit = aQuotaManager.GetUsageAndLimitForEstimate(mOriginMetadata); + + return NS_OK; +} + +void EstimateOp::GetResponse(RequestResponse& aResponse) { + AssertIsOnOwningThread(); + + EstimateResponse estimateResponse; + + estimateResponse.usage() = mUsageAndLimit.first; + estimateResponse.limit() = mUsageAndLimit.second; + + aResponse = estimateResponse; +} + +void EstimateOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +ListOriginsOp::ListOriginsOp(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager) + : OpenStorageDirectoryHelper(std::move(aQuotaManager), + "dom::quota::ListOriginsOp") { + AssertIsOnOwningThread(); +} + +RefPtr<BoolPromise> ListOriginsOp::OpenDirectory() { + AssertIsOnOwningThread(); + + return OpenStorageDirectory(Nullable<PersistenceType>(), + OriginScope::FromNull(), Nullable<Client::Type>(), + /* aExclusive */ false); +} + +nsresult ListOriginsOp::DoDirectoryWork(QuotaManager& aQuotaManager) { + AssertIsOnIOThread(); + aQuotaManager.AssertStorageIsInitializedInternal(); + + AUTO_PROFILER_LABEL("ListOriginsOp::DoDirectoryWork", OTHER); + + for (const PersistenceType type : kAllPersistenceTypes) { + QM_TRY(MOZ_TO_RESULT(TraverseRepository(aQuotaManager, type))); + } + + // TraverseRepository above only consulted the file-system to get a list of + // known origins, but we also need to include origins that have pending + // quota usage. + + aQuotaManager.CollectPendingOriginsForListing([this](const auto& originInfo) { + mOrigins.AppendElement(originInfo->Origin()); + }); + + return NS_OK; +} + +const Atomic<bool>& ListOriginsOp::GetIsCanceledFlag() { + AssertIsOnIOThread(); + + return mCanceled; +} + +nsresult ListOriginsOp::ProcessOrigin(QuotaManager& aQuotaManager, + nsIFile& aOriginDir, + const bool aPersistent, + const PersistenceType aPersistenceType) { + AssertIsOnIOThread(); + + QM_TRY_UNWRAP(auto maybeMetadata, + QM_OR_ELSE_WARN_IF( + // Expression + aQuotaManager.GetOriginMetadata(&aOriginDir) + .map([](auto metadata) -> Maybe<OriginMetadata> { + return Some(std::move(metadata)); + }), + // Predicate. + IsSpecificError<NS_ERROR_MALFORMED_URI>, + // Fallback. + ErrToDefaultOk<Maybe<OriginMetadata>>)); + + if (!maybeMetadata) { + // Unknown directories during listing are allowed. Just warn if we find + // them. + QM_TRY_INSPECT(const auto& leafName, + MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsAutoString, aOriginDir, + GetLeafName)); + + UNKNOWN_FILE_WARNING(leafName); + return NS_OK; + } + + auto metadata = maybeMetadata.extract(); + + if (aQuotaManager.IsOriginInternal(metadata.mOrigin)) { + return NS_OK; + } + + mOrigins.AppendElement(std::move(metadata.mOrigin)); + + return NS_OK; +} + +void ListOriginsOp::GetResponse(RequestResponse& aResponse) { + AssertIsOnOwningThread(); + + aResponse = ListOriginsResponse(); + if (mOrigins.IsEmpty()) { + return; + } + + nsTArray<nsCString>& origins = aResponse.get_ListOriginsResponse().origins(); + mOrigins.SwapElements(origins); +} + +void ListOriginsOp::CloseDirectory() { + AssertIsOnOwningThread(); + + mDirectoryLock = nullptr; +} + +} // namespace mozilla::dom::quota |