Files
tubestation/dom/quota/test/gtest/QuotaManagerDependencyFixture.cpp
Jan Varga 7783acd7c8 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
2024-10-12 20:58:36 +00:00

424 lines
13 KiB
C++

/* -*- 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 "QuotaManagerDependencyFixture.h"
#include "mozIStorageService.h"
#include "mozStorageCID.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/quota/QuotaManagerService.h"
#include "mozilla/dom/quota/ResultExtensions.h"
#include "mozilla/dom/quota/UsageInfo.h"
#include "mozilla/gtest/MozAssertions.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsIQuotaCallbacks.h"
#include "nsIQuotaRequests.h"
#include "nsIVariant.h"
#include "nsScriptSecurityManager.h"
namespace mozilla::dom::quota::test {
namespace {
class RequestResolver final : public nsIQuotaCallback {
public:
RequestResolver() : mDone(false) {}
bool IsDone() const { return mDone; }
NS_DECL_ISUPPORTS
NS_IMETHOD OnComplete(nsIQuotaRequest* aRequest) override {
mDone = true;
return NS_OK;
}
private:
~RequestResolver() = default;
bool mDone;
};
void CreateContentPrincipalInfo(const nsACString& aOrigin,
mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
nsCOMPtr<nsIPrincipal> principal =
BasePrincipal::CreateContentPrincipal(aOrigin);
QM_TRY(MOZ_TO_RESULT(principal), QM_TEST_FAIL);
mozilla::ipc::PrincipalInfo principalInfo;
QM_TRY(MOZ_TO_RESULT(PrincipalToPrincipalInfo(principal, &aPrincipalInfo)),
QM_TEST_FAIL);
}
} // namespace
NS_IMPL_ISUPPORTS(RequestResolver, nsIQuotaCallback)
// static
void QuotaManagerDependencyFixture::InitializeFixture() {
// Some QuotaManagerService methods fail if the testing pref is not set.
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
prefs->SetBoolPref("dom.quotaManager.testing", true);
// The first initialization of storage service must be done on the main
// thread.
nsCOMPtr<mozIStorageService> storageService =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
ASSERT_TRUE(storageService);
nsIObserver* observer = QuotaManager::GetObserver();
ASSERT_TRUE(observer);
nsresult rv = observer->Observe(nullptr, "profile-do-change", nullptr);
ASSERT_NS_SUCCEEDED(rv);
// Force creation of the quota manager.
ASSERT_NO_FATAL_FAILURE(EnsureQuotaManager());
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
ASSERT_TRUE(quotaManager->OwningThread());
sBackgroundTarget = quotaManager->OwningThread();
}
// static
void QuotaManagerDependencyFixture::ShutdownFixture() {
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
prefs->SetBoolPref("dom.quotaManager.testing", false);
nsIObserver* observer = QuotaManager::GetObserver();
ASSERT_TRUE(observer);
nsresult rv = observer->Observe(nullptr, "profile-before-change-qm", nullptr);
ASSERT_NS_SUCCEEDED(rv);
PerformOnBackgroundThread([]() { QuotaManager::Reset(); });
sBackgroundTarget = nullptr;
}
// static
void QuotaManagerDependencyFixture::InitializeStorage() {
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
Await(quotaManager->InitializeStorage());
});
}
// static
void QuotaManagerDependencyFixture::StorageInitialized(bool* aResult) {
ASSERT_TRUE(aResult);
PerformOnBackgroundThread([aResult]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
auto value = Await(quotaManager->StorageInitialized());
if (value.IsResolve()) {
*aResult = value.ResolveValue();
} else {
*aResult = false;
}
});
}
// static
void QuotaManagerDependencyFixture::AssertStorageInitialized() {
bool result;
ASSERT_NO_FATAL_FAILURE(StorageInitialized(&result));
ASSERT_TRUE(result);
}
// static
void QuotaManagerDependencyFixture::AssertStorageNotInitialized() {
bool result;
ASSERT_NO_FATAL_FAILURE(StorageInitialized(&result));
ASSERT_FALSE(result);
}
// static
void QuotaManagerDependencyFixture::ClearStorage() {
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
Await(quotaManager->ClearStorage());
});
}
// static
void QuotaManagerDependencyFixture::ShutdownStorage() {
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
Await(quotaManager->ShutdownStorage());
});
}
// static
void QuotaManagerDependencyFixture::InitializeTemporaryStorage() {
PerformOnBackgroundThread([]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
Await(quotaManager->InitializeTemporaryStorage());
});
}
// static
void QuotaManagerDependencyFixture::TemporaryStorageInitialized(bool* aResult) {
ASSERT_TRUE(aResult);
PerformOnBackgroundThread([aResult]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
auto value = Await(quotaManager->TemporaryStorageInitialized());
if (value.IsResolve()) {
*aResult = value.ResolveValue();
} else {
*aResult = false;
}
});
}
// static
void QuotaManagerDependencyFixture::AssertTemporaryStorageInitialized() {
bool result;
ASSERT_NO_FATAL_FAILURE(TemporaryStorageInitialized(&result));
ASSERT_TRUE(result);
}
// static
void QuotaManagerDependencyFixture::AssertTemporaryStorageNotInitialized() {
bool result;
ASSERT_NO_FATAL_FAILURE(TemporaryStorageInitialized(&result));
ASSERT_FALSE(result);
}
// static
void QuotaManagerDependencyFixture::ShutdownTemporaryStorage() {
// TODO: It would be nice to have a dedicated operation for shutting down
// temporary storage.
ASSERT_NO_FATAL_FAILURE(ShutdownStorage());
ASSERT_NO_FATAL_FAILURE(InitializeStorage());
}
// static
void QuotaManagerDependencyFixture::InitializeTemporaryOrigin(
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),
aCreateIfNonExistent]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
Await(quotaManager->InitializeTemporaryOrigin(
persistenceType, principalInfo, aCreateIfNonExistent));
});
}
// static
void QuotaManagerDependencyFixture::TemporaryOriginInitialized(
const OriginMetadata& aOriginMetadata, bool* aResult) {
ASSERT_TRUE(aResult);
mozilla::ipc::PrincipalInfo principalInfo;
ASSERT_NO_FATAL_FAILURE(
CreateContentPrincipalInfo(aOriginMetadata.mOrigin, principalInfo));
PerformOnBackgroundThread([persistenceType = aOriginMetadata.mPersistenceType,
principalInfo = std::move(principalInfo),
aResult]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
auto value = Await(quotaManager->TemporaryOriginInitialized(persistenceType,
principalInfo));
if (value.IsResolve()) {
*aResult = value.ResolveValue();
} else {
*aResult = false;
}
});
}
// static
void QuotaManagerDependencyFixture::AssertTemporaryOriginInitialized(
const OriginMetadata& aOriginMetadata) {
bool result;
ASSERT_NO_FATAL_FAILURE(TemporaryOriginInitialized(aOriginMetadata, &result));
ASSERT_TRUE(result);
}
// static
void QuotaManagerDependencyFixture::AssertTemporaryOriginNotInitialized(
const OriginMetadata& aOriginMetadata) {
bool result;
ASSERT_NO_FATAL_FAILURE(TemporaryOriginInitialized(aOriginMetadata, &result));
ASSERT_FALSE(result);
}
// static
void QuotaManagerDependencyFixture::GetOriginUsage(
const OriginMetadata& aOriginMetadata, UsageInfo* aResult) {
ASSERT_TRUE(aResult);
mozilla::ipc::PrincipalInfo principalInfo;
ASSERT_NO_FATAL_FAILURE(
CreateContentPrincipalInfo(aOriginMetadata.mOrigin, principalInfo));
PerformOnBackgroundThread(
[aResult, principalInfo = std::move(principalInfo)]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
auto value = Await(quotaManager->GetOriginUsage(principalInfo));
if (value.IsResolve()) {
*aResult = value.ResolveValue();
} else {
*aResult = UsageInfo();
}
});
}
// static
void QuotaManagerDependencyFixture::GetCachedOriginUsage(
const OriginMetadata& aOriginMetadata, UsageInfo* aResult) {
ASSERT_TRUE(aResult);
mozilla::ipc::PrincipalInfo principalInfo;
ASSERT_NO_FATAL_FAILURE(
CreateContentPrincipalInfo(aOriginMetadata.mOrigin, principalInfo));
PerformOnBackgroundThread(
[aResult, principalInfo = std::move(principalInfo)]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
auto value = Await(quotaManager->GetCachedOriginUsage(principalInfo));
if (value.IsResolve()) {
*aResult = UsageInfo(DatabaseUsageType(Some(value.ResolveValue())));
} else {
*aResult = UsageInfo();
}
});
}
// static
void QuotaManagerDependencyFixture::ClearStoragesForOrigin(
const OriginMetadata& aOriginMetadata) {
mozilla::ipc::PrincipalInfo principalInfo;
ASSERT_NO_FATAL_FAILURE(
CreateContentPrincipalInfo(aOriginMetadata.mOrigin, principalInfo));
PerformOnBackgroundThread([principalInfo = std::move(principalInfo)]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
Await(quotaManager->ClearStoragesForOrigin(/* aPersistenceType */ Nothing(),
principalInfo));
});
}
// static
void QuotaManagerDependencyFixture::InitializeTemporaryClient(
const ClientMetadata& aClientMetadata) {
mozilla::ipc::PrincipalInfo principalInfo;
ASSERT_NO_FATAL_FAILURE(
CreateContentPrincipalInfo(aClientMetadata.mOrigin, principalInfo));
PerformOnBackgroundThread([persistenceType = aClientMetadata.mPersistenceType,
principalInfo = std::move(principalInfo),
clientType = aClientMetadata.mClientType]() {
QuotaManager* quotaManager = QuotaManager::Get();
ASSERT_TRUE(quotaManager);
Await(quotaManager->InitializeTemporaryClient(persistenceType,
principalInfo, clientType));
});
}
// static
OriginMetadata QuotaManagerDependencyFixture::GetTestOriginMetadata() {
return {""_ns,
"example.com"_ns,
"http://example.com"_ns,
"http://example.com"_ns,
/* aIsPrivate */ false,
PERSISTENCE_TYPE_DEFAULT};
}
// static
ClientMetadata QuotaManagerDependencyFixture::GetTestClientMetadata() {
return {GetTestOriginMetadata(), Client::SDB};
}
// static
OriginMetadata QuotaManagerDependencyFixture::GetOtherTestOriginMetadata() {
return {""_ns,
"other-example.com"_ns,
"http://other-example.com"_ns,
"http://other-example.com"_ns,
/* aIsPrivate */ false,
PERSISTENCE_TYPE_DEFAULT};
}
// static
ClientMetadata QuotaManagerDependencyFixture::GetOtherTestClientMetadata() {
return {GetOtherTestOriginMetadata(), Client::SDB};
}
// static
void QuotaManagerDependencyFixture::EnsureQuotaManager() {
// This is needed to satisfy the IsCallerChrome check in
// QuotaManagerService::StorageName. In more detail, accessing the Subject
// Principal without an AutoJSAPI on the stack is forbidden.
AutoJSAPI jsapi;
bool ok = jsapi.Init(xpc::PrivilegedJunkScope());
ASSERT_TRUE(ok);
nsCOMPtr<nsIQuotaManagerService> qms = QuotaManagerService::GetOrCreate();
ASSERT_TRUE(qms);
// In theory, any nsIQuotaManagerService method which ensures quota manager
// on the PBackground thread, could be called here. `StorageName` was chosen
// because it doesn't need to do any directory locking or IO.
// XXX Consider adding a dedicated nsIQuotaManagerService method for this.
nsCOMPtr<nsIQuotaRequest> request;
nsresult rv = qms->StorageName(getter_AddRefs(request));
ASSERT_NS_SUCCEEDED(rv);
RefPtr<RequestResolver> resolver = new RequestResolver();
rv = request->SetCallback(resolver);
ASSERT_NS_SUCCEEDED(rv);
SpinEventLoopUntil("Promise is fulfilled"_ns,
[&resolver]() { return resolver->IsDone(); });
}
nsCOMPtr<nsISerialEventTarget> QuotaManagerDependencyFixture::sBackgroundTarget;
} // namespace mozilla::dom::quota::test