зеркало из https://github.com/mozilla/gecko-dev.git
395 строки
12 KiB
C++
395 строки
12 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 "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
|