Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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