Bug 1553272 - Eliminate the unnecessary ProtocolState object, r=froydnj
authorNika Layzell <nika@thelayzells.com>
Thu, 06 Jun 2019 14:57:34 +0000
changeset 477660 60ce5565ab9a8cef6347fd680593d81ab5555054
parent 477659 a40fab0b4398e572b31b86d19ffac1f6cc473db5
child 477661 5a2114b7f34004218d948bfe62b95b1e17e2f23c
push id87287
push usernlayzell@mozilla.com
push dateThu, 06 Jun 2019 17:30:16 +0000
treeherderautoland@60ce5565ab9a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1553272
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1553272 - Eliminate the unnecessary ProtocolState object, r=froydnj The vast majority of the virtual methods which were used on ProtocolState were actually methods which only had meaningful implementations on the toplevel protocol. This patch adds a new field to IProtocol holding a direct pointer to the protocol's `IToplevelProtocol`, and the methods formerly implemented with ProtocolState now directly call the corresponding method on IToplevelProtocol. IToplevelProtocol then shadows these methods with the toplevel protocol implementation, meaning that the right code is run in the right places. In addition, some state was maintained for protocols inside of the separate ProtocolState allocation, and this patch moves that state back into the actor itself. Differential Revision: https://phabricator.services.mozilla.com/D32044
ipc/glue/ProtocolUtils.cpp
ipc/glue/ProtocolUtils.h
ipc/ipdl/ipdl/lower.py
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
tools/fuzzing/ipc/ProtocolFuzzer.h
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -371,16 +371,84 @@ IProtocol::~IProtocol() {
     MOZ_ASSERT(mLinkStatus != LinkStatus::Inactive);
     if (mLinkStatus != LinkStatus::Destroyed) {
       NS_IF_RELEASE(mLifecycleProxy);
     }
     mLifecycleProxy = nullptr;
   }
 }
 
+// The following methods either directly forward to the toplevel protocol, or
+// almost directly do.
+int32_t IProtocol::Register(IProtocol* aRouted) {
+  return mToplevel->Register(aRouted);
+}
+int32_t IProtocol::RegisterID(IProtocol* aRouted, int32_t aId) {
+  return mToplevel->RegisterID(aRouted, aId);
+}
+IProtocol* IProtocol::Lookup(int32_t aId) { return mToplevel->Lookup(aId); }
+void IProtocol::Unregister(int32_t aId) {
+  if (aId == mId) {
+    mId = kFreedActorId;
+  }
+  return mToplevel->Unregister(aId);
+}
+
+Shmem::SharedMemory* IProtocol::CreateSharedMemory(
+    size_t aSize, SharedMemory::SharedMemoryType aType, bool aUnsafe,
+    int32_t* aId) {
+  return mToplevel->CreateSharedMemory(aSize, aType, aUnsafe, aId);
+}
+Shmem::SharedMemory* IProtocol::LookupSharedMemory(int32_t aId) {
+  return mToplevel->LookupSharedMemory(aId);
+}
+bool IProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* aSegment) {
+  return mToplevel->IsTrackingSharedMemory(aSegment);
+}
+bool IProtocol::DestroySharedMemory(Shmem& aShmem) {
+  return mToplevel->DestroySharedMemory(aShmem);
+}
+
+MessageChannel* IProtocol::GetIPCChannel() {
+  return mToplevel->GetIPCChannel();
+}
+const MessageChannel* IProtocol::GetIPCChannel() const {
+  return mToplevel->GetIPCChannel();
+}
+
+void IProtocol::SetEventTargetForActor(IProtocol* aActor,
+                                       nsIEventTarget* aEventTarget) {
+  // Make sure we have a manager for the internal method to access.
+  aActor->SetManager(this);
+  mToplevel->SetEventTargetForActorInternal(aActor, aEventTarget);
+}
+
+void IProtocol::ReplaceEventTargetForActor(IProtocol* aActor,
+                                           nsIEventTarget* aEventTarget) {
+  MOZ_ASSERT(aActor->Manager());
+  mToplevel->ReplaceEventTargetForActor(aActor, aEventTarget);
+}
+
+void IProtocol::SetEventTargetForRoute(int32_t aRoute,
+                                       nsIEventTarget* aEventTarget) {
+  mToplevel->SetEventTargetForRoute(aRoute, aEventTarget);
+}
+
+nsIEventTarget* IProtocol::GetActorEventTarget() {
+  // FIXME: It's a touch sketchy that we don't return a strong reference here.
+  RefPtr<nsIEventTarget> target = GetActorEventTarget(this);
+  return target;
+}
+already_AddRefed<nsIEventTarget> IProtocol::GetActorEventTarget(
+    IProtocol* aActor) {
+  return mToplevel->GetActorEventTarget(aActor);
+}
+
+ProcessId IProtocol::OtherPid() const { return mToplevel->OtherPid(); }
+
 void IProtocol::SetId(int32_t aId) {
   MOZ_ASSERT(mId == aId || mLinkStatus == LinkStatus::Inactive);
   mId = aId;
 }
 
 Maybe<IProtocol*> IProtocol::ReadActor(const IPC::Message* aMessage,
                                        PickleIterator* aIter, bool aNullable,
                                        const char* aActorDescription,
@@ -409,62 +477,16 @@ Maybe<IProtocol*> IProtocol::ReadActor(c
   if (listener->GetProtocolId() != aProtocolTypeId) {
     MismatchedActorTypeError(aActorDescription);
     return Nothing();
   }
 
   return Some(listener);
 }
 
-int32_t IProtocol::ManagedState::Register(IProtocol* aRouted) {
-  return mProtocol->Manager()->Register(aRouted);
-}
-
-int32_t IProtocol::ManagedState::RegisterID(IProtocol* aRouted, int32_t aId) {
-  return mProtocol->Manager()->RegisterID(aRouted, aId);
-}
-
-IProtocol* IProtocol::ManagedState::Lookup(int32_t aId) {
-  return mProtocol->Manager()->Lookup(aId);
-}
-
-void IProtocol::ManagedState::Unregister(int32_t aId) {
-  if (mProtocol->mId == aId) {
-    mProtocol->mId = kFreedActorId;
-  }
-  mProtocol->Manager()->Unregister(aId);
-}
-
-Shmem::SharedMemory* IProtocol::ManagedState::CreateSharedMemory(
-    size_t aSize, SharedMemory::SharedMemoryType aType, bool aUnsafe,
-    int32_t* aId) {
-  return mProtocol->Manager()->CreateSharedMemory(aSize, aType, aUnsafe, aId);
-}
-
-Shmem::SharedMemory* IProtocol::ManagedState::LookupSharedMemory(int32_t aId) {
-  return mProtocol->Manager()->LookupSharedMemory(aId);
-}
-
-bool IProtocol::ManagedState::IsTrackingSharedMemory(
-    Shmem::SharedMemory* aSegment) {
-  return mProtocol->Manager()->IsTrackingSharedMemory(aSegment);
-}
-
-bool IProtocol::ManagedState::DestroySharedMemory(Shmem& aShmem) {
-  return mProtocol->Manager()->DestroySharedMemory(aShmem);
-}
-
-const MessageChannel* IProtocol::ManagedState::GetIPCChannel() const {
-  return mChannel;
-}
-
-MessageChannel* IProtocol::ManagedState::GetIPCChannel() { return mChannel; }
-
-ProcessId IProtocol::OtherPid() const { return Manager()->OtherPid(); }
-
 void IProtocol::FatalError(const char* const aErrorMsg) const {
   HandleFatalError(aErrorMsg);
 }
 
 void IProtocol::HandleFatalError(const char* aErrorMsg) const {
   if (IProtocol* manager = Manager()) {
     manager->HandleFatalError(aErrorMsg);
     return;
@@ -513,95 +535,33 @@ bool IProtocol::DeallocShmem(Shmem& aMem
 #endif  // DEBUG
   aMem.forget(Shmem::PrivateIPDLCaller());
   return ok;
 }
 
 void IProtocol::SetManager(IProtocol* aManager) {
   MOZ_RELEASE_ASSERT(!mManager || mManager == aManager);
   mManager = aManager;
+  mToplevel = aManager->mToplevel;
 }
 
 void IProtocol::SetManagerAndRegister(IProtocol* aManager) {
   // Set the manager prior to registering so registering properly inherits
   // the manager's event target.
   SetManager(aManager);
 
   aManager->Register(this);
-
-  mState->SetIPCChannel(aManager->GetIPCChannel());
 }
 
 void IProtocol::SetManagerAndRegister(IProtocol* aManager, int32_t aId) {
   // Set the manager prior to registering so registering properly inherits
   // the manager's event target.
   SetManager(aManager);
 
   aManager->RegisterID(this, aId);
-
-  mState->SetIPCChannel(aManager->GetIPCChannel());
-}
-
-void IProtocol::SetEventTargetForActor(IProtocol* aActor,
-                                       nsIEventTarget* aEventTarget) {
-  // Make sure we have a manager for the internal method to access.
-  aActor->SetManager(this);
-  mState->SetEventTargetForActor(aActor, aEventTarget);
-}
-
-void IProtocol::ReplaceEventTargetForActor(IProtocol* aActor,
-                                           nsIEventTarget* aEventTarget) {
-  // Ensure the actor has been registered.
-  MOZ_ASSERT(aActor->Manager());
-  mState->ReplaceEventTargetForActor(aActor, aEventTarget);
-}
-
-void IProtocol::SetEventTargetForRoute(int32_t aRoute,
-                                       nsIEventTarget* aEventTarget) {
-  mState->SetEventTargetForRoute(aRoute, aEventTarget);
-}
-
-nsIEventTarget* IProtocol::GetActorEventTarget() {
-  return mState->GetActorEventTarget();
-}
-
-already_AddRefed<nsIEventTarget> IProtocol::GetActorEventTarget(
-    IProtocol* aActor) {
-  return mState->GetActorEventTarget(aActor);
-}
-
-nsIEventTarget* IProtocol::ManagedState::GetActorEventTarget() {
-  // We should only call this function when this actor has been registered and
-  // is not unregistered yet.
-  MOZ_RELEASE_ASSERT(mProtocol->mId != kNullActorId &&
-                     mProtocol->mId != kFreedActorId);
-  RefPtr<nsIEventTarget> target = GetActorEventTarget(mProtocol);
-  return target;
-}
-
-void IProtocol::ManagedState::SetEventTargetForActor(
-    IProtocol* aActor, nsIEventTarget* aEventTarget) {
-  // Go directly through the state so we don't try to redundantly (and
-  // wrongly) call SetManager() on aActor.
-  mProtocol->Manager()->mState->SetEventTargetForActor(aActor, aEventTarget);
-}
-
-void IProtocol::ManagedState::ReplaceEventTargetForActor(
-    IProtocol* aActor, nsIEventTarget* aEventTarget) {
-  mProtocol->Manager()->ReplaceEventTargetForActor(aActor, aEventTarget);
-}
-
-void IProtocol::ManagedState::SetEventTargetForRoute(
-    int32_t aRoute, nsIEventTarget* aEventTarget) {
-  mProtocol->Manager()->SetEventTargetForRoute(aRoute, aEventTarget);
-}
-
-already_AddRefed<nsIEventTarget> IProtocol::ManagedState::GetActorEventTarget(
-    IProtocol* aActor) {
-  return mProtocol->Manager()->GetActorEventTarget(aActor);
 }
 
 bool IProtocol::ChannelSend(IPC::Message* aMsg) {
   UniquePtr<IPC::Message> msg(aMsg);
   if (CanSend()) {
     return GetIPCChannel()->Send(msg.release());
   }
 
@@ -697,39 +657,39 @@ void IProtocol::DestroySubtree(ActorDest
   // `LinkStatus::Destroyed`.
   GetIPCChannel()->RejectPendingResponsesForActor(this);
   ActorDestroy(aWhy);
   mLinkStatus = LinkStatus::Destroyed;
 }
 
 IToplevelProtocol::IToplevelProtocol(const char* aName, ProtocolId aProtoId,
                                      Side aSide)
-    : IProtocol(aProtoId, aSide, MakeUnique<ToplevelState>(aName, this, aSide)),
+    : IProtocol(aProtoId, aSide),
       mOtherPid(mozilla::ipc::kInvalidProcessId),
-      mIsMainThreadProtocol(false) {}
+      mLastLocalId(0),
+      mEventTargetMutex("ProtocolEventTargetMutex"),
+      mMiddlemanChannelOverride(nullptr),
+      mChannel(aName, this) {
+  mToplevel = this;
+}
 
 IToplevelProtocol::~IToplevelProtocol() {
-  mState = nullptr;
   if (mTrans) {
     RefPtr<DeleteTask<Transport>> task =
         new DeleteTask<Transport>(mTrans.release());
     XRE_GetIOMessageLoop()->PostTask(task.forget());
   }
 }
 
 base::ProcessId IToplevelProtocol::OtherPid() const {
   base::ProcessId pid = OtherPidMaybeInvalid();
   MOZ_RELEASE_ASSERT(pid != kInvalidProcessId);
   return pid;
 }
 
-base::ProcessId IToplevelProtocol::OtherPidMaybeInvalid() const {
-  return mOtherPid;
-}
-
 void IToplevelProtocol::SetOtherProcessId(base::ProcessId aOtherPid) {
   // When recording an execution, all communication we do is forwarded from
   // the middleman to the parent process, so use its pid instead of the
   // middleman's pid.
   if (recordreplay::IsRecordingOrReplaying() &&
       aOtherPid == recordreplay::child::MiddlemanProcessId()) {
     mOtherPid = recordreplay::child::ParentProcessId();
   } else {
@@ -775,37 +735,37 @@ void IToplevelProtocol::Close() { GetIPC
 void IToplevelProtocol::SetReplyTimeoutMs(int32_t aTimeoutMs) {
   GetIPCChannel()->SetReplyTimeoutMs(aTimeoutMs);
 }
 
 bool IToplevelProtocol::IsOnCxxStack() const {
   return GetIPCChannel()->IsOnCxxStack();
 }
 
-int32_t IToplevelProtocol::ToplevelState::NextId() {
+int32_t IToplevelProtocol::NextId() {
   // Genreate the next ID to use for a shared memory or protocol. Parent and
   // Child sides of the protocol use different pools, and actors created in the
   // middleman need to use a distinct pool as well.
   int32_t tag = 0;
   if (recordreplay::IsMiddleman()) {
     tag |= 1 << 0;
   }
-  if (mProtocol->GetSide() == ParentSide) {
+  if (GetSide() == ParentSide) {
     tag |= 1 << 1;
   }
 
   // Check any overflow
   MOZ_RELEASE_ASSERT(mLastLocalId < (1 << 29));
 
   // Compute the ID to use with the low two bits as our tag, and the remaining
   // bits as a monotonic.
   return (++mLastLocalId << 2) | tag;
 }
 
-int32_t IToplevelProtocol::ToplevelState::Register(IProtocol* aRouted) {
+int32_t IToplevelProtocol::Register(IProtocol* aRouted) {
   if (aRouted->Id() != kNullActorId && aRouted->Id() != kFreedActorId) {
     // If there's already an ID, just return that.
     return aRouted->Id();
   }
   int32_t id = RegisterID(aRouted, NextId());
 
   // Inherit our event target from our manager.
   if (IProtocol* manager = aRouted->Manager()) {
@@ -814,148 +774,135 @@ int32_t IToplevelProtocol::ToplevelState
             mEventTargetMap.Lookup(manager->Id())) {
       mEventTargetMap.AddWithID(target, id);
     }
   }
 
   return id;
 }
 
-int32_t IToplevelProtocol::ToplevelState::RegisterID(IProtocol* aRouted,
-                                                     int32_t aId) {
+int32_t IToplevelProtocol::RegisterID(IProtocol* aRouted, int32_t aId) {
   aRouted->SetId(aId);
   aRouted->ActorConnected();
   mActorMap.AddWithID(aRouted, aId);
   return aId;
 }
 
-IProtocol* IToplevelProtocol::ToplevelState::Lookup(int32_t aId) {
+IProtocol* IToplevelProtocol::Lookup(int32_t aId) {
   return mActorMap.Lookup(aId);
 }
 
-void IToplevelProtocol::ToplevelState::Unregister(int32_t aId) {
+void IToplevelProtocol::Unregister(int32_t aId) {
   mActorMap.Remove(aId);
 
   MutexAutoLock lock(mEventTargetMutex);
   mEventTargetMap.RemoveIfPresent(aId);
 }
 
-IToplevelProtocol::ToplevelState::ToplevelState(const char* aName,
-                                                IToplevelProtocol* aProtocol,
-                                                Side aSide)
-    : ProtocolState(),
-      mProtocol(aProtocol),
-      mLastLocalId(0),
-      mEventTargetMutex("ProtocolEventTargetMutex"),
-      mChannel(aName, aProtocol) {}
-
-Shmem::SharedMemory* IToplevelProtocol::ToplevelState::CreateSharedMemory(
+Shmem::SharedMemory* IToplevelProtocol::CreateSharedMemory(
     size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, bool aUnsafe,
     Shmem::id_t* aId) {
-  // XXX the mProtocol uses here should go away!
   RefPtr<Shmem::SharedMemory> segment(
       Shmem::Alloc(Shmem::PrivateIPDLCaller(), aSize, aType, aUnsafe));
   if (!segment) {
     return nullptr;
   }
   int32_t id = NextId();
   Shmem shmem(Shmem::PrivateIPDLCaller(), segment.get(), id);
 
   base::ProcessId pid =
 #ifdef ANDROID
       // We use OtherPidMaybeInvalid() because on Android this method is
       // actually called on an unconnected protocol, but Android's shared memory
       // implementation doesn't actually use the PID.
-      mProtocol->OtherPidMaybeInvalid();
+      OtherPidMaybeInvalid();
 #else
-      mProtocol->OtherPid();
+      OtherPid();
 #endif
 
   Message* descriptor =
       shmem.ShareTo(Shmem::PrivateIPDLCaller(), pid, MSG_ROUTING_CONTROL);
   if (!descriptor) {
     return nullptr;
   }
-  Unused << mProtocol->GetIPCChannel()->Send(descriptor);
+  Unused << GetIPCChannel()->Send(descriptor);
 
   *aId = shmem.Id(Shmem::PrivateIPDLCaller());
   Shmem::SharedMemory* rawSegment = segment.get();
   mShmemMap.AddWithID(segment.forget().take(), *aId);
   return rawSegment;
 }
 
-Shmem::SharedMemory* IToplevelProtocol::ToplevelState::LookupSharedMemory(
-    Shmem::id_t aId) {
+Shmem::SharedMemory* IToplevelProtocol::LookupSharedMemory(Shmem::id_t aId) {
   return mShmemMap.Lookup(aId);
 }
 
-bool IToplevelProtocol::ToplevelState::IsTrackingSharedMemory(
-    Shmem::SharedMemory* segment) {
+bool IToplevelProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* segment) {
   return mShmemMap.HasData(segment);
 }
 
-bool IToplevelProtocol::ToplevelState::DestroySharedMemory(Shmem& shmem) {
+bool IToplevelProtocol::DestroySharedMemory(Shmem& shmem) {
   Shmem::id_t aId = shmem.Id(Shmem::PrivateIPDLCaller());
   Shmem::SharedMemory* segment = LookupSharedMemory(aId);
   if (!segment) {
     return false;
   }
 
   Message* descriptor =
       shmem.UnshareFrom(Shmem::PrivateIPDLCaller(), MSG_ROUTING_CONTROL);
 
   mShmemMap.Remove(aId);
   Shmem::Dealloc(Shmem::PrivateIPDLCaller(), segment);
 
-  MessageChannel* channel = mProtocol->GetIPCChannel();
+  MessageChannel* channel = GetIPCChannel();
   if (!channel->CanSend()) {
     delete descriptor;
     return true;
   }
 
   return descriptor && channel->Send(descriptor);
 }
 
-void IToplevelProtocol::ToplevelState::DeallocShmems() {
+void IToplevelProtocol::DeallocShmems() {
   for (IDMap<SharedMemory*>::const_iterator cit = mShmemMap.begin();
        cit != mShmemMap.end(); ++cit) {
     Shmem::Dealloc(Shmem::PrivateIPDLCaller(), cit->second);
   }
   mShmemMap.Clear();
 }
 
-bool IToplevelProtocol::ToplevelState::ShmemCreated(const Message& aMsg) {
+bool IToplevelProtocol::ShmemCreated(const Message& aMsg) {
   Shmem::id_t id;
   RefPtr<Shmem::SharedMemory> rawmem(
       Shmem::OpenExisting(Shmem::PrivateIPDLCaller(), aMsg, &id, true));
   if (!rawmem) {
     return false;
   }
   mShmemMap.AddWithID(rawmem.forget().take(), id);
   return true;
 }
 
-bool IToplevelProtocol::ToplevelState::ShmemDestroyed(const Message& aMsg) {
+bool IToplevelProtocol::ShmemDestroyed(const Message& aMsg) {
   Shmem::id_t id;
   PickleIterator iter = PickleIterator(aMsg);
   if (!IPC::ReadParam(&aMsg, &iter, &id)) {
     return false;
   }
   aMsg.EndRead(iter);
 
   Shmem::SharedMemory* rawmem = LookupSharedMemory(id);
   if (rawmem) {
     mShmemMap.Remove(id);
     Shmem::Dealloc(Shmem::PrivateIPDLCaller(), rawmem);
   }
   return true;
 }
 
-already_AddRefed<nsIEventTarget>
-IToplevelProtocol::ToplevelState::GetMessageEventTarget(const Message& aMsg) {
+already_AddRefed<nsIEventTarget> IToplevelProtocol::GetMessageEventTarget(
+    const Message& aMsg) {
   int32_t route = aMsg.routing_id();
 
   Maybe<MutexAutoLock> lock;
   lock.emplace(mEventTargetMutex);
 
   nsCOMPtr<nsIEventTarget> target = mEventTargetMap.Lookup(route);
 
   if (aMsg.is_constructor()) {
@@ -965,56 +912,58 @@ IToplevelProtocol::ToplevelState::GetMes
       return nullptr;
     }
 
     // Normally a new actor inherits its event target from its manager. If the
     // manager has no event target, we give the subclass a chance to make a new
     // one.
     if (!target) {
       MutexAutoUnlock unlock(mEventTargetMutex);
-      target = mProtocol->GetConstructedEventTarget(aMsg);
+      target = GetConstructedEventTarget(aMsg);
     }
 
     mEventTargetMap.AddWithID(target, handle.mId);
   } else if (!target) {
     // We don't need the lock after this point.
     lock.reset();
 
-    target = mProtocol->GetSpecificMessageEventTarget(aMsg);
+    target = GetSpecificMessageEventTarget(aMsg);
   }
 
   return target.forget();
 }
 
-already_AddRefed<nsIEventTarget>
-IToplevelProtocol::ToplevelState::GetActorEventTarget(IProtocol* aActor) {
+already_AddRefed<nsIEventTarget> IToplevelProtocol::GetActorEventTarget(
+    IProtocol* aActor) {
   MOZ_RELEASE_ASSERT(aActor->Id() != kNullActorId &&
                      aActor->Id() != kFreedActorId);
 
   MutexAutoLock lock(mEventTargetMutex);
   nsCOMPtr<nsIEventTarget> target = mEventTargetMap.Lookup(aActor->Id());
   return target.forget();
 }
 
-nsIEventTarget* IToplevelProtocol::ToplevelState::GetActorEventTarget() {
+nsIEventTarget* IToplevelProtocol::GetActorEventTarget() {
   // The EventTarget of a ToplevelProtocol shall never be set.
   return nullptr;
 }
 
-void IToplevelProtocol::ToplevelState::SetEventTargetForActor(
+void IToplevelProtocol::SetEventTargetForActorInternal(
     IProtocol* aActor, nsIEventTarget* aEventTarget) {
   // The EventTarget of a ToplevelProtocol shall never be set.
-  MOZ_RELEASE_ASSERT(aActor != mProtocol);
+  MOZ_RELEASE_ASSERT(aActor != this);
 
   // We should only call this function on actors that haven't been used for IPC
   // code yet. Otherwise we'll be posting stuff to the wrong event target before
   // we're called.
   MOZ_RELEASE_ASSERT(aActor->Id() == kNullActorId ||
                      aActor->Id() == kFreedActorId);
 
+  MOZ_ASSERT(aActor->Manager() && aActor->ToplevelProtocol() == this);
+
   // Register the actor early. When it's registered again, it will keep the same
   // ID.
   int32_t id = Register(aActor);
   aActor->SetId(id);
 
   MutexAutoLock lock(mEventTargetMutex);
   // FIXME bug 1445121 - sometimes the id is already mapped.
   // (IDMap debug-asserts that the existing state is as expected.)
@@ -1024,41 +973,33 @@ void IToplevelProtocol::ToplevelState::S
 #endif
   if (replace) {
     mEventTargetMap.ReplaceWithID(aEventTarget, id);
   } else {
     mEventTargetMap.AddWithID(aEventTarget, id);
   }
 }
 
-void IToplevelProtocol::ToplevelState::ReplaceEventTargetForActor(
+void IToplevelProtocol::ReplaceEventTargetForActor(
     IProtocol* aActor, nsIEventTarget* aEventTarget) {
   // The EventTarget of a ToplevelProtocol shall never be set.
-  MOZ_RELEASE_ASSERT(aActor != mProtocol);
+  MOZ_RELEASE_ASSERT(aActor != this);
 
   int32_t id = aActor->Id();
   // The ID of the actor should have existed.
   MOZ_RELEASE_ASSERT(id != kNullActorId && id != kFreedActorId);
 
   MutexAutoLock lock(mEventTargetMutex);
   mEventTargetMap.ReplaceWithID(aEventTarget, id);
 }
 
-void IToplevelProtocol::ToplevelState::SetEventTargetForRoute(
-    int32_t aRoute, nsIEventTarget* aEventTarget) {
-  MOZ_RELEASE_ASSERT(aRoute != mProtocol->Id());
+void IToplevelProtocol::SetEventTargetForRoute(int32_t aRoute,
+                                               nsIEventTarget* aEventTarget) {
+  MOZ_RELEASE_ASSERT(aRoute != Id());
   MOZ_RELEASE_ASSERT(aRoute != kNullActorId && aRoute != kFreedActorId);
 
   MutexAutoLock lock(mEventTargetMutex);
   MOZ_ASSERT(!mEventTargetMap.Lookup(aRoute));
   mEventTargetMap.AddWithID(aEventTarget, aRoute);
 }
 
-const MessageChannel* IToplevelProtocol::ToplevelState::GetIPCChannel() const {
-  return ProtocolState::mChannel ? ProtocolState::mChannel : &mChannel;
-}
-
-MessageChannel* IToplevelProtocol::ToplevelState::GetIPCChannel() {
-  return ProtocolState::mChannel ? ProtocolState::mChannel : &mChannel;
-}
-
 }  // namespace ipc
 }  // namespace mozilla
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -157,201 +157,56 @@ typedef IPCMessageStart ProtocolId;
 
 // Generated by IPDL compiler
 const char* ProtocolIdToName(IPCMessageStart aId);
 
 class IToplevelProtocol;
 class ActorLifecycleProxy;
 
 class IProtocol : public HasResultCodes {
-#ifdef FUZZING
-  friend class mozilla::ipc::ProtocolFuzzerHelper;
-#endif
-
  public:
   enum ActorDestroyReason {
     FailedConstructor,
     Deletion,
     AncestorDeletion,
     NormalShutdown,
     AbnormalShutdown
   };
 
-  // A lot of the functionality of IProtocol only differs between toplevel
-  // protocols (IToplevelProtocol) and managed protocols (everything else).
-  // If we put such functionality in IProtocol via virtual methods, that
-  // means that *every* protocol inherits that functionality through said
-  // virtual methods, then every protocol needs a (largely redundant)
-  // entry in its vtable.  That redundancy adds up quickly with several
-  // hundred protocols.
-  //
-  // This class (and its two subclasses) ensure that we don't have a bunch
-  // of redundant entries in protocol vtables: we have a single vtable per
-  // subclass, and then each protocol has its own instance of one of the
-  // subclasses.  This setup makes things a bit slower, but the space
-  // savings are worth it.
-  class ProtocolState {
-   public:
-    ProtocolState() : mChannel(nullptr) {}
-    virtual ~ProtocolState() = default;
-
-    // Shared memory functions.
-    virtual Shmem::SharedMemory* CreateSharedMemory(
-        size_t, SharedMemory::SharedMemoryType, bool, int32_t*) = 0;
-    virtual Shmem::SharedMemory* LookupSharedMemory(int32_t) = 0;
-    virtual bool IsTrackingSharedMemory(Shmem::SharedMemory*) = 0;
-    virtual bool DestroySharedMemory(Shmem&) = 0;
-
-    // Protocol management functions.
-    virtual int32_t Register(IProtocol*) = 0;
-    virtual int32_t RegisterID(IProtocol*, int32_t) = 0;
-    virtual IProtocol* Lookup(int32_t) = 0;
-    virtual void Unregister(int32_t) = 0;
-
-    // Returns the event target set by SetEventTargetForActor() if available.
-    virtual nsIEventTarget* GetActorEventTarget() = 0;
-
-    virtual void SetEventTargetForActor(IProtocol* aActor,
-                                        nsIEventTarget* aEventTarget) = 0;
-    virtual void ReplaceEventTargetForActor(IProtocol* aActor,
-                                            nsIEventTarget* aEventTarget) = 0;
-    virtual void SetEventTargetForRoute(int32_t aRoute,
-                                        nsIEventTarget* aEventTarget) = 0;
-
-    virtual already_AddRefed<nsIEventTarget> GetActorEventTarget(
-        IProtocol* aActor) = 0;
-
-    virtual const MessageChannel* GetIPCChannel() const = 0;
-    virtual MessageChannel* GetIPCChannel() = 0;
-
-    // XXX we have this weird setup where ProtocolState has an mChannel
-    // member, but it (probably?) only gets set for protocols that have
-    // a manager.  That is, for toplevel protocols, this member is dead
-    // weight and should be removed, since toplevel protocols maintain
-    // their own channel.
-    void SetIPCChannel(MessageChannel* aChannel) { mChannel = aChannel; }
-
-   protected:
-    MessageChannel* mChannel;
-  };
-
-  // Managed protocols just forward all of their operations to the topmost
-  // managing protocol.
-  class ManagedState final : public ProtocolState {
-   public:
-    explicit ManagedState(IProtocol* aProtocol)
-        : ProtocolState(), mProtocol(aProtocol) {}
-
-    Shmem::SharedMemory* CreateSharedMemory(size_t,
-                                            SharedMemory::SharedMemoryType,
-                                            bool, int32_t*) override;
-    Shmem::SharedMemory* LookupSharedMemory(int32_t) override;
-    bool IsTrackingSharedMemory(Shmem::SharedMemory*) override;
-    bool DestroySharedMemory(Shmem&) override;
-
-    int32_t Register(IProtocol*) override;
-    int32_t RegisterID(IProtocol*, int32_t) override;
-    IProtocol* Lookup(int32_t) override;
-    void Unregister(int32_t) override;
-
-    nsIEventTarget* GetActorEventTarget() override;
-    void SetEventTargetForActor(IProtocol* aActor,
-                                nsIEventTarget* aEventTarget) override;
-    void ReplaceEventTargetForActor(IProtocol* aActor,
-                                    nsIEventTarget* aEventTarget) override;
-    void SetEventTargetForRoute(int32_t aRoute,
-                                nsIEventTarget* aEventTarget) override;
-    already_AddRefed<nsIEventTarget> GetActorEventTarget(
-        IProtocol* aActor) override;
-
-    const MessageChannel* GetIPCChannel() const override;
-    MessageChannel* GetIPCChannel() override;
-
-   private:
-    IProtocol* const mProtocol;
-  };
-
   typedef base::ProcessId ProcessId;
   typedef IPC::Message Message;
   typedef IPC::MessageInfo MessageInfo;
 
-  explicit IProtocol(ProtocolId aProtoId, Side aSide)
-      : IProtocol(aProtoId, aSide, MakeUnique<ManagedState>(this)) {}
+  IProtocol(ProtocolId aProtoId, Side aSide)
+      : mId(0),
+        mProtocolId(aProtoId),
+        mSide(aSide),
+        mLinkStatus(LinkStatus::Inactive),
+        mLifecycleProxy(nullptr),
+        mManager(nullptr),
+        mToplevel(nullptr) {}
 
-  int32_t Register(IProtocol* aRouted) { return mState->Register(aRouted); }
-  int32_t RegisterID(IProtocol* aRouted, int32_t aId) {
-    return mState->RegisterID(aRouted, aId);
-  }
-  IProtocol* Lookup(int32_t aId) { return mState->Lookup(aId); }
-  void Unregister(int32_t aId) { return mState->Unregister(aId); }
+  IToplevelProtocol* ToplevelProtocol() { return mToplevel; }
 
-  virtual void RemoveManagee(int32_t, IProtocol*) = 0;
-  virtual void DeallocManagee(int32_t, IProtocol*) = 0;
+  // The following methods either directly forward to the toplevel protocol, or
+  // almost directly do.
+  int32_t Register(IProtocol* aRouted);
+  int32_t RegisterID(IProtocol* aRouted, int32_t aId);
+  IProtocol* Lookup(int32_t aId);
+  void Unregister(int32_t aId);
 
   Shmem::SharedMemory* CreateSharedMemory(size_t aSize,
                                           SharedMemory::SharedMemoryType aType,
-                                          bool aUnsafe, int32_t* aId) {
-    return mState->CreateSharedMemory(aSize, aType, aUnsafe, aId);
-  }
-  Shmem::SharedMemory* LookupSharedMemory(int32_t aId) {
-    return mState->LookupSharedMemory(aId);
-  }
-  bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment) {
-    return mState->IsTrackingSharedMemory(aSegment);
-  }
-  bool DestroySharedMemory(Shmem& aShmem) {
-    return mState->DestroySharedMemory(aShmem);
-  }
-
-  MessageChannel* GetIPCChannel() { return mState->GetIPCChannel(); }
-  const MessageChannel* GetIPCChannel() const {
-    return mState->GetIPCChannel();
-  }
-  void SetMiddlemanIPCChannel(MessageChannel* aChannel) {
-    // Middleman processes sometimes need to change the channel used by a
-    // protocol.
-    MOZ_RELEASE_ASSERT(recordreplay::IsMiddleman());
-    mState->SetIPCChannel(aChannel);
-  }
-
-  // XXX odd ducks, acknowledged
-  virtual ProcessId OtherPid() const;
-  Side GetSide() const { return mSide; }
+                                          bool aUnsafe, int32_t* aId);
+  Shmem::SharedMemory* LookupSharedMemory(int32_t aId);
+  bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
+  bool DestroySharedMemory(Shmem& aShmem);
 
-  bool CanSend() const { return mLinkStatus == LinkStatus::Connected; }
-  bool CanRecv() const {
-    return mLinkStatus == LinkStatus::Connected ||
-           mLinkStatus == LinkStatus::Doomed;
-  }
-
-  void FatalError(const char* const aErrorMsg) const;
-  virtual void HandleFatalError(const char* aErrorMsg) const;
-
-  Maybe<IProtocol*> ReadActor(const IPC::Message* aMessage,
-                              PickleIterator* aIter, bool aNullable,
-                              const char* aActorDescription,
-                              int32_t aProtocolTypeId);
-
-  virtual Result OnMessageReceived(const Message& aMessage) = 0;
-  virtual Result OnMessageReceived(const Message& aMessage,
-                                   Message*& aReply) = 0;
-  virtual Result OnCallReceived(const Message& aMessage, Message*& aReply) = 0;
-
-  ProtocolId GetProtocolId() const { return mProtocolId; }
-  const char* GetProtocolName() const { return ProtocolIdToName(mProtocolId); }
-
-  int32_t Id() const { return mId; }
-  IProtocol* Manager() const { return mManager; }
-
-  bool AllocShmem(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType,
-                  Shmem* aOutMem);
-  bool AllocUnsafeShmem(size_t aSize,
-                        Shmem::SharedMemory::SharedMemoryType aType,
-                        Shmem* aOutMem);
-  bool DeallocShmem(Shmem& aMem);
+  MessageChannel* GetIPCChannel();
+  const MessageChannel* GetIPCChannel() const;
 
   // Sets an event target to which all messages for aActor will be
   // dispatched. This method must be called before right before the SendPFoo
   // message for aActor is sent. And SendPFoo *must* be called if
   // SetEventTargetForActor is called. The receiver when calling
   // SetEventTargetForActor must be the actor that will be the manager for
   // aActor.
   void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget);
@@ -362,28 +217,58 @@ class IProtocol : public HasResultCodes 
   void ReplaceEventTargetForActor(IProtocol* aActor,
                                   nsIEventTarget* aEventTarget);
 
   void SetEventTargetForRoute(int32_t aRoute, nsIEventTarget* aEventTarget);
 
   nsIEventTarget* GetActorEventTarget();
   already_AddRefed<nsIEventTarget> GetActorEventTarget(IProtocol* aActor);
 
+  ProcessId OtherPid() const;
+
+  // Actor lifecycle and other properties.
+  ProtocolId GetProtocolId() const { return mProtocolId; }
+  const char* GetProtocolName() const { return ProtocolIdToName(mProtocolId); }
+
+  int32_t Id() const { return mId; }
+  IProtocol* Manager() const { return mManager; }
+
   ActorLifecycleProxy* GetLifecycleProxy() { return mLifecycleProxy; }
 
+  Side GetSide() const { return mSide; }
+  bool CanSend() const { return mLinkStatus == LinkStatus::Connected; }
+  bool CanRecv() const {
+    return mLinkStatus == LinkStatus::Connected ||
+           mLinkStatus == LinkStatus::Doomed;
+  }
+
+  // Remove or deallocate a managee given its type.
+  virtual void RemoveManagee(int32_t, IProtocol*) = 0;
+  virtual void DeallocManagee(int32_t, IProtocol*) = 0;
+
+  Maybe<IProtocol*> ReadActor(const IPC::Message* aMessage,
+                              PickleIterator* aIter, bool aNullable,
+                              const char* aActorDescription,
+                              int32_t aProtocolTypeId);
+
+  virtual Result OnMessageReceived(const Message& aMessage) = 0;
+  virtual Result OnMessageReceived(const Message& aMessage,
+                                   Message*& aReply) = 0;
+  virtual Result OnCallReceived(const Message& aMessage, Message*& aReply) = 0;
+  bool AllocShmem(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType,
+                  Shmem* aOutMem);
+  bool AllocUnsafeShmem(size_t aSize,
+                        Shmem::SharedMemory::SharedMemoryType aType,
+                        Shmem* aOutMem);
+  bool DeallocShmem(Shmem& aMem);
+
+  void FatalError(const char* const aErrorMsg) const;
+  virtual void HandleFatalError(const char* aErrorMsg) const;
+
  protected:
-  IProtocol(ProtocolId aProtoId, Side aSide, UniquePtr<ProtocolState> aState)
-      : mId(0),
-        mProtocolId(aProtoId),
-        mSide(aSide),
-        mLinkStatus(LinkStatus::Inactive),
-        mLifecycleProxy(nullptr),
-        mManager(nullptr),
-        mState(std::move(aState)) {}
-
   virtual ~IProtocol();
 
   friend class IToplevelProtocol;
   friend class ActorLifecycleProxy;
 
   void SetId(int32_t aId);
 
   // We have separate functions because the accessibility code manually
@@ -449,17 +334,17 @@ class IProtocol : public HasResultCodes 
 
  private:
   int32_t mId;
   ProtocolId mProtocolId;
   Side mSide;
   LinkStatus mLinkStatus;
   ActorLifecycleProxy* mLifecycleProxy;
   IProtocol* mManager;
-  UniquePtr<ProtocolState> mState;
+  IToplevelProtocol* mToplevel;
 };
 
 #define IPC_OK() mozilla::ipc::IPCResult::Ok()
 #define IPC_FAIL(actor, why) \
   mozilla::ipc::IPCResult::Fail(WrapNotNull(actor), __func__, (why))
 #define IPC_FAIL_NO_REASON(actor) \
   mozilla::ipc::IPCResult::Fail(WrapNotNull(actor), __func__)
 
@@ -486,87 +371,73 @@ class Endpoint;
 
 /**
  * All top-level protocols should inherit this class.
  *
  * IToplevelProtocol tracks all top-level protocol actors created from
  * this protocol actor.
  */
 class IToplevelProtocol : public IProtocol {
+#ifdef FUZZING
+  friend class mozilla::ipc::ProtocolFuzzerHelper;
+#endif
+
   template <class PFooSide>
   friend class Endpoint;
 
  protected:
   explicit IToplevelProtocol(const char* aName, ProtocolId aProtoId,
                              Side aSide);
   ~IToplevelProtocol();
 
  public:
-  class ToplevelState final : public ProtocolState {
-#ifdef FUZZING
-    friend class mozilla::ipc::ProtocolFuzzerHelper;
-#endif
-
-   public:
-    ToplevelState(const char* aName, IToplevelProtocol* aProtocol, Side aSide);
+  // Shadow methods on IProtocol which are implemented directly on toplevel
+  // actors.
+  int32_t Register(IProtocol* aRouted);
+  int32_t RegisterID(IProtocol* aRouted, int32_t aId);
+  IProtocol* Lookup(int32_t aId);
+  void Unregister(int32_t aId);
 
-    Shmem::SharedMemory* CreateSharedMemory(size_t,
-                                            SharedMemory::SharedMemoryType,
-                                            bool, int32_t*) override;
-    Shmem::SharedMemory* LookupSharedMemory(int32_t) override;
-    bool IsTrackingSharedMemory(Shmem::SharedMemory*) override;
-    bool DestroySharedMemory(Shmem&) override;
-
-    void DeallocShmems();
-
-    bool ShmemCreated(const Message& aMsg);
-    bool ShmemDestroyed(const Message& aMsg);
-
-    int32_t Register(IProtocol*) override;
-    int32_t RegisterID(IProtocol*, int32_t) override;
-    IProtocol* Lookup(int32_t) override;
-    void Unregister(int32_t) override;
+  Shmem::SharedMemory* CreateSharedMemory(size_t aSize,
+                                          SharedMemory::SharedMemoryType aType,
+                                          bool aUnsafe, int32_t* aId);
+  Shmem::SharedMemory* LookupSharedMemory(int32_t aId);
+  bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
+  bool DestroySharedMemory(Shmem& aShmem);
 
-    nsIEventTarget* GetActorEventTarget() override;
-    void SetEventTargetForActor(IProtocol* aActor,
-                                nsIEventTarget* aEventTarget) override;
-    void ReplaceEventTargetForActor(IProtocol* aActor,
-                                    nsIEventTarget* aEventTarget) override;
-    void SetEventTargetForRoute(int32_t aRoute,
-                                nsIEventTarget* aEventTarget) override;
-    already_AddRefed<nsIEventTarget> GetActorEventTarget(
-        IProtocol* aActor) override;
-
-    virtual already_AddRefed<nsIEventTarget> GetMessageEventTarget(
-        const Message& aMsg);
+  MessageChannel* GetIPCChannel() {
+    if (mMiddlemanChannelOverride) {
+      return mMiddlemanChannelOverride;
+    }
+    return &mChannel;
+  }
+  const MessageChannel* GetIPCChannel() const {
+    if (mMiddlemanChannelOverride) {
+      return mMiddlemanChannelOverride;
+    }
+    return &mChannel;
+  }
 
-    const MessageChannel* GetIPCChannel() const override;
-    MessageChannel* GetIPCChannel() override;
-
-   private:
-    int32_t NextId();
+  // NOTE: The target actor's Manager must already be set.
+  void SetEventTargetForActorInternal(IProtocol* aActor,
+                                      nsIEventTarget* aEventTarget);
+  void ReplaceEventTargetForActor(IProtocol* aActor,
+                                  nsIEventTarget* aEventTarget);
+  void SetEventTargetForRoute(int32_t aRoute, nsIEventTarget* aEventTarget);
+  nsIEventTarget* GetActorEventTarget();
+  already_AddRefed<nsIEventTarget> GetActorEventTarget(IProtocol* aActor);
 
-    IToplevelProtocol* const mProtocol;
-    int32_t mLastLocalId;
-    IDMap<IProtocol*> mActorMap;
-    IDMap<Shmem::SharedMemory*> mShmemMap;
+  ProcessId OtherPid() const;
+  void SetOtherProcessId(base::ProcessId aOtherPid);
 
-    Mutex mEventTargetMutex;
-    IDMap<nsCOMPtr<nsIEventTarget>> mEventTargetMap;
-
-    MessageChannel mChannel;
-  };
-
+  // Toplevel protocol specific methods.
   void SetTransport(UniquePtr<Transport> aTrans) { mTrans = std::move(aTrans); }
 
   Transport* GetTransport() const { return mTrans.get(); }
 
-  base::ProcessId OtherPid() const final;
-  void SetOtherProcessId(base::ProcessId aOtherPid);
-
   virtual void OnChannelClose() = 0;
   virtual void OnChannelError() = 0;
   virtual void ProcessingError(Result aError, const char* aMsgName) {}
   virtual void OnChannelConnected(int32_t peer_pid) {}
 
   bool Open(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherPid,
             MessageLoop* aThread = nullptr,
             mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
@@ -589,24 +460,19 @@ class IToplevelProtocol : public IProtoc
   // will crash.
   bool OpenOnSameThread(MessageChannel* aChannel,
                         mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
 
   void Close();
 
   void SetReplyTimeoutMs(int32_t aTimeoutMs);
 
-  void DeallocShmems() { DowncastState()->DeallocShmems(); }
-
-  bool ShmemCreated(const Message& aMsg) {
-    return DowncastState()->ShmemCreated(aMsg);
-  }
-  bool ShmemDestroyed(const Message& aMsg) {
-    return DowncastState()->ShmemDestroyed(aMsg);
-  }
+  void DeallocShmems();
+  bool ShmemCreated(const Message& aMsg);
+  bool ShmemDestroyed(const Message& aMsg);
 
   virtual bool ShouldContinueFromReplyTimeout() { return false; }
 
   // WARNING: This function is called with the MessageChannel monitor held.
   virtual void IntentionalCrash() { MOZ_CRASH("Intentional IPDL crash"); }
 
   // The code here is only useful for fuzzing. It should not be used for any
   // other purpose.
@@ -651,51 +517,69 @@ class IToplevelProtocol : public IProtoc
 
   virtual void OnEnteredSyncSend() {}
   virtual void OnExitedSyncSend() {}
 
   virtual void ProcessRemoteNativeEventsInInterruptCall() {}
 
   virtual void OnChannelReceivedMessage(const Message& aMsg) {}
 
-  bool IsMainThreadProtocol() const { return mIsMainThreadProtocol; }
-  void OnIPCChannelOpened() {
-    mIsMainThreadProtocol = NS_IsMainThread();
-    ActorConnected();
-  }
+  void OnIPCChannelOpened() { ActorConnected(); }
+
+  already_AddRefed<nsIEventTarget> GetMessageEventTarget(const Message& aMsg);
 
-  already_AddRefed<nsIEventTarget> GetMessageEventTarget(const Message& aMsg) {
-    return DowncastState()->GetMessageEventTarget(aMsg);
+  void SetMiddlemanIPCChannel(MessageChannel* aChannel) {
+    // Middleman processes sometimes need to change the channel used by a
+    // protocol.
+    MOZ_RELEASE_ASSERT(recordreplay::IsMiddleman());
+    mMiddlemanChannelOverride = aChannel;
   }
 
  protected:
-  ToplevelState* DowncastState() const {
-    return static_cast<ToplevelState*>(mState.get());
-  }
-
   // Override this method in top-level protocols to change the event target
   // for a new actor (and its sub-actors).
   virtual already_AddRefed<nsIEventTarget> GetConstructedEventTarget(
       const Message& aMsg) {
     return nullptr;
   }
 
   // Override this method in top-level protocols to change the event target
   // for specific messages.
   virtual already_AddRefed<nsIEventTarget> GetSpecificMessageEventTarget(
       const Message& aMsg) {
     return nullptr;
   }
 
  private:
-  base::ProcessId OtherPidMaybeInvalid() const;
+  base::ProcessId OtherPidMaybeInvalid() const { return mOtherPid; }
+
+  int32_t NextId();
 
   UniquePtr<Transport> mTrans;
   base::ProcessId mOtherPid;
-  bool mIsMainThreadProtocol;
+
+  // NOTE NOTE NOTE
+  // Used to be on mState
+  int32_t mLastLocalId;
+  IDMap<IProtocol*> mActorMap;
+  IDMap<Shmem::SharedMemory*> mShmemMap;
+
+  // XXX: We no longer need mEventTargetMap for Quantum DOM, so it may be
+  // worthwhile to remove it before people start depending on it for other weird
+  // things.
+  Mutex mEventTargetMutex;
+  IDMap<nsCOMPtr<nsIEventTarget>> mEventTargetMap;
+
+  // In the middleman process for recordreplay, we override the channel which
+  // should be used by an actor. Due to this, we need to hold a separate pointer
+  // here which can be used to specify that we shouldn't send messages to our
+  // mChannel actor member. FIXME: This should probably be removed.
+  MessageChannel* mMiddlemanChannelOverride;
+
+  MessageChannel mChannel;
 };
 
 class IShmemAllocator {
  public:
   virtual bool AllocShmem(size_t aSize,
                           mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
                           mozilla::ipc::Shmem* aShmem) = 0;
   virtual bool AllocUnsafeShmem(
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -3634,17 +3634,17 @@ class _GenerateProtocolActorCode(ipdl.as
             self.cls.addstmts([onerror, Whitespace.NL])
 
         if (ptype.isToplevel() and ptype.isInterrupt()):
             processnative = MethodDefn(
                 MethodDecl('ProcessNativeEventsInInterruptCall', ret=Type.VOID))
             processnative.addcode(
                 '''
                 #ifdef OS_WIN
-                DowncastState()->GetIPCChannel()->ProcessNativeEventsInInterruptCall();
+                GetIPCChannel()->ProcessNativeEventsInInterruptCall();
                 #else
                 FatalError("This method is Windows-only");
                 #endif
                 ''')
 
             self.cls.addstmts([processnative, Whitespace.NL])
 
         # private methods
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -257,17 +257,17 @@ void HttpChannelParent::CleanupBackgroun
     }
   }
 }
 
 base::ProcessId HttpChannelParent::OtherPid() const {
   if (mIPCClosed) {
     return 0;
   }
-  return Manager()->OtherPid();
+  return IProtocol::OtherPid();
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsISupports
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ADDREF(HttpChannelParent)
 NS_IMPL_RELEASE(HttpChannelParent)
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -116,17 +116,17 @@ class HttpChannelParent final : public n
   // Calls SendSetPriority if mIPCClosed is false.
   void DoSendSetPriority(int16_t aValue);
 
   // Callback while background channel is ready.
   void OnBackgroundParentReady(HttpBackgroundChannelParent* aBgParent);
   // Callback while background channel is destroyed.
   void OnBackgroundParentDestroyed();
 
-  base::ProcessId OtherPid() const override;
+  base::ProcessId OtherPid() const;
 
   // Calling this method will cancel the HttpChannelChild because the consumer
   // needs to be relocated to another process.
   // Any OnStart/Stop/DataAvailable calls that follow will not be sent to the
   // child channel.
   void CancelChildCrossProcessRedirect();
 
   already_AddRefed<HttpChannelParentListener> GetParentListener();
--- a/tools/fuzzing/ipc/ProtocolFuzzer.h
+++ b/tools/fuzzing/ipc/ProtocolFuzzer.h
@@ -17,35 +17,24 @@ namespace ipc {
 
 class ProtocolFuzzerHelper {
  public:
   static mozilla::dom::ContentParent* CreateContentParent(
       mozilla::dom::ContentParent* aOpener, const nsAString& aRemoteType);
 
   static void CompositorBridgeParentSetup();
 
-  template <typename T>
-  static void AddShmemToProtocol(T* aProtocol, Shmem::SharedMemory* aSegment,
-                                 int32_t aId) {
-    GetToplevelState(aProtocol)->mShmemMap.AddWithID(aSegment, aId);
+  static void AddShmemToProtocol(IToplevelProtocol* aProtocol,
+                                 Shmem::SharedMemory* aSegment, int32_t aId) {
+    aProtocol->mShmemMap.AddWithID(aSegment, aId);
   }
 
-  template <typename T>
-  static void RemoveShmemFromProtocol(T* aProtocol, int32_t aId) {
-    GetToplevelState(aProtocol)->mShmemMap.RemoveIfPresent(aId);
-  }
-
- private:
-  template <typename T>
-  static mozilla::ipc::IToplevelProtocol::ToplevelState* GetToplevelState(
-      T* aProtocol) {
-    static_assert(std::is_base_of<mozilla::ipc::IToplevelProtocol, T>::value,
-                  "Only ToplevelProtocols are supported for now");
-    return static_cast<mozilla::ipc::IToplevelProtocol::ToplevelState*>(
-        static_cast<mozilla::ipc::IToplevelProtocol*>(aProtocol)->mState.get());
+  static void RemoveShmemFromProtocol(IToplevelProtocol* aProtocol,
+                                      int32_t aId) {
+    aProtocol->mShmemMap.RemoveIfPresent(aId);
   }
 };
 
 template <typename T>
 void FuzzProtocol(T* aProtocol, const uint8_t* aData, size_t aSize,
                   const nsTArray<nsCString>& aIgnoredMessageTypes) {
   while (true) {
     uint32_t msg_size =