diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /dom/quota/QuotaManagerService.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/quota/QuotaManagerService.cpp')
-rw-r--r-- | dom/quota/QuotaManagerService.cpp | 1264 |
1 files changed, 1264 insertions, 0 deletions
diff --git a/dom/quota/QuotaManagerService.cpp b/dom/quota/QuotaManagerService.cpp new file mode 100644 index 0000000000..265a621a08 --- /dev/null +++ b/dom/quota/QuotaManagerService.cpp @@ -0,0 +1,1264 @@ +/* -*- 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 "QuotaManagerService.h" + +// Local includes +#include "ActorsChild.h" +#include "Client.h" +#include "QuotaManager.h" +#include "QuotaRequests.h" + +// Global includes +#include <cstdint> +#include <cstring> +#include <utility> +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/Atomics.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Hal.h" +#include "mozilla/MacroForEach.h" +#include "mozilla/Maybe.h" +#include "mozilla/OriginAttributes.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Unused.h" +#include "mozilla/Variant.h" +#include "mozilla/dom/quota/PQuota.h" +#include "mozilla/dom/quota/PersistenceType.h" +#include "mozilla/dom/quota/ResultExtensions.h" +#include "mozilla/fallible.h" +#include "mozilla/hal_sandbox/PHal.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsDebug.h" +#include "nsError.h" +#include "nsIObserverService.h" +#include "nsIPrincipal.h" +#include "nsIUserIdleService.h" +#include "nsServiceManagerUtils.h" +#include "nsStringFwd.h" +#include "nsVariant.h" +#include "nsXULAppAPI.h" +#include "nscore.h" + +#define PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID "profile-before-change-qm" + +namespace mozilla::dom::quota { + +using namespace mozilla::ipc; + +namespace { + +const char kIdleServiceContractId[] = "@mozilla.org/widget/useridleservice;1"; + +// The number of seconds we will wait after receiving the idle-daily +// notification before beginning maintenance. +const uint32_t kIdleObserverTimeSec = 1; + +mozilla::StaticRefPtr<QuotaManagerService> gQuotaManagerService; + +mozilla::Atomic<bool> gInitialized(false); +mozilla::Atomic<bool> gClosed(false); + +nsresult CheckedPrincipalToPrincipalInfo(nsIPrincipal* aPrincipal, + PrincipalInfo& aPrincipalInfo) { + MOZ_ASSERT(aPrincipal); + + nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aPrincipalInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) { + return NS_ERROR_FAILURE; + } + + if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo && + aPrincipalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +nsresult GetClearResetOriginParams(nsIPrincipal* aPrincipal, + const nsACString& aPersistenceType, + const nsAString& aClientType, + ClearResetOriginParams& aParams) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + + nsresult rv = + CheckedPrincipalToPrincipalInfo(aPrincipal, aParams.principalInfo()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (aPersistenceType.IsVoid()) { + aParams.persistenceTypeIsExplicit() = false; + } else { + const auto maybePersistenceType = + PersistenceTypeFromString(aPersistenceType, fallible); + if (NS_WARN_IF(maybePersistenceType.isNothing())) { + return NS_ERROR_INVALID_ARG; + } + + aParams.persistenceType() = maybePersistenceType.value(); + aParams.persistenceTypeIsExplicit() = true; + } + + if (aClientType.IsVoid()) { + aParams.clientTypeIsExplicit() = false; + } else { + Client::Type clientType; + bool ok = Client::TypeFromText(aClientType, clientType, fallible); + if (NS_WARN_IF(!ok)) { + return NS_ERROR_INVALID_ARG; + } + + aParams.clientType() = clientType; + aParams.clientTypeIsExplicit() = true; + } + + return NS_OK; +} + +class BoolResponsePromiseResolveOrRejectCallback { + public: + using PromiseType = BoolResponsePromise; + using RequestType = Request; + + explicit BoolResponsePromiseResolveOrRejectCallback( + RefPtr<RequestType> aRequest) + : mRequest(std::move(aRequest)) {} + + void operator()(const PromiseType::ResolveOrRejectValue& aValue) { + if (aValue.IsResolve()) { + const BoolResponse& response = aValue.ResolveValue(); + + switch (response.type()) { + case BoolResponse::Tnsresult: + mRequest->SetError(response.get_nsresult()); + break; + + case BoolResponse::Tbool: { + RefPtr<nsVariant> variant = new nsVariant(); + variant->SetAsBool(response.get_bool()); + + mRequest->SetResult(variant); + break; + } + default: + MOZ_CRASH("Unknown response type!"); + } + + } else { + mRequest->SetError(NS_ERROR_FAILURE); + } + } + + private: + RefPtr<RequestType> mRequest; +}; + +} // namespace + +class QuotaManagerService::PendingRequestInfo { + protected: + RefPtr<RequestBase> mRequest; + + public: + explicit PendingRequestInfo(RequestBase* aRequest) : mRequest(aRequest) {} + + virtual ~PendingRequestInfo() = default; + + RequestBase* GetRequest() const { return mRequest; } + + virtual nsresult InitiateRequest(QuotaChild* aActor) = 0; +}; + +class QuotaManagerService::UsageRequestInfo : public PendingRequestInfo { + UsageRequestParams mParams; + + public: + UsageRequestInfo(UsageRequest* aRequest, const UsageRequestParams& aParams) + : PendingRequestInfo(aRequest), mParams(aParams) { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aParams.type() != UsageRequestParams::T__None); + } + + virtual nsresult InitiateRequest(QuotaChild* aActor) override; +}; + +class QuotaManagerService::RequestInfo : public PendingRequestInfo { + RequestParams mParams; + + public: + RequestInfo(Request* aRequest, const RequestParams& aParams) + : PendingRequestInfo(aRequest), mParams(aParams) { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + } + + virtual nsresult InitiateRequest(QuotaChild* aActor) override; +}; + +class QuotaManagerService::IdleMaintenanceInfo : public PendingRequestInfo { + const bool mStart; + + public: + explicit IdleMaintenanceInfo(bool aStart) + : PendingRequestInfo(nullptr), mStart(aStart) {} + + virtual nsresult InitiateRequest(QuotaChild* aActor) override; +}; + +QuotaManagerService::QuotaManagerService() + : mBackgroundActor(nullptr), + mBackgroundActorFailed(false), + mIdleObserverRegistered(false) { + MOZ_ASSERT(NS_IsMainThread()); +} + +QuotaManagerService::~QuotaManagerService() { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mIdleObserverRegistered); +} + +// static +QuotaManagerService* QuotaManagerService::GetOrCreate() { + MOZ_ASSERT(NS_IsMainThread()); + + if (gClosed) { + MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!"); + return nullptr; + } + + if (!gQuotaManagerService) { + RefPtr<QuotaManagerService> instance(new QuotaManagerService()); + + nsresult rv = instance->Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + if (gInitialized.exchange(true)) { + MOZ_ASSERT(false, "Initialized more than once?!"); + } + + gQuotaManagerService = instance; + + ClearOnShutdown(&gQuotaManagerService); + } + + return gQuotaManagerService; +} + +// static +QuotaManagerService* QuotaManagerService::Get() { + // Does not return an owning reference. + return gQuotaManagerService; +} + +// static +already_AddRefed<QuotaManagerService> QuotaManagerService::FactoryCreate() { + RefPtr<QuotaManagerService> quotaManagerService = GetOrCreate(); + return quotaManagerService.forget(); +} + +void QuotaManagerService::ClearBackgroundActor() { + MOZ_ASSERT(NS_IsMainThread()); + + mBackgroundActor = nullptr; +} + +void QuotaManagerService::AbortOperationsForProcess( + ContentParentId aContentParentId) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + nsresult rv = EnsureBackgroundActor(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + if (NS_WARN_IF( + !mBackgroundActor->SendAbortOperationsForProcess(aContentParentId))) { + return; + } +} + +nsresult QuotaManagerService::Init() { + MOZ_ASSERT(NS_IsMainThread()); + + if (XRE_IsParentProcess()) { + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (NS_WARN_IF(!observerService)) { + return NS_ERROR_FAILURE; + } + + nsresult rv = observerService->AddObserver( + this, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID, false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + return NS_OK; +} + +void QuotaManagerService::Destroy() { + // Setting the closed flag prevents the service from being recreated. + // Don't set it though if there's no real instance created. + if (gInitialized && gClosed.exchange(true)) { + MOZ_ASSERT(false, "Shutdown more than once?!"); + } + + delete this; +} + +nsresult QuotaManagerService::EnsureBackgroundActor() { + MOZ_ASSERT(NS_IsMainThread()); + + // Nothing can be done here if we have previously failed to create a + // background actor. + if (mBackgroundActorFailed) { + return NS_ERROR_FAILURE; + } + + if (!mBackgroundActor) { + PBackgroundChild* backgroundActor = + BackgroundChild::GetOrCreateForCurrentThread(); + if (NS_WARN_IF(!backgroundActor)) { + mBackgroundActorFailed = true; + return NS_ERROR_FAILURE; + } + + { + RefPtr<QuotaChild> actor = new QuotaChild(this); + + mBackgroundActor = static_cast<QuotaChild*>( + backgroundActor->SendPQuotaConstructor(actor)); + } + } + + if (!mBackgroundActor) { + mBackgroundActorFailed = true; + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult QuotaManagerService::InitiateRequest(PendingRequestInfo& aInfo) { + nsresult rv = EnsureBackgroundActor(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aInfo.InitiateRequest(mBackgroundActor); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +void QuotaManagerService::PerformIdleMaintenance() { + using namespace mozilla::hal; + + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + // If we're running on battery power then skip all idle maintenance since we + // would otherwise be doing lots of disk I/O. + BatteryInformation batteryInfo; + +#ifdef MOZ_WIDGET_ANDROID + // Android XPCShell doesn't load the AndroidBridge that is needed to make + // GetCurrentBatteryInformation work... + if (!QuotaManager::IsRunningXPCShellTests()) +#endif + { + // In order to give the correct battery level, hal must have registered + // battery observers. + RegisterBatteryObserver(this); + GetCurrentBatteryInformation(&batteryInfo); + UnregisterBatteryObserver(this); + } + + // If we're running XPCShell because we always want to be able to test this + // code so pretend that we're always charging. + if (QuotaManager::IsRunningXPCShellTests()) { + batteryInfo.level() = 100; + batteryInfo.charging() = true; + } + + if (NS_WARN_IF(!batteryInfo.charging())) { + return; + } + + if (QuotaManager::IsRunningXPCShellTests()) { + // We don't want user activity to impact this code if we're running tests. + Unused << Observe(nullptr, OBSERVER_TOPIC_IDLE, nullptr); + } else if (!mIdleObserverRegistered) { + nsCOMPtr<nsIUserIdleService> idleService = + do_GetService(kIdleServiceContractId); + MOZ_ASSERT(idleService); + + MOZ_ALWAYS_SUCCEEDS( + idleService->AddIdleObserver(this, kIdleObserverTimeSec)); + + mIdleObserverRegistered = true; + } +} + +void QuotaManagerService::RemoveIdleObserver() { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (mIdleObserverRegistered) { + nsCOMPtr<nsIUserIdleService> idleService = + do_GetService(kIdleServiceContractId); + MOZ_ASSERT(idleService); + + // Ignore the return value of RemoveIdleObserver, it may fail if the + // observer has already been unregistered during shutdown. + Unused << idleService->RemoveIdleObserver(this, kIdleObserverTimeSec); + + mIdleObserverRegistered = false; + } +} + +NS_IMPL_ADDREF(QuotaManagerService) +NS_IMPL_RELEASE_WITH_DESTROY(QuotaManagerService, Destroy()) +NS_IMPL_QUERY_INTERFACE(QuotaManagerService, nsIQuotaManagerService, + nsIObserver) + +NS_IMETHODIMP +QuotaManagerService::StorageName(nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + if (NS_WARN_IF(!StaticPrefs::dom_quotaManager_testing())) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<Request> request = new Request(); + + StorageNameParams params; + + RequestInfo info(request, params); + + nsresult rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::StorageInitialized(nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + if (NS_WARN_IF(!StaticPrefs::dom_quotaManager_testing())) { + return NS_ERROR_UNEXPECTED; + } + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor->SendStorageInitialized()->Then( + GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::TemporaryStorageInitialized(nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + if (NS_WARN_IF(!StaticPrefs::dom_quotaManager_testing())) { + return NS_ERROR_UNEXPECTED; + } + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor->SendTemporaryStorageInitialized()->Then( + GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::Init(nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + if (NS_WARN_IF(!StaticPrefs::dom_quotaManager_testing())) { + return NS_ERROR_UNEXPECTED; + } + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor->SendInitializeStorage()->Then( + GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::InitTemporaryStorage(nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + if (NS_WARN_IF(!StaticPrefs::dom_quotaManager_testing())) { + return NS_ERROR_UNEXPECTED; + } + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor->SendInitializeTemporaryStorage()->Then( + GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::InitializePersistentOrigin(nsIPrincipal* aPrincipal, + nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + if (NS_WARN_IF(!StaticPrefs::dom_quotaManager_testing())) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<Request> request = new Request(); + + InitializePersistentOriginParams params; + + nsresult rv = + CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + RequestInfo info(request, params); + + rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::InitializeTemporaryOrigin( + const nsACString& aPersistenceType, nsIPrincipal* aPrincipal, + nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + if (NS_WARN_IF(!StaticPrefs::dom_quotaManager_testing())) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<Request> request = new Request(); + + InitializeTemporaryOriginParams params; + + const auto maybePersistenceType = + PersistenceTypeFromString(aPersistenceType, fallible); + if (NS_WARN_IF(maybePersistenceType.isNothing())) { + return NS_ERROR_INVALID_ARG; + } + + if (NS_WARN_IF(!IsBestEffortPersistenceType(maybePersistenceType.value()))) { + return NS_ERROR_FAILURE; + } + + params.persistenceType() = maybePersistenceType.value(); + + nsresult rv = + CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + RequestInfo info(request, params); + + rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::InitializePersistentClient(nsIPrincipal* aPrincipal, + const nsAString& aClientType, + nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + QM_TRY(MOZ_TO_RESULT(StaticPrefs::dom_quotaManager_testing()), + NS_ERROR_UNEXPECTED); + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + QM_TRY_INSPECT( + const auto& principalInfo, + ([&aPrincipal]() -> Result<PrincipalInfo, nsresult> { + PrincipalInfo principalInfo; + QM_TRY(MOZ_TO_RESULT( + PrincipalToPrincipalInfo(aPrincipal, &principalInfo))); + + QM_TRY(MOZ_TO_RESULT(QuotaManager::IsPrincipalInfoValid(principalInfo)), + Err(NS_ERROR_INVALID_ARG)); + + return principalInfo; + }())); + + QM_TRY_INSPECT(const auto& clientType, + ([&aClientType]() -> Result<Client::Type, nsresult> { + Client::Type clientType; + QM_TRY(MOZ_TO_RESULT(Client::TypeFromText( + aClientType, clientType, fallible)), + Err(NS_ERROR_INVALID_ARG)); + + return clientType; + }())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor->SendInitializePersistentClient(principalInfo, clientType) + ->Then(GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::InitializeTemporaryClient( + const nsACString& aPersistenceType, nsIPrincipal* aPrincipal, + const nsAString& aClientType, nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + QM_TRY(MOZ_TO_RESULT(StaticPrefs::dom_quotaManager_testing()), + NS_ERROR_UNEXPECTED); + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + QM_TRY_INSPECT( + const auto& persistenceType, + ([&aPersistenceType]() -> Result<PersistenceType, nsresult> { + const auto persistenceType = + PersistenceTypeFromString(aPersistenceType, fallible); + QM_TRY(MOZ_TO_RESULT(persistenceType.isSome()), + Err(NS_ERROR_INVALID_ARG)); + + QM_TRY( + MOZ_TO_RESULT(IsBestEffortPersistenceType(persistenceType.ref())), + Err(NS_ERROR_INVALID_ARG)); + + return persistenceType.ref(); + }())); + + QM_TRY_INSPECT( + const auto& principalInfo, + ([&aPrincipal]() -> Result<PrincipalInfo, nsresult> { + PrincipalInfo principalInfo; + QM_TRY(MOZ_TO_RESULT( + PrincipalToPrincipalInfo(aPrincipal, &principalInfo))); + + QM_TRY(MOZ_TO_RESULT(QuotaManager::IsPrincipalInfoValid(principalInfo)), + Err(NS_ERROR_INVALID_ARG)); + + return principalInfo; + }())); + + QM_TRY_INSPECT(const auto& clientType, + ([&aClientType]() -> Result<Client::Type, nsresult> { + Client::Type clientType; + QM_TRY(MOZ_TO_RESULT(Client::TypeFromText( + aClientType, clientType, fallible)), + Err(NS_ERROR_INVALID_ARG)); + + return clientType; + }())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor + ->SendInitializeTemporaryClient(persistenceType, principalInfo, + clientType) + ->Then(GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::GetFullOriginMetadata(const nsACString& aPersistenceType, + nsIPrincipal* aPrincipal, + nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + QM_TRY(OkIf(StaticPrefs::dom_quotaManager_testing()), NS_ERROR_UNEXPECTED); + + const auto maybePersistenceType = + PersistenceTypeFromString(aPersistenceType, fallible); + QM_TRY(OkIf(maybePersistenceType.isSome()), NS_ERROR_INVALID_ARG); + QM_TRY(OkIf(IsBestEffortPersistenceType(*maybePersistenceType)), + NS_ERROR_INVALID_ARG); + + PrincipalInfo principalInfo; + QM_TRY(MOZ_TO_RESULT(PrincipalToPrincipalInfo(aPrincipal, &principalInfo))); + QM_TRY(OkIf(QuotaManager::IsPrincipalInfoValid(principalInfo)), + NS_ERROR_INVALID_ARG); + + RefPtr<Request> request = new Request(); + + GetFullOriginMetadataParams params; + + params.persistenceType() = *maybePersistenceType; + params.principalInfo() = std::move(principalInfo); + + RequestInfo info(request, params); + + QM_TRY(MOZ_TO_RESULT(InitiateRequest(info))); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::GetUsage(nsIQuotaUsageCallback* aCallback, bool aGetAll, + nsIQuotaUsageRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aCallback); + + RefPtr<UsageRequest> request = new UsageRequest(aCallback); + + AllUsageParams params; + + params.getAll() = aGetAll; + + UsageRequestInfo info(request, params); + + nsresult rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::GetUsageForPrincipal(nsIPrincipal* aPrincipal, + nsIQuotaUsageCallback* aCallback, + bool aFromMemory, + nsIQuotaUsageRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aCallback); + + RefPtr<UsageRequest> request = new UsageRequest(aPrincipal, aCallback); + + OriginUsageParams params; + + nsresult rv = + CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + params.fromMemory() = aFromMemory; + + UsageRequestInfo info(request, params); + + rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::Clear(nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + + if (NS_WARN_IF(!StaticPrefs::dom_quotaManager_testing())) { + return NS_ERROR_UNEXPECTED; + } + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor->SendClearStorage()->Then( + GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::ClearStoragesForPrivateBrowsing( + nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor->SendClearStoragesForPrivateBrowsing()->Then( + GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::ClearStoragesForOriginAttributesPattern( + const nsAString& aPattern, nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + OriginAttributesPattern pattern; + MOZ_ALWAYS_TRUE(pattern.Init(aPattern)); + + RefPtr<Request> request = new Request(); + + mBackgroundActor->SendClearStoragesForOriginAttributesPattern(pattern)->Then( + GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::ClearStoragesForPrincipal( + nsIPrincipal* aPrincipal, const nsACString& aPersistenceType, + const nsAString& aClientType, nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + QM_TRY_INSPECT( + const auto& persistenceType, + ([&aPersistenceType]() -> Result<Maybe<PersistenceType>, nsresult> { + if (aPersistenceType.IsVoid()) { + return Maybe<PersistenceType>(); + } + + const auto persistenceType = + PersistenceTypeFromString(aPersistenceType, fallible); + QM_TRY(MOZ_TO_RESULT(persistenceType.isSome()), + Err(NS_ERROR_INVALID_ARG)); + + return persistenceType; + }())); + + QM_TRY_INSPECT( + const auto& principalInfo, + ([&aPrincipal]() -> Result<PrincipalInfo, nsresult> { + PrincipalInfo principalInfo; + QM_TRY(MOZ_TO_RESULT( + PrincipalToPrincipalInfo(aPrincipal, &principalInfo))); + + QM_TRY(MOZ_TO_RESULT(QuotaManager::IsPrincipalInfoValid(principalInfo)), + Err(NS_ERROR_INVALID_ARG)); + + return principalInfo; + }())); + + QM_TRY_INSPECT(const auto& clientType, + ([&aClientType]() -> Result<Maybe<Client::Type>, nsresult> { + if (aClientType.IsVoid()) { + return Maybe<Client::Type>(); + } + + Client::Type clientType; + QM_TRY(MOZ_TO_RESULT(Client::TypeFromText( + aClientType, clientType, fallible)), + Err(NS_ERROR_INVALID_ARG)); + + return Some(clientType); + }())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor + ->SendClearStoragesForOrigin(persistenceType, principalInfo, clientType) + ->Then(GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::ClearStoragesForOriginPrefix( + nsIPrincipal* aPrincipal, const nsACString& aPersistenceType, + nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + QM_TRY_INSPECT( + const auto& persistenceType, + ([&aPersistenceType]() -> Result<Maybe<PersistenceType>, nsresult> { + if (aPersistenceType.IsVoid()) { + return Maybe<PersistenceType>(); + } + + const auto persistenceType = + PersistenceTypeFromString(aPersistenceType, fallible); + QM_TRY(MOZ_TO_RESULT(persistenceType.isSome()), + Err(NS_ERROR_INVALID_ARG)); + + return persistenceType; + }())); + + QM_TRY_INSPECT( + const auto& principalInfo, + ([&aPrincipal]() -> Result<PrincipalInfo, nsresult> { + PrincipalInfo principalInfo; + QM_TRY(MOZ_TO_RESULT( + PrincipalToPrincipalInfo(aPrincipal, &principalInfo))); + + QM_TRY(MOZ_TO_RESULT(QuotaManager::IsPrincipalInfoValid(principalInfo)), + Err(NS_ERROR_INVALID_ARG)); + + if (principalInfo.type() == PrincipalInfo::TContentPrincipalInfo) { + nsCString suffix; + principalInfo.get_ContentPrincipalInfo().attrs().CreateSuffix(suffix); + + QM_TRY(MOZ_TO_RESULT(suffix.IsEmpty()), Err(NS_ERROR_INVALID_ARG)); + } + + return principalInfo; + }())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor + ->SendClearStoragesForOriginPrefix(persistenceType, principalInfo) + ->Then(GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::Reset(nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + + if (NS_WARN_IF(!StaticPrefs::dom_quotaManager_testing())) { + return NS_ERROR_UNEXPECTED; + } + + QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); + + RefPtr<Request> request = new Request(); + + mBackgroundActor->SendShutdownStorage()->Then( + GetCurrentSerialEventTarget(), __func__, + BoolResponsePromiseResolveOrRejectCallback(request)); + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::ResetStoragesForPrincipal( + nsIPrincipal* aPrincipal, const nsACString& aPersistenceType, + const nsAString& aClientType, nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + + if (NS_WARN_IF(!StaticPrefs::dom_quotaManager_testing())) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr<Request> request = new Request(aPrincipal); + + ClearResetOriginParams commonParams; + + nsresult rv = GetClearResetOriginParams(aPrincipal, aPersistenceType, + aClientType, commonParams); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + RequestParams params; + params = ResetOriginParams(commonParams); + + RequestInfo info(request, params); + + rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::Persisted(nsIPrincipal* aPrincipal, + nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(_retval); + + RefPtr<Request> request = new Request(aPrincipal); + + PersistedParams params; + + nsresult rv = + CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + RequestInfo info(request, params); + + rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::Persist(nsIPrincipal* aPrincipal, + nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(_retval); + + RefPtr<Request> request = new Request(aPrincipal); + + PersistParams params; + + nsresult rv = + CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + RequestInfo info(request, params); + + rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::Estimate(nsIPrincipal* aPrincipal, + nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + + RefPtr<Request> request = new Request(aPrincipal); + + EstimateParams params; + + nsresult rv = + CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + RequestInfo info(request, params); + + rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::ListOrigins(nsIQuotaRequest** _retval) { + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr<Request> request = new Request(); + + ListOriginsParams params; + + RequestInfo info(request, params); + + nsresult rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + request.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +QuotaManagerService::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (!strcmp(aTopic, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID)) { + RemoveIdleObserver(); + return NS_OK; + } + + if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) { + PerformIdleMaintenance(); + return NS_OK; + } + + if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE)) { + IdleMaintenanceInfo info(/* aStart */ true); + + nsresult rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; + } + + if (!strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) { + RemoveIdleObserver(); + + IdleMaintenanceInfo info(/* aStart */ false); + + nsresult rv = InitiateRequest(info); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; + } + + MOZ_ASSERT_UNREACHABLE("Should never get here!"); + return NS_OK; +} + +void QuotaManagerService::Notify(const hal::BatteryInformation& aBatteryInfo) { + // This notification is received when battery data changes. We don't need to + // deal with this notification. +} + +nsresult QuotaManagerService::UsageRequestInfo::InitiateRequest( + QuotaChild* aActor) { + MOZ_ASSERT(aActor); + + auto request = static_cast<UsageRequest*>(mRequest.get()); + + auto actor = new QuotaUsageRequestChild(request); + + if (!aActor->SendPQuotaUsageRequestConstructor(actor, mParams)) { + request->SetError(NS_ERROR_FAILURE); + return NS_ERROR_FAILURE; + } + + request->SetBackgroundActor(actor); + + return NS_OK; +} + +nsresult QuotaManagerService::RequestInfo::InitiateRequest(QuotaChild* aActor) { + MOZ_ASSERT(aActor); + + auto request = static_cast<Request*>(mRequest.get()); + + auto actor = new QuotaRequestChild(request); + + if (!aActor->SendPQuotaRequestConstructor(actor, mParams)) { + request->SetError(NS_ERROR_FAILURE); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult QuotaManagerService::IdleMaintenanceInfo::InitiateRequest( + QuotaChild* aActor) { + MOZ_ASSERT(aActor); + + bool result; + + if (mStart) { + result = aActor->SendStartIdleMaintenance(); + } else { + result = aActor->SendStopIdleMaintenance(); + } + + if (!result) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +} // namespace mozilla::dom::quota |