Bug 1883706: part 3) Implement `createHTML`, `createScript` and `createScriptURL...
[gecko.git] / ipc / glue / ProtocolUtils.cpp
blobb29221d476cf5645f33c60c5835dd2206f660cca
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 http://mozilla.org/MPL/2.0/. */
7 #include "base/process_util.h"
8 #include "base/task.h"
10 #ifdef XP_UNIX
11 # include <errno.h>
12 #endif
13 #include <type_traits>
15 #include "mozilla/IntegerPrintfMacros.h"
17 #include "mozilla/ipc/ProtocolMessageUtils.h"
18 #include "mozilla/ipc/ProtocolUtils.h"
20 #include "mozilla/ipc/MessageChannel.h"
21 #include "mozilla/ipc/IPDLParamTraits.h"
22 #include "mozilla/StaticMutex.h"
23 #if defined(DEBUG) || defined(FUZZING)
24 # include "mozilla/Tokenizer.h"
25 #endif
26 #include "mozilla/Unused.h"
27 #include "nsPrintfCString.h"
28 #include "nsReadableUtils.h"
30 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
31 # include "mozilla/sandboxTarget.h"
32 #endif
34 #if defined(XP_WIN)
35 # include "aclapi.h"
36 # include "sddl.h"
37 #endif
39 #ifdef FUZZING_SNAPSHOT
40 # include "mozilla/fuzzing/IPCFuzzController.h"
41 #endif
43 using namespace IPC;
45 using base::GetCurrentProcId;
46 using base::ProcessHandle;
47 using base::ProcessId;
49 namespace mozilla {
50 namespace ipc {
52 /* static */
53 IPCResult IPCResult::FailImpl(NotNull<IProtocol*> actor, const char* where,
54 const char* why) {
55 // Calls top-level protocol to handle the error.
56 nsPrintfCString errorMsg("%s %s\n", where, why);
57 actor->GetIPCChannel()->Listener()->ProcessingError(
58 HasResultCodes::MsgProcessingError, errorMsg.get());
60 #if defined(DEBUG) && !defined(FUZZING)
61 // We do not expect IPC_FAIL to ever happen in normal operations. If this
62 // happens in DEBUG, we most likely see some behavior during a test we should
63 // really investigate.
64 nsPrintfCString crashMsg(
65 "Use IPC_FAIL only in an "
66 "unrecoverable, unexpected state: %s",
67 errorMsg.get());
68 // We already leak the same information potentially on child process failures
69 // even in release, and here we are only in DEBUG.
70 MOZ_CRASH_UNSAFE(crashMsg.get());
71 #else
72 return IPCResult(false);
73 #endif
76 /* static */
77 IPCResult IPCResult::FailForTesting(NotNull<IProtocol*> actor,
78 const char* where, const char* why) {
79 return IPCResult(false);
82 void AnnotateSystemError() {
83 uint32_t error = 0;
84 #if defined(XP_WIN)
85 error = ::GetLastError();
86 #else
87 error = errno;
88 #endif
89 if (error) {
90 CrashReporter::RecordAnnotationU32(
91 CrashReporter::Annotation::IPCSystemError, error);
95 #if defined(XP_MACOSX)
96 void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error) {
97 CrashReporter::RecordAnnotationU32(tag, static_cast<uint32_t>(error));
99 #endif // defined(XP_MACOSX)
101 #if defined(DEBUG) || defined(FUZZING)
102 // If aTopLevelProtocol matches any token in aFilter, return true.
104 // aTopLevelProtocol is a protocol name, without the "Parent" / "Child" suffix.
105 // aSide indicates whether we're logging parent-side or child-side activity.
107 // aFilter is a list of protocol names separated by commas and/or
108 // spaces. These may include the "Child" / "Parent" suffix, or omit
109 // the suffix to log activity on both sides.
111 // This overload is for testability; application code should use the single-
112 // argument version (defined in the ProtocolUtils.h) which takes the filter from
113 // the environment.
114 bool LoggingEnabledFor(const char* aTopLevelProtocol, Side aSide,
115 const char* aFilter) {
116 if (!aFilter) {
117 return false;
119 if (strcmp(aFilter, "1") == 0) {
120 return true;
123 const char kDelimiters[] = ", ";
124 Tokenizer tokens(aFilter, kDelimiters);
125 Tokenizer::Token t;
126 while (tokens.Next(t)) {
127 if (t.Type() == Tokenizer::TOKEN_WORD) {
128 auto filter = t.AsString();
130 // Since aTopLevelProtocol never includes the "Parent" / "Child" suffix,
131 // this will only occur when filter doesn't include it either, meaning
132 // that we should log activity on both sides.
133 if (filter == aTopLevelProtocol) {
134 return true;
137 if (aSide == ParentSide &&
138 StringEndsWith(filter, nsDependentCString("Parent")) &&
139 Substring(filter, 0, filter.Length() - 6) == aTopLevelProtocol) {
140 return true;
143 if (aSide == ChildSide &&
144 StringEndsWith(filter, nsDependentCString("Child")) &&
145 Substring(filter, 0, filter.Length() - 5) == aTopLevelProtocol) {
146 return true;
151 return false;
153 #endif // defined(DEBUG) || defined(FUZZING)
155 void LogMessageForProtocol(const char* aTopLevelProtocol,
156 base::ProcessId aOtherPid,
157 const char* aContextDescription, uint32_t aMessageId,
158 MessageDirection aDirection) {
159 nsPrintfCString logMessage(
160 "[time: %" PRId64 "][%" PRIPID "%s%" PRIPID "] [%s] %s %s\n", PR_Now(),
161 base::GetCurrentProcId(),
162 aDirection == MessageDirection::eReceiving ? "<-" : "->", aOtherPid,
163 aTopLevelProtocol, aContextDescription,
164 StringFromIPCMessageType(aMessageId));
165 #ifdef ANDROID
166 __android_log_write(ANDROID_LOG_INFO, "GeckoIPC", logMessage.get());
167 #endif
168 fputs(logMessage.get(), stderr);
171 void ProtocolErrorBreakpoint(const char* aMsg) {
172 // Bugs that generate these error messages can be tough to
173 // reproduce. Log always in the hope that someone finds the error
174 // message.
175 printf_stderr("IPDL protocol error: %s\n", aMsg);
178 void PickleFatalError(const char* aMsg, IProtocol* aActor) {
179 if (aActor) {
180 aActor->FatalError(aMsg);
181 } else {
182 FatalError(aMsg, false);
186 void FatalError(const char* aMsg, bool aIsParent) {
187 #ifndef FUZZING
188 ProtocolErrorBreakpoint(aMsg);
189 #endif
191 nsAutoCString formattedMessage("IPDL error: \"");
192 formattedMessage.AppendASCII(aMsg);
193 if (aIsParent) {
194 // We're going to crash the parent process because at this time
195 // there's no other really nice way of getting a minidump out of
196 // this process if we're off the main thread.
197 formattedMessage.AppendLiteral("\". Intentionally crashing.");
198 NS_ERROR(formattedMessage.get());
199 CrashReporter::RecordAnnotationCString(
200 CrashReporter::Annotation::IPCFatalErrorMsg, aMsg);
201 AnnotateSystemError();
202 #ifndef FUZZING
203 MOZ_CRASH("IPC FatalError in the parent process!");
204 #endif
205 } else {
206 formattedMessage.AppendLiteral("\". abort()ing as a result.");
207 #ifndef FUZZING
208 MOZ_CRASH_UNSAFE(formattedMessage.get());
209 #endif
213 void LogicError(const char* aMsg) { MOZ_CRASH_UNSAFE(aMsg); }
215 void ActorIdReadError(const char* aActorDescription) {
216 #ifndef FUZZING
217 MOZ_CRASH_UNSAFE_PRINTF("Error deserializing id for %s", aActorDescription);
218 #endif
221 void BadActorIdError(const char* aActorDescription) {
222 nsPrintfCString message("bad id for %s", aActorDescription);
223 ProtocolErrorBreakpoint(message.get());
226 void ActorLookupError(const char* aActorDescription) {
227 nsPrintfCString message("could not lookup id for %s", aActorDescription);
228 ProtocolErrorBreakpoint(message.get());
231 void MismatchedActorTypeError(const char* aActorDescription) {
232 nsPrintfCString message("actor that should be of type %s has different type",
233 aActorDescription);
234 ProtocolErrorBreakpoint(message.get());
237 void UnionTypeReadError(const char* aUnionName) {
238 MOZ_CRASH_UNSAFE_PRINTF("error deserializing type of union %s", aUnionName);
241 void ArrayLengthReadError(const char* aElementName) {
242 MOZ_CRASH_UNSAFE_PRINTF("error deserializing length of %s[]", aElementName);
245 void SentinelReadError(const char* aClassName) {
246 MOZ_CRASH_UNSAFE_PRINTF("incorrect sentinel when reading %s", aClassName);
249 ActorLifecycleProxy::ActorLifecycleProxy(IProtocol* aActor) : mActor(aActor) {
250 MOZ_ASSERT(mActor);
251 MOZ_ASSERT(mActor->CanSend(),
252 "Cannot create LifecycleProxy for non-connected actor!");
254 // Take a reference to our manager's lifecycle proxy to try to hold it &
255 // ensure it doesn't die before us.
256 if (mActor->mManager) {
257 mManager = mActor->mManager->mLifecycleProxy;
260 // Record that we've taken our first reference to our actor.
261 mActor->ActorAlloc();
264 WeakActorLifecycleProxy* ActorLifecycleProxy::GetWeakProxy() {
265 if (!mWeakProxy) {
266 mWeakProxy = new WeakActorLifecycleProxy(this);
268 return mWeakProxy;
271 ActorLifecycleProxy::~ActorLifecycleProxy() {
272 if (mWeakProxy) {
273 mWeakProxy->mProxy = nullptr;
274 mWeakProxy = nullptr;
277 // When the LifecycleProxy's lifetime has come to an end, it means that the
278 // actor should have its `Dealloc` method called on it. In a well-behaved
279 // actor, this will release the IPC-held reference to the actor.
281 // If the actor has already died before the `LifecycleProxy`, the `IProtocol`
282 // destructor below will clear our reference to it, preventing us from
283 // performing a use-after-free here.
284 if (!mActor) {
285 return;
288 // Clear our actor's state back to inactive, and then invoke ActorDealloc.
289 MOZ_ASSERT(mActor->mLinkStatus == LinkStatus::Destroyed,
290 "Deallocating non-destroyed actor!");
291 mActor->mLifecycleProxy = nullptr;
292 mActor->mLinkStatus = LinkStatus::Inactive;
293 mActor->ActorDealloc();
294 mActor = nullptr;
297 WeakActorLifecycleProxy::WeakActorLifecycleProxy(ActorLifecycleProxy* aProxy)
298 : mProxy(aProxy), mActorEventTarget(GetCurrentSerialEventTarget()) {}
300 WeakActorLifecycleProxy::~WeakActorLifecycleProxy() {
301 MOZ_DIAGNOSTIC_ASSERT(!mProxy, "Destroyed before mProxy was cleared?");
304 IProtocol* WeakActorLifecycleProxy::Get() const {
305 MOZ_DIAGNOSTIC_ASSERT(mActorEventTarget->IsOnCurrentThread());
306 return mProxy ? mProxy->Get() : nullptr;
309 WeakActorLifecycleProxy* IProtocol::GetWeakLifecycleProxy() {
310 return mLifecycleProxy ? mLifecycleProxy->GetWeakProxy() : nullptr;
313 IProtocol::~IProtocol() {
314 // If the actor still has a lifecycle proxy when it is being torn down, it
315 // means that IPC was not given control over the lifecycle of the actor
316 // correctly. Usually this means that the actor was destroyed while IPC is
317 // calling a message handler for it, and the actor incorrectly frees itself
318 // during that operation.
320 // As this happens unfortunately frequently, due to many odd protocols in
321 // Gecko, simply emit a warning and clear the weak backreference from our
322 // LifecycleProxy back to us.
323 if (mLifecycleProxy) {
324 NS_WARNING(
325 nsPrintfCString("Actor destructor for '%s%s' called before IPC "
326 "lifecycle complete!\n"
327 "References to this actor may unexpectedly dangle!",
328 GetProtocolName(), StringFromIPCSide(GetSide()))
329 .get());
331 mLifecycleProxy->mActor = nullptr;
333 // If we are somehow being destroyed while active, make sure that the
334 // existing IPC reference has been freed. If the status of the actor is
335 // `Destroyed`, the reference has already been freed, and we shouldn't free
336 // it a second time.
337 MOZ_ASSERT(mLinkStatus != LinkStatus::Inactive);
338 if (mLinkStatus != LinkStatus::Destroyed) {
339 NS_IF_RELEASE(mLifecycleProxy);
341 mLifecycleProxy = nullptr;
345 // The following methods either directly forward to the toplevel protocol, or
346 // almost directly do.
347 int32_t IProtocol::Register(IProtocol* aRouted) {
348 return mToplevel->Register(aRouted);
350 int32_t IProtocol::RegisterID(IProtocol* aRouted, int32_t aId) {
351 return mToplevel->RegisterID(aRouted, aId);
353 IProtocol* IProtocol::Lookup(int32_t aId) { return mToplevel->Lookup(aId); }
354 void IProtocol::Unregister(int32_t aId) {
355 if (aId == mId) {
356 mId = kFreedActorId;
358 return mToplevel->Unregister(aId);
361 Shmem::SharedMemory* IProtocol::CreateSharedMemory(size_t aSize, bool aUnsafe,
362 int32_t* aId) {
363 return mToplevel->CreateSharedMemory(aSize, aUnsafe, aId);
365 Shmem::SharedMemory* IProtocol::LookupSharedMemory(int32_t aId) {
366 return mToplevel->LookupSharedMemory(aId);
368 bool IProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* aSegment) {
369 return mToplevel->IsTrackingSharedMemory(aSegment);
371 bool IProtocol::DestroySharedMemory(Shmem& aShmem) {
372 return mToplevel->DestroySharedMemory(aShmem);
375 MessageChannel* IProtocol::GetIPCChannel() {
376 return mToplevel->GetIPCChannel();
378 const MessageChannel* IProtocol::GetIPCChannel() const {
379 return mToplevel->GetIPCChannel();
382 nsISerialEventTarget* IProtocol::GetActorEventTarget() {
383 return GetIPCChannel()->GetWorkerEventTarget();
386 void IProtocol::SetId(int32_t aId) {
387 MOZ_ASSERT(mId == aId || mLinkStatus == LinkStatus::Inactive);
388 mId = aId;
391 Maybe<IProtocol*> IProtocol::ReadActor(IPC::MessageReader* aReader,
392 bool aNullable,
393 const char* aActorDescription,
394 int32_t aProtocolTypeId) {
395 int32_t id;
396 if (!IPC::ReadParam(aReader, &id)) {
397 ActorIdReadError(aActorDescription);
398 return Nothing();
401 if (id == 1 || (id == 0 && !aNullable)) {
402 BadActorIdError(aActorDescription);
403 return Nothing();
406 if (id == 0) {
407 return Some(static_cast<IProtocol*>(nullptr));
410 IProtocol* listener = this->Lookup(id);
411 if (!listener) {
412 ActorLookupError(aActorDescription);
413 return Nothing();
416 if (listener->GetProtocolId() != aProtocolTypeId) {
417 MismatchedActorTypeError(aActorDescription);
418 return Nothing();
421 return Some(listener);
424 void IProtocol::FatalError(const char* const aErrorMsg) {
425 HandleFatalError(aErrorMsg);
428 void IProtocol::HandleFatalError(const char* aErrorMsg) {
429 if (IProtocol* manager = Manager()) {
430 manager->HandleFatalError(aErrorMsg);
431 return;
434 mozilla::ipc::FatalError(aErrorMsg, mSide == ParentSide);
435 if (CanSend()) {
436 GetIPCChannel()->InduceConnectionError();
440 bool IProtocol::AllocShmem(size_t aSize, Shmem* aOutMem) {
441 if (!CanSend()) {
442 NS_WARNING(
443 "Shmem not allocated. Cannot communicate with the other actor.");
444 return false;
447 Shmem::id_t id;
448 Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, false, &id));
449 if (!rawmem) {
450 return false;
453 *aOutMem = Shmem(rawmem, id, aSize, false);
454 return true;
457 bool IProtocol::AllocUnsafeShmem(size_t aSize, Shmem* aOutMem) {
458 if (!CanSend()) {
459 NS_WARNING(
460 "Shmem not allocated. Cannot communicate with the other actor.");
461 return false;
464 Shmem::id_t id;
465 Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, true, &id));
466 if (!rawmem) {
467 return false;
470 *aOutMem = Shmem(rawmem, id, aSize, true);
471 return true;
474 bool IProtocol::DeallocShmem(Shmem& aMem) {
475 bool ok = DestroySharedMemory(aMem);
476 #ifdef DEBUG
477 if (!ok) {
478 if (mSide == ChildSide) {
479 FatalError("bad Shmem");
480 } else {
481 NS_WARNING("bad Shmem");
483 return false;
485 #endif // DEBUG
486 aMem.forget();
487 return ok;
490 void IProtocol::SetManager(IProtocol* aManager) {
491 MOZ_RELEASE_ASSERT(!mManager || mManager == aManager);
492 mManager = aManager;
493 mToplevel = aManager->mToplevel;
496 void IProtocol::SetManagerAndRegister(IProtocol* aManager) {
497 // Set the manager prior to registering so registering properly inherits
498 // the manager's event target.
499 SetManager(aManager);
501 aManager->Register(this);
504 void IProtocol::SetManagerAndRegister(IProtocol* aManager, int32_t aId) {
505 // Set the manager prior to registering so registering properly inherits
506 // the manager's event target.
507 SetManager(aManager);
509 aManager->RegisterID(this, aId);
512 bool IProtocol::ChannelSend(UniquePtr<IPC::Message> aMsg) {
513 if (CanSend()) {
514 // NOTE: This send call failing can only occur during toplevel channel
515 // teardown. As this is an async call, this isn't reasonable to predict or
516 // respond to, so just drop the message on the floor silently.
517 GetIPCChannel()->Send(std::move(aMsg));
518 return true;
521 WarnMessageDiscarded(aMsg.get());
522 return false;
525 bool IProtocol::ChannelSend(UniquePtr<IPC::Message> aMsg,
526 UniquePtr<IPC::Message>* aReply) {
527 if (CanSend()) {
528 return GetIPCChannel()->Send(std::move(aMsg), aReply);
531 WarnMessageDiscarded(aMsg.get());
532 return false;
535 #ifdef DEBUG
536 void IProtocol::WarnMessageDiscarded(IPC::Message* aMsg) {
537 NS_WARNING(nsPrintfCString("IPC message '%s' discarded: actor cannot send",
538 aMsg->name())
539 .get());
541 #endif
543 void IProtocol::ActorConnected() {
544 if (mLinkStatus != LinkStatus::Inactive) {
545 return;
548 #ifdef FUZZING_SNAPSHOT
549 fuzzing::IPCFuzzController::instance().OnActorConnected(this);
550 #endif
552 mLinkStatus = LinkStatus::Connected;
554 MOZ_ASSERT(!mLifecycleProxy, "double-connecting live actor");
555 mLifecycleProxy = new ActorLifecycleProxy(this);
556 NS_ADDREF(mLifecycleProxy); // Reference freed in DestroySubtree();
559 void IProtocol::DoomSubtree() {
560 MOZ_ASSERT(CanSend(), "dooming non-connected actor");
561 MOZ_ASSERT(mLifecycleProxy, "dooming zombie actor");
563 nsTArray<RefPtr<ActorLifecycleProxy>> managed;
564 AllManagedActors(managed);
565 for (ActorLifecycleProxy* proxy : managed) {
566 // Guard against actor being disconnected or destroyed during previous Doom
567 IProtocol* actor = proxy->Get();
568 if (actor && actor->CanSend()) {
569 actor->DoomSubtree();
573 // ActorDoom is called immediately before changing state, this allows messages
574 // to be sent during ActorDoom immediately before the channel is closed and
575 // sending messages is disabled.
576 ActorDoom();
577 mLinkStatus = LinkStatus::Doomed;
580 void IProtocol::DestroySubtree(ActorDestroyReason aWhy) {
581 MOZ_ASSERT(CanRecv(), "destroying non-connected actor");
582 MOZ_ASSERT(mLifecycleProxy, "destroying zombie actor");
584 #ifdef FUZZING_SNAPSHOT
585 fuzzing::IPCFuzzController::instance().OnActorDestroyed(this);
586 #endif
588 int32_t id = Id();
590 // If we're a managed actor, unregister from our manager
591 if (Manager()) {
592 Unregister(id);
595 // Destroy subtree
596 ActorDestroyReason subtreeWhy = aWhy;
597 if (aWhy == Deletion || aWhy == FailedConstructor) {
598 subtreeWhy = AncestorDeletion;
601 nsTArray<RefPtr<ActorLifecycleProxy>> managed;
602 AllManagedActors(managed);
603 for (ActorLifecycleProxy* proxy : managed) {
604 // Guard against actor being disconnected or destroyed during previous
605 // Destroy
606 IProtocol* actor = proxy->Get();
607 if (actor && actor->CanRecv()) {
608 actor->DestroySubtree(subtreeWhy);
612 // Ensure that we don't send any messages while we're calling `ActorDestroy`
613 // by setting our state to `Doomed`.
614 mLinkStatus = LinkStatus::Doomed;
616 // The actor is being destroyed, reject any pending responses, invoke
617 // `ActorDestroy` to destroy it, and then clear our status to
618 // `LinkStatus::Destroyed`.
619 GetIPCChannel()->RejectPendingResponsesForActor(id);
620 ActorDestroy(aWhy);
621 mLinkStatus = LinkStatus::Destroyed;
624 IToplevelProtocol::IToplevelProtocol(const char* aName, ProtocolId aProtoId,
625 Side aSide)
626 : IRefCountedProtocol(aProtoId, aSide),
627 mOtherPid(base::kInvalidProcessId),
628 mLastLocalId(0),
629 mChannel(aName, this) {
630 mToplevel = this;
633 void IToplevelProtocol::SetOtherProcessId(base::ProcessId aOtherPid) {
634 mOtherPid = aOtherPid;
637 bool IToplevelProtocol::Open(ScopedPort aPort, const nsID& aMessageChannelId,
638 base::ProcessId aOtherPid,
639 nsISerialEventTarget* aEventTarget) {
640 SetOtherProcessId(aOtherPid);
641 return GetIPCChannel()->Open(std::move(aPort), mSide, aMessageChannelId,
642 aEventTarget);
645 bool IToplevelProtocol::Open(IToplevelProtocol* aTarget,
646 nsISerialEventTarget* aEventTarget,
647 mozilla::ipc::Side aSide) {
648 SetOtherProcessId(base::GetCurrentProcId());
649 aTarget->SetOtherProcessId(base::GetCurrentProcId());
650 return GetIPCChannel()->Open(aTarget->GetIPCChannel(), aEventTarget, aSide);
653 bool IToplevelProtocol::OpenOnSameThread(IToplevelProtocol* aTarget,
654 Side aSide) {
655 SetOtherProcessId(base::GetCurrentProcId());
656 aTarget->SetOtherProcessId(base::GetCurrentProcId());
657 return GetIPCChannel()->OpenOnSameThread(aTarget->GetIPCChannel(), aSide);
660 void IToplevelProtocol::NotifyImpendingShutdown() {
661 if (CanRecv()) {
662 GetIPCChannel()->NotifyImpendingShutdown();
666 void IToplevelProtocol::Close() { GetIPCChannel()->Close(); }
668 void IToplevelProtocol::SetReplyTimeoutMs(int32_t aTimeoutMs) {
669 GetIPCChannel()->SetReplyTimeoutMs(aTimeoutMs);
672 bool IToplevelProtocol::IsOnCxxStack() const {
673 return GetIPCChannel()->IsOnCxxStack();
676 int32_t IToplevelProtocol::NextId() {
677 // Generate the next ID to use for a shared memory or protocol. Parent and
678 // Child sides of the protocol use different pools.
679 int32_t tag = 0;
680 if (GetSide() == ParentSide) {
681 tag |= 1 << 1;
684 // Check any overflow
685 MOZ_RELEASE_ASSERT(mLastLocalId < (1 << 29));
687 // Compute the ID to use with the low two bits as our tag, and the remaining
688 // bits as a monotonic.
689 return (++mLastLocalId << 2) | tag;
692 int32_t IToplevelProtocol::Register(IProtocol* aRouted) {
693 if (aRouted->Id() != kNullActorId && aRouted->Id() != kFreedActorId) {
694 // If there's already an ID, just return that.
695 return aRouted->Id();
697 return RegisterID(aRouted, NextId());
700 int32_t IToplevelProtocol::RegisterID(IProtocol* aRouted, int32_t aId) {
701 aRouted->SetId(aId);
702 aRouted->ActorConnected();
703 MOZ_ASSERT(!mActorMap.Contains(aId), "Don't insert with an existing ID");
704 mActorMap.InsertOrUpdate(aId, aRouted);
705 return aId;
708 IProtocol* IToplevelProtocol::Lookup(int32_t aId) { return mActorMap.Get(aId); }
710 void IToplevelProtocol::Unregister(int32_t aId) {
711 MOZ_ASSERT(mActorMap.Contains(aId),
712 "Attempting to remove an ID not in the actor map");
713 mActorMap.Remove(aId);
716 Shmem::SharedMemory* IToplevelProtocol::CreateSharedMemory(size_t aSize,
717 bool aUnsafe,
718 Shmem::id_t* aId) {
719 RefPtr<Shmem::SharedMemory> segment(Shmem::Alloc(aSize));
720 if (!segment) {
721 return nullptr;
723 int32_t id = NextId();
724 Shmem shmem(segment.get(), id, aSize, aUnsafe);
726 UniquePtr<Message> descriptor = shmem.MkCreatedMessage(MSG_ROUTING_CONTROL);
727 if (!descriptor) {
728 return nullptr;
730 Unused << GetIPCChannel()->Send(std::move(descriptor));
732 *aId = shmem.Id();
733 Shmem::SharedMemory* rawSegment = segment.get();
734 MOZ_ASSERT(!mShmemMap.Contains(*aId), "Don't insert with an existing ID");
735 mShmemMap.InsertOrUpdate(*aId, std::move(segment));
736 return rawSegment;
739 Shmem::SharedMemory* IToplevelProtocol::LookupSharedMemory(Shmem::id_t aId) {
740 auto entry = mShmemMap.Lookup(aId);
741 return entry ? entry.Data().get() : nullptr;
744 bool IToplevelProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* segment) {
745 for (const auto& shmem : mShmemMap.Values()) {
746 if (segment == shmem) {
747 return true;
750 return false;
753 bool IToplevelProtocol::DestroySharedMemory(Shmem& shmem) {
754 Shmem::id_t aId = shmem.Id();
755 Shmem::SharedMemory* segment = LookupSharedMemory(aId);
756 if (!segment) {
757 return false;
760 UniquePtr<Message> descriptor = shmem.MkDestroyedMessage(MSG_ROUTING_CONTROL);
762 MOZ_ASSERT(mShmemMap.Contains(aId),
763 "Attempting to remove an ID not in the shmem map");
764 mShmemMap.Remove(aId);
766 MessageChannel* channel = GetIPCChannel();
767 if (!channel->CanSend()) {
768 return true;
771 return descriptor && channel->Send(std::move(descriptor));
774 void IToplevelProtocol::DeallocShmems() { mShmemMap.Clear(); }
776 bool IToplevelProtocol::ShmemCreated(const Message& aMsg) {
777 Shmem::id_t id;
778 RefPtr<Shmem::SharedMemory> rawmem(Shmem::OpenExisting(aMsg, &id, true));
779 if (!rawmem) {
780 return false;
782 MOZ_ASSERT(!mShmemMap.Contains(id), "Don't insert with an existing ID");
783 mShmemMap.InsertOrUpdate(id, std::move(rawmem));
784 return true;
787 bool IToplevelProtocol::ShmemDestroyed(const Message& aMsg) {
788 Shmem::id_t id;
789 MessageReader reader(aMsg);
790 if (!IPC::ReadParam(&reader, &id)) {
791 return false;
793 reader.EndRead();
795 mShmemMap.Remove(id);
796 return true;
799 IPDLResolverInner::IPDLResolverInner(UniquePtr<IPC::Message> aReply,
800 IProtocol* aActor)
801 : mReply(std::move(aReply)),
802 mWeakProxy(aActor->GetLifecycleProxy()->GetWeakProxy()) {}
804 void IPDLResolverInner::ResolveOrReject(
805 bool aResolve, FunctionRef<void(IPC::Message*, IProtocol*)> aWrite) {
806 MOZ_ASSERT(mWeakProxy);
807 MOZ_ASSERT(mWeakProxy->ActorEventTarget()->IsOnCurrentThread());
808 MOZ_ASSERT(mReply);
810 UniquePtr<IPC::Message> reply = std::move(mReply);
812 IProtocol* actor = mWeakProxy->Get();
813 if (!actor) {
814 NS_WARNING(nsPrintfCString("Not resolving response '%s': actor is dead",
815 reply->name())
816 .get());
817 return;
820 IPC::MessageWriter writer(*reply, actor);
821 WriteIPDLParam(&writer, actor, aResolve);
822 aWrite(reply.get(), actor);
824 actor->ChannelSend(std::move(reply));
827 void IPDLResolverInner::Destroy() {
828 if (mReply) {
829 NS_PROXY_DELETE_TO_EVENT_TARGET(IPDLResolverInner,
830 mWeakProxy->ActorEventTarget());
831 } else {
832 // If we've already been consumed, just delete without proxying. This avoids
833 // leaking the resolver if the actor's thread is already dead.
834 delete this;
838 IPDLResolverInner::~IPDLResolverInner() {
839 if (mReply) {
840 NS_WARNING(
841 nsPrintfCString(
842 "Rejecting reply '%s': resolver dropped without being called",
843 mReply->name())
844 .get());
845 ResolveOrReject(false, [](IPC::Message* aMessage, IProtocol* aActor) {
846 IPC::MessageWriter writer(*aMessage, aActor);
847 ResponseRejectReason reason = ResponseRejectReason::ResolverDestroyed;
848 WriteIPDLParam(&writer, aActor, reason);
853 } // namespace ipc
854 } // namespace mozilla