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
424 lines
13 KiB
C++
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
|