/* -*- 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 "mozilla/ipc/Endpoint.h" #include "chrome/common/ipc_message.h" #include "mozilla/ipc/IPDLParamTraits.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->CanRecv()) { MOZ_DIAGNOSTIC_ASSERT(actor->Id() == id, "Wrong Actor?"); RefPtr 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()) { actor->GetIPCChannel()->Send(MakeUnique( id, MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE)); } })); } } bool UntypedManagedEndpoint::BindCommon(IProtocol* aActor, IProtocol* aManager) { MOZ_ASSERT(aManager); if (!mInner) { 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 (NS_WARN_IF(aManager->Id() != mInner->mManagerId) || NS_WARN_IF(aManager->GetProtocolId() != mInner->mManagerType) || NS_WARN_IF(aActor->GetProtocolId() != mInner->mType)) { MOZ_ASSERT_UNREACHABLE("Actor and manager do not match Endpoint"); return false; } if (!aManager->CanSend() || !aManager->GetIPCChannel()) { NS_WARNING("Manager cannot send"); return false; } int32_t id = mInner->mId; mInner.reset(); // Our typed caller will insert the actor into the managed container. aActor->SetManagerAndRegister(aManager, id); aManager->GetIPCChannel()->Send( MakeUnique(id, MANAGED_ENDPOINT_BOUND_MESSAGE_TYPE)); return true; } /* static */ void IPDLParamTraits::Write(IPC::MessageWriter* aWriter, IProtocol* aActor, paramType&& aParam) { bool isValid = aParam.mInner.isSome(); WriteIPDLParam(aWriter, aActor, 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() && inner.mOtherSide->Get()->ToplevelProtocol() == aActor->ToplevelProtocol(), "Must be being sent over the same toplevel protocol"); WriteIPDLParam(aWriter, aActor, inner.mId); WriteIPDLParam(aWriter, aActor, inner.mType); WriteIPDLParam(aWriter, aActor, inner.mManagerId); WriteIPDLParam(aWriter, aActor, inner.mManagerType); } /* static */ bool IPDLParamTraits::Read(IPC::MessageReader* aReader, IProtocol* aActor, paramType* aResult) { *aResult = UntypedManagedEndpoint{}; bool isValid = false; if (!aActor || !ReadIPDLParam(aReader, aActor, &isValid)) { return false; } if (!isValid) { return true; } aResult->mInner.emplace(); auto& inner = *aResult->mInner; inner.mToplevel = aActor->ToplevelProtocol()->GetWeakLifecycleProxy(); return ReadIPDLParam(aReader, aActor, &inner.mId) && ReadIPDLParam(aReader, aActor, &inner.mType) && ReadIPDLParam(aReader, aActor, &inner.mManagerId) && ReadIPDLParam(aReader, aActor, &inner.mManagerType); } } // namespace mozilla::ipc namespace IPC { void ParamTraits::Write(MessageWriter* aWriter, paramType&& aParam) { IPC::WriteParam(aWriter, std::move(aParam.mPort)); IPC::WriteParam(aWriter, aParam.mMessageChannelId); IPC::WriteParam(aWriter, aParam.mMyPid); IPC::WriteParam(aWriter, aParam.mOtherPid); } bool ParamTraits::Read(MessageReader* aReader, paramType* aResult) { return IPC::ReadParam(aReader, &aResult->mPort) && IPC::ReadParam(aReader, &aResult->mMessageChannelId) && IPC::ReadParam(aReader, &aResult->mMyPid) && IPC::ReadParam(aReader, &aResult->mOtherPid); } } // namespace IPC