Endpoint.h (10468B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 7 #ifndef IPC_GLUE_ENDPOINT_H_ 8 #define IPC_GLUE_ENDPOINT_H_ 9 10 #include <utility> 11 #include "CrashAnnotations.h" 12 #include "base/process.h" 13 #include "base/process_util.h" 14 #include "mozilla/Assertions.h" 15 #include "mozilla/Maybe.h" 16 #include "mozilla/ipc/MessageLink.h" 17 #include "mozilla/ipc/ProtocolUtils.h" 18 #include "mozilla/ipc/NodeController.h" 19 #include "mozilla/ipc/ScopedPort.h" 20 #include "nsXULAppAPI.h" 21 #include "nscore.h" 22 23 namespace IPC { 24 template <class P> 25 struct ParamTraits; 26 } 27 28 namespace mozilla { 29 namespace ipc { 30 31 namespace endpoint_detail { 32 33 template <class T> 34 static auto ActorNeedsOtherPidHelper(int) 35 -> decltype(std::declval<T>().OtherPid(), std::true_type{}); 36 template <class> 37 static auto ActorNeedsOtherPidHelper(long) -> std::false_type; 38 39 template <typename T> 40 constexpr bool ActorNeedsOtherPid = 41 decltype(ActorNeedsOtherPidHelper<T>(0))::value; 42 43 } // namespace endpoint_detail 44 45 struct PrivateIPDLInterface {}; 46 47 class UntypedEndpoint { 48 public: 49 UntypedEndpoint() = default; 50 51 UntypedEndpoint(const PrivateIPDLInterface&, ScopedPort aPort, 52 const nsID& aMessageChannelId, 53 EndpointProcInfo aMyProcInfo = EndpointProcInfo::Invalid(), 54 EndpointProcInfo aOtherProcInfo = EndpointProcInfo::Invalid()) 55 : mPort(std::move(aPort)), 56 mMessageChannelId(aMessageChannelId), 57 mMyProcInfo(aMyProcInfo), 58 mOtherProcInfo(aOtherProcInfo) {} 59 60 UntypedEndpoint(const UntypedEndpoint&) = delete; 61 UntypedEndpoint(UntypedEndpoint&& aOther) = default; 62 63 UntypedEndpoint& operator=(const UntypedEndpoint&) = delete; 64 UntypedEndpoint& operator=(UntypedEndpoint&& aOther) = default; 65 66 // This method binds aActor to this endpoint. After this call, the actor can 67 // be used to send and receive messages. The endpoint becomes invalid. 68 // 69 // If specified, aEventTarget is the target the actor will be bound to, and 70 // must be on the current thread. Otherwise, GetCurrentSerialEventTarget() is 71 // used. 72 bool Bind(IToplevelProtocol* aActor, 73 nsISerialEventTarget* aEventTarget = nullptr) { 74 MOZ_RELEASE_ASSERT(IsValid()); 75 MOZ_RELEASE_ASSERT(mMyProcInfo == EndpointProcInfo::Invalid() || 76 mMyProcInfo == EndpointProcInfo::Current()); 77 MOZ_RELEASE_ASSERT(!aEventTarget || aEventTarget->IsOnCurrentThread()); 78 return aActor->Open(std::move(mPort), mMessageChannelId, mOtherProcInfo, 79 aEventTarget); 80 } 81 82 bool IsValid() const { return mPort.IsValid(); } 83 84 protected: 85 friend struct IPC::ParamTraits<UntypedEndpoint>; 86 87 ScopedPort mPort; 88 nsID mMessageChannelId{}; 89 EndpointProcInfo mMyProcInfo; 90 EndpointProcInfo mOtherProcInfo; 91 }; 92 93 /** 94 * An endpoint represents one end of a partially initialized IPDL channel. To 95 * set up a new top-level protocol: 96 * 97 * Endpoint<PFooParent> parentEp; 98 * Endpoint<PFooChild> childEp; 99 * nsresult rv; 100 * rv = PFoo::CreateEndpoints(&parentEp, &childEp); 101 * 102 * Endpoints can be passed in IPDL messages or sent to other threads using 103 * PostTask. Once an Endpoint has arrived at its destination process and thread, 104 * you need to create the top-level actor and bind it to the endpoint: 105 * 106 * FooParent* parent = new FooParent(); 107 * bool rv1 = parentEp.Bind(parent, processActor); 108 * bool rv2 = parent->SendBar(...); 109 * 110 * (See Bind below for an explanation of processActor.) Once the actor is bound 111 * to the endpoint, it can send and receive messages. 112 * 113 * If creating endpoints for a [NeedsOtherPid] actor, you're required to also 114 * pass in parentPid and childPid, which are the pids of the processes in which 115 * the parent and child endpoints will be used. 116 */ 117 template <class PFooSide> 118 class Endpoint final : public UntypedEndpoint { 119 public: 120 using UntypedEndpoint::IsValid; 121 using UntypedEndpoint::UntypedEndpoint; 122 123 EndpointProcInfo OtherEndpointProcInfo() const { 124 static_assert( 125 endpoint_detail::ActorNeedsOtherPid<PFooSide>, 126 "OtherPid may only be called on Endpoints for actors which are " 127 "[NeedsOtherPid]"); 128 MOZ_RELEASE_ASSERT(mOtherProcInfo != EndpointProcInfo::Invalid()); 129 return mOtherProcInfo; 130 } 131 132 base::ProcessId OtherPid() const { return OtherEndpointProcInfo().mPid; } 133 134 GeckoChildID OtherChildID() const { return OtherEndpointProcInfo().mChildID; } 135 136 // This method binds aActor to this endpoint. After this call, the actor can 137 // be used to send and receive messages. The endpoint becomes invalid. 138 // 139 // If specified, aEventTarget is the target the actor will be bound to, and 140 // must be on the current thread. Otherwise, GetCurrentSerialEventTarget() is 141 // used. 142 bool Bind(PFooSide* aActor, nsISerialEventTarget* aEventTarget = nullptr) { 143 return UntypedEndpoint::Bind(aActor, aEventTarget); 144 } 145 }; 146 147 #if defined(XP_MACOSX) 148 void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error); 149 #else 150 inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, 151 int error) {} 152 #endif 153 154 // This function is used internally to create a pair of Endpoints. See the 155 // comment above Endpoint for a description of how it might be used. 156 template <class PFooParent, class PFooChild> 157 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate, 158 Endpoint<PFooParent>* aParentEndpoint, 159 Endpoint<PFooChild>* aChildEndpoint) { 160 static_assert( 161 !endpoint_detail::ActorNeedsOtherPid<PFooParent> && 162 !endpoint_detail::ActorNeedsOtherPid<PFooChild>, 163 "Pids are required when creating endpoints for [NeedsOtherPid] actors"); 164 165 auto [parentPort, childPort] = 166 NodeController::GetSingleton()->CreatePortPair(); 167 nsID channelId = nsID::GenerateUUID(); 168 *aParentEndpoint = 169 Endpoint<PFooParent>(aPrivate, std::move(parentPort), channelId); 170 *aChildEndpoint = 171 Endpoint<PFooChild>(aPrivate, std::move(childPort), channelId); 172 return NS_OK; 173 } 174 175 template <class PFooParent, class PFooChild> 176 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate, 177 EndpointProcInfo aParentDestProcInfo, 178 EndpointProcInfo aChildDestProcInfo, 179 Endpoint<PFooParent>* aParentEndpoint, 180 Endpoint<PFooChild>* aChildEndpoint) { 181 MOZ_RELEASE_ASSERT(aParentDestProcInfo != EndpointProcInfo::Invalid()); 182 MOZ_RELEASE_ASSERT(aChildDestProcInfo != EndpointProcInfo::Invalid()); 183 184 auto [parentPort, childPort] = 185 NodeController::GetSingleton()->CreatePortPair(); 186 nsID channelId = nsID::GenerateUUID(); 187 *aParentEndpoint = 188 Endpoint<PFooParent>(aPrivate, std::move(parentPort), channelId, 189 aParentDestProcInfo, aChildDestProcInfo); 190 *aChildEndpoint = 191 Endpoint<PFooChild>(aPrivate, std::move(childPort), channelId, 192 aChildDestProcInfo, aParentDestProcInfo); 193 return NS_OK; 194 } 195 196 class UntypedManagedEndpoint { 197 public: 198 bool IsValid() const { return mInner.isSome(); } 199 200 UntypedManagedEndpoint(const UntypedManagedEndpoint&) = delete; 201 UntypedManagedEndpoint& operator=(const UntypedManagedEndpoint&) = delete; 202 203 protected: 204 UntypedManagedEndpoint() = default; 205 explicit UntypedManagedEndpoint(IProtocol* aActor); 206 207 UntypedManagedEndpoint(UntypedManagedEndpoint&& aOther) noexcept 208 : mInner(std::move(aOther.mInner)) { 209 aOther.mInner = Nothing(); 210 } 211 UntypedManagedEndpoint& operator=(UntypedManagedEndpoint&& aOther) noexcept { 212 this->~UntypedManagedEndpoint(); 213 new (this) UntypedManagedEndpoint(std::move(aOther)); 214 return *this; 215 } 216 217 ~UntypedManagedEndpoint() noexcept; 218 219 bool BindCommon(IProtocol* aActor, IRefCountedProtocol* aManager); 220 221 private: 222 friend struct IPC::ParamTraits<UntypedManagedEndpoint>; 223 224 struct Inner { 225 // Pointers to the toplevel actor which will manage this connection. When 226 // created, only `mOtherSide` will be set, and will reference the 227 // toplevel actor which the other side is managed by. After being sent over 228 // IPC, only `mToplevel` will be set, and will be the toplevel actor for the 229 // channel which received the IPC message. 230 RefPtr<WeakActorLifecycleProxy> mOtherSide; 231 RefPtr<WeakActorLifecycleProxy> mToplevel; 232 233 ActorId mId = 0; 234 ProtocolId mType = LastMsgIndex; 235 ActorId mManagerId = 0; 236 ProtocolId mManagerType = LastMsgIndex; 237 }; 238 Maybe<Inner> mInner; 239 }; 240 241 /** 242 * A managed endpoint represents one end of a partially initialized managed 243 * IPDL actor. It is used for situations where the usual IPDL Constructor 244 * methods do not give sufficient control over the construction of actors, such 245 * as when constructing actors within replies, or constructing multiple related 246 * actors simultaneously. 247 * 248 * FooParent* parent = new FooParent(); 249 * ManagedEndpoint<PFooChild> childEp = parentMgr->OpenPFooEndpoint(parent); 250 * 251 * ManagedEndpoints should be sent using IPDL messages or other mechanisms to 252 * the other side of the manager channel. Once the ManagedEndpoint has arrived 253 * at its destination, you can create the actor, and bind it to the endpoint. 254 * 255 * FooChild* child = new FooChild(); 256 * childMgr->BindPFooEndpoint(childEp, child); 257 * 258 * WARNING: If the remote side of an endpoint has not been bound before it 259 * begins to receive messages, an IPC routing error will occur, likely causing 260 * a browser crash. 261 */ 262 template <class PFooSide> 263 class ManagedEndpoint : public UntypedManagedEndpoint { 264 public: 265 ManagedEndpoint() = default; 266 ManagedEndpoint(ManagedEndpoint&&) noexcept = default; 267 ManagedEndpoint& operator=(ManagedEndpoint&&) noexcept = default; 268 269 ManagedEndpoint(const PrivateIPDLInterface&, IProtocol* aActor) 270 : UntypedManagedEndpoint(aActor) {} 271 272 bool Bind(const PrivateIPDLInterface&, PFooSide* aActor, 273 IRefCountedProtocol* aManager) { 274 return BindCommon(aActor, aManager); 275 } 276 277 // Only invalid ManagedEndpoints can be equal, as valid endpoints are unique. 278 bool operator==(const ManagedEndpoint& _o) const { 279 return !IsValid() && !_o.IsValid(); 280 } 281 }; 282 283 } // namespace ipc 284 } // namespace mozilla 285 286 #endif // IPC_GLUE_ENDPOINT_H_