/* -*- 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 #include #include #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 OpenStorageDirectoryHelper : public Base { protected: OpenStorageDirectoryHelper(MovingNotNull> aQuotaManager, const char* aName) : Base(std::move(aQuotaManager), aName) {} RefPtr OpenStorageDirectory( const Nullable& aPersistenceType, const OriginScope& aOriginScope, const Nullable& aClientType, bool aExclusive); RefPtr mDirectoryLock; }; class FinalizeOriginEvictionOp : public OriginOperationBase { nsTArray> mLocks; public: FinalizeOriginEvictionOp(MovingNotNull> aQuotaManager, nsTArray>&& aLocks) : OriginOperationBase(std::move(aQuotaManager), "dom::quota::FinalizeOriginEvictionOp"), mLocks(std::move(aLocks)) { AssertIsOnOwningThread(); } private: ~FinalizeOriginEvictionOp() = default; virtual RefPtr Open() override; virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; virtual void UnblockOpen() override; }; class SaveOriginAccessTimeOp : public OpenStorageDirectoryHelper { const OriginMetadata mOriginMetadata; int64_t mTimestamp; public: SaveOriginAccessTimeOp(MovingNotNull> aQuotaManager, const OriginMetadata& aOriginMetadata, int64_t aTimestamp) : OpenStorageDirectoryHelper(std::move(aQuotaManager), "dom::quota::SaveOriginAccessTimeOp"), mOriginMetadata(aOriginMetadata), mTimestamp(aTimestamp) { AssertIsOnOwningThread(); } private: ~SaveOriginAccessTimeOp() = default; RefPtr OpenDirectory() override; virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; virtual void SendResults() override; void CloseDirectory() override; }; class ClearPrivateRepositoryOp : public OpenStorageDirectoryHelper> { public: explicit ClearPrivateRepositoryOp( MovingNotNull> aQuotaManager) : OpenStorageDirectoryHelper(std::move(aQuotaManager), "dom::quota::ClearPrivateRepositoryOp") { AssertIsOnOwningThread(); } private: ~ClearPrivateRepositoryOp() = default; RefPtr OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; bool GetResolveValue() override { return true; } void CloseDirectory() override; }; class ShutdownStorageOp : public ResolvableNormalOriginOp { RefPtr mDirectoryLock; public: explicit ShutdownStorageOp(MovingNotNull> aQuotaManager) : ResolvableNormalOriginOp(std::move(aQuotaManager), "dom::quota::ShutdownStorageOp") { AssertIsOnOwningThread(); } private: ~ShutdownStorageOp() = default; #ifdef DEBUG nsresult DirectoryOpen() override; #endif RefPtr 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& GetIsCanceledFlag() = 0; virtual nsresult ProcessOrigin(QuotaManager& aQuotaManager, nsIFile& aOriginDir, const bool aPersistent, const PersistenceType aPersistenceType) = 0; }; class GetUsageOp final : public OpenStorageDirectoryHelper, public TraverseRepositoryHelper { nsTArray mOriginUsages; nsTHashMap mOriginUsagesIndex; bool mGetAll; public: GetUsageOp(MovingNotNull> 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 OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; const Atomic& 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 { const OriginUsageParams mParams; PrincipalMetadata mPrincipalMetadata; UsageInfo mUsageInfo; bool mFromMemory; public: GetOriginUsageOp(MovingNotNull> aQuotaManager, const UsageRequestParams& aParams); private: ~GetOriginUsageOp() = default; nsresult DoInit(QuotaManager& aQuotaManager) override; RefPtr 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> aQuotaManager); private: ~StorageNameOp() = default; RefPtr OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; void GetResponse(RequestResponse& aResponse) override; void CloseDirectory() override; }; class InitializedRequestBase : public ResolvableNormalOriginOp { protected: bool mInitialized; InitializedRequestBase(MovingNotNull> aQuotaManager, const char* aName); private: RefPtr OpenDirectory() override; void CloseDirectory() override; }; class StorageInitializedOp final : public InitializedRequestBase { public: explicit StorageInitializedOp( MovingNotNull> 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> aQuotaManager) : InitializedRequestBase(std::move(aQuotaManager), "dom::quota::StorageInitializedOp") {} private: ~TemporaryStorageInitializedOp() = default; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; bool GetResolveValue() override; }; class InitOp final : public ResolvableNormalOriginOp { RefPtr mDirectoryLock; public: InitOp(MovingNotNull> aQuotaManager, RefPtr aDirectoryLock); private: ~InitOp() = default; RefPtr OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; bool GetResolveValue() override; void CloseDirectory() override; }; class InitTemporaryStorageOp final : public ResolvableNormalOriginOp { RefPtr mDirectoryLock; public: InitTemporaryStorageOp(MovingNotNull> aQuotaManager, RefPtr aDirectoryLock); private: ~InitTemporaryStorageOp() = default; RefPtr OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; bool GetResolveValue() override; void CloseDirectory() override; }; class InitializeOriginRequestBase : public QuotaRequestBase { protected: const PrincipalInfo mPrincipalInfo; PrincipalMetadata mPrincipalMetadata; RefPtr mDirectoryLock; const PersistenceType mPersistenceType; bool mCreated; InitializeOriginRequestBase(MovingNotNull> aQuotaManager, const char* aName, PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo); nsresult DoInit(QuotaManager& aQuotaManager) override; private: RefPtr OpenDirectory() override; void CloseDirectory() override; }; class InitializePersistentOriginOp final : public InitializeOriginRequestBase { public: InitializePersistentOriginOp( MovingNotNull> 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> aQuotaManager, const RequestParams& aParams); private: ~InitializeTemporaryOriginOp() = default; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; void GetResponse(RequestResponse& aResponse) override; }; class InitializeClientBase : public ResolvableNormalOriginOp { protected: const PrincipalInfo mPrincipalInfo; ClientMetadata mClientMetadata; RefPtr mDirectoryLock; const PersistenceType mPersistenceType; const Client::Type mClientType; bool mCreated; InitializeClientBase(MovingNotNull> aQuotaManager, const char* aName, PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, Client::Type aClientType); nsresult DoInit(QuotaManager& aQuotaManager) override; private: RefPtr OpenDirectory() override; void CloseDirectory() override; }; class InitializePersistentClientOp : public InitializeClientBase { public: InitializePersistentClientOp( MovingNotNull> aQuotaManager, const PrincipalInfo& aPrincipalInfo, Client::Type aClientType); private: nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; bool GetResolveValue() override; }; class InitializeTemporaryClientOp : public InitializeClientBase { public: InitializeTemporaryClientOp(MovingNotNull> aQuotaManager, PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, Client::Type aClientType); private: nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; bool GetResolveValue() override; }; class GetFullOriginMetadataOp : public OpenStorageDirectoryHelper { const GetFullOriginMetadataParams mParams; // XXX Consider wrapping with LazyInitializedOnce OriginMetadata mOriginMetadata; Maybe mMaybeFullOriginMetadata; public: GetFullOriginMetadataOp(MovingNotNull> aQuotaManager, const GetFullOriginMetadataParams& aParams); private: nsresult DoInit(QuotaManager& aQuotaManager) override; RefPtr OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; void GetResponse(RequestResponse& aResponse) override; void CloseDirectory() override; }; class ClearStorageOp final : public OpenStorageDirectoryHelper> { public: explicit ClearStorageOp(MovingNotNull> aQuotaManager); private: ~ClearStorageOp() = default; void DeleteFiles(QuotaManager& aQuotaManager); void DeleteStorageFile(QuotaManager& aQuotaManager); RefPtr OpenDirectory() override; virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; bool GetResolveValue() override; void CloseDirectory() override; }; class ClearRequestBase : public OpenStorageDirectoryHelper> { protected: ClearRequestBase(MovingNotNull> aQuotaManager, const char* aName) : OpenStorageDirectoryHelper(std::move(aQuotaManager), aName) { AssertIsOnOwningThread(); } void DeleteFiles(QuotaManager& aQuotaManager, const OriginMetadata& aOriginMetadata, const Nullable& aClientType); void DeleteFiles(QuotaManager& aQuotaManager, PersistenceType aPersistenceType, const OriginScope& aOriginScope, const Nullable& aClientType); private: template void DeleteFilesInternal(QuotaManager& aQuotaManager, PersistenceType aPersistenceType, const OriginScope& aOriginScope, const Nullable& aClientType, const FileCollector& aFileCollector); }; class ClearOriginOp final : public ClearRequestBase { const PrincipalInfo mPrincipalInfo; PrincipalMetadata mPrincipalMetadata; const Nullable mPersistenceType; const Nullable mClientType; public: ClearOriginOp(MovingNotNull> aQuotaManager, const mozilla::Maybe& aPersistenceType, const PrincipalInfo& aPrincipalInfo, const mozilla::Maybe& aClientType); private: ~ClearOriginOp() = default; nsresult DoInit(QuotaManager& aQuotaManager) override; RefPtr OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; bool GetResolveValue() override; void CloseDirectory() override; }; class ClearStoragesForOriginPrefixOp final : public OpenStorageDirectoryHelper { const nsCString mPrefix; const Nullable mPersistenceType; public: ClearStoragesForOriginPrefixOp( MovingNotNull> aQuotaManager, const Maybe& aPersistenceType, const PrincipalInfo& aPrincipalInfo); private: ~ClearStoragesForOriginPrefixOp() = default; RefPtr OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; bool GetResolveValue() override; void CloseDirectory() override; }; class ClearDataOp final : public ClearRequestBase { const OriginAttributesPattern mPattern; public: ClearDataOp(MovingNotNull> aQuotaManager, const OriginAttributesPattern& aPattern); private: ~ClearDataOp() = default; RefPtr OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; bool GetResolveValue() override; void CloseDirectory() override; }; class ResetOriginOp final : public QuotaRequestBase { nsCString mOrigin; RefPtr mDirectoryLock; Nullable mPersistenceType; Nullable mClientType; public: ResetOriginOp(MovingNotNull> aQuotaManager, const RequestParams& aParams); private: ~ResetOriginOp() = default; RefPtr OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; void GetResponse(RequestResponse& aResponse) override; void CloseDirectory() override; }; class PersistRequestBase : public OpenStorageDirectoryHelper { const PrincipalInfo mPrincipalInfo; protected: PrincipalMetadata mPrincipalMetadata; protected: PersistRequestBase(MovingNotNull> aQuotaManager, const PrincipalInfo& aPrincipalInfo); nsresult DoInit(QuotaManager& aQuotaManager) override; private: RefPtr OpenDirectory() override; void CloseDirectory() override; }; class PersistedOp final : public PersistRequestBase { bool mPersisted; public: PersistedOp(MovingNotNull> 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> aQuotaManager, const RequestParams& aParams); private: ~PersistOp() = default; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; void GetResponse(RequestResponse& aResponse) override; }; class EstimateOp final : public OpenStorageDirectoryHelper { const EstimateParams mParams; OriginMetadata mOriginMetadata; std::pair mUsageAndLimit; public: EstimateOp(MovingNotNull> aQuotaManager, const EstimateParams& aParams); private: ~EstimateOp() = default; nsresult DoInit(QuotaManager& aQuotaManager) override; RefPtr OpenDirectory() override; virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; void GetResponse(RequestResponse& aResponse) override; void CloseDirectory() override; }; class ListOriginsOp final : public OpenStorageDirectoryHelper, public TraverseRepositoryHelper { // XXX Bug 1521541 will make each origin has it's own state. nsTArray mOrigins; public: explicit ListOriginsOp(MovingNotNull> aQuotaManager); private: ~ListOriginsOp() = default; RefPtr OpenDirectory() override; nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; const Atomic& GetIsCanceledFlag() override; nsresult ProcessOrigin(QuotaManager& aQuotaManager, nsIFile& aOriginDir, const bool aPersistent, const PersistenceType aPersistenceType) override; void GetResponse(RequestResponse& aResponse) override; void CloseDirectory() override; }; RefPtr CreateFinalizeOriginEvictionOp( MovingNotNull> aQuotaManager, nsTArray>&& aLocks) { return MakeRefPtr(std::move(aQuotaManager), std::move(aLocks)); } RefPtr CreateSaveOriginAccessTimeOp( MovingNotNull> aQuotaManager, const OriginMetadata& aOriginMetadata, int64_t aTimestamp) { return MakeRefPtr(std::move(aQuotaManager), aOriginMetadata, aTimestamp); } RefPtr> CreateClearPrivateRepositoryOp( MovingNotNull> aQuotaManager) { return MakeRefPtr(std::move(aQuotaManager)); } RefPtr> CreateShutdownStorageOp( MovingNotNull> aQuotaManager) { return MakeRefPtr(std::move(aQuotaManager)); } RefPtr CreateGetUsageOp( MovingNotNull> aQuotaManager, const UsageRequestParams& aParams) { return MakeRefPtr(std::move(aQuotaManager), aParams); } RefPtr CreateGetOriginUsageOp( MovingNotNull> aQuotaManager, const UsageRequestParams& aParams) { return MakeRefPtr(std::move(aQuotaManager), aParams); } RefPtr CreateStorageNameOp( MovingNotNull> aQuotaManager) { return MakeRefPtr(std::move(aQuotaManager)); } RefPtr> CreateStorageInitializedOp( MovingNotNull> aQuotaManager) { return MakeRefPtr(std::move(aQuotaManager)); } RefPtr> CreateTemporaryStorageInitializedOp( MovingNotNull> aQuotaManager) { return MakeRefPtr(std::move(aQuotaManager)); } RefPtr> CreateInitOp( MovingNotNull> aQuotaManager, RefPtr aDirectoryLock) { return MakeRefPtr(std::move(aQuotaManager), std::move(aDirectoryLock)); } RefPtr> CreateInitTemporaryStorageOp( MovingNotNull> aQuotaManager, RefPtr aDirectoryLock) { return MakeRefPtr(std::move(aQuotaManager), std::move(aDirectoryLock)); } RefPtr CreateInitializePersistentOriginOp( MovingNotNull> aQuotaManager, const RequestParams& aParams) { return MakeRefPtr(std::move(aQuotaManager), aParams); } RefPtr CreateInitializeTemporaryOriginOp( MovingNotNull> aQuotaManager, const RequestParams& aParams) { return MakeRefPtr(std::move(aQuotaManager), aParams); } RefPtr> CreateInitializePersistentClientOp( MovingNotNull> aQuotaManager, const mozilla::ipc::PrincipalInfo& aPrincipalInfo, const Client::Type aClientType) { return MakeRefPtr(std::move(aQuotaManager), aPrincipalInfo, aClientType); } RefPtr> CreateInitializeTemporaryClientOp( MovingNotNull> aQuotaManager, const PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, const Client::Type aClientType) { return MakeRefPtr( std::move(aQuotaManager), aPersistenceType, aPrincipalInfo, aClientType); } RefPtr CreateGetFullOriginMetadataOp( MovingNotNull> aQuotaManager, const GetFullOriginMetadataParams& aParams) { return MakeRefPtr(std::move(aQuotaManager), aParams); } RefPtr> CreateClearStorageOp( MovingNotNull> aQuotaManager) { return MakeRefPtr(std::move(aQuotaManager)); } RefPtr> CreateClearOriginOp( MovingNotNull> aQuotaManager, const Maybe& aPersistenceType, const PrincipalInfo& aPrincipalInfo, const Maybe& aClientType) { return MakeRefPtr(std::move(aQuotaManager), aPersistenceType, aPrincipalInfo, aClientType); } RefPtr> CreateClearStoragesForOriginPrefixOp( MovingNotNull> aQuotaManager, const Maybe& aPersistenceType, const PrincipalInfo& aPrincipalInfo) { return MakeRefPtr( std::move(aQuotaManager), aPersistenceType, aPrincipalInfo); } RefPtr> CreateClearDataOp( MovingNotNull> aQuotaManager, const OriginAttributesPattern& aPattern) { return MakeRefPtr(std::move(aQuotaManager), aPattern); } RefPtr CreateResetOriginOp( MovingNotNull> aQuotaManager, const RequestParams& aParams) { return MakeRefPtr(std::move(aQuotaManager), aParams); } RefPtr CreatePersistedOp( MovingNotNull> aQuotaManager, const RequestParams& aParams) { return MakeRefPtr(std::move(aQuotaManager), aParams); } RefPtr CreatePersistOp( MovingNotNull> aQuotaManager, const RequestParams& aParams) { return MakeRefPtr(std::move(aQuotaManager), aParams); } RefPtr CreateEstimateOp( MovingNotNull> aQuotaManager, const EstimateParams& aParams) { return MakeRefPtr(std::move(aQuotaManager), aParams); } RefPtr CreateListOriginsOp( MovingNotNull> aQuotaManager) { return MakeRefPtr(std::move(aQuotaManager)); } template RefPtr OpenStorageDirectoryHelper::OpenStorageDirectory( const Nullable& aPersistenceType, const OriginScope& aOriginScope, const Nullable& 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 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()); } return NS_OK; } void FinalizeOriginEvictionOp::UnblockOpen() { AssertIsOnOwningThread(); #ifdef DEBUG NoteActorDestroyed(); #endif mLocks.Clear(); } RefPtr SaveOriginAccessTimeOp::OpenDirectory() { AssertIsOnOwningThread(); return OpenStorageDirectory( Nullable(mOriginMetadata.mPersistenceType), OriginScope::FromOrigin(mOriginMetadata.mOrigin), Nullable(), /* 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 ClearPrivateRepositoryOp::OpenDirectory() { AssertIsOnOwningThread(); return OpenStorageDirectory( Nullable(PERSISTENCE_TYPE_PRIVATE), OriginScope::FromNull(), Nullable(), /* 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 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(), OriginScope::FromNull(), Nullable(), /* 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& originDir) -> Result { 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> 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(originUsage->lastAccessed(), aTimestamp); } const Atomic& 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 { return Some(std::move(metadata)); }), // Predicate. IsSpecificError, // Fallback. ErrToDefaultOk>)); 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 GetUsageOp::OpenDirectory() { AssertIsOnOwningThread(); return OpenStorageDirectory(Nullable(), OriginScope::FromNull(), Nullable(), /* 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> 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 GetOriginUsageOp::OpenDirectory() { AssertIsOnOwningThread(); return OpenStorageDirectory( Nullable(), OriginScope::FromOrigin(mPrincipalMetadata.mOrigin), Nullable(), /* 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> aQuotaManager) : QuotaRequestBase(std::move(aQuotaManager), "dom::quota::StorageNameOp") { AssertIsOnOwningThread(); } RefPtr 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> aQuotaManager, const char* aName) : ResolvableNormalOriginOp(std::move(aQuotaManager), aName), mInitialized(false) { AssertIsOnOwningThread(); } RefPtr 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> aQuotaManager, RefPtr aDirectoryLock) : ResolvableNormalOriginOp(std::move(aQuotaManager), "dom::quota::InitOp"), mDirectoryLock(std::move(aDirectoryLock)) { AssertIsOnOwningThread(); MOZ_ASSERT(mDirectoryLock); } RefPtr 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> aQuotaManager, RefPtr aDirectoryLock) : ResolvableNormalOriginOp(std::move(aQuotaManager), "dom::quota::InitTemporaryStorageOp"), mDirectoryLock(std::move(aDirectoryLock)) { AssertIsOnOwningThread(); } RefPtr 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> 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 InitializeOriginRequestBase::OpenDirectory() { AssertIsOnOwningThread(); mDirectoryLock = mQuotaManager->CreateDirectoryLockInternal( Nullable(mPersistenceType), OriginScope::FromOrigin(mPrincipalMetadata.mOrigin), Nullable(), /* aExclusive */ false); return mDirectoryLock->Acquire(); } void InitializeOriginRequestBase::CloseDirectory() { AssertIsOnOwningThread(); mDirectoryLock = nullptr; } InitializePersistentOriginOp::InitializePersistentOriginOp( MovingNotNull> 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> 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> 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 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> 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> 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> 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 GetFullOriginMetadataOp::OpenDirectory() { AssertIsOnOwningThread(); return OpenStorageDirectory( Nullable(mOriginMetadata.mPersistenceType), OriginScope::FromOrigin(mOriginMetadata.mOrigin), Nullable(), /* 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> aQuotaManager) : OpenStorageDirectoryHelper(std::move(aQuotaManager), "dom::quota::ClearStorageOp") { AssertIsOnOwningThread(); } void ClearStorageOp::DeleteFiles(QuotaManager& aQuotaManager) { AssertIsOnIOThread(); nsresult rv = aQuotaManager.AboutToClearOrigins(Nullable(), OriginScope::FromNull(), Nullable()); if (NS_WARN_IF(NS_FAILED(rv))) { return; } auto directoryOrErr = QM_NewLocalFile(aQuotaManager.GetStoragePath()); if (NS_WARN_IF(directoryOrErr.isErr())) { return; } nsCOMPtr 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 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(), OriginScope::FromNull(), Nullable(), /* 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& aClientType) { AssertIsOnIOThread(); DeleteFilesInternal( aQuotaManager, aOriginMetadata.mPersistenceType, OriginScope::FromOrigin(aOriginMetadata.mOrigin), aClientType, [&aQuotaManager, &aOriginMetadata]( const std::function(nsCOMPtr&&)>& aBody) -> Result { 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& aClientType) { AssertIsOnIOThread(); DeleteFilesInternal( aQuotaManager, aPersistenceType, aOriginScope, aClientType, [&aQuotaManager, &aPersistenceType]( const std::function(nsCOMPtr&&)>& aBody) -> Result { 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 void ClearRequestBase::DeleteFilesInternal( QuotaManager& aQuotaManager, PersistenceType aPersistenceType, const OriginScope& aOriginScope, const Nullable& aClientType, const FileCollector& aFileCollector) { AssertIsOnIOThread(); QM_TRY(MOZ_TO_RESULT(aQuotaManager.AboutToClearOrigins( Nullable(aPersistenceType), aOriginScope, aClientType)), QM_VOID); nsTArray> directoriesForRemovalRetry; aQuotaManager.MaybeRecordQuotaManagerShutdownStep( "ClearRequestBase: Starting deleting files"_ns); QM_TRY( aFileCollector([&aClientType, &originScope = aOriginScope, aPersistenceType, &aQuotaManager, &directoriesForRemovalRetry](nsCOMPtr&& file) -> mozilla::Result { 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 { return Some(std::move(metadata)); }), // Predicate. IsSpecificError, // Fallback. ErrToDefaultOk>)); 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>{})) { 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> aQuotaManager, const mozilla::Maybe& aPersistenceType, const PrincipalInfo& aPrincipalInfo, const mozilla::Maybe& aClientType) : ClearRequestBase(std::move(aQuotaManager), "dom::quota::ClearOriginOp"), mPrincipalInfo(aPrincipalInfo), mPersistenceType(aPersistenceType ? Nullable(*aPersistenceType) : Nullable()), mClientType(aClientType ? Nullable(*aClientType) : Nullable()) { AssertIsOnOwningThread(); } nsresult ClearOriginOp::DoInit(QuotaManager& aQuotaManager) { AssertIsOnOwningThread(); QM_TRY_UNWRAP( mPrincipalMetadata, aQuotaManager.GetInfoFromValidatedPrincipalInfo(mPrincipalInfo)); mPrincipalMetadata.AssertInvariants(); return NS_OK; } RefPtr 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> aQuotaManager, const Maybe& aPersistenceType, const PrincipalInfo& aPrincipalInfo) : OpenStorageDirectoryHelper(std::move(aQuotaManager), "dom::quota::ClearStoragesForOriginPrefixOp"), mPrefix( QuotaManager::GetOriginFromValidatedPrincipalInfo(aPrincipalInfo)), mPersistenceType(aPersistenceType ? Nullable(*aPersistenceType) : Nullable()) { AssertIsOnOwningThread(); } RefPtr ClearStoragesForOriginPrefixOp::OpenDirectory() { AssertIsOnOwningThread(); return OpenStorageDirectory(mPersistenceType, OriginScope::FromPrefix(mPrefix), Nullable(), /* 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()); } } else { DeleteFiles(aQuotaManager, mPersistenceType.Value(), OriginScope::FromPrefix(mPrefix), Nullable()); } return NS_OK; } bool ClearStoragesForOriginPrefixOp::GetResolveValue() { AssertIsOnOwningThread(); return true; } void ClearStoragesForOriginPrefixOp::CloseDirectory() { AssertIsOnOwningThread(); mDirectoryLock = nullptr; } ClearDataOp::ClearDataOp(MovingNotNull> aQuotaManager, const OriginAttributesPattern& aPattern) : ClearRequestBase(std::move(aQuotaManager), "dom::quota::ClearDataOp"), mPattern(aPattern) {} RefPtr ClearDataOp::OpenDirectory() { AssertIsOnOwningThread(); return OpenStorageDirectory(Nullable(), OriginScope::FromPattern(mPattern), Nullable(), /* 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()); } return NS_OK; } bool ClearDataOp::GetResolveValue() { AssertIsOnOwningThread(); return true; } void ClearDataOp::CloseDirectory() { AssertIsOnOwningThread(); mDirectoryLock = nullptr; } ResetOriginOp::ResetOriginOp(MovingNotNull> 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 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> 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 PersistRequestBase::OpenDirectory() { AssertIsOnOwningThread(); return OpenStorageDirectory( Nullable(PERSISTENCE_TYPE_DEFAULT), OriginScope::FromOrigin(mPrincipalMetadata.mOrigin), Nullable(), /* aExclusive */ false); } void PersistRequestBase::CloseDirectory() { AssertIsOnOwningThread(); mDirectoryLock = nullptr; } PersistedOp::PersistedOp(MovingNotNull> 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 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> 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> 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 EstimateOp::OpenDirectory() { AssertIsOnOwningThread(); // XXX In theory, we should be locking entire group, not just one origin. return OpenStorageDirectory( Nullable(mOriginMetadata.mPersistenceType), OriginScope::FromOrigin(mOriginMetadata.mOrigin), Nullable(), /* 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> aQuotaManager) : OpenStorageDirectoryHelper(std::move(aQuotaManager), "dom::quota::ListOriginsOp") { AssertIsOnOwningThread(); } RefPtr ListOriginsOp::OpenDirectory() { AssertIsOnOwningThread(); return OpenStorageDirectory(Nullable(), OriginScope::FromNull(), Nullable(), /* 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& 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 { return Some(std::move(metadata)); }), // Predicate. IsSpecificError, // Fallback. ErrToDefaultOk>)); 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& origins = aResponse.get_ListOriginsResponse().origins(); mOrigins.SwapElements(origins); } void ListOriginsOp::CloseDirectory() { AssertIsOnOwningThread(); mDirectoryLock = nullptr; } } // namespace mozilla::dom::quota