/* -*- 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 #include #include #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 gQuotaManagerService; mozilla::Atomic gInitialized(false); mozilla::Atomic 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 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 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 mRequest; }; } // namespace class QuotaManagerService::PendingRequestInfo { protected: RefPtr 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 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::FactoryCreate() { RefPtr 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 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 actor = new QuotaChild(this); mBackgroundActor = static_cast( 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 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 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 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 clientType; QM_TRY(MOZ_TO_RESULT(Client::TypeFromText( aClientType, clientType, fallible)), Err(NS_ERROR_INVALID_ARG)); return clientType; }())); RefPtr 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 { 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 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 clientType; QM_TRY(MOZ_TO_RESULT(Client::TypeFromText( aClientType, clientType, fallible)), Err(NS_ERROR_INVALID_ARG)); return clientType; }())); RefPtr 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 = 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 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 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 = 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 = 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 = 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, nsresult> { if (aPersistenceType.IsVoid()) { return Maybe(); } 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 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, nsresult> { if (aClientType.IsVoid()) { return Maybe(); } Client::Type clientType; QM_TRY(MOZ_TO_RESULT(Client::TypeFromText( aClientType, clientType, fallible)), Err(NS_ERROR_INVALID_ARG)); return Some(clientType); }())); RefPtr 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, nsresult> { if (aPersistenceType.IsVoid()) { return Maybe(); } 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 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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(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(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