From 7783acd7c8d4a875d65c509bad28bb48a0588512 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Sat, 12 Oct 2024 20:58:36 +0000 Subject: [PATCH] Bug 1866402 - Make it possible to initialize temporary origins without ensuring origin directories; r=dom-storage-reviewers,jari LSNG already uses some QuotaManager APIs to achieve that origin directories are not created if they don't exist during datastore preparation, but the feature is not easy to use and it's also not generalized enough for use in other quota clients. Besides that, the way how it's currently done in LSNG complicates removal of QuotaManager::EnsureTemporaryOriginIsInitializedInternal calls from LSNG. This patch is about generalizing of the feature, making it available to all quota clients. Differential Revision: https://phabricator.services.mozilla.com/D195551 --- dom/cache/Context.cpp | 14 +- dom/cache/test/xpcshell/head.js | 8 +- .../datamodel/FileSystemFileManager.cpp | 11 +- .../gtest/parent/FileSystemParentTest.cpp | 5 +- .../test/gtest/parent/FileSystemParentTest.h | 2 +- .../TestFileSystemOriginInitialization.cpp | 3 +- dom/indexedDB/ActorsParent.cpp | 18 +- dom/localstorage/ActorsParent.cpp | 67 +++--- dom/localstorage/test/unit/head.js | 12 +- dom/quota/ActorsParent.cpp | 191 ++++++++++----- dom/quota/OriginInfo.cpp | 7 + dom/quota/OriginInfo.h | 11 +- dom/quota/OriginOperations.cpp | 149 ++++++++++-- dom/quota/OriginOperations.h | 2 +- dom/quota/PQuota.ipdl | 3 +- dom/quota/QuotaManager.h | 58 +++-- dom/quota/QuotaManagerImpl.h | 15 +- dom/quota/QuotaManagerService.cpp | 5 +- dom/quota/QuotaParent.cpp | 6 +- dom/quota/QuotaParent.h | 2 +- dom/quota/nsIQuotaManagerService.idl | 7 +- .../gtest/QuotaManagerDependencyFixture.cpp | 9 +- .../gtest/QuotaManagerDependencyFixture.h | 3 +- dom/quota/test/gtest/TestFileOutputStream.cpp | 5 +- dom/quota/test/gtest/TestQuotaManager.cpp | 221 +++++++++++++++++- .../test/gtest/TestStorageConnection.cpp | 14 +- dom/quota/test/marionette/quota_test_case.py | 7 +- dom/quota/test/xpcshell/common/head.js | 10 +- .../test_qm_first_initialization_attempt.js | 24 +- dom/quota/test/xpcshell/test_persist.js | 7 +- .../xpcshell/test_quotaClientInteractions.js | 76 ++++++ .../test/xpcshell/test_unaccessedOrigins.js | 6 +- dom/quota/test/xpcshell/test_validOrigins.js | 6 +- .../test_upgradeFromFlatOriginDirectories.js | 1 + .../test_upgradeFromIndexedDBDirectory.js | 1 + ...t_upgradeFromPersistentStorageDirectory.js | 1 + ...orageDirectory_upgradeOriginDirectories.js | 1 + .../upgrades/test_upgradeStorageFrom0_0.js | 1 + ...geFrom1_0_stripObsoleteOriginAttributes.js | 1 + dom/quota/test/xpcshell/xpcshell.toml | 2 + dom/simpledb/ActorsParent.cpp | 8 +- 41 files changed, 790 insertions(+), 210 deletions(-) create mode 100644 dom/quota/test/xpcshell/test_quotaClientInteractions.js diff --git a/dom/cache/Context.cpp b/dom/cache/Context.cpp index 5f3acc415d11..31e254bf1b5c 100644 --- a/dom/cache/Context.cpp +++ b/dom/cache/Context.cpp @@ -406,11 +406,15 @@ Context::QuotaInitRunnable::Run() { QuotaManager* quotaManager = QuotaManager::Get(); MOZ_DIAGNOSTIC_ASSERT(quotaManager); - QM_TRY_UNWRAP(mDirectoryMetadata->mDir, - quotaManager - ->EnsureTemporaryOriginIsInitializedInternal( - *mDirectoryMetadata) - .map([](const auto& res) { return res.first; })); + QM_TRY_UNWRAP( + mDirectoryMetadata->mDir, + quotaManager + ->EnsureTemporaryOriginIsInitializedInternal( + *mDirectoryMetadata, /* aCreateIfNonExistent */ true) + .map([](const auto& res) { return res.first; })); + + QM_TRY(quotaManager->EnsureTemporaryOriginDirectoryCreated( + *mDirectoryMetadata)); auto* cacheQuotaClient = CacheQuotaClient::Get(); MOZ_DIAGNOSTIC_ASSERT(cacheQuotaClient); diff --git a/dom/cache/test/xpcshell/head.js b/dom/cache/test/xpcshell/head.js index 557ded64ce99..580f675a4214 100644 --- a/dom/cache/test/xpcshell/head.js +++ b/dom/cache/test/xpcshell/head.js @@ -76,8 +76,12 @@ function initPersistentOrigin(principal) { return Services.qms.initializePersistentOrigin(principal); } -function initTemporaryOrigin(principal) { - return Services.qms.initializeTemporaryOrigin("default", principal); +function initTemporaryOrigin(principal, createIfNonExistent = true) { + return Services.qms.initializeTemporaryOrigin( + "default", + principal, + createIfNonExistent + ); } function clearOrigin(principal, persistence) { diff --git a/dom/fs/parent/datamodel/FileSystemFileManager.cpp b/dom/fs/parent/datamodel/FileSystemFileManager.cpp index c3403f0d5ac3..a49bf00c058a 100644 --- a/dom/fs/parent/datamodel/FileSystemFileManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemFileManager.cpp @@ -170,10 +170,13 @@ nsresult EnsureFileSystemDirectory( quota::QuotaManager* quotaManager = quota::QuotaManager::Get(); MOZ_ASSERT(quotaManager); - QM_TRY_INSPECT( - const auto& fileSystemDirectory, - quotaManager->EnsureTemporaryOriginIsInitializedInternal(aOriginMetadata) - .map([](const auto& aPair) { return aPair.first; })); + QM_TRY_INSPECT(const auto& fileSystemDirectory, + quotaManager + ->EnsureTemporaryOriginIsInitializedInternal( + aOriginMetadata, /* aCreateIfNonExistent */ true) + .map([](const auto& aPair) { return aPair.first; })); + + QM_TRY(quotaManager->EnsureTemporaryOriginDirectoryCreated(aOriginMetadata)); QM_TRY(QM_TO_RESULT(fileSystemDirectory->AppendRelativePath( NS_LITERAL_STRING_FROM_CSTRING(FILESYSTEM_DIRECTORY_NAME)))); diff --git a/dom/fs/test/gtest/parent/FileSystemParentTest.cpp b/dom/fs/test/gtest/parent/FileSystemParentTest.cpp index 67ff39e9a6cf..624fc3872c2a 100644 --- a/dom/fs/test/gtest/parent/FileSystemParentTest.cpp +++ b/dom/fs/test/gtest/parent/FileSystemParentTest.cpp @@ -36,10 +36,11 @@ void FileSystemParentTest::TearDown() { } // static -void FileSystemParentTest::InitializeTemporaryOrigin() { +void FileSystemParentTest::InitializeTemporaryOrigin( + bool aCreateIfNonExistent) { ASSERT_NO_FATAL_FAILURE( QuotaManagerDependencyFixture::InitializeTemporaryOrigin( - GetTestOriginMetadata())); + GetTestOriginMetadata(), aCreateIfNonExistent)); } // static diff --git a/dom/fs/test/gtest/parent/FileSystemParentTest.h b/dom/fs/test/gtest/parent/FileSystemParentTest.h index ca53706e9cd4..ae3bc0982bba 100644 --- a/dom/fs/test/gtest/parent/FileSystemParentTest.h +++ b/dom/fs/test/gtest/parent/FileSystemParentTest.h @@ -46,7 +46,7 @@ class FileSystemParentTest : public quota::test::QuotaManagerDependencyFixture { void TearDown() override; - static void InitializeTemporaryOrigin(); + static void InitializeTemporaryOrigin(bool aCreateIfNonExistent = true); static void GetOriginUsage(quota::UsageInfo& aResult); diff --git a/dom/fs/test/gtest/parent/TestFileSystemOriginInitialization.cpp b/dom/fs/test/gtest/parent/TestFileSystemOriginInitialization.cpp index 858075b67da4..f867086bc86d 100644 --- a/dom/fs/test/gtest/parent/TestFileSystemOriginInitialization.cpp +++ b/dom/fs/test/gtest/parent/TestFileSystemOriginInitialization.cpp @@ -131,7 +131,8 @@ TEST_F(TestFileSystemOriginInitialization, EmptyOriginDirectory) { // Initialize origin ASSERT_NO_FATAL_FAILURE(InitializeStorage()); ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage()); - ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin()); + ASSERT_NO_FATAL_FAILURE( + InitializeTemporaryOrigin(/* aCreateIfNonExistent */ true)); // After initialization, // * origin usage is nothing diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 5cd7cd9493d8..41aa279eda18 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -15263,8 +15263,14 @@ nsresult OpenDatabaseOp::DoDatabaseWork() { mOriginMetadata)); } - QM_TRY_RETURN(quotaManager->EnsureTemporaryOriginIsInitializedInternal( + QM_TRY_UNWRAP(auto dbDirectory, + quotaManager->EnsureTemporaryOriginIsInitializedInternal( + mOriginMetadata, /* aCreateIfNonExistent */ true)); + + QM_TRY(quotaManager->EnsureTemporaryOriginDirectoryCreated( mOriginMetadata)); + + return std::move(dbDirectory); }() .map([](const auto& res) { return res.first; }))); @@ -16849,8 +16855,14 @@ nsresult GetDatabasesOp::DoDatabaseWork() { mOriginMetadata)); } - QM_TRY_RETURN(quotaManager->EnsureTemporaryOriginIsInitializedInternal( - mOriginMetadata)); + QM_TRY_UNWRAP(auto dbDirectory, + quotaManager->EnsureTemporaryOriginIsInitializedInternal( + mOriginMetadata, /* aCreateIfNonExistent */ true)); + + QM_TRY( + quotaManager->EnsureTemporaryOriginDirectoryCreated(mOriginMetadata)); + + return std::move(dbDirectory); }() .map([](const auto& res) { return Ok{}; }))); diff --git a/dom/localstorage/ActorsParent.cpp b/dom/localstorage/ActorsParent.cpp index fdbc7b865639..4f6ab907b322 100644 --- a/dom/localstorage/ActorsParent.cpp +++ b/dom/localstorage/ActorsParent.cpp @@ -1245,7 +1245,7 @@ class ConnectionDatastoreOperationBase : public DatastoreOperationBase { class Connection final : public CachingDatabaseConnection { friend class ConnectionThread; - class InitTemporaryOriginHelper; + class GetOrCreateTemporaryOriginDirectoryHelper; class FlushOp; class CloseOp; @@ -1360,16 +1360,17 @@ class Connection final : public CachingDatabaseConnection { }; /** - * Helper to invoke EnsureTemporaryOriginIsInitializedInternal on the - * QuotaManager IO thread from the LocalStorage connection thread when creating - * a database connection on demand. This is necessary because we attempt to - * defer the creation of the origin directory and the database until absolutely - * needed, but the directory creation and origin initialization must happen on - * the QM IO thread for invariant reasons. (We can't just use a mutex because - * there could be logic on the IO thread that also wants to deal with the same - * origin, so we need to queue a runnable and wait our turn.) + * Helper to invoke GetOrCreateTemporaryOriginDirectory on the QuotaManager IO + * thread from the LocalStorage connection thread when creating a database + * connection on demand. This is necessary because we attempt to defer the + * creation of the origin directory and the database until absolutely needed, + * but the directory creation must happen on the QM IO thread for invariant + * reasons. (We can't just use a mutex because there could be logic on the IO + * thread that also wants to deal with the same origin, so we need to queue a + * runnable and wait our turn.) */ -class Connection::InitTemporaryOriginHelper final : public Runnable { +class Connection::GetOrCreateTemporaryOriginDirectoryHelper final + : public Runnable { mozilla::Monitor mMonitor MOZ_UNANNOTATED; const OriginMetadata mOriginMetadata; nsString mOriginDirectoryPath; @@ -1377,9 +1378,12 @@ class Connection::InitTemporaryOriginHelper final : public Runnable { bool mWaiting; public: - explicit InitTemporaryOriginHelper(const OriginMetadata& aOriginMetadata) - : Runnable("dom::localstorage::Connection::InitTemporaryOriginHelper"), - mMonitor("InitTemporaryOriginHelper::mMonitor"), + explicit GetOrCreateTemporaryOriginDirectoryHelper( + const OriginMetadata& aOriginMetadata) + : Runnable( + "dom::localstorage::Connection::" + "GetOrCreateTemporaryOriginDirectoryHelper"), + mMonitor("GetOrCreateTemporaryOriginDirectoryHelper::mMonitor"), mOriginMetadata(aOriginMetadata), mIOThreadResultCode(NS_OK), mWaiting(true) { @@ -1389,7 +1393,7 @@ class Connection::InitTemporaryOriginHelper final : public Runnable { Result BlockAndReturnOriginDirectoryPath(); private: - ~InitTemporaryOriginHelper() = default; + ~GetOrCreateTemporaryOriginDirectoryHelper() = default; nsresult RunOnIOThread(); @@ -4069,8 +4073,8 @@ nsresult Connection::EnsureStorageConnection() { return NS_OK; } - RefPtr helper = - new InitTemporaryOriginHelper(mOriginMetadata); + auto helper = + MakeRefPtr(mOriginMetadata); QM_TRY_INSPECT(const auto& originDirectoryPath, helper->BlockAndReturnOriginDirectoryPath()); @@ -4223,7 +4227,8 @@ void Connection::FlushTimerCallback(nsITimer* aTimer, void* aClosure) { } Result -Connection::InitTemporaryOriginHelper::BlockAndReturnOriginDirectoryPath() { +Connection::GetOrCreateTemporaryOriginDirectoryHelper:: + BlockAndReturnOriginDirectoryPath() { AssertIsOnGlobalConnectionThread(); QuotaManager* quotaManager = QuotaManager::Get(); @@ -4242,7 +4247,8 @@ Connection::InitTemporaryOriginHelper::BlockAndReturnOriginDirectoryPath() { return mOriginDirectoryPath; } -nsresult Connection::InitTemporaryOriginHelper::RunOnIOThread() { +nsresult +Connection::GetOrCreateTemporaryOriginDirectoryHelper::RunOnIOThread() { AssertIsOnIOThread(); QuotaManager* quotaManager = QuotaManager::Get(); @@ -4250,8 +4256,7 @@ nsresult Connection::InitTemporaryOriginHelper::RunOnIOThread() { QM_TRY_INSPECT( const auto& directoryEntry, - quotaManager->EnsureTemporaryOriginIsInitializedInternal(mOriginMetadata) - .map([](const auto& res) { return res.first; })); + quotaManager->GetOrCreateTemporaryOriginDirectory(mOriginMetadata)); QM_TRY(MOZ_TO_RESULT(directoryEntry->GetPath(mOriginDirectoryPath))); @@ -4259,7 +4264,7 @@ nsresult Connection::InitTemporaryOriginHelper::RunOnIOThread() { } NS_IMETHODIMP -Connection::InitTemporaryOriginHelper::Run() { +Connection::GetOrCreateTemporaryOriginDirectoryHelper::Run() { AssertIsOnIOThread(); nsresult rv = RunOnIOThread(); @@ -7010,21 +7015,21 @@ nsresult PrepareDatastoreOp::DatabaseWork() { ([hasDataForMigration, "aManager, this]() -> mozilla::Result, nsresult> { if (hasDataForMigration) { - QM_TRY_RETURN(quotaManager - ->EnsureTemporaryOriginIsInitializedInternal( - mOriginMetadata) - .map([](const auto& res) { return res.first; })); + QM_TRY_RETURN( + quotaManager + ->EnsureTemporaryOriginIsInitializedInternal( + mOriginMetadata, /* aCreateIfNonExistent*/ true) + .map([](const auto& res) { return res.first; })); } MOZ_ASSERT(mOriginMetadata.mPersistenceType == PERSISTENCE_TYPE_DEFAULT); - QM_TRY_UNWRAP(auto directoryEntry, - quotaManager->GetOriginDirectory(mOriginMetadata)); - - quotaManager->EnsureQuotaForOrigin(mOriginMetadata); - - return directoryEntry; + QM_TRY_RETURN( + quotaManager + ->EnsureTemporaryOriginIsInitializedInternal( + mOriginMetadata, /* aCreateIfNonExistent*/ false) + .map([](const auto& res) { return res.first; })); }())); QM_TRY(MOZ_TO_RESULT(directoryEntry->Append( diff --git a/dom/localstorage/test/unit/head.js b/dom/localstorage/test/unit/head.js index d53f23956ada..896745b2ac88 100644 --- a/dom/localstorage/test/unit/head.js +++ b/dom/localstorage/test/unit/head.js @@ -102,8 +102,16 @@ function initPersistentOrigin(principal) { return Services.qms.initializePersistentOrigin(principal); } -function initTemporaryOrigin(persistence, principal) { - return Services.qms.initializeTemporaryOrigin(persistence, principal); +function initTemporaryOrigin( + persistence, + principal, + createIfNonExistent = true +) { + return Services.qms.initializeTemporaryOrigin( + persistence, + principal, + createIfNonExistent + ); } function getOriginUsage(principal) { diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index ef36d0b507fd..3f0b3be67626 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -98,6 +98,7 @@ #include "mozilla/dom/quota/FileUtils.h" #include "mozilla/dom/quota/MozPromiseUtils.h" #include "mozilla/dom/quota/PersistenceType.h" +#include "mozilla/dom/quota/QuotaManagerImpl.h" #include "mozilla/dom/quota/QuotaManagerService.h" #include "mozilla/dom/quota/ResultExtensions.h" #include "mozilla/dom/quota/ScopedLogExtraInfo.h" @@ -2434,7 +2435,8 @@ void QuotaManager::Shutdown() { void QuotaManager::InitQuotaForOrigin( const FullOriginMetadata& aFullOriginMetadata, - const ClientUsageArray& aClientUsages, uint64_t aUsageBytes) { + const ClientUsageArray& aClientUsages, uint64_t aUsageBytes, + bool aDirectoryExists) { AssertIsOnIOThread(); MOZ_ASSERT(IsBestEffortPersistenceType(aFullOriginMetadata.mPersistenceType)); @@ -2448,61 +2450,7 @@ void QuotaManager::InitQuotaForOrigin( groupInfo, aFullOriginMetadata.mOrigin, aFullOriginMetadata.mStorageOrigin, aFullOriginMetadata.mIsPrivate, aClientUsages, aUsageBytes, aFullOriginMetadata.mLastAccessTime, - aFullOriginMetadata.mPersisted, - /* aDirectoryExists */ true)); -} - -void QuotaManager::EnsureQuotaForOrigin(const OriginMetadata& aOriginMetadata) { - AssertIsOnIOThread(); - MOZ_ASSERT(IsBestEffortPersistenceType(aOriginMetadata.mPersistenceType)); - - MutexAutoLock lock(mQuotaMutex); - - RefPtr groupInfo = LockedGetOrCreateGroupInfo( - aOriginMetadata.mPersistenceType, aOriginMetadata.mSuffix, - aOriginMetadata.mGroup); - - RefPtr originInfo = - groupInfo->LockedGetOriginInfo(aOriginMetadata.mOrigin); - if (!originInfo) { - groupInfo->LockedAddOriginInfo(MakeNotNull>( - groupInfo, aOriginMetadata.mOrigin, aOriginMetadata.mStorageOrigin, - aOriginMetadata.mIsPrivate, ClientUsageArray(), - /* aUsageBytes */ 0, - /* aAccessTime */ PR_Now(), /* aPersisted */ false, - /* aDirectoryExists */ false)); - } -} - -int64_t QuotaManager::NoteOriginDirectoryCreated( - const OriginMetadata& aOriginMetadata) { - AssertIsOnIOThread(); - MOZ_ASSERT(IsBestEffortPersistenceType(aOriginMetadata.mPersistenceType)); - - int64_t timestamp; - - MutexAutoLock lock(mQuotaMutex); - - RefPtr groupInfo = LockedGetOrCreateGroupInfo( - aOriginMetadata.mPersistenceType, aOriginMetadata.mSuffix, - aOriginMetadata.mGroup); - - RefPtr originInfo = - groupInfo->LockedGetOriginInfo(aOriginMetadata.mOrigin); - if (originInfo) { - timestamp = originInfo->LockedAccessTime(); - originInfo->mDirectoryExists = true; - } else { - timestamp = PR_Now(); - groupInfo->LockedAddOriginInfo(MakeNotNull>( - groupInfo, aOriginMetadata.mOrigin, aOriginMetadata.mStorageOrigin, - aOriginMetadata.mIsPrivate, ClientUsageArray(), - /* aUsageBytes */ 0, /* aAccessTime */ timestamp, - /* aPersisted */ false, - /* aDirectoryExists */ true)); - } - - return timestamp; + aFullOriginMetadata.mPersisted, aDirectoryExists)); } void QuotaManager::DecreaseUsageForClient(const ClientMetadata& aClientMetadata, @@ -2954,7 +2902,7 @@ void QuotaManager::UnloadQuota() { for (const auto& originInfo : groupInfo->mOriginInfos) { MOZ_ASSERT(!originInfo->mCanonicalQuotaObjects.Count()); - if (!originInfo->mDirectoryExists) { + if (!originInfo->LockedDirectoryExists()) { continue; } @@ -3006,6 +2954,43 @@ void QuotaManager::UnloadQuota() { QM_TRY(MOZ_TO_RESULT(transaction.Commit()), QM_VOID); } +void QuotaManager::RemoveOriginFromCache( + const OriginMetadata& aOriginMetadata) { + AssertIsOnIOThread(); + MOZ_ASSERT(mStorageConnection); + MOZ_ASSERT(!mTemporaryStorageInitializedInternal); + + if (!mCacheUsable) { + return; + } + + mozStorageTransaction transaction( + mStorageConnection, false, mozIStorageConnection::TRANSACTION_IMMEDIATE); + + QM_TRY_INSPECT( + const auto& stmt, + MOZ_TO_RESULT_INVOKE_MEMBER_TYPED( + nsCOMPtr, mStorageConnection, CreateStatement, + "DELETE FROM origin WHERE repository_id = :repository_id AND suffix = :suffix AND group_ = :group AND origin = :origin;"_ns), + QM_VOID); + + QM_TRY(MOZ_TO_RESULT(stmt->BindInt32ByName("repository_id"_ns, + aOriginMetadata.mPersistenceType)), + QM_VOID); + QM_TRY(MOZ_TO_RESULT( + stmt->BindUTF8StringByName("suffix"_ns, aOriginMetadata.mSuffix)), + QM_VOID); + QM_TRY(MOZ_TO_RESULT( + stmt->BindUTF8StringByName("group"_ns, aOriginMetadata.mGroup)), + QM_VOID); + QM_TRY(MOZ_TO_RESULT( + stmt->BindUTF8StringByName("origin"_ns, aOriginMetadata.mOrigin)), + QM_VOID); + QM_TRY(MOZ_TO_RESULT(stmt->Execute()), QM_VOID); + + QM_TRY(MOZ_TO_RESULT(transaction.Commit()), QM_VOID); +} + already_AddRefed QuotaManager::GetQuotaObject( PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata, Client::Type aClientType, nsIFile* aFile, int64_t aFileSize, @@ -3220,6 +3205,49 @@ Result QuotaManager::DoesOriginDirectoryExist( QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists)); } +Result, nsresult> +QuotaManager::GetOrCreateTemporaryOriginDirectory( + const OriginMetadata& aOriginMetadata) { + AssertIsOnIOThread(); + MOZ_ASSERT(aOriginMetadata.mPersistenceType != PERSISTENCE_TYPE_PERSISTENT); + MOZ_DIAGNOSTIC_ASSERT(IsStorageInitializedInternal()); + MOZ_DIAGNOSTIC_ASSERT(IsTemporaryStorageInitializedInternal()); + MOZ_DIAGNOSTIC_ASSERT(IsTemporaryOriginInitializedInternal(aOriginMetadata)); + + QM_TRY_UNWRAP(auto directory, GetOriginDirectory(aOriginMetadata)); + + QM_TRY_INSPECT(const bool& created, EnsureOriginDirectory(*directory)); + + if (created) { + // A new origin directory has been created. + + // We have a temporary origin which has been initialized without ensuring + // respective origin directory. So OriginInfo already exists and it needs + // to be updated because the origin directory has been just created. + + auto [timestamp, persisted] = + WithOriginInfo(aOriginMetadata, [](const auto& originInfo) { + const int64_t timestamp = originInfo->LockedAccessTime(); + const bool persisted = originInfo->LockedPersisted(); + + originInfo->LockedDirectoryCreated(); + + return std::make_pair(timestamp, persisted); + }); + + QM_TRY(MOZ_TO_RESULT(CreateDirectoryMetadata2(*directory, timestamp, + persisted, aOriginMetadata))); + } + + return std::move(directory); +} + +Result QuotaManager::EnsureTemporaryOriginDirectoryCreated( + const OriginMetadata& aOriginMetadata) { + QM_TRY_RETURN(GetOrCreateTemporaryOriginDirectory(aOriginMetadata) + .map([](const auto& res) { return Ok{}; })); +} + // static nsresult QuotaManager::CreateDirectoryMetadata( nsIFile& aDirectory, int64_t aTimestamp, @@ -5373,6 +5401,16 @@ RefPtr QuotaManager::CreateDirectoryLockInternal( aClientType, aExclusive, aCategory); } +bool QuotaManager::IsPendingOrigin( + const OriginMetadata& aOriginMetadata) const { + MutexAutoLock lock(mQuotaMutex); + + RefPtr originInfo = + LockedGetOriginInfo(aOriginMetadata.mPersistenceType, aOriginMetadata); + + return originInfo && !originInfo->LockedDirectoryExists(); +} + RefPtr QuotaManager::InitializePersistentOrigin( const PrincipalInfo& aPrincipalInfo) { AssertIsOnOwningThread(); @@ -5544,7 +5582,8 @@ QuotaManager::EnsurePersistentOriginIsInitializedInternal( } RefPtr QuotaManager::InitializeTemporaryOrigin( - PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo) { + PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, + bool aCreateIfNonExistent) { AssertIsOnOwningThread(); QM_TRY_UNWRAP(PrincipalMetadata principalMetadata, @@ -5571,19 +5610,21 @@ RefPtr QuotaManager::InitializeTemporaryOrigin( return directoryLock->Acquire()->Then( GetCurrentSerialEventTarget(), __func__, [self = RefPtr(this), aPersistenceType, aPrincipalInfo, + aCreateIfNonExistent, directoryLock](const BoolPromise::ResolveOrRejectValue& aValue) mutable { if (aValue.IsReject()) { return BoolPromise::CreateAndReject(aValue.RejectValue(), __func__); } return self->InitializeTemporaryOrigin(aPersistenceType, aPrincipalInfo, + aCreateIfNonExistent, std::move(directoryLock)); }); } RefPtr QuotaManager::InitializeTemporaryOrigin( PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, - RefPtr aDirectoryLock) { + bool aCreateIfNonExistent, RefPtr aDirectoryLock) { AssertIsOnOwningThread(); MOZ_ASSERT(aDirectoryLock); MOZ_ASSERT(aDirectoryLock->Acquired()); @@ -5603,7 +5644,7 @@ RefPtr QuotaManager::InitializeTemporaryOrigin( auto initializeTemporaryOriginOp = CreateInitializeTemporaryOriginOp( WrapMovingNotNullUnchecked(this), aPersistenceType, aPrincipalInfo, - std::move(aDirectoryLock)); + aCreateIfNonExistent, std::move(aDirectoryLock)); RegisterNormalOriginOp(*initializeTemporaryOriginOp); @@ -5658,26 +5699,47 @@ bool QuotaManager::IsTemporaryOriginInitializedInternal( Result, bool>, nsresult> QuotaManager::EnsureTemporaryOriginIsInitializedInternal( - const OriginMetadata& aOriginMetadata) { + const OriginMetadata& aOriginMetadata, bool aCreateIfNonExistent) { AssertIsOnIOThread(); MOZ_ASSERT(aOriginMetadata.mPersistenceType != PERSISTENCE_TYPE_PERSISTENT); MOZ_DIAGNOSTIC_ASSERT(mStorageConnection); MOZ_DIAGNOSTIC_ASSERT(mTemporaryStorageInitializedInternal); - const auto innerFunc = [&aOriginMetadata, this](const auto&) + const auto innerFunc = [&aOriginMetadata, aCreateIfNonExistent, + this](const auto&) -> mozilla::Result, bool>, nsresult> { // Get directory for this origin and persistence type. QM_TRY_UNWRAP(auto directory, GetOriginDirectory(aOriginMetadata)); + if (IsTemporaryOriginInitializedInternal(aOriginMetadata)) { + return std::pair(std::move(directory), false); + } + + if (!aCreateIfNonExistent) { + const int64_t timestamp = PR_Now(); + + InitQuotaForOrigin(FullOriginMetadata{aOriginMetadata, + /* aPersisted */ false, timestamp}, + ClientUsageArray(), /* aUsageBytes */ 0, + /* aDirectoryExists */ false); + + return std::pair(std::move(directory), false); + } + QM_TRY_INSPECT(const bool& created, EnsureOriginDirectory(*directory)); if (created) { - const int64_t timestamp = NoteOriginDirectoryCreated(aOriginMetadata); + const int64_t timestamp = PR_Now(); // Only creating .metadata-v2 to reduce IO. QM_TRY(MOZ_TO_RESULT(CreateDirectoryMetadata2(*directory, timestamp, /* aPersisted */ false, aOriginMetadata))); + + // Don't need to traverse the directory, since it's empty. + InitQuotaForOrigin(FullOriginMetadata{aOriginMetadata, + /* aPersisted */ false, timestamp}, + ClientUsageArray(), /* aUsageBytes */ 0); } // TODO: If the metadata file exists and we didn't call @@ -5758,7 +5820,8 @@ QuotaManager::EnsureTemporaryClientIsInitialized( MOZ_DIAGNOSTIC_ASSERT(IsTemporaryStorageInitializedInternal()); MOZ_DIAGNOSTIC_ASSERT(IsTemporaryOriginInitializedInternal(aClientMetadata)); - QM_TRY_UNWRAP(auto directory, GetOriginDirectory(aClientMetadata)); + QM_TRY_UNWRAP(auto directory, + GetOrCreateTemporaryOriginDirectory(aClientMetadata)); QM_TRY(MOZ_TO_RESULT( directory->Append(Client::TypeToString(aClientMetadata.mClientType)))); diff --git a/dom/quota/OriginInfo.cpp b/dom/quota/OriginInfo.cpp index 89da63a3f75c..98d16be04c92 100644 --- a/dom/quota/OriginInfo.cpp +++ b/dom/quota/OriginInfo.cpp @@ -173,4 +173,11 @@ void OriginInfo::LockedPersist() { mGroupInfo->mUsage -= mUsage; } +void OriginInfo::LockedDirectoryCreated() { + AssertCurrentThreadOwnsQuotaMutex(); + MOZ_ASSERT(!mDirectoryExists); + + mDirectoryExists = true; +} + } // namespace mozilla::dom::quota diff --git a/dom/quota/OriginInfo.h b/dom/quota/OriginInfo.h index c1376abd5b44..cbb6793fd52c 100644 --- a/dom/quota/OriginInfo.h +++ b/dom/quota/OriginInfo.h @@ -19,6 +19,7 @@ class GroupInfo; class OriginInfo final { friend class CanonicalQuotaObject; friend class GroupInfo; + friend class PersistOp; friend class QuotaManager; public: @@ -65,6 +66,12 @@ class OriginInfo final { bool IsExtensionOrigin() const { return mIsExtension; } + bool LockedDirectoryExists() const { + AssertCurrentThreadOwnsQuotaMutex(); + + return mDirectoryExists; + } + OriginMetadata FlattenToOriginMetadata() const; FullOriginMetadata LockedFlattenToFullOriginMetadata() const; @@ -96,6 +103,8 @@ class OriginInfo final { void LockedPersist(); + void LockedDirectoryCreated(); + nsTHashMap> mCanonicalQuotaObjects; ClientUsageArray mClientUsages; @@ -114,7 +123,7 @@ class OriginInfo final { * want to be able to track quota for an origin without creating its origin * directory or the per-client files until they are actually needed to store * data. In those cases, the OriginInfo will be created by - * EnsureQuotaForOrigin and the resulting mDirectoryExists will be false until + * InitQuotaForOrigin and the resulting mDirectoryExists will be false until * the origin actually needs to be created. It is possible for mUsage to be * greater than zero while mDirectoryExists is false, representing a state * where a client like LocalStorage has reserved quota for disk writes, but diff --git a/dom/quota/OriginOperations.cpp b/dom/quota/OriginOperations.cpp index 4504a55acca5..20ce22e1b68e 100644 --- a/dom/quota/OriginOperations.cpp +++ b/dom/quota/OriginOperations.cpp @@ -476,11 +476,13 @@ class InitializePersistentOriginOp final : public InitializeOriginRequestBase { class InitializeTemporaryOriginOp final : public InitializeOriginRequestBase { const PersistenceType mPersistenceType; + const bool mCreateIfNonExistent; public: InitializeTemporaryOriginOp(MovingNotNull> aQuotaManager, PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, + bool aCreateIfNonExistent, RefPtr aDirectoryLock); private: @@ -984,10 +986,10 @@ RefPtr> CreateInitializePersistentOriginOp( RefPtr> CreateInitializeTemporaryOriginOp( MovingNotNull> aQuotaManager, const PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, - RefPtr aDirectoryLock) { + bool aCreateIfNonExistent, RefPtr aDirectoryLock) { return MakeRefPtr( std::move(aQuotaManager), aPersistenceType, aPrincipalInfo, - std::move(aDirectoryLock)); + aCreateIfNonExistent, std::move(aDirectoryLock)); } RefPtr> CreateInitializePersistentClientOp( @@ -1986,11 +1988,12 @@ bool InitializePersistentOriginOp::GetResolveValue() { InitializeTemporaryOriginOp::InitializeTemporaryOriginOp( MovingNotNull> aQuotaManager, PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, - RefPtr aDirectoryLock) + bool aCreateIfNonExistent, RefPtr aDirectoryLock) : InitializeOriginRequestBase(std::move(aQuotaManager), "dom::quota::InitializeTemporaryOriginOp", aPrincipalInfo, std::move(aDirectoryLock)), - mPersistenceType(aPersistenceType) { + mPersistenceType(aPersistenceType), + mCreateIfNonExistent(aCreateIfNonExistent) { AssertIsOnOwningThread(); } @@ -2009,7 +2012,8 @@ nsresult InitializeTemporaryOriginOp::DoDirectoryWork( QM_TRY_UNWRAP(mCreated, (aQuotaManager .EnsureTemporaryOriginIsInitializedInternal( - OriginMetadata{mPrincipalMetadata, mPersistenceType}) + OriginMetadata{mPrincipalMetadata, mPersistenceType}, + mCreateIfNonExistent) .map([](const auto& res) { return res.second; }))); return NS_OK; @@ -2371,17 +2375,14 @@ void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager, aQuotaManager, aOriginMetadata.mPersistenceType, OriginScope::FromOrigin(aOriginMetadata.mOrigin), [&aQuotaManager, &aOriginMetadata]( - const std::function(nsCOMPtr&&)>& aBody) + 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{}; - } + // We're not checking if the origin directory actualy exists because + // it can be a pending origin (OriginInfo does exist but the origin + // directory hasn't been created yet). QM_TRY_RETURN(aBody(std::move(directory))); }); @@ -2395,7 +2396,7 @@ void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager, DeleteFilesInternal( aQuotaManager, aPersistenceType, aOriginScope, [&aQuotaManager, &aPersistenceType]( - const std::function(nsCOMPtr&&)>& aBody) + const std::function(nsCOMPtr)>& aBody) -> Result { QM_TRY_INSPECT( const auto& directory, @@ -2408,7 +2409,38 @@ void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager, return Ok{}; } - QM_TRY_RETURN(CollectEachFile(*directory, aBody)); + QM_TRY(CollectEachFile(*directory, aBody)); + + // CollectEachFile 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. + + nsTArray originMetadataArray; + aQuotaManager.CollectPendingOriginsForListing( + [aPersistenceType, &originMetadataArray](const auto& originInfo) { + if (originInfo->GetGroupInfo()->GetPersistenceType() != + aPersistenceType) { + return; + } + originMetadataArray.AppendElement( + originInfo->FlattenToOriginMetadata()); + }); + + if (originMetadataArray.IsEmpty()) { + return Ok{}; + } + + nsTArray> originDirectories; + QM_TRY(TransformAbortOnErr( + originMetadataArray, MakeBackInserter(originDirectories), + [&aQuotaManager](const auto& originMetadata) + -> Result, nsresult> { + QM_TRY_UNWRAP(auto originDirectory, + aQuotaManager.GetOriginDirectory(originMetadata)); + return originDirectory; + })); + + QM_TRY_RETURN(CollectEachInRange(originDirectories, aBody)); }); } @@ -2431,7 +2463,7 @@ void ClearRequestBase::DeleteFilesInternal( QM_TRY( aFileCollector([&originScope = aOriginScope, aPersistenceType, &aQuotaManager, &directoriesForRemovalRetry, - this](nsCOMPtr&& file) + this](nsCOMPtr file) -> mozilla::Result { QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*file)); @@ -2486,8 +2518,10 @@ void ClearRequestBase::DeleteFilesInternal( : aQuotaManager.IsTemporaryStorageInitializedInternal(); // If it hasn't been initialized, we don't need to update the - // quota and notify the removing client. + // quota and notify the removing client, but we do need to remove + // it from quota info cache. if (!initialized) { + aQuotaManager.RemoveOriginFromCache(metadata); break; } @@ -2511,9 +2545,34 @@ void ClearRequestBase::DeleteFilesInternal( break; } - case nsIFileKind::DoesNotExist: - // Ignore files that got removed externally while iterating. + case nsIFileKind::DoesNotExist: { + if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + break; + } + + QM_TRY_UNWRAP(auto metadata, aQuotaManager.GetOriginMetadata(file)); + + 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 (!aQuotaManager.IsPendingOrigin(metadata)) { + break; + } + + mOriginMetadataArray.AppendElement(metadata); + + aQuotaManager.RemoveQuotaForOrigin(aPersistenceType, metadata); + + aQuotaManager.OriginClearCompleted( + aPersistenceType, metadata.mOrigin, Nullable()); + break; + } } mIterations++; @@ -2885,6 +2944,11 @@ void ShutdownOriginOp::CollectOriginMetadata( QM_TRY_INSPECT(const bool& exists, MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists), QM_VOID); if (!exists) { + if (aOriginMetadata.mPersistenceType != PERSISTENCE_TYPE_PERSISTENT && + mQuotaManager->IsPendingOrigin(aOriginMetadata)) { + mOriginMetadataArray.AppendElement(aOriginMetadata); + } + return; } @@ -3094,6 +3158,7 @@ nsresult PersistOp::DoDirectoryWork(QuotaManager& aQuotaManager) { // Update directory metadata on disk first. Then, create/update the // originInfo if needed. + QM_TRY_INSPECT(const auto& directory, aQuotaManager.GetOriginDirectory(originMetadata)); @@ -3101,22 +3166,58 @@ nsresult PersistOp::DoDirectoryWork(QuotaManager& aQuotaManager) { aQuotaManager.EnsureOriginDirectory(*directory)); if (created) { + // A new origin directory has been created. + + // XXX The code below could be converted to a function which returns the + // timestamp. int64_t timestamp; - // Origin directory has been successfully created. - // Create OriginInfo too if temporary storage was already initialized. + // Update OriginInfo too if temporary origin was already initialized. if (aQuotaManager.IsTemporaryStorageInitializedInternal()) { - timestamp = aQuotaManager.NoteOriginDirectoryCreated(originMetadata); + if (aQuotaManager.IsTemporaryOriginInitializedInternal(originMetadata)) { + // We have a temporary origin which has been initialized without + // ensuring respective origin directory. So OriginInfo already exists + // and it needs to be updated because the origin directory has been + // just created. + + timestamp = aQuotaManager.WithOriginInfo( + originMetadata, [](const auto& originInfo) { + const int64_t timestamp = originInfo->LockedAccessTime(); + + originInfo->LockedDirectoryCreated(); + + return timestamp; + }); + } else { + timestamp = PR_Now(); + } } else { timestamp = PR_Now(); } QM_TRY(MOZ_TO_RESULT(QuotaManager::CreateDirectoryMetadata2( - *directory, timestamp, - /* aPersisted */ true, originMetadata))); + *directory, timestamp, /* aPersisted */ true, originMetadata))); + // Update or create OriginInfo too if temporary storage was already + // initialized. if (aQuotaManager.IsTemporaryStorageInitializedInternal()) { - aQuotaManager.PersistOrigin(originMetadata); + if (aQuotaManager.IsTemporaryOriginInitializedInternal(originMetadata)) { + // In this case, we have a temporary origin which has been initialized + // without ensuring respective origin directory. So OriginInfo already + // exists and it needs to be updated because the origin directory has + // been just created. + + aQuotaManager.PersistOrigin(originMetadata); + } else { + // In this case, we have a temporary origin which hasn't been + // initialized yet. So OriginInfo needs to be created because the + // origin directory has been just created. + + aQuotaManager.InitQuotaForOrigin( + FullOriginMetadata{originMetadata, /* aPersisted */ true, + timestamp}, + ClientUsageArray(), /* aUsageBytes */ 0); + } } } else { QM_TRY_INSPECT( diff --git a/dom/quota/OriginOperations.h b/dom/quota/OriginOperations.h index 894644843131..46e7dfde511a 100644 --- a/dom/quota/OriginOperations.h +++ b/dom/quota/OriginOperations.h @@ -98,7 +98,7 @@ RefPtr> CreateInitializeTemporaryOriginOp( MovingNotNull> aQuotaManager, const PersistenceType aPersistenceType, const mozilla::ipc::PrincipalInfo& aPrincipalInfo, - RefPtr aDirectoryLock); + bool aCreateIfNonExistent, RefPtr aDirectoryLock); RefPtr> CreateInitializePersistentClientOp( MovingNotNull> aQuotaManager, diff --git a/dom/quota/PQuota.ipdl b/dom/quota/PQuota.ipdl index 2a7ff1b1e3f9..bed2ccf1ea15 100644 --- a/dom/quota/PQuota.ipdl +++ b/dom/quota/PQuota.ipdl @@ -142,7 +142,8 @@ parent: returns(BoolResponse response); async InitializeTemporaryOrigin(PersistenceType persistenceType, - PrincipalInfo principalInfo) + PrincipalInfo principalInfo, + bool createIfNonExistent) returns(BoolResponse response); async InitializePersistentClient(PrincipalInfo principalInfo, diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h index bb136e178f78..674437112974 100644 --- a/dom/quota/QuotaManager.h +++ b/dom/quota/QuotaManager.h @@ -158,36 +158,19 @@ class QuotaManager final : public BackgroundThreadObject { } /** - * For initialization of an origin where the directory already exists. This is - * used by EnsureTemporaryStorageIsInitializedInternal/InitializeRepository - * once it has tallied origin usage by calling each of the QuotaClient - * InitOrigin methods. + * For initialization of an origin where the directory either exists or it + * does not. The directory exists case is used by InitializeOrigin once it + * has tallied origin usage by calling each of the QuotaClient InitOrigin + * methods. It's also used by LoadQuota when quota information is available + * from the cache. EnsureTemporaryStorageIsInitializedInternal calls this + * either if the directory exists or it does not depending on requirements + * of a particular quota client. The special case when origin directory is + * not created during origin initialization is currently utilized only by + * LSNG. */ void InitQuotaForOrigin(const FullOriginMetadata& aFullOriginMetadata, const ClientUsageArray& aClientUsages, - uint64_t aUsageBytes); - - /** - * For use in special-cases like LSNG where we need to be able to know that - * there is no data stored for an origin. LSNG knows that there is 0 usage for - * its storage of an origin and wants to make sure there is a QuotaObject - * tracking this. This method will create a non-persisted, 0-usage, - * mDirectoryExists=false OriginInfo if there isn't already an OriginInfo. If - * an OriginInfo already exists, it will be left as-is, because that implies a - * different client has usages for the origin (and there's no need to add - * LSNG's 0 usage to the QuotaObject). - */ - void EnsureQuotaForOrigin(const OriginMetadata& aOriginMetadata); - - /** - * For use when creating an origin directory. It's possible that origin usage - * is already being tracked due to a call to EnsureQuotaForOrigin, and in that - * case we need to update the existing OriginInfo rather than create a new - * one. - * - * @return last access time of the origin. - */ - int64_t NoteOriginDirectoryCreated(const OriginMetadata& aOriginMetadata); + uint64_t aUsageBytes, bool aDirectoryExists = true); // XXX clients can use QuotaObject instead of calling this method directly. void DecreaseUsageForClient(const ClientMetadata& aClientMetadata, @@ -219,6 +202,8 @@ class QuotaManager final : public BackgroundThreadObject { void UnloadQuota(); + void RemoveOriginFromCache(const OriginMetadata& aOriginMetadata); + already_AddRefed GetQuotaObject( PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata, Client::Type aClientType, nsIFile* aFile, int64_t aFileSize = -1, @@ -236,6 +221,10 @@ class QuotaManager final : public BackgroundThreadObject { void PersistOrigin(const OriginMetadata& aOriginMetadata); + template + auto WithOriginInfo(const OriginMetadata& aOriginMetadata, F aFunction) + -> std::invoke_result_t&>; + using DirectoryLockIdTableArray = AutoTArray; void AbortOperationsForLocks(const DirectoryLockIdTableArray& aLockIds); @@ -250,6 +239,12 @@ class QuotaManager final : public BackgroundThreadObject { Result DoesOriginDirectoryExist( const OriginMetadata& aOriginMetadata) const; + Result, nsresult> GetOrCreateTemporaryOriginDirectory( + const OriginMetadata& aOriginMetadata); + + Result EnsureTemporaryOriginDirectoryCreated( + const OriginMetadata& aOriginMetadata); + static nsresult CreateDirectoryMetadata( nsIFile& aDirectory, int64_t aTimestamp, const OriginMetadata& aOriginMetadata); @@ -334,6 +329,8 @@ class QuotaManager final : public BackgroundThreadObject { template void CollectPendingOriginsForListing(P aPredicate); + bool IsPendingOrigin(const OriginMetadata& aOriginMetadata) const; + RefPtr InitializeStorage(); RefPtr InitializeStorage( @@ -388,11 +385,12 @@ class QuotaManager final : public BackgroundThreadObject { const OriginMetadata& aOriginMetadata); RefPtr InitializeTemporaryOrigin( - PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo); + PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, + bool aCreateIfNonExistent); RefPtr InitializeTemporaryOrigin( PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo, - RefPtr aDirectoryLock); + bool aCreateIfNonExistent, RefPtr aDirectoryLock); RefPtr TemporaryOriginInitialized( PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo); @@ -407,7 +405,7 @@ class QuotaManager final : public BackgroundThreadObject { // indicating whether the directory was newly created. Result, bool>, nsresult> EnsureTemporaryOriginIsInitializedInternal( - const OriginMetadata& aOriginMetadata); + const OriginMetadata& aOriginMetadata, bool aCreateIfNonExistent); RefPtr InitializePersistentClient( const PrincipalInfo& aPrincipalInfo, Client::Type aClientType); diff --git a/dom/quota/QuotaManagerImpl.h b/dom/quota/QuotaManagerImpl.h index aead8672fd2e..f28c3e37415d 100644 --- a/dom/quota/QuotaManagerImpl.h +++ b/dom/quota/QuotaManagerImpl.h @@ -14,6 +14,19 @@ namespace mozilla::dom::quota { +template +auto QuotaManager::WithOriginInfo(const OriginMetadata& aOriginMetadata, + F aFunction) + -> std::invoke_result_t&> { + MutexAutoLock lock(mQuotaMutex); + + RefPtr originInfo = + LockedGetOriginInfo(aOriginMetadata.mPersistenceType, aOriginMetadata); + MOZ_ASSERT(originInfo); + + return aFunction(originInfo); +} + template void QuotaManager::CollectPendingOriginsForListing(P aPredicate) { MutexAutoLock lock(mQuotaMutex); @@ -28,7 +41,7 @@ void QuotaManager::CollectPendingOriginsForListing(P aPredicate) { pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT); if (groupInfo) { for (const auto& originInfo : groupInfo->mOriginInfos) { - if (!originInfo->mDirectoryExists) { + if (!originInfo->LockedDirectoryExists()) { aPredicate(originInfo); } } diff --git a/dom/quota/QuotaManagerService.cpp b/dom/quota/QuotaManagerService.cpp index bee45e28caa0..e4cff881f954 100644 --- a/dom/quota/QuotaManagerService.cpp +++ b/dom/quota/QuotaManagerService.cpp @@ -711,7 +711,7 @@ QuotaManagerService::InitializePersistentOrigin(nsIPrincipal* aPrincipal, NS_IMETHODIMP QuotaManagerService::InitializeTemporaryOrigin( const nsACString& aPersistenceType, nsIPrincipal* aPrincipal, - nsIQuotaRequest** _retval) { + bool aCreateIfNonExistent, nsIQuotaRequest** _retval) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aPrincipal); MOZ_ASSERT(nsContentUtils::IsCallerChrome()); @@ -752,7 +752,8 @@ QuotaManagerService::InitializeTemporaryOrigin( auto request = MakeRefPtr(); mBackgroundActor - ->SendInitializeTemporaryOrigin(persistenceType, principalInfo) + ->SendInitializeTemporaryOrigin(persistenceType, principalInfo, + aCreateIfNonExistent) ->Then(GetCurrentSerialEventTarget(), __func__, BoolResponsePromiseResolveOrRejectCallback(request)); diff --git a/dom/quota/QuotaParent.cpp b/dom/quota/QuotaParent.cpp index 7f51dff65a32..ecd34fca4803 100644 --- a/dom/quota/QuotaParent.cpp +++ b/dom/quota/QuotaParent.cpp @@ -440,7 +440,7 @@ mozilla::ipc::IPCResult Quota::RecvInitializePersistentOrigin( mozilla::ipc::IPCResult Quota::RecvInitializeTemporaryOrigin( const PersistenceType& aPersistenceType, - const PrincipalInfo& aPrincipalInfo, + const PrincipalInfo& aPrincipalInfo, const bool& aCreateIfNonExistent, InitializeTemporaryOriginResolver&& aResolve) { AssertIsOnBackgroundThread(); @@ -459,7 +459,9 @@ mozilla::ipc::IPCResult Quota::RecvInitializeTemporaryOrigin( QuotaManager::GetOrCreate(), ResolveBoolResponseAndReturn(aResolve)); - quotaManager->InitializeTemporaryOrigin(aPersistenceType, aPrincipalInfo) + quotaManager + ->InitializeTemporaryOrigin(aPersistenceType, aPrincipalInfo, + aCreateIfNonExistent) ->Then(GetCurrentSerialEventTarget(), __func__, BoolPromiseResolveOrRejectCallback(this, std::move(aResolve))); diff --git a/dom/quota/QuotaParent.h b/dom/quota/QuotaParent.h index 5cf65c1a7178..fe5812321994 100644 --- a/dom/quota/QuotaParent.h +++ b/dom/quota/QuotaParent.h @@ -64,7 +64,7 @@ class Quota final : public PQuotaParent { virtual mozilla::ipc::IPCResult RecvInitializeTemporaryOrigin( const PersistenceType& aPersistenceType, - const PrincipalInfo& aPrincipalInfo, + const PrincipalInfo& aPrincipalInfo, const bool& aCreateIfNonExistent, InitializeTemporaryOriginResolver&& aResolve) override; virtual mozilla::ipc::IPCResult RecvInitializePersistentClient( diff --git a/dom/quota/nsIQuotaManagerService.idl b/dom/quota/nsIQuotaManagerService.idl index d94013478f72..1be834229509 100644 --- a/dom/quota/nsIQuotaManagerService.idl +++ b/dom/quota/nsIQuotaManagerService.idl @@ -116,10 +116,15 @@ interface nsIQuotaManagerService : nsISupports * * @param aPrincipal * A principal for the origin whose directory is to be initialized. + * + * @param aCreateIfNonExistent + * An optional boolean to indicate creation of origin directory if it + * doesn't exist yet. */ [must_use] nsIQuotaRequest initializeTemporaryOrigin(in ACString aPersistenceType, - in nsIPrincipal aPrincipal); + in nsIPrincipal aPrincipal, + [optional] in boolean aCreateIfNonExistent); /** * Initializes persistent client directory for the given origin and client. diff --git a/dom/quota/test/gtest/QuotaManagerDependencyFixture.cpp b/dom/quota/test/gtest/QuotaManagerDependencyFixture.cpp index 13a9a5f726fc..56323f2236aa 100644 --- a/dom/quota/test/gtest/QuotaManagerDependencyFixture.cpp +++ b/dom/quota/test/gtest/QuotaManagerDependencyFixture.cpp @@ -221,18 +221,19 @@ void QuotaManagerDependencyFixture::ShutdownTemporaryStorage() { // static void QuotaManagerDependencyFixture::InitializeTemporaryOrigin( - const OriginMetadata& aOriginMetadata) { + const OriginMetadata& aOriginMetadata, bool aCreateIfNonExistent) { mozilla::ipc::PrincipalInfo principalInfo; ASSERT_NO_FATAL_FAILURE( CreateContentPrincipalInfo(aOriginMetadata.mOrigin, principalInfo)); PerformOnBackgroundThread([persistenceType = aOriginMetadata.mPersistenceType, - principalInfo = std::move(principalInfo)]() { + principalInfo = std::move(principalInfo), + aCreateIfNonExistent]() { QuotaManager* quotaManager = QuotaManager::Get(); ASSERT_TRUE(quotaManager); - Await(quotaManager->InitializeTemporaryOrigin(persistenceType, - principalInfo)); + Await(quotaManager->InitializeTemporaryOrigin( + persistenceType, principalInfo, aCreateIfNonExistent)); }); } diff --git a/dom/quota/test/gtest/QuotaManagerDependencyFixture.h b/dom/quota/test/gtest/QuotaManagerDependencyFixture.h index ccdd1b8c3d33..8779f2e4f728 100644 --- a/dom/quota/test/gtest/QuotaManagerDependencyFixture.h +++ b/dom/quota/test/gtest/QuotaManagerDependencyFixture.h @@ -45,7 +45,8 @@ class QuotaManagerDependencyFixture : public testing::Test { static void AssertTemporaryStorageNotInitialized(); static void ShutdownTemporaryStorage(); - static void InitializeTemporaryOrigin(const OriginMetadata& aOriginMetadata); + static void InitializeTemporaryOrigin(const OriginMetadata& aOriginMetadata, + bool aCreateIfNonExistent = true); static void TemporaryOriginInitialized(const OriginMetadata& aOriginMetadata, bool* aResult); static void AssertTemporaryOriginInitialized( diff --git a/dom/quota/test/gtest/TestFileOutputStream.cpp b/dom/quota/test/gtest/TestFileOutputStream.cpp index 4a1c1c7d884f..573f028ad1d3 100644 --- a/dom/quota/test/gtest/TestFileOutputStream.cpp +++ b/dom/quota/test/gtest/TestFileOutputStream.cpp @@ -59,7 +59,7 @@ TEST_F(TestFileOutputStream, extendFileStreamWithSetEOF) { { auto res = quotaManager->EnsureTemporaryOriginIsInitializedInternal( - originMetadata); + originMetadata, /* aCreateIfNonExistent */ true); ASSERT_TRUE(res.isOk()); } @@ -80,7 +80,8 @@ TEST_F(TestFileOutputStream, extendFileStreamWithSetEOF) { quota::Client::Type::SDB); { - auto testPathRes = quotaManager->GetOriginDirectory(originMetadata); + auto testPathRes = + quotaManager->GetOrCreateTemporaryOriginDirectory(originMetadata); ASSERT_TRUE(testPathRes.isOk()); diff --git a/dom/quota/test/gtest/TestQuotaManager.cpp b/dom/quota/test/gtest/TestQuotaManager.cpp index 5c910a887fb7..a4f03828e9d1 100644 --- a/dom/quota/test/gtest/TestQuotaManager.cpp +++ b/dom/quota/test/gtest/TestQuotaManager.cpp @@ -23,6 +23,10 @@ class TestQuotaManager : public QuotaManagerDependencyFixture { static void SetUpTestCase() { ASSERT_NO_FATAL_FAILURE(InitializeFixture()); } static void TearDownTestCase() { ASSERT_NO_FATAL_FAILURE(ShutdownFixture()); } + + void TearDown() override { + ASSERT_NO_FATAL_FAILURE(ClearStoragesForOrigin(GetTestOriginMetadata())); + } }; // Test OpenStorageDirectory when an opening of the storage directory is @@ -1417,7 +1421,8 @@ TEST_F(TestQuotaManager, promises.AppendElement(quotaManager->InitializeStorage()); promises.AppendElement(quotaManager->InitializeTemporaryStorage()); promises.AppendElement(quotaManager->InitializeTemporaryOrigin( - testOriginMetadata.mPersistenceType, principalInfo)); + testOriginMetadata.mPersistenceType, principalInfo, + /* aCreateIfNonExistent */ false)); { auto value = @@ -1436,7 +1441,8 @@ TEST_F(TestQuotaManager, promises.AppendElement(quotaManager->InitializeStorage()); promises.AppendElement(quotaManager->InitializeTemporaryStorage()); promises.AppendElement(quotaManager->InitializeTemporaryOrigin( - testOriginMetadata.mPersistenceType, principalInfo)); + testOriginMetadata.mPersistenceType, principalInfo, + /* aCreateIfNonExistent */ true)); { auto value = @@ -1466,7 +1472,58 @@ TEST_F(TestQuotaManager, ClearStoragesForOrigin_Simple) { ASSERT_NO_FATAL_FAILURE(InitializeStorage()); ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage()); - ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin(GetTestOriginMetadata())); + ASSERT_NO_FATAL_FAILURE( + InitializeTemporaryOrigin(GetTestOriginMetadata(), + /* aCreateIfNonExistent */ true)); + + ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); + ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized()); + ASSERT_NO_FATAL_FAILURE( + AssertTemporaryOriginInitialized(GetTestOriginMetadata())); + + PerformOnBackgroundThread([]() { + auto testOriginMetadata = GetTestOriginMetadata(); + + nsCOMPtr principal = + BasePrincipal::CreateContentPrincipal(testOriginMetadata.mOrigin); + QM_TRY(MOZ_TO_RESULT(principal), QM_TEST_FAIL); + + mozilla::ipc::PrincipalInfo principalInfo; + QM_TRY(MOZ_TO_RESULT(PrincipalToPrincipalInfo(principal, &principalInfo)), + QM_TEST_FAIL); + + QuotaManager* quotaManager = QuotaManager::Get(); + ASSERT_TRUE(quotaManager); + + { + auto value = Await(quotaManager->ClearStoragesForOrigin( + /* aPersistenceType */ Nothing(), principalInfo)); + ASSERT_TRUE(value.IsResolve()); + + ASSERT_TRUE(quotaManager->IsStorageInitialized()); + ASSERT_TRUE(quotaManager->IsTemporaryStorageInitialized()); + ASSERT_FALSE(quotaManager->IsTemporaryOriginInitialized( + testOriginMetadata.mPersistenceType, principalInfo)); + } + }); + + ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); + + ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); +} + +TEST_F(TestQuotaManager, ClearStoragesForOrigin_NonExistentOriginDirectory) { + ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); + + ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE( + AssertTemporaryOriginNotInitialized(GetTestOriginMetadata())); + + ASSERT_NO_FATAL_FAILURE(InitializeStorage()); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage()); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin( + GetTestOriginMetadata(), /* aCreateIfNonExistent */ false)); ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized()); @@ -1515,7 +1572,58 @@ TEST_F(TestQuotaManager, ClearStoragesForOriginPrefix_Simple) { ASSERT_NO_FATAL_FAILURE(InitializeStorage()); ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage()); - ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin(GetTestOriginMetadata())); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin( + GetTestOriginMetadata(), /* aCreateIfNonExistent */ true)); + + ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); + ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized()); + ASSERT_NO_FATAL_FAILURE( + AssertTemporaryOriginInitialized(GetTestOriginMetadata())); + + PerformOnBackgroundThread([]() { + auto testOriginMetadata = GetTestOriginMetadata(); + + nsCOMPtr principal = + BasePrincipal::CreateContentPrincipal(testOriginMetadata.mOrigin); + QM_TRY(MOZ_TO_RESULT(principal), QM_TEST_FAIL); + + mozilla::ipc::PrincipalInfo principalInfo; + QM_TRY(MOZ_TO_RESULT(PrincipalToPrincipalInfo(principal, &principalInfo)), + QM_TEST_FAIL); + + QuotaManager* quotaManager = QuotaManager::Get(); + ASSERT_TRUE(quotaManager); + + { + auto value = Await(quotaManager->ClearStoragesForOriginPrefix( + /* aPersistenceType */ Nothing(), principalInfo)); + ASSERT_TRUE(value.IsResolve()); + + ASSERT_TRUE(quotaManager->IsStorageInitialized()); + ASSERT_TRUE(quotaManager->IsTemporaryStorageInitialized()); + ASSERT_FALSE(quotaManager->IsTemporaryOriginInitialized( + testOriginMetadata.mPersistenceType, principalInfo)); + } + }); + + ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); + + ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); +} + +TEST_F(TestQuotaManager, + ClearStoragesForOriginPrefix_NonExistentOriginDirectory) { + ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); + + ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE( + AssertTemporaryOriginNotInitialized(GetTestOriginMetadata())); + + ASSERT_NO_FATAL_FAILURE(InitializeStorage()); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage()); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin( + GetTestOriginMetadata(), /* aCreateIfNonExistent */ false)); ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized()); @@ -1564,7 +1672,58 @@ TEST_F(TestQuotaManager, ClearStoragesForOriginAttributesPattern_Simple) { ASSERT_NO_FATAL_FAILURE(InitializeStorage()); ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage()); - ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin(GetTestOriginMetadata())); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin( + GetTestOriginMetadata(), /* aCreateIfNonExistent */ true)); + + ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); + ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized()); + ASSERT_NO_FATAL_FAILURE( + AssertTemporaryOriginInitialized(GetTestOriginMetadata())); + + PerformOnBackgroundThread([]() { + auto testOriginMetadata = GetTestOriginMetadata(); + + nsCOMPtr principal = + BasePrincipal::CreateContentPrincipal(testOriginMetadata.mOrigin); + QM_TRY(MOZ_TO_RESULT(principal), QM_TEST_FAIL); + + mozilla::ipc::PrincipalInfo principalInfo; + QM_TRY(MOZ_TO_RESULT(PrincipalToPrincipalInfo(principal, &principalInfo)), + QM_TEST_FAIL); + + QuotaManager* quotaManager = QuotaManager::Get(); + ASSERT_TRUE(quotaManager); + + { + auto value = Await(quotaManager->ClearStoragesForOriginAttributesPattern( + OriginAttributesPattern())); + ASSERT_TRUE(value.IsResolve()); + + ASSERT_TRUE(quotaManager->IsStorageInitialized()); + ASSERT_TRUE(quotaManager->IsTemporaryStorageInitialized()); + ASSERT_FALSE(quotaManager->IsTemporaryOriginInitialized( + testOriginMetadata.mPersistenceType, principalInfo)); + } + }); + + ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); + + ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); +} + +TEST_F(TestQuotaManager, + ClearStoragesForOriginAttributesPattern_NonExistentOriginDirectory) { + ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); + + ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE( + AssertTemporaryOriginNotInitialized(GetTestOriginMetadata())); + + ASSERT_NO_FATAL_FAILURE(InitializeStorage()); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage()); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin( + GetTestOriginMetadata(), /* aCreateIfNonExistent */ false)); ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized()); @@ -1613,7 +1772,57 @@ TEST_F(TestQuotaManager, ShutdownStoragesForOrigin_Simple) { ASSERT_NO_FATAL_FAILURE(InitializeStorage()); ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage()); - ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin(GetTestOriginMetadata())); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin( + GetTestOriginMetadata(), /* aCreateIfNonExistent */ true)); + + ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); + ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized()); + ASSERT_NO_FATAL_FAILURE( + AssertTemporaryOriginInitialized(GetTestOriginMetadata())); + + PerformOnBackgroundThread([]() { + auto testOriginMetadata = GetTestOriginMetadata(); + + nsCOMPtr principal = + BasePrincipal::CreateContentPrincipal(testOriginMetadata.mOrigin); + QM_TRY(MOZ_TO_RESULT(principal), QM_TEST_FAIL); + + mozilla::ipc::PrincipalInfo principalInfo; + QM_TRY(MOZ_TO_RESULT(PrincipalToPrincipalInfo(principal, &principalInfo)), + QM_TEST_FAIL); + + QuotaManager* quotaManager = QuotaManager::Get(); + ASSERT_TRUE(quotaManager); + + { + auto value = Await(quotaManager->ShutdownStoragesForOrigin( + /* aPersistenceType */ Nothing(), principalInfo)); + ASSERT_TRUE(value.IsResolve()); + + ASSERT_TRUE(quotaManager->IsStorageInitialized()); + ASSERT_TRUE(quotaManager->IsTemporaryStorageInitialized()); + ASSERT_FALSE(quotaManager->IsTemporaryOriginInitialized( + testOriginMetadata.mPersistenceType, principalInfo)); + } + }); + + ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); + + ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); +} + +TEST_F(TestQuotaManager, ShutdownStoragesForOrigin_NonExistentOriginDirectory) { + ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); + + ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE( + AssertTemporaryOriginNotInitialized(GetTestOriginMetadata())); + + ASSERT_NO_FATAL_FAILURE(InitializeStorage()); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryStorage()); + ASSERT_NO_FATAL_FAILURE(InitializeTemporaryOrigin( + GetTestOriginMetadata(), /* aCreateIfNonExistent */ false)); ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized()); diff --git a/dom/quota/test/gtest/TestStorageConnection.cpp b/dom/quota/test/gtest/TestStorageConnection.cpp index 233c010f2545..b87dd6cb1bd1 100644 --- a/dom/quota/test/gtest/TestStorageConnection.cpp +++ b/dom/quota/test/gtest/TestStorageConnection.cpp @@ -32,11 +32,15 @@ namespace { void InitializeClientDirectory(const ClientMetadata& aClientMetadata) { QuotaManager* quotaManager = QuotaManager::Get(); - QM_TRY_INSPECT( - const auto& directory, - quotaManager->EnsureTemporaryOriginIsInitializedInternal(aClientMetadata) - .map([](const auto& aPair) { return aPair.first; }), - QM_TEST_FAIL); + QM_TRY_INSPECT(const auto& directory, + quotaManager + ->EnsureTemporaryOriginIsInitializedInternal( + aClientMetadata, /* aCreateIfNonExistent */ true) + .map([](const auto& aPair) { return aPair.first; }), + QM_TEST_FAIL); + + QM_TRY(quotaManager->EnsureTemporaryOriginDirectoryCreated(aClientMetadata), + QM_TEST_FAIL); QM_TRY(MOZ_TO_RESULT(directory->Append( Client::TypeToString(aClientMetadata.mClientType))), diff --git a/dom/quota/test/marionette/quota_test_case.py b/dom/quota/test/marionette/quota_test_case.py index 4fd71d610313..4bf6ed96e997 100644 --- a/dom/quota/test/marionette/quota_test_case.py +++ b/dom/quota/test/marionette/quota_test_case.py @@ -121,16 +121,16 @@ class QuotaTestCase(MarionetteTestCase): script_args=(), ) - def initTemporaryOrigin(self, persistenceType, origin): + def initTemporaryOrigin(self, persistenceType, origin, createIfNonExistent=True): with self.marionette.using_context(self.marionette.CONTEXT_CHROME): return self.executeAsyncScript( """ - const [persistenceType, origin] = arguments; + const [persistenceType, origin, createIfNonExistent] = arguments; async function main() { const principal = Services.scriptSecurityManager. createContentPrincipalFromOrigin(origin); - let req = Services.qms.initializeTemporaryOrigin(persistenceType, principal); + let req = Services.qms.initializeTemporaryOrigin(persistenceType, principal, createIfNonExistent); await requestFinished(req) return true; @@ -139,6 +139,7 @@ class QuotaTestCase(MarionetteTestCase): script_args=( persistenceType, origin, + createIfNonExistent, ), ) diff --git a/dom/quota/test/xpcshell/common/head.js b/dom/quota/test/xpcshell/common/head.js index fba7b3f6fd7c..8e4e6f9ff620 100644 --- a/dom/quota/test/xpcshell/common/head.js +++ b/dom/quota/test/xpcshell/common/head.js @@ -178,10 +178,16 @@ function initPersistentOrigin(principal, callback) { return request; } -function initTemporaryOrigin(persistence, principal, callback) { +function initTemporaryOrigin( + persistence, + principal, + createIfNonExistent = true, + callback +) { let request = SpecialPowers._getQuotaManager().initializeTemporaryOrigin( persistence, - principal + principal, + createIfNonExistent ); request.callback = callback; diff --git a/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js b/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js index 9a3afba8c564..5811908b20b8 100644 --- a/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js +++ b/dom/quota/test/xpcshell/telemetry/test_qm_first_initialization_attempt.js @@ -594,7 +594,11 @@ const testcases = [ }, { name: initTemporaryOrigin, - args: ["default", getPrincipal("https://example2.com")], + args: [ + "default", + getPrincipal("https://example2.com"), + /* createIfNonExistent */ true, + ], }, ], expectedSnapshots: { @@ -668,15 +672,27 @@ const testcases = [ initFunctions: [ { name: initTemporaryOrigin, - args: ["temporary", getPrincipal("https://example.com")], + args: [ + "temporary", + getPrincipal("https://example.com"), + /* createIfNonExistent */ true, + ], }, { name: initTemporaryOrigin, - args: ["default", getPrincipal("https://example.com")], + args: [ + "default", + getPrincipal("https://example.com"), + /* createIfNonExistent */ true, + ], }, { name: initTemporaryOrigin, - args: ["default", getPrincipal("https://example1.com")], + args: [ + "default", + getPrincipal("https://example1.com"), + /* createIfNonExistent */ true, + ], }, { name: initPersistentOrigin, diff --git a/dom/quota/test/xpcshell/test_persist.js b/dom/quota/test/xpcshell/test_persist.js index d99a6eec565c..d12563e64ff5 100644 --- a/dom/quota/test/xpcshell/test_persist.js +++ b/dom/quota/test/xpcshell/test_persist.js @@ -75,7 +75,12 @@ function* testSteps() { initTemporaryStorage(continueToNextStepSync); yield undefined; - initTemporaryOrigin(origin.persistence, principal, continueToNextStepSync); + initTemporaryOrigin( + origin.persistence, + principal, + /* createIfNonExistent */ true, + continueToNextStepSync + ); yield undefined; info("Reading out contents of metadata file"); diff --git a/dom/quota/test/xpcshell/test_quotaClientInteractions.js b/dom/quota/test/xpcshell/test_quotaClientInteractions.js new file mode 100644 index 000000000000..0fa258f537f3 --- /dev/null +++ b/dom/quota/test/xpcshell/test_quotaClientInteractions.js @@ -0,0 +1,76 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const { IndexedDBUtils } = ChromeUtils.importESModule( + "resource://testing-common/dom/indexedDB/test/modules/IndexedDBUtils.sys.mjs" +); +const { LocalStorageUtils } = ChromeUtils.importESModule( + "resource://testing-common/dom/localstorage/test/modules/LocalStorageUtils.sys.mjs" +); +const { PrincipalUtils } = ChromeUtils.importESModule( + "resource://testing-common/dom/quota/test/modules/PrincipalUtils.sys.mjs" +); +const { QuotaUtils } = ChromeUtils.importESModule( + "resource://testing-common/dom/quota/test/modules/QuotaUtils.sys.mjs" +); + +async function testNonExistentOriginDirectory() { + const principal = PrincipalUtils.createPrincipal("https://example.com"); + const name = "test_quotaClientInteractions.js"; + const objectStoreName = "foo"; + + info("Opening LocalStorage database"); + + { + const storage = LocalStorageUtils.createStorage(principal); + storage.open(); + } + + info("Opening IndexedDB database"); + + { + const request = indexedDB.openForPrincipal(principal, name); + request.onupgradeneeded = function (event) { + const database = event.target.result; + database.createObjectStore(objectStoreName); + }; + const database = await IndexedDBUtils.requestFinished(request); + database.close(); + } + + info("Resetting storage"); + + { + const request = Services.qms.reset(); + await QuotaUtils.requestFinished(request); + } + + info("Opening LocalStorage database"); + + { + const storage = LocalStorageUtils.createStorage(principal); + storage.open(); + } + + info("Deleting IndexedDB database"); + + { + const request = indexedDB.deleteForPrincipal(principal, name); + await IndexedDBUtils.requestFinished(request); + } +} + +/* exported testSteps */ +async function testSteps() { + add_task( + { + pref_set: [ + ["dom.storage.testing", true], + ["dom.storage.client_validation", false], + ], + }, + testNonExistentOriginDirectory + ); +} diff --git a/dom/quota/test/xpcshell/test_unaccessedOrigins.js b/dom/quota/test/xpcshell/test_unaccessedOrigins.js index 93b38501f4b0..91322d1d6f4f 100644 --- a/dom/quota/test/xpcshell/test_unaccessedOrigins.js +++ b/dom/quota/test/xpcshell/test_unaccessedOrigins.js @@ -85,7 +85,11 @@ async function testSteps() { info("Initializing origins"); for (let index = 0; index < 30; index++) { - request = initTemporaryOrigin("default", getPrincipal(getOrigin(index))); + request = initTemporaryOrigin( + "default", + getPrincipal(getOrigin(index)), + /* createIfNonExistent */ true + ); await requestFinished(request); } diff --git a/dom/quota/test/xpcshell/test_validOrigins.js b/dom/quota/test/xpcshell/test_validOrigins.js index 74db0ea531ba..84c8e45093de 100644 --- a/dom/quota/test/xpcshell/test_validOrigins.js +++ b/dom/quota/test/xpcshell/test_validOrigins.js @@ -78,7 +78,11 @@ async function testSteps() { info(`Testing ${origin.url}`); try { - request = initTemporaryOrigin("default", getPrincipal(origin.url)); + request = initTemporaryOrigin( + "default", + getPrincipal(origin.url), + /* createIfNonExistent */ true + ); await requestFinished(request); ok(true, "Should not have thrown"); diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeFromFlatOriginDirectories.js b/dom/quota/test/xpcshell/upgrades/test_upgradeFromFlatOriginDirectories.js index 66396fef6c8b..c1e5eb142ad8 100644 --- a/dom/quota/test/xpcshell/upgrades/test_upgradeFromFlatOriginDirectories.js +++ b/dom/quota/test/xpcshell/upgrades/test_upgradeFromFlatOriginDirectories.js @@ -172,6 +172,7 @@ function* testSteps() { request = initTemporaryOrigin( origin.persistence, principal, + /* createIfNonExistent */ true, continueToNextStepSync ); yield undefined; diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory.js b/dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory.js index fb6ad9c5a7ab..b47860f6a8d1 100644 --- a/dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory.js +++ b/dom/quota/test/xpcshell/upgrades/test_upgradeFromIndexedDBDirectory.js @@ -107,6 +107,7 @@ function* testSteps() { request = initTemporaryOrigin( origin.persistence, principal, + /* createIfNonExistent */ true, continueToNextStepSync ); yield undefined; diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory.js b/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory.js index 20c015292126..1a26859d3817 100644 --- a/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory.js +++ b/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory.js @@ -363,6 +363,7 @@ function* testSteps() { request = initTemporaryOrigin( origin.persistence, principal, + /* createIfNonExistent */ true, continueToNextStepSync ); } diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_upgradeOriginDirectories.js b/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_upgradeOriginDirectories.js index 6965a95e6911..f6ea0ffaec23 100644 --- a/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_upgradeOriginDirectories.js +++ b/dom/quota/test/xpcshell/upgrades/test_upgradeFromPersistentStorageDirectory_upgradeOriginDirectories.js @@ -148,6 +148,7 @@ function* testSteps() { request = initTemporaryOrigin( origin.persistence, principal, + /* createIfNonExistent */ true, continueToNextStepSync ); yield undefined; diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom0_0.js b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom0_0.js index f1e97ab043ac..b7fcff1b83bd 100644 --- a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom0_0.js +++ b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom0_0.js @@ -144,6 +144,7 @@ function* testSteps() { request = initTemporaryOrigin( origin.persistence, principal, + /* createIfNonExistent */ true, continueToNextStepSync ); yield undefined; diff --git a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_stripObsoleteOriginAttributes.js b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_stripObsoleteOriginAttributes.js index b0706d36409d..cf446d64bff4 100644 --- a/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_stripObsoleteOriginAttributes.js +++ b/dom/quota/test/xpcshell/upgrades/test_upgradeStorageFrom1_0_stripObsoleteOriginAttributes.js @@ -165,6 +165,7 @@ function* testSteps() { request = initTemporaryOrigin( origin.persistence, principal, + /* createIfNonExistent */ true, continueToNextStepSync ); } diff --git a/dom/quota/test/xpcshell/xpcshell.toml b/dom/quota/test/xpcshell/xpcshell.toml index 5e2bd8b3f449..6bf0bf4cdaf3 100644 --- a/dom/quota/test/xpcshell/xpcshell.toml +++ b/dom/quota/test/xpcshell/xpcshell.toml @@ -103,6 +103,8 @@ skip-if = ["condprof"] # frequent perma fail, then goes away. ["test_persist_groupLimit.js"] +["test_quotaClientInteractions.js"] + ["test_removeLocalStorage.js"] ["test_simpledb.js"] diff --git a/dom/simpledb/ActorsParent.cpp b/dom/simpledb/ActorsParent.cpp index 5d415507b741..0d4d47912854 100644 --- a/dom/simpledb/ActorsParent.cpp +++ b/dom/simpledb/ActorsParent.cpp @@ -1196,8 +1196,14 @@ nsresult OpenOp::DatabaseWork() { mOriginMetadata)); } - QM_TRY_RETURN(quotaManager->EnsureTemporaryOriginIsInitializedInternal( + QM_TRY_UNWRAP(auto dbDirectory, + quotaManager->EnsureTemporaryOriginIsInitializedInternal( + mOriginMetadata, /* aCreateIfNonExistent */ true)); + + QM_TRY(quotaManager->EnsureTemporaryOriginDirectoryCreated( mOriginMetadata)); + + return std::move(dbDirectory); }() .map([](const auto& res) { return res.first; })));