Bug 1797433 [wpt PR 36660] - [anchor-position] Add tests for fixed position in multic...
[gecko.git] / ipc / glue / ProtocolUtils.cpp
blob91e888e3b0b8935d389a39ebda6bdd0a4af045d3
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 OS_POSIX
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"
29 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
30 # include "mozilla/sandboxTarget.h"
31 #endif
33 #if defined(XP_WIN)
34 # include "aclapi.h"
35 # include "sddl.h"
36 #endif
38 #ifdef FUZZING_SNAPSHOT
39 # include "mozilla/fuzzing/IPCFuzzController.h"
40 #endif
42 using namespace IPC;
44 using base::GetCurrentProcId;
45 using base::ProcessHandle;
46 using base::ProcessId;
48 namespace mozilla {
50 #if defined(XP_WIN)
51 // Generate RAII classes for LPTSTR and PSECURITY_DESCRIPTOR.
52 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedLPTStr,
53 std::remove_pointer_t<LPTSTR>,
54 ::LocalFree)
55 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(
56 ScopedPSecurityDescriptor, std::remove_pointer_t<PSECURITY_DESCRIPTOR>,
57 ::LocalFree)
58 #endif
60 namespace ipc {
62 IPCResult IPCResult::Fail(NotNull<IProtocol*> actor, const char* where,
63 const char* why) {
64 // Calls top-level protocol to handle the error.
65 nsPrintfCString errorMsg("%s %s\n", where, why);
66 actor->GetIPCChannel()->Listener()->ProcessingError(
67 HasResultCodes::MsgProcessingError, errorMsg.get());
69 MOZ_ASSERT_UNLESS_FUZZING(false,
70 "Please ensure to IPC_FAIL only when in an "
71 "unrecoverable, unexpected state.");
73 return IPCResult(false);
76 void AnnotateSystemError() {
77 int64_t error = 0;
78 #if defined(XP_WIN)
79 error = ::GetLastError();
80 #elif defined(OS_POSIX)
81 error = errno;
82 #endif
83 if (error) {
84 CrashReporter::AnnotateCrashReport(
85 CrashReporter::Annotation::IPCSystemError,
86 nsPrintfCString("%" PRId64, error));
90 #if defined(XP_MACOSX)
91 void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error) {
92 CrashReporter::AnnotateCrashReport(tag, error);
94 #endif // defined(XP_MACOSX)
96 #if defined(DEBUG) || defined(FUZZING)
97 // This overload is for testability; application code should use the single-
98 // argument version (defined in the ProtocolUtils.h) which takes the filter from
99 // the environment.
100 bool LoggingEnabledFor(const char* aTopLevelProtocol, const char* aFilter) {
101 if (!aFilter) {
102 return false;
104 if (strcmp(aFilter, "1") == 0) {
105 return true;
108 const char kDelimiters[] = ", ";
109 Tokenizer tokens(aFilter, kDelimiters);
110 Tokenizer::Token t;
111 while (tokens.Next(t)) {
112 if (t.Type() == Tokenizer::TOKEN_WORD &&
113 t.AsString() == aTopLevelProtocol) {
114 return true;
118 return false;
120 #endif // defined(DEBUG) || defined(FUZZING)
122 void LogMessageForProtocol(const char* aTopLevelProtocol,
123 base::ProcessId aOtherPid,
124 const char* aContextDescription, uint32_t aMessageId,
125 MessageDirection aDirection) {
126 nsPrintfCString logMessage(
127 "[time: %" PRId64 "][%" PRIPID "%s%" PRIPID "] [%s] %s %s\n", PR_Now(),
128 base::GetCurrentProcId(),
129 aDirection == MessageDirection::eReceiving ? "<-" : "->", aOtherPid,
130 aTopLevelProtocol, aContextDescription,
131 StringFromIPCMessageType(aMessageId));
132 #ifdef ANDROID
133 __android_log_write(ANDROID_LOG_INFO, "GeckoIPC", logMessage.get());
134 #endif
135 fputs(logMessage.get(), stderr);
138 void ProtocolErrorBreakpoint(const char* aMsg) {
139 // Bugs that generate these error messages can be tough to
140 // reproduce. Log always in the hope that someone finds the error
141 // message.
142 printf_stderr("IPDL protocol error: %s\n", aMsg);
145 void PickleFatalError(const char* aMsg, IProtocol* aActor) {
146 if (aActor) {
147 aActor->FatalError(aMsg);
148 } else {
149 FatalError(aMsg, false);
153 void FatalError(const char* aMsg, bool aIsParent) {
154 #ifndef FUZZING
155 ProtocolErrorBreakpoint(aMsg);
156 #endif
158 nsAutoCString formattedMessage("IPDL error: \"");
159 formattedMessage.AppendASCII(aMsg);
160 if (aIsParent) {
161 // We're going to crash the parent process because at this time
162 // there's no other really nice way of getting a minidump out of
163 // this process if we're off the main thread.
164 formattedMessage.AppendLiteral("\". Intentionally crashing.");
165 NS_ERROR(formattedMessage.get());
166 CrashReporter::AnnotateCrashReport(
167 CrashReporter::Annotation::IPCFatalErrorMsg, nsDependentCString(aMsg));
168 AnnotateSystemError();
169 #ifndef FUZZING
170 MOZ_CRASH("IPC FatalError in the parent process!");
171 #endif
172 } else {
173 formattedMessage.AppendLiteral("\". abort()ing as a result.");
174 #ifndef FUZZING
175 MOZ_CRASH_UNSAFE(formattedMessage.get());
176 #endif
180 void LogicError(const char* aMsg) { MOZ_CRASH_UNSAFE(aMsg); }
182 void ActorIdReadError(const char* aActorDescription) {
183 #ifndef FUZZING
184 MOZ_CRASH_UNSAFE_PRINTF("Error deserializing id for %s", aActorDescription);
185 #endif
188 void BadActorIdError(const char* aActorDescription) {
189 nsPrintfCString message("bad id for %s", aActorDescription);
190 ProtocolErrorBreakpoint(message.get());
193 void ActorLookupError(const char* aActorDescription) {
194 nsPrintfCString message("could not lookup id for %s", aActorDescription);
195 ProtocolErrorBreakpoint(message.get());
198 void MismatchedActorTypeError(const char* aActorDescription) {
199 nsPrintfCString message("actor that should be of type %s has different type",
200 aActorDescription);
201 ProtocolErrorBreakpoint(message.get());
204 void UnionTypeReadError(const char* aUnionName) {
205 MOZ_CRASH_UNSAFE_PRINTF("error deserializing type of union %s", aUnionName);
208 void ArrayLengthReadError(const char* aElementName) {
209 MOZ_CRASH_UNSAFE_PRINTF("error deserializing length of %s[]", aElementName);
212 void SentinelReadError(const char* aClassName) {
213 MOZ_CRASH_UNSAFE_PRINTF("incorrect sentinel when reading %s", aClassName);
216 ActorLifecycleProxy::ActorLifecycleProxy(IProtocol* aActor) : mActor(aActor) {
217 MOZ_ASSERT(mActor);
218 MOZ_ASSERT(mActor->CanSend(),
219 "Cannot create LifecycleProxy for non-connected actor!");
221 // Take a reference to our manager's lifecycle proxy to try to hold it &
222 // ensure it doesn't die before us.
223 if (mActor->mManager) {
224 mManager = mActor->mManager->mLifecycleProxy;
227 // Record that we've taken our first reference to our actor.
228 mActor->ActorAlloc();
231 WeakActorLifecycleProxy* ActorLifecycleProxy::GetWeakProxy() {
232 if (!mWeakProxy) {
233 mWeakProxy = new WeakActorLifecycleProxy(this);
235 return mWeakProxy;
238 ActorLifecycleProxy::~ActorLifecycleProxy() {
239 if (mWeakProxy) {
240 mWeakProxy->mProxy = nullptr;
241 mWeakProxy = nullptr;
244 // When the LifecycleProxy's lifetime has come to an end, it means that the
245 // actor should have its `Dealloc` method called on it. In a well-behaved
246 // actor, this will release the IPC-held reference to the actor.
248 // If the actor has already died before the `LifecycleProxy`, the `IProtocol`
249 // destructor below will clear our reference to it, preventing us from
250 // performing a use-after-free here.
251 if (!mActor) {
252 return;
255 // Clear our actor's state back to inactive, and then invoke ActorDealloc.
256 MOZ_ASSERT(mActor->mLinkStatus == LinkStatus::Destroyed,
257 "Deallocating non-destroyed actor!");
258 mActor->mLifecycleProxy = nullptr;
259 mActor->mLinkStatus = LinkStatus::Inactive;
260 mActor->ActorDealloc();
261 mActor = nullptr;
264 WeakActorLifecycleProxy::WeakActorLifecycleProxy(ActorLifecycleProxy* aProxy)
265 : mProxy(aProxy), mActorEventTarget(GetCurrentSerialEventTarget()) {}
267 WeakActorLifecycleProxy::~WeakActorLifecycleProxy() {
268 MOZ_DIAGNOSTIC_ASSERT(!mProxy, "Destroyed before mProxy was cleared?");
271 IProtocol* WeakActorLifecycleProxy::Get() const {
272 MOZ_DIAGNOSTIC_ASSERT(mActorEventTarget->IsOnCurrentThread());
273 return mProxy ? mProxy->Get() : nullptr;
276 WeakActorLifecycleProxy* IProtocol::GetWeakLifecycleProxy() {
277 return mLifecycleProxy ? mLifecycleProxy->GetWeakProxy() : nullptr;
280 IProtocol::~IProtocol() {
281 // If the actor still has a lifecycle proxy when it is being torn down, it
282 // means that IPC was not given control over the lifecycle of the actor
283 // correctly. Usually this means that the actor was destroyed while IPC is
284 // calling a message handler for it, and the actor incorrectly frees itself
285 // during that operation.
287 // As this happens unfortunately frequently, due to many odd protocols in
288 // Gecko, simply emit a warning and clear the weak backreference from our
289 // LifecycleProxy back to us.
290 if (mLifecycleProxy) {
291 NS_WARNING(
292 nsPrintfCString("Actor destructor for '%s%s' called before IPC "
293 "lifecycle complete!\n"
294 "References to this actor may unexpectedly dangle!",
295 GetProtocolName(),
296 GetSide() == ChildSide ? "Child" : "Parent")
297 .get());
299 mLifecycleProxy->mActor = nullptr;
301 // If we are somehow being destroyed while active, make sure that the
302 // existing IPC reference has been freed. If the status of the actor is
303 // `Destroyed`, the reference has already been freed, and we shouldn't free
304 // it a second time.
305 MOZ_ASSERT(mLinkStatus != LinkStatus::Inactive);
306 if (mLinkStatus != LinkStatus::Destroyed) {
307 NS_IF_RELEASE(mLifecycleProxy);
309 mLifecycleProxy = nullptr;
313 // The following methods either directly forward to the toplevel protocol, or
314 // almost directly do.
315 int32_t IProtocol::Register(IProtocol* aRouted) {
316 return mToplevel->Register(aRouted);
318 int32_t IProtocol::RegisterID(IProtocol* aRouted, int32_t aId) {
319 return mToplevel->RegisterID(aRouted, aId);
321 IProtocol* IProtocol::Lookup(int32_t aId) { return mToplevel->Lookup(aId); }
322 void IProtocol::Unregister(int32_t aId) {
323 if (aId == mId) {
324 mId = kFreedActorId;
326 return mToplevel->Unregister(aId);
329 Shmem::SharedMemory* IProtocol::CreateSharedMemory(size_t aSize, bool aUnsafe,
330 int32_t* aId) {
331 return mToplevel->CreateSharedMemory(aSize, aUnsafe, aId);
333 Shmem::SharedMemory* IProtocol::LookupSharedMemory(int32_t aId) {
334 return mToplevel->LookupSharedMemory(aId);
336 bool IProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* aSegment) {
337 return mToplevel->IsTrackingSharedMemory(aSegment);
339 bool IProtocol::DestroySharedMemory(Shmem& aShmem) {
340 return mToplevel->DestroySharedMemory(aShmem);
343 MessageChannel* IProtocol::GetIPCChannel() {
344 return mToplevel->GetIPCChannel();
346 const MessageChannel* IProtocol::GetIPCChannel() const {
347 return mToplevel->GetIPCChannel();
350 nsISerialEventTarget* IProtocol::GetActorEventTarget() {
351 return GetIPCChannel()->GetWorkerEventTarget();
354 void IProtocol::SetId(int32_t aId) {
355 MOZ_ASSERT(mId == aId || mLinkStatus == LinkStatus::Inactive);
356 mId = aId;
359 Maybe<IProtocol*> IProtocol::ReadActor(IPC::MessageReader* aReader,
360 bool aNullable,
361 const char* aActorDescription,
362 int32_t aProtocolTypeId) {
363 int32_t id;
364 if (!IPC::ReadParam(aReader, &id)) {
365 ActorIdReadError(aActorDescription);
366 return Nothing();
369 if (id == 1 || (id == 0 && !aNullable)) {
370 BadActorIdError(aActorDescription);
371 return Nothing();
374 if (id == 0) {
375 return Some(static_cast<IProtocol*>(nullptr));
378 IProtocol* listener = this->Lookup(id);
379 if (!listener) {
380 ActorLookupError(aActorDescription);
381 return Nothing();
384 if (listener->GetProtocolId() != aProtocolTypeId) {
385 MismatchedActorTypeError(aActorDescription);
386 return Nothing();
389 return Some(listener);
392 void IProtocol::FatalError(const char* const aErrorMsg) const {
393 HandleFatalError(aErrorMsg);
396 void IProtocol::HandleFatalError(const char* aErrorMsg) const {
397 if (IProtocol* manager = Manager()) {
398 manager->HandleFatalError(aErrorMsg);
399 return;
402 mozilla::ipc::FatalError(aErrorMsg, mSide == ParentSide);
405 bool IProtocol::AllocShmem(size_t aSize, Shmem* aOutMem) {
406 if (!CanSend()) {
407 NS_WARNING(
408 "Shmem not allocated. Cannot communicate with the other actor.");
409 return false;
412 Shmem::id_t id;
413 Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, false, &id));
414 if (!rawmem) {
415 return false;
418 *aOutMem = Shmem(rawmem, id);
419 return true;
422 bool IProtocol::AllocUnsafeShmem(size_t aSize, Shmem* aOutMem) {
423 if (!CanSend()) {
424 NS_WARNING(
425 "Shmem not allocated. Cannot communicate with the other actor.");
426 return false;
429 Shmem::id_t id;
430 Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, true, &id));
431 if (!rawmem) {
432 return false;
435 *aOutMem = Shmem(rawmem, id);
436 return true;
439 bool IProtocol::DeallocShmem(Shmem& aMem) {
440 bool ok = DestroySharedMemory(aMem);
441 #ifdef DEBUG
442 if (!ok) {
443 if (mSide == ChildSide) {
444 FatalError("bad Shmem");
445 } else {
446 NS_WARNING("bad Shmem");
448 return false;
450 #endif // DEBUG
451 aMem.forget();
452 return ok;
455 void IProtocol::SetManager(IProtocol* aManager) {
456 MOZ_RELEASE_ASSERT(!mManager || mManager == aManager);
457 mManager = aManager;
458 mToplevel = aManager->mToplevel;
461 void IProtocol::SetManagerAndRegister(IProtocol* aManager) {
462 // Set the manager prior to registering so registering properly inherits
463 // the manager's event target.
464 SetManager(aManager);
466 aManager->Register(this);
469 void IProtocol::SetManagerAndRegister(IProtocol* aManager, int32_t aId) {
470 // Set the manager prior to registering so registering properly inherits
471 // the manager's event target.
472 SetManager(aManager);
474 aManager->RegisterID(this, aId);
477 bool IProtocol::ChannelSend(UniquePtr<IPC::Message> aMsg) {
478 if (CanSend()) {
479 // NOTE: This send call failing can only occur during toplevel channel
480 // teardown. As this is an async call, this isn't reasonable to predict or
481 // respond to, so just drop the message on the floor silently.
482 GetIPCChannel()->Send(std::move(aMsg));
483 return true;
486 WarnMessageDiscarded(aMsg.get());
487 return false;
490 bool IProtocol::ChannelSend(UniquePtr<IPC::Message> aMsg,
491 UniquePtr<IPC::Message>* aReply) {
492 if (CanSend()) {
493 return GetIPCChannel()->Send(std::move(aMsg), aReply);
496 WarnMessageDiscarded(aMsg.get());
497 return false;
500 #ifdef DEBUG
501 void IProtocol::WarnMessageDiscarded(IPC::Message* aMsg) {
502 NS_WARNING(nsPrintfCString("IPC message '%s' discarded: actor cannot send",
503 aMsg->name())
504 .get());
506 #endif
508 void IProtocol::ActorConnected() {
509 if (mLinkStatus != LinkStatus::Inactive) {
510 return;
513 #ifdef FUZZING_SNAPSHOT
514 fuzzing::IPCFuzzController::instance().OnActorConnected(this);
515 #endif
517 mLinkStatus = LinkStatus::Connected;
519 MOZ_ASSERT(!mLifecycleProxy, "double-connecting live actor");
520 mLifecycleProxy = new ActorLifecycleProxy(this);
521 NS_ADDREF(mLifecycleProxy); // Reference freed in DestroySubtree();
524 void IProtocol::DoomSubtree() {
525 MOZ_ASSERT(CanSend(), "dooming non-connected actor");
526 MOZ_ASSERT(mLifecycleProxy, "dooming zombie actor");
528 nsTArray<RefPtr<ActorLifecycleProxy>> managed;
529 AllManagedActors(managed);
530 for (ActorLifecycleProxy* proxy : managed) {
531 // Guard against actor being disconnected or destroyed during previous Doom
532 IProtocol* actor = proxy->Get();
533 if (actor && actor->CanSend()) {
534 actor->DoomSubtree();
538 // ActorDoom is called immediately before changing state, this allows messages
539 // to be sent during ActorDoom immediately before the channel is closed and
540 // sending messages is disabled.
541 ActorDoom();
542 mLinkStatus = LinkStatus::Doomed;
545 void IProtocol::DestroySubtree(ActorDestroyReason aWhy) {
546 MOZ_ASSERT(CanRecv(), "destroying non-connected actor");
547 MOZ_ASSERT(mLifecycleProxy, "destroying zombie actor");
549 #ifdef FUZZING_SNAPSHOT
550 fuzzing::IPCFuzzController::instance().OnActorDestroyed(this);
551 #endif
553 int32_t id = Id();
555 // If we're a managed actor, unregister from our manager
556 if (Manager()) {
557 Unregister(id);
560 // Destroy subtree
561 ActorDestroyReason subtreeWhy = aWhy;
562 if (aWhy == Deletion || aWhy == FailedConstructor) {
563 subtreeWhy = AncestorDeletion;
566 nsTArray<RefPtr<ActorLifecycleProxy>> managed;
567 AllManagedActors(managed);
568 for (ActorLifecycleProxy* proxy : managed) {
569 // Guard against actor being disconnected or destroyed during previous
570 // Destroy
571 IProtocol* actor = proxy->Get();
572 if (actor && actor->CanRecv()) {
573 actor->DestroySubtree(subtreeWhy);
577 // Ensure that we don't send any messages while we're calling `ActorDestroy`
578 // by setting our state to `Doomed`.
579 mLinkStatus = LinkStatus::Doomed;
581 // The actor is being destroyed, reject any pending responses, invoke
582 // `ActorDestroy` to destroy it, and then clear our status to
583 // `LinkStatus::Destroyed`.
584 GetIPCChannel()->RejectPendingResponsesForActor(id);
585 ActorDestroy(aWhy);
586 mLinkStatus = LinkStatus::Destroyed;
589 IToplevelProtocol::IToplevelProtocol(const char* aName, ProtocolId aProtoId,
590 Side aSide)
591 : IProtocol(aProtoId, aSide),
592 mOtherPid(base::kInvalidProcessId),
593 mLastLocalId(0),
594 mChannel(aName, this) {
595 mToplevel = this;
598 void IToplevelProtocol::SetOtherProcessId(base::ProcessId aOtherPid) {
599 mOtherPid = aOtherPid;
602 bool IToplevelProtocol::Open(ScopedPort aPort, const nsID& aMessageChannelId,
603 base::ProcessId aOtherPid,
604 nsISerialEventTarget* aEventTarget) {
605 SetOtherProcessId(aOtherPid);
606 return GetIPCChannel()->Open(std::move(aPort), mSide, aMessageChannelId,
607 aEventTarget);
610 bool IToplevelProtocol::Open(IToplevelProtocol* aTarget,
611 nsISerialEventTarget* aEventTarget,
612 mozilla::ipc::Side aSide) {
613 SetOtherProcessId(base::GetCurrentProcId());
614 aTarget->SetOtherProcessId(base::GetCurrentProcId());
615 return GetIPCChannel()->Open(aTarget->GetIPCChannel(), aEventTarget, aSide);
618 bool IToplevelProtocol::OpenOnSameThread(IToplevelProtocol* aTarget,
619 Side aSide) {
620 SetOtherProcessId(base::GetCurrentProcId());
621 aTarget->SetOtherProcessId(base::GetCurrentProcId());
622 return GetIPCChannel()->OpenOnSameThread(aTarget->GetIPCChannel(), aSide);
625 void IToplevelProtocol::NotifyImpendingShutdown() {
626 if (CanRecv()) {
627 GetIPCChannel()->NotifyImpendingShutdown();
631 void IToplevelProtocol::Close() { GetIPCChannel()->Close(); }
633 void IToplevelProtocol::SetReplyTimeoutMs(int32_t aTimeoutMs) {
634 GetIPCChannel()->SetReplyTimeoutMs(aTimeoutMs);
637 bool IToplevelProtocol::IsOnCxxStack() const {
638 return GetIPCChannel()->IsOnCxxStack();
641 int32_t IToplevelProtocol::NextId() {
642 // Generate the next ID to use for a shared memory or protocol. Parent and
643 // Child sides of the protocol use different pools.
644 int32_t tag = 0;
645 if (GetSide() == ParentSide) {
646 tag |= 1 << 1;
649 // Check any overflow
650 MOZ_RELEASE_ASSERT(mLastLocalId < (1 << 29));
652 // Compute the ID to use with the low two bits as our tag, and the remaining
653 // bits as a monotonic.
654 return (++mLastLocalId << 2) | tag;
657 int32_t IToplevelProtocol::Register(IProtocol* aRouted) {
658 if (aRouted->Id() != kNullActorId && aRouted->Id() != kFreedActorId) {
659 // If there's already an ID, just return that.
660 return aRouted->Id();
662 return RegisterID(aRouted, NextId());
665 int32_t IToplevelProtocol::RegisterID(IProtocol* aRouted, int32_t aId) {
666 aRouted->SetId(aId);
667 aRouted->ActorConnected();
668 MOZ_ASSERT(!mActorMap.Contains(aId), "Don't insert with an existing ID");
669 mActorMap.InsertOrUpdate(aId, aRouted);
670 return aId;
673 IProtocol* IToplevelProtocol::Lookup(int32_t aId) { return mActorMap.Get(aId); }
675 void IToplevelProtocol::Unregister(int32_t aId) {
676 MOZ_ASSERT(mActorMap.Contains(aId),
677 "Attempting to remove an ID not in the actor map");
678 mActorMap.Remove(aId);
681 Shmem::SharedMemory* IToplevelProtocol::CreateSharedMemory(size_t aSize,
682 bool aUnsafe,
683 Shmem::id_t* aId) {
684 RefPtr<Shmem::SharedMemory> segment(Shmem::Alloc(aSize, aUnsafe));
685 if (!segment) {
686 return nullptr;
688 int32_t id = NextId();
689 Shmem shmem(segment.get(), id);
691 UniquePtr<Message> descriptor = shmem.MkCreatedMessage(MSG_ROUTING_CONTROL);
692 if (!descriptor) {
693 return nullptr;
695 Unused << GetIPCChannel()->Send(std::move(descriptor));
697 *aId = shmem.Id();
698 Shmem::SharedMemory* rawSegment = segment.get();
699 MOZ_ASSERT(!mShmemMap.Contains(*aId), "Don't insert with an existing ID");
700 mShmemMap.InsertOrUpdate(*aId, segment.forget().take());
701 return rawSegment;
704 Shmem::SharedMemory* IToplevelProtocol::LookupSharedMemory(Shmem::id_t aId) {
705 return mShmemMap.Get(aId);
708 bool IToplevelProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* segment) {
709 for (const auto& shmem : mShmemMap.Values()) {
710 if (segment == shmem) {
711 return true;
714 return false;
717 bool IToplevelProtocol::DestroySharedMemory(Shmem& shmem) {
718 Shmem::id_t aId = shmem.Id();
719 Shmem::SharedMemory* segment = LookupSharedMemory(aId);
720 if (!segment) {
721 return false;
724 UniquePtr<Message> descriptor = shmem.MkDestroyedMessage(MSG_ROUTING_CONTROL);
726 MOZ_ASSERT(mShmemMap.Contains(aId),
727 "Attempting to remove an ID not in the shmem map");
728 mShmemMap.Remove(aId);
729 Shmem::Dealloc(segment);
731 MessageChannel* channel = GetIPCChannel();
732 if (!channel->CanSend()) {
733 return true;
736 return descriptor && channel->Send(std::move(descriptor));
739 void IToplevelProtocol::DeallocShmems() {
740 for (const auto& shmem : mShmemMap.Values()) {
741 Shmem::Dealloc(shmem);
743 mShmemMap.Clear();
746 bool IToplevelProtocol::ShmemCreated(const Message& aMsg) {
747 Shmem::id_t id;
748 RefPtr<Shmem::SharedMemory> rawmem(Shmem::OpenExisting(aMsg, &id, true));
749 if (!rawmem) {
750 return false;
752 MOZ_ASSERT(!mShmemMap.Contains(id), "Don't insert with an existing ID");
753 mShmemMap.InsertOrUpdate(id, rawmem.forget().take());
754 return true;
757 bool IToplevelProtocol::ShmemDestroyed(const Message& aMsg) {
758 Shmem::id_t id;
759 MessageReader reader(aMsg);
760 if (!IPC::ReadParam(&reader, &id)) {
761 return false;
763 reader.EndRead();
765 Shmem::SharedMemory* rawmem = LookupSharedMemory(id);
766 if (rawmem) {
767 MOZ_ASSERT(mShmemMap.Contains(id),
768 "Attempting to remove an ID not in the shmem map");
769 mShmemMap.Remove(id);
770 Shmem::Dealloc(rawmem);
772 return true;
775 IPDLResolverInner::IPDLResolverInner(UniquePtr<IPC::Message> aReply,
776 IProtocol* aActor)
777 : mReply(std::move(aReply)),
778 mWeakProxy(aActor->GetLifecycleProxy()->GetWeakProxy()) {}
780 void IPDLResolverInner::ResolveOrReject(
781 bool aResolve, FunctionRef<void(IPC::Message*, IProtocol*)> aWrite) {
782 MOZ_ASSERT(mWeakProxy);
783 MOZ_ASSERT(mWeakProxy->ActorEventTarget()->IsOnCurrentThread());
784 MOZ_ASSERT(mReply);
786 UniquePtr<IPC::Message> reply = std::move(mReply);
788 IProtocol* actor = mWeakProxy->Get();
789 if (!actor) {
790 NS_WARNING(nsPrintfCString("Not resolving response '%s': actor is dead",
791 reply->name())
792 .get());
793 return;
796 IPC::MessageWriter writer(*reply, actor);
797 WriteIPDLParam(&writer, actor, aResolve);
798 aWrite(reply.get(), actor);
800 actor->ChannelSend(std::move(reply));
803 void IPDLResolverInner::Destroy() {
804 if (mReply) {
805 NS_PROXY_DELETE_TO_EVENT_TARGET(IPDLResolverInner,
806 mWeakProxy->ActorEventTarget());
807 } else {
808 // If we've already been consumed, just delete without proxying. This avoids
809 // leaking the resolver if the actor's thread is already dead.
810 delete this;
814 IPDLResolverInner::~IPDLResolverInner() {
815 if (mReply) {
816 NS_WARNING(
817 nsPrintfCString(
818 "Rejecting reply '%s': resolver dropped without being called",
819 mReply->name())
820 .get());
821 ResolveOrReject(false, [](IPC::Message* aMessage, IProtocol* aActor) {
822 IPC::MessageWriter writer(*aMessage, aActor);
823 ResponseRejectReason reason = ResponseRejectReason::ResolverDestroyed;
824 WriteIPDLParam(&writer, aActor, reason);
829 } // namespace ipc
830 } // namespace mozilla