Bug 1660755 [wpt PR 25207] - [scroll-animations] Allow null source in ScrollTimeline...
[gecko.git] / ipc / glue / ProtocolUtils.h
blob5a685c195b075b73c15acfbcc4186f704cd39535
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/. */
7 #ifndef mozilla_ipc_ProtocolUtils_h
8 #define mozilla_ipc_ProtocolUtils_h 1
10 #include "base/process.h"
11 #include "base/process_util.h"
12 #include "chrome/common/ipc_message_utils.h"
14 #include "prenv.h"
16 #include "IPCMessageStart.h"
17 #include "mozilla/AlreadyAddRefed.h"
18 #include "mozilla/Attributes.h"
19 #include "mozilla/ipc/ByteBuf.h"
20 #include "mozilla/ipc/FileDescriptor.h"
21 #include "mozilla/ipc/MessageChannel.h"
22 #include "mozilla/ipc/Shmem.h"
23 #include "mozilla/ipc/Transport.h"
24 #include "mozilla/ipc/MessageLink.h"
25 #include "mozilla/LinkedList.h"
26 #include "mozilla/Maybe.h"
27 #include "mozilla/MozPromise.h"
28 #include "mozilla/Mutex.h"
29 #include "mozilla/NotNull.h"
30 #include "mozilla/Scoped.h"
31 #include "mozilla/UniquePtr.h"
32 #include "MainThreadUtils.h"
34 #include "nsDataHashtable.h"
35 #include "nsHashKeys.h"
37 #if defined(ANDROID) && defined(DEBUG)
38 # include <android/log.h>
39 #endif
41 template <typename T>
42 class nsTHashtable;
43 template <typename T>
44 class nsPtrHashKey;
46 // WARNING: this takes into account the private, special-message-type
47 // enum in ipc_channel.h. They need to be kept in sync.
48 namespace {
49 // XXX the max message ID is actually kuint32max now ... when this
50 // changed, the assumptions of the special message IDs changed in that
51 // they're not carving out messages from likely-unallocated space, but
52 // rather carving out messages from the end of space allocated to
53 // protocol 0. Oops! We can get away with this until protocol 0
54 // starts approaching its 65,536th message.
55 enum {
56 IMPENDING_SHUTDOWN_MESSAGE_TYPE = kuint16max - 9,
57 BUILD_IDS_MATCH_MESSAGE_TYPE = kuint16max - 8,
58 BUILD_ID_MESSAGE_TYPE = kuint16max - 7, // unused
59 CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 6,
60 SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5,
61 SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 4,
62 GOODBYE_MESSAGE_TYPE = kuint16max - 3,
63 CANCEL_MESSAGE_TYPE = kuint16max - 2,
65 // kuint16max - 1 is used by ipc_channel.h.
68 } // namespace
70 class nsISerialEventTarget;
72 namespace mozilla {
73 class SchedulerGroup;
75 namespace dom {
76 class ContentParent;
77 } // namespace dom
79 namespace net {
80 class NeckoParent;
81 } // namespace net
83 namespace ipc {
85 #ifdef FUZZING
86 class ProtocolFuzzerHelper;
87 #endif
89 class MessageChannel;
91 #ifdef XP_WIN
92 const base::ProcessHandle kInvalidProcessHandle = INVALID_HANDLE_VALUE;
94 // In theory, on Windows, this is a valid process ID, but in practice they are
95 // currently divisible by four. Process IDs share the kernel handle allocation
96 // code and they are guaranteed to be divisible by four.
97 // As this could change for process IDs we shouldn't generally rely on this
98 // property, however even if that were to change, it seems safe to rely on this
99 // particular value never being used.
100 const base::ProcessId kInvalidProcessId = kuint32max;
101 #else
102 const base::ProcessHandle kInvalidProcessHandle = -1;
103 const base::ProcessId kInvalidProcessId = -1;
104 #endif
106 // Scoped base::ProcessHandle to ensure base::CloseProcessHandle is called.
107 struct ScopedProcessHandleTraits {
108 typedef base::ProcessHandle type;
110 static type empty() { return kInvalidProcessHandle; }
112 static void release(type aProcessHandle) {
113 if (aProcessHandle && aProcessHandle != kInvalidProcessHandle) {
114 base::CloseProcessHandle(aProcessHandle);
118 typedef mozilla::Scoped<ScopedProcessHandleTraits> ScopedProcessHandle;
120 class ProtocolFdMapping;
121 class ProtocolCloneContext;
123 // Used to pass references to protocol actors across the wire.
124 // Actors created on the parent-side have a positive ID, and actors
125 // allocated on the child side have a negative ID.
126 struct ActorHandle {
127 int mId;
130 // What happens if Interrupt calls race?
131 enum RacyInterruptPolicy { RIPError, RIPChildWins, RIPParentWins };
133 enum class LinkStatus : uint8_t {
134 // The actor has not established a link yet, or the actor is no longer in use
135 // by IPC, and its 'Dealloc' method has been called or is being called.
137 // NOTE: This state is used instead of an explicit `Freed` state when IPC no
138 // longer holds references to the current actor as we currently re-open
139 // existing actors. Once we fix these poorly behaved actors, this loopback
140 // state can be split to have the final state not be the same as the initial
141 // state.
142 Inactive,
144 // A live link is connected to the other side of this actor.
145 Connected,
147 // The link has begun being destroyed. Messages may still be received, but
148 // cannot be sent. (exception: sync/intr replies may be sent while Doomed).
149 Doomed,
151 // The link has been destroyed, and messages will no longer be sent or
152 // received.
153 Destroyed,
156 typedef IPCMessageStart ProtocolId;
158 // Generated by IPDL compiler
159 const char* ProtocolIdToName(IPCMessageStart aId);
161 class IToplevelProtocol;
162 class ActorLifecycleProxy;
164 class IProtocol : public HasResultCodes {
165 public:
166 enum ActorDestroyReason {
167 FailedConstructor,
168 Deletion,
169 AncestorDeletion,
170 NormalShutdown,
171 AbnormalShutdown
174 typedef base::ProcessId ProcessId;
175 typedef IPC::Message Message;
176 typedef IPC::MessageInfo MessageInfo;
178 IProtocol(ProtocolId aProtoId, Side aSide)
179 : mId(0),
180 mProtocolId(aProtoId),
181 mSide(aSide),
182 mLinkStatus(LinkStatus::Inactive),
183 mLifecycleProxy(nullptr),
184 mManager(nullptr),
185 mToplevel(nullptr) {}
187 IToplevelProtocol* ToplevelProtocol() { return mToplevel; }
189 // The following methods either directly forward to the toplevel protocol, or
190 // almost directly do.
191 int32_t Register(IProtocol* aRouted);
192 int32_t RegisterID(IProtocol* aRouted, int32_t aId);
193 IProtocol* Lookup(int32_t aId);
194 void Unregister(int32_t aId);
196 Shmem::SharedMemory* CreateSharedMemory(size_t aSize,
197 SharedMemory::SharedMemoryType aType,
198 bool aUnsafe, int32_t* aId);
199 Shmem::SharedMemory* LookupSharedMemory(int32_t aId);
200 bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
201 bool DestroySharedMemory(Shmem& aShmem);
203 MessageChannel* GetIPCChannel();
204 const MessageChannel* GetIPCChannel() const;
206 // Sets an event target to which all messages for aActor will be
207 // dispatched. This method must be called before right before the SendPFoo
208 // message for aActor is sent. And SendPFoo *must* be called if
209 // SetEventTargetForActor is called. The receiver when calling
210 // SetEventTargetForActor must be the actor that will be the manager for
211 // aActor.
212 void SetEventTargetForActor(IProtocol* aActor,
213 nsISerialEventTarget* aEventTarget);
215 // Replace the event target for the messages of aActor. There must not be
216 // any messages of aActor in the task queue, or we might run into some
217 // unexpected behavior.
218 void ReplaceEventTargetForActor(IProtocol* aActor,
219 nsISerialEventTarget* aEventTarget);
221 nsISerialEventTarget* GetActorEventTarget();
222 already_AddRefed<nsISerialEventTarget> GetActorEventTarget(IProtocol* aActor);
224 ProcessId OtherPid() const;
226 // Actor lifecycle and other properties.
227 ProtocolId GetProtocolId() const { return mProtocolId; }
228 const char* GetProtocolName() const { return ProtocolIdToName(mProtocolId); }
230 int32_t Id() const { return mId; }
231 IProtocol* Manager() const { return mManager; }
233 ActorLifecycleProxy* GetLifecycleProxy() { return mLifecycleProxy; }
235 Side GetSide() const { return mSide; }
236 bool CanSend() const { return mLinkStatus == LinkStatus::Connected; }
237 bool CanRecv() const {
238 return mLinkStatus == LinkStatus::Connected ||
239 mLinkStatus == LinkStatus::Doomed;
242 // Remove or deallocate a managee given its type.
243 virtual void RemoveManagee(int32_t, IProtocol*) = 0;
244 virtual void DeallocManagee(int32_t, IProtocol*) = 0;
246 Maybe<IProtocol*> ReadActor(const IPC::Message* aMessage,
247 PickleIterator* aIter, bool aNullable,
248 const char* aActorDescription,
249 int32_t aProtocolTypeId);
251 virtual Result OnMessageReceived(const Message& aMessage) = 0;
252 virtual Result OnMessageReceived(const Message& aMessage,
253 Message*& aReply) = 0;
254 virtual Result OnCallReceived(const Message& aMessage, Message*& aReply) = 0;
255 bool AllocShmem(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType,
256 Shmem* aOutMem);
257 bool AllocUnsafeShmem(size_t aSize,
258 Shmem::SharedMemory::SharedMemoryType aType,
259 Shmem* aOutMem);
260 bool DeallocShmem(Shmem& aMem);
262 void FatalError(const char* const aErrorMsg) const;
263 virtual void HandleFatalError(const char* aErrorMsg) const;
265 protected:
266 virtual ~IProtocol();
268 friend class IToplevelProtocol;
269 friend class ActorLifecycleProxy;
271 void SetId(int32_t aId);
273 // We have separate functions because the accessibility code manually
274 // calls SetManager.
275 void SetManager(IProtocol* aManager);
277 // Sets the manager for the protocol and registers the protocol with
278 // its manager, setting up channels for the protocol as well. Not
279 // for use outside of IPDL.
280 void SetManagerAndRegister(IProtocol* aManager);
281 void SetManagerAndRegister(IProtocol* aManager, int32_t aId);
283 // Helpers for calling `Send` on our underlying IPC channel.
284 bool ChannelSend(IPC::Message* aMsg);
285 bool ChannelSend(IPC::Message* aMsg, IPC::Message* aReply);
286 bool ChannelCall(IPC::Message* aMsg, IPC::Message* aReply);
287 template <typename Value>
288 void ChannelSend(IPC::Message* aMsg, ResolveCallback<Value>&& aResolve,
289 RejectCallback&& aReject) {
290 UniquePtr<IPC::Message> msg(aMsg);
291 if (CanSend()) {
292 GetIPCChannel()->Send(std::move(msg), this, std::move(aResolve),
293 std::move(aReject));
294 } else {
295 NS_WARNING("IPC message discarded: actor cannot send");
296 aReject(ResponseRejectReason::SendError);
300 // Collect all actors managed by this object in an array. To make this safer
301 // to iterate, `ActorLifecycleProxy` references are returned rather than raw
302 // actor pointers.
303 virtual void AllManagedActors(
304 nsTArray<RefPtr<ActorLifecycleProxy>>& aActors) const = 0;
306 // Internal method called when the actor becomes connected.
307 void ActorConnected();
309 // Called immediately before setting the actor state to doomed, and triggering
310 // async actor destruction. Messages may be sent from this callback, but no
311 // later.
312 // FIXME(nika): This is currently unused!
313 virtual void ActorDoom() {}
314 void DoomSubtree();
316 // Called when the actor has been destroyed due to an error, a __delete__
317 // message, or a __doom__ reply.
318 virtual void ActorDestroy(ActorDestroyReason aWhy) {}
319 void DestroySubtree(ActorDestroyReason aWhy);
321 // Called when IPC has acquired its first reference to the actor. This method
322 // may take references which will later be freed by `ActorDealloc`.
323 virtual void ActorAlloc() {}
325 // Called when IPC has released its final reference to the actor. It will call
326 // the dealloc method, causing the actor to be actually freed.
328 // The actor has been freed after this method returns.
329 virtual void ActorDealloc() {
330 if (Manager()) {
331 Manager()->DeallocManagee(mProtocolId, this);
335 static const int32_t kNullActorId = 0;
336 static const int32_t kFreedActorId = 1;
338 private:
339 int32_t mId;
340 ProtocolId mProtocolId;
341 Side mSide;
342 LinkStatus mLinkStatus;
343 ActorLifecycleProxy* mLifecycleProxy;
344 IProtocol* mManager;
345 IToplevelProtocol* mToplevel;
348 #define IPC_OK() mozilla::ipc::IPCResult::Ok()
349 #define IPC_FAIL(actor, why) \
350 mozilla::ipc::IPCResult::Fail(WrapNotNull(actor), __func__, (why))
351 #define IPC_FAIL_NO_REASON(actor) \
352 mozilla::ipc::IPCResult::Fail(WrapNotNull(actor), __func__)
355 * All message deserializer and message handler should return this
356 * type via above macros. We use a less generic name here to avoid
357 * conflict with mozilla::Result because we have quite a few using
358 * namespace mozilla::ipc; in the code base.
360 class IPCResult {
361 public:
362 static IPCResult Ok() { return IPCResult(true); }
363 static IPCResult Fail(NotNull<IProtocol*> aActor, const char* aWhere,
364 const char* aWhy = "");
365 MOZ_IMPLICIT operator bool() const { return mSuccess; }
367 private:
368 explicit IPCResult(bool aResult) : mSuccess(aResult) {}
369 bool mSuccess;
372 template <class PFooSide>
373 class Endpoint;
376 * All top-level protocols should inherit this class.
378 * IToplevelProtocol tracks all top-level protocol actors created from
379 * this protocol actor.
381 class IToplevelProtocol : public IProtocol {
382 #ifdef FUZZING
383 friend class mozilla::ipc::ProtocolFuzzerHelper;
384 #endif
386 template <class PFooSide>
387 friend class Endpoint;
389 protected:
390 explicit IToplevelProtocol(const char* aName, ProtocolId aProtoId,
391 Side aSide);
392 ~IToplevelProtocol() = default;
394 public:
395 // Shadow methods on IProtocol which are implemented directly on toplevel
396 // actors.
397 int32_t Register(IProtocol* aRouted);
398 int32_t RegisterID(IProtocol* aRouted, int32_t aId);
399 IProtocol* Lookup(int32_t aId);
400 void Unregister(int32_t aId);
402 Shmem::SharedMemory* CreateSharedMemory(size_t aSize,
403 SharedMemory::SharedMemoryType aType,
404 bool aUnsafe, int32_t* aId);
405 Shmem::SharedMemory* LookupSharedMemory(int32_t aId);
406 bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
407 bool DestroySharedMemory(Shmem& aShmem);
409 MessageChannel* GetIPCChannel() { return &mChannel; }
410 const MessageChannel* GetIPCChannel() const { return &mChannel; }
412 // NOTE: The target actor's Manager must already be set.
413 void SetEventTargetForActorInternal(IProtocol* aActor,
414 nsISerialEventTarget* aEventTarget);
415 void ReplaceEventTargetForActor(IProtocol* aActor,
416 nsISerialEventTarget* aEventTarget);
417 nsISerialEventTarget* GetActorEventTarget();
418 already_AddRefed<nsISerialEventTarget> GetActorEventTarget(IProtocol* aActor);
420 ProcessId OtherPid() const;
421 void SetOtherProcessId(base::ProcessId aOtherPid);
423 virtual void OnChannelClose() = 0;
424 virtual void OnChannelError() = 0;
425 virtual void ProcessingError(Result aError, const char* aMsgName) {}
426 virtual void OnChannelConnected(int32_t peer_pid) {}
428 bool Open(UniquePtr<Transport> aTransport, base::ProcessId aOtherPid,
429 MessageLoop* aThread = nullptr,
430 mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
432 bool Open(MessageChannel* aChannel, nsISerialEventTarget* aEventTarget,
433 mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
435 // Open a toplevel actor such that both ends of the actor's channel are on
436 // the same thread. This method should be called on the thread to perform
437 // the link.
439 // WARNING: Attempting to send a sync or intr message on the same thread
440 // will crash.
441 bool OpenOnSameThread(MessageChannel* aChannel,
442 mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
445 * This sends a special message that is processed on the IO thread, so that
446 * other actors can know that the process will soon shutdown.
448 void NotifyImpendingShutdown();
450 void Close();
452 void SetReplyTimeoutMs(int32_t aTimeoutMs);
454 void DeallocShmems();
455 bool ShmemCreated(const Message& aMsg);
456 bool ShmemDestroyed(const Message& aMsg);
458 virtual bool ShouldContinueFromReplyTimeout() { return false; }
460 // WARNING: This function is called with the MessageChannel monitor held.
461 virtual void IntentionalCrash() { MOZ_CRASH("Intentional IPDL crash"); }
463 // The code here is only useful for fuzzing. It should not be used for any
464 // other purpose.
465 #ifdef DEBUG
466 // Returns true if we should simulate a timeout.
467 // WARNING: This is a testing-only function that is called with the
468 // MessageChannel monitor held. Don't do anything fancy here or we could
469 // deadlock.
470 virtual bool ArtificialTimeout() { return false; }
472 // Returns true if we want to cause the worker thread to sleep with the
473 // monitor unlocked.
474 virtual bool NeedArtificialSleep() { return false; }
476 // This function should be implemented to sleep for some amount of time on
477 // the worker thread. Will only be called if NeedArtificialSleep() returns
478 // true.
479 virtual void ArtificialSleep() {}
480 #else
481 bool ArtificialTimeout() { return false; }
482 bool NeedArtificialSleep() { return false; }
483 void ArtificialSleep() {}
484 #endif
486 virtual void EnteredCxxStack() {}
487 virtual void ExitedCxxStack() {}
488 virtual void EnteredCall() {}
489 virtual void ExitedCall() {}
491 bool IsOnCxxStack() const;
493 virtual RacyInterruptPolicy MediateInterruptRace(const MessageInfo& parent,
494 const MessageInfo& child) {
495 return RIPChildWins;
499 * Return true if windows messages can be handled while waiting for a reply
500 * to a sync IPDL message.
502 virtual bool HandleWindowsMessages(const Message& aMsg) const { return true; }
504 virtual void OnEnteredSyncSend() {}
505 virtual void OnExitedSyncSend() {}
507 virtual void ProcessRemoteNativeEventsInInterruptCall() {}
509 virtual void OnChannelReceivedMessage(const Message& aMsg) {}
511 void OnIPCChannelOpened() { ActorConnected(); }
513 already_AddRefed<nsISerialEventTarget> GetMessageEventTarget(
514 const Message& aMsg);
516 private:
517 base::ProcessId OtherPidMaybeInvalid() const { return mOtherPid; }
519 int32_t NextId();
521 template <class T>
522 using IDMap = nsDataHashtable<nsUint32HashKey, T>;
524 base::ProcessId mOtherPid;
526 // NOTE NOTE NOTE
527 // Used to be on mState
528 int32_t mLastLocalId;
529 IDMap<IProtocol*> mActorMap;
530 IDMap<Shmem::SharedMemory*> mShmemMap;
532 // XXX: We no longer need mEventTargetMap for Quantum DOM, so it may be
533 // worthwhile to remove it before people start depending on it for other weird
534 // things.
535 Mutex mEventTargetMutex;
536 IDMap<nsCOMPtr<nsISerialEventTarget>> mEventTargetMap;
538 MessageChannel mChannel;
541 class IShmemAllocator {
542 public:
543 virtual bool AllocShmem(size_t aSize,
544 mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
545 mozilla::ipc::Shmem* aShmem) = 0;
546 virtual bool AllocUnsafeShmem(
547 size_t aSize, mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
548 mozilla::ipc::Shmem* aShmem) = 0;
549 virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) = 0;
552 #define FORWARD_SHMEM_ALLOCATOR_TO(aImplClass) \
553 virtual bool AllocShmem( \
554 size_t aSize, mozilla::ipc::SharedMemory::SharedMemoryType aShmType, \
555 mozilla::ipc::Shmem* aShmem) override { \
556 return aImplClass::AllocShmem(aSize, aShmType, aShmem); \
558 virtual bool AllocUnsafeShmem( \
559 size_t aSize, mozilla::ipc::SharedMemory::SharedMemoryType aShmType, \
560 mozilla::ipc::Shmem* aShmem) override { \
561 return aImplClass::AllocUnsafeShmem(aSize, aShmType, aShmem); \
563 virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override { \
564 return aImplClass::DeallocShmem(aShmem); \
567 inline bool LoggingEnabled() {
568 #if defined(DEBUG) || defined(FUZZING)
569 return !!PR_GetEnv("MOZ_IPC_MESSAGE_LOG");
570 #else
571 return false;
572 #endif
575 #if defined(DEBUG) || defined(FUZZING)
576 bool LoggingEnabledFor(const char* aTopLevelProtocol, const char* aFilter);
577 #endif
579 inline bool LoggingEnabledFor(const char* aTopLevelProtocol) {
580 #if defined(DEBUG) || defined(FUZZING)
581 return LoggingEnabledFor(aTopLevelProtocol, PR_GetEnv("MOZ_IPC_MESSAGE_LOG"));
582 #else
583 return false;
584 #endif
587 MOZ_NEVER_INLINE void LogMessageForProtocol(const char* aTopLevelProtocol,
588 base::ProcessId aOtherPid,
589 const char* aContextDescription,
590 uint32_t aMessageId,
591 MessageDirection aDirection);
593 MOZ_NEVER_INLINE void ProtocolErrorBreakpoint(const char* aMsg);
595 // The code generator calls this function for errors which come from the
596 // methods of protocols. Doing this saves codesize by making the error
597 // cases significantly smaller.
598 MOZ_NEVER_INLINE void FatalError(const char* aMsg, bool aIsParent);
600 // The code generator calls this function for errors which are not
601 // protocol-specific: errors in generated struct methods or errors in
602 // transition functions, for instance. Doing this saves codesize by
603 // by making the error cases significantly smaller.
604 MOZ_NEVER_INLINE void LogicError(const char* aMsg);
606 MOZ_NEVER_INLINE void ActorIdReadError(const char* aActorDescription);
608 MOZ_NEVER_INLINE void BadActorIdError(const char* aActorDescription);
610 MOZ_NEVER_INLINE void ActorLookupError(const char* aActorDescription);
612 MOZ_NEVER_INLINE void MismatchedActorTypeError(const char* aActorDescription);
614 MOZ_NEVER_INLINE void UnionTypeReadError(const char* aUnionName);
616 MOZ_NEVER_INLINE void ArrayLengthReadError(const char* aElementName);
618 MOZ_NEVER_INLINE void SentinelReadError(const char* aElementName);
620 struct PrivateIPDLInterface {};
622 #if defined(XP_WIN)
623 // This is a restricted version of Windows' DuplicateHandle() function
624 // that works inside the sandbox and can send handles but not retrieve
625 // them. Unlike DuplicateHandle(), it takes a process ID rather than
626 // a process handle. It returns true on success, false otherwise.
627 bool DuplicateHandle(HANDLE aSourceHandle, DWORD aTargetProcessId,
628 HANDLE* aTargetHandle, DWORD aDesiredAccess,
629 DWORD aOptions);
630 #endif
633 * Annotate the crash reporter with the error code from the most recent system
634 * call. Returns the system error.
636 void AnnotateSystemError();
639 * An endpoint represents one end of a partially initialized IPDL channel. To
640 * set up a new top-level protocol:
642 * Endpoint<PFooParent> parentEp;
643 * Endpoint<PFooChild> childEp;
644 * nsresult rv;
645 * rv = PFoo::CreateEndpoints(parentPid, childPid, &parentEp, &childEp);
647 * You're required to pass in parentPid and childPid, which are the pids of the
648 * processes in which the parent and child endpoints will be used.
650 * Endpoints can be passed in IPDL messages or sent to other threads using
651 * PostTask. Once an Endpoint has arrived at its destination process and thread,
652 * you need to create the top-level actor and bind it to the endpoint:
654 * FooParent* parent = new FooParent();
655 * bool rv1 = parentEp.Bind(parent, processActor);
656 * bool rv2 = parent->SendBar(...);
658 * (See Bind below for an explanation of processActor.) Once the actor is bound
659 * to the endpoint, it can send and receive messages.
661 template <class PFooSide>
662 class Endpoint {
663 public:
664 typedef base::ProcessId ProcessId;
666 Endpoint()
667 : mValid(false),
668 mMode(static_cast<mozilla::ipc::Transport::Mode>(0)),
669 mMyPid(0),
670 mOtherPid(0) {}
672 Endpoint(const PrivateIPDLInterface&, mozilla::ipc::Transport::Mode aMode,
673 TransportDescriptor aTransport, ProcessId aMyPid,
674 ProcessId aOtherPid)
675 : mValid(true),
676 mMode(aMode),
677 mTransport(aTransport),
678 mMyPid(aMyPid),
679 mOtherPid(aOtherPid) {}
681 Endpoint(Endpoint&& aOther)
682 : mValid(aOther.mValid),
683 mTransport(aOther.mTransport),
684 mMyPid(aOther.mMyPid),
685 mOtherPid(aOther.mOtherPid) {
686 if (aOther.mValid) {
687 mMode = aOther.mMode;
689 aOther.mValid = false;
692 Endpoint& operator=(Endpoint&& aOther) {
693 mValid = aOther.mValid;
694 if (aOther.mValid) {
695 mMode = aOther.mMode;
697 mTransport = aOther.mTransport;
698 mMyPid = aOther.mMyPid;
699 mOtherPid = aOther.mOtherPid;
701 aOther.mValid = false;
702 return *this;
705 ~Endpoint() {
706 if (mValid) {
707 CloseDescriptor(mTransport);
711 ProcessId OtherPid() const { return mOtherPid; }
713 // This method binds aActor to this endpoint. After this call, the actor can
714 // be used to send and receive messages. The endpoint becomes invalid.
715 bool Bind(PFooSide* aActor) {
716 MOZ_RELEASE_ASSERT(mValid);
717 MOZ_RELEASE_ASSERT(mMyPid == base::GetCurrentProcId());
719 UniquePtr<Transport> transport =
720 mozilla::ipc::OpenDescriptor(mTransport, mMode);
721 if (!transport) {
722 return false;
724 if (!aActor->Open(
725 std::move(transport), mOtherPid, XRE_GetIOMessageLoop(),
726 mMode == Transport::MODE_SERVER ? ParentSide : ChildSide)) {
727 return false;
729 mValid = false;
730 return true;
733 bool IsValid() const { return mValid; }
735 private:
736 friend struct IPC::ParamTraits<Endpoint<PFooSide>>;
738 Endpoint(const Endpoint&) = delete;
739 Endpoint& operator=(const Endpoint&) = delete;
741 bool mValid;
742 mozilla::ipc::Transport::Mode mMode;
743 TransportDescriptor mTransport;
744 ProcessId mMyPid, mOtherPid;
747 #if defined(XP_MACOSX)
748 void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error);
749 #else
750 static inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag,
751 int error) {}
752 #endif
754 // This function is used internally to create a pair of Endpoints. See the
755 // comment above Endpoint for a description of how it might be used.
756 template <class PFooParent, class PFooChild>
757 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate,
758 base::ProcessId aParentDestPid,
759 base::ProcessId aChildDestPid,
760 Endpoint<PFooParent>* aParentEndpoint,
761 Endpoint<PFooChild>* aChildEndpoint) {
762 MOZ_RELEASE_ASSERT(aParentDestPid);
763 MOZ_RELEASE_ASSERT(aChildDestPid);
765 TransportDescriptor parentTransport, childTransport;
766 nsresult rv;
767 if (NS_FAILED(rv = CreateTransport(aParentDestPid, &parentTransport,
768 &childTransport))) {
769 AnnotateCrashReportWithErrno(
770 CrashReporter::Annotation::IpcCreateEndpointsNsresult, int(rv));
771 return rv;
774 *aParentEndpoint =
775 Endpoint<PFooParent>(aPrivate, mozilla::ipc::Transport::MODE_SERVER,
776 parentTransport, aParentDestPid, aChildDestPid);
778 *aChildEndpoint =
779 Endpoint<PFooChild>(aPrivate, mozilla::ipc::Transport::MODE_CLIENT,
780 childTransport, aChildDestPid, aParentDestPid);
782 return NS_OK;
786 * A managed endpoint represents one end of a partially initialized managed
787 * IPDL actor. It is used for situations where the usual IPDL Constructor
788 * methods do not give sufficient control over the construction of actors, such
789 * as when constructing actors within replies, or constructing multiple related
790 * actors simultaneously.
792 * FooParent* parent = new FooParent();
793 * ManagedEndpoint<PFooChild> childEp = parentMgr->OpenPFooEndpoint(parent);
795 * ManagedEndpoints should be sent using IPDL messages or other mechanisms to
796 * the other side of the manager channel. Once the ManagedEndpoint has arrived
797 * at its destination, you can create the actor, and bind it to the endpoint.
799 * FooChild* child = new FooChild();
800 * childMgr->BindPFooEndpoint(childEp, child);
802 * WARNING: If the remote side of an endpoint has not been bound before it
803 * begins to receive messages, an IPC routing error will occur, likely causing
804 * a browser crash.
806 template <class PFooSide>
807 class ManagedEndpoint {
808 public:
809 ManagedEndpoint() : mId(0) {}
811 ManagedEndpoint(const PrivateIPDLInterface&, int32_t aId) : mId(aId) {}
813 ManagedEndpoint(ManagedEndpoint&& aOther) : mId(aOther.mId) {
814 aOther.mId = 0;
817 ManagedEndpoint& operator=(ManagedEndpoint&& aOther) {
818 mId = aOther.mId;
819 aOther.mId = 0;
820 return *this;
823 bool IsValid() const { return mId != 0; }
825 Maybe<int32_t> ActorId() const { return IsValid() ? Some(mId) : Nothing(); }
827 bool operator==(const ManagedEndpoint& _o) const { return mId == _o.mId; }
829 private:
830 friend struct IPC::ParamTraits<ManagedEndpoint<PFooSide>>;
832 ManagedEndpoint(const ManagedEndpoint&) = delete;
833 ManagedEndpoint& operator=(const ManagedEndpoint&) = delete;
835 // The routing ID for the to-be-created endpoint.
836 int32_t mId;
838 // XXX(nika): Might be nice to have other info for assertions?
839 // e.g. mManagerId, mManagerType, etc.
842 // The ActorLifecycleProxy is a helper type used internally by IPC to maintain a
843 // maybe-owning reference to an IProtocol object. For well-behaved actors
844 // which are not freed until after their `Dealloc` method is called, a
845 // reference to an actor's `ActorLifecycleProxy` object is an owning one, as the
846 // `Dealloc` method will only be called when all references to the
847 // `ActorLifecycleProxy` are released.
849 // Unfortunately, some actors may be destroyed before their `Dealloc` method
850 // is called. For these actors, `ActorLifecycleProxy` acts as a weak pointer,
851 // and will begin to return `nullptr` from its `Get()` method once the
852 // corresponding actor object has been destroyed.
854 // When calling a `Recv` method, IPC will hold a `ActorLifecycleProxy` reference
855 // to the target actor, meaning that well-behaved actors can behave as though a
856 // strong reference is being held.
858 // Generic IPC code MUST treat ActorLifecycleProxy references as weak
859 // references!
860 class ActorLifecycleProxy {
861 public:
862 NS_INLINE_DECL_REFCOUNTING_ONEVENTTARGET(ActorLifecycleProxy)
864 IProtocol* Get() { return mActor; }
866 private:
867 friend class IProtocol;
869 explicit ActorLifecycleProxy(IProtocol* aActor);
870 ~ActorLifecycleProxy();
872 ActorLifecycleProxy(const ActorLifecycleProxy&) = delete;
873 ActorLifecycleProxy& operator=(const ActorLifecycleProxy&) = delete;
875 IProtocol* MOZ_NON_OWNING_REF mActor;
877 // Hold a reference to the actor's manager's ActorLifecycleProxy to help
878 // prevent it from dying while we're still alive!
879 RefPtr<ActorLifecycleProxy> mManager;
882 void TableToArray(const nsTHashtable<nsPtrHashKey<void>>& aTable,
883 nsTArray<void*>& aArray);
885 } // namespace ipc
887 template <typename Protocol>
888 class ManagedContainer : public nsTHashtable<nsPtrHashKey<Protocol>> {
889 typedef nsTHashtable<nsPtrHashKey<Protocol>> BaseClass;
891 public:
892 // Having the core logic work on void pointers, rather than typed pointers,
893 // means that we can have one instance of this code out-of-line, rather
894 // than several hundred instances of this code out-of-lined. (Those
895 // repeated instances don't necessarily get folded together by the linker
896 // because they contain member offsets and such that differ between the
897 // functions.) We do have to pay for it with some eye-bleedingly bad casts,
898 // though.
899 void ToArray(nsTArray<Protocol*>& aArray) const {
900 ::mozilla::ipc::TableToArray(
901 *reinterpret_cast<const nsTHashtable<nsPtrHashKey<void>>*>(
902 static_cast<const BaseClass*>(this)),
903 reinterpret_cast<nsTArray<void*>&>(aArray));
907 template <typename Protocol>
908 Protocol* LoneManagedOrNullAsserts(
909 const ManagedContainer<Protocol>& aManagees) {
910 if (aManagees.IsEmpty()) {
911 return nullptr;
913 MOZ_ASSERT(aManagees.Count() == 1);
914 return aManagees.ConstIter().Get()->GetKey();
917 template <typename Protocol>
918 Protocol* SingleManagedOrNull(const ManagedContainer<Protocol>& aManagees) {
919 if (aManagees.Count() != 1) {
920 return nullptr;
922 return aManagees.ConstIter().Get()->GetKey();
925 } // namespace mozilla
927 namespace IPC {
929 template <>
930 struct ParamTraits<mozilla::ipc::ActorHandle> {
931 typedef mozilla::ipc::ActorHandle paramType;
933 static void Write(Message* aMsg, const paramType& aParam) {
934 IPC::WriteParam(aMsg, aParam.mId);
937 static bool Read(const Message* aMsg, PickleIterator* aIter,
938 paramType* aResult) {
939 int id;
940 if (IPC::ReadParam(aMsg, aIter, &id)) {
941 aResult->mId = id;
942 return true;
944 return false;
947 static void Log(const paramType& aParam, std::wstring* aLog) {
948 aLog->append(StringPrintf(L"(%d)", aParam.mId));
952 template <class PFooSide>
953 struct ParamTraits<mozilla::ipc::Endpoint<PFooSide>> {
954 typedef mozilla::ipc::Endpoint<PFooSide> paramType;
956 static void Write(Message* aMsg, const paramType& aParam) {
957 IPC::WriteParam(aMsg, aParam.mValid);
958 if (!aParam.mValid) {
959 return;
962 IPC::WriteParam(aMsg, static_cast<uint32_t>(aParam.mMode));
964 // We duplicate the descriptor so that our own file descriptor remains
965 // valid after the write. An alternative would be to set
966 // aParam.mTransport.mValid to false, but that won't work because aParam
967 // is const.
968 mozilla::ipc::TransportDescriptor desc =
969 mozilla::ipc::DuplicateDescriptor(aParam.mTransport);
970 IPC::WriteParam(aMsg, desc);
972 IPC::WriteParam(aMsg, aParam.mMyPid);
973 IPC::WriteParam(aMsg, aParam.mOtherPid);
976 static bool Read(const Message* aMsg, PickleIterator* aIter,
977 paramType* aResult) {
978 MOZ_RELEASE_ASSERT(!aResult->mValid);
980 if (!IPC::ReadParam(aMsg, aIter, &aResult->mValid)) {
981 return false;
983 if (!aResult->mValid) {
984 // Object is empty, but read succeeded.
985 return true;
988 uint32_t mode;
989 if (!IPC::ReadParam(aMsg, aIter, &mode) ||
990 !IPC::ReadParam(aMsg, aIter, &aResult->mTransport) ||
991 !IPC::ReadParam(aMsg, aIter, &aResult->mMyPid) ||
992 !IPC::ReadParam(aMsg, aIter, &aResult->mOtherPid)) {
993 return false;
995 aResult->mMode = Channel::Mode(mode);
996 return true;
999 static void Log(const paramType& aParam, std::wstring* aLog) {
1000 aLog->append(StringPrintf(L"Endpoint"));
1004 template <class PFooSide>
1005 struct ParamTraits<mozilla::ipc::ManagedEndpoint<PFooSide>> {
1006 typedef mozilla::ipc::ManagedEndpoint<PFooSide> paramType;
1008 static void Write(Message* aMsg, const paramType& aParam) {
1009 IPC::WriteParam(aMsg, aParam.mId);
1012 static bool Read(const Message* aMsg, PickleIterator* aIter,
1013 paramType* aResult) {
1014 MOZ_RELEASE_ASSERT(aResult->mId == 0);
1016 if (!IPC::ReadParam(aMsg, aIter, &aResult->mId)) {
1017 return false;
1019 return true;
1022 static void Log(const paramType& aParam, std::wstring* aLog) {
1023 aLog->append(StringPrintf(L"ManagedEndpoint"));
1027 } // namespace IPC
1029 #endif // mozilla_ipc_ProtocolUtils_h