diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 1f3955597d96..653b38ff79a9 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -1628,7 +1628,14 @@ MessageChannel::MessageTask::Post() mScheduled = true; RefPtr self = this; - mChannel->mWorkerLoop->PostTask(self.forget()); + nsCOMPtr eventTarget = + mChannel->mListener->GetMessageEventTarget(mMessage); + + if (eventTarget) { + eventTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL); + } else { + mChannel->mWorkerLoop->PostTask(self.forget()); + } } void diff --git a/ipc/glue/ProtocolUtils.cpp b/ipc/glue/ProtocolUtils.cpp index 9933a28b2274..8e076e37a512 100644 --- a/ipc/glue/ProtocolUtils.cpp +++ b/ipc/glue/ProtocolUtils.cpp @@ -417,6 +417,9 @@ IProtocol::Lookup(int32_t aId) void IProtocol::Unregister(int32_t aId) { + if (mId == aId) { + mId = kFreedActorId; + } Manager()->Unregister(aId); } @@ -518,12 +521,35 @@ IProtocol::DeallocShmem(Shmem& aMem) return ok; } +void +IProtocol::SetManager(IProtocol* aManager) +{ + MOZ_RELEASE_ASSERT(!mManager || mManager == aManager); + mManager = aManager; +} + +void +IProtocol::SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) +{ + // Make sure we have a manager for the internal method to access. + aActor->SetManager(this); + SetEventTargetForActorInternal(aActor, aEventTarget); +} + +void +IProtocol::SetEventTargetForActorInternal(IProtocol* aActor, + nsIEventTarget* aEventTarget) +{ + Manager()->SetEventTargetForActorInternal(aActor, aEventTarget); +} + IToplevelProtocol::IToplevelProtocol(ProtocolId aProtoId, Side aSide) : IProtocol(aSide), mProtocolId(aProtoId), mOtherPid(mozilla::ipc::kInvalidProcessId), - mLastRouteId(aSide == ParentSide ? 1 : 0), - mLastShmemId(aSide == ParentSide ? 1 : 0) + mLastRouteId(aSide == ParentSide ? kFreedActorId : kNullActorId), + mLastShmemId(aSide == ParentSide ? kFreedActorId : kNullActorId), + mEventTargetMutex("ProtocolEventTargetMutex") { } @@ -598,8 +624,22 @@ IToplevelProtocol::IsOnCxxStack() const 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 = GetSide() == ParentSide ? ++mLastRouteId : --mLastRouteId; mActorMap.AddWithID(aRouted, id); + aRouted->SetId(id); + + // Inherit our event target from our manager. + if (IProtocol* manager = aRouted->Manager()) { + MutexAutoLock lock(mEventTargetMutex); + if (nsCOMPtr target = mEventTargetMap.Lookup(manager->Id())) { + mEventTargetMap.AddWithID(target, id); + } + } + return id; } @@ -608,6 +648,7 @@ IToplevelProtocol::RegisterID(IProtocol* aRouted, int32_t aId) { mActorMap.AddWithID(aRouted, aId); + aRouted->SetId(aId); return aId; } @@ -620,7 +661,10 @@ IToplevelProtocol::Lookup(int32_t aId) void IToplevelProtocol::Unregister(int32_t aId) { - return mActorMap.Remove(aId); + mActorMap.Remove(aId); + + MutexAutoLock lock(mEventTargetMutex); + mEventTargetMap.RemoveIfPresent(aId); } Shmem::SharedMemory* @@ -726,5 +770,52 @@ IToplevelProtocol::ShmemDestroyed(const Message& aMsg) return true; } +already_AddRefed +IToplevelProtocol::GetMessageEventTarget(const Message& aMsg) +{ + int32_t route = aMsg.routing_id(); + + MutexAutoLock lock(mEventTargetMutex); + nsCOMPtr target = mEventTargetMap.Lookup(route); + + if (aMsg.is_constructor()) { + ActorHandle handle; + PickleIterator iter = PickleIterator(aMsg); + if (!IPC::ReadParam(&aMsg, &iter, &handle)) { + 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 = GetConstructedEventTarget(aMsg); + } + + mEventTargetMap.AddWithID(target, handle.mId); + } + + return target.forget(); +} + +void +IToplevelProtocol::SetEventTargetForActorInternal(IProtocol* aActor, + nsIEventTarget* aEventTarget) +{ + // 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); + + // 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); + mEventTargetMap.AddWithID(aEventTarget, id); +} + } // namespace ipc } // namespace mozilla diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index 3bc9e0c74480..3e5c2c6d195e 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -16,6 +16,7 @@ #include "prenv.h" #include "IPCMessageStart.h" +#include "mozilla/AlreadyAddRefed.h" #include "mozilla/Attributes.h" #include "mozilla/ipc/FileDescriptor.h" #include "mozilla/ipc/Shmem.h" @@ -56,6 +57,8 @@ enum { } // namespace +class nsIEventTarget; + namespace mozilla { namespace dom { class ContentParent; @@ -141,6 +144,8 @@ enum RacyInterruptPolicy { RIPParentWins }; +class IToplevelProtocol; + class IProtocol : public HasResultCodes { public: @@ -196,11 +201,26 @@ class IProtocol : public HasResultCodes bool AllocUnsafeShmem(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aOutMem); bool DeallocShmem(Shmem& aMem); + // 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); + protected: + friend class IToplevelProtocol; + void SetId(int32_t aId) { mId = aId; } - void SetManager(IProtocol* aManager) { mManager = aManager; } + void SetManager(IProtocol* aManager); void SetIPCChannel(MessageChannel* aChannel) { mChannel = aChannel; } + virtual void SetEventTargetForActorInternal(IProtocol* aActor, nsIEventTarget* aEventTarget); + + static const int32_t kNullActorId = 0; + static const int32_t kFreedActorId = 1; + private: int32_t mId; Side mSide; @@ -359,7 +379,16 @@ class IToplevelProtocol : public IProtocol virtual void ProcessRemoteNativeEventsInInterruptCall() { } -private: + virtual already_AddRefed + GetMessageEventTarget(const Message& aMsg); + +protected: + virtual already_AddRefed + GetConstructedEventTarget(const Message& aMsg) { return nullptr; } + + virtual void SetEventTargetForActorInternal(IProtocol* aActor, nsIEventTarget* aEventTarget); + + private: ProtocolId mProtocolId; UniquePtr mTrans; base::ProcessId mOtherPid; @@ -367,6 +396,9 @@ class IToplevelProtocol : public IProtocol int32_t mLastRouteId; IDMap mShmemMap; Shmem::id_t mLastShmemId; + + Mutex mEventTargetMutex; + IDMap> mEventTargetMap; }; class IShmemAllocator diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index b81f370aec5e..9f0430aabb4f 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -4096,15 +4096,15 @@ def ctorPrologue(self, md, errfn=ExprLiteral.NULL, idexpr=None): actortype = ipdl.type.ActorType(actorproto) if idexpr is None: - idexpr = ExprCall(self.protocol.registerMethod(), - args=[ actorvar ]) + registerexpr = ExprCall(self.protocol.registerMethod(), + args=[ actorvar ]) else: - idexpr = ExprCall(self.protocol.registerIDMethod(), - args=[ actorvar, idexpr ]) + registerexpr = ExprCall(self.protocol.registerIDMethod(), + args=[ actorvar, idexpr ]) return [ self.failIfNullActor(actorvar, errfn, msg="Error constructing actor %s" % actortype.name() + self.side.capitalize()), - StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetId'), args=[idexpr])), + StmtExpr(ExprCall(registerexpr)), StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetManager'), args=[ExprVar.THIS])), StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetIPCChannel'), args=[self.protocol.callGetChannel()])), @@ -4211,11 +4211,14 @@ def destroyActor(self, md, actorexpr, why=_DestroyReason.Deletion): destroyedType = md.decl.type.constructedType() else: destroyedType = self.protocol.decl.type - return ([ StmtExpr(self.callActorDestroy(actorexpr, why)), + managervar = ExprVar('mgr') + return ([ StmtDecl(Decl(Type('IProtocol', ptr=1), managervar.name), + init=self.protocol.managerVar(actorexpr)), + StmtExpr(self.callActorDestroy(actorexpr, why)), StmtExpr(self.callDeallocSubtree(md, actorexpr)), StmtExpr(self.callRemoveActor( actorexpr, - manager=self.protocol.managerVar(actorexpr), + manager=managervar, ipdltype=destroyedType)) ]) @@ -4358,8 +4361,7 @@ def failIfNullActor(self, actorExpr, retOnNull=ExprLiteral.FALSE, msg=None): def unregisterActor(self): return [ StmtExpr(ExprCall(self.protocol.unregisterMethod(), - args=[ _actorId() ])), - StmtExpr(ExprCall(ExprVar('SetId'), args=[_FREED_ACTOR_ID])) ] + args=[ _actorId() ])) ] def makeMessage(self, md, errfn, fromActor=None): msgvar = self.msgvar