diff options
Diffstat (limited to 'dom/clients/manager/ClientManager.cpp')
-rw-r--r-- | dom/clients/manager/ClientManager.cpp | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp new file mode 100644 index 0000000000..e7049cc851 --- /dev/null +++ b/dom/clients/manager/ClientManager.cpp @@ -0,0 +1,394 @@ +/* -*- 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 "ClientManager.h" + +#include "ClientHandle.h" +#include "ClientManagerChild.h" +#include "ClientManagerOpChild.h" +#include "ClientSource.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "mozilla/ClearOnShutdown.h" // PastShutdownPhase +#include "mozilla/StaticPrefs_dom.h" +#include "prthread.h" + +namespace mozilla::dom { + +using mozilla::ipc::BackgroundChild; +using mozilla::ipc::PBackgroundChild; +using mozilla::ipc::PrincipalInfo; + +namespace { + +const uint32_t kBadThreadLocalIndex = -1; +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +const uint32_t kThreadLocalMagic1 = 0x8d57eea6; +const uint32_t kThreadLocalMagic2 = 0x59f375c9; +#endif + +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +uint32_t sClientManagerThreadLocalMagic1 = kThreadLocalMagic1; +#endif + +uint32_t sClientManagerThreadLocalIndex = kBadThreadLocalIndex; + +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +uint32_t sClientManagerThreadLocalMagic2 = kThreadLocalMagic2; +uint32_t sClientManagerThreadLocalIndexDuplicate = kBadThreadLocalIndex; +#endif + +} // anonymous namespace + +ClientManager::ClientManager() { + PBackgroundChild* parentActor = + BackgroundChild::GetOrCreateForCurrentThread(); + if (NS_WARN_IF(!parentActor)) { + Shutdown(); + return; + } + + RefPtr<ClientManagerChild> actor = ClientManagerChild::Create(); + if (NS_WARN_IF(!actor)) { + Shutdown(); + return; + } + + PClientManagerChild* sentActor = + parentActor->SendPClientManagerConstructor(actor); + if (NS_WARN_IF(!sentActor)) { + Shutdown(); + return; + } + MOZ_DIAGNOSTIC_ASSERT(sentActor == actor); + + ActivateThing(actor); +} + +ClientManager::~ClientManager() { + NS_ASSERT_OWNINGTHREAD(ClientManager); + + Shutdown(); + + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic1 == kThreadLocalMagic1); + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic2 == kThreadLocalMagic2); + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex != kBadThreadLocalIndex); + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex == + sClientManagerThreadLocalIndexDuplicate); + MOZ_DIAGNOSTIC_ASSERT(this == + PR_GetThreadPrivate(sClientManagerThreadLocalIndex)); + +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED + PRStatus status = +#endif + PR_SetThreadPrivate(sClientManagerThreadLocalIndex, nullptr); + MOZ_DIAGNOSTIC_ASSERT(status == PR_SUCCESS); +} + +void ClientManager::Shutdown() { + NS_ASSERT_OWNINGTHREAD(ClientManager); + + if (IsShutdown()) { + return; + } + + ShutdownThing(); +} + +UniquePtr<ClientSource> ClientManager::CreateSourceInternal( + ClientType aType, nsISerialEventTarget* aEventTarget, + const PrincipalInfo& aPrincipal) { + NS_ASSERT_OWNINGTHREAD(ClientManager); + + nsID id; + nsresult rv = nsID::GenerateUUIDInPlace(id); + MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); + if (NS_WARN_IF(NS_FAILED(rv))) { + // If we can't even get a UUID, at least make sure not to use a garbage + // value. Instead return a shutdown ClientSource with a zero'd id. + // This should be exceptionally rare, if it happens at all. + id.Clear(); + ClientSourceConstructorArgs args(id, aType, aPrincipal, TimeStamp::Now()); + UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args)); + source->Shutdown(); + return source; + } + + ClientSourceConstructorArgs args(id, aType, aPrincipal, TimeStamp::Now()); + UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args)); + + if (IsShutdown()) { + source->Shutdown(); + return source; + } + + source->Activate(GetActor()); + + return source; +} + +UniquePtr<ClientSource> ClientManager::CreateSourceInternal( + const ClientInfo& aClientInfo, nsISerialEventTarget* aEventTarget) { + NS_ASSERT_OWNINGTHREAD(ClientManager); + + ClientSourceConstructorArgs args(aClientInfo.Id(), aClientInfo.Type(), + aClientInfo.PrincipalInfo(), + aClientInfo.CreationTime()); + UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args)); + + if (IsShutdown()) { + source->Shutdown(); + return source; + } + + source->Activate(GetActor()); + + return source; +} + +already_AddRefed<ClientHandle> ClientManager::CreateHandleInternal( + const ClientInfo& aClientInfo, nsISerialEventTarget* aSerialEventTarget) { + NS_ASSERT_OWNINGTHREAD(ClientManager); + MOZ_DIAGNOSTIC_ASSERT(aSerialEventTarget); + + RefPtr<ClientHandle> handle = + new ClientHandle(this, aSerialEventTarget, aClientInfo); + + if (IsShutdown()) { + handle->Shutdown(); + return handle.forget(); + } + + handle->Activate(GetActor()); + + return handle.forget(); +} + +RefPtr<ClientOpPromise> ClientManager::StartOp( + const ClientOpConstructorArgs& aArgs, + nsISerialEventTarget* aSerialEventTarget) { + RefPtr<ClientOpPromise::Private> promise = + new ClientOpPromise::Private(__func__); + + // Hold a ref to the client until the remote operation completes. Otherwise + // the ClientHandle might get de-refed and teardown the actor before we + // get an answer. + RefPtr<ClientManager> kungFuGrip = this; + + MaybeExecute( + [&aArgs, promise, kungFuGrip](ClientManagerChild* aActor) { + ClientManagerOpChild* actor = + new ClientManagerOpChild(kungFuGrip, aArgs, promise); + if (!aActor->SendPClientManagerOpConstructor(actor, aArgs)) { + // Constructor failure will reject promise via ActorDestroy() + return; + } + }, + [promise] { + CopyableErrorResult rv; + rv.ThrowInvalidStateError("Client has been destroyed"); + promise->Reject(rv, __func__); + }); + + return promise; +} + +// static +already_AddRefed<ClientManager> ClientManager::GetOrCreateForCurrentThread() { + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic1 == kThreadLocalMagic1); + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic2 == kThreadLocalMagic2); + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex != kBadThreadLocalIndex); + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex == + sClientManagerThreadLocalIndexDuplicate); + RefPtr<ClientManager> cm = static_cast<ClientManager*>( + PR_GetThreadPrivate(sClientManagerThreadLocalIndex)); + + if (!cm) { + cm = new ClientManager(); + +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED + PRStatus status = +#endif + PR_SetThreadPrivate(sClientManagerThreadLocalIndex, cm.get()); + MOZ_DIAGNOSTIC_ASSERT(status == PR_SUCCESS); + } + + MOZ_DIAGNOSTIC_ASSERT(cm); + + if (StaticPrefs::dom_workers_testing_enabled()) { + // Check that the ClientManager instance associated to the current thread + // has not been kept alive when it was expected to have been already + // deallocated (e.g. due to a leak ClientManager's mShutdown can have ben + // set to true from its RevokeActor method but never fully deallocated and + // unset from the thread locals). + MOZ_DIAGNOSTIC_ASSERT(!cm->IsShutdown()); + } + return cm.forget(); +} + +WorkerPrivate* ClientManager::GetWorkerPrivate() const { + NS_ASSERT_OWNINGTHREAD(ClientManager); + MOZ_DIAGNOSTIC_ASSERT(GetActor()); + return GetActor()->GetWorkerPrivate(); +} + +// Used to share logic between ExpectFutureSource and ForgetFutureSource. +/* static */ bool ClientManager::ExpectOrForgetFutureSource( + const ClientInfo& aClientInfo, + bool (PClientManagerChild::*aMethod)(const IPCClientInfo&)) { + // Return earlier if called late in the XPCOM shutdown path, + // ClientManager would be already shutdown at the point. + if (NS_WARN_IF(PastShutdownPhase(ShutdownPhase::XPCOMShutdown))) { + return false; + } + + bool rv = true; + + RefPtr<ClientManager> mgr = ClientManager::GetOrCreateForCurrentThread(); + mgr->MaybeExecute( + [&](ClientManagerChild* aActor) { + if (!(aActor->*aMethod)(aClientInfo.ToIPC())) { + rv = false; + } + }, + [&] { rv = false; }); + + return rv; +} + +/* static */ bool ClientManager::ExpectFutureSource( + const ClientInfo& aClientInfo) { + return ExpectOrForgetFutureSource( + aClientInfo, &PClientManagerChild::SendExpectFutureClientSource); +} + +/* static */ bool ClientManager::ForgetFutureSource( + const ClientInfo& aClientInfo) { + return ExpectOrForgetFutureSource( + aClientInfo, &PClientManagerChild::SendForgetFutureClientSource); +} + +// static +void ClientManager::Startup() { + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic1 == kThreadLocalMagic1); + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic2 == kThreadLocalMagic2); + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex == kBadThreadLocalIndex); + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex == + sClientManagerThreadLocalIndexDuplicate); + +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED + PRStatus status = +#endif + PR_NewThreadPrivateIndex(&sClientManagerThreadLocalIndex, nullptr); + MOZ_DIAGNOSTIC_ASSERT(status == PR_SUCCESS); + + MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex != kBadThreadLocalIndex); +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED + sClientManagerThreadLocalIndexDuplicate = sClientManagerThreadLocalIndex; +#endif +} + +// static +UniquePtr<ClientSource> ClientManager::CreateSource( + ClientType aType, nsISerialEventTarget* aEventTarget, + nsIPrincipal* aPrincipal) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + + PrincipalInfo principalInfo; + nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_CRASH("ClientManager::CreateSource() cannot serialize bad principal"); + } + + RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread(); + return mgr->CreateSourceInternal(aType, aEventTarget, principalInfo); +} + +// static +UniquePtr<ClientSource> ClientManager::CreateSource( + ClientType aType, nsISerialEventTarget* aEventTarget, + const PrincipalInfo& aPrincipal) { + RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread(); + return mgr->CreateSourceInternal(aType, aEventTarget, aPrincipal); +} + +// static +UniquePtr<ClientSource> ClientManager::CreateSourceFromInfo( + const ClientInfo& aClientInfo, nsISerialEventTarget* aEventTarget) { + RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread(); + return mgr->CreateSourceInternal(aClientInfo, aEventTarget); +} + +Maybe<ClientInfo> ClientManager::CreateInfo(ClientType aType, + nsIPrincipal* aPrincipal) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + + PrincipalInfo principalInfo; + nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_CRASH("ClientManager::CreateSource() cannot serialize bad principal"); + } + + nsID id; + rv = nsID::GenerateUUIDInPlace(id); + MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return Nothing(); + } + + return Some(ClientInfo(id, aType, principalInfo, TimeStamp::Now())); +} + +// static +already_AddRefed<ClientHandle> ClientManager::CreateHandle( + const ClientInfo& aClientInfo, nsISerialEventTarget* aSerialEventTarget) { + RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread(); + return mgr->CreateHandleInternal(aClientInfo, aSerialEventTarget); +} + +// static +RefPtr<ClientOpPromise> ClientManager::MatchAll( + const ClientMatchAllArgs& aArgs, nsISerialEventTarget* aSerialEventTarget) { + RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread(); + return mgr->StartOp(aArgs, aSerialEventTarget); +} + +// static +RefPtr<ClientOpPromise> ClientManager::Claim( + const ClientClaimArgs& aArgs, nsISerialEventTarget* aSerialEventTarget) { + RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread(); + return mgr->StartOp(aArgs, aSerialEventTarget); +} + +// static +RefPtr<ClientOpPromise> ClientManager::GetInfoAndState( + const ClientGetInfoAndStateArgs& aArgs, + nsISerialEventTarget* aSerialEventTarget) { + RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread(); + return mgr->StartOp(aArgs, aSerialEventTarget); +} + +// static +RefPtr<ClientOpPromise> ClientManager::Navigate( + const ClientNavigateArgs& aArgs, nsISerialEventTarget* aSerialEventTarget) { + RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread(); + return mgr->StartOp(aArgs, aSerialEventTarget); +} + +// static +RefPtr<ClientOpPromise> ClientManager::OpenWindow( + const ClientOpenWindowArgs& aArgs, + nsISerialEventTarget* aSerialEventTarget) { + RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread(); + return mgr->StartOp(aArgs, aSerialEventTarget); +} + +} // namespace mozilla::dom |