diff options
Diffstat (limited to '')
-rw-r--r-- | ipc/glue/Endpoint.h | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/ipc/glue/Endpoint.h b/ipc/glue/Endpoint.h new file mode 100644 index 0000000000..d7eea94dfc --- /dev/null +++ b/ipc/glue/Endpoint.h @@ -0,0 +1,288 @@ +/* -*- 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 https://mozilla.org/MPL/2.0/. */ + +#ifndef IPC_GLUE_ENDPOINT_H_ +#define IPC_GLUE_ENDPOINT_H_ + +#include <utility> +#include "CrashAnnotations.h" +#include "base/process.h" +#include "base/process_util.h" +#include "mozilla/Assertions.h" +#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/ipc/MessageLink.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/NodeController.h" +#include "mozilla/ipc/ScopedPort.h" +#include "nsXULAppAPI.h" +#include "nscore.h" + +namespace IPC { +template <class P> +struct ParamTraits; +} + +namespace mozilla { +namespace ipc { + +namespace endpoint_detail { + +template <class T> +static auto ActorNeedsOtherPidHelper(int) + -> decltype(std::declval<T>().OtherPid(), std::true_type{}); +template <class> +static auto ActorNeedsOtherPidHelper(long) -> std::false_type; + +template <typename T> +constexpr bool ActorNeedsOtherPid = + decltype(ActorNeedsOtherPidHelper<T>(0))::value; + +} // namespace endpoint_detail + +struct PrivateIPDLInterface {}; + +class UntypedEndpoint { + public: + using ProcessId = base::ProcessId; + + UntypedEndpoint() = default; + + UntypedEndpoint(const PrivateIPDLInterface&, ScopedPort aPort, + const nsID& aMessageChannelId, + ProcessId aMyPid = base::kInvalidProcessId, + ProcessId aOtherPid = base::kInvalidProcessId) + : mPort(std::move(aPort)), + mMessageChannelId(aMessageChannelId), + mMyPid(aMyPid), + mOtherPid(aOtherPid) {} + + UntypedEndpoint(const UntypedEndpoint&) = delete; + UntypedEndpoint(UntypedEndpoint&& aOther) = default; + + UntypedEndpoint& operator=(const UntypedEndpoint&) = delete; + UntypedEndpoint& operator=(UntypedEndpoint&& aOther) = default; + + // This method binds aActor to this endpoint. After this call, the actor can + // be used to send and receive messages. The endpoint becomes invalid. + // + // If specified, aEventTarget is the target the actor will be bound to, and + // must be on the current thread. Otherwise, GetCurrentSerialEventTarget() is + // used. + bool Bind(IToplevelProtocol* aActor, + nsISerialEventTarget* aEventTarget = nullptr) { + MOZ_RELEASE_ASSERT(IsValid()); + MOZ_RELEASE_ASSERT(mMyPid == base::kInvalidProcessId || + mMyPid == base::GetCurrentProcId()); + MOZ_RELEASE_ASSERT(!aEventTarget || aEventTarget->IsOnCurrentThread()); + return aActor->Open(std::move(mPort), mMessageChannelId, mOtherPid, + aEventTarget); + } + + bool IsValid() const { return mPort.IsValid(); } + + protected: + friend struct IPC::ParamTraits<UntypedEndpoint>; + + ScopedPort mPort; + nsID mMessageChannelId{}; + ProcessId mMyPid = base::kInvalidProcessId; + ProcessId mOtherPid = base::kInvalidProcessId; +}; + +/** + * An endpoint represents one end of a partially initialized IPDL channel. To + * set up a new top-level protocol: + * + * Endpoint<PFooParent> parentEp; + * Endpoint<PFooChild> childEp; + * nsresult rv; + * rv = PFoo::CreateEndpoints(&parentEp, &childEp); + * + * Endpoints can be passed in IPDL messages or sent to other threads using + * PostTask. Once an Endpoint has arrived at its destination process and thread, + * you need to create the top-level actor and bind it to the endpoint: + * + * FooParent* parent = new FooParent(); + * bool rv1 = parentEp.Bind(parent, processActor); + * bool rv2 = parent->SendBar(...); + * + * (See Bind below for an explanation of processActor.) Once the actor is bound + * to the endpoint, it can send and receive messages. + * + * If creating endpoints for a [NeedsOtherPid] actor, you're required to also + * pass in parentPid and childPid, which are the pids of the processes in which + * the parent and child endpoints will be used. + */ +template <class PFooSide> +class Endpoint final : public UntypedEndpoint { + public: + using UntypedEndpoint::IsValid; + using UntypedEndpoint::UntypedEndpoint; + + base::ProcessId OtherPid() const { + static_assert( + endpoint_detail::ActorNeedsOtherPid<PFooSide>, + "OtherPid may only be called on Endpoints for actors which are " + "[NeedsOtherPid]"); + MOZ_RELEASE_ASSERT(mOtherPid != base::kInvalidProcessId); + return mOtherPid; + } + + // This method binds aActor to this endpoint. After this call, the actor can + // be used to send and receive messages. The endpoint becomes invalid. + // + // If specified, aEventTarget is the target the actor will be bound to, and + // must be on the current thread. Otherwise, GetCurrentSerialEventTarget() is + // used. + bool Bind(PFooSide* aActor, nsISerialEventTarget* aEventTarget = nullptr) { + return UntypedEndpoint::Bind(aActor, aEventTarget); + } +}; + +#if defined(XP_MACOSX) +void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error); +#else +inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, + int error) {} +#endif + +// This function is used internally to create a pair of Endpoints. See the +// comment above Endpoint for a description of how it might be used. +template <class PFooParent, class PFooChild> +nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate, + Endpoint<PFooParent>* aParentEndpoint, + Endpoint<PFooChild>* aChildEndpoint) { + static_assert( + !endpoint_detail::ActorNeedsOtherPid<PFooParent> && + !endpoint_detail::ActorNeedsOtherPid<PFooChild>, + "Pids are required when creating endpoints for [NeedsOtherPid] actors"); + + auto [parentPort, childPort] = + NodeController::GetSingleton()->CreatePortPair(); + nsID channelId = nsID::GenerateUUID(); + *aParentEndpoint = + Endpoint<PFooParent>(aPrivate, std::move(parentPort), channelId); + *aChildEndpoint = + Endpoint<PFooChild>(aPrivate, std::move(childPort), channelId); + return NS_OK; +} + +template <class PFooParent, class PFooChild> +nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate, + base::ProcessId aParentDestPid, + base::ProcessId aChildDestPid, + Endpoint<PFooParent>* aParentEndpoint, + Endpoint<PFooChild>* aChildEndpoint) { + MOZ_RELEASE_ASSERT(aParentDestPid != base::kInvalidProcessId); + MOZ_RELEASE_ASSERT(aChildDestPid != base::kInvalidProcessId); + + auto [parentPort, childPort] = + NodeController::GetSingleton()->CreatePortPair(); + nsID channelId = nsID::GenerateUUID(); + *aParentEndpoint = + Endpoint<PFooParent>(aPrivate, std::move(parentPort), channelId, + aParentDestPid, aChildDestPid); + *aChildEndpoint = Endpoint<PFooChild>( + aPrivate, std::move(childPort), channelId, aChildDestPid, aParentDestPid); + return NS_OK; +} + +class UntypedManagedEndpoint { + public: + bool IsValid() const { return mInner.isSome(); } + + UntypedManagedEndpoint(const UntypedManagedEndpoint&) = delete; + UntypedManagedEndpoint& operator=(const UntypedManagedEndpoint&) = delete; + + protected: + UntypedManagedEndpoint() = default; + explicit UntypedManagedEndpoint(IProtocol* aActor); + + UntypedManagedEndpoint(UntypedManagedEndpoint&& aOther) noexcept + : mInner(std::move(aOther.mInner)) { + aOther.mInner = Nothing(); + } + UntypedManagedEndpoint& operator=(UntypedManagedEndpoint&& aOther) noexcept { + this->~UntypedManagedEndpoint(); + new (this) UntypedManagedEndpoint(std::move(aOther)); + return *this; + } + + ~UntypedManagedEndpoint() noexcept; + + bool BindCommon(IProtocol* aActor, IProtocol* aManager); + + private: + friend struct IPDLParamTraits<UntypedManagedEndpoint>; + + struct Inner { + // Pointers to the toplevel actor which will manage this connection. When + // created, only `mOtherSide` will be set, and will reference the + // toplevel actor which the other side is managed by. After being sent over + // IPC, only `mToplevel` will be set, and will be the toplevel actor for the + // channel which received the IPC message. + RefPtr<WeakActorLifecycleProxy> mOtherSide; + RefPtr<WeakActorLifecycleProxy> mToplevel; + + int32_t mId = 0; + ProtocolId mType = LastMsgIndex; + int32_t mManagerId = 0; + ProtocolId mManagerType = LastMsgIndex; + }; + Maybe<Inner> mInner; +}; + +/** + * A managed endpoint represents one end of a partially initialized managed + * IPDL actor. It is used for situations where the usual IPDL Constructor + * methods do not give sufficient control over the construction of actors, such + * as when constructing actors within replies, or constructing multiple related + * actors simultaneously. + * + * FooParent* parent = new FooParent(); + * ManagedEndpoint<PFooChild> childEp = parentMgr->OpenPFooEndpoint(parent); + * + * ManagedEndpoints should be sent using IPDL messages or other mechanisms to + * the other side of the manager channel. Once the ManagedEndpoint has arrived + * at its destination, you can create the actor, and bind it to the endpoint. + * + * FooChild* child = new FooChild(); + * childMgr->BindPFooEndpoint(childEp, child); + * + * WARNING: If the remote side of an endpoint has not been bound before it + * begins to receive messages, an IPC routing error will occur, likely causing + * a browser crash. + */ +template <class PFooSide> +class ManagedEndpoint : public UntypedManagedEndpoint { + public: + ManagedEndpoint() = default; + ManagedEndpoint(ManagedEndpoint&&) noexcept = default; + ManagedEndpoint& operator=(ManagedEndpoint&&) noexcept = default; + + ManagedEndpoint(const PrivateIPDLInterface&, IProtocol* aActor) + : UntypedManagedEndpoint(aActor) {} + + bool Bind(const PrivateIPDLInterface&, PFooSide* aActor, IProtocol* aManager, + ManagedContainer<PFooSide>& aContainer) { + if (!BindCommon(aActor, aManager)) { + return false; + } + aContainer.Insert(aActor); + return true; + } + + // Only invalid ManagedEndpoints can be equal, as valid endpoints are unique. + bool operator==(const ManagedEndpoint& _o) const { + return !IsValid() && !_o.IsValid(); + } +}; + +} // namespace ipc +} // namespace mozilla + +#endif // IPC_GLUE_ENDPOINT_H_ |