diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh index fb1ce11d26ff..a93f4ff9b864 100644 --- a/dom/clients/manager/ClientIPCTypes.ipdlh +++ b/dom/clients/manager/ClientIPCTypes.ipdlh @@ -67,6 +67,19 @@ struct ClientControlledArgs IPCServiceWorkerDescriptor serviceWorker; }; +union ClientEndPoint +{ + IPCClientInfo; + IPCServiceWorkerDescriptor; +}; + +struct ClientMatchAllArgs +{ + ClientEndPoint endpoint; + ClientType type; + bool includeUncontrolled; +}; + struct ClientGetInfoAndStateArgs { nsID id; @@ -80,9 +93,15 @@ struct ClientOpenWindowArgs union ClientOpConstructorArgs { ClientControlledArgs; + ClientMatchAllArgs; ClientGetInfoAndStateArgs; }; +struct ClientList +{ + ClientInfoAndState[] values; +}; + struct ClientNavigateOpConstructorArgs { }; @@ -91,6 +110,7 @@ union ClientOpResult { nsresult; ClientInfoAndState; + ClientList; }; } // namespace dom diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp index 6e4ce6fcb9a4..04879f7e556c 100644 --- a/dom/clients/manager/ClientManager.cpp +++ b/dom/clients/manager/ClientManager.cpp @@ -257,6 +257,15 @@ ClientManager::CreateHandle(const ClientInfo& aClientInfo, return mgr->CreateHandleInternal(aClientInfo, aSerialEventTarget); } +// static +RefPtr +ClientManager::MatchAll(const ClientMatchAllArgs& aArgs, + nsISerialEventTarget* aSerialEventTarget) +{ + RefPtr mgr = GetOrCreateForCurrentThread(); + return mgr->StartOp(aArgs, aSerialEventTarget); +} + // static RefPtr ClientManager::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs, diff --git a/dom/clients/manager/ClientManager.h b/dom/clients/manager/ClientManager.h index 6b16ef1be1b4..afbd709f5f2f 100644 --- a/dom/clients/manager/ClientManager.h +++ b/dom/clients/manager/ClientManager.h @@ -22,6 +22,7 @@ class ClientGetInfoAndStateArgs; class ClientHandle; class ClientInfo; class ClientManagerChild; +class ClientMatchAllArgs; class ClientOpConstructorArgs; class ClientSource; enum class ClientType : uint8_t; @@ -94,6 +95,9 @@ public: CreateHandle(const ClientInfo& aClientInfo, nsISerialEventTarget* aSerialEventTarget); + static RefPtr + MatchAll(const ClientMatchAllArgs& aArgs, nsISerialEventTarget* aTarget); + static RefPtr GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs, nsISerialEventTarget* aSerialEventTarget); diff --git a/dom/clients/manager/ClientManagerOpParent.cpp b/dom/clients/manager/ClientManagerOpParent.cpp index e559625a3632..4a596b9e34b6 100644 --- a/dom/clients/manager/ClientManagerOpParent.cpp +++ b/dom/clients/manager/ClientManagerOpParent.cpp @@ -48,6 +48,12 @@ void ClientManagerOpParent::Init(const ClientOpConstructorArgs& aArgs) { switch (aArgs.type()) { + case ClientOpConstructorArgs::TClientMatchAllArgs: + { + DoServiceOp(&ClientManagerService::MatchAll, + aArgs.get_ClientMatchAllArgs()); + break; + } case ClientOpConstructorArgs::TClientGetInfoAndStateArgs: { DoServiceOp(&ClientManagerService::GetInfoAndState, diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp index 9d85d615ccd9..430cbac51999 100644 --- a/dom/clients/manager/ClientManagerService.cpp +++ b/dom/clients/manager/ClientManagerService.cpp @@ -302,6 +302,151 @@ ClientManagerService::RemoveManager(ClientManagerParent* aManager) MOZ_ASSERT(removed); } +namespace +{ + +class PromiseListHolder final +{ + RefPtr mResultPromise; + nsTArray> mPromiseList; + nsTArray mResultList; + uint32_t mOutstandingPromiseCount; + + void + ProcessSuccess(const ClientInfoAndState& aResult) + { + mResultList.AppendElement(aResult); + ProcessCompletion(); + } + + void + ProcessCompletion() + { + MOZ_DIAGNOSTIC_ASSERT(mOutstandingPromiseCount > 0); + mOutstandingPromiseCount -= 1; + MaybeFinish(); + } + + ~PromiseListHolder() = default; +public: + PromiseListHolder() + : mResultPromise(new ClientOpPromise::Private(__func__)) + , mOutstandingPromiseCount(0) + { + } + + already_AddRefed + GetResultPromise() + { + RefPtr kungFuDeathGrip = this; + mResultPromise->Then( + GetCurrentThreadSerialEventTarget(), __func__, + [kungFuDeathGrip] (const ClientOpResult& aResult) { }, + [kungFuDeathGrip] (nsresult aResult) { }); + + RefPtr ref = mResultPromise; + return ref.forget(); + } + + void + AddPromise(RefPtr&& aPromise) + { + mPromiseList.AppendElement(Move(aPromise)); + MOZ_DIAGNOSTIC_ASSERT(mPromiseList.LastElement()); + mOutstandingPromiseCount += 1; + + RefPtr self(this); + mPromiseList.LastElement()->Then( + GetCurrentThreadSerialEventTarget(), __func__, + [self] (const ClientOpResult& aResult) { + // TODO: This is pretty clunky. Try to figure out a better + // wait for MatchAll() and Claim() to share this code + // even though they expect different return values. + if (aResult.type() == ClientOpResult::TClientInfoAndState) { + self->ProcessSuccess(aResult.get_ClientInfoAndState()); + } else { + self->ProcessCompletion(); + } + }, [self] (nsresult aResult) { + self->ProcessCompletion(); + }); + } + + void + MaybeFinish() + { + if (!mOutstandingPromiseCount) { + mResultPromise->Resolve(mResultList, __func__); + } + } + + NS_INLINE_DECL_REFCOUNTING(PromiseListHolder) +}; + +} // anonymous namespace + +RefPtr +ClientManagerService::MatchAll(const ClientMatchAllArgs& aArgs) +{ + AssertIsOnBackgroundThread(); + + const ClientEndPoint& endpoint = aArgs.endpoint(); + + const PrincipalInfo& principalInfo = + endpoint.type() == ClientEndPoint::TIPCClientInfo + ? endpoint.get_IPCClientInfo().principalInfo() + : endpoint.get_IPCServiceWorkerDescriptor().principalInfo(); + + RefPtr promiseList = new PromiseListHolder(); + + for (auto iter = mSourceTable.Iter(); !iter.Done(); iter.Next()) { + ClientSourceParent* source = iter.UserData(); + MOZ_DIAGNOSTIC_ASSERT(source); + + if (source->IsFrozen() || !source->ExecutionReady()) { + continue; + } + + if (aArgs.type() != ClientType::All && + source->Info().Type() != aArgs.type()) { + continue; + } + + if (!MatchPrincipalInfo(source->Info().PrincipalInfo(), principalInfo)) { + continue; + } + + if (!aArgs.includeUncontrolled()) { + if (endpoint.type() != ClientEndPoint::TIPCServiceWorkerDescriptor) { + continue; + } + + const Maybe& controller = + source->GetController(); + if (controller.isNothing()) { + continue; + } + + const IPCServiceWorkerDescriptor& serviceWorker = + endpoint.get_IPCServiceWorkerDescriptor(); + + if(controller.ref().Id() != serviceWorker.id() || + controller.ref().Scope() != serviceWorker.scope()) { + continue; + } + } + + promiseList->AddPromise( + source->StartOp(Move(ClientGetInfoAndStateArgs(source->Info().Id(), + source->Info().PrincipalInfo())))); + } + + // Maybe finish the promise now in case we didn't find any matching clients. + promiseList->MaybeFinish(); + + return promiseList->GetResultPromise(); +} + RefPtr ClientManagerService::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs) { diff --git a/dom/clients/manager/ClientManagerService.h b/dom/clients/manager/ClientManagerService.h index 937ab23c2b3d..8842c03cc69a 100644 --- a/dom/clients/manager/ClientManagerService.h +++ b/dom/clients/manager/ClientManagerService.h @@ -59,6 +59,9 @@ public: void RemoveManager(ClientManagerParent* aManager); + RefPtr + MatchAll(const ClientMatchAllArgs& aArgs); + RefPtr GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs); diff --git a/dom/clients/manager/ClientSourceParent.cpp b/dom/clients/manager/ClientSourceParent.cpp index e3bd5b09513b..bb9672904968 100644 --- a/dom/clients/manager/ClientSourceParent.cpp +++ b/dom/clients/manager/ClientSourceParent.cpp @@ -225,6 +225,12 @@ ClientSourceParent::ExecutionReady() const return mExecutionReady; } +const Maybe& +ClientSourceParent::GetController() const +{ + return mController; +} + void ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle) { diff --git a/dom/clients/manager/ClientSourceParent.h b/dom/clients/manager/ClientSourceParent.h index 3c39036f7448..6a0e192c7011 100644 --- a/dom/clients/manager/ClientSourceParent.h +++ b/dom/clients/manager/ClientSourceParent.h @@ -70,6 +70,9 @@ public: bool ExecutionReady() const; + const Maybe& + GetController() const; + void AttachHandle(ClientHandleParent* aClientSource);