245 lines
8.6 KiB
C++
245 lines
8.6 KiB
C++
/* 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 "mozilla/ipc/Endpoint.h"
|
|
#include "chrome/common/ipc_message.h"
|
|
#include "ipc/IPCMessageUtilsSpecializations.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "mozilla/ipc/ProtocolMessageUtils.h"
|
|
|
|
namespace mozilla::ipc {
|
|
|
|
UntypedManagedEndpoint::UntypedManagedEndpoint(IProtocol* aActor)
|
|
: mInner(Some(Inner{
|
|
/* mOtherSide */ aActor->GetWeakLifecycleProxy(),
|
|
/* mToplevel */ nullptr,
|
|
aActor->Id(),
|
|
aActor->GetProtocolId(),
|
|
aActor->Manager()->Id(),
|
|
aActor->Manager()->GetProtocolId(),
|
|
})) {}
|
|
|
|
UntypedManagedEndpoint::~UntypedManagedEndpoint() {
|
|
if (!IsValid()) {
|
|
return;
|
|
}
|
|
|
|
if (mInner->mOtherSide) {
|
|
// If this ManagedEndpoint was never sent over IPC, deliver a fake
|
|
// MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE message directly to the other side
|
|
// actor.
|
|
mInner->mOtherSide->ActorEventTarget()->Dispatch(NS_NewRunnableFunction(
|
|
"~ManagedEndpoint (Local)",
|
|
[otherSide = mInner->mOtherSide, id = mInner->mId] {
|
|
if (IProtocol* actor = otherSide->Get(); actor && actor->CanSend()) {
|
|
MOZ_DIAGNOSTIC_ASSERT(actor->Id() == id, "Wrong Actor?");
|
|
RefPtr<ActorLifecycleProxy> strongProxy(actor->GetLifecycleProxy());
|
|
strongProxy->Get()->OnMessageReceived(
|
|
IPC::Message(id, MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE));
|
|
}
|
|
}));
|
|
} else if (mInner->mToplevel) {
|
|
// If it was sent over IPC, we'll need to send the message to the sending
|
|
// side. Let's send the message async.
|
|
mInner->mToplevel->ActorEventTarget()->Dispatch(NS_NewRunnableFunction(
|
|
"~ManagedEndpoint (Remote)",
|
|
[toplevel = mInner->mToplevel, id = mInner->mId] {
|
|
if (IProtocol* actor = toplevel->Get();
|
|
actor && actor->CanSend() && actor->GetIPCChannel()) {
|
|
// Clear the reservation which was taken when the
|
|
// UntypedManagedEndpoint was deserialized.
|
|
actor->ToplevelProtocol()->ClearReservation(id);
|
|
actor->GetIPCChannel()->Send(MakeUnique<IPC::Message>(
|
|
id, MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE));
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
bool UntypedManagedEndpoint::IsValidForManager(
|
|
IRefCountedProtocol* aManager) const {
|
|
return IsValid() && aManager && aManager->Id() == mInner->mManagerId &&
|
|
aManager->GetProtocolId() == mInner->mManagerType;
|
|
}
|
|
|
|
bool UntypedManagedEndpoint::IsValidForManager(
|
|
const UntypedManagedEndpoint& aManager) const {
|
|
return IsValid() && aManager.IsValid() &&
|
|
aManager.mInner->mId == mInner->mManagerId &&
|
|
aManager.mInner->mType == mInner->mManagerType;
|
|
}
|
|
|
|
bool UntypedManagedEndpoint::BindCommon(IProtocol* aActor,
|
|
IRefCountedProtocol* aManager) {
|
|
MOZ_ASSERT(aManager);
|
|
if (!aActor) {
|
|
NS_WARNING("Cannot bind to null actor");
|
|
return false;
|
|
}
|
|
|
|
if (!IsForProtocol(aActor->GetProtocolId())) {
|
|
NS_WARNING("Cannot bind to incorrect protocol");
|
|
return false;
|
|
}
|
|
|
|
if (!IsValidForManager(aManager)) {
|
|
NS_WARNING("Cannot bind to invalid endpoint");
|
|
return false;
|
|
}
|
|
|
|
// Perform thread assertions.
|
|
if (mInner->mToplevel) {
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
mInner->mToplevel->ActorEventTarget()->IsOnCurrentThread());
|
|
MOZ_DIAGNOSTIC_ASSERT(aManager->ToplevelProtocol() ==
|
|
mInner->mToplevel->Get());
|
|
}
|
|
|
|
if (!aManager->CanSend() || !aManager->GetIPCChannel()) {
|
|
NS_WARNING("Manager cannot send");
|
|
return false;
|
|
}
|
|
|
|
// The endpoint was never sent over IPC, so instead we'll reserve the
|
|
// ActorId as-if it was sent over IPC.
|
|
// WARNING: If you introduce error return paths after this point, but before
|
|
// SetManagerAndRegister, we may leak our ActorId reservation.
|
|
if (!mInner->mToplevel &&
|
|
!aManager->ToplevelProtocol()->TryReserve(mInner->mId)) {
|
|
MOZ_ASSERT_UNREACHABLE(
|
|
"Failed to reserve ActorId for in-proc UntypedManagedEndpoint");
|
|
return false;
|
|
}
|
|
|
|
ActorId id = mInner->mId;
|
|
mInner.reset();
|
|
|
|
// Our typed caller will insert the actor into the managed container.
|
|
MOZ_ALWAYS_TRUE(aActor->SetManagerAndRegister(aManager, id));
|
|
|
|
aManager->GetIPCChannel()->Send(
|
|
MakeUnique<IPC::Message>(id, MANAGED_ENDPOINT_BOUND_MESSAGE_TYPE));
|
|
return true;
|
|
}
|
|
|
|
} // namespace mozilla::ipc
|
|
|
|
namespace IPC {
|
|
|
|
/* static */
|
|
void ParamTraits<mozilla::ipc::UntypedManagedEndpoint>::Write(
|
|
MessageWriter* aWriter, paramType&& aParam) {
|
|
bool isValid = aParam.mInner.isSome();
|
|
WriteParam(aWriter, isValid);
|
|
if (!isValid) {
|
|
return;
|
|
}
|
|
|
|
auto inner = std::move(*aParam.mInner);
|
|
aParam.mInner.reset();
|
|
|
|
MOZ_RELEASE_ASSERT(inner.mOtherSide, "Has not been sent over IPC yet");
|
|
MOZ_RELEASE_ASSERT(inner.mOtherSide->ActorEventTarget()->IsOnCurrentThread(),
|
|
"Must be being sent from the correct thread");
|
|
MOZ_RELEASE_ASSERT(inner.mOtherSide->Get() && aWriter->GetActor() &&
|
|
inner.mOtherSide->Get()->ToplevelProtocol() ==
|
|
aWriter->GetActor()->ToplevelProtocol(),
|
|
"Must be being sent over the same toplevel protocol");
|
|
|
|
WriteParam(aWriter, inner.mId);
|
|
WriteParam(aWriter, inner.mType);
|
|
WriteParam(aWriter, inner.mManagerId);
|
|
WriteParam(aWriter, inner.mManagerType);
|
|
}
|
|
|
|
/* static */
|
|
bool ParamTraits<mozilla::ipc::UntypedManagedEndpoint>::Read(
|
|
MessageReader* aReader, paramType* aResult) {
|
|
*aResult = mozilla::ipc::UntypedManagedEndpoint{};
|
|
bool isValid = false;
|
|
if (!aReader->GetActor() || !ReadParam(aReader, &isValid)) {
|
|
return false;
|
|
}
|
|
if (!isValid) {
|
|
return true;
|
|
}
|
|
|
|
mozilla::ipc::IToplevelProtocol* toplevel =
|
|
aReader->GetActor()->ToplevelProtocol();
|
|
|
|
mozilla::ipc::ActorId id = 0;
|
|
if (!ReadParam(aReader, &id)) {
|
|
return false;
|
|
}
|
|
|
|
// Attempt to perform a reservation.
|
|
// If this succeeds, immediately construct mInner, so that the reservation is
|
|
// cleaned up when the ManagedEndpoint is destroyed.
|
|
if (!toplevel->TryReserve(id)) {
|
|
aReader->FatalError("Failed to reserve remote ActorId with toplevel");
|
|
return false;
|
|
}
|
|
|
|
aResult->mInner.emplace();
|
|
auto& inner = *aResult->mInner;
|
|
inner.mToplevel = toplevel->GetWeakLifecycleProxy();
|
|
inner.mId = id;
|
|
|
|
return ReadParam(aReader, &inner.mType) &&
|
|
ReadParam(aReader, &inner.mManagerId) &&
|
|
ReadParam(aReader, &inner.mManagerType);
|
|
}
|
|
|
|
void ParamTraits<mozilla::ipc::UntypedEndpoint>::Write(MessageWriter* aWriter,
|
|
paramType&& aParam) {
|
|
if (aParam.mOtherProcInfo != mozilla::ipc::EndpointProcInfo::Invalid()) {
|
|
MOZ_RELEASE_ASSERT(
|
|
XRE_IsParentProcess() ||
|
|
aParam.mOtherProcInfo == mozilla::ipc::EndpointProcInfo::Current(),
|
|
"If specified, OtherProcInfo must be in the current process for "
|
|
"Endpoints sent from child processes.");
|
|
}
|
|
|
|
IPC::WriteParam(aWriter, std::move(aParam.mPort));
|
|
IPC::WriteParam(aWriter, aParam.mMessageChannelId);
|
|
IPC::WriteParam(aWriter, aParam.mMyProcInfo);
|
|
IPC::WriteParam(aWriter, aParam.mOtherProcInfo);
|
|
}
|
|
|
|
bool ParamTraits<mozilla::ipc::UntypedEndpoint>::Read(MessageReader* aReader,
|
|
paramType* aResult) {
|
|
if (!IPC::ReadParam(aReader, &aResult->mPort) ||
|
|
!IPC::ReadParam(aReader, &aResult->mMessageChannelId) ||
|
|
!IPC::ReadParam(aReader, &aResult->mMyProcInfo) ||
|
|
!IPC::ReadParam(aReader, &aResult->mOtherProcInfo)) {
|
|
return false;
|
|
}
|
|
|
|
// If specified, mOtherProcInfo must either be bound in the other process, or
|
|
// the sender must be the parent process.
|
|
if (aResult->mOtherProcInfo != mozilla::ipc::EndpointProcInfo::Invalid()) {
|
|
mozilla::ipc::IProtocol* actor = aReader->GetActor();
|
|
if (!actor) {
|
|
aReader->FatalError("Must send UntypedEndpoint over an actor");
|
|
return false;
|
|
}
|
|
|
|
// FIXME: Would be nice to also check that `actor->ToplevelProtocol()` is
|
|
// `[NeedsOtherPid]` here, but that information is currently not tracked.
|
|
// This will be easier once we centralize static protocol/actor metadata.
|
|
mozilla::ipc::EndpointProcInfo peer{
|
|
.mPid = actor->ToplevelProtocol()->OtherPidMaybeInvalid(),
|
|
.mChildID = actor->ToplevelProtocol()->OtherChildIDMaybeInvalid()};
|
|
if (peer != aResult->mOtherProcInfo && peer.mChildID != 0) {
|
|
aReader->FatalError(
|
|
"Other end of UntypedEndpoint must be bound in sending process, if "
|
|
"OtherProcInfo is specified");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace IPC
|