Bug 1866402 - Implement origin initialization status tracking on the owning thread; r=dom-storage-reviewers,jari

Origin initialization tracking on the owning thread is needed for covering
origin initialization in QuotaManager::OpenClientDirectory.

Differential Revision: https://phabricator.services.mozilla.com/D195535
This commit is contained in:
Jan Varga 2024-10-09 22:16:52 +00:00
parent 55899a161a
commit 7d76266a7c
4 changed files with 360 additions and 15 deletions

View File

@ -5410,7 +5410,15 @@ RefPtr<BoolPromise> QuotaManager::InitializePersistentOrigin(
initializePersistentOriginOp->RunImmediately();
return initializePersistentOriginOp->OnResults();
return Map<BoolPromise>(
initializePersistentOriginOp->OnResults(),
[self = RefPtr(this),
origin = GetOriginFromValidatedPrincipalInfo(aPrincipalInfo)](
const BoolPromise::ResolveOrRejectValue& aValue) {
self->NoteInitializedOrigin(PERSISTENCE_TYPE_PERSISTENT, origin);
return aValue.ResolveValue();
});
}
RefPtr<BoolPromise> QuotaManager::PersistentOriginInitialized(
@ -5427,6 +5435,15 @@ RefPtr<BoolPromise> QuotaManager::PersistentOriginInitialized(
return persistentOriginInitializedOp->OnResults();
}
bool QuotaManager::IsPersistentOriginInitialized(
const PrincipalInfo& aPrincipalInfo) {
AssertIsOnOwningThread();
auto origin = GetOriginFromValidatedPrincipalInfo(aPrincipalInfo);
return IsOriginInitialized(PERSISTENCE_TYPE_PERSISTENT, origin);
}
bool QuotaManager::IsPersistentOriginInitializedInternal(
const OriginMetadata& aOriginMetadata) const {
AssertIsOnIOThread();
@ -5541,7 +5558,15 @@ RefPtr<BoolPromise> QuotaManager::InitializeTemporaryOrigin(
initializeTemporaryOriginOp->RunImmediately();
return initializeTemporaryOriginOp->OnResults();
return Map<BoolPromise>(
initializeTemporaryOriginOp->OnResults(),
[self = RefPtr(this), aPersistenceType,
origin = GetOriginFromValidatedPrincipalInfo(aPrincipalInfo)](
const BoolPromise::ResolveOrRejectValue& aValue) {
self->NoteInitializedOrigin(aPersistenceType, origin);
return aValue.ResolveValue();
});
}
RefPtr<BoolPromise> QuotaManager::TemporaryOriginInitialized(
@ -5558,6 +5583,15 @@ RefPtr<BoolPromise> QuotaManager::TemporaryOriginInitialized(
return temporaryOriginInitializedOp->OnResults();
}
bool QuotaManager::IsTemporaryOriginInitialized(
PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo) {
AssertIsOnOwningThread();
auto origin = GetOriginFromValidatedPrincipalInfo(aPrincipalInfo);
return IsOriginInitialized(aPersistenceType, origin);
}
bool QuotaManager::IsTemporaryOriginInitializedInternal(
const OriginMetadata& aOriginMetadata) const {
AssertIsOnIOThread();
@ -5883,7 +5917,10 @@ RefPtr<BoolPromise> QuotaManager::ClearStoragesForOrigin(
return Map<BoolPromise>(
clearOriginOp->OnResults(),
[](OriginMetadataArrayPromise::ResolveOrRejectValue&& aValue) {
[self = RefPtr(this)](
OriginMetadataArrayPromise::ResolveOrRejectValue&& aValue) {
self->NoteUninitializedOrigins(aValue.ResolveValue());
return true;
});
}
@ -5918,7 +5955,10 @@ RefPtr<BoolPromise> QuotaManager::ClearStoragesForOriginPrefix(
return Map<BoolPromise>(
clearStoragesForOriginPrefixOp->OnResults(),
[](OriginMetadataArrayPromise::ResolveOrRejectValue&& aValue) {
[self = RefPtr(this)](
OriginMetadataArrayPromise::ResolveOrRejectValue&& aValue) {
self->NoteUninitializedOrigins(aValue.ResolveValue());
return true;
});
}
@ -5936,7 +5976,10 @@ RefPtr<BoolPromise> QuotaManager::ClearStoragesForOriginAttributesPattern(
return Map<BoolPromise>(
clearDataOp->OnResults(),
[](OriginMetadataArrayPromise::ResolveOrRejectValue&& aValue) {
[self = RefPtr(this)](
OriginMetadataArrayPromise::ResolveOrRejectValue&& aValue) {
self->NoteUninitializedOrigins(aValue.ResolveValue());
return true;
});
}
@ -5951,7 +5994,13 @@ RefPtr<BoolPromise> QuotaManager::ClearPrivateRepository() {
clearPrivateRepositoryOp->RunImmediately();
return clearPrivateRepositoryOp->OnResults();
return Map<BoolPromise>(
clearPrivateRepositoryOp->OnResults(),
[self = RefPtr(this)](const BoolPromise::ResolveOrRejectValue& aValue) {
self->NoteUninitializedRepository(PERSISTENCE_TYPE_PRIVATE);
return aValue.ResolveValue();
});
}
RefPtr<BoolPromise> QuotaManager::ClearStorage() {
@ -5970,6 +6019,7 @@ RefPtr<BoolPromise> QuotaManager::ClearStorage() {
return BoolPromise::CreateAndReject(aValue.RejectValue(), __func__);
}
self->mInitializedOrigins.Clear();
self->mTemporaryStorageInitialized = false;
self->mStorageInitialized = false;
@ -5991,7 +6041,10 @@ RefPtr<BoolPromise> QuotaManager::ShutdownStoragesForOrigin(
return Map<BoolPromise>(
shutdownOriginOp->OnResults(),
[](OriginMetadataArrayPromise::ResolveOrRejectValue&& aValue) {
[self = RefPtr(this)](
OriginMetadataArrayPromise::ResolveOrRejectValue&& aValue) {
self->NoteUninitializedOrigins(aValue.ResolveValue());
return true;
});
}
@ -6035,6 +6088,7 @@ RefPtr<BoolPromise> QuotaManager::ShutdownStorage(
return BoolPromise::CreateAndReject(aValue.RejectValue(), __func__);
}
self->mInitializedOrigins.Clear();
self->mTemporaryStorageInitialized = false;
self->mStorageInitialized = false;
@ -7069,6 +7123,70 @@ bool QuotaManager::IsSanitizedOriginValid(const nsACString& aSanitizedOrigin) {
});
}
void QuotaManager::NoteInitializedOrigin(PersistenceType aPersistenceType,
const nsACString& aOrigin) {
AssertIsOnOwningThread();
auto& boolArray = mInitializedOrigins.LookupOrInsertWith(aOrigin, []() {
BoolArray boolArray;
boolArray.AppendElements(PERSISTENCE_TYPE_INVALID);
std::fill(boolArray.begin(), boolArray.end(), false);
return boolArray;
});
boolArray[aPersistenceType] = true;
}
void QuotaManager::NoteUninitializedOrigins(
const OriginMetadataArray& aOriginMetadataArray) {
AssertIsOnOwningThread();
for (const auto& originMetadata : aOriginMetadataArray) {
auto entry = mInitializedOrigins.Lookup(originMetadata.mOrigin);
if (!entry) {
return;
}
auto& boolArray = *entry;
if (boolArray[originMetadata.mPersistenceType]) {
boolArray[originMetadata.mPersistenceType] = false;
if (std::all_of(boolArray.begin(), boolArray.end(),
[](bool entry) { return !entry; })) {
entry.Remove();
}
}
}
}
void QuotaManager::NoteUninitializedRepository(
PersistenceType aPersistenceType) {
AssertIsOnOwningThread();
for (auto iter = mInitializedOrigins.Iter(); !iter.Done(); iter.Next()) {
auto& boolArray = iter.Data();
if (boolArray[aPersistenceType]) {
boolArray[aPersistenceType] = false;
if (std::all_of(boolArray.begin(), boolArray.end(),
[](bool entry) { return !entry; })) {
iter.Remove();
}
}
}
}
bool QuotaManager::IsOriginInitialized(PersistenceType aPersistenceType,
const nsACString& aOrigin) const {
AssertIsOnOwningThread();
const auto entry = mInitializedOrigins.Lookup(aOrigin);
return entry && (*entry)[aPersistenceType];
}
Result<nsCString, nsresult> QuotaManager::EnsureStorageOriginFromOrigin(
const nsACString& aOrigin) {
MutexAutoLock lock(mQuotaMutex);

View File

@ -1140,6 +1140,17 @@ nsresult FinalizeOriginEvictionOp::DoDirectoryWork(
void FinalizeOriginEvictionOp::UnblockOpen() {
AssertIsOnOwningThread();
nsTArray<OriginMetadata> origins;
std::transform(mLocks.cbegin(), mLocks.cend(), MakeBackInserter(origins),
[](const auto& lock) { return lock->OriginMetadata(); });
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(NS_NewRunnableFunction(
"dom::quota::FinalizeOriginEvictionOp::UnblockOpen",
[quotaManager = mQuotaManager, origins = std::move(origins)]() {
quotaManager->NoteUninitializedOrigins(origins);
})));
for (const auto& lock : mLocks) {
lock->Drop();
}

View File

@ -25,6 +25,7 @@
#include "mozilla/dom/quota/CommonMetadata.h"
#include "mozilla/dom/quota/DirectoryLockCategory.h"
#include "mozilla/dom/quota/ForwardDecls.h"
#include "mozilla/dom/quota/HashKeys.h"
#include "mozilla/dom/quota/InitializationTypes.h"
#include "mozilla/dom/quota/NotifyUtils.h"
#include "mozilla/dom/quota/OriginOperationCallbacks.h"
@ -84,6 +85,7 @@ class QuotaManager final : public BackgroundThreadObject {
friend class CanonicalQuotaObject;
friend class ClearStorageOp;
friend class DirectoryLockImpl;
friend class FinalizeOriginEvictionOp;
friend class GroupInfo;
friend class InitOp;
friend class InitTemporaryStorageOp;
@ -375,6 +377,8 @@ class QuotaManager final : public BackgroundThreadObject {
RefPtr<BoolPromise> PersistentOriginInitialized(
const PrincipalInfo& aPrincipalInfo);
bool IsPersistentOriginInitialized(const PrincipalInfo& aPrincipalInfo);
bool IsPersistentOriginInitializedInternal(
const OriginMetadata& aOriginMetadata) const;
@ -394,6 +398,9 @@ class QuotaManager final : public BackgroundThreadObject {
RefPtr<BoolPromise> TemporaryOriginInitialized(
PersistenceType aPersistenceType, const PrincipalInfo& aPrincipalInfo);
bool IsTemporaryOriginInitialized(PersistenceType aPersistenceType,
const PrincipalInfo& aPrincipalInfo);
bool IsTemporaryOriginInitializedInternal(
const OriginMetadata& aOriginMetadata) const;
@ -743,6 +750,17 @@ class QuotaManager final : public BackgroundThreadObject {
void ClearDirectoryLockTables();
void NoteInitializedOrigin(PersistenceType aPersistenceType,
const nsACString& aOrigin);
void NoteUninitializedOrigins(
const OriginMetadataArray& aOriginMetadataArray);
void NoteUninitializedRepository(PersistenceType aPersistenceType);
bool IsOriginInitialized(PersistenceType aPersistenceType,
const nsACString& aOrigin) const;
bool IsSanitizedOriginValid(const nsACString& aSanitizedOrigin);
Result<nsCString, nsresult> EnsureStorageOriginFromOrigin(
@ -821,6 +839,10 @@ class QuotaManager final : public BackgroundThreadObject {
DirectoryLockTable mDefaultDirectoryLockTable;
DirectoryLockTable mPrivateDirectoryLockTable;
using BoolArray = AutoTArray<bool, PERSISTENCE_TYPE_INVALID>;
nsTHashMap<nsCStringHashKeyWithDisabledMemmove, BoolArray>
mInitializedOrigins;
// A list of all successfully initialized persistent origins. This list isn't
// protected by any mutex but it is only ever touched on the IO thread.
nsTArray<nsCString> mInitializedOriginsInternal;

View File

@ -1367,8 +1367,7 @@ TEST_F(TestQuotaManager,
ASSERT_TRUE(value.IsResolve());
ASSERT_TRUE(quotaManager->IsStorageInitialized());
// XXX Assert IsPersistentOriginInitialized once the method is
// available.
ASSERT_TRUE(quotaManager->IsPersistentOriginInitialized(principalInfo));
}
promises.Clear();
@ -1384,8 +1383,7 @@ TEST_F(TestQuotaManager,
ASSERT_TRUE(value.IsResolve());
ASSERT_TRUE(quotaManager->IsStorageInitialized());
// XXX Assert IsPersistentOriginInitialized once the method is
// available.
ASSERT_TRUE(quotaManager->IsPersistentOriginInitialized(principalInfo));
}
});
@ -1428,8 +1426,8 @@ TEST_F(TestQuotaManager,
ASSERT_TRUE(quotaManager->IsStorageInitialized());
ASSERT_TRUE(quotaManager->IsTemporaryStorageInitialized());
// XXX Assert IsTemporaryOriginInitialized once the method is
// available.
ASSERT_TRUE(quotaManager->IsTemporaryOriginInitialized(
testOriginMetadata.mPersistenceType, principalInfo));
}
promises.Clear();
@ -1447,8 +1445,204 @@ TEST_F(TestQuotaManager,
ASSERT_TRUE(quotaManager->IsStorageInitialized());
ASSERT_TRUE(quotaManager->IsTemporaryStorageInitialized());
// XXX Assert IsTemporaryOriginInitialized once the method is
// available.
ASSERT_TRUE(quotaManager->IsTemporaryOriginInitialized(
testOriginMetadata.mPersistenceType, principalInfo));
}
});
ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
}
// Test simple ClearStoragesForOrigin.
TEST_F(TestQuotaManager, ClearStoragesForOrigin_Simple) {
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()));
ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized());
ASSERT_NO_FATAL_FAILURE(
AssertTemporaryOriginInitialized(GetTestOriginMetadata()));
PerformOnBackgroundThread([]() {
auto testOriginMetadata = GetTestOriginMetadata();
nsCOMPtr<nsIPrincipal> 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 simple ClearStoragesForOriginPrefix.
TEST_F(TestQuotaManager, ClearStoragesForOriginPrefix_Simple) {
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()));
ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized());
ASSERT_NO_FATAL_FAILURE(
AssertTemporaryOriginInitialized(GetTestOriginMetadata()));
PerformOnBackgroundThread([]() {
auto testOriginMetadata = GetTestOriginMetadata();
nsCOMPtr<nsIPrincipal> 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 simple ClearStoragesForOriginAttributesPattern.
TEST_F(TestQuotaManager, ClearStoragesForOriginAttributesPattern_Simple) {
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()));
ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized());
ASSERT_NO_FATAL_FAILURE(
AssertTemporaryOriginInitialized(GetTestOriginMetadata()));
PerformOnBackgroundThread([]() {
auto testOriginMetadata = GetTestOriginMetadata();
nsCOMPtr<nsIPrincipal> 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 simple ShutdownStoragesForOrigin.
TEST_F(TestQuotaManager, ShutdownStoragesForOrigin_Simple) {
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()));
ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized());
ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageInitialized());
ASSERT_NO_FATAL_FAILURE(
AssertTemporaryOriginInitialized(GetTestOriginMetadata()));
PerformOnBackgroundThread([]() {
auto testOriginMetadata = GetTestOriginMetadata();
nsCOMPtr<nsIPrincipal> 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));
}
});