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/UniquePtr.h" 17 #include "mozilla/ipc/MessageLink.h" 18 #include "mozilla/ipc/ProtocolUtils.h" 19 #include "mozilla/ipc/NodeController.h" 20 #include "mozilla/ipc/ScopedPort.h" 21 #include "nsXULAppAPI.h" 22 #include "nscore.h" 23 24 namespace IPC { 25 template <class P> 26 struct ParamTraits; 27 } 28 29 namespace mozilla { 30 namespace ipc { 31 32 namespace endpoint_detail { 33 34 template <class T> 35 static auto ActorNeedsOtherPidHelper(int) 36 -> decltype(std::declval<T>().OtherPid(), std::true_type{}); 37 template <class> 38 static auto ActorNeedsOtherPidHelper(long) -> std::false_type; 39 40 template <typename T> 41 constexpr bool ActorNeedsOtherPid = 42 decltype(ActorNeedsOtherPidHelper<T>(0))::value; 43 44 } // namespace endpoint_detail 45 46 struct PrivateIPDLInterface {}; 47 48 /** 49 * An endpoint represents one end of a partially initialized IPDL channel. To 50 * set up a new top-level protocol: 51 * 52 * Endpoint<PFooParent> parentEp; 53 * Endpoint<PFooChild> childEp; 54 * nsresult rv; 55 * rv = PFoo::CreateEndpoints(&parentEp, &childEp); 56 * 57 * Endpoints can be passed in IPDL messages or sent to other threads using 58 * PostTask. Once an Endpoint has arrived at its destination process and thread, 59 * you need to create the top-level actor and bind it to the endpoint: 60 * 61 * FooParent* parent = new FooParent(); 62 * bool rv1 = parentEp.Bind(parent, processActor); 63 * bool rv2 = parent->SendBar(...); 64 * 65 * (See Bind below for an explanation of processActor.) Once the actor is bound 66 * to the endpoint, it can send and receive messages. 67 * 68 * If creating endpoints for a [NeedsOtherPid] actor, you're required to also 69 * pass in parentPid and childPid, which are the pids of the processes in which 70 * the parent and child endpoints will be used. 71 */ 72 template <class PFooSide> 73 class Endpoint { 74 public: 75 using ProcessId = base::ProcessId; 76 77 Endpoint() = default; 78 79 Endpoint(const PrivateIPDLInterface&, ScopedPort aPort, 80 ProcessId aMyPid = base::kInvalidProcessId, 81 ProcessId aOtherPid = base::kInvalidProcessId) mPort(std::move (aPort))82 : mPort(std::move(aPort)), mMyPid(aMyPid), mOtherPid(aOtherPid) {} 83 84 Endpoint(const Endpoint&) = delete; 85 Endpoint(Endpoint&& aOther) = default; 86 87 Endpoint& operator=(const Endpoint&) = delete; 88 Endpoint& operator=(Endpoint&& aOther) = default; 89 OtherPid()90 base::ProcessId OtherPid() const { 91 static_assert( 92 endpoint_detail::ActorNeedsOtherPid<PFooSide>, 93 "OtherPid may only be called on Endpoints for actors which are " 94 "[NeedsOtherPid]"); 95 MOZ_RELEASE_ASSERT(mOtherPid != base::kInvalidProcessId); 96 return mOtherPid; 97 } 98 99 // This method binds aActor to this endpoint. After this call, the actor can 100 // be used to send and receive messages. The endpoint becomes invalid. Bind(PFooSide * aActor)101 bool Bind(PFooSide* aActor) { 102 MOZ_RELEASE_ASSERT(IsValid()); 103 MOZ_RELEASE_ASSERT(mMyPid == base::kInvalidProcessId || 104 mMyPid == base::GetCurrentProcId()); 105 return aActor->Open(std::move(mPort), mOtherPid); 106 } 107 IsValid()108 bool IsValid() const { return mPort.IsValid(); } 109 110 private: 111 friend struct IPC::ParamTraits<Endpoint<PFooSide>>; 112 113 ScopedPort mPort; 114 ProcessId mMyPid = base::kInvalidProcessId; 115 ProcessId mOtherPid = base::kInvalidProcessId; 116 }; 117 118 #if defined(XP_MACOSX) 119 void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error); 120 #else 121 inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, 122 int error) {} 123 #endif 124 125 // This function is used internally to create a pair of Endpoints. See the 126 // comment above Endpoint for a description of how it might be used. 127 template <class PFooParent, class PFooChild> 128 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate, 129 Endpoint<PFooParent>* aParentEndpoint, 130 Endpoint<PFooChild>* aChildEndpoint) { 131 static_assert( 132 !endpoint_detail::ActorNeedsOtherPid<PFooParent> && 133 !endpoint_detail::ActorNeedsOtherPid<PFooChild>, 134 "Pids are required when creating endpoints for [NeedsOtherPid] actors"); 135 136 auto [parentPort, childPort] = 137 NodeController::GetSingleton()->CreatePortPair(); 138 *aParentEndpoint = Endpoint<PFooParent>(aPrivate, std::move(parentPort)); 139 *aChildEndpoint = Endpoint<PFooChild>(aPrivate, std::move(childPort)); 140 return NS_OK; 141 } 142 143 template <class PFooParent, class PFooChild> 144 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate, 145 base::ProcessId aParentDestPid, 146 base::ProcessId aChildDestPid, 147 Endpoint<PFooParent>* aParentEndpoint, 148 Endpoint<PFooChild>* aChildEndpoint) { 149 MOZ_RELEASE_ASSERT(aParentDestPid != base::kInvalidProcessId); 150 MOZ_RELEASE_ASSERT(aChildDestPid != base::kInvalidProcessId); 151 152 auto [parentPort, childPort] = 153 NodeController::GetSingleton()->CreatePortPair(); 154 *aParentEndpoint = Endpoint<PFooParent>(aPrivate, std::move(parentPort), 155 aParentDestPid, aChildDestPid); 156 *aChildEndpoint = Endpoint<PFooChild>(aPrivate, std::move(childPort), 157 aChildDestPid, aParentDestPid); 158 return NS_OK; 159 } 160 161 class UntypedManagedEndpoint { 162 public: 163 bool IsValid() const { return mInner.isSome(); } 164 165 UntypedManagedEndpoint(const UntypedManagedEndpoint&) = delete; 166 UntypedManagedEndpoint& operator=(const UntypedManagedEndpoint&) = delete; 167 168 protected: 169 UntypedManagedEndpoint() = default; 170 explicit UntypedManagedEndpoint(IProtocol* aActor); 171 172 UntypedManagedEndpoint(UntypedManagedEndpoint&& aOther) noexcept 173 : mInner(std::move(aOther.mInner)) { 174 aOther.mInner = Nothing(); 175 } 176 UntypedManagedEndpoint& operator=(UntypedManagedEndpoint&& aOther) noexcept { 177 this->~UntypedManagedEndpoint(); 178 new (this) UntypedManagedEndpoint(std::move(aOther)); 179 return *this; 180 } 181 182 ~UntypedManagedEndpoint() noexcept; 183 184 bool BindCommon(IProtocol* aActor, IProtocol* aManager); 185 186 private: 187 friend struct IPDLParamTraits<UntypedManagedEndpoint>; 188 189 struct Inner { 190 // Pointers to the toplevel actor which will manage this connection. When 191 // created, only `mOtherSide` will be set, and will reference the 192 // toplevel actor which the other side is managed by. After being sent over 193 // IPC, only `mToplevel` will be set, and will be the toplevel actor for the 194 // channel which received the IPC message. 195 RefPtr<WeakActorLifecycleProxy> mOtherSide; 196 RefPtr<WeakActorLifecycleProxy> mToplevel; 197 198 int32_t mId = 0; 199 ProtocolId mType = LastMsgIndex; 200 int32_t mManagerId = 0; 201 ProtocolId mManagerType = LastMsgIndex; 202 }; 203 Maybe<Inner> mInner; 204 }; 205 206 /** 207 * A managed endpoint represents one end of a partially initialized managed 208 * IPDL actor. It is used for situations where the usual IPDL Constructor 209 * methods do not give sufficient control over the construction of actors, such 210 * as when constructing actors within replies, or constructing multiple related 211 * actors simultaneously. 212 * 213 * FooParent* parent = new FooParent(); 214 * ManagedEndpoint<PFooChild> childEp = parentMgr->OpenPFooEndpoint(parent); 215 * 216 * ManagedEndpoints should be sent using IPDL messages or other mechanisms to 217 * the other side of the manager channel. Once the ManagedEndpoint has arrived 218 * at its destination, you can create the actor, and bind it to the endpoint. 219 * 220 * FooChild* child = new FooChild(); 221 * childMgr->BindPFooEndpoint(childEp, child); 222 * 223 * WARNING: If the remote side of an endpoint has not been bound before it 224 * begins to receive messages, an IPC routing error will occur, likely causing 225 * a browser crash. 226 */ 227 template <class PFooSide> 228 class ManagedEndpoint : public UntypedManagedEndpoint { 229 public: 230 ManagedEndpoint() = default; 231 ManagedEndpoint(ManagedEndpoint&&) noexcept = default; 232 ManagedEndpoint& operator=(ManagedEndpoint&&) noexcept = default; 233 234 ManagedEndpoint(const PrivateIPDLInterface&, IProtocol* aActor) 235 : UntypedManagedEndpoint(aActor) {} 236 237 bool Bind(const PrivateIPDLInterface&, PFooSide* aActor, IProtocol* aManager, 238 ManagedContainer<PFooSide>& aContainer) { 239 if (!BindCommon(aActor, aManager)) { 240 return false; 241 } 242 aContainer.Insert(aActor); 243 return true; 244 } 245 246 // Only invalid ManagedEndpoints can be equal, as valid endpoints are unique. 247 bool operator==(const ManagedEndpoint& _o) const { 248 return !IsValid() && !_o.IsValid(); 249 } 250 }; 251 252 } // namespace ipc 253 } // namespace mozilla 254 255 #endif // IPC_GLUE_ENDPOINT_H_ 256